chore: update gpui-components

This commit is contained in:
2025-02-05 07:33:21 +07:00
parent c7d17ef90d
commit 72e42bf22a
16 changed files with 99 additions and 82 deletions

View File

@@ -16,9 +16,7 @@ use ui::{
Icon, IconName, Root, Sizable, TitleBar, Icon, IconName, Root, Sizable, TitleBar,
}; };
use super::{ use super::{chat, contacts, onboarding, profile, settings, sidebar, welcome};
chat, contacts, onboarding, profile, settings, sidebar::Sidebar, welcome::WelcomePanel,
};
#[derive(Clone, PartialEq, Eq, Deserialize)] #[derive(Clone, PartialEq, Eq, Deserialize)]
pub enum PanelKind { pub enum PanelKind {
@@ -65,11 +63,11 @@ impl AppView {
pub fn new(account: NostrProfile, window: &mut Window, cx: &mut Context<'_, Self>) -> AppView { pub fn new(account: NostrProfile, window: &mut Window, cx: &mut Context<'_, Self>) -> AppView {
let dock = cx.new(|cx| DockArea::new(DOCK_AREA.id, Some(DOCK_AREA.version), window, cx)); let dock = cx.new(|cx| DockArea::new(DOCK_AREA.id, Some(DOCK_AREA.version), window, cx));
let weak_dock = dock.downgrade(); let weak_dock = dock.downgrade();
let left_panel = DockItem::panel(Arc::new(Sidebar::new(window, cx))); let left_panel = DockItem::panel(Arc::new(sidebar::init(window, cx)));
let center_panel = DockItem::split_with_sizes( let center_panel = DockItem::split_with_sizes(
Axis::Vertical, Axis::Vertical,
vec![DockItem::tabs( vec![DockItem::tabs(
vec![Arc::new(WelcomePanel::new(window, cx))], vec![Arc::new(welcome::init(window, cx))],
None, None,
&weak_dock, &weak_dock,
window, window,

View File

@@ -5,10 +5,10 @@ use common::{
utils::{compare, message_time, nip96_upload}, utils::{compare, message_time, nip96_upload},
}; };
use gpui::{ use gpui::{
div, img, list, px, white, AnyElement, App, AppContext, Context, Entity, EventEmitter, Flatten, div, img, list, prelude::FluentBuilder, px, white, AnyElement, App, AppContext, Context,
FocusHandle, Focusable, InteractiveElement, IntoElement, ListAlignment, ListState, ObjectFit, Entity, EventEmitter, Flatten, FocusHandle, Focusable, InteractiveElement, IntoElement,
ParentElement, PathPromptOptions, Pixels, Render, SharedString, StatefulInteractiveElement, ListAlignment, ListState, ObjectFit, ParentElement, PathPromptOptions, Pixels, Render,
Styled, StyledImage, WeakEntity, Window, SharedString, StatefulInteractiveElement, Styled, StyledImage, WeakEntity, Window,
}; };
use itertools::Itertools; use itertools::Itertools;
use message::Message; use message::Message;
@@ -21,7 +21,6 @@ use ui::{
dock_area::panel::{Panel, PanelEvent}, dock_area::panel::{Panel, PanelEvent},
input::{InputEvent, TextInput}, input::{InputEvent, TextInput},
popup_menu::PopupMenu, popup_menu::PopupMenu,
prelude::FluentBuilder,
theme::{scale::ColorScaleStep, ActiveTheme}, theme::{scale::ColorScaleStep, ActiveTheme},
v_flex, ContextModal, Icon, IconName, Sizable, v_flex, ContextModal, Icon, IconName, Sizable,
}; };

View File

@@ -1,8 +1,8 @@
use common::profile::NostrProfile; use common::profile::NostrProfile;
use gpui::{ use gpui::{
div, img, px, uniform_list, AnyElement, App, AppContext, Context, Entity, EventEmitter, div, img, prelude::FluentBuilder, px, uniform_list, AnyElement, App, AppContext, Context,
FocusHandle, Focusable, InteractiveElement, IntoElement, ParentElement, Render, SharedString, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement, IntoElement, ParentElement,
Styled, Window, Render, SharedString, Styled, Window,
}; };
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use state::get_client; use state::get_client;
@@ -12,7 +12,6 @@ use ui::{
dock_area::panel::{Panel, PanelEvent}, dock_area::panel::{Panel, PanelEvent},
indicator::Indicator, indicator::Indicator,
popup_menu::PopupMenu, popup_menu::PopupMenu,
prelude::FluentBuilder,
theme::{scale::ColorScaleStep, ActiveTheme}, theme::{scale::ColorScaleStep, ActiveTheme},
Sizable, Sizable,
}; };

View File

@@ -1,8 +1,8 @@
use app_state::registry::AppRegistry; use app_state::registry::AppRegistry;
use common::profile::NostrProfile; use common::profile::NostrProfile;
use gpui::{ use gpui::{
div, relative, svg, App, AppContext, BorrowAppContext, Context, Entity, IntoElement, div, prelude::FluentBuilder, relative, svg, App, AppContext, BorrowAppContext, Context, Entity,
ParentElement, Render, Styled, Window, IntoElement, ParentElement, Render, Styled, Window,
}; };
use nostr_connect::prelude::*; use nostr_connect::prelude::*;
use state::get_client; use state::get_client;
@@ -12,7 +12,6 @@ use ui::{
button::{Button, ButtonVariants}, button::{Button, ButtonVariants},
input::{InputEvent, TextInput}, input::{InputEvent, TextInput},
notification::NotificationType, notification::NotificationType,
prelude::FluentBuilder,
theme::{scale::ColorScaleStep, ActiveTheme}, theme::{scale::ColorScaleStep, ActiveTheme},
ContextModal, Root, Size, StyledExt, ContextModal, Root, Size, StyledExt,
}; };

View File

@@ -5,9 +5,9 @@ use common::{
utils::{random_name, room_hash}, utils::{random_name, room_hash},
}; };
use gpui::{ use gpui::{
div, img, impl_internal_actions, px, uniform_list, App, AppContext, Context, Entity, div, img, impl_internal_actions, prelude::FluentBuilder, px, uniform_list, App, AppContext,
FocusHandle, InteractiveElement, IntoElement, ParentElement, Render, SharedString, Context, Entity, FocusHandle, InteractiveElement, IntoElement, ParentElement, Render,
StatefulInteractiveElement, Styled, Window, SharedString, StatefulInteractiveElement, Styled, Window,
}; };
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use serde::Deserialize; use serde::Deserialize;
@@ -18,7 +18,6 @@ use ui::{
button::{Button, ButtonRounded}, button::{Button, ButtonRounded},
indicator::Indicator, indicator::Indicator,
input::{InputEvent, TextInput}, input::{InputEvent, TextInput},
prelude::FluentBuilder,
theme::{scale::ColorScaleStep, ActiveTheme}, theme::{scale::ColorScaleStep, ActiveTheme},
Icon, IconName, Sizable, Size, StyledExt, Icon, IconName, Sizable, Size, StyledExt,
}; };

View File

@@ -18,6 +18,10 @@ use ui::{
mod compose; mod compose;
mod inbox; mod inbox;
pub fn init(window: &mut Window, cx: &mut App) -> Entity<Sidebar> {
Sidebar::new(window, cx)
}
pub struct Sidebar { pub struct Sidebar {
// Panel // Panel
name: SharedString, name: SharedString,

View File

@@ -10,14 +10,18 @@ use ui::{
StyledExt, StyledExt,
}; };
pub struct WelcomePanel { pub fn init(window: &mut Window, cx: &mut App) -> Entity<Welcome> {
Welcome::new(window, cx)
}
pub struct Welcome {
name: SharedString, name: SharedString,
closable: bool, closable: bool,
zoomable: bool, zoomable: bool,
focus_handle: FocusHandle, focus_handle: FocusHandle,
} }
impl WelcomePanel { impl Welcome {
pub fn new(window: &mut Window, cx: &mut App) -> Entity<Self> { pub fn new(window: &mut Window, cx: &mut App) -> Entity<Self> {
cx.new(|cx| Self::view(window, cx)) cx.new(|cx| Self::view(window, cx))
} }
@@ -32,7 +36,7 @@ impl WelcomePanel {
} }
} }
impl Panel for WelcomePanel { impl Panel for Welcome {
fn panel_id(&self) -> SharedString { fn panel_id(&self) -> SharedString {
"WelcomePanel".into() "WelcomePanel".into()
} }
@@ -58,15 +62,15 @@ impl Panel for WelcomePanel {
} }
} }
impl EventEmitter<PanelEvent> for WelcomePanel {} impl EventEmitter<PanelEvent> for Welcome {}
impl Focusable for WelcomePanel { impl Focusable for Welcome {
fn focus_handle(&self, _: &App) -> gpui::FocusHandle { fn focus_handle(&self, _: &App) -> gpui::FocusHandle {
self.focus_handle.clone() self.focus_handle.clone()
} }
} }
impl Render for WelcomePanel { impl Render for Welcome {
fn render(&mut self, _window: &mut gpui::Window, cx: &mut Context<Self>) -> impl IntoElement { fn render(&mut self, _window: &mut gpui::Window, cx: &mut Context<Self>) -> impl IntoElement {
div() div()
.size_full() .size_full()

View File

@@ -2,7 +2,8 @@ use gpui::{
actions, anchored, canvas, deferred, div, prelude::FluentBuilder, px, rems, AnyElement, App, actions, anchored, canvas, deferred, div, prelude::FluentBuilder, px, rems, AnyElement, App,
AppContext, Bounds, ClickEvent, Context, DismissEvent, ElementId, Entity, EventEmitter, AppContext, Bounds, ClickEvent, Context, DismissEvent, ElementId, Entity, EventEmitter,
FocusHandle, Focusable, InteractiveElement, IntoElement, KeyBinding, Length, ParentElement, FocusHandle, Focusable, InteractiveElement, IntoElement, KeyBinding, Length, ParentElement,
Pixels, Render, SharedString, StatefulInteractiveElement, Styled, Task, WeakEntity, Window, Pixels, Render, SharedString, StatefulInteractiveElement, Styled, Subscription, Task,
WeakEntity, Window,
}; };
use crate::{ use crate::{
@@ -245,6 +246,8 @@ pub struct Dropdown<D: DropdownDelegate + 'static> {
/// Store the bounds of the input /// Store the bounds of the input
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
disabled: bool, disabled: bool,
#[allow(dead_code)]
subscriptions: Vec<Subscription>,
} }
pub struct SearchableVec<T> { pub struct SearchableVec<T> {
@@ -255,6 +258,7 @@ pub struct SearchableVec<T> {
impl<T: DropdownItem + Clone> SearchableVec<T> { impl<T: DropdownItem + Clone> SearchableVec<T> {
pub fn new(items: impl Into<Vec<T>>) -> Self { pub fn new(items: impl Into<Vec<T>>) -> Self {
let items = items.into(); let items = items.into();
Self { Self {
items: items.clone(), items: items.clone(),
matched_items: items, matched_items: items,
@@ -345,10 +349,10 @@ where
list list
}); });
cx.on_blur(&list.focus_handle(cx), window, Self::on_blur) let subscriptions = vec![
.detach(); cx.on_blur(&list.focus_handle(cx), window, Self::on_blur),
cx.on_blur(&focus_handle, window, Self::on_blur),
cx.on_blur(&focus_handle, window, Self::on_blur).detach(); ];
let mut this = Self { let mut this = Self {
id: id.into(), id: id.into(),
@@ -365,6 +369,7 @@ where
menu_width: Length::Auto, menu_width: Length::Auto,
bounds: Bounds::default(), bounds: Bounds::default(),
disabled: false, disabled: false,
subscriptions,
}; };
this.set_selected_index(selected_index, window, cx); this.set_selected_index(selected_index, window, cx);
this this

View File

@@ -3,7 +3,7 @@ use gpui::{
ClipboardItem, Context, Entity, EntityInputHandler, EventEmitter, FocusHandle, Focusable, ClipboardItem, Context, Entity, EntityInputHandler, EventEmitter, FocusHandle, Focusable,
InteractiveElement as _, IntoElement, KeyBinding, KeyDownEvent, MouseButton, MouseDownEvent, InteractiveElement as _, IntoElement, KeyBinding, KeyDownEvent, MouseButton, MouseDownEvent,
MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Point, Rems, Render, ScrollHandle, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Point, Rems, Render, ScrollHandle,
ScrollWheelEvent, SharedString, Styled as _, UTF16Selection, Window, WrappedLine, ScrollWheelEvent, SharedString, Styled as _, Subscription, UTF16Selection, Window, WrappedLine,
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{cell::Cell, ops::Range, rc::Rc}; use std::{cell::Cell, ops::Range, rc::Rc};
@@ -213,6 +213,8 @@ pub struct TextInput {
pub(crate) scroll_size: gpui::Size<Pixels>, pub(crate) scroll_size: gpui::Size<Pixels>,
/// To remember the horizontal column (x-coordinate) of the cursor position. /// To remember the horizontal column (x-coordinate) of the cursor position.
preferred_x_offset: Option<Pixels>, preferred_x_offset: Option<Pixels>,
#[allow(dead_code)]
subscriptions: Vec<Subscription>,
} }
impl EventEmitter<InputEvent> for TextInput {} impl EventEmitter<InputEvent> for TextInput {}
@@ -222,7 +224,25 @@ impl TextInput {
let focus_handle = cx.focus_handle(); let focus_handle = cx.focus_handle();
let blink_cursor = cx.new(|_| BlinkCursor::new()); let blink_cursor = cx.new(|_| BlinkCursor::new());
let history = History::new().group_interval(std::time::Duration::from_secs(1)); let history = History::new().group_interval(std::time::Duration::from_secs(1));
let input = Self { let subscriptions = vec![
// Observe the blink cursor to repaint the view when it changes.
cx.observe(&blink_cursor, |_, _, cx| cx.notify()),
// Blink the cursor when the window is active, pause when it's not.
cx.observe_window_activation(window, |input, window, cx| {
if window.is_window_active() {
let focus_handle = input.focus_handle.clone();
if focus_handle.is_focused(window) {
input.blink_cursor.update(cx, |blink_cursor, cx| {
blink_cursor.start(cx);
});
}
}
}),
cx.on_focus(&focus_handle, window, Self::on_focus),
cx.on_blur(&focus_handle, window, Self::on_blur),
];
Self {
focus_handle: focus_handle.clone(), focus_handle: focus_handle.clone(),
text: "".into(), text: "".into(),
multi_line: false, multi_line: false,
@@ -256,30 +276,9 @@ impl TextInput {
scrollbar_state: Rc::new(Cell::new(ScrollbarState::default())), scrollbar_state: Rc::new(Cell::new(ScrollbarState::default())),
scroll_size: gpui::size(px(0.), px(0.)), scroll_size: gpui::size(px(0.), px(0.)),
preferred_x_offset: None, preferred_x_offset: None,
}; subscriptions,
// Observe the blink cursor to repaint the view when it changes.
cx.observe(&input.blink_cursor, |_, _, cx| cx.notify())
.detach();
// Blink the cursor when the window is active, pause when it's not.
cx.observe_window_activation(window, |input, window, cx| {
if window.is_window_active() {
let focus_handle = input.focus_handle.clone();
if focus_handle.is_focused(window) {
input.blink_cursor.update(cx, |blink_cursor, cx| {
blink_cursor.start(cx);
});
} }
} }
})
.detach();
cx.on_focus(&focus_handle, window, Self::on_focus).detach();
cx.on_blur(&focus_handle, window, Self::on_blur).detach();
input
}
/// Use the text input field as a multi-line Textarea. /// Use the text input field as a multi-line Textarea.
pub fn multi_line(mut self) -> Self { pub fn multi_line(mut self) -> Self {

View File

@@ -15,7 +15,6 @@ pub mod modal;
pub mod notification; pub mod notification;
pub mod popover; pub mod popover;
pub mod popup_menu; pub mod popup_menu;
pub mod prelude;
pub mod progress; pub mod progress;
pub mod radio; pub mod radio;
pub mod resizable; pub mod resizable;

View File

@@ -8,7 +8,7 @@ use gpui::{
actions, div, prelude::FluentBuilder, px, uniform_list, AnyElement, App, AppContext, Context, actions, div, prelude::FluentBuilder, px, uniform_list, AnyElement, App, AppContext, Context,
Entity, FocusHandle, Focusable, InteractiveElement, IntoElement, KeyBinding, Length, Entity, FocusHandle, Focusable, InteractiveElement, IntoElement, KeyBinding, Length,
ListSizingBehavior, MouseButton, ParentElement, Render, ScrollStrategy, SharedString, Styled, ListSizingBehavior, MouseButton, ParentElement, Render, ScrollStrategy, SharedString, Styled,
Task, UniformListScrollHandle, Window, Subscription, Task, UniformListScrollHandle, Window,
}; };
use smol::Timer; use smol::Timer;
use std::{cell::Cell, rc::Rc, time::Duration}; use std::{cell::Cell, rc::Rc, time::Duration};
@@ -111,6 +111,7 @@ pub struct List<D: ListDelegate> {
selected_index: Option<usize>, selected_index: Option<usize>,
right_clicked_index: Option<usize>, right_clicked_index: Option<usize>,
_search_task: Task<()>, _search_task: Task<()>,
query_input_subscription: Subscription,
} }
impl<D> List<D> impl<D> List<D>
@@ -129,8 +130,8 @@ where
.cleanable() .cleanable()
}); });
cx.subscribe_in(&query_input, window, Self::on_query_input_event) let query_input_subscription =
.detach(); cx.subscribe_in(&query_input, window, Self::on_query_input_event);
Self { Self {
focus_handle: cx.focus_handle(), focus_handle: cx.focus_handle(),
@@ -146,6 +147,7 @@ where
loading: false, loading: false,
size: Size::default(), size: Size::default(),
_search_task: Task::ready(()), _search_task: Task::ready(()),
query_input_subscription,
} }
} }
@@ -180,8 +182,8 @@ where
window: &mut Window, window: &mut Window,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) { ) {
cx.subscribe_in(&query_input, window, Self::on_query_input_event) self.query_input_subscription =
.detach(); cx.subscribe_in(&query_input, window, Self::on_query_input_event);
self.query_input = Some(query_input); self.query_input = Some(query_input);
} }

View File

@@ -12,10 +12,16 @@ use crate::{
use gpui::{ use gpui::{
div, prelude::FluentBuilder, px, Animation, AnimationExt, App, AppContext, ClickEvent, Context, div, prelude::FluentBuilder, px, Animation, AnimationExt, App, AppContext, ClickEvent, Context,
DismissEvent, ElementId, Entity, EventEmitter, InteractiveElement as _, IntoElement, DismissEvent, ElementId, Entity, EventEmitter, InteractiveElement as _, IntoElement,
ParentElement as _, Render, SharedString, StatefulInteractiveElement, Styled, Window, ParentElement as _, Render, SharedString, StatefulInteractiveElement, Styled, Subscription,
Window,
}; };
use smol::Timer; use smol::Timer;
use std::{any::TypeId, collections::VecDeque, sync::Arc, time::Duration}; use std::{
any::TypeId,
collections::{HashMap, VecDeque},
sync::Arc,
time::Duration,
};
pub enum NotificationType { pub enum NotificationType {
Info, Info,
@@ -24,7 +30,7 @@ pub enum NotificationType {
Error, Error,
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone, Hash, Eq)]
pub(crate) enum NotificationId { pub(crate) enum NotificationId {
Id(TypeId), Id(TypeId),
IdAndElementId(TypeId, ElementId), IdAndElementId(TypeId, ElementId),
@@ -294,6 +300,7 @@ pub struct NotificationList {
/// Notifications that will be auto hidden. /// Notifications that will be auto hidden.
pub(crate) notifications: VecDeque<Entity<Notification>>, pub(crate) notifications: VecDeque<Entity<Notification>>,
expanded: bool, expanded: bool,
subscriptions: HashMap<NotificationId, Subscription>,
} }
impl NotificationList { impl NotificationList {
@@ -301,6 +308,7 @@ impl NotificationList {
Self { Self {
notifications: VecDeque::new(), notifications: VecDeque::new(),
expanded: false, expanded: false,
subscriptions: HashMap::new(),
} }
} }
@@ -319,10 +327,13 @@ impl NotificationList {
let notification = cx.new(|_| notification); let notification = cx.new(|_| notification);
self.subscriptions.insert(
id.clone(),
cx.subscribe(&notification, move |view, _, _: &DismissEvent, cx| { cx.subscribe(&notification, move |view, _, _: &DismissEvent, cx| {
view.notifications.retain(|note| id != note.read(cx).id); view.notifications.retain(|note| id != note.read(cx).id);
}) view.subscriptions.remove(&id);
.detach(); }),
);
self.notifications.push_back(notification.clone()); self.notifications.push_back(notification.clone());
if autohide { if autohide {

View File

@@ -11,7 +11,8 @@ use gpui::{
actions, anchored, canvas, div, prelude::FluentBuilder, px, rems, Action, AnyElement, App, actions, anchored, canvas, div, prelude::FluentBuilder, px, rems, Action, AnyElement, App,
AppContext, Bounds, Context, Corner, DismissEvent, Edges, Entity, EventEmitter, FocusHandle, AppContext, Bounds, Context, Corner, DismissEvent, Edges, Entity, EventEmitter, FocusHandle,
Focusable, InteractiveElement, IntoElement, KeyBinding, Keystroke, ParentElement, Pixels, Focusable, InteractiveElement, IntoElement, KeyBinding, Keystroke, ParentElement, Pixels,
Render, ScrollHandle, SharedString, StatefulInteractiveElement, Styled, WeakEntity, Window, Render, ScrollHandle, SharedString, StatefulInteractiveElement, Styled, Subscription,
WeakEntity, Window,
}; };
use std::{cell::Cell, ops::Deref, rc::Rc}; use std::{cell::Cell, ops::Deref, rc::Rc};
@@ -114,7 +115,8 @@ pub struct PopupMenu {
scroll_state: Rc<Cell<ScrollbarState>>, scroll_state: Rc<Cell<ScrollbarState>>,
action_focus_handle: Option<FocusHandle>, action_focus_handle: Option<FocusHandle>,
_subscriptions: [gpui::Subscription; 1], #[allow(dead_code)]
subscriptions: Vec<Subscription>,
} }
impl PopupMenu { impl PopupMenu {
@@ -125,10 +127,12 @@ impl PopupMenu {
) -> Entity<Self> { ) -> Entity<Self> {
cx.new(|cx| { cx.new(|cx| {
let focus_handle = cx.focus_handle(); let focus_handle = cx.focus_handle();
let _on_blur_subscription = let subscriptions =
vec![
cx.on_blur(&focus_handle, window, |this: &mut PopupMenu, window, cx| { cx.on_blur(&focus_handle, window, |this: &mut PopupMenu, window, cx| {
this.dismiss(&Dismiss, window, cx) this.dismiss(&Dismiss, window, cx)
}); }),
];
let menu = Self { let menu = Self {
focus_handle, focus_handle,
@@ -144,7 +148,7 @@ impl PopupMenu {
scrollable: false, scrollable: false,
scroll_handle: ScrollHandle::default(), scroll_handle: ScrollHandle::default(),
scroll_state: Rc::new(Cell::new(ScrollbarState::default())), scroll_state: Rc::new(Cell::new(ScrollbarState::default())),
_subscriptions: [_on_blur_subscription], subscriptions,
}; };
window.refresh(); window.refresh();
f(menu, window, cx) f(menu, window, cx)

View File

@@ -1,5 +0,0 @@
pub use gpui::prelude::*;
pub use gpui::{
div, px, relative, rems, AbsoluteLength, DefiniteLength, Div, Element, ElementId,
InteractiveElement, ParentElement, Pixels, Rems, RenderOnce, SharedString, Styled, Window,
};