forked from reya/gpui-native-kit
feat: replace sidebar row icon with prefix and suffix slots
This commit is contained in:
+64
-58
@@ -10,7 +10,6 @@ use gpui::{
|
||||
Styled as _, Window, div,
|
||||
};
|
||||
use gpui_native_core::scroll_area::ScrollArea;
|
||||
use gpui_native_icons::Icon;
|
||||
use gpui_native_theme::ActiveTheme as _;
|
||||
use gpui_native_utils::{nav_key, paint_selectable_row, selectable_label_color};
|
||||
|
||||
@@ -19,15 +18,15 @@ type ChangeHandler = Rc<dyn Fn(Option<usize>, &mut Window, &mut App)>;
|
||||
type EntriesFn = Box<dyn Fn(&mut Window, &mut App) -> Vec<SidebarEntry>>;
|
||||
|
||||
/// One entry of a [`Sidebar`], a section header or a selectable row.
|
||||
#[derive(Clone)]
|
||||
pub enum SidebarEntry {
|
||||
/// A non-selectable section label.
|
||||
Header { label: SharedString },
|
||||
/// A selectable row.
|
||||
Row {
|
||||
label: SharedString,
|
||||
icon: Option<Icon>,
|
||||
detail: Option<SharedString>,
|
||||
prefix: Option<AnyElement>,
|
||||
suffix: Option<AnyElement>,
|
||||
disabled: bool,
|
||||
},
|
||||
}
|
||||
@@ -50,23 +49,13 @@ impl SidebarEntry {
|
||||
{
|
||||
Self::Row {
|
||||
label: label.into(),
|
||||
icon: None,
|
||||
detail: None,
|
||||
prefix: None,
|
||||
suffix: None,
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Shows an icon leading the label, a role or any custom [`IconNamed`].
|
||||
pub fn icon<I>(mut self, icon: I) -> Self
|
||||
where
|
||||
I: Into<Icon>,
|
||||
{
|
||||
if let Self::Row { icon: slot, .. } = &mut self {
|
||||
*slot = Some(icon.into());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Shows muted trailing text.
|
||||
pub fn detail<D>(mut self, detail: D) -> Self
|
||||
where
|
||||
@@ -81,6 +70,28 @@ impl SidebarEntry {
|
||||
self
|
||||
}
|
||||
|
||||
/// Shows an element leading the row.
|
||||
pub fn prefix<E>(mut self, prefix: E) -> Self
|
||||
where
|
||||
E: IntoElement,
|
||||
{
|
||||
if let Self::Row { prefix: slot, .. } = &mut self {
|
||||
*slot = Some(prefix.into_any_element());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Shows an element trailing the row.
|
||||
pub fn suffix<E>(mut self, suffix: E) -> Self
|
||||
where
|
||||
E: IntoElement,
|
||||
{
|
||||
if let Self::Row { suffix: slot, .. } = &mut self {
|
||||
*slot = Some(suffix.into_any_element());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Renders the row dimmed; clicks and keyboard skip it.
|
||||
pub fn disabled(mut self, disabled: bool) -> Self {
|
||||
if let Self::Row {
|
||||
@@ -207,7 +218,7 @@ impl RenderOnce for Sidebar {
|
||||
|
||||
let mut row_position = 0usize;
|
||||
let children: Vec<AnyElement> = entries
|
||||
.iter()
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(index, entry)| {
|
||||
let is_row = matches!(entry, SidebarEntry::Row { .. });
|
||||
@@ -274,7 +285,7 @@ impl RenderOnce for Sidebar {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn render_entry(
|
||||
cx: &App,
|
||||
entry: &SidebarEntry,
|
||||
entry: SidebarEntry,
|
||||
index: usize,
|
||||
position_in_set: usize,
|
||||
size_of_set: usize,
|
||||
@@ -293,17 +304,18 @@ fn render_entry(
|
||||
.text_sm()
|
||||
.font_weight(gpui::FontWeight::MEDIUM)
|
||||
.text_color(theme.colors.muted_foreground)
|
||||
.child(label.clone())
|
||||
.child(label)
|
||||
.into_any_element(),
|
||||
SidebarEntry::Row {
|
||||
label,
|
||||
icon: row_icon,
|
||||
detail,
|
||||
prefix,
|
||||
suffix,
|
||||
disabled: row_disabled,
|
||||
} => {
|
||||
let label_color = selectable_label_color(cx, selected, *row_disabled);
|
||||
let label_color = selectable_label_color(cx, selected, row_disabled);
|
||||
|
||||
let mut row = div()
|
||||
div()
|
||||
.id(("sidebar-row", index))
|
||||
.flex()
|
||||
.items_center()
|
||||
@@ -316,51 +328,45 @@ fn render_entry(
|
||||
.aria_position_in_set(position_in_set)
|
||||
.aria_size_of_set(size_of_set)
|
||||
.map(|row| {
|
||||
if material && !*row_disabled {
|
||||
if material && !row_disabled {
|
||||
if selected {
|
||||
row.bg(theme.material.selection)
|
||||
} else {
|
||||
row.hover(|row| row.bg(theme.material.hover))
|
||||
}
|
||||
} else {
|
||||
paint_selectable_row(cx, row, selected, *row_disabled)
|
||||
paint_selectable_row(cx, row, selected, row_disabled)
|
||||
}
|
||||
});
|
||||
|
||||
if !*row_disabled {
|
||||
row = row.cursor_pointer().on_click({
|
||||
let on_change = on_change.clone();
|
||||
let focus = focus.clone();
|
||||
move |_, window, cx| {
|
||||
window.focus(&focus, cx);
|
||||
if let Some(on_change) = &on_change {
|
||||
on_change(Some(index), window, cx);
|
||||
})
|
||||
.when(!row_disabled, |row| {
|
||||
row.cursor_pointer().on_click({
|
||||
let on_change = on_change.clone();
|
||||
let focus = focus.clone();
|
||||
move |_, window, cx| {
|
||||
window.focus(&focus, cx);
|
||||
if let Some(on_change) = &on_change {
|
||||
on_change(Some(index), window, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
row = row.when_some(row_icon.clone(), |row, icon| {
|
||||
row.child(icon.size(theme.sizes.medium.icon_size).color(label_color))
|
||||
});
|
||||
|
||||
row = row.child(div().flex_1().text_color(label_color).child(label.clone()));
|
||||
|
||||
if let Some(detail) = detail {
|
||||
row = row.child(
|
||||
div()
|
||||
.flex_none()
|
||||
.text_sm()
|
||||
.text_color(if selected {
|
||||
theme.colors.primary_foreground
|
||||
} else {
|
||||
theme.colors.muted_foreground
|
||||
})
|
||||
.child(detail.clone()),
|
||||
);
|
||||
}
|
||||
|
||||
row.into_any_element()
|
||||
})
|
||||
})
|
||||
.when_some(prefix, |row, prefix| row.child(prefix))
|
||||
.child(div().flex_1().text_color(label_color).child(label))
|
||||
.when_some(detail, |row, detail| {
|
||||
row.child(
|
||||
div()
|
||||
.flex_none()
|
||||
.text_sm()
|
||||
.text_color(if selected {
|
||||
theme.colors.primary_foreground
|
||||
} else {
|
||||
theme.colors.muted_foreground
|
||||
})
|
||||
.child(detail),
|
||||
)
|
||||
})
|
||||
.when_some(suffix, |row, suffix| row.child(suffix))
|
||||
.into_any_element()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user