//! TRACES: FR-CAT-7 | FR-UI-5 | NFR-P9 //! The collections sidebar, grid selection, and the drag between them. //! //! `dr_catalog::collections` owns the data rules — hierarchy, membership, //! revisions, cycles. This module owns the *interaction*: what is selected, //! what a drag is carrying, and where a release lands. //! //! # What this module does and does not own //! //! Slint's `DragArea`/`DropArea` own the *gesture* — pointer capture, the //! threshold separating a click from a drag, arbitration against the grid's //! Flickable, the image under the cursor, and hit-testing the release. So the //! drop arrives already addressed to a collection, and nothing here tracks //! pointer positions or guesses a target. //! //! What is left here is what Slint cannot know: //! //! - **the payload** — which images the drag carries, built from the selection //! at the moment the drag starts; //! - **the spring** — a dwell timer that opens a collapsed collection so a //! nested child can be reached mid-drag, and closes again what the drag only //! passed over. //! //! An earlier version hand-rolled the whole gesture on `TouchArea` and did not //! work, for a reason worth keeping: an interactive `Flickable` claims any drag //! starting inside it for scrolling and cancels the child TouchArea's press, so //! the drag could never leave the grid. //! //! # Selection //! //! Selection is by **catalog image id**, never by row index. The grid is a //! window over the catalog (FR-CAT-4) and scrubbing replaces every row, so an //! index-based selection would silently come to mean forty different //! photographs after a scrub. Ids survive that; they also survive a rescan. //! //! # Split into one module per area (docs/dev/code-health.md CH-1) //! //! `controller` holds the running state and the pure drop/delete/release //! decisions; `press` the click-and-release selection policy; `tree_sync` //! rebuilding the sidebar and pushing catalog state into the grid; `drag` the //! cursor bitmap and the spring; `trash` the soft delete; `wiring_grid` and //! `wiring_tree` the callbacks, split in two because together they were the //! largest single piece; and `rename_menu` creating, naming and the row's //! context menu. `mod.rs` re-exports what the rest of the crate calls by //! `collections_ui::` path, so nothing outside this module needs to change. mod controller; mod drag; mod press; mod rename_menu; mod trash; mod tree_sync; mod wiring_grid; mod wiring_tree; pub use controller::CollectionsController; pub use press::{cancel_press, commit_press, select_row}; pub use trash::refresh_trash; pub use tree_sync::{refresh_tree, sync_badges, sync_selection}; pub use wiring_grid::wire; /// Test-only helpers shared by more than one of this module's submodules. /// /// `ids` was defined once in the pre-split file and called from tests all /// over it — both the pure `decide_drop` tests in `controller` and the press /// tests in `press`. Splitting the tests with the code they exercise left it /// needed in both, so it lives here once instead of being copied. #[cfg(test)] pub(super) mod test_support { use dr_types::ImageId; pub(super) fn ids(n: u64) -> Vec { (1..=n).map(ImageId).collect() } }