mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-28 12:08:10 -06:00
LR-EDIT-SECTION-SOLO: section.<id> {solo?} and header clicks run the command
Option-click goes through section.<id> {solo: true}, so agents can open just one
section; view.soloSections and section.* refuse flags that are not true / false
(bool_param); toggle_section reuses solo_section.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d8f62a29b2
commit
12015179ea
@@ -124,6 +124,7 @@ pub const UI_COMMANDS: &[UiCommand] = &[
|
||||
("panel.versions", "Versions", Some("Shift+V"), "Window"),
|
||||
("panel.activity", "History", None, "Window"),
|
||||
("panel.close", "Close Panel", None, ""),
|
||||
// {solo?}: true opens just that section (⌥-click on its header); otherwise opens or closes it
|
||||
("section.light", "Light", Some("Cmd+1"), "Window>Edit Sections"),
|
||||
("section.color", "Color", Some("Cmd+2"), "Window>Edit Sections"),
|
||||
("section.effects", "Effects", Some("Cmd+3"), "Window>Edit Sections"),
|
||||
@@ -955,11 +956,10 @@ pub fn run_ui_command(app: &mut LightcraftApp, id: &str, p: &Value) -> Option<Re
|
||||
}
|
||||
Ok(json!({"hidden": app.ui.hidden_locations}))
|
||||
}
|
||||
"view.soloSections" => {
|
||||
let on = p.get("on").and_then(Value::as_bool).unwrap_or(!app.ui.single_panel);
|
||||
app.ui.set_single_panel(on);
|
||||
Ok(json!({"soloSections": app.ui.single_panel}))
|
||||
}
|
||||
"view.soloSections" => bool_param(id, p, "on").map(|on| {
|
||||
app.ui.set_single_panel(on.unwrap_or(!app.ui.single_panel));
|
||||
json!({"soloSections": app.ui.single_panel})
|
||||
}),
|
||||
"view.filterBar" => {
|
||||
app.ui.filter_bar = !app.ui.filter_bar;
|
||||
if app.ui.filter_bar && !matches!(app.ui.view, ViewMode::PhotoGrid | ViewMode::SquareGrid) {
|
||||
@@ -1040,14 +1040,18 @@ pub fn run_ui_command(app: &mut LightcraftApp, id: &str, p: &Value) -> Option<Re
|
||||
app.ui.presets = false;
|
||||
Ok(Value::Null)
|
||||
}
|
||||
s if s.starts_with("section.") => {
|
||||
s if s.starts_with("section.") => bool_param(s, p, "solo").map(|solo| {
|
||||
let sec = &s["section.".len()..];
|
||||
if app.ui.right != RightPanel::Edit {
|
||||
app.ui.right = RightPanel::Edit;
|
||||
}
|
||||
app.ui.toggle_section(sec);
|
||||
Ok(Value::Null)
|
||||
}
|
||||
if solo == Some(true) {
|
||||
app.ui.solo_section(sec);
|
||||
} else {
|
||||
app.ui.toggle_section(sec);
|
||||
}
|
||||
Value::Null
|
||||
}),
|
||||
s if s.starts_with("tool.") => {
|
||||
let tool = &s["tool.".len()..];
|
||||
match tool {
|
||||
|
||||
@@ -532,10 +532,9 @@ fn section(
|
||||
let _ = app.run("develop.sectionEnabled", json!({"section": id, "enabled": on}));
|
||||
} else if resp.clicked() {
|
||||
// ⌥-click opens just this section, as in Lightroom Classic
|
||||
if ui.input(|i| i.modifiers.alt) {
|
||||
app.ui.solo_section(id);
|
||||
} else {
|
||||
app.ui.toggle_section(id);
|
||||
let solo = ui.input(|i| i.modifiers.alt);
|
||||
if let Err(e) = app.run(&format!("section.{id}"), json!({"solo": solo})) {
|
||||
app.toast(ui.ctx(), e);
|
||||
}
|
||||
}
|
||||
resp.context_menu(|ui| {
|
||||
|
||||
@@ -1170,22 +1170,23 @@ impl UiState {
|
||||
pub fn toggle_section(&mut self, id: &str) {
|
||||
if self.section_open(id) {
|
||||
self.open_sections.retain(|s| s != id);
|
||||
} else if self.single_panel {
|
||||
self.solo_section(id);
|
||||
} else {
|
||||
if self.single_panel {
|
||||
self.open_sections.clear();
|
||||
}
|
||||
self.open_sections.push(id.to_string());
|
||||
}
|
||||
}
|
||||
/// Opens `id` and closes every other Edit section (⌥-click on a section header).
|
||||
pub fn solo_section(&mut self, id: &str) {
|
||||
self.open_sections = vec![id.to_string()];
|
||||
self.open_sections.clear();
|
||||
self.open_sections.push(id.to_string());
|
||||
}
|
||||
/// Turns Solo Mode on or off. Turning it on keeps only the section opened last.
|
||||
pub fn set_single_panel(&mut self, on: bool) {
|
||||
self.single_panel = on;
|
||||
if on && let Some(last) = self.open_sections.pop() {
|
||||
self.open_sections = vec![last];
|
||||
if on {
|
||||
let keep = self.open_sections.len().saturating_sub(1);
|
||||
self.open_sections.drain(..keep);
|
||||
}
|
||||
}
|
||||
/// Whether the sidebar section with this id is folded shut (an unknown id: no).
|
||||
|
||||
@@ -30,20 +30,17 @@ fn exec(h: &mut Headless, command: &str, params: serde_json::Value) -> serde_jso
|
||||
r["result"].clone()
|
||||
}
|
||||
|
||||
fn click(h: &mut Headless, id: &str, button: Option<&str>) {
|
||||
let params = match button {
|
||||
Some(b) => json!({"id": id, "button": b}),
|
||||
None => json!({"id": id}),
|
||||
};
|
||||
let r = h.request("ui.clickWidget", params, T);
|
||||
assert_eq!(r["ok"], true, "click {id}: {r}");
|
||||
/// A real click through the control channel: `{id, button?, alt?}` as `ui.clickWidget` takes them.
|
||||
fn click(h: &mut Headless, params: serde_json::Value) {
|
||||
let r = h.request("ui.clickWidget", params.clone(), T);
|
||||
assert_eq!(r["ok"], true, "click {params}: {r}");
|
||||
h.step();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn solo_mode_closes_the_other_sections_when_one_opens() {
|
||||
let mut h = edit_open();
|
||||
click(&mut h, "section:color", None);
|
||||
click(&mut h, json!({"id": "section:color"}));
|
||||
assert_eq!(h.app.ui.open_sections, ["light", "color"], "off: sections open side by side");
|
||||
|
||||
assert_eq!(exec(&mut h, "view.soloSections", json!({})), json!({"soloSections": true}));
|
||||
@@ -51,7 +48,7 @@ fn solo_mode_closes_the_other_sections_when_one_opens() {
|
||||
assert_eq!(crate::menubar::checked(&h.app, "view.soloSections"), Some(true));
|
||||
h.step();
|
||||
|
||||
click(&mut h, "section:detail", None);
|
||||
click(&mut h, json!({"id": "section:detail"}));
|
||||
assert_eq!(h.app.ui.open_sections, ["detail"]);
|
||||
// the Window > Edit Sections shortcuts follow it too
|
||||
exec(&mut h, "section.optics", json!({}));
|
||||
@@ -69,9 +66,9 @@ fn solo_mode_closes_the_other_sections_when_one_opens() {
|
||||
#[test]
|
||||
fn the_section_header_menu_turns_solo_mode_on() {
|
||||
let mut h = edit_open();
|
||||
click(&mut h, "section:color", None);
|
||||
click(&mut h, "section:effects", Some("right"));
|
||||
click(&mut h, "sectionMenu:solo", None);
|
||||
click(&mut h, json!({"id": "section:color"}));
|
||||
click(&mut h, json!({"id": "section:effects", "button": "right"}));
|
||||
click(&mut h, json!({"id": "sectionMenu:solo"}));
|
||||
assert!(h.app.ui.single_panel, "the header's Solo Mode item turns it on");
|
||||
assert_eq!(h.app.ui.open_sections, ["color"]);
|
||||
}
|
||||
@@ -79,18 +76,22 @@ fn the_section_header_menu_turns_solo_mode_on() {
|
||||
#[test]
|
||||
fn option_click_opens_just_that_section() {
|
||||
let mut h = edit_open();
|
||||
click(&mut h, "section:color", None);
|
||||
let r = h.request("ui.clickWidget", json!({"id": "section:detail", "alt": true}), T);
|
||||
assert_eq!(r["ok"], true, "{r}");
|
||||
h.step();
|
||||
click(&mut h, json!({"id": "section:color"}));
|
||||
click(&mut h, json!({"id": "section:detail", "alt": true}));
|
||||
assert_eq!(h.app.ui.open_sections, ["detail"], "⌥-click closes the others");
|
||||
assert!(!h.app.ui.single_panel, "⌥-click doesn't turn Solo Mode on");
|
||||
// agents do the same with the section's command (Calibration has no menu item but runs too)
|
||||
exec(&mut h, "section.calibration", json!({"solo": true}));
|
||||
assert_eq!(h.app.ui.open_sections, ["calibration"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn solo_mode_survives_a_restart() {
|
||||
let mut ui = crate::state::UiState::default();
|
||||
ui.set_single_panel(true);
|
||||
let back: crate::state::UiState = serde_json::from_value(serde_json::to_value(&ui).unwrap()).unwrap();
|
||||
assert!(back.single_panel);
|
||||
fn flags_that_are_not_true_or_false_are_refused() {
|
||||
let mut h = edit_open();
|
||||
for (command, params) in [("view.soloSections", json!({"on": "yes"})), ("section.color", json!({"solo": 1}))] {
|
||||
let r = h.request("engine.execute", json!({"command": command, "params": params}), T);
|
||||
assert_eq!(r["ok"], false, "{command} {params}: {r}");
|
||||
}
|
||||
assert!(!h.app.ui.single_panel);
|
||||
assert_eq!(h.app.ui.open_sections, ["light"]);
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ differences and missing bindings are in [ui-parity.md](ui-parity.md#shortcuts).
|
||||
| LR-EDIT-LENSBLUR | Lens blur | P2 | ⬜ | | settings field reserved, not rendered |
|
||||
| LR-EDIT-CALIB | Calibration [Classic] | P1 | ✅ | `ctl:calibration.*` | shadows tint, red/green/blue primary hue and saturation; read/written in XMP |
|
||||
| LR-EDIT-SECTION-TOGGLE | Section on/off | P1 | ✅ | `cmd:develop.sectionEnabled`, `crates/develop/src/lib.rs` (`effective`) | the eye on a section header hides its adjustments: Light (with the curve), Color (white balance, presence, mixers, grading, point colour), Effects, Detail, Optics, Geometry, Calibration; masks stay on (issue #316 fixed Light, Color and Detail, which had no effect) |
|
||||
| LR-EDIT-SECTION-SOLO | Solo Mode for Edit sections | P2 | ✅ | `cmd:view.soloSections`, `crates/ui-egui/src/state.rs` (`toggle_section`, `set_single_panel`, `solo_section`), `crates/ui-egui/src/panels/edit.rs` (`section`), `crates/ui-egui/src/tests_solo_sections.rs` | Lightroom Classic's panel Solo Mode: while it is on, opening one Edit section (a header click or ⌘1–⌘5) closes the others. Window ▸ Edit Sections ▸ Solo Mode (checked while on) or a right-click on any section header (`sectionMenu:solo`) turns it on and off; turning it on keeps only the section opened last. ⌥-click on a header opens just that section once, without turning the mode on. `view.soloSections` `{on?}` (no params toggles) answers `{soloSections}`. Kept with the UI state (`singlePanel`) |
|
||||
| LR-EDIT-SECTION-SOLO | Solo Mode for Edit sections | P2 | ✅ | `cmd:view.soloSections`, `crates/ui-egui/src/state.rs` (`toggle_section`, `set_single_panel`, `solo_section`), `crates/ui-egui/src/panels/edit.rs` (`section`), `crates/ui-egui/src/tests_solo_sections.rs` | Lightroom Classic's panel Solo Mode: while it is on, opening one Edit section (a header click or ⌘1–⌘5) closes the others. Window ▸ Edit Sections ▸ Solo Mode (checked while on) or a right-click on any section header (`sectionMenu:solo`) turns it on and off; turning it on keeps only the section opened last. ⌥-click on a header opens just that section once, without turning the mode on (agents: `section.<id>` `{solo: true}`; header clicks run `section.<id>` too). `view.soloSections` `{on?}` (no params toggles) answers `{soloSections}`. Kept with the UI state (`singlePanel`) |
|
||||
| LR-EDIT-RESET | Reset all / section / slider | P0 | ✅ | `cmd:develop.reset`, `cmd:develop.resetSection`, `cmd:develop.resetControl`, `crates/ui-egui/src/widgets.rs` (double-click) | no "reset to open" |
|
||||
| LR-EDIT-SHOWORIG | Show original | P0 | ✅ | `cmd:view.showOriginal` | |
|
||||
|
||||
|
||||
Reference in New Issue
Block a user