chore: reduce component overhead (#3)

Reviewed-on: reya/gpui-native-kit#3
This commit is contained in:
2026-11-09 10:09:56 +00:00
parent 6815585528
commit 1c091db699
44 changed files with 1376 additions and 1241 deletions
+24 -8
View File
@@ -195,19 +195,33 @@ impl RenderOnce for Sidebar {
.clone()
.unwrap_or_else(|| SharedString::from(self.id.to_string()));
let id_text = self.id.to_string();
let scroll_area_id = (self.id.clone(), "entries");
// Entry paint reads the theme through this borrow.
let row_count = entries
.iter()
.filter(|entry| matches!(entry, SidebarEntry::Row { .. }))
.count();
let mut row_position = 0usize;
let children: Vec<AnyElement> = entries
.iter()
.enumerate()
.map(|(index, entry)| {
let is_row = matches!(entry, SidebarEntry::Row { .. });
if is_row {
row_position += 1;
}
let (position_in_set, size_of_set) = if is_row {
(row_position, row_count)
} else {
(0, 0)
};
render_entry(
cx,
entry,
index,
entries.len(),
position_in_set,
size_of_set,
self.selected == Some(index),
material,
&self.on_change,
@@ -216,9 +230,10 @@ impl RenderOnce for Sidebar {
})
.collect();
let id = self.id;
div()
.id(self.id)
.debug_selector(move || format!("sidebar-{id_text}"))
.id(id.clone())
.debug_selector(move || format!("sidebar-{id}"))
.size_full()
.flex()
.flex_col()
@@ -258,7 +273,8 @@ fn render_entry(
cx: &App,
entry: &SidebarEntry,
index: usize,
count: usize,
position_in_set: usize,
size_of_set: usize,
selected: bool,
material: bool,
on_change: &Option<ChangeHandler>,
@@ -294,8 +310,8 @@ fn render_entry(
.rounded(theme.radius.sm)
.role(Role::ListItem)
.aria_selected(selected)
.aria_position_in_set(index + 1)
.aria_size_of_set(count)
.aria_position_in_set(position_in_set)
.aria_size_of_set(size_of_set)
.map(|row| {
if material && !*row_disabled {
if selected {
+16 -11
View File
@@ -32,6 +32,8 @@ pub enum TabsVariant {
/// One tab of a [`Tabs`] strip.
pub struct Tab {
/// The stable identity for element state; must survive reordering.
key: ElementId,
label: SharedString,
icon: Option<IconRole>,
lead: Option<AnyElement>,
@@ -42,12 +44,14 @@ pub struct Tab {
}
impl Tab {
/// Creates a tab with this label.
pub fn new<L>(label: L) -> Self
/// Creates a tab shown as `label`, identified by `key`.
pub fn new<K, L>(key: K, label: L) -> Self
where
K: Into<ElementId>,
L: Into<SharedString>,
{
Self {
key: key.into(),
label: label.into(),
icon: None,
lead: None,
@@ -210,7 +214,6 @@ impl RenderOnce for Tabs {
cx,
tab,
index,
self.selected == index,
self.selected,
self.variant,
&self.on_change,
@@ -268,7 +271,6 @@ fn render_tab(
cx: &App,
tab: Tab,
index: usize,
selected: bool,
selected_index: usize,
variant: TabsVariant,
on_change: &Option<ChangeHandler>,
@@ -276,6 +278,7 @@ fn render_tab(
focus: &gpui::FocusHandle,
) -> AnyElement {
let mut tab = tab;
let selected = index == selected_index;
let theme = cx.theme();
let metrics = theme.sizes.resolve(Size::default());
@@ -301,10 +304,11 @@ fn render_tab(
};
let mut strip_tab = div()
.id(("tab", index))
.id((tab.key.clone(), "tab"))
.debug_selector({
let id = format!("{tabs_id}-tab-{}", tab.label);
move || id
let tabs_id = tabs_id.clone();
let label = tab.label.clone();
move || format!("{tabs_id}-tab-{}", label)
})
.flex()
.items_center()
@@ -364,7 +368,7 @@ fn render_tab(
if tab.busy && !tab.disabled {
strip_tab = strip_tab.child(
Spinner::new(("tab-busy", index))
Spinner::new((tab.key.clone(), "tab-busy"))
.small()
.accessibility_label("Loading"),
);
@@ -401,14 +405,15 @@ fn render_tab_close(
index: usize,
on_close: &Option<CloseHandler>,
) -> AnyElement {
Button::new(("tab-close", index))
Button::new((tab.key.clone(), "tab-close"))
.icon(IconRole::Close)
.variant(ButtonVariant::Ghost)
.small()
.tab_stop(false)
.debug_selector({
let id = format!("{tabs_id}-close-{}", tab.label);
move || id.clone()
let tabs_id = tabs_id.clone();
let label = tab.label.clone();
move || format!("{tabs_id}-close-{}", label)
})
.tooltip(Tooltip::text("Close"))
.accessibility_label(SharedString::from(format!("Close {}", tab.label)))
+42 -6
View File
@@ -166,14 +166,49 @@ impl RenderOnce for Tree {
.clone()
.unwrap_or_else(|| SharedString::from(self.id.to_string()));
let sibling_sets: Vec<(usize, usize)> = {
let mut sets = vec![(0usize, 0usize); items.len()];
let mut start = 0usize;
while start < items.len() {
let depth = items[start].depth;
let mut end = start + 1;
while end < items.len() && items[end].depth > depth {
end += 1;
}
let mut position = 0usize;
for index in start..end {
if items[index].depth == depth {
position += 1;
sets[index] = (position, 0);
}
}
for index in start..end {
if items[index].depth == depth {
sets[index].1 = position;
}
}
start = end;
}
sets
};
let rows: Vec<AnyElement> = items
.iter()
.enumerate()
.map(|(index, _item)| {
let (position_in_set, size_of_set) = sibling_sets[index];
render_row(
cx,
&items,
&items[index],
index,
position_in_set,
size_of_set,
self.selected == Some(index),
&self.on_change,
&self.on_disclosure,
@@ -228,21 +263,21 @@ impl RenderOnce for Tree {
}
}
#[allow(clippy::too_many_arguments)]
fn render_row(
cx: &App,
items: &[TreeItem],
item: &TreeItem,
index: usize,
position_in_set: usize,
size_of_set: usize,
selected: bool,
on_change: &Option<ChangeHandler>,
on_disclosure: &Option<DisclosureHandler>,
focus: &FocusHandle,
) -> AnyElement {
let theme = cx.theme();
let item = &items[index];
let count = items.len();
let label_color = selectable_label_color(cx, selected, item.disabled);
let disabled = item.disabled;
// One indent step per depth level, on the theme's horizontal padding.
let indent = theme.sizes.medium.padding_x * (item.depth as f32 + 1.0);
@@ -258,7 +293,8 @@ fn render_row(
.role(Role::TreeItem)
.aria_selected(selected)
.aria_level(item.depth + 1)
.aria_size_of_set(count)
.aria_position_in_set(position_in_set)
.aria_size_of_set(size_of_set)
.when(item.expandable, |row| row.aria_expanded(item.expanded))
.map(|row| paint_selectable_row(cx, row, selected, disabled))
.when(!disabled, |row| {
+4 -4
View File
@@ -30,10 +30,10 @@ impl Render for Harness {
})
.tabs(|_, _| {
vec![
Tab::new("One"),
Tab::new("Two"),
Tab::new("Off").disabled(true),
Tab::new("End"),
Tab::new("one", "One"),
Tab::new("two", "Two"),
Tab::new("off", "Off").disabled(true),
Tab::new("end", "End"),
]
}),
)