Split collections_ui::wire into one function per section

The 1,457-line wire() registered every collections-sidebar and grid-drag
callback in one function, sectioned only by comment. Lift each section
into its own fn: wire_selection (split further into wire_selection and
wire_selection_filing, since the original section ran to 375 lines),
wire_drag, wire_trash, wire_trash_from_grid, wire_tree_navigation,
wire_create, wire_rename, wire_remove, wire_row_drag (the tree row's own
hold-drag, which the original "remove" comment's span covered but which
is really a separate feature), wire_row_menu, and wire_membership.
wire() itself now just coerces the shared closures to trait objects and
calls each in the original order. visible_ids is coerced to
Rc<dyn Fn() -> Vec<ImageId>> at the top, alongside on_scope_changed and
session, so the new functions take plain trait objects instead of
threading a generic parameter through every one of them.
This commit is contained in:
2026-09-20 18:36:18 +02:00
parent 6b1aac477d
commit 8540518022
2 changed files with 174 additions and 55 deletions
+166 -47
View File
@@ -1672,17 +1672,52 @@ pub fn wire<S, R, P, C>(
P: Fn(usize, usize) -> Vec<ImageId> + 'static,
C: Fn() -> Option<dr_sync::Connection> + 'static,
{
// Coerced to trait objects here rather than at each use: `start_trash` and
// `drain_trash` are shared by three callbacks, and a generic parameter would
// make each of them a separate instantiation for no gain.
// Coerced to trait objects here rather than at each use: each is shared by
// several of the functions below, and a generic parameter would make every
// one of them a separate instantiation for no gain.
let on_scope_changed: Rc<dyn Fn()> = Rc::new(on_scope_changed);
let visible_ids = Rc::new(visible_ids);
let visible_ids: Rc<dyn Fn() -> Vec<ImageId>> = Rc::new(visible_ids);
// A shift-click asks the catalog what lies between its two ends, and the
// catalog belongs to the grid's controller — see `span_source`.
*ctl.span_source.borrow_mut() = Some(Rc::new(span_ids));
let session: Rc<dyn Fn() -> Option<dr_sync::Connection>> = Rc::new(session);
// --- selection ---------------------------------------------------------
wire_selection(window, &ctl, &catalog, &visible_ids, &on_scope_changed);
wire_selection_filing(window, &ctl, &catalog, &visible_ids, &on_scope_changed);
wire_drag(
window,
&ctl,
&catalog,
&visible_ids,
&on_scope_changed,
&session,
);
wire_trash(window, &ctl, &catalog, &session, &on_scope_changed);
wire_trash_from_grid(
window,
&ctl,
&catalog,
&session,
&on_scope_changed,
&visible_ids,
);
wire_tree_navigation(window, &ctl, &catalog, &on_scope_changed);
wire_create(window, &ctl, &catalog);
wire_rename(window, &ctl, &catalog);
wire_remove(window, &ctl, &catalog, &visible_ids, &on_scope_changed);
wire_row_drag(window, &ctl, &catalog);
wire_row_menu(window, &ctl, &catalog, &on_scope_changed, &visible_ids);
wire_membership(window, &ctl, &catalog, &visible_ids, &on_scope_changed);
}
/// Selection.
fn wire_selection(
window: &AppWindow,
ctl: &Rc<CollectionsController>,
catalog: &Rc<RefCell<Option<Catalog>>>,
visible_ids: &Rc<dyn Fn() -> Vec<ImageId>>,
on_scope_changed: &Rc<dyn Fn()>,
) {
{
let weak = window.as_weak();
let ctl = ctl.clone();
@@ -1882,7 +1917,17 @@ pub fn wire<S, R, P, C>(
sync_selection(&w, &ctl, &ids);
});
}
}
/// Selection, continued: turning it into a collection, toggling the
/// mode, and filing it into one that already exists.
fn wire_selection_filing(
window: &AppWindow,
ctl: &Rc<CollectionsController>,
catalog: &Rc<RefCell<Option<Catalog>>>,
visible_ids: &Rc<dyn Fn() -> Vec<ImageId>>,
on_scope_changed: &Rc<dyn Fn()>,
) {
// TRACES: FR-CAT-5
// A collection holding exactly what is selected.
//
@@ -2056,12 +2101,20 @@ pub fn wire<S, R, P, C>(
}
});
}
}
// --- drag --------------------------------------------------------------
//
// Slint owns the gesture (see the preamble). What is left here is the
// payload — the image ids the drop will act on — and the spring.
/// Drag.
///
/// Slint owns the gesture (see the preamble). What is left here is the
/// payload — the image ids the drop will act on — and the spring.
fn wire_drag(
window: &AppWindow,
ctl: &Rc<CollectionsController>,
catalog: &Rc<RefCell<Option<Catalog>>>,
visible_ids: &Rc<dyn Fn() -> Vec<ImageId>>,
on_scope_changed: &Rc<dyn Fn()>,
session: &Rc<dyn Fn() -> Option<dr_sync::Connection>>,
) {
// TRACES: FR-CAT-7
// **What arms the drag, not what it carries.**
//
@@ -2335,13 +2388,21 @@ pub fn wire<S, R, P, C>(
}
});
}
}
// --- trash -------------------------------------------------------------
//
// TRACES: FR-CAT-15
// A drop here is a *soft delete*: the file moves to a trash folder on the
// server and the catalog records where it came from. Nothing is destroyed
// until the user empties it, which is a separate, deliberate action.
/// Trash.
///
/// TRACES: FR-CAT-15
/// A drop here is a *soft delete*: the file moves to a trash folder on the
/// server and the catalog records where it came from. Nothing is destroyed
/// until the user empties it, which is a separate, deliberate action.
fn wire_trash(
window: &AppWindow,
ctl: &Rc<CollectionsController>,
catalog: &Rc<RefCell<Option<Catalog>>>,
session: &Rc<dyn Fn() -> Option<dr_sync::Connection>>,
on_scope_changed: &Rc<dyn Fn()>,
) {
{
let weak = window.as_weak();
let ctl = ctl.clone();
@@ -2379,19 +2440,27 @@ pub fn wire<S, R, P, C>(
start_restore(&w, &ctl, &catalog, &session, &chosen, &reload);
});
}
}
// --- trash from the grid ----------------------------------------------
//
// Until now the only route to the trash was dragging onto the sidebar row.
// These two are the direct gestures: the trash target on a cell's rating
// strip, and the `Delete` key.
//
// Both land here rather than in `library_ui` because everything the
// operation needs — the selection, the session closure, `start_trash` and
// its drain — already lives in this module. Reaching them from the grid
// side would mean either duplicating the worker plumbing or moving it, and
// trash is one feature whichever component happens to trigger it.
/// Trash from the grid.
///
/// Until now the only route to the trash was dragging onto the sidebar row.
/// These two are the direct gestures: the trash target on a cell's rating
/// strip, and the `Delete` key.
///
/// Both land here rather than in `library_ui` because everything the
/// operation needs — the selection, the session closure, `start_trash` and
/// its drain — already lives in this module. Reaching them from the grid
/// side would mean either duplicating the worker plumbing or moving it, and
/// trash is one feature whichever component happens to trigger it.
fn wire_trash_from_grid(
window: &AppWindow,
ctl: &Rc<CollectionsController>,
catalog: &Rc<RefCell<Option<Catalog>>>,
session: &Rc<dyn Fn() -> Option<dr_sync::Connection>>,
on_scope_changed: &Rc<dyn Fn()>,
visible_ids: &Rc<dyn Fn() -> Vec<ImageId>>,
) {
// The trash glyph on one cell. Acts on that photograph alone: the pointer
// named it, and a click that silently trashed an entire selection would be
// exactly the trap the strip's other targets are laid out to avoid.
@@ -2494,8 +2563,15 @@ pub fn wire<S, R, P, C>(
);
});
}
}
// --- tree navigation ---------------------------------------------------
/// Tree navigation.
fn wire_tree_navigation(
window: &AppWindow,
ctl: &Rc<CollectionsController>,
catalog: &Rc<RefCell<Option<Catalog>>>,
on_scope_changed: &Rc<dyn Fn()>,
) {
{
let weak = window.as_weak();
let ctl = ctl.clone();
@@ -2554,8 +2630,14 @@ pub fn wire<S, R, P, C>(
}
});
}
}
// --- create ------------------------------------------------------------
/// Create.
fn wire_create(
window: &AppWindow,
ctl: &Rc<CollectionsController>,
catalog: &Rc<RefCell<Option<Catalog>>>,
) {
{
let weak = window.as_weak();
let ctl = ctl.clone();
@@ -2574,14 +2656,19 @@ pub fn wire<S, R, P, C>(
create_child(&w, &ctl, cat, *ctl.scope.borrow());
});
}
}
// --- rename ------------------------------------------------------------
//
// Inline in the row, opened by a double-click or `F2`. The gesture is worth
// the field rather than a dialog: renaming is how a hierarchy gets tidied,
// and it is done in runs of several — a modal per collection would make
// that a chore.
/// Rename.
///
/// Inline in the row, opened by a double-click or `F2`. The gesture is worth
/// the field rather than a dialog: renaming is how a hierarchy gets tidied,
/// and it is done in runs of several — a modal per collection would make
/// that a chore.
fn wire_rename(
window: &AppWindow,
ctl: &Rc<CollectionsController>,
catalog: &Rc<RefCell<Option<Catalog>>>,
) {
{
let weak = window.as_weak();
let ctl = ctl.clone();
@@ -2656,8 +2743,16 @@ pub fn wire<S, R, P, C>(
w.set_collection_error(slint::SharedString::new());
});
}
}
// --- remove from the collection being shown ---------------------------
/// Remove from the collection being shown.
fn wire_remove(
window: &AppWindow,
ctl: &Rc<CollectionsController>,
catalog: &Rc<RefCell<Option<Catalog>>>,
visible_ids: &Rc<dyn Fn() -> Vec<ImageId>>,
on_scope_changed: &Rc<dyn Fn()>,
) {
{
let weak = window.as_weak();
let ctl = ctl.clone();
@@ -2697,7 +2792,15 @@ pub fn wire<S, R, P, C>(
}
});
}
}
/// The tree row's own hold-drag: lift a row, drag it onto another to
/// reparent, or onto the root to un-nest it.
fn wire_row_drag(
window: &AppWindow,
ctl: &Rc<CollectionsController>,
catalog: &Rc<RefCell<Option<Catalog>>>,
) {
// TRACES: FR-CAT-7 | FR-UI-2 | FR-UI-3 | FR-UI-4
// The hold on a sidebar row: it arms the drag that rearranges the tree,
// and it opens the row menu. Which one the user gets is decided on release
@@ -2836,15 +2939,23 @@ pub fn wire<S, R, P, C>(
}
});
}
}
// --- the row menu ------------------------------------------------------
//
// Right-click, or a long press on touch. This gesture used to *delete an
// empty collection outright* and refuse with an error message otherwise,
// which was wrong in both directions at once: the destructive half fired
// with no confirmation and nothing on screen said it would, and the
// refusing half meant a collection holding anything could not be deleted
// at all — the user emptied it by hand and then did the same thing.
/// The row menu.
///
/// Right-click, or a long press on touch. This gesture used to *delete an
/// empty collection outright* and refuse with an error message otherwise,
/// which was wrong in both directions at once: the destructive half fired
/// with no confirmation and nothing on screen said it would, and the
/// refusing half meant a collection holding anything could not be deleted
/// at all — the user emptied it by hand and then did the same thing.
fn wire_row_menu(
window: &AppWindow,
ctl: &Rc<CollectionsController>,
catalog: &Rc<RefCell<Option<Catalog>>>,
on_scope_changed: &Rc<dyn Fn()>,
visible_ids: &Rc<dyn Fn() -> Vec<ImageId>>,
) {
{
let weak = window.as_weak();
let ctl = ctl.clone();
@@ -3044,8 +3155,16 @@ pub fn wire<S, R, P, C>(
close_row_menu(&w, &ctl);
});
}
}
// --- where the selection is filed --------------------------------------
/// Where the selection is filed.
fn wire_membership(
window: &AppWindow,
ctl: &Rc<CollectionsController>,
catalog: &Rc<RefCell<Option<Catalog>>>,
visible_ids: &Rc<dyn Fn() -> Vec<ImageId>>,
on_scope_changed: &Rc<dyn Fn()>,
) {
{
let weak = window.as_weak();
let ctl = ctl.clone();