Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b75968ee0f | ||
|
|
ec906839ed | ||
|
|
1c091db699 |
Generated
+688
-709
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -24,9 +24,9 @@ gpui-native-icons = { path = "crates/icons" }
|
|||||||
gpui-native-theme = { path = "crates/theme" }
|
gpui-native-theme = { path = "crates/theme" }
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
gpui = { git = "https://github.com/zed-industries/zed" }
|
gpui = { package = "gpui-pre", version = "0.3.10" }
|
||||||
gpui_platform = { git = "https://github.com/zed-industries/zed", features = ["font-kit"] }
|
gpui_platform = { package = "gpui-pre-platform", version = "0.3.10", features = ["font-kit"] }
|
||||||
reqwest_client = { git = "https://github.com/zed-industries/zed" }
|
reqwest_client = { package = "gpui-pre-reqwest-client", version = "0.3.10" }
|
||||||
|
|
||||||
gpui-native-core = { path = "crates/core", default-features = false }
|
gpui-native-core = { path = "crates/core", default-features = false }
|
||||||
gpui-native-data = { path = "crates/data", default-features = false }
|
gpui-native-data = { path = "crates/data", default-features = false }
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ badge = []
|
|||||||
input = ["button"]
|
input = ["button"]
|
||||||
clipboard = ["button"]
|
clipboard = ["button"]
|
||||||
popover = []
|
popover = []
|
||||||
scroll_area = ["scrollbar"]
|
scroll_area = []
|
||||||
scrollbar = ["scroll_area"]
|
scrollbar = ["scroll_area"]
|
||||||
segmented = []
|
segmented = []
|
||||||
separator = []
|
separator = []
|
||||||
|
|||||||
+15
-11
@@ -6,7 +6,7 @@ use gpui::{
|
|||||||
IntoElement, ParentElement as _, RenderOnce, SharedString, StatefulInteractiveElement as _,
|
IntoElement, ParentElement as _, RenderOnce, SharedString, StatefulInteractiveElement as _,
|
||||||
Styled as _, Window, deferred, div, ease_out_quint, px,
|
Styled as _, Window, deferred, div, ease_out_quint, px,
|
||||||
};
|
};
|
||||||
use gpui_native_icons::{IconRole, icon};
|
use gpui_native_icons::{Icon, IconRole, icon};
|
||||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||||
|
|
||||||
use crate::common::DismissHandler;
|
use crate::common::DismissHandler;
|
||||||
@@ -33,7 +33,7 @@ pub struct Alert {
|
|||||||
title: Option<SharedString>,
|
title: Option<SharedString>,
|
||||||
message: Option<SharedString>,
|
message: Option<SharedString>,
|
||||||
severity: AlertSeverity,
|
severity: AlertSeverity,
|
||||||
icon: Option<IconRole>,
|
icon: Option<Icon>,
|
||||||
show_icon: bool,
|
show_icon: bool,
|
||||||
on_dismiss: Option<DismissHandler>,
|
on_dismiss: Option<DismissHandler>,
|
||||||
}
|
}
|
||||||
@@ -78,9 +78,12 @@ impl Alert {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Overrides the severity-derived icon with this glyph.
|
/// Overrides the severity-derived icon.
|
||||||
pub fn icon(mut self, role: IconRole) -> Self {
|
pub fn icon<I>(mut self, icon: I) -> Self
|
||||||
self.icon = Some(role);
|
where
|
||||||
|
I: Into<Icon>,
|
||||||
|
{
|
||||||
|
self.icon = Some(icon.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,10 +119,12 @@ impl RenderOnce for Alert {
|
|||||||
AlertSeverity::Danger => theme.ramps.destructive,
|
AlertSeverity::Danger => theme.ramps.destructive,
|
||||||
};
|
};
|
||||||
|
|
||||||
let icon_role = self.icon.unwrap_or(match self.severity {
|
let icon_glyph = self.icon.unwrap_or_else(|| {
|
||||||
AlertSeverity::Info => IconRole::Info,
|
icon(match self.severity {
|
||||||
AlertSeverity::Success => IconRole::Check,
|
AlertSeverity::Info => IconRole::Info,
|
||||||
AlertSeverity::Warning | AlertSeverity::Danger => IconRole::Warning,
|
AlertSeverity::Success => IconRole::Check,
|
||||||
|
AlertSeverity::Warning | AlertSeverity::Danger => IconRole::Warning,
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let foreground = theme.colors.foreground;
|
let foreground = theme.colors.foreground;
|
||||||
@@ -158,7 +163,7 @@ impl RenderOnce for Alert {
|
|||||||
.flex()
|
.flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
.h(window.line_height())
|
.h(window.line_height())
|
||||||
.child(icon(icon_role).size(icon_size).color(ramp.base)),
|
.child(icon_glyph.size(icon_size).color(ramp.base)),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.child(
|
.child(
|
||||||
@@ -332,7 +337,6 @@ impl RenderOnce for AlertsView {
|
|||||||
div()
|
div()
|
||||||
.relative()
|
.relative()
|
||||||
.w_full()
|
.w_full()
|
||||||
.debug_selector(|| "alert-entry".to_string())
|
|
||||||
.child(entry.alert.clone())
|
.child(entry.alert.clone())
|
||||||
.with_animation(
|
.with_animation(
|
||||||
("alert-enter", id),
|
("alert-enter", id),
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use gpui::{
|
|||||||
RenderOnce, Role, SharedString, StatefulInteractiveElement as _, Styled as _, Window, canvas,
|
RenderOnce, Role, SharedString, StatefulInteractiveElement as _, Styled as _, Window, canvas,
|
||||||
relative, transparent_black,
|
relative, transparent_black,
|
||||||
};
|
};
|
||||||
use gpui_native_icons::{IconRole, icon};
|
use gpui_native_icons::Icon;
|
||||||
use gpui_native_theme::{ActiveTheme as _, BORDER_WIDTH, Size};
|
use gpui_native_theme::{ActiveTheme as _, BORDER_WIDTH, Size};
|
||||||
use gpui_native_utils::{focus_ring, record_anchor_bounds};
|
use gpui_native_utils::{focus_ring, record_anchor_bounds};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@@ -38,7 +38,7 @@ pub enum ButtonVariant {
|
|||||||
pub struct Button {
|
pub struct Button {
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
label: Option<SharedString>,
|
label: Option<SharedString>,
|
||||||
icon: Option<IconRole>,
|
icon: Option<Icon>,
|
||||||
variant: ButtonVariant,
|
variant: ButtonVariant,
|
||||||
size: Size,
|
size: Size,
|
||||||
disabled: bool,
|
disabled: bool,
|
||||||
@@ -82,8 +82,11 @@ impl Button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Shows an icon beside the label.
|
/// Shows an icon beside the label.
|
||||||
pub fn icon(mut self, role: IconRole) -> Self {
|
pub fn icon<I>(mut self, icon: I) -> Self
|
||||||
self.icon = Some(role);
|
where
|
||||||
|
I: Into<Icon>,
|
||||||
|
{
|
||||||
|
self.icon = Some(icon.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,8 +239,8 @@ impl RenderOnce for Button {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
.when(!self.loading, |button| {
|
.when(!self.loading, |button| {
|
||||||
button.when_some(self.icon, |button, role| {
|
button.when_some(self.icon, |button, icon| {
|
||||||
button.child(icon(role).size(metrics.icon_size).color(foreground))
|
button.child(icon.size(metrics.icon_size).color(foreground))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.when_some(self.label, |button, label| button.child(label))
|
.when_some(self.label, |button, label| button.child(label))
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use gpui::{
|
|||||||
StatefulInteractiveElement as _, Styled as _, Subscription, Window, actions, canvas, deferred,
|
StatefulInteractiveElement as _, Styled as _, Subscription, Window, actions, canvas, deferred,
|
||||||
div, relative,
|
div, relative,
|
||||||
};
|
};
|
||||||
use gpui_native_icons::{IconRole, icon};
|
use gpui_native_icons::Icon;
|
||||||
use gpui_native_theme::{ActiveTheme as _, POPUP_MAX_HEIGHT, Size};
|
use gpui_native_theme::{ActiveTheme as _, POPUP_MAX_HEIGHT, Size};
|
||||||
|
|
||||||
use crate::common::{h_flex, v_flex};
|
use crate::common::{h_flex, v_flex};
|
||||||
@@ -49,7 +49,7 @@ pub struct Command {
|
|||||||
/// Gray text on the row's right edge, e.g. the keybinding.
|
/// Gray text on the row's right edge, e.g. the keybinding.
|
||||||
pub hint: Option<SharedString>,
|
pub hint: Option<SharedString>,
|
||||||
/// Leading glyph shown before the label.
|
/// Leading glyph shown before the label.
|
||||||
pub icon: Option<IconRole>,
|
pub icon: Option<Icon>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl Command {
|
||||||
@@ -64,8 +64,11 @@ impl Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Shows an icon before the label.
|
/// Shows an icon before the label.
|
||||||
pub fn icon(mut self, role: IconRole) -> Self {
|
pub fn icon<I>(mut self, icon: I) -> Self
|
||||||
self.icon = Some(role);
|
where
|
||||||
|
I: Into<Icon>,
|
||||||
|
{
|
||||||
|
self.icon = Some(icon.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,8 +364,8 @@ impl Render for CommandPaletteState {
|
|||||||
state.update(cx, |state, cx| state.choose_row(row_ix, window, cx));
|
state.update(cx, |state, cx| state.choose_row(row_ix, window, cx));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.when_some(command.icon, |row, role| {
|
.when_some(command.icon.clone(), |row, icon| {
|
||||||
row.child(icon(role).size(metrics.icon_size).color(colors.foreground))
|
row.child(icon.size(metrics.icon_size).color(colors.foreground))
|
||||||
})
|
})
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ use crate::common::v_flex;
|
|||||||
pub type ChangeHandler = Rc<dyn Fn(bool, &mut Window, &mut App)>;
|
pub type ChangeHandler = Rc<dyn Fn(bool, &mut Window, &mut App)>;
|
||||||
type ContentFn = Box<dyn Fn(&mut Window, &mut App) -> AnyElement>;
|
type ContentFn = Box<dyn Fn(&mut Window, &mut App) -> AnyElement>;
|
||||||
type ActionsFn = Box<dyn Fn(&mut Window, &mut App) -> Vec<AnyElement>>;
|
type ActionsFn = Box<dyn Fn(&mut Window, &mut App) -> Vec<AnyElement>>;
|
||||||
|
|
||||||
/// A centred panel over a dim scrim: the platform's modal.
|
/// A centred panel over a dim scrim: the platform's modal.
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
pub struct Dialog {
|
pub struct Dialog {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use gpui::{
|
|||||||
MouseButton, MouseDownEvent, ParentElement as _, RenderOnce, Role, SharedString,
|
MouseButton, MouseDownEvent, ParentElement as _, RenderOnce, Role, SharedString,
|
||||||
StatefulInteractiveElement as _, Styled as _, Window, canvas, div, relative,
|
StatefulInteractiveElement as _, Styled as _, Window, canvas, div, relative,
|
||||||
};
|
};
|
||||||
use gpui_native_icons::{IconRole, icon};
|
use gpui_native_icons::{Icon, IconRole, icon};
|
||||||
use gpui_native_theme::{ActiveTheme as _, BORDER_WIDTH, Size};
|
use gpui_native_theme::{ActiveTheme as _, BORDER_WIDTH, Size};
|
||||||
use gpui_native_utils::{focus_handle, focus_ring};
|
use gpui_native_utils::{focus_handle, focus_ring};
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ type ItemsFn = Box<dyn Fn(&mut Window, &mut App) -> Vec<MenuItem>>;
|
|||||||
pub struct DropdownMenu {
|
pub struct DropdownMenu {
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
label: Option<SharedString>,
|
label: Option<SharedString>,
|
||||||
icon: Option<IconRole>,
|
icon: Option<Icon>,
|
||||||
variant: ButtonVariant,
|
variant: ButtonVariant,
|
||||||
size: Size,
|
size: Size,
|
||||||
disabled: bool,
|
disabled: bool,
|
||||||
@@ -73,8 +73,11 @@ impl DropdownMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Shows an icon beside the label.
|
/// Shows an icon beside the label.
|
||||||
pub fn icon(mut self, role: IconRole) -> Self {
|
pub fn icon<I>(mut self, icon: I) -> Self
|
||||||
self.icon = Some(role);
|
where
|
||||||
|
I: Into<Icon>,
|
||||||
|
{
|
||||||
|
self.icon = Some(icon.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,8 +270,8 @@ impl RenderOnce for DropdownMenu {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
.when(!self.loading, |zone| {
|
.when(!self.loading, |zone| {
|
||||||
zone.when_some(self.icon, |zone, role| {
|
zone.when_some(self.icon, |zone, icon| {
|
||||||
zone.child(icon(role).size(metrics.icon_size).color(foreground))
|
zone.child(icon.size(metrics.icon_size).color(foreground))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.when_some(self.label, |zone, label| zone.child(label));
|
.when_some(self.label, |zone, label| zone.child(label));
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use gpui::{
|
|||||||
App, IntoElement, ParentElement as _, RenderOnce, SharedString, StyleRefinement, Styled as _,
|
App, IntoElement, ParentElement as _, RenderOnce, SharedString, StyleRefinement, Styled as _,
|
||||||
Window, div, relative,
|
Window, div, relative,
|
||||||
};
|
};
|
||||||
use gpui_native_icons::{IconRole, icon};
|
use gpui_native_icons::Icon;
|
||||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||||
|
|
||||||
use crate::common::{StyledExt, v_flex};
|
use crate::common::{StyledExt, v_flex};
|
||||||
@@ -11,7 +11,7 @@ use crate::common::{StyledExt, v_flex};
|
|||||||
/// The centered "nothing here yet" panel for lists, tables, and searches.
|
/// The centered "nothing here yet" panel for lists, tables, and searches.
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
pub struct EmptyState {
|
pub struct EmptyState {
|
||||||
icon: Option<IconRole>,
|
icon: Option<Icon>,
|
||||||
headline: SharedString,
|
headline: SharedString,
|
||||||
hint: Option<SharedString>,
|
hint: Option<SharedString>,
|
||||||
action: Option<gpui::AnyElement>,
|
action: Option<gpui::AnyElement>,
|
||||||
@@ -34,8 +34,11 @@ impl EmptyState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a large muted icon above the headline.
|
/// Sets a large muted icon above the headline.
|
||||||
pub fn icon(mut self, role: IconRole) -> Self {
|
pub fn icon<I>(mut self, icon: I) -> Self
|
||||||
self.icon = Some(role);
|
where
|
||||||
|
I: Into<Icon>,
|
||||||
|
{
|
||||||
|
self.icon = Some(icon.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,11 +81,11 @@ impl RenderOnce for EmptyState {
|
|||||||
.justify_center()
|
.justify_center()
|
||||||
.text_center()
|
.text_center()
|
||||||
.p_4()
|
.p_4()
|
||||||
.when_some(self.icon, |state, role| {
|
.when_some(self.icon, |state, icon| {
|
||||||
state.child(
|
state.child(
|
||||||
div()
|
div()
|
||||||
.mb_2()
|
.mb_2()
|
||||||
.child(icon(role).size(illustration).color(muted_foreground)),
|
.child(icon.size(illustration).color(muted_foreground)),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.child(
|
.child(
|
||||||
|
|||||||
+10
-7
@@ -9,7 +9,7 @@ use gpui::{
|
|||||||
StatefulInteractiveElement as _, Styled as _, Window, anchored, canvas, deferred, div,
|
StatefulInteractiveElement as _, Styled as _, Window, anchored, canvas, deferred, div,
|
||||||
ease_out_quint, point, px, relative,
|
ease_out_quint, point, px, relative,
|
||||||
};
|
};
|
||||||
use gpui_native_icons::{IconRole, icon as icon_glyph};
|
use gpui_native_icons::{Icon, IconRole, icon as icon_glyph};
|
||||||
use gpui_native_theme::{ActiveTheme as _, POPUP_MAX_HEIGHT};
|
use gpui_native_theme::{ActiveTheme as _, POPUP_MAX_HEIGHT};
|
||||||
|
|
||||||
use crate::common::v_flex;
|
use crate::common::v_flex;
|
||||||
@@ -30,7 +30,7 @@ type ItemsFn = Box<dyn Fn(&mut Window, &mut App) -> Vec<MenuItem>>;
|
|||||||
pub struct MenuItem {
|
pub struct MenuItem {
|
||||||
kind: MenuItemKind,
|
kind: MenuItemKind,
|
||||||
label: SharedString,
|
label: SharedString,
|
||||||
icon: Option<IconRole>,
|
icon: Option<Icon>,
|
||||||
shortcut: Option<SharedString>,
|
shortcut: Option<SharedString>,
|
||||||
handler: Option<ItemHandler>,
|
handler: Option<ItemHandler>,
|
||||||
disabled: bool,
|
disabled: bool,
|
||||||
@@ -86,8 +86,11 @@ impl MenuItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Shows an icon before the label.
|
/// Shows an icon before the label.
|
||||||
pub fn icon(mut self, role: IconRole) -> Self {
|
pub fn icon<I>(mut self, icon: I) -> Self
|
||||||
self.icon = Some(role);
|
where
|
||||||
|
I: Into<Icon>,
|
||||||
|
{
|
||||||
|
self.icon = Some(icon.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,7 +529,7 @@ fn render_item(
|
|||||||
let destructive = item.destructive;
|
let destructive = item.destructive;
|
||||||
let checked = item.checked;
|
let checked = item.checked;
|
||||||
let shortcut = item.shortcut.clone();
|
let shortcut = item.shortcut.clone();
|
||||||
let icon_role = item.icon;
|
let icon = item.icon.clone();
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.id(("menu-item", index))
|
.id(("menu-item", index))
|
||||||
@@ -576,8 +579,8 @@ fn render_item(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.when_some(icon_role, |row, role| {
|
.when_some(icon, |row, icon| {
|
||||||
row.child(icon_glyph(role).size(chrome.icon_size).color(label_color))
|
row.child(icon.size(chrome.icon_size).color(label_color))
|
||||||
})
|
})
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use gpui::{
|
|||||||
SpringConfig, StatefulInteractiveElement as _, StyleRefinement, Styled as _, Toggled, Window,
|
SpringConfig, StatefulInteractiveElement as _, StyleRefinement, Styled as _, Toggled, Window,
|
||||||
canvas, div,
|
canvas, div,
|
||||||
};
|
};
|
||||||
use gpui_native_icons::{IconRole, icon};
|
use gpui_native_icons::Icon;
|
||||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||||
use gpui_native_utils::{focus_handle, focus_ring};
|
use gpui_native_utils::{focus_handle, focus_ring};
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ type ChangeHandler<T> = Rc<dyn Fn(T, &mut Window, &mut App)>;
|
|||||||
pub struct SegmentedOption<T> {
|
pub struct SegmentedOption<T> {
|
||||||
value: T,
|
value: T,
|
||||||
label: SharedString,
|
label: SharedString,
|
||||||
icon: Option<IconRole>,
|
icon: Option<Icon>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> SegmentedOption<T> {
|
impl<T> SegmentedOption<T> {
|
||||||
@@ -38,8 +38,11 @@ impl<T> SegmentedOption<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a leading icon for the segment.
|
/// Sets a leading icon for the segment.
|
||||||
pub fn icon(mut self, role: IconRole) -> Self {
|
pub fn icon<I>(mut self, icon: I) -> Self
|
||||||
self.icon = Some(role);
|
where
|
||||||
|
I: Into<Icon>,
|
||||||
|
{
|
||||||
|
self.icon = Some(icon.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,8 +229,8 @@ where
|
|||||||
.role(Role::RadioButton)
|
.role(Role::RadioButton)
|
||||||
.aria_label(segment.label.clone())
|
.aria_label(segment.label.clone())
|
||||||
.aria_toggled(Toggled::from(is_selected))
|
.aria_toggled(Toggled::from(is_selected))
|
||||||
.when_some(segment.icon, |segment, role| {
|
.when_some(segment.icon.clone(), |segment, icon| {
|
||||||
segment.child(icon(role).size(icon_size).color(content_color))
|
segment.child(icon.size(icon_size).color(content_color))
|
||||||
})
|
})
|
||||||
.child(segment.label.clone())
|
.child(segment.label.clone())
|
||||||
.child(
|
.child(
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use gpui::{
|
|||||||
App, ElementId, InteractiveElement as _, IntoElement, ParentElement as _, RenderOnce, Role,
|
App, ElementId, InteractiveElement as _, IntoElement, ParentElement as _, RenderOnce, Role,
|
||||||
SharedString, StatefulInteractiveElement as _, StyleRefinement, Styled as _, Window,
|
SharedString, StatefulInteractiveElement as _, StyleRefinement, Styled as _, Window,
|
||||||
};
|
};
|
||||||
use gpui_native_icons::{IconRole, icon};
|
use gpui_native_icons::{Icon, IconRole, icon};
|
||||||
use gpui_native_theme::{ActiveTheme as _, BORDER_WIDTH, Size};
|
use gpui_native_theme::{ActiveTheme as _, BORDER_WIDTH, Size};
|
||||||
use gpui_native_utils::focus_ring;
|
use gpui_native_utils::focus_ring;
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ pub type SelectHandler<T> = Rc<dyn Fn(Option<T>, &mut Window, &mut App)>;
|
|||||||
pub struct SelectOption<T> {
|
pub struct SelectOption<T> {
|
||||||
value: T,
|
value: T,
|
||||||
label: SharedString,
|
label: SharedString,
|
||||||
icon: Option<IconRole>,
|
icon: Option<Icon>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> SelectOption<T> {
|
impl<T> SelectOption<T> {
|
||||||
@@ -37,8 +37,11 @@ impl<T> SelectOption<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a leading icon for the option's row.
|
/// Sets a leading icon for the option's row.
|
||||||
pub fn icon(mut self, role: IconRole) -> Self {
|
pub fn icon<I>(mut self, icon: I) -> Self
|
||||||
self.icon = Some(role);
|
where
|
||||||
|
I: Into<Icon>,
|
||||||
|
{
|
||||||
|
self.icon = Some(icon.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -272,8 +275,8 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut row = MenuItem::new(option.label.clone());
|
let mut row = MenuItem::new(option.label.clone());
|
||||||
if let Some(role) = option.icon {
|
if let Some(icon) = option.icon.clone() {
|
||||||
row = row.icon(role);
|
row = row.icon(icon);
|
||||||
}
|
}
|
||||||
row.checked(is_selected).handler(handler)
|
row.checked(is_selected).handler(handler)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use gpui::{
|
|||||||
IntoElement, ParentElement as _, RenderOnce, SharedString, StatefulInteractiveElement as _,
|
IntoElement, ParentElement as _, RenderOnce, SharedString, StatefulInteractiveElement as _,
|
||||||
Styled as _, Task, Window, deferred, div, ease_out_quint, px,
|
Styled as _, Task, Window, deferred, div, ease_out_quint, px,
|
||||||
};
|
};
|
||||||
use gpui_native_icons::{IconRole, icon};
|
use gpui_native_icons::{Icon, IconRole, icon};
|
||||||
use gpui_native_theme::ActiveTheme as _;
|
use gpui_native_theme::ActiveTheme as _;
|
||||||
|
|
||||||
use crate::button::{Button, ButtonVariant};
|
use crate::button::{Button, ButtonVariant};
|
||||||
@@ -43,7 +43,7 @@ impl ToastAction {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Toast {
|
pub struct Toast {
|
||||||
message: SharedString,
|
message: SharedString,
|
||||||
icon: Option<IconRole>,
|
icon: Option<Icon>,
|
||||||
action: Option<ToastAction>,
|
action: Option<ToastAction>,
|
||||||
duration: Option<Duration>,
|
duration: Option<Duration>,
|
||||||
on_dismiss: Option<DismissHandler>,
|
on_dismiss: Option<DismissHandler>,
|
||||||
@@ -68,8 +68,11 @@ impl Toast {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Shows this glyph before the content.
|
/// Shows this glyph before the content.
|
||||||
pub fn icon(mut self, role: IconRole) -> Self {
|
pub fn icon<I>(mut self, icon: I) -> Self
|
||||||
self.icon = Some(role);
|
where
|
||||||
|
I: Into<Icon>,
|
||||||
|
{
|
||||||
|
self.icon = Some(icon.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,7 +278,6 @@ fn pill(entry: &ToastEntry, toasts: &Entity<Toasts>, cx: &App) -> gpui::Stateful
|
|||||||
|
|
||||||
div()
|
div()
|
||||||
.id(("toast", id))
|
.id(("toast", id))
|
||||||
.debug_selector(|| "toast-pill".to_string())
|
|
||||||
.max_w(px(480.))
|
.max_w(px(480.))
|
||||||
.flex()
|
.flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
@@ -286,14 +288,14 @@ fn pill(entry: &ToastEntry, toasts: &Entity<Toasts>, cx: &App) -> gpui::Stateful
|
|||||||
.border_color(border)
|
.border_color(border)
|
||||||
.rounded(radius)
|
.rounded(radius)
|
||||||
.shadow(shadows.clone())
|
.shadow(shadows.clone())
|
||||||
.when_some(toast.icon, |pill, icon_role| {
|
.when_some(toast.icon.clone(), |pill, icon| {
|
||||||
pill.child(
|
pill.child(
|
||||||
div()
|
div()
|
||||||
.flex_none()
|
.flex_none()
|
||||||
.mr_2()
|
.mr_2()
|
||||||
.flex()
|
.flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
.child(icon(icon_role).size(icon_size).color(foreground)),
|
.child(icon.size(icon_size).color(foreground)),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.child(
|
.child(
|
||||||
|
|||||||
+10
-11
@@ -6,7 +6,7 @@ use gpui::{
|
|||||||
ParentElement as _, RenderOnce, Role, SharedString, StatefulInteractiveElement as _,
|
ParentElement as _, RenderOnce, Role, SharedString, StatefulInteractiveElement as _,
|
||||||
Styled as _, Window, div,
|
Styled as _, Window, div,
|
||||||
};
|
};
|
||||||
use gpui_native_icons::{IconRole, icon};
|
use gpui_native_icons::Icon;
|
||||||
use gpui_native_theme::ActiveTheme as _;
|
use gpui_native_theme::ActiveTheme as _;
|
||||||
use gpui_native_utils::{nav_key, paint_selectable_row, selectable_label_color};
|
use gpui_native_utils::{nav_key, paint_selectable_row, selectable_label_color};
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ type ItemsFn = Box<dyn Fn(&mut Window, &mut App) -> Vec<ListItem>>;
|
|||||||
/// One row of a [`List`].
|
/// One row of a [`List`].
|
||||||
pub struct ListItem {
|
pub struct ListItem {
|
||||||
label: SharedString,
|
label: SharedString,
|
||||||
icon: Option<IconRole>,
|
icon: Option<Icon>,
|
||||||
detail: Option<SharedString>,
|
detail: Option<SharedString>,
|
||||||
disabled: bool,
|
disabled: bool,
|
||||||
}
|
}
|
||||||
@@ -36,9 +36,12 @@ impl ListItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shows an icon leading the label.
|
/// Shows an icon leading the label, a role or any custom [`IconNamed`].
|
||||||
pub fn icon(mut self, role: IconRole) -> Self {
|
pub fn icon<I>(mut self, icon: I) -> Self
|
||||||
self.icon = Some(role);
|
where
|
||||||
|
I: Into<Icon>,
|
||||||
|
{
|
||||||
|
self.icon = Some(icon.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,12 +225,8 @@ fn render_row(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.when_some(item.icon, |row, role| {
|
.when_some(item.icon.clone(), |row, icon| {
|
||||||
row.child(
|
row.child(icon.size(theme.sizes.medium.icon_size).color(label_color))
|
||||||
icon(role)
|
|
||||||
.size(theme.sizes.medium.icon_size)
|
|
||||||
.color(label_color),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
|
|||||||
+11
-12
@@ -10,7 +10,7 @@ use gpui::{
|
|||||||
Styled as _, Window, div,
|
Styled as _, Window, div,
|
||||||
};
|
};
|
||||||
use gpui_native_core::scroll_area::ScrollArea;
|
use gpui_native_core::scroll_area::ScrollArea;
|
||||||
use gpui_native_icons::{IconRole, icon};
|
use gpui_native_icons::Icon;
|
||||||
use gpui_native_theme::ActiveTheme as _;
|
use gpui_native_theme::ActiveTheme as _;
|
||||||
use gpui_native_utils::{nav_key, paint_selectable_row, selectable_label_color};
|
use gpui_native_utils::{nav_key, paint_selectable_row, selectable_label_color};
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ pub enum SidebarEntry {
|
|||||||
/// A selectable row.
|
/// A selectable row.
|
||||||
Row {
|
Row {
|
||||||
label: SharedString,
|
label: SharedString,
|
||||||
icon: Option<IconRole>,
|
icon: Option<Icon>,
|
||||||
detail: Option<SharedString>,
|
detail: Option<SharedString>,
|
||||||
disabled: bool,
|
disabled: bool,
|
||||||
},
|
},
|
||||||
@@ -56,10 +56,13 @@ impl SidebarEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shows an icon leading the label.
|
/// Shows an icon leading the label, a role or any custom [`IconNamed`].
|
||||||
pub fn icon(mut self, role: IconRole) -> Self {
|
pub fn icon<I>(mut self, icon: I) -> Self
|
||||||
if let Self::Row { icon, .. } = &mut self {
|
where
|
||||||
*icon = Some(role);
|
I: Into<Icon>,
|
||||||
|
{
|
||||||
|
if let Self::Row { icon: slot, .. } = &mut self {
|
||||||
|
*slot = Some(icon.into());
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -337,12 +340,8 @@ fn render_entry(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
row = row.when_some(*row_icon, |row, role| {
|
row = row.when_some(row_icon.clone(), |row, icon| {
|
||||||
row.child(
|
row.child(icon.size(theme.sizes.medium.icon_size).color(label_color))
|
||||||
icon(role)
|
|
||||||
.size(theme.sizes.medium.icon_size)
|
|
||||||
.color(label_color),
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
row = row.child(div().flex_1().text_color(label_color).child(label.clone()));
|
row = row.child(div().flex_1().text_color(label_color).child(label.clone()));
|
||||||
|
|||||||
+15
-12
@@ -10,7 +10,7 @@ use gpui_native_core::button::{Button, ButtonVariant};
|
|||||||
use gpui_native_core::common::Sizable as _;
|
use gpui_native_core::common::Sizable as _;
|
||||||
use gpui_native_core::spinner::Spinner;
|
use gpui_native_core::spinner::Spinner;
|
||||||
use gpui_native_core::tooltip::Tooltip;
|
use gpui_native_core::tooltip::Tooltip;
|
||||||
use gpui_native_icons::{IconRole, icon};
|
use gpui_native_icons::{Icon, IconRole};
|
||||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||||
use gpui_native_utils::adjacent_selectable;
|
use gpui_native_utils::adjacent_selectable;
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ pub struct Tab {
|
|||||||
/// The stable identity for element state; must survive reordering.
|
/// The stable identity for element state; must survive reordering.
|
||||||
key: ElementId,
|
key: ElementId,
|
||||||
label: SharedString,
|
label: SharedString,
|
||||||
icon: Option<IconRole>,
|
icon: Option<Icon>,
|
||||||
lead: Option<AnyElement>,
|
lead: Option<AnyElement>,
|
||||||
busy: bool,
|
busy: bool,
|
||||||
disabled: bool,
|
disabled: bool,
|
||||||
@@ -63,8 +63,11 @@ impl Tab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Shows an icon leading the label.
|
/// Shows an icon leading the label.
|
||||||
pub fn icon(mut self, role: IconRole) -> Self {
|
pub fn icon<I>(mut self, icon: I) -> Self
|
||||||
self.icon = Some(role);
|
where
|
||||||
|
I: Into<Icon>,
|
||||||
|
{
|
||||||
|
self.icon = Some(icon.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,18 +347,18 @@ fn render_tab(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let lead = tab.lead.take();
|
let lead = tab.lead.take();
|
||||||
|
let icon = tab.icon.take();
|
||||||
|
|
||||||
strip_tab = match lead {
|
strip_tab = match lead {
|
||||||
Some(lead) => strip_tab.child(lead),
|
Some(lead) => strip_tab.child(lead),
|
||||||
None => match variant {
|
None => match variant {
|
||||||
TabsVariant::Underline => strip_tab.child(div().flex_none().pb_0p5().when_some(
|
TabsVariant::Underline => {
|
||||||
tab.icon,
|
strip_tab.child(div().flex_none().pb_0p5().when_some(icon, |slot, icon| {
|
||||||
|slot, role| {
|
slot.child(icon.size(metrics.dense_icon_size).color(label_color))
|
||||||
slot.child(icon(role).size(metrics.dense_icon_size).color(label_color))
|
}))
|
||||||
},
|
}
|
||||||
)),
|
TabsVariant::Pill => strip_tab.when_some(icon, |strip_tab, icon| {
|
||||||
TabsVariant::Pill => strip_tab.when_some(tab.icon, |strip_tab, role| {
|
strip_tab.child(icon.size(metrics.dense_icon_size).color(label_color))
|
||||||
strip_tab.child(icon(role).size(metrics.dense_icon_size).color(label_color))
|
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
+9
-10
@@ -6,7 +6,7 @@ use gpui::{
|
|||||||
ParentElement as _, RenderOnce, Role, SharedString, StatefulInteractiveElement as _,
|
ParentElement as _, RenderOnce, Role, SharedString, StatefulInteractiveElement as _,
|
||||||
Styled as _, Window, div,
|
Styled as _, Window, div,
|
||||||
};
|
};
|
||||||
use gpui_native_icons::{IconRole, icon};
|
use gpui_native_icons::{Icon, IconRole, icon};
|
||||||
use gpui_native_theme::ActiveTheme as _;
|
use gpui_native_theme::ActiveTheme as _;
|
||||||
use gpui_native_utils::{nav_key, paint_selectable_row, selectable_label_color};
|
use gpui_native_utils::{nav_key, paint_selectable_row, selectable_label_color};
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ type ItemsFn = Box<dyn Fn(&mut Window, &mut App) -> Vec<TreeItem>>;
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TreeItem {
|
pub struct TreeItem {
|
||||||
label: SharedString,
|
label: SharedString,
|
||||||
icon: Option<IconRole>,
|
icon: Option<Icon>,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
expandable: bool,
|
expandable: bool,
|
||||||
expanded: bool,
|
expanded: bool,
|
||||||
@@ -44,8 +44,11 @@ impl TreeItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Shows an icon leading the label.
|
/// Shows an icon leading the label.
|
||||||
pub fn icon(mut self, role: IconRole) -> Self {
|
pub fn icon<I>(mut self, icon: I) -> Self
|
||||||
self.icon = Some(role);
|
where
|
||||||
|
I: Into<Icon>,
|
||||||
|
{
|
||||||
|
self.icon = Some(icon.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,12 +320,8 @@ fn render_row(
|
|||||||
selected,
|
selected,
|
||||||
on_disclosure,
|
on_disclosure,
|
||||||
))
|
))
|
||||||
.when_some(item.icon, |row, role| {
|
.when_some(item.icon.clone(), |row, icon| {
|
||||||
row.child(
|
row.child(icon.size(theme.sizes.medium.icon_size).color(label_color))
|
||||||
icon(role)
|
|
||||||
.size(theme.sizes.medium.icon_size)
|
|
||||||
.color(label_color),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name = "gpui-native-icons"
|
name = "gpui-native-icons"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
description = "System icons for GPUI: semantic roles resolved per platform, with a bundled Lucide fallback"
|
description = "System icons for GPUI: semantic roles resolved per platform, a bundled Lucide fallback, and app-defined icon sets"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
|||||||
+124
-58
@@ -1,10 +1,11 @@
|
|||||||
//! System icons for GPUI: components and apps reference semantic roles,
|
//! System icons for GPUI: components and apps reference semantic roles,
|
||||||
//! and the crate resolves each role to a glyph for the current platform.
|
//! and the crate resolves each role to a glyph for the current platform.
|
||||||
|
//! Apps ship their own icon set by implementing [`IconNamed`].
|
||||||
|
|
||||||
use gpui::prelude::FluentBuilder as _;
|
use gpui::prelude::FluentBuilder as _;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AssetSource, Hsla, IntoElement, ParentElement as _, Pixels, RenderOnce, SharedString,
|
AnyElement, AssetSource, Hsla, IntoElement, ParentElement as _, Pixels, RenderOnce,
|
||||||
Styled as _, Transformation, Window, div, px, svg,
|
SharedString, Styled as _, Transformation, Window, div, px, svg,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
|
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
|
||||||
@@ -194,10 +195,29 @@ impl IconRole {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A glyph named by an application's own icon set.
|
||||||
|
pub trait IconNamed {
|
||||||
|
/// The SVG asset path, e.g. `icons/commit.svg`.
|
||||||
|
fn path(self) -> SharedString;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IconNamed for IconRole {
|
||||||
|
fn path(self) -> SharedString {
|
||||||
|
SharedString::from(self.asset_path())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The glyph an icon draws.
|
||||||
|
#[derive(Clone)]
|
||||||
|
enum Glyph {
|
||||||
|
Role(IconRole),
|
||||||
|
Named(SharedString),
|
||||||
|
}
|
||||||
|
|
||||||
/// An icon element. Unpainted unless a color is set.
|
/// An icon element. Unpainted unless a color is set.
|
||||||
#[derive(IntoElement)]
|
#[derive(Clone, IntoElement)]
|
||||||
pub struct Icon {
|
pub struct Icon {
|
||||||
role: IconRole,
|
glyph: Glyph,
|
||||||
size: Pixels,
|
size: Pixels,
|
||||||
color: Option<Hsla>,
|
color: Option<Hsla>,
|
||||||
transformation: Option<Transformation>,
|
transformation: Option<Transformation>,
|
||||||
@@ -206,7 +226,7 @@ pub struct Icon {
|
|||||||
/// Creates an icon for the role at the default size of 16px.
|
/// Creates an icon for the role at the default size of 16px.
|
||||||
pub fn icon(role: IconRole) -> Icon {
|
pub fn icon(role: IconRole) -> Icon {
|
||||||
Icon {
|
Icon {
|
||||||
role,
|
glyph: Glyph::Role(role),
|
||||||
size: px(16.0),
|
size: px(16.0),
|
||||||
color: None,
|
color: None,
|
||||||
transformation: None,
|
transformation: None,
|
||||||
@@ -214,6 +234,19 @@ pub fn icon(role: IconRole) -> Icon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Icon {
|
impl Icon {
|
||||||
|
/// Creates an icon from an application-defined icon set.
|
||||||
|
pub fn named<I>(named: I) -> Self
|
||||||
|
where
|
||||||
|
I: IconNamed,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
glyph: Glyph::Named(named.path()),
|
||||||
|
size: px(16.0),
|
||||||
|
color: None,
|
||||||
|
transformation: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn size(mut self, size: Pixels) -> Self {
|
pub fn size(mut self, size: Pixels) -> Self {
|
||||||
self.size = size;
|
self.size = size;
|
||||||
self
|
self
|
||||||
@@ -232,66 +265,99 @@ impl Icon {
|
|||||||
|
|
||||||
/// The asset key a resolved freedesktop file is served under.
|
/// The asset key a resolved freedesktop file is served under.
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
fn freedesktop_asset_path(&self) -> Option<SharedString> {
|
fn freedesktop_asset_path(size: Pixels, role: &IconRole) -> Option<SharedString> {
|
||||||
let size = self.size.as_f32().max(1.0).round() as u32;
|
let size = size.as_f32().max(1.0).round() as u32;
|
||||||
freedesktop::cached_file(self.role, size).map(|_| {
|
freedesktop::cached_file(*role, size)
|
||||||
SharedString::from(format!("icons/freedesktop/{}-{size}.svg", self.role.slug()))
|
.map(|_| SharedString::from(format!("icons/freedesktop/{}-{size}.svg", role.slug())))
|
||||||
})
|
}
|
||||||
|
|
||||||
|
/// Roles resolve per platform.
|
||||||
|
fn render_role(
|
||||||
|
size: Pixels,
|
||||||
|
color: Option<Hsla>,
|
||||||
|
transformation: Option<Transformation>,
|
||||||
|
role: IconRole,
|
||||||
|
) -> AnyElement {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
if let Some(glyph) = segoe::glyph(role) {
|
||||||
|
return Self::render_glyph(size, color, glyph).into_any_element();
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
if let Some(path) = Self::freedesktop_asset_path(size, &role) {
|
||||||
|
return Self::render_svg_path(size, color, transformation, path).into_any_element();
|
||||||
|
}
|
||||||
|
Self::render_svg_path(
|
||||||
|
size,
|
||||||
|
color,
|
||||||
|
transformation,
|
||||||
|
SharedString::from(role.asset_path()),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Windows: the Segoe Fluent codepoint drawn as text in the system font.
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn render_glyph(size: Pixels, color: Option<Hsla>, glyph: char) -> AnyElement {
|
||||||
|
div()
|
||||||
|
.flex_none()
|
||||||
|
.size(size)
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.justify_center()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.font_family(segoe::FONT_FAMILY)
|
||||||
|
.text_size(size)
|
||||||
|
.when_some(color, |el, color| el.text_color(color))
|
||||||
|
.child(glyph.to_string()),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_svg_path(
|
||||||
|
size: Pixels,
|
||||||
|
color: Option<Hsla>,
|
||||||
|
transformation: Option<Transformation>,
|
||||||
|
path: SharedString,
|
||||||
|
) -> AnyElement {
|
||||||
|
div()
|
||||||
|
.flex_none()
|
||||||
|
.size(size)
|
||||||
|
.child(
|
||||||
|
svg()
|
||||||
|
.size_full()
|
||||||
|
.path(path)
|
||||||
|
.when_some(color, |el, color| el.text_color(color))
|
||||||
|
.when_some(transformation, |el, transformation| {
|
||||||
|
el.with_transformation(transformation)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> From<I> for Icon
|
||||||
|
where
|
||||||
|
I: IconNamed,
|
||||||
|
{
|
||||||
|
fn from(named: I) -> Self {
|
||||||
|
Icon::named(named)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderOnce for Icon {
|
impl RenderOnce for Icon {
|
||||||
fn render(self, _window: &mut Window, _cx: &mut gpui::App) -> impl IntoElement {
|
fn render(self, _window: &mut Window, _cx: &mut gpui::App) -> impl IntoElement {
|
||||||
#[cfg(target_os = "windows")]
|
let Icon {
|
||||||
return self.render_glyph();
|
glyph,
|
||||||
#[cfg(not(target_os = "windows"))]
|
size,
|
||||||
return self.render_svg();
|
color,
|
||||||
}
|
transformation,
|
||||||
}
|
} = self;
|
||||||
|
match glyph {
|
||||||
impl Icon {
|
Glyph::Named(path) => Self::render_svg_path(size, color, transformation, path),
|
||||||
/// Windows: the Segoe Fluent codepoint drawn as text in the system font;
|
Glyph::Role(role) => Self::render_role(size, color, transformation, role),
|
||||||
/// roles without a codepoint fall back to the bundled SVG.
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
fn render_glyph(self) -> impl IntoElement {
|
|
||||||
match segoe::glyph(self.role) {
|
|
||||||
Some(glyph) => div()
|
|
||||||
.flex_none()
|
|
||||||
.size(self.size)
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.justify_center()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.font_family(segoe::FONT_FAMILY)
|
|
||||||
.text_size(self.size)
|
|
||||||
.when_some(self.color, |el, color| el.text_color(color))
|
|
||||||
.child(glyph.to_string()),
|
|
||||||
)
|
|
||||||
.into_any_element(),
|
|
||||||
None => self.render_svg().into_any_element(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_svg(self) -> impl IntoElement {
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
let path: SharedString = self
|
|
||||||
.freedesktop_asset_path()
|
|
||||||
.unwrap_or_else(|| SharedString::from(self.role.asset_path()));
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
|
||||||
let path: SharedString = SharedString::from(self.role.asset_path());
|
|
||||||
|
|
||||||
div().flex_none().size(self.size).child(
|
|
||||||
svg()
|
|
||||||
.size_full()
|
|
||||||
.path(path)
|
|
||||||
.when_some(self.color, |el, color| el.text_color(color))
|
|
||||||
.when_some(self.transformation, |el, transformation| {
|
|
||||||
el.with_transformation(transformation)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The asset source serving this crate's bundled glyphs.
|
/// The asset source serving this crate's bundled glyphs.
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ gpui = { workspace = true, features = ["test-support"] }
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["full"]
|
default = ["full"]
|
||||||
full = ["dock", "resize_handle", "resizable", "shell", "window_controls", "toast", "alert"]
|
full = ["dock", "resize_handle", "resizable", "shell", "window_controls"]
|
||||||
dock = [
|
dock = [
|
||||||
"resize_handle",
|
"resize_handle",
|
||||||
"dep:gpui-native-core",
|
"dep:gpui-native-core",
|
||||||
@@ -36,6 +36,4 @@ shell = [
|
|||||||
"dep:gpui-native-data",
|
"dep:gpui-native-data",
|
||||||
"dep:gpui-native-icons",
|
"dep:gpui-native-icons",
|
||||||
]
|
]
|
||||||
toast = ["shell", "gpui-native-core/toast"]
|
|
||||||
alert = ["shell", "gpui-native-core/alert"]
|
|
||||||
window_controls = ["dep:gpui-native-icons"]
|
window_controls = ["dep:gpui-native-icons"]
|
||||||
|
|||||||
+13
-10
@@ -15,7 +15,7 @@ use gpui_native_core::common::{h_flex, v_flex};
|
|||||||
use gpui_native_core::context_menu::ContextMenu;
|
use gpui_native_core::context_menu::ContextMenu;
|
||||||
use gpui_native_core::menu::MenuItem;
|
use gpui_native_core::menu::MenuItem;
|
||||||
use gpui_native_data::tabs::{Tab, Tabs, TabsVariant};
|
use gpui_native_data::tabs::{Tab, Tabs, TabsVariant};
|
||||||
use gpui_native_icons::IconRole;
|
use gpui_native_icons::{Icon, IconRole};
|
||||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||||
|
|
||||||
use crate::resize_handle::ResizeHandle;
|
use crate::resize_handle::ResizeHandle;
|
||||||
@@ -35,7 +35,7 @@ type OpenRightSidebarHandler = Rc<dyn Fn(&mut App)>;
|
|||||||
pub struct PanelDescriptor {
|
pub struct PanelDescriptor {
|
||||||
id: SharedString,
|
id: SharedString,
|
||||||
title: SharedString,
|
title: SharedString,
|
||||||
icon: Option<IconRole>,
|
icon: Option<Icon>,
|
||||||
build: PanelBuilder,
|
build: PanelBuilder,
|
||||||
right_sidebar: Option<PanelBuilder>,
|
right_sidebar: Option<PanelBuilder>,
|
||||||
}
|
}
|
||||||
@@ -58,8 +58,11 @@ impl PanelDescriptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Shows an icon leading the tab label.
|
/// Shows an icon leading the tab label.
|
||||||
pub fn icon(mut self, role: IconRole) -> Self {
|
pub fn icon<I>(mut self, icon: I) -> Self
|
||||||
self.icon = Some(role);
|
where
|
||||||
|
I: Into<Icon>,
|
||||||
|
{
|
||||||
|
self.icon = Some(icon.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +133,7 @@ impl Render for DragPreview {
|
|||||||
struct PanelTab {
|
struct PanelTab {
|
||||||
panel: SharedString,
|
panel: SharedString,
|
||||||
title: SharedString,
|
title: SharedString,
|
||||||
icon: Option<IconRole>,
|
icon: Option<Icon>,
|
||||||
view: AnyView,
|
view: AnyView,
|
||||||
right_view: Option<AnyView>,
|
right_view: Option<AnyView>,
|
||||||
}
|
}
|
||||||
@@ -215,7 +218,7 @@ impl DockState {
|
|||||||
let tab = PanelTab {
|
let tab = PanelTab {
|
||||||
panel: SharedString::from(id),
|
panel: SharedString::from(id),
|
||||||
title: panel.title.clone(),
|
title: panel.title.clone(),
|
||||||
icon: panel.icon,
|
icon: panel.icon.clone(),
|
||||||
view: (panel.build)(window, cx),
|
view: (panel.build)(window, cx),
|
||||||
right_view,
|
right_view,
|
||||||
};
|
};
|
||||||
@@ -679,7 +682,7 @@ impl DockState {
|
|||||||
.map(|tab| TabView {
|
.map(|tab| TabView {
|
||||||
panel: tab.panel.clone(),
|
panel: tab.panel.clone(),
|
||||||
title: tab.title.clone(),
|
title: tab.title.clone(),
|
||||||
icon: tab.icon,
|
icon: tab.icon.clone(),
|
||||||
view: tab.view.clone(),
|
view: tab.view.clone(),
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
@@ -747,7 +750,7 @@ fn take_node(node: &mut Node) -> Node {
|
|||||||
struct TabView {
|
struct TabView {
|
||||||
panel: SharedString,
|
panel: SharedString,
|
||||||
title: SharedString,
|
title: SharedString,
|
||||||
icon: Option<IconRole>,
|
icon: Option<Icon>,
|
||||||
view: AnyView,
|
view: AnyView,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1039,8 +1042,8 @@ fn render_pane(
|
|||||||
tabs.iter()
|
tabs.iter()
|
||||||
.map(|tab| {
|
.map(|tab| {
|
||||||
let mut entry = Tab::new(tab.panel.clone(), tab.title.clone());
|
let mut entry = Tab::new(tab.panel.clone(), tab.title.clone());
|
||||||
if let Some(role) = tab.icon {
|
if let Some(icon) = tab.icon.clone() {
|
||||||
entry = entry.icon(role);
|
entry = entry.icon(icon);
|
||||||
}
|
}
|
||||||
entry.closeable(true).wrap({
|
entry.closeable(true).wrap({
|
||||||
let panel = tab.panel.clone();
|
let panel = tab.panel.clone();
|
||||||
|
|||||||
@@ -6,12 +6,8 @@ use gpui::{
|
|||||||
AnyElement, App, AppContext as _, Context, ElementId, Entity, InteractiveElement as _,
|
AnyElement, App, AppContext as _, Context, ElementId, Entity, InteractiveElement as _,
|
||||||
IntoElement, ParentElement as _, Pixels, RenderOnce, Styled as _, Window, div, px,
|
IntoElement, ParentElement as _, Pixels, RenderOnce, Styled as _, Window, div, px,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "alert")]
|
|
||||||
use gpui_native_core::alert::{Alert, Alerts, AlertsView};
|
|
||||||
use gpui_native_core::button::{Button, ButtonVariant};
|
use gpui_native_core::button::{Button, ButtonVariant};
|
||||||
use gpui_native_core::common::{Sizable as _, h_flex, v_flex};
|
use gpui_native_core::common::{Sizable as _, h_flex, v_flex};
|
||||||
#[cfg(feature = "toast")]
|
|
||||||
use gpui_native_core::toast::{Toast, Toasts, ToastsView};
|
|
||||||
use gpui_native_data::sidebar::Sidebar;
|
use gpui_native_data::sidebar::Sidebar;
|
||||||
use gpui_native_icons::IconRole;
|
use gpui_native_icons::IconRole;
|
||||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||||
@@ -33,24 +29,16 @@ pub struct LayoutState {
|
|||||||
right_visible: bool,
|
right_visible: bool,
|
||||||
left_width: Pixels,
|
left_width: Pixels,
|
||||||
right_width: Pixels,
|
right_width: Pixels,
|
||||||
#[cfg(feature = "toast")]
|
|
||||||
toasts: Entity<Toasts>,
|
|
||||||
#[cfg(feature = "alert")]
|
|
||||||
alerts: Entity<Alerts>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayoutState {
|
impl LayoutState {
|
||||||
/// Creates the state: left sidebar visible, right sidebar hidden.
|
/// Creates the state: left sidebar visible, right sidebar hidden.
|
||||||
pub fn new(cx: &mut App) -> Entity<Self> {
|
pub fn new(cx: &mut App) -> Entity<Self> {
|
||||||
cx.new(|cx| Self {
|
cx.new(|_| Self {
|
||||||
left_visible: true,
|
left_visible: true,
|
||||||
right_visible: false,
|
right_visible: false,
|
||||||
left_width: px(DEFAULT_SIDEBAR_WIDTH),
|
left_width: px(DEFAULT_SIDEBAR_WIDTH),
|
||||||
right_width: px(DEFAULT_SIDEBAR_WIDTH),
|
right_width: px(DEFAULT_SIDEBAR_WIDTH),
|
||||||
#[cfg(feature = "toast")]
|
|
||||||
toasts: cx.new(|_| Toasts::default()),
|
|
||||||
#[cfg(feature = "alert")]
|
|
||||||
alerts: cx.new(|_| Alerts::default()),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,42 +91,6 @@ impl LayoutState {
|
|||||||
(self.right_width - delta).clamp(px(MIN_SIDEBAR_WIDTH), px(MAX_SIDEBAR_WIDTH));
|
(self.right_width - delta).clamp(px(MIN_SIDEBAR_WIDTH), px(MAX_SIDEBAR_WIDTH));
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Raises a toast in the shell's overlay. Returns its id.
|
|
||||||
#[cfg(feature = "toast")]
|
|
||||||
pub fn push_toast(&mut self, toast: Toast, cx: &mut Context<Self>) -> usize {
|
|
||||||
self.toasts.update(cx, |toasts, cx| toasts.push(toast, cx))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dismisses one toast by id.
|
|
||||||
#[cfg(feature = "toast")]
|
|
||||||
pub fn dismiss_toast(&mut self, id: usize, cx: &mut Context<Self>) {
|
|
||||||
self.toasts.update(cx, |toasts, cx| toasts.dismiss(id, cx));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dismisses the toast on screen.
|
|
||||||
#[cfg(feature = "toast")]
|
|
||||||
pub fn clear_toasts(&mut self, cx: &mut Context<Self>) {
|
|
||||||
self.toasts.update(cx, |toasts, cx| toasts.clear(cx));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pushes an alert onto the shell's stack. Returns its id.
|
|
||||||
#[cfg(feature = "alert")]
|
|
||||||
pub fn push_alert(&mut self, alert: Alert, cx: &mut Context<Self>) -> usize {
|
|
||||||
self.alerts.update(cx, |alerts, cx| alerts.push(alert, cx))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dismisses one alert by id, running its dismiss handler.
|
|
||||||
#[cfg(feature = "alert")]
|
|
||||||
pub fn dismiss_alert(&mut self, id: usize, cx: &mut Context<Self>) {
|
|
||||||
self.alerts.update(cx, |alerts, cx| alerts.dismiss(id, cx));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dismisses every alert, running each entry's `on_dismiss` handler.
|
|
||||||
#[cfg(feature = "alert")]
|
|
||||||
pub fn clear_alerts(&mut self, cx: &mut Context<Self>) {
|
|
||||||
self.alerts.update(cx, |alerts, cx| alerts.clear(cx));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Which sidebar a region or toggle belongs to.
|
/// Which sidebar a region or toggle belongs to.
|
||||||
@@ -289,16 +241,8 @@ impl RenderOnce for Shell {
|
|||||||
))
|
))
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wraps the layout in a window frame
|
// Wraps the layout in a window frame.
|
||||||
frame(layout, window, cx)
|
frame(layout, window, cx)
|
||||||
.when(cfg!(feature = "toast"), |this| {
|
|
||||||
let toasts = state.read(cx).toasts.clone();
|
|
||||||
this.child(ToastsView::new((id.clone(), "toasts"), toasts))
|
|
||||||
})
|
|
||||||
.when(cfg!(feature = "alert"), |this| {
|
|
||||||
let alerts = state.read(cx).alerts.clone();
|
|
||||||
this.child(AlertsView::new((id, "alerts"), alerts))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -235,88 +235,3 @@ fn the_right_seam_grows_the_right_sidebar_leftwards() {
|
|||||||
let region = bounds(&mut visual, "test-right").expect("right region missing");
|
let region = bounds(&mut visual, "test-right").expect("right region missing");
|
||||||
assert_eq!(region.size.width, grown);
|
assert_eq!(region.size.width, grown);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Toasts and alerts live on the shell's [`LayoutState`]; pushing one must
|
|
||||||
/// show up in the rendered shell with no observing or manual overlay.
|
|
||||||
#[cfg(feature = "toast")]
|
|
||||||
mod toasts {
|
|
||||||
use super::*;
|
|
||||||
use gpui_native_core::toast::{Toast, ToastAction};
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn a_pushed_toast_renders_in_the_shell() {
|
|
||||||
let mut cx = TestAppContext::single();
|
|
||||||
let (state, mut visual) = layout_window(&mut cx);
|
|
||||||
|
|
||||||
let id = cx.update(|cx| {
|
|
||||||
state.update(cx, |state, cx| {
|
|
||||||
state.push_toast(
|
|
||||||
Toast::new("Draft deleted").action(ToastAction::new("Undo", |_| {
|
|
||||||
// A real app restores the draft here.
|
|
||||||
})),
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
// No redraw, no observation: the shell repaints its own overlay.
|
|
||||||
cx.run_until_parked();
|
|
||||||
assert!(
|
|
||||||
bounds(&mut visual, "toast-pill").is_some(),
|
|
||||||
"the pushed toast must appear in the shell's overlay"
|
|
||||||
);
|
|
||||||
|
|
||||||
cx.update(|cx| {
|
|
||||||
state.update(cx, |state, cx| state.dismiss_toast(id, cx));
|
|
||||||
});
|
|
||||||
cx.run_until_parked();
|
|
||||||
// The fade-out keeps the pill mounted briefly.
|
|
||||||
assert!(
|
|
||||||
bounds(&mut visual, "toast-pill").is_some(),
|
|
||||||
"the dismissed toast must stay mounted through its fade-out"
|
|
||||||
);
|
|
||||||
cx.executor().advance_clock(Duration::from_secs(2));
|
|
||||||
cx.run_until_parked();
|
|
||||||
assert!(
|
|
||||||
bounds(&mut visual, "toast-pill").is_none(),
|
|
||||||
"the dismissed toast must leave the overlay once faded"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "alert")]
|
|
||||||
mod alerts {
|
|
||||||
use super::*;
|
|
||||||
use gpui_native_core::alert::{Alert, AlertSeverity};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn a_pushed_alert_renders_in_the_shell() {
|
|
||||||
let mut cx = TestAppContext::single();
|
|
||||||
let (state, mut visual) = layout_window(&mut cx);
|
|
||||||
|
|
||||||
cx.update(|cx| {
|
|
||||||
state.update(cx, |state, cx| {
|
|
||||||
state.push_alert(
|
|
||||||
Alert::new("shell-alert")
|
|
||||||
.title("Backed up")
|
|
||||||
.severity(AlertSeverity::Info),
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
cx.run_until_parked();
|
|
||||||
assert!(
|
|
||||||
bounds(&mut visual, "alert-entry").is_some(),
|
|
||||||
"the pushed alert must appear in the shell's overlay"
|
|
||||||
);
|
|
||||||
|
|
||||||
cx.update(|cx| {
|
|
||||||
state.update(cx, |state, cx| state.clear_alerts(cx));
|
|
||||||
});
|
|
||||||
cx.run_until_parked();
|
|
||||||
assert!(
|
|
||||||
bounds(&mut visual, "alert-entry").is_none(),
|
|
||||||
"cleared alerts must leave the overlay"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ Gaps surfaced by the signed comparison; ordered by signed's usage.
|
|||||||
|
|
||||||
- [x] `IconRole` + bundled Lucide fallback, Segoe Fluent codepoints (Windows),
|
- [x] `IconRole` + bundled Lucide fallback, Segoe Fluent codepoints (Windows),
|
||||||
freedesktop theme walker (Linux/Omarchy)
|
freedesktop theme walker (Linux/Omarchy)
|
||||||
|
- [x] `IconNamed` — app-defined icon sets: implement the trait for an enum of
|
||||||
|
asset paths, render it with `Icon::named`, and hand it to any icon
|
||||||
|
builder (`impl Into<Icon>`) alongside the built-in roles
|
||||||
|
|
||||||
### core (`crates/core`)
|
### core (`crates/core`)
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use gpui::{
|
|||||||
SharedString, Styled as _, UniformListScrollHandle, WeakEntity, Window, WindowBounds,
|
SharedString, Styled as _, UniformListScrollHandle, WeakEntity, Window, WindowBounds,
|
||||||
WindowOptions, actions, div, list, px, size, uniform_list,
|
WindowOptions, actions, div, list, px, size, uniform_list,
|
||||||
};
|
};
|
||||||
use gpui_native_core::alert::{Alert, AlertSeverity};
|
use gpui_native_core::alert::{Alert, AlertSeverity, Alerts, AlertsView};
|
||||||
use gpui_native_core::avatar::Avatar;
|
use gpui_native_core::avatar::Avatar;
|
||||||
use gpui_native_core::badge::{Badge, BadgeVariant};
|
use gpui_native_core::badge::{Badge, BadgeVariant};
|
||||||
use gpui_native_core::button::{Button, ButtonVariant};
|
use gpui_native_core::button::{Button, ButtonVariant};
|
||||||
@@ -36,7 +36,7 @@ use gpui_native_core::slider::Slider;
|
|||||||
use gpui_native_core::spinner::{Spinner, SpinnerVariant};
|
use gpui_native_core::spinner::{Spinner, SpinnerVariant};
|
||||||
use gpui_native_core::status_dot::{StatusDot, StatusDotVariant};
|
use gpui_native_core::status_dot::{StatusDot, StatusDotVariant};
|
||||||
use gpui_native_core::switch::Switch;
|
use gpui_native_core::switch::Switch;
|
||||||
use gpui_native_core::toast::{Toast, ToastAction};
|
use gpui_native_core::toast::{Toast, ToastAction, Toasts, ToastsView};
|
||||||
use gpui_native_core::toggle::Toggle;
|
use gpui_native_core::toggle::Toggle;
|
||||||
use gpui_native_core::tooltip::Tooltip;
|
use gpui_native_core::tooltip::Tooltip;
|
||||||
use gpui_native_data::list::{List, ListItem};
|
use gpui_native_data::list::{List, ListItem};
|
||||||
@@ -217,6 +217,8 @@ fn gallery_commands() -> Vec<Command> {
|
|||||||
struct Gallery {
|
struct Gallery {
|
||||||
layout_state: Entity<LayoutState>,
|
layout_state: Entity<LayoutState>,
|
||||||
dock_state: Entity<DockState>,
|
dock_state: Entity<DockState>,
|
||||||
|
alerts: Entity<Alerts>,
|
||||||
|
toasts: Entity<Toasts>,
|
||||||
palette: Option<Entity<CommandPaletteState>>,
|
palette: Option<Entity<CommandPaletteState>>,
|
||||||
palette_runs: Vec<SharedString>,
|
palette_runs: Vec<SharedString>,
|
||||||
dialog_open: bool,
|
dialog_open: bool,
|
||||||
@@ -393,6 +395,8 @@ impl Gallery {
|
|||||||
fn register_panels(
|
fn register_panels(
|
||||||
dock: &mut DockState,
|
dock: &mut DockState,
|
||||||
layout_state: Entity<LayoutState>,
|
layout_state: Entity<LayoutState>,
|
||||||
|
alerts: Entity<Alerts>,
|
||||||
|
toasts: Entity<Toasts>,
|
||||||
palette: Entity<CommandPaletteState>,
|
palette: Entity<CommandPaletteState>,
|
||||||
gallery: WeakEntity<Self>,
|
gallery: WeakEntity<Self>,
|
||||||
cx: &mut Context<DockState>,
|
cx: &mut Context<DockState>,
|
||||||
@@ -509,20 +513,20 @@ impl Gallery {
|
|||||||
.into()
|
.into()
|
||||||
}));
|
}));
|
||||||
dock.register_panel(PanelDescriptor::new("alert", "Alert", {
|
dock.register_panel(PanelDescriptor::new("alert", "Alert", {
|
||||||
let layout_state = layout_state.clone();
|
let alerts = alerts.clone();
|
||||||
move |_, cx| {
|
move |_, cx| {
|
||||||
cx.new(|_| AlertDemo {
|
cx.new(|_| AlertDemo {
|
||||||
layout_state: layout_state.clone(),
|
alerts: alerts.clone(),
|
||||||
dismissed: false,
|
dismissed: false,
|
||||||
})
|
})
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
dock.register_panel(PanelDescriptor::new("toast", "Toast", {
|
dock.register_panel(PanelDescriptor::new("toast", "Toast", {
|
||||||
let layout_state = layout_state.clone();
|
let toasts = toasts.clone();
|
||||||
move |_, cx| {
|
move |_, cx| {
|
||||||
cx.new(|_| ToastDemo {
|
cx.new(|_| ToastDemo {
|
||||||
layout_state: layout_state.clone(),
|
toasts: toasts.clone(),
|
||||||
})
|
})
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
@@ -645,6 +649,8 @@ impl Render for Gallery {
|
|||||||
.child(dialog)
|
.child(dialog)
|
||||||
.child(alert_dialog)
|
.child(alert_dialog)
|
||||||
.when_some(self.palette.clone(), |root, palette| root.child(palette))
|
.when_some(self.palette.clone(), |root, palette| root.child(palette))
|
||||||
|
.child(AlertsView::new("gallery-alerts", self.alerts.clone()))
|
||||||
|
.child(ToastsView::new("gallery-toasts", self.toasts.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2281,16 +2287,16 @@ impl Render for TabsDemo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct AlertDemo {
|
struct AlertDemo {
|
||||||
layout_state: Entity<LayoutState>,
|
alerts: Entity<Alerts>,
|
||||||
dismissed: bool,
|
dismissed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for AlertDemo {
|
impl Render for AlertDemo {
|
||||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
let view = cx.entity();
|
let view = cx.entity();
|
||||||
let alerts_info = self.layout_state.clone();
|
let alerts_info = self.alerts.clone();
|
||||||
let alerts_warning = self.layout_state.clone();
|
let alerts_warning = self.alerts.clone();
|
||||||
let alerts_clear = self.layout_state.clone();
|
let alerts_clear = self.alerts.clone();
|
||||||
let colors = cx.theme().colors;
|
let colors = cx.theme().colors;
|
||||||
|
|
||||||
ScrollArea::new("alert-demo").size_full().p_4().child(
|
ScrollArea::new("alert-demo").size_full().p_4().child(
|
||||||
@@ -2333,8 +2339,8 @@ impl Render for AlertDemo {
|
|||||||
.label("Push info")
|
.label("Push info")
|
||||||
.variant(ButtonVariant::Secondary)
|
.variant(ButtonVariant::Secondary)
|
||||||
.on_click(move |_, _, cx| {
|
.on_click(move |_, _, cx| {
|
||||||
alerts_info.update(cx, |state, cx| {
|
alerts_info.update(cx, |alerts, cx| {
|
||||||
state.push_alert(
|
alerts.push(
|
||||||
Alert::new("alert-demo-pushed-info")
|
Alert::new("alert-demo-pushed-info")
|
||||||
.title("Backed up")
|
.title("Backed up")
|
||||||
.message("Everything is in sync.")
|
.message("Everything is in sync.")
|
||||||
@@ -2349,8 +2355,8 @@ impl Render for AlertDemo {
|
|||||||
.label("Push warning")
|
.label("Push warning")
|
||||||
.variant(ButtonVariant::Secondary)
|
.variant(ButtonVariant::Secondary)
|
||||||
.on_click(move |_, _, cx| {
|
.on_click(move |_, _, cx| {
|
||||||
alerts_warning.update(cx, |state, cx| {
|
alerts_warning.update(cx, |alerts, cx| {
|
||||||
state.push_alert(
|
alerts.push(
|
||||||
Alert::new("alert-demo-pushed-warning")
|
Alert::new("alert-demo-pushed-warning")
|
||||||
.title("Storage almost full")
|
.title("Storage almost full")
|
||||||
.message("1.2 GB remaining on this device.")
|
.message("1.2 GB remaining on this device.")
|
||||||
@@ -2365,7 +2371,7 @@ impl Render for AlertDemo {
|
|||||||
.label("Clear all")
|
.label("Clear all")
|
||||||
.variant(ButtonVariant::Secondary)
|
.variant(ButtonVariant::Secondary)
|
||||||
.on_click(move |_, _, cx| {
|
.on_click(move |_, _, cx| {
|
||||||
alerts_clear.update(cx, |state, cx| state.clear_alerts(cx));
|
alerts_clear.update(cx, |alerts, cx| alerts.clear(cx));
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
@@ -2374,14 +2380,14 @@ impl Render for AlertDemo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ToastDemo {
|
struct ToastDemo {
|
||||||
layout_state: Entity<LayoutState>,
|
toasts: Entity<Toasts>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for ToastDemo {
|
impl Render for ToastDemo {
|
||||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
let toasts_undo = self.layout_state.clone();
|
let toasts_undo = self.toasts.clone();
|
||||||
let toasts_saved = self.layout_state.clone();
|
let toasts_saved = self.toasts.clone();
|
||||||
let toasts_connection = self.layout_state.clone();
|
let toasts_connection = self.toasts.clone();
|
||||||
let colors = cx.theme().colors;
|
let colors = cx.theme().colors;
|
||||||
|
|
||||||
ScrollArea::new("toast-demo")
|
ScrollArea::new("toast-demo")
|
||||||
@@ -2398,8 +2404,8 @@ impl Render for ToastDemo {
|
|||||||
.label("Deleted (with undo)")
|
.label("Deleted (with undo)")
|
||||||
.variant(ButtonVariant::Secondary)
|
.variant(ButtonVariant::Secondary)
|
||||||
.on_click(move |_, _, cx| {
|
.on_click(move |_, _, cx| {
|
||||||
toasts_undo.update(cx, |state, cx| {
|
toasts_undo.update(cx, |toasts, cx| {
|
||||||
state.push_toast(
|
toasts.push(
|
||||||
Toast::new("Draft deleted").action(ToastAction::new(
|
Toast::new("Draft deleted").action(ToastAction::new(
|
||||||
"Undo",
|
"Undo",
|
||||||
|cx| {
|
|cx| {
|
||||||
@@ -2417,8 +2423,8 @@ impl Render for ToastDemo {
|
|||||||
.label("Saved (with icon)")
|
.label("Saved (with icon)")
|
||||||
.variant(ButtonVariant::Secondary)
|
.variant(ButtonVariant::Secondary)
|
||||||
.on_click(move |_, _, cx| {
|
.on_click(move |_, _, cx| {
|
||||||
toasts_saved.update(cx, |state, cx| {
|
toasts_saved.update(cx, |toasts, cx| {
|
||||||
state.push_toast(
|
toasts.push(
|
||||||
Toast::new("Workspace saved").icon(IconRole::Check),
|
Toast::new("Workspace saved").icon(IconRole::Check),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
@@ -2430,8 +2436,8 @@ impl Render for ToastDemo {
|
|||||||
.label("Connection lost (sticky)")
|
.label("Connection lost (sticky)")
|
||||||
.variant(ButtonVariant::Secondary)
|
.variant(ButtonVariant::Secondary)
|
||||||
.on_click(move |_, _, cx| {
|
.on_click(move |_, _, cx| {
|
||||||
toasts_connection.update(cx, |state, cx| {
|
toasts_connection.update(cx, |toasts, cx| {
|
||||||
state.push_toast(
|
toasts.push(
|
||||||
Toast::new("Connection lost")
|
Toast::new("Connection lost")
|
||||||
.icon(IconRole::Warning)
|
.icon(IconRole::Warning)
|
||||||
.duration(Duration::ZERO),
|
.duration(Duration::ZERO),
|
||||||
@@ -4019,14 +4025,24 @@ fn main() {
|
|||||||
cx.refresh_windows();
|
cx.refresh_windows();
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
let gallery = cx.new(|_| Gallery {
|
let toasts = cx.new(|_| Toasts::default());
|
||||||
layout_state: layout_state.clone(),
|
let alerts = cx.new(|_| Alerts::default());
|
||||||
dock_state: dock_state.clone(),
|
let gallery = cx.new(|cx| {
|
||||||
palette: None,
|
// Toast pushes and dismissals repaint the stack.
|
||||||
palette_runs: Vec::new(),
|
cx.observe(&toasts, |_, _, cx| cx.notify()).detach();
|
||||||
dialog_open: false,
|
// Alert pushes and dismissals repaint it.
|
||||||
alert_open: false,
|
cx.observe(&alerts, |_, _, cx| cx.notify()).detach();
|
||||||
sidebar_transparent: true,
|
Gallery {
|
||||||
|
layout_state: layout_state.clone(),
|
||||||
|
dock_state: dock_state.clone(),
|
||||||
|
alerts: alerts.clone(),
|
||||||
|
toasts: toasts.clone(),
|
||||||
|
palette: None,
|
||||||
|
palette_runs: Vec::new(),
|
||||||
|
dialog_open: false,
|
||||||
|
alert_open: false,
|
||||||
|
sidebar_transparent: true,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
let palette = cx.new(|cx| {
|
let palette = cx.new(|cx| {
|
||||||
let matcher = |query: &str, commands: &[Command]| {
|
let matcher = |query: &str, commands: &[Command]| {
|
||||||
@@ -4060,6 +4076,8 @@ fn main() {
|
|||||||
Gallery::register_panels(
|
Gallery::register_panels(
|
||||||
dock,
|
dock,
|
||||||
layout_state.clone(),
|
layout_state.clone(),
|
||||||
|
alerts.clone(),
|
||||||
|
toasts.clone(),
|
||||||
palette.clone(),
|
palette.clone(),
|
||||||
gallery.downgrade(),
|
gallery.downgrade(),
|
||||||
cx,
|
cx,
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ pub use gpui_native_data as data;
|
|||||||
#[cfg(feature = "layout")]
|
#[cfg(feature = "layout")]
|
||||||
pub use gpui_native_layout as layout;
|
pub use gpui_native_layout as layout;
|
||||||
|
|
||||||
|
#[cfg(feature = "editor")]
|
||||||
|
pub use gpui_native_editor as editor;
|
||||||
|
|
||||||
use gpui::App;
|
use gpui::App;
|
||||||
|
|
||||||
/// Initializes the framework.
|
/// Initializes the framework.
|
||||||
|
|||||||
Reference in New Issue
Block a user