@@ -1,42 +1,14 @@
-// use crate::{
-// face_pile::FacePile, toggle_deafen, toggle_mute, toggle_screen_sharing, LeaveCall,
-// ToggleDeafen, ToggleMute, ToggleScreenSharing,
-// };
-// use auto_update::AutoUpdateStatus;
-// use call::{ActiveCall, ParticipantLocation, Room};
-// use client::{proto::PeerId, Client, SignIn, SignOut, User, UserStore};
-// use clock::ReplicaId;
-// use context_menu::{ContextMenu, ContextMenuItem};
-// use gpui::{
-// actions,
-// color::Color,
-// elements::*,
-// geometry::{rect::RectF, vector::vec2f, PathBuilder},
-// json::{self, ToJson},
-// platform::{CursorStyle, MouseButton},
-// AppContext, Entity, ImageData, ModelHandle, Subscription, View, ViewContext, ViewHandle,
-// WeakViewHandle,
-// };
-// use picker::PickerEvent;
-// use project::{Project, RepositoryEntry};
-// use recent_projects::{build_recent_projects, RecentProjects};
-// use std::{ops::Range, sync::Arc};
-// use theme::{AvatarStyle, Theme};
-// use util::ResultExt;
-// use vcs_menu::{build_branch_list, BranchList, OpenRecent as ToggleVcsMenu};
-// use workspace::{FollowNextCollaborator, Workspace, WORKSPACE_DB};
-
-use std::sync::Arc;
-
-use call::ActiveCall;
-use client::{Client, UserStore};
+use crate::face_pile::FacePile;
+use call::{ActiveCall, ParticipantLocation, Room};
+use client::{proto::PeerId, Client, ParticipantIndex, User, UserStore};
use gpui::{
- actions, div, px, rems, AppContext, Div, Element, InteractiveElement, IntoElement, Model,
- MouseButton, ParentElement, Render, RenderOnce, Stateful, StatefulInteractiveElement, Styled,
+ actions, canvas, div, point, px, rems, AppContext, Div, Element, Hsla, InteractiveElement,
+ IntoElement, Model, ParentElement, Path, Render, Stateful, StatefulInteractiveElement, Styled,
Subscription, ViewContext, VisualContext, WeakView, WindowBounds,
};
use project::{Project, RepositoryEntry};
-use theme::ActiveTheme;
+use std::sync::Arc;
+use theme::{ActiveTheme, PlayerColors};
use ui::{
h_stack, popover_menu, prelude::*, Avatar, Button, ButtonLike, ButtonStyle, ContextMenu, Icon,
IconButton, IconElement, KeyBinding, Tooltip,
@@ -44,8 +16,6 @@ use ui::{
use util::ResultExt;
use workspace::{notifications::NotifyResultExt, Workspace};
-use crate::face_pile::FacePile;
-
const MAX_PROJECT_NAME_LENGTH: usize = 40;
const MAX_BRANCH_NAME_LENGTH: usize = 40;
@@ -57,17 +27,6 @@ actions!(
SwitchBranch
);
-// actions!(
-// collab,
-// [
-// ToggleUserMenu,
-// ToggleProjectMenu,
-// SwitchBranch,
-// ShareProject,
-// UnshareProject,
-// ]
-// );
-
pub fn init(cx: &mut AppContext) {
cx.observe_new_views(|workspace: &mut Workspace, cx| {
let titlebar_item = cx.build_view(|cx| CollabTitlebarItem::new(workspace, cx));
@@ -83,11 +42,8 @@ pub fn init(cx: &mut AppContext) {
pub struct CollabTitlebarItem {
project: Model<Project>,
- #[allow(unused)] // todo!()
user_store: Model<UserStore>,
- #[allow(unused)] // todo!()
client: Arc<Client>,
- #[allow(unused)] // todo!()
workspace: WeakView<Workspace>,
//branch_popover: Option<ViewHandle<BranchList>>,
//project_popover: Option<ViewHandle<recent_projects::RecentProjects>>,
@@ -99,28 +55,11 @@ impl Render for CollabTitlebarItem {
type Element = Stateful<Div>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
- let room = ActiveCall::global(cx).read(cx).room();
- let is_in_room = room.is_some();
- let is_shared = is_in_room && self.project.read(cx).is_shared();
+ let room = ActiveCall::global(cx).read(cx).room().cloned();
let current_user = self.user_store.read(cx).current_user();
let client = self.client.clone();
- let remote_participants = room.map(|room| {
- room.read(cx)
- .remote_participants()
- .values()
- .map(|participant| (participant.user.clone(), participant.peer_id))
- .collect::<Vec<_>>()
- });
- let is_muted = room.map_or(false, |room| room.read(cx).is_muted(cx));
- let is_deafened = room
- .and_then(|room| room.read(cx).is_deafened())
- .unwrap_or(false);
- let speakers_icon = if is_deafened {
- ui::Icon::AudioOff
- } else {
- ui::Icon::AudioOn
- };
- let workspace = self.workspace.clone();
+ let project_id = self.project.read(cx).remote_id();
+
h_stack()
.id("titlebar")
.justify_between()
@@ -141,262 +80,222 @@ impl Render for CollabTitlebarItem {
cx.zoom_window();
}
})
+ // left side
.child(
h_stack()
.gap_1()
- .when(is_in_room, |this| {
- this.children(self.render_project_owner(cx))
- })
+ .children(self.render_project_host(cx))
.child(self.render_project_name(cx))
- .children(self.render_project_branch(cx)),
- )
- .when_some(
- remote_participants.zip(current_user.clone()),
- |this, (remote_participants, current_user)| {
- let mut pile = FacePile::default();
- pile.extend(
+ .children(self.render_project_branch(cx))
+ .when_some(
current_user
- .avatar
.clone()
- .map(|avatar| {
- div().child(Avatar::data(avatar.clone())).into_any_element()
- })
- .into_iter()
- .chain(remote_participants.into_iter().filter_map(
- |(user, peer_id)| {
- let avatar = user.avatar.as_ref()?;
+ .zip(client.peer_id())
+ .zip(room.clone())
+ .zip(project_id),
+ |this, (((current_user, peer_id), room), project_id)| {
+ let player_colors = cx.theme().players();
+ let room = room.read(cx);
+ let mut remote_participants =
+ room.remote_participants().values().collect::<Vec<_>>();
+ remote_participants.sort_by_key(|p| p.participant_index.0);
+
+ this.children(self.render_collaborator(
+ ¤t_user,
+ peer_id,
+ ParticipantLocation::SharedProject { project_id },
+ room.is_speaking(),
+ room.is_muted(cx),
+ &room,
+ project_id,
+ ¤t_user,
+ ))
+ .children(
+ remote_participants.iter().filter_map(|collaborator| {
+ let face_pile = self.render_collaborator(
+ &collaborator.user,
+ collaborator.peer_id,
+ collaborator.location.clone(),
+ collaborator.speaking,
+ collaborator.muted,
+ &room,
+ project_id,
+ ¤t_user,
+ )?;
+
Some(
- div()
- .child(
- Avatar::data(avatar.clone())
- .into_element()
- .into_any(),
- )
- .on_mouse_down(MouseButton::Left, {
- let workspace = workspace.clone();
- move |_, cx| {
- workspace
- .update(cx, |this, cx| {
- this.open_shared_screen(peer_id, cx);
+ v_stack()
+ .id(("collaborator", collaborator.user.id))
+ .child(face_pile)
+ .child(render_color_ribbon(
+ collaborator.participant_index,
+ player_colors,
+ ))
+ .cursor_pointer()
+ .on_click({
+ let peer_id = collaborator.peer_id;
+ cx.listener(move |this, _, cx| {
+ this.workspace
+ .update(cx, |workspace, cx| {
+ workspace.follow(peer_id, cx);
})
- .log_err();
- }
+ .ok();
+ })
})
- .into_any_element(),
+ .tooltip({
+ let login = collaborator.user.github_login.clone();
+ move |cx| {
+ Tooltip::text(format!("Follow {login}"), cx)
+ }
+ }),
)
+ }),
+ )
+ },
+ ),
+ )
+ // right side
+ .child(
+ h_stack()
+ .gap_1()
+ .when_some(room, |this, room| {
+ let room = room.read(cx);
+ let is_shared = self.project.read(cx).is_shared();
+ let is_muted = room.is_muted(cx);
+ let is_deafened = room.is_deafened().unwrap_or(false);
+
+ this.child(
+ Button::new(
+ "toggle_sharing",
+ if is_shared { "Unshare" } else { "Share" },
+ )
+ .style(ButtonStyle::Subtle)
+ .on_click(cx.listener(
+ move |this, _, cx| {
+ if is_shared {
+ this.unshare_project(&Default::default(), cx);
+ } else {
+ this.share_project(&Default::default(), cx);
+ }
},
)),
- );
- this.child(pile.render(cx))
- },
- )
- .child(div().flex_1())
- .when(is_in_room, |this| {
- this.child(
- h_stack()
- .gap_1()
+ )
.child(
- h_stack()
- .gap_1()
- .child(
- Button::new(
- "toggle_sharing",
- if is_shared { "Unshare" } else { "Share" },
- )
- .style(ButtonStyle::Subtle)
- .on_click(cx.listener(
- move |this, _, cx| {
- if is_shared {
- this.unshare_project(&Default::default(), cx);
- } else {
- this.share_project(&Default::default(), cx);
- }
- },
- )),
- )
- .child(
- IconButton::new("leave-call", ui::Icon::Exit)
- .style(ButtonStyle::Subtle)
- .on_click(move |_, cx| {
- ActiveCall::global(cx)
- .update(cx, |call, cx| call.hang_up(cx))
- .detach_and_log_err(cx);
- }),
- ),
+ IconButton::new("leave-call", ui::Icon::Exit)
+ .style(ButtonStyle::Subtle)
+ .on_click(move |_, cx| {
+ ActiveCall::global(cx)
+ .update(cx, |call, cx| call.hang_up(cx))
+ .detach_and_log_err(cx);
+ }),
)
.child(
- h_stack()
- .gap_1()
- .child(
- IconButton::new(
- "mute-microphone",
- if is_muted {
- ui::Icon::MicMute
- } else {
- ui::Icon::Mic
- },
- )
- .style(ButtonStyle::Subtle)
- .selected(is_muted)
- .on_click(move |_, cx| {
- crate::toggle_mute(&Default::default(), cx)
- }),
- )
- .child(
- IconButton::new("mute-sound", speakers_icon)
- .style(ButtonStyle::Subtle)
- .selected(is_deafened.clone())
- .tooltip(move |cx| {
- Tooltip::with_meta(
- "Deafen Audio",
- None,
- "Mic will be muted",
- cx,
- )
+ IconButton::new(
+ "mute-microphone",
+ if is_muted {
+ ui::Icon::MicMute
+ } else {
+ ui::Icon::Mic
+ },
+ )
+ .style(ButtonStyle::Subtle)
+ .selected(is_muted)
+ .on_click(move |_, cx| crate::toggle_mute(&Default::default(), cx)),
+ )
+ .child(
+ IconButton::new(
+ "mute-sound",
+ if is_deafened {
+ ui::Icon::AudioOff
+ } else {
+ ui::Icon::AudioOn
+ },
+ )
+ .style(ButtonStyle::Subtle)
+ .selected(is_deafened.clone())
+ .tooltip(move |cx| {
+ Tooltip::with_meta("Deafen Audio", None, "Mic will be muted", cx)
+ })
+ .on_click(move |_, cx| crate::toggle_mute(&Default::default(), cx)),
+ )
+ .child(
+ IconButton::new("screen-share", ui::Icon::Screen)
+ .style(ButtonStyle::Subtle)
+ .on_click(move |_, cx| {
+ crate::toggle_screen_sharing(&Default::default(), cx)
+ }),
+ )
+ })
+ .child(h_stack().px_1p5().map(|this| {
+ if let Some(user) = current_user {
+ this.when_some(user.avatar.clone(), |this, avatar| {
+ // TODO: Finish implementing user menu popover
+ //
+ this.child(
+ popover_menu("user-menu")
+ .menu(|cx| {
+ ContextMenu::build(cx, |menu, _| menu.header("ADADA"))
})
- .on_click(move |_, cx| {
- crate::toggle_mute(&Default::default(), cx)
- }),
- )
- .child(
- IconButton::new("screen-share", ui::Icon::Screen)
- .style(ButtonStyle::Subtle)
- .on_click(move |_, cx| {
- crate::toggle_screen_sharing(&Default::default(), cx)
- }),
- )
- .pl_2(),
- ),
- )
- })
- .child(h_stack().px_1p5().map(|this| {
- if let Some(user) = current_user {
- this.when_some(user.avatar.clone(), |this, avatar| {
- // TODO: Finish implementing user menu popover
- //
- this.child(
- popover_menu("user-menu")
- .menu(|cx| ContextMenu::build(cx, |menu, _| menu.header("ADADA")))
- .trigger(
- ButtonLike::new("user-menu")
- .child(
- h_stack().gap_0p5().child(Avatar::data(avatar)).child(
- IconElement::new(Icon::ChevronDown)
- .color(Color::Muted),
- ),
+ .trigger(
+ ButtonLike::new("user-menu")
+ .child(
+ h_stack()
+ .gap_0p5()
+ .child(Avatar::data(avatar))
+ .child(
+ IconElement::new(Icon::ChevronDown)
+ .color(Color::Muted),
+ ),
+ )
+ .style(ButtonStyle::Subtle)
+ .tooltip(move |cx| {
+ Tooltip::text("Toggle User Menu", cx)
+ }),
)
- .style(ButtonStyle::Subtle)
- .tooltip(move |cx| Tooltip::text("Toggle User Menu", cx)),
+ .anchor(gpui::AnchorCorner::TopRight),
)
- .anchor(gpui::AnchorCorner::TopRight),
- )
- // this.child(
- // ButtonLike::new("user-menu")
- // .child(
- // h_stack().gap_0p5().child(Avatar::data(avatar)).child(
- // IconElement::new(Icon::ChevronDown).color(Color::Muted),
- // ),
- // )
- // .style(ButtonStyle::Subtle)
- // .tooltip(move |cx| Tooltip::text("Toggle User Menu", cx)),
- // )
- })
- } else {
- this.child(Button::new("sign_in", "Sign in").on_click(move |_, cx| {
- let client = client.clone();
- cx.spawn(move |mut cx| async move {
- client
- .authenticate_and_connect(true, &cx)
- .await
- .notify_async_err(&mut cx);
- })
- .detach();
- }))
- }
- }))
+ // this.child(
+ // ButtonLike::new("user-menu")
+ // .child(
+ // h_stack().gap_0p5().child(Avatar::data(avatar)).child(
+ // IconElement::new(Icon::ChevronDown).color(Color::Muted),
+ // ),
+ // )
+ // .style(ButtonStyle::Subtle)
+ // .tooltip(move |cx| Tooltip::text("Toggle User Menu", cx)),
+ // )
+ })
+ } else {
+ this.child(Button::new("sign_in", "Sign in").on_click(move |_, cx| {
+ let client = client.clone();
+ cx.spawn(move |mut cx| async move {
+ client
+ .authenticate_and_connect(true, &cx)
+ .await
+ .notify_async_err(&mut cx);
+ })
+ .detach();
+ }))
+ }
+ })),
+ )
}
}
-// impl Entity for CollabTitlebarItem {
-// type Event = ();
-// }
-
-// impl View for CollabTitlebarItem {
-// fn ui_name() -> &'static str {
-// "CollabTitlebarItem"
-// }
-
-// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-// let workspace = if let Some(workspace) = self.workspace.upgrade(cx) {
-// workspace
-// } else {
-// return Empty::new().into_any();
-// };
-
-// let theme = theme::current(cx).clone();
-// let mut left_container = Flex::row();
-// let mut right_container = Flex::row().align_children_center();
-
-// left_container.add_child(self.collect_title_root_names(theme.clone(), cx));
-
-// let user = self.user_store.read(cx).current_user();
-// let peer_id = self.client.peer_id();
-// if let Some(((user, peer_id), room)) = user
-// .as_ref()
-// .zip(peer_id)
-// .zip(ActiveCall::global(cx).read(cx).room().cloned())
-// {
-// if room.read(cx).can_publish() {
-// right_container
-// .add_children(self.render_in_call_share_unshare_button(&workspace, &theme, cx));
-// }
-// right_container.add_child(self.render_leave_call(&theme, cx));
-// let muted = room.read(cx).is_muted(cx);
-// let speaking = room.read(cx).is_speaking();
-// left_container.add_child(
-// self.render_current_user(&workspace, &theme, &user, peer_id, muted, speaking, cx),
-// );
-// left_container.add_children(self.render_collaborators(&workspace, &theme, &room, cx));
-// if room.read(cx).can_publish() {
-// right_container.add_child(self.render_toggle_mute(&theme, &room, cx));
-// }
-// right_container.add_child(self.render_toggle_deafen(&theme, &room, cx));
-// if room.read(cx).can_publish() {
-// right_container
-// .add_child(self.render_toggle_screen_sharing_button(&theme, &room, cx));
-// }
-// }
-
-// let status = workspace.read(cx).client().status();
-// let status = &*status.borrow();
-// if matches!(status, client::Status::Connected { .. }) {
-// let avatar = user.as_ref().and_then(|user| user.avatar.clone());
-// right_container.add_child(self.render_user_menu_button(&theme, avatar, cx));
-// } else {
-// right_container.add_children(self.render_connection_status(status, cx));
-// right_container.add_child(self.render_sign_in_button(&theme, cx));
-// right_container.add_child(self.render_user_menu_button(&theme, None, cx));
-// }
-
-// Stack::new()
-// .with_child(left_container)
-// .with_child(
-// Flex::row()
-// .with_child(
-// right_container.contained().with_background_color(
-// theme
-// .titlebar
-// .container
-// .background_color
-// .unwrap_or_else(|| Color::transparent_black()),
-// ),
-// )
-// .aligned()
-// .right(),
-// )
-// .into_any()
-// }
-// }
+fn render_color_ribbon(participant_index: ParticipantIndex, colors: &PlayerColors) -> gpui::Canvas {
+ let color = colors.color_for_participant(participant_index.0).cursor;
+ canvas(move |bounds, cx| {
+ let mut path = Path::new(bounds.lower_left());
+ let height = bounds.size.height;
+ path.curve_to(bounds.origin + point(height, px(0.)), bounds.origin);
+ path.line_to(bounds.upper_right() - point(height, px(0.)));
+ path.curve_to(bounds.lower_right(), bounds.upper_right());
+ path.line_to(bounds.lower_left());
+ cx.paint_path(path, color);
+ })
+ .h_1()
+ .w_full()
+}
impl CollabTitlebarItem {
pub fn new(workspace: &Workspace, cx: &mut ViewContext<Self>) -> Self {
@@ -435,7 +334,7 @@ impl CollabTitlebarItem {
// resolve if you are in a room -> render_project_owner
// render_project_owner -> resolve if you are in a room -> Option<foo>
- pub fn render_project_owner(&self, cx: &mut ViewContext<Self>) -> Option<impl Element> {
+ pub fn render_project_host(&self, cx: &mut ViewContext<Self>) -> Option<impl Element> {
let host = self.project.read(cx).host()?;
let host = self.user_store.read(cx).get_cached_user(host.user_id)?;
let participant_index = self
@@ -509,169 +408,58 @@ impl CollabTitlebarItem {
)
}
- // fn collect_title_root_names(
- // &self,
- // theme: Arc<Theme>,
- // cx: &mut ViewContext<Self>,
- // ) -> AnyElement<Self> {
- // let project = self.project.read(cx);
-
- // let (name, entry) = {
- // let mut names_and_branches = project.visible_worktrees(cx).map(|worktree| {
- // let worktree = worktree.read(cx);
- // (worktree.root_name(), worktree.root_git_entry())
- // });
-
- // names_and_branches.next().unwrap_or(("", None))
- // };
-
- // let name = util::truncate_and_trailoff(name, MAX_PROJECT_NAME_LENGTH);
- // let branch_prepended = entry
- // .as_ref()
- // .and_then(RepositoryEntry::branch)
- // .map(|branch| util::truncate_and_trailoff(&branch, MAX_BRANCH_NAME_LENGTH));
- // let project_style = theme.titlebar.project_menu_button.clone();
- // let git_style = theme.titlebar.git_menu_button.clone();
- // let item_spacing = theme.titlebar.item_spacing;
-
- // let mut ret = Flex::row();
-
- // if let Some(project_host) = self.collect_project_host(theme.clone(), cx) {
- // ret = ret.with_child(project_host)
- // }
-
- // ret = ret.with_child(
- // Stack::new()
- // .with_child(
- // MouseEventHandler::new::<ToggleProjectMenu, _>(0, cx, |mouse_state, cx| {
- // let style = project_style
- // .in_state(self.project_popover.is_some())
- // .style_for(mouse_state);
- // enum RecentProjectsTooltip {}
- // Label::new(name, style.text.clone())
- // .contained()
- // .with_style(style.container)
- // .aligned()
- // .left()
- // .with_tooltip::<RecentProjectsTooltip>(
- // 0,
- // "Recent projects",
- // Some(Box::new(recent_projects::OpenRecent)),
- // theme.tooltip.clone(),
- // cx,
- // )
- // .into_any_named("title-project-name")
- // })
- // .with_cursor_style(CursorStyle::PointingHand)
- // .on_down(MouseButton::Left, move |_, this, cx| {
- // this.toggle_project_menu(&Default::default(), cx)
- // })
- // .on_click(MouseButton::Left, move |_, _, _| {}),
- // )
- // .with_children(self.render_project_popover_host(&theme.titlebar, cx)),
- // );
- // if let Some(git_branch) = branch_prepended {
- // ret = ret.with_child(
- // Flex::row().with_child(
- // Stack::new()
- // .with_child(
- // MouseEventHandler::new::<ToggleVcsMenu, _>(0, cx, |mouse_state, cx| {
- // enum BranchPopoverTooltip {}
- // let style = git_style
- // .in_state(self.branch_popover.is_some())
- // .style_for(mouse_state);
- // Label::new(git_branch, style.text.clone())
- // .contained()
- // .with_style(style.container.clone())
- // .with_margin_right(item_spacing)
- // .aligned()
- // .left()
- // .with_tooltip::<BranchPopoverTooltip>(
- // 0,
- // "Recent branches",
- // Some(Box::new(ToggleVcsMenu)),
- // theme.tooltip.clone(),
- // cx,
- // )
- // .into_any_named("title-project-branch")
- // })
- // .with_cursor_style(CursorStyle::PointingHand)
- // .on_down(MouseButton::Left, move |_, this, cx| {
- // this.toggle_vcs_menu(&Default::default(), cx)
- // })
- // .on_click(MouseButton::Left, move |_, _, _| {}),
- // )
- // .with_children(self.render_branches_popover_host(&theme.titlebar, cx)),
- // ),
- // )
- // }
- // ret.into_any()
- // }
-
- // fn collect_project_host(
- // &self,
- // theme: Arc<Theme>,
- // cx: &mut ViewContext<Self>,
- // ) -> Option<AnyElement<Self>> {
- // if ActiveCall::global(cx).read(cx).room().is_none() {
- // return None;
- // }
- // let project = self.project.read(cx);
- // let user_store = self.user_store.read(cx);
-
- // if project.is_local() {
- // return None;
- // }
-
- // let Some(host) = project.host() else {
- // return None;
- // };
- // let (Some(host_user), Some(participant_index)) = (
- // user_store.get_cached_user(host.user_id),
- // user_store.participant_indices().get(&host.user_id),
- // ) else {
- // return None;
- // };
-
- // enum ProjectHost {}
- // enum ProjectHostTooltip {}
-
- // let host_style = theme.titlebar.project_host.clone();
- // let selection_style = theme
- // .editor
- // .selection_style_for_room_participant(participant_index.0);
- // let peer_id = host.peer_id.clone();
-
- // Some(
- // MouseEventHandler::new::<ProjectHost, _>(0, cx, |mouse_state, _| {
- // let mut host_style = host_style.style_for(mouse_state).clone();
- // host_style.text.color = selection_style.cursor;
- // Label::new(host_user.github_login.clone(), host_style.text)
- // .contained()
- // .with_style(host_style.container)
- // .aligned()
- // .left()
- // })
- // .with_cursor_style(CursorStyle::PointingHand)
- // .on_click(MouseButton::Left, move |_, this, cx| {
- // if let Some(workspace) = this.workspace.upgrade(cx) {
- // if let Some(task) =
- // workspace.update(cx, |workspace, cx| workspace.follow(peer_id, cx))
- // {
- // task.detach_and_log_err(cx);
- // }
- // }
- // })
- // .with_tooltip::<ProjectHostTooltip>(
- // 0,
- // host_user.github_login.clone() + " is sharing this project. Click to follow.",
- // None,
- // theme.tooltip.clone(),
- // cx,
- // )
- // .into_any_named("project-host"),
- // )
- // }
+ fn render_collaborator(
+ &self,
+ user: &Arc<User>,
+ peer_id: PeerId,
+ location: ParticipantLocation,
+ is_speaking: bool,
+ is_muted: bool,
+ room: &Room,
+ project_id: u64,
+ current_user: &Arc<User>,
+ ) -> Option<FacePile> {
+ let followers = room.followers_for(peer_id, project_id);
+ let mut pile = FacePile::default();
+ pile.extend(
+ user.avatar
+ .clone()
+ .map(|avatar| {
+ div()
+ .child(
+ Avatar::data(avatar.clone())
+ .grayscale(
+ location != ParticipantLocation::SharedProject { project_id },
+ )
+ .border_color(if is_speaking {
+ gpui::blue()
+ } else if is_muted {
+ gpui::red()
+ } else {
+ Hsla::default()
+ }),
+ )
+ .into_any_element()
+ })
+ .into_iter()
+ .chain(followers.iter().filter_map(|follower_peer_id| {
+ let follower = room
+ .remote_participants()
+ .values()
+ .find_map(|p| (p.peer_id == *follower_peer_id).then_some(&p.user))
+ .or_else(|| {
+ (self.client.peer_id() == Some(*follower_peer_id))
+ .then_some(current_user)
+ })?
+ .clone();
+ follower
+ .avatar
+ .clone()
+ .map(|avatar| div().child(Avatar::data(avatar.clone())).into_any_element())
+ })),
+ );
+ Some(pile)
+ }
fn window_activation_changed(&mut self, cx: &mut ViewContext<Self>) {
let project = if cx.is_window_active() {
@@ -868,245 +656,6 @@ impl CollabTitlebarItem {
// cx.notify();
// }
- // fn render_toggle_screen_sharing_button(
- // &self,
- // theme: &Theme,
- // room: &ModelHandle<Room>,
- // cx: &mut ViewContext<Self>,
- // ) -> AnyElement<Self> {
- // let icon;
- // let tooltip;
- // if room.read(cx).is_screen_sharing() {
- // icon = "icons/desktop.svg";
- // tooltip = "Stop Sharing Screen"
- // } else {
- // icon = "icons/desktop.svg";
- // tooltip = "Share Screen";
- // }
-
- // let active = room.read(cx).is_screen_sharing();
- // let titlebar = &theme.titlebar;
- // MouseEventHandler::new::<ToggleScreenSharing, _>(0, cx, |state, _| {
- // let style = titlebar
- // .screen_share_button
- // .in_state(active)
- // .style_for(state);
-
- // Svg::new(icon)
- // .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)
- // })
- // .with_cursor_style(CursorStyle::PointingHand)
- // .on_click(MouseButton::Left, move |_, _, cx| {
- // toggle_screen_sharing(&Default::default(), cx)
- // })
- // .with_tooltip::<ToggleScreenSharing>(
- // 0,
- // tooltip,
- // Some(Box::new(ToggleScreenSharing)),
- // theme.tooltip.clone(),
- // cx,
- // )
- // .aligned()
- // .into_any()
- // }
- // fn render_toggle_mute(
- // &self,
- // theme: &Theme,
- // room: &ModelHandle<Room>,
- // cx: &mut ViewContext<Self>,
- // ) -> AnyElement<Self> {
- // let icon;
- // let tooltip;
- // let is_muted = room.read(cx).is_muted(cx);
- // if is_muted {
- // icon = "icons/mic-mute.svg";
- // tooltip = "Unmute microphone";
- // } else {
- // icon = "icons/mic.svg";
- // tooltip = "Mute microphone";
- // }
-
- // let titlebar = &theme.titlebar;
- // MouseEventHandler::new::<ToggleMute, _>(0, cx, |state, _| {
- // let style = titlebar
- // .toggle_microphone_button
- // .in_state(is_muted)
- // .style_for(state);
- // let image = Svg::new(icon)
- // .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);
- // if let Some(color) = style.container.background_color {
- // image.with_background_color(color)
- // } else {
- // image
- // }
- // })
- // .with_cursor_style(CursorStyle::PointingHand)
- // .on_click(MouseButton::Left, move |_, _, cx| {
- // toggle_mute(&Default::default(), cx)
- // })
- // .with_tooltip::<ToggleMute>(
- // 0,
- // tooltip,
- // Some(Box::new(ToggleMute)),
- // theme.tooltip.clone(),
- // cx,
- // )
- // .aligned()
- // .into_any()
- // }
- // fn render_toggle_deafen(
- // &self,
- // theme: &Theme,
- // room: &ModelHandle<Room>,
- // cx: &mut ViewContext<Self>,
- // ) -> AnyElement<Self> {
- // let icon;
- // let tooltip;
- // let is_deafened = room.read(cx).is_deafened().unwrap_or(false);
- // if is_deafened {
- // icon = "icons/speaker-off.svg";
- // tooltip = "Unmute speakers";
- // } else {
- // icon = "icons/speaker-loud.svg";
- // tooltip = "Mute speakers";
- // }
-
- // let titlebar = &theme.titlebar;
- // MouseEventHandler::new::<ToggleDeafen, _>(0, cx, |state, _| {
- // let style = titlebar
- // .toggle_speakers_button
- // .in_state(is_deafened)
- // .style_for(state);
- // Svg::new(icon)
- // .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)
- // })
- // .with_cursor_style(CursorStyle::PointingHand)
- // .on_click(MouseButton::Left, move |_, _, cx| {
- // toggle_deafen(&Default::default(), cx)
- // })
- // .with_tooltip::<ToggleDeafen>(
- // 0,
- // tooltip,
- // Some(Box::new(ToggleDeafen)),
- // theme.tooltip.clone(),
- // cx,
- // )
- // .aligned()
- // .into_any()
- // }
- // fn render_leave_call(&self, theme: &Theme, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
- // let icon = "icons/exit.svg";
- // let tooltip = "Leave call";
-
- // let titlebar = &theme.titlebar;
- // MouseEventHandler::new::<LeaveCall, _>(0, cx, |state, _| {
- // let style = titlebar.leave_call_button.style_for(state);
- // Svg::new(icon)
- // .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)
- // })
- // .with_cursor_style(CursorStyle::PointingHand)
- // .on_click(MouseButton::Left, move |_, _, cx| {
- // ActiveCall::global(cx)
- // .update(cx, |call, cx| call.hang_up(cx))
- // .detach_and_log_err(cx);
- // })
- // .with_tooltip::<LeaveCall>(
- // 0,
- // tooltip,
- // Some(Box::new(LeaveCall)),
- // theme.tooltip.clone(),
- // cx,
- // )
- // .aligned()
- // .into_any()
- // }
- // fn render_in_call_share_unshare_button(
- // &self,
- // workspace: &ViewHandle<Workspace>,
- // theme: &Theme,
- // cx: &mut ViewContext<Self>,
- // ) -> Option<AnyElement<Self>> {
- // let project = workspace.read(cx).project();
- // if project.read(cx).is_remote() {
- // return None;
- // }
-
- // let is_shared = project.read(cx).is_shared();
- // let label = if is_shared { "Stop Sharing" } else { "Share" };
- // let tooltip = if is_shared {
- // "Stop sharing project with call participants"
- // } else {
- // "Share project with call participants"
- // };
-
- // let titlebar = &theme.titlebar;
-
- // enum ShareUnshare {}
- // Some(
- // Stack::new()
- // .with_child(
- // MouseEventHandler::new::<ShareUnshare, _>(0, cx, |state, _| {
- // //TODO: Ensure this button has consistent width for both text variations
- // let style = titlebar.share_button.inactive_state().style_for(state);
- // Label::new(label, style.text.clone())
- // .contained()
- // .with_style(style.container)
- // })
- // .with_cursor_style(CursorStyle::PointingHand)
- // .on_click(MouseButton::Left, move |_, this, cx| {
- // if is_shared {
- // this.unshare_project(&Default::default(), cx);
- // } else {
- // this.share_project(&Default::default(), cx);
- // }
- // })
- // .with_tooltip::<ShareUnshare>(
- // 0,
- // tooltip.to_owned(),
- // None,
- // theme.tooltip.clone(),
- // cx,
- // ),
- // )
- // .aligned()
- // .contained()
- // .with_margin_left(theme.titlebar.item_spacing)
- // .into_any(),
- // )
- // }
-
// fn render_user_menu_button(
// &self,
// theme: &Theme,