Files
dtourolle 46f5b95828 Show and set colour labels in the grid and develop, and filter by them
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.
2026-09-24 21:52:23 -04:00

303 lines
12 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.