title_bar: Factor out collab-related code into collab
module (#13957)
This PR refactors the `TitleBar` component to move all of the collab-related code into the `collab` module. This simplifies the top-level `Render` implementation of `TitleBar` by a lot and makes it easier to read. Release Notes: - N/A
This commit is contained in:
parent
9e36a66fec
commit
efc2336be5
3 changed files with 400 additions and 392 deletions
|
@ -1,58 +0,0 @@
|
|||
use call::{report_call_event_for_room, ActiveCall};
|
||||
use gpui::{actions, AppContext, Task, WindowContext};
|
||||
use workspace::notifications::DetachAndPromptErr;
|
||||
|
||||
actions!(
|
||||
collab,
|
||||
[ToggleScreenSharing, ToggleMute, ToggleDeafen, LeaveCall]
|
||||
);
|
||||
|
||||
pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut WindowContext) {
|
||||
let call = ActiveCall::global(cx).read(cx);
|
||||
if let Some(room) = call.room().cloned() {
|
||||
let client = call.client();
|
||||
let toggle_screen_sharing = room.update(cx, |room, cx| {
|
||||
if room.is_screen_sharing() {
|
||||
report_call_event_for_room(
|
||||
"disable screen share",
|
||||
room.id(),
|
||||
room.channel_id(),
|
||||
&client,
|
||||
);
|
||||
Task::ready(room.unshare_screen(cx))
|
||||
} else {
|
||||
report_call_event_for_room(
|
||||
"enable screen share",
|
||||
room.id(),
|
||||
room.channel_id(),
|
||||
&client,
|
||||
);
|
||||
room.share_screen(cx)
|
||||
}
|
||||
});
|
||||
toggle_screen_sharing.detach_and_prompt_err("Sharing Screen Failed", cx, |e, _| Some(format!("{:?}\n\nPlease check that you have given Zed permissions to record your screen in Settings.", e)));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle_mute(_: &ToggleMute, cx: &mut AppContext) {
|
||||
let call = ActiveCall::global(cx).read(cx);
|
||||
if let Some(room) = call.room().cloned() {
|
||||
let client = call.client();
|
||||
room.update(cx, |room, cx| {
|
||||
let operation = if room.is_muted() {
|
||||
"enable microphone"
|
||||
} else {
|
||||
"disable microphone"
|
||||
};
|
||||
report_call_event_for_room(operation, room.id(), room.channel_id(), &client);
|
||||
|
||||
room.toggle_mute(cx)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) {
|
||||
if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
|
||||
room.update(cx, |room, cx| room.toggle_deafen(cx));
|
||||
}
|
||||
}
|
|
@ -1,13 +1,72 @@
|
|||
use crate::TitleBar;
|
||||
use call::Room;
|
||||
use client::{proto::PeerId, User};
|
||||
use gpui::{canvas, point, Hsla, IntoElement, Path, Styled};
|
||||
use rpc::proto::{self};
|
||||
use std::sync::Arc;
|
||||
use theme::ActiveTheme;
|
||||
use ui::{prelude::*, Avatar, AvatarAudioStatusIndicator, Facepile, Tooltip};
|
||||
|
||||
pub(crate) fn render_color_ribbon(color: Hsla) -> impl Element {
|
||||
use call::{report_call_event_for_room, ActiveCall, ParticipantLocation, Room};
|
||||
use client::{proto::PeerId, User};
|
||||
use gpui::{actions, AppContext, Task, WindowContext};
|
||||
use gpui::{canvas, point, AnyElement, Hsla, IntoElement, MouseButton, Path, Styled};
|
||||
use rpc::proto::{self};
|
||||
use theme::ActiveTheme;
|
||||
use ui::{prelude::*, Avatar, AvatarAudioStatusIndicator, Facepile, TintColor, Tooltip};
|
||||
use workspace::notifications::DetachAndPromptErr;
|
||||
|
||||
use crate::TitleBar;
|
||||
|
||||
actions!(
|
||||
collab,
|
||||
[ToggleScreenSharing, ToggleMute, ToggleDeafen, LeaveCall]
|
||||
);
|
||||
|
||||
fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut WindowContext) {
|
||||
let call = ActiveCall::global(cx).read(cx);
|
||||
if let Some(room) = call.room().cloned() {
|
||||
let client = call.client();
|
||||
let toggle_screen_sharing = room.update(cx, |room, cx| {
|
||||
if room.is_screen_sharing() {
|
||||
report_call_event_for_room(
|
||||
"disable screen share",
|
||||
room.id(),
|
||||
room.channel_id(),
|
||||
&client,
|
||||
);
|
||||
Task::ready(room.unshare_screen(cx))
|
||||
} else {
|
||||
report_call_event_for_room(
|
||||
"enable screen share",
|
||||
room.id(),
|
||||
room.channel_id(),
|
||||
&client,
|
||||
);
|
||||
room.share_screen(cx)
|
||||
}
|
||||
});
|
||||
toggle_screen_sharing.detach_and_prompt_err("Sharing Screen Failed", cx, |e, _| Some(format!("{:?}\n\nPlease check that you have given Zed permissions to record your screen in Settings.", e)));
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_mute(_: &ToggleMute, cx: &mut AppContext) {
|
||||
let call = ActiveCall::global(cx).read(cx);
|
||||
if let Some(room) = call.room().cloned() {
|
||||
let client = call.client();
|
||||
room.update(cx, |room, cx| {
|
||||
let operation = if room.is_muted() {
|
||||
"enable microphone"
|
||||
} else {
|
||||
"disable microphone"
|
||||
};
|
||||
report_call_event_for_room(operation, room.id(), room.channel_id(), &client);
|
||||
|
||||
room.toggle_mute(cx)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) {
|
||||
if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
|
||||
room.update(cx, |room, cx| room.toggle_deafen(cx));
|
||||
}
|
||||
}
|
||||
|
||||
fn render_color_ribbon(color: Hsla) -> impl Element {
|
||||
canvas(
|
||||
move |_, _| {},
|
||||
move |bounds, _, cx| {
|
||||
|
@ -33,8 +92,99 @@ pub(crate) fn render_color_ribbon(color: Hsla) -> impl Element {
|
|||
}
|
||||
|
||||
impl TitleBar {
|
||||
pub(crate) fn render_collaborator_list(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
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 project_id = self.project.read(cx).remote_id();
|
||||
let workspace = self.workspace.upgrade();
|
||||
|
||||
h_flex()
|
||||
.id("collaborator-list")
|
||||
.w_full()
|
||||
.gap_1()
|
||||
.overflow_x_scroll()
|
||||
.when_some(
|
||||
current_user.clone().zip(client.peer_id()).zip(room.clone()),
|
||||
|this, ((current_user, peer_id), room)| {
|
||||
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);
|
||||
|
||||
let current_user_face_pile = self.render_collaborator(
|
||||
¤t_user,
|
||||
peer_id,
|
||||
true,
|
||||
room.is_speaking(),
|
||||
room.is_muted(),
|
||||
None,
|
||||
&room,
|
||||
project_id,
|
||||
¤t_user,
|
||||
cx,
|
||||
);
|
||||
|
||||
this.children(current_user_face_pile.map(|face_pile| {
|
||||
v_flex()
|
||||
.on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
|
||||
.child(face_pile)
|
||||
.child(render_color_ribbon(player_colors.local().cursor))
|
||||
}))
|
||||
.children(remote_participants.iter().filter_map(|collaborator| {
|
||||
let player_color =
|
||||
player_colors.color_for_participant(collaborator.participant_index.0);
|
||||
let is_following = workspace
|
||||
.as_ref()?
|
||||
.read(cx)
|
||||
.is_being_followed(collaborator.peer_id);
|
||||
let is_present = project_id.map_or(false, |project_id| {
|
||||
collaborator.location
|
||||
== ParticipantLocation::SharedProject { project_id }
|
||||
});
|
||||
|
||||
let facepile = self.render_collaborator(
|
||||
&collaborator.user,
|
||||
collaborator.peer_id,
|
||||
is_present,
|
||||
collaborator.speaking,
|
||||
collaborator.muted,
|
||||
is_following.then_some(player_color.selection),
|
||||
&room,
|
||||
project_id,
|
||||
¤t_user,
|
||||
cx,
|
||||
)?;
|
||||
|
||||
Some(
|
||||
v_flex()
|
||||
.id(("collaborator", collaborator.user.id))
|
||||
.child(facepile)
|
||||
.child(render_color_ribbon(player_color.cursor))
|
||||
.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);
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
})
|
||||
.tooltip({
|
||||
let login = collaborator.user.github_login.clone();
|
||||
move |cx| Tooltip::text(format!("Follow {login}"), cx)
|
||||
}),
|
||||
)
|
||||
}))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn render_collaborator(
|
||||
fn render_collaborator(
|
||||
&self,
|
||||
user: &Arc<User>,
|
||||
peer_id: PeerId,
|
||||
|
@ -123,4 +273,165 @@ impl TitleBar {
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn render_call_controls(&self, cx: &mut ViewContext<Self>) -> Vec<AnyElement> {
|
||||
let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() else {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
let room = room.read(cx);
|
||||
let project = self.project.read(cx);
|
||||
let is_local = project.is_local();
|
||||
let is_dev_server_project = project.dev_server_project_id().is_some();
|
||||
let is_shared = (is_local || is_dev_server_project) && project.is_shared();
|
||||
let is_muted = room.is_muted();
|
||||
let is_deafened = room.is_deafened().unwrap_or(false);
|
||||
let is_screen_sharing = room.is_screen_sharing();
|
||||
let can_use_microphone = room.can_use_microphone();
|
||||
let can_share_projects = room.can_share_projects();
|
||||
let platform_supported = match self.platform_style {
|
||||
PlatformStyle::Mac => true,
|
||||
PlatformStyle::Linux | PlatformStyle::Windows => false,
|
||||
};
|
||||
|
||||
let mut children = Vec::new();
|
||||
|
||||
if (is_local || is_dev_server_project) && can_share_projects {
|
||||
children.push(
|
||||
Button::new(
|
||||
"toggle_sharing",
|
||||
if is_shared { "Unshare" } else { "Share" },
|
||||
)
|
||||
.tooltip(move |cx| {
|
||||
Tooltip::text(
|
||||
if is_shared {
|
||||
"Stop sharing project with call participants"
|
||||
} else {
|
||||
"Share project with call participants"
|
||||
},
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.style(ButtonStyle::Subtle)
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
.selected(is_shared)
|
||||
.label_size(LabelSize::Small)
|
||||
.on_click(cx.listener(move |this, _, cx| {
|
||||
if is_shared {
|
||||
this.unshare_project(&Default::default(), cx);
|
||||
} else {
|
||||
this.share_project(&Default::default(), cx);
|
||||
}
|
||||
}))
|
||||
.into_any_element(),
|
||||
);
|
||||
}
|
||||
|
||||
children.push(
|
||||
div()
|
||||
.pr_2()
|
||||
.child(
|
||||
IconButton::new("leave-call", ui::IconName::Exit)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.tooltip(|cx| Tooltip::text("Leave call", cx))
|
||||
.icon_size(IconSize::Small)
|
||||
.on_click(move |_, cx| {
|
||||
ActiveCall::global(cx)
|
||||
.update(cx, |call, cx| call.hang_up(cx))
|
||||
.detach_and_log_err(cx);
|
||||
}),
|
||||
)
|
||||
.into_any_element(),
|
||||
);
|
||||
|
||||
if can_use_microphone {
|
||||
children.push(
|
||||
IconButton::new(
|
||||
"mute-microphone",
|
||||
if is_muted {
|
||||
ui::IconName::MicMute
|
||||
} else {
|
||||
ui::IconName::Mic
|
||||
},
|
||||
)
|
||||
.tooltip(move |cx| {
|
||||
Tooltip::text(
|
||||
if !platform_supported {
|
||||
"Cannot share microphone"
|
||||
} else if is_muted {
|
||||
"Unmute microphone"
|
||||
} else {
|
||||
"Mute microphone"
|
||||
},
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.style(ButtonStyle::Subtle)
|
||||
.icon_size(IconSize::Small)
|
||||
.selected(platform_supported && is_muted)
|
||||
.disabled(!platform_supported)
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Negative))
|
||||
.on_click(move |_, cx| {
|
||||
toggle_mute(&Default::default(), cx);
|
||||
})
|
||||
.into_any_element(),
|
||||
);
|
||||
}
|
||||
|
||||
children.push(
|
||||
IconButton::new(
|
||||
"mute-sound",
|
||||
if is_deafened {
|
||||
ui::IconName::AudioOff
|
||||
} else {
|
||||
ui::IconName::AudioOn
|
||||
},
|
||||
)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Negative))
|
||||
.icon_size(IconSize::Small)
|
||||
.selected(is_deafened)
|
||||
.disabled(!platform_supported)
|
||||
.tooltip(move |cx| {
|
||||
if !platform_supported {
|
||||
Tooltip::text("Cannot share microphone", cx)
|
||||
} else if can_use_microphone {
|
||||
Tooltip::with_meta("Deafen Audio", None, "Mic will be muted", cx)
|
||||
} else {
|
||||
Tooltip::text("Deafen Audio", cx)
|
||||
}
|
||||
})
|
||||
.on_click(move |_, cx| toggle_deafen(&Default::default(), cx))
|
||||
.into_any_element(),
|
||||
);
|
||||
|
||||
if can_share_projects {
|
||||
children.push(
|
||||
IconButton::new("screen-share", ui::IconName::Screen)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.icon_size(IconSize::Small)
|
||||
.selected(is_screen_sharing)
|
||||
.disabled(!platform_supported)
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
.tooltip(move |cx| {
|
||||
Tooltip::text(
|
||||
if !platform_supported {
|
||||
"Cannot share screen"
|
||||
} else if is_screen_sharing {
|
||||
"Stop Sharing Screen"
|
||||
} else {
|
||||
"Share Screen"
|
||||
},
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.on_click(move |_, cx| toggle_screen_sharing(&Default::default(), cx))
|
||||
.into_any_element(),
|
||||
);
|
||||
}
|
||||
|
||||
children.push(div().pr_2().into_any_element());
|
||||
|
||||
children
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
mod application_menu;
|
||||
mod call_controls;
|
||||
mod collab;
|
||||
mod platforms;
|
||||
mod window_controls;
|
||||
|
@ -10,9 +9,8 @@ mod stories;
|
|||
use crate::application_menu::ApplicationMenu;
|
||||
use crate::platforms::{platform_linux, platform_mac, platform_windows};
|
||||
use auto_update::AutoUpdateStatus;
|
||||
use call::{ActiveCall, ParticipantLocation};
|
||||
use call::ActiveCall;
|
||||
use client::{Client, UserStore};
|
||||
use collab::render_color_ribbon;
|
||||
use gpui::{
|
||||
actions, div, px, Action, AnyElement, AppContext, Decorations, Element, InteractiveElement,
|
||||
Interactivity, IntoElement, Model, MouseButton, ParentElement, Render, Stateful,
|
||||
|
@ -25,8 +23,8 @@ use smallvec::SmallVec;
|
|||
use std::sync::Arc;
|
||||
use theme::ActiveTheme;
|
||||
use ui::{
|
||||
h_flex, prelude::*, Avatar, Button, ButtonLike, ButtonStyle, ContextMenu, Icon, IconButton,
|
||||
IconName, Indicator, PopoverMenu, TintColor, Tooltip,
|
||||
h_flex, prelude::*, Avatar, Button, ButtonLike, ButtonStyle, ContextMenu, Icon, IconName,
|
||||
Indicator, PopoverMenu, Tooltip,
|
||||
};
|
||||
use util::ResultExt;
|
||||
use vcs_menu::{BranchList, OpenRecent as ToggleVcsMenu};
|
||||
|
@ -71,15 +69,7 @@ pub struct TitleBar {
|
|||
|
||||
impl Render for TitleBar {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
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 project_id = self.project.read(cx).remote_id();
|
||||
let workspace = self.workspace.upgrade();
|
||||
let close_action = Box::new(workspace::CloseWindow);
|
||||
|
||||
let platform_supported = cfg!(target_os = "macos");
|
||||
|
||||
let height = Self::height(cx);
|
||||
let supported_controls = cx.window_controls();
|
||||
let decorations = cx.window_decorations();
|
||||
|
@ -98,15 +88,15 @@ impl Render for TitleBar {
|
|||
this.pl_2()
|
||||
}
|
||||
})
|
||||
.map(|el| {
|
||||
match decorations {
|
||||
Decorations::Server => el,
|
||||
Decorations::Client { tiling, .. } => el
|
||||
.when(!(tiling.top || tiling.right), |el| {
|
||||
el.rounded_tr(theme::CLIENT_SIDE_DECORATION_ROUNDING)
|
||||
})
|
||||
.when(!(tiling.top || tiling.left), |el| el.rounded_tl(theme::CLIENT_SIDE_DECORATION_ROUNDING))
|
||||
}
|
||||
.map(|el| match decorations {
|
||||
Decorations::Server => el,
|
||||
Decorations::Client { tiling, .. } => el
|
||||
.when(!(tiling.top || tiling.right), |el| {
|
||||
el.rounded_tr(theme::CLIENT_SIDE_DECORATION_ROUNDING)
|
||||
})
|
||||
.when(!(tiling.top || tiling.left), |el| {
|
||||
el.rounded_tl(theme::CLIENT_SIDE_DECORATION_ROUNDING)
|
||||
}),
|
||||
})
|
||||
.bg(cx.theme().colors().title_bar_background)
|
||||
.content_stretch()
|
||||
|
@ -117,317 +107,82 @@ impl Render for TitleBar {
|
|||
.flex_row()
|
||||
.justify_between()
|
||||
.w_full()
|
||||
// note: on windows titlebar behaviour is handled by the platform implementation
|
||||
.when(cfg!(not(windows)), |this| {
|
||||
// Note: On Windows the title bar behavior is handled by the platform implementation.
|
||||
.when(self.platform_style != PlatformStyle::Windows, |this| {
|
||||
this.on_click(|event, cx| {
|
||||
if event.up.click_count == 2 {
|
||||
cx.zoom_window();
|
||||
}
|
||||
})
|
||||
})
|
||||
// left side
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.children(match self.platform_style {
|
||||
PlatformStyle::Mac => None,
|
||||
PlatformStyle::Linux | PlatformStyle::Windows => Some(ApplicationMenu::new())
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.children(match self.platform_style {
|
||||
PlatformStyle::Mac => None,
|
||||
PlatformStyle::Linux | PlatformStyle::Windows => {
|
||||
Some(ApplicationMenu::new())
|
||||
}
|
||||
})
|
||||
.children(self.render_project_host(cx))
|
||||
.child(self.render_project_name(cx))
|
||||
.children(self.render_project_branch(cx))
|
||||
.on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation()),
|
||||
)
|
||||
.child(self.render_collaborator_list(cx))
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.pr_1()
|
||||
.on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
|
||||
.children(self.render_call_controls(cx))
|
||||
.map(|el| {
|
||||
let status = self.client.status();
|
||||
let status = &*status.borrow();
|
||||
if matches!(status, client::Status::Connected { .. }) {
|
||||
el.child(self.render_user_menu_button(cx))
|
||||
} else {
|
||||
el.children(self.render_connection_status(status, cx))
|
||||
.child(self.render_sign_in_button(cx))
|
||||
.child(self.render_user_menu_button(cx))
|
||||
}
|
||||
}),
|
||||
),
|
||||
)
|
||||
.when(!cx.is_fullscreen(), |title_bar| match self.platform_style {
|
||||
PlatformStyle::Mac => title_bar,
|
||||
PlatformStyle::Linux => {
|
||||
if matches!(decorations, Decorations::Client { .. }) {
|
||||
title_bar
|
||||
.child(platform_linux::LinuxWindowControls::new(close_action))
|
||||
.when(supported_controls.window_menu, |titlebar| {
|
||||
titlebar.on_mouse_down(gpui::MouseButton::Right, move |ev, cx| {
|
||||
cx.show_window_menu(ev.position)
|
||||
})
|
||||
.children(self.render_project_host(cx))
|
||||
.child(self.render_project_name(cx))
|
||||
.children(self.render_project_branch(cx))
|
||||
.on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.id("collaborator-list")
|
||||
.w_full()
|
||||
.gap_1()
|
||||
.overflow_x_scroll()
|
||||
.when_some(
|
||||
current_user.clone().zip(client.peer_id()).zip(room.clone()),
|
||||
|this, ((current_user, peer_id), room)| {
|
||||
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);
|
||||
|
||||
let current_user_face_pile = self.render_collaborator(
|
||||
¤t_user,
|
||||
peer_id,
|
||||
true,
|
||||
room.is_speaking(),
|
||||
room.is_muted(),
|
||||
None,
|
||||
&room,
|
||||
project_id,
|
||||
¤t_user,
|
||||
cx,
|
||||
);
|
||||
|
||||
this.children(current_user_face_pile.map(|face_pile| {
|
||||
v_flex()
|
||||
.on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
|
||||
.child(face_pile)
|
||||
.child(render_color_ribbon(player_colors.local().cursor))
|
||||
}))
|
||||
.children(
|
||||
remote_participants.iter().filter_map(|collaborator| {
|
||||
let player_color = player_colors
|
||||
.color_for_participant(collaborator.participant_index.0);
|
||||
let is_following = workspace
|
||||
.as_ref()?
|
||||
.read(cx)
|
||||
.is_being_followed(collaborator.peer_id);
|
||||
let is_present = project_id.map_or(false, |project_id| {
|
||||
collaborator.location
|
||||
== ParticipantLocation::SharedProject { project_id }
|
||||
});
|
||||
|
||||
let facepile = self.render_collaborator(
|
||||
&collaborator.user,
|
||||
collaborator.peer_id,
|
||||
is_present,
|
||||
collaborator.speaking,
|
||||
collaborator.muted,
|
||||
is_following.then_some(player_color.selection),
|
||||
&room,
|
||||
project_id,
|
||||
¤t_user,
|
||||
cx,
|
||||
)?;
|
||||
|
||||
Some(
|
||||
v_flex()
|
||||
.id(("collaborator", collaborator.user.id))
|
||||
.child(facepile)
|
||||
.child(render_color_ribbon(player_color.cursor))
|
||||
.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);
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
})
|
||||
.tooltip({
|
||||
let login = collaborator.user.github_login.clone();
|
||||
move |cx| {
|
||||
Tooltip::text(format!("Follow {login}"), cx)
|
||||
}
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
// right side
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.pr_1()
|
||||
.on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
|
||||
.when_some(room, |this, room| {
|
||||
let room = room.read(cx);
|
||||
let project = self.project.read(cx);
|
||||
let is_local = project.is_local();
|
||||
let is_dev_server_project = project.dev_server_project_id().is_some();
|
||||
let is_shared = (is_local || is_dev_server_project) && project.is_shared();
|
||||
let is_muted = room.is_muted();
|
||||
let is_deafened = room.is_deafened().unwrap_or(false);
|
||||
let is_screen_sharing = room.is_screen_sharing();
|
||||
let can_use_microphone = room.can_use_microphone();
|
||||
let can_share_projects = room.can_share_projects();
|
||||
|
||||
this.when(
|
||||
(is_local || is_dev_server_project) && can_share_projects,
|
||||
|this| {
|
||||
this.child(
|
||||
Button::new(
|
||||
"toggle_sharing",
|
||||
if is_shared { "Unshare" } else { "Share" },
|
||||
)
|
||||
.tooltip(move |cx| {
|
||||
Tooltip::text(
|
||||
if is_shared {
|
||||
"Stop sharing project with call participants"
|
||||
} else {
|
||||
"Share project with call participants"
|
||||
},
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.style(ButtonStyle::Subtle)
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
.selected(is_shared)
|
||||
.label_size(LabelSize::Small)
|
||||
.on_click(cx.listener(
|
||||
move |this, _, cx| {
|
||||
if is_shared {
|
||||
this.unshare_project(&Default::default(), cx);
|
||||
} else {
|
||||
this.share_project(&Default::default(), cx);
|
||||
}
|
||||
},
|
||||
)),
|
||||
)
|
||||
},
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.child(
|
||||
IconButton::new("leave-call", ui::IconName::Exit)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.tooltip(|cx| Tooltip::text("Leave call", cx))
|
||||
.icon_size(IconSize::Small)
|
||||
.on_click(move |_, cx| {
|
||||
ActiveCall::global(cx)
|
||||
.update(cx, |call, cx| call.hang_up(cx))
|
||||
.detach_and_log_err(cx);
|
||||
}),
|
||||
)
|
||||
.pr_2(),
|
||||
)
|
||||
.when(can_use_microphone, |this| {
|
||||
this.child(
|
||||
IconButton::new(
|
||||
"mute-microphone",
|
||||
if is_muted {
|
||||
ui::IconName::MicMute
|
||||
} else {
|
||||
ui::IconName::Mic
|
||||
},
|
||||
)
|
||||
.tooltip(move |cx| {
|
||||
Tooltip::text(
|
||||
if !platform_supported {
|
||||
"Cannot share microphone"
|
||||
} else if is_muted {
|
||||
"Unmute microphone"
|
||||
} else {
|
||||
"Mute microphone"
|
||||
},
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.style(ButtonStyle::Subtle)
|
||||
.icon_size(IconSize::Small)
|
||||
.selected(platform_supported && is_muted)
|
||||
.disabled(!platform_supported)
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Negative))
|
||||
.on_click(move |_, cx| {
|
||||
call_controls::toggle_mute(&Default::default(), cx);
|
||||
}),
|
||||
)
|
||||
})
|
||||
.child(
|
||||
IconButton::new(
|
||||
"mute-sound",
|
||||
if is_deafened {
|
||||
ui::IconName::AudioOff
|
||||
} else {
|
||||
ui::IconName::AudioOn
|
||||
},
|
||||
)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Negative))
|
||||
.icon_size(IconSize::Small)
|
||||
.selected(is_deafened)
|
||||
.disabled(!platform_supported)
|
||||
.tooltip(move |cx| {
|
||||
if !platform_supported {
|
||||
Tooltip::text("Cannot share microphone", cx)
|
||||
} else if can_use_microphone {
|
||||
Tooltip::with_meta(
|
||||
"Deafen Audio",
|
||||
None,
|
||||
"Mic will be muted",
|
||||
cx,
|
||||
)
|
||||
} else {
|
||||
Tooltip::text("Deafen Audio", cx)
|
||||
}
|
||||
})
|
||||
.on_click(move |_, cx| {
|
||||
call_controls::toggle_deafen(&Default::default(), cx)
|
||||
}),
|
||||
)
|
||||
.when(can_share_projects, |this| {
|
||||
this.child(
|
||||
IconButton::new("screen-share", ui::IconName::Screen)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.icon_size(IconSize::Small)
|
||||
.selected(is_screen_sharing)
|
||||
.disabled(!platform_supported)
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
.tooltip(move |cx| {
|
||||
Tooltip::text(
|
||||
if !platform_supported {
|
||||
"Cannot share screen"
|
||||
} else if is_screen_sharing {
|
||||
"Stop Sharing Screen"
|
||||
} else {
|
||||
"Share Screen"
|
||||
},
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.on_click(move |_, cx| {
|
||||
call_controls::toggle_screen_sharing(&Default::default(), cx)
|
||||
}),
|
||||
)
|
||||
})
|
||||
.child(div().pr_2())
|
||||
})
|
||||
.map(|el| {
|
||||
let status = self.client.status();
|
||||
let status = &*status.borrow();
|
||||
if matches!(status, client::Status::Connected { .. }) {
|
||||
el.child(self.render_user_menu_button(cx))
|
||||
} else {
|
||||
el.children(self.render_connection_status(status, cx))
|
||||
.child(self.render_sign_in_button(cx))
|
||||
.child(self.render_user_menu_button(cx))
|
||||
}
|
||||
})
|
||||
.on_mouse_move(cx.listener(move |this, _ev, cx| {
|
||||
if this.should_move {
|
||||
this.should_move = false;
|
||||
cx.start_window_move();
|
||||
}
|
||||
}))
|
||||
.on_mouse_down_out(cx.listener(move |this, _ev, _cx| {
|
||||
this.should_move = false;
|
||||
}))
|
||||
.on_mouse_down(
|
||||
gpui::MouseButton::Left,
|
||||
cx.listener(move |this, _ev, _cx| {
|
||||
this.should_move = true;
|
||||
}),
|
||||
)
|
||||
|
||||
).when(
|
||||
self.platform_style == PlatformStyle::Windows && !cx.is_fullscreen(),
|
||||
|title_bar| title_bar.child(platform_windows::WindowsWindowControls::new(height)),
|
||||
).when(
|
||||
self.platform_style == PlatformStyle::Linux
|
||||
&& !cx.is_fullscreen()
|
||||
&& matches!(decorations, Decorations::Client { .. }),
|
||||
|title_bar| {
|
||||
title_bar
|
||||
.child(platform_linux::LinuxWindowControls::new(close_action))
|
||||
.when(supported_controls.window_menu, |titlebar| {
|
||||
titlebar.on_mouse_down(gpui::MouseButton::Right, move |ev, cx| {
|
||||
cx.show_window_menu(ev.position)
|
||||
})
|
||||
})
|
||||
|
||||
.on_mouse_move(cx.listener(move |this, _ev, cx| {
|
||||
if this.should_move {
|
||||
this.should_move = false;
|
||||
cx.start_window_move();
|
||||
}
|
||||
}))
|
||||
.on_mouse_down_out(cx.listener(move |this, _ev, _cx| {
|
||||
this.should_move = false;
|
||||
}))
|
||||
.on_mouse_down(gpui::MouseButton::Left, cx.listener(move |this, _ev, _cx| {
|
||||
this.should_move = true;
|
||||
}))
|
||||
|
||||
},
|
||||
)
|
||||
)
|
||||
} else {
|
||||
title_bar
|
||||
}
|
||||
}
|
||||
PlatformStyle::Windows => {
|
||||
title_bar.child(platform_windows::WindowsWindowControls::new(height))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue