Let a paste carry some kinds of edit and not others

FR-DEV-6 asks for presets "covering a subset of the edit graph". What
landed with the named presets covered two subsets: everything, and
everything but the crop. "Match the colour but not the sharpening" had
no way to be said.

`Scope` is now a set of `Attribute` — the same six kinds every operation
already declares and the develop panel already builds its tabs from. The
photographer ticking "tone and colour" is naming the groups they
navigate by, and neither this module nor the interface has to name an
operation to do it (FR-DEV-3c).

The pleasing part is what left. Framing used to be excluded by an
explicit test against one operation's id; it is now excluded because
Geometry is not in the default set. The special case dissolved into the
general rule, and the argument for it — a crop is a decision about *this*
photograph, and carrying it across forty destroys forty compositions —
is now a statement about a kind of edit rather than about a node. All
thirty-three existing preset tests pass unchanged, which is the evidence
that the generalisation kept its promises.

One decision that is a field rather than a rule, because the two cases
genuinely differ. An operation this build cannot classify — from a newer
version, arriving over sync — travels under "everything" and "everything
but the crop", because those are claims about the whole edit and an
unrecognised operation is part of it (FR-NC-8). It does not travel under
a hand-picked set, because that is a claim about kinds, and an unknown
kind is not one of the kinds that were ticked.

The settings page's "Copy crop and rotation" checkbox is gone, replaced
by the same chips the preset sheet draws. It asked the right first
question — geometry is the kind whose accidental travel destroys work —
but it was the only question a boolean could ask. The field stays in
`Settings`, read exactly once to seed the new set, so anyone who had
ticked it keeps their behaviour.

The chips are deliberately not in the develop column. Six of them there
would set the width of the whole sidebar, which is the bug `ChipGrid`'s
comment records at length.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-29 20:38:19 +02:00
co-authored by Claude Opus 5
parent eb39599f12
commit bb35665bd2
11 changed files with 689 additions and 133 deletions
+39 -17
View File
@@ -1,6 +1,7 @@
import { Theme } from "theme.slint";
import { Button, PanelHeading, Label, Value, Caption, Panel, ProgressBar, ActivityRow } from "widgets.slint";
import { Segmented, TextRow, Check, SliderRow } from "controls.slint";
import { ScopeChips, ScopeKind } from "presets.slint";
// Settings: how much disk the app may spend, and what an export defaults to.
//
@@ -98,8 +99,11 @@ export component SettingsPage inherits Rectangle {
in property <bool> thumbnail-unlimited: false;
in property <bool> keep-opened: true;
/// TRACES: FR-DEV-6
/// Whether copying settings carries the crop and rotation with it.
in property <bool> copy-includes-framing: false;
/// Which kinds of edit a copy or preset carries. Supersedes the boolean
/// above, which named only the one kind anybody wanted to exclude.
in property <[ScopeKind]> copy-scope-kinds;
in property <bool> copy-scope-empty: false;
callback copy-scope-toggled(string);
/// What the cache currently holds. Empty hides the line.
in property <string> cache-usage;
@@ -141,7 +145,6 @@ export component SettingsPage inherits Rectangle {
callback thumbnail-budget-changed(string);
callback thumbnail-unlimited-toggled(bool);
callback keep-opened-toggled(bool);
callback copy-includes-framing-toggled(bool);
// --- library -------------------------------------------------------
/// TRACES: FR-CAT-6
@@ -551,20 +554,39 @@ export component SettingsPage inherits Rectangle {
PanelHeading { text: "DEVELOP"; }
Check {
label: "Copy crop and rotation with settings";
// Says what the *off* state does, because off is
// the default and is the behaviour that needs
// explaining: a user who has never opened this
// page should still be able to predict what a
// paste did to their crops.
hint: "Off, pasting settings changes only colour and "
+ "tone, and each photograph keeps its own "
+ "composition. On, the crop, straightening, "
+ "rotation and flips travel too — which "
+ "re-frames every image pasted onto.";
checked: root.copy-includes-framing;
toggled(on) => { root.copy-includes-framing-toggled(on); }
// TRACES: FR-DEV-6
// Which kinds of edit a copy, a paste or a preset
// carries.
//
// This replaced a single "copy crop and rotation"
// checkbox. That question was the right one to ask
// first — geometry is the kind whose accidental
// travel destroys work — but it was the only question
// a boolean could ask, and "match the colour but not
// the sharpening" had no way to be said.
//
// Chips rather than six checkboxes: these are one
// question with six answers, and a column of ticks
// reads as six unrelated preferences.
VerticalLayout {
spacing: 4px;
Label { text: "Copying carries"; }
ScopeChips {
kinds: root.copy-scope-kinds;
columns: 6;
toggled(name) => { root.copy-scope-toggled(name); }
}
Caption {
text: root.copy-scope-empty
? "Nothing selected — a paste would change nothing."
: "Geometry is off by default: a crop is a decision "
+ "about one photograph's composition, and carrying "
+ "it re-frames every image pasted onto.";
warn: root.copy-scope-empty;
}
}
}
}