From 33cd6f520a32a271963d818794169d9e14858ef9 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 18 Nov 2023 21:51:47 -0700 Subject: [PATCH] Clean compile with redesigned element traits --- crates/collab_ui2/src/collab_panel.rs | 2 +- crates/collab_ui2/src/collab_titlebar_item.rs | 4 +- .../command_palette2/src/command_palette.rs | 4 +- crates/editor2/src/editor.rs | 35 ++--- crates/editor2/src/editor_tests.rs | 2 +- crates/editor2/src/element.rs | 39 +++--- crates/file_finder2/src/file_finder.rs | 7 +- crates/go_to_line2/src/go_to_line.rs | 2 +- crates/gpui2/src/element.rs | 130 ++++++++++++------ crates/gpui2/src/elements/div.rs | 24 ++-- crates/gpui2/src/elements/img.rs | 8 +- crates/gpui2/src/elements/overlay.rs | 8 +- crates/gpui2/src/elements/svg.rs | 8 +- crates/gpui2/src/elements/text.rs | 32 ++--- crates/gpui2/src/elements/uniform_list.rs | 8 +- crates/gpui2/src/view.rs | 24 ++-- crates/gpui2_macros/src/derive_render_once.rs | 4 + crates/picker2/src/picker2.rs | 8 +- crates/project_panel2/src/project_panel.rs | 8 +- crates/storybook2/src/stories/colors.rs | 4 +- crates/storybook2/src/stories/focus.rs | 2 +- crates/storybook2/src/stories/kitchen_sink.rs | 2 +- crates/storybook2/src/stories/picker.rs | 9 +- crates/storybook2/src/stories/scroll.rs | 2 +- crates/storybook2/src/stories/text.rs | 6 +- crates/storybook2/src/stories/z_index.rs | 24 ++-- crates/storybook2/src/storybook2.rs | 2 +- crates/terminal_view2/src/terminal_panel.rs | 2 +- crates/terminal_view2/src/terminal_view.rs | 10 +- crates/ui2/src/components/context_menu.rs | 31 ++--- crates/ui2/src/components/divider.rs | 21 ++- crates/workspace2/src/dock.rs | 9 +- crates/workspace2/src/pane.rs | 6 +- crates/workspace2/src/pane_group.rs | 3 +- crates/workspace2/src/status_bar.rs | 4 +- 35 files changed, 278 insertions(+), 216 deletions(-) diff --git a/crates/collab_ui2/src/collab_panel.rs b/crates/collab_ui2/src/collab_panel.rs index 22f6dc9d88..901348d2e2 100644 --- a/crates/collab_ui2/src/collab_panel.rs +++ b/crates/collab_ui2/src/collab_panel.rs @@ -3294,7 +3294,7 @@ impl CollabPanel { // .with_width(size.x()) // } -impl Render for CollabPanel { +impl Render for CollabPanel { type Element = Focusable>; fn render(&mut self, _cx: &mut ViewContext) -> Self::Element { diff --git a/crates/collab_ui2/src/collab_titlebar_item.rs b/crates/collab_ui2/src/collab_titlebar_item.rs index 570ff9f45e..42800269c7 100644 --- a/crates/collab_ui2/src/collab_titlebar_item.rs +++ b/crates/collab_ui2/src/collab_titlebar_item.rs @@ -31,7 +31,7 @@ use std::sync::Arc; use call::ActiveCall; use client::{Client, UserStore}; use gpui::{ - div, px, rems, AppContext, Component, Div, InteractiveElement, Model, ParentElement, Render, + div, px, rems, AppContext, Div, InteractiveElement, Model, ParentElement, Render, RenderOnce, Stateful, StatefulInteractiveElement, Styled, Subscription, ViewContext, VisualContext, WeakView, WindowBounds, }; @@ -81,7 +81,7 @@ pub struct CollabTitlebarItem { _subscriptions: Vec, } -impl Render for CollabTitlebarItem { +impl Render for CollabTitlebarItem { type Element = Stateful>; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { diff --git a/crates/command_palette2/src/command_palette.rs b/crates/command_palette2/src/command_palette.rs index 7e4994638e..cf3be8379a 100644 --- a/crates/command_palette2/src/command_palette.rs +++ b/crates/command_palette2/src/command_palette.rs @@ -1,7 +1,7 @@ use collections::{CommandPaletteFilter, HashMap}; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ - actions, div, prelude::*, Action, AppContext, Component, Dismiss, Div, FocusHandle, Keystroke, + actions, div, prelude::*, Action, AppContext, Dismiss, Div, FocusHandle, Keystroke, ManagedView, ParentElement, Render, Styled, View, ViewContext, VisualContext, WeakView, }; use picker::{Picker, PickerDelegate}; @@ -74,7 +74,7 @@ impl ManagedView for CommandPalette { } } -impl Render for CommandPalette { +impl Render for CommandPalette { type Element = Div; fn render(&mut self, _cx: &mut ViewContext) -> Self::Element { diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index b241b76184..6e9f0f0b6e 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -42,9 +42,9 @@ use gpui::{ actions, div, point, prelude::*, px, relative, rems, size, uniform_list, Action, AnyElement, AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context, EventEmitter, FocusHandle, FocusableView, FontFeatures, FontStyle, FontWeight, HighlightStyle, - Hsla, InputHandler, KeyContext, Model, MouseButton, ParentElement, Pixels, Render, Styled, - Subscription, Task, TextStyle, UniformListScrollHandle, View, ViewContext, VisualContext, - WeakView, WindowContext, + Hsla, InputHandler, KeyContext, Model, MouseButton, ParentElement, Pixels, Render, + SharedString, Styled, Subscription, Task, TextStyle, UniformListScrollHandle, View, + ViewContext, VisualContext, WeakView, WindowContext, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; @@ -1580,7 +1580,8 @@ impl CodeActionsMenu { ) .map(|task| task.detach_and_log_err(cx)); }) - .child(action.lsp_action.title.clone()) + // TASK: It would be good to make lsp_action.title a SharedString to avoid allocating here. + .child(SharedString::from(action.lsp_action.title.clone())) }) .collect() }, @@ -1595,7 +1596,7 @@ impl CodeActionsMenu { .max_by_key(|(_, action)| action.lsp_action.title.chars().count()) .map(|(ix, _)| ix), ) - .render_once(); + .render_into_any(); if self.deployed_from_indicator { *cursor_position.column_mut() = 0; @@ -4353,19 +4354,19 @@ impl Editor { style: &EditorStyle, is_active: bool, cx: &mut ViewContext, - ) -> Option> { + ) -> Option> { if self.available_code_actions.is_some() { Some( - IconButton::new("code_actions_indicator", ui::Icon::Bolt) - .on_click(|editor: &mut Editor, cx| { + IconButton::new("code_actions_indicator", ui::Icon::Bolt).on_click( + |editor: &mut Editor, cx| { editor.toggle_code_actions( &ToggleCodeActions { deployed_from_indicator: true, }, cx, ); - }) - .into_any(), + }, + ), ) } else { None @@ -4380,7 +4381,7 @@ impl Editor { line_height: Pixels, gutter_margin: Pixels, cx: &mut ViewContext, - ) -> Vec>> { + ) -> Vec>> { fold_data .iter() .enumerate() @@ -4392,16 +4393,16 @@ impl Editor { FoldStatus::Folded => ui::Icon::ChevronRight, FoldStatus::Foldable => ui::Icon::ChevronDown, }; - IconButton::new(ix as usize, icon) - .on_click(move |editor: &mut Editor, cx| match fold_status { + IconButton::new(ix as usize, icon).on_click( + move |editor: &mut Editor, cx| match fold_status { FoldStatus::Folded => { editor.unfold_at(&UnfoldAt { buffer_row }, cx); } FoldStatus::Foldable => { editor.fold_at(&FoldAt { buffer_row }, cx); } - }) - .into_any() + }, + ) }) }) .flatten() @@ -7792,7 +7793,7 @@ impl Editor { cx.editor_style.diagnostic_style.clone(), }, ))) - .render_once() + .render_into_any() } }), disposition: BlockDisposition::Below, @@ -9994,7 +9995,7 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend cx.write_to_clipboard(ClipboardItem::new(message.clone())); }) .tooltip(|_, cx| Tooltip::text("Copy diagnostic message", cx)) - .render_once() + .render_into_any() }) } diff --git a/crates/editor2/src/editor_tests.rs b/crates/editor2/src/editor_tests.rs index f23618aa21..e27f13cd59 100644 --- a/crates/editor2/src/editor_tests.rs +++ b/crates/editor2/src/editor_tests.rs @@ -3048,7 +3048,7 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) { position: snapshot.anchor_after(Point::new(2, 0)), disposition: BlockDisposition::Below, height: 1, - render: Arc::new(|_| div().render_once()), + render: Arc::new(|_| div().into_any()), }], Some(Autoscroll::fit()), cx, diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 3140008bc3..a207e064c5 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -490,6 +490,7 @@ impl EditorElement { for (ix, fold_indicator) in layout.fold_indicators.drain(..).enumerate() { if let Some(mut fold_indicator) = fold_indicator { + let mut fold_indicator = fold_indicator.render_into_any(); let available_space = size( AvailableSpace::MinContent, AvailableSpace::Definite(line_height * 0.55), @@ -509,20 +510,21 @@ impl EditorElement { } } - if let Some(mut indicator) = layout.code_actions_indicator.take() { + if let Some(indicator) = layout.code_actions_indicator.take() { + let mut button = indicator.button.render_into_any(); let available_space = size( AvailableSpace::MinContent, AvailableSpace::Definite(line_height), ); - let indicator_size = indicator.element.measure(available_space, editor, cx); + let indicator_size = button.measure(available_space, editor, cx); + let mut x = Pixels::ZERO; let mut y = indicator.row as f32 * line_height - scroll_top; // Center indicator. x += ((layout.gutter_padding + layout.gutter_margin) - indicator_size.width) / 2.; y += (line_height - indicator_size.height) / 2.; - indicator - .element - .draw(bounds.origin + point(x, y), available_space, editor, cx); + + button.draw(bounds.origin + point(x, y), available_space, editor, cx); } } @@ -1810,7 +1812,7 @@ impl EditorElement { .render_code_actions_indicator(&style, active, cx) .map(|element| CodeActionsIndicator { row: newest_selection_head.row(), - element, + button: element, }); } } @@ -2041,14 +2043,19 @@ impl EditorElement { // Can't use .and_then() because `.file_name()` and `.parent()` return references :( if let Some(path) = path { filename = path.file_name().map(|f| f.to_string_lossy().to_string()); - parent_path = - path.parent().map(|p| p.to_string_lossy().to_string() + "/"); + parent_path = path + .parent() + .map(|p| SharedString::from(p.to_string_lossy().to_string() + "/")); } h_stack() .size_full() .bg(gpui::red()) - .child(filename.unwrap_or_else(|| "untitled".to_string())) + .child( + filename + .map(SharedString::from) + .unwrap_or_else(|| "untitled".into()), + ) .children(parent_path) .children(jump_icon) // .p_x(gutter_padding) } else { @@ -2059,7 +2066,7 @@ impl EditorElement { .child("⋯") .children(jump_icon) // .p_x(gutter_padding) }; - element.render() + element.into_any() } }; @@ -2391,10 +2398,6 @@ enum Invisible { impl Element for EditorElement { type State = (); - fn element_id(&self) -> Option { - Some(self.editor_id.into()) - } - fn layout( &mut self, editor: &mut Editor, @@ -2469,6 +2472,10 @@ impl Element for EditorElement { impl RenderOnce for EditorElement { type Element = Self; + fn element_id(&self) -> Option { + Some(self.editor_id.into()) + } + fn render_once(self) -> Self::Element { self } @@ -3098,14 +3105,14 @@ pub struct LayoutState { context_menu: Option<(DisplayPoint, AnyElement)>, code_actions_indicator: Option, // hover_popovers: Option<(DisplayPoint, Vec>)>, - fold_indicators: Vec>>, + fold_indicators: Vec>>, tab_invisible: ShapedLine, space_invisible: ShapedLine, } struct CodeActionsIndicator { row: u32, - element: AnyElement, + button: IconButton, } struct PositionMap { diff --git a/crates/file_finder2/src/file_finder.rs b/crates/file_finder2/src/file_finder.rs index 3488b6916c..77bc14fa96 100644 --- a/crates/file_finder2/src/file_finder.rs +++ b/crates/file_finder2/src/file_finder.rs @@ -2,9 +2,8 @@ use collections::HashMap; use editor::{scroll::autoscroll::Autoscroll, Bias, Editor}; use fuzzy::{CharBag, PathMatch, PathMatchCandidate}; use gpui::{ - actions, div, AppContext, Component, Dismiss, Div, FocusHandle, InteractiveElement, - ManagedView, Model, ParentElement, Render, Styled, Task, View, ViewContext, VisualContext, - WeakView, + actions, div, AppContext, Dismiss, Div, FocusHandle, InteractiveElement, ManagedView, Model, + ParentElement, Render, RenderOnce, Styled, Task, View, ViewContext, VisualContext, WeakView, }; use picker::{Picker, PickerDelegate}; use project::{PathMatchCandidateSet, Project, ProjectPath, WorktreeId}; @@ -116,7 +115,7 @@ impl ManagedView for FileFinder { self.picker.focus_handle(cx) } } -impl Render for FileFinder { +impl Render for FileFinder { type Element = Div; fn render(&mut self, _cx: &mut ViewContext) -> Self::Element { diff --git a/crates/go_to_line2/src/go_to_line.rs b/crates/go_to_line2/src/go_to_line.rs index 26641bfa09..81e1d990f1 100644 --- a/crates/go_to_line2/src/go_to_line.rs +++ b/crates/go_to_line2/src/go_to_line.rs @@ -143,7 +143,7 @@ impl GoToLine { } } -impl Render for GoToLine { +impl Render for GoToLine { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { diff --git a/crates/gpui2/src/element.rs b/crates/gpui2/src/element.rs index c60c902e25..990de68b06 100644 --- a/crates/gpui2/src/element.rs +++ b/crates/gpui2/src/element.rs @@ -14,12 +14,50 @@ pub trait Render: 'static + Sized { pub trait RenderOnce: Sized { type Element: Element + 'static; + fn element_id(&self) -> Option; + fn render_once(self) -> Self::Element; fn render_into_any(self) -> AnyElement { self.render_once().into_any() } + fn draw( + self, + origin: Point, + available_space: Size, + view_state: &mut V, + cx: &mut ViewContext, + f: impl FnOnce(&mut >::State, &mut ViewContext) -> R, + ) -> R + where + T: Clone + Default + Debug + Into, + { + let element = self.render_once(); + let element_id = element.element_id(); + let element = DrawableElement { + element: Some(element), + phase: ElementDrawPhase::Start, + }; + let frame_state = DrawableElement::draw( + element, + origin, + available_space.map(Into::into), + view_state, + cx, + ); + + if let Some(mut frame_state) = frame_state { + f(&mut frame_state, cx) + } else { + cx.with_element_state(element_id.unwrap(), |element_state, cx| { + let mut element_state = element_state.unwrap(); + let result = f(&mut element_state, cx); + (result, element_state) + }) + } + } + fn map(self, f: impl FnOnce(Self) -> U) -> U where Self: Sized, @@ -52,8 +90,6 @@ pub trait RenderOnce: Sized { pub trait Element: 'static + RenderOnce { type State: 'static; - fn element_id(&self) -> Option; - fn layout( &mut self, view_state: &mut V, @@ -72,35 +108,6 @@ pub trait Element: 'static + RenderOnce { fn into_any(self) -> AnyElement { AnyElement::new(self) } - - fn draw( - self, - origin: Point, - available_space: Size, - view_state: &mut V, - cx: &mut ViewContext, - f: impl FnOnce(&mut Self::State, &mut ViewContext) -> R, - ) -> R - where - T: Clone + Default + Debug + Into, - { - let element_id = self.element_id(); - let element = DrawableElement { - element: Some(self), - phase: ElementDrawPhase::Start, - }; - let frame_state = element.draw(origin, available_space.map(Into::into), view_state, cx); - - if let Some(mut frame_state) = frame_state { - f(&mut frame_state, cx) - } else { - cx.with_element_state(element_id.unwrap(), |element_state, cx| { - let mut element_state = element_state.unwrap(); - let result = f(&mut element_state, cx); - (result, element_state) - }) - } - } } pub trait Component: 'static { @@ -131,10 +138,6 @@ impl CompositeElement { impl> Element for CompositeElement { type State = CompositeElementState; - fn element_id(&self) -> Option { - None - } - fn layout( &mut self, view: &mut V, @@ -174,6 +177,10 @@ impl> Element for CompositeElement { impl> RenderOnce for CompositeElement { type Element = Self; + fn element_id(&self) -> Option { + None + } + fn render_once(self) -> Self::Element { self } @@ -231,23 +238,21 @@ pub struct DrawableElement> { } #[derive(Default)] -enum ElementDrawPhase { +enum ElementDrawPhase { #[default] Start, LayoutRequested { layout_id: LayoutId, - frame_state: Option, + frame_state: Option, }, LayoutComputed { layout_id: LayoutId, available_space: Size, - frame_state: Option, + frame_state: Option, }, } -/// Internal struct that wraps an element to store Layout and ElementState after the element is rendered. -/// It's allocated as a trait object to erase the element type and wrapped in AnyElement for -/// improved usability. +/// A wrapper around an implementer of [Element] that allows it to be drawn in a window. impl> DrawableElement { fn new(element: E) -> Self { DrawableElement { @@ -379,6 +384,41 @@ impl> DrawableElement { } } +// impl> Element for DrawableElement { +// type State = >::State; + +// fn layout( +// &mut self, +// view_state: &mut V, +// element_state: Option, +// cx: &mut ViewContext, +// ) -> (LayoutId, Self::State) { + +// } + +// fn paint( +// self, +// bounds: Bounds, +// view_state: &mut V, +// element_state: &mut Self::State, +// cx: &mut ViewContext, +// ) { +// todo!() +// } +// } + +// impl> RenderOnce for DrawableElement { +// type Element = Self; + +// fn element_id(&self) -> Option { +// self.element.as_ref()?.element_id() +// } + +// fn render_once(self) -> Self::Element { +// self +// } +// } + impl ElementObject for Option> where E: Element, @@ -476,10 +516,6 @@ impl AnyElement { impl Element for AnyElement { type State = (); - fn element_id(&self) -> Option { - AnyElement::element_id(self) - } - fn layout( &mut self, view_state: &mut V, @@ -504,6 +540,10 @@ impl Element for AnyElement { impl RenderOnce for AnyElement { type Element = Self; + fn element_id(&self) -> Option { + AnyElement::element_id(self) + } + fn render_once(self) -> Self::Element { self } diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index 26de4ea25f..256791ed04 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -602,10 +602,6 @@ impl ParentElement for Div { impl Element for Div { type State = DivState; - fn element_id(&self) -> Option { - self.interactivity.element_id.clone() - } - fn layout( &mut self, view_state: &mut V, @@ -694,6 +690,10 @@ impl Element for Div { impl RenderOnce for Div { type Element = Self; + fn element_id(&self) -> Option { + self.interactivity.element_id.clone() + } + fn render_once(self) -> Self::Element { self } @@ -1293,10 +1293,6 @@ where { type State = E::State; - fn element_id(&self) -> Option { - self.element.element_id() - } - fn layout( &mut self, view_state: &mut V, @@ -1324,6 +1320,10 @@ where { type Element = Self; + fn element_id(&self) -> Option { + self.element.element_id() + } + fn render_once(self) -> Self::Element { self } @@ -1381,10 +1381,6 @@ where { type State = E::State; - fn element_id(&self) -> Option { - self.element.element_id() - } - fn layout( &mut self, view_state: &mut V, @@ -1412,6 +1408,10 @@ where { type Element = Self; + fn element_id(&self) -> Option { + self.element.element_id() + } + fn render_once(self) -> Self::Element { self } diff --git a/crates/gpui2/src/elements/img.rs b/crates/gpui2/src/elements/img.rs index 16f20869ab..b4c439d0cb 100644 --- a/crates/gpui2/src/elements/img.rs +++ b/crates/gpui2/src/elements/img.rs @@ -37,10 +37,6 @@ where impl Element for Img { type State = InteractiveElementState; - fn element_id(&self) -> Option { - self.interactivity.element_id.clone() - } - fn layout( &mut self, _view_state: &mut V, @@ -98,6 +94,10 @@ impl Element for Img { impl RenderOnce for Img { type Element = Self; + fn element_id(&self) -> Option { + self.interactivity.element_id.clone() + } + fn render_once(self) -> Self::Element { self } diff --git a/crates/gpui2/src/elements/overlay.rs b/crates/gpui2/src/elements/overlay.rs index 4e01b8aad1..79a3643bdc 100644 --- a/crates/gpui2/src/elements/overlay.rs +++ b/crates/gpui2/src/elements/overlay.rs @@ -60,10 +60,6 @@ impl ParentElement for Overlay { impl Element for Overlay { type State = OverlayState; - fn element_id(&self) -> Option { - None - } - fn layout( &mut self, view_state: &mut V, @@ -160,6 +156,10 @@ impl Element for Overlay { impl RenderOnce for Overlay { type Element = Self; + fn element_id(&self) -> Option { + None + } + fn render_once(self) -> Self::Element { self } diff --git a/crates/gpui2/src/elements/svg.rs b/crates/gpui2/src/elements/svg.rs index f6823c50d5..0940bdd1eb 100644 --- a/crates/gpui2/src/elements/svg.rs +++ b/crates/gpui2/src/elements/svg.rs @@ -26,10 +26,6 @@ impl Svg { impl Element for Svg { type State = InteractiveElementState; - fn element_id(&self) -> Option { - self.interactivity.element_id.clone() - } - fn layout( &mut self, _view_state: &mut V, @@ -62,6 +58,10 @@ impl Element for Svg { impl RenderOnce for Svg { type Element = Self; + fn element_id(&self) -> Option { + self.interactivity.element_id.clone() + } + fn render_once(self) -> Self::Element { self } diff --git a/crates/gpui2/src/elements/text.rs b/crates/gpui2/src/elements/text.rs index bfd63d3c3d..9c7cd35e25 100644 --- a/crates/gpui2/src/elements/text.rs +++ b/crates/gpui2/src/elements/text.rs @@ -11,10 +11,6 @@ use util::ResultExt; impl Element for &'static str { type State = TextState; - fn element_id(&self) -> Option { - None - } - fn layout( &mut self, _: &mut V, @@ -40,6 +36,10 @@ impl Element for &'static str { impl RenderOnce for &'static str { type Element = Self; + fn element_id(&self) -> Option { + None + } + fn render_once(self) -> Self::Element { self } @@ -48,10 +48,6 @@ impl RenderOnce for &'static str { impl Element for SharedString { type State = TextState; - fn element_id(&self) -> Option { - Some(self.clone().into()) - } - fn layout( &mut self, _: &mut V, @@ -78,6 +74,10 @@ impl Element for SharedString { impl RenderOnce for SharedString { type Element = Self; + fn element_id(&self) -> Option { + Some(self.clone().into()) + } + fn render_once(self) -> Self::Element { self } @@ -105,10 +105,6 @@ impl StyledText { impl Element for StyledText { type State = TextState; - fn element_id(&self) -> Option { - None - } - fn layout( &mut self, _view: &mut V, @@ -194,6 +190,10 @@ impl Element for StyledText { impl RenderOnce for StyledText { type Element = Self; + fn element_id(&self) -> Option { + None + } + fn render_once(self) -> Self::Element { self } @@ -300,10 +300,6 @@ struct InteractiveTextState { impl Element for InteractiveText { type State = InteractiveTextState; - fn element_id(&self) -> Option { - Some(self.id.clone()) - } - fn layout( &mut self, view_state: &mut V, @@ -346,6 +342,10 @@ impl Element for InteractiveText { impl RenderOnce for InteractiveText { type Element = Self; + fn element_id(&self) -> Option { + Some(self.id.clone()) + } + fn render_once(self) -> Self::Element { self } diff --git a/crates/gpui2/src/elements/uniform_list.rs b/crates/gpui2/src/elements/uniform_list.rs index 997904d913..caf18962ec 100644 --- a/crates/gpui2/src/elements/uniform_list.rs +++ b/crates/gpui2/src/elements/uniform_list.rs @@ -104,10 +104,6 @@ pub struct UniformListState { impl Element for UniformList { type State = UniformListState; - fn element_id(&self) -> Option { - Some(self.id.clone()) - } - fn layout( &mut self, view_state: &mut V, @@ -255,6 +251,10 @@ impl Element for UniformList { impl RenderOnce for UniformList { type Element = Self; + fn element_id(&self) -> Option { + Some(self.id.clone()) + } + fn render_once(self) -> Self::Element { self } diff --git a/crates/gpui2/src/view.rs b/crates/gpui2/src/view.rs index c32bc70e4a..6d6863b54b 100644 --- a/crates/gpui2/src/view.rs +++ b/crates/gpui2/src/view.rs @@ -208,10 +208,6 @@ impl> From> for AnyView { impl, ParentV: 'static> Element for View { type State = Option>; - fn element_id(&self) -> Option { - Some(self.model.entity_id.into()) - } - fn layout( &mut self, _parent_view: &mut ParentV, @@ -241,6 +237,10 @@ impl, ParentV: 'static> Element for View { impl, ParentV: 'static> RenderOnce for View { type Element = View; + fn element_id(&self) -> Option { + Some(self.model.entity_id.into()) + } + fn render_once(self) -> Self::Element { self } @@ -249,10 +249,6 @@ impl, ParentV: 'static> RenderOnce for View { impl Element for AnyView { type State = Option>; - fn element_id(&self) -> Option { - Some(self.model.entity_id.into()) - } - fn layout( &mut self, _view_state: &mut V, @@ -277,6 +273,10 @@ impl Element for AnyView { impl RenderOnce for AnyView { type Element = Self; + fn element_id(&self) -> Option { + Some(self.model.entity_id.into()) + } + fn render_once(self) -> Self::Element { self } @@ -334,10 +334,6 @@ where { type State = Option>; - fn element_id(&self) -> Option { - Some(self.view.entity_id().into()) - } - fn layout( &mut self, _: &mut ParentV, @@ -371,6 +367,10 @@ where { type Element = Self; + fn element_id(&self) -> Option { + self.element.as_ref().unwrap().element_id() + } + fn render_once(self) -> Self::Element { self } diff --git a/crates/gpui2_macros/src/derive_render_once.rs b/crates/gpui2_macros/src/derive_render_once.rs index ee01e22358..d1dcfc5dc1 100644 --- a/crates/gpui2_macros/src/derive_render_once.rs +++ b/crates/gpui2_macros/src/derive_render_once.rs @@ -33,6 +33,10 @@ pub fn derive_render_once(input: TokenStream) -> TokenStream { { type Element = gpui::CompositeElement<#view_type, Self>; + fn element_id(&self) -> Option { + None + } + fn render_once(self) -> Self::Element { gpui::CompositeElement::new(self) } diff --git a/crates/picker2/src/picker2.rs b/crates/picker2/src/picker2.rs index 3491fc3d4a..ef4ded65ea 100644 --- a/crates/picker2/src/picker2.rs +++ b/crates/picker2/src/picker2.rs @@ -1,7 +1,7 @@ use editor::Editor; use gpui::{ - div, prelude::*, uniform_list, AppContext, Component, Div, FocusHandle, FocusableView, - MouseButton, Render, Task, UniformListScrollHandle, View, ViewContext, WindowContext, + div, prelude::*, uniform_list, AppContext, Div, FocusHandle, FocusableView, MouseButton, + Render, Task, UniformListScrollHandle, View, ViewContext, WindowContext, }; use std::{cmp, sync::Arc}; use ui::{prelude::*, v_stack, Divider, Label, TextColor}; @@ -15,7 +15,7 @@ pub struct Picker { } pub trait PickerDelegate: Sized + 'static { - type ListItem: Component>; + type ListItem: RenderOnce>; fn match_count(&self) -> usize; fn selected_index(&self) -> usize; @@ -180,7 +180,7 @@ impl Picker { } } -impl Render for Picker { +impl Render for Picker { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { diff --git a/crates/project_panel2/src/project_panel.rs b/crates/project_panel2/src/project_panel.rs index 46ad7a7a41..7fbb6cd553 100644 --- a/crates/project_panel2/src/project_panel.rs +++ b/crates/project_panel2/src/project_panel.rs @@ -9,9 +9,9 @@ use file_associations::FileAssociations; use anyhow::{anyhow, Result}; use gpui::{ actions, div, px, uniform_list, Action, AppContext, AssetSource, AsyncWindowContext, - ClipboardItem, Component, Div, EventEmitter, FocusHandle, Focusable, FocusableView, - InteractiveElement, Model, MouseButton, ParentElement, Pixels, Point, PromptLevel, Render, - Stateful, StatefulInteractiveElement, Styled, Task, UniformListScrollHandle, View, ViewContext, + ClipboardItem, Div, EventEmitter, FocusHandle, Focusable, FocusableView, InteractiveElement, + Model, MouseButton, ParentElement, Pixels, Point, PromptLevel, Render, RenderOnce, Stateful, + StatefulInteractiveElement, Styled, Task, UniformListScrollHandle, View, ViewContext, VisualContext as _, WeakView, WindowContext, }; use menu::{Confirm, SelectNext, SelectPrev}; @@ -1423,7 +1423,7 @@ impl ProjectPanel { } } -impl Render for ProjectPanel { +impl Render for ProjectPanel { type Element = Focusable>>; fn render(&mut self, _cx: &mut gpui::ViewContext) -> Self::Element { diff --git a/crates/storybook2/src/stories/colors.rs b/crates/storybook2/src/stories/colors.rs index 4f8c54fa6f..b690435e01 100644 --- a/crates/storybook2/src/stories/colors.rs +++ b/crates/storybook2/src/stories/colors.rs @@ -5,7 +5,7 @@ use ui::prelude::*; pub struct ColorsStory; -impl Render for ColorsStory { +impl Render for ColorsStory { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { @@ -28,7 +28,7 @@ impl Render for ColorsStory { div() .w(px(75.)) .line_height(px(24.)) - .child(scale.name().to_string()), + .child(scale.name().clone()), ) .child( div() diff --git a/crates/storybook2/src/stories/focus.rs b/crates/storybook2/src/stories/focus.rs index 571882f1f2..12c7ea81a0 100644 --- a/crates/storybook2/src/stories/focus.rs +++ b/crates/storybook2/src/stories/focus.rs @@ -26,7 +26,7 @@ impl FocusStory { } } -impl Render for FocusStory { +impl Render for FocusStory { type Element = Focusable>>; fn render(&mut self, cx: &mut gpui::ViewContext) -> Self::Element { diff --git a/crates/storybook2/src/stories/kitchen_sink.rs b/crates/storybook2/src/stories/kitchen_sink.rs index 507aa8db2d..2d31cefed6 100644 --- a/crates/storybook2/src/stories/kitchen_sink.rs +++ b/crates/storybook2/src/stories/kitchen_sink.rs @@ -11,7 +11,7 @@ impl KitchenSinkStory { } } -impl Render for KitchenSinkStory { +impl Render for KitchenSinkStory { type Element = Stateful>; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { diff --git a/crates/storybook2/src/stories/picker.rs b/crates/storybook2/src/stories/picker.rs index a3f9ef5eb8..7c2412a02f 100644 --- a/crates/storybook2/src/stories/picker.rs +++ b/crates/storybook2/src/stories/picker.rs @@ -1,5 +1,7 @@ use fuzzy::StringMatchCandidate; -use gpui::{div, prelude::*, Div, KeyBinding, Render, Styled, Task, View, WindowContext}; +use gpui::{ + div, prelude::*, Div, KeyBinding, Render, SharedString, Styled, Task, View, WindowContext, +}; use picker::{Picker, PickerDelegate}; use std::sync::Arc; use theme2::ActiveTheme; @@ -54,7 +56,8 @@ impl PickerDelegate for Delegate { let Some(candidate_ix) = self.matches.get(ix) else { return div(); }; - let candidate = self.candidates[*candidate_ix].string.clone(); + // TASK: Make StringMatchCandidate::string a SharedString + let candidate = SharedString::from(self.candidates[*candidate_ix].string.clone()); div() .text_color(colors.text) @@ -202,7 +205,7 @@ impl PickerStory { } } -impl Render for PickerStory { +impl Render for PickerStory { type Element = Div; fn render(&mut self, cx: &mut gpui::ViewContext) -> Self::Element { diff --git a/crates/storybook2/src/stories/scroll.rs b/crates/storybook2/src/stories/scroll.rs index f1bb7b4e7c..bbab0b1d11 100644 --- a/crates/storybook2/src/stories/scroll.rs +++ b/crates/storybook2/src/stories/scroll.rs @@ -10,7 +10,7 @@ impl ScrollStory { } } -impl Render for ScrollStory { +impl Render for ScrollStory { type Element = Stateful>; fn render(&mut self, cx: &mut gpui::ViewContext) -> Self::Element { diff --git a/crates/storybook2/src/stories/text.rs b/crates/storybook2/src/stories/text.rs index 94c9d0d51f..722832c76a 100644 --- a/crates/storybook2/src/stories/text.rs +++ b/crates/storybook2/src/stories/text.rs @@ -1,6 +1,4 @@ -use gpui::{ - div, white, Div, ParentElement, Render, Styled, View, VisualContext, WindowContext, -}; +use gpui::{div, white, Div, ParentElement, Render, Styled, View, VisualContext, WindowContext}; pub struct TextStory; @@ -10,7 +8,7 @@ impl TextStory { } } -impl Render for TextStory { +impl Render for TextStory { type Element = Div; fn render(&mut self, cx: &mut gpui::ViewContext) -> Self::Element { diff --git a/crates/storybook2/src/stories/z_index.rs b/crates/storybook2/src/stories/z_index.rs index 4916f192b1..087ed913fd 100644 --- a/crates/storybook2/src/stories/z_index.rs +++ b/crates/storybook2/src/stories/z_index.rs @@ -1,4 +1,4 @@ -use gpui::{px, rgb, Div, Hsla, Render}; +use gpui::{px, rgb, Div, Hsla, Render, RenderOnce}; use ui::prelude::*; use crate::story::Story; @@ -7,7 +7,7 @@ use crate::story::Story; /// [https://developer.mozilla.org/en-US/docs/Web/CSS/z-index](https://developer.mozilla.org/en-US/docs/Web/CSS/z-index). pub struct ZIndexStory; -impl Render for ZIndexStory { +impl Render for ZIndexStory { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { @@ -79,17 +79,15 @@ trait Styles: Styled + Sized { impl Styles for Div {} -// #[derive(RenderOnce)] +#[derive(RenderOnce)] struct ZIndexExample { z_index: u32, } -impl ZIndexExample { - pub fn new(z_index: u32) -> Self { - Self { z_index } - } +impl Component for ZIndexExample { + type Rendered = Div; - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Element { + fn render(self, view: &mut V, cx: &mut ViewContext) -> Self::Rendered { div() .relative() .size_full() @@ -109,14 +107,14 @@ impl ZIndexExample { // HACK: Simulate `text-align: center`. .pl(px(24.)) .z_index(self.z_index) - .child(format!( + .child(SharedString::from(format!( "z-index: {}", if self.z_index == 0 { "auto".to_string() } else { self.z_index.to_string() } - )), + ))), ) // Blue blocks. .child( @@ -173,3 +171,9 @@ impl ZIndexExample { ) } } + +impl ZIndexExample { + pub fn new(z_index: u32) -> Self { + Self { z_index } + } +} diff --git a/crates/storybook2/src/storybook2.rs b/crates/storybook2/src/storybook2.rs index a0bc7cd72f..2a22d91382 100644 --- a/crates/storybook2/src/storybook2.rs +++ b/crates/storybook2/src/storybook2.rs @@ -105,7 +105,7 @@ impl StoryWrapper { } } -impl Render for StoryWrapper { +impl Render for StoryWrapper { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { diff --git a/crates/terminal_view2/src/terminal_panel.rs b/crates/terminal_view2/src/terminal_panel.rs index 6321e61e35..46885913ed 100644 --- a/crates/terminal_view2/src/terminal_panel.rs +++ b/crates/terminal_view2/src/terminal_panel.rs @@ -335,7 +335,7 @@ impl TerminalPanel { impl EventEmitter for TerminalPanel {} -impl Render for TerminalPanel { +impl Render for TerminalPanel { type Element = Div; fn render(&mut self, _cx: &mut ViewContext) -> Self::Element { diff --git a/crates/terminal_view2/src/terminal_view.rs b/crates/terminal_view2/src/terminal_view.rs index 8f5aeb5630..079f2b9f88 100644 --- a/crates/terminal_view2/src/terminal_view.rs +++ b/crates/terminal_view2/src/terminal_view.rs @@ -9,7 +9,7 @@ pub mod terminal_panel; // use crate::terminal_element::TerminalElement; use editor::{scroll::autoscroll::Autoscroll, Editor}; use gpui::{ - actions, div, img, red, Action, AnyElement, AppContext, Component, DispatchPhase, Div, + actions, div, img, red, Action, AnyElement, AppContext, DispatchPhase, Div, Element, EventEmitter, FocusEvent, FocusHandle, Focusable, FocusableElement, FocusableView, InputHandler, InteractiveElement, KeyDownEvent, Keystroke, Model, MouseButton, ParentElement, Pixels, Render, SharedString, Styled, Task, View, ViewContext, VisualContext, WeakView, @@ -538,7 +538,7 @@ impl TerminalView { } } -impl Render for TerminalView { +impl Render for TerminalView { type Element = Focusable>; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { @@ -578,7 +578,7 @@ impl Render for TerminalView { .children( self.context_menu .clone() - .map(|context_menu| div().z_index(1).absolute().child(context_menu.render())), + .map(|context_menu| div().z_index(1).absolute().child(context_menu)), ) .track_focus(&self.focus_handle) .on_focus_in(Self::focus_in) @@ -756,8 +756,8 @@ impl Item for TerminalView { div() .child(img().uri("icons/terminal.svg").bg(red())) - .child(title) - .render() + .child(SharedString::from(title)) + .into_any() } fn clone_on_split( diff --git a/crates/ui2/src/components/context_menu.rs b/crates/ui2/src/components/context_menu.rs index 41675e6bcd..6235704d7e 100644 --- a/crates/ui2/src/components/context_menu.rs +++ b/crates/ui2/src/components/context_menu.rs @@ -78,7 +78,7 @@ impl Render for ContextMenu { } pub struct MenuHandle { - id: Option, + id: ElementId, child_builder: Option AnyElement + 'static>>, menu_builder: Option) -> View + 'static>>, @@ -87,11 +87,6 @@ pub struct MenuHandle { } impl MenuHandle { - pub fn id(mut self, id: impl Into) -> Self { - self.id = Some(id.into()); - self - } - pub fn menu(mut self, f: impl Fn(&mut V, &mut ViewContext) -> View + 'static) -> Self { self.menu_builder = Some(Rc::new(f)); self @@ -116,9 +111,9 @@ impl MenuHandle { } } -pub fn menu_handle() -> MenuHandle { +pub fn menu_handle(id: impl Into) -> MenuHandle { MenuHandle { - id: None, + id: id.into(), child_builder: None, menu_builder: None, anchor: None, @@ -136,10 +131,6 @@ pub struct MenuHandleState { impl Element for MenuHandle { type State = MenuHandleState; - fn element_id(&self) -> Option { - Some(self.id.clone().expect("menu_handle must have an id()")) - } - fn layout( &mut self, view_state: &mut V, @@ -251,6 +242,10 @@ impl Element for MenuHandle { impl RenderOnce for MenuHandle { type Element = Self; + fn element_id(&self) -> Option { + Some(self.id.clone()) + } + fn render_once(self) -> Self::Element { self } @@ -297,8 +292,7 @@ mod stories { .flex_col() .justify_between() .child( - menu_handle() - .id("test2") + menu_handle("test2") .child(|is_open| { Label::new(if is_open { "TOP LEFT" @@ -309,8 +303,7 @@ mod stories { .menu(move |_, cx| build_menu(cx, "top left")), ) .child( - menu_handle() - .id("test1") + menu_handle("test1") .child(|is_open| { Label::new(if is_open { "BOTTOM LEFT" @@ -329,8 +322,7 @@ mod stories { .flex_col() .justify_between() .child( - menu_handle() - .id("test3") + menu_handle("test3") .child(|is_open| { Label::new(if is_open { "TOP RIGHT" @@ -342,8 +334,7 @@ mod stories { .menu(move |_, cx| build_menu(cx, "top right")), ) .child( - menu_handle() - .id("test4") + menu_handle("test4") .child(|is_open| { Label::new(if is_open { "BOTTOM RIGHT" diff --git a/crates/ui2/src/components/divider.rs b/crates/ui2/src/components/divider.rs index a651ab08af..206f2e26db 100644 --- a/crates/ui2/src/components/divider.rs +++ b/crates/ui2/src/components/divider.rs @@ -1,4 +1,4 @@ -use gpui::RenderOnce; +use gpui::{Div, RenderOnce}; use crate::prelude::*; @@ -7,12 +7,29 @@ enum DividerDirection { Vertical, } -// #[derive(RenderOnce)] +#[derive(RenderOnce)] pub struct Divider { direction: DividerDirection, inset: bool, } +impl Component for Divider { + type Rendered = Div; + + fn render(self, view: &mut V, cx: &mut ViewContext) -> Self::Rendered { + div() + .map(|this| match self.direction { + DividerDirection::Horizontal => { + this.h_px().w_full().when(self.inset, |this| this.mx_1p5()) + } + DividerDirection::Vertical => { + this.w_px().h_full().when(self.inset, |this| this.my_1p5()) + } + }) + .bg(cx.theme().colors().border_variant) + } +} + impl Divider { pub fn horizontal() -> Self { Self { diff --git a/crates/workspace2/src/dock.rs b/crates/workspace2/src/dock.rs index d6b8d9ae79..f143b2c2e5 100644 --- a/crates/workspace2/src/dock.rs +++ b/crates/workspace2/src/dock.rs @@ -1,8 +1,8 @@ use crate::{status_bar::StatusItemView, Axis, Workspace}; use gpui::{ - div, px, Action, AnchorCorner, AnyView, AppContext, Component, Div, Entity, EntityId, - EventEmitter, FocusHandle, FocusableView, ParentElement, Render, RenderOnce, SharedString, - Styled, Subscription, View, ViewContext, VisualContext, WeakView, WindowContext, + div, px, Action, AnchorCorner, AnyView, AppContext, Div, Entity, EntityId, EventEmitter, + FocusHandle, FocusableView, ParentElement, Render, RenderOnce, SharedString, Styled, + Subscription, View, ViewContext, VisualContext, WeakView, WindowContext, }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -653,8 +653,7 @@ impl Render for PanelButtons { }; Some( - menu_handle() - .id(name) + menu_handle(name) .menu(move |_, cx| { cx.build_view(|cx| ContextMenu::new(cx).header("SECTION")) }) diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index 4ab78d8bca..50eaa043ed 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -7,9 +7,9 @@ use crate::{ use anyhow::Result; use collections::{HashMap, HashSet, VecDeque}; use gpui::{ - actions, prelude::*, Action, AppContext, AsyncWindowContext, Component, Div, EntityId, - EventEmitter, FocusHandle, Focusable, FocusableView, Model, Pixels, Point, PromptLevel, Render, - Task, View, ViewContext, VisualContext, WeakView, WindowContext, + actions, prelude::*, Action, AppContext, AsyncWindowContext, Div, EntityId, EventEmitter, + FocusHandle, Focusable, FocusableView, Model, Pixels, Point, PromptLevel, Render, Task, View, + ViewContext, VisualContext, WeakView, WindowContext, }; use parking_lot::Mutex; use project2::{Project, ProjectEntryId, ProjectPath}; diff --git a/crates/workspace2/src/pane_group.rs b/crates/workspace2/src/pane_group.rs index 68f26295dc..5d02214f9f 100644 --- a/crates/workspace2/src/pane_group.rs +++ b/crates/workspace2/src/pane_group.rs @@ -7,8 +7,7 @@ use db2::sqlez::{ statement::Statement, }; use gpui::{ - point, size, AnyElement, AnyWeakView, Bounds, Div, Model, Pixels, Point, RenderOnce, View, - ViewContext, + point, size, AnyWeakView, Bounds, Div, Model, Pixels, Point, RenderOnce, View, ViewContext, }; use parking_lot::Mutex; use project2::Project; diff --git a/crates/workspace2/src/status_bar.rs b/crates/workspace2/src/status_bar.rs index f961cac8f0..945760260c 100644 --- a/crates/workspace2/src/status_bar.rs +++ b/crates/workspace2/src/status_bar.rs @@ -2,8 +2,8 @@ use std::any::TypeId; use crate::{ItemHandle, Pane}; use gpui::{ - div, AnyView, Component, Div, ParentElement, Render, RenderOnce, Styled, Subscription, View, - ViewContext, WindowContext, + div, AnyView, Div, ParentElement, Render, RenderOnce, Styled, Subscription, View, ViewContext, + WindowContext, }; use theme2::ActiveTheme; use ui::h_stack;