7ad75dd90586d7a8cd82dffb7b6b8ce4b81f1891
`collections::set_order` and `Sort::CollectionPosition` have been in the catalog since collections were, and nothing above dr-catalog has ever called either. A manual order existed, could not be seen, and could not be set. This is the half that was missing. Three pieces, because it needed all three to be visible at all. The catalog gains `orders_manually` and `members_in_order`. The first is the rule about *when* a manual order means anything, kept in one place with one name: a collection must be manual, and it must have no children. A set shows its descendants' images, and positions are only ever assigned within one collection — so two children's positions are unrelated integers, and ordering by them would sort the grid by a coincidence. The existing comment on `read_cells_scoped` already argued this; now something enforces it. `members_in_order` returns the *whole* membership rather than the filtered view, because `set_order` renumbers exactly what it is handed. Reordering a filtered list would renumber those and leave every hidden image on a stale position — two images sharing one, and a grid that rearranges itself the moment the filter comes off. The grid reads position where the scope qualifies and capture time everywhere else. Manual order joins the member row rather than testing membership with `IN`, which is safe from fanning out rows *because* that branch is a single collection. The gesture is a DropArea over the viewport, drawn only where a reorder means something, with a caret in the gap the photographs would go into — a line between two images rather than a highlight on one, because lighting up a cell would say the drop replaces it. The trap worth naming: `DropEvent.position` is in **window** coordinates. Slint maps it through `map_to_window` when the drag begins and hands every target the same event untranslated, so a target inside a Flickable has to subtract its own `absolute-position`. Getting that wrong is invisible until the grid is scrolled, because at the top the two frames coincide. `reordered` is pure and names its destination by the image it goes before rather than by an index, because the grid can only name a gap in what it is showing and the ids are what survive a window swap. A drop that changes nothing returns the order untouched: that counter is what a cross-device merge resolves by, and spending a revision on a no-op makes this device win an argument it did not have. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DarkRoom
A cross-platform, non-destructive RAW photo editor for Linux and Android.
Status: early. v0.1 is a remote library viewer — see docs/milestone-v0.1.md.
Documentation
| Document | Contents |
|---|---|
| requirements.md | What the software must do — 122 numbered requirements |
| architecture.md | How it is built — crates, GPU pipeline, data model, sync |
| milestone-v0.1.md | The first buildable milestone |
| faces.md | Face detection and identity — the models, the licence problem, and what S14 measures |
Building
Desktop:
cargo run -p darkroom-desktop
Android (containerised toolchain, see docker/android):
./docker/android/build.sh cargo ndk -t arm64-v8a build --release
Current state
Working: workspace, GPU context and compute pass, adaptive Slint shell, Android cross-compilation of the core crates.
Not yet working: the zero-copy display path. The build currently uploads frames through the CPU, which is exactly what ARCH §6.1 forbids — measured at 96% of frame time at 4K. Replacing it is spike S1, the project's highest priority.
cargo run -p dr-gpu --example bench --features readback
reproduces that measurement.
Licence
GPL-3.0-or-later.
Releases
20
DarkRoom 0.24.0
Latest
Languages
Rust
86.1%
Slint
10.3%
Python
1.1%
Shell
1%
WGSL
0.9%
Other
0.6%