Render more than one item

Conrad Irwin created

Change summary

crates/command_palette2/src/command_palette.rs | 67 ++++------------
crates/gpui2/src/elements/uniform_list.rs      | 81 +++++++++++++++----
crates/gpui2/src/view.rs                       |  2 
3 files changed, 82 insertions(+), 68 deletions(-)

Detailed changes

crates/command_palette2/src/command_palette.rs 🔗

@@ -147,6 +147,7 @@ impl PickerDelegate for CommandPaletteDelegate {
     type ListItem = Div<Picker<Self>>;
 
     fn match_count(&self) -> usize {
+        dbg!(self.matches.len());
         self.matches.len()
     }
 
@@ -163,44 +164,11 @@ impl PickerDelegate for CommandPaletteDelegate {
         query: String,
         cx: &mut ViewContext<Picker<Self>>,
     ) -> gpui::Task<()> {
-        let view_id = &self.previous_focus_handle;
-        let window = cx.window();
-        cx.spawn(move |picker, mut cx| async move {
-            let mut actions = picker
-                .update(&mut cx, |this, _| this.delegate.commands.clone())
-                .expect("todo: handle picker no longer being around");
-            // _ = window
-            //     .available_actions(view_id, &cx)
-            //     .into_iter()
-            //     .flatten()
-            //     .filter_map(|(name, action, bindings)| {
-            //         let filtered = cx.read(|cx| {
-            //             if cx.has_global::<CommandPaletteFilter>() {
-            //                 let filter = cx.global::<CommandPaletteFilter>();
-            //                 filter.filtered_namespaces.contains(action.namespace())
-            //             } else {
-            //                 false
-            //             }
-            //         });
-
-            //         if filtered {
-            //             None
-            //         } else {
-            //             Some(Command {
-            //                 name: humanize_action_name(name),
-            //                 action,
-            //                 keystrokes: bindings
-            //                     .iter()
-            //                     .map(|binding| binding.keystrokes())
-            //                     .last()
-            //                     .map_or(Vec::new(), |keystrokes| keystrokes.to_vec()),
-            //             })
-            //         }
-            //     })
-            //     .collect::<Vec<_>>();
+        let mut commands = self.commands.clone();
 
+        cx.spawn(move |picker, mut cx| async move {
             cx.read_global::<HitCounts, _>(|hit_counts, _| {
-                actions.sort_by_key(|action| {
+                commands.sort_by_key(|action| {
                     (
                         Reverse(hit_counts.0.get(&action.name).cloned()),
                         action.name.clone(),
@@ -209,7 +177,7 @@ impl PickerDelegate for CommandPaletteDelegate {
             })
             .ok();
 
-            let candidates = actions
+            let candidates = commands
                 .iter()
                 .enumerate()
                 .map(|(ix, command)| StringMatchCandidate {
@@ -240,15 +208,13 @@ impl PickerDelegate for CommandPaletteDelegate {
                 )
                 .await
             };
-            let mut intercept_result = None;
-            // todo!() for vim mode
-            // cx.read(|cx| {
-            //     if cx.has_global::<CommandPaletteInterceptor>() {
-            //         cx.global::<CommandPaletteInterceptor>()(&query, cx)
-            //     } else {
-            //         None
-            //     }
-            // });
+
+            let mut intercept_result = cx
+                .try_read_global(|interceptor: &CommandPaletteInterceptor, cx| {
+                    (interceptor)(&query, cx)
+                })
+                .flatten();
+
             if *RELEASE_CHANNEL == ReleaseChannel::Dev {
                 if parse_zed_link(&query).is_some() {
                     intercept_result = Some(CommandInterceptResult {
@@ -266,11 +232,11 @@ impl PickerDelegate for CommandPaletteDelegate {
             {
                 if let Some(idx) = matches
                     .iter()
-                    .position(|m| actions[m.candidate_id].action.type_id() == action.type_id())
+                    .position(|m| commands[m.candidate_id].action.type_id() == action.type_id())
                 {
                     matches.remove(idx);
                 }
-                actions.push(Command {
+                commands.push(Command {
                     name: string.clone(),
                     action,
                     keystrokes: vec![],
@@ -278,7 +244,7 @@ impl PickerDelegate for CommandPaletteDelegate {
                 matches.insert(
                     0,
                     StringMatch {
-                        candidate_id: actions.len() - 1,
+                        candidate_id: commands.len() - 1,
                         string,
                         positions,
                         score: 0.0,
@@ -288,7 +254,8 @@ impl PickerDelegate for CommandPaletteDelegate {
             picker
                 .update(&mut cx, |picker, _| {
                     let delegate = &mut picker.delegate;
-                    delegate.commands = actions;
+                    dbg!(&matches);
+                    delegate.commands = commands;
                     delegate.matches = matches;
                     if delegate.matches.is_empty() {
                         delegate.selected_ix = 0;

crates/gpui2/src/elements/uniform_list.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
-    point, px, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element, ElementId,
-    ElementInteractivity, InteractiveElementState, LayoutId, Pixels, Point, Size,
+    point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element,
+    ElementId, ElementInteractivity, InteractiveElementState, LayoutId, Pixels, Point, Size,
     StatefulInteractive, StatefulInteractivity, StatelessInteractive, StatelessInteractivity,
     StyleRefinement, Styled, ViewContext,
 };
@@ -86,8 +86,14 @@ impl<V: 'static> Styled for UniformList<V> {
     }
 }
 
+#[derive(Default)]
+pub struct UniformListState {
+    interactive: InteractiveElementState,
+    item_size: Size<Pixels>,
+}
+
 impl<V: 'static> Element<V> for UniformList<V> {
-    type ElementState = InteractiveElementState;
+    type ElementState = UniformListState;
 
     fn id(&self) -> Option<crate::ElementId> {
         Some(self.id.clone())
@@ -95,20 +101,49 @@ impl<V: 'static> Element<V> for UniformList<V> {
 
     fn initialize(
         &mut self,
-        _: &mut V,
+        view_state: &mut V,
         element_state: Option<Self::ElementState>,
-        _: &mut ViewContext<V>,
+        cx: &mut ViewContext<V>,
     ) -> Self::ElementState {
-        element_state.unwrap_or_default()
+        element_state.unwrap_or_else(|| {
+            let item_size = self.measure_first_item(view_state, None, cx);
+            UniformListState {
+                interactive: InteractiveElementState::default(),
+                item_size,
+            }
+        })
     }
 
     fn layout(
         &mut self,
         _view_state: &mut V,
-        _element_state: &mut Self::ElementState,
+        element_state: &mut Self::ElementState,
         cx: &mut ViewContext<V>,
     ) -> LayoutId {
-        cx.request_layout(&self.computed_style(), None)
+        let max_items = self.item_count;
+        let item_size = element_state.item_size;
+        let rem_size = cx.rem_size();
+        cx.request_measured_layout(
+            self.computed_style(),
+            rem_size,
+            move |known_dimensions: Size<Option<Pixels>>, available_space: Size<AvailableSpace>| {
+                let desired_height = item_size.height * max_items;
+                let width = known_dimensions
+                    .width
+                    .unwrap_or(match available_space.width {
+                        AvailableSpace::Definite(x) => x,
+                        AvailableSpace::MinContent => item_size.width,
+                        AvailableSpace::MaxContent => item_size.width,
+                    });
+                let height = match available_space.height {
+                    AvailableSpace::Definite(x) => desired_height.min(x),
+                    AvailableSpace::MinContent => desired_height,
+                    AvailableSpace::MaxContent => desired_height,
+                };
+                dbg!(known_dimensions, available_space, size(width, height));
+                size(width, height)
+            },
+        )
     }
 
     fn paint(
@@ -133,12 +168,15 @@ impl<V: 'static> Element<V> for UniformList<V> {
         cx.with_z_index(style.z_index.unwrap_or(0), |cx| {
             let content_size;
             if self.item_count > 0 {
-                let item_height = self.measure_item_height(view_state, padded_bounds, cx);
+                let item_height = self
+                    .measure_first_item(view_state, Some(padded_bounds.size.width), cx)
+                    .height;
+                dbg!(item_height, padded_bounds);
                 if let Some(scroll_handle) = self.scroll_handle.clone() {
                     scroll_handle.0.lock().replace(ScrollHandleState {
                         item_height,
                         list_height: padded_bounds.size.height,
-                        scroll_offset: element_state.track_scroll_offset(),
+                        scroll_offset: element_state.interactive.track_scroll_offset(),
                     });
                 }
                 let visible_item_count = if item_height > px(0.) {
@@ -146,7 +184,9 @@ impl<V: 'static> Element<V> for UniformList<V> {
                 } else {
                     0
                 };
+                dbg!(visible_item_count);
                 let scroll_offset = element_state
+                    .interactive
                     .scroll_offset()
                     .map_or((0.0).into(), |offset| offset.y);
                 let first_visible_element_ix = (-scroll_offset / item_height).floor() as usize;
@@ -190,20 +230,25 @@ impl<V: 'static> Element<V> for UniformList<V> {
             let overflow = point(style.overflow.x, Overflow::Scroll);
 
             cx.with_z_index(0, |cx| {
-                self.interactivity
-                    .paint(bounds, content_size, overflow, element_state, cx);
+                self.interactivity.paint(
+                    bounds,
+                    content_size,
+                    overflow,
+                    &mut element_state.interactive,
+                    cx,
+                );
             });
         })
     }
 }
 
 impl<V> UniformList<V> {
-    fn measure_item_height(
+    fn measure_first_item(
         &self,
         view_state: &mut V,
-        list_bounds: Bounds<Pixels>,
+        list_width: Option<Pixels>,
         cx: &mut ViewContext<V>,
-    ) -> Pixels {
+    ) -> Size<Pixels> {
         let mut items = (self.render_items)(view_state, 0..1, cx);
         debug_assert!(items.len() == 1);
         let mut item_to_measure = items.pop().unwrap();
@@ -212,11 +257,13 @@ impl<V> UniformList<V> {
         cx.compute_layout(
             layout_id,
             Size {
-                width: AvailableSpace::Definite(list_bounds.size.width),
+                width: list_width.map_or(AvailableSpace::MinContent, |width| {
+                    AvailableSpace::Definite(width)
+                }),
                 height: AvailableSpace::MinContent,
             },
         );
-        cx.layout_bounds(layout_id).size.height
+        cx.layout_bounds(layout_id).size
     }
 
     pub fn track_scroll(mut self, handle: UniformListScrollHandle) -> Self {

crates/gpui2/src/view.rs 🔗

@@ -145,7 +145,7 @@ impl<V> Eq for WeakView<V> {}
 #[derive(Clone, Debug)]
 pub struct AnyView {
     model: AnyModel,
-    pub initialize: fn(&AnyView, &mut WindowContext) -> AnyBox,
+    initialize: fn(&AnyView, &mut WindowContext) -> AnyBox,
     layout: fn(&AnyView, &mut AnyBox, &mut WindowContext) -> LayoutId,
     paint: fn(&AnyView, &mut AnyBox, &mut WindowContext),
 }