diff --git a/crates/engine/src/commands/viewer_cmds.rs b/crates/engine/src/commands/viewer_cmds.rs index 263f7191..aac24267 100644 --- a/crates/engine/src/commands/viewer_cmds.rs +++ b/crates/engine/src/commands/viewer_cmds.rs @@ -173,6 +173,20 @@ fn show_snapshot(s: &mut Session, p: &Value) -> Result { Ok(json!(s.state.viewer.show_snapshot)) } +/// A `view.fastPreviews.*` command choosing one Fast Previews mode. +macro_rules! fast { + ($id:literal, $label:literal, $sc:literal, $mode:literal) => { + cmd!($id, $label, [], Some($sc), "{}", always, |s, _| fast_previews(s, &json!({"mode": $mode}))) + }; +} + +/// A `view.channel.*` command showing one channel (again: back to RGB), in grayscale or colorized. +macro_rules! chan { + ($id:literal, $label:literal, $sc:literal, $channel:literal, $colorized:literal) => { + cmd!($id, $label, [], Some($sc), "{}", always, |s, _| channel(s, &json!({"channel": $channel, "colorized": $colorized, "toggle": true}))) + }; +} + fn fast_previews(s: &mut Session, p: &Value) -> Result { let name = str_p(p, "mode").unwrap_or("off"); let m = @@ -504,6 +518,22 @@ pub fn specs() -> Vec { cmd!("view.takeSnapshot", "Take Snapshot", [], Some("Shift+F5"), "{comp?, scale?: 0.05..1}", has_comp, take_snapshot), cmd!("view.showSnapshot", "Show Snapshot", [], None, "{value?}", has_snapshot, show_snapshot), cmd!("view.fastPreviewMode", "Fast Previews", [], None, "{mode: off|adaptive|draft|fastDraft|wireframe}", always, fast_previews), + fast!("view.fastPreviews.off", "Fast Previews: Off (Final Quality)", "Cmd+Alt+1", "off"), + fast!("view.fastPreviews.adaptive", "Fast Previews: Adaptive Resolution", "Cmd+Alt+2", "adaptive"), + fast!("view.fastPreviews.draft", "Fast Previews: Draft", "Cmd+Alt+3", "draft"), + fast!("view.fastPreviews.fastDraft", "Fast Previews: Fast Draft", "Cmd+Alt+4", "fastDraft"), + fast!("view.fastPreviews.wireframe", "Fast Previews: Wireframe", "Cmd+Alt+5", "wireframe"), + chan!("view.channel.red", "Show Channel: Red", "Alt+1", "red", false), + chan!("view.channel.green", "Show Channel: Green", "Alt+2", "green", false), + chan!("view.channel.blue", "Show Channel: Blue", "Alt+3", "blue", false), + chan!("view.channel.alpha", "Show Channel: Alpha", "Alt+4", "alpha", false), + chan!("view.channel.redColorized", "Show Channel: Red, Colorized", "Alt+Shift+1", "red", true), + chan!("view.channel.greenColorized", "Show Channel: Green, Colorized", "Alt+Shift+2", "green", true), + chan!("view.channel.blueColorized", "Show Channel: Blue, Colorized", "Alt+Shift+3", "blue", true), + cmd!("view.channel.rgbStraight", "Show Channel: RGB Straight", [], Some("Alt+Shift+4"), "{}", always, |s, _| channel( + s, + &json!({"channel": "rgbStraight", "toggle": true}) + )), cmd!("view.moveGuide", "Move Guide", [], None, "{comp?, index, position (comp px), merge?}", has_comp, move_guide), cmd!("view.removeGuide", "Remove Guide", [], None, "{comp?, index}", has_comp, remove_guide), cmd!( diff --git a/crates/engine/src/tests_settings.rs b/crates/engine/src/tests_settings.rs index 02c2a00b..f072d4cc 100644 --- a/crates/engine/src/tests_settings.rs +++ b/crates/engine/src/tests_settings.rs @@ -375,6 +375,41 @@ fn defaults_have_no_conflicts_and_match_the_menus() { assert!(t.bindables.len() > 500); } +/// Each Fast Previews and Show Channel mode is a command with a default key. +#[test] +fn fast_previews_and_show_channel_modes_have_default_keys() { + let s = Session::default(); + let t = s.shortcuts(); + for (id, key) in [ + ("view.fastPreviews.off", "Cmd+Alt+1"), + ("view.fastPreviews.adaptive", "Cmd+Alt+2"), + ("view.fastPreviews.draft", "Cmd+Alt+3"), + ("view.fastPreviews.fastDraft", "Cmd+Alt+4"), + ("view.fastPreviews.wireframe", "Cmd+Alt+5"), + ("view.channel.red", "Alt+1"), + ("view.channel.green", "Alt+2"), + ("view.channel.blue", "Alt+3"), + ("view.channel.alpha", "Alt+4"), + ("view.channel.redColorized", "Alt+Shift+1"), + ("view.channel.greenColorized", "Alt+Shift+2"), + ("view.channel.blueColorized", "Alt+Shift+3"), + ("view.channel.rgbStraight", "Alt+Shift+4"), + ] { + assert_eq!(t.shortcut_of(id, &json!(null)), Some(key), "{id}"); + } + assert!(t.all_conflicts().is_empty(), "{:?}", t.all_conflicts()); + // The commands set their mode; a channel's key again is RGB. + let mut s = Session::default(); + s.execute("view.fastPreviews.fastDraft", json!({})).unwrap(); + assert_eq!(s.state.viewer.fast_previews.id(), "fastDraft"); + s.execute("view.channel.redColorized", json!({})).unwrap(); + assert_eq!((s.state.viewer.channel.id(), s.state.viewer.colorized), ("red", true)); + s.execute("view.channel.green", json!({})).unwrap(); + assert_eq!((s.state.viewer.channel.id(), s.state.viewer.colorized), ("green", false)); + s.execute("view.channel.green", json!({})).unwrap(); + assert_eq!(s.state.viewer.channel.id(), "rgb"); +} + /// Cmd+Left / Cmd+Right Arrow step a frame like Page Up / Page Down (Shift: 10 frames). #[test] fn cmd_arrows_step_frames_like_page_keys() { diff --git a/docs/control-protocol.md b/docs/control-protocol.md index f5487981..75ef0e76 100644 --- a/docs/control-protocol.md +++ b/docs/control-protocol.md @@ -134,7 +134,9 @@ Viewer state that agents drive headless too: `view.snapping`, `view.snappingOpti `header.snappingOptions`: After Effects' Snap Edges Extended and Snap to Features in Collapsed Compositions and Text Layers), `view.channel {channel, colorized?}`, `view.exposure {stops | delta}`, `view.resetExposure`, `view.takeSnapshot`, `view.showSnapshot`, -`view.fastPreviewMode {mode}`, `view.res.full|half|third|quarter|auto {comp?}` and +`view.fastPreviewMode {mode}` (one mode each, for shortcuts: +`view.fastPreviews.off|adaptive|draft|fastDraft|wireframe`, and for the channels +`view.channel.red|green|blue|alpha|redColorized|greenColorized|blueColorized|rgbStraight`), `view.res.full|half|third|quarter|auto {comp?}` and `view.res.custom {factor, comp?}` (the comp's Resolution, saved with the project but no undo step; `ui.set {viewer: {res}}` sets the active comp's, and `ui.inspect` reports it as `ui.viewer.res`), `view.setRegionOfInterest {rect}`, `view.addGuide`,