Give the develop view's chrome a file of its own
StatusBar and InfoPanel sat above AppWindow in app.slint, which read as though they were part of the application shell. They are not: neither is instantiated anywhere but the develop view, and the shell's actual job -- choosing which of the five screens is up -- is easier to follow without two unrelated components standing in front of it. Moved verbatim to develop.slint, matching src/develop.rs. No behaviour change; app.slint loses 232 lines and gains one import. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+1
-241
@@ -11,251 +11,11 @@ import { CollectionsPanel, CollectionRow, OfflinePrompt } from "collections.slin
|
||||
import { HistogramPanel, HistogramView } from "histogram.slint";
|
||||
import { SettingsPage } from "settings.slint";
|
||||
import { ImportPage } from "import.slint";
|
||||
import { StatusBar, InfoPanel } from "develop.slint";
|
||||
|
||||
export { LibraryCell, TimelineBar, CollectionRow, ActivityRow, HistogramView }
|
||||
export { ViewMode, GradientHandle, HandleRole, SpotHandle, SpotRole }
|
||||
|
||||
// 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.
|
||||
component StatusBar inherits Rectangle {
|
||||
in property <string> adapter;
|
||||
in property <string> backend;
|
||||
in property <string> layout-class;
|
||||
in property <int> 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 <bool> expanded: true;
|
||||
in property <string> filename;
|
||||
in property <string> 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 <bool> can-return-to-library: false;
|
||||
/// Whether the develop column is currently shown, for the toggle's label.
|
||||
in property <bool> 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 <string> export-label: "Export";
|
||||
in property <bool> 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 <string> export-status;
|
||||
/// Whether the edit can be stepped either way (FR-DEV-5).
|
||||
in property <bool> can-undo: false;
|
||||
in property <bool> 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.
|
||||
component InfoPanel inherits Rectangle {
|
||||
in property <string> camera;
|
||||
in property <string> exposure;
|
||||
in property <string> 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 <length> 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; }
|
||||
}
|
||||
}
|
||||
|
||||
export component AppWindow inherits Window {
|
||||
title: "DarkRoom";
|
||||
// The one raster asset in the UI, and the exception that the header of
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
// 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 <string> adapter;
|
||||
in property <string> backend;
|
||||
in property <string> layout-class;
|
||||
in property <int> 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 <bool> expanded: true;
|
||||
in property <string> filename;
|
||||
in property <string> 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 <bool> can-return-to-library: false;
|
||||
/// Whether the develop column is currently shown, for the toggle's label.
|
||||
in property <bool> 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 <string> export-label: "Export";
|
||||
in property <bool> 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 <string> export-status;
|
||||
/// Whether the edit can be stepped either way (FR-DEV-5).
|
||||
in property <bool> can-undo: false;
|
||||
in property <bool> 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 <string> camera;
|
||||
in property <string> exposure;
|
||||
in property <string> 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 <length> 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; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user