This commit is contained in:
Brandon Thomas
2026-10-09 20:50:06 -04:00
3 changed files with 46 additions and 1 deletions
+5
View File
@@ -349,6 +349,11 @@ impl LightcraftApp {
return r;
}
let r = self.session.execute(id, &params).map_err(|e| e.to_string());
if r.is_ok() && id == "mask.adjust" {
// Judge local adjustments on the photo, without the selection overlay obscuring them.
// Keep it hidden after release; O / the overlay eye can show it again.
self.ui.mask_overlay = false;
}
if r.is_ok() && id == "photo.label" {
let label = params.get("label").and_then(Value::as_str).and_then(lightcraft_catalog::ColorLabel::parse);
let text = match label {
+40
View File
@@ -132,6 +132,46 @@ fn new_brush_starts_a_separate_mask_in_paint_mode() {
h.settle(SETTLE);
}
#[test]
fn mask_adjustments_hide_overlay_without_disabling_the_mask() {
use lightcraft_pipeline::Overlay;
let mut h = detail("panel.masking");
exec(&mut h, "mask.add", json!({"kind": "linear", "start": [0.5, 0.2], "end": [0.5, 0.7]}));
assert!(h.app.ui.mask_overlay);
// Changing the shape needs the overlay; changing the image needs an unobscured preview.
exec(&mut h, "mask.refine", json!({"value": 10.0}));
assert!(h.app.ui.mask_overlay);
let r = h.request("ui.clickWidget", json!({"id": "slider:exposure", "fx": 0.7}), T);
assert_eq!(r["ok"], true, "{r}");
assert!(!h.app.ui.mask_overlay, "the exposure slider hides the overlay after release");
let d = develop(&h);
assert!(d.masks[0].adjust.exposure > 0.0);
assert!(d.masks[0].visible, "hiding the overlay must not disable the mask");
assert_eq!(crate::panels::detail::view_overlay(&h.app, &d), Overlay::None);
let mask = d.masks[0].clone();
let r = h.request("ui.key", json!({"key": "o"}), T);
assert_eq!(r["ok"], true, "{r}");
assert!(h.app.ui.mask_overlay, "O can restore the overlay");
assert_eq!(develop(&h).masks[0], mask, "toggling the overlay leaves the mask and its effect unchanged");
assert!(matches!(crate::panels::detail::view_overlay(&h.app, &develop(&h)), Overlay::Mask { .. }));
// Failed edits and global adjustments must not hide the mask overlay.
assert!(h.app.run("mask.adjust", json!({"id": 999, "values": {"exposure": 1.0}})).is_err());
assert!(h.app.ui.mask_overlay);
exec(&mut h, "develop.set", json!({"control": "light.exposure", "value": 0.5}));
assert!(h.app.ui.mask_overlay);
// Numeric edits/resets and Amount use the same command path as the sliders.
for values in [json!({"exposure": 0.0}), json!({"saturation": 20.0}), json!({"amount": 50.0})] {
exec(&mut h, "view.maskOverlay", json!({"show": true}));
exec(&mut h, "mask.adjust", json!({"values": values}));
assert!(!h.app.ui.mask_overlay);
}
h.settle(SETTLE);
}
#[test]
fn radial_body_drag_moves_rotated_components_and_undoes_once() {
let mut h = detail("panel.masking");
+1 -1
View File
@@ -363,7 +363,7 @@ under the UI command that wraps it (a no-op error); the UI command now wins.
| LR-MASK-AMOUNT | Mask amount | P1 | ✅ | `cmd:mask.adjust` (`amount`), `crates/ui-egui/src/panels/masking.rs` | |
| LR-MASK-FEATHER-EDGE | Refine mask edges | P2 | ✅ | `cmd:mask.refine`, `crates/pipeline/src/masks.rs` (`evaluate_one`) | Refine Edges slider per mask: guided filter on the photo's luminance snaps soft mask edges to the photo's edges (rendered on the CPU) |
| LR-MASK-SLIDERS | Local adjustment sliders | P0 | ✅ | `cmd:mask.adjust`, `crates/pipeline/src/finish.rs` | every slider renders (CPU + GPU), incl. Noise, Moiré, Defringe (negative Defringe has no effect); no local curve or effect presets |
| LR-MASK-OVERLAY | Mask overlay | P0 | ✅ | `cmd:view.maskOverlay`, `cmd:view.maskOverlayMode`, `cmd:view.maskOverlayColor`, `crates/pipeline/src/visualize.rs` | rendered alpha of the selected mask (CPU + GPU): colour, colour on B&W, image on black/white, white on black; ⇧O cycles while masking; no auto-show on hover |
| LR-MASK-OVERLAY | Mask overlay | P0 | ✅ | `cmd:view.maskOverlay`, `cmd:view.maskOverlayMode`, `cmd:view.maskOverlayColor`, `crates/pipeline/src/visualize.rs` | rendered alpha of the selected mask (CPU + GPU): colour, colour on B&W, image on black/white, white on black; ⇧O cycles while masking; successful local adjustments (including Amount) hide the overlay until manually shown again with O / the eye; shape/edge edits retain it; no auto-show on hover |
| LR-MASK-PINS | Pins | P1 | ✅ | `cmd:view.maskPins`, `crates/ui-egui/src/panels/detail.rs` | a pin per radial/linear/brush component: click selects its mask, drag moves it; no pins for range/AI components, no Auto mode |
| LR-MASK-UPDATE | Recompute AI masks | P2 | ⬜ | | |
| LR-MASK-SYNC | Copy masks to other photos | P1 | ✅ | `cmd:develop.copy` (`groups`) | |