diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 4bbf322205..8b467e6cb4 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -17,10 +17,14 @@ use std::{ops::Range, sync::Arc}; use theme::Theme; use workspace::{FollowNextCollaborator, ToggleFollow, Workspace}; -actions!(contacts_titlebar_item, [ToggleContactsPopover]); +actions!( + contacts_titlebar_item, + [ToggleContactsPopover, ShareProject] +); pub fn init(cx: &mut MutableAppContext) { cx.add_action(CollabTitlebarItem::toggle_contacts_popover); + cx.add_action(CollabTitlebarItem::share_project); } pub struct CollabTitlebarItem { @@ -47,12 +51,20 @@ impl View for CollabTitlebarItem { }; let theme = cx.global::().theme.clone(); - Flex::row() - .with_children(self.render_toggle_contacts_button(&workspace, &theme, cx)) - .with_children(self.render_collaborators(&workspace, &theme, cx)) - .with_children(self.render_current_user(&workspace, &theme, cx)) - .with_children(self.render_connection_status(&workspace, cx)) - .boxed() + let project = workspace.read(cx).project().read(cx); + + let mut container = Flex::row(); + if workspace.read(cx).client().status().borrow().is_connected() { + if project.is_shared() || ActiveCall::global(cx).read(cx).room().is_none() { + container.add_child(self.render_toggle_contacts_button(&theme, cx)); + } else { + container.add_child(self.render_share_button(&theme, cx)); + } + } + container.add_children(self.render_collaborators(&workspace, &theme, cx)); + container.add_children(self.render_current_user(&workspace, &theme, cx)); + container.add_children(self.render_connection_status(&workspace, cx)); + container.boxed() } } @@ -81,6 +93,18 @@ impl CollabTitlebarItem { cx.notify(); } + fn share_project(&mut self, _: &ShareProject, cx: &mut ViewContext) { + if let Some(workspace) = self.workspace.upgrade(cx) { + if let Some(room) = ActiveCall::global(cx).read(cx).room() { + let room_id = room.read(cx).id(); + let project = workspace.read(cx).project().clone(); + project + .update(cx, |project, cx| project.share(room_id, cx)) + .detach_and_log_err(cx); + } + } + } + fn toggle_contacts_popover(&mut self, _: &ToggleContactsPopover, cx: &mut ViewContext) { match self.contacts_popover.take() { Some(_) => {} @@ -108,58 +132,72 @@ impl CollabTitlebarItem { fn render_toggle_contacts_button( &self, - workspace: &ViewHandle, theme: &Theme, cx: &mut RenderContext, - ) -> Option { - if !workspace.read(cx).client().status().borrow().is_connected() { - return None; - } + ) -> ElementBox { + let titlebar = &theme.workspace.titlebar; + Stack::new() + .with_child( + MouseEventHandler::::new(0, cx, |state, _| { + 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() + .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(ToggleContactsPopover); + }) + .aligned() + .boxed(), + ) + .with_children(self.contacts_popover.as_ref().map(|popover| { + Overlay::new( + ChildView::new(popover) + .contained() + .with_margin_top(titlebar.height) + .with_margin_right(-titlebar.toggle_contacts_button.default.button_width) + .boxed(), + ) + .with_fit_mode(OverlayFitMode::SwitchAnchor) + .with_anchor_corner(AnchorCorner::BottomLeft) + .boxed() + })) + .boxed() + } + + fn render_share_button(&self, theme: &Theme, cx: &mut RenderContext) -> ElementBox { + enum Share {} let titlebar = &theme.workspace.titlebar; - - Some( - Stack::new() - .with_child( - MouseEventHandler::::new(0, cx, |state, _| { - 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() - .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(ToggleContactsPopover); - }) - .aligned() - .boxed(), - ) - .with_children(self.contacts_popover.as_ref().map(|popover| { - Overlay::new( - ChildView::new(popover) - .contained() - .with_margin_top(titlebar.height) - .with_margin_right( - -titlebar.toggle_contacts_button.default.button_width, - ) - .boxed(), - ) - .with_fit_mode(OverlayFitMode::SwitchAnchor) - .with_anchor_corner(AnchorCorner::BottomLeft) - .boxed() - })) - .boxed(), + MouseEventHandler::::new(0, cx, |state, _| { + let style = titlebar.share_button.style_for(state, false); + Label::new("Share".into(), style.text.clone()) + .contained() + .with_style(style.container) + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, |_, cx| cx.dispatch_action(ShareProject)) + .with_tooltip::( + 0, + "Share project with call participants".into(), + None, + theme.tooltip.clone(), + cx, ) + .aligned() + .boxed() } fn render_collaborators( diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 7d0473bfc5..7420961041 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -479,40 +479,49 @@ impl ContactsPopover { is_selected: bool, cx: &mut RenderContext, ) -> ElementBox { + let online = contact.online; let user_id = contact.user.id; - MouseEventHandler::::new(contact.user.id as usize, cx, |_, _| { - Flex::row() - .with_children(contact.user.avatar.clone().map(|avatar| { - Image::new(avatar) - .with_style(theme.contact_avatar) + let mut element = + MouseEventHandler::::new(contact.user.id as usize, cx, |_, _| { + Flex::row() + .with_children(contact.user.avatar.clone().map(|avatar| { + Image::new(avatar) + .with_style(theme.contact_avatar) + .aligned() + .left() + .boxed() + })) + .with_child( + Label::new( + contact.user.github_login.clone(), + theme.contact_username.text.clone(), + ) + .contained() + .with_style(theme.contact_username.container) .aligned() .left() - .boxed() - })) - .with_child( - Label::new( - contact.user.github_login.clone(), - theme.contact_username.text.clone(), + .flex(1., true) + .boxed(), ) + .constrained() + .with_height(theme.row_height) .contained() - .with_style(theme.contact_username.container) - .aligned() - .left() - .flex(1., true) - .boxed(), - ) - .constrained() - .with_height(theme.row_height) - .contained() - .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) - .boxed() - }) - .on_click(MouseButton::Left, move |_, cx| { - cx.dispatch_action(Call { - recipient_user_id: user_id, + .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) + .boxed() }) - }) - .boxed() + .on_click(MouseButton::Left, move |_, cx| { + if online { + cx.dispatch_action(Call { + recipient_user_id: user_id, + }); + } + }); + + if online { + element = element.with_cursor_style(CursorStyle::PointingHand); + } + + element.boxed() } fn render_contact_request( diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 7e78f74c59..52b34462aa 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -74,6 +74,7 @@ pub struct Titlebar { pub inactive_avatar: ImageStyle, pub sign_in_prompt: Interactive, pub outdated_warning: ContainedText, + pub share_button: Interactive, pub toggle_contacts_button: Interactive, pub contacts_popover: AddParticipantPopover, } diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 4889ee3f10..40ed44e8db 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -17,6 +17,26 @@ export function workspaceBackground(theme: Theme) { export default function workspace(theme: Theme) { const titlebarPadding = 6; + const titlebarButton = { + background: backgroundColor(theme, 100), + border: border(theme, "secondary"), + cornerRadius: 6, + margin: { + top: 1, + }, + padding: { + top: 1, + bottom: 1, + left: 7, + right: 7, + }, + ...text(theme, "sans", "secondary", { size: "xs" }), + hover: { + ...text(theme, "sans", "active", { size: "xs" }), + background: backgroundColor(theme, "on300", "hovered"), + border: border(theme, "primary"), + }, + }; return { background: backgroundColor(theme, 300), @@ -81,24 +101,7 @@ export default function workspace(theme: Theme) { }, border: border(theme, "primary", { bottom: true, overlay: true }), signInPrompt: { - background: backgroundColor(theme, 100), - border: border(theme, "secondary"), - cornerRadius: 6, - margin: { - top: 1, - }, - padding: { - top: 1, - bottom: 1, - left: 7, - right: 7, - }, - ...text(theme, "sans", "secondary", { size: "xs" }), - hover: { - ...text(theme, "sans", "active", { size: "xs" }), - background: backgroundColor(theme, "on300", "hovered"), - border: border(theme, "primary"), - }, + ...titlebarButton }, offlineIcon: { color: iconColor(theme, "secondary"), @@ -137,6 +140,9 @@ export default function workspace(theme: Theme) { color: iconColor(theme, "active"), }, }, + shareButton: { + ...titlebarButton + }, contactsPopover: { background: backgroundColor(theme, 300, "base"), cornerRadius: 6,