From 47aec5e64d6a2196f95a097652e2d10b192f3cc8 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Sat, 7 Sep 2024 09:05:57 +0800 Subject: [PATCH] Improve popup menu to leave some margin with window edges (#17159) Release Notes: - Improved popup menu to leave some margin with window edges. ## Updates in GPUI - gpui: Add `snap_to_window_with_margin` method to `anchored` to support leave margin to window edges. ## Before before-snap-to-window 2024-08-30 222506 ## After snap-to-window1 2024-08-30 222506 snap-to-window 2024-08-30 222506 --- crates/editor/src/element.rs | 2 +- crates/gpui/src/elements/anchored.rs | 33 ++++++++++++++------ crates/gpui/src/geometry.rs | 15 ++++++--- crates/ui/src/components/popover_menu.rs | 4 ++- crates/ui/src/components/right_click_menu.rs | 4 +-- 5 files changed, 40 insertions(+), 18 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index a1c7b24c7a..d4f5c565c2 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -2732,7 +2732,7 @@ impl EditorElement { .position(position) .child(context_menu) .anchor(AnchorCorner::TopLeft) - .snap_to_window(), + .snap_to_window_with_margin(px(8.)), ) .with_priority(1) .into_any(), diff --git a/crates/gpui/src/elements/anchored.rs b/crates/gpui/src/elements/anchored.rs index ed68e3e40d..7872ba6349 100644 --- a/crates/gpui/src/elements/anchored.rs +++ b/crates/gpui/src/elements/anchored.rs @@ -2,8 +2,8 @@ use smallvec::SmallVec; use taffy::style::{Display, Position}; use crate::{ - point, AnyElement, Bounds, Element, GlobalElementId, IntoElement, LayoutId, ParentElement, - Pixels, Point, Size, Style, WindowContext, + point, AnyElement, Bounds, Edges, Element, GlobalElementId, IntoElement, LayoutId, + ParentElement, Pixels, Point, Size, Style, WindowContext, }; /// The state that the anchored element element uses to track its children. @@ -60,6 +60,12 @@ impl Anchored { self.fit_mode = AnchoredFitMode::SnapToWindow; self } + + /// Snap to window edge and leave some margins. + pub fn snap_to_window_with_margin(mut self, edges: impl Into>) -> Self { + self.fit_mode = AnchoredFitMode::SnapToWindowWithMargin(edges.into()); + self + } } impl ParentElement for Anchored { @@ -153,22 +159,27 @@ impl Element for Anchored { } } + let edges = match self.fit_mode { + AnchoredFitMode::SnapToWindowWithMargin(edges) => edges, + _ => Edges::default(), + }; + // Snap the horizontal edges of the anchored element to the horizontal edges of the window if // its horizontal bounds overflow, aligning to the left if it is wider than the limits. if desired.right() > limits.right() { - desired.origin.x -= desired.right() - limits.right(); + desired.origin.x -= desired.right() - limits.right() + edges.right; } if desired.left() < limits.left() { - desired.origin.x = limits.origin.x; + desired.origin.x = limits.origin.x + edges.left; } // Snap the vertical edges of the anchored element to the vertical edges of the window if // its vertical bounds overflow, aligning to the top if it is taller than the limits. if desired.bottom() > limits.bottom() { - desired.origin.y -= desired.bottom() - limits.bottom(); + desired.origin.y -= desired.bottom() - limits.bottom() + edges.bottom; } if desired.top() < limits.top() { - desired.origin.y = limits.origin.y; + desired.origin.y = limits.origin.y + edges.top; } let offset = desired.origin - bounds.origin; @@ -211,18 +222,20 @@ enum Axis { /// Which algorithm to use when fitting the anchored element to be inside the window. #[derive(Copy, Clone, PartialEq)] pub enum AnchoredFitMode { - /// Snap the anchored element to the window edge + /// Snap the anchored element to the window edge. SnapToWindow, - /// Switch which corner anchor this anchored element is attached to + /// Snap to window edge and leave some margins. + SnapToWindowWithMargin(Edges), + /// Switch which corner anchor this anchored element is attached to. SwitchAnchor, } /// Which algorithm to use when positioning the anchored element. #[derive(Copy, Clone, PartialEq)] pub enum AnchoredPositionMode { - /// Position the anchored element relative to the window + /// Position the anchored element relative to the window. Window, - /// Position the anchored element relative to its parent + /// Position the anchored element relative to its parent. Local, } diff --git a/crates/gpui/src/geometry.rs b/crates/gpui/src/geometry.rs index eb04756cef..8de9e6f009 100644 --- a/crates/gpui/src/geometry.rs +++ b/crates/gpui/src/geometry.rs @@ -1836,11 +1836,18 @@ impl Edges { impl From for Edges { fn from(val: f32) -> Self { + let val: Pixels = val.into(); + val.into() + } +} + +impl From for Edges { + fn from(val: Pixels) -> Self { Edges { - top: val.into(), - right: val.into(), - bottom: val.into(), - left: val.into(), + top: val, + right: val, + bottom: val, + left: val, } } } diff --git a/crates/ui/src/components/popover_menu.rs b/crates/ui/src/components/popover_menu.rs index 11768d19f7..02604b2cc6 100644 --- a/crates/ui/src/components/popover_menu.rs +++ b/crates/ui/src/components/popover_menu.rs @@ -252,7 +252,9 @@ impl Element for PopoverMenu { let mut menu_layout_id = None; let menu_element = element_state.menu.borrow_mut().as_mut().map(|menu| { - let mut anchored = anchored().snap_to_window().anchor(self.anchor); + let mut anchored = anchored() + .snap_to_window_with_margin(px(8.)) + .anchor(self.anchor); if let Some(child_bounds) = element_state.child_bounds { anchored = anchored.position( self.resolved_attach().corner(child_bounds) + self.resolved_offset(cx), diff --git a/crates/ui/src/components/right_click_menu.rs b/crates/ui/src/components/right_click_menu.rs index 25481252b3..c340a4ad89 100644 --- a/crates/ui/src/components/right_click_menu.rs +++ b/crates/ui/src/components/right_click_menu.rs @@ -1,7 +1,7 @@ use std::{cell::RefCell, rc::Rc}; use gpui::{ - anchored, deferred, div, AnchorCorner, AnyElement, Bounds, DismissEvent, DispatchPhase, + anchored, deferred, div, px, AnchorCorner, AnyElement, Bounds, DismissEvent, DispatchPhase, Element, ElementId, GlobalElementId, Hitbox, InteractiveElement, IntoElement, LayoutId, ManagedView, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext, WindowContext, @@ -118,7 +118,7 @@ impl Element for RightClickMenu { let mut menu_layout_id = None; let menu_element = element_state.menu.borrow_mut().as_mut().map(|menu| { - let mut anchored = anchored().snap_to_window(); + let mut anchored = anchored().snap_to_window_with_margin(px(8.)); if let Some(anchor) = this.anchor { anchored = anchored.anchor(anchor); }