mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-26 11:09:23 -06:00
Library grids: Home/End and Shift+Left/Right selection keys; Cmd+O and Shift+H
In Photo Grid and Square Grid, Home and End make the first or last photo of the view the active, selected one, and Shift+Left / Shift+Right add the previous or next photo of the view to the selection. These keys wait while the full-screen preview covers the grid, in every other view, while a text field or a dialog has the keyboard, and when the user gave the key to a command. They are listed under Fixed keys in the keyboard shortcuts dialog. Open Library now has Cmd+O and Visualize HDR has Shift+H (Cmd+Shift+H stays the Histogram).
This commit is contained in:
@@ -87,7 +87,7 @@ pub const UI_COMMANDS: &[UiCommand] = &[
|
||||
("view.zoomIn", "Zoom In", Some("Cmd+="), "View"),
|
||||
("view.zoomOut", "Zoom Out", Some("Cmd+-"), "View"),
|
||||
("view.clipping", "Show Clipping", Some("J"), "View"),
|
||||
("view.visualizeHdr", "Visualize HDR", None, "View"),
|
||||
("view.visualizeHdr", "Visualize HDR", Some("Shift+H"), "View"),
|
||||
// in grids S expands/collapses stacks (the engine command it shadows)
|
||||
("view.softProof", "Soft Proofing", Some("S"), "View"),
|
||||
("view.histogram", "Histogram", Some("Cmd+Shift+H"), "View"),
|
||||
@@ -196,7 +196,7 @@ pub const UI_COMMANDS: &[UiCommand] = &[
|
||||
("file.exportCurvePresets", "Export Point Curve Presets…", None, ""),
|
||||
("app.settings", "Settings…", Some("Cmd+,"), "Edit"),
|
||||
("app.newLibrary", "New Library…", None, "File"),
|
||||
("app.openLibrary", "Open Library…", None, "File"),
|
||||
("app.openLibrary", "Open Library…", Some("Cmd+O"), "File"),
|
||||
("app.recentLibraries", "Recent Libraries", None, ""),
|
||||
("app.clearRecentLibraries", "Clear Recent Libraries", None, ""),
|
||||
// Settings ▸ Display
|
||||
|
||||
@@ -151,6 +151,15 @@ fn fixed_keys(app: &LightcraftApp, ui: &mut egui::Ui, t: &Tokens, mac: bool, nee
|
||||
for (sc, label) in [("6", "Red label"), ("7", "Yellow label"), ("8", "Green label"), ("9", "Blue label")] {
|
||||
fixed.push((sc.to_string(), label));
|
||||
}
|
||||
// grid navigation (Photo Grid and Square Grid): fixed keys that belong to no single command
|
||||
for (sc, label) in [
|
||||
("Home", "Grid: first photo of the view"),
|
||||
("End", "Grid: last photo of the view"),
|
||||
("Shift+Left", "Grid: add the previous photo to the selection"),
|
||||
("Shift+Right", "Grid: add the next photo to the selection"),
|
||||
] {
|
||||
fixed.push((sc.to_string(), label));
|
||||
}
|
||||
let fixed: Vec<_> = fixed
|
||||
.into_iter()
|
||||
.filter(|(sc, label)| {
|
||||
|
||||
@@ -349,6 +349,9 @@ pub fn handle(app: &mut LightcraftApp, ctx: &egui::Context) {
|
||||
let mut aliased: Vec<(&str, serde_json::Value)> = Vec::new();
|
||||
let keymap = &app.ui.settings.keymap;
|
||||
let grid = library_grid(app);
|
||||
// The grid navigation keys act on the photos in view, so they wait while the full-screen preview
|
||||
// covers the grid: a key must not move the selection onto photos nobody can see.
|
||||
let grid_navigation = grid && !app.ui.fullscreen;
|
||||
// keys the user gave to a command: the fixed bindings below (aliases, ratings) yield to them
|
||||
let taken: Vec<(Modifiers, Key)> = keymap.values().filter_map(|s| parse(s)).collect();
|
||||
// an open popup (a menu, a date picker's calendar) closes on Esc itself: Esc's command (Back,
|
||||
@@ -391,6 +394,20 @@ pub fn handle(app: &mut LightcraftApp, ctx: &egui::Context) {
|
||||
fire.push(format!("label:{label}"));
|
||||
}
|
||||
}
|
||||
if grid_navigation {
|
||||
// Home / End: the first / last photo of the view
|
||||
for (key, id) in [(Key::Home, "grid:first"), (Key::End, "grid:last")] {
|
||||
if matches(i, Modifiers::NONE, key) && !taken.contains(&(Modifiers::NONE, key)) {
|
||||
fire.push(id.to_string());
|
||||
}
|
||||
}
|
||||
// Shift+← / Shift+→: the previous / next photo of the view joins the selection
|
||||
for (key, step) in [(Key::ArrowLeft, -1i64), (Key::ArrowRight, 1)] {
|
||||
if matches(i, Modifiers::SHIFT, key) && !taken.contains(&(Modifiers::SHIFT, key)) {
|
||||
fire.push(format!("grid:extend:{step}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
use crate::panels::compare;
|
||||
// rating/flag/label keys: in Compare/Survey they act on the active photo only; Shift+key or
|
||||
@@ -448,6 +465,29 @@ pub fn handle(app: &mut LightcraftApp, ctx: &egui::Context) {
|
||||
} else if f == "view.softProof" && matches!(app.ui.view, crate::state::ViewMode::PhotoGrid | crate::state::ViewMode::SquareGrid) {
|
||||
// S in a grid: expand / collapse the stack (Lightroom's Library binding)
|
||||
let _ = app.run("stack.toggle", json!({}));
|
||||
} else if f == "grid:first" || f == "grid:last" {
|
||||
// Home / End in a grid: make the first / last photo of the view the active, selected one
|
||||
let vis = app.session.visible_cloned();
|
||||
let edge = if f == "grid:first" { vis.first() } else { vis.last() };
|
||||
if let Some(id) = edge {
|
||||
let _ = app.run("library.select", json!({"ids": [id.0], "active": id.0}));
|
||||
}
|
||||
} else if let Some(step) = f.strip_prefix("grid:extend:") {
|
||||
// Shift+← / Shift+→ in a grid: the neighbour of the active photo joins the selection and
|
||||
// becomes the active one. At either end of the view the key does nothing.
|
||||
let step = step.parse::<i64>().unwrap_or(0);
|
||||
let vis = app.session.visible_cloned();
|
||||
let neighbour = app
|
||||
.session
|
||||
.active()
|
||||
.and_then(|active| vis.iter().position(|p| *p == active))
|
||||
.and_then(|pos| i64::try_from(pos).ok())
|
||||
.and_then(|pos| pos.checked_add(step))
|
||||
.and_then(|pos| usize::try_from(pos).ok())
|
||||
.and_then(|pos| vis.get(pos));
|
||||
if let Some(id) = neighbour {
|
||||
let _ = app.run("library.select", json!({"ids": [id.0], "mode": "add"}));
|
||||
}
|
||||
} else if compare::culling(app) && (f == "library.next" || f == "library.previous") {
|
||||
let d = if f == "library.next" { 1 } else { -1 };
|
||||
let _ = if app.ui.view == crate::state::ViewMode::Compare { compare::compare_step(app, d) } else { compare::survey_step(app, d) };
|
||||
|
||||
Reference in New Issue
Block a user