Allow measuring arbitrary items in UniformList

Antonio Scandurra created

Change summary

crates/editor2/src/editor.rs              | 79 ++----------------------
crates/gpui2/src/elements/uniform_list.rs | 21 +++++-
2 files changed, 23 insertions(+), 77 deletions(-)

Detailed changes

crates/editor2/src/editor.rs 🔗

@@ -1605,6 +1605,13 @@ impl CodeActionsMenu {
         .bg(cx.theme().colors().element_background)
         .px_2()
         .py_1()
+        .with_width_from_item(
+            self.actions
+                .iter()
+                .enumerate()
+                .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
+                .map(|(ix, _)| ix),
+        )
         .render();
 
         if self.deployed_from_indicator {
@@ -1613,78 +1620,6 @@ impl CodeActionsMenu {
 
         (cursor_position, element)
     }
-    //     enum ActionTag {}
-
-    //     let container_style = style.autocomplete.container;
-    //     let actions = self.actions.clone();
-    //     let selected_item = self.selected_item;
-    //     let element = UniformList::new(
-    //         self.list.clone(),
-    //         actions.len(),
-    //         cx,
-    //         move |_, range, items, cx| {
-    //             let start_ix = range.start;
-    //             for (ix, action) in actions[range].iter().enumerate() {
-    //                 let item_ix = start_ix + ix;
-    //                 items.push(
-    //                     MouseEventHandler::new::<ActionTag, _>(item_ix, cx, |state, _| {
-    //                         let item_style = if item_ix == selected_item {
-    //                             style.autocomplete.selected_item
-    //                         } else if state.hovered() {
-    //                             style.autocomplete.hovered_item
-    //                         } else {
-    //                             style.autocomplete.item
-    //                         };
-
-    //                         Text::new(action.lsp_action.title.clone(), style.text.clone())
-    //                             .with_soft_wrap(false)
-    //                             .contained()
-    //                             .with_style(item_style)
-    //                     })
-    //                     .with_cursor_style(CursorStyle::PointingHand)
-    //                     .on_down(MouseButton::Left, move |_, this, cx| {
-    //                         let workspace = this
-    //                             .workspace
-    //                             .as_ref()
-    //                             .and_then(|(workspace, _)| workspace.upgrade(cx));
-    //                         cx.window_context().defer(move |cx| {
-    //                             if let Some(workspace) = workspace {
-    //                                 workspace.update(cx, |workspace, cx| {
-    //                                     if let Some(task) = Editor::confirm_code_action(
-    //                                         workspace,
-    //                                         &ConfirmCodeAction {
-    //                                             item_ix: Some(item_ix),
-    //                                         },
-    //                                         cx,
-    //                                     ) {
-    //                                         task.detach_and_log_err(cx);
-    //                                     }
-    //                                 });
-    //                             }
-    //                         });
-    //                     })
-    //                     .into_any(),
-    //                 );
-    //             }
-    //         },
-    //     )
-    //     .with_width_from_item(
-    //         self.actions
-    //             .iter()
-    //             .enumerate()
-    //             .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
-    //             .map(|(ix, _)| ix),
-    //     )
-    //     .contained()
-    //     .with_style(container_style)
-    //     .into_any();
-
-    //     if self.deployed_from_indicator {
-    //         *cursor_position.column_mut() = 0;
-    //     }
-
-    //     (cursor_position, element)
-    // }
 }
 
 pub struct CopilotState {

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

@@ -30,6 +30,7 @@ where
         id: id.clone(),
         style,
         item_count,
+        item_to_measure_index: 0,
         render_items: Box::new(move |view, visible_range, cx| {
             f(view, visible_range, cx)
                 .into_iter()
@@ -45,6 +46,7 @@ pub struct UniformList<V: 'static> {
     id: ElementId,
     style: StyleRefinement,
     item_count: usize,
+    item_to_measure_index: usize,
     render_items: Box<
         dyn for<'a> Fn(
             &'a mut V,
@@ -112,7 +114,7 @@ impl<V: 'static> Element<V> for UniformList<V> {
         cx: &mut ViewContext<V>,
     ) -> Self::ElementState {
         element_state.unwrap_or_else(|| {
-            let item_size = self.measure_first_item(view_state, None, cx);
+            let item_size = self.measure_item(view_state, None, cx);
             UniformListState {
                 interactive: InteractiveElementState::default(),
                 item_size,
@@ -174,7 +176,7 @@ impl<V: 'static> Element<V> for UniformList<V> {
             let content_size;
             if self.item_count > 0 {
                 let item_height = self
-                    .measure_first_item(view_state, Some(padded_bounds.size.width), cx)
+                    .measure_item(view_state, Some(padded_bounds.size.width), cx)
                     .height;
                 if let Some(scroll_handle) = self.scroll_handle.clone() {
                     scroll_handle.0.lock().replace(ScrollHandleState {
@@ -240,14 +242,23 @@ impl<V: 'static> Element<V> for UniformList<V> {
 }
 
 impl<V> UniformList<V> {
-    fn measure_first_item(
+    pub fn with_width_from_item(mut self, item_index: Option<usize>) -> Self {
+        self.item_to_measure_index = item_index.unwrap_or(0);
+        self
+    }
+
+    fn measure_item(
         &self,
         view_state: &mut V,
         list_width: Option<Pixels>,
         cx: &mut ViewContext<V>,
     ) -> Size<Pixels> {
-        let mut items = (self.render_items)(view_state, 0..1, cx);
-        debug_assert_eq!(items.len(), 1);
+        if self.item_count == 0 {
+            return Size::default();
+        }
+
+        let item_ix = cmp::min(self.item_to_measure_index, self.item_count - 1);
+        let mut items = (self.render_items)(view_state, item_ix..item_ix + 1, cx);
         let mut item_to_measure = items.pop().unwrap();
         let available_space = size(
             list_width.map_or(AvailableSpace::MinContent, |width| {