Checkpoint

Nathan Sobo created

Change summary

crates/gpui3/src/app/entity_map.rs           |   8 
crates/gpui3/src/view.rs                     |  12 +-
crates/gpui3_macros/src/derive_element.rs    |   1 
crates/storybook2/src/story_selector.rs      |   4 
crates/storybook2/src/storybook2.rs          |   1 
crates/ui2/src/components/assistant_panel.rs | 103 ++++++++++-----------
crates/ui2/src/components/chat_panel.rs      |  57 +++++------
crates/ui2/src/components/multi_buffer.rs    |   6 +
crates/ui2/src/components/panel.rs           |  46 +++++----
crates/ui2/src/components/project_panel.rs   |  15 +-
crates/ui2/src/components/workspace.rs       |  89 ++++++------------
11 files changed, 161 insertions(+), 181 deletions(-)

Detailed changes

crates/gpui3/src/app/entity_map.rs 🔗

@@ -102,10 +102,10 @@ impl<T> core::ops::DerefMut for Lease<T> {
 
 impl<T> Drop for Lease<T> {
     fn drop(&mut self) {
-        assert!(
-            self.entity.is_none(),
-            "Leases must be ended with EntityMap::end_lease"
-        );
+        if self.entity.is_some() {
+            // We don't panic here, because other panics can cause us to drop the lease without ending it cleanly.
+            log::error!("Leases must be ended with EntityMap::end_lease")
+        }
     }
 }
 

crates/gpui3/src/view.rs 🔗

@@ -33,8 +33,8 @@ pub fn view<S, E>(
     render: impl Fn(&mut S, &mut ViewContext<S>) -> E + Send + Sync + 'static,
 ) -> View<S>
 where
+    E: IntoAnyElement<S>,
     S: 'static + Send + Sync,
-    E: Element<ViewState = S>,
 {
     View {
         state,
@@ -86,6 +86,8 @@ impl<S: 'static + Send + Sync> Element for View<S> {
     }
 }
 
+impl<S: Send + Sync + 'static> IdentifiedElement for View<S> {}
+
 struct EraseViewState<ViewState: 'static + Send + Sync, ParentViewState> {
     view: View<ViewState>,
     parent_view_state_type: PhantomData<ParentViewState>,
@@ -137,11 +139,9 @@ where
 trait ViewObject: 'static + Send + Sync {
     fn entity_id(&self) -> EntityId;
     fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, AnyBox);
-    fn paint(&mut self, bounds: Bounds<Pixels>, element: &mut dyn Any, cx: &mut WindowContext);
+    fn paint(&mut self, bounds: Bounds<Pixels>, element: &mut AnyBox, cx: &mut WindowContext);
 }
 
-impl<S: Send + Sync + 'static> IdentifiedElement for View<S> {}
-
 impl<S: Send + Sync + 'static> ViewObject for View<S> {
     fn entity_id(&self) -> EntityId {
         self.state.id
@@ -158,7 +158,7 @@ impl<S: Send + Sync + 'static> ViewObject for View<S> {
         })
     }
 
-    fn paint(&mut self, _: Bounds<Pixels>, element: &mut dyn Any, cx: &mut WindowContext) {
+    fn paint(&mut self, _: Bounds<Pixels>, element: &mut AnyBox, cx: &mut WindowContext) {
         cx.with_element_id(IdentifiedElement::element_id(self), |cx| {
             self.state.update(cx, |state, cx| {
                 let element = element.downcast_mut::<AnyElement<S>>().unwrap();
@@ -208,7 +208,7 @@ impl Element for AnyView {
         element: &mut AnyBox,
         cx: &mut ViewContext<Self::ViewState>,
     ) {
-        self.view.lock().paint(bounds, element.as_mut(), cx)
+        self.view.lock().paint(bounds, element, cx)
     }
 }
 

crates/gpui3_macros/src/derive_element.rs 🔗

@@ -56,6 +56,7 @@ pub fn derive_element(input: TokenStream) -> TokenStream {
             type ElementState = gpui3::AnyElement<#state_type>;
 
             fn element_id(&self) -> Option<gpui3::ElementId> {
+                // todo!("What should element_id be here?")
                 None
             }
 

crates/storybook2/src/story_selector.rs 🔗

@@ -7,7 +7,7 @@ use clap::ValueEnum;
 use gpui3::AnyElement;
 use strum::{EnumIter, EnumString, IntoEnumIterator};
 
-use ui::prelude::*;
+use ui::{prelude::*, AssistantPanelStory};
 
 #[derive(Debug, PartialEq, Eq, Clone, Copy, strum::Display, EnumString, EnumIter)]
 #[strum(serialize_all = "snake_case")]
@@ -90,7 +90,7 @@ impl ComponentStory {
             Self::Toast => ui::ToastStory::new().into_any(),
             Self::Toolbar => ui::ToolbarStory::new().into_any(),
             Self::TrafficLights => ui::TrafficLightsStory::new().into_any(),
-            Self::Workspace => todo!(),
+            Self::Workspace => ui::workspace_story(cx).into_any().into_any(),
         }
     }
 }

crates/ui2/src/components/assistant_panel.rs 🔗

@@ -33,59 +33,50 @@ impl<S: 'static + Send + Sync + Clone> AssistantPanel<S> {
             pub scroll_state: ScrollState,
         }
 
-        Panel::new(
-            self.scroll_state.clone(),
-            |_, payload| {
-                let payload = payload.downcast_ref::<PanelPayload>().unwrap();
-
-                vec![div()
-                    .flex()
-                    .flex_col()
-                    .h_full()
-                    .px_2()
-                    .gap_2()
-                    // Header
-                    .child(
-                        div()
-                            .flex()
-                            .justify_between()
-                            .gap_2()
-                            .child(
-                                div()
-                                    .flex()
-                                    .child(IconButton::new(Icon::Menu))
-                                    .child(Label::new("New Conversation")),
-                            )
-                            .child(
-                                div()
-                                    .flex()
-                                    .items_center()
-                                    .gap_px()
-                                    .child(IconButton::new(Icon::SplitMessage))
-                                    .child(IconButton::new(Icon::Quote))
-                                    .child(IconButton::new(Icon::MagicWand))
-                                    .child(IconButton::new(Icon::Plus))
-                                    .child(IconButton::new(Icon::Maximize)),
-                            ),
-                    )
-                    // Chat Body
-                    .child(
-                        div()
-                            .w_full()
-                            .flex()
-                            .flex_col()
-                            .gap_3()
-                            .overflow_y_scroll(payload.scroll_state.clone())
-                            .child(Label::new("Is this thing on?")),
-                    )
-                    .into_any()]
-            },
-            Box::new(PanelPayload {
-                scroll_state: self.scroll_state.clone(),
-            }),
-        )
-        .side(self.current_side)
-        .width(AbsoluteLength::Rems(rems(32.)))
+        Panel::new(self.scroll_state.clone())
+            .children(vec![div()
+                .flex()
+                .flex_col()
+                .h_full()
+                .px_2()
+                .gap_2()
+                // Header
+                .child(
+                    div()
+                        .flex()
+                        .justify_between()
+                        .gap_2()
+                        .child(
+                            div()
+                                .flex()
+                                .child(IconButton::new(Icon::Menu))
+                                .child(Label::new("New Conversation")),
+                        )
+                        .child(
+                            div()
+                                .flex()
+                                .items_center()
+                                .gap_px()
+                                .child(IconButton::new(Icon::SplitMessage))
+                                .child(IconButton::new(Icon::Quote))
+                                .child(IconButton::new(Icon::MagicWand))
+                                .child(IconButton::new(Icon::Plus))
+                                .child(IconButton::new(Icon::Maximize)),
+                        ),
+                )
+                // Chat Body
+                .child(
+                    div()
+                        .w_full()
+                        .flex()
+                        .flex_col()
+                        .gap_3()
+                        .overflow_y_scroll(self.scroll_state.clone())
+                        .child(Label::new("Is this thing on?")),
+                )
+                .into_any()])
+            .side(self.current_side)
+            .width(AbsoluteLength::Rems(rems(32.)))
     }
 }
 
@@ -110,7 +101,11 @@ mod stories {
             }
         }
 
-        fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
+        fn render(
+            &mut self,
+            _view: &mut S,
+            cx: &mut ViewContext<S>,
+        ) -> impl Element<ViewState = S> {
             Story::container(cx)
                 .child(Story::title_for::<_, AssistantPanel<S>>(cx))
                 .child(Story::label(cx, "Default"))

crates/ui2/src/components/chat_panel.rs 🔗

@@ -20,7 +20,7 @@ impl<S: 'static + Send + Sync + Clone> ChatPanel<S> {
         }
     }
 
-    pub fn with_messages(mut self, messages: Vec<ChatMessage<S>>) -> Self {
+    pub fn messages(mut self, messages: Vec<ChatMessage<S>>) -> Self {
         self.messages = messages;
         self
     }
@@ -130,39 +130,36 @@ mod stories {
             }
         }
 
-        fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
+        fn render(
+            &mut self,
+            _view: &mut S,
+            cx: &mut ViewContext<S>,
+        ) -> impl Element<ViewState = S> {
             Story::container(cx)
                 .child(Story::title_for::<_, ChatPanel<S>>(cx))
                 .child(Story::label(cx, "Default"))
-                .child(Panel::new(
-                    ScrollState::default(),
-                    |_, _| vec![ChatPanel::new(ScrollState::default()).into_any()],
-                    Box::new(()),
-                ))
+                .child(
+                    Panel::new(ScrollState::default())
+                        .child(ChatPanel::new(ScrollState::default())),
+                )
                 .child(Story::label(cx, "With Mesages"))
-                .child(Panel::new(
-                    ScrollState::default(),
-                    |_, _| {
-                        vec![ChatPanel::new(ScrollState::default())
-                            .with_messages(vec![
-                                ChatMessage::new(
-                                    "osiewicz".to_string(),
-                                    "is this thing on?".to_string(),
-                                    DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z")
-                                        .unwrap()
-                                        .naive_local(),
-                                ),
-                                ChatMessage::new(
-                                    "maxdeviant".to_string(),
-                                    "Reading you loud and clear!".to_string(),
-                                    DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z")
-                                        .unwrap()
-                                        .naive_local(),
-                                ),
-                            ])
-                            .into_any()]
-                    },
-                    Box::new(()),
+                .child(Panel::new(ScrollState::default()).child(
+                    ChatPanel::new(ScrollState::default()).messages(vec![
+                        ChatMessage::new(
+                            "osiewicz".to_string(),
+                            "is this thing on?".to_string(),
+                            DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z")
+                                .unwrap()
+                                .naive_local(),
+                        ),
+                        ChatMessage::new(
+                            "maxdeviant".to_string(),
+                            "Reading you loud and clear!".to_string(),
+                            DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z")
+                                .unwrap()
+                                .naive_local(),
+                        ),
+                    ]),
                 ))
         }
     }

crates/ui2/src/components/multi_buffer.rs 🔗

@@ -62,7 +62,11 @@ mod stories {
             }
         }
 
-        fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
+        fn render(
+            &mut self,
+            _view: &mut S,
+            cx: &mut ViewContext<S>,
+        ) -> impl Element<ViewState = S> {
             let theme = theme(cx);
 
             Story::container(cx)

crates/ui2/src/components/panel.rs 🔗

@@ -1,6 +1,7 @@
 use std::marker::PhantomData;
 
-use gpui3::AbsoluteLength;
+use gpui3::{AbsoluteLength, AnyElement};
+use smallvec::SmallVec;
 
 use crate::{prelude::*, theme};
 use crate::{token, v_stack};
@@ -47,16 +48,11 @@ pub struct Panel<S: 'static + Send + Sync> {
     allowed_sides: PanelAllowedSides,
     initial_width: AbsoluteLength,
     width: Option<AbsoluteLength>,
-    children: HackyChildren<S>,
-    payload: HackyChildrenPayload,
+    children: SmallVec<[AnyElement<S>; 2]>,
 }
 
 impl<S: 'static + Send + Sync> Panel<S> {
-    pub fn new(
-        scroll_state: ScrollState,
-        children: HackyChildren<S>,
-        payload: HackyChildrenPayload,
-    ) -> Self {
+    pub fn new(scroll_state: ScrollState) -> Self {
         let token = token();
 
         Self {
@@ -66,8 +62,7 @@ impl<S: 'static + Send + Sync> Panel<S> {
             allowed_sides: PanelAllowedSides::default(),
             initial_width: token.default_panel_size,
             width: None,
-            children,
-            payload,
+            children: SmallVec::new(),
         }
     }
 
@@ -140,7 +135,15 @@ impl<S: 'static + Send + Sync> Panel<S> {
             }
         }
 
-        panel_base.children_any((self.children)(cx, self.payload.as_ref()))
+        panel_base.children(self.children.drain(..))
+    }
+}
+
+impl<S: 'static + Send + Sync> ParentElement for Panel<S> {
+    type State = S;
+
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::State>; 2]> {
+        &mut self.children
     }
 }
 
@@ -165,20 +168,21 @@ mod stories {
             }
         }
 
-        fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
+        fn render(
+            &mut self,
+            _view: &mut S,
+            cx: &mut ViewContext<S>,
+        ) -> impl Element<ViewState = S> {
             Story::container(cx)
                 .child(Story::title_for::<_, Panel<S>>(cx))
                 .child(Story::label(cx, "Default"))
-                .child(Panel::new(
-                    ScrollState::default(),
-                    |_, _| {
-                        vec![div()
+                .child(
+                    Panel::new(ScrollState::default()).child(
+                        div()
                             .overflow_y_scroll(ScrollState::default())
-                            .children((0..100).map(|ix| Label::new(format!("Item {}", ix + 1))))
-                            .into_any()]
-                    },
-                    Box::new(()),
-                ))
+                            .children((0..100).map(|ix| Label::new(format!("Item {}", ix + 1)))),
+                    ),
+                )
         }
     }
 }

crates/ui2/src/components/project_panel.rs 🔗

@@ -78,15 +78,18 @@ mod stories {
             }
         }
 
-        fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
+        fn render(
+            &mut self,
+            _view: &mut S,
+            cx: &mut ViewContext<S>,
+        ) -> impl Element<ViewState = S> {
             Story::container(cx)
                 .child(Story::title_for::<_, ProjectPanel<S>>(cx))
                 .child(Story::label(cx, "Default"))
-                .child(Panel::new(
-                    ScrollState::default(),
-                    |_, _| vec![ProjectPanel::new(ScrollState::default()).into_any()],
-                    Box::new(()),
-                ))
+                .child(
+                    Panel::new(ScrollState::default())
+                        .child(ProjectPanel::new(ScrollState::default())),
+                )
         }
     }
 }

crates/ui2/src/components/workspace.rs 🔗

@@ -244,27 +244,17 @@ impl Workspace {
                     .border_color(theme.lowest.base.default.border)
                     .children(
                         Some(
-                            Panel::new(
-                                self.left_panel_scroll_state.clone(),
-                                |_, payload| {
-                                    vec![ProjectPanel::new(ScrollState::default()).into_any()]
-                                },
-                                Box::new(()),
-                            )
-                            .side(PanelSide::Left),
+                            Panel::new(self.left_panel_scroll_state.clone())
+                                .side(PanelSide::Left)
+                                .child(ProjectPanel::new(ScrollState::default())),
                         )
                         .filter(|_| workspace_state.is_project_panel_open()),
                     )
                     .children(
                         Some(
-                            Panel::new(
-                                self.left_panel_scroll_state.clone(),
-                                |_, payload| {
-                                    vec![CollabPanel::new(ScrollState::default()).into_any()]
-                                },
-                                Box::new(()),
-                            )
-                            .side(PanelSide::Left),
+                            Panel::new(self.left_panel_scroll_state.clone())
+                                .child(CollabPanel::new(ScrollState::default()))
+                                .side(PanelSide::Left),
                         )
                         .filter(|_| workspace_state.is_collab_panel_open()),
                     )
@@ -285,57 +275,42 @@ impl Workspace {
                             )
                             .children(
                                 Some(
-                                    Panel::new(
-                                        self.bottom_panel_scroll_state.clone(),
-                                        |_, _| vec![Terminal::new().into_any()],
-                                        Box::new(()),
-                                    )
-                                    .allowed_sides(PanelAllowedSides::BottomOnly)
-                                    .side(PanelSide::Bottom),
+                                    Panel::new(self.bottom_panel_scroll_state.clone())
+                                        .child(Terminal::new())
+                                        .allowed_sides(PanelAllowedSides::BottomOnly)
+                                        .side(PanelSide::Bottom),
                                 )
                                 .filter(|_| workspace_state.show_terminal.load(Ordering::SeqCst)),
                             ),
                     )
                     .children(
                         Some(
-                            Panel::new(
-                                self.right_panel_scroll_state.clone(),
-                                |_, payload| {
-                                    vec![ChatPanel::new(ScrollState::default())
-                                        .with_messages(vec![
-                                            ChatMessage::new(
-                                                "osiewicz".to_string(),
-                                                "is this thing on?".to_string(),
-                                                DateTime::parse_from_rfc3339(
-                                                    "2023-09-27T15:40:52.707Z",
-                                                )
-                                                .unwrap()
-                                                .naive_local(),
-                                            ),
-                                            ChatMessage::new(
-                                                "maxdeviant".to_string(),
-                                                "Reading you loud and clear!".to_string(),
-                                                DateTime::parse_from_rfc3339(
-                                                    "2023-09-28T15:40:52.707Z",
-                                                )
-                                                .unwrap()
-                                                .naive_local(),
-                                            ),
-                                        ])
-                                        .into_any()]
-                                },
-                                Box::new(()),
-                            )
-                            .side(PanelSide::Right),
+                            Panel::new(self.right_panel_scroll_state.clone())
+                                .side(PanelSide::Right)
+                                .child(ChatPanel::new(ScrollState::default()).messages(vec![
+                                    ChatMessage::new(
+                                        "osiewicz".to_string(),
+                                        "is this thing on?".to_string(),
+                                        DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z")
+                                            .unwrap()
+                                            .naive_local(),
+                                    ),
+                                    ChatMessage::new(
+                                        "maxdeviant".to_string(),
+                                        "Reading you loud and clear!".to_string(),
+                                        DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z")
+                                            .unwrap()
+                                            .naive_local(),
+                                    ),
+                                ])),
                         )
                         .filter(|_| workspace_state.is_chat_panel_open()),
                     )
                     .children(
-                        Some(Panel::new(
-                            self.right_panel_scroll_state.clone(),
-                            |_, _| vec![AssistantPanel::new().into_any()],
-                            Box::new(()),
-                        ))
+                        Some(
+                            Panel::new(self.right_panel_scroll_state.clone())
+                                .child(AssistantPanel::new()),
+                        )
                         .filter(|_| workspace_state.is_assistant_panel_open()),
                     ),
             )