diff --git a/crates/chat_panel/src/chat_panel.rs b/crates/chat_panel/src/chat_panel.rs index 8bce551a8c3030f922fe0544653895a4a43b10ad..a8db280bf86a79b537fe3a9edb84373057eabab4 100644 --- a/crates/chat_panel/src/chat_panel.rs +++ b/crates/chat_panel/src/chat_panel.rs @@ -75,9 +75,9 @@ impl ChatPanel { }) }); - let mut message_list = ListState::new(0, Orientation::Bottom, 1000., { + let mut message_list = ListState::new(0, Orientation::Bottom, 1000., cx, { let this = cx.weak_handle(); - move |ix, cx| { + move |_, ix, cx| { let this = this.upgrade(cx).unwrap().read(cx); let message = this.active_channel.as_ref().unwrap().0.read(cx).message(ix); this.render_message(message, cx) diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 9f0f396d8530b4d074a4763772c0584a9d5f263b..0708826ea704a3ca3a30725185b0beef87ca14e5 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -1,9 +1,9 @@ use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ actions, - elements::{ChildView, Flex, Label, MouseState, ParentElement}, + elements::{ChildView, Flex, Label, ParentElement}, keymap::Keystroke, - Action, Element, Entity, MutableAppContext, View, ViewContext, ViewHandle, + Action, Element, Entity, MouseState, MutableAppContext, View, ViewContext, ViewHandle, }; use picker::{Picker, PickerDelegate}; use settings::Settings; @@ -203,7 +203,7 @@ impl PickerDelegate for CommandPalette { fn render_match( &self, ix: usize, - mouse_state: &MouseState, + mouse_state: MouseState, selected: bool, cx: &gpui::AppContext, ) -> gpui::ElementBox { diff --git a/crates/contacts_panel/src/contact_finder.rs b/crates/contacts_panel/src/contact_finder.rs index 18e17a93d9ba448f533d83a0a09701b5d3ae84cc..244cfcad4a5af9bc11964da485e76041d56f9c69 100644 --- a/crates/contacts_panel/src/contact_finder.rs +++ b/crates/contacts_panel/src/contact_finder.rs @@ -1,7 +1,7 @@ use client::{ContactRequestStatus, User, UserStore}; use gpui::{ - actions, elements::*, Entity, ModelHandle, MutableAppContext, RenderContext, Task, View, - ViewContext, ViewHandle, + actions, elements::*, Entity, ModelHandle, MouseState, MutableAppContext, RenderContext, Task, + View, ViewContext, ViewHandle, }; use picker::{Picker, PickerDelegate}; use settings::Settings; @@ -105,7 +105,7 @@ impl PickerDelegate for ContactFinder { fn render_match( &self, ix: usize, - mouse_state: &MouseState, + mouse_state: MouseState, selected: bool, cx: &gpui::AppContext, ) -> ElementBox { diff --git a/crates/contacts_panel/src/contacts_panel.rs b/crates/contacts_panel/src/contacts_panel.rs index 4b965d3c1d7dc53ae612de7c51e1aa553aa49b4d..13485e96f22fc8847507ee8f98756b224b75514b 100644 --- a/crates/contacts_panel/src/contacts_panel.rs +++ b/crates/contacts_panel/src/contacts_panel.rs @@ -12,8 +12,8 @@ use gpui::{ geometry::{rect::RectF, vector::vec2f}, impl_actions, impl_internal_actions, platform::CursorStyle, - AppContext, ClipboardItem, Element, ElementBox, Entity, LayoutContext, ModelHandle, - MutableAppContext, RenderContext, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, + AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle, MutableAppContext, + RenderContext, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, }; use join_project_notification::JoinProjectNotification; use menu::{Confirm, SelectNext, SelectPrev}; @@ -181,11 +181,8 @@ impl ContactsPanel { .detach(); let mut this = Self { - list_state: ListState::new(0, Orientation::Top, 1000., { - let this = cx.weak_handle(); - move |ix, cx| { - let this = this.upgrade(cx).unwrap(); - let this = this.read(cx); + list_state: ListState::new(0, Orientation::Top, 1000., cx, { + move |this, ix, cx| { let theme = cx.global::().theme.clone(); let theme = &theme.contacts_panel; let current_user_id = @@ -255,11 +252,11 @@ impl ContactsPanel { theme: &theme::ContactsPanel, is_selected: bool, is_collapsed: bool, - cx: &mut LayoutContext, + cx: &mut RenderContext, ) -> ElementBox { enum Header {} - let header_style = theme.header_row.style_for(&Default::default(), is_selected); + let header_style = theme.header_row.style_for(Default::default(), is_selected); let text = match section { Section::Requests => "Requests", Section::Online => "Online", @@ -331,11 +328,7 @@ impl ContactsPanel { .constrained() .with_height(theme.row_height) .contained() - .with_style( - *theme - .contact_row - .style_for(&Default::default(), is_selected), - ) + .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) .boxed() } @@ -346,7 +339,7 @@ impl ContactsPanel { theme: &theme::ContactsPanel, is_last_project: bool, is_selected: bool, - cx: &mut LayoutContext, + cx: &mut RenderContext, ) -> ElementBox { let project = &contact.projects[project_index]; let project_id = project.id; @@ -459,7 +452,7 @@ impl ContactsPanel { theme: &theme::ContactsPanel, is_incoming: bool, is_selected: bool, - cx: &mut LayoutContext, + cx: &mut RenderContext, ) -> ElementBox { enum Decline {} enum Accept {} @@ -558,11 +551,7 @@ impl ContactsPanel { row.constrained() .with_height(theme.row_height) .contained() - .with_style( - *theme - .contact_row - .style_for(&Default::default(), is_selected), - ) + .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) .boxed() } diff --git a/crates/context_menu/src/context_menu.rs b/crates/context_menu/src/context_menu.rs index 7e42c2596e951a1c81546f6367b7dd635c6ab7e9..507a88c5f2df0404a72b2a4ac7ebe9badd532285 100644 --- a/crates/context_menu/src/context_menu.rs +++ b/crates/context_menu/src/context_menu.rs @@ -185,10 +185,9 @@ impl ContextMenu { .with_children(self.items.iter().enumerate().map(|(ix, item)| { match item { ContextMenuItem::Item { label, .. } => { - let style = style.item.style_for( - &Default::default(), - Some(ix) == self.selected_index, - ); + let style = style + .item + .style_for(Default::default(), Some(ix) == self.selected_index); Label::new(label.to_string(), style.label.clone()) .contained() .with_style(style.container) @@ -210,10 +209,9 @@ impl ContextMenu { .with_children(self.items.iter().enumerate().map(|(ix, item)| { match item { ContextMenuItem::Item { action, .. } => { - let style = style.item.style_for( - &Default::default(), - Some(ix) == self.selected_index, - ); + let style = style + .item + .style_for(Default::default(), Some(ix) == self.selected_index); KeystrokeLabel::new( action.boxed_clone(), style.keystroke.container, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 556b7a9091228c17650c0b26fd4b55155d74f6db..56e88add5401197d5c469099b3cfdfc8cd0e0773 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -592,11 +592,11 @@ impl ContextMenu { &self, cursor_position: DisplayPoint, style: EditorStyle, - cx: &AppContext, + cx: &mut RenderContext, ) -> (DisplayPoint, ElementBox) { match self { ContextMenu::Completions(menu) => (cursor_position, menu.render(style, cx)), - ContextMenu::CodeActions(menu) => menu.render(cursor_position, style), + ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx), } } } @@ -633,54 +633,62 @@ impl CompletionsMenu { !self.matches.is_empty() } - fn render(&self, style: EditorStyle, _: &AppContext) -> ElementBox { + fn render(&self, style: EditorStyle, cx: &mut RenderContext) -> ElementBox { enum CompletionTag {} let completions = self.completions.clone(); let matches = self.matches.clone(); let selected_item = self.selected_item; let container_style = style.autocomplete.container; - UniformList::new(self.list.clone(), matches.len(), move |range, items, cx| { - let start_ix = range.start; - for (ix, mat) in matches[range].iter().enumerate() { - let completion = &completions[mat.candidate_id]; - let item_ix = start_ix + ix; - items.push( - MouseEventHandler::new::( - mat.candidate_id, - 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(completion.label.text.clone(), style.text.clone()) - .with_soft_wrap(false) - .with_highlights(combine_syntax_and_fuzzy_match_highlights( - &completion.label.text, - style.text.color.into(), - styled_runs_for_code_label(&completion.label, &style.syntax), - &mat.positions, - )) - .contained() - .with_style(item_style) - .boxed() - }, - ) - .with_cursor_style(CursorStyle::PointingHand) - .on_mouse_down(move |_, cx| { - cx.dispatch_action(ConfirmCompletion { - item_ix: Some(item_ix), - }); - }) - .boxed(), - ); - } - }) + UniformList::new( + self.list.clone(), + matches.len(), + cx, + move |_, range, items, cx| { + let start_ix = range.start; + for (ix, mat) in matches[range].iter().enumerate() { + let completion = &completions[mat.candidate_id]; + let item_ix = start_ix + ix; + items.push( + MouseEventHandler::new::( + mat.candidate_id, + 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(completion.label.text.clone(), style.text.clone()) + .with_soft_wrap(false) + .with_highlights(combine_syntax_and_fuzzy_match_highlights( + &completion.label.text, + style.text.color.into(), + styled_runs_for_code_label( + &completion.label, + &style.syntax, + ), + &mat.positions, + )) + .contained() + .with_style(item_style) + .boxed() + }, + ) + .with_cursor_style(CursorStyle::PointingHand) + .on_mouse_down(move |_, cx| { + cx.dispatch_action(ConfirmCompletion { + item_ix: Some(item_ix), + }); + }) + .boxed(), + ); + } + }, + ) .with_width_from_item( self.matches .iter() @@ -772,14 +780,18 @@ impl CodeActionsMenu { &self, mut cursor_position: DisplayPoint, style: EditorStyle, + cx: &mut RenderContext, ) -> (DisplayPoint, ElementBox) { 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(), move |range, items, cx| { + 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; @@ -808,17 +820,18 @@ impl CodeActionsMenu { .boxed(), ); } - }) - .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) - .boxed(); + }, + ) + .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) + .boxed(); if self.deployed_from_indicator { *cursor_position.column_mut() = 0; @@ -2578,7 +2591,7 @@ impl Editor { pub fn render_code_actions_indicator( &self, style: &EditorStyle, - cx: &mut ViewContext, + cx: &mut RenderContext, ) -> Option { if self.available_code_actions.is_some() { enum Tag {} @@ -2612,7 +2625,7 @@ impl Editor { &self, cursor_position: DisplayPoint, style: EditorStyle, - cx: &AppContext, + cx: &mut RenderContext, ) -> Option<(DisplayPoint, ElementBox)> { self.context_menu .as_ref() diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index f71bc09d23c31af5fca5088ac07aabcd760dec52..28636e6b6f3b0f7749dc86ea9ede7f297eb6dcdc 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -18,8 +18,9 @@ use gpui::{ json::{self, ToJson}, platform::CursorStyle, text_layout::{self, Line, RunStyle, TextLayoutCache}, - AppContext, Axis, Border, Element, ElementBox, Event, EventContext, LayoutContext, - MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle, + AppContext, Axis, Border, CursorRegion, Element, ElementBox, Event, EventContext, + LayoutContext, MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, + WeakViewHandle, }; use json::json; use language::{Bias, DiagnosticSeverity}; @@ -330,7 +331,10 @@ impl EditorElement { let content_origin = bounds.origin() + vec2f(layout.gutter_margin, 0.); cx.scene.push_layer(Some(bounds)); - cx.scene.push_cursor_style(bounds, CursorStyle::IBeam); + cx.scene.push_cursor_region(CursorRegion { + bounds, + style: CursorStyle::IBeam, + }); for (range, color) in &layout.highlighted_ranges { self.paint_highlighted_range( @@ -1020,8 +1024,6 @@ impl Element for EditorElement { max_row.saturating_sub(1) as f32, ); - let mut context_menu = None; - let mut code_actions_indicator = None; self.update_view(cx.app, |view, cx| { let clamped = view.clamp_scroll_left(scroll_max.x()); let autoscrolled; @@ -1041,7 +1043,11 @@ impl Element for EditorElement { if clamped || autoscrolled { snapshot = view.snapshot(cx); } + }); + let mut context_menu = None; + let mut code_actions_indicator = None; + cx.render(&self.view.upgrade(cx).unwrap(), |view, cx| { let newest_selection_head = view .selections .newest::(cx) diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index 84b69735336b97511d7c295e063d3f63e470be75..cdefa6b593a61c84e83d104a182d8525e61a43d2 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -1,7 +1,7 @@ use fuzzy::PathMatch; use gpui::{ - actions, elements::*, AppContext, Entity, ModelHandle, MutableAppContext, RenderContext, Task, - View, ViewContext, ViewHandle, + actions, elements::*, AppContext, Entity, ModelHandle, MouseState, MutableAppContext, + RenderContext, Task, View, ViewContext, ViewHandle, }; use picker::{Picker, PickerDelegate}; use project::{Project, ProjectPath, WorktreeId}; @@ -226,7 +226,7 @@ impl PickerDelegate for FileFinder { fn render_match( &self, ix: usize, - mouse_state: &MouseState, + mouse_state: MouseState, selected: bool, cx: &AppContext, ) -> ElementBox { diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 6c667710828dc8cc40d74a38188a4259c9348857..91c357ddee8eb6fd09238ebdb940e365c77afa82 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -7,7 +7,8 @@ use crate::{ platform::{self, Platform, PromptLevel, WindowOptions}, presenter::Presenter, util::post_inc, - AssetCache, AssetSource, ClipboardItem, FontCache, PathPromptOptions, TextLayoutCache, + AssetCache, AssetSource, ClipboardItem, FontCache, MouseRegionId, PathPromptOptions, + TextLayoutCache, }; pub use action::*; use anyhow::{anyhow, Context, Result}; @@ -126,26 +127,6 @@ 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 struct Menu<'a> { pub name: &'a str, pub items: Vec>, @@ -467,6 +448,27 @@ impl TestAppContext { result } + pub fn render(&mut self, handle: &ViewHandle, f: F) -> T + where + F: FnOnce(&mut V, &mut RenderContext) -> T, + V: View, + { + handle.update(&mut *self.cx.borrow_mut(), |view, cx| { + let mut render_cx = RenderContext { + app: cx, + window_id: handle.window_id(), + view_id: handle.id(), + view_type: PhantomData, + titlebar_height: 0., + hovered_region_ids: Default::default(), + clicked_region_id: None, + right_clicked_region_id: None, + refreshing: false, + }; + f(view, &mut render_cx) + }) + } + pub fn to_async(&self) -> AsyncAppContext { AsyncAppContext(self.cx.clone()) } @@ -1040,19 +1042,15 @@ impl MutableAppContext { .map_or(false, |window| window.is_active) } - pub fn render_view( - &mut self, - window_id: usize, - view_id: usize, - titlebar_height: f32, - refreshing: bool, - ) -> Result { + pub fn render_view(&mut self, params: RenderParams) -> Result { + let window_id = params.window_id; + let view_id = params.view_id; let mut view = self .cx .views - .remove(&(window_id, view_id)) + .remove(&(params.window_id, params.view_id)) .ok_or(anyhow!("view not found"))?; - let element = view.render(window_id, view_id, titlebar_height, refreshing, self); + let element = view.render(params, self); self.cx.views.insert((window_id, view_id), view); Ok(element) } @@ -1079,8 +1077,16 @@ impl MutableAppContext { .map(|view_id| { ( view_id, - self.render_view(window_id, view_id, titlebar_height, false) - .unwrap(), + self.render_view(RenderParams { + window_id, + view_id, + titlebar_height, + hovered_region_ids: Default::default(), + clicked_region_id: None, + right_clicked_region_id: None, + refreshing: false, + }) + .unwrap(), ) }) .collect() @@ -1366,7 +1372,10 @@ impl MutableAppContext { .unwrap() .0 .clone(); - let dispatch_path = presenter.borrow().dispatch_path_from(view_id); + let mut dispatch_path = Vec::new(); + presenter + .borrow() + .compute_dispatch_path_from(view_id, &mut dispatch_path); for view_id in dispatch_path { if let Some(view) = self.views.get(&(window_id, view_id)) { let view_type = view.as_any().type_id(); @@ -1443,7 +1452,10 @@ impl MutableAppContext { .unwrap() .0 .clone(); - let dispatch_path = presenter.borrow().dispatch_path_from(view_id); + let mut dispatch_path = Vec::new(); + presenter + .borrow() + .compute_dispatch_path_from(view_id, &mut dispatch_path); self.dispatch_action_any(window_id, &dispatch_path, action); } @@ -1775,23 +1787,6 @@ impl MutableAppContext { ) } - pub fn build_render_context( - &mut self, - window_id: usize, - view_id: usize, - titlebar_height: f32, - refreshing: bool, - ) -> RenderContext { - RenderContext { - app: self, - titlebar_height, - refreshing, - window_id, - view_id, - view_type: PhantomData, - } - } - pub fn add_view(&mut self, window_id: usize, build_view: F) -> ViewHandle where T: View, @@ -2917,14 +2912,7 @@ pub trait AnyView { cx: &mut MutableAppContext, ) -> Option>>>; fn ui_name(&self) -> &'static str; - fn render<'a>( - &mut self, - window_id: usize, - view_id: usize, - titlebar_height: f32, - refreshing: bool, - cx: &mut MutableAppContext, - ) -> ElementBox; + fn render<'a>(&mut self, params: RenderParams, cx: &mut MutableAppContext) -> ElementBox; fn on_focus(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize); fn on_blur(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize); fn keymap_context(&self, cx: &AppContext) -> keymap::Context; @@ -2958,25 +2946,8 @@ where T::ui_name() } - fn render<'a>( - &mut self, - window_id: usize, - view_id: usize, - titlebar_height: f32, - refreshing: bool, - cx: &mut MutableAppContext, - ) -> ElementBox { - View::render( - self, - &mut RenderContext { - window_id, - view_id, - app: cx, - view_type: PhantomData::, - titlebar_height, - refreshing, - }, - ) + fn render<'a>(&mut self, params: RenderParams, cx: &mut MutableAppContext) -> ElementBox { + View::render(self, &mut RenderContext::new(params, cx)) } fn on_focus(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize) { @@ -3458,23 +3429,86 @@ impl<'a, T: View> ViewContext<'a, T> { } } +pub struct RenderParams { + pub window_id: usize, + pub view_id: usize, + pub titlebar_height: f32, + pub hovered_region_ids: HashSet, + pub clicked_region_id: Option, + pub right_clicked_region_id: Option, + pub refreshing: bool, +} + pub struct RenderContext<'a, T: View> { + pub(crate) window_id: usize, + pub(crate) view_id: usize, + pub(crate) view_type: PhantomData, + pub(crate) hovered_region_ids: HashSet, + pub(crate) clicked_region_id: Option, + pub(crate) right_clicked_region_id: Option, pub app: &'a mut MutableAppContext, pub titlebar_height: f32, pub refreshing: bool, - window_id: usize, - view_id: usize, - view_type: PhantomData, } -impl<'a, T: View> RenderContext<'a, T> { - pub fn handle(&self) -> WeakViewHandle { +#[derive(Clone, Copy, Default)] +pub struct MouseState { + pub hovered: bool, + pub clicked: bool, + pub right_clicked: bool, +} + +impl<'a, V: View> RenderContext<'a, V> { + fn new(params: RenderParams, app: &'a mut MutableAppContext) -> Self { + Self { + app, + window_id: params.window_id, + view_id: params.view_id, + view_type: PhantomData, + titlebar_height: params.titlebar_height, + hovered_region_ids: params.hovered_region_ids.clone(), + clicked_region_id: params.clicked_region_id, + right_clicked_region_id: params.right_clicked_region_id, + refreshing: params.refreshing, + } + } + + pub fn handle(&self) -> WeakViewHandle { WeakViewHandle::new(self.window_id, self.view_id) } pub fn view_id(&self) -> usize { self.view_id } + + pub fn mouse_state(&self, region_id: usize) -> MouseState { + let region_id = MouseRegionId { + view_id: self.view_id, + tag: TypeId::of::(), + region_id, + }; + MouseState { + hovered: self.hovered_region_ids.contains(®ion_id), + clicked: self.clicked_region_id == Some(region_id), + right_clicked: self.right_clicked_region_id == Some(region_id), + } + } + + pub fn element_state( + &mut self, + element_id: usize, + ) -> ElementStateHandle { + let id = ElementStateId { + view_id: self.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) + } } impl AsRef for &AppContext { @@ -3519,12 +3553,6 @@ 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 @@ -3584,6 +3612,16 @@ impl UpgradeViewHandle for ViewContext<'_, V> { } } +impl UpgradeViewHandle for RenderContext<'_, V> { + fn upgrade_view_handle(&self, handle: &WeakViewHandle) -> Option> { + self.cx.upgrade_view_handle(handle) + } + + fn upgrade_any_view_handle(&self, handle: &AnyWeakViewHandle) -> Option { + self.cx.upgrade_any_view_handle(handle) + } +} + impl UpdateModel for ViewContext<'_, V> { fn update_model( &mut self, @@ -3613,12 +3651,6 @@ 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; diff --git a/crates/gpui/src/elements/container.rs b/crates/gpui/src/elements/container.rs index 62f19636b7c5dd95a02d7a6f4d200b4343ddec9c..004052b9baec4e310dcfd879f6950d264a2749e2 100644 --- a/crates/gpui/src/elements/container.rs +++ b/crates/gpui/src/elements/container.rs @@ -7,7 +7,7 @@ use crate::{ }, json::ToJson, platform::CursorStyle, - scene::{self, Border, Quad}, + scene::{self, Border, CursorRegion, Quad}, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext, SizeConstraint, }; use serde::Deserialize; @@ -213,7 +213,10 @@ impl Element for Container { } if let Some(style) = self.style.cursor { - cx.scene.push_cursor_style(quad_bounds, style); + cx.scene.push_cursor_region(CursorRegion { + bounds: quad_bounds, + style, + }); } let child_origin = diff --git a/crates/gpui/src/elements/flex.rs b/crates/gpui/src/elements/flex.rs index 3f42f984075a16589de9256aa5f414a97bf7bf05..8d1fb37a08b0afafdb3ea2671bb14d9bdb7e4317 100644 --- a/crates/gpui/src/elements/flex.rs +++ b/crates/gpui/src/elements/flex.rs @@ -2,8 +2,8 @@ use std::{any::Any, f32::INFINITY}; use crate::{ json::{self, ToJson, Value}, - Axis, DebugContext, Element, ElementBox, ElementStateContext, ElementStateHandle, Event, - EventContext, LayoutContext, PaintContext, SizeConstraint, Vector2FExt, + Axis, DebugContext, Element, ElementBox, ElementStateHandle, Event, EventContext, + LayoutContext, PaintContext, RenderContext, SizeConstraint, Vector2FExt, View, }; use pathfinder_geometry::{ rect::RectF, @@ -40,15 +40,15 @@ impl Flex { Self::new(Axis::Vertical) } - pub fn scrollable( + pub fn scrollable( mut self, element_id: usize, scroll_to: Option, - cx: &mut C, + cx: &mut RenderContext, ) -> Self where Tag: 'static, - C: ElementStateContext, + V: View, { let scroll_state = cx.element_state::(element_id); scroll_state.update(cx, |scroll_state, _| scroll_state.scroll_to = scroll_to); diff --git a/crates/gpui/src/elements/list.rs b/crates/gpui/src/elements/list.rs index 969fe3cb842ee04e3f5cf127136831c341933dcb..10f7ef7b79ef037248e77189ebcfe34aa53d4280 100644 --- a/crates/gpui/src/elements/list.rs +++ b/crates/gpui/src/elements/list.rs @@ -5,7 +5,7 @@ use crate::{ }, json::json, DebugContext, Element, ElementBox, ElementRc, Event, EventContext, LayoutContext, PaintContext, - SizeConstraint, + RenderContext, SizeConstraint, View, ViewContext, }; use std::{cell::RefCell, collections::VecDeque, ops::Range, rc::Rc}; use sum_tree::{Bias, SumTree}; @@ -26,7 +26,7 @@ pub enum Orientation { struct StateInner { last_layout_width: Option, - render_item: Box ElementBox>, + render_item: Box Option>, rendered_range: Range, items: SumTree, logical_scroll_top: Option, @@ -135,9 +135,12 @@ impl Element for List { break; } - let element = state.render_item(scroll_top.item_ix + ix, item, item_constraint, cx); - rendered_height += element.size().y(); - rendered_items.push_back(ListItem::Rendered(element)); + if let Some(element) = + state.render_item(scroll_top.item_ix + ix, item, item_constraint, cx) + { + rendered_height += element.size().y(); + rendered_items.push_back(ListItem::Rendered(element)); + } } // Prepare to start walking upward from the item at the scroll top. @@ -149,9 +152,12 @@ impl Element for List { while rendered_height < size.y() { cursor.prev(&()); if let Some(item) = cursor.item() { - let element = state.render_item(cursor.start().0, item, item_constraint, cx); - rendered_height += element.size().y(); - rendered_items.push_front(ListItem::Rendered(element)); + if let Some(element) = + state.render_item(cursor.start().0, item, item_constraint, cx) + { + rendered_height += element.size().y(); + rendered_items.push_front(ListItem::Rendered(element)); + } } else { break; } @@ -182,9 +188,12 @@ impl Element for List { while leading_overdraw < state.overdraw { cursor.prev(&()); if let Some(item) = cursor.item() { - let element = state.render_item(cursor.start().0, item, item_constraint, cx); - leading_overdraw += element.size().y(); - rendered_items.push_front(ListItem::Rendered(element)); + if let Some(element) = + state.render_item(cursor.start().0, item, item_constraint, cx) + { + leading_overdraw += element.size().y(); + rendered_items.push_front(ListItem::Rendered(element)); + } } else { break; } @@ -330,20 +339,25 @@ impl Element for List { } impl ListState { - pub fn new( + pub fn new( element_count: usize, orientation: Orientation, overdraw: f32, - render_item: F, + cx: &mut ViewContext, + mut render_item: F, ) -> Self where - F: 'static + FnMut(usize, &mut LayoutContext) -> ElementBox, + V: View, + F: 'static + FnMut(&mut V, usize, &mut RenderContext) -> ElementBox, { let mut items = SumTree::new(); items.extend((0..element_count).map(|_| ListItem::Unrendered), &()); + let handle = cx.handle(); Self(Rc::new(RefCell::new(StateInner { last_layout_width: None, - render_item: Box::new(render_item), + render_item: Box::new(move |ix, cx| { + Some(cx.render(&handle, |view, cx| render_item(view, ix, cx))) + }), rendered_range: 0..0, items, logical_scroll_top: None, @@ -414,13 +428,13 @@ impl StateInner { existing_item: &ListItem, constraint: SizeConstraint, cx: &mut LayoutContext, - ) -> ElementRc { + ) -> Option { if let ListItem::Rendered(element) = existing_item { - element.clone() + Some(element.clone()) } else { - let mut element = (self.render_item)(ix, cx); + let mut element = (self.render_item)(ix, cx)?; element.layout(constraint, cx); - element.into() + Some(element.into()) } } @@ -593,22 +607,26 @@ impl<'a> sum_tree::SeekTarget<'a, ListItemSummary, ListItemSummary> for Height { #[cfg(test)] mod tests { use super::*; - use crate::geometry::vector::vec2f; + use crate::{elements::Empty, geometry::vector::vec2f, Entity}; use rand::prelude::*; use std::env; #[crate::test(self)] fn test_layout(cx: &mut crate::MutableAppContext) { let mut presenter = cx.build_presenter(0, 0.); + let (_, view) = cx.add_window(Default::default(), |_| TestView); let constraint = SizeConstraint::new(vec2f(0., 0.), vec2f(100., 40.)); let elements = Rc::new(RefCell::new(vec![(0, 20.), (1, 30.), (2, 100.)])); - let state = ListState::new(elements.borrow().len(), Orientation::Top, 1000.0, { - let elements = elements.clone(); - move |ix, _| { - let (id, height) = elements.borrow()[ix]; - TestElement::new(id, height).boxed() - } + + let state = view.update(cx, |_, cx| { + ListState::new(elements.borrow().len(), Orientation::Top, 1000.0, cx, { + let elements = elements.clone(); + move |_, ix, _| { + let (id, height) = elements.borrow()[ix]; + TestElement::new(id, height).boxed() + } + }) }); let mut list = List::new(state.clone()); @@ -694,6 +712,7 @@ mod tests { .map(|i| i.parse().expect("invalid `OPERATIONS` variable")) .unwrap_or(10); + let (_, view) = cx.add_window(Default::default(), |_| TestView); let mut presenter = cx.build_presenter(0, 0.); let mut next_id = 0; let elements = Rc::new(RefCell::new( @@ -709,12 +728,15 @@ mod tests { .choose(&mut rng) .unwrap(); let overdraw = rng.gen_range(1..=100) as f32; - let state = ListState::new(elements.borrow().len(), orientation, overdraw, { - let elements = elements.clone(); - move |ix, _| { - let (id, height) = elements.borrow()[ix]; - TestElement::new(id, height).boxed() - } + + let state = view.update(cx, |_, cx| { + ListState::new(elements.borrow().len(), orientation, overdraw, cx, { + let elements = elements.clone(); + move |_, ix, _| { + let (id, height) = elements.borrow()[ix]; + TestElement::new(id, height).boxed() + } + }) }); let mut width = rng.gen_range(0..=2000) as f32 / 2.; @@ -851,6 +873,22 @@ mod tests { } } + struct TestView; + + impl Entity for TestView { + type Event = (); + } + + impl View for TestView { + fn ui_name() -> &'static str { + "TestView" + } + + fn render(&mut self, _: &mut RenderContext<'_, Self>) -> ElementBox { + Empty::new().boxed() + } + } + struct TestElement { id: usize, size: Vector2F, diff --git a/crates/gpui/src/elements/mouse_event_handler.rs b/crates/gpui/src/elements/mouse_event_handler.rs index 7e0281bffe553c60bc9cf02f0ca39b7c617c8953..1ca4c13fc856e21f886883aa1101dca27ae879d6 100644 --- a/crates/gpui/src/elements/mouse_event_handler.rs +++ b/crates/gpui/src/elements/mouse_event_handler.rs @@ -1,3 +1,5 @@ +use std::{any::TypeId, rc::Rc}; + use super::Padding; use crate::{ geometry::{ @@ -5,49 +7,42 @@ use crate::{ vector::{vec2f, Vector2F}, }, platform::CursorStyle, - DebugContext, Element, ElementBox, ElementStateContext, ElementStateHandle, Event, - EventContext, LayoutContext, PaintContext, SizeConstraint, + scene::CursorRegion, + DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, MouseRegion, MouseState, + PaintContext, RenderContext, SizeConstraint, View, }; use serde_json::json; pub struct MouseEventHandler { - state: ElementStateHandle, child: ElementBox, + tag: TypeId, + id: usize, cursor_style: Option, - mouse_down_handler: Option>, - click_handler: Option>, - drag_handler: Option>, - right_mouse_down_handler: Option>, - right_click_handler: Option>, + mouse_down_handler: Option>, + click_handler: Option>, + right_mouse_down_handler: Option>, + right_click_handler: Option>, + drag_handler: Option>, padding: Padding, } -#[derive(Default, Debug)] -pub struct MouseState { - pub hovered: bool, - pub clicked: bool, - pub right_clicked: bool, - prev_drag_position: Option, -} - impl MouseEventHandler { - pub fn new(id: usize, cx: &mut C, render_child: F) -> Self + pub fn new(id: usize, cx: &mut RenderContext, render_child: F) -> Self where Tag: 'static, - C: ElementStateContext, - F: FnOnce(&MouseState, &mut C) -> ElementBox, + V: View, + F: FnOnce(MouseState, &mut RenderContext) -> ElementBox, { - let state_handle = cx.element_state::(id); - let child = state_handle.update(cx, |state, cx| render_child(state, cx)); Self { - state: state_handle, - child, + id, + tag: TypeId::of::(), + child: render_child(cx.mouse_state::(id), cx), cursor_style: None, mouse_down_handler: None, click_handler: None, - drag_handler: None, right_mouse_down_handler: None, right_click_handler: None, + drag_handler: None, padding: Default::default(), } } @@ -59,38 +54,38 @@ impl MouseEventHandler { pub fn on_mouse_down( mut self, - handler: impl FnMut(Vector2F, &mut EventContext) + 'static, + handler: impl Fn(Vector2F, &mut EventContext) + 'static, ) -> Self { - self.mouse_down_handler = Some(Box::new(handler)); + self.mouse_down_handler = Some(Rc::new(handler)); self } pub fn on_click( mut self, - handler: impl FnMut(Vector2F, usize, &mut EventContext) + 'static, + handler: impl Fn(Vector2F, usize, &mut EventContext) + 'static, ) -> Self { - self.click_handler = Some(Box::new(handler)); - self - } - - pub fn on_drag(mut self, handler: impl FnMut(Vector2F, &mut EventContext) + 'static) -> Self { - self.drag_handler = Some(Box::new(handler)); + self.click_handler = Some(Rc::new(handler)); self } pub fn on_right_mouse_down( mut self, - handler: impl FnMut(Vector2F, &mut EventContext) + 'static, + handler: impl Fn(Vector2F, &mut EventContext) + 'static, ) -> Self { - self.right_mouse_down_handler = Some(Box::new(handler)); + self.right_mouse_down_handler = Some(Rc::new(handler)); self } pub fn on_right_click( mut self, - handler: impl FnMut(Vector2F, usize, &mut EventContext) + 'static, + handler: impl Fn(Vector2F, usize, &mut EventContext) + 'static, ) -> Self { - self.right_click_handler = Some(Box::new(handler)); + self.right_click_handler = Some(Rc::new(handler)); + self + } + + pub fn on_drag(mut self, handler: impl Fn(Vector2F, &mut EventContext) + 'static) -> Self { + self.drag_handler = Some(Rc::new(handler)); self } @@ -127,10 +122,26 @@ impl Element for MouseEventHandler { _: &mut Self::LayoutState, cx: &mut PaintContext, ) -> Self::PaintState { - if let Some(cursor_style) = self.cursor_style { - cx.scene - .push_cursor_style(self.hit_bounds(bounds), cursor_style); + if let Some(style) = self.cursor_style { + cx.scene.push_cursor_region(CursorRegion { + bounds: self.hit_bounds(bounds), + style, + }); } + + cx.scene.push_mouse_region(MouseRegion { + view_id: cx.current_view_id(), + tag: self.tag, + region_id: self.id, + bounds: self.hit_bounds(bounds), + hover: None, + click: self.click_handler.clone(), + mouse_down: self.mouse_down_handler.clone(), + right_click: self.right_click_handler.clone(), + right_mouse_down: self.right_mouse_down_handler.clone(), + drag: self.drag_handler.clone(), + }); + self.child.paint(bounds.origin(), visible_bounds, cx); } @@ -138,113 +149,12 @@ impl Element for MouseEventHandler { &mut self, event: &Event, _: RectF, - visible_bounds: RectF, + _: RectF, _: &mut Self::LayoutState, _: &mut Self::PaintState, cx: &mut EventContext, ) -> bool { - let hit_bounds = self.hit_bounds(visible_bounds); - let mouse_down_handler = self.mouse_down_handler.as_mut(); - let click_handler = self.click_handler.as_mut(); - let drag_handler = self.drag_handler.as_mut(); - let right_mouse_down_handler = self.right_mouse_down_handler.as_mut(); - let right_click_handler = self.right_click_handler.as_mut(); - - let handled_in_child = self.child.dispatch_event(event, cx); - - self.state.update(cx, |state, cx| match event { - Event::MouseMoved { - position, - left_mouse_down, - } => { - if !left_mouse_down { - let mouse_in = hit_bounds.contains_point(*position); - if state.hovered != mouse_in { - state.hovered = mouse_in; - cx.notify(); - return true; - } - } - handled_in_child - } - Event::LeftMouseDown { position, .. } => { - if !handled_in_child && hit_bounds.contains_point(*position) { - state.clicked = true; - state.prev_drag_position = Some(*position); - cx.notify(); - if let Some(handler) = mouse_down_handler { - handler(*position, cx); - } - true - } else { - handled_in_child - } - } - Event::LeftMouseUp { - position, - click_count, - .. - } => { - state.prev_drag_position = None; - if !handled_in_child && state.clicked { - state.clicked = false; - cx.notify(); - if let Some(handler) = click_handler { - if hit_bounds.contains_point(*position) { - handler(*position, *click_count, cx); - } - } - true - } else { - handled_in_child - } - } - Event::LeftMouseDragged { position, .. } => { - if !handled_in_child && state.clicked { - let prev_drag_position = state.prev_drag_position.replace(*position); - if let Some((handler, prev_position)) = drag_handler.zip(prev_drag_position) { - let delta = *position - prev_position; - if !delta.is_zero() { - (handler)(delta, cx); - } - } - true - } else { - handled_in_child - } - } - Event::RightMouseDown { position, .. } => { - if !handled_in_child && hit_bounds.contains_point(*position) { - state.right_clicked = true; - cx.notify(); - if let Some(handler) = right_mouse_down_handler { - handler(*position, cx); - } - true - } else { - handled_in_child - } - } - Event::RightMouseUp { - position, - click_count, - .. - } => { - if !handled_in_child && state.right_clicked { - state.right_clicked = false; - cx.notify(); - if let Some(handler) = right_click_handler { - if hit_bounds.contains_point(*position) { - handler(*position, *click_count, cx); - } - } - true - } else { - handled_in_child - } - } - _ => handled_in_child, - }) + self.child.dispatch_event(event, cx) } fn debug( diff --git a/crates/gpui/src/elements/uniform_list.rs b/crates/gpui/src/elements/uniform_list.rs index 3f384b5ea526f6ead4fc9eaaca8b5f5867d65911..de217a017c7699bddad0d991fadca9cf72ba02a9 100644 --- a/crates/gpui/src/elements/uniform_list.rs +++ b/crates/gpui/src/elements/uniform_list.rs @@ -5,7 +5,7 @@ use crate::{ vector::{vec2f, Vector2F}, }, json::{self, json}, - ElementBox, + ElementBox, RenderContext, View, }; use json::ToJson; use std::{cell::RefCell, cmp, ops::Range, rc::Rc}; @@ -41,27 +41,37 @@ pub struct LayoutState { items: Vec, } -pub struct UniformList -where - F: Fn(Range, &mut Vec, &mut LayoutContext), -{ +pub struct UniformList { state: UniformListState, item_count: usize, - append_items: F, + append_items: Box, &mut Vec, &mut LayoutContext)>, padding_top: f32, padding_bottom: f32, get_width_from_item: Option, } -impl UniformList -where - F: Fn(Range, &mut Vec, &mut LayoutContext), -{ - pub fn new(state: UniformListState, item_count: usize, append_items: F) -> Self { +impl UniformList { + pub fn new( + state: UniformListState, + item_count: usize, + cx: &mut RenderContext, + append_items: F, + ) -> Self + where + V: View, + F: 'static + Fn(&mut V, Range, &mut Vec, &mut RenderContext), + { + let handle = cx.handle(); Self { state, item_count, - append_items, + append_items: Box::new(move |range, items, cx| { + if let Some(handle) = handle.upgrade(cx) { + cx.render(&handle, |view, cx| { + append_items(view, range, items, cx); + }); + } + }), padding_top: 0., padding_bottom: 0., get_width_from_item: None, @@ -144,10 +154,7 @@ where } } -impl Element for UniformList -where - F: Fn(Range, &mut Vec, &mut LayoutContext), -{ +impl Element for UniformList { type LayoutState = LayoutState; type PaintState = (); @@ -162,40 +169,51 @@ where ); } + let no_items = ( + constraint.min, + LayoutState { + item_height: 0., + scroll_max: 0., + items: Default::default(), + }, + ); + if self.item_count == 0 { - return ( - constraint.min, - LayoutState { - item_height: 0., - scroll_max: 0., - items: Default::default(), - }, - ); + return no_items; } let mut items = Vec::new(); let mut size = constraint.max; let mut item_size; let sample_item_ix; - let mut sample_item; + let sample_item; if let Some(sample_ix) = self.get_width_from_item { (self.append_items)(sample_ix..sample_ix + 1, &mut items, cx); sample_item_ix = sample_ix; - sample_item = items.pop().unwrap(); - item_size = sample_item.layout(constraint, cx); - size.set_x(item_size.x()); + + if let Some(mut item) = items.pop() { + item_size = item.layout(constraint, cx); + size.set_x(item_size.x()); + sample_item = item; + } else { + return no_items; + } } else { (self.append_items)(0..1, &mut items, cx); sample_item_ix = 0; - sample_item = items.pop().unwrap(); - item_size = sample_item.layout( - SizeConstraint::new( - vec2f(constraint.max.x(), 0.0), - vec2f(constraint.max.x(), f32::INFINITY), - ), - cx, - ); - item_size.set_x(size.x()); + if let Some(mut item) = items.pop() { + item_size = item.layout( + SizeConstraint::new( + vec2f(constraint.max.x(), 0.0), + vec2f(constraint.max.x(), f32::INFINITY), + ), + cx, + ); + item_size.set_x(size.x()); + sample_item = item + } else { + return no_items; + } } let item_constraint = SizeConstraint { diff --git a/crates/gpui/src/gpui.rs b/crates/gpui/src/gpui.rs index e58bbec1c60f485ae5a24986de99841f00d2a705..c7d619fd7d37c1122b1439f57737a2bc54994138 100644 --- a/crates/gpui/src/gpui.rs +++ b/crates/gpui/src/gpui.rs @@ -16,7 +16,7 @@ pub mod fonts; pub mod geometry; mod presenter; mod scene; -pub use scene::{Border, Quad, Scene}; +pub use scene::{Border, CursorRegion, MouseRegion, MouseRegionId, Quad, Scene}; pub mod text_layout; pub use text_layout::TextLayoutCache; mod util; diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index 2c9c4719d6ecbdd9dee153726c115fed34ffadbd..87e40db1ea8b347c0fd8d66dde7a06a69d98d4e0 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -6,16 +6,19 @@ use crate::{ json::{self, ToJson}, keymap::Keystroke, platform::{CursorStyle, Event}, + scene::CursorRegion, text_layout::TextLayoutCache, - Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AssetCache, ElementBox, - ElementStateContext, Entity, FontSystem, ModelHandle, ReadModel, ReadView, Scene, - UpgradeModelHandle, UpgradeViewHandle, View, ViewHandle, WeakModelHandle, WeakViewHandle, + Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AssetCache, ElementBox, Entity, + FontSystem, ModelHandle, MouseRegion, MouseRegionId, ReadModel, ReadView, RenderContext, + RenderParams, Scene, UpgradeModelHandle, UpgradeViewHandle, View, ViewHandle, WeakModelHandle, + WeakViewHandle, }; use pathfinder_geometry::vector::{vec2f, Vector2F}; use serde_json::json; use smallvec::SmallVec; use std::{ collections::{HashMap, HashSet}, + marker::PhantomData, ops::{Deref, DerefMut}, sync::Arc, }; @@ -24,11 +27,16 @@ pub struct Presenter { window_id: usize, pub(crate) rendered_views: HashMap, parents: HashMap, - cursor_styles: Vec<(RectF, CursorStyle)>, + cursor_regions: Vec, + mouse_regions: Vec, font_cache: Arc, text_layout_cache: TextLayoutCache, asset_cache: Arc, last_mouse_moved_event: Option, + hovered_region_ids: HashSet, + clicked_region: Option, + right_clicked_region: Option, + prev_drag_position: Option, titlebar_height: f32, } @@ -45,32 +53,35 @@ impl Presenter { window_id, rendered_views: cx.render_views(window_id, titlebar_height), parents: HashMap::new(), - cursor_styles: Default::default(), + cursor_regions: Default::default(), + mouse_regions: Default::default(), font_cache, text_layout_cache, asset_cache, last_mouse_moved_event: None, + hovered_region_ids: Default::default(), + clicked_region: None, + right_clicked_region: None, + prev_drag_position: None, titlebar_height, } } pub fn dispatch_path(&self, app: &AppContext) -> Vec { + let mut path = Vec::new(); if let Some(view_id) = app.focused_view_id(self.window_id) { - self.dispatch_path_from(view_id) - } else { - Vec::new() + self.compute_dispatch_path_from(view_id, &mut path) } + path } - pub(crate) fn dispatch_path_from(&self, mut view_id: usize) -> Vec { - let mut path = Vec::new(); + pub(crate) fn compute_dispatch_path_from(&self, mut view_id: usize, path: &mut Vec) { path.push(view_id); while let Some(parent_id) = self.parents.get(&view_id).copied() { path.push(parent_id); view_id = parent_id; } path.reverse(); - path } pub fn invalidate( @@ -87,8 +98,19 @@ impl Presenter { for view_id in &invalidation.updated { self.rendered_views.insert( *view_id, - cx.render_view(self.window_id, *view_id, self.titlebar_height, false) - .unwrap(), + cx.render_view(RenderParams { + window_id: self.window_id, + view_id: *view_id, + titlebar_height: self.titlebar_height, + hovered_region_ids: self.hovered_region_ids.clone(), + clicked_region_id: self.clicked_region.as_ref().map(MouseRegion::id), + right_clicked_region_id: self + .right_clicked_region + .as_ref() + .map(MouseRegion::id), + refreshing: false, + }) + .unwrap(), ); } } @@ -98,7 +120,18 @@ impl Presenter { for (view_id, view) in &mut self.rendered_views { if !invalidation.updated.contains(view_id) { *view = cx - .render_view(self.window_id, *view_id, self.titlebar_height, true) + .render_view(RenderParams { + window_id: self.window_id, + view_id: *view_id, + titlebar_height: self.titlebar_height, + hovered_region_ids: self.hovered_region_ids.clone(), + clicked_region_id: self.clicked_region.as_ref().map(MouseRegion::id), + right_clicked_region_id: self + .right_clicked_region + .as_ref() + .map(MouseRegion::id), + refreshing: true, + }) .unwrap(); } } @@ -122,7 +155,8 @@ impl Presenter { RectF::new(Vector2F::zero(), window_size), ); self.text_layout_cache.finish_frame(); - self.cursor_styles = scene.cursor_styles(); + self.cursor_regions = scene.cursor_regions(); + self.mouse_regions = scene.mouse_regions(); if cx.window_is_active(self.window_id) { if let Some(event) = self.last_mouse_moved_event.clone() { @@ -153,13 +187,17 @@ impl Presenter { window_id: self.window_id, rendered_views: &mut self.rendered_views, parents: &mut self.parents, - refreshing, - window_size, font_cache: &self.font_cache, font_system: cx.platform().fonts(), text_layout_cache: &self.text_layout_cache, asset_cache: &self.asset_cache, view_stack: Vec::new(), + refreshing, + hovered_region_ids: self.hovered_region_ids.clone(), + clicked_region_id: self.clicked_region.as_ref().map(MouseRegion::id), + right_clicked_region_id: self.right_clicked_region.as_ref().map(MouseRegion::id), + titlebar_height: self.titlebar_height, + window_size, app: cx, } } @@ -174,13 +212,69 @@ impl Presenter { font_cache: &self.font_cache, text_layout_cache: &self.text_layout_cache, rendered_views: &mut self.rendered_views, + view_stack: Vec::new(), app: cx, } } pub fn dispatch_event(&mut self, event: Event, cx: &mut MutableAppContext) { if let Some(root_view_id) = cx.root_view_id(self.window_id) { + let mut invalidated_views = Vec::new(); + let mut hovered_regions = Vec::new(); + let mut unhovered_regions = Vec::new(); + let mut mouse_down_region = None; + let mut clicked_region = None; + let mut right_mouse_down_region = None; + let mut right_clicked_region = None; + let mut dragged_region = None; + match event { + Event::LeftMouseDown { position, .. } => { + for region in self.mouse_regions.iter().rev() { + if region.bounds.contains_point(position) { + invalidated_views.push(region.view_id); + mouse_down_region = Some((region.clone(), position)); + self.clicked_region = Some(region.clone()); + self.prev_drag_position = Some(position); + break; + } + } + } + Event::LeftMouseUp { + position, + click_count, + .. + } => { + self.prev_drag_position.take(); + if let Some(region) = self.clicked_region.take() { + invalidated_views.push(region.view_id); + if region.bounds.contains_point(position) { + clicked_region = Some((region, position, click_count)); + } + } + } + Event::RightMouseDown { position, .. } => { + for region in self.mouse_regions.iter().rev() { + if region.bounds.contains_point(position) { + invalidated_views.push(region.view_id); + right_mouse_down_region = Some((region.clone(), position)); + self.right_clicked_region = Some(region.clone()); + break; + } + } + } + Event::RightMouseUp { + position, + click_count, + .. + } => { + if let Some(region) = self.right_clicked_region.take() { + invalidated_views.push(region.view_id); + if region.bounds.contains_point(position) { + right_clicked_region = Some((region, position, click_count)); + } + } + } Event::MouseMoved { position, left_mouse_down, @@ -189,16 +283,43 @@ impl Presenter { if !left_mouse_down { let mut style_to_assign = CursorStyle::Arrow; - for (bounds, style) in self.cursor_styles.iter().rev() { - if bounds.contains_point(position) { - style_to_assign = *style; + for region in self.cursor_regions.iter().rev() { + if region.bounds.contains_point(position) { + style_to_assign = region.style; break; } } cx.platform().set_cursor_style(style_to_assign); + + for region in self.mouse_regions.iter().rev() { + let region_id = region.id(); + if region.bounds.contains_point(position) { + if !self.hovered_region_ids.contains(®ion_id) { + invalidated_views.push(region.view_id); + hovered_regions.push(region.clone()); + self.hovered_region_ids.insert(region_id); + } + } else { + if self.hovered_region_ids.contains(®ion_id) { + invalidated_views.push(region.view_id); + unhovered_regions.push(region.clone()); + self.hovered_region_ids.remove(®ion_id); + } + } + } } } Event::LeftMouseDragged { position } => { + if let Some((clicked_region, prev_drag_position)) = self + .clicked_region + .as_ref() + .zip(self.prev_drag_position.as_mut()) + { + dragged_region = + Some((clicked_region.clone(), position - *prev_drag_position)); + *prev_drag_position = position; + } + self.last_mouse_moved_event = Some(Event::MouseMoved { position, left_mouse_down: true, @@ -208,16 +329,88 @@ impl Presenter { } let mut event_cx = self.build_event_context(cx); - event_cx.dispatch_event(root_view_id, &event); + let mut handled = false; + for unhovered_region in unhovered_regions { + if let Some(hover_callback) = unhovered_region.hover { + handled = true; + event_cx.with_current_view(unhovered_region.view_id, |event_cx| { + hover_callback(false, event_cx); + }) + } + } - let invalidated_views = event_cx.invalidated_views; + for hovered_region in hovered_regions { + if let Some(hover_callback) = hovered_region.hover { + handled = true; + event_cx.with_current_view(hovered_region.view_id, |event_cx| { + hover_callback(true, event_cx); + }) + } + } + + if let Some((mouse_down_region, position)) = mouse_down_region { + if let Some(mouse_down_callback) = mouse_down_region.mouse_down { + handled = true; + event_cx.with_current_view(mouse_down_region.view_id, |event_cx| { + mouse_down_callback(position, event_cx); + }) + } + } + + if let Some((clicked_region, position, click_count)) = clicked_region { + if let Some(click_callback) = clicked_region.click { + handled = true; + event_cx.with_current_view(clicked_region.view_id, |event_cx| { + click_callback(position, click_count, event_cx); + }) + } + } + + if let Some((right_mouse_down_region, position)) = right_mouse_down_region { + if let Some(right_mouse_down_callback) = right_mouse_down_region.right_mouse_down { + handled = true; + event_cx.with_current_view(right_mouse_down_region.view_id, |event_cx| { + right_mouse_down_callback(position, event_cx); + }) + } + } + + if let Some((right_clicked_region, position, click_count)) = right_clicked_region { + if let Some(right_click_callback) = right_clicked_region.right_click { + handled = true; + event_cx.with_current_view(right_clicked_region.view_id, |event_cx| { + right_click_callback(position, click_count, event_cx); + }) + } + } + + if let Some((dragged_region, delta)) = dragged_region { + if let Some(drag_callback) = dragged_region.drag { + handled = true; + event_cx.with_current_view(dragged_region.view_id, |event_cx| { + drag_callback(delta, event_cx); + }) + } + } + + if !handled { + event_cx.dispatch_event(root_view_id, &event); + } + + invalidated_views.extend(event_cx.invalidated_views); let dispatch_directives = event_cx.dispatched_actions; for view_id in invalidated_views { cx.notify_view(self.window_id, view_id); } + + let mut dispatch_path = Vec::new(); for directive in dispatch_directives { - cx.dispatch_action_any(self.window_id, &directive.path, directive.action.as_ref()); + dispatch_path.clear(); + if let Some(view_id) = directive.dispatcher_view_id { + self.compute_dispatch_path_from(view_id, &mut dispatch_path); + } + cx.dispatch_action_any(self.window_id, &dispatch_path, directive.action.as_ref()); } } } @@ -255,7 +448,7 @@ impl Presenter { } pub struct DispatchDirective { - pub path: Vec, + pub dispatcher_view_id: Option, pub action: Box, } @@ -264,16 +457,28 @@ pub struct LayoutContext<'a> { rendered_views: &'a mut HashMap, parents: &'a mut HashMap, view_stack: Vec, - pub refreshing: bool, - pub window_size: Vector2F, pub font_cache: &'a Arc, pub font_system: Arc, pub text_layout_cache: &'a TextLayoutCache, pub asset_cache: &'a AssetCache, pub app: &'a mut MutableAppContext, + pub refreshing: bool, + pub window_size: Vector2F, + titlebar_height: f32, + hovered_region_ids: HashSet, + clicked_region_id: Option, + right_clicked_region_id: Option, } impl<'a> LayoutContext<'a> { + pub(crate) fn keystrokes_for_action( + &self, + action: &dyn Action, + ) -> Option> { + self.app + .keystrokes_for_action(self.window_id, &self.view_stack, action) + } + fn layout(&mut self, view_id: usize, constraint: SizeConstraint) -> Vector2F { if let Some(parent_id) = self.view_stack.last() { self.parents.insert(view_id, *parent_id); @@ -286,12 +491,25 @@ impl<'a> LayoutContext<'a> { size } - pub(crate) fn keystrokes_for_action( - &self, - action: &dyn Action, - ) -> Option> { - self.app - .keystrokes_for_action(self.window_id, &self.view_stack, action) + pub fn render(&mut self, handle: &ViewHandle, f: F) -> T + where + F: FnOnce(&mut V, &mut RenderContext) -> T, + V: View, + { + handle.update(self.app, |view, cx| { + let mut render_cx = RenderContext { + app: cx, + window_id: handle.window_id(), + view_id: handle.id(), + view_type: PhantomData, + titlebar_height: self.titlebar_height, + hovered_region_ids: self.hovered_region_ids.clone(), + clicked_region_id: self.clicked_region_id, + right_clicked_region_id: self.right_clicked_region_id, + refreshing: self.refreshing, + }; + f(view, &mut render_cx) + }) } } @@ -348,14 +566,9 @@ 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, + view_stack: Vec, pub scene: &'a mut Scene, pub font_cache: &'a FontCache, pub text_layout_cache: &'a TextLayoutCache, @@ -365,10 +578,16 @@ pub struct PaintContext<'a> { impl<'a> PaintContext<'a> { fn paint(&mut self, view_id: usize, origin: Vector2F, visible_bounds: RectF) { if let Some(mut tree) = self.rendered_views.remove(&view_id) { + self.view_stack.push(view_id); tree.paint(origin, visible_bounds, self); self.rendered_views.insert(view_id, tree); + self.view_stack.pop(); } } + + pub fn current_view_id(&self) -> usize { + *self.view_stack.last().unwrap() + } } impl<'a> Deref for PaintContext<'a> { @@ -393,9 +612,8 @@ pub struct EventContext<'a> { impl<'a> EventContext<'a> { fn dispatch_event(&mut self, view_id: usize, event: &Event) -> bool { if let Some(mut element) = self.rendered_views.remove(&view_id) { - self.view_stack.push(view_id); - let result = element.dispatch_event(event, self); - self.view_stack.pop(); + let result = + self.with_current_view(view_id, |this| element.dispatch_event(event, this)); self.rendered_views.insert(view_id, element); result } else { @@ -403,9 +621,19 @@ impl<'a> EventContext<'a> { } } + fn with_current_view(&mut self, view_id: usize, f: F) -> T + where + F: FnOnce(&mut Self) -> T, + { + self.view_stack.push(view_id); + let result = f(self); + self.view_stack.pop(); + result + } + pub fn dispatch_any_action(&mut self, action: Box) { self.dispatched_actions.push(DispatchDirective { - path: self.view_stack.clone(), + dispatcher_view_id: self.view_stack.last().copied(), action, }); } diff --git a/crates/gpui/src/scene.rs b/crates/gpui/src/scene.rs index 7c358b85a04991272b05a4fb4ececeec07b51ef0..843a3b62d6309b0a0293a86040763ba6833865e6 100644 --- a/crates/gpui/src/scene.rs +++ b/crates/gpui/src/scene.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde_json::json; -use std::{borrow::Cow, sync::Arc}; +use std::{any::TypeId, borrow::Cow, rc::Rc, sync::Arc}; use crate::{ color::Color, @@ -8,7 +8,7 @@ use crate::{ geometry::{rect::RectF, vector::Vector2F}, json::ToJson, platform::CursorStyle, - ImageData, + EventContext, ImageData, }; pub struct Scene { @@ -33,7 +33,35 @@ pub struct Layer { image_glyphs: Vec, icons: Vec, paths: Vec, - cursor_styles: Vec<(RectF, CursorStyle)>, + cursor_regions: Vec, + mouse_regions: Vec, +} + +#[derive(Copy, Clone)] +pub struct CursorRegion { + pub bounds: RectF, + pub style: CursorStyle, +} + +#[derive(Clone)] +pub struct MouseRegion { + pub view_id: usize, + pub tag: TypeId, + pub region_id: usize, + pub bounds: RectF, + pub hover: Option>, + pub mouse_down: Option>, + pub click: Option>, + pub right_mouse_down: Option>, + pub right_click: Option>, + pub drag: Option>, +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct MouseRegionId { + pub view_id: usize, + pub tag: TypeId, + pub region_id: usize, } #[derive(Default, Debug)] @@ -175,13 +203,20 @@ impl Scene { self.stacking_contexts.iter().flat_map(|s| &s.layers) } - pub fn cursor_styles(&self) -> Vec<(RectF, CursorStyle)> { + pub fn cursor_regions(&self) -> Vec { self.layers() - .flat_map(|layer| &layer.cursor_styles) + .flat_map(|layer| &layer.cursor_regions) .copied() .collect() } + pub fn mouse_regions(&self) -> Vec { + self.layers() + .flat_map(|layer| &layer.mouse_regions) + .cloned() + .collect() + } + pub fn push_stacking_context(&mut self, clip_bounds: Option) { self.active_stacking_context_stack .push(self.stacking_contexts.len()); @@ -206,8 +241,12 @@ impl Scene { self.active_layer().push_quad(quad) } - pub fn push_cursor_style(&mut self, bounds: RectF, style: CursorStyle) { - self.active_layer().push_cursor_style(bounds, style); + pub fn push_cursor_region(&mut self, region: CursorRegion) { + self.active_layer().push_cursor_region(region); + } + + pub fn push_mouse_region(&mut self, region: MouseRegion) { + self.active_layer().push_mouse_region(region); } pub fn push_image(&mut self, image: Image) { @@ -298,7 +337,8 @@ impl Layer { glyphs: Default::default(), icons: Default::default(), paths: Default::default(), - cursor_styles: Default::default(), + cursor_regions: Default::default(), + mouse_regions: Default::default(), } } @@ -316,10 +356,24 @@ impl Layer { self.quads.as_slice() } - fn push_cursor_style(&mut self, bounds: RectF, style: CursorStyle) { - if let Some(bounds) = bounds.intersection(self.clip_bounds.unwrap_or(bounds)) { + fn push_cursor_region(&mut self, region: CursorRegion) { + if let Some(bounds) = region + .bounds + .intersection(self.clip_bounds.unwrap_or(region.bounds)) + { if can_draw(bounds) { - self.cursor_styles.push((bounds, style)); + self.cursor_regions.push(region); + } + } + } + + fn push_mouse_region(&mut self, region: MouseRegion) { + if let Some(bounds) = region + .bounds + .intersection(self.clip_bounds.unwrap_or(region.bounds)) + { + if can_draw(bounds) { + self.mouse_regions.push(region); } } } @@ -484,6 +538,16 @@ impl ToJson for Border { } } +impl MouseRegion { + pub fn id(&self) -> MouseRegionId { + MouseRegionId { + view_id: self.view_id, + tag: self.tag, + region_id: self.region_id, + } + } +} + fn can_draw(bounds: RectF) -> bool { let size = bounds.size(); size.x() > 0. && size.y() > 0. diff --git a/crates/gpui/src/views/select.rs b/crates/gpui/src/views/select.rs index a58be77c03299c195948c5ca0791179182d4e2b5..80c3ba28847e616e5727a3fd040af50976420568 100644 --- a/crates/gpui/src/views/select.rs +++ b/crates/gpui/src/views/select.rs @@ -123,7 +123,6 @@ impl View for Select { .boxed(), ); if self.is_open { - let handle = self.handle.clone(); result.add_child( Overlay::new( Container::new( @@ -131,9 +130,8 @@ impl View for Select { UniformList::new( self.list_state.clone(), self.item_count, - move |mut range, items, cx| { - let handle = handle.upgrade(cx).unwrap(); - let this = handle.read(cx); + cx, + move |this, mut range, items, cx| { let selected_item_ix = this.selected_item_ix; range.end = range.end.min(this.item_count); items.extend(range.map(|ix| { @@ -141,7 +139,7 @@ impl View for Select { ix, cx, |mouse_state, cx| { - (handle.read(cx).render_item)( + (this.render_item)( ix, if ix == selected_item_ix { ItemType::Selected diff --git a/crates/outline/src/outline.rs b/crates/outline/src/outline.rs index 57c7441bfe761e0d16d559abbade8a024be8f2d6..19b309116a45f7486d07c235c23212d66b77762a 100644 --- a/crates/outline/src/outline.rs +++ b/crates/outline/src/outline.rs @@ -4,8 +4,8 @@ use editor::{ }; use fuzzy::StringMatch; use gpui::{ - actions, elements::*, geometry::vector::Vector2F, AppContext, Entity, MutableAppContext, - RenderContext, Task, View, ViewContext, ViewHandle, + actions, elements::*, geometry::vector::Vector2F, AppContext, Entity, MouseState, + MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle, }; use language::Outline; use ordered_float::OrderedFloat; @@ -231,7 +231,7 @@ impl PickerDelegate for OutlineView { fn render_match( &self, ix: usize, - mouse_state: &MouseState, + mouse_state: MouseState, selected: bool, cx: &AppContext, ) -> ElementBox { diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index 19dc3054b7c4fbe0e97ebc6616d89560a10f95dc..8f8e5d26b95e989777cb92de0da03ee8e62a321d 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -1,14 +1,14 @@ use editor::Editor; use gpui::{ elements::{ - ChildView, Flex, Label, MouseEventHandler, MouseState, ParentElement, ScrollTarget, - UniformList, UniformListState, + ChildView, Flex, Label, MouseEventHandler, ParentElement, ScrollTarget, UniformList, + UniformListState, }, geometry::vector::{vec2f, Vector2F}, keymap, platform::CursorStyle, - AppContext, Axis, Element, ElementBox, Entity, MutableAppContext, RenderContext, Task, View, - ViewContext, ViewHandle, WeakViewHandle, + AppContext, Axis, Element, ElementBox, Entity, MouseState, MutableAppContext, RenderContext, + Task, View, ViewContext, ViewHandle, WeakViewHandle, }; use menu::{Cancel, Confirm, SelectFirst, SelectIndex, SelectLast, SelectNext, SelectPrev}; use settings::Settings; @@ -32,7 +32,7 @@ pub trait PickerDelegate: View { fn render_match( &self, ix: usize, - state: &MouseState, + state: MouseState, selected: bool, cx: &AppContext, ) -> ElementBox; @@ -52,6 +52,7 @@ impl View for Picker { fn render(&mut self, cx: &mut RenderContext) -> gpui::ElementBox { let settings = cx.global::(); + let container_style = settings.theme.picker.container; let delegate = self.delegate.clone(); let match_count = if let Some(delegate) = delegate.upgrade(cx.app) { delegate.read(cx).match_count() @@ -78,8 +79,9 @@ impl View for Picker { UniformList::new( self.list_state.clone(), match_count, - move |mut range, items, cx| { - let delegate = delegate.upgrade(cx).unwrap(); + cx, + move |this, mut range, items, cx| { + let delegate = this.delegate.upgrade(cx).unwrap(); let selected_ix = delegate.read(cx).selected_index(); range.end = cmp::min(range.end, delegate.read(cx).match_count()); items.extend(range.map(move |ix| { @@ -101,7 +103,7 @@ impl View for Picker { .boxed(), ) .contained() - .with_style(settings.theme.picker.container) + .with_style(container_style) .constrained() .with_max_width(self.max_size.x()) .with_max_height(self.max_size.y()) diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index f1dac49f814d362083ffbb92deeb7da2759cd478..2ce233fa21ee79b1390a94dfd50cb9aa2fc4e718 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -12,7 +12,7 @@ use gpui::{ impl_internal_actions, keymap, platform::CursorStyle, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle, MutableAppContext, - PromptLevel, Task, View, ViewContext, ViewHandle, WeakViewHandle, + PromptLevel, RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle, }; use menu::{Confirm, SelectNext, SelectPrev}; use project::{Entry, EntryKind, Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId}; @@ -795,8 +795,8 @@ impl ProjectPanel { fn for_each_visible_entry( &self, range: Range, - cx: &mut ViewContext, - mut callback: impl FnMut(ProjectEntryId, EntryDetails, &mut ViewContext), + cx: &mut RenderContext, + mut callback: impl FnMut(ProjectEntryId, EntryDetails, &mut RenderContext), ) { let mut ix = 0; for (worktree_id, visible_worktree_entries) in &self.visible_entries { @@ -869,7 +869,7 @@ impl ProjectPanel { details: EntryDetails, editor: &ViewHandle, theme: &theme::ProjectPanel, - cx: &mut ViewContext, + cx: &mut RenderContext, ) -> ElementBox { let kind = details.kind; let show_editor = details.is_editing && !details.is_processing; @@ -961,34 +961,27 @@ impl View for ProjectPanel { let theme = &cx.global::().theme.project_panel; let mut container_style = theme.container; let padding = std::mem::take(&mut container_style.padding); - let handle = self.handle.clone(); Stack::new() .with_child( - MouseEventHandler::new::(0, cx, |_, _| { + MouseEventHandler::new::(0, cx, |_, cx| { UniformList::new( self.list.clone(), self.visible_entries .iter() .map(|(_, worktree_entries)| worktree_entries.len()) .sum(), - move |range, items, cx| { + cx, + move |this, range, items, cx| { let theme = cx.global::().theme.clone(); - let this = handle.upgrade(cx).unwrap(); - this.update(cx.app, |this, cx| { - this.for_each_visible_entry( - range.clone(), + this.for_each_visible_entry(range.clone(), cx, |id, details, cx| { + items.push(Self::render_entry( + id, + details, + &this.filename_editor, + &theme.project_panel, cx, - |id, details, cx| { - items.push(Self::render_entry( - id, - details, - &this.filename_editor, - &theme.project_panel, - cx, - )); - }, - ); - }) + )); + }); }, ) .with_padding_top(padding.top) @@ -1458,7 +1451,7 @@ mod tests { let mut result = Vec::new(); let mut project_entries = HashSet::new(); let mut has_editor = false; - panel.update(cx, |panel, cx| { + cx.render(panel, |panel, cx| { panel.for_each_visible_entry(range, cx, |project_entry, details, _| { if details.is_editing { assert!(!has_editor, "duplicate editor entry"); diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 20da49b1dfdc93d846f98b8a2fa2a33980b6c87d..ea99767e0a15b63f57cab7e163bbfc10bfa641c3 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -3,8 +3,8 @@ use editor::{ }; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ - actions, elements::*, AppContext, Entity, ModelHandle, MutableAppContext, RenderContext, Task, - View, ViewContext, ViewHandle, + actions, elements::*, AppContext, Entity, ModelHandle, MouseState, MutableAppContext, + RenderContext, Task, View, ViewContext, ViewHandle, }; use ordered_float::OrderedFloat; use picker::{Picker, PickerDelegate}; @@ -221,7 +221,7 @@ impl PickerDelegate for ProjectSymbolsView { fn render_match( &self, ix: usize, - mouse_state: &MouseState, + mouse_state: MouseState, selected: bool, cx: &AppContext, ) -> ElementBox { diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 2edd3cef45eae765962b13b8c8c0320ee928ac60..2e38e501e26beefc6d4a0f5484f4e4e39c0616b0 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -2,9 +2,9 @@ mod theme_registry; use gpui::{ color::Color, - elements::{ContainerStyle, ImageStyle, LabelStyle, MouseState}, + elements::{ContainerStyle, ImageStyle, LabelStyle}, fonts::{HighlightStyle, TextStyle}, - Border, + Border, MouseState, }; use serde::{de::DeserializeOwned, Deserialize}; use serde_json::Value; @@ -505,7 +505,7 @@ pub struct Interactive { } impl Interactive { - pub fn style_for(&self, state: &MouseState, active: bool) -> &T { + pub fn style_for(&self, state: MouseState, active: bool) -> &T { if active { if state.hovered { self.active_hover diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index 9f445c633af0ff2c8e9715ab5fe107790f240db3..106e6ad42921fabc425e65dea1a68f634e730ab6 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -1,6 +1,6 @@ use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; use gpui::{ - actions, elements::*, AppContext, Element, ElementBox, Entity, MutableAppContext, + actions, elements::*, AppContext, Element, ElementBox, Entity, MouseState, MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; use picker::{Picker, PickerDelegate}; @@ -213,7 +213,7 @@ impl PickerDelegate for ThemeSelector { fn render_match( &self, ix: usize, - mouse_state: &MouseState, + mouse_state: MouseState, selected: bool, cx: &AppContext, ) -> ElementBox { diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index ba2a21bce4cad4e0ada71ef312366afe7e0f555e..db157ed8503ef619b202c21de86f12e52da13047 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -689,6 +689,7 @@ impl Pane { let theme = cx.global::().theme.clone(); enum Tabs {} + enum Tab {} let pane = cx.handle(); let tabs = MouseEventHandler::new::(0, cx, |mouse_state, cx| { let autoscroll = if mem::take(&mut self.autoscroll) { @@ -717,7 +718,7 @@ impl Pane { style.container.border.left = false; } - EventHandler::new( + MouseEventHandler::new::(ix, cx, |_, cx| { Container::new( Flex::row() .with_child( @@ -807,11 +808,10 @@ impl Pane { .boxed(), ) .with_style(style.container) - .boxed(), - ) - .on_mouse_down(move |cx| { + .boxed() + }) + .on_mouse_down(move |_, cx| { cx.dispatch_action(ActivateItem(ix)); - true }) .boxed() }) diff --git a/crates/workspace/src/sidebar.rs b/crates/workspace/src/sidebar.rs index 5aec332913420cc8beeab4a071b7a95468919d6b..9aaf2b832ab49d6f44ca5fd3293c15f12574aa7f 100644 --- a/crates/workspace/src/sidebar.rs +++ b/crates/workspace/src/sidebar.rs @@ -165,6 +165,7 @@ impl Sidebar { ..Default::default() }) .with_cursor_style(CursorStyle::ResizeLeftRight) + .on_mouse_down(|_, _| {}) // This prevents the mouse down event from being propagated elsewhere .on_drag(move |delta, cx| { let prev_width = *actual_width.borrow(); *custom_width.borrow_mut() = 0f32