Let a photographer choose the film, and remember which one
The stock model rendered correctly and nothing could ask for it. This is the picker, and the sidecar key that makes the choice outlive the session. How the choice persists was the open question, and the answer was already written down twice in sidecar.rs: `rating` is a top-level key "because a rating is not an edit", and `masks` are one "because a layer is not a scalar". A stock is that kind of thing -- a choice of material, not a number a slider moves -- so it is a top-level key too. It stores the **id**, not an index. Stocks are files that users add, so an index would mean installing a profile silently changed which film every existing photograph had been developed on. A name this build has no profile for still round-trips untouched, because the alternative is that syncing to an older phone quietly un-develops the picture. Only the names travel. Turning one back into tables needs the profile database, which dr-pipeline deliberately does not link, so `Version::apply` clears the film and the session re-bakes -- after the parameters, because the bake reads the film's own exposure sliders and the print balance is solved against them. That is also why moving those sliders rebuilds the lookup where no other control in the panel does: an enlarger's filtration depends on how the negative was exposed. The panel keeps its rule. It still names no operation and still generates every control from a declared parameter kind; the stock gets a bespoke control beside those, exactly as the mask stack does, and for the same reason. The film's exposure and print exposure arrive as ordinary generated sliders. Two defaults worth stating. Picking a colour negative prints it, because an unprinted one is an orange strip and offering that as the first thing somebody sees after choosing Portra reads as a bug rather than as a choice -- the toggle is there for anyone who wants the scan. And a paste carries no film: a preset is a parameter map, and a stock is not a parameter, so pasting one would paste a choice the clipboard never took. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -876,6 +876,28 @@ export component AdjustPanel inherits Rectangle {
|
||||
callback op-reset(int);
|
||||
callback reset-all();
|
||||
|
||||
// TRACES: FR-DEV-3f
|
||||
// The film stock, which is not a parameter and so is not a `ParamRow`.
|
||||
//
|
||||
// A bespoke control for the same reason the mask panel is one: a stock is
|
||||
// a choice of *material*, named by an id, not a number on a slider. The
|
||||
// generic rows above keep their rule — they still name no operation — and
|
||||
// the film's own exposure sliders arrive through them like any other.
|
||||
//
|
||||
// Names are supplied already resolved, and the list is whatever profiles
|
||||
// are installed: this file does not know what a Kodachrome is.
|
||||
in property <[string]> film-stocks;
|
||||
/// Index into `film-stocks`. Zero is the first entry, which Rust makes
|
||||
/// "no film" — so a fresh photograph selects it without a sentinel.
|
||||
in property <int> film-selected: 0;
|
||||
/// Whether the chosen stock is a negative with a paper to print on. False
|
||||
/// for a reversal stock, which is the picture already, and for no film.
|
||||
in property <bool> film-can-print: false;
|
||||
/// Print it, rather than view the negative as scanned.
|
||||
in property <bool> film-print: false;
|
||||
callback film-picked(int);
|
||||
callback film-print-toggled(bool);
|
||||
|
||||
background: Theme.surface;
|
||||
|
||||
// A slider below has claimed the current gesture, so this panel must stop
|
||||
@@ -921,6 +943,38 @@ export component AdjustPanel inherits Rectangle {
|
||||
|
||||
if !root.enabled: Caption { text: "No image"; }
|
||||
|
||||
// TRACES: FR-DEV-3f
|
||||
// Above the rows, because it decides what they mean: the exposure
|
||||
// slider below is a slider on *this stock's* characteristic curve, and
|
||||
// the same number is a different picture on a different film.
|
||||
if root.enabled && root.film-stocks.length > 1: VerticalLayout {
|
||||
spacing: Theme.gap-sm;
|
||||
|
||||
Segmented {
|
||||
label: "Film";
|
||||
options: root.film-stocks;
|
||||
selected: root.film-selected;
|
||||
picked(i) => { root.film-picked(i); }
|
||||
}
|
||||
|
||||
// Only for a negative. A reversal stock has no paper — it is the
|
||||
// photograph as it comes — so offering the choice would be
|
||||
// offering something that cannot happen.
|
||||
if root.film-can-print: HorizontalLayout {
|
||||
spacing: Theme.gap-sm;
|
||||
alignment: start;
|
||||
print-toggle := TouchArea {
|
||||
width: 120px;
|
||||
height: Theme.touch-target;
|
||||
clicked => { root.film-print-toggled(!root.film-print); }
|
||||
Label {
|
||||
text: root.film-print ? "Printed" : "Scanned negative";
|
||||
emphasised: print-toggle.has-hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No Flickable here any more. The histogram, the capture metadata and
|
||||
// the geometry controls sat *above* this one and could not be scrolled
|
||||
// away, so on a 280px column in portrait they ate the height the
|
||||
|
||||
@@ -704,6 +704,16 @@ export component AppWindow inherits Window {
|
||||
// do. This window knows the *shape* of a control, never which operations
|
||||
// exist (FR-DEV-3a).
|
||||
in property <[ParamRow]> adjust-rows;
|
||||
// TRACES: FR-DEV-3f
|
||||
// The installed film stocks, "None" first, and which one this photograph
|
||||
// is on. Resolved names: the core publishes ids, and a stock's name is a
|
||||
// product name that does not translate.
|
||||
in property <[string]> film-stocks;
|
||||
in property <int> film-selected: 0;
|
||||
in property <bool> film-can-print: false;
|
||||
in property <bool> film-print: false;
|
||||
callback film-picked(int);
|
||||
callback film-print-toggled(bool);
|
||||
in property <[string]> adjust-tabs;
|
||||
in property <int> adjust-active-tab: -1;
|
||||
callback adjust-tab-picked(int);
|
||||
@@ -2357,6 +2367,13 @@ in property <bool> panel-visible: true;
|
||||
// kept in step with it.
|
||||
op-reset(op) => { root.curve-reset(op); }
|
||||
reset-all => { root.reset-all(); }
|
||||
// TRACES: FR-DEV-3f
|
||||
film-stocks: root.film-stocks;
|
||||
film-selected: root.film-selected;
|
||||
film-can-print: root.film-can-print;
|
||||
film-print: root.film-print;
|
||||
film-picked(i) => { root.film-picked(i); }
|
||||
film-print-toggled(on) => { root.film-print-toggled(on); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user