mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-14 19:27:05 -04:00
Settings: Appearance > UI Scale sizes the whole interface (#284)
Settings > Appearance > UI Scale (appearance.uiScale, 75-200 %, default 100) scales the whole interface on top of the display's own scaling, live as it is chosen; Cancel puts it back, OK saves it and a saved scale applies from the first frame. The Settings dialog's choice rows show a number setting with choices as its text. Automation id: settings.appearance.uiScale. egui's own Ctrl+= / Ctrl+- / Ctrl+0 zoom is off now: Ctrl+0 (Window > Project) also reset the zoom behind the app's back, and the scale is a setting. Test: engine tests_settings ui_scale_is_a_percentage_from_75_to_200; ui_panels ui_scale_setting_sizes_the_interface. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
8e4d9988af
commit
7a9c459e42
7 files changed
+80
-2
No files matched your search
@@ -25,6 +25,9 @@ pub const PREFS_FILE: &str = "prefs.json";
|
||||
/// Settings ▸ General ▸ Language: (label, `general.language` value).
|
||||
pub const LANGUAGES: &[(&str, &str)] = &[("Match System", "system"), ("English", "en"), ("日本語", "ja"), ("简体中文", "zh-hans"), ("繁體中文", "zh-hant")];
|
||||
|
||||
/// Settings ▸ Appearance ▸ UI Scale: (label, `appearance.uiScale` percent).
|
||||
pub const UI_SCALES: &[(&str, &str)] = &[("75%", "75"), ("100%", "100"), ("125%", "125"), ("150%", "150"), ("175%", "175"), ("200%", "200")];
|
||||
|
||||
/// Settings ▸ Startup & Repair ▸ Window Graphics: (label, `startup.windowGraphics` value).
|
||||
pub const WINDOW_GRAPHICS: &[(&str, &str)] = &[("Automatic", "auto"), ("OpenGL (compatibility)", "gl")];
|
||||
|
||||
@@ -140,6 +143,8 @@ page!(Appearance {
|
||||
theme: String = "dark".into(),
|
||||
/// User interface brightness, -1 (darker) … 1 (lighter).
|
||||
brightness: f64 = 0.0,
|
||||
/// The size of the whole interface in percent (75–200), on top of the display's own scale.
|
||||
ui_scale: u32 = 100,
|
||||
use_label_color_for_handles: bool = true,
|
||||
use_label_color_for_tabs: bool = true,
|
||||
cycle_mask_colors: bool = true,
|
||||
@@ -454,6 +459,7 @@ impl Prefs {
|
||||
a.interval_minutes = a.interval_minutes.clamp(1, 240);
|
||||
a.max_versions = a.max_versions.clamp(1, 99);
|
||||
self.appearance.brightness = self.appearance.brightness.clamp(-1.0, 1.0);
|
||||
self.appearance.ui_scale = self.appearance.ui_scale.clamp(75, 200);
|
||||
let defaults = default_labels();
|
||||
self.labels.truncate(defaults.len());
|
||||
for d in defaults.iter().skip(self.labels.len()) {
|
||||
@@ -1009,6 +1015,7 @@ pub fn pages() -> Vec<Page> {
|
||||
items: vec![
|
||||
s("appearance.theme", "Theme", Kind::Choice(&[("Dark", "dark"), ("Darker", "darker"), ("Light", "light")]), true),
|
||||
s("appearance.brightness", "Brightness", Kind::Slider(-1.0, 1.0), true),
|
||||
s("appearance.uiScale", "UI Scale", Kind::Choice(UI_SCALES), true),
|
||||
Section("Labels and Colors"),
|
||||
s("appearance.useLabelColorForHandles", "Use Label Color for Layer Handles and Paths", B, true),
|
||||
s("appearance.useLabelColorForTabs", "Use Label Color for Related Tabs", B, true),
|
||||
|
||||
@@ -87,6 +87,28 @@ fn prefs_set_validates_keys_types_and_ranges() {
|
||||
assert!(p.reset(Some("bogus")).is_err());
|
||||
}
|
||||
|
||||
/// Settings ▸ Appearance ▸ UI Scale (#284): a percentage, 100 by default, kept within 75–200;
|
||||
/// the dialog's choices set it as a number.
|
||||
#[test]
|
||||
fn ui_scale_is_a_percentage_from_75_to_200() {
|
||||
let mut p = Prefs::default();
|
||||
assert_eq!(p.appearance.ui_scale, 100);
|
||||
p.set("appearance.uiScale", json!("150")).unwrap();
|
||||
assert_eq!(p.appearance.ui_scale, 150);
|
||||
assert_eq!(p.get("appearance.uiScale"), Some(json!(150)));
|
||||
p.set("appearance.uiScale", json!(500)).unwrap();
|
||||
assert_eq!(p.appearance.ui_scale, 200);
|
||||
p.set("appearance.uiScale", json!(10)).unwrap();
|
||||
assert_eq!(p.appearance.ui_scale, 75);
|
||||
let page = crate::prefs::pages().into_iter().find(|pg| pg.id == "appearance").unwrap();
|
||||
let listed = page.items.iter().any(|i| {
|
||||
matches!(i, crate::prefs::Item::Setting { key: "appearance.uiScale", kind: crate::prefs::Kind::Choice(c), live: true, .. } if c.contains(&("200%", "200")))
|
||||
});
|
||||
assert!(listed, "Settings ▸ Appearance offers it");
|
||||
// Older settings files without it get the default.
|
||||
assert_eq!(Prefs::from_json(r#"{"version": 2, "appearance": {"theme": "light"}}"#).appearance.ui_scale, 100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_schema_key_exists_and_docs_list_the_todo_settings() {
|
||||
let p = Prefs::default();
|
||||
|
||||
@@ -373,6 +373,10 @@ impl EffectcraftApp {
|
||||
theme::apply_visuals(ctx, &self.tokens);
|
||||
let tips = p.general.show_tool_tips;
|
||||
ctx.all_styles_mut(|s| s.interaction.tooltip_delay = if tips { 0.5 } else { f32::INFINITY });
|
||||
// Settings ▸ Appearance ▸ UI Scale (#284). egui's own Ctrl+= / Ctrl+- / Ctrl+0 zoom stays
|
||||
// off: Ctrl+0 is Window ▸ Project, and the scale is a setting.
|
||||
ctx.options_mut(|o| o.zoom_with_keyboard = false);
|
||||
ctx.set_zoom_factor(p.appearance.ui_scale as f32 / 100.0);
|
||||
self.ui.cache_when_idle = p.previews.cache_frames_when_idle;
|
||||
self.ui.viewer.fast_preview = p.previews.fast_previews;
|
||||
self.frames.set_budget(p.cache_budgets(self.session.sys_memory).preview);
|
||||
|
||||
@@ -141,7 +141,8 @@ fn page_ui(app: &mut EffectcraftApp, ui: &mut egui::Ui, page: &Page, cur: &Value
|
||||
r
|
||||
}
|
||||
Kind::Choice(opts) => {
|
||||
let curv = v.as_str().unwrap_or("").to_string();
|
||||
// (A number with choices, UI Scale, shows as its text.)
|
||||
let curv = v.as_str().map_or_else(|| v.to_string(), str::to_string);
|
||||
let shown = opts.iter().find(|o| o.1 == curv).map(|o| o.0).unwrap_or(curv.as_str()).to_string();
|
||||
egui::ComboBox::from_id_salt(("settings", key))
|
||||
.selected_text(shown)
|
||||
|
||||
@@ -190,6 +190,48 @@ fn leaving_the_learn_workspace_closes_its_tutorials() {
|
||||
}
|
||||
}
|
||||
|
||||
/// #284: Settings ▸ Appearance ▸ UI Scale sizes the whole interface as soon as it is chosen,
|
||||
/// Cancel puts it back, OK keeps it; Ctrl+0 (Window ▸ Project) doesn't reset it.
|
||||
#[test]
|
||||
fn ui_scale_setting_sizes_the_interface() {
|
||||
let mut h = harness();
|
||||
let ctx = h.ctx.clone();
|
||||
let choose = |h: &mut Harness<'_, EffectcraftApp>, label: &str| {
|
||||
let ctx = h.ctx.clone();
|
||||
effectcraft_ui_egui::menus::invoke(h.state_mut(), &ctx, "app.settings", json!({"page": "appearance"})).unwrap();
|
||||
h.run_steps(3);
|
||||
click(h, "settings.appearance.uiScale");
|
||||
let option = h.query_by_label(label).unwrap_or_else(|| panic!("the {label} choice")).rect().center();
|
||||
click_at(h, option);
|
||||
h.run_steps(2);
|
||||
};
|
||||
assert_eq!(ctx.zoom_factor(), 1.0);
|
||||
choose(&mut h, "150%");
|
||||
assert_eq!(h.state().session.prefs.appearance.ui_scale, 150);
|
||||
assert_eq!(ctx.zoom_factor(), 1.5, "live");
|
||||
click(&mut h, "settings.cancel");
|
||||
h.run_steps(2);
|
||||
assert_eq!((h.state().session.prefs.appearance.ui_scale, ctx.zoom_factor()), (100, 1.0), "Cancel put it back");
|
||||
choose(&mut h, "125%");
|
||||
click(&mut h, "settings.ok");
|
||||
h.run_steps(2);
|
||||
assert_eq!(h.state().dialog, None);
|
||||
assert_eq!((h.state().session.prefs.appearance.ui_scale, ctx.zoom_factor()), (125, 1.25), "OK kept it");
|
||||
// Ctrl+0 opens the Project panel; egui's own Ctrl+0 zoom reset stays out of it.
|
||||
for pressed in [true, false] {
|
||||
h.input_mut().events.push(Event::Key { key: egui::Key::Num0, physical_key: None, pressed, repeat: false, modifiers: egui::Modifiers::COMMAND });
|
||||
h.step();
|
||||
}
|
||||
h.run_steps(2);
|
||||
assert_eq!(ctx.zoom_factor(), 1.25);
|
||||
// A saved scale applies from the first frame.
|
||||
let mut s = Session::default();
|
||||
s.prefs.set("appearance.uiScale", json!(175)).unwrap();
|
||||
let mut h2 = Harness::builder().with_size(egui::vec2(1600.0, 1000.0)).build_eframe(|_| EffectcraftApp::new(s));
|
||||
h2.run_steps(3);
|
||||
assert_eq!(h2.ctx.zoom_factor(), 1.75);
|
||||
}
|
||||
|
||||
/// Headless look at the panels (wgpu offscreen; needs a GPU adapter). Run with
|
||||
/// `PANELS_SNAPSHOT=/abs/dir cargo test -p effectcraft-ui-egui --test ui_panels -- --ignored`.
|
||||
#[test]
|
||||
|
||||
+1
-1
@@ -171,7 +171,7 @@ the disk cache in the formats wave; the 3D Camera Tracker in M12.6.)
|
||||
| Web | 98% | 0.1 | No shared-memory threads inside one engine instance (the decided design is one engine instance per worker; a threaded build needs nightly `build-std`) (M13.30: Render Queue, analyses and Content-Aware Fill render on each job worker's own WebGPU device in passes, particles and Advanced 3D read back under keys, layer buffers in the browser's disk cache; browser storage, Web Audio, Web Worker renders/analyses, WebGPU viewer and offline install landed in M15.2; viewer frames in frame workers fed by project diffs, Roto Brush propagation in a worker, non-blocking `wait: true` jobs and a browser Media Browser (File System Access folders, browser storage) in M13.10; WEB-1 in M13.24: GPU effects and the GPU compositor in the frame workers on their own WebGPU devices with deferred readbacks (frames render in passes; Backend Auto per comp), the disk cache in the Origin Private File System (written by the workers with sync access handles, LRU under the settings' limit, served after a reload), the storage manager (Settings ▸ Disk ▸ Browser Storage, `storage.info` / `storage.persist` / `storage.clear`), and a WGSL constant Chrome rejected (it disabled every GPU kernel in Chrome) fixed) |
|
||||
| 3D | 88% | 5.5 | reflections of other layers (Advanced 3D surfaces reflect only the Environment light; Material Options ▸ Appears in Reflections is stored but has no visible effect yet, #261), multi-view layouts, the Extended Viewer for Advanced 3D comps (Classic 3D Extended Viewer landed in M13.5 UI completion); collapsed precomps of another size seen through the parent's camera render (fixed in M13.2); stereo rigs, orbit nulls, lights controlled by the camera, cameras/lights from glTF models, environment backgrounds, Advanced 3D motion blur, blend modes and track mattes landed in M7.7; Classic 3D iris-shaped bokeh with highlights, progressive depth of field on tilted layers and the focus-link commands landed in M13.5; Advanced 3D depth of field with the iris and highlight options, collapsed precomps as real Advanced 3D geometry and extruded text/shape strokes landed in M13.8; Advanced 3D (glTF/OBJ models, primitives, extruded text and shapes, PBR, image-based light, shadow maps, GPU rasteriser) in M7.4–M7.6; Advanced 3D end to end on the GPU (motion blur, iris depth of field, compositing) in M13.11 |
|
||||
| Effects | ≈ 85% | 3.0 | GPU versions of the remaining effects (280 run on the GPU since M13.29, EFF-5: M13.28 added the remaining pixel effects and the geometry and audio generators (Lightning, Advanced Lightning, Beam, Lens Flare, Radio Waves, Vegas, Stroke, Scribble, Write-on, Paint Bucket, Eyedropper Fill, CC Glue Gun, CC Threads, Audio Spectrum / Waveform, Basic / Path Text); M13.29 added the particle effects (CC Particle World / Systems II, Particle Playground, CC Ball Action, CC Pixel Polly, CC Scatterize, Curl Noise) and removed the fallbacks of Shatter's wireframe views, Foam's extras, Timewarp's Matte Layer, strong Warp Fisheye / Twist bends and custom `.ocio` configs; M13.23 added the 3D Channel and Immersive Video families, Apply Color LUT, the OCIO effects, Color Profile Converter and the simulations' render passes (Card Wipe included); M13.22 added the CC light family, the CC transitions, Block Dissolve, Radial Shadow, CC Bender / Blobbylize / Cylinder / Sphere / Spotlight / Environment, 3D Glasses, Numbers, Timecode and the time effects joined the 166 of M13.13; Fractal and the analysis / tool effects still render on the CPU) and the missing controls listed as partial in [effects.md](effects.md) (every After Effects effect exists since M9.11, M12.5 and M12.6; parameter names, order, twirl-downs, popups, units and defaults were aligned in M9.12) |
|
||||
| Interface | 75% | 5.0 | more Learn tutorials and pixel-level fidelity of dialogs (G2 on 9 October: the in-window menu bar works from the keyboard (Alt, arrows, Enter, Escape) and the app's shortcuts wait while a menu is open, #279; the Timeline's composition marker bin and numpad * add comp markers, #275; G2 on 7 October: Project items, files and effects dropped on the Composition viewer, Project items dropped in the Timeline between layers and at a time, #85, #88, #89; popup menus moved to fit the window take clicks, so Render Queue templates apply, #117; View ▸ New Viewer landed in M13.32; an automation id on every control of Composition and Solid Settings, Group / Ungroup Shapes in the Layer menu, Window ▸ Learn, the flowchart shortcuts and Reveal in Explorer landed in M3.10 and M3.13; the Home ▸ Templates gallery (eight original built-in templates, user templates from File ▸ Save as Template…) and View ▸ Simulate Output ▸ My Custom RGB… landed in M13.25; Timeline layer reordering by drag, a non-snapping viewer pan, a working rename field and twirl arrows, and the full set of property reveal shortcuts — double presses, Alt+Shift keyframes, Ctrl+` — landed in M13.17–M13.20; the Home ▸ Learn tab with interactive tutorials and a UI fidelity pass landed in M13.5 UI completion; visual editors for Lumetri RGB / hue-saturation curves, Colorama's output cycle wheel, Glow's colour map and Reshape's correspondence points (viewer handles), a scrolling Preview panel and AE-style Composition / Timeline tabs (close, label swatch, viewer lock) landed in M13.10; Timeline outline and Project panel columns scroll horizontally, the Layer Style dialog, ROI resize handles, Pan Behind snapping and 3D Reference Axes landed in M13.5; native macOS menu bar, Timeline columns/search/reveal-add, Home screen with recent projects and all AE workspaces landed; viewer rulers/snapping/channels/snapshots landed in M0.13) |
|
||||
| Interface | 75% | 5.0 | more Learn tutorials and pixel-level fidelity of dialogs (G2 on 9 October: the in-window menu bar works from the keyboard (Alt, arrows, Enter, Escape) and the app's shortcuts wait while a menu is open, #279; the Timeline's composition marker bin and numpad * add comp markers, #275; Settings ▸ Appearance ▸ UI Scale (75–200 %, beyond After Effects, which follows the system's scaling), #284; G2 on 7 October: Project items, files and effects dropped on the Composition viewer, Project items dropped in the Timeline between layers and at a time, #85, #88, #89; popup menus moved to fit the window take clicks, so Render Queue templates apply, #117; View ▸ New Viewer landed in M13.32; an automation id on every control of Composition and Solid Settings, Group / Ungroup Shapes in the Layer menu, Window ▸ Learn, the flowchart shortcuts and Reveal in Explorer landed in M3.10 and M3.13; the Home ▸ Templates gallery (eight original built-in templates, user templates from File ▸ Save as Template…) and View ▸ Simulate Output ▸ My Custom RGB… landed in M13.25; Timeline layer reordering by drag, a non-snapping viewer pan, a working rename field and twirl arrows, and the full set of property reveal shortcuts — double presses, Alt+Shift keyframes, Ctrl+` — landed in M13.17–M13.20; the Home ▸ Learn tab with interactive tutorials and a UI fidelity pass landed in M13.5 UI completion; visual editors for Lumetri RGB / hue-saturation curves, Colorama's output cycle wheel, Glow's colour map and Reshape's correspondence points (viewer handles), a scrolling Preview panel and AE-style Composition / Timeline tabs (close, label swatch, viewer lock) landed in M13.10; Timeline outline and Project panel columns scroll horizontally, the Layer Style dialog, ROI resize handles, Pan Behind snapping and 3D Reference Axes landed in M13.5; native macOS menu bar, Timeline columns/search/reveal-add, Home screen with recent projects and all AE workspaces landed; viewer rulers/snapping/channels/snapshots landed in M0.13) |
|
||||
| Project | ≈ 68% | 5.5 | OCIO displays beyond the built-in tone map; relative footage paths and relinking when a project moves (unsaved-changes prompts, a modified mark that follows undo and project files that name their version and always reopen landed in M3.9 and M3.14; auto-save and crash recovery in M3.7; Color Engine with OCIO/ACES working spaces, HDR compand/tone mapping, Rec. 2100 PQ/HLG output, Feet + Frames, display colour management, Simulate Output and the locked viewer landed in M7.7; proxies and Interpret Footage fields / pixel aspect / alpha guess landed: PRJ-8, PRJ-3) |
|
||||
| Masks & roto | 74% | 5.0 | (audit estimates; Roto Brush 2.0 / 3.0's trained model, MobileSAM in the swappable `segment` module, has since landed in M13.35; variable-width mask feather points with the Mask Feather tool landed in M13.5; mask tracking and Mask Interpolation landed in M6.6; Roto Brush & Refine Edge with graph-cut segmentation, flow propagation, edge matting, decontamination and Freeze in M6.7) |
|
||||
| Preview | 77% | 2.8 | GPU effects for the audio-driven effects and the stepped particle systems; content-keyed RAM preview frames (an edit drops every comp's), audio scrubbing, more snapshot slots (G2 on 7 October: Audio, Lock and Shy switches keep the cached frames and playback with audio waits for frames instead of skipping them, #103; running out of video memory renders the frame on the CPU and keeps fewer GPU frames instead of crashing, #106; RAM preview keys with the render options, least-recently-shown eviction, Auto resolution while playing, Cache Before Playback that fits, an achieved-fps readout, Cache Frames When Idle and purges that clear what they name landed in M4.5–M4.8; 3D channel, VR, OCIO, Card Wipe and the simulations' render passes run on the GPU since M13.23; Advanced 3D layers with blend modes / track mattes / Preserve Transparency and environment backgrounds composite on the GPU since M13.22; pooled GPU textures and fused quantisation since M13.13: Lower Third GPU warm 21 → 4 ms/frame, and Auto now picks the CPU or the GPU per comp from measured frame times; Advanced 3D runs — raster, motion blur, iris depth of field, compositing — and wireframes run on the GPU since M13.11: CPU warm 3394 → GPU warm 290 ms/frame at 1920×1080 (11.7×; Half 968 → 85 ms), GPU ≠ CPU on 0.005 % of pixels, on an M4 Pro under load; Classic 3D bokeh depth of field — iris shapes, highlights, fringe, progressive blur on tilted planes — runs in WGSL since M13.6: 3D Showcase GPU warm 205 → 54 ms/frame at full size on an M4 Pro; the Preview panel's five shortcuts with their own Include / Loop / Cache Before Playback / Range / Play From / Frame Rate / Skip / Resolution / Full Screen / stop options and `playback.settings.get/set` landed in M13.8; Classic 3D runs and adjustment layers composite on the GPU since M12.7; persistent disk cache with the blue cache bar landed; region of interest, snapshots, exposure and Fast Previews landed in M0.13) |
|
||||
|
||||
@@ -56,6 +56,8 @@ Commands (CLI, MCP, control channel):
|
||||
- `previews.fastPreviews`: Fast Previews (Draft 3D, Faster Effects)
|
||||
- `appearance.theme`: Theme
|
||||
- `appearance.brightness`: Brightness
|
||||
- `appearance.uiScale`: UI Scale, the size of the whole interface (75–200 %, on top of the
|
||||
display's own scaling), applied as soon as it is chosen
|
||||
- `appearance.useLabelColorForHandles`: Use Label Color for Layer Handles and Paths
|
||||
- `grids.gridColor`: Color
|
||||
- `grids.gridSpacing`: Gridline Every
|
||||
|
||||
Reference in new issue
Block a user