From 2e03c848e31257ddae10dcbe6d5be793d6394966 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Tue, 16 Jan 2024 16:18:06 -0500 Subject: [PATCH] Add dedicated indicator for showing a muted call participant (#4076) This PR improves the muted indicators to make it clearer when a call participant is muted. Previously we used a red border color to denote when a participant was muted. Now we render an indicator with an icon to more clearly indicate the participant's muted status: Screenshot 2024-01-16 at 4 05 15 PM Hovering over the indicator will display a tooltip for further explanation: Screenshot 2024-01-16 at 4 05 25 PM This change also paves the way for denoting the deafened status for call participants. Release Notes: - Improved the mute indicator for call participants. --- crates/collab_ui/src/collab_titlebar_item.rs | 20 +++--- crates/ui/src/components/avatar.rs | 2 + crates/ui/src/components/avatar/avatar.rs | 1 + .../avatar/avatar_audio_status_indicator.rs | 65 +++++++++++++++++++ crates/ui/src/components/icon.rs | 2 + crates/ui/src/components/stories/avatar.rs | 12 +++- 6 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 crates/ui/src/components/avatar/avatar_audio_status_indicator.rs diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 72b16d49185f6928ca9195c6159e7395add273d2..9f11870d983a20013666ad61c5a6cd1bd30bdba3 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -14,8 +14,8 @@ use rpc::proto; use std::sync::Arc; use theme::ActiveTheme; use ui::{ - h_flex, popover_menu, prelude::*, Avatar, Button, ButtonLike, ButtonStyle, ContextMenu, Icon, - IconButton, IconName, TintColor, Tooltip, + h_flex, popover_menu, prelude::*, Avatar, AvatarAudioStatusIndicator, Button, ButtonLike, + ButtonStyle, ContextMenu, Icon, IconButton, IconName, TintColor, Tooltip, }; use util::ResultExt; use vcs_menu::{build_branch_list, BranchList, OpenRecent as ToggleVcsMenu}; @@ -486,12 +486,16 @@ impl CollabTitlebarItem { .child( Avatar::new(user.avatar_uri.clone()) .grayscale(!is_present) - .border_color(if is_speaking { - cx.theme().status().info_border - } else if is_muted { - cx.theme().status().error_border - } else { - Hsla::default() + .when(is_speaking, |avatar| { + avatar.border_color(cx.theme().status().info_border) + }) + .when(is_muted, |avatar| { + avatar.indicator( + AvatarAudioStatusIndicator::new(ui::AudioStatus::Muted).tooltip({ + let github_login = user.github_login.clone(); + move |cx| Tooltip::text(format!("{} is muted", github_login), cx) + }), + ) }), ) .children(followers.iter().filter_map(|follower_peer_id| { diff --git a/crates/ui/src/components/avatar.rs b/crates/ui/src/components/avatar.rs index b200828ce6cd5a4ac190faf65f8417e08844cf43..6c2d88916e7fe47f9fbcfe86b7d88ebd43e4b62f 100644 --- a/crates/ui/src/components/avatar.rs +++ b/crates/ui/src/components/avatar.rs @@ -1,5 +1,7 @@ mod avatar; +mod avatar_audio_status_indicator; mod avatar_availability_indicator; pub use avatar::*; +pub use avatar_audio_status_indicator::*; pub use avatar_availability_indicator::*; diff --git a/crates/ui/src/components/avatar/avatar.rs b/crates/ui/src/components/avatar/avatar.rs index 22ba255a60f12742e173f016886ce25b404d2c0f..5154d90bd2e38718d003ad167a4eabb7a5852479 100644 --- a/crates/ui/src/components/avatar/avatar.rs +++ b/crates/ui/src/components/avatar/avatar.rs @@ -1,4 +1,5 @@ use crate::prelude::*; + use gpui::{img, AnyElement, Hsla, ImageSource, Img, IntoElement, Styled}; /// The shape of an [`Avatar`]. diff --git a/crates/ui/src/components/avatar/avatar_audio_status_indicator.rs b/crates/ui/src/components/avatar/avatar_audio_status_indicator.rs new file mode 100644 index 0000000000000000000000000000000000000000..55f98d3db3b8a49f827ffac0d13d69c00dfc5d75 --- /dev/null +++ b/crates/ui/src/components/avatar/avatar_audio_status_indicator.rs @@ -0,0 +1,65 @@ +use gpui::AnyView; + +use crate::prelude::*; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] +pub enum AudioStatus { + Muted, + Deafened, +} + +#[derive(IntoElement)] +pub struct AvatarAudioStatusIndicator { + audio_status: AudioStatus, + tooltip: Option AnyView>>, +} + +impl AvatarAudioStatusIndicator { + pub fn new(audio_status: AudioStatus) -> Self { + Self { + audio_status, + tooltip: None, + } + } + + pub fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self { + self.tooltip = Some(Box::new(tooltip)); + self + } +} + +impl RenderOnce for AvatarAudioStatusIndicator { + fn render(self, cx: &mut WindowContext) -> impl IntoElement { + let icon_size = IconSize::Indicator; + + let width_in_px = icon_size.rems() * cx.rem_size(); + let padding_x = px(4.); + + div() + .absolute() + .bottom(rems(-1. / 16.)) + .right(rems(-4. / 16.)) + .w(width_in_px + padding_x) + .h(icon_size.rems()) + .child( + h_flex() + .id("muted-indicator") + .justify_center() + .px(padding_x) + .py(px(2.)) + .bg(cx.theme().status().error_background) + .rounded_md() + .child( + Icon::new(match self.audio_status { + AudioStatus::Muted => IconName::MicMute, + AudioStatus::Deafened => IconName::AudioOff, + }) + .size(icon_size) + .color(Color::Error), + ) + .when_some(self.tooltip, |this, tooltip| { + this.tooltip(move |cx| tooltip(cx)) + }), + ) + } +} diff --git a/crates/ui/src/components/icon.rs b/crates/ui/src/components/icon.rs index 908e76ef918b56aefff6949e86ea6473a272253d..bdc691dc9a26e178ad65eaaeda85caa7973dd526 100644 --- a/crates/ui/src/components/icon.rs +++ b/crates/ui/src/components/icon.rs @@ -5,6 +5,7 @@ use crate::prelude::*; #[derive(Default, PartialEq, Copy, Clone)] pub enum IconSize { + Indicator, XSmall, Small, #[default] @@ -14,6 +15,7 @@ pub enum IconSize { impl IconSize { pub fn rems(self) -> Rems { match self { + IconSize::Indicator => rems(10. / 16.), IconSize::XSmall => rems(12. / 16.), IconSize::Small => rems(14. / 16.), IconSize::Medium => rems(16. / 16.), diff --git a/crates/ui/src/components/stories/avatar.rs b/crates/ui/src/components/stories/avatar.rs index c7aae8a73cde4098dca874754c40dd994a1da934..c3409b1ca81dfcf412111eb832a61b83c1a768bc 100644 --- a/crates/ui/src/components/stories/avatar.rs +++ b/crates/ui/src/components/stories/avatar.rs @@ -1,8 +1,8 @@ use gpui::Render; use story::Story; -use crate::Avatar; -use crate::{prelude::*, Availability, AvatarAvailabilityIndicator}; +use crate::{prelude::*, AudioStatus, Availability, AvatarAvailabilityIndicator}; +use crate::{Avatar, AvatarAudioStatusIndicator}; pub struct AvatarStory; @@ -25,5 +25,13 @@ impl Render for AvatarStory { Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4") .indicator(AvatarAvailabilityIndicator::new(Availability::Busy)), ) + .child( + Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4") + .indicator(AvatarAudioStatusIndicator::new(AudioStatus::Muted)), + ) + .child( + Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4") + .indicator(AvatarAudioStatusIndicator::new(AudioStatus::Deafened)), + ) } }