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;