Move the people tray's ticks in place instead of rebuilding it per press
Benchmarks / CPU and I/O (per commit) (push) Successful in 1m52s
Benchmarks / Frame budget (on demand) (push) Skipped
Build and test / Desktop (Linux) (push) Successful in 45m4s
Build and test / Layer separation (push) Successful in 41s
Traceability / Requirement traces (push) Successful in 29s
🐳 Android image / Build and push (push) Successful in 1s
Build and test / android-image (push) Successful in 2s
🐳 Windows image / Build and push (push) Successful in 1s
Build and test / windows-image (push) Successful in 1s
Build and test / Android (aarch64) (push) Successful in 29m25s
Build and test / Windows (x86_64, cross) (push) Successful in 34m4s
Build and test / Publish the release (push) Skipped
Benchmarks / CPU and I/O (per commit) (push) Successful in 1m52s
Benchmarks / Frame budget (on demand) (push) Skipped
Build and test / Desktop (Linux) (push) Successful in 45m4s
Build and test / Layer separation (push) Successful in 41s
Traceability / Requirement traces (push) Successful in 29s
🐳 Android image / Build and push (push) Successful in 1s
Build and test / android-image (push) Successful in 2s
🐳 Windows image / Build and push (push) Successful in 1s
Build and test / windows-image (push) Successful in 1s
Build and test / Android (aarch64) (push) Successful in 29m25s
Build and test / Windows (x86_64, cross) (push) Successful in 34m4s
Build and test / Publish the release (push) Skipped
Filtering the grid by a face crawled on the reference library. The SQL is not it — the person predicate counts in ~20 ms, the eyes-open term in ~60 — but every press on the tray ran `push_people_chips`, which read the whole people table (26,362 rows, nearly all empty groups a regrouping pass left behind) and then called `push_people_roster`, which read it again and replaced the roster model. The roster is every person holding a face, 1,581 chips, in a row Slint does not virtualise: a new model tore down and re-created all of them and laid the row out again, to move one tick. A press now walks the roster model and sets `picked` on the rows whose tick changed; the roster is built only when the tray opens. Both reads use `people_in_use` (2,140 rows) rather than `people`. A picked person the in-use query leaves out — emptied by a split while the filter held them — still gets a chip, since a term with no chip cannot be removed, and without one the in-place update would fall back to a rebuild on every press.
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use slint::ComponentHandle;
|
||||
use slint::{ComponentHandle, Model};
|
||||
|
||||
use crate::library;
|
||||
use crate::{AppWindow, Library, PersonChip, View};
|
||||
@@ -585,15 +585,20 @@ pub(super) fn push_people_chips(window: &AppWindow, ctl: &Rc<LibraryController>)
|
||||
)));
|
||||
// The tray, if it is open, has to lose its ticks with the chips: the
|
||||
// roster carries `picked` and is the same fact drawn a second time.
|
||||
push_people_roster(window, ctl);
|
||||
retick_roster(window, ctl);
|
||||
return;
|
||||
}
|
||||
|
||||
let borrow = ctl.catalog.borrow();
|
||||
let people = borrow
|
||||
.as_ref()
|
||||
.and_then(|cat| dr_catalog::faces::people(cat.connection()).ok())
|
||||
.unwrap_or_default();
|
||||
// `people_in_use`, not `people`: the chips name at most a handful, and the
|
||||
// full table is every empty group a regrouping pass left behind — 26,000
|
||||
// rows on the reference library, read to name two.
|
||||
let people = {
|
||||
let borrow = ctl.catalog.borrow();
|
||||
borrow
|
||||
.as_ref()
|
||||
.and_then(|cat| dr_catalog::faces::people_in_use(cat.connection()).ok())
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
let chips: Vec<PersonChip> = ids
|
||||
.iter()
|
||||
@@ -633,7 +638,48 @@ pub(super) fn push_people_chips(window: &AppWindow, ctl: &Rc<LibraryController>)
|
||||
ctl.filter.borrow().people_mode,
|
||||
library::PeopleMode::All
|
||||
));
|
||||
push_people_roster(window, ctl);
|
||||
retick_roster(window, ctl);
|
||||
}
|
||||
|
||||
/// Move the tray's ticks to match the filter, touching only the chips that
|
||||
/// changed.
|
||||
///
|
||||
/// # Why not rebuild it
|
||||
///
|
||||
/// Rebuilding was what every toggle did, and it made filtering by a face
|
||||
/// crawl. The roster is every person holding a face — 1,581 chips on the
|
||||
/// reference library — in a row Slint does not virtualise, so a new model
|
||||
/// tore down and re-created all of them and laid the row out again, to move
|
||||
/// one tick. It also read the whole people table twice to do it. The people
|
||||
/// have not changed between two presses on the tray; only `picked` has.
|
||||
///
|
||||
/// Falls back to a rebuild when a picked person has no chip — someone sent
|
||||
/// from the People screen who was set aside, say — because a term with no
|
||||
/// chip is a term the user cannot take back out. An empty roster is left
|
||||
/// alone: the tray is closed, and opening it builds it.
|
||||
fn retick_roster(window: &AppWindow, ctl: &Rc<LibraryController>) {
|
||||
let picked = ctl.filter.borrow().people.clone();
|
||||
let model = window.global::<Library>().get_library_people();
|
||||
if model.row_count() == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut shown = 0;
|
||||
for row in 0..model.row_count() {
|
||||
let Some(mut chip) = model.row_data(row) else {
|
||||
continue;
|
||||
};
|
||||
let on = picked.contains(&(chip.id as u64));
|
||||
shown += usize::from(on);
|
||||
if chip.picked != on {
|
||||
chip.picked = on;
|
||||
model.set_row_data(row, chip);
|
||||
}
|
||||
}
|
||||
|
||||
if shown < picked.len() {
|
||||
push_people_roster(window, ctl);
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill the filter bar's people tray.
|
||||
@@ -650,10 +696,14 @@ pub(super) fn push_people_chips(window: &AppWindow, ctl: &Rc<LibraryController>)
|
||||
/// says it back.
|
||||
fn push_people_roster(window: &AppWindow, ctl: &Rc<LibraryController>) {
|
||||
let picked = ctl.filter.borrow().people.clone();
|
||||
// In use rather than the whole table: the tray shows people holding a
|
||||
// face, and the rest are the empty groups a regrouping pass leaves
|
||||
// behind. A picked person can be one of those — emptied by a split while
|
||||
// the filter held them — and is given a chip below regardless.
|
||||
let borrow = ctl.catalog.borrow();
|
||||
let people = borrow
|
||||
.as_ref()
|
||||
.and_then(|cat| dr_catalog::faces::people(cat.connection()).ok())
|
||||
.and_then(|cat| dr_catalog::faces::people_in_use(cat.connection()).ok())
|
||||
.unwrap_or_default();
|
||||
|
||||
// Sorted as (unnamed, -faces) pairs beside the chip rather than by
|
||||
@@ -694,6 +744,22 @@ fn push_people_roster(window: &AppWindow, ctl: &Rc<LibraryController>) {
|
||||
.collect();
|
||||
// Stable, so the catalog's own tiebreak by name survives inside each of the
|
||||
// two blocks.
|
||||
// A term with no chip is a term the user cannot remove, and `retick_roster`
|
||||
// would rebuild on every press looking for it.
|
||||
for id in &picked {
|
||||
if !people.iter().any(|p| p.id.0 == *id) {
|
||||
rows.push((
|
||||
true,
|
||||
0,
|
||||
PersonChip {
|
||||
id: *id as i32,
|
||||
name: "Unnamed (0 faces)".into(),
|
||||
faces: 0,
|
||||
picked: true,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
rows.sort_by_key(|(unnamed, faces, _)| (*unnamed, -faces));
|
||||
|
||||
let rows: Vec<PersonChip> = rows.into_iter().map(|(_, _, chip)| chip).collect();
|
||||
|
||||
Reference in New Issue
Block a user