Skip to main content

gpui_platform/
popup.rs

1//! Parent-anchored popup window options shared by `gpui` and its platform backends.
2
3use crate::WindowId;
4use bitflags::bitflags;
5use gpui_types::{Bounds, Pixels, Point};
6use thiserror::Error;
7
8/// Options for a parent-anchored popup window such as a menu, dropdown, context menu or tooltip.
9///
10/// A popup is placed relative to an anchor rectangle on its parent window rather than at an
11/// absolute screen position. The platform resolves the final position, so this works both on
12/// systems where the compositor owns window placement (Wayland) and on platforms with absolute
13/// coordinates.
14///
15/// The popup's size comes from [`WindowOptions::window_bounds`](crate::WindowOptions), whose
16/// origin is ignored. All coordinates are in logical pixels.
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct PopupOptions {
19    /// The window the popup is anchored to.
20    pub parent: WindowId,
21
22    /// The rectangle the popup is positioned relative to, in the parent window's coordinate
23    /// space (the same space element bounds are in). For example, a dropdown menu uses the
24    /// bounds of the button that opened it.
25    pub anchor_rect: Bounds<Pixels>,
26
27    /// Which point of [`Self::anchor_rect`] the popup is anchored to.
28    pub anchor: PopupAnchor,
29
30    /// The direction in which the popup extends away from the anchor point. A dropdown that
31    /// drops below its button anchors to [`PopupAnchor::BottomLeft`] with a gravity of
32    /// [`PopupGravity::BottomRight`] so it grows down and to the right.
33    pub gravity: PopupGravity,
34
35    /// How the platform may adjust the popup if the requested placement would put it off-screen.
36    pub constraint_adjustment: PopupConstraintAdjustment,
37
38    /// An additional offset applied to the popup after anchoring.
39    pub offset: Point<Pixels>,
40
41    /// Whether the popup should take an explicit input grab.
42    ///
43    /// Grabbing popups behave like menus: they take keyboard focus and are dismissed when the
44    /// user clicks outside of them or presses a dismissing key. Use it for menus and comboboxes,
45    /// not for tooltips or other passive popups.
46    ///
47    /// A grab must be requested while the triggering input is still active, in practice the
48    /// press of the mouse button that opens the popup. Open grabbing popups from a mouse-down
49    /// handler rather than a click handler, otherwise the grab is refused.
50    ///
51    /// Automatic dismissal only covers input aimed at other applications. A click elsewhere in
52    /// your own application still reaches it as usual, so closing the popup in that case is up
53    /// to you. Nested grabbing popups must be closed in the reverse order they were opened.
54    pub grab: bool,
55}
56
57/// The point of the anchor rectangle that a popup is anchored to.
58#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
59pub enum PopupAnchor {
60    /// Anchor to the center of the anchor rectangle.
61    #[default]
62    Center,
63    /// Anchor to the center of the top edge.
64    Top,
65    /// Anchor to the center of the bottom edge.
66    Bottom,
67    /// Anchor to the center of the left edge.
68    Left,
69    /// Anchor to the center of the right edge.
70    Right,
71    /// Anchor to the top-left corner.
72    TopLeft,
73    /// Anchor to the bottom-left corner.
74    BottomLeft,
75    /// Anchor to the top-right corner.
76    TopRight,
77    /// Anchor to the bottom-right corner.
78    BottomRight,
79}
80
81/// The direction in which a popup extends away from its anchor point.
82///
83/// For instance, a gravity of [`PopupGravity::BottomRight`] places the popup below and to the
84/// right of the anchor point.
85#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
86pub enum PopupGravity {
87    /// The popup is centered over the anchor point.
88    #[default]
89    Center,
90    /// The popup extends upwards from the anchor point.
91    Top,
92    /// The popup extends downwards from the anchor point.
93    Bottom,
94    /// The popup extends to the left of the anchor point.
95    Left,
96    /// The popup extends to the right of the anchor point.
97    Right,
98    /// The popup extends up and to the left of the anchor point.
99    TopLeft,
100    /// The popup extends down and to the left of the anchor point.
101    BottomLeft,
102    /// The popup extends up and to the right of the anchor point.
103    TopRight,
104    /// The popup extends down and to the right of the anchor point.
105    BottomRight,
106}
107
108bitflags! {
109    /// How a popup may be adjusted by the platform if the requested placement would put it
110    /// off-screen. If no flags are set, the popup is placed exactly as requested and may be
111    /// clipped.
112    #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
113    pub struct PopupConstraintAdjustment: u32 {
114        /// The popup may be slid horizontally to stay on-screen.
115        const SLIDE_X = 1;
116        /// The popup may be slid vertically to stay on-screen.
117        const SLIDE_Y = 2;
118        /// The popup's anchor and gravity may be flipped horizontally to stay on-screen.
119        const FLIP_X = 4;
120        /// The popup's anchor and gravity may be flipped vertically to stay on-screen.
121        const FLIP_Y = 8;
122        /// The popup may be shrunk horizontally to stay on-screen.
123        const RESIZE_X = 16;
124        /// The popup may be shrunk vertically to stay on-screen.
125        const RESIZE_Y = 32;
126    }
127}
128
129/// Returned when the current platform has no native popup implementation yet.
130///
131/// Native popups are separate from gpui's in-window popovers, which are drawn as elements inside
132/// an existing window. A caller that wants a popup on every platform should treat this error as
133/// a cue to fall back to that in-window rendering.
134#[derive(Debug, Error)]
135#[error("popups are not supported on this platform")]
136pub struct PopupNotSupportedError;