WIP update popover_menu

Nate Butler created

Change summary

crates/collab_ui2/src/collab_titlebar_item.rs | 41 ++++++-----
crates/ui2/src/components/popover_menu.rs     | 71 +++++++++++++++++---
2 files changed, 81 insertions(+), 31 deletions(-)

Detailed changes

crates/collab_ui2/src/collab_titlebar_item.rs 🔗

@@ -39,7 +39,7 @@ use project::{Project, RepositoryEntry};
 use theme::ActiveTheme;
 use ui::{
     h_stack, prelude::*, Avatar, Button, ButtonLike, ButtonStyle2, Icon, IconButton, IconElement,
-    KeyBinding, Tooltip,
+    KeyBinding, PopoverMenu, Tooltip,
 };
 use util::ResultExt;
 use workspace::{notifications::NotifyResultExt, Workspace};
@@ -289,28 +289,29 @@ impl Render for CollabTitlebarItem {
                     this.when_some(user.avatar.clone(), |this, avatar| {
                         // TODO: Finish implementing user menu popover
                         //
-                        // this.child(
-                        //     PopoverMenu::new(
-                        //         ButtonLike::new("user-menu")
-                        //             .child(h_stack().gap_0p5().child(Avatar::data(avatar)).child(
-                        //                 IconElement::new(Icon::ChevronDown).color(Color::Muted),
-                        //             ))
-                        //             .style(ButtonStyle2::Subtle)
-                        //             .tooltip(move |cx| Tooltip::text("Toggle User Menu", cx))
-                        //             .into_any_element(),
-                        //     )
-                        //     .children(vec![div().w_96().h_96().bg(gpui::red())]),
-                        // )
                         this.child(
-                            ButtonLike::new("user-menu")
-                                .child(
-                                    h_stack().gap_0p5().child(Avatar::data(avatar)).child(
+                            PopoverMenu::new(
+                                ButtonLike::new("user-menu")
+                                    .child(h_stack().gap_0p5().child(Avatar::data(avatar)).child(
                                         IconElement::new(Icon::ChevronDown).color(Color::Muted),
-                                    ),
-                                )
-                                .style(ButtonStyle2::Subtle)
-                                .tooltip(move |cx| Tooltip::text("Toggle User Menu", cx)),
+                                    ))
+                                    .style(ButtonStyle2::Subtle)
+                                    .tooltip(move |cx| Tooltip::text("Toggle User Menu", cx))
+                                    .into_any_element(),
+                            )
+                            .anchor(gpui::AnchorCorner::TopRight)
+                            .children(vec![div().w_96().h_96().bg(gpui::red())]),
                         )
+                        // this.child(
+                        //     ButtonLike::new("user-menu")
+                        //         .child(
+                        //             h_stack().gap_0p5().child(Avatar::data(avatar)).child(
+                        //                 IconElement::new(Icon::ChevronDown).color(Color::Muted),
+                        //             ),
+                        //         )
+                        //         .style(ButtonStyle2::Subtle)
+                        //         .tooltip(move |cx| Tooltip::text("Toggle User Menu", cx)),
+                        // )
                     })
                 } else {
                     this.child(Button::new("sign_in", "Sign in").on_click(move |_, cx| {

crates/ui2/src/components/popover_menu.rs 🔗

@@ -1,33 +1,70 @@
-use gpui::{div, overlay, px, AnyElement, Div, ParentElement, RenderOnce, Styled, WindowContext};
+use gpui::{
+    div, overlay, rems, AnchorCorner, AnyElement, Div, ParentElement, RenderOnce, Styled,
+    WindowContext,
+};
 use smallvec::SmallVec;
 
 use crate::{prelude::*, Popover};
 
-// 🚧 Under Construction
-
 #[derive(IntoElement)]
 pub struct PopoverMenu {
+    /// The element that triggers the popover menu when clicked
+    /// Usually a button
     trigger: AnyElement,
+    /// The content of the popover menu
+    /// This will automatically be wrapped in a [Popover] element
     children: SmallVec<[AnyElement; 2]>,
+    /// The direction the popover menu will open by default
+    ///
+    /// When not enough space is available in the default direction,
+    /// the popover menu will follow the rules of [gpui2::elements::overlay]
+    anchor: AnchorCorner,
+    /// Whether the popover menu is currently open
+    show_menu: bool,
 }
 
 impl RenderOnce for PopoverMenu {
     type Rendered = Div;
 
     fn render(self, _cx: &mut WindowContext) -> Self::Rendered {
+        // Default offset = 4px padding + 1px border
+        let offset = 5. / 16.;
+
+        let (top, right, bottom, left) = match self.anchor {
+            AnchorCorner::TopRight => (None, Some(-offset), Some(-offset), None),
+            AnchorCorner::TopLeft => (None, None, Some(-offset), Some(-offset)),
+            AnchorCorner::BottomRight => (Some(-offset), Some(-offset), None, None),
+            AnchorCorner::BottomLeft => (Some(-offset), None, None, Some(-offset)),
+        };
+
         div()
+            .flex()
+            .flex_none()
             .bg(gpui::green())
             .relative()
-            .child(div().bg(gpui::blue()).child(self.trigger))
             .child(
-                overlay()
-                    .position(gpui::Point {
-                        x: px(100.),
-                        y: px(100.),
-                    })
-                    .anchor(gpui::AnchorCorner::TopRight)
-                    .child(Popover::new().children(self.children)),
+                div()
+                    .flex_none()
+                    .relative()
+                    .bg(gpui::blue())
+                    .child(self.trigger),
             )
+            .when(self.show_menu, |this| {
+                this.child(
+                    div()
+                        .absolute()
+                        .size_0()
+                        .when_some(top, |this, t| this.top(rems(t)))
+                        .when_some(right, |this, r| this.right(rems(r)))
+                        .when_some(bottom, |this, b| this.bottom(rems(b)))
+                        .when_some(left, |this, l| this.left(rems(l)))
+                        .child(
+                            overlay()
+                                .anchor(AnchorCorner::TopRight)
+                                .child(Popover::new().children(self.children)),
+                        ),
+                )
+            })
     }
 }
 
@@ -36,8 +73,20 @@ impl PopoverMenu {
         Self {
             trigger,
             children: SmallVec::new(),
+            anchor: AnchorCorner::TopLeft,
+            show_menu: false,
         }
     }
+
+    pub fn anchor(mut self, anchor: AnchorCorner) -> Self {
+        self.anchor = anchor;
+        self
+    }
+
+    pub fn show_menu(mut self, show_menu: bool) -> Self {
+        self.show_menu = show_menu;
+        self
+    }
 }
 
 impl ParentElement for PopoverMenu {