// What the develop view is doing, and whether there is anything to do it to. // // **Why the develop panels have globals at all.** Every panel in the develop // column used to declare its inputs and its callbacks and have `app.slint` // bind each one to a property or a callback on the window root. That is fine // while a panel is drawn once. `ui-navigation.md` N9 draws them a second time, // in the portrait dock, and forty lines of hand-written forwarding copied into // a second composition is the kind of duplication that drifts silently: a // callback added to one copy and not the other compiles, renders, and simply // does nothing on the layout nobody was looking at. // // So the wiring moved to globals. A panel reads the global and calls the // global; Rust hooks the global instead of the window; and a second // instantiation is the panel's name and a pair of braces. // // **Why there is more than one of them.** The obvious shape is a single // `Develop` global carrying all of it. Slint rejects cyclic imports, and each // panel's model struct — `ParamRow`, `MaskRow`, `HistogramView` — is declared // in the panel's own file. One global holding `[MaskRow]` and `[ParamRow]` // would therefore have to live in a file importing `masks.slint` and // `adjust.slint`, both of which would import the global back. Breaking that // needs the six model declarations relocated, which is a change to the data // model rather than to the plumbing this is about. // // A global per panel family instead, each declared in the file of the panel it // serves: `Adjustments`, `Framing` and `Transfer` in `adjust.slint`, `Masking` // in `masks.slint`, `Repair` in `spots.slint`, `Peaking` in `peaking.slint`, // `Levels` in `histogram.slint`, `Capture` in `develop.slint`, `Steps` in // `history.slint`. That also lets each name drop the prefix it was carrying // only because the window root is one flat namespace: `root.spot-radius` is // `Repair.radius`, and `root.peaking-on` is `Peaking.showing`. // // This file holds what is left: the two facts every one of those families // needs and none of them owns. It imports nothing, which is what lets every // panel file import it. /// Which mode the develop view is in. /// /// `crop` was a bare `bool` on the window and local masking was a panel with /// two toggles, so nothing stopped both being on at once — and nothing on /// screen said which of them the canvas and the column were obeying. One value /// with three states cannot be in two of them, which is the whole reason this /// is an enum rather than a tidier pair of flags. export enum ViewMode { /// The whole photograph. Sliders are global, the canvas pans and zooms. photo, /// The crop overlay is up and the canvas shows the uncropped frame. crop, /// The region map is drawn, a click on the photograph selects, and the /// column is the mask stack and the selected layer's adjustments. local, /// TRACES: FR-DEV-8 /// The repairs are drawn on the photograph, a click places one, and the /// column describes whichever is selected. /// /// A mode rather than a panel button for the reason the enum exists at /// all: arming a click on the canvas is something only one tool may be /// doing at a time, and two flags could both be true. spots, } /// The develop session, as every panel in the column sees it. /// /// Two facts, and the three readings of the second one. They are here rather /// than in any one family's global because all of them need at least one: /// every panel greys out on `enabled`, and which panels are drawn at all is /// the mode's answer. export global Develop { /// Whether there is an open photograph whose edit can be changed. /// /// Was `adjust-enabled` on the window root. Every panel in the column /// takes it, which is why it is not in any of their globals. in property enabled: false; /// TRACES: FR-UI-5 /// What the develop view is currently doing (see `ViewMode`). /// /// One value rather than a flag per mode, so two modes cannot be on at /// once — which, with `crop-mode` a bare bool beside a masking panel that /// armed the canvas through two toggles of its own, they could be. Rust /// owns it: entering a mode has side effects on the session (crop drops /// the zoom, local clears nothing and leaving it clears the selection), /// and a property the interface could set directly would let the view /// change without them — which is why nothing here assigns it and the /// rail's pick goes through `mode-picked` on the window instead. /// /// In `crop` the canvas shows the *whole* frame — otherwise the area being /// cropped away would not be on screen to drag across — and the surround /// is greyed. /// /// `in-out` because Rust reads it back as well as setting it, to know /// which gesture the canvas is armed for. in-out property view-mode: ViewMode.photo; /// The three readings of the mode, derived once here rather than written /// out at each of the dozen places in `app.slint` that test one. /// /// `out`, so nothing outside can set one of them and leave it disagreeing /// with the mode it is supposed to be a reading of. out property cropping: view-mode == ViewMode.crop; out property local-mode: view-mode == ViewMode.local; /// TRACES: FR-DEV-8 out property repairing: view-mode == ViewMode.spots; }