Narrow the grid to several people at once, either way
"Show photos" could only ever mean one person. The two questions a photographer actually asks are "every picture of Anna or Bob" and "the pictures they are both in", and the second is not reachable by any sequence of single-person filters — no amount of switching between one person and another finds the frame they share. So the filter holds a *set* of people and a mode. `RatingFilter` was already the right home, as its own doc says: every query path threads it, so the count in the header and the cells in the grid are narrowed by the same thing, and this composes with stars, flags and the date range for free. The union is `EXISTS ... person_id IN (...)`. The intersection counts **distinct** people per image and compares against the size of the selection — one subquery rather than one per person, and it does not grow the statement with the selection. `DISTINCT` is what makes it correct: three faces of Anna in one frame must not satisfy a filter asking for Anna and Bob, and there is a test that says so. Any rather than All is the default. With one person the modes are the same filter, and adding a second to a union can only ever show more — so a user who has not noticed the toggle never ends up staring at an empty grid wondering what they broke. The toggle only appears at two people, because a control that demonstrably does nothing is a control that teaches the user to ignore it. Building the set needs no picker of its own: the Identity screen gains "And also…" beside "Show photos", offered only once the grid is already narrowed to somebody. Each person is a chip on the filter bar and each chip removes just that person, so a selection of three can be taken apart one at a time rather than only cleared wholesale. `RatingFilter` stops being `Copy`, since it now holds a `Vec`. Every query path already took it by reference; the casualties were two struct updates and one `Cell` that becomes a `RefCell`. 484 dr-ui tests pass, including the union, the intersection, that one person reads the same in both modes, and the repeated-faces trap. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+15
-9
@@ -5,7 +5,7 @@ import { SpotHandle, SpotPanel, SpotRole } from "spots.slint";
|
||||
import { HistoryPanel, HistoryRow } from "history.slint";
|
||||
import { LaunchScreen } from "launch.slint";
|
||||
import { IdentityScreen, IdentityPerson, IdentityFace } from "identity.slint";
|
||||
import { LibraryGrid, LibraryCell, TimelineBar, PhotoRoll, KeywordRow } from "library.slint";
|
||||
import { LibraryGrid, LibraryCell, TimelineBar, PhotoRoll, KeywordRow, PersonChip } from "library.slint";
|
||||
import { Button, PanelHeading, Label, Value, Caption, Panel, EmptyState, ProgressBar, ActivityRow } from "widgets.slint";
|
||||
import { CollectionsPanel, CollectionRow, OfflinePrompt } from "collections.slint";
|
||||
import { HistogramPanel, HistogramView } from "histogram.slint";
|
||||
@@ -13,7 +13,7 @@ import { SettingsPage } from "settings.slint";
|
||||
import { ImportPage } from "import.slint";
|
||||
import { StatusBar, InfoPanel } from "develop.slint";
|
||||
|
||||
export { LibraryCell, TimelineBar, CollectionRow, ActivityRow, HistogramView }
|
||||
export { LibraryCell, TimelineBar, CollectionRow, ActivityRow, HistogramView, PersonChip }
|
||||
export { ViewMode, GradientHandle, HandleRole, SpotHandle, SpotRole }
|
||||
|
||||
export component AppWindow inherits Window {
|
||||
@@ -365,7 +365,7 @@ export component AppWindow inherits Window {
|
||||
in property <bool> identity-selected-ignored: false;
|
||||
callback identity-toggle-show-ignored();
|
||||
callback identity-ignore-person(int, bool);
|
||||
callback identity-show-photos(int);
|
||||
callback identity-show-photos(int, bool);
|
||||
callback identity-index();
|
||||
callback identity-stop-indexing();
|
||||
callback identity-check-coverage();
|
||||
@@ -541,9 +541,12 @@ export component AppWindow inherits Window {
|
||||
/// the two arguments is -1, saying which axis was *not* meant.
|
||||
callback library-judged(int, int);
|
||||
|
||||
/// Whose photographs the grid is narrowed to. Empty when it is not.
|
||||
in property <string> library-filter-person-name;
|
||||
callback library-filter-person-cleared();
|
||||
/// Whose photographs the grid is narrowed to, one entry each.
|
||||
in property <[PersonChip]> library-filter-people;
|
||||
/// Whether those people are an intersection rather than a union.
|
||||
in property <bool> library-filter-people-all: false;
|
||||
callback library-filter-person-cleared(int);
|
||||
callback library-filter-people-mode-toggled();
|
||||
in property <int> library-filter-min-rating: 0;
|
||||
in property <bool> library-filter-unjudged: false;
|
||||
in property <int> library-filter-flag: 0;
|
||||
@@ -1198,6 +1201,7 @@ in property <bool> panel-visible: true;
|
||||
confirm-all() => { root.identity-confirm-all(); }
|
||||
split-picked() => { root.identity-split-picked(); }
|
||||
compact: !root.expanded;
|
||||
photos-filtered: root.library-filter-people.length > 0;
|
||||
regrouping: root.identity-regrouping;
|
||||
regroup-status: root.identity-regroup-status;
|
||||
ignored-count: root.identity-ignored-count;
|
||||
@@ -1205,7 +1209,7 @@ in property <bool> panel-visible: true;
|
||||
selected-ignored: root.identity-selected-ignored;
|
||||
toggle-show-ignored() => { root.identity-toggle-show-ignored(); }
|
||||
ignore-person(id, on) => { root.identity-ignore-person(id, on); }
|
||||
show-photos(id) => { root.identity-show-photos(id); }
|
||||
show-photos(id, add) => { root.identity-show-photos(id, add); }
|
||||
recluster() => { root.identity-recluster(); }
|
||||
index-faces() => { root.identity-index(); }
|
||||
stop-indexing() => { root.identity-stop-indexing(); }
|
||||
@@ -1431,7 +1435,8 @@ in property <bool> panel-visible: true;
|
||||
root.library-remove-from-collection();
|
||||
}
|
||||
|
||||
filter-person-name: root.library-filter-person-name;
|
||||
filter-people: root.library-filter-people;
|
||||
filter-people-all: root.library-filter-people-all;
|
||||
filter-min-rating: root.library-filter-min-rating;
|
||||
filter-unjudged: root.library-filter-unjudged;
|
||||
filter-flag: root.library-filter-flag;
|
||||
@@ -1462,7 +1467,8 @@ in property <bool> panel-visible: true;
|
||||
root.library-filter-unjudged-changed(on);
|
||||
}
|
||||
filter-flag-changed(f) => { root.library-filter-flag-changed(f); }
|
||||
filter-person-cleared() => { root.library-filter-person-cleared(); }
|
||||
filter-person-cleared(id) => { root.library-filter-person-cleared(id); }
|
||||
filter-people-mode-toggled() => { root.library-filter-people-mode-toggled(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user