1#![allow(missing_docs)]
2
3use crate::prelude::*;
4use crate::v_flex;
5use gpui::{
6 div, AnyElement, App, Element, IntoElement, ParentElement, Pixels, RenderOnce, Styled, Window,
7};
8use smallvec::SmallVec;
9
10/// Y height added beyond the size of the contents.
11pub const POPOVER_Y_PADDING: Pixels = px(8.);
12
13/// A popover is used to display a menu or show some options.
14///
15/// Clicking the element that launches the popover should not change the current view,
16/// and the popover should be statically positioned relative to that element (not the
17/// user's mouse.)
18///
19/// Example: A "new" menu with options like "new file", "new folder", etc,
20/// Linear's "Display" menu, a profile menu that appears when you click your avatar.
21///
22/// Related elements:
23///
24/// [`ContextMenu`](crate::ContextMenu):
25///
26/// Used to display a popover menu that only contains a list of items. Context menus are always
27/// launched by secondary clicking on an element. The menu is positioned relative to the user's cursor.
28///
29/// Example: Right clicking a file in the file tree to get a list of actions, right clicking
30/// a tab to in the tab bar to get a list of actions.
31///
32/// `Dropdown`:
33///
34/// Used to display a list of options when the user clicks an element. The menu is
35/// positioned relative the element that was clicked, and clicking an item in the
36/// dropdown should change the value of the element that was clicked.
37///
38/// Example: A theme select control. Displays "One Dark", clicking it opens a list of themes.
39/// When one is selected, the theme select control displays the selected theme.
40#[derive(IntoElement)]
41pub struct Popover {
42 children: SmallVec<[AnyElement; 2]>,
43 aside: Option<AnyElement>,
44}
45
46impl RenderOnce for Popover {
47 fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
48 div()
49 .flex()
50 .gap_1()
51 .child(
52 v_flex()
53 .elevation_2(cx)
54 .py(POPOVER_Y_PADDING / 2.)
55 .children(self.children),
56 )
57 .when_some(self.aside, |this, aside| {
58 this.child(
59 v_flex()
60 .elevation_2(cx)
61 .bg(cx.theme().colors().surface_background)
62 .px_1()
63 .child(aside),
64 )
65 })
66 }
67}
68
69impl Default for Popover {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75impl Popover {
76 pub fn new() -> Self {
77 Self {
78 children: SmallVec::new(),
79 aside: None,
80 }
81 }
82
83 pub fn aside(mut self, aside: impl IntoElement) -> Self
84 where
85 Self: Sized,
86 {
87 self.aside = Some(aside.into_element().into_any());
88 self
89 }
90}
91
92impl ParentElement for Popover {
93 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
94 self.children.extend(elements)
95 }
96}