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( fn system_theme_update(
&mut self, &mut self,
_keys: &[&'static str], _keys: &[&'static str],
new_theme: &cosmic::cosmic_theme::Theme, _new_theme: &cosmic::cosmic_theme::Theme,
) -> Task<Self::Message> { ) -> Task<Self::Message> {
self.update(Message::SystemThemeModeChange) self.update(Message::SystemThemeModeChange)
} }

View file

@ -4738,6 +4738,16 @@ impl Tab {
rows_m1 + 1 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() let mut grid = widget::grid()
.column_spacing(column_spacing) .column_spacing(column_spacing)
.row_spacing(grid_spacing) .row_spacing(grid_spacing)
@ -4765,14 +4775,22 @@ impl Tab {
continue; continue;
} }
item.pos_opt.set(Some((row, col))); item.pos_opt.set(Some((row, col)));
item.rect_opt.set(Some(Rectangle::new( let item_rect = Rectangle::new(
Point::new( Point::new(
(col * (item_width + column_spacing as usize) + space_m as usize) as f32, (col * (item_width + column_spacing as usize) + space_m as usize) as f32,
(row * (item_height + grid_spacing as usize)) as f32, (row * (item_height + grid_spacing as usize)) as f32,
), ),
Size::new(item_width as f32, item_height 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) //TODO: one focus group per grid item (needs custom widget)
let buttons: Vec<Element<Message>> = vec![ let buttons: Vec<Element<Message>> = vec![
widget::button::custom( widget::button::custom(
@ -4846,12 +4864,17 @@ impl Tab {
.on_middle_press(move |_| Message::MiddleClick(i)) .on_middle_press(move |_| Message::MiddleClick(i))
.on_enter(move || Message::HighlightActivate(i)) .on_enter(move || Message::HighlightActivate(i))
.on_exit(move || Message::HighlightDeactivate(i)); .on_exit(move || Message::HighlightDeactivate(i));
grid_elements[row].push(Element::from(mouse_area));
//TODO: error if the row or col is already set? } else {
while grid_elements.len() <= row { // Add a spacer if the row is empty, so scroll works
grid_elements.push(Vec::new()); 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; count += 1;
if matches!(self.mode, Mode::Desktop) { 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 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(); let mut drag_items = Vec::new();
if let Some(items) = items { if let Some(items) = self.column_sort() {
let mut count = 0; let mut count = 0;
let mut hidden = 0; let mut hidden = 0;
for (i, item) in items { for (i, item) in items {
@ -5041,10 +5073,11 @@ impl Tab {
continue; continue;
} }
item.pos_opt.set(Some((count, 0))); 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), Point::new(space_m as f32, y),
Size::new(size.width - (2 * space_m) as f32, row_height as f32), Size::new(size.width - (2 * space_m) as f32, row_height as f32),
))); );
item.rect_opt.set(Some(item_rect));
if count > 0 { if count > 0 {
children.push( children.push(
@ -5055,6 +5088,8 @@ impl Tab {
y += 1.0; 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 { let modified_text = match &item.metadata {
ItemMetadata::Path { metadata, .. } => match metadata.modified() { ItemMetadata::Path { metadata, .. } => match metadata.modified() {
Ok(time) => self.format_time(time).to_string(), Ok(time) => self.format_time(time).to_string(),
@ -5247,7 +5282,10 @@ impl Tab {
widget::column::with_children(vec![ widget::column::with_children(vec![
widget::text::body(item.display_name.clone()).into(), widget::text::body(item.display_name.clone()).into(),
//TODO: translate? //TODO: translate?
widget::text::body(format!("{} - {}", modified_text, size_text)) widget::text::body(format!(
"{} - {}",
modified_text, size_text
))
.into(), .into(),
]) ])
.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; count += 1;
y += row_height as f32; y += row_height as f32;
children.push(button_row); children.push(button_row);
@ -5557,12 +5603,6 @@ impl Tab {
Rectangle::new(point, size) 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() { for item in items.iter() {
if item.thumbnail_opt.is_some() { if item.thumbnail_opt.is_some() {
// Skip items that already have a mime type and thumbnail // Skip items that already have a mime type and thumbnail