Move ratings, flags and keywording onto the library's Slint global
The keywording sheet's rows and its open/assign/unassign callbacks, the star and flag callbacks a cell click or a judgement key fires, the burst toggle and representative-chosen callbacks, the trash-selection shortcut, and the rating/unjudged/flag filter chips with their rating-counts model move from AppWindow onto the `Library` global started in the previous commit. Rust reaches them through window.global::<Library>() rather than window.set_/get_/on_ on the root.
This commit is contained in:
@@ -891,17 +891,19 @@ fn wire_trash_from_grid(
|
||||
let session = session.clone();
|
||||
let reload = on_scope_changed.clone();
|
||||
let visible = visible_ids.clone();
|
||||
window.on_library_cell_trashed(move |row| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
// Resolved through the visible ids rather than the row index alone:
|
||||
// the grid is a window over the catalog, so a stale index from
|
||||
// before a scroll would name a different photograph — and here that
|
||||
// would move the wrong file.
|
||||
let Some(&id) = visible().get(row as usize) else {
|
||||
return;
|
||||
};
|
||||
start_trash(&w, &ctl, &catalog, &session, &[id], &reload);
|
||||
});
|
||||
window
|
||||
.global::<Library>()
|
||||
.on_library_cell_trashed(move |row| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
// Resolved through the visible ids rather than the row index alone:
|
||||
// the grid is a window over the catalog, so a stale index from
|
||||
// before a scroll would name a different photograph — and here that
|
||||
// would move the wrong file.
|
||||
let Some(&id) = visible().get(row as usize) else {
|
||||
return;
|
||||
};
|
||||
start_trash(&w, &ctl, &catalog, &session, &[id], &reload);
|
||||
});
|
||||
}
|
||||
|
||||
// `Delete` on the selection — the bulk gesture.
|
||||
@@ -911,17 +913,19 @@ fn wire_trash_from_grid(
|
||||
let catalog = catalog.clone();
|
||||
let session = session.clone();
|
||||
let reload = on_scope_changed.clone();
|
||||
window.on_library_trash_selection(move || {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
let chosen = ctl.selected();
|
||||
if chosen.is_empty() {
|
||||
// A keystroke that does nothing reads as a broken key, so it
|
||||
// says why rather than failing silently.
|
||||
w.set_library_status("Select an image first".into());
|
||||
return;
|
||||
}
|
||||
start_trash(&w, &ctl, &catalog, &session, &chosen, &reload);
|
||||
});
|
||||
window
|
||||
.global::<Library>()
|
||||
.on_library_trash_selection(move || {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
let chosen = ctl.selected();
|
||||
if chosen.is_empty() {
|
||||
// A keystroke that does nothing reads as a broken key, so it
|
||||
// says why rather than failing silently.
|
||||
w.set_library_status("Select an image first".into());
|
||||
return;
|
||||
}
|
||||
start_trash(&w, &ctl, &catalog, &session, &chosen, &reload);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -31,42 +31,48 @@ pub(super) fn wire_filter_ratings_and_people(window: &AppWindow, ctl: &Rc<Librar
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
window.on_library_filter_min_rating_changed(move |n| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
ctl.filter.borrow_mut().min_rating = n.clamp(0, 5) as u8;
|
||||
// Stars and "unrated" are contradictory terms — asking for four
|
||||
// stars *and* nothing judged matches nothing at all, which reads
|
||||
// as a broken filter rather than an impossible question.
|
||||
if n > 0 {
|
||||
ctl.filter.borrow_mut().unjudged = false;
|
||||
}
|
||||
w.set_library_filter_min_rating(n.clamp(0, 5));
|
||||
w.set_library_filter_unjudged(ctl.filter.borrow().unjudged);
|
||||
refilter(&w, &ctl);
|
||||
});
|
||||
window
|
||||
.global::<Library>()
|
||||
.on_library_filter_min_rating_changed(move |n| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
ctl.filter.borrow_mut().min_rating = n.clamp(0, 5) as u8;
|
||||
// Stars and "unrated" are contradictory terms — asking for four
|
||||
// stars *and* nothing judged matches nothing at all, which reads
|
||||
// as a broken filter rather than an impossible question.
|
||||
if n > 0 {
|
||||
ctl.filter.borrow_mut().unjudged = false;
|
||||
}
|
||||
w.global::<Library>()
|
||||
.set_library_filter_min_rating(n.clamp(0, 5));
|
||||
w.global::<Library>()
|
||||
.set_library_filter_unjudged(ctl.filter.borrow().unjudged);
|
||||
refilter(&w, &ctl);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
window.on_library_filter_unjudged_changed(move |on| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
{
|
||||
let mut f = ctl.filter.borrow_mut();
|
||||
f.unjudged = on;
|
||||
// See above: the two cannot both hold.
|
||||
if on {
|
||||
f.min_rating = 0;
|
||||
f.flag = None;
|
||||
window
|
||||
.global::<Library>()
|
||||
.on_library_filter_unjudged_changed(move |on| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
{
|
||||
let mut f = ctl.filter.borrow_mut();
|
||||
f.unjudged = on;
|
||||
// See above: the two cannot both hold.
|
||||
if on {
|
||||
f.min_rating = 0;
|
||||
f.flag = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
w.set_library_filter_unjudged(on);
|
||||
if on {
|
||||
w.set_library_filter_min_rating(0);
|
||||
w.set_library_filter_flag(0);
|
||||
}
|
||||
refilter(&w, &ctl);
|
||||
});
|
||||
w.global::<Library>().set_library_filter_unjudged(on);
|
||||
if on {
|
||||
w.global::<Library>().set_library_filter_min_rating(0);
|
||||
w.global::<Library>().set_library_filter_flag(0);
|
||||
}
|
||||
refilter(&w, &ctl);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
@@ -177,23 +183,26 @@ pub(super) fn wire_filter_ratings_and_people(window: &AppWindow, ctl: &Rc<Librar
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
window.on_library_filter_flag_changed(move |f| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
{
|
||||
let mut filter = ctl.filter.borrow_mut();
|
||||
filter.flag = match f {
|
||||
1 => Some(dr_types::FlagState::Pick),
|
||||
2 => Some(dr_types::FlagState::Reject),
|
||||
_ => None,
|
||||
};
|
||||
if f > 0 {
|
||||
filter.unjudged = false;
|
||||
window
|
||||
.global::<Library>()
|
||||
.on_library_filter_flag_changed(move |f| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
{
|
||||
let mut filter = ctl.filter.borrow_mut();
|
||||
filter.flag = match f {
|
||||
1 => Some(dr_types::FlagState::Pick),
|
||||
2 => Some(dr_types::FlagState::Reject),
|
||||
_ => None,
|
||||
};
|
||||
if f > 0 {
|
||||
filter.unjudged = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
w.set_library_filter_flag(f);
|
||||
w.set_library_filter_unjudged(ctl.filter.borrow().unjudged);
|
||||
refilter(&w, &ctl);
|
||||
});
|
||||
w.global::<Library>().set_library_filter_flag(f);
|
||||
w.global::<Library>()
|
||||
.set_library_filter_unjudged(ctl.filter.borrow().unjudged);
|
||||
refilter(&w, &ctl);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,9 @@ pub fn sync_ratings(window: &AppWindow, catalog: &Catalog, ids: &[dr_types::Imag
|
||||
pub(super) fn refresh_rating_counts(window: &AppWindow, catalog: &Catalog) {
|
||||
let counts = dr_catalog::rating::rating_histogram(catalog.connection()).unwrap_or_default();
|
||||
let as_i32: Vec<i32> = counts.iter().map(|n| *n as i32).collect();
|
||||
window.set_library_rating_counts(slint::ModelRc::new(slint::VecModel::from(as_i32)));
|
||||
window
|
||||
.global::<Library>()
|
||||
.set_library_rating_counts(slint::ModelRc::new(slint::VecModel::from(as_i32)));
|
||||
|
||||
// TRACES: FR-CAT-9
|
||||
// How many originals are actually here, for the "On this device" chip.
|
||||
@@ -126,7 +128,9 @@ fn refresh_keywords(window: &AppWindow, ctl: &Rc<LibraryController>, images: &[d
|
||||
})
|
||||
.collect();
|
||||
|
||||
window.set_library_keywords(slint::ModelRc::new(slint::VecModel::from(model)));
|
||||
window
|
||||
.global::<Library>()
|
||||
.set_library_keywords(slint::ModelRc::new(slint::VecModel::from(model)));
|
||||
}
|
||||
|
||||
/// Put a keyword on the selection, or take it off.
|
||||
@@ -824,17 +828,19 @@ pub(super) fn wire_ratings_and_flags(
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
window.on_library_cell_rated(move |row, stars| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
let id = ctl
|
||||
.image_ids
|
||||
.borrow()
|
||||
.get(row as usize)
|
||||
.map(|id| dr_types::ImageId(*id as u64));
|
||||
let Some(id) = id else { return };
|
||||
window
|
||||
.global::<Library>()
|
||||
.on_library_cell_rated(move |row, stars| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
let id = ctl
|
||||
.image_ids
|
||||
.borrow()
|
||||
.get(row as usize)
|
||||
.map(|id| dr_types::ImageId(*id as u64));
|
||||
let Some(id) = id else { return };
|
||||
|
||||
apply_judgement(&w, &ctl, &[id], Some(stars.clamp(0, 5) as u8), None);
|
||||
});
|
||||
apply_judgement(&w, &ctl, &[id], Some(stars.clamp(0, 5) as u8), None);
|
||||
});
|
||||
}
|
||||
|
||||
// Fold a burst up, or open it out (FR-CULL-5). A reload rather than a repaint, because
|
||||
@@ -842,27 +848,29 @@ pub(super) fn wire_ratings_and_flags(
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
window.on_library_burst_toggled(move |row| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
let id = ctl
|
||||
.image_ids
|
||||
.borrow()
|
||||
.get(row as usize)
|
||||
.map(|id| dr_types::ImageId(*id as u64));
|
||||
let Some(id) = id else { return };
|
||||
window
|
||||
.global::<Library>()
|
||||
.on_library_burst_toggled(move |row| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
let id = ctl
|
||||
.image_ids
|
||||
.borrow()
|
||||
.get(row as usize)
|
||||
.map(|id| dr_types::ImageId(*id as u64));
|
||||
let Some(id) = id else { return };
|
||||
|
||||
let changed = {
|
||||
let borrow = ctl.catalog.borrow();
|
||||
match borrow.as_ref() {
|
||||
Some(catalog) => crate::bursts::toggle(catalog, id),
|
||||
None => false,
|
||||
let changed = {
|
||||
let borrow = ctl.catalog.borrow();
|
||||
match borrow.as_ref() {
|
||||
Some(catalog) => crate::bursts::toggle(catalog, id),
|
||||
None => false,
|
||||
}
|
||||
};
|
||||
if changed {
|
||||
ctl.requested.borrow_mut().clear();
|
||||
load_window(&w, &ctl);
|
||||
}
|
||||
};
|
||||
if changed {
|
||||
ctl.requested.borrow_mut().clear();
|
||||
load_window(&w, &ctl);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Name the frame an open burst folds to (FR-CULL-5). The default is the
|
||||
@@ -878,26 +886,28 @@ pub(super) fn wire_ratings_and_flags(
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
window.on_library_burst_representative_chosen(move |row| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
let ids: Vec<dr_types::ImageId> = ctl
|
||||
.image_ids
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|id| dr_types::ImageId(*id as u64))
|
||||
.collect();
|
||||
let Some(id) = ids.get(row as usize).copied() else {
|
||||
return;
|
||||
};
|
||||
window
|
||||
.global::<Library>()
|
||||
.on_library_burst_representative_chosen(move |row| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
let ids: Vec<dr_types::ImageId> = ctl
|
||||
.image_ids
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|id| dr_types::ImageId(*id as u64))
|
||||
.collect();
|
||||
let Some(id) = ids.get(row as usize).copied() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let borrow = ctl.catalog.borrow();
|
||||
let Some(catalog) = borrow.as_ref() else {
|
||||
return;
|
||||
};
|
||||
if crate::bursts::choose(catalog, id) {
|
||||
crate::bursts::sync_badges(&w, catalog, &ids);
|
||||
}
|
||||
});
|
||||
let borrow = ctl.catalog.borrow();
|
||||
let Some(catalog) = borrow.as_ref() else {
|
||||
return;
|
||||
};
|
||||
if crate::bursts::choose(catalog, id) {
|
||||
crate::bursts::sync_badges(&w, catalog, &ids);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// A rating or flag key. Applies to the whole selection, which is what
|
||||
@@ -906,18 +916,20 @@ pub(super) fn wire_ratings_and_flags(
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
let coll_for_keys = coll_ctl.clone();
|
||||
window.on_library_judged(move |rating, flag| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
let chosen = coll_for_keys.selected();
|
||||
window
|
||||
.global::<Library>()
|
||||
.on_library_judged(move |rating, flag| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
let chosen = coll_for_keys.selected();
|
||||
|
||||
// Exactly one axis is meant per keystroke; the other arrives as
|
||||
// -1 so a star press cannot disturb a flag or the reverse.
|
||||
if rating >= 0 {
|
||||
apply_judgement(&w, &ctl, &chosen, Some(rating.clamp(0, 5) as u8), None);
|
||||
} else if flag >= 0 {
|
||||
apply_judgement(&w, &ctl, &chosen, None, Some(flag_from_code(flag)));
|
||||
}
|
||||
});
|
||||
// Exactly one axis is meant per keystroke; the other arrives as
|
||||
// -1 so a star press cannot disturb a flag or the reverse.
|
||||
if rating >= 0 {
|
||||
apply_judgement(&w, &ctl, &chosen, Some(rating.clamp(0, 5) as u8), None);
|
||||
} else if flag >= 0 {
|
||||
apply_judgement(&w, &ctl, &chosen, None, Some(flag_from_code(flag)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -936,28 +948,34 @@ pub(super) fn wire_keywords(
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
let coll_for_keywords = coll_ctl.clone();
|
||||
window.on_library_keywords_opened(move || {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
refresh_keywords(&w, &ctl, &coll_for_keywords.selected());
|
||||
});
|
||||
window
|
||||
.global::<Library>()
|
||||
.on_library_keywords_opened(move || {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
refresh_keywords(&w, &ctl, &coll_for_keywords.selected());
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
window.on_library_assign_keyword(move |word| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
apply_keyword(&w, &ctl, word.as_str(), true);
|
||||
});
|
||||
window
|
||||
.global::<Library>()
|
||||
.on_library_assign_keyword(move |word| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
apply_keyword(&w, &ctl, word.as_str(), true);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
window.on_library_unassign_keyword(move |word| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
apply_keyword(&w, &ctl, word.as_str(), false);
|
||||
});
|
||||
window
|
||||
.global::<Library>()
|
||||
.on_library_unassign_keyword(move |word| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
apply_keyword(&w, &ctl, word.as_str(), false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -883,17 +883,23 @@ enum Found {
|
||||
/// first act would be to try to clear a filter the bar says is off.
|
||||
fn push_filter(window: &AppWindow, ctl: &Rc<LibraryController>) {
|
||||
let filter = ctl.filter.borrow().clone();
|
||||
window.set_library_filter_min_rating(filter.min_rating as i32);
|
||||
window.set_library_filter_unjudged(filter.unjudged);
|
||||
window
|
||||
.global::<Library>()
|
||||
.set_library_filter_min_rating(filter.min_rating as i32);
|
||||
window
|
||||
.global::<Library>()
|
||||
.set_library_filter_unjudged(filter.unjudged);
|
||||
// The bar's own numbering, from `on_library_filter_flag_changed`. It has no
|
||||
// code for `Unflagged` because it cannot produce one — "nothing has judged
|
||||
// this" is the `unjudged` chip, over both stars and flags — so a place can
|
||||
// never carry one either, and it falls in with "no flag constraint".
|
||||
window.set_library_filter_flag(match filter.flag {
|
||||
Some(dr_types::FlagState::Pick) => 1,
|
||||
Some(dr_types::FlagState::Reject) => 2,
|
||||
_ => 0,
|
||||
});
|
||||
window
|
||||
.global::<Library>()
|
||||
.set_library_filter_flag(match filter.flag {
|
||||
Some(dr_types::FlagState::Pick) => 1,
|
||||
Some(dr_types::FlagState::Reject) => 2,
|
||||
_ => 0,
|
||||
});
|
||||
window
|
||||
.global::<Library>()
|
||||
.set_library_local_only(filter.local_only);
|
||||
|
||||
+17
-63
@@ -506,48 +506,10 @@ export component AppWindow inherits Window {
|
||||
/// Ask about a collection's local copies: the tray on a row, or a long
|
||||
/// press anywhere on it.
|
||||
callback collection-offline-menu(int);
|
||||
/// TRACES: FR-CAT-5 | FR-CAT-6
|
||||
/// Keywording the grid's selection. The catalog has been searchable by
|
||||
/// keyword since it existed and there was nowhere to type one; this is it.
|
||||
///
|
||||
/// The vocabulary arrives already answered against the selection — each row
|
||||
/// says how many of the selected photographs carry that word — because only
|
||||
/// Rust knows what is selected, and a `.slint` file counting it would need
|
||||
/// the selection as a second model that could disagree with the first.
|
||||
in property <[KeywordRow]> library-keywords;
|
||||
/// The sheet is opening: recompute the rows against the selection as it
|
||||
/// stands now. Pulled rather than pushed, because the selection changes on
|
||||
/// every arrow key and the sheet is shut for almost all of them.
|
||||
callback library-keywords-opened();
|
||||
/// Put a keyword on the selection, creating it if it is new. By name, so a
|
||||
/// word typed into the field and a word tapped in the list are one path.
|
||||
callback library-assign-keyword(string);
|
||||
/// Take a keyword off the selection. Never deletes the keyword itself —
|
||||
/// it stays in the vocabulary and on every other photograph that carries it.
|
||||
callback library-unassign-keyword(string);
|
||||
|
||||
|
||||
|
||||
|
||||
// --- ratings and flags (FR-CAT-5, FR-CULL-4) ---
|
||||
//
|
||||
// Stars and pick/reject, set from the grid and persisted to the catalog
|
||||
// and the sidecar. Every image enters unrated, which is a state of its
|
||||
// own rather than a zero score.
|
||||
/// A star was clicked on a cell: row, then the rating 0..5.
|
||||
callback library-cell-rated(int, int);
|
||||
/// The trash target was clicked on one cell, by row.
|
||||
callback library-cell-trashed(int);
|
||||
/// The burst mark was clicked on one cell, by row (FR-CULL-5).
|
||||
callback library-burst-toggled(int);
|
||||
/// One frame of an open burst was named as the one the group folds to, by
|
||||
/// row (FR-CULL-5).
|
||||
callback library-burst-representative-chosen(int);
|
||||
/// Move the grid selection to the trash — the `Delete` key.
|
||||
callback library-trash-selection();
|
||||
/// A judgement key was pressed, applying to the whole selection. One of
|
||||
/// the two arguments is -1, saying which axis was *not* meant.
|
||||
callback library-judged(int, int);
|
||||
|
||||
/// Whose photographs the grid is narrowed to, one entry each.
|
||||
in property <[PersonChip]> library-filter-people;
|
||||
@@ -566,15 +528,7 @@ export component AppWindow inherits Window {
|
||||
in property <[PersonChip]> library-people;
|
||||
callback library-people-listed();
|
||||
callback library-filter-person-toggled(int);
|
||||
in property <int> library-filter-min-rating: 0;
|
||||
in property <bool> library-filter-unjudged: false;
|
||||
in property <int> library-filter-flag: 0;
|
||||
/// Images per star count, index 0 unrated, for the filter chips.
|
||||
in-out property <[int]> library-rating-counts;
|
||||
|
||||
callback library-filter-min-rating-changed(int);
|
||||
callback library-filter-unjudged-changed(bool);
|
||||
callback library-filter-flag-changed(int);
|
||||
|
||||
callback next-image();
|
||||
callback prev-image();
|
||||
@@ -1618,10 +1572,10 @@ in property <bool> panel-visible: true;
|
||||
file-in-collection(id, moves) => {
|
||||
Library.library-file-in-collection(id, moves);
|
||||
}
|
||||
keywords: root.library-keywords;
|
||||
keywords-opened() => { root.library-keywords-opened(); }
|
||||
assign-keyword(word) => { root.library-assign-keyword(word); }
|
||||
unassign-keyword(word) => { root.library-unassign-keyword(word); }
|
||||
keywords: Library.library-keywords;
|
||||
keywords-opened() => { Library.library-keywords-opened(); }
|
||||
assign-keyword(word) => { Library.library-assign-keyword(word); }
|
||||
unassign-keyword(word) => { Library.library-unassign-keyword(word); }
|
||||
cursor: Library.library-cursor;
|
||||
move-cursor(delta, extend) => {
|
||||
Library.library-move-cursor(delta, extend);
|
||||
@@ -1641,24 +1595,24 @@ in property <bool> panel-visible: true;
|
||||
filter-eyes-open: root.library-filter-eyes-open;
|
||||
people: root.library-people;
|
||||
gestures: root.library-gestures;
|
||||
filter-min-rating: root.library-filter-min-rating;
|
||||
filter-unjudged: root.library-filter-unjudged;
|
||||
filter-flag: root.library-filter-flag;
|
||||
rating-counts: root.library-rating-counts;
|
||||
filter-min-rating: Library.library-filter-min-rating;
|
||||
filter-unjudged: Library.library-filter-unjudged;
|
||||
filter-flag: Library.library-filter-flag;
|
||||
rating-counts: Library.library-rating-counts;
|
||||
|
||||
cell-rated(i, n) => { root.library-cell-rated(i, n); }
|
||||
cell-trashed(i) => { root.library-cell-trashed(i); }
|
||||
burst-toggled(i) => { root.library-burst-toggled(i); }
|
||||
cell-rated(i, n) => { Library.library-cell-rated(i, n); }
|
||||
cell-trashed(i) => { Library.library-cell-trashed(i); }
|
||||
burst-toggled(i) => { Library.library-burst-toggled(i); }
|
||||
burst-representative-chosen(i) => {
|
||||
root.library-burst-representative-chosen(i);
|
||||
Library.library-burst-representative-chosen(i);
|
||||
}
|
||||
trash-selection() => { root.library-trash-selection(); }
|
||||
trash-selection() => { Library.library-trash-selection(); }
|
||||
// Derived from the sidebar's own selection rather than
|
||||
// mirrored in a second property: `-1` is already the sentinel
|
||||
// for the trash row (see collections.slint), and two sources
|
||||
// for one fact is how they come to disagree.
|
||||
viewing-trash: root.collection-selected == -1;
|
||||
judged(rating, flag) => { root.library-judged(rating, flag); }
|
||||
judged(rating, flag) => { Library.library-judged(rating, flag); }
|
||||
// `F2` renames whatever the grid is scoped to. Answered from
|
||||
// the sidebar's selection rather than a second property, for
|
||||
// the same reason `viewing-trash` above is: one fact, one
|
||||
@@ -1669,12 +1623,12 @@ in property <bool> panel-visible: true;
|
||||
Collections.collection-rename-start(root.collection-selected);
|
||||
}
|
||||
filter-min-rating-changed(n) => {
|
||||
root.library-filter-min-rating-changed(n);
|
||||
Library.library-filter-min-rating-changed(n);
|
||||
}
|
||||
filter-unjudged-toggled(on) => {
|
||||
root.library-filter-unjudged-changed(on);
|
||||
Library.library-filter-unjudged-changed(on);
|
||||
}
|
||||
filter-flag-changed(f) => { root.library-filter-flag-changed(f); }
|
||||
filter-flag-changed(f) => { Library.library-filter-flag-changed(f); }
|
||||
filter-person-cleared(id) => { root.library-filter-person-cleared(id); }
|
||||
filter-people-mode-toggled() => { root.library-filter-people-mode-toggled(); }
|
||||
filter-eyes-open-toggled() => { root.library-filter-eyes-open-toggled(); }
|
||||
|
||||
@@ -600,6 +600,52 @@ export global Library {
|
||||
in property <int> library-cursor: -1;
|
||||
callback library-move-cursor(int, bool);
|
||||
callback library-open-cursor();
|
||||
/// TRACES: FR-CAT-5 | FR-CAT-6
|
||||
/// Keywording the grid's selection. The catalog has been searchable by
|
||||
/// keyword since it existed and there was nowhere to type one; this is it.
|
||||
///
|
||||
/// The vocabulary arrives already answered against the selection — each row
|
||||
/// says how many of the selected photographs carry that word — because only
|
||||
/// Rust knows what is selected, and a `.slint` file counting it would need
|
||||
/// the selection as a second model that could disagree with the first.
|
||||
in property <[KeywordRow]> library-keywords;
|
||||
/// The sheet is opening: recompute the rows against the selection as it
|
||||
/// stands now. Pulled rather than pushed, because the selection changes on
|
||||
/// every arrow key and the sheet is shut for almost all of them.
|
||||
callback library-keywords-opened();
|
||||
/// Put a keyword on the selection, creating it if it is new. By name, so a
|
||||
/// word typed into the field and a word tapped in the list are one path.
|
||||
callback library-assign-keyword(string);
|
||||
/// Take a keyword off the selection. Never deletes the keyword itself —
|
||||
/// it stays in the vocabulary and on every other photograph that carries it.
|
||||
callback library-unassign-keyword(string);
|
||||
// --- ratings and flags (FR-CAT-5, FR-CULL-4) ---
|
||||
//
|
||||
// Stars and pick/reject, set from the grid and persisted to the catalog
|
||||
// and the sidecar. Every image enters unrated, which is a state of its
|
||||
// own rather than a zero score.
|
||||
/// A star was clicked on a cell: row, then the rating 0..5.
|
||||
callback library-cell-rated(int, int);
|
||||
/// The trash target was clicked on one cell, by row.
|
||||
callback library-cell-trashed(int);
|
||||
/// The burst mark was clicked on one cell, by row (FR-CULL-5).
|
||||
callback library-burst-toggled(int);
|
||||
/// One frame of an open burst was named as the one the group folds to, by
|
||||
/// row (FR-CULL-5).
|
||||
callback library-burst-representative-chosen(int);
|
||||
/// Move the grid selection to the trash — the `Delete` key.
|
||||
callback library-trash-selection();
|
||||
/// A judgement key was pressed, applying to the whole selection. One of
|
||||
/// the two arguments is -1, saying which axis was *not* meant.
|
||||
callback library-judged(int, int);
|
||||
in property <int> library-filter-min-rating: 0;
|
||||
in property <bool> library-filter-unjudged: false;
|
||||
in property <int> library-filter-flag: 0;
|
||||
/// Images per star count, index 0 unrated, for the filter chips.
|
||||
in-out property <[int]> library-rating-counts;
|
||||
callback library-filter-min-rating-changed(int);
|
||||
callback library-filter-unjudged-changed(bool);
|
||||
callback library-filter-flag-changed(int);
|
||||
}
|
||||
|
||||
// The photo roll: the grid's loaded window along the foot of the develop view.
|
||||
|
||||
Reference in New Issue
Block a user