// The develop view's own chrome: the strip across the top of the window and // the capture-metadata block in the column beside the canvas. // // Split out of `app.slint`, where they sat above `AppWindow` and were read as // part of the application shell. They are not: neither is instantiated // anywhere but the develop view, and the shell's job — choosing which of the // five screens is up — is easier to read without two unrelated components in // front of it. import { Theme } from "theme.slint"; import { Button, PanelHeading, Label, Value, Caption, Panel } from "widgets.slint"; // Status strip — surfaces the GPU backend and adapter, which matters during // v0.1 because assumption A1 is exactly "does this compositing path work on // this hardware". Seeing the adapter at a glance makes vendor differences // obvious during the S1/S2 spikes. export component StatusBar inherits Rectangle { in property adapter; in property backend; in property layout-class; in property fps; /// FR-UI-1: the layout class, so the instrumentation can stand down on a /// strip too narrow to carry both it and the controls. in property expanded: true; in property filename; in property position; /// Only offered where there is a library to go back to — with files named /// on the command line there is no grid behind this view. in property can-return-to-library: false; /// Whether the develop column is currently shown, for the toggle's label. in property panel-visible: true; /// What the export button says. Rust owns it because the answer depends /// on settings this component does not see — the format, and whether the /// destination is this device or the server. in property export-label: "Export"; in property export-busy: false; /// What the last export did. Sits beside the button rather than in a /// dialogue: an export that succeeded needs no acknowledging, and one /// that failed needs its reason where the retry is. in property export-status; /// Whether the edit can be stepped either way (FR-DEV-5). in property can-undo: false; in property can-redo: false; callback back-to-library(); callback open-people(); callback open-settings(); callback toggle-panel(); callback export-image(); callback undo(); callback redo(); // 44px and `surface`, the same bar the library and settings draw. // // It was 28px — the control height — which was a reasonable size for a // readout and the wrong size for the thing it actually is: this strip // carries the only way out of develop, and it was half the height of the // identical control on the two screens either side of it. Three screens // whose headers do not agree read as three applications, and the one place // the user needs to find a way back was the one drawn smallest. // // Controls keep their own 28px and are centred in it, exactly as the // library header centres the same buttons: a `Button` sets a fixed height, // which a HorizontalLayout parks at the top of the row rather than // centring for it. height: 44px; background: Theme.surface; // Scrolls rather than overflowing. A layout given less width than its // children need does not shrink them — it overruns the edge and reports // the oversized minimum to whatever contains it. Collapsing the // instrumentation below buys back about 200 logical pixels, which is // enough for a tablet; it is not enough for a phone, and nothing here // should be unreachable merely because the window is narrow. Flickable { width: 100%; height: 100%; viewport-height: self.height; viewport-width: max(self.width, strip.preferred-width); strip := HorizontalLayout { width: parent.viewport-width; height: parent.viewport-height; padding-left: Theme.gap; padding-right: Theme.gap; spacing: Theme.gap; alignment: start; // Kept instantiated with `visible` rather than wrapped in an `if`: // the strip is inside the layout that `expanded` feeds, and adding a // conditional child here is the shape that has already caused binding // loops in this file (see the panel below). Button { text: "‹ Library"; y: (parent.height - self.height) / 2; visible: root.can-return-to-library; clicked => { root.back-to-library(); } } // The third screen, reachable from the second. Beside the way back // because it is the same kind of move — leaving this photograph for // somewhere else in the library — and because a mode you can only // reach from one of the other two is not a peer of them, whatever the // navigation model says. Button { text: "Identity"; y: (parent.height - self.height) / 2; visible: root.can-return-to-library; clicked => { root.open-people(); } } Value { text: root.filename; compact: true; overflow: elide; } Caption { text: root.position; } Rectangle { horizontal-stretch: 1; } // --- instrumentation lives in Settings now ------------------------ // // The render backend, the layout class and the frame rate are // developer readouts. They cost about 200 logical pixels of a strip // that also carries the only way out of develop, the undo pair, the // panel toggle and the export button — and a `HorizontalLayout` given // less width than its children's minimums does not shrink them, it // runs off the end. // // A tablet in portrait is 768 logical pixels and was already below the // breakpoint that hid them, so it kept its header. Landscape is 1200 // and is not, so the readouts came back and the header ran off the // right-hand edge there instead — a breakpoint that hides a problem at // one size and not another is a workaround, not a fix. // // The backend, the frame rate and the layout class used to live here. // They are diagnostics — read once when something looks wrong, and // never again — and as permanent furniture in a 44px strip they cost // three slots that the controls beside them actually needed. On a // tablet in landscape the header ran off the right-hand edge, and this // was a third of the reason. // // They are in Settings under ABOUT now, next to the version, which is // where someone goes when they have a bug to report rather than a // photograph to edit. // Undo and redo, in the strip rather than in the develop column: the // panel can be put away, and the one control that takes back a // mis-drag must not go away with it. Disabled rather than hidden, so // the pair keeps its place and the keyboard shortcut has something // visible to correspond to. Button { text: "Undo"; enabled: root.can-undo; y: (parent.height - self.height) / 2; clicked => { root.undo(); } } Button { text: "Redo"; enabled: root.can-redo; y: (parent.height - self.height) / 2; clicked => { root.redo(); } } // Show or hide the develop column. On a tablet the panel is 280px of a // screen that is mostly photograph, and the whole point of opening an // image is to look at it — so being able to put the instruments away // and bring them back is worth a control of its own. The glyph points // the way the panel will move. Button { text: root.panel-visible ? "Hide panel ›" : "‹ Panel"; active: root.panel-visible; y: (parent.height - self.height) / 2; clicked => { root.toggle-panel(); } } Caption { text: root.export-status; vertical-alignment: center; overflow: elide; } // The export itself, beside the settings that shape it. This is the // screen where a photograph is finished, so it is the screen where // one is asked for — the grid can export a selection later, but a // single finished frame is exported from in front of it. Button { text: root.export-busy ? "Exporting…" : root.export-label; enabled: !root.export-busy; y: (parent.height - self.height) / 2; clicked => { root.export-image(); } } // Reachable from develop as well as from the grid: export defaults are // most likely to be wanted with a finished photograph on screen, which // is exactly where this bar is and the library header is not. Button { text: "Settings"; y: (parent.height - self.height) / 2; clicked => { root.open-settings(); } } } } Rectangle { y: parent.height - 1px; height: 1px; background: Theme.rule; } } // Capture metadata. Read-only; the adjustment controls live in AdjustPanel, // which is generated from pipeline capabilities rather than written here. // // Flat, like everything else in this column. This was briefly a collapsible // section so that every panel could be put away; the sidebar as a whole now // closes from the status strip instead, which is the control that was actually // wanted, and the per-group lids only stood between the user and the controls. export component InfoPanel inherits Rectangle { in property camera; in property exposure; in property dimensions; /// TRACES: FR-UI-2 /// How wide this panel has to be before it starts clipping itself. The /// develop column is the largest of these and nothing else; publishing it /// as `min-width` too is what makes the enclosing layout aggregate them /// without this panel having to be referenced by name. out property content-width: panel.preferred-width; min-width: root.content-width; background: transparent; height: panel.preferred-height; panel := Panel { // Flat: this abuts the adjust panel below it and the rule between // them is drawn by the column that stacks the two. flat: true; width: 100%; PanelHeading { text: "IMAGE"; } Value { text: root.camera == "" ? "—" : root.camera; placeholder: root.camera == ""; wrap: word-wrap; } Label { text: root.exposure; } Caption { text: root.dimensions; } } }