From 10580f96a3699bcf41933f33505ad741ced15697 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 17 Feb 2022 13:43:58 -0800 Subject: [PATCH] Automatically include current view id in element state ids --- crates/chat_panel/src/chat_panel.rs | 26 ++-- crates/contacts_panel/src/contacts_panel.rs | 7 +- crates/diagnostics/src/items.rs | 2 +- crates/editor/src/editor.rs | 39 ++---- crates/find/src/find.rs | 29 ++--- crates/gpui/src/app.rs | 118 ++++++++---------- .../gpui/src/elements/mouse_event_handler.rs | 12 +- crates/gpui/src/presenter.rs | 10 +- crates/gpui/src/views/select.rs | 6 +- crates/project_panel/src/project_panel.rs | 2 +- crates/workspace/src/pane.rs | 4 +- crates/workspace/src/sidebar.rs | 41 +++--- crates/workspace/src/workspace.rs | 4 +- 13 files changed, 129 insertions(+), 171 deletions(-) diff --git a/crates/chat_panel/src/chat_panel.rs b/crates/chat_panel/src/chat_panel.rs index 800084ff1d..6b04bd485d 100644 --- a/crates/chat_panel/src/chat_panel.rs +++ b/crates/chat_panel/src/chat_panel.rs @@ -325,21 +325,17 @@ impl ChatPanel { enum SignInPromptLabel {} Align::new( - MouseEventHandler::new::( - cx.view_id(), - cx, - |mouse_state, _| { - Label::new( - "Sign in to use chat".to_string(), - if mouse_state.hovered { - theme.chat_panel.hovered_sign_in_prompt.clone() - } else { - theme.chat_panel.sign_in_prompt.clone() - }, - ) - .boxed() - }, - ) + MouseEventHandler::new::(0, cx, |mouse_state, _| { + Label::new( + "Sign in to use chat".to_string(), + if mouse_state.hovered { + theme.chat_panel.hovered_sign_in_prompt.clone() + } else { + theme.chat_panel.sign_in_prompt.clone() + }, + ) + .boxed() + }) .with_cursor_style(CursorStyle::PointingHand) .on_click(move |cx| { let rpc = rpc.clone(); diff --git a/crates/contacts_panel/src/contacts_panel.rs b/crates/contacts_panel/src/contacts_panel.rs index b4ea4cf63d..9c8f4368a6 100644 --- a/crates/contacts_panel/src/contacts_panel.rs +++ b/crates/contacts_panel/src/contacts_panel.rs @@ -27,7 +27,6 @@ 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(); @@ -36,7 +35,6 @@ impl ContactsPanel { &contacts[ix], current_user_id, app_state.clone(), - view_id, cx, ) } @@ -58,7 +56,6 @@ impl ContactsPanel { collaborator: &Contact, current_user_id: Option, app_state: Arc, - view_id: usize, cx: &mut LayoutContext, ) -> ElementBox { let theme = &app_state.settings.borrow().theme.contacts_panel; @@ -159,8 +156,8 @@ impl ContactsPanel { let is_shared = project.is_shared; let app_state = app_state.clone(); - MouseEventHandler::new::( - (view_id, project_id as usize), + MouseEventHandler::new::( + project_id as usize, cx, |mouse_state, _| { let style = match (project.is_shared, mouse_state.hovered) { diff --git a/crates/diagnostics/src/items.rs b/crates/diagnostics/src/items.rs index 7949fe952c..581999919d 100644 --- a/crates/diagnostics/src/items.rs +++ b/crates/diagnostics/src/items.rs @@ -57,7 +57,7 @@ impl View for DiagnosticSummary { let theme = &self.settings.borrow().theme.project_diagnostics; let in_progress = self.in_progress; - MouseEventHandler::new::(cx.view_id(), cx, |_, _| { + MouseEventHandler::new::(0, cx, |_, _| { if in_progress { Label::new( "Checking... ".to_string(), diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index cecf5108bb..d7db6d747e 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -523,7 +523,6 @@ impl ContextMenu { } struct CompletionsMenu { - editor_id: usize, id: CompletionId, initial_position: Anchor, buffer: ModelHandle, @@ -561,7 +560,6 @@ 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); @@ -570,8 +568,8 @@ impl CompletionsMenu { let completion = &completions[mat.candidate_id]; let item_ix = start_ix + ix; items.push( - MouseEventHandler::new::( - (editor_id, mat.candidate_id), + MouseEventHandler::new::( + mat.candidate_id, cx, |state, _| { let item_style = if item_ix == selected_item { @@ -668,7 +666,6 @@ impl CompletionsMenu { #[derive(Clone)] struct CodeActionsMenu { - editor_id: usize, actions: Arc<[CodeAction]>, buffer: ModelHandle, selected_item: usize, @@ -705,7 +702,6 @@ 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| { @@ -714,28 +710,21 @@ impl CodeActionsMenu { for (ix, action) in actions[range].iter().enumerate() { let item_ix = start_ix + ix; items.push( - MouseEventHandler::new::( - (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 - }; + MouseEventHandler::new::(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(), - ) + 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))); @@ -1948,7 +1937,6 @@ impl Editor { } let mut menu = CompletionsMenu { - editor_id: this.id(), id, initial_position: position, match_candidates: completions @@ -2131,7 +2119,6 @@ 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(), @@ -2277,7 +2264,7 @@ impl Editor { enum Tag {} let style = (self.build_settings)(cx).style; Some( - MouseEventHandler::new::(cx.view_id(), cx, |_, _| { + MouseEventHandler::new::(0, cx, |_, _| { Svg::new("icons/zap.svg") .with_color(style.code_actions_indicator) .boxed() diff --git a/crates/find/src/find.rs b/crates/find/src/find.rs index 47d31cf765..1088edf663 100644 --- a/crates/find/src/find.rs +++ b/crates/find/src/find.rs @@ -227,7 +227,7 @@ impl FindBar { ) -> ElementBox { let theme = &self.settings.borrow().theme.find; let is_active = self.is_mode_enabled(mode); - MouseEventHandler::new::((cx.view_id(), mode as usize), cx, |state, _| { + MouseEventHandler::new::(mode as usize, cx, |state, _| { let style = match (is_active, state.hovered) { (false, false) => &theme.mode_button, (false, true) => &theme.hovered_mode_button, @@ -251,21 +251,18 @@ impl FindBar { cx: &mut RenderContext, ) -> ElementBox { let theme = &self.settings.borrow().theme.find; - MouseEventHandler::new::( - (cx.view_id(), 10 + direction as usize), - cx, - |state, _| { - let style = if state.hovered { - &theme.hovered_mode_button - } else { - &theme.mode_button - }; - Label::new(icon.to_string(), style.text.clone()) - .contained() - .with_style(style.container) - .boxed() - }, - ) + enum NavButton {} + MouseEventHandler::new::(direction as usize, cx, |state, _| { + let style = if state.hovered { + &theme.hovered_mode_button + } else { + &theme.mode_button + }; + Label::new(icon.to_string(), style.text.clone()) + .contained() + .with_style(style.container) + .boxed() + }) .on_click(move |cx| cx.dispatch_action(GoToMatch(direction))) .with_cursor_style(CursorStyle::PointingHand) .boxed() diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index af07b9eca5..80b9ead9bc 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -116,6 +116,26 @@ pub trait UpdateView { T: View; } +pub trait ElementStateContext: DerefMut { + fn current_view_id(&self) -> usize; + + fn element_state( + &mut self, + element_id: usize, + ) -> ElementStateHandle { + let id = ElementStateId { + view_id: self.current_view_id(), + element_id, + tag: TypeId::of::(), + }; + self.cx + .element_states + .entry(id) + .or_insert_with(|| Box::new(T::default())); + ElementStateHandle::new(id, self.frame_count, &self.cx.ref_counts) + } +} + pub trait Action: 'static + AnyAction { type Argument: 'static + Clone; } @@ -1414,23 +1434,6 @@ impl MutableAppContext { }) } - pub fn element_state( - &mut self, - id: ElementStateId, - ) -> ElementStateHandle { - let key = (TypeId::of::(), id); - self.cx - .element_states - .entry(key) - .or_insert_with(|| Box::new(T::default())); - ElementStateHandle::new( - TypeId::of::(), - id, - self.frame_count, - &self.cx.ref_counts, - ) - } - fn remove_dropped_entities(&mut self) { loop { let (dropped_models, dropped_views, dropped_element_states) = @@ -1850,7 +1853,7 @@ pub struct AppContext { models: HashMap>, views: HashMap<(usize, usize), Box>, windows: HashMap, - element_states: HashMap<(TypeId, ElementStateId), Box>, + element_states: HashMap>, background: Arc, ref_counts: Arc>, font_cache: Arc, @@ -2607,6 +2610,12 @@ impl ReadView for RenderContext<'_, V> { } } +impl ElementStateContext for RenderContext<'_, V> { + fn current_view_id(&self) -> usize { + self.view_id + } +} + impl AsRef for ViewContext<'_, M> { fn as_ref(&self) -> &AppContext { &self.app.cx @@ -2687,6 +2696,12 @@ impl UpdateView for ViewContext<'_, V> { } } +impl ElementStateContext for ViewContext<'_, V> { + fn current_view_id(&self) -> usize { + self.view_id + } +} + pub trait Handle { type Weak: 'static; fn id(&self) -> usize; @@ -3430,41 +3445,24 @@ impl Hash for WeakViewHandle { } } -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub struct ElementStateId(usize, usize); - -impl From for ElementStateId { - fn from(id: usize) -> Self { - Self(id, 0) - } -} - -impl From<(usize, usize)> for ElementStateId { - fn from(id: (usize, usize)) -> Self { - Self(id.0, id.1) - } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct ElementStateId { + view_id: usize, + element_id: usize, + tag: TypeId, } pub struct ElementStateHandle { value_type: PhantomData, - tag_type_id: TypeId, id: ElementStateId, ref_counts: Weak>, } impl ElementStateHandle { - fn new( - tag_type_id: TypeId, - id: ElementStateId, - frame_id: usize, - ref_counts: &Arc>, - ) -> Self { - ref_counts - .lock() - .inc_element_state(tag_type_id, id, frame_id); + fn new(id: ElementStateId, frame_id: usize, ref_counts: &Arc>) -> Self { + ref_counts.lock().inc_element_state(id, frame_id); Self { value_type: PhantomData, - tag_type_id, id, ref_counts: Arc::downgrade(ref_counts), } @@ -3472,7 +3470,7 @@ impl ElementStateHandle { pub fn read<'a>(&self, cx: &'a AppContext) -> &'a T { cx.element_states - .get(&(self.tag_type_id, self.id)) + .get(&self.id) .unwrap() .downcast_ref() .unwrap() @@ -3482,17 +3480,12 @@ impl ElementStateHandle { where C: DerefMut, { - let mut element_state = cx - .deref_mut() - .cx - .element_states - .remove(&(self.tag_type_id, self.id)) - .unwrap(); + let mut element_state = cx.deref_mut().cx.element_states.remove(&self.id).unwrap(); let result = f(element_state.downcast_mut().unwrap(), cx); cx.deref_mut() .cx .element_states - .insert((self.tag_type_id, self.id), element_state); + .insert(self.id, element_state); result } } @@ -3500,9 +3493,7 @@ impl ElementStateHandle { impl Drop for ElementStateHandle { fn drop(&mut self) { if let Some(ref_counts) = self.ref_counts.upgrade() { - ref_counts - .lock() - .dec_element_state(self.tag_type_id, self.id); + ref_counts.lock().dec_element_state(self.id); } } } @@ -3600,10 +3591,10 @@ impl Drop for Subscription { #[derive(Default)] struct RefCounts { entity_counts: HashMap, - element_state_counts: HashMap<(TypeId, ElementStateId), ElementStateRefCount>, + element_state_counts: HashMap, dropped_models: HashSet, dropped_views: HashSet<(usize, usize)>, - dropped_element_states: HashSet<(TypeId, ElementStateId)>, + dropped_element_states: HashSet, } struct ElementStateRefCount { @@ -3634,8 +3625,8 @@ impl RefCounts { } } - fn inc_element_state(&mut self, tag_type_id: TypeId, id: ElementStateId, frame_id: usize) { - match self.element_state_counts.entry((tag_type_id, id)) { + fn inc_element_state(&mut self, id: ElementStateId, frame_id: usize) { + match self.element_state_counts.entry(id) { Entry::Occupied(mut entry) => { let entry = entry.get_mut(); if entry.frame_id == frame_id || entry.ref_count >= 2 { @@ -3649,7 +3640,7 @@ impl RefCounts { ref_count: 1, frame_id, }); - self.dropped_element_states.remove(&(tag_type_id, id)); + self.dropped_element_states.remove(&id); } } } @@ -3672,13 +3663,12 @@ impl RefCounts { } } - fn dec_element_state(&mut self, tag_type_id: TypeId, id: ElementStateId) { - let key = (tag_type_id, id); - let entry = self.element_state_counts.get_mut(&key).unwrap(); + fn dec_element_state(&mut self, id: ElementStateId) { + let entry = self.element_state_counts.get_mut(&id).unwrap(); entry.ref_count -= 1; if entry.ref_count == 0 { - self.element_state_counts.remove(&key); - self.dropped_element_states.insert(key); + self.element_state_counts.remove(&id); + self.dropped_element_states.insert(id); } } @@ -3691,7 +3681,7 @@ impl RefCounts { ) -> ( HashSet, HashSet<(usize, usize)>, - HashSet<(TypeId, ElementStateId)>, + HashSet, ) { ( std::mem::take(&mut self.dropped_models), diff --git a/crates/gpui/src/elements/mouse_event_handler.rs b/crates/gpui/src/elements/mouse_event_handler.rs index 2cc01c3080..a9cfd3334e 100644 --- a/crates/gpui/src/elements/mouse_event_handler.rs +++ b/crates/gpui/src/elements/mouse_event_handler.rs @@ -5,11 +5,10 @@ use crate::{ vector::{vec2f, Vector2F}, }, platform::CursorStyle, - CursorStyleHandle, DebugContext, Element, ElementBox, ElementStateHandle, ElementStateId, - Event, EventContext, LayoutContext, MutableAppContext, PaintContext, SizeConstraint, + CursorStyleHandle, DebugContext, Element, ElementBox, ElementStateContext, ElementStateHandle, + Event, EventContext, LayoutContext, PaintContext, SizeConstraint, }; use serde_json::json; -use std::ops::DerefMut; pub struct MouseEventHandler { state: ElementStateHandle, @@ -30,14 +29,13 @@ pub struct MouseState { } impl MouseEventHandler { - pub fn new(id: Id, cx: &mut C, render_child: F) -> Self + pub fn new(id: usize, cx: &mut C, render_child: F) -> Self where Tag: 'static, + C: ElementStateContext, F: FnOnce(&MouseState, &mut C) -> ElementBox, - C: DerefMut, - Id: Into, { - let state_handle = cx.element_state::(id.into()); + let state_handle = cx.element_state::(id); let child = state_handle.update(cx, |state, cx| render_child(state, cx)); Self { state: state_handle, diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index f49081ae2d..ba59e14a30 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -7,8 +7,8 @@ use crate::{ platform::Event, text_layout::TextLayoutCache, Action, AnyAction, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AssetCache, ElementBox, - Entity, FontSystem, ModelHandle, ReadModel, ReadView, Scene, UpgradeModelHandle, - UpgradeViewHandle, View, ViewHandle, WeakModelHandle, WeakViewHandle, + ElementStateContext, Entity, FontSystem, ModelHandle, ReadModel, ReadView, Scene, + UpgradeModelHandle, UpgradeViewHandle, View, ViewHandle, WeakModelHandle, WeakViewHandle, }; use pathfinder_geometry::vector::{vec2f, Vector2F}; use serde_json::json; @@ -292,6 +292,12 @@ impl<'a> UpgradeViewHandle for LayoutContext<'a> { } } +impl<'a> ElementStateContext for LayoutContext<'a> { + fn current_view_id(&self) -> usize { + *self.view_stack.last().unwrap() + } +} + pub struct PaintContext<'a> { rendered_views: &'a mut HashMap, pub scene: &'a mut Scene, diff --git a/crates/gpui/src/views/select.rs b/crates/gpui/src/views/select.rs index d7deea6d55..944f45f0d4 100644 --- a/crates/gpui/src/views/select.rs +++ b/crates/gpui/src/views/select.rs @@ -104,7 +104,7 @@ impl View for Select { Default::default() }; let mut result = Flex::column().with_child( - MouseEventHandler::new::(self.handle.id(), cx, |mouse_state, cx| { + MouseEventHandler::new::(self.handle.id(), cx, |mouse_state, cx| { Container::new((self.render_item)( self.selected_item_ix, ItemType::Header, @@ -132,8 +132,8 @@ impl View for Select { let selected_item_ix = this.selected_item_ix; range.end = range.end.min(this.item_count); items.extend(range.map(|ix| { - MouseEventHandler::new::( - (handle.id(), ix), + MouseEventHandler::new::( + ix, cx, |mouse_state, cx| { (handle.read(cx).render_item)( diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 1b6f5c6313..76fb51108a 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -476,7 +476,7 @@ impl ProjectPanel { cx: &mut ViewContext, ) -> ElementBox { let is_dir = details.is_dir; - MouseEventHandler::new::((cx.view_id(), entry.entry_id), cx, |state, _| { + MouseEventHandler::new::(entry.entry_id, cx, |state, _| { let style = match (details.is_selected, state.hovered) { (false, false) => &theme.entry, (false, true) => &theme.hovered_entry, diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 8c7c001e33..cca3ee48b4 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -466,7 +466,7 @@ impl Pane { let theme = &settings.theme; enum Tabs {} - let tabs = MouseEventHandler::new::(cx.view_id(), cx, |mouse_state, cx| { + let tabs = MouseEventHandler::new::(0, cx, |mouse_state, cx| { let mut row = Flex::row(); for (ix, (_, item_view)) in self.item_views.iter().enumerate() { let is_active = ix == self.active_item_index; @@ -543,7 +543,7 @@ impl Pane { let item_id = item_view.id(); enum TabCloseButton {} let icon = Svg::new("icons/x.svg"); - MouseEventHandler::new::( + MouseEventHandler::new::( item_id, cx, |mouse_state, _| { diff --git a/crates/workspace/src/sidebar.rs b/crates/workspace/src/sidebar.rs index f3c8b21471..9de355c303 100644 --- a/crates/workspace/src/sidebar.rs +++ b/crates/workspace/src/sidebar.rs @@ -83,26 +83,22 @@ impl Sidebar { &theme.item }; enum SidebarButton {} - MouseEventHandler::new::( - item.view.id(), - cx, - |_, _| { - ConstrainedBox::new( - Align::new( - ConstrainedBox::new( - Svg::new(item.icon_path) - .with_color(theme.icon_color) - .boxed(), - ) - .with_height(theme.icon_size) - .boxed(), + MouseEventHandler::new::(item.view.id(), cx, |_, _| { + ConstrainedBox::new( + Align::new( + ConstrainedBox::new( + Svg::new(item.icon_path) + .with_color(theme.icon_color) + .boxed(), ) + .with_height(theme.icon_size) .boxed(), ) - .with_height(theme.height) - .boxed() - }, - ) + .boxed(), + ) + .with_height(theme.height) + .boxed() + }) .with_cursor_style(CursorStyle::PointingHand) .on_mouse_down(move |cx| { cx.dispatch_action(ToggleSidebarItem(SidebarItemId { @@ -161,7 +157,7 @@ impl Sidebar { ) -> ElementBox { let width = self.width.clone(); let side = self.side; - MouseEventHandler::new::((cx.view_id(), self.side.id()), cx, |_, _| { + MouseEventHandler::new::(side as usize, cx, |_, _| { Container::new(Empty::new().boxed()) .with_style(self.theme(settings).resize_handle) .boxed() @@ -184,12 +180,3 @@ impl Sidebar { .boxed() } } - -impl Side { - fn id(self) -> usize { - match self { - Side::Left => 0, - Side::Right => 1, - } - } -} diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index ebd7a9e26b..ba27d0daab 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1191,7 +1191,7 @@ impl Workspace { if let Some(avatar) = user.and_then(|user| user.avatar.clone()) { self.render_avatar(avatar, replica_id, theme) } else { - MouseEventHandler::new::(cx.view_id(), cx, |state, _| { + MouseEventHandler::new::(0, cx, |state, _| { let style = if state.hovered { &theme.workspace.titlebar.hovered_sign_in_prompt } else { @@ -1252,7 +1252,7 @@ impl Workspace { theme.workspace.titlebar.share_icon_color }; Some( - MouseEventHandler::new::(cx.view_id(), cx, |_, _| { + MouseEventHandler::new::(0, cx, |_, _| { Align::new( ConstrainedBox::new( Svg::new("icons/broadcast-24.svg").with_color(color).boxed(),