diff --git a/crates/chat_ui/src/lib.rs b/crates/chat_ui/src/lib.rs index 5de1a90..e7f078d 100644 --- a/crates/chat_ui/src/lib.rs +++ b/crates/chat_ui/src/lib.rs @@ -8,7 +8,7 @@ use chat::{Message, RenderedMessage, Room, RoomEvent, SendReport}; use common::RenderedTimestamp; use gpui::prelude::FluentBuilder; use gpui::{ - deferred, div, img, list, px, red, relative, rems, svg, white, AnyElement, App, AppContext, + deferred, div, img, list, px, red, relative, svg, white, AnyElement, App, AppContext, ClipboardItem, Context, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement, IntoElement, ListAlignment, ListOffset, ListState, MouseButton, ObjectFit, ParentElement, PathPromptOptions, Render, SharedString, StatefulInteractiveElement, Styled, StyledImage, @@ -758,7 +758,7 @@ impl ChatPanel { this.child( div() .id(SharedString::from(format!("{ix}-avatar"))) - .child(Avatar::new(author.avatar()).size(rems(2.))) + .child(Avatar::new(author.avatar())) .context_menu(move |this, _window, _cx| { let view = Box::new(OpenPublicKey(public_key)); let copy = Box::new(CopyPublicKey(public_key)); @@ -940,7 +940,7 @@ impl ChatPanel { h_flex() .gap_1() .font_semibold() - .child(Avatar::new(avatar).size(rems(1.25))) + .child(Avatar::new(avatar).small()) .child(name.clone()), ), ) @@ -1283,7 +1283,7 @@ impl Panel for ChatPanel { h_flex() .gap_1p5() - .child(Avatar::new(url).size(rems(1.25))) + .child(Avatar::new(url).small()) .child(label) .into_any_element() }) diff --git a/crates/coop/src/dialogs/screening.rs b/crates/coop/src/dialogs/screening.rs index cff37c7..208f5ad 100644 --- a/crates/coop/src/dialogs/screening.rs +++ b/crates/coop/src/dialogs/screening.rs @@ -5,9 +5,8 @@ use anyhow::{Context as AnyhowContext, Error}; use common::RenderedTimestamp; use gpui::prelude::FluentBuilder; use gpui::{ - div, px, relative, rems, uniform_list, App, AppContext, Context, Div, Entity, - InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription, - Task, Window, + div, px, relative, uniform_list, App, AppContext, Context, Div, Entity, InteractiveElement, + IntoElement, ParentElement, Render, SharedString, Styled, Subscription, Task, Window, }; use nostr_sdk::prelude::*; use person::{shorten_pubkey, Person, PersonRegistry}; @@ -275,7 +274,7 @@ impl Screening { .rounded(cx.theme().radius) .text_sm() .hover(|this| this.bg(cx.theme().elevated_surface_background)) - .child(Avatar::new(profile.avatar()).size(rems(1.75))) + .child(Avatar::new(profile.avatar()).small()) .child(profile.name()), ); } @@ -315,7 +314,7 @@ impl Render for Screening { .items_center() .justify_center() .text_center() - .child(Avatar::new(profile.avatar()).size(rems(4.))) + .child(Avatar::new(profile.avatar()).large()) .child( div() .font_semibold() diff --git a/crates/coop/src/panels/contact_list.rs b/crates/coop/src/panels/contact_list.rs index fa70f59..c80da7b 100644 --- a/crates/coop/src/panels/contact_list.rs +++ b/crates/coop/src/panels/contact_list.rs @@ -234,7 +234,7 @@ impl ContactListPanel { h_flex() .gap_2() .text_sm() - .child(Avatar::new(profile.avatar()).size(rems(1.5))) + .child(Avatar::new(profile.avatar()).small()) .child(profile.name()), ) .child( diff --git a/crates/coop/src/panels/profile.rs b/crates/coop/src/panels/profile.rs index 3757b4a..b320eec 100644 --- a/crates/coop/src/panels/profile.rs +++ b/crates/coop/src/panels/profile.rs @@ -3,9 +3,9 @@ use std::time::Duration; use anyhow::{Context as AnyhowContext, Error}; use gpui::{ - div, rems, AnyElement, App, AppContext, ClipboardItem, Context, Entity, EventEmitter, - FocusHandle, Focusable, IntoElement, ParentElement, PathPromptOptions, Render, SharedString, - Styled, Task, Window, + div, AnyElement, App, AppContext, ClipboardItem, Context, Entity, EventEmitter, FocusHandle, + Focusable, IntoElement, ParentElement, PathPromptOptions, Render, SharedString, Styled, Task, + Window, }; use nostr_sdk::prelude::*; use person::{shorten_pubkey, Person, PersonRegistry}; @@ -322,7 +322,7 @@ impl Render for ProfilePanel { .items_center() .justify_center() .gap_4() - .child(Avatar::new(avatar).size(rems(4.25))) + .child(Avatar::new(avatar).large()) .child( Button::new("upload") .icon(IconName::PlusCircle) diff --git a/crates/coop/src/sidebar/entry.rs b/crates/coop/src/sidebar/entry.rs index 2a4b73d..d07618b 100644 --- a/crates/coop/src/sidebar/entry.rs +++ b/crates/coop/src/sidebar/entry.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use chat::RoomKind; use gpui::prelude::FluentBuilder; use gpui::{ - div, rems, App, ClickEvent, InteractiveElement, IntoElement, ParentElement as _, RenderOnce, + div, App, ClickEvent, InteractiveElement, IntoElement, ParentElement as _, RenderOnce, SharedString, StatefulInteractiveElement, Styled, Window, }; use nostr_sdk::prelude::*; @@ -106,14 +106,7 @@ impl RenderOnce for RoomEntry { .rounded(cx.theme().radius) .when(!hide_avatar, |this| { this.when_some(self.avatar, |this, avatar| { - this.child( - div() - .flex_shrink_0() - .size_6() - .rounded_full() - .overflow_hidden() - .child(Avatar::new(avatar).size(rems(1.5))), - ) + this.child(Avatar::new(avatar).small().flex_shrink_0()) }) }) .child( diff --git a/crates/coop/src/workspace.rs b/crates/coop/src/workspace.rs index 27139db..1e5e6b6 100644 --- a/crates/coop/src/workspace.rs +++ b/crates/coop/src/workspace.rs @@ -4,7 +4,7 @@ use ::settings::AppSettings; use chat::{ChatEvent, ChatRegistry, InboxState}; use gpui::prelude::FluentBuilder; use gpui::{ - div, px, rems, Action, App, AppContext, Axis, Context, Entity, InteractiveElement, IntoElement, + div, px, Action, App, AppContext, Axis, Context, Entity, InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription, Window, }; use person::PersonRegistry; @@ -385,7 +385,7 @@ impl Workspace { this.child( Button::new("current-user") - .child(Avatar::new(profile.avatar()).size(rems(1.25))) + .child(Avatar::new(profile.avatar()).xsmall()) .small() .caret() .compact() diff --git a/crates/ui/src/avatar.rs b/crates/ui/src/avatar.rs index 03f8da5..2d79545 100644 --- a/crates/ui/src/avatar.rs +++ b/crates/ui/src/avatar.rs @@ -1,10 +1,24 @@ use gpui::prelude::FluentBuilder; use gpui::{ - div, img, px, rems, AbsoluteLength, App, Hsla, ImageSource, Img, IntoElement, ParentElement, - RenderOnce, Styled, StyledImage, Window, + div, img, px, AbsoluteLength, App, Div, Hsla, ImageSource, Img, InteractiveElement, + Interactivity, IntoElement, ParentElement, RenderOnce, StyleRefinement, Styled, StyledImage, + Window, }; use theme::ActiveTheme; +use crate::{Sizable, Size}; + +/// Returns the size of the avatar based on the given [`Size`]. +pub(super) fn avatar_size(size: Size) -> AbsoluteLength { + match size { + Size::Large => px(64.).into(), + Size::Medium => px(32.).into(), + Size::Small => px(24.).into(), + Size::XSmall => px(20.).into(), + Size::Size(size) => size.into(), + } +} + /// An element that renders a user avatar with customizable appearance options. /// /// # Examples @@ -18,8 +32,10 @@ use theme::ActiveTheme; /// ``` #[derive(IntoElement)] pub struct Avatar { + base: Div, image: Img, - size: Option, + style: StyleRefinement, + size: Size, border_color: Option, } @@ -27,8 +43,10 @@ impl Avatar { /// Creates a new avatar element with the specified image source. pub fn new(src: impl Into) -> Self { Avatar { + base: div(), image: img(src), - size: None, + style: StyleRefinement::default(), + size: Size::Medium, border_color: None, } } @@ -56,14 +74,27 @@ impl Avatar { self.border_color = Some(color.into()); self } +} - /// Size overrides the avatar size. By default they are 1rem. - pub fn size>(mut self, size: impl Into>) -> Self { - self.size = size.into().map(Into::into); +impl Sizable for Avatar { + fn with_size(mut self, size: impl Into) -> Self { + self.size = size.into(); self } } +impl Styled for Avatar { + fn style(&mut self) -> &mut StyleRefinement { + &mut self.style + } +} + +impl InteractiveElement for Avatar { + fn interactivity(&mut self) -> &mut Interactivity { + self.base.interactivity() + } +} + impl RenderOnce for Avatar { fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { let border_width = if self.border_color.is_some() { @@ -71,8 +102,7 @@ impl RenderOnce for Avatar { } else { px(0.) }; - - let image_size = self.size.unwrap_or_else(|| rems(1.).into()); + let image_size = avatar_size(self.size); let container_size = image_size.to_pixels(window.rem_size()) + border_width * 2.; div()