Checkpoint – Notifications Panel
This commit is contained in:
parent
e3d948f60b
commit
32028fbbb1
10 changed files with 318 additions and 107 deletions
|
@ -13,7 +13,8 @@ mod keybinding;
|
||||||
mod language_selector;
|
mod language_selector;
|
||||||
mod list;
|
mod list;
|
||||||
mod multi_buffer;
|
mod multi_buffer;
|
||||||
mod notification;
|
mod notification_toast;
|
||||||
|
mod notifications_panel;
|
||||||
mod palette;
|
mod palette;
|
||||||
mod panel;
|
mod panel;
|
||||||
mod panes;
|
mod panes;
|
||||||
|
@ -46,7 +47,8 @@ pub use keybinding::*;
|
||||||
pub use language_selector::*;
|
pub use language_selector::*;
|
||||||
pub use list::*;
|
pub use list::*;
|
||||||
pub use multi_buffer::*;
|
pub use multi_buffer::*;
|
||||||
pub use notification::*;
|
pub use notification_toast::*;
|
||||||
|
pub use notifications_panel::*;
|
||||||
pub use palette::*;
|
pub use palette::*;
|
||||||
pub use panel::*;
|
pub use panel::*;
|
||||||
pub use panes::*;
|
pub use panes::*;
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use gpui3::{div, Div};
|
use gpui3::{div, relative, Div};
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::settings::user_settings;
|
use crate::settings::user_settings;
|
||||||
use crate::{h_stack, v_stack, Avatar, Icon, IconColor, IconElement, IconSize, Label, LabelColor};
|
use crate::{
|
||||||
|
h_stack, v_stack, Avatar, ClickHandler, Icon, IconColor, IconElement, IconSize, Label,
|
||||||
|
LabelColor,
|
||||||
|
};
|
||||||
|
use crate::{prelude::*, Button};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, Debug, PartialEq)]
|
#[derive(Clone, Copy, Default, Debug, PartialEq)]
|
||||||
pub enum ListItemVariant {
|
pub enum ListItemVariant {
|
||||||
|
@ -201,6 +204,7 @@ pub enum ListEntrySize {
|
||||||
#[derive(Element)]
|
#[derive(Element)]
|
||||||
pub enum ListItem<S: 'static + Send + Sync> {
|
pub enum ListItem<S: 'static + Send + Sync> {
|
||||||
Entry(ListEntry<S>),
|
Entry(ListEntry<S>),
|
||||||
|
Details(ListDetailsEntry<S>),
|
||||||
Separator(ListSeparator<S>),
|
Separator(ListSeparator<S>),
|
||||||
Header(ListSubHeader<S>),
|
Header(ListSubHeader<S>),
|
||||||
}
|
}
|
||||||
|
@ -211,6 +215,12 @@ impl<S: 'static + Send + Sync> From<ListEntry<S>> for ListItem<S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<S: 'static + Send + Sync> From<ListDetailsEntry<S>> for ListItem<S> {
|
||||||
|
fn from(entry: ListDetailsEntry<S>) -> Self {
|
||||||
|
Self::Details(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<S: 'static + Send + Sync> From<ListSeparator<S>> for ListItem<S> {
|
impl<S: 'static + Send + Sync> From<ListSeparator<S>> for ListItem<S> {
|
||||||
fn from(entry: ListSeparator<S>) -> Self {
|
fn from(entry: ListSeparator<S>) -> Self {
|
||||||
Self::Separator(entry)
|
Self::Separator(entry)
|
||||||
|
@ -229,6 +239,7 @@ impl<S: 'static + Send + Sync> ListItem<S> {
|
||||||
ListItem::Entry(entry) => div().child(entry.render(view, cx)),
|
ListItem::Entry(entry) => div().child(entry.render(view, cx)),
|
||||||
ListItem::Separator(separator) => div().child(separator.render(view, cx)),
|
ListItem::Separator(separator) => div().child(separator.render(view, cx)),
|
||||||
ListItem::Header(header) => div().child(header.render(view, cx)),
|
ListItem::Header(header) => div().child(header.render(view, cx)),
|
||||||
|
ListItem::Details(details) => div().child(details.render(view, cx)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,6 +266,7 @@ pub struct ListEntry<S: 'static + Send + Sync> {
|
||||||
size: ListEntrySize,
|
size: ListEntrySize,
|
||||||
state: InteractionState,
|
state: InteractionState,
|
||||||
toggle: Option<ToggleState>,
|
toggle: Option<ToggleState>,
|
||||||
|
overflow: OverflowStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: 'static + Send + Sync> ListEntry<S> {
|
impl<S: 'static + Send + Sync> ListEntry<S> {
|
||||||
|
@ -270,6 +282,7 @@ impl<S: 'static + Send + Sync> ListEntry<S> {
|
||||||
// TODO: Should use Toggleable::NotToggleable
|
// TODO: Should use Toggleable::NotToggleable
|
||||||
// or remove Toggleable::NotToggleable from the system
|
// or remove Toggleable::NotToggleable from the system
|
||||||
toggle: None,
|
toggle: None,
|
||||||
|
overflow: OverflowStyle::Hidden,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn set_variant(mut self, variant: ListItemVariant) -> Self {
|
pub fn set_variant(mut self, variant: ListItemVariant) -> Self {
|
||||||
|
@ -416,6 +429,96 @@ impl<S: 'static + Send + Sync> ListEntry<S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ListDetailsEntryHandlers<S: 'static + Send + Sync> {
|
||||||
|
click: Option<ClickHandler<S>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: 'static + Send + Sync> Default for ListDetailsEntryHandlers<S> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { click: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Element)]
|
||||||
|
pub struct ListDetailsEntry<S: 'static + Send + Sync> {
|
||||||
|
label: SharedString,
|
||||||
|
meta: Option<SharedString>,
|
||||||
|
left_content: Option<LeftContent>,
|
||||||
|
handlers: ListDetailsEntryHandlers<S>,
|
||||||
|
actions: Option<Vec<Button<S>>>,
|
||||||
|
// TODO: make this more generic instead of
|
||||||
|
// specifically for notifications
|
||||||
|
seen: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: 'static + Send + Sync> ListDetailsEntry<S> {
|
||||||
|
pub fn new(label: impl Into<SharedString>) -> Self {
|
||||||
|
Self {
|
||||||
|
label: label.into(),
|
||||||
|
meta: None,
|
||||||
|
left_content: None,
|
||||||
|
handlers: ListDetailsEntryHandlers::default(),
|
||||||
|
actions: None,
|
||||||
|
seen: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn meta(mut self, meta: impl Into<SharedString>) -> Self {
|
||||||
|
self.meta = Some(meta.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seen(mut self, seen: bool) -> Self {
|
||||||
|
self.seen = seen;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_click(mut self, handler: ClickHandler<S>) -> Self {
|
||||||
|
self.handlers.click = Some(handler);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn actions(mut self, actions: Vec<Button<S>>) -> Self {
|
||||||
|
self.actions = Some(actions);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
|
||||||
|
let color = ThemeColor::new(cx);
|
||||||
|
let settings = user_settings(cx);
|
||||||
|
|
||||||
|
let (item_bg, item_bg_hover, item_bg_active) = match self.seen {
|
||||||
|
true => (
|
||||||
|
color.ghost_element,
|
||||||
|
color.ghost_element_hover,
|
||||||
|
color.ghost_element_active,
|
||||||
|
),
|
||||||
|
false => (
|
||||||
|
color.filled_element,
|
||||||
|
color.filled_element_hover,
|
||||||
|
color.filled_element_active,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
let label_color = match self.seen {
|
||||||
|
true => LabelColor::Muted,
|
||||||
|
false => LabelColor::Default,
|
||||||
|
};
|
||||||
|
|
||||||
|
v_stack()
|
||||||
|
.relative()
|
||||||
|
.group("")
|
||||||
|
.bg(item_bg)
|
||||||
|
.p_1()
|
||||||
|
.w_full()
|
||||||
|
.line_height(relative(1.2))
|
||||||
|
.child(Label::new(self.label.clone()).color(label_color))
|
||||||
|
.when(self.meta.is_some(), |this| {
|
||||||
|
this.child(Label::new(self.meta.clone().unwrap()).color(LabelColor::Muted))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Element)]
|
#[derive(Clone, Element)]
|
||||||
pub struct ListSeparator<S: 'static + Send + Sync> {
|
pub struct ListSeparator<S: 'static + Send + Sync> {
|
||||||
state_type: PhantomData<S>,
|
state_type: PhantomData<S>,
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use gpui3::{Element, ParentElement, Styled, ViewContext};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
h_stack, v_stack, Button, Icon, IconButton, IconElement, Label, ThemeColor, Toast, ToastOrigin,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Notification toasts are used to display a message
|
|
||||||
/// that requires them to take action.
|
|
||||||
///
|
|
||||||
/// You must provide a primary action for the user to take.
|
|
||||||
///
|
|
||||||
/// To simply convey information, use a `StatusToast`.
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct NotificationToast<S: 'static + Send + Sync + Clone> {
|
|
||||||
state_type: PhantomData<S>,
|
|
||||||
left_icon: Option<Icon>,
|
|
||||||
title: String,
|
|
||||||
message: String,
|
|
||||||
primary_action: Option<Button<S>>,
|
|
||||||
secondary_action: Option<Button<S>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: 'static + Send + Sync + Clone> NotificationToast<S> {
|
|
||||||
pub fn new(
|
|
||||||
// TODO: use a `SharedString` here
|
|
||||||
title: impl Into<String>,
|
|
||||||
message: impl Into<String>,
|
|
||||||
primary_action: Button<S>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
state_type: PhantomData,
|
|
||||||
left_icon: None,
|
|
||||||
title: title.into(),
|
|
||||||
message: message.into(),
|
|
||||||
primary_action: Some(primary_action),
|
|
||||||
secondary_action: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn left_icon(mut self, icon: Icon) -> Self {
|
|
||||||
self.left_icon = Some(icon);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn secondary_action(mut self, action: Button<S>) -> Self {
|
|
||||||
self.secondary_action = Some(action);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
|
|
||||||
let color = ThemeColor::new(cx);
|
|
||||||
|
|
||||||
let notification = h_stack()
|
|
||||||
.min_w_64()
|
|
||||||
.max_w_96()
|
|
||||||
.gap_1()
|
|
||||||
.items_start()
|
|
||||||
.p_1()
|
|
||||||
.children(self.left_icon.map(|i| IconElement::new(i)))
|
|
||||||
.child(
|
|
||||||
v_stack()
|
|
||||||
.flex_1()
|
|
||||||
.w_full()
|
|
||||||
.gap_1()
|
|
||||||
.child(
|
|
||||||
h_stack()
|
|
||||||
.justify_between()
|
|
||||||
.child(Label::new(self.title.clone()))
|
|
||||||
.child(IconButton::new(Icon::Close).color(crate::IconColor::Muted)),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
v_stack()
|
|
||||||
.overflow_hidden_x()
|
|
||||||
.gap_1()
|
|
||||||
.child(Label::new(self.message.clone()))
|
|
||||||
.child(
|
|
||||||
h_stack()
|
|
||||||
.gap_1()
|
|
||||||
.justify_end()
|
|
||||||
.children(self.secondary_action.take())
|
|
||||||
.children(self.primary_action.take()),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Toast::new(ToastOrigin::BottomRight).child(notification)
|
|
||||||
}
|
|
||||||
}
|
|
48
crates/ui2/src/components/notification_toast.rs
Normal file
48
crates/ui2/src/components/notification_toast.rs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use gpui3::rems;
|
||||||
|
|
||||||
|
use crate::{h_stack, prelude::*, Icon};
|
||||||
|
|
||||||
|
#[derive(Element)]
|
||||||
|
pub struct NotificationToast<S: 'static + Send + Sync + Clone> {
|
||||||
|
state_type: PhantomData<S>,
|
||||||
|
label: SharedString,
|
||||||
|
icon: Option<Icon>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: 'static + Send + Sync + Clone> NotificationToast<S> {
|
||||||
|
pub fn new(label: SharedString) -> Self {
|
||||||
|
Self {
|
||||||
|
state_type: PhantomData,
|
||||||
|
label,
|
||||||
|
icon: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn icon<I>(mut self, icon: I) -> Self
|
||||||
|
where
|
||||||
|
I: Into<Option<Icon>>,
|
||||||
|
{
|
||||||
|
self.icon = icon.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
|
||||||
|
let color = ThemeColor::new(cx);
|
||||||
|
|
||||||
|
h_stack()
|
||||||
|
.z_index(5)
|
||||||
|
.absolute()
|
||||||
|
.top_1()
|
||||||
|
.right_1()
|
||||||
|
.w(rems(9999.))
|
||||||
|
.max_w_56()
|
||||||
|
.py_1()
|
||||||
|
.px_1p5()
|
||||||
|
.rounded_lg()
|
||||||
|
.shadow_md()
|
||||||
|
.bg(color.elevated_surface)
|
||||||
|
.child(div().size_full().child(self.label.clone()))
|
||||||
|
}
|
||||||
|
}
|
80
crates/ui2/src/components/notifications_panel.rs
Normal file
80
crates/ui2/src/components/notifications_panel.rs
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use crate::{prelude::*, static_new_notification_items, static_read_notification_items};
|
||||||
|
use crate::{List, ListHeader};
|
||||||
|
|
||||||
|
#[derive(Element)]
|
||||||
|
pub struct NotificationsPanel<S: 'static + Send + Sync + Clone> {
|
||||||
|
state_type: PhantomData<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: 'static + Send + Sync + Clone> NotificationsPanel<S> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
state_type: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
|
||||||
|
let color = ThemeColor::new(cx);
|
||||||
|
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.w_full()
|
||||||
|
.h_full()
|
||||||
|
.bg(color.surface)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.w_full()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.overflow_y_scroll(ScrollState::default())
|
||||||
|
.child(
|
||||||
|
List::new(static_new_notification_items())
|
||||||
|
.header(ListHeader::new("NEW").set_toggle(ToggleState::Toggled))
|
||||||
|
.set_toggle(ToggleState::Toggled),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
List::new(static_read_notification_items())
|
||||||
|
.header(ListHeader::new("EARLIER").set_toggle(ToggleState::Toggled))
|
||||||
|
.empty_message("No new notifications")
|
||||||
|
.set_toggle(ToggleState::Toggled),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "stories")]
|
||||||
|
pub use stories::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "stories")]
|
||||||
|
mod stories {
|
||||||
|
use crate::{Panel, Story};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Element)]
|
||||||
|
pub struct NotificationsPanelStory<S: 'static + Send + Sync + Clone> {
|
||||||
|
state_type: PhantomData<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: 'static + Send + Sync + Clone> NotificationsPanelStory<S> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
state_type: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
_view: &mut S,
|
||||||
|
cx: &mut ViewContext<S>,
|
||||||
|
) -> impl Element<ViewState = S> {
|
||||||
|
Story::container(cx)
|
||||||
|
.child(Story::title_for::<_, NotificationsPanel<S>>(cx))
|
||||||
|
.child(Story::label(cx, "Default"))
|
||||||
|
.child(Panel::new(cx).child(NotificationsPanel::new()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use gpui3::{px, relative, rems, view, Context, Size, View};
|
use gpui3::{px, relative, rems, view, Context, Size, View};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::{prelude::*, NotificationToast, NotificationsPanel};
|
||||||
use crate::{
|
use crate::{
|
||||||
static_livestream, theme, user_settings_mut, v_stack, AssistantPanel, Button, ChatMessage,
|
static_livestream, theme, user_settings_mut, v_stack, AssistantPanel, Button, ChatMessage,
|
||||||
ChatPanel, CollabPanel, EditorPane, FakeSettings, Label, LanguageSelector, Pane, PaneGroup,
|
ChatPanel, CollabPanel, EditorPane, FakeSettings, Label, LanguageSelector, Pane, PaneGroup,
|
||||||
|
@ -249,6 +249,9 @@ impl Workspace {
|
||||||
)
|
)
|
||||||
.filter(|_| self.is_collab_panel_open()),
|
.filter(|_| self.is_collab_panel_open()),
|
||||||
)
|
)
|
||||||
|
// .child(NotificationToast::new(
|
||||||
|
// "maxbrunsfeld has requested to add you as a contact.".into(),
|
||||||
|
// ))
|
||||||
.child(
|
.child(
|
||||||
v_stack()
|
v_stack()
|
||||||
.flex_1()
|
.flex_1()
|
||||||
|
@ -289,7 +292,7 @@ impl Workspace {
|
||||||
Some(
|
Some(
|
||||||
Panel::new(cx)
|
Panel::new(cx)
|
||||||
.side(PanelSide::Right)
|
.side(PanelSide::Right)
|
||||||
.child(div().w_96().h_full().child("Notifications")),
|
.child(NotificationsPanel::new()),
|
||||||
)
|
)
|
||||||
.filter(|_| self.is_notifications_panel_open()),
|
.filter(|_| self.is_notifications_panel_open()),
|
||||||
)
|
)
|
||||||
|
|
|
@ -197,6 +197,31 @@ impl<S: 'static + Send + Sync> Button<S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Element)]
|
||||||
|
pub struct ButtonGroup<S: 'static + Send + Sync> {
|
||||||
|
state_type: PhantomData<S>,
|
||||||
|
buttons: Vec<Button<S>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: 'static + Send + Sync> ButtonGroup<S> {
|
||||||
|
pub fn new(buttons: Vec<Button<S>>) -> Self {
|
||||||
|
Self {
|
||||||
|
state_type: PhantomData,
|
||||||
|
buttons,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
|
||||||
|
let mut el = h_stack().text_size(ui_size(cx, 1.));
|
||||||
|
|
||||||
|
for button in &mut self.buttons {
|
||||||
|
el = el.child(button.render(_view, cx));
|
||||||
|
}
|
||||||
|
|
||||||
|
el
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "stories")]
|
#[cfg(feature = "stories")]
|
||||||
pub use stories::*;
|
pub use stories::*;
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::{prelude::*, v_stack, ButtonGroup};
|
||||||
|
|
||||||
#[derive(Element)]
|
#[derive(Element)]
|
||||||
pub struct Details<S: 'static + Send + Sync> {
|
pub struct Details<S: 'static + Send + Sync> {
|
||||||
state_type: PhantomData<S>,
|
state_type: PhantomData<S>,
|
||||||
text: &'static str,
|
text: &'static str,
|
||||||
meta: Option<&'static str>,
|
meta: Option<&'static str>,
|
||||||
|
actions: Option<ButtonGroup<S>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: 'static + Send + Sync> Details<S> {
|
impl<S: 'static + Send + Sync> Details<S> {
|
||||||
|
@ -15,6 +16,7 @@ impl<S: 'static + Send + Sync> Details<S> {
|
||||||
state_type: PhantomData,
|
state_type: PhantomData,
|
||||||
text,
|
text,
|
||||||
meta: None,
|
meta: None,
|
||||||
|
actions: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,18 +25,22 @@ impl<S: 'static + Send + Sync> Details<S> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn actions(mut self, actions: ButtonGroup<S>) -> Self {
|
||||||
|
self.actions = Some(actions);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
|
fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
|
||||||
let color = ThemeColor::new(cx);
|
let color = ThemeColor::new(cx);
|
||||||
|
|
||||||
div()
|
v_stack()
|
||||||
// .flex()
|
|
||||||
// .w_full()
|
|
||||||
.p_1()
|
.p_1()
|
||||||
.gap_0p5()
|
.gap_0p5()
|
||||||
.text_xs()
|
.text_xs()
|
||||||
.text_color(color.text)
|
.text_color(color.text)
|
||||||
.child(self.text)
|
.child(self.text)
|
||||||
.children(self.meta.map(|m| m))
|
.children(self.meta.map(|m| m))
|
||||||
|
.children(self.actions.take().map(|a| a))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -190,7 +190,7 @@ impl ThemeColor {
|
||||||
border_variant: theme.lowest.variant.default.border,
|
border_variant: theme.lowest.variant.default.border,
|
||||||
border_focused: theme.lowest.accent.default.border,
|
border_focused: theme.lowest.accent.default.border,
|
||||||
border_transparent: system_color.transparent,
|
border_transparent: system_color.transparent,
|
||||||
elevated_surface: theme.middle.base.default.background,
|
elevated_surface: theme.lowest.base.default.background,
|
||||||
surface: theme.middle.base.default.background,
|
surface: theme.middle.base.default.background,
|
||||||
background: theme.lowest.base.default.background,
|
background: theme.lowest.base.default.background,
|
||||||
filled_element: theme.lowest.base.default.background,
|
filled_element: theme.lowest.base.default.background,
|
||||||
|
@ -397,6 +397,12 @@ pub enum DisclosureControlStyle {
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, EnumIter)]
|
||||||
|
pub enum OverflowStyle {
|
||||||
|
Hidden,
|
||||||
|
Wrap,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Copy, Clone, EnumIter, strum::Display)]
|
#[derive(Default, PartialEq, Copy, Clone, EnumIter, strum::Display)]
|
||||||
pub enum InteractionState {
|
pub enum InteractionState {
|
||||||
#[default]
|
#[default]
|
||||||
|
|
|
@ -4,13 +4,13 @@ use std::str::FromStr;
|
||||||
use gpui3::WindowContext;
|
use gpui3::WindowContext;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
use crate::HighlightedText;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Buffer, BufferRow, BufferRows, EditorPane, FileSystemStatus, GitStatus, HighlightedLine, Icon,
|
Buffer, BufferRow, BufferRows, Button, EditorPane, FileSystemStatus, GitStatus,
|
||||||
Keybinding, Label, LabelColor, ListEntry, ListEntrySize, ListItem, Livestream, MicStatus,
|
HighlightedLine, Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, ListItem,
|
||||||
ModifierKeys, PaletteItem, Player, PlayerCallStatus, PlayerWithCallStatus, ScreenShareStatus,
|
Livestream, MicStatus, ModifierKeys, PaletteItem, Player, PlayerCallStatus,
|
||||||
Symbol, Tab, ThemeColor, ToggleState, VideoStatus,
|
PlayerWithCallStatus, ScreenShareStatus, Symbol, Tab, ThemeColor, ToggleState, VideoStatus,
|
||||||
};
|
};
|
||||||
|
use crate::{HighlightedText, ListDetailsEntry};
|
||||||
|
|
||||||
pub fn static_tabs_example<S: 'static + Send + Sync + Clone>() -> Vec<Tab<S>> {
|
pub fn static_tabs_example<S: 'static + Send + Sync + Clone>() -> Vec<Tab<S>> {
|
||||||
vec![
|
vec![
|
||||||
|
@ -324,6 +324,34 @@ pub fn static_players_with_call_status() -> Vec<PlayerWithCallStatus> {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn static_new_notification_items<S: 'static + Send + Sync + Clone>() -> Vec<ListItem<S>> {
|
||||||
|
vec![
|
||||||
|
ListEntry::new(Label::new(
|
||||||
|
"maxdeviant invited you to join a stream in #design.",
|
||||||
|
))
|
||||||
|
.set_left_icon(Icon::FileLock.into()),
|
||||||
|
ListEntry::new(Label::new("nathansobo accepted your contact request."))
|
||||||
|
.set_left_icon(Icon::FileToml.into()),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.map(From::from)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn static_read_notification_items<S: 'static + Send + Sync + Clone>() -> Vec<ListItem<S>> {
|
||||||
|
vec![
|
||||||
|
ListDetailsEntry::new("mikaylamaki added you as a contact.")
|
||||||
|
.actions(vec![Button::new("Decline"), Button::new("Accept")]),
|
||||||
|
ListDetailsEntry::new("maxdeviant invited you to a stream in #design.")
|
||||||
|
.seen(true)
|
||||||
|
.meta("This stream has ended."),
|
||||||
|
ListDetailsEntry::new("nathansobo accepted your contact request."),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.map(From::from)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn static_project_panel_project_items<S: 'static + Send + Sync + Clone>() -> Vec<ListItem<S>> {
|
pub fn static_project_panel_project_items<S: 'static + Send + Sync + Clone>() -> Vec<ListItem<S>> {
|
||||||
vec![
|
vec![
|
||||||
ListEntry::new(Label::new("zed"))
|
ListEntry::new(Label::new("zed"))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue