Merge: the collections, identity, merge, launch and import wiring lifted into named functions
This commit is contained in:
+166
-47
@@ -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();
|
||||
|
||||
+156
-40
@@ -582,6 +582,20 @@ fn push_grouping(window: &AppWindow, settings: &crate::settings_ui::SettingsCont
|
||||
window.set_identity_min_group_size(s.faces.min_group_size as i32);
|
||||
}
|
||||
|
||||
macro_rules! reload {
|
||||
($w:expr, $ctl:expr, $catalog:expr, $store:expr, $settings:expr, $eyes:expr, $changed:ident) => {
|
||||
refresh(
|
||||
&$w,
|
||||
&$ctl,
|
||||
&$catalog,
|
||||
$store(),
|
||||
&model_id(&$settings),
|
||||
$eyes(),
|
||||
Changed::$changed,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
/// Attach every Identity callback.
|
||||
///
|
||||
/// Eight arguments because the screen has eight distinct dependencies and no
|
||||
@@ -616,10 +630,69 @@ pub fn wire<S, M, P>(
|
||||
let models: Rc<dyn Fn() -> Option<ModelPaths>> = Rc::new(models);
|
||||
let paths: Rc<dyn Fn() -> Option<SweepPaths>> = Rc::new(paths);
|
||||
|
||||
// The dials start where the settings file left them, once, rather than on
|
||||
// every open: the screen writes them back through the two callbacks below,
|
||||
// and re-pushing them mid-drag would fight the slider's own live value.
|
||||
push_grouping(window, &settings);
|
||||
// Re-read everything and redraw. Every mutating callback ends in this
|
||||
// rather than patching the model in place: the operations here have
|
||||
// second-order effects — a merge empties a person, a split creates one,
|
||||
// a rejection changes two counts — and a model patched by hand would
|
||||
// drift from the catalog in exactly the cases that matter.
|
||||
// Whether the eye models are on this machine, asked each time rather
|
||||
// than once: it is two `is_file` checks, and a user who drops the files
|
||||
// in while the app is open should see the faces become work to measure.
|
||||
let eyes_available: Rc<dyn Fn() -> bool> = {
|
||||
let models = models.clone();
|
||||
Rc::new(move || models().is_some_and(|m| m.eyes.is_some()))
|
||||
};
|
||||
// The availability closure is an argument rather than named in the
|
||||
// body: macro hygiene would bind the name to this scope's `Rc`, which the
|
||||
// first `move` closure would then take with it.
|
||||
// The last argument is a `Changed` variant, named bare so the call
|
||||
// stays on one line.
|
||||
|
||||
wire_dials(window, &settings);
|
||||
|
||||
wire_navigation(
|
||||
window,
|
||||
&ctl,
|
||||
&catalog,
|
||||
&store,
|
||||
&models,
|
||||
&settings,
|
||||
&eyes_available,
|
||||
);
|
||||
wire_rename_and_merge(window, &ctl, &catalog, &store, &settings, &eyes_available);
|
||||
wire_face_actions(window, &ctl, &catalog, &store, &settings, &eyes_available);
|
||||
wire_grouping_preview(window, &ctl, &paths, &settings);
|
||||
wire_recluster(
|
||||
window,
|
||||
&ctl,
|
||||
&catalog,
|
||||
&store,
|
||||
&paths,
|
||||
&settings,
|
||||
&eyes_available,
|
||||
);
|
||||
wire_indexing(
|
||||
window,
|
||||
&ctl,
|
||||
&catalog,
|
||||
&activity,
|
||||
&store,
|
||||
&models,
|
||||
&paths,
|
||||
&gpu,
|
||||
&settings,
|
||||
&eyes_available,
|
||||
);
|
||||
wire_coverage_and_ignore(window, &ctl, &catalog, &store, &settings, &eyes_available);
|
||||
}
|
||||
|
||||
/// The dials.
|
||||
///
|
||||
/// The dials start where the settings file left them, once, rather than on
|
||||
/// every open: the screen writes them back through the two callbacks below,
|
||||
/// and re-pushing them mid-drag would fight the slider's own live value.
|
||||
fn wire_dials(window: &AppWindow, settings: &Rc<crate::settings_ui::SettingsController>) {
|
||||
push_grouping(window, settings);
|
||||
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
@@ -645,38 +718,18 @@ pub fn wire<S, M, P>(
|
||||
w.set_identity_grouping_preview(Default::default());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Re-read everything and redraw. Every mutating callback ends in this
|
||||
// rather than patching the model in place: the operations here have
|
||||
// second-order effects — a merge empties a person, a split creates one,
|
||||
// a rejection changes two counts — and a model patched by hand would
|
||||
// drift from the catalog in exactly the cases that matter.
|
||||
// Whether the eye models are on this machine, asked each time rather
|
||||
// than once: it is two `is_file` checks, and a user who drops the files
|
||||
// in while the app is open should see the faces become work to measure.
|
||||
let eyes_available: Rc<dyn Fn() -> bool> = {
|
||||
let models = models.clone();
|
||||
Rc::new(move || models().is_some_and(|m| m.eyes.is_some()))
|
||||
};
|
||||
// The availability closure is an argument rather than named in the
|
||||
// body: macro hygiene would bind the name to this scope's `Rc`, which the
|
||||
// first `move` closure would then take with it.
|
||||
// The last argument is a `Changed` variant, named bare so the call
|
||||
// stays on one line.
|
||||
macro_rules! reload {
|
||||
($w:expr, $ctl:expr, $catalog:expr, $store:expr, $settings:expr, $eyes:expr, $changed:ident) => {
|
||||
refresh(
|
||||
&$w,
|
||||
&$ctl,
|
||||
&$catalog,
|
||||
$store(),
|
||||
&model_id(&$settings),
|
||||
$eyes(),
|
||||
Changed::$changed,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
/// Opening and closing the screen, and switching which person is shown.
|
||||
fn wire_navigation(
|
||||
window: &AppWindow,
|
||||
ctl: &Rc<IdentityController>,
|
||||
catalog: &Rc<RefCell<Option<Catalog>>>,
|
||||
store: &Rc<dyn Fn() -> Option<Rc<ThumbStore>>>,
|
||||
models: &Rc<dyn Fn() -> Option<ModelPaths>>,
|
||||
settings: &Rc<crate::settings_ui::SettingsController>,
|
||||
eyes_available: &Rc<dyn Fn() -> bool>,
|
||||
) {
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let eyes_available = eyes_available.clone();
|
||||
@@ -742,7 +795,17 @@ pub fn wire<S, M, P>(
|
||||
reset_name_field(&w);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Renaming a person, and the namesake merge offer a rename can raise.
|
||||
fn wire_rename_and_merge(
|
||||
window: &AppWindow,
|
||||
ctl: &Rc<IdentityController>,
|
||||
catalog: &Rc<RefCell<Option<Catalog>>>,
|
||||
store: &Rc<dyn Fn() -> Option<Rc<ThumbStore>>>,
|
||||
settings: &Rc<crate::settings_ui::SettingsController>,
|
||||
eyes_available: &Rc<dyn Fn() -> bool>,
|
||||
) {
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let eyes_available = eyes_available.clone();
|
||||
@@ -850,7 +913,17 @@ pub fn wire<S, M, P>(
|
||||
clear_merge_offer(&w, &ctl);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// The grid: picking, confirming, rejecting and splitting off faces.
|
||||
fn wire_face_actions(
|
||||
window: &AppWindow,
|
||||
ctl: &Rc<IdentityController>,
|
||||
catalog: &Rc<RefCell<Option<Catalog>>>,
|
||||
store: &Rc<dyn Fn() -> Option<Rc<ThumbStore>>>,
|
||||
settings: &Rc<crate::settings_ui::SettingsController>,
|
||||
eyes_available: &Rc<dyn Fn() -> bool>,
|
||||
) {
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let eyes_available = eyes_available.clone();
|
||||
@@ -970,7 +1043,15 @@ pub fn wire<S, M, P>(
|
||||
reload!(w, ctl, catalog, store, settings, eyes_available, Identities);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// A dry run of the grouping dials, without writing anything.
|
||||
fn wire_grouping_preview(
|
||||
window: &AppWindow,
|
||||
ctl: &Rc<IdentityController>,
|
||||
paths: &Rc<dyn Fn() -> Option<SweepPaths>>,
|
||||
settings: &Rc<crate::settings_ui::SettingsController>,
|
||||
) {
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
@@ -1033,7 +1114,18 @@ pub fn wire<S, M, P>(
|
||||
park_preview_timer(timer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Regroup: rerun clustering over faces already indexed.
|
||||
fn wire_recluster(
|
||||
window: &AppWindow,
|
||||
ctl: &Rc<IdentityController>,
|
||||
catalog: &Rc<RefCell<Option<Catalog>>>,
|
||||
store: &Rc<dyn Fn() -> Option<Rc<ThumbStore>>>,
|
||||
paths: &Rc<dyn Fn() -> Option<SweepPaths>>,
|
||||
settings: &Rc<crate::settings_ui::SettingsController>,
|
||||
eyes_available: &Rc<dyn Fn() -> bool>,
|
||||
) {
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
@@ -1135,12 +1227,26 @@ pub fn wire<S, M, P>(
|
||||
park_timer(timer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// One launcher behind two buttons. "Index faces" and "Re-index every
|
||||
// face" differ only in which images the pass visits (`FaceSweepScope`);
|
||||
// the models, the progress, the activity row and the Stop button are the
|
||||
// same, and a second copy of this closure would be a second place for
|
||||
// them to disagree.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
/// One launcher behind two buttons. "Index faces" and "Re-index every
|
||||
/// face" differ only in which images the pass visits (`FaceSweepScope`);
|
||||
/// the models, the progress, the activity row and the Stop button are the
|
||||
/// same, and a second copy of this closure would be a second place for
|
||||
/// them to disagree.
|
||||
fn wire_indexing(
|
||||
window: &AppWindow,
|
||||
ctl: &Rc<IdentityController>,
|
||||
catalog: &Rc<RefCell<Option<Catalog>>>,
|
||||
activity: &Rc<crate::activity::ActivityLog>,
|
||||
store: &Rc<dyn Fn() -> Option<Rc<ThumbStore>>>,
|
||||
models: &Rc<dyn Fn() -> Option<ModelPaths>>,
|
||||
paths: &Rc<dyn Fn() -> Option<SweepPaths>>,
|
||||
gpu: &Rc<Option<dr_gpu::GpuContext>>,
|
||||
settings: &Rc<crate::settings_ui::SettingsController>,
|
||||
eyes_available: &Rc<dyn Fn() -> bool>,
|
||||
) {
|
||||
let launch: Rc<dyn Fn(crate::repairs::Scope)> = {
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
@@ -1347,7 +1453,17 @@ pub fn wire<S, M, P>(
|
||||
reload!(w, ctl, catalog, store, settings, eyes_available, Library);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Coverage, setting a person aside, and clearing every person and face.
|
||||
fn wire_coverage_and_ignore(
|
||||
window: &AppWindow,
|
||||
ctl: &Rc<IdentityController>,
|
||||
catalog: &Rc<RefCell<Option<Catalog>>>,
|
||||
store: &Rc<dyn Fn() -> Option<Rc<ThumbStore>>>,
|
||||
settings: &Rc<crate::settings_ui::SettingsController>,
|
||||
eyes_available: &Rc<dyn Fn() -> bool>,
|
||||
) {
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let catalog = catalog.clone();
|
||||
|
||||
@@ -310,7 +310,17 @@ where
|
||||
let context = Rc::new(context);
|
||||
let on_imported = Rc::new(on_imported);
|
||||
|
||||
// --- opening and closing ---------------------------------------------
|
||||
wire_opening_and_closing(window, &ctl, &context);
|
||||
wire_choosing_a_source(window, &ctl, &context);
|
||||
wire_options(window, &ctl);
|
||||
wire_running(window, &ctl, &context, &on_imported);
|
||||
}
|
||||
|
||||
/// Opening and closing.
|
||||
fn wire_opening_and_closing<C>(window: &AppWindow, ctl: &Rc<ImportController>, context: &Rc<C>)
|
||||
where
|
||||
C: Fn() -> Option<Context> + 'static,
|
||||
{
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
@@ -351,8 +361,13 @@ where
|
||||
w.set_show_import(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// --- choosing a source -------------------------------------------------
|
||||
/// Choosing a source.
|
||||
fn wire_choosing_a_source<C>(window: &AppWindow, ctl: &Rc<ImportController>, context: &Rc<C>)
|
||||
where
|
||||
C: Fn() -> Option<Context> + 'static,
|
||||
{
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
@@ -395,8 +410,10 @@ where
|
||||
render(&w, &ctl);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// --- options -----------------------------------------------------------
|
||||
/// Options.
|
||||
fn wire_options(window: &AppWindow, ctl: &Rc<ImportController>) {
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
@@ -465,8 +482,18 @@ where
|
||||
render(&w, &ctl);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// --- running -----------------------------------------------------------
|
||||
/// Running.
|
||||
fn wire_running<C, F>(
|
||||
window: &AppWindow,
|
||||
ctl: &Rc<ImportController>,
|
||||
context: &Rc<C>,
|
||||
on_imported: &Rc<F>,
|
||||
) where
|
||||
C: Fn() -> Option<Context> + 'static,
|
||||
F: Fn() + 'static,
|
||||
{
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
|
||||
+44
-16
@@ -91,7 +91,19 @@ pub fn wire<F>(window: &AppWindow, controller: Rc<LaunchController>, on_open_lib
|
||||
where
|
||||
F: Fn(Account) + 'static,
|
||||
{
|
||||
// --- sign in -------------------------------------------------------
|
||||
wire_sign_in(window, &controller);
|
||||
wire_use_folder(window, &controller);
|
||||
wire_sign_out(window, &controller);
|
||||
wire_formats(window, &controller);
|
||||
wire_choose_folder_and_open(window, &controller, on_open_library);
|
||||
wire_folder_picker_navigation(window, &controller);
|
||||
wire_copy_url(window, &controller);
|
||||
|
||||
render(window, &controller);
|
||||
}
|
||||
|
||||
/// Sign in: the browser flow, and the app-password fallback.
|
||||
fn wire_sign_in(window: &AppWindow, controller: &Rc<LaunchController>) {
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = controller.clone();
|
||||
@@ -117,7 +129,7 @@ where
|
||||
});
|
||||
}
|
||||
|
||||
// --- sign in with an app password -----------------------------------
|
||||
// Sign in with an app password.
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = controller.clone();
|
||||
@@ -144,13 +156,15 @@ where
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// --- use a folder ---------------------------------------------------
|
||||
//
|
||||
// No thread, no waiting state, no credential: the whole sign-in is a
|
||||
// `stat`. That asymmetry with the browser flow above is not a special
|
||||
// case in the screen — it is what [`SignIn::EndpointOnly`] means, and any
|
||||
// future connector declaring it lands here rather than in new code.
|
||||
/// Use a folder.
|
||||
///
|
||||
/// No thread, no waiting state, no credential: the whole sign-in is a
|
||||
/// `stat`. That asymmetry with the browser flow above is not a special
|
||||
/// case in the screen — it is what [`SignIn::EndpointOnly`] means, and any
|
||||
/// future connector declaring it lands here rather than in new code.
|
||||
fn wire_use_folder(window: &AppWindow, controller: &Rc<LaunchController>) {
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = controller.clone();
|
||||
@@ -166,8 +180,10 @@ where
|
||||
render(&w, &ctl);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// --- sign out ------------------------------------------------------
|
||||
/// Sign out.
|
||||
fn wire_sign_out(window: &AppWindow, controller: &Rc<LaunchController>) {
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = controller.clone();
|
||||
@@ -186,8 +202,10 @@ where
|
||||
render(&w, &ctl);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// --- format tick-boxes ---------------------------------------------
|
||||
/// Format tick-boxes.
|
||||
fn wire_formats(window: &AppWindow, controller: &Rc<LaunchController>) {
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = controller.clone();
|
||||
@@ -210,8 +228,16 @@ where
|
||||
render(&w, &ctl);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// --- choose folder --------------------------------------------------
|
||||
/// Choose folder, and open library.
|
||||
fn wire_choose_folder_and_open<F>(
|
||||
window: &AppWindow,
|
||||
controller: &Rc<LaunchController>,
|
||||
on_open_library: F,
|
||||
) where
|
||||
F: Fn(Account) + 'static,
|
||||
{
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = controller.clone();
|
||||
@@ -223,7 +249,7 @@ where
|
||||
});
|
||||
}
|
||||
|
||||
// --- open library ---------------------------------------------------
|
||||
// Open library.
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = controller.clone();
|
||||
@@ -236,8 +262,10 @@ where
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// --- folder picker navigation ---------------------------------------
|
||||
/// Folder picker navigation.
|
||||
fn wire_folder_picker_navigation(window: &AppWindow, controller: &Rc<LaunchController>) {
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = controller.clone();
|
||||
@@ -296,8 +324,10 @@ where
|
||||
render(&w, &ctl);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// --- copy the login URL ---------------------------------------------
|
||||
/// Copy the login URL.
|
||||
fn wire_copy_url(window: &AppWindow, controller: &Rc<LaunchController>) {
|
||||
{
|
||||
let ctl = controller.clone();
|
||||
window.on_launch_copy_url(move || {
|
||||
@@ -310,8 +340,6 @@ where
|
||||
log::info!("login url: {url}");
|
||||
});
|
||||
}
|
||||
|
||||
render(window, &controller);
|
||||
}
|
||||
|
||||
/// TRACES: FR-NC-13
|
||||
|
||||
@@ -92,11 +92,28 @@ pub fn wire<S, C, F>(
|
||||
S: Fn() -> Vec<crate::export::Source> + 'static,
|
||||
C: Fn() -> Option<Context> + 'static,
|
||||
F: Fn(&AppWindow) + 'static,
|
||||
{
|
||||
wire_start(window, &ctl, gpu, sources, context, on_done);
|
||||
wire_decision(window, &ctl);
|
||||
wire_stop_and_leave(window, &ctl);
|
||||
}
|
||||
|
||||
/// Start.
|
||||
fn wire_start<S, C, F>(
|
||||
window: &AppWindow,
|
||||
ctl: &Rc<MergeController>,
|
||||
gpu: Option<dr_gpu::GpuContext>,
|
||||
sources: S,
|
||||
context: C,
|
||||
on_done: F,
|
||||
) where
|
||||
S: Fn() -> Vec<crate::export::Source> + 'static,
|
||||
C: Fn() -> Option<Context> + 'static,
|
||||
F: Fn(&AppWindow) + 'static,
|
||||
{
|
||||
let on_done = Rc::new(on_done);
|
||||
let gpu_for_start = gpu.clone();
|
||||
|
||||
// --- start -----------------------------------------------------------
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
@@ -243,7 +260,7 @@ pub fn wire<S, C, F>(
|
||||
};
|
||||
start(
|
||||
window,
|
||||
&ctl,
|
||||
ctl,
|
||||
gpu,
|
||||
names,
|
||||
MergeDestination::Local(dir),
|
||||
@@ -265,8 +282,10 @@ pub fn wire<S, C, F>(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- the decision ----------------------------------------------------
|
||||
/// The decision.
|
||||
fn wire_decision(window: &AppWindow, ctl: &Rc<MergeController>) {
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
@@ -360,8 +379,10 @@ pub fn wire<S, C, F>(
|
||||
w.set_merge_stage("Filling the preview".into());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// --- stopping and leaving --------------------------------------------
|
||||
/// Stopping and leaving.
|
||||
fn wire_stop_and_leave(window: &AppWindow, ctl: &Rc<MergeController>) {
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let ctl = ctl.clone();
|
||||
|
||||
Reference in New Issue
Block a user