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: <img width="303" alt="Screenshot 2024-01-16 at 4 05 15 PM" src="https://github.com/zed-industries/zed/assets/1486634/d30fcd84-48e7-4959-b3c4-1054162c6bd6"> Hovering over the indicator will display a tooltip for further explanation: <img width="456" alt="Screenshot 2024-01-16 at 4 05 25 PM" src="https://github.com/zed-industries/zed/assets/1486634/6345846f-196c-47d9-8d65-c8d86e63f823"> This change also paves the way for denoting the deafened status for call participants. Release Notes: - Improved the mute indicator for call participants.
This commit is contained in:
parent
9903b7ae6e
commit
2e03c848e3
6 changed files with 92 additions and 10 deletions
|
@ -14,8 +14,8 @@ use rpc::proto;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
use ui::{
|
use ui::{
|
||||||
h_flex, popover_menu, prelude::*, Avatar, Button, ButtonLike, ButtonStyle, ContextMenu, Icon,
|
h_flex, popover_menu, prelude::*, Avatar, AvatarAudioStatusIndicator, Button, ButtonLike,
|
||||||
IconButton, IconName, TintColor, Tooltip,
|
ButtonStyle, ContextMenu, Icon, IconButton, IconName, TintColor, Tooltip,
|
||||||
};
|
};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use vcs_menu::{build_branch_list, BranchList, OpenRecent as ToggleVcsMenu};
|
use vcs_menu::{build_branch_list, BranchList, OpenRecent as ToggleVcsMenu};
|
||||||
|
@ -486,12 +486,16 @@ impl CollabTitlebarItem {
|
||||||
.child(
|
.child(
|
||||||
Avatar::new(user.avatar_uri.clone())
|
Avatar::new(user.avatar_uri.clone())
|
||||||
.grayscale(!is_present)
|
.grayscale(!is_present)
|
||||||
.border_color(if is_speaking {
|
.when(is_speaking, |avatar| {
|
||||||
cx.theme().status().info_border
|
avatar.border_color(cx.theme().status().info_border)
|
||||||
} else if is_muted {
|
})
|
||||||
cx.theme().status().error_border
|
.when(is_muted, |avatar| {
|
||||||
} else {
|
avatar.indicator(
|
||||||
Hsla::default()
|
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| {
|
.children(followers.iter().filter_map(|follower_peer_id| {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
mod avatar;
|
mod avatar;
|
||||||
|
mod avatar_audio_status_indicator;
|
||||||
mod avatar_availability_indicator;
|
mod avatar_availability_indicator;
|
||||||
|
|
||||||
pub use avatar::*;
|
pub use avatar::*;
|
||||||
|
pub use avatar_audio_status_indicator::*;
|
||||||
pub use avatar_availability_indicator::*;
|
pub use avatar_availability_indicator::*;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use gpui::{img, AnyElement, Hsla, ImageSource, Img, IntoElement, Styled};
|
use gpui::{img, AnyElement, Hsla, ImageSource, Img, IntoElement, Styled};
|
||||||
|
|
||||||
/// The shape of an [`Avatar`].
|
/// The shape of an [`Avatar`].
|
||||||
|
|
|
@ -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<Box<dyn Fn(&mut WindowContext) -> 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))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ use crate::prelude::*;
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Copy, Clone)]
|
#[derive(Default, PartialEq, Copy, Clone)]
|
||||||
pub enum IconSize {
|
pub enum IconSize {
|
||||||
|
Indicator,
|
||||||
XSmall,
|
XSmall,
|
||||||
Small,
|
Small,
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -14,6 +15,7 @@ pub enum IconSize {
|
||||||
impl IconSize {
|
impl IconSize {
|
||||||
pub fn rems(self) -> Rems {
|
pub fn rems(self) -> Rems {
|
||||||
match self {
|
match self {
|
||||||
|
IconSize::Indicator => rems(10. / 16.),
|
||||||
IconSize::XSmall => rems(12. / 16.),
|
IconSize::XSmall => rems(12. / 16.),
|
||||||
IconSize::Small => rems(14. / 16.),
|
IconSize::Small => rems(14. / 16.),
|
||||||
IconSize::Medium => rems(16. / 16.),
|
IconSize::Medium => rems(16. / 16.),
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use gpui::Render;
|
use gpui::Render;
|
||||||
use story::Story;
|
use story::Story;
|
||||||
|
|
||||||
use crate::Avatar;
|
use crate::{prelude::*, AudioStatus, Availability, AvatarAvailabilityIndicator};
|
||||||
use crate::{prelude::*, Availability, AvatarAvailabilityIndicator};
|
use crate::{Avatar, AvatarAudioStatusIndicator};
|
||||||
|
|
||||||
pub struct AvatarStory;
|
pub struct AvatarStory;
|
||||||
|
|
||||||
|
@ -25,5 +25,13 @@ impl Render for AvatarStory {
|
||||||
Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
|
Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
|
||||||
.indicator(AvatarAvailabilityIndicator::new(Availability::Busy)),
|
.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)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue