UI-1: every After Effects workspace
Adds Review, Learn, Essential Graphics, Color and Undocked Panels to the built-in workspaces (Libraries, an Adobe service, is left out), lists them all in Window > Workspace in After Effects' order, and gives each its own layout: Review (large viewer, Markers and Project), Learn (Default plus the Home screen with the community links), Essential Graphics (Properties panel full height, type panels), Color (wide Effect Controls, Info), Undocked Panels (Composition docked, the other panels floating). Tests check every AE workspace exists, has its own layout, has each panel once and is in the menu. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
541c740f18
commit
6aca8fe85c
|
|
@ -17,7 +17,8 @@
|
|||
//!
|
||||
//! An entry's shortcut is the command's default shortcut unless the line gives one (needed for
|
||||
//! entries with bound parameters). Adobe-service entries (Team Projects, Libraries, Bridge, Media
|
||||
//! Encoder, Essential Graphics, Behance, Creative Cloud…) are intentionally absent.
|
||||
//! Encoder, Behance, Creative Cloud…) are intentionally absent; the Essential Graphics workspace
|
||||
//! uses the Properties panel.
|
||||
|
||||
use std::sync::OnceLock;
|
||||
|
||||
|
|
@ -909,16 +910,22 @@ View
|
|||
Enter Full Screen | view.fullScreen
|
||||
Window
|
||||
Workspace
|
||||
Default | window.workspace {"name":"Default"} | Shift+F10
|
||||
Review | window.workspace {"name":"Review"}
|
||||
Learn | window.workspace {"name":"Learn"}
|
||||
Small Screen | window.workspace {"name":"Small Screen"} | Shift+F12
|
||||
Standard | window.workspace {"name":"Standard"} | Shift+F11
|
||||
---
|
||||
All Panels | window.workspace {"name":"All Panels"}
|
||||
Animation | window.workspace {"name":"Animation"}
|
||||
Default | window.workspace {"name":"Default"} | Shift+F10
|
||||
Color | window.workspace {"name":"Color"}
|
||||
Effects | window.workspace {"name":"Effects"}
|
||||
Essential Graphics | window.workspace {"name":"Essential Graphics"}
|
||||
Minimal | window.workspace {"name":"Minimal"}
|
||||
Motion Tracking | window.workspace {"name":"Motion Tracking"}
|
||||
Paint | window.workspace {"name":"Paint"}
|
||||
Small Screen | window.workspace {"name":"Small Screen"} | Shift+F12
|
||||
Standard | window.workspace {"name":"Standard"} | Shift+F11
|
||||
Text | window.workspace {"name":"Text"}
|
||||
Undocked Panels | window.workspace {"name":"Undocked Panels"}
|
||||
---
|
||||
Reset to Saved Layout | window.resetWorkspace
|
||||
Save Changes to this Workspace | window.saveWorkspace
|
||||
|
|
|
|||
|
|
@ -172,7 +172,43 @@ fn vsplit(size: SplitSize, a: DockNode, b: DockNode) -> DockNode {
|
|||
DockNode::Split { vertical: true, size, a: Box::new(a), b: Box::new(b) }
|
||||
}
|
||||
|
||||
pub const WORKSPACES: [&str; 10] = ["Default", "Standard", "Small Screen", "Animation", "Effects", "Motion Tracking", "Paint", "Text", "Minimal", "All Panels"];
|
||||
/// The built-in workspaces: After Effects 2026's (Window ▸ Workspace), less Libraries (an Adobe
|
||||
/// service). Learn shows the Home screen (community links instead of Adobe's tutorials);
|
||||
/// Essential Graphics uses the Properties panel; Undocked Panels floats the panels over the
|
||||
/// Composition panel.
|
||||
pub const WORKSPACES: &[&str] = &[
|
||||
"Default",
|
||||
"Standard",
|
||||
"Small Screen",
|
||||
"Animation",
|
||||
"Effects",
|
||||
"Motion Tracking",
|
||||
"Paint",
|
||||
"Text",
|
||||
"Minimal",
|
||||
"All Panels",
|
||||
"Review",
|
||||
"Learn",
|
||||
"Essential Graphics",
|
||||
"Color",
|
||||
"Undocked Panels",
|
||||
];
|
||||
|
||||
/// The floating panels a workspace starts with (Undocked Panels), in points for a window of
|
||||
/// about 1680×1020 (they are kept inside the window when drawn).
|
||||
pub fn workspace_floating(name: &str) -> Vec<Floating> {
|
||||
use PanelKind::*;
|
||||
let f = |panels: &[PanelKind], rect: [f32; 4]| Floating { panels: panels.to_vec(), active: 0, rect };
|
||||
match name {
|
||||
"Undocked Panels" => vec![
|
||||
f(&[Project, EffectControls], [24.0, 110.0, 320.0, 420.0]),
|
||||
f(&[Preview, Info, Audio], [1330.0, 110.0, 320.0, 300.0]),
|
||||
f(&[EffectsPresets, Properties, Character, Paragraph], [1330.0, 430.0, 320.0, 360.0]),
|
||||
f(&[Timeline, RenderQueue], [24.0, 640.0, 1280.0, 340.0]),
|
||||
],
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// The default layout of a named workspace.
|
||||
pub fn workspace(name: &str) -> DockNode {
|
||||
|
|
@ -226,6 +262,40 @@ pub fn workspace(name: &str) -> DockNode {
|
|||
tabs(&[Timeline], 0),
|
||||
),
|
||||
"Minimal" => vsplit(Ratio(0.6), tabs(&[Composition], 0), tabs(&[Timeline], 0)),
|
||||
// Review: a large viewer with the markers and the project beside it.
|
||||
"Review" => vsplit(
|
||||
Ratio(0.66),
|
||||
hsplit(FixedA(280.0), tabs(&[Project, Markers], 0), hsplit(FixedB(260.0), tabs(&[Composition, Layer], 0), right(&[Preview, Info], &[Audio]))),
|
||||
tabs(&[Timeline, RenderQueue], 0),
|
||||
),
|
||||
// Essential Graphics: the Properties panel (After Effects' successor to the Essential
|
||||
// Graphics panel) full height on the right, type panels beside the viewer.
|
||||
"Essential Graphics" => hsplit(
|
||||
FixedB(320.0),
|
||||
vsplit(
|
||||
Ratio(0.58),
|
||||
hsplit(
|
||||
FixedA(280.0),
|
||||
tabs(&[Project, EffectControls], 0),
|
||||
hsplit(FixedB(260.0), tabs(&[Composition, Layer], 0), right(&[Character, Paragraph], &[Align, Preview])),
|
||||
),
|
||||
tabs(&[Timeline], 0),
|
||||
),
|
||||
stack(&[(Properties, true, None), (EffectsPresets, false, None)]),
|
||||
),
|
||||
// Color: wide Effect Controls for grading effects, Info for colour readouts.
|
||||
"Color" => vsplit(
|
||||
Ratio(0.6),
|
||||
hsplit(
|
||||
FixedA(360.0),
|
||||
tabs(&[EffectControls, Project], 0),
|
||||
hsplit(FixedB(300.0), tabs(&[Composition, Layer], 0), right(&[Info, Preview], &[EffectsPresets])),
|
||||
),
|
||||
tabs(&[Timeline], 0),
|
||||
),
|
||||
// Undocked Panels: the Composition panel docked, the others floating
|
||||
// ([`workspace_floating`]).
|
||||
"Undocked Panels" => tabs(&[Composition, Layer, Flowchart], 0),
|
||||
"Small Screen" => vsplit(
|
||||
Ratio(0.55),
|
||||
hsplit(
|
||||
|
|
@ -814,8 +884,59 @@ mod tests {
|
|||
fn workspaces_contain_core_panels() {
|
||||
for w in WORKSPACES {
|
||||
let d = workspace(w);
|
||||
assert!(d.contains(PanelKind::Timeline), "{w}");
|
||||
let floating: Vec<PanelKind> = workspace_floating(w).into_iter().flat_map(|f| f.panels).collect();
|
||||
assert!(d.contains(PanelKind::Timeline) || floating.contains(&PanelKind::Timeline), "{w}");
|
||||
assert!(d.contains(PanelKind::Composition) || d.contains(PanelKind::Layer), "{w}");
|
||||
// A panel is in one place only.
|
||||
let mut all = vec![];
|
||||
d.panels(&mut all);
|
||||
all.extend(floating);
|
||||
let n = all.len();
|
||||
all.sort_by_key(|p| p.id());
|
||||
all.dedup();
|
||||
assert_eq!(all.len(), n, "{w} has a panel twice");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn after_effects_workspaces_exist() {
|
||||
// Window ▸ Workspace in After Effects 2026, less Libraries (an Adobe service).
|
||||
for w in [
|
||||
"Default",
|
||||
"Review",
|
||||
"Learn",
|
||||
"Small Screen",
|
||||
"Standard",
|
||||
"All Panels",
|
||||
"Animation",
|
||||
"Essential Graphics",
|
||||
"Color",
|
||||
"Effects",
|
||||
"Minimal",
|
||||
"Motion Tracking",
|
||||
"Paint",
|
||||
"Text",
|
||||
"Undocked Panels",
|
||||
] {
|
||||
assert!(WORKSPACES.contains(&w), "{w}");
|
||||
}
|
||||
assert!(!WORKSPACES.contains(&"Libraries"));
|
||||
// Layouts differ from Default (except Learn, which is Default plus the Home screen).
|
||||
let def = workspace("Default");
|
||||
for w in WORKSPACES.iter().filter(|w| !matches!(**w, "Default" | "Learn")) {
|
||||
assert_ne!(workspace(w), def, "{w} falls back to Default");
|
||||
}
|
||||
assert!(workspace("Essential Graphics").contains(PanelKind::Properties));
|
||||
assert!(workspace("Color").contains(PanelKind::EffectControls));
|
||||
assert!(!workspace_floating("Undocked Panels").is_empty());
|
||||
// Every built-in workspace is in Window ▸ Workspace.
|
||||
let menu: Vec<String> = effectcraft_engine::menus::entries()
|
||||
.into_iter()
|
||||
.filter(|(_, e)| e.command == "window.workspace")
|
||||
.filter_map(|(_, e)| e.params.get("name").and_then(|v| v.as_str()).map(str::to_string))
|
||||
.collect();
|
||||
for w in WORKSPACES {
|
||||
assert!(menu.iter().any(|m| m == w), "{w} missing from Window > Workspace");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -279,8 +279,12 @@ impl EffectcraftApp {
|
|||
pub fn set_workspace(&mut self, name: &str) {
|
||||
self.ui.workspace = name.to_string();
|
||||
self.ui.dock = self.ui.saved_workspaces.get(name).cloned().unwrap_or_else(|| dock::workspace(name));
|
||||
self.ui.floating = self.ui.saved_floating.get(name).cloned().unwrap_or_default();
|
||||
self.ui.floating = self.ui.saved_floating.get(name).cloned().unwrap_or_else(|| dock::workspace_floating(name));
|
||||
self.ui.maximized = None;
|
||||
// Learn: the Home screen (community links) in the Composition panel.
|
||||
if name == "Learn" {
|
||||
self.ui.start_screen = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Built-in workspaces followed by the saved ones (Window ▸ Workspace ▸ Save as New Workspace).
|
||||
|
|
|
|||
Loading…
Reference in New Issue