Fix non-unique ids passed to MouseEventHandlers

Max Brunsfeld and Antonio Scandurra created

Co-Authored-By: Antonio Scandurra <me@as-cii.com>

Change summary

crates/contacts_panel/src/contacts_panel.rs |  5 +
crates/editor/src/editor.rs                 | 37 ++++++---
crates/project_panel/src/project_panel.rs   | 90 ++++++++++------------
crates/workspace/src/sidebar.rs             | 10 +-
crates/workspace/src/workspace.rs           |  2 
5 files changed, 77 insertions(+), 67 deletions(-)

Detailed changes

crates/contacts_panel/src/contacts_panel.rs 🔗

@@ -27,6 +27,7 @@ impl ContactsPanel {
                 1000.,
                 {
                     let app_state = app_state.clone();
+                    let view_id = cx.view_id();
                     move |ix, cx| {
                         let user_store = app_state.user_store.read(cx);
                         let contacts = user_store.contacts().clone();
@@ -35,6 +36,7 @@ impl ContactsPanel {
                             &contacts[ix],
                             current_user_id,
                             app_state.clone(),
+                            view_id,
                             cx,
                         )
                     }
@@ -56,6 +58,7 @@ impl ContactsPanel {
         collaborator: &Contact,
         current_user_id: Option<u64>,
         app_state: Arc<AppState>,
+        view_id: usize,
         cx: &mut LayoutContext,
     ) -> ElementBox {
         let theme = &app_state.settings.borrow().theme.contacts_panel;
@@ -157,7 +160,7 @@ impl ContactsPanel {
                                 let app_state = app_state.clone();
 
                                 MouseEventHandler::new::<ContactsPanel, _, _, _>(
-                                    project_id as usize,
+                                    view_id,
                                     cx,
                                     |mouse_state, _| {
                                         let style = match (project.is_shared, mouse_state.hovered) {

crates/editor/src/editor.rs 🔗

@@ -523,6 +523,7 @@ impl ContextMenu {
 }
 
 struct CompletionsMenu {
+    editor_id: usize,
     id: CompletionId,
     initial_position: Anchor,
     buffer: ModelHandle<Buffer>,
@@ -560,6 +561,7 @@ impl CompletionsMenu {
         let settings = build_settings(cx);
         let completions = self.completions.clone();
         let matches = self.matches.clone();
+        let editor_id = self.editor_id;
         let selected_item = self.selected_item;
         UniformList::new(self.list.clone(), matches.len(), move |range, items, cx| {
             let settings = build_settings(cx);
@@ -569,7 +571,7 @@ impl CompletionsMenu {
                 let item_ix = start_ix + ix;
                 items.push(
                     MouseEventHandler::new::<CompletionTag, _, _, _>(
-                        mat.candidate_id,
+                        (editor_id, mat.candidate_id),
                         cx,
                         |state, _| {
                             let item_style = if item_ix == selected_item {
@@ -666,6 +668,7 @@ impl CompletionsMenu {
 
 #[derive(Clone)]
 struct CodeActionsMenu {
+    editor_id: usize,
     actions: Arc<[CodeAction]>,
     buffer: ModelHandle<Buffer>,
     selected_item: usize,
@@ -702,6 +705,7 @@ impl CodeActionsMenu {
 
         let settings = build_settings(cx);
         let actions = self.actions.clone();
+        let editor_id = self.editor_id;
         let selected_item = self.selected_item;
         let element =
             UniformList::new(self.list.clone(), actions.len(), move |range, items, cx| {
@@ -710,21 +714,28 @@ impl CodeActionsMenu {
                 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 {
-                                settings.style.autocomplete.selected_item
-                            } else if state.hovered {
-                                settings.style.autocomplete.hovered_item
-                            } else {
-                                settings.style.autocomplete.item
-                            };
-
-                            Text::new(action.lsp_action.title.clone(), settings.style.text.clone())
+                        MouseEventHandler::new::<ActionTag, _, _, _>(
+                            (editor_id, item_ix),
+                            cx,
+                            |state, _| {
+                                let item_style = if item_ix == selected_item {
+                                    settings.style.autocomplete.selected_item
+                                } else if state.hovered {
+                                    settings.style.autocomplete.hovered_item
+                                } else {
+                                    settings.style.autocomplete.item
+                                };
+
+                                Text::new(
+                                    action.lsp_action.title.clone(),
+                                    settings.style.text.clone(),
+                                )
                                 .with_soft_wrap(false)
                                 .contained()
                                 .with_style(item_style)
                                 .boxed()
-                        })
+                            },
+                        )
                         .with_cursor_style(CursorStyle::PointingHand)
                         .on_mouse_down(move |cx| {
                             cx.dispatch_action(ConfirmCodeAction(Some(item_ix)));
@@ -1937,6 +1948,7 @@ impl Editor {
                 }
 
                 let mut menu = CompletionsMenu {
+                    editor_id: this.id(),
                     id,
                     initial_position: position,
                     match_candidates: completions
@@ -2119,6 +2131,7 @@ impl Editor {
                         if let Some((buffer, actions)) = this.available_code_actions.clone() {
                             this.show_context_menu(
                                 ContextMenu::CodeActions(CodeActionsMenu {
+                                    editor_id: this.handle.id(),
                                     buffer,
                                     actions,
                                     selected_item: Default::default(),

crates/project_panel/src/project_panel.rs 🔗

@@ -476,58 +476,54 @@ impl ProjectPanel {
         cx: &mut ViewContext<Self>,
     ) -> ElementBox {
         let is_dir = details.is_dir;
-        MouseEventHandler::new::<Self, _, _, _>(
-            (entry.worktree_id.to_usize(), entry.entry_id),
-            cx,
-            |state, _| {
-                let style = match (details.is_selected, state.hovered) {
-                    (false, false) => &theme.entry,
-                    (false, true) => &theme.hovered_entry,
-                    (true, false) => &theme.selected_entry,
-                    (true, true) => &theme.hovered_selected_entry,
-                };
-                Flex::row()
-                    .with_child(
-                        ConstrainedBox::new(
-                            Align::new(
-                                ConstrainedBox::new(if is_dir {
-                                    if details.is_expanded {
-                                        Svg::new("icons/disclosure-open.svg")
-                                            .with_color(style.icon_color)
-                                            .boxed()
-                                    } else {
-                                        Svg::new("icons/disclosure-closed.svg")
-                                            .with_color(style.icon_color)
-                                            .boxed()
-                                    }
+        MouseEventHandler::new::<Self, _, _, _>((cx.view_id(), entry.entry_id), cx, |state, _| {
+            let style = match (details.is_selected, state.hovered) {
+                (false, false) => &theme.entry,
+                (false, true) => &theme.hovered_entry,
+                (true, false) => &theme.selected_entry,
+                (true, true) => &theme.hovered_selected_entry,
+            };
+            Flex::row()
+                .with_child(
+                    ConstrainedBox::new(
+                        Align::new(
+                            ConstrainedBox::new(if is_dir {
+                                if details.is_expanded {
+                                    Svg::new("icons/disclosure-open.svg")
+                                        .with_color(style.icon_color)
+                                        .boxed()
                                 } else {
-                                    Empty::new().boxed()
-                                })
-                                .with_max_width(style.icon_size)
-                                .with_max_height(style.icon_size)
-                                .boxed(),
-                            )
+                                    Svg::new("icons/disclosure-closed.svg")
+                                        .with_color(style.icon_color)
+                                        .boxed()
+                                }
+                            } else {
+                                Empty::new().boxed()
+                            })
+                            .with_max_width(style.icon_size)
+                            .with_max_height(style.icon_size)
                             .boxed(),
                         )
-                        .with_width(style.icon_size)
                         .boxed(),
                     )
-                    .with_child(
-                        Label::new(details.filename, style.text.clone())
-                            .contained()
-                            .with_margin_left(style.icon_spacing)
-                            .aligned()
-                            .left()
-                            .boxed(),
-                    )
-                    .constrained()
-                    .with_height(theme.entry.height)
-                    .contained()
-                    .with_style(style.container)
-                    .with_padding_left(theme.container.padding.left + details.depth as f32 * 20.)
-                    .boxed()
-            },
-        )
+                    .with_width(style.icon_size)
+                    .boxed(),
+                )
+                .with_child(
+                    Label::new(details.filename, style.text.clone())
+                        .contained()
+                        .with_margin_left(style.icon_spacing)
+                        .aligned()
+                        .left()
+                        .boxed(),
+                )
+                .constrained()
+                .with_height(theme.entry.height)
+                .contained()
+                .with_style(style.container)
+                .with_padding_left(theme.container.padding.left + details.depth as f32 * 20.)
+                .boxed()
+        })
         .on_click(move |cx| {
             if is_dir {
                 cx.dispatch_action(ToggleExpanded(entry))

crates/workspace/src/sidebar.rs 🔗

@@ -1,8 +1,6 @@
 use super::Workspace;
 use crate::Settings;
-use gpui::{
-    action, elements::*, platform::CursorStyle, AnyViewHandle, MutableAppContext, RenderContext,
-};
+use gpui::{action, elements::*, platform::CursorStyle, AnyViewHandle, RenderContext};
 use std::{cell::RefCell, rc::Rc};
 
 pub struct Sidebar {
@@ -126,7 +124,7 @@ impl Sidebar {
     pub fn render_active_item(
         &self,
         settings: &Settings,
-        cx: &mut MutableAppContext,
+        cx: &mut RenderContext<Workspace>,
     ) -> Option<ElementBox> {
         if let Some(active_item) = self.active_item() {
             let mut container = Flex::row();
@@ -159,11 +157,11 @@ impl Sidebar {
     fn render_resize_handle(
         &self,
         settings: &Settings,
-        mut cx: &mut MutableAppContext,
+        cx: &mut RenderContext<Workspace>,
     ) -> ElementBox {
         let width = self.width.clone();
         let side = self.side;
-        MouseEventHandler::new::<Self, _, _, _>(self.side.id(), &mut cx, |_, _| {
+        MouseEventHandler::new::<Self, _, _, _>((cx.view_id(), self.side.id()), cx, |_, _| {
             Container::new(Empty::new().boxed())
                 .with_style(self.theme(settings).resize_handle)
                 .boxed()

crates/workspace/src/workspace.rs 🔗

@@ -1252,7 +1252,7 @@ impl Workspace {
                 theme.workspace.titlebar.share_icon_color
             };
             Some(
-                MouseEventHandler::new::<Share, _, _, _>(0, cx, |_, _| {
+                MouseEventHandler::new::<Share, _, _, _>(cx.view_id(), cx, |_, _| {
                     Align::new(
                         ConstrainedBox::new(
                             Svg::new("icons/broadcast-24.svg").with_color(color).boxed(),