Merge master into partial-preset-scope
🐳 Android image / Build and push (push) Successful in 8s
Build and test / android-image (push) Successful in 8s
Build and test / Desktop (Linux) (push) Failing after 1h17m12s
Build and test / Layer separation (push) Successful in 56s
Traceability / Requirement traces (push) Successful in 1m33s
Build and test / Android (aarch64) (push) Successful in 1h0m38s
🐳 Android image / Build and push (push) Successful in 8s
Build and test / android-image (push) Successful in 8s
Build and test / Desktop (Linux) (push) Failing after 1h17m12s
Build and test / Layer separation (push) Successful in 56s
Traceability / Requirement traces (push) Successful in 1m33s
Build and test / Android (aarch64) (push) Successful in 1h0m38s
# Conflicts: # docs/traceability.md # ui/dr-ui/ui/app.slint
This commit is contained in:
@@ -39,6 +39,7 @@ mod library_ui;
|
||||
mod live_style;
|
||||
mod masks_ui;
|
||||
mod net_runtime;
|
||||
mod peaking;
|
||||
mod preset_store;
|
||||
mod presets;
|
||||
mod remote;
|
||||
@@ -317,6 +318,12 @@ fn reset_view_state(window: &AppWindow) {
|
||||
// beside the next one's filename is a confident, precise lie, and the gap
|
||||
// before the new frame settles is exactly long enough to read it.
|
||||
window.set_histogram(histogram::empty());
|
||||
// TRACES: FR-CULL-3
|
||||
// The marks go down with it, and for the same reason. What is *not* reset
|
||||
// is whether peaking is switched on: that is a way of looking at a folder
|
||||
// rather than a property of one photograph, so it survives to the next
|
||||
// frame — see `chosen_peaking` for the whole of that argument.
|
||||
window.set_focus_overlay_ready(false);
|
||||
// TRACES: FR-DEV-3
|
||||
// The region map belongs to one photograph. Carrying the stack, the
|
||||
// overlay or the crosshair to the next one would offer a selection of
|
||||
@@ -1465,11 +1472,24 @@ pub fn run(paths: Vec<PathBuf>) -> Result<()> {
|
||||
// is redrawn, and those are very different rates.
|
||||
let drawn_history: Rc<Cell<Option<u64>>> = Rc::new(Cell::new(None));
|
||||
|
||||
// TRACES: FR-CULL-3
|
||||
// How the photographer wants focus peaking drawn, or `None` for off.
|
||||
//
|
||||
// **Held here rather than on the session, which is the opposite of where
|
||||
// every edit lives.** A session is one photograph; peaking is a way of
|
||||
// *looking* at a folder of them. Someone culling three thousand frames
|
||||
// switches it on once, and a flag that reset with the session would ask
|
||||
// them to switch it on three thousand times — which is why
|
||||
// `reset_view_state` deliberately leaves it alone while emptying the
|
||||
// histogram beside it.
|
||||
let chosen_peaking: Rc<Cell<Option<dr_gpu::FocusPeaking>>> = Rc::new(Cell::new(None));
|
||||
|
||||
let render_now: Render = {
|
||||
let session = session.clone();
|
||||
let viewport = viewport.clone();
|
||||
let drawn_history = drawn_history.clone();
|
||||
let display = display.clone();
|
||||
let chosen_peaking = chosen_peaking.clone();
|
||||
Rc::new(move |window: &AppWindow, draft: bool| {
|
||||
let mut slot = session.borrow_mut();
|
||||
let Some(s) = slot.as_mut() else { return };
|
||||
@@ -1530,6 +1550,16 @@ pub fn run(paths: Vec<PathBuf>) -> Result<()> {
|
||||
// arrival takes.
|
||||
spots_ui::sync_panel(window, s);
|
||||
|
||||
// TRACES: FR-CULL-3
|
||||
// The session owns the pass and the interface owns the choice, so
|
||||
// they are joined here — on the one path every frame takes, which
|
||||
// is also what makes a photograph opened with peaking already on
|
||||
// arrive with its marks rather than without them.
|
||||
if s.peaking() != chosen_peaking.get() {
|
||||
s.set_peaking(chosen_peaking.get());
|
||||
}
|
||||
window.set_peaking_available(s.peaking_available());
|
||||
|
||||
let (mut w, mut h) = *viewport.borrow();
|
||||
|
||||
// **Half resolution while the gesture is still moving.**
|
||||
@@ -1592,6 +1622,34 @@ pub fn run(paths: Vec<PathBuf>) -> Result<()> {
|
||||
.map_or_else(histogram::empty, histogram::view),
|
||||
);
|
||||
}
|
||||
|
||||
// TRACES: FR-CULL-3 | NFR-P14
|
||||
// **Marked on the settled frame and no other**, and unlike
|
||||
// the histogram beside it the marks are taken *down* in
|
||||
// between rather than left standing.
|
||||
//
|
||||
// The reason is not budget — the dispatch is a fraction of
|
||||
// a millisecond and would fit inside a draft frame
|
||||
// comfortably. It is that peaking measures the top octave
|
||||
// of the frame it is given, and a draft frame is rendered
|
||||
// at half resolution: a defocused edge that spans four
|
||||
// pixels there spans two, which is the signature of a
|
||||
// sharp one. Measuring it would mark the out-of-focus
|
||||
// background of every photograph, briefly, during every
|
||||
// drag. A stale overlay is no better, because a pan moves
|
||||
// the picture out from under it.
|
||||
//
|
||||
// So the marks pause while a control is moving and return
|
||||
// when it stops, which the panel says out loud rather than
|
||||
// leaving to be discovered.
|
||||
let overlay = (!draft).then(|| s.focus_overlay()).flatten();
|
||||
match overlay {
|
||||
Some(image) => {
|
||||
window.set_focus_overlay(image);
|
||||
window.set_focus_overlay_ready(true);
|
||||
}
|
||||
None => window.set_focus_overlay_ready(false),
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("render failed: {e}");
|
||||
@@ -1599,6 +1657,11 @@ pub fn run(paths: Vec<PathBuf>) -> Result<()> {
|
||||
// No frame, so nothing to describe. The stale plot would
|
||||
// otherwise sit beside the error message looking current.
|
||||
window.set_histogram(histogram::empty());
|
||||
// TRACES: FR-CULL-3
|
||||
// And nothing to mark. Focus marks over the last frame
|
||||
// that rendered, beside a message saying this one did not,
|
||||
// is the same confident lie in a second instrument.
|
||||
window.set_focus_overlay_ready(false);
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -2844,6 +2907,70 @@ pub fn run(paths: Vec<PathBuf>) -> Result<()> {
|
||||
});
|
||||
}
|
||||
|
||||
// TRACES: FR-CULL-3
|
||||
// The peaking switch and its two choices.
|
||||
//
|
||||
// All three write `chosen_peaking` and then redraw, because the marks are
|
||||
// produced by a compute pass over the rendered frame: there is nothing the
|
||||
// interface can change about the overlay that does not require the frame
|
||||
// to be measured again. Turning peaking *off* redraws for the same reason
|
||||
// — that render is what drops the overlay textures and clears the flag.
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let chosen = chosen_peaking.clone();
|
||||
let redraw = redraw.clone();
|
||||
window.on_peaking_toggled(move |on| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
// Built from the chips as they currently stand rather than from a
|
||||
// remembered value: they are what the photographer can see, and an
|
||||
// overlay that came back in a configuration the panel is not
|
||||
// showing would be the panel lying about itself.
|
||||
let next = on.then(|| dr_gpu::FocusPeaking {
|
||||
sensitivity: peaking::sensitivity(w.get_peaking_sensitivity()),
|
||||
colour: peaking::colour(w.get_peaking_colour()),
|
||||
});
|
||||
chosen.set(next);
|
||||
w.set_peaking_on(next.is_some());
|
||||
redraw(&w);
|
||||
});
|
||||
}
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let chosen = chosen_peaking.clone();
|
||||
let redraw = redraw.clone();
|
||||
window.on_peaking_sensitivity_picked(move |index| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
w.set_peaking_sensitivity(index);
|
||||
// Only reachable while peaking is on — the chips are not drawn
|
||||
// otherwise — but written as a conditional rather than an
|
||||
// `expect`, because a panel is free to change its mind about that
|
||||
// and nothing here should fall over when it does.
|
||||
if let Some(mut current) = chosen.get() {
|
||||
current.sensitivity = peaking::sensitivity(index);
|
||||
chosen.set(Some(current));
|
||||
redraw(&w);
|
||||
}
|
||||
});
|
||||
}
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let chosen = chosen_peaking.clone();
|
||||
let redraw = redraw.clone();
|
||||
window.on_peaking_colour_picked(move |index| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
w.set_peaking_colour(index);
|
||||
if let Some(mut current) = chosen.get() {
|
||||
current.colour = peaking::colour(index);
|
||||
chosen.set(Some(current));
|
||||
redraw(&w);
|
||||
}
|
||||
});
|
||||
}
|
||||
// The chips open on whatever the vocabulary calls its default, so the
|
||||
// panel and the pass agree before anything has been pressed.
|
||||
window.set_peaking_sensitivity(peaking::sensitivity_index(Default::default()));
|
||||
window.set_peaking_colour(peaking::colour_index(Default::default()));
|
||||
|
||||
// TRACES: FR-DSP-8 | FR-DSP-6
|
||||
// And which display that canvas is on, from now until the window closes.
|
||||
display_ui::attach(&window, &display, &viewport, redraw.clone());
|
||||
|
||||
Reference in New Issue
Block a user