Colour labels could be read from a Lightroom sidecar and queried by the selector, but nothing drew one or set one, so the only labels a library held were ones another program had written. Every mark carries its label's initial on its colour — R, Y, G, B, P — so a label is read without telling red from green, which is what NFR-A11Y-3 asks of colour labels by name. A grid cell shows the mark before its filename. In the grid, 6, 7, 8 and 9 set red, yellow, green and blue as Lightroom's keys do, on the photograph under the pointer or on the selection by the rule the star keys follow; the same key again takes the label off, and over a mixed selection it sets it on all. The selection bar gains Label, which opens the six choices — each a mark and a name — and purple, which has no key, is there. In develop the top bar says "Label: Green" beside the mark, opens the same choices, and 6-9 label the open photograph. Each gesture is one catalog transaction, then the grid, the counts and both sidecars are written as a rating's are. The filter bar gains a chip per label, its mark and its name with a count, one at a time; the filter is one SQL term, travels in the place record, and "All" clears it.
303 lines
12 KiB
YAML
303 lines
12 KiB
YAML
# The source of truth for the UI's colour and length tokens.
|
||
#
|
||
# `build.rs` reads this file and generates `theme.slint` into OUT_DIR at
|
||
# compile time; nothing here is read at runtime unless the `live-style`
|
||
# feature is on. Edit this file, never the generated one.
|
||
#
|
||
# Leaf values only. A token that binds several values into one concept — a
|
||
# panel heading's colour *and* size *and* weight — is a Slint component, not
|
||
# a row in a YAML file (see widgets.slint).
|
||
#
|
||
# Structure:
|
||
# preamble — prose emitted at the top of the generated file
|
||
# colors: — name -> "#RRGGBB", or `alias:` naming another colour token
|
||
# lengths: — name -> pixels
|
||
# Any token may carry `note:` (a `//` comment above it) or `doc:` (a `///`
|
||
# doc comment, which Slint surfaces to editors).
|
||
#
|
||
# Two entries are dividers rather than tokens, because YAML discards the blank
|
||
# lines between map entries and the generated file's grouping has to survive:
|
||
# `section: <title>` — a headed run, optionally with its own `note:`
|
||
# `break: true` — a bare blank line between related tokens
|
||
|
||
preamble: |
|
||
Near-neutral dark palette, achromatic signalling.
|
||
|
||
Two commitments, both about not lying to the photographer.
|
||
|
||
**Dark ground.** A light UI surrounding an image biases how that image is
|
||
judged — the eye adapts to the brightest thing in view, and a white panel
|
||
makes a correctly-exposed photograph look dark.
|
||
|
||
**Near-neutral greys.** The earlier palette was a warm "darkroom safelight"
|
||
brown (ground #14120F, R twelve points above B). That is worse than it
|
||
sounds: simultaneous contrast pushes perception of the image *away* from the
|
||
surround, so a warm chrome makes a neutral photograph read cool, and the
|
||
photographer corrects toward warm to compensate. Every export drifts yellow.
|
||
It is the same reason a print viewing booth is neutral grey rather than
|
||
whatever colour the room happens to be.
|
||
|
||
These greys carry a 2–3 point blue lift rather than being flatly achromatic.
|
||
Pure R=G=B reads as dead to most eyes; a trace of cool reads as instrument
|
||
rather than absence, and biases far less than warmth because the eye is more
|
||
tolerant of a cool surround. The lift is small enough not to matter
|
||
perceptually and deliberate enough not to be mistaken for drift.
|
||
|
||
**No hue in the chrome.** Active and modified states are signalled by
|
||
brightness alone. An accent sitting beside the image competes with it for
|
||
attention and shifts the perception of nearby colours; a photo editor cannot
|
||
afford either. The one exception is `warn-ink` — a caution is genuinely a
|
||
different kind of thing from an active state, and hue is the fastest way to
|
||
say so.
|
||
|
||
colors:
|
||
ground: "#121314"
|
||
surface: "#1B1C1E"
|
||
surface-raised: "#252629"
|
||
rule: "#323438"
|
||
|
||
_inks: { break: true }
|
||
ink: "#EDEEF0"
|
||
ink-dim: "#9EA1A6"
|
||
ink-faint: "#71747A"
|
||
|
||
hover:
|
||
value: "#2E3034"
|
||
note: |
|
||
Interactive states for surfaces. Named rather than written inline so a
|
||
button, a section header and a list row cannot drift apart: hover lifts
|
||
toward the light, press sinks back past the resting surface so the
|
||
control reads as depressed rather than merely lit.
|
||
pressed: "#17181A"
|
||
|
||
_signalling:
|
||
section: signalling
|
||
note: |
|
||
Achromatic, so the separation has to come from luminance. These are
|
||
spaced further apart than a coloured palette would need: with hue
|
||
unavailable, a two-step brightness difference is invisible, and a
|
||
modified marker that cannot be spotted at a glance is not a marker.
|
||
|
||
active:
|
||
value: "#FFFFFF"
|
||
doc: |
|
||
An active or engaged control: a slider fill, a curve line, a checked
|
||
box. Brighter than `ink` so it reads as lit rather than merely present.
|
||
active-dim:
|
||
value: "#C6C9CE"
|
||
doc: Active, at rest — a filled control that is not under the pointer.
|
||
active-pressed:
|
||
value: "#8E9298"
|
||
doc: Active, pressed. Sinks rather than lifts, matching `pressed`.
|
||
|
||
modified:
|
||
alias: active
|
||
note: |
|
||
"This differs from its default." Deliberately the brightest thing in the
|
||
chrome: it is the one piece of state the photographer scans for, and
|
||
with no hue to carry it, brightness is all there is.
|
||
|
||
selected:
|
||
value: "#383B40"
|
||
doc: |
|
||
A selected grid cell. A lifted neutral rather than a tint — distinct
|
||
from `hover` because a multi-selection must stay legible after the
|
||
pointer has moved on, which is the whole point of selecting several
|
||
before dragging them.
|
||
selected-ring:
|
||
value: "#D5D8DD"
|
||
doc: |
|
||
The ring around a selected cell. Brighter than the fill so selection
|
||
survives against a pale thumbnail, where the fill alone would vanish.
|
||
|
||
warn-ink:
|
||
value: "#C9A05A"
|
||
note: |
|
||
Semantic, and the only hue in the palette. A caution is not an active
|
||
state, and it is worth the one exception to say that instantly. Muted
|
||
rather than saturated so it does not shift perception of a nearby image.
|
||
|
||
_plot:
|
||
section: plot series
|
||
note: |
|
||
The histogram's four traces (FR-DSP-7). Hue here is the same exception the
|
||
swatch takes and not a second one: a per-channel histogram has to say
|
||
*which channel*, and no achromatic treatment can distinguish red from blue
|
||
— so the colour is data, exactly as the image beside it is.
|
||
|
||
Held well back from full strength, and darker than `ink`, for the reason
|
||
the theme preamble gives: three saturated traces sitting a few centimetres
|
||
from the photograph would compete with it and shift how its colours read.
|
||
These are legible against `ground` at a glance and no louder than that.
|
||
|
||
plot-red: "#C4626A"
|
||
plot-green: "#6BA867"
|
||
plot-blue: "#5F8CCB"
|
||
|
||
plot-luma:
|
||
value: "#4E5257"
|
||
doc: |
|
||
The luminance trace, drawn filled and behind the three channels. Neutral
|
||
because luminance is not a channel — it is the axis exposure is read off,
|
||
and a hue would imply it were one series among four rather than the one
|
||
the other three are decomposing.
|
||
|
||
_labels:
|
||
section: colour labels
|
||
note: |
|
||
The five colour labels (FR-CAT-5). The same exception the plot series
|
||
take: a label's name *is* a colour, so its mark is filled with it. Held
|
||
to mid-tones so one dark ink reads on every fill — the mark carries the
|
||
label's initial in `ground`, which is what keeps it legible without hue
|
||
(NFR-A11Y-3).
|
||
|
||
label-red: "#D0666E"
|
||
label-yellow: "#D6B84E"
|
||
label-green: "#72B06E"
|
||
label-blue: "#6A96D4"
|
||
label-purple: "#A988D0"
|
||
|
||
lengths:
|
||
_gaps: { break: true }
|
||
gap-sm: 6
|
||
gap: 12
|
||
gap-lg: 20
|
||
|
||
_text: { break: true }
|
||
text-sm: 11
|
||
text: 13
|
||
text-lg: 17
|
||
text-xl: 24
|
||
|
||
radius-sm:
|
||
value: 3
|
||
note: |
|
||
Corner radii. Two steps only: `radius-sm` for things that sit inside
|
||
other things, `radius` for the controls themselves.
|
||
radius: 4
|
||
|
||
touch-target:
|
||
value: 44
|
||
note: "FR-UI-3: minimum 44pt hit target under touch."
|
||
|
||
row-height:
|
||
value: 26
|
||
note: |
|
||
One row of the collections tree, and one level of nesting. Both are
|
||
tokens because a tree's indentation has to stay proportional to its row
|
||
height; hard-coding either makes the hierarchy read wrong when the other
|
||
changes.
|
||
indent: 14
|
||
|
||
control-height:
|
||
value: 28
|
||
note: |
|
||
The drawn height of a button or section header. Deliberately shorter
|
||
than `touch-target` — chrome this tall in every row would crowd the
|
||
photograph — so controls grow their TouchArea past their own bounds to
|
||
meet FR-UI-3 rather than growing their ink.
|
||
control-min-width:
|
||
value: 88
|
||
doc: |
|
||
Floor on button width, so a one-word label is still a comfortable
|
||
target and a row of buttons has an even rhythm.
|
||
|
||
_develop:
|
||
section: the develop view's two fixed columns
|
||
note: |
|
||
Both are *mandated* sizes rather than measured ones, which is the
|
||
opposite of how the rest of this interface is laid out, and the reason
|
||
is that these two flank the photograph. Everything else may grow to fit
|
||
its contents; a column beside the image cannot, because the pixels it
|
||
takes come out of the picture and a control gaining a word is not a
|
||
reason to give the photograph less room.
|
||
|
||
What that costs is that a panel wider than the number below clips, and
|
||
the Flickables inside it are what make the overflow reachable. That is
|
||
the trade being made deliberately: a control you may have to scroll to
|
||
is worse than one you can see, and a photograph that changes size when
|
||
you switch tools is worse than both.
|
||
|
||
rail-width:
|
||
value: 60
|
||
doc: |
|
||
The tool rail down the left of the develop view. Wide enough for a 20px
|
||
icon over a `text-sm` label at the longest name in the table, and narrow
|
||
enough to stay a rail rather than becoming a third panel.
|
||
rail-entry-height:
|
||
value: 54
|
||
doc: |
|
||
One tool in that rail. Over `touch-target`, because unlike the controls
|
||
inside a panel these are not crowding anything — the rail holds four
|
||
entries on a screen with room for a dozen.
|
||
|
||
panel-width:
|
||
value: 360
|
||
doc: |
|
||
The develop column on the other side. One number for tablet and desktop
|
||
alike, replacing the 280 the first was drawn for and the 380 the second
|
||
was — the alternative is a column that changes width with the window,
|
||
which is a photograph that changes size when you resize by a pixel.
|
||
|
||
**Measured, not chosen.** The column's contents report a minimum width of
|
||
344 in every mode — photo, local and repair alike — and below that they
|
||
do not compress, they clip: a `Text` that does not elide reports the same
|
||
minimum as preferred, and most of this column is text. 320 was tried
|
||
first and sliced "Paste" down the middle. So this is that floor plus
|
||
enough not to be sitting on it.
|
||
|
||
To re-measure after changing a panel, bind a `Text` in `app.slint` to
|
||
`column.min-width` and read it off the running app; there is no way to
|
||
get the number out of the layout engine short of asking it.
|
||
|
||
dock-height:
|
||
value: 480
|
||
doc: |
|
||
The same column, docked under the photograph on a tall window (D-N7).
|
||
|
||
Mandated for the reason `panel-width` above is, on the other axis: a dock
|
||
that sizes itself to its contents is a photograph that changes height
|
||
when a caption wraps, and the photograph is the thing being judged.
|
||
|
||
480 is room for the pinned instruments — about 260 of histogram and
|
||
capture metadata, per N4 — with a group of sliders under them, chosen
|
||
against what has to fit *above* it: a 3:2 frame across a 900px dock is
|
||
600 tall, and 480 below it still leaves that frame its height on the
|
||
tablet at either of the two scale factors Android might report.
|
||
|
||
Provisional until N6 measures the device. Every figure behind it was
|
||
computed at a guessed density, and the guess moves the available height
|
||
by 200px.
|
||
|
||
|
||
_roll:
|
||
section: the photo roll
|
||
note: |
|
||
The strip of thumbnails along the foot of the develop view, and the
|
||
handle that pulls it out. Both numbers are tokens rather than private
|
||
properties of `PhotoRoll` because the canvas has to keep out of their
|
||
way: the roll's swipe handler takes every press inside itself, so the
|
||
controls floating over the photograph are positioned against these — a
|
||
button under that band is a button that does nothing.
|
||
|
||
roll-strip:
|
||
value: 108
|
||
doc: |
|
||
The strip's height. Enough for a thumbnail big enough to recognise a
|
||
frame by, and no more: it is drawn over the photograph.
|
||
roll-reach:
|
||
value: 28
|
||
doc: |
|
||
The handle, and the band of canvas left grabbable when the roll is away.
|
||
A thumb's worth, and no more — it is taken off the bottom of the
|
||
photograph.
|
||
|
||
swatch:
|
||
value: 12
|
||
note: |
|
||
The colour square identifying which hue band a row edits. Small on
|
||
purpose: a swatch is data, not chrome — the one sanctioned exception to
|
||
an achromatic palette — and it earns that exception by identifying the
|
||
row without competing with the photograph beside it. Big enough to name
|
||
a hue at a glance, and no bigger.
|