diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 7fc5244b92758024d8cf5df8afeca54d29d81d68..8031bc5db60923b2470f723035c4df801cdd164e 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -884,7 +884,11 @@ impl EditorElement { bounds: text_bounds, }), |cx| { - if text_bounds.contains(&cx.mouse_position()) { + let interactive_text_bounds = InteractiveBounds { + bounds: text_bounds, + stacking_order: cx.stacking_order().clone(), + }; + if interactive_text_bounds.visibly_contains(&cx.mouse_position(), cx) { if self .editor .read(cx) @@ -1361,8 +1365,12 @@ impl EditorElement { )); } + let interactive_track_bounds = InteractiveBounds { + bounds: track_bounds, + stacking_order: cx.stacking_order().clone(), + }; let mut mouse_position = cx.mouse_position(); - if track_bounds.contains(&mouse_position) { + if interactive_track_bounds.visibly_contains(&mouse_position, cx) { cx.set_cursor_style(CursorStyle::Arrow); } @@ -1392,7 +1400,7 @@ impl EditorElement { cx.stop_propagation(); } else { editor.scroll_manager.set_is_dragging_scrollbar(false, cx); - if track_bounds.contains(&event.position) { + if interactive_track_bounds.visibly_contains(&event.position, cx) { editor.scroll_manager.show_scrollbar(cx); } } diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index fa1eb9e5b63454d0a3fa370913e094f30c3ad68c..908b302dfc1cd28f6ff873eff215d583d1b489f4 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -62,6 +62,18 @@ impl Interactivity { })); } + pub fn capture_any_mouse_down( + &mut self, + listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static, + ) { + self.mouse_down_listeners + .push(Box::new(move |event, bounds, phase, cx| { + if phase == DispatchPhase::Capture && bounds.visibly_contains(&event.position, cx) { + (listener)(event, cx) + } + })); + } + pub fn on_any_mouse_down( &mut self, listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static, @@ -90,6 +102,18 @@ impl Interactivity { })); } + pub fn capture_any_mouse_up( + &mut self, + listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static, + ) { + self.mouse_up_listeners + .push(Box::new(move |event, bounds, phase, cx| { + if phase == DispatchPhase::Capture && bounds.visibly_contains(&event.position, cx) { + (listener)(event, cx) + } + })); + } + pub fn on_any_mouse_up( &mut self, listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static, @@ -384,6 +408,14 @@ pub trait InteractiveElement: Sized { self } + fn capture_any_mouse_down( + mut self, + listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static, + ) -> Self { + self.interactivity().capture_any_mouse_down(listener); + self + } + fn on_any_mouse_down( mut self, listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static, @@ -401,6 +433,14 @@ pub trait InteractiveElement: Sized { self } + fn capture_any_mouse_up( + mut self, + listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static, + ) -> Self { + self.interactivity().capture_any_mouse_up(listener); + self + } + fn on_mouse_down_out( mut self, listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static, diff --git a/crates/gpui2/src/elements/text.rs b/crates/gpui2/src/elements/text.rs index 175a79c19a512832c7c70571780adb6268b91ab2..7716d81536117a6213133db7cdf8377f94f1f647 100644 --- a/crates/gpui2/src/elements/text.rs +++ b/crates/gpui2/src/elements/text.rs @@ -358,14 +358,13 @@ impl Element for InteractiveText { fn paint(&mut self, bounds: Bounds, state: &mut Self::State, cx: &mut WindowContext) { if let Some(click_listener) = self.click_listener.take() { - if let Some(ix) = state - .text_state - .index_for_position(bounds, cx.mouse_position()) - { + let mouse_position = cx.mouse_position(); + if let Some(ix) = state.text_state.index_for_position(bounds, mouse_position) { if self .clickable_ranges .iter() .any(|range| range.contains(&ix)) + && cx.was_top_layer(&mouse_position, cx.stacking_order()) { cx.set_cursor_style(crate::CursorStyle::PointingHand) } diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index cc435bf0b2463c311f8b0833003c054ee4341f27..07be281f0aafede051b21ef9f4486fd3d8525246 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -273,6 +273,7 @@ pub struct Window { pub(crate) drawing: bool, activation_observers: SubscriberSet<(), AnyObserver>, pub(crate) focus: Option, + focus_enabled: bool, #[cfg(any(test, feature = "test-support"))] pub(crate) focus_invalidated: bool, @@ -420,6 +421,7 @@ impl Window { drawing: false, activation_observers: SubscriberSet::new(), focus: None, + focus_enabled: true, #[cfg(any(test, feature = "test-support"))] focus_invalidated: false, @@ -496,7 +498,7 @@ impl<'a> WindowContext<'a> { /// Move focus to the element associated with the given `FocusHandle`. pub fn focus(&mut self, handle: &FocusHandle) { - if self.window.focus == Some(handle.id) { + if !self.window.focus_enabled || self.window.focus == Some(handle.id) { return; } @@ -516,10 +518,19 @@ impl<'a> WindowContext<'a> { /// Remove focus from all elements within this context's window. pub fn blur(&mut self) { + if !self.window.focus_enabled { + return; + } + self.window.focus = None; self.notify(); } + pub fn disable_focus(&mut self) { + self.blur(); + self.window.focus_enabled = false; + } + pub fn dispatch_action(&mut self, action: Box) { let focus_handle = self.focused(); diff --git a/crates/workspace2/src/pane_group.rs b/crates/workspace2/src/pane_group.rs index 7416b72331b094ceeba92c0f32971c93d6dca33a..06bd506e56e68b0a6932c18f3cb1ee190f45ab40 100644 --- a/crates/workspace2/src/pane_group.rs +++ b/crates/workspace2/src/pane_group.rs @@ -564,9 +564,9 @@ mod element { use std::{cell::RefCell, iter, rc::Rc, sync::Arc}; use gpui::{ - px, relative, Along, AnyElement, Axis, Bounds, CursorStyle, Element, IntoElement, - MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Size, Style, - WindowContext, + px, relative, Along, AnyElement, Axis, Bounds, CursorStyle, Element, InteractiveBounds, + IntoElement, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, + Size, Style, WindowContext, }; use parking_lot::Mutex; use smallvec::SmallVec; @@ -717,7 +717,11 @@ mod element { }; cx.with_z_index(3, |cx| { - if handle_bounds.contains(&cx.mouse_position()) { + let interactive_handle_bounds = InteractiveBounds { + bounds: handle_bounds, + stacking_order: cx.stacking_order().clone(), + }; + if interactive_handle_bounds.visibly_contains(&cx.mouse_position(), cx) { cx.set_cursor_style(match axis { Axis::Vertical => CursorStyle::ResizeUpDown, Axis::Horizontal => CursorStyle::ResizeLeftRight, diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index 95b05f314e73b7bffdd798423fca5d599fe965eb..797e95088d89970f5759efee28b14e5237325f3b 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -25,12 +25,13 @@ use futures::{ Future, FutureExt, StreamExt, }; use gpui::{ - actions, canvas, div, impl_actions, point, size, Action, AnyModel, AnyView, AnyWeakView, - AnyWindowHandle, AppContext, AsyncAppContext, AsyncWindowContext, Bounds, Context, Div, - DragMoveEvent, Entity, EntityId, EventEmitter, FocusHandle, FocusableView, GlobalPixels, - InteractiveElement, KeyContext, ManagedView, Model, ModelContext, ParentElement, - PathPromptOptions, Pixels, Point, PromptLevel, Render, Size, Styled, Subscription, Task, View, - ViewContext, VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions, + actions, canvas, div, impl_actions, point, size, Action, AnyElement, AnyModel, AnyView, + AnyWeakView, AnyWindowHandle, AppContext, AsyncAppContext, AsyncWindowContext, BorrowWindow, + Bounds, Context, Div, DragMoveEvent, Element, Entity, EntityId, EventEmitter, FocusHandle, + FocusableView, GlobalPixels, InteractiveElement, IntoElement, KeyContext, LayoutId, + ManagedView, Model, ModelContext, ParentElement, PathPromptOptions, Pixels, Point, PromptLevel, + Render, Size, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, + WindowBounds, WindowContext, WindowHandle, WindowOptions, }; use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem}; use itertools::Itertools; @@ -64,6 +65,7 @@ use std::{ use theme::{ActiveTheme, ThemeSettings}; pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView}; pub use ui; +use ui::Label; use util::ResultExt; use uuid::Uuid; pub use workspace_settings::{AutosaveSetting, WorkspaceSettings}; @@ -502,7 +504,7 @@ impl Workspace { project::Event::DisconnectedFromHost => { this.update_window_edited(cx); - cx.blur(); + cx.disable_focus(); } project::Event::Closed => { @@ -2519,32 +2521,6 @@ impl Workspace { } } - // fn render_disconnected_overlay( - // &self, - // cx: &mut ViewContext, - // ) -> Option> { - // if self.project.read(cx).is_read_only() { - // enum DisconnectedOverlay {} - // Some( - // MouseEventHandler::new::(0, cx, |_, cx| { - // let theme = &theme::current(cx); - // Label::new( - // "Your connection to the remote project has been lost.", - // theme.workspace.disconnected_overlay.text.clone(), - // ) - // .aligned() - // .contained() - // .with_style(theme.workspace.disconnected_overlay.container) - // }) - // .with_cursor_style(CursorStyle::Arrow) - // .capture_all() - // .into_any_named("disconnected overlay"), - // ) - // } else { - // None - // } - // } - fn render_notifications(&self, _cx: &ViewContext) -> Option
{ if self.notifications.is_empty() { None @@ -3661,6 +3637,11 @@ impl Render for Workspace { })), ) .child(self.status_bar.clone()) + .children(if self.project.read(cx).is_read_only() { + Some(DisconnectedOverlay) + } else { + None + }) } } @@ -4284,6 +4265,56 @@ fn parse_pixel_size_env_var(value: &str) -> Option> { Some(size((width as f64).into(), (height as f64).into())) } +struct DisconnectedOverlay; + +impl Element for DisconnectedOverlay { + type State = AnyElement; + + fn layout( + &mut self, + _: Option, + cx: &mut WindowContext, + ) -> (LayoutId, Self::State) { + let mut background = cx.theme().colors().elevated_surface_background; + background.fade_out(0.2); + let mut overlay = div() + .bg(background) + .absolute() + .left_0() + .top_0() + .size_full() + .flex() + .items_center() + .justify_center() + .capture_any_mouse_down(|_, cx| cx.stop_propagation()) + .capture_any_mouse_up(|_, cx| cx.stop_propagation()) + .child(Label::new( + "Your connection to the remote project has been lost.", + )) + .into_any(); + (overlay.layout(cx), overlay) + } + + fn paint(&mut self, bounds: Bounds, overlay: &mut Self::State, cx: &mut WindowContext) { + cx.with_z_index(u8::MAX, |cx| { + cx.add_opaque_layer(bounds); + overlay.paint(cx); + }) + } +} + +impl IntoElement for DisconnectedOverlay { + type Element = Self; + + fn element_id(&self) -> Option { + None + } + + fn into_element(self) -> Self::Element { + self + } +} + #[cfg(test)] mod tests { use std::{cell::RefCell, rc::Rc};