feat: show facepill on tabpanel

This commit is contained in:
2025-01-16 08:49:24 +07:00
parent 161e6a5342
commit b119722392
9 changed files with 59 additions and 2051 deletions

1
Cargo.lock generated
View File

@@ -5629,7 +5629,6 @@ dependencies = [
"gpui",
"image",
"itertools 0.13.0",
"nostr-sdk",
"once_cell",
"paste",
"regex",

View File

@@ -35,7 +35,7 @@ impl RenderOnce for Message {
.border_l_2()
.border_color(cx.theme().background)
.hover(|this| {
this.bg(cx.theme().accent.step(cx, ColorScaleStep::TWO))
this.bg(cx.theme().accent.step(cx, ColorScaleStep::ONE))
.border_color(cx.theme().accent.step(cx, ColorScaleStep::NINE))
})
.child(

View File

@@ -386,8 +386,15 @@ impl Panel for ChatPanel {
self.id.clone()
}
fn panel_metadata(&self) -> Option<Metadata> {
None
fn panel_facepile(&self, cx: &WindowContext) -> Option<Vec<String>> {
Some(
self.room
.read(cx)
.members
.iter()
.map(|member| member.avatar())
.collect(),
)
}
fn title(&self, _cx: &WindowContext) -> AnyElement {

View File

@@ -13,7 +13,6 @@ smallvec.workspace = true
anyhow.workspace = true
itertools.workspace = true
chrono.workspace = true
nostr-sdk.workspace = true
paste = "1"
regex = "1"

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,6 @@ use gpui::{
AnyElement, AnyView, AppContext, EventEmitter, FocusHandle, FocusableView, Global, Hsla,
IntoElement, SharedString, View, WeakView, WindowContext,
};
use nostr_sdk::prelude::Metadata;
use std::{collections::HashMap, sync::Arc};
pub enum PanelEvent {
@@ -38,8 +37,8 @@ pub trait Panel: EventEmitter<PanelEvent> + FocusableView {
/// Once you have defined a panel id, this must not be changed.
fn panel_id(&self) -> SharedString;
/// The optional metadata of the panel
fn panel_metadata(&self) -> Option<Metadata> {
/// The optional facepile of the panel
fn panel_facepile(&self, _cx: &WindowContext) -> Option<Vec<String>> {
None
}
@@ -76,7 +75,7 @@ pub trait Panel: EventEmitter<PanelEvent> + FocusableView {
pub trait PanelView: 'static + Send + Sync {
fn panel_id(&self, cx: &WindowContext) -> SharedString;
fn panel_metadata(&self, cx: &WindowContext) -> Option<Metadata>;
fn panel_facepile(&self, cx: &WindowContext) -> Option<Vec<String>>;
fn title(&self, _cx: &WindowContext) -> AnyElement;
fn closeable(&self, cx: &WindowContext) -> bool;
fn zoomable(&self, cx: &WindowContext) -> bool;
@@ -92,8 +91,8 @@ impl<T: Panel> PanelView for View<T> {
self.read(cx).panel_id()
}
fn panel_metadata(&self, cx: &WindowContext) -> Option<Metadata> {
self.read(cx).panel_metadata()
fn panel_facepile(&self, cx: &WindowContext) -> Option<Vec<String>> {
self.read(cx).panel_facepile(cx)
}
fn title(&self, cx: &WindowContext) -> AnyElement {

View File

@@ -516,26 +516,25 @@ impl TabPanel {
.gap_1()
.text_ellipsis()
.text_xs()
.child(div().when_some(
panel.panel_metadata(cx),
|this, metadata| {
if let Some(picture) = metadata.picture {
this.flex_shrink_0().child(
img(format!(
"https://wsrv.nl/?url={}&w=100&h=100&n=-1",
picture
))
.size_4()
.rounded_full()
.object_fit(ObjectFit::Cover),
)
} else {
this.flex_shrink_0().child(
img("brand/avatar.png").size_4().rounded_full(),
)
}
},
))
.when_some(panel.panel_facepile(cx), |this, facepill| {
this.child(
div()
.flex()
.flex_row_reverse()
.items_center()
.justify_start()
.children(facepill.into_iter().enumerate().rev().map(
|(ix, face)| {
div().when(ix > 0, |div| div.ml_neg_1()).child(
img(face)
.size_4()
.rounded_full()
.object_fit(ObjectFit::Cover),
)
},
)),
)
})
.child(panel.title(cx)),
)
.when(state.draggable, |this| {
@@ -595,7 +594,7 @@ impl TabPanel {
active = false;
}
Tab::new(("tab", ix), panel.title(cx), panel.panel_metadata(cx))
Tab::new(("tab", ix), panel.title(cx), panel.panel_facepile(cx))
.py_2()
.selected(active)
.disabled(disabled)

View File

@@ -1,6 +1,5 @@
use std::time::Duration;
use gpui::{ModelContext, Timer};
use std::time::Duration;
static INTERVAL: Duration = Duration::from_millis(500);
static PAUSE_DELAY: Duration = Duration::from_millis(300);
@@ -72,6 +71,7 @@ impl BlinkCursor {
// delay 500ms to start the blinking
let epoch = self.next_epoch();
cx.spawn(|this, mut cx| async move {
Timer::after(PAUSE_DELAY).await;

View File

@@ -3,7 +3,6 @@ use crate::theme::ActiveTheme;
use crate::Selectable;
use gpui::prelude::FluentBuilder;
use gpui::*;
use nostr_sdk::prelude::*;
pub mod tab_bar;
@@ -12,7 +11,7 @@ pub struct Tab {
id: ElementId,
base: Stateful<Div>,
label: AnyElement,
metadata: Option<Metadata>,
facepill: Option<Vec<String>>,
prefix: Option<AnyElement>,
suffix: Option<AnyElement>,
disabled: bool,
@@ -23,7 +22,7 @@ impl Tab {
pub fn new(
id: impl Into<ElementId>,
label: impl IntoElement,
metadata: Option<Metadata>,
facepill: Option<Vec<String>>,
) -> Self {
let id: ElementId = id.into();
@@ -31,11 +30,11 @@ impl Tab {
id: id.clone(),
base: div().id(id),
label: label.into_any_element(),
metadata,
disabled: false,
selected: false,
prefix: None,
suffix: None,
facepill,
}
}
@@ -143,19 +142,25 @@ impl RenderOnce for Tab {
.gap_1()
.text_ellipsis()
.text_xs()
.child(div().when_some(self.metadata, |this, metadata| {
if let Some(picture) = metadata.picture {
this.flex_shrink_0().child(
img(format!("https://wsrv.nl/?url={}&w=100&h=100&n=-1", picture))
.size_4()
.rounded_full()
.object_fit(ObjectFit::Cover),
)
} else {
this.flex_shrink_0()
.child(img("brand/avatar.png").size_4().rounded_full())
}
}))
.when_some(self.facepill, |this, facepill| {
this.child(
div()
.flex()
.flex_row_reverse()
.items_center()
.justify_start()
.children(facepill.into_iter().enumerate().rev().map(
|(ix, face)| {
div().when(ix > 0, |div| div.ml_neg_1()).child(
img(face)
.size_4()
.rounded_full()
.object_fit(ObjectFit::Cover),
)
},
)),
)
})
.child(self.label),
)
.when_some(self.suffix, |this, suffix| this.child(suffix))