Merge: the wire sections lifted into named functions, beside the module splits

This commit is contained in:
2026-09-20 18:26:24 +02:00
4 changed files with 252 additions and 95 deletions
+84 -25
View File
@@ -5884,13 +5884,35 @@ pub fn wire<F>(
});
}
// --- the keyboard cursor (FR-CULL-4) -----------------------------------
//
// Walking the grid with the arrows, and opening with `Return`. Together
// with the judgement keys already bound in the grid, this is what makes a
// culling pass a keyboard job: move, rate, move, open the doubtful one,
// come back. A cull is thousands of decisions, and reaching for the mouse
// between each of them is the difference between an hour and an evening.
wire_grid_cursor_and_zoom(window, &ctl, &coll_ctl, &on_open_image);
wire_grid_sync_and_load(window, &ctl);
wire_timeline(window, &ctl);
wire_grid_routes(window, &ctl, &coll_ctl, on_leave_develop);
wire_ratings_and_flags(window, &ctl, &coll_ctl);
wire_keywords(window, &ctl, &coll_ctl);
wire_filter_ratings_and_people(window, &ctl);
wire_filter_dates(window, &ctl);
wire_filter_scope_and_offline(window, &ctl, &coll_ctl);
// Last, and in its own module: the answers to a damaged catalog have
// nothing to do with the library view except that they run before it
// exists.
crate::recovery_ui::wire(window, &ctl, &coll_ctl);
}
/// --- the keyboard cursor (FR-CULL-4) -----------------------------------
///
/// Walking the grid with the arrows, and opening with `Return`. Together
/// with the judgement keys already bound in the grid, this is what makes a
/// culling pass a keyboard job: move, rate, move, open the doubtful one,
/// come back. A cull is thousands of decisions, and reaching for the mouse
/// between each of them is the difference between an hour and an evening.
fn wire_grid_cursor_and_zoom(
window: &AppWindow,
ctl: &Rc<LibraryController>,
coll_ctl: &Rc<crate::collections_ui::CollectionsController>,
on_open_image: &OpenImage,
) {
{
let weak = window.as_weak();
let ctl = ctl.clone();
@@ -6011,7 +6033,11 @@ pub fn wire<F>(
crate::collections_ui::cancel_press(&w, &coll, &ctl.visible_ids());
});
}
}
/// Reload triggers for the grid: an explicit sync or thumbnail sweep, and
/// the geometry and scroll changes that move which rows are loaded.
fn wire_grid_sync_and_load(window: &AppWindow, ctl: &Rc<LibraryController>) {
// Explicit sync, for when the user wants the exchange now rather than
// after the next sweep.
{
@@ -6212,7 +6238,11 @@ pub fn wire<F>(
load_window(&w, &ctl);
});
}
}
/// The capture-time sidebar: panning, zooming and scrubbing the axis that
/// the grid's loaded window follows.
fn wire_timeline(window: &AppWindow, ctl: &Rc<LibraryController>) {
// Panning the timeline slides the visible span without changing its width.
{
let weak = window.as_weak();
@@ -6320,7 +6350,16 @@ pub fn wire<F>(
apply_zoom(&w, &ctl, steps);
});
}
}
/// Moving between the grid and the screens around it: the launch screen,
/// develop, and a manual rescan.
fn wire_grid_routes(
window: &AppWindow,
ctl: &Rc<LibraryController>,
coll_ctl: &Rc<crate::collections_ui::CollectionsController>,
on_leave_develop: Rc<dyn Fn()>,
) {
// Grid → launch screen. The route that was missing: once past the launch
// screen there was no way back to it, so a library pointed at the wrong
// folder could not be changed without clearing stored state by hand.
@@ -6379,9 +6418,14 @@ pub fn wire<F>(
start_rescan(&w, &ctl, &coll_ctl);
});
}
}
// --- ratings and flags (FR-CAT-5, FR-CULL-4) --------------------------
/// --- ratings and flags (FR-CAT-5, FR-CULL-4) --------------------------
fn wire_ratings_and_flags(
window: &AppWindow,
ctl: &Rc<LibraryController>,
coll_ctl: &Rc<crate::collections_ui::CollectionsController>,
) {
// Clicking a star rates *that cell*, not the selection. The pointer names
// one photograph unambiguously, and a click that silently rated forty
// others would be a trap — the keyboard is the bulk gesture.
@@ -6483,13 +6527,19 @@ pub fn wire<F>(
}
});
}
}
// --- keywords (FR-CAT-5, FR-CAT-6) ------------------------------------
//
// Three callbacks and no state of their own: the sheet's open/shut is local
// to the `.slint` file, and what a keyword applies to is the grid selection
// the collections controller already owns. A second copy of either here is
// a second thing that can disagree with the first.
/// --- keywords (FR-CAT-5, FR-CAT-6) ------------------------------------
///
/// Three callbacks and no state of their own: the sheet's open/shut is local
/// to the `.slint` file, and what a keyword applies to is the grid selection
/// the collections controller already owns. A second copy of either here is
/// a second thing that can disagree with the first.
fn wire_keywords(
window: &AppWindow,
ctl: &Rc<LibraryController>,
coll_ctl: &Rc<crate::collections_ui::CollectionsController>,
) {
{
let weak = window.as_weak();
let ctl = ctl.clone();
@@ -6517,12 +6567,14 @@ pub fn wire<F>(
apply_keyword(&w, &ctl, word.as_str(), false);
});
}
}
// --- the filter bar ---------------------------------------------------
//
// Each of these narrows what the grid *queries*, so all three reset the
// scroll offset: the window's position was an ordinal into a different
// set of images and means nothing once the set changes.
/// --- the filter bar ---------------------------------------------------
///
/// Each of these narrows what the grid *queries*, so all three reset the
/// scroll offset: the window's position was an ordinal into a different
/// set of images and means nothing once the set changes.
fn wire_filter_ratings_and_people(window: &AppWindow, ctl: &Rc<LibraryController>) {
{
let weak = window.as_weak();
let ctl = ctl.clone();
@@ -6691,7 +6743,11 @@ pub fn wire<F>(
refilter(&w, &ctl);
});
}
}
/// The date-range filter: turned on from the timeline's own visible span,
/// dragged along the axis, or typed as two dates.
fn wire_filter_dates(window: &AppWindow, ctl: &Rc<LibraryController>) {
// Turn the range on, over the period the histogram is showing.
//
// The range is taken from the timeline rather than typed into two date
@@ -6860,7 +6916,15 @@ pub fn wire<F>(
refilter(&w, &ctl);
});
}
}
/// Whether the grid is scoped to what is available offline, and the
/// prompt those controls open.
fn wire_filter_scope_and_offline(
window: &AppWindow,
ctl: &Rc<LibraryController>,
coll_ctl: &Rc<crate::collections_ui::CollectionsController>,
) {
// TRACES: FR-CAT-9
// "On this device" — the images openable without a server. Composes with
// the rating terms rather than replacing them: "five-star frames I can
@@ -6959,11 +7023,6 @@ pub fn wire<F>(
start_rescan(&w, &ctl, &coll_ctl);
});
}
// Last, and in its own module: the answers to a damaged catalog have
// nothing to do with the library view except that they run before it
// exists.
crate::recovery_ui::wire(window, &ctl, &coll_ctl);
}
/// Reload the grid after the filter changed.
+95 -29
View File
@@ -421,10 +421,26 @@ pub(crate) fn wire(
session: &Rc<RefCell<Option<DevelopSession>>>,
rows: &Rc<VecModel<ParamRow>>,
redraw: &Rc<dyn Fn(&AppWindow)>,
) {
wire_segment(window, session, rows, redraw);
wire_mask_refine(window, session, rows, redraw);
wire_gradient_drag(window, session, redraw);
wire_region_select(window, session, rows, redraw);
wire_stack(window, session, rows, redraw);
wire_edge_treatment(window, session, redraw);
wire_layers_paint(window, session, rows, redraw);
wire_layers_parts(window, session, rows, redraw);
}
/// --- computing the region map -----------------------------------------
fn wire_segment(
window: &AppWindow,
session: &Rc<RefCell<Option<DevelopSession>>>,
rows: &Rc<VecModel<ParamRow>>,
redraw: &Rc<dyn Fn(&AppWindow)>,
) {
let running: Rc<RefCell<Running>> = Rc::default();
// --- computing the region map -----------------------------------------
{
let weak = window.as_weak();
let session = session.clone();
@@ -468,13 +484,20 @@ pub(crate) fn wire(
watch(&w, id, rx, &session, &rows, &redraw, &running);
});
}
}
// --- refining one subject's mask ---------------------------------------
//
// Its own `Running` slot rather than sharing the segmentation one: the
// two answer different questions (the whole frame's subjects versus one
// already-found instance) and there is no reason a refine in flight
// should block a fresh "Find subjects", or the reverse.
/// --- refining one subject's mask ---------------------------------------
///
/// Its own `Running` slot rather than sharing the segmentation one: the
/// two answer different questions (the whole frame's subjects versus one
/// already-found instance) and there is no reason a refine in flight
/// should block a fresh "Find subjects", or the reverse.
fn wire_mask_refine(
window: &AppWindow,
session: &Rc<RefCell<Option<DevelopSession>>>,
rows: &Rc<VecModel<ParamRow>>,
redraw: &Rc<dyn Fn(&AppWindow)>,
) {
let refining: Rc<RefCell<Running>> = Rc::default();
{
let weak = window.as_weak();
@@ -511,16 +534,22 @@ pub(crate) fn wire(
watch_refine(&w, session_id, rx, &session, &rows, &redraw, &running);
});
}
}
// --- dragging a gradient on the photograph ----------------------------
//
// The geometry the gesture started from, held for its duration.
//
// **A drag is applied to where the mask was when the press landed**, not
// to where it was one frame ago. Accumulating frame by frame would let the
// clamps compound — a radius dragged past its limit and back would not
// return to where it started — and would make the result depend on how
// many events the pointer happened to deliver.
/// --- dragging a gradient on the photograph ----------------------------
///
/// The geometry the gesture started from, held for its duration.
///
/// **A drag is applied to where the mask was when the press landed**, not
/// to where it was one frame ago. Accumulating frame by frame would let the
/// clamps compound — a radius dragged past its limit and back would not
/// return to where it started — and would make the result depend on how
/// many events the pointer happened to deliver.
fn wire_gradient_drag(
window: &AppWindow,
session: &Rc<RefCell<Option<DevelopSession>>>,
redraw: &Rc<dyn Fn(&AppWindow)>,
) {
let dragging: Rc<RefCell<Option<dr_pipeline::mask::MaskSource>>> = Rc::new(RefCell::new(None));
{
let weak = window.as_weak();
@@ -571,8 +600,15 @@ pub(crate) fn wire(
}
});
}
}
// --- selecting on the photograph --------------------------------------
/// --- selecting on the photograph --------------------------------------
fn wire_region_select(
window: &AppWindow,
session: &Rc<RefCell<Option<DevelopSession>>>,
rows: &Rc<VecModel<ParamRow>>,
redraw: &Rc<dyn Fn(&AppWindow)>,
) {
{
let weak = window.as_weak();
let session = session.clone();
@@ -596,8 +632,15 @@ pub(crate) fn wire(
redraw(&w);
});
}
}
// --- the stack ---------------------------------------------------------
/// --- the stack ---------------------------------------------------------
fn wire_stack(
window: &AppWindow,
session: &Rc<RefCell<Option<DevelopSession>>>,
rows: &Rc<VecModel<ParamRow>>,
redraw: &Rc<dyn Fn(&AppWindow)>,
) {
{
let weak = window.as_weak();
let session = session.clone();
@@ -724,12 +767,18 @@ pub(crate) fn wire(
redraw(&w);
});
}
}
// --- the edge treatment -------------------------------------------------
//
// All four read one distance field, so all four are live: nothing here
// rebuilds anything except a compound morphology, which `develop` keys on
// separately.
/// --- the edge treatment -------------------------------------------------
///
/// All four read one distance field, so all four are live: nothing here
/// rebuilds anything except a compound morphology, which `develop` keys on
/// separately.
fn wire_edge_treatment(
window: &AppWindow,
session: &Rc<RefCell<Option<DevelopSession>>>,
redraw: &Rc<dyn Fn(&AppWindow)>,
) {
{
let weak = window.as_weak();
let session = session.clone();
@@ -853,13 +902,20 @@ pub(crate) fn wire(
redraw(&w);
});
}
}
// --- adding layers ------------------------------------------------------
// TRACES: FR-DEV-19b
// Painting. Three handlers for one gesture, and the middle one runs per
// pointer report — so it does the least it can: map the point, record it,
// redraw. No sync, because nothing the panel shows changes while a finger
// is down, and rebuilding eight rows a frame is how a brush starts to lag.
/// --- adding layers ------------------------------------------------------
/// TRACES: FR-DEV-19b
/// Painting. Three handlers for one gesture, and the middle one runs per
/// pointer report — so it does the least it can: map the point, record it,
/// redraw. No sync, because nothing the panel shows changes while a finger
/// is down, and rebuilding eight rows a frame is how a brush starts to lag.
fn wire_layers_paint(
window: &AppWindow,
session: &Rc<RefCell<Option<DevelopSession>>>,
rows: &Rc<VecModel<ParamRow>>,
redraw: &Rc<dyn Fn(&AppWindow)>,
) {
{
let weak = window.as_weak();
let session = session.clone();
@@ -1014,6 +1070,16 @@ pub(crate) fn wire(
redraw(&w);
});
}
}
/// Parts of the active mask, and the buttons that add a new layer —
/// gradient, brush, range, category or subject.
fn wire_layers_parts(
window: &AppWindow,
session: &Rc<RefCell<Option<DevelopSession>>>,
rows: &Rc<VecModel<ParamRow>>,
redraw: &Rc<dyn Fn(&AppWindow)>,
) {
// TRACES: FR-DEV-19a
{
let weak = window.as_weak();
+48 -16
View File
@@ -403,14 +403,26 @@ pub fn wire<F, G>(
F: Fn(&Settings) + 'static,
G: Fn(&AppWindow) + 'static,
{
let on_budget_changed = Rc::new(on_budget_changed);
let on_budget_changed: Rc<dyn Fn(&Settings)> = Rc::new(on_budget_changed);
// Run when the page opens, to fill in figures this module does not own —
// the face-indexing coverage, which lives in the catalog. Read here rather
// than kept up to date continuously, because it is only ever looked at
// when this page is on screen and it is two counts and an indexed scan.
let on_open: Rc<dyn Fn(&AppWindow)> = Rc::new(on_open);
// --- opening and closing -------------------------------------------
wire_open_close(window, &controller, &on_open);
wire_cache(window, &controller, &on_budget_changed);
wire_faces(window, &controller, &on_budget_changed);
wire_export(window, &controller);
wire_reset(window, &controller, &on_budget_changed);
}
/// --- opening and closing -------------------------------------------
fn wire_open_close(
window: &AppWindow,
controller: &Rc<SettingsController>,
on_open: &Rc<dyn Fn(&AppWindow)>,
) {
{
let weak = window.as_weak();
let on_open = on_open.clone();
@@ -435,12 +447,18 @@ pub fn wire<F, G>(
w.set_show_settings(false);
});
}
}
// --- cache ---------------------------------------------------------
//
// A budget arrives as typed text. An unparseable entry leaves the previous
// value in place and `render` puts the stored one back in the field, so a
// typo is visibly rejected rather than silently shrinking a cache.
/// --- cache ---------------------------------------------------------
///
/// A budget arrives as typed text. An unparseable entry leaves the previous
/// value in place and `render` puts the stored one back in the field, so a
/// typo is visibly rejected rather than silently shrinking a cache.
fn wire_cache(
window: &AppWindow,
controller: &Rc<SettingsController>,
on_budget_changed: &Rc<dyn Fn(&Settings)>,
) {
{
let weak = window.as_weak();
let ctl = controller.clone();
@@ -546,14 +564,20 @@ pub fn wire<F, G>(
render(&w, &ctl);
});
}
}
// --- faces ---------------------------------------------------------
//
// TRACES: FR-CULL-8
// Notified like a budget change, because the effect is outside this
// page: the library controller carries the pipeline id into the next
// sync, and the coverage line under the picker has to be re-counted
// under the new id — which the caller's `on_budget_changed` does.
/// --- faces ---------------------------------------------------------
///
/// TRACES: FR-CULL-8
/// Notified like a budget change, because the effect is outside this
/// page: the library controller carries the pipeline id into the next
/// sync, and the coverage line under the picker has to be re-counted
/// under the new id — which the caller's `on_budget_changed` does.
fn wire_faces(
window: &AppWindow,
controller: &Rc<SettingsController>,
on_budget_changed: &Rc<dyn Fn(&Settings)>,
) {
{
let weak = window.as_weak();
let ctl = controller.clone();
@@ -568,8 +592,10 @@ pub fn wire<F, G>(
render(&w, &ctl);
});
}
}
// --- export --------------------------------------------------------
/// --- export --------------------------------------------------------
fn wire_export(window: &AppWindow, controller: &Rc<SettingsController>) {
{
let weak = window.as_weak();
let ctl = controller.clone();
@@ -797,8 +823,14 @@ pub fn wire<F, G>(
render(&w, &ctl);
});
}
}
// --- reset ---------------------------------------------------------
/// --- reset ---------------------------------------------------------
fn wire_reset(
window: &AppWindow,
controller: &Rc<SettingsController>,
on_budget_changed: &Rc<dyn Fn(&Settings)>,
) {
{
let weak = window.as_weak();
let ctl = controller.clone();