Only build widgets for visible items

This commit is contained in:
Jeremy Soller 2025-07-10 10:52:57 -06:00
parent 10f234bf79
commit 5e4d4523f4
No known key found for this signature in database
GPG key ID: 670FDFB5428E05CA
2 changed files with 311 additions and 271 deletions

View file

@ -5363,7 +5363,7 @@ impl Application for App {
fn system_theme_update(
&mut self,
_keys: &[&'static str],
new_theme: &cosmic::cosmic_theme::Theme,
_new_theme: &cosmic::cosmic_theme::Theme,
) -> Task<Self::Message> {
self.update(Message::SystemThemeModeChange)
}

View file

@ -4738,6 +4738,16 @@ impl Tab {
rows_m1 + 1
};
//TODO: move to function
let visible_rect = {
let point = match self.scroll_opt {
Some(offset) => Point::new(0.0, offset.y),
None => Point::new(0.0, 0.0),
};
let size = self.size_opt.get().unwrap_or_else(|| Size::new(0.0, 0.0));
Rectangle::new(point, size)
};
let mut grid = widget::grid()
.column_spacing(column_spacing)
.row_spacing(grid_spacing)
@ -4765,14 +4775,22 @@ impl Tab {
continue;
}
item.pos_opt.set(Some((row, col)));
item.rect_opt.set(Some(Rectangle::new(
let item_rect = Rectangle::new(
Point::new(
(col * (item_width + column_spacing as usize) + space_m as usize) as f32,
(row * (item_height + grid_spacing as usize)) as f32,
),
Size::new(item_width as f32, item_height as f32),
)));
);
item.rect_opt.set(Some(item_rect));
//TODO: error if the row or col is already set?
while grid_elements.len() <= row {
grid_elements.push(Vec::new());
}
// Only build elements if visible (for performance)
if item_rect.intersects(&visible_rect) {
//TODO: one focus group per grid item (needs custom widget)
let buttons: Vec<Element<Message>> = vec![
widget::button::custom(
@ -4846,12 +4864,17 @@ impl Tab {
.on_middle_press(move |_| Message::MiddleClick(i))
.on_enter(move || Message::HighlightActivate(i))
.on_exit(move || Message::HighlightDeactivate(i));
//TODO: error if the row or col is already set?
while grid_elements.len() <= row {
grid_elements.push(Vec::new());
grid_elements[row].push(Element::from(mouse_area));
} else {
// Add a spacer if the row is empty, so scroll works
if grid_elements[row].is_empty() {
grid_elements[row].push(Element::from(
widget::column()
.width(Length::Fill)
.height(Length::Fixed(item_height as f32)),
));
}
}
grid_elements[row].push(mouse_area);
count += 1;
if matches!(self.mode, Mode::Desktop) {
@ -5028,9 +5051,18 @@ impl Tab {
let rule_padding = theme::active().cosmic().corner_radii.radius_xs[0] as u16;
let items = self.column_sort();
//TODO: move to function
let visible_rect = {
let point = match self.scroll_opt {
Some(offset) => Point::new(0.0, offset.y),
None => Point::new(0.0, 0.0),
};
let size = self.size_opt.get().unwrap_or_else(|| Size::new(0.0, 0.0));
Rectangle::new(point, size)
};
let mut drag_items = Vec::new();
if let Some(items) = items {
if let Some(items) = self.column_sort() {
let mut count = 0;
let mut hidden = 0;
for (i, item) in items {
@ -5041,10 +5073,11 @@ impl Tab {
continue;
}
item.pos_opt.set(Some((count, 0)));
item.rect_opt.set(Some(Rectangle::new(
let item_rect = Rectangle::new(
Point::new(space_m as f32, y),
Size::new(size.width - (2 * space_m) as f32, row_height as f32),
)));
);
item.rect_opt.set(Some(item_rect));
if count > 0 {
children.push(
@ -5055,6 +5088,8 @@ impl Tab {
y += 1.0;
}
// Only build elements if visible (for performance)
let button_row = if item_rect.intersects(&visible_rect) {
let modified_text = match &item.metadata {
ItemMetadata::Path { metadata, .. } => match metadata.modified() {
Ok(time) => self.format_time(time).to_string(),
@ -5247,7 +5282,10 @@ impl Tab {
widget::column::with_children(vec![
widget::text::body(item.display_name.clone()).into(),
//TODO: translate?
widget::text::body(format!("{} - {}", modified_text, size_text))
widget::text::body(format!(
"{} - {}",
modified_text, size_text
))
.into(),
])
.into(),
@ -5312,6 +5350,14 @@ impl Tab {
}
}
button_row
} else {
widget::column()
.width(Length::Fill)
.height(Length::Fixed(row_height as f32))
.into()
};
count += 1;
y += row_height as f32;
children.push(button_row);
@ -5557,12 +5603,6 @@ impl Tab {
Rectangle::new(point, size)
};
//TODO: HACK to ensure positions are up to date since subscription runs before view
match self.config.view {
View::Grid => _ = self.grid_view(),
View::List => _ = self.list_view(),
};
for item in items.iter() {
if item.thumbnail_opt.is_some() {
// Skip items that already have a mime type and thumbnail