Give the collections sidebar its own Slint global

AppWindow carried the sidebar's tree, its row menu, renaming, drag and
drop between rows, the trash row, and the membership sheet as ~40
properties and callbacks on the root component, in the pattern CH-1
describes and the develop screen's globals (Adjustments, Framing, Steps,
...) already replaced. Collections.* in collections.slint now holds that
state, declared next to the MembershipRow struct it and the membership
sheet both use; Rust reaches it through window.global::<Collections>()
instead of window.set_/get_/on_/invoke_ on the root.

collection-selected, collection-select, and collection-offline-menu stay
on AppWindow: library_ui.rs invokes collection-select directly and
registers collection-offline-menu's handler, and lib.rs reads
collection-selected for back-navigation, so moving them would have meant
editing library_ui.rs, which another change on this branch is splitting
into a module directory. collections-visible stays too — it is
lib.rs's panel-layout state, seeded from the saved layout before the
sidebar exists, and collections_ui never touches it. Everything prefixed
library- (the grid's drag, selection and keyword state that the sidebar's
Rust also wires for cross-feature gestures like filing a selection into
a collection) stays on the window as well, since it belongs to the
library screen, not the sidebar.
This commit is contained in:
2026-09-20 21:07:25 +02:00
parent 14ed1dc410
commit f6ff5eabd9
9 changed files with 816 additions and 643 deletions
+15 -7
View File
@@ -11,9 +11,9 @@ use std::rc::Rc;
use dr_catalog::collections::{self as coll, CollectionKind};
use dr_catalog::Catalog;
use dr_types::{CollectionId, ImageId};
use slint::Model as _;
use slint::{ComponentHandle as _, Model as _};
use crate::{AppWindow, CollectionRow};
use crate::{AppWindow, CollectionRow, Collections};
use super::controller::CollectionsController;
@@ -27,7 +27,9 @@ pub fn refresh_tree(window: &AppWindow, ctl: &Rc<CollectionsController>, catalog
let rows = match coll::tree(catalog.connection()) {
Ok(r) => r,
Err(e) => {
window.set_collection_error(format!("reading collections: {e}").into());
window
.global::<Collections>()
.set_collection_error(format!("reading collections: {e}").into());
return;
}
};
@@ -95,7 +97,9 @@ pub fn refresh_tree(window: &AppWindow, ctl: &Rc<CollectionsController>, catalog
*ctl.row_smart.borrow_mut() = smart;
*ctl.row_has_children.borrow_mut() = has_kids;
*ctl.row_nested.borrow_mut() = nested;
window.set_collection_rows(slint::ModelRc::new(slint::VecModel::from(out)));
window
.global::<Collections>()
.set_collection_rows(slint::ModelRc::new(slint::VecModel::from(out)));
// The rows are what `sync_reorderable` reads, so it has to be told they
// changed: a collection that gains a child, or one that is deleted out from
@@ -251,7 +255,7 @@ pub fn sync_selection(window: &AppWindow, ctl: &Rc<CollectionsController>, ids:
/// looking at, and a scope change costs no extra query.
pub(super) fn sync_reorderable(window: &AppWindow) {
let id = window.get_collection_selected();
let rows = window.get_collection_rows();
let rows = window.global::<Collections>().get_collection_rows();
let manual = id > 0
&& (0..rows.row_count())
.filter_map(|i| rows.row_data(i))
@@ -346,7 +350,9 @@ pub(super) fn refresh_membership(
let rows = match coll::membership_of(catalog.connection(), &chosen) {
Ok(r) => r,
Err(e) => {
window.set_collection_error(format!("reading collections: {e}").into());
window
.global::<Collections>()
.set_collection_error(format!("reading collections: {e}").into());
Vec::new()
}
};
@@ -367,7 +373,9 @@ pub(super) fn refresh_membership(
})
.collect();
window.set_membership_rows(slint::ModelRc::new(slint::VecModel::from(out)));
window
.global::<Collections>()
.set_membership_rows(slint::ModelRc::new(slint::VecModel::from(out)));
}
#[cfg(test)]