Avoid moving contacts popover during call start & add button style state

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Julia 2023-02-20 13:15:04 -05:00
parent dd02bc7748
commit 1ed47663ef
2 changed files with 114 additions and 70 deletions

View file

@ -31,6 +31,12 @@ actions!(
] ]
); );
#[derive(PartialEq, Eq)]
enum ContactsPopoverSide {
Left,
Right,
}
pub fn init(cx: &mut MutableAppContext) { pub fn init(cx: &mut MutableAppContext) {
cx.add_action(CollabTitlebarItem::toggle_collaborator_list_popover); cx.add_action(CollabTitlebarItem::toggle_collaborator_list_popover);
cx.add_action(CollabTitlebarItem::toggle_contacts_popover); cx.add_action(CollabTitlebarItem::toggle_contacts_popover);
@ -42,6 +48,7 @@ pub struct CollabTitlebarItem {
workspace: WeakViewHandle<Workspace>, workspace: WeakViewHandle<Workspace>,
user_store: ModelHandle<UserStore>, user_store: ModelHandle<UserStore>,
contacts_popover: Option<ViewHandle<ContactsPopover>>, contacts_popover: Option<ViewHandle<ContactsPopover>>,
contacts_popover_side: ContactsPopoverSide,
collaborator_list_popover: Option<ViewHandle<CollaboratorListPopover>>, collaborator_list_popover: Option<ViewHandle<CollaboratorListPopover>>,
_subscriptions: Vec<Subscription>, _subscriptions: Vec<Subscription>,
} }
@ -98,8 +105,7 @@ impl View for CollabTitlebarItem {
right_container right_container
.add_child(self.render_in_call_share_unshare_button(&workspace, &theme, cx)); .add_child(self.render_in_call_share_unshare_button(&workspace, &theme, cx));
} else { } else {
right_container right_container.add_child(self.render_outside_call_share_button(&theme, cx));
.add_child(self.render_outside_call_share_button(&workspace, &theme, cx));
} }
right_container.add_children(self.render_connection_status(&workspace, cx)); right_container.add_children(self.render_connection_status(&workspace, cx));
@ -166,6 +172,7 @@ impl CollabTitlebarItem {
workspace: workspace.downgrade(), workspace: workspace.downgrade(),
user_store: user_store.clone(), user_store: user_store.clone(),
contacts_popover: None, contacts_popover: None,
contacts_popover_side: ContactsPopoverSide::Right,
collaborator_list_popover: None, collaborator_list_popover: None,
_subscriptions: subscriptions, _subscriptions: subscriptions,
} }
@ -306,27 +313,31 @@ impl CollabTitlebarItem {
} }
pub fn toggle_contacts_popover(&mut self, _: &ToggleContactsMenu, cx: &mut ViewContext<Self>) { pub fn toggle_contacts_popover(&mut self, _: &ToggleContactsMenu, cx: &mut ViewContext<Self>) {
match self.contacts_popover.take() { if self.contacts_popover.take().is_none() {
Some(_) => {} if let Some(workspace) = self.workspace.upgrade(cx) {
None => { let project = workspace.read(cx).project().clone();
if let Some(workspace) = self.workspace.upgrade(cx) { let user_store = workspace.read(cx).user_store().clone();
let project = workspace.read(cx).project().clone(); let view = cx.add_view(|cx| ContactsPopover::new(project, user_store, cx));
let user_store = workspace.read(cx).user_store().clone(); cx.subscribe(&view, |this, _, event, cx| {
let view = cx.add_view(|cx| ContactsPopover::new(project, user_store, cx)); match event {
cx.subscribe(&view, |this, _, event, cx| { contacts_popover::Event::Dismissed => {
match event { this.contacts_popover = None;
contacts_popover::Event::Dismissed => {
this.contacts_popover = None;
}
} }
}
cx.notify(); cx.notify();
}) })
.detach(); .detach();
self.contacts_popover = Some(view);
} self.contacts_popover_side = match ActiveCall::global(cx).read(cx).room() {
Some(_) => ContactsPopoverSide::Left,
None => ContactsPopoverSide::Right,
};
self.contacts_popover = Some(view);
} }
} }
cx.notify(); cx.notify();
} }
@ -361,9 +372,11 @@ impl CollabTitlebarItem {
Stack::new() Stack::new()
.with_child( .with_child(
MouseEventHandler::<ToggleContactsMenu>::new(0, cx, |state, _| { MouseEventHandler::<ToggleContactsMenu>::new(0, cx, |state, _| {
let style = titlebar let style = titlebar.toggle_contacts_button.style_for(
.toggle_contacts_button state,
.style_for(state, self.contacts_popover.is_some()); self.contacts_popover.is_some()
&& self.contacts_popover_side == ContactsPopoverSide::Left,
);
Svg::new("icons/plus_8.svg") Svg::new("icons/plus_8.svg")
.with_color(style.color) .with_color(style.color)
.constrained() .constrained()
@ -384,20 +397,7 @@ impl CollabTitlebarItem {
.boxed(), .boxed(),
) )
.with_children(badge) .with_children(badge)
.with_children(self.contacts_popover.as_ref().map(|popover| { .with_children(self.contacts_popover_host(ContactsPopoverSide::Left, titlebar, cx))
Overlay::new(
ChildView::new(popover, cx)
.contained()
.with_margin_top(titlebar.height)
.with_margin_left(titlebar.toggle_contacts_button.default.button_width)
.with_margin_right(-titlebar.toggle_contacts_button.default.button_width)
.boxed(),
)
.with_fit_mode(OverlayFitMode::SwitchAnchor)
.with_anchor_corner(AnchorCorner::BottomLeft)
.with_z_index(999)
.boxed()
}))
.boxed() .boxed()
} }
@ -468,33 +468,47 @@ impl CollabTitlebarItem {
let titlebar = &theme.workspace.titlebar; let titlebar = &theme.workspace.titlebar;
enum ShareUnshare {} enum ShareUnshare {}
MouseEventHandler::<ShareUnshare>::new(0, cx, |state, _| { Stack::new()
//TODO: Ensure this button has consistant width for both text variations .with_child(
let style = titlebar.share_button.style_for(state, false); MouseEventHandler::<ShareUnshare>::new(0, cx, |state, _| {
Label::new(label, style.text.clone()) //TODO: Ensure this button has consistant width for both text variations
.contained() let style = titlebar.share_button.style_for(
.with_style(style.container) state,
.boxed() self.contacts_popover.is_some()
}) && self.contacts_popover_side == ContactsPopoverSide::Right,
.with_cursor_style(CursorStyle::PointingHand) );
.on_click(MouseButton::Left, move |_, cx| { Label::new(label, style.text.clone())
if is_shared { .contained()
cx.dispatch_action(UnshareProject); .with_style(style.container)
} else { .boxed()
cx.dispatch_action(ShareProject); })
} .with_cursor_style(CursorStyle::PointingHand)
}) .on_click(MouseButton::Left, move |_, cx| {
.with_tooltip::<ShareUnshare, _>(0, tooltip.to_owned(), None, theme.tooltip.clone(), cx) if is_shared {
.aligned() cx.dispatch_action(UnshareProject);
.contained() } else {
.with_margin_left(theme.workspace.titlebar.avatar_margin) cx.dispatch_action(ShareProject);
.with_margin_right(theme.workspace.titlebar.avatar_margin) }
.boxed() })
.with_tooltip::<ShareUnshare, _>(
0,
tooltip.to_owned(),
None,
theme.tooltip.clone(),
cx,
)
.boxed(),
)
.with_children(self.contacts_popover_host(ContactsPopoverSide::Right, titlebar, cx))
.aligned()
.contained()
.with_margin_left(theme.workspace.titlebar.avatar_margin)
.with_margin_right(theme.workspace.titlebar.avatar_margin)
.boxed()
} }
fn render_outside_call_share_button( fn render_outside_call_share_button(
&self, &self,
workspace: &ViewHandle<Workspace>,
theme: &Theme, theme: &Theme,
cx: &mut RenderContext<Self>, cx: &mut RenderContext<Self>,
) -> ElementBox { ) -> ElementBox {
@ -506,7 +520,11 @@ impl CollabTitlebarItem {
.with_child( .with_child(
MouseEventHandler::<OutsideCallShare>::new(0, cx, |state, _| { MouseEventHandler::<OutsideCallShare>::new(0, cx, |state, _| {
//TODO: Ensure this button has consistant width for both text variations //TODO: Ensure this button has consistant width for both text variations
let style = titlebar.share_button.style_for(state, false); 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()) Label::new("Share".to_owned(), style.text.clone())
.contained() .contained()
.with_style(style.container) .with_style(style.container)
@ -525,25 +543,37 @@ impl CollabTitlebarItem {
) )
.boxed(), .boxed(),
) )
.with_children(self.contacts_popover.as_ref().map(|popover| { .with_children(self.contacts_popover_host(ContactsPopoverSide::Right, titlebar, cx))
.aligned()
.contained()
.with_margin_left(theme.workspace.titlebar.avatar_margin)
.with_margin_right(theme.workspace.titlebar.avatar_margin)
.boxed()
}
fn 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( Overlay::new(
ChildView::new(popover, cx) ChildView::new(popover, cx)
.contained() .contained()
.with_margin_top(titlebar.height) .with_margin_top(theme.height)
.with_margin_left(titlebar.toggle_contacts_button.default.button_width) .with_margin_left(theme.toggle_contacts_button.default.button_width)
.with_margin_right(-titlebar.toggle_contacts_button.default.button_width) .with_margin_right(-theme.toggle_contacts_button.default.button_width)
.boxed(), .boxed(),
) )
.with_fit_mode(OverlayFitMode::SwitchAnchor) .with_fit_mode(OverlayFitMode::SwitchAnchor)
.with_anchor_corner(AnchorCorner::BottomLeft) .with_anchor_corner(AnchorCorner::BottomLeft)
.with_z_index(999) .with_z_index(999)
.boxed() .boxed()
})) })
.aligned()
.contained()
.with_margin_left(theme.workspace.titlebar.avatar_margin)
.with_margin_right(theme.workspace.titlebar.avatar_margin)
.boxed()
} }
fn render_collaborators( fn render_collaborators(

View file

@ -29,6 +29,16 @@ export default function workspace(colorScheme: ColorScheme) {
background: background(layer, "variant", "hovered"), background: background(layer, "variant", "hovered"),
border: border(layer, "variant", "hovered"), border: border(layer, "variant", "hovered"),
}, },
clicked: {
...text(layer, "sans", "variant", "pressed", { size: "xs" }),
background: background(layer, "variant", "pressed"),
border: border(layer, "variant", "pressed"),
},
active: {
...text(layer, "sans", "variant", "active", { size: "xs" }),
background: background(layer, "variant", "active"),
border: border(layer, "variant", "active"),
},
}; };
const avatarWidth = 18; const avatarWidth = 18;
@ -178,6 +188,10 @@ export default function workspace(colorScheme: ColorScheme) {
background: background(layer, "variant", "active"), background: background(layer, "variant", "active"),
color: foreground(layer, "variant", "active"), color: foreground(layer, "variant", "active"),
}, },
clicked: {
background: background(layer, "variant", "pressed"),
color: foreground(layer, "variant", "pressed"),
},
hover: { hover: {
background: background(layer, "variant", "hovered"), background: background(layer, "variant", "hovered"),
color: foreground(layer, "variant", "hovered"), color: foreground(layer, "variant", "hovered"),