Merge: mask a whole category, not just one instance
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> # Conflicts: # apps/darkroom-desktop/Cargo.toml # docs/traceability.md
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { Theme } from "theme.slint";
|
||||
import { AdjustPanel, GeometryPanel, GroupStrip, ParamRow, TransferPanel, ViewMode } from "adjust.slint";
|
||||
import { GradientHandle, GradientHandles, HandleRole, MaskPanel, MaskRow, SubjectRow } from "masks.slint";
|
||||
import { CategoryRow, GradientHandle, GradientHandles, HandleRole, MaskPanel, MaskRow, SubjectRow } from "masks.slint";
|
||||
import { SpotHandle, SpotHandles, SpotPanel, SpotRole } from "spots.slint";
|
||||
import { CropOverlay } from "crop.slint";
|
||||
import { HistoryPanel, HistoryRow } from "history.slint";
|
||||
@@ -985,6 +985,7 @@ export component AppWindow inherits Window {
|
||||
|
||||
in property <[MaskRow]> mask-rows;
|
||||
in property <[SubjectRow]> subject-rows;
|
||||
in property <[CategoryRow]> category-rows;
|
||||
in property <bool> segmented: false;
|
||||
in property <bool> segmenting: false;
|
||||
in property <bool> refining: false;
|
||||
@@ -1068,6 +1069,7 @@ export component AppWindow inherits Window {
|
||||
callback mask-morph-radius-changed(string, float);
|
||||
callback add-gradient-mask(bool);
|
||||
callback add-subject-mask(int);
|
||||
callback add-category-mask(string);
|
||||
|
||||
/// Whether the develop column — image info, geometry, adjust — is shown.
|
||||
///
|
||||
@@ -2510,6 +2512,7 @@ in property <bool> panel-visible: true;
|
||||
enabled: root.adjust-enabled;
|
||||
masks: root.mask-rows;
|
||||
subjects: root.subject-rows;
|
||||
categories: root.category-rows;
|
||||
segmented: root.segmented;
|
||||
segmenting: root.segmenting;
|
||||
refining: root.refining;
|
||||
@@ -2543,6 +2546,7 @@ in property <bool> panel-visible: true;
|
||||
}
|
||||
add-gradient(radial) => { root.add-gradient-mask(radial); }
|
||||
add-subject(i) => { root.add-subject-mask(i); }
|
||||
add-category(n) => { root.add-category-mask(n); }
|
||||
}
|
||||
|
||||
if root.local-mode: Rectangle {
|
||||
|
||||
@@ -70,6 +70,18 @@ export struct SubjectRow {
|
||||
score: float,
|
||||
}
|
||||
|
||||
/// One photographic category the scene model weighed.
|
||||
export struct CategoryRow {
|
||||
/// The descriptor's name — "sky", "vegetation". Carried rather than an
|
||||
/// index because the descriptor is editable and an index would repoint.
|
||||
name: string,
|
||||
label: string,
|
||||
/// 0..1 of the frame. Shown for the same reason a subject's score is: it
|
||||
/// tells the photographer whether the category is worth reaching for
|
||||
/// before they spend a click finding out.
|
||||
coverage: float,
|
||||
}
|
||||
|
||||
component MaskEntry inherits Rectangle {
|
||||
in property <MaskRow> data;
|
||||
in property <bool> enabled: true;
|
||||
@@ -270,6 +282,7 @@ export component MaskPanel inherits Rectangle {
|
||||
in property <bool> enabled: true;
|
||||
in property <[MaskRow]> masks;
|
||||
in property <[SubjectRow]> subjects;
|
||||
in property <[CategoryRow]> categories;
|
||||
|
||||
/// A region map has been computed for this image.
|
||||
in property <bool> segmented: false;
|
||||
@@ -302,6 +315,7 @@ export component MaskPanel inherits Rectangle {
|
||||
|
||||
callback add-gradient(bool);
|
||||
callback add-subject(int);
|
||||
callback add-category(string);
|
||||
|
||||
background: Theme.surface;
|
||||
|
||||
@@ -450,6 +464,55 @@ export component MaskPanel inherits Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
// --- whole categories ---------------------------------------------
|
||||
//
|
||||
// Below the subjects rather than above, and the order is the argument:
|
||||
// clicking the photograph is how a local adjustment usually starts, so
|
||||
// the things that were *found* come first. Categories are the move you
|
||||
// reach for deliberately — grade the sky, not this one bird.
|
||||
if root.enabled && root.segmented && root.categories.length > 0: Caption {
|
||||
text: "Or grade a whole category. One adjustment covers every pixel of it.";
|
||||
wrap: word-wrap;
|
||||
}
|
||||
|
||||
if root.enabled && root.segmented: VerticalLayout {
|
||||
spacing: 0px;
|
||||
for category in root.categories: category-row := TouchArea {
|
||||
height: Theme.touch-target;
|
||||
mouse-cursor: pointer;
|
||||
clicked => { root.add-category(category.name); }
|
||||
|
||||
HorizontalLayout {
|
||||
spacing: Theme.gap-sm;
|
||||
|
||||
Label {
|
||||
text: category.label;
|
||||
emphasised: category-row.has-hover;
|
||||
horizontal-stretch: 1;
|
||||
overflow: elide;
|
||||
// Same layout-time reason as the subject row above:
|
||||
// a Text asks for its whole string's width whether or
|
||||
// not it elides, and the develop column takes the
|
||||
// widest minimum any panel declares.
|
||||
max-width: 160px;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
|
||||
Label {
|
||||
// Coverage as a percentage, which is the unit the
|
||||
// number means something in. A category under half a
|
||||
// percent never reaches this list at all.
|
||||
// A plain Label is already `ink-dim`; only
|
||||
// `emphasised` lifts it, and a coverage readout is
|
||||
// exactly the thing that should not compete with the
|
||||
// name beside it.
|
||||
text: round(category.coverage * 100) + "%";
|
||||
vertical-alignment: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if root.enabled && root.segmented && root.subjects.length == 0: Caption {
|
||||
text: "Nothing recognised. The model knows people, animals and vehicles — "
|
||||
+ "a landscape has no subject for it to find. Add a gradient instead.";
|
||||
|
||||
Reference in New Issue
Block a user