mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-24 10:09:01 -06:00
Merge PR #522
This commit is contained in:
@@ -349,6 +349,11 @@ impl LightcraftApp {
|
||||
return r;
|
||||
}
|
||||
let r = self.session.execute(id, ¶ms).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 {
|
||||
|
||||
@@ -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
@@ -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`) | |
|
||||
|
||||
Reference in New Issue
Block a user