From ad1947fa50d1561fd9741dafb1e9744480941c8f Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 19 Jan 2023 12:34:13 -0500 Subject: [PATCH 1/3] Add in-window on-move-out mouse handler concept --- crates/editor/src/element.rs | 3 +++ .../gpui/src/elements/mouse_event_handler.rs | 10 ++++++- crates/gpui/src/presenter.rs | 17 ++++++++++-- crates/gpui/src/scene/mouse_event.rs | 13 +++++++++ crates/gpui/src/scene/mouse_region.rs | 27 ++++++++++++++++++- 5 files changed, 66 insertions(+), 4 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 7d69d3833c..5c34b98380 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -190,6 +190,9 @@ impl EditorElement { } } }) + .on_move_out(|e, cx| { + println!("on move out"); + }) .on_scroll({ let position_map = position_map.clone(); move |e, cx| { diff --git a/crates/gpui/src/elements/mouse_event_handler.rs b/crates/gpui/src/elements/mouse_event_handler.rs index 132a915f0b..761e134262 100644 --- a/crates/gpui/src/elements/mouse_event_handler.rs +++ b/crates/gpui/src/elements/mouse_event_handler.rs @@ -7,7 +7,7 @@ use crate::{ platform::CursorStyle, scene::{ CursorRegion, HandlerSet, MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseHover, - MouseMove, MouseScrollWheel, MouseUp, MouseUpOut, + MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut, }, DebugContext, Element, ElementBox, EventContext, LayoutContext, MeasurementContext, MouseButton, MouseRegion, MouseState, PaintContext, RenderContext, SizeConstraint, View, @@ -82,6 +82,14 @@ impl MouseEventHandler { self } + pub fn on_move_out( + mut self, + handler: impl Fn(MouseMoveOut, &mut EventContext) + 'static, + ) -> Self { + self.handlers = self.handlers.on_move_out(handler); + self + } + pub fn on_down( mut self, button: MouseButton, diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index bbaf1ed0bb..1573b10a10 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -8,7 +8,7 @@ use crate::{ platform::{CursorStyle, Event}, scene::{ CursorRegion, MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseEvent, MouseHover, - MouseMove, MouseScrollWheel, MouseUp, MouseUpOut, Scene, + MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut, Scene, }, text_layout::TextLayoutCache, Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AnyWeakViewHandle, Appearance, @@ -245,8 +245,11 @@ impl Presenter { // -> Also updates mouse-related state match &event { Event::KeyDown(e) => return cx.dispatch_key_down(self.window_id, e), + Event::KeyUp(e) => return cx.dispatch_key_up(self.window_id, e), + Event::ModifiersChanged(e) => return cx.dispatch_modifiers_changed(self.window_id, e), + Event::MouseDown(e) => { // Click events are weird because they can be fired after a drag event. // MDN says that browsers handle this by starting from 'the most @@ -279,6 +282,7 @@ impl Presenter { platform_event: e.clone(), })); } + Event::MouseUp(e) => { // NOTE: The order of event pushes is important! MouseUp events MUST be fired // before click events, and so the MouseUp events need to be pushed before @@ -296,6 +300,7 @@ impl Presenter { platform_event: e.clone(), })); } + Event::MouseMoved( e @ MouseMovedEvent { position, @@ -347,9 +352,13 @@ impl Presenter { platform_event: e.clone(), started: false, })); + mouse_events.push(MouseEvent::MoveOut(MouseMoveOut { + region: Default::default(), + })); self.last_mouse_moved_event = Some(event.clone()); } + Event::ScrollWheel(e) => mouse_events.push(MouseEvent::ScrollWheel(MouseScrollWheel { region: Default::default(), platform_event: e.clone(), @@ -407,6 +416,7 @@ impl Presenter { } } } + MouseEvent::Down(_) | MouseEvent::Up(_) => { for (region, _) in self.mouse_regions.iter().rev() { if region.bounds.contains_point(self.mouse_position) { @@ -417,6 +427,7 @@ impl Presenter { } } } + MouseEvent::Click(e) => { // Only raise click events if the released button is the same as the one stored if self @@ -439,6 +450,7 @@ impl Presenter { } } } + MouseEvent::Drag(_) => { for (mouse_region, _) in self.mouse_regions.iter().rev() { if self.clicked_region_ids.contains(&mouse_region.id()) { @@ -447,7 +459,7 @@ impl Presenter { } } - MouseEvent::UpOut(_) | MouseEvent::DownOut(_) => { + MouseEvent::MoveOut(_) | MouseEvent::UpOut(_) | MouseEvent::DownOut(_) => { for (mouse_region, _) in self.mouse_regions.iter().rev() { // NOT contains if !mouse_region.bounds.contains_point(self.mouse_position) { @@ -455,6 +467,7 @@ impl Presenter { } } } + _ => { for (mouse_region, _) in self.mouse_regions.iter().rev() { // Contains diff --git a/crates/gpui/src/scene/mouse_event.rs b/crates/gpui/src/scene/mouse_event.rs index 00d1ddbf8b..2effb8ab53 100644 --- a/crates/gpui/src/scene/mouse_event.rs +++ b/crates/gpui/src/scene/mouse_event.rs @@ -21,6 +21,11 @@ impl Deref for MouseMove { } } +#[derive(Debug, Default, Clone)] +pub struct MouseMoveOut { + pub region: RectF, +} + #[derive(Debug, Default, Clone)] pub struct MouseDrag { pub region: RectF, @@ -138,6 +143,7 @@ impl Deref for MouseScrollWheel { #[derive(Debug, Clone)] pub enum MouseEvent { Move(MouseMove), + MoveOut(MouseMoveOut), Drag(MouseDrag), Hover(MouseHover), Down(MouseDown), @@ -152,6 +158,7 @@ impl MouseEvent { pub fn set_region(&mut self, region: RectF) { match self { MouseEvent::Move(r) => r.region = region, + MouseEvent::MoveOut(r) => r.region = region, MouseEvent::Drag(r) => r.region = region, MouseEvent::Hover(r) => r.region = region, MouseEvent::Down(r) => r.region = region, @@ -168,6 +175,7 @@ impl MouseEvent { pub fn is_capturable(&self) -> bool { match self { MouseEvent::Move(_) => true, + MouseEvent::MoveOut(_) => false, MouseEvent::Drag(_) => true, MouseEvent::Hover(_) => false, MouseEvent::Down(_) => true, @@ -185,6 +193,10 @@ impl MouseEvent { discriminant(&MouseEvent::Move(Default::default())) } + pub fn move_out_disc() -> Discriminant { + discriminant(&MouseEvent::MoveOut(Default::default())) + } + pub fn drag_disc() -> Discriminant { discriminant(&MouseEvent::Drag(Default::default())) } @@ -220,6 +232,7 @@ impl MouseEvent { pub fn handler_key(&self) -> HandlerKey { match self { MouseEvent::Move(_) => HandlerKey::new(Self::move_disc(), None), + MouseEvent::MoveOut(_) => HandlerKey::new(Self::move_out_disc(), None), MouseEvent::Drag(e) => HandlerKey::new(Self::drag_disc(), e.pressed_button), MouseEvent::Hover(_) => HandlerKey::new(Self::hover_disc(), None), MouseEvent::Down(e) => HandlerKey::new(Self::down_disc(), Some(e.button)), diff --git a/crates/gpui/src/scene/mouse_region.rs b/crates/gpui/src/scene/mouse_region.rs index 0fdc76ebbf..7cbbc5b897 100644 --- a/crates/gpui/src/scene/mouse_region.rs +++ b/crates/gpui/src/scene/mouse_region.rs @@ -12,7 +12,7 @@ use super::{ MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseEvent, MouseHover, MouseMove, MouseUp, MouseUpOut, }, - MouseScrollWheel, + MouseMoveOut, MouseScrollWheel, }; #[derive(Clone)] @@ -124,6 +124,14 @@ impl MouseRegion { self } + pub fn on_move_out( + mut self, + handler: impl Fn(MouseMoveOut, &mut EventContext) + 'static, + ) -> Self { + self.handlers = self.handlers.on_move_out(handler); + self + } + pub fn on_scroll( mut self, handler: impl Fn(MouseScrollWheel, &mut EventContext) + 'static, @@ -289,6 +297,23 @@ impl HandlerSet { self } + pub fn on_move_out( + mut self, + handler: impl Fn(MouseMoveOut, &mut EventContext) + 'static, + ) -> Self { + self.insert(MouseEvent::move_out_disc(), None, + Rc::new(move |region_event, cx| { + if let MouseEvent::MoveOut(e) = region_event { + handler(e, cx); + } else { + panic!( + "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::MoveOut, found {:?}", + region_event); + } + })); + self + } + pub fn on_down( mut self, button: MouseButton, From 131f3471fc5b16f3972ad0bac072b32c6fe17be2 Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 20 Jan 2023 09:50:02 -0500 Subject: [PATCH 2/3] Don't dispatch mousemove without focus & avoid swallowing external moves Co-Authored-By: Antonio Scandurra --- crates/gpui/src/platform/mac/window.rs | 33 ++++++++++---------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 958ac2ebd6..ca233ccf65 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -66,12 +66,6 @@ const NSNormalWindowLevel: NSInteger = 0; #[allow(non_upper_case_globals)] const NSPopUpWindowLevel: NSInteger = 101; #[allow(non_upper_case_globals)] -const NSTrackingMouseMoved: NSUInteger = 0x02; -#[allow(non_upper_case_globals)] -const NSTrackingActiveAlways: NSUInteger = 0x80; -#[allow(non_upper_case_globals)] -const NSTrackingInVisibleRect: NSUInteger = 0x200; -#[allow(non_upper_case_globals)] const NSWindowAnimationBehaviorUtilityWindow: NSInteger = 4; #[repr(C)] @@ -469,15 +463,7 @@ impl Window { native_window.setTitlebarAppearsTransparent_(YES); } - let tracking_area: id = msg_send![class!(NSTrackingArea), alloc]; - let _: () = msg_send![ - tracking_area, - initWithRect: NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 0.)) - options: NSTrackingMouseMoved | NSTrackingActiveAlways | NSTrackingInVisibleRect - owner: native_view - userInfo: nil - ]; - let _: () = msg_send![native_view, addTrackingArea: tracking_area.autorelease()]; + native_window.setAcceptsMouseMovedEvents_(YES); native_view.setAutoresizingMask_(NSViewWidthSizable | NSViewHeightSizable); native_view.setWantsBestResolutionOpenGLSurface_(YES); @@ -873,11 +859,10 @@ extern "C" fn handle_key_down(this: &Object, _: Sel, native_event: id) { extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: bool) -> BOOL { let window_state = unsafe { get_window_state(this) }; - let mut window_state_borrow = window_state.as_ref().borrow_mut(); - let event = - unsafe { Event::from_native(native_event, Some(window_state_borrow.content_size().y())) }; + let window_height = window_state_borrow.content_size().y(); + let event = unsafe { Event::from_native(native_event, Some(window_height)) }; if let Some(event) = event { if key_equivalent { @@ -902,6 +887,7 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: function_is_held = event.keystroke.function; Some((event, None)) } + _ => return NO, }; @@ -968,9 +954,11 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) { let window_state = unsafe { get_window_state(this) }; let weak_window_state = Rc::downgrade(&window_state); let mut window_state_borrow = window_state.as_ref().borrow_mut(); + let is_active = unsafe { window_state_borrow.native_window.isKeyWindow() == YES }; + + let window_height = window_state_borrow.content_size().y(); + let event = unsafe { Event::from_native(native_event, Some(window_height)) }; - let event = - unsafe { Event::from_native(native_event, Some(window_state_borrow.content_size().y())) }; if let Some(event) = event { match &event { Event::MouseMoved( @@ -989,12 +977,16 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) { )) .detach(); } + + Event::MouseMoved(_) if !is_active => return, + Event::MouseUp(MouseButtonEvent { button: MouseButton::Left, .. }) => { window_state_borrow.synthetic_drag_counter += 1; } + Event::ModifiersChanged(ModifiersChangedEvent { modifiers }) => { // Only raise modifiers changed event when they have actually changed if let Some(Event::ModifiersChanged(ModifiersChangedEvent { @@ -1008,6 +1000,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) { window_state_borrow.previous_modifiers_changed_event = Some(event.clone()); } + _ => {} } From 467e3dc50ac846260bbf44a51d458e3028311a5b Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 20 Jan 2023 10:16:24 -0500 Subject: [PATCH 3/3] Hide editor hover on mouse move out & always notify when hiding hover Co-Authored-By: Antonio Scandurra --- crates/editor/src/editor.rs | 10 +++++----- crates/editor/src/element.rs | 10 +++++++--- crates/editor/src/hover_popover.rs | 16 ++++++++++++---- crates/editor/src/scroll.rs | 8 ++++---- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 07c0bce6b6..057a86e7f3 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -44,7 +44,7 @@ use gpui::{ ViewContext, ViewHandle, WeakViewHandle, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; -use hover_popover::{hide_hover, HoverState}; +use hover_popover::{hide_hover, HideHover, HoverState}; pub use items::MAX_TAB_TITLE_LEN; use itertools::Itertools; pub use language::{char_kind, CharKind}; @@ -1319,7 +1319,7 @@ impl Editor { } } - hide_hover(self, cx); + hide_hover(self, &HideHover, cx); if old_cursor_position.to_display_point(&display_map).row() != new_cursor_position.to_display_point(&display_map).row() @@ -1694,7 +1694,7 @@ impl Editor { return; } - if hide_hover(self, cx) { + if hide_hover(self, &HideHover, cx) { return; } @@ -6174,7 +6174,7 @@ impl View for Editor { cx.defer(move |cx| { if let Some(editor) = handle.upgrade(cx) { editor.update(cx, |editor, cx| { - hide_hover(editor, cx); + hide_hover(editor, &HideHover, cx); hide_link_definition(editor, cx); }) } @@ -6223,7 +6223,7 @@ impl View for Editor { self.buffer .update(cx, |buffer, cx| buffer.remove_active_selections(cx)); self.hide_context_menu(cx); - hide_hover(self, cx); + hide_hover(self, &HideHover, cx); cx.emit(Event::Blurred); cx.notify(); } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 5c34b98380..bce63ca0cf 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -7,7 +7,7 @@ use crate::{ display_map::{BlockStyle, DisplaySnapshot, TransformBlock}, git::{diff_hunk_to_display, DisplayDiffHunk}, hover_popover::{ - HoverAt, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT, + HideHover, HoverAt, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT, }, link_go_to_definition::{ GoToFetchedDefinition, GoToFetchedTypeDefinition, UpdateGoToDefinitionLink, @@ -114,6 +114,7 @@ impl EditorElement { fn attach_mouse_handlers( view: &WeakViewHandle, position_map: &Arc, + has_popovers: bool, visible_bounds: RectF, text_bounds: RectF, gutter_bounds: RectF, @@ -190,8 +191,10 @@ impl EditorElement { } } }) - .on_move_out(|e, cx| { - println!("on move out"); + .on_move_out(move |_, cx| { + if has_popovers { + cx.dispatch_action(HideHover); + } }) .on_scroll({ let position_map = position_map.clone(); @@ -1873,6 +1876,7 @@ impl Element for EditorElement { Self::attach_mouse_handlers( &self.view, &layout.position_map, + layout.hover_popovers.is_some(), visible_bounds, text_bounds, gutter_bounds, diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 7369b0a6f4..6d003cae5d 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -29,12 +29,16 @@ pub struct HoverAt { pub point: Option, } +#[derive(Copy, Clone, PartialEq)] +pub struct HideHover; + actions!(editor, [Hover]); -impl_internal_actions!(editor, [HoverAt]); +impl_internal_actions!(editor, [HoverAt, HideHover]); pub fn init(cx: &mut MutableAppContext) { cx.add_action(hover); cx.add_action(hover_at); + cx.add_action(hide_hover); } /// Bindable action which uses the most recent selection head to trigger a hover @@ -50,7 +54,7 @@ pub fn hover_at(editor: &mut Editor, action: &HoverAt, cx: &mut ViewContext) -> bool { +pub fn hide_hover(editor: &mut Editor, _: &HideHover, cx: &mut ViewContext) -> bool { let did_hide = editor.hover_state.info_popover.take().is_some() | editor.hover_state.diagnostic_popover.take().is_some(); @@ -67,6 +71,10 @@ pub fn hide_hover(editor: &mut Editor, cx: &mut ViewContext) -> bool { editor.clear_background_highlights::(cx); + if did_hide { + cx.notify(); + } + did_hide } @@ -121,7 +129,7 @@ fn show_hover( // Hover triggered from same location as last time. Don't show again. return; } else { - hide_hover(editor, cx); + hide_hover(editor, &HideHover, cx); } } } diff --git a/crates/editor/src/scroll.rs b/crates/editor/src/scroll.rs index 0ca837a843..1b3fd96dbd 100644 --- a/crates/editor/src/scroll.rs +++ b/crates/editor/src/scroll.rs @@ -17,7 +17,7 @@ use workspace::WorkspaceId; use crate::{ display_map::{DisplaySnapshot, ToDisplayPoint}, - hover_popover::hide_hover, + hover_popover::{hide_hover, HideHover}, persistence::DB, Anchor, DisplayPoint, Editor, EditorMode, Event, MultiBufferSnapshot, ToPoint, }; @@ -307,7 +307,7 @@ impl Editor { ) { let map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - hide_hover(self, cx); + hide_hover(self, &HideHover, cx); self.scroll_manager.set_scroll_position( scroll_position, &map, @@ -323,7 +323,7 @@ impl Editor { } pub fn set_scroll_anchor(&mut self, scroll_anchor: ScrollAnchor, cx: &mut ViewContext) { - hide_hover(self, cx); + hide_hover(self, &HideHover, cx); let top_row = scroll_anchor .top_anchor .to_point(&self.buffer().read(cx).snapshot(cx)) @@ -337,7 +337,7 @@ impl Editor { scroll_anchor: ScrollAnchor, cx: &mut ViewContext, ) { - hide_hover(self, cx); + hide_hover(self, &HideHover, cx); let top_row = scroll_anchor .top_anchor .to_point(&self.buffer().read(cx).snapshot(cx))