M2.11: before/after top/bottom (⌥Y) and split top/bottom (⇧⌥Y)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
96903a5fff
commit
6015af99b7
|
|
@ -18,6 +18,8 @@ pub const UI_COMMANDS: &[UiCommand] = &[
|
|||
("view.leftPanel", "My Photos Panel", Some("Cmd+Shift+L"), "View"),
|
||||
("view.beforeAfter", "Compare Before and After", Some("Y"), "View"),
|
||||
("view.beforeAfterSplit", "Before/After Split", Some("Shift+Y"), "View"),
|
||||
("view.beforeAfterTopBottom", "Before/After Top/Bottom", Some("Alt+Y"), "View"),
|
||||
("view.beforeAfterSplitTopBottom", "Before/After Split Top/Bottom", Some("Alt+Shift+Y"), "View"),
|
||||
("view.showOriginal", "Show Original", Some("\\"), "View"),
|
||||
("view.zoomFit", "Zoom to Fit", Some("Cmd+0"), "View"),
|
||||
("view.zoom100", "Zoom 100%", Some("Cmd+1"), "View"),
|
||||
|
|
@ -125,6 +127,14 @@ pub fn run_ui_command(app: &mut LightcraftApp, id: &str, p: &Value) -> Option<Re
|
|||
app.ui.before_after = if app.ui.before_after == BeforeAfter::Split { BeforeAfter::Off } else { BeforeAfter::Split };
|
||||
Ok(Value::Null)
|
||||
}
|
||||
"view.beforeAfterTopBottom" => {
|
||||
app.ui.before_after = if app.ui.before_after == BeforeAfter::TopBottom { BeforeAfter::Off } else { BeforeAfter::TopBottom };
|
||||
Ok(Value::Null)
|
||||
}
|
||||
"view.beforeAfterSplitTopBottom" => {
|
||||
app.ui.before_after = if app.ui.before_after == BeforeAfter::SplitTopBottom { BeforeAfter::Off } else { BeforeAfter::SplitTopBottom };
|
||||
Ok(Value::Null)
|
||||
}
|
||||
"view.showOriginal" => {
|
||||
app.ui.before_after = if app.ui.before_after == BeforeAfter::Original { BeforeAfter::Off } else { BeforeAfter::Original };
|
||||
Ok(Value::Null)
|
||||
|
|
|
|||
|
|
@ -124,15 +124,25 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) {
|
|||
let ppp = ui.ctx().pixels_per_point();
|
||||
let area = canvas.shrink(if crop_tool { 48.0 } else { 24.0 });
|
||||
let native = [photo.width.max(1) as usize, photo.height.max(1) as usize];
|
||||
let split = app.ui.before_after == BeforeAfter::SideBySide;
|
||||
let areas: Vec<Rect> = if split {
|
||||
let half = area.width() / 2.0 - 6.0;
|
||||
vec![
|
||||
Rect::from_min_size(area.min, vec2(half, area.height())),
|
||||
Rect::from_min_size(pos2(area.center().x + 6.0, area.top()), vec2(half, area.height())),
|
||||
]
|
||||
} else {
|
||||
vec![area]
|
||||
// two views (before, after): side by side or stacked
|
||||
let split = matches!(app.ui.before_after, BeforeAfter::SideBySide | BeforeAfter::TopBottom);
|
||||
let split_view = matches!(app.ui.before_after, BeforeAfter::Split | BeforeAfter::SplitTopBottom);
|
||||
let areas: Vec<Rect> = match app.ui.before_after {
|
||||
BeforeAfter::SideBySide => {
|
||||
let half = area.width() / 2.0 - 6.0;
|
||||
vec![
|
||||
Rect::from_min_size(area.min, vec2(half, area.height())),
|
||||
Rect::from_min_size(pos2(area.center().x + 6.0, area.top()), vec2(half, area.height())),
|
||||
]
|
||||
}
|
||||
BeforeAfter::TopBottom => {
|
||||
let half = area.height() / 2.0 - 14.0;
|
||||
vec![
|
||||
Rect::from_min_size(area.min, vec2(area.width(), half)),
|
||||
Rect::from_min_size(pos2(area.left(), area.center().y + 14.0), vec2(area.width(), half)),
|
||||
]
|
||||
}
|
||||
_ => vec![area],
|
||||
};
|
||||
let main_area = *areas.last().unwrap_or(&area);
|
||||
let img_rect = fit_rect(main_area, aspect, app.ui.zoom, native, ppp, app.ui.pan);
|
||||
|
|
@ -174,7 +184,7 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) {
|
|||
app.renderer.request_quick(Slot::Preview, q, 110);
|
||||
}
|
||||
let show_before = app.ui.before_after == BeforeAfter::Original || ui.input(|i| i.key_down(egui::Key::Backslash));
|
||||
if (split || show_before || app.ui.before_after == BeforeAfter::Split)
|
||||
if (split || show_before || split_view)
|
||||
&& let Some(job) = app.session.render_job(id, rw.max(8), rh.max(8), true, !crop_tool)
|
||||
{
|
||||
app.renderer.request(Slot::Before, job, 90);
|
||||
|
|
@ -221,6 +231,14 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) {
|
|||
}
|
||||
p.line_segment([pos2(mid, img_rect.top()), pos2(mid, img_rect.bottom())], Stroke::new(1.5, Color32::WHITE));
|
||||
}
|
||||
if app.ui.before_after == BeforeAfter::SplitTopBottom {
|
||||
let mid = img_rect.center().y;
|
||||
if let Some(tex) = app.renderer.textures.get(&Slot::Before).filter(|t| t.photo == id) {
|
||||
let top = Rect::from_min_max(img_rect.min, pos2(img_rect.right(), mid));
|
||||
p.image(tex.tex.id(), top, Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 0.5)), Color32::WHITE);
|
||||
}
|
||||
p.line_segment([pos2(img_rect.left(), mid), pos2(img_rect.right(), mid)], Stroke::new(1.5, Color32::WHITE));
|
||||
}
|
||||
if app.ui.show_clipping && !show_before {
|
||||
clipping_overlay(app, &p, img_rect);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,10 @@ pub enum BeforeAfter {
|
|||
Original,
|
||||
SideBySide,
|
||||
Split,
|
||||
/// Before above after.
|
||||
TopBottom,
|
||||
/// One image split horizontally: before above the line.
|
||||
SplitTopBottom,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
|
|
|||
|
|
@ -45,10 +45,10 @@ and prints the summary; `cargo xtask parity --write` refreshes the summary table
|
|||
| V. Preferences (PREF) | 0 | 3 | 5 | 3 | 0/1 (0%) | 0/4 (0%) |
|
||||
| W. Cloud & AI infrastructure (CLOUD / AI) | 0 | 1 | 1 | 2 | — | — |
|
||||
| X. Cross-cutting behaviours (BEHAV) | 10 | 2 | 5 | 1 | 7/8 (88%) | 3/5 (60%) |
|
||||
| Y. Menus | 41 | 20 | 24 | 8 | 36/47 (77%) | 3/23 (13%) |
|
||||
| Z. Keyboard shortcuts (desktop) | 44 | 16 | 20 | 1 | 39/52 (75%) | 5/23 (22%) |
|
||||
| Y. Menus | 42 | 19 | 24 | 8 | 37/47 (79%) | 3/23 (13%) |
|
||||
| Z. Keyboard shortcuts (desktop) | 45 | 16 | 19 | 1 | 39/52 (75%) | 6/23 (26%) |
|
||||
| Lightroom Classic extras | 1 | 17 | 61 | 9 | — | 1/21 (5%) |
|
||||
| **Total** | 182 | 111 | 207 | 37 | 147/198 (74%) | 32/144 (22%) |
|
||||
| **Total** | 184 | 110 | 206 | 37 | 148/198 (75%) | 33/144 (23%) |
|
||||
<!-- /parity:summary -->
|
||||
|
||||
## Top gaps
|
||||
|
|
@ -124,7 +124,7 @@ under the UI command that wraps it (a no-op error); the UI command now wins.
|
|||
|
||||
**Still missing / broken:**
|
||||
- No command yet: F full-screen preview, ⇧⌘F window full screen, ⇧⌘V paste selected,
|
||||
⌘, settings, ⌘F focus search, ⌘G / ⇧⌘G stacks, ⌥Y top/bottom before/after, A visualize spots, `[` `]` / ⇧`[` ⇧`]`
|
||||
⌘, settings, ⌘F focus search, ⌘G / ⇧⌘G stacks, A visualize spots, `[` `]` / ⇧`[` ⇧`]`
|
||||
brush size/feather, ⌃H / ⌃M merges, F1 help, ⇧6–9 label + advance.
|
||||
- `⌫` in the Masking panel deletes the active mask; spots have no pin selection yet (⌫ does nothing in Remove).
|
||||
- `H` opens Remove; Lightroom also uses it (Classic) to hide pins — no pin toggle yet.
|
||||
|
|
@ -195,7 +195,7 @@ under the UI command that wraps it (a no-op error); the UI command now wins.
|
|||
| LR-VIEW-FILMSTRIP | Filmstrip | P0 | ✅ | `cmd:view.filmstrip` | |
|
||||
| LR-VIEW-ZOOM | Zoom & pan | P0 | ✅ | `cmd:view.zoomFit`, `cmd:view.zoom100`, `cmd:view.zoomIn`, `cmd:view.zoomOut`, `cmd:view.zoomToggle` | steps 25–800 % (not 6–1600 %); Fill only in the bottom bar |
|
||||
| LR-VIEW-NAVIGATOR | Navigator mini map | P1 | ⬜ | | |
|
||||
| LR-VIEW-BEFOREAFTER | Before / after | P0 | 🟡 | `cmd:view.showOriginal`, `cmd:view.beforeAfter`, `cmd:view.beforeAfterSplit` | no top/bottom layouts; "before" is always the original |
|
||||
| LR-VIEW-BEFOREAFTER | Before / after | P0 | 🟡 | `cmd:view.showOriginal`, `cmd:view.beforeAfter`, `cmd:view.beforeAfterSplit`, `cmd:view.beforeAfterTopBottom`, `cmd:view.beforeAfterSplitTopBottom` | all four layouts; "before" is always the original (no before-state from history) |
|
||||
| LR-VIEW-COMPARE | Compare two photos | P1 | ⬜ | | |
|
||||
| LR-VIEW-SURVEY | Survey view [Classic] | P2 | ⬜ | | |
|
||||
| LR-VIEW-INFOOVERLAY | Info overlay on the photo | P1 | ⬜ | | |
|
||||
|
|
@ -581,7 +581,7 @@ visible menu bar yet: the menu model is only exposed through the control channel
|
|||
| MENU-VIEW-HISTOGRAM | Show/Hide histogram | P0 | ✅ | `cmd:view.histogram` | |
|
||||
| MENU-VIEW-INFOOVERLAY | Show info overlay | P1 | ⬜ | | |
|
||||
| MENU-VIEW-SHOWORIGINAL | Show Original | P0 | ✅ | `cmd:view.showOriginal` | |
|
||||
| MENU-VIEW-BEFOREAFTER | Before/After submenu | P0 | 🟡 | `cmd:view.beforeAfter`, `cmd:view.beforeAfterSplit` | left/right and split left/right only |
|
||||
| MENU-VIEW-BEFOREAFTER | Before/After submenu | P0 | ✅ | `cmd:view.beforeAfter`, `cmd:view.beforeAfterSplit`, `cmd:view.beforeAfterTopBottom`, `cmd:view.beforeAfterSplitTopBottom` | |
|
||||
| MENU-VIEW-ZOOM | Zoom in / out / toggle / fit / 1:1 | P0 | ✅ | `cmd:view.zoomIn`, `cmd:view.zoomOut`, `cmd:view.zoomToggle`, `cmd:view.zoomFit`, `cmd:view.zoom100` | |
|
||||
| MENU-VIEW-CLIPPING | Show Clipping | P0 | ✅ | `cmd:view.clipping` | |
|
||||
| MENU-VIEW-MASKOVERLAY | Mask overlay / cycle colour | P0 | 🟡 | `cmd:view.maskOverlay` | no colour cycle |
|
||||
|
|
@ -696,7 +696,7 @@ From `06-shortcuts.md` part 1. Evidence is our binding; conflicts are explained
|
|||
| KEY-FLAGADVANCE | Flag and advance — ⇧Z / ⇧X / ⇧U | P1 | 🟡 | `cmd:photo.flag` (`advance`) | ⇧X and ⇧U bound; pick-and-advance has no key (⇧P = presets) |
|
||||
| KEY-NEXTPREV | Next / previous — → / ← | P0 | ✅ | `cmd:library.next`, `cmd:library.previous` | |
|
||||
| KEY-BA-CYCLE | Before/after — Y | P0 | ✅ | `cmd:view.beforeAfter` | toggles side by side (no cycling) |
|
||||
| KEY-BA-TOPBOTTOM | Before/after top/bottom — ⌥Y | P1 | ⬜ | | |
|
||||
| KEY-BA-TOPBOTTOM | Before/after top/bottom — ⌥Y | P1 | ✅ | `cmd:view.beforeAfterTopBottom`, `cmd:view.beforeAfterSplitTopBottom` | ⌥Y, ⇧⌥Y |
|
||||
| KEY-BA-SPLIT | Split before/after — ⇧Y | P0 | ✅ | `cmd:view.beforeAfterSplit` | |
|
||||
| KEY-CHOOSECOPY | Choose settings to copy — ⇧⌘C | P0 | ✅ | `cmd:dialog.copySettings` | |
|
||||
| KEY-RESETALL | Reset all — ⇧⌘R | P0 | ✅ | `cmd:develop.reset` | |
|
||||
|
|
|
|||
Loading…
Reference in New Issue