Cargo.lock 🔗
@@ -1257,6 +1257,7 @@ dependencies = [
"client",
"clock",
"collections",
+ "context_menu",
"editor",
"futures 0.3.25",
"fuzzy",
Max Brunsfeld created
* Replace username in titelbar with a `...` user menu that shows
the current user name and contains a sign-in/sign-out button.
* Move the '+' (toggle contacts) button back to the right side.
* Move the collaborators back to the right side.
* Move the share/unshare button to the left side, beside the project title
* Only show the share/unshare button when in a call.
Cargo.lock | 1
assets/icons/ellipsis_14.svg | 3
crates/client/src/client.rs | 12
crates/collab_ui/Cargo.toml | 1
crates/collab_ui/src/collab_titlebar_item.rs | 271 ++++++++++-----------
crates/context_menu/src/context_menu.rs | 45 ++-
crates/theme/src/theme.rs | 1
styles/src/styleTree/workspace.ts | 5
8 files changed, 183 insertions(+), 156 deletions(-)
@@ -1257,6 +1257,7 @@ dependencies = [
"client",
"clock",
"collections",
+ "context_menu",
"editor",
"futures 0.3.25",
"fuzzy",
@@ -0,0 +1,3 @@
+<svg width="14" height="4" viewBox="0 0 14 4" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M3.125 2C3.125 2.62132 2.62132 3.125 2 3.125C1.37868 3.125 0.875 2.62132 0.875 2C0.875 1.37868 1.37868 0.875 2 0.875C2.62132 0.875 3.125 1.37868 3.125 2ZM8.125 2C8.125 2.62132 7.62132 3.125 7 3.125C6.37868 3.125 5.875 2.62132 5.875 2C5.875 1.37868 6.37868 0.875 7 0.875C7.62132 0.875 8.125 1.37868 8.125 2ZM12 3.125C12.6213 3.125 13.125 2.62132 13.125 2C13.125 1.37868 12.6213 0.875 12 0.875C11.3787 0.875 10.875 1.37868 10.875 2C10.875 2.62132 11.3787 3.125 12 3.125Z" fill="#ABB2BF"/>
+</svg>
@@ -66,7 +66,7 @@ pub const ZED_SECRET_CLIENT_TOKEN: &str = "618033988749894";
pub const INITIAL_RECONNECTION_DELAY: Duration = Duration::from_millis(100);
pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(5);
-actions!(client, [Authenticate]);
+actions!(client, [Authenticate, SignOut]);
pub fn init(client: Arc<Client>, cx: &mut MutableAppContext) {
cx.add_global_action({
@@ -79,6 +79,16 @@ pub fn init(client: Arc<Client>, cx: &mut MutableAppContext) {
.detach();
}
});
+ cx.add_global_action({
+ let client = client.clone();
+ move |_: &SignOut, cx| {
+ let client = client.clone();
+ cx.spawn(|cx| async move {
+ client.set_status(Status::SignedOut, &cx);
+ })
+ .detach();
+ }
+ });
}
pub struct Client {
@@ -27,6 +27,7 @@ call = { path = "../call" }
client = { path = "../client" }
clock = { path = "../clock" }
collections = { path = "../collections" }
+context_menu = { path = "../context_menu" }
editor = { path = "../editor" }
fuzzy = { path = "../fuzzy" }
gpui = { path = "../gpui" }
@@ -4,9 +4,10 @@ use crate::{
ToggleScreenSharing,
};
use call::{ActiveCall, ParticipantLocation, Room};
-use client::{proto::PeerId, Authenticate, ContactEventKind, User, UserStore};
+use client::{proto::PeerId, Authenticate, ContactEventKind, SignOut, User, UserStore};
use clock::ReplicaId;
use contacts_popover::ContactsPopover;
+use context_menu::{ContextMenu, ContextMenuItem};
use gpui::{
actions,
color::Color,
@@ -28,8 +29,9 @@ actions!(
[
ToggleCollaboratorList,
ToggleContactsMenu,
+ ToggleUserMenu,
ShareProject,
- UnshareProject
+ UnshareProject,
]
);
@@ -38,25 +40,20 @@ impl_internal_actions!(collab, [LeaveCall]);
#[derive(Copy, Clone, PartialEq)]
pub(crate) struct LeaveCall;
-#[derive(PartialEq, Eq)]
-enum ContactsPopoverSide {
- Left,
- Right,
-}
-
pub fn init(cx: &mut MutableAppContext) {
cx.add_action(CollabTitlebarItem::toggle_collaborator_list_popover);
cx.add_action(CollabTitlebarItem::toggle_contacts_popover);
cx.add_action(CollabTitlebarItem::share_project);
cx.add_action(CollabTitlebarItem::unshare_project);
cx.add_action(CollabTitlebarItem::leave_call);
+ cx.add_action(CollabTitlebarItem::toggle_user_menu);
}
pub struct CollabTitlebarItem {
workspace: WeakViewHandle<Workspace>,
user_store: ModelHandle<UserStore>,
contacts_popover: Option<ViewHandle<ContactsPopover>>,
- contacts_popover_side: ContactsPopoverSide,
+ user_menu: ViewHandle<ContextMenu>,
collaborator_list_popover: Option<ViewHandle<CollaboratorListPopover>>,
_subscriptions: Vec<Subscription>,
}
@@ -93,6 +90,7 @@ impl View for CollabTitlebarItem {
let user = workspace.read(cx).user_store().read(cx).current_user();
let mut left_container = Flex::row();
+ let mut right_container = Flex::row();
left_container.add_child(
Label::new(project_title, theme.workspace.titlebar.title.clone())
@@ -104,40 +102,25 @@ impl View for CollabTitlebarItem {
);
if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
- left_container.add_child(self.render_current_user(&workspace, &theme, &user, cx));
- left_container.add_children(self.render_collaborators(&workspace, &theme, room, cx));
- left_container.add_child(self.render_toggle_contacts_button(&theme, cx));
- }
-
- let mut right_container = Flex::row();
+ left_container
+ .add_children(self.render_in_call_share_unshare_button(&workspace, &theme, cx));
- if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
+ right_container.add_children(self.render_collaborators(&workspace, &theme, &room, cx));
+ right_container.add_child(self.render_current_user(&workspace, &theme, &user, cx));
right_container.add_child(self.render_toggle_screen_sharing_button(&theme, &room, cx));
right_container.add_child(self.render_leave_call_button(&theme, cx));
- right_container
- .add_children(self.render_in_call_share_unshare_button(&workspace, &theme, cx));
- } else {
- right_container.add_child(self.render_outside_call_share_button(&theme, cx));
}
- right_container.add_children(self.render_connection_status(&workspace, cx));
-
- if let Some(user) = user {
- //TODO: Add style
- right_container.add_child(
- Label::new(
- user.github_login.clone(),
- theme.workspace.titlebar.title.clone(),
- )
- .aligned()
- .contained()
- .with_margin_left(theme.workspace.titlebar.item_spacing)
- .boxed(),
- );
+ let status = workspace.read(cx).client().status();
+ let status = &*status.borrow();
+ if matches!(status, client::Status::Connected { .. }) {
+ right_container.add_child(self.render_toggle_contacts_button(&theme, cx));
} else {
- right_container.add_child(Self::render_authenticate(&theme, cx));
+ right_container.add_children(self.render_connection_status(status, cx));
}
+ right_container.add_child(self.render_user_menu_button(&theme, cx));
+
Stack::new()
.with_child(left_container.boxed())
.with_child(right_container.aligned().right().boxed())
@@ -186,7 +169,11 @@ impl CollabTitlebarItem {
workspace: workspace.downgrade(),
user_store: user_store.clone(),
contacts_popover: None,
- contacts_popover_side: ContactsPopoverSide::Right,
+ user_menu: cx.add_view(|cx| {
+ let mut menu = ContextMenu::new(cx);
+ menu.set_position_mode(OverlayPositionMode::Local);
+ menu
+ }),
collaborator_list_popover: None,
_subscriptions: subscriptions,
}
@@ -278,12 +265,6 @@ impl CollabTitlebarItem {
cx.notify();
})
.detach();
-
- self.contacts_popover_side = match ActiveCall::global(cx).read(cx).room() {
- Some(_) => ContactsPopoverSide::Left,
- None => ContactsPopoverSide::Right,
- };
-
self.contacts_popover = Some(view);
}
}
@@ -291,6 +272,44 @@ impl CollabTitlebarItem {
cx.notify();
}
+ pub fn toggle_user_menu(&mut self, _: &ToggleUserMenu, cx: &mut ViewContext<Self>) {
+ let theme = cx.global::<Settings>().theme.clone();
+ let label_style = theme.context_menu.item.disabled_style().label.clone();
+ self.user_menu.update(cx, |user_menu, cx| {
+ let items = if let Some(user) = self.user_store.read(cx).current_user() {
+ vec![
+ ContextMenuItem::Static(Box::new(move |_| {
+ Label::new(user.github_login.clone(), label_style.clone()).boxed()
+ })),
+ ContextMenuItem::Item {
+ label: "Sign out".into(),
+ action: Box::new(SignOut),
+ },
+ ]
+ } else {
+ vec![ContextMenuItem::Item {
+ label: "Sign in".into(),
+ action: Box::new(Authenticate),
+ }]
+ };
+
+ user_menu.show(
+ vec2f(
+ theme
+ .workspace
+ .titlebar
+ .user_menu_button
+ .default
+ .button_width,
+ theme.workspace.titlebar.height,
+ ),
+ AnchorCorner::TopRight,
+ items,
+ cx,
+ );
+ });
+ }
+
fn leave_call(&mut self, _: &LeaveCall, cx: &mut ViewContext<Self>) {
ActiveCall::global(cx)
.update(cx, |call, cx| call.hang_up(cx))
@@ -328,11 +347,9 @@ impl CollabTitlebarItem {
Stack::new()
.with_child(
MouseEventHandler::<ToggleContactsMenu>::new(0, cx, |state, _| {
- let style = titlebar.toggle_contacts_button.style_for(
- state,
- self.contacts_popover.is_some()
- && self.contacts_popover_side == ContactsPopoverSide::Left,
- );
+ let style = titlebar
+ .toggle_contacts_button
+ .style_for(state, self.contacts_popover.is_some());
Svg::new("icons/plus_8.svg")
.with_color(style.color)
.constrained()
@@ -360,11 +377,7 @@ impl CollabTitlebarItem {
.boxed(),
)
.with_children(badge)
- .with_children(self.render_contacts_popover_host(
- ContactsPopoverSide::Left,
- titlebar,
- cx,
- ))
+ .with_children(self.render_contacts_popover_host(titlebar, cx))
.boxed()
}
@@ -475,11 +488,9 @@ impl CollabTitlebarItem {
.with_child(
MouseEventHandler::<ShareUnshare>::new(0, cx, |state, _| {
//TODO: Ensure this button has consistant width for both text variations
- let style = titlebar.share_button.style_for(
- state,
- self.contacts_popover.is_some()
- && self.contacts_popover_side == ContactsPopoverSide::Right,
- );
+ let style = titlebar
+ .share_button
+ .style_for(state, self.contacts_popover.is_some());
Label::new(label, style.text.clone())
.contained()
.with_style(style.container)
@@ -502,11 +513,6 @@ impl CollabTitlebarItem {
)
.boxed(),
)
- .with_children(self.render_contacts_popover_host(
- ContactsPopoverSide::Right,
- titlebar,
- cx,
- ))
.aligned()
.contained()
.with_margin_left(theme.workspace.titlebar.item_spacing)
@@ -514,83 +520,71 @@ impl CollabTitlebarItem {
)
}
- fn render_outside_call_share_button(
- &self,
- theme: &Theme,
- cx: &mut RenderContext<Self>,
- ) -> ElementBox {
- let tooltip = "Share project with new call";
+ fn render_user_menu_button(&self, theme: &Theme, cx: &mut RenderContext<Self>) -> ElementBox {
let titlebar = &theme.workspace.titlebar;
- enum OutsideCallShare {}
Stack::new()
.with_child(
- MouseEventHandler::<OutsideCallShare>::new(0, cx, |state, _| {
- //TODO: Ensure this button has consistant width for both text variations
- let style = titlebar.share_button.style_for(
- state,
- self.contacts_popover.is_some()
- && self.contacts_popover_side == ContactsPopoverSide::Right,
- );
- Label::new("Share".to_owned(), style.text.clone())
+ MouseEventHandler::<ToggleUserMenu>::new(0, cx, |state, _| {
+ let style = titlebar.call_control.style_for(state, false);
+ Svg::new("icons/ellipsis_14.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, move |_, cx| {
- cx.dispatch_action(ToggleContactsMenu);
+ cx.dispatch_action(ToggleUserMenu);
})
- .with_tooltip::<OutsideCallShare, _>(
+ .with_tooltip::<ToggleUserMenu, _>(
0,
- tooltip.to_owned(),
- None,
+ "Toggle user menu".to_owned(),
+ Some(Box::new(ToggleUserMenu)),
theme.tooltip.clone(),
cx,
)
+ .contained()
+ .with_margin_left(theme.workspace.titlebar.item_spacing)
+ .aligned()
.boxed(),
)
- .with_children(self.render_contacts_popover_host(
- ContactsPopoverSide::Right,
- titlebar,
- cx,
- ))
- .aligned()
- .contained()
- .with_margin_left(theme.workspace.titlebar.item_spacing)
+ .with_child(ChildView::new(&self.user_menu, cx).boxed())
.boxed()
}
fn render_contacts_popover_host<'a>(
&'a self,
- side: ContactsPopoverSide,
theme: &'a theme::Titlebar,
cx: &'a RenderContext<Self>,
- ) -> impl Iterator<Item = ElementBox> + 'a {
- self.contacts_popover
- .iter()
- .filter(move |_| self.contacts_popover_side == side)
- .map(|popover| {
- Overlay::new(
- ChildView::new(popover, cx)
- .contained()
- .with_margin_top(theme.height)
- .with_margin_left(theme.toggle_contacts_button.default.button_width)
- .with_margin_right(-theme.toggle_contacts_button.default.button_width)
- .boxed(),
- )
- .with_fit_mode(OverlayFitMode::SwitchAnchor)
- .with_anchor_corner(AnchorCorner::BottomLeft)
- .with_z_index(999)
- .boxed()
- })
+ ) -> Option<ElementBox> {
+ self.contacts_popover.as_ref().map(|popover| {
+ Overlay::new(
+ ChildView::new(popover, cx)
+ .contained()
+ .with_margin_top(theme.height)
+ .with_margin_left(theme.toggle_contacts_button.default.button_width)
+ .with_margin_right(-theme.toggle_contacts_button.default.button_width)
+ .boxed(),
+ )
+ .with_fit_mode(OverlayFitMode::SwitchAnchor)
+ .with_anchor_corner(AnchorCorner::BottomLeft)
+ .with_z_index(999)
+ .boxed()
+ })
}
fn render_collaborators(
&self,
workspace: &ViewHandle<Workspace>,
theme: &Theme,
- room: ModelHandle<Room>,
+ room: &ModelHandle<Room>,
cx: &mut RenderContext<Self>,
) -> Vec<ElementBox> {
let project = workspace.read(cx).project().read(cx);
@@ -622,7 +616,7 @@ impl CollabTitlebarItem {
theme,
cx,
))
- .with_margin_left(theme.workspace.titlebar.face_pile_spacing)
+ .with_margin_right(theme.workspace.titlebar.face_pile_spacing)
.boxed(),
)
})
@@ -643,25 +637,16 @@ impl CollabTitlebarItem {
.client()
.peer_id()
.expect("Active call without peer id");
- self.render_face_pile(user, Some(replica_id), peer_id, None, workspace, theme, cx)
- }
-
- fn render_authenticate(theme: &Theme, cx: &mut RenderContext<Self>) -> ElementBox {
- MouseEventHandler::<Authenticate>::new(0, cx, |state, _| {
- let style = theme
- .workspace
- .titlebar
- .sign_in_prompt
- .style_for(state, false);
- Label::new("Sign in", style.text.clone())
- .contained()
- .with_style(style.container)
- .with_margin_left(theme.workspace.titlebar.item_spacing)
- .boxed()
- })
- .on_click(MouseButton::Left, |_, cx| cx.dispatch_action(Authenticate))
- .with_cursor_style(CursorStyle::PointingHand)
- .aligned()
+ Container::new(self.render_face_pile(
+ user,
+ Some(replica_id),
+ peer_id,
+ None,
+ workspace,
+ theme,
+ cx,
+ ))
+ .with_margin_right(theme.workspace.titlebar.item_spacing)
.boxed()
}
@@ -717,7 +702,7 @@ impl CollabTitlebarItem {
}
}
- let content = Stack::new()
+ let mut content = Stack::new()
.with_children(user.avatar.as_ref().map(|avatar| {
let face_pile = FacePile::new(theme.workspace.titlebar.follower_avatar_overlap)
.with_child(Self::render_face(
@@ -789,7 +774,10 @@ impl CollabTitlebarItem {
if let Some(location) = location {
if let Some(replica_id) = replica_id {
- MouseEventHandler::<ToggleFollow>::new(replica_id.into(), cx, move |_, _| content)
+ content =
+ MouseEventHandler::<ToggleFollow>::new(replica_id.into(), cx, move |_, _| {
+ content
+ })
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, cx| {
cx.dispatch_action(ToggleFollow(peer_id))
@@ -805,12 +793,14 @@ impl CollabTitlebarItem {
theme.tooltip.clone(),
cx,
)
- .boxed()
+ .boxed();
} else if let ParticipantLocation::SharedProject { project_id } = location {
let user_id = user.id;
- MouseEventHandler::<JoinProject>::new(peer_id.as_u64() as usize, cx, move |_, _| {
- content
- })
+ content = MouseEventHandler::<JoinProject>::new(
+ peer_id.as_u64() as usize,
+ cx,
+ move |_, _| content,
+ )
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, cx| {
cx.dispatch_action(JoinProject {
@@ -825,13 +815,10 @@ impl CollabTitlebarItem {
theme.tooltip.clone(),
cx,
)
- .boxed()
- } else {
- content
+ .boxed();
}
- } else {
- content
}
+ content
}
fn render_face(
@@ -854,13 +841,13 @@ impl CollabTitlebarItem {
fn render_connection_status(
&self,
- workspace: &ViewHandle<Workspace>,
+ status: &client::Status,
cx: &mut RenderContext<Self>,
) -> Option<ElementBox> {
enum ConnectionStatusButton {}
let theme = &cx.global::<Settings>().theme.clone();
- match &*workspace.read(cx).client().status().borrow() {
+ match status {
client::Status::ConnectionError
| client::Status::ConnectionLost
| client::Status::Reauthenticating { .. }
@@ -5,7 +5,9 @@ use gpui::{
};
use menu::*;
use settings::Settings;
-use std::{any::TypeId, time::Duration};
+use std::{any::TypeId, borrow::Cow, time::Duration};
+
+pub type StaticItem = Box<dyn Fn(&mut MutableAppContext) -> ElementBox>;
#[derive(Copy, Clone, PartialEq)]
struct Clicked;
@@ -24,16 +26,17 @@ pub fn init(cx: &mut MutableAppContext) {
pub enum ContextMenuItem {
Item {
- label: String,
+ label: Cow<'static, str>,
action: Box<dyn Action>,
},
+ Static(StaticItem),
Separator,
}
impl ContextMenuItem {
- pub fn item(label: impl ToString, action: impl 'static + Action) -> Self {
+ pub fn item(label: impl Into<Cow<'static, str>>, action: impl 'static + Action) -> Self {
Self::Item {
- label: label.to_string(),
+ label: label.into(),
action: Box::new(action),
}
}
@@ -42,14 +45,14 @@ impl ContextMenuItem {
Self::Separator
}
- fn is_separator(&self) -> bool {
- matches!(self, Self::Separator)
+ fn is_action(&self) -> bool {
+ matches!(self, Self::Item { .. })
}
fn action_id(&self) -> Option<TypeId> {
match self {
ContextMenuItem::Item { action, .. } => Some(action.id()),
- ContextMenuItem::Separator => None,
+ ContextMenuItem::Static(..) | ContextMenuItem::Separator => None,
}
}
}
@@ -58,6 +61,7 @@ pub struct ContextMenu {
show_count: usize,
anchor_position: Vector2F,
anchor_corner: AnchorCorner,
+ position_mode: OverlayPositionMode,
items: Vec<ContextMenuItem>,
selected_index: Option<usize>,
visible: bool,
@@ -105,6 +109,7 @@ impl View for ContextMenu {
.with_fit_mode(OverlayFitMode::SnapToWindow)
.with_anchor_position(self.anchor_position)
.with_anchor_corner(self.anchor_corner)
+ .with_position_mode(self.position_mode)
.boxed()
}
@@ -121,6 +126,7 @@ impl ContextMenu {
show_count: 0,
anchor_position: Default::default(),
anchor_corner: AnchorCorner::TopLeft,
+ position_mode: OverlayPositionMode::Window,
items: Default::default(),
selected_index: Default::default(),
visible: Default::default(),
@@ -188,13 +194,13 @@ impl ContextMenu {
}
fn select_first(&mut self, _: &SelectFirst, cx: &mut ViewContext<Self>) {
- self.selected_index = self.items.iter().position(|item| !item.is_separator());
+ self.selected_index = self.items.iter().position(|item| item.is_action());
cx.notify();
}
fn select_last(&mut self, _: &SelectLast, cx: &mut ViewContext<Self>) {
for (ix, item) in self.items.iter().enumerate().rev() {
- if !item.is_separator() {
+ if item.is_action() {
self.selected_index = Some(ix);
cx.notify();
break;
@@ -205,7 +211,7 @@ impl ContextMenu {
fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
if let Some(ix) = self.selected_index {
for (ix, item) in self.items.iter().enumerate().skip(ix + 1) {
- if !item.is_separator() {
+ if item.is_action() {
self.selected_index = Some(ix);
cx.notify();
break;
@@ -219,7 +225,7 @@ impl ContextMenu {
fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext<Self>) {
if let Some(ix) = self.selected_index {
for (ix, item) in self.items.iter().enumerate().take(ix).rev() {
- if !item.is_separator() {
+ if item.is_action() {
self.selected_index = Some(ix);
cx.notify();
break;
@@ -234,7 +240,7 @@ impl ContextMenu {
&mut self,
anchor_position: Vector2F,
anchor_corner: AnchorCorner,
- items: impl IntoIterator<Item = ContextMenuItem>,
+ items: Vec<ContextMenuItem>,
cx: &mut ViewContext<Self>,
) {
let mut items = items.into_iter().peekable();
@@ -254,6 +260,10 @@ impl ContextMenu {
cx.notify();
}
+ pub fn set_position_mode(&mut self, mode: OverlayPositionMode) {
+ self.position_mode = mode;
+ }
+
fn render_menu_for_measurement(&self, cx: &mut RenderContext<Self>) -> impl Element {
let window_id = cx.window_id();
let style = cx.global::<Settings>().theme.context_menu.clone();
@@ -273,6 +283,9 @@ impl ContextMenu {
.with_style(style.container)
.boxed()
}
+
+ ContextMenuItem::Static(f) => f(cx),
+
ContextMenuItem::Separator => Empty::new()
.collapsed()
.contained()
@@ -302,6 +315,9 @@ impl ContextMenu {
)
.boxed()
}
+
+ ContextMenuItem::Static(_) => Empty::new().boxed(),
+
ContextMenuItem::Separator => Empty::new()
.collapsed()
.constrained()
@@ -339,7 +355,7 @@ impl ContextMenu {
Flex::row()
.with_child(
- Label::new(label.to_string(), style.label.clone())
+ Label::new(label.clone(), style.label.clone())
.contained()
.boxed(),
)
@@ -366,6 +382,9 @@ impl ContextMenu {
.on_drag(MouseButton::Left, |_, _| {})
.boxed()
}
+
+ ContextMenuItem::Static(f) => f(cx),
+
ContextMenuItem::Separator => Empty::new()
.constrained()
.with_height(1.)
@@ -88,6 +88,7 @@ pub struct Titlebar {
pub share_button: Interactive<ContainedText>,
pub call_control: Interactive<IconButton>,
pub toggle_contacts_button: Interactive<IconButton>,
+ pub user_menu_button: Interactive<IconButton>,
pub toggle_contacts_badge: ContainerStyle,
}
@@ -197,6 +197,11 @@ export default function workspace(colorScheme: ColorScheme) {
color: foreground(layer, "variant", "hovered"),
},
},
+ userMenuButton: {
+ buttonWidth: 20,
+ iconWidth: 12,
+ ...titlebarButton,
+ },
toggleContactsBadge: {
cornerRadius: 3,
padding: 2,