Show add participant popover on click

Antonio Scandurra created

Change summary

crates/collab_titlebar_item/src/add_participant_popover.rs | 36 +++
crates/collab_titlebar_item/src/collab_titlebar_item.rs    | 89 ++++---
crates/theme/src/theme.rs                                  | 11 
styles/src/styleTree/workspace.ts                          | 16 +
4 files changed, 114 insertions(+), 38 deletions(-)

Detailed changes

crates/collab_titlebar_item/src/add_participant_popover.rs 🔗

@@ -0,0 +1,36 @@
+use gpui::{elements::*, Entity, RenderContext, View};
+use settings::Settings;
+
+pub struct AddParticipantPopover {}
+
+impl Entity for AddParticipantPopover {
+    type Event = ();
+}
+
+impl View for AddParticipantPopover {
+    fn ui_name() -> &'static str {
+        "AddParticipantPopover"
+    }
+
+    fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
+        let theme = &cx
+            .global::<Settings>()
+            .theme
+            .workspace
+            .titlebar
+            .add_participant_popover;
+        Empty::new()
+            .contained()
+            .with_style(theme.container)
+            .constrained()
+            .with_width(theme.width)
+            .with_height(theme.height)
+            .boxed()
+    }
+}
+
+impl AddParticipantPopover {
+    pub fn new() -> Self {
+        Self {}
+    }
+}

crates/collab_titlebar_item/src/collab_titlebar_item.rs 🔗

@@ -1,10 +1,13 @@
+mod add_participant_popover;
+
+use add_participant_popover::AddParticipantPopover;
 use client::{Authenticate, PeerId};
 use clock::ReplicaId;
 use gpui::{
+    actions,
     color::Color,
     elements::*,
     geometry::{rect::RectF, vector::vec2f, PathBuilder},
-    impl_internal_actions,
     json::{self, ToJson},
     Border, CursorStyle, Entity, ImageData, MouseButton, MutableAppContext, RenderContext,
     Subscription, View, ViewContext, ViewHandle, WeakViewHandle,
@@ -14,19 +17,15 @@ use std::{ops::Range, sync::Arc};
 use theme::Theme;
 use workspace::{FollowNextCollaborator, ToggleFollow, Workspace};
 
-impl_internal_actions!(contacts_titlebar_item, [ToggleAddParticipantPopover]);
+actions!(contacts_titlebar_item, [ToggleAddParticipantPopover]);
 
 pub fn init(cx: &mut MutableAppContext) {
     cx.add_action(CollabTitlebarItem::toggle_add_participant_popover);
 }
 
-#[derive(Clone, PartialEq)]
-struct ToggleAddParticipantPopover {
-    button_rect: RectF,
-}
-
 pub struct CollabTitlebarItem {
     workspace: WeakViewHandle<Workspace>,
+    add_participant_popover: Option<ViewHandle<AddParticipantPopover>>,
     _subscriptions: Vec<Subscription>,
 }
 
@@ -61,16 +60,24 @@ impl CollabTitlebarItem {
         let observe_workspace = cx.observe(workspace, |_, _, cx| cx.notify());
         Self {
             workspace: workspace.downgrade(),
+            add_participant_popover: None,
             _subscriptions: vec![observe_workspace],
         }
     }
 
     fn toggle_add_participant_popover(
         &mut self,
-        _action: &ToggleAddParticipantPopover,
-        _cx: &mut ViewContext<Self>,
+        _: &ToggleAddParticipantPopover,
+        cx: &mut ViewContext<Self>,
     ) {
-        dbg!("!!!!!!!!!");
+        match self.add_participant_popover.take() {
+            Some(_) => {}
+            None => {
+                let view = cx.add_view(|_| AddParticipantPopover::new());
+                self.add_participant_popover = Some(view);
+            }
+        }
+        cx.notify();
     }
 
     fn render_toggle_contacts_button(
@@ -83,33 +90,47 @@ impl CollabTitlebarItem {
             return None;
         }
 
+        let titlebar = &theme.workspace.titlebar;
+
         Some(
-            MouseEventHandler::<ToggleAddParticipantPopover>::new(0, cx, |state, _| {
-                let style = theme
-                    .workspace
-                    .titlebar
-                    .add_collaborator_button
-                    .style_for(state, false);
-                Svg::new("icons/plus_8.svg")
-                    .with_color(style.color)
-                    .constrained()
-                    .with_width(style.icon_width)
+            Stack::new()
+                .with_child(
+                    MouseEventHandler::<ToggleAddParticipantPopover>::new(0, cx, |state, _| {
+                        let style = titlebar.add_participant_button.style_for(state, false);
+                        Svg::new("icons/plus_8.svg")
+                            .with_color(style.color)
+                            .constrained()
+                            .with_width(style.icon_width)
+                            .aligned()
+                            .constrained()
+                            .with_width(style.button_width)
+                            .with_height(style.button_width)
+                            .contained()
+                            .with_style(style.container)
+                            .boxed()
+                    })
+                    .with_cursor_style(CursorStyle::PointingHand)
+                    .on_click(MouseButton::Left, |_, cx| {
+                        cx.dispatch_action(ToggleAddParticipantPopover);
+                    })
                     .aligned()
-                    .constrained()
-                    .with_width(style.button_width)
-                    .with_height(style.button_width)
-                    .contained()
-                    .with_style(style.container)
+                    .boxed(),
+                )
+                .with_children(self.add_participant_popover.as_ref().map(|popover| {
+                    Overlay::new(
+                        ChildView::new(popover)
+                            .contained()
+                            .with_margin_top(titlebar.height)
+                            .with_margin_right(
+                                -titlebar.add_participant_button.default.button_width,
+                            )
+                            .boxed(),
+                    )
+                    .with_fit_mode(OverlayFitMode::SwitchAnchor)
+                    .with_anchor_corner(AnchorCorner::BottomLeft)
                     .boxed()
-            })
-            .with_cursor_style(CursorStyle::PointingHand)
-            .on_click(MouseButton::Left, |event, cx| {
-                cx.dispatch_action(ToggleAddParticipantPopover {
-                    button_rect: event.region,
-                });
-            })
-            .aligned()
-            .boxed(),
+                }))
+                .boxed(),
         )
     }
 

crates/theme/src/theme.rs 🔗

@@ -74,7 +74,16 @@ pub struct Titlebar {
     pub avatar: ImageStyle,
     pub sign_in_prompt: Interactive<ContainedText>,
     pub outdated_warning: ContainedText,
-    pub add_collaborator_button: Interactive<IconButton>,
+    pub add_participant_button: Interactive<IconButton>,
+    pub add_participant_popover: AddParticipantPopover,
+}
+
+#[derive(Clone, Deserialize, Default)]
+pub struct AddParticipantPopover {
+    #[serde(flatten)]
+    pub container: ContainerStyle,
+    pub height: f32,
+    pub width: f32,
 }
 
 #[derive(Clone, Deserialize, Default)]

styles/src/styleTree/workspace.ts 🔗

@@ -5,6 +5,7 @@ import {
   border,
   iconColor,
   modalShadow,
+  popoverShadow,
   text,
 } from "./components";
 import statusBar from "./statusBar";
@@ -16,7 +17,6 @@ export function workspaceBackground(theme: Theme) {
 
 export default function workspace(theme: Theme) {
   const titlebarPadding = 6;
-  const titlebarHeight = 33;
 
   return {
     background: backgroundColor(theme, 300),
@@ -55,7 +55,7 @@ export default function workspace(theme: Theme) {
     titlebar: {
       avatarWidth: 18,
       avatarMargin: 8,
-      height: titlebarHeight,
+      height: 33,
       background: backgroundColor(theme, 100),
       padding: {
         left: 80,
@@ -119,7 +119,7 @@ export default function workspace(theme: Theme) {
         },
         cornerRadius: 6,
       },
-      addCollaboratorButton: {
+      addParticipantButton: {
         cornerRadius: 6,
         color: iconColor(theme, "secondary"),
         iconWidth: 8,
@@ -129,6 +129,16 @@ export default function workspace(theme: Theme) {
           color: iconColor(theme, "active"),
         },
       },
+      addParticipantPopover: {
+        background: backgroundColor(theme, 300, "base"),
+        cornerRadius: 6,
+        padding: 6,
+        shadow: popoverShadow(theme),
+        border: border(theme, "primary"),
+        margin: { top: -5 },
+        width: 255,
+        height: 200
+      }
     },
     toolbar: {
       height: 34,