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.
132 lines
5.0 KiB
Plaintext
132 lines
5.0 KiB
Plaintext
// TRACES: FR-CAT-5 | NFR-A11Y-3
|
|
// Colour labels: the mark that shows one, and the row that chooses one.
|
|
//
|
|
// **A label is named, not only tinted.** NFR-A11Y-3 names colour labels as
|
|
// the first thing that must not be told apart by hue alone, and a colour
|
|
// label is the one status here whose *name* is a colour — so the temptation
|
|
// is exactly a swatch. Every mark therefore carries the label's initial
|
|
// (R, Y, G, B, P) in dark ink on the fill, and every place a label can be
|
|
// chosen or filtered on says its name in words. Red and green are the pair
|
|
// the commonest colour-vision deficiency merges, and they are "R" and "G"
|
|
// here before they are anything else.
|
|
//
|
|
// The integers are `versions.label`'s own codes (`dr_catalog::rating::
|
|
// label_code`): 0 for none, 1 red, 2 yellow, 3 green, 4 blue, 5 purple. One
|
|
// numbering from the column to the pixel, so nothing translates between them.
|
|
|
|
import { Theme } from "theme.slint";
|
|
|
|
/// TRACES: FR-CAT-5 | NFR-A11Y-3
|
|
/// What each label code is called, and the letter its mark carries.
|
|
export global Labels {
|
|
/// Indexed by code. "None" at 0 is what the picker's clearing choice says.
|
|
out property <[string]> names: ["None", "Red", "Yellow", "Green", "Blue", "Purple"];
|
|
/// The glyph on the mark, indexed by code. Distinct for every label: this
|
|
/// is what carries the label for an eye that does not separate the fills.
|
|
out property <[string]> letters: ["", "R", "Y", "G", "B", "P"];
|
|
|
|
/// The fill for a code. Data rather than chrome, the same exception the
|
|
/// histogram's traces take (see `style.yaml`): the label *is* a colour.
|
|
public pure function fill(code: int) -> color {
|
|
if (code == 1) { return Theme.label-red; }
|
|
if (code == 2) { return Theme.label-yellow; }
|
|
if (code == 3) { return Theme.label-green; }
|
|
if (code == 4) { return Theme.label-blue; }
|
|
if (code == 5) { return Theme.label-purple; }
|
|
return transparent;
|
|
}
|
|
}
|
|
|
|
/// TRACES: FR-CAT-5 | NFR-A11Y-3
|
|
/// One label, drawn: a rounded tag in the label's colour with its initial
|
|
/// on it. Invisible for code 0, so a caller can place it unconditionally.
|
|
export component LabelMark inherits Rectangle {
|
|
in property <int> code: 0;
|
|
in property <length> size: 16px;
|
|
|
|
accessible-role: text;
|
|
accessible-label: Labels.names[root.code] + " label";
|
|
|
|
visible: root.code > 0 && root.code <= 5;
|
|
width: root.size;
|
|
height: root.size;
|
|
border-radius: root.size / 4;
|
|
background: Labels.fill(root.code);
|
|
// A rim in the ground colour, so a yellow tag on a pale sky still has an
|
|
// edge.
|
|
border-width: 1px;
|
|
border-color: Theme.ground;
|
|
|
|
Text {
|
|
// The letter is the label. Dark ink on every fill: the fills are held
|
|
// to mid-tones in `style.yaml` so one ink reads on all five.
|
|
text: Labels.letters[root.code];
|
|
color: Theme.ground;
|
|
font-size: root.size * 0.62;
|
|
font-weight: 800;
|
|
width: 100%;
|
|
height: 100%;
|
|
horizontal-alignment: center;
|
|
vertical-alignment: center;
|
|
}
|
|
}
|
|
|
|
/// TRACES: FR-CAT-5 | FR-UI-4 | NFR-A11Y-3
|
|
/// The six choices — none, then the five labels — each a mark and a word.
|
|
///
|
|
/// Inline rather than a popup: it opens in the bar that asked for it, where
|
|
/// a finger already is, and there is no second surface to dismiss.
|
|
export component LabelPicker inherits HorizontalLayout {
|
|
/// The code currently carried, drawn as pressed. -1 where the photographs
|
|
/// being labelled disagree, so nothing is shown as chosen.
|
|
in property <int> current: -1;
|
|
|
|
callback chosen(int);
|
|
|
|
spacing: 4px;
|
|
|
|
for code in [0, 1, 2, 3, 4, 5]: Rectangle {
|
|
accessible-role: button;
|
|
accessible-label: code == 0 ? "No label" : Labels.names[code] + " label";
|
|
accessible-checkable: true;
|
|
accessible-checked: root.current == code;
|
|
accessible-action-default => { root.chosen(code); }
|
|
|
|
height: Theme.control-height - 4px;
|
|
width: row.preferred-width + 2 * Theme.gap-sm;
|
|
y: (parent.height - self.height) / 2;
|
|
border-radius: Theme.radius;
|
|
border-width: 1px;
|
|
border-color: root.current == code ? Theme.active : Theme.rule;
|
|
background: touch.pressed ? Theme.pressed
|
|
: (touch.has-hover ? Theme.hover : Theme.surface-raised);
|
|
|
|
touch := TouchArea {
|
|
width: 100%;
|
|
height: max(parent.height, Theme.touch-target);
|
|
y: (parent.height - self.height) / 2;
|
|
mouse-cursor: pointer;
|
|
clicked => { root.chosen(code); }
|
|
}
|
|
|
|
row := HorizontalLayout {
|
|
padding-left: Theme.gap-sm;
|
|
padding-right: Theme.gap-sm;
|
|
spacing: 4px;
|
|
|
|
if code > 0: LabelMark {
|
|
code: code;
|
|
size: 14px;
|
|
y: (parent.height - self.height) / 2;
|
|
}
|
|
|
|
Text {
|
|
text: Labels.names[code];
|
|
color: Theme.ink;
|
|
font-size: Theme.text-sm;
|
|
vertical-alignment: center;
|
|
}
|
|
}
|
|
}
|
|
}
|