From 11066418bb8a114d38881b40b3ffc9027a306682 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Sun, 11 Jan 2026 15:41:37 +0000 Subject: [PATCH 01/28] wip --- desktop/src/app.rs | 5 +++++ desktop/src/window.rs | 10 +++++++++- desktop/wrapper/src/intercept_frontend_message.rs | 3 +++ desktop/wrapper/src/messages.rs | 1 + editor/src/messages/app_window/app_window_message.rs | 1 + .../messages/app_window/app_window_message_handler.rs | 3 +++ editor/src/messages/frontend/frontend_message.rs | 1 + editor/src/messages/input_mapper/input_mappings.rs | 3 +++ 8 files changed, 26 insertions(+), 1 deletion(-) diff --git a/desktop/src/app.rs b/desktop/src/app.rs index 1648616f29..004d44c5b2 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -352,6 +352,11 @@ impl App { window.toggle_maximize(); } } + DesktopFrontendMessage::WindowFullscreen => { + if let Some(window) = &self.window { + window.toggle_fullscreen(); + } + } DesktopFrontendMessage::WindowDrag => { if let Some(window) = &self.window { window.start_drag(); diff --git a/desktop/src/window.rs b/desktop/src/window.rs index 249ba2d455..914c7b29cd 100644 --- a/desktop/src/window.rs +++ b/desktop/src/window.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::sync::Arc; use winit::cursor::{CursorIcon, CustomCursor, CustomCursorSource}; use winit::event_loop::ActiveEventLoop; -use winit::window::{Window as WinitWindow, WindowAttributes}; +use winit::window::{Fullscreen, Window as WinitWindow, WindowAttributes}; use crate::consts::APP_NAME; use crate::event::AppEventScheduler; @@ -118,6 +118,14 @@ impl Window { self.winit_window.is_maximized() } + pub(crate) fn toggle_fullscreen(&self) { + if self.is_fullscreen() { + self.winit_window.set_fullscreen(None); + } else { + self.winit_window.set_fullscreen(Some(Fullscreen::Borderless(None))); + } + } + pub(crate) fn is_fullscreen(&self) -> bool { self.winit_window.fullscreen().is_some() } diff --git a/desktop/wrapper/src/intercept_frontend_message.rs b/desktop/wrapper/src/intercept_frontend_message.rs index baa8000b39..9b64020eb5 100644 --- a/desktop/wrapper/src/intercept_frontend_message.rs +++ b/desktop/wrapper/src/intercept_frontend_message.rs @@ -148,6 +148,9 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD FrontendMessage::WindowMaximize => { dispatcher.respond(DesktopFrontendMessage::WindowMaximize); } + FrontendMessage::WindowFullscreen => { + dispatcher.respond(DesktopFrontendMessage::WindowFullscreen); + } FrontendMessage::WindowDrag => { dispatcher.respond(DesktopFrontendMessage::WindowDrag); } diff --git a/desktop/wrapper/src/messages.rs b/desktop/wrapper/src/messages.rs index f9d4bc5ecc..08c31df36a 100644 --- a/desktop/wrapper/src/messages.rs +++ b/desktop/wrapper/src/messages.rs @@ -69,6 +69,7 @@ pub enum DesktopFrontendMessage { WindowClose, WindowMinimize, WindowMaximize, + WindowFullscreen, WindowDrag, WindowHide, WindowHideOthers, diff --git a/editor/src/messages/app_window/app_window_message.rs b/editor/src/messages/app_window/app_window_message.rs index 7a988c6cde..0dcc39ae5d 100644 --- a/editor/src/messages/app_window/app_window_message.rs +++ b/editor/src/messages/app_window/app_window_message.rs @@ -11,6 +11,7 @@ pub enum AppWindowMessage { Close, Minimize, Maximize, + Fullscreen, Drag, Hide, HideOthers, diff --git a/editor/src/messages/app_window/app_window_message_handler.rs b/editor/src/messages/app_window/app_window_message_handler.rs index b862d3b18e..0d56f7c4ee 100644 --- a/editor/src/messages/app_window/app_window_message_handler.rs +++ b/editor/src/messages/app_window/app_window_message_handler.rs @@ -30,6 +30,9 @@ impl MessageHandler for AppWindowMessageHandler { AppWindowMessage::Maximize => { responses.add(FrontendMessage::WindowMaximize); } + AppWindowMessage::Fullscreen => { + responses.add(FrontendMessage::WindowFullscreen); + } AppWindowMessage::Drag => { responses.add(FrontendMessage::WindowDrag); } diff --git a/editor/src/messages/frontend/frontend_message.rs b/editor/src/messages/frontend/frontend_message.rs index e6c724069f..912cc205f9 100644 --- a/editor/src/messages/frontend/frontend_message.rs +++ b/editor/src/messages/frontend/frontend_message.rs @@ -371,6 +371,7 @@ pub enum FrontendMessage { WindowClose, WindowMinimize, WindowMaximize, + WindowFullscreen, WindowDrag, WindowHide, WindowHideOthers, diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index 9e774b6c25..a570d2082a 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -54,6 +54,9 @@ pub fn input_mappings() -> Mapping { // Hack to prevent Left Click + Accel + Z combo (this effectively blocks you from making a double undo with AbortTransaction) entry!(KeyDown(KeyZ); modifiers=[Accel, MouseLeft], action_dispatch=DocumentMessage::Noop), // + // AppWindowMessage + entry!(KeyDown(F11); action_dispatch=AppWindowMessage::Fullscreen), + // // ClipboardMessage entry!(KeyDown(KeyX); modifiers=[Accel], action_dispatch=ClipboardMessage::Cut), entry!(KeyDown(KeyC); modifiers=[Accel], action_dispatch=ClipboardMessage::Copy), From 81ee17f16d6dab3f3e9601921250032d11711438 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Sun, 11 Jan 2026 18:53:17 +0000 Subject: [PATCH 02/28] fix --- desktop/src/window.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/desktop/src/window.rs b/desktop/src/window.rs index 914c7b29cd..f3af68b170 100644 --- a/desktop/src/window.rs +++ b/desktop/src/window.rs @@ -2,7 +2,8 @@ use std::collections::HashMap; use std::sync::Arc; use winit::cursor::{CursorIcon, CustomCursor, CustomCursorSource}; use winit::event_loop::ActiveEventLoop; -use winit::window::{Fullscreen, Window as WinitWindow, WindowAttributes}; +use winit::monitor::Fullscreen; +use winit::window::{Window as WinitWindow, WindowAttributes}; use crate::consts::APP_NAME; use crate::event::AppEventScheduler; From 0489860cbdae8b11e912e808f5bf5aed75fa497c Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Sun, 11 Jan 2026 18:58:21 +0000 Subject: [PATCH 03/28] fix web --- frontend/src/io-managers/input.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/io-managers/input.ts b/frontend/src/io-managers/input.ts index 05d7cc8b31..8434eeab91 100644 --- a/frontend/src/io-managers/input.ts +++ b/frontend/src/io-managers/input.ts @@ -91,8 +91,8 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli // Don't redirect paste in web if (key === "KeyV" && accelKey) return false; - // Don't redirect a fullscreen request - if (key === "F11" && e.type === "keydown" && !e.repeat) { + // Don't redirect a fullscreen request on web + if (key === "F11" && e.type === "keydown" && !e.repeat && isDesktop()) { e.preventDefault(); fullscreen.toggleFullscreen(); return false; From 1389d4ba8ddc2c15b7c5398cba3494c19fb8f080 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Sun, 11 Jan 2026 19:08:32 +0000 Subject: [PATCH 04/28] fix --- frontend/src/io-managers/input.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/io-managers/input.ts b/frontend/src/io-managers/input.ts index 8434eeab91..47f2b9e0e1 100644 --- a/frontend/src/io-managers/input.ts +++ b/frontend/src/io-managers/input.ts @@ -92,7 +92,7 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli if (key === "KeyV" && accelKey) return false; // Don't redirect a fullscreen request on web - if (key === "F11" && e.type === "keydown" && !e.repeat && isDesktop()) { + if (key === "F11" && e.type === "keydown" && !e.repeat && !isDesktop()) { e.preventDefault(); fullscreen.toggleFullscreen(); return false; From e706112f046269a5d8a467289c8245233c1a9eec Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Sun, 11 Jan 2026 23:25:08 +0000 Subject: [PATCH 05/28] fix --- editor/src/messages/app_window/app_window_message_handler.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/editor/src/messages/app_window/app_window_message_handler.rs b/editor/src/messages/app_window/app_window_message_handler.rs index 0d56f7c4ee..66c4ef0bab 100644 --- a/editor/src/messages/app_window/app_window_message_handler.rs +++ b/editor/src/messages/app_window/app_window_message_handler.rs @@ -51,6 +51,7 @@ impl MessageHandler for AppWindowMessageHandler { Close, Minimize, Maximize, + Fullscreen, Drag, Hide, HideOthers, From 0a3872f52205dbeaedc96037b43c9d3cff37869f Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Tue, 13 Jan 2026 15:39:31 +0000 Subject: [PATCH 06/28] Use default fullscreen shortcut on mac --- .../messages/input_mapper/input_mappings.rs | 15 ++---- .../input_mapper/utility_types/macros.rs | 47 ++++++++++++++++--- .../input_mapper/utility_types/misc.rs | 2 + 3 files changed, 47 insertions(+), 17 deletions(-) diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index a570d2082a..48abf5f47c 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -55,7 +55,9 @@ pub fn input_mappings() -> Mapping { entry!(KeyDown(KeyZ); modifiers=[Accel, MouseLeft], action_dispatch=DocumentMessage::Noop), // // AppWindowMessage - entry!(KeyDown(F11); action_dispatch=AppWindowMessage::Fullscreen), + entry!(KeyDown(F11); active=cfg!(not(target_os = "macos")), action_dispatch=AppWindowMessage::Fullscreen), + entry!(KeyDown(KeyF); modifiers=[Accel, Meta], active=cfg!(target_os = "macos"), action_dispatch=AppWindowMessage::Fullscreen), + entry!(KeyDown(KeyQ); modifiers=[Accel], active=cfg!(target_os = "macos"), action_dispatch=AppWindowMessage::Close), // // ClipboardMessage entry!(KeyDown(KeyX); modifiers=[Accel], action_dispatch=ClipboardMessage::Cut), @@ -474,7 +476,7 @@ pub fn input_mappings() -> Mapping { // Sort `pointer_shake` sort(&mut pointer_shake); - let mut mapping = Mapping { + Mapping { key_up, key_down, key_up_no_repeat, @@ -483,16 +485,7 @@ pub fn input_mappings() -> Mapping { wheel_scroll, pointer_move, pointer_shake, - }; - - if cfg!(target_os = "macos") { - let remove: [&[&[MappingEntry; 0]; 0]; 0] = []; - let add = [entry!(KeyDown(KeyQ); modifiers=[Accel], action_dispatch=AppWindowMessage::Close)]; - - apply_mapping_patch(&mut mapping, remove, add); } - - mapping } /// Default mappings except that scrolling without modifier keys held down is bound to zooming instead of vertical panning diff --git a/editor/src/messages/input_mapper/utility_types/macros.rs b/editor/src/messages/input_mapper/utility_types/macros.rs index bd0354d280..267f2b0d6d 100644 --- a/editor/src/messages/input_mapper/utility_types/macros.rs +++ b/editor/src/messages/input_mapper/utility_types/macros.rs @@ -25,17 +25,45 @@ macro_rules! modifiers { /// When an action is currently available, and the user enters that input, the action's message is dispatched on the message bus. macro_rules! entry { // Pattern with canonical parameter - ($input:expr_2021; $(modifiers=[$($modifier:ident),*],)? $(refresh_keys=[$($refresh:ident),* $(,)?],)? canonical, action_dispatch=$action_dispatch:expr_2021$(,)?) => { - entry!($input; $($($modifier),*)?; $($($refresh),*)?; $action_dispatch; true) + ( + $input:expr_2021; + $(modifiers=[$($modifier:ident),*],)? + $(refresh_keys=[$($refresh:ident),* $(,)?],)? + canonical, + $(active=$active:expr,)? + action_dispatch=$action_dispatch:expr_2021$(,)? + ) => { + entry!( + $input; + $($($modifier),*)?; + $($($refresh),*)?; + $action_dispatch; + true; + true $( && $active )? + ) }; // Pattern without canonical parameter - ($input:expr_2021; $(modifiers=[$($modifier:ident),*],)? $(refresh_keys=[$($refresh:ident),* $(,)?],)? action_dispatch=$action_dispatch:expr_2021$(,)?) => { - entry!($input; $($($modifier),*)?; $($($refresh),*)?; $action_dispatch; false) + ( + $input:expr_2021; + $(modifiers=[$($modifier:ident),*],)? + $(refresh_keys=[$($refresh:ident),* $(,)?],)? + $(active=$active:expr,)? + action_dispatch=$action_dispatch:expr_2021$(,)? + ) => { + entry!( + $input; + $($($modifier),*)?; + $($($refresh),*)?; + $action_dispatch; + false; + true $( && $active )? + ) }; // Implementation macro to avoid code duplication - ($input:expr; $($modifier:ident),*; $($refresh:ident),*; $action_dispatch:expr; $canonical:expr) => { + ($input:expr; $($modifier:ident),*; $($refresh:ident),*; $action_dispatch:expr; $canonical:expr; $active:expr) => { + &[&[ // Cause the `action_dispatch` message to be sent when the specified input occurs. MappingEntry { @@ -43,33 +71,37 @@ macro_rules! entry { input: $input, modifiers: modifiers!($($modifier),*), canonical: $canonical, + active: $active, }, - // Also cause the `action_dispatch` message to be sent when any of the specified refresh keys change. $( MappingEntry { action: $action_dispatch.into(), input: InputMapperMessage::KeyDown(Key::$refresh), modifiers: modifiers!(), canonical: $canonical, + active: $active, }, MappingEntry { action: $action_dispatch.into(), input: InputMapperMessage::KeyUp(Key::$refresh), modifiers: modifiers!(), canonical: $canonical, + active: $active, }, MappingEntry { action: $action_dispatch.into(), input: InputMapperMessage::KeyDownNoRepeat(Key::$refresh), modifiers: modifiers!(), canonical: $canonical, + active: $active, }, MappingEntry { action: $action_dispatch.into(), input: InputMapperMessage::KeyUpNoRepeat(Key::$refresh), modifiers: modifiers!(), canonical: $canonical, + active: $active, }, )* ]] @@ -97,6 +129,9 @@ macro_rules! mapping { for entry_slice in $entry { // Each entry in the slice (usually just one, except when `refresh_keys` adds additional key entries) for entry in entry_slice.into_iter() { + if !entry.active { + continue; + } let corresponding_list = match entry.input { InputMapperMessage::KeyDown(key) => &mut key_down[key as usize], InputMapperMessage::KeyUp(key) => &mut key_up[key as usize], diff --git a/editor/src/messages/input_mapper/utility_types/misc.rs b/editor/src/messages/input_mapper/utility_types/misc.rs index 1f8be1056e..34f2efd073 100644 --- a/editor/src/messages/input_mapper/utility_types/misc.rs +++ b/editor/src/messages/input_mapper/utility_types/misc.rs @@ -125,6 +125,8 @@ pub struct MappingEntry { pub modifiers: KeyStates, /// True indicates that this takes priority as the labeled hotkey shown in UI menus and tooltips instead of an alternate binding for the same action pub canonical: bool, + /// Whether this mapping is active + pub active: bool, } #[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] From 17c21bbba97572119d9423188496d46b4f8f3e0a Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Thu, 15 Jan 2026 13:06:28 +0000 Subject: [PATCH 07/28] review improvements --- desktop/src/window.rs | 2 + .../messages/input_mapper/input_mappings.rs | 66 +++++-------------- .../input_mapper/utility_types/macros.rs | 22 +++---- .../input_mapper/utility_types/misc.rs | 4 +- .../menu_bar/menu_bar_message_handler.rs | 9 ++- .../src/components/window/TitleBar.svelte | 16 +++-- frontend/wasm/src/editor_api.rs | 6 ++ 7 files changed, 56 insertions(+), 69 deletions(-) diff --git a/desktop/src/window.rs b/desktop/src/window.rs index f3af68b170..ca7e9dc048 100644 --- a/desktop/src/window.rs +++ b/desktop/src/window.rs @@ -122,7 +122,9 @@ impl Window { pub(crate) fn toggle_fullscreen(&self) { if self.is_fullscreen() { self.winit_window.set_fullscreen(None); + self.winit_window.set_maximized(true); } else { + self.winit_window.set_maximized(false); self.winit_window.set_fullscreen(Some(Fullscreen::Borderless(None))); } } diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index 48abf5f47c..d72a374c00 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -17,16 +17,18 @@ use glam::DVec2; impl From for Mapping { fn from(value: MappingVariant) -> Self { match value { - MappingVariant::Default => input_mappings(), - MappingVariant::ZoomWithScroll => zoom_with_scroll(), + MappingVariant::Default => input_mappings(false), + MappingVariant::ZoomWithScroll => input_mappings(true), } } } -pub fn input_mappings() -> Mapping { +pub fn input_mappings(zoom_with_scroll: bool) -> Mapping { use InputMapperMessage::*; use Key::*; + let keyboard_platform = GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout(); + // NOTICE: // If a new mapping you added here isn't working (and perhaps another lower-precedence one is instead), make sure to advertise // it as an available action in the respective message handler file (such as the bottom of `document_message_handler.rs`). @@ -55,9 +57,9 @@ pub fn input_mappings() -> Mapping { entry!(KeyDown(KeyZ); modifiers=[Accel, MouseLeft], action_dispatch=DocumentMessage::Noop), // // AppWindowMessage - entry!(KeyDown(F11); active=cfg!(not(target_os = "macos")), action_dispatch=AppWindowMessage::Fullscreen), - entry!(KeyDown(KeyF); modifiers=[Accel, Meta], active=cfg!(target_os = "macos"), action_dispatch=AppWindowMessage::Fullscreen), - entry!(KeyDown(KeyQ); modifiers=[Accel], active=cfg!(target_os = "macos"), action_dispatch=AppWindowMessage::Close), + entry!(KeyDown(F11); disabled=cfg!(target_os = "macos"), action_dispatch=AppWindowMessage::Fullscreen), + entry!(KeyDown(KeyF); modifiers=[Accel, Meta], disabled=cfg!(not(target_os = "macos")), action_dispatch=AppWindowMessage::Fullscreen), + entry!(KeyDown(KeyQ); modifiers=[Accel], disabled=cfg!(not(target_os = "macos")), action_dispatch=AppWindowMessage::Close), // // ClipboardMessage entry!(KeyDown(KeyX); modifiers=[Accel], action_dispatch=ClipboardMessage::Cut), @@ -421,10 +423,14 @@ pub fn input_mappings() -> Mapping { entry!(KeyDown(FakeKeyPlus); modifiers=[Accel], canonical, action_dispatch=NavigationMessage::CanvasZoomIncrease { center_on_mouse: false }), entry!(KeyDown(Equal); modifiers=[Accel], action_dispatch=NavigationMessage::CanvasZoomIncrease { center_on_mouse: false }), entry!(KeyDown(Minus); modifiers=[Accel], action_dispatch=NavigationMessage::CanvasZoomDecrease { center_on_mouse: false }), - entry!(WheelScroll; modifiers=[Control], action_dispatch=NavigationMessage::CanvasZoomMouseWheel), - entry!(WheelScroll; modifiers=[Command], action_dispatch=NavigationMessage::CanvasZoomMouseWheel), - entry!(WheelScroll; modifiers=[Shift], action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: true }), - entry!(WheelScroll; action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: false }), + entry!(WheelScroll; modifiers=[Control], disabled=zoom_with_scroll, action_dispatch=NavigationMessage::CanvasZoomMouseWheel), + entry!(WheelScroll; modifiers=[Command], disabled=zoom_with_scroll, action_dispatch=NavigationMessage::CanvasZoomMouseWheel), + entry!(WheelScroll; modifiers=[Shift], disabled=zoom_with_scroll, action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: true }), + entry!(WheelScroll; disabled=zoom_with_scroll, action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: false }), + // On Mac, the OS already converts Shift+scroll into horizontal scrolling so we have to reverse the behavior from normal to produce the same outcome + entry!(WheelScroll; modifiers=[Control], disabled=!zoom_with_scroll, action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: keyboard_platform == KeyboardPlatformLayout::Mac }), + entry!(WheelScroll; modifiers=[Shift], disabled=!zoom_with_scroll, action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: keyboard_platform != KeyboardPlatformLayout::Mac }), + entry!(WheelScroll; disabled=!zoom_with_scroll, action_dispatch=NavigationMessage::CanvasZoomMouseWheel), entry!(KeyDown(PageUp); modifiers=[Shift], action_dispatch=NavigationMessage::CanvasPanByViewportFraction { delta: DVec2::new(1., 0.) }), entry!(KeyDown(PageDown); modifiers=[Shift], action_dispatch=NavigationMessage::CanvasPanByViewportFraction { delta: DVec2::new(-1., 0.) }), entry!(KeyDown(PageUp); action_dispatch=NavigationMessage::CanvasPanByViewportFraction { delta: DVec2::new(0., 1.) }), @@ -487,43 +493,3 @@ pub fn input_mappings() -> Mapping { pointer_shake, } } - -/// Default mappings except that scrolling without modifier keys held down is bound to zooming instead of vertical panning -pub fn zoom_with_scroll() -> Mapping { - use InputMapperMessage::*; - - // On Mac, the OS already converts Shift+scroll into horizontal scrolling so we have to reverse the behavior from normal to produce the same outcome - let keyboard_platform = GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout(); - - let mut mapping = input_mappings(); - - let remove = [ - entry!(WheelScroll; modifiers=[Control], action_dispatch=NavigationMessage::CanvasZoomMouseWheel), - entry!(WheelScroll; modifiers=[Command], action_dispatch=NavigationMessage::CanvasZoomMouseWheel), - entry!(WheelScroll; modifiers=[Shift], action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: true }), - entry!(WheelScroll; action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: false }), - ]; - let add = [ - entry!(WheelScroll; modifiers=[Control], action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: keyboard_platform == KeyboardPlatformLayout::Mac }), - entry!(WheelScroll; modifiers=[Shift], action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: keyboard_platform != KeyboardPlatformLayout::Mac }), - entry!(WheelScroll; action_dispatch=NavigationMessage::CanvasZoomMouseWheel), - ]; - - apply_mapping_patch(&mut mapping, remove, add); - - mapping -} - -fn apply_mapping_patch<'a, const N: usize, const M: usize, const X: usize, const Y: usize>( - mapping: &mut Mapping, - remove: impl IntoIterator, - add: impl IntoIterator, -) { - for entry in remove.into_iter().flat_map(|inner| inner.iter()).flat_map(|inner| inner.iter()) { - mapping.remove(entry); - } - - for entry in add.into_iter().flat_map(|inner| inner.iter()).flat_map(|inner| inner.iter()) { - mapping.add(entry.clone()); - } -} diff --git a/editor/src/messages/input_mapper/utility_types/macros.rs b/editor/src/messages/input_mapper/utility_types/macros.rs index 267f2b0d6d..5a9d162b25 100644 --- a/editor/src/messages/input_mapper/utility_types/macros.rs +++ b/editor/src/messages/input_mapper/utility_types/macros.rs @@ -30,7 +30,7 @@ macro_rules! entry { $(modifiers=[$($modifier:ident),*],)? $(refresh_keys=[$($refresh:ident),* $(,)?],)? canonical, - $(active=$active:expr,)? + $(disabled=$disabled:expr,)? action_dispatch=$action_dispatch:expr_2021$(,)? ) => { entry!( @@ -39,7 +39,7 @@ macro_rules! entry { $($($refresh),*)?; $action_dispatch; true; - true $( && $active )? + true $( && $disabled )? ) }; @@ -48,7 +48,7 @@ macro_rules! entry { $input:expr_2021; $(modifiers=[$($modifier:ident),*],)? $(refresh_keys=[$($refresh:ident),* $(,)?],)? - $(active=$active:expr,)? + $(disabled=$disabled:expr,)? action_dispatch=$action_dispatch:expr_2021$(,)? ) => { entry!( @@ -57,12 +57,12 @@ macro_rules! entry { $($($refresh),*)?; $action_dispatch; false; - true $( && $active )? + true $( && $disabled )? ) }; // Implementation macro to avoid code duplication - ($input:expr; $($modifier:ident),*; $($refresh:ident),*; $action_dispatch:expr; $canonical:expr; $active:expr) => { + ($input:expr; $($modifier:ident),*; $($refresh:ident),*; $action_dispatch:expr; $canonical:expr; $disabled:expr) => { &[&[ // Cause the `action_dispatch` message to be sent when the specified input occurs. @@ -71,7 +71,7 @@ macro_rules! entry { input: $input, modifiers: modifiers!($($modifier),*), canonical: $canonical, - active: $active, + disabled: $disabled, }, $( @@ -80,28 +80,28 @@ macro_rules! entry { input: InputMapperMessage::KeyDown(Key::$refresh), modifiers: modifiers!(), canonical: $canonical, - active: $active, + disabled: $disabled, }, MappingEntry { action: $action_dispatch.into(), input: InputMapperMessage::KeyUp(Key::$refresh), modifiers: modifiers!(), canonical: $canonical, - active: $active, + disabled: $disabled, }, MappingEntry { action: $action_dispatch.into(), input: InputMapperMessage::KeyDownNoRepeat(Key::$refresh), modifiers: modifiers!(), canonical: $canonical, - active: $active, + disabled: $disabled, }, MappingEntry { action: $action_dispatch.into(), input: InputMapperMessage::KeyUpNoRepeat(Key::$refresh), modifiers: modifiers!(), canonical: $canonical, - active: $active, + disabled: $disabled, }, )* ]] @@ -129,7 +129,7 @@ macro_rules! mapping { for entry_slice in $entry { // Each entry in the slice (usually just one, except when `refresh_keys` adds additional key entries) for entry in entry_slice.into_iter() { - if !entry.active { + if entry.disabled { continue; } let corresponding_list = match entry.input { diff --git a/editor/src/messages/input_mapper/utility_types/misc.rs b/editor/src/messages/input_mapper/utility_types/misc.rs index 34f2efd073..bd0f4187be 100644 --- a/editor/src/messages/input_mapper/utility_types/misc.rs +++ b/editor/src/messages/input_mapper/utility_types/misc.rs @@ -125,8 +125,8 @@ pub struct MappingEntry { pub modifiers: KeyStates, /// True indicates that this takes priority as the labeled hotkey shown in UI menus and tooltips instead of an alternate binding for the same action pub canonical: bool, - /// Whether this mapping is active - pub active: bool, + /// Whether this mapping is disabled + pub disabled: bool, } #[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] diff --git a/editor/src/messages/menu_bar/menu_bar_message_handler.rs b/editor/src/messages/menu_bar/menu_bar_message_handler.rs index b06b7397a7..ec785db165 100644 --- a/editor/src/messages/menu_bar/menu_bar_message_handler.rs +++ b/editor/src/messages/menu_bar/menu_bar_message_handler.rs @@ -621,6 +621,13 @@ impl LayoutHolder for MenuBarMessageHandler { .label("Window") .flush(true) .menu_list_children(vec![ + vec![ + MenuListEntry::new("Fullscreen") + .label("Fullscreen") + .icon("FullscreenEnter") + .tooltip_shortcut(action_shortcut!(AppWindowMessageDiscriminant::Fullscreen)) + .on_commit(|_| AppWindowMessage::Fullscreen.into()), + ], vec![ MenuListEntry::new("Properties") .label("Properties") @@ -632,8 +639,6 @@ impl LayoutHolder for MenuBarMessageHandler { .icon(if self.layers_panel_open { "CheckboxChecked" } else { "CheckboxUnchecked" }) .tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::ToggleLayersPanelOpen)) .on_commit(|_| PortfolioMessage::ToggleLayersPanelOpen.into()), - ], - vec![ MenuListEntry::new("Data") .label("Data") .icon(if self.data_panel_open { "CheckboxChecked" } else { "CheckboxUnchecked" }) diff --git a/frontend/src/components/window/TitleBar.svelte b/frontend/src/components/window/TitleBar.svelte index 24a1716fab..3ce892176b 100644 --- a/frontend/src/components/window/TitleBar.svelte +++ b/frontend/src/components/window/TitleBar.svelte @@ -11,6 +11,7 @@ import LayoutRow from "@graphite/components/layout/LayoutRow.svelte"; import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte"; import WidgetLayout from "@graphite/components/widgets/WidgetLayout.svelte"; + import { isDesktop } from "/src/utility-functions/platform"; const appWindow = getContext("appWindow"); const editor = getContext("editor"); @@ -19,7 +20,8 @@ let menuBarLayout: Layout = []; - $: showFullscreenButton = $appWindow.platform === "Web" || $fullscreen.windowFullscreen; + $: showFullscreenButton = $appWindow.platform === "Web" || $fullscreen.windowFullscreen || (isDesktop() && $appWindow.fullscreen); + $: isFullscreen = isDesktop() ? $appWindow.fullscreen : $fullscreen.windowFullscreen; // On Mac, the menu bar height needs to be scaled by the inverse of the UI scale to fit its native window buttons $: height = $appWindow.platform === "Mac" ? 28 * (1 / $appWindow.uiScale) : 28; @@ -45,14 +47,20 @@ {#if $appWindow.platform !== "Mac"} {#if showFullscreenButton} ($fullscreen.windowFullscreen ? fullscreen.exitFullscreen : fullscreen.enterFullscreen)()} + on:click={() => { + if (isDesktop()) { + editor.handle.appWindowFullscreen(); + } else { + ($fullscreen.windowFullscreen ? fullscreen.exitFullscreen : fullscreen.enterFullscreen)(); + } + }} > - + {:else} editor.handle.appWindowMinimize()}> diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index 6d637f0d1b..cfb4024914 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -295,6 +295,12 @@ impl EditorHandle { self.dispatch(message); } + #[wasm_bindgen(js_name = appWindowFullscreen)] + pub fn app_window_fullscreen(&self) { + let message = AppWindowMessage::Fullscreen; + self.dispatch(message); + } + /// Closes the application window #[wasm_bindgen(js_name = appWindowClose)] pub fn app_window_close(&self) { From 4494b6e9c7873f6d461ad33cffd059597b673a96 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Thu, 15 Jan 2026 13:28:18 +0000 Subject: [PATCH 08/28] fix --- editor/src/messages/input_mapper/input_mappings.rs | 2 +- editor/src/messages/input_mapper/utility_types/macros.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index d72a374c00..2a55034642 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -58,7 +58,7 @@ pub fn input_mappings(zoom_with_scroll: bool) -> Mapping { // // AppWindowMessage entry!(KeyDown(F11); disabled=cfg!(target_os = "macos"), action_dispatch=AppWindowMessage::Fullscreen), - entry!(KeyDown(KeyF); modifiers=[Accel, Meta], disabled=cfg!(not(target_os = "macos")), action_dispatch=AppWindowMessage::Fullscreen), + entry!(KeyDown(KeyF); modifiers=[Accel, Control], disabled=cfg!(not(target_os = "macos")), action_dispatch=AppWindowMessage::Fullscreen), entry!(KeyDown(KeyQ); modifiers=[Accel], disabled=cfg!(not(target_os = "macos")), action_dispatch=AppWindowMessage::Close), // // ClipboardMessage diff --git a/editor/src/messages/input_mapper/utility_types/macros.rs b/editor/src/messages/input_mapper/utility_types/macros.rs index 5a9d162b25..f8e406da98 100644 --- a/editor/src/messages/input_mapper/utility_types/macros.rs +++ b/editor/src/messages/input_mapper/utility_types/macros.rs @@ -39,7 +39,7 @@ macro_rules! entry { $($($refresh),*)?; $action_dispatch; true; - true $( && $disabled )? + false $( || $disabled )? ) }; @@ -57,7 +57,7 @@ macro_rules! entry { $($($refresh),*)?; $action_dispatch; false; - true $( && $disabled )? + false $( || $disabled )? ) }; From 574183c4b0bdbcd4375e0f0af9d5aa75df6c0ab1 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Thu, 15 Jan 2026 14:50:07 +0000 Subject: [PATCH 09/28] fixup --- desktop/src/app.rs | 2 +- desktop/src/window.rs | 7 +++++-- frontend/src/components/window/TitleBar.svelte | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/desktop/src/app.rs b/desktop/src/app.rs index 004d44c5b2..e3a3970c85 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -353,7 +353,7 @@ impl App { } } DesktopFrontendMessage::WindowFullscreen => { - if let Some(window) = &self.window { + if let Some(window) = &mut self.window { window.toggle_fullscreen(); } } diff --git a/desktop/src/window.rs b/desktop/src/window.rs index ca7e9dc048..8e7c3da726 100644 --- a/desktop/src/window.rs +++ b/desktop/src/window.rs @@ -43,6 +43,7 @@ pub(crate) struct Window { native_handle: native::NativeWindowImpl, custom_cursors: HashMap, clipboard: Option, + was_maximized_before_fullscreen: bool, } impl Drop for Window { fn drop(&mut self) { @@ -75,6 +76,7 @@ impl Window { native_handle, custom_cursors: HashMap::new(), clipboard, + was_maximized_before_fullscreen: false, } } @@ -119,11 +121,12 @@ impl Window { self.winit_window.is_maximized() } - pub(crate) fn toggle_fullscreen(&self) { + pub(crate) fn toggle_fullscreen(&mut self) { if self.is_fullscreen() { self.winit_window.set_fullscreen(None); - self.winit_window.set_maximized(true); + self.winit_window.set_maximized(self.was_maximized_before_fullscreen); } else { + self.was_maximized_before_fullscreen = self.winit_window.is_maximized(); self.winit_window.set_maximized(false); self.winit_window.set_fullscreen(Some(Fullscreen::Borderless(None))); } diff --git a/frontend/src/components/window/TitleBar.svelte b/frontend/src/components/window/TitleBar.svelte index 3ce892176b..e3968ebc42 100644 --- a/frontend/src/components/window/TitleBar.svelte +++ b/frontend/src/components/window/TitleBar.svelte @@ -69,7 +69,7 @@ editor.handle.appWindowMaximize()}> - editor.handle.appWindowClose()}> + editor.handle.appWindowClose()}> {/if} @@ -130,7 +130,7 @@ background: #2d2d2d; } - &:last-of-type:hover { + &.close:hover { background: #c42b1c; } } From e2009f014083b9530e8872c24940ee55fdd08012 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Thu, 15 Jan 2026 15:00:57 +0000 Subject: [PATCH 10/28] fix --- desktop/src/window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/src/window.rs b/desktop/src/window.rs index 8e7c3da726..5f75b99cb5 100644 --- a/desktop/src/window.rs +++ b/desktop/src/window.rs @@ -123,8 +123,8 @@ impl Window { pub(crate) fn toggle_fullscreen(&mut self) { if self.is_fullscreen() { - self.winit_window.set_fullscreen(None); self.winit_window.set_maximized(self.was_maximized_before_fullscreen); + self.winit_window.set_fullscreen(None); } else { self.was_maximized_before_fullscreen = self.winit_window.is_maximized(); self.winit_window.set_maximized(false); From 6c10f0f9ec05416590a495f9aa93afb566b1e65c Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Thu, 15 Jan 2026 15:03:24 +0000 Subject: [PATCH 11/28] fix --- desktop/src/window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/src/window.rs b/desktop/src/window.rs index 5f75b99cb5..de5203b555 100644 --- a/desktop/src/window.rs +++ b/desktop/src/window.rs @@ -127,8 +127,8 @@ impl Window { self.winit_window.set_fullscreen(None); } else { self.was_maximized_before_fullscreen = self.winit_window.is_maximized(); - self.winit_window.set_maximized(false); self.winit_window.set_fullscreen(Some(Fullscreen::Borderless(None))); + self.winit_window.set_maximized(false); } } From 8f33142f3a8e502700a74a9cb4e6274c6261da9d Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Thu, 15 Jan 2026 15:04:09 +0000 Subject: [PATCH 12/28] fix --- frontend/src/components/window/TitleBar.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/window/TitleBar.svelte b/frontend/src/components/window/TitleBar.svelte index e3968ebc42..2d36c03b6f 100644 --- a/frontend/src/components/window/TitleBar.svelte +++ b/frontend/src/components/window/TitleBar.svelte @@ -69,7 +69,7 @@ editor.handle.appWindowMaximize()}> - editor.handle.appWindowClose()}> + editor.handle.appWindowClose()}> {/if} From 3b1bd74cacae9df9eba5709160dc41294b17f758 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Thu, 15 Jan 2026 15:12:53 +0000 Subject: [PATCH 13/28] fix --- desktop/src/window.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/desktop/src/window.rs b/desktop/src/window.rs index de5203b555..af94604ae9 100644 --- a/desktop/src/window.rs +++ b/desktop/src/window.rs @@ -43,7 +43,6 @@ pub(crate) struct Window { native_handle: native::NativeWindowImpl, custom_cursors: HashMap, clipboard: Option, - was_maximized_before_fullscreen: bool, } impl Drop for Window { fn drop(&mut self) { @@ -76,7 +75,6 @@ impl Window { native_handle, custom_cursors: HashMap::new(), clipboard, - was_maximized_before_fullscreen: false, } } @@ -123,12 +121,9 @@ impl Window { pub(crate) fn toggle_fullscreen(&mut self) { if self.is_fullscreen() { - self.winit_window.set_maximized(self.was_maximized_before_fullscreen); self.winit_window.set_fullscreen(None); } else { - self.was_maximized_before_fullscreen = self.winit_window.is_maximized(); self.winit_window.set_fullscreen(Some(Fullscreen::Borderless(None))); - self.winit_window.set_maximized(false); } } From 1cbc2ae38b8cf34880fe92f916a5cb7bb91b1f11 Mon Sep 17 00:00:00 2001 From: Timon Date: Thu, 15 Jan 2026 16:42:23 +0100 Subject: [PATCH 14/28] fix on windows --- desktop/src/window/win/native_handle.rs | 35 ++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/desktop/src/window/win/native_handle.rs b/desktop/src/window/win/native_handle.rs index 228324dc0f..23db92d69c 100644 --- a/desktop/src/window/win/native_handle.rs +++ b/desktop/src/window/win/native_handle.rs @@ -211,7 +211,7 @@ unsafe fn ensure_helper_class() { unsafe extern "system" fn main_window_handle_message(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT { if msg == WM_NCCALCSIZE && wparam.0 != 0 { // When maximized, shrink to visible frame so content doesn't extend beyond it. - if unsafe { IsZoomed(hwnd).as_bool() } { + if unsafe { IsZoomed(hwnd).as_bool() } && !is_effectively_fullscreen(hwnd) { let params = unsafe { &mut *(lparam.0 as *mut NCCALCSIZE_PARAMS) }; let dpi = unsafe { GetDpiForWindow(hwnd) }; @@ -366,3 +366,36 @@ unsafe fn calculate_resize_direction(helper: HWND, lparam: LPARAM) -> Option None, } } + +// Check if the window is effectively fullscreen, meaning it covers the entire monitor. +// We need to use this heuristic because Windows doesn't provide a way to check for fullscreen state. +fn is_effectively_fullscreen(hwnd: HWND) -> bool { + if hwnd.is_invalid() { + return false; + } + + let mut view_rect = RECT::default(); + if unsafe { GetWindowRect(hwnd, &mut view_rect) }.is_err() { + return false; + } + + let hmon = unsafe { MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) }; + if hmon.is_invalid() { + return false; + } + + let mut monitor_info = MONITORINFO { + cbSize: std::mem::size_of::() as u32, + ..Default::default() + }; + if !unsafe { GetMonitorInfoW(hmon, &mut monitor_info) }.as_bool() { + return false; + } + + // Allow a tiny tolerance for DPI / rounding issues + const EPS: i32 = 1; + (view_rect.left - monitor_info.rcMonitor.left).abs() <= EPS + && (view_rect.top - monitor_info.rcMonitor.top).abs() <= EPS + && (view_rect.right - monitor_info.rcMonitor.right).abs() <= EPS + && (view_rect.bottom - monitor_info.rcMonitor.bottom).abs() <= EPS +} From 3ef8103f0426eea3e68961994fd5c41740de76ce Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Thu, 15 Jan 2026 16:19:59 -0800 Subject: [PATCH 15/28] Fix CSS --- frontend/src/components/window/TitleBar.svelte | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/window/TitleBar.svelte b/frontend/src/components/window/TitleBar.svelte index 2d36c03b6f..2ad7ceea22 100644 --- a/frontend/src/components/window/TitleBar.svelte +++ b/frontend/src/components/window/TitleBar.svelte @@ -53,11 +53,8 @@ : undefined} tooltipShortcut={$tooltip.f11Shortcut} on:click={() => { - if (isDesktop()) { - editor.handle.appWindowFullscreen(); - } else { - ($fullscreen.windowFullscreen ? fullscreen.exitFullscreen : fullscreen.enterFullscreen)(); - } + if (isDesktop()) editor.handle.appWindowFullscreen(); + else ($fullscreen.windowFullscreen ? fullscreen.exitFullscreen : fullscreen.enterFullscreen)(); }} > @@ -69,7 +66,7 @@ editor.handle.appWindowMaximize()}> - editor.handle.appWindowClose()}> + editor.handle.appWindowClose()}> {/if} @@ -123,19 +120,19 @@ padding: 0 8px; } - &.windows > .layout-row { + &.windows:not(.fullscreen) > .layout-row { padding: 0 17px; &:hover { background: #2d2d2d; } - &.close:hover { + &:last-of-type:hover { background: #c42b1c; } } - &.linux > .layout-row { + &.linux:not(.fullscreen) > .layout-row { padding: 0 12px; &:hover { From 6b7ffd5eeb9e46bcd161327f50d492d0a8e8f156 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Fri, 16 Jan 2026 10:21:58 +0000 Subject: [PATCH 16/28] don't allow drag and maximize when in fullscreen --- desktop/src/window.rs | 6 ++++++ frontend/src/components/window/TitleBar.svelte | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/desktop/src/window.rs b/desktop/src/window.rs index af94604ae9..b69a0be2e7 100644 --- a/desktop/src/window.rs +++ b/desktop/src/window.rs @@ -112,6 +112,9 @@ impl Window { } pub(crate) fn toggle_maximize(&self) { + if self.is_fullscreen() { + return; + } self.winit_window.set_maximized(!self.winit_window.is_maximized()); } @@ -132,6 +135,9 @@ impl Window { } pub(crate) fn start_drag(&self) { + if self.is_fullscreen() { + return; + } let _ = self.winit_window.drag_window(); } diff --git a/frontend/src/components/window/TitleBar.svelte b/frontend/src/components/window/TitleBar.svelte index 2ad7ceea22..a633bc16e3 100644 --- a/frontend/src/components/window/TitleBar.svelte +++ b/frontend/src/components/window/TitleBar.svelte @@ -41,7 +41,7 @@ {/if} - editor.handle.appWindowDrag()} on:dblclick={() => editor.handle.appWindowMaximize()} /> + {if (!isFullscreen) editor.handle.appWindowDrag()}} on:dblclick={() => {if (!isFullscreen)editor.handle.appWindowMaximize()}} /> {#if $appWindow.platform !== "Mac"} From 8f176ba9c5c794aaa72df320db6d9634c7d6d6cb Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Fri, 16 Jan 2026 02:25:05 -0800 Subject: [PATCH 17/28] JS nitpick --- frontend/src/components/window/TitleBar.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/window/TitleBar.svelte b/frontend/src/components/window/TitleBar.svelte index a633bc16e3..b3d72faded 100644 --- a/frontend/src/components/window/TitleBar.svelte +++ b/frontend/src/components/window/TitleBar.svelte @@ -41,7 +41,7 @@ {/if} - {if (!isFullscreen) editor.handle.appWindowDrag()}} on:dblclick={() => {if (!isFullscreen)editor.handle.appWindowMaximize()}} /> + !isFullscreen && editor.handle.appWindowDrag()} on:dblclick={() => !isFullscreen && editor.handle.appWindowMaximize()} /> {#if $appWindow.platform !== "Mac"} From a6987078dfbe9913e8099996cab3df93b2520782 Mon Sep 17 00:00:00 2001 From: Timon Date: Fri, 16 Jan 2026 13:31:26 +0100 Subject: [PATCH 18/28] fix fullscreen after maximize --- desktop/src/window/win/native_handle.rs | 28 +++++++++---------------- 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/desktop/src/window/win/native_handle.rs b/desktop/src/window/win/native_handle.rs index 23db92d69c..333477e65b 100644 --- a/desktop/src/window/win/native_handle.rs +++ b/desktop/src/window/win/native_handle.rs @@ -210,10 +210,10 @@ unsafe fn ensure_helper_class() { // Main window message handler, called on the UI thread for every message the main window receives. unsafe extern "system" fn main_window_handle_message(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT { if msg == WM_NCCALCSIZE && wparam.0 != 0 { - // When maximized, shrink to visible frame so content doesn't extend beyond it. - if unsafe { IsZoomed(hwnd).as_bool() } && !is_effectively_fullscreen(hwnd) { - let params = unsafe { &mut *(lparam.0 as *mut NCCALCSIZE_PARAMS) }; + let params = unsafe { &mut *(lparam.0 as *mut NCCALCSIZE_PARAMS) }; + // When maximized, shrink to visible frame so content doesn't extend beyond it. + if unsafe { IsZoomed(hwnd).as_bool() } && !is_effectively_fullscreen(params.rgrc[0]) { let dpi = unsafe { GetDpiForWindow(hwnd) }; let size = unsafe { GetSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) }; let pad = unsafe { GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi) }; @@ -367,19 +367,11 @@ unsafe fn calculate_resize_direction(helper: HWND, lparam: LPARAM) -> Option bool { - if hwnd.is_invalid() { - return false; - } - - let mut view_rect = RECT::default(); - if unsafe { GetWindowRect(hwnd, &mut view_rect) }.is_err() { - return false; - } +fn is_effectively_fullscreen(rect: RECT) -> bool { - let hmon = unsafe { MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) }; + let hmon = unsafe { MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST) }; if hmon.is_invalid() { return false; } @@ -394,8 +386,8 @@ fn is_effectively_fullscreen(hwnd: HWND) -> bool { // Allow a tiny tolerance for DPI / rounding issues const EPS: i32 = 1; - (view_rect.left - monitor_info.rcMonitor.left).abs() <= EPS - && (view_rect.top - monitor_info.rcMonitor.top).abs() <= EPS - && (view_rect.right - monitor_info.rcMonitor.right).abs() <= EPS - && (view_rect.bottom - monitor_info.rcMonitor.bottom).abs() <= EPS + (rect.left - monitor_info.rcMonitor.left).abs() <= EPS + && (rect.top - monitor_info.rcMonitor.top).abs() <= EPS + && (rect.right - monitor_info.rcMonitor.right).abs() <= EPS + && (rect.bottom - monitor_info.rcMonitor.bottom).abs() <= EPS } From 8559e866379a8bbdd9924fef68167421531744b5 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Fri, 16 Jan 2026 14:37:23 -0800 Subject: [PATCH 19/28] Fix eslint crash --- frontend/src/components/window/TitleBar.svelte | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/components/window/TitleBar.svelte b/frontend/src/components/window/TitleBar.svelte index b3d72faded..6888d419dd 100644 --- a/frontend/src/components/window/TitleBar.svelte +++ b/frontend/src/components/window/TitleBar.svelte @@ -141,4 +141,6 @@ } } } + + // paddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpadding From c7968c15903efeb56adddd2749c579f1f531a0a5 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Fri, 16 Jan 2026 15:18:50 -0800 Subject: [PATCH 20/28] Show correct fullscreen button tooltip shortcut on Mac web version; code review --- desktop/src/window.rs | 7 ++-- desktop/src/window/win/native_handle.rs | 1 - .../src/messages/frontend/frontend_message.rs | 4 +- .../messages/input_mapper/input_mappings.rs | 6 +-- .../input_mapper/utility_types/macros.rs | 2 +- .../portfolio/portfolio_message_handler.rs | 3 +- .../src/components/window/TitleBar.svelte | 2 +- frontend/src/io-managers/input.ts | 38 ++++++++++--------- frontend/src/messages.ts | 6 ++- frontend/src/state-providers/tooltip.ts | 9 +++-- 10 files changed, 42 insertions(+), 36 deletions(-) diff --git a/desktop/src/window.rs b/desktop/src/window.rs index b69a0be2e7..4540e56200 100644 --- a/desktop/src/window.rs +++ b/desktop/src/window.rs @@ -1,3 +1,6 @@ +use crate::consts::APP_NAME; +use crate::event::AppEventScheduler; +use crate::wrapper::messages::MenuItem; use std::collections::HashMap; use std::sync::Arc; use winit::cursor::{CursorIcon, CustomCursor, CustomCursorSource}; @@ -5,10 +8,6 @@ use winit::event_loop::ActiveEventLoop; use winit::monitor::Fullscreen; use winit::window::{Window as WinitWindow, WindowAttributes}; -use crate::consts::APP_NAME; -use crate::event::AppEventScheduler; -use crate::wrapper::messages::MenuItem; - pub(crate) trait NativeWindow { fn init() {} fn configure(attributes: WindowAttributes, event_loop: &dyn ActiveEventLoop) -> WindowAttributes; diff --git a/desktop/src/window/win/native_handle.rs b/desktop/src/window/win/native_handle.rs index 333477e65b..b0a08fefdf 100644 --- a/desktop/src/window/win/native_handle.rs +++ b/desktop/src/window/win/native_handle.rs @@ -370,7 +370,6 @@ unsafe fn calculate_resize_direction(helper: HWND, lparam: LPARAM) -> Option bool { - let hmon = unsafe { MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST) }; if hmon.is_invalid() { return false; diff --git a/editor/src/messages/frontend/frontend_message.rs b/editor/src/messages/frontend/frontend_message.rs index 912cc205f9..c4c19cfe64 100644 --- a/editor/src/messages/frontend/frontend_message.rs +++ b/editor/src/messages/frontend/frontend_message.rs @@ -64,8 +64,10 @@ pub enum FrontendMessage { #[serde(rename = "nodeTypes")] node_types: Vec, }, - SendShortcutF11 { + SendShortcutFullscreen { shortcut: Option, + #[serde(rename = "shortcutMc")] + shortcut_mac: Option, }, SendShortcutAltClick { shortcut: Option, diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index 2a55034642..f0f7d1e8e6 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -58,8 +58,8 @@ pub fn input_mappings(zoom_with_scroll: bool) -> Mapping { // // AppWindowMessage entry!(KeyDown(F11); disabled=cfg!(target_os = "macos"), action_dispatch=AppWindowMessage::Fullscreen), - entry!(KeyDown(KeyF); modifiers=[Accel, Control], disabled=cfg!(not(target_os = "macos")), action_dispatch=AppWindowMessage::Fullscreen), - entry!(KeyDown(KeyQ); modifiers=[Accel], disabled=cfg!(not(target_os = "macos")), action_dispatch=AppWindowMessage::Close), + entry!(KeyDown(KeyF); modifiers=[Command, Control], disabled=cfg!(not(target_os = "macos")), action_dispatch=AppWindowMessage::Fullscreen), + entry!(KeyDown(KeyQ); modifiers=[Command], disabled=cfg!(not(target_os = "macos")), action_dispatch=AppWindowMessage::Close), // // ClipboardMessage entry!(KeyDown(KeyX); modifiers=[Accel], action_dispatch=ClipboardMessage::Cut), @@ -426,7 +426,7 @@ pub fn input_mappings(zoom_with_scroll: bool) -> Mapping { entry!(WheelScroll; modifiers=[Control], disabled=zoom_with_scroll, action_dispatch=NavigationMessage::CanvasZoomMouseWheel), entry!(WheelScroll; modifiers=[Command], disabled=zoom_with_scroll, action_dispatch=NavigationMessage::CanvasZoomMouseWheel), entry!(WheelScroll; modifiers=[Shift], disabled=zoom_with_scroll, action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: true }), - entry!(WheelScroll; disabled=zoom_with_scroll, action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: false }), + entry!(WheelScroll; disabled=zoom_with_scroll, action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: false }), // On Mac, the OS already converts Shift+scroll into horizontal scrolling so we have to reverse the behavior from normal to produce the same outcome entry!(WheelScroll; modifiers=[Control], disabled=!zoom_with_scroll, action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: keyboard_platform == KeyboardPlatformLayout::Mac }), entry!(WheelScroll; modifiers=[Shift], disabled=!zoom_with_scroll, action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: keyboard_platform != KeyboardPlatformLayout::Mac }), diff --git a/editor/src/messages/input_mapper/utility_types/macros.rs b/editor/src/messages/input_mapper/utility_types/macros.rs index f8e406da98..9dae86c9a5 100644 --- a/editor/src/messages/input_mapper/utility_types/macros.rs +++ b/editor/src/messages/input_mapper/utility_types/macros.rs @@ -63,7 +63,6 @@ macro_rules! entry { // Implementation macro to avoid code duplication ($input:expr; $($modifier:ident),*; $($refresh:ident),*; $action_dispatch:expr; $canonical:expr; $disabled:expr) => { - &[&[ // Cause the `action_dispatch` message to be sent when the specified input occurs. MappingEntry { @@ -132,6 +131,7 @@ macro_rules! mapping { if entry.disabled { continue; } + let corresponding_list = match entry.input { InputMapperMessage::KeyDown(key) => &mut key_down[key as usize], InputMapperMessage::KeyUp(key) => &mut key_up[key as usize], diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 2a704f49e4..83e4e35af1 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -117,8 +117,9 @@ impl MessageHandler> for Portfolio }); // Send shortcuts for widgets created in the frontend which need shortcut tooltips - responses.add(FrontendMessage::SendShortcutF11 { + responses.add(FrontendMessage::SendShortcutFullscreen { shortcut: action_shortcut_manual!(Key::F11), + shortcut_mac: action_shortcut_manual!(Key::Control, Key::Command, Key::KeyF), }); responses.add(FrontendMessage::SendShortcutAltClick { shortcut: action_shortcut_manual!(Key::Alt, Key::MouseLeft), diff --git a/frontend/src/components/window/TitleBar.svelte b/frontend/src/components/window/TitleBar.svelte index 6888d419dd..25a724131c 100644 --- a/frontend/src/components/window/TitleBar.svelte +++ b/frontend/src/components/window/TitleBar.svelte @@ -51,7 +51,7 @@ tooltipDescription={$appWindow.platform === "Web" && $fullscreen.keyboardLockApiSupported ? "While fullscreen, keyboard shortcuts normally reserved by the browser become available." : undefined} - tooltipShortcut={$tooltip.f11Shortcut} + tooltipShortcut={$tooltip.fullscreenShortcut} on:click={() => { if (isDesktop()) editor.handle.appWindowFullscreen(); else ($fullscreen.windowFullscreen ? fullscreen.exitFullscreen : fullscreen.enterFullscreen)(); diff --git a/frontend/src/io-managers/input.ts b/frontend/src/io-managers/input.ts index 47f2b9e0e1..e830cad007 100644 --- a/frontend/src/io-managers/input.ts +++ b/frontend/src/io-managers/input.ts @@ -84,28 +84,12 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli // Cut, copy, and paste is handled in the backend on desktop if (isDesktop() && accelKey && ["KeyX", "KeyC", "KeyV"].includes(key)) return true; + // But on web, we want to not redirect paste + if (!isDesktop() && key === "KeyV" && accelKey) return false; // Don't redirect user input from text entry into HTML elements if (targetIsTextField(e.target || undefined) && key !== "Escape" && !(accelKey && ["Enter", "NumpadEnter"].includes(key))) return false; - // Don't redirect paste in web - if (key === "KeyV" && accelKey) return false; - - // Don't redirect a fullscreen request on web - if (key === "F11" && e.type === "keydown" && !e.repeat && !isDesktop()) { - e.preventDefault(); - fullscreen.toggleFullscreen(); - return false; - } - - // Don't redirect a reload request - if (key === "F5") return false; - if (key === "KeyR" && accelKey) return false; - - // Don't redirect debugging tools - if (["F12", "F8"].includes(key)) return false; - if (["KeyC", "KeyI", "KeyJ"].includes(key) && accelKey && e.shiftKey) return false; - // Don't redirect tab or enter if not in canvas (to allow navigating elements) potentiallyRestoreCanvasFocus(e); if (!canvasFocused && !targetIsTextField(e.target || undefined) && ["Tab", "Enter", "NumpadEnter", "Space", "ArrowDown", "ArrowLeft", "ArrowRight", "ArrowUp"].includes(key)) return false; @@ -113,6 +97,24 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli // Don't redirect if a MenuList is open if (window.document.querySelector("[data-floating-menu-content]")) return false; + // Web-only keyboard shortcuts + if (!isDesktop()) { + // Don't redirect a fullscreen request + if (key === "F11" && e.type === "keydown" && !e.repeat) { + e.preventDefault(); + fullscreen.toggleFullscreen(); + return false; + } + + // Don't redirect a reload request + if (key === "F5") return false; + if (key === "KeyR" && accelKey) return false; + + // Don't redirect debugging tools + if (["F12", "F8"].includes(key)) return false; + if (["KeyC", "KeyI", "KeyJ"].includes(key) && accelKey && e.shiftKey) return false; + } + // Redirect to the backend return true; } diff --git a/frontend/src/messages.ts b/frontend/src/messages.ts index 788b2a911e..59263aeb94 100644 --- a/frontend/src/messages.ts +++ b/frontend/src/messages.ts @@ -107,9 +107,11 @@ export class SendUIMetadata extends JsMessage { readonly nodeTypes!: FrontendNodeType[]; } -export class SendShortcutF11 extends JsMessage { +export class SendShortcutFullscreen extends JsMessage { @Transform(({ value }: { value: ActionShortcut }) => value || undefined) readonly shortcut!: ActionShortcut | undefined; + @Transform(({ value }: { value: ActionShortcut }) => value || undefined) + readonly shortcutMac!: ActionShortcut | undefined; } export class SendShortcutAltClick extends JsMessage { @@ -1666,7 +1668,7 @@ export const messageMakers: Record = { DisplayEditableTextboxTransform, DisplayRemoveEditableTextbox, SendUIMetadata, - SendShortcutF11, + SendShortcutFullscreen, SendShortcutAltClick, SendShortcutShiftClick, TriggerAboutGraphiteLocalizedCommitDate, diff --git a/frontend/src/state-providers/tooltip.ts b/frontend/src/state-providers/tooltip.ts index 8d6055693f..f3943083f4 100644 --- a/frontend/src/state-providers/tooltip.ts +++ b/frontend/src/state-providers/tooltip.ts @@ -1,7 +1,8 @@ import { writable } from "svelte/store"; import { type Editor } from "@graphite/editor"; -import { SendShortcutAltClick, SendShortcutF11, SendShortcutShiftClick, type ActionShortcut } from "@graphite/messages"; +import { SendShortcutAltClick, SendShortcutFullscreen, SendShortcutShiftClick, type ActionShortcut } from "@graphite/messages"; +import { operatingSystem } from "@graphite/utility-functions/platform"; const SHOW_TOOLTIP_DELAY_MS = 500; @@ -12,7 +13,7 @@ export function createTooltipState(editor: Editor) { position: { x: 0, y: 0 }, shiftClickShortcut: undefined as ActionShortcut | undefined, altClickShortcut: undefined as ActionShortcut | undefined, - f11Shortcut: undefined as ActionShortcut | undefined, + fullscreenShortcut: undefined as ActionShortcut | undefined, }); let tooltipTimeout: ReturnType | undefined = undefined; @@ -63,9 +64,9 @@ export function createTooltipState(editor: Editor) { return state; }); }); - editor.subscriptions.subscribeJsMessage(SendShortcutF11, async (data) => { + editor.subscriptions.subscribeJsMessage(SendShortcutFullscreen, async (data) => { update((state) => { - state.f11Shortcut = data.shortcut; + state.fullscreenShortcut = operatingSystem() === "Mac" ? data.shortcutMac : data.shortcut; return state; }); }); From 30138b781aedb33eb65ef113a1c91a9dc838b113 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Fri, 16 Jan 2026 17:07:07 -0800 Subject: [PATCH 21/28] Fix fullscreen on Mac web --- editor/src/messages/frontend/frontend_message.rs | 2 +- editor/src/messages/input_mapper/input_mappings.rs | 5 +++-- frontend/src/io-managers/input.ts | 6 +++--- frontend/src/messages.ts | 3 +++ frontend/src/state-providers/fullscreen.ts | 7 ++++++- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/editor/src/messages/frontend/frontend_message.rs b/editor/src/messages/frontend/frontend_message.rs index c4c19cfe64..e5e64720ed 100644 --- a/editor/src/messages/frontend/frontend_message.rs +++ b/editor/src/messages/frontend/frontend_message.rs @@ -66,7 +66,7 @@ pub enum FrontendMessage { }, SendShortcutFullscreen { shortcut: Option, - #[serde(rename = "shortcutMc")] + #[serde(rename = "shortcutMac")] shortcut_mac: Option, }, SendShortcutAltClick { diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index f0f7d1e8e6..6ad22cb71c 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -27,6 +27,7 @@ pub fn input_mappings(zoom_with_scroll: bool) -> Mapping { use InputMapperMessage::*; use Key::*; + // TODO: Fix this failing to load the correct data (and throwing a console warning) because it's occurring before the value has been supplied during initialization from the JS `initAfterFrontendReady` let keyboard_platform = GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout(); // NOTICE: @@ -57,8 +58,8 @@ pub fn input_mappings(zoom_with_scroll: bool) -> Mapping { entry!(KeyDown(KeyZ); modifiers=[Accel, MouseLeft], action_dispatch=DocumentMessage::Noop), // // AppWindowMessage - entry!(KeyDown(F11); disabled=cfg!(target_os = "macos"), action_dispatch=AppWindowMessage::Fullscreen), - entry!(KeyDown(KeyF); modifiers=[Command, Control], disabled=cfg!(not(target_os = "macos")), action_dispatch=AppWindowMessage::Fullscreen), + entry!(KeyDown(F11); disabled=(keyboard_platform == KeyboardPlatformLayout::Mac), action_dispatch=AppWindowMessage::Fullscreen), + entry!(KeyDown(KeyF); modifiers=[Command, Control], disabled=(keyboard_platform != KeyboardPlatformLayout::Mac), action_dispatch=AppWindowMessage::Fullscreen), entry!(KeyDown(KeyQ); modifiers=[Command], disabled=cfg!(not(target_os = "macos")), action_dispatch=AppWindowMessage::Close), // // ClipboardMessage diff --git a/frontend/src/io-managers/input.ts b/frontend/src/io-managers/input.ts index e830cad007..c606bbe998 100644 --- a/frontend/src/io-managers/input.ts +++ b/frontend/src/io-managers/input.ts @@ -99,8 +99,8 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli // Web-only keyboard shortcuts if (!isDesktop()) { - // Don't redirect a fullscreen request - if (key === "F11" && e.type === "keydown" && !e.repeat) { + // Don't redirect a fullscreen request, but process it immediately instead + if (((operatingSystem() !== "Mac" && key === "F11") || (operatingSystem() === "Mac" && e.ctrlKey && e.metaKey && key === "KeyF")) && e.type === "keydown" && !e.repeat) { e.preventDefault(); fullscreen.toggleFullscreen(); return false; @@ -241,7 +241,7 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli } function onMouseDown(e: MouseEvent) { - // Block middle mouse button auto-scroll mode (the circlar gizmo that appears and allows quick scrolling by moving the cursor above or below it) + // Block middle mouse button auto-scroll mode (the circular gizmo that appears and allows quick scrolling by moving the cursor above or below it) if (e.button === BUTTON_MIDDLE) e.preventDefault(); } diff --git a/frontend/src/messages.ts b/frontend/src/messages.ts index 59263aeb94..5be4a1eea7 100644 --- a/frontend/src/messages.ts +++ b/frontend/src/messages.ts @@ -337,6 +337,8 @@ export class WindowPointerLockMove extends JsMessage { readonly y!: number; } +export class WindowFullscreen extends JsMessage {} + // Rust enum `Key` export type KeyRaw = string; // Serde converts a Rust `Key` enum variant into this format with both the `Key` variant name (called `RawKey` in TS) and the localized `label` for the key @@ -1736,6 +1738,7 @@ export const messageMakers: Record = { UpdateMaximized, UpdateFullscreen, WindowPointerLockMove, + WindowFullscreen, UpdatePropertiesPanelLayout, UpdatePropertiesPanelState, UpdateStatusBarHintsLayout, diff --git a/frontend/src/state-providers/fullscreen.ts b/frontend/src/state-providers/fullscreen.ts index 1378864e04..bde7b42cd7 100644 --- a/frontend/src/state-providers/fullscreen.ts +++ b/frontend/src/state-providers/fullscreen.ts @@ -1,8 +1,9 @@ import { writable } from "svelte/store"; import { type Editor } from "@graphite/editor"; +import { WindowFullscreen } from "@graphite/messages"; -export function createFullscreenState(_: Editor) { +export function createFullscreenState(editor: Editor) { // Experimental Keyboard API: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/keyboard // eslint-disable-next-line @typescript-eslint/no-explicit-any const keyboardLockApiSupported: Readonly = "keyboard" in navigator && (navigator as any).keyboard && "lock" in (navigator as any).keyboard; @@ -50,6 +51,10 @@ export function createFullscreenState(_: Editor) { }); } + editor.subscriptions.subscribeJsMessage(WindowFullscreen, () => { + toggleFullscreen(); + }); + return { subscribe, fullscreenModeChanged, From 482834f4936f8401b9e03ec94c88a2e277c656f6 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Sat, 17 Jan 2026 19:31:15 +0000 Subject: [PATCH 22/28] cleanup --- .../input_mapper_message_handler.rs | 22 ------------------- .../input_mapper/utility_types/misc.rs | 10 --------- 2 files changed, 32 deletions(-) diff --git a/editor/src/messages/input_mapper/input_mapper_message_handler.rs b/editor/src/messages/input_mapper/input_mapper_message_handler.rs index 166889d8bf..058fb70c83 100644 --- a/editor/src/messages/input_mapper/input_mapper_message_handler.rs +++ b/editor/src/messages/input_mapper/input_mapper_message_handler.rs @@ -4,7 +4,6 @@ use crate::messages::input_mapper::utility_types::input_keyboard::{self, Key}; use crate::messages::input_mapper::utility_types::misc::MappingEntry; use crate::messages::portfolio::utility_types::KeyboardPlatformLayout; use crate::messages::prelude::*; -use std::fmt::Write; #[derive(ExtractField)] pub struct InputMapperMessageContext<'a> { @@ -34,27 +33,6 @@ impl InputMapperMessageHandler { self.mapping = mapping; } - pub fn hints(&self, actions: ActionList) -> String { - let mut output = String::new(); - let mut actions = actions - .into_iter() - .flatten() - .filter(|a| !matches!(*a, MessageDiscriminant::Tool(ToolMessageDiscriminant::ActivateTool) | MessageDiscriminant::Debug(_))); - self.mapping - .key_down - .iter() - .enumerate() - .filter_map(|(i, m)| { - let ma = m.0.iter().find_map(|m| actions.find_map(|a| (a == m.action.to_discriminant()).then(|| m.action.to_discriminant()))); - - ma.map(|a| ((i as u8).try_into().unwrap(), a)) - }) - .for_each(|(k, a): (Key, _)| { - let _ = write!(output, "{}: {}, ", k.to_discriminant().local_name(), a.local_name().split('.').next_back().unwrap()); - }); - output.replace("Key", "") - } - pub fn action_input_mapping(&self, action_to_find: &MessageDiscriminant) -> Option { let all_key_mapping_entries = std::iter::empty() .chain(self.mapping.key_up.iter()) diff --git a/editor/src/messages/input_mapper/utility_types/misc.rs b/editor/src/messages/input_mapper/utility_types/misc.rs index bd0f4187be..59dacd9e86 100644 --- a/editor/src/messages/input_mapper/utility_types/misc.rs +++ b/editor/src/messages/input_mapper/utility_types/misc.rs @@ -29,16 +29,6 @@ impl Mapping { list.match_mapping(keyboard_state, actions) } - pub fn remove(&mut self, target_entry: &MappingEntry) { - let list = self.associated_entries_mut(&target_entry.input); - list.remove(target_entry); - } - - pub fn add(&mut self, new_entry: MappingEntry) { - let list = self.associated_entries_mut(&new_entry.input); - list.push(new_entry); - } - fn associated_entries(&self, message: &InputMapperMessage) -> &KeyMappingEntries { match message { InputMapperMessage::KeyDown(key) => &self.key_down[*key as usize], From 2cca1653c11121de4da0c76d7779271885e0b23a Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Mon, 19 Jan 2026 15:59:17 +0000 Subject: [PATCH 23/28] remove unused --- .../src/messages/input_mapper/utility_types/misc.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/editor/src/messages/input_mapper/utility_types/misc.rs b/editor/src/messages/input_mapper/utility_types/misc.rs index 59dacd9e86..2470e7a719 100644 --- a/editor/src/messages/input_mapper/utility_types/misc.rs +++ b/editor/src/messages/input_mapper/utility_types/misc.rs @@ -41,19 +41,6 @@ impl Mapping { InputMapperMessage::PointerShake => &self.pointer_shake, } } - - fn associated_entries_mut(&mut self, message: &InputMapperMessage) -> &mut KeyMappingEntries { - match message { - InputMapperMessage::KeyDown(key) => &mut self.key_down[*key as usize], - InputMapperMessage::KeyUp(key) => &mut self.key_up[*key as usize], - InputMapperMessage::KeyDownNoRepeat(key) => &mut self.key_down_no_repeat[*key as usize], - InputMapperMessage::KeyUpNoRepeat(key) => &mut self.key_up_no_repeat[*key as usize], - InputMapperMessage::DoubleClick(key) => &mut self.double_click[*key as usize], - InputMapperMessage::WheelScroll => &mut self.wheel_scroll, - InputMapperMessage::PointerMove => &mut self.pointer_move, - InputMapperMessage::PointerShake => &mut self.pointer_shake, - } - } } #[derive(Debug, Clone)] From 0aea68987dd83d2a198dc7c4100c786dad7f334d Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Mon, 19 Jan 2026 15:59:38 +0000 Subject: [PATCH 24/28] Replace globals with editor enviroment --- desktop/src/app.rs | 16 ++---- desktop/src/lib.rs | 8 +-- .../src/handle_desktop_wrapper_message.rs | 12 +--- desktop/wrapper/src/lib.rs | 21 +++++-- desktop/wrapper/src/messages.rs | 7 --- editor/src/application.rs | 56 ++++++++++++++++++- editor/src/dispatcher.rs | 13 ++--- .../messages/app_window/app_window_message.rs | 4 +- .../app_window/app_window_message_handler.rs | 27 ++++++--- .../src/messages/globals/global_variables.rs | 4 -- .../src/messages/globals/globals_message.rs | 8 --- .../globals/globals_message_handler.rs | 20 ------- editor/src/messages/globals/mod.rs | 9 --- .../input_mapper_message_handler.rs | 4 +- .../messages/input_mapper/input_mappings.rs | 13 ++--- .../utility_types/input_keyboard.rs | 6 +- editor/src/messages/message.rs | 3 +- editor/src/messages/mod.rs | 1 - .../navigation/navigation_message_handler.rs | 6 +- .../src/messages/portfolio/utility_types.rs | 32 ++++------- editor/src/messages/prelude.rs | 2 - editor/src/test_utils.rs | 2 +- frontend/src/components/Editor.svelte | 3 +- frontend/src/editor.ts | 15 +++-- frontend/wasm/src/editor_api.rs | 39 ++++++------- node-graph/libraries/core-types/src/uuid.rs | 18 ++---- 26 files changed, 164 insertions(+), 185 deletions(-) delete mode 100644 editor/src/messages/globals/global_variables.rs delete mode 100644 editor/src/messages/globals/globals_message.rs delete mode 100644 editor/src/messages/globals/globals_message_handler.rs delete mode 100644 editor/src/messages/globals/mod.rs diff --git a/desktop/src/app.rs b/desktop/src/app.rs index e3a3970c85..f06baa89f3 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -1,3 +1,4 @@ +use rand::Rng; use rfd::AsyncFileDialog; use std::fs; use std::path::PathBuf; @@ -16,7 +17,7 @@ use crate::event::{AppEvent, AppEventScheduler}; use crate::persist::PersistentData; use crate::render::{RenderError, RenderState}; use crate::window::Window; -use crate::wrapper::messages::{DesktopFrontendMessage, DesktopWrapperMessage, InputMessage, MouseKeys, MouseState, Platform}; +use crate::wrapper::messages::{DesktopFrontendMessage, DesktopWrapperMessage, InputMessage, MouseKeys, MouseState}; use crate::wrapper::{DesktopWrapper, NodeGraphExecutionResult, WgpuContext, serialize_frontend_messages}; pub(crate) struct App { @@ -46,7 +47,8 @@ pub(crate) struct App { } impl App { - pub(crate) fn init() { + pub(crate) fn init(wgpu_context: &WgpuContext) { + DesktopWrapper::init(wgpu_context, rand::rng().random()); Window::init(); } @@ -476,16 +478,6 @@ impl ApplicationHandler for App { } self.resize(); - - self.desktop_wrapper.init(self.wgpu_context.clone()); - - #[cfg(target_os = "windows")] - let platform = Platform::Windows; - #[cfg(target_os = "macos")] - let platform = Platform::Mac; - #[cfg(target_os = "linux")] - let platform = Platform::Linux; - self.dispatch_desktop_wrapper_message(DesktopWrapperMessage::UpdatePlatform(platform)); } fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) { diff --git a/desktop/src/lib.rs b/desktop/src/lib.rs index 93ac5c58e8..eb7d6d5bdb 100644 --- a/desktop/src/lib.rs +++ b/desktop/src/lib.rs @@ -66,10 +66,10 @@ pub fn start() { } }; - App::init(); - let wgpu_context = futures::executor::block_on(gpu_context::create_wgpu_context()); + App::init(&wgpu_context); + let event_loop = EventLoop::new().unwrap(); let (app_event_sender, app_event_receiver) = std::sync::mpsc::channel(); let app_event_scheduler = event_loop.create_app_event_scheduler(app_event_sender); @@ -78,9 +78,9 @@ pub fn start() { let cef_handler = cef::CefHandler::new(wgpu_context.clone(), app_event_scheduler.clone(), cef_view_info_receiver); let cef_context = match cef_context_builder.initialize(cef_handler, cli.disable_ui_acceleration) { - Ok(c) => { + Ok(context) => { tracing::info!("CEF initialized successfully"); - c + context } Err(cef::InitError::AlreadyRunning) => { tracing::error!("Another instance is already running, Exiting."); diff --git a/desktop/wrapper/src/handle_desktop_wrapper_message.rs b/desktop/wrapper/src/handle_desktop_wrapper_message.rs index a830931fff..5330e67977 100644 --- a/desktop/wrapper/src/handle_desktop_wrapper_message.rs +++ b/desktop/wrapper/src/handle_desktop_wrapper_message.rs @@ -1,11 +1,10 @@ use graphene_std::Color; use graphene_std::raster::Image; -use graphite_editor::messages::app_window::app_window_message_handler::AppWindowPlatform; use graphite_editor::messages::clipboard::utility_types::ClipboardContentRaw; use graphite_editor::messages::prelude::*; use super::DesktopWrapperMessageDispatcher; -use super::messages::{DesktopFrontendMessage, DesktopWrapperMessage, EditorMessage, OpenFileDialogContext, Platform, SaveFileDialogContext}; +use super::messages::{DesktopFrontendMessage, DesktopWrapperMessage, EditorMessage, OpenFileDialogContext, SaveFileDialogContext}; pub(super) fn handle_desktop_wrapper_message(dispatcher: &mut DesktopWrapperMessageDispatcher, message: DesktopWrapperMessage) { match message { @@ -111,15 +110,6 @@ pub(super) fn handle_desktop_wrapper_message(dispatcher: &mut DesktopWrapperMess dispatcher.queue_editor_message(message); } DesktopWrapperMessage::PollNodeGraphEvaluation => dispatcher.poll_node_graph_evaluation(), - DesktopWrapperMessage::UpdatePlatform(platform) => { - let platform = match platform { - Platform::Windows => AppWindowPlatform::Windows, - Platform::Mac => AppWindowPlatform::Mac, - Platform::Linux => AppWindowPlatform::Linux, - }; - let message = AppWindowMessage::UpdatePlatform { platform }; - dispatcher.queue_editor_message(message); - } DesktopWrapperMessage::UpdateMaximized { maximized } => { let message = FrontendMessage::UpdateMaximized { maximized }; dispatcher.queue_editor_message(message); diff --git a/desktop/wrapper/src/lib.rs b/desktop/wrapper/src/lib.rs index bdb9978e61..d2960d476f 100644 --- a/desktop/wrapper/src/lib.rs +++ b/desktop/wrapper/src/lib.rs @@ -1,5 +1,5 @@ use graph_craft::wasm_application_io::WasmApplicationIo; -use graphite_editor::application::Editor; +use graphite_editor::application::{Editor, Environment, Host, Platform}; use graphite_editor::messages::prelude::{FrontendMessage, Message}; // TODO: Remove usage of this reexport in desktop create and remove this line @@ -27,15 +27,24 @@ pub struct DesktopWrapper { } impl DesktopWrapper { - pub fn new() -> Self { - Self { editor: Editor::new() } - } + pub fn init(wgpu_context: &WgpuContext, uuid_random_seed: u64) { + #[cfg(target_os = "windows")] + let host = Host::Windows; + #[cfg(target_os = "macos")] + let host = Host::Mac; + #[cfg(target_os = "linux")] + let host = Host::Linux; + let env = Environment { platform: Platform::Desktop, host }; + Editor::init(env, uuid_random_seed); - pub fn init(&self, wgpu_context: WgpuContext) { - let application_io = WasmApplicationIo::new_with_context(wgpu_context); + let application_io = WasmApplicationIo::new_with_context(wgpu_context.clone()); futures::executor::block_on(graphite_editor::node_graph_executor::replace_application_io(application_io)); } + pub fn new() -> Self { + Self { editor: Editor::new() } + } + pub fn dispatch(&mut self, message: DesktopWrapperMessage) -> Vec { let mut executor = DesktopWrapperMessageDispatcher::new(&mut self.editor); executor.queue_desktop_wrapper_message(message); diff --git a/desktop/wrapper/src/messages.rs b/desktop/wrapper/src/messages.rs index 08c31df36a..23a9247547 100644 --- a/desktop/wrapper/src/messages.rs +++ b/desktop/wrapper/src/messages.rs @@ -109,7 +109,6 @@ pub enum DesktopWrapperMessage { content: Vec, }, PollNodeGraphEvaluation, - UpdatePlatform(Platform), UpdateMaximized { maximized: bool, }, @@ -163,12 +162,6 @@ pub enum SaveFileDialogContext { File { content: Vec }, } -pub enum Platform { - Windows, - Mac, - Linux, -} - pub enum MenuItem { Action { id: String, diff --git a/editor/src/application.rs b/editor/src/application.rs index 34016c2fa2..59a21dc72b 100644 --- a/editor/src/application.rs +++ b/editor/src/application.rs @@ -1,3 +1,5 @@ +use std::sync::OnceLock; + use crate::dispatcher::Dispatcher; use crate::messages::prelude::*; pub use graphene_std::uuid::*; @@ -8,8 +10,12 @@ pub struct Editor { } impl Editor { - /// Construct the editor. - /// Remember to provide a random seed with `editor::set_uuid_seed(seed)` before any editors can be used. + pub fn init(enviroment: Environment, uuid_random_seed: u64) { + ENVIROMENT.set(enviroment).expect("Editor shoud only be initialized once"); + graphene_std::uuid::init_rng(uuid_random_seed); + } + + /// `Editor::init` must be called before this function pub fn new() -> Self { Self { dispatcher: Dispatcher::new() } } @@ -31,13 +37,57 @@ impl Editor { self.dispatcher.poll_node_graph_evaluation(responses) } } - impl Default for Editor { fn default() -> Self { Self::new() } } +static ENVIROMENT: OnceLock = OnceLock::new(); +impl Editor { + pub fn environment() -> &'static Environment { + ENVIROMENT.get().expect("Editor environment accessed before initialization") + } +} + +#[derive(Clone, Copy, Debug)] +pub struct Environment { + pub platform: Platform, + pub host: Host, +} +#[derive(Clone, Copy, Debug)] +pub enum Platform { + Desktop, + Web, +} +#[derive(Clone, Copy, Debug)] +pub enum Host { + Linux, + Mac, + Windows, + Unknown, +} +impl Environment { + pub fn is_desktop(&self) -> bool { + matches!(self.platform, Platform::Desktop) + } + pub fn is_web(&self) -> bool { + matches!(self.platform, Platform::Web) + } + pub fn is_linux(&self) -> bool { + matches!(self.host, Host::Linux) + } + pub fn is_mac(&self) -> bool { + matches!(self.host, Host::Mac) + } + pub fn is_windows(&self) -> bool { + matches!(self.host, Host::Windows) + } + pub fn is_unknown_host(&self) -> bool { + matches!(self.host, Host::Unknown) + } +} + pub const GRAPHITE_RELEASE_SERIES: &str = env!("GRAPHITE_RELEASE_SERIES"); pub const GRAPHITE_GIT_COMMIT_BRANCH: Option<&str> = option_env!("GRAPHITE_GIT_COMMIT_BRANCH"); pub const GRAPHITE_GIT_COMMIT_HASH: &str = env!("GRAPHITE_GIT_COMMIT_HASH"); diff --git a/editor/src/dispatcher.rs b/editor/src/dispatcher.rs index cfffefefe2..8ea9b011d7 100644 --- a/editor/src/dispatcher.rs +++ b/editor/src/dispatcher.rs @@ -1,3 +1,4 @@ +use crate::application::Editor; use crate::messages::debug::utility_types::MessageLoggingVerbosity; use crate::messages::defer::DeferMessageContext; use crate::messages::dialog::DialogMessageContext; @@ -23,7 +24,6 @@ pub struct DispatcherMessageHandlers { debug_message_handler: DebugMessageHandler, defer_message_handler: DeferMessageHandler, dialog_message_handler: DialogMessageHandler, - globals_message_handler: GlobalsMessageHandler, input_preprocessor_message_handler: InputPreprocessorMessageHandler, key_mapping_message_handler: KeyMappingMessageHandler, layout_message_handler: LayoutMessageHandler, @@ -192,17 +192,12 @@ impl Dispatcher { self.responses.push(message); } } - Message::Globals(message) => { - self.message_handlers.globals_message_handler.process_message(message, &mut queue, ()); - } Message::InputPreprocessor(message) => { - let keyboard_platform = GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout(); - self.message_handlers.input_preprocessor_message_handler.process_message( message, &mut queue, InputPreprocessorMessageContext { - keyboard_platform, + keyboard_platform: Editor::environment().host.into(), viewport: &self.message_handlers.viewport_message_handler, }, ); @@ -309,6 +304,10 @@ impl Dispatcher { Message::Viewport(message) => { self.message_handlers.viewport_message_handler.process_message(message, &mut queue, ()); } + Message::Init => { + self.handle_message(AppWindowMessage::Init, false); + self.handle_message(PortfolioMessage::Init, false); + } Message::NoOp => {} Message::Batched { messages } => { messages.into_iter().for_each(|message| self.handle_message(message, false)); diff --git a/editor/src/messages/app_window/app_window_message.rs b/editor/src/messages/app_window/app_window_message.rs index 0dcc39ae5d..3a11c8a6d5 100644 --- a/editor/src/messages/app_window/app_window_message.rs +++ b/editor/src/messages/app_window/app_window_message.rs @@ -1,11 +1,9 @@ use crate::messages::prelude::*; -use super::app_window_message_handler::AppWindowPlatform; - #[impl_message(Message, AppWindow)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)] pub enum AppWindowMessage { - UpdatePlatform { platform: AppWindowPlatform }, + Init, PointerLock, PointerLockMove { x: f64, y: f64 }, Close, diff --git a/editor/src/messages/app_window/app_window_message_handler.rs b/editor/src/messages/app_window/app_window_message_handler.rs index 66c4ef0bab..b0090e8401 100644 --- a/editor/src/messages/app_window/app_window_message_handler.rs +++ b/editor/src/messages/app_window/app_window_message_handler.rs @@ -1,19 +1,19 @@ -use crate::messages::app_window::AppWindowMessage; +use crate::application::{Editor, Environment}; use crate::messages::prelude::*; +use crate::{application::Host, messages::app_window::AppWindowMessage}; use graphite_proc_macros::{ExtractField, message_handler_data}; #[derive(Debug, Clone, Default, ExtractField)] -pub struct AppWindowMessageHandler { - platform: AppWindowPlatform, -} +pub struct AppWindowMessageHandler {} #[message_handler_data] impl MessageHandler for AppWindowMessageHandler { fn process_message(&mut self, message: AppWindowMessage, responses: &mut std::collections::VecDeque, _: ()) { match message { - AppWindowMessage::UpdatePlatform { platform } => { - self.platform = platform; - responses.add(FrontendMessage::UpdatePlatform { platform: self.platform }); + AppWindowMessage::Init => { + responses.add(FrontendMessage::UpdatePlatform { + platform: Editor::environment().into(), + }); } AppWindowMessage::PointerLock => { responses.add(FrontendMessage::WindowPointerLock); @@ -66,3 +66,16 @@ pub enum AppWindowPlatform { Mac, Linux, } + +impl From<&Environment> for AppWindowPlatform { + fn from(environment: &Environment) -> Self { + if environment.is_web() { + return AppWindowPlatform::Web; + } + match environment.host { + Host::Linux => AppWindowPlatform::Linux, + Host::Mac => AppWindowPlatform::Mac, + Host::Windows | Host::Unknown => AppWindowPlatform::Windows, + } + } +} diff --git a/editor/src/messages/globals/global_variables.rs b/editor/src/messages/globals/global_variables.rs deleted file mode 100644 index b3fb71da06..0000000000 --- a/editor/src/messages/globals/global_variables.rs +++ /dev/null @@ -1,4 +0,0 @@ -use crate::messages::portfolio::utility_types::Platform; -use std::sync::OnceLock; - -pub static GLOBAL_PLATFORM: OnceLock = OnceLock::new(); diff --git a/editor/src/messages/globals/globals_message.rs b/editor/src/messages/globals/globals_message.rs deleted file mode 100644 index 11a39e9cc1..0000000000 --- a/editor/src/messages/globals/globals_message.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::messages::portfolio::utility_types::Platform; -use crate::messages::prelude::*; - -#[impl_message(Message, Globals)] -#[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize)] -pub enum GlobalsMessage { - SetPlatform { platform: Platform }, -} diff --git a/editor/src/messages/globals/globals_message_handler.rs b/editor/src/messages/globals/globals_message_handler.rs deleted file mode 100644 index 8974bd16ce..0000000000 --- a/editor/src/messages/globals/globals_message_handler.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::messages::prelude::*; - -#[derive(Debug, Default, ExtractField)] -pub struct GlobalsMessageHandler {} - -#[message_handler_data] -impl MessageHandler for GlobalsMessageHandler { - fn process_message(&mut self, message: GlobalsMessage, _responses: &mut VecDeque, _: ()) { - match message { - GlobalsMessage::SetPlatform { platform } => { - if GLOBAL_PLATFORM.get() != Some(&platform) { - GLOBAL_PLATFORM.set(platform).expect("Failed to set GLOBAL_PLATFORM"); - } - } - } - } - - advertise_actions!(GlobalsMessageDiscriminant; - ); -} diff --git a/editor/src/messages/globals/mod.rs b/editor/src/messages/globals/mod.rs deleted file mode 100644 index 65cc989f6e..0000000000 --- a/editor/src/messages/globals/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod globals_message; -mod globals_message_handler; - -pub mod global_variables; - -#[doc(inline)] -pub use globals_message::{GlobalsMessage, GlobalsMessageDiscriminant}; -#[doc(inline)] -pub use globals_message_handler::GlobalsMessageHandler; diff --git a/editor/src/messages/input_mapper/input_mapper_message_handler.rs b/editor/src/messages/input_mapper/input_mapper_message_handler.rs index 058fb70c83..d616ac4050 100644 --- a/editor/src/messages/input_mapper/input_mapper_message_handler.rs +++ b/editor/src/messages/input_mapper/input_mapper_message_handler.rs @@ -1,5 +1,6 @@ use super::utility_types::input_keyboard::KeysGroup; use super::utility_types::misc::Mapping; +use crate::application::Editor; use crate::messages::input_mapper::utility_types::input_keyboard::{self, Key}; use crate::messages::input_mapper::utility_types::misc::MappingEntry; use crate::messages::portfolio::utility_types::KeyboardPlatformLayout; @@ -48,8 +49,7 @@ impl InputMapperMessageHandler { let found_actions = all_mapping_entries.filter(|entry| entry.action.to_discriminant() == *action_to_find); // Get the `Key` for this platform's accelerator key - let keyboard_layout = || GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout(); - let platform_accel_key = match keyboard_layout() { + let platform_accel_key = match Editor::environment().host.into() { KeyboardPlatformLayout::Standard => Key::Control, KeyboardPlatformLayout::Mac => Key::Command, }; diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index 6ad22cb71c..8daf93ca9f 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -1,3 +1,4 @@ +use crate::application::Editor; use crate::consts::{BIG_NUDGE_AMOUNT, BRUSH_SIZE_CHANGE_KEYBOARD, NUDGE_AMOUNT}; use crate::messages::input_mapper::key_mapping::MappingVariant; use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeyStates}; @@ -8,7 +9,6 @@ use crate::messages::input_mapper::utility_types::misc::{KeyMappingEntries, Mapp use crate::messages::portfolio::document::node_graph::utility_types::Direction; use crate::messages::portfolio::document::utility_types::clipboards::Clipboard; use crate::messages::portfolio::document::utility_types::misc::GroupFolderType; -use crate::messages::portfolio::utility_types::KeyboardPlatformLayout; use crate::messages::prelude::*; use crate::messages::tool::tool_messages::brush_tool::BrushToolMessageOptionsUpdate; use crate::messages::tool::tool_messages::select_tool::SelectToolPointerKeys; @@ -27,8 +27,7 @@ pub fn input_mappings(zoom_with_scroll: bool) -> Mapping { use InputMapperMessage::*; use Key::*; - // TODO: Fix this failing to load the correct data (and throwing a console warning) because it's occurring before the value has been supplied during initialization from the JS `initAfterFrontendReady` - let keyboard_platform = GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout(); + let is_mac = Editor::environment().is_mac(); // NOTICE: // If a new mapping you added here isn't working (and perhaps another lower-precedence one is instead), make sure to advertise @@ -58,8 +57,8 @@ pub fn input_mappings(zoom_with_scroll: bool) -> Mapping { entry!(KeyDown(KeyZ); modifiers=[Accel, MouseLeft], action_dispatch=DocumentMessage::Noop), // // AppWindowMessage - entry!(KeyDown(F11); disabled=(keyboard_platform == KeyboardPlatformLayout::Mac), action_dispatch=AppWindowMessage::Fullscreen), - entry!(KeyDown(KeyF); modifiers=[Command, Control], disabled=(keyboard_platform != KeyboardPlatformLayout::Mac), action_dispatch=AppWindowMessage::Fullscreen), + entry!(KeyDown(F11); disabled=is_mac, action_dispatch=AppWindowMessage::Fullscreen), + entry!(KeyDown(KeyF); modifiers=[Command, Control], disabled=!is_mac, action_dispatch=AppWindowMessage::Fullscreen), entry!(KeyDown(KeyQ); modifiers=[Command], disabled=cfg!(not(target_os = "macos")), action_dispatch=AppWindowMessage::Close), // // ClipboardMessage @@ -429,8 +428,8 @@ pub fn input_mappings(zoom_with_scroll: bool) -> Mapping { entry!(WheelScroll; modifiers=[Shift], disabled=zoom_with_scroll, action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: true }), entry!(WheelScroll; disabled=zoom_with_scroll, action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: false }), // On Mac, the OS already converts Shift+scroll into horizontal scrolling so we have to reverse the behavior from normal to produce the same outcome - entry!(WheelScroll; modifiers=[Control], disabled=!zoom_with_scroll, action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: keyboard_platform == KeyboardPlatformLayout::Mac }), - entry!(WheelScroll; modifiers=[Shift], disabled=!zoom_with_scroll, action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: keyboard_platform != KeyboardPlatformLayout::Mac }), + entry!(WheelScroll; modifiers=[Control], disabled=!zoom_with_scroll, action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: is_mac }), + entry!(WheelScroll; modifiers=[Shift], disabled=!zoom_with_scroll, action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: !is_mac }), entry!(WheelScroll; disabled=!zoom_with_scroll, action_dispatch=NavigationMessage::CanvasZoomMouseWheel), entry!(KeyDown(PageUp); modifiers=[Shift], action_dispatch=NavigationMessage::CanvasPanByViewportFraction { delta: DVec2::new(1., 0.) }), entry!(KeyDown(PageDown); modifiers=[Shift], action_dispatch=NavigationMessage::CanvasPanByViewportFraction { delta: DVec2::new(-1., 0.) }), diff --git a/editor/src/messages/input_mapper/utility_types/input_keyboard.rs b/editor/src/messages/input_mapper/utility_types/input_keyboard.rs index e5b97c2bc3..dfc4d7cbcd 100644 --- a/editor/src/messages/input_mapper/utility_types/input_keyboard.rs +++ b/editor/src/messages/input_mapper/utility_types/input_keyboard.rs @@ -1,3 +1,4 @@ +use crate::application::Editor; use crate::messages::portfolio::utility_types::KeyboardPlatformLayout; use crate::messages::prelude::*; use bitflags::bitflags; @@ -258,7 +259,7 @@ impl fmt::Display for Key { return write!(f, "{}", key_name.chars().skip(KEY_PREFIX.len()).collect::()); } - let keyboard_layout = || GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout(); + let keyboard_layout = || Editor::environment().host.into(); let name = match self { // Writing system keys @@ -356,10 +357,9 @@ impl fmt::Display for KeysGroup { .0 .iter() .map(|key| { - let keyboard_layout = GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout(); let key_is_modifier = matches!(*key, Key::Control | Key::Command | Key::Alt | Key::Shift | Key::Meta | Key::Accel); - if keyboard_layout == KeyboardPlatformLayout::Mac && key_is_modifier { + if Editor::environment().is_mac() && key_is_modifier { key.to_string() } else { key.to_string() + JOINER_MARK diff --git a/editor/src/messages/message.rs b/editor/src/messages/message.rs index 4b104495ee..944e13dc56 100644 --- a/editor/src/messages/message.rs +++ b/editor/src/messages/message.rs @@ -22,8 +22,6 @@ pub enum Message { #[child] Frontend(FrontendMessage), #[child] - Globals(GlobalsMessage), - #[child] InputPreprocessor(InputPreprocessorMessage), #[child] KeyMapping(KeyMappingMessage), @@ -41,6 +39,7 @@ pub enum Message { Viewport(ViewportMessage), // Messages + Init, Batched { messages: Box<[Message]>, }, diff --git a/editor/src/messages/mod.rs b/editor/src/messages/mod.rs index 7edfea806e..7b3c39e7d1 100644 --- a/editor/src/messages/mod.rs +++ b/editor/src/messages/mod.rs @@ -8,7 +8,6 @@ pub mod debug; pub mod defer; pub mod dialog; pub mod frontend; -pub mod globals; pub mod input_mapper; pub mod input_preprocessor; pub mod layout; diff --git a/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs b/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs index 00e280b51c..3806a1e245 100644 --- a/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs +++ b/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs @@ -1,3 +1,4 @@ +use crate::application::Editor; use crate::consts::{ VIEWPORT_ROTATE_SNAP_INTERVAL, VIEWPORT_SCROLL_RATE, VIEWPORT_ZOOM_LEVELS, VIEWPORT_ZOOM_MIN_FRACTION_COVER, VIEWPORT_ZOOM_MOUSE_RATE, VIEWPORT_ZOOM_SCALE_MAX, VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR, @@ -8,7 +9,6 @@ use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition; use crate::messages::portfolio::document::navigation::utility_types::NavigationOperation; use crate::messages::portfolio::document::utility_types::misc::PTZ; use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface; -use crate::messages::portfolio::utility_types::KeyboardPlatformLayout; use crate::messages::prelude::*; use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo}; use glam::{DAffine2, DVec2}; @@ -176,9 +176,7 @@ impl MessageHandler> for Navigat } NavigationMessage::CanvasPanMouseWheel { use_y_as_x } => { // On Mac, the OS already converts Shift+scroll into horizontal scrolling - let keyboard_platform = GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout(); - - let delta = if use_y_as_x && keyboard_platform == KeyboardPlatformLayout::Standard { + let delta = if use_y_as_x && !Editor::environment().is_mac() { (ipp.mouse.scroll_delta.y, 0.).into() } else { ipp.mouse.scroll_delta.as_dvec2() diff --git a/editor/src/messages/portfolio/utility_types.rs b/editor/src/messages/portfolio/utility_types.rs index 54a526ecdc..ba2cb9e330 100644 --- a/editor/src/messages/portfolio/utility_types.rs +++ b/editor/src/messages/portfolio/utility_types.rs @@ -1,5 +1,7 @@ use graphene_std::text::{Font, FontCache}; +use crate::application::Host; + #[derive(Debug, Default)] pub struct PersistentData { pub font_cache: FontCache, @@ -82,28 +84,6 @@ impl FontCatalogStyle { } } -#[derive(PartialEq, Eq, Clone, Copy, Default, Debug, serde::Serialize, serde::Deserialize)] -pub enum Platform { - #[default] - Unknown, - Windows, - Mac, - Linux, -} - -impl Platform { - pub fn as_keyboard_platform_layout(&self) -> KeyboardPlatformLayout { - match self { - Platform::Mac => KeyboardPlatformLayout::Mac, - Platform::Windows | Platform::Linux => KeyboardPlatformLayout::Standard, - Platform::Unknown => { - warn!("The platform has not been set, remember to send `GlobalsMessage::SetPlatform` during editor initialization."); - KeyboardPlatformLayout::Standard - } - } - } -} - #[derive(PartialEq, Eq, Clone, Copy, Default, Debug, serde::Serialize, serde::Deserialize)] pub enum KeyboardPlatformLayout { /// Standard keyboard mapping used by Windows and Linux @@ -112,6 +92,14 @@ pub enum KeyboardPlatformLayout { /// Keyboard mapping used by Macs where Command is sometimes used in favor of Control Mac, } +impl From for KeyboardPlatformLayout { + fn from(host: Host) -> Self { + match host { + Host::Mac => KeyboardPlatformLayout::Mac, + Host::Windows | Host::Linux | Host::Unknown => KeyboardPlatformLayout::Standard, + } + } +} #[derive(PartialEq, Eq, Clone, Copy, Debug, Default, serde::Serialize, serde::Deserialize)] pub enum PanelType { diff --git a/editor/src/messages/prelude.rs b/editor/src/messages/prelude.rs index f64cbf0ee9..d331697cc0 100644 --- a/editor/src/messages/prelude.rs +++ b/editor/src/messages/prelude.rs @@ -15,7 +15,6 @@ pub use crate::messages::dialog::new_document_dialog::{NewDocumentDialogMessage, pub use crate::messages::dialog::preferences_dialog::{PreferencesDialogMessage, PreferencesDialogMessageContext, PreferencesDialogMessageDiscriminant, PreferencesDialogMessageHandler}; pub use crate::messages::dialog::{DialogMessage, DialogMessageContext, DialogMessageDiscriminant, DialogMessageHandler}; pub use crate::messages::frontend::{FrontendMessage, FrontendMessageDiscriminant}; -pub use crate::messages::globals::{GlobalsMessage, GlobalsMessageDiscriminant, GlobalsMessageHandler}; pub use crate::messages::input_mapper::key_mapping::{KeyMappingMessage, KeyMappingMessageContext, KeyMappingMessageDiscriminant, KeyMappingMessageHandler}; pub use crate::messages::input_mapper::{InputMapperMessage, InputMapperMessageContext, InputMapperMessageDiscriminant, InputMapperMessageHandler}; pub use crate::messages::input_preprocessor::{InputPreprocessorMessage, InputPreprocessorMessageContext, InputPreprocessorMessageDiscriminant, InputPreprocessorMessageHandler}; @@ -51,7 +50,6 @@ pub use crate::messages::tool::tool_messages::spline_tool::{SplineToolMessage, S pub use crate::messages::tool::tool_messages::text_tool::{TextToolMessage, TextToolMessageDiscriminant}; // Helper/miscellaneous -pub use crate::messages::globals::global_variables::*; pub use crate::messages::portfolio::document::utility_types::misc::DocumentId; pub use graphite_proc_macros::*; pub use std::collections::{HashMap, HashSet, VecDeque}; diff --git a/editor/src/test_utils.rs b/editor/src/test_utils.rs index 63291d1f1c..b6c4c34583 100644 --- a/editor/src/test_utils.rs +++ b/editor/src/test_utils.rs @@ -33,7 +33,7 @@ impl EditorTestUtils { // It isn't sufficient to guard the message dispatch here with a check if the once_cell is empty, because that isn't atomic and the time between checking and handling the dispatch can let multiple through. let _ = GLOBAL_PLATFORM.set(Platform::Windows).is_ok(); - editor.handle_message(PortfolioMessage::Init); + editor.handle_message(Message::Init); Self { editor, runtime } } diff --git a/frontend/src/components/Editor.svelte b/frontend/src/components/Editor.svelte index 584201eb51..1e220a1e37 100644 --- a/frontend/src/components/Editor.svelte +++ b/frontend/src/components/Editor.svelte @@ -16,7 +16,6 @@ import { createNodeGraphState } from "@graphite/state-providers/node-graph"; import { createPortfolioState } from "@graphite/state-providers/portfolio"; import { createTooltipState } from "@graphite/state-providers/tooltip"; - import { operatingSystem } from "@graphite/utility-functions/platform"; import MainWindow from "@graphite/components/window/MainWindow.svelte"; @@ -51,7 +50,7 @@ onMount(() => { // Initialize certain setup tasks required by the editor backend to be ready for the user now that the frontend is ready - editor.handle.initAfterFrontendReady(operatingSystem()); + editor.handle.initAfterFrontendReady(); }); onDestroy(() => { diff --git a/frontend/src/editor.ts b/frontend/src/editor.ts index fcbbba3eee..e33198cb9c 100644 --- a/frontend/src/editor.ts +++ b/frontend/src/editor.ts @@ -1,7 +1,9 @@ // import { panicProxy } from "@graphite/utility-functions/panic-proxy"; -import init, { setRandomSeed, wasmMemory, EditorHandle, receiveNativeMessage } from "@graphite/../wasm/pkg/graphite_wasm"; + +import init, { wasmMemory, EditorHandle, receiveNativeMessage, editorInit } from "@graphite/../wasm/pkg/graphite_wasm"; import { type JsMessageType } from "@graphite/messages"; import { createSubscriptionRouter, type SubscriptionRouter } from "@graphite/subscription-router"; +import { operatingSystem } from "@graphite/utility-functions/platform"; export type Editor = { raw: WebAssembly.Memory; @@ -28,11 +30,6 @@ export async function initWasm() { (window as any).imageCanvases = {}; // eslint-disable-next-line @typescript-eslint/no-explicit-any (window as any).receiveNativeMessage = receiveNativeMessage; - - // Provide a random starter seed which must occur after initializing the WASM module, since WASM can't generate its own random numbers - const randomSeedFloat = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); - const randomSeed = BigInt(randomSeedFloat); - setRandomSeed(randomSeed); } // Should be called after running `initWasm()` and its promise resolving. @@ -41,6 +38,12 @@ export function createEditor(): Editor { if (!wasmImport) throw new Error("Editor WASM backend was not initialized at application startup"); const raw: WebAssembly.Memory = wasmImport; + // Provide a random starter seed which must occur after initializing the WASM module, since WASM can't generate its own random numbers + const randomSeedFloat = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); + const randomSeed = BigInt(randomSeedFloat); + + editorInit(operatingSystem(), randomSeed); + // Handle: object containing many functions from `editor_api.rs` that are part of the `EditorHandle` struct (generated by wasm-bindgen) const handle: EditorHandle = new EditorHandle((messageType: JsMessageType, messageData: Record) => { // This callback is called by WASM when a FrontendMessage is received from the WASM wrapper `EditorHandle` diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index cfb4024914..df23da1951 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -12,7 +12,7 @@ use editor::messages::input_mapper::utility_types::input_keyboard::ModifierKeys; use editor::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, ScrollDelta}; use editor::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use editor::messages::portfolio::document::utility_types::network_interface::ImportOrExport; -use editor::messages::portfolio::utility_types::{FontCatalog, FontCatalogFamily, Platform}; +use editor::messages::portfolio::utility_types::{FontCatalog, FontCatalogFamily}; use editor::messages::prelude::*; use editor::messages::tool::tool_messages::tool_prelude::WidgetId; use graph_craft::document::NodeId; @@ -31,7 +31,7 @@ use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, ImageData, window}; #[cfg(not(feature = "native"))] use crate::EDITOR; #[cfg(not(feature = "native"))] -use editor::application::Editor; +use editor::application::{Editor, Environment, Host, Platform}; static IMAGE_DATA_HASH: AtomicU64 = AtomicU64::new(0); @@ -43,13 +43,6 @@ fn calculate_hash(t: &T) -> u64 { hasher.finish() } -/// Set the random seed used by the editor by calling this from JS upon initialization. -/// This is necessary because WASM doesn't have a random number generator. -#[wasm_bindgen(js_name = setRandomSeed)] -pub fn set_random_seed(seed: u64) { - editor::application::set_uuid_seed(seed); -} - /// Provides a handle to access the raw WASM memory. #[wasm_bindgen(js_name = wasmMemory)] pub fn wasm_memory() -> JsValue { @@ -86,6 +79,22 @@ impl EditorHandle { } } +#[cfg(not(feature = "native"))] +#[wasm_bindgen(js_name = editorInit)] +pub fn editor_init(platform: String, uuid_random_seed: u64) { + let host = match platform.as_str() { + "Linux" => Host::Linux, + "Mac" => Host::Mac, + "Windows" => Host::Windows, + _ => Host::Unknown, + }; + Editor::init(Environment { platform: Platform::Web, host }, uuid_random_seed); +} + +#[cfg(feature = "native")] +#[wasm_bindgen(js_name = editorInit)] +pub fn editor_init(_platform: String, _uuid_random_seed: u64) {} + #[wasm_bindgen] impl EditorHandle { #[cfg(not(feature = "native"))] @@ -184,19 +193,11 @@ impl EditorHandle { // ======================================================================== #[wasm_bindgen(js_name = initAfterFrontendReady)] - pub fn init_after_frontend_ready(&self, platform: String) { + pub fn init_after_frontend_ready(&self) { #[cfg(feature = "native")] crate::native_communcation::initialize_native_communication(); - // Send initialization messages - let platform = match platform.as_str() { - "Windows" => Platform::Windows, - "Mac" => Platform::Mac, - "Linux" => Platform::Linux, - _ => Platform::Unknown, - }; - self.dispatch(GlobalsMessage::SetPlatform { platform }); - self.dispatch(PortfolioMessage::Init); + self.dispatch(Message::Init); // Poll node graph evaluation on `requestAnimationFrame` { diff --git a/node-graph/libraries/core-types/src/uuid.rs b/node-graph/libraries/core-types/src/uuid.rs index 3df5007e08..d37dcaaa4d 100644 --- a/node-graph/libraries/core-types/src/uuid.rs +++ b/node-graph/libraries/core-types/src/uuid.rs @@ -45,27 +45,19 @@ mod u64_string { mod uuid_generation { use rand_chacha::ChaCha20Rng; use rand_chacha::rand_core::{RngCore, SeedableRng}; - use std::cell::Cell; use std::sync::Mutex; static RNG: Mutex> = Mutex::new(None); - thread_local! { - pub static UUID_SEED: Cell> = const { Cell::new(None) }; - } - pub fn set_uuid_seed(random_seed: u64) { - UUID_SEED.with(|seed| seed.set(Some(random_seed))) + pub fn init_rng(random_seed: u64) { + let mut lock = RNG.lock().expect("UUID mutex poisoned"); + *lock = Some(ChaCha20Rng::seed_from_u64(random_seed)); } pub fn generate_uuid() -> u64 { let Ok(mut lock) = RNG.lock() else { panic!("UUID mutex poisoned") }; - if lock.is_none() { - UUID_SEED.with(|seed| { - let random_seed = seed.get().unwrap_or(42); - *lock = Some(ChaCha20Rng::seed_from_u64(random_seed)); - }) - } - lock.as_mut().map(ChaCha20Rng::next_u64).expect("UUID mutex poisoned") + let rng = lock.as_mut().expect("UUID generator not initialized. Forgot to call `init_rng`?"); + rng.next_u64() } } From 42e95605860a5240d8f2c22fdc65d919718be392 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Mon, 19 Jan 2026 15:59:38 +0000 Subject: [PATCH 25/28] Code review --- editor/src/application.rs | 28 +++----- editor/src/dispatcher.rs | 8 +-- .../messages/app_window/app_window_message.rs | 1 - .../app_window/app_window_message_handler.rs | 19 ++--- .../input_mapper_message_handler.rs | 6 +- .../utility_types/input_keyboard.rs | 33 +++++---- .../input_preprocessor_message_handler.rs | 40 +++++------ editor/src/messages/message.rs | 1 - .../portfolio/portfolio_message_handler.rs | 14 ++-- .../src/messages/portfolio/utility_types.rs | 19 ----- editor/src/test_utils.rs | 2 +- frontend/src/editor.ts | 22 +++--- frontend/src/io-managers/panic.ts | 2 +- frontend/src/utility-functions/platform.ts | 5 +- frontend/wasm/src/editor_api.rs | 71 +++++++++---------- 15 files changed, 111 insertions(+), 160 deletions(-) diff --git a/editor/src/application.rs b/editor/src/application.rs index 59a21dc72b..bebf5b6bd2 100644 --- a/editor/src/application.rs +++ b/editor/src/application.rs @@ -1,17 +1,15 @@ -use std::sync::OnceLock; - use crate::dispatcher::Dispatcher; use crate::messages::prelude::*; pub use graphene_std::uuid::*; +use std::sync::OnceLock; -// TODO: serialize with serde to save the current editor state pub struct Editor { pub dispatcher: Dispatcher, } impl Editor { - pub fn init(enviroment: Environment, uuid_random_seed: u64) { - ENVIROMENT.set(enviroment).expect("Editor shoud only be initialized once"); + pub fn init(environment: Environment, uuid_random_seed: u64) { + ENVIRONMENT.set(environment).expect("Editor shoud only be initialized once"); graphene_std::uuid::init_rng(uuid_random_seed); } @@ -43,10 +41,10 @@ impl Default for Editor { } } -static ENVIROMENT: OnceLock = OnceLock::new(); +static ENVIRONMENT: OnceLock = OnceLock::new(); impl Editor { pub fn environment() -> &'static Environment { - ENVIROMENT.get().expect("Editor environment accessed before initialization") + ENVIRONMENT.get().expect("Editor environment accessed before initialization") } } @@ -62,10 +60,9 @@ pub enum Platform { } #[derive(Clone, Copy, Debug)] pub enum Host { - Linux, - Mac, Windows, - Unknown, + Mac, + Linux, } impl Environment { pub fn is_desktop(&self) -> bool { @@ -74,17 +71,14 @@ impl Environment { pub fn is_web(&self) -> bool { matches!(self.platform, Platform::Web) } - pub fn is_linux(&self) -> bool { - matches!(self.host, Host::Linux) + pub fn is_windows(&self) -> bool { + matches!(self.host, Host::Windows) } pub fn is_mac(&self) -> bool { matches!(self.host, Host::Mac) } - pub fn is_windows(&self) -> bool { - matches!(self.host, Host::Windows) - } - pub fn is_unknown_host(&self) -> bool { - matches!(self.host, Host::Unknown) + pub fn is_linux(&self) -> bool { + matches!(self.host, Host::Linux) } } diff --git a/editor/src/dispatcher.rs b/editor/src/dispatcher.rs index 8ea9b011d7..296084c835 100644 --- a/editor/src/dispatcher.rs +++ b/editor/src/dispatcher.rs @@ -1,4 +1,3 @@ -use crate::application::Editor; use crate::messages::debug::utility_types::MessageLoggingVerbosity; use crate::messages::defer::DeferMessageContext; use crate::messages::dialog::DialogMessageContext; @@ -28,7 +27,7 @@ pub struct DispatcherMessageHandlers { key_mapping_message_handler: KeyMappingMessageHandler, layout_message_handler: LayoutMessageHandler, menu_bar_message_handler: MenuBarMessageHandler, - pub portfolio_message_handler: PortfolioMessageHandler, + pub(crate) portfolio_message_handler: PortfolioMessageHandler, preferences_message_handler: PreferencesMessageHandler, tool_message_handler: ToolMessageHandler, viewport_message_handler: ViewportMessageHandler, @@ -197,7 +196,6 @@ impl Dispatcher { message, &mut queue, InputPreprocessorMessageContext { - keyboard_platform: Editor::environment().host.into(), viewport: &self.message_handlers.viewport_message_handler, }, ); @@ -304,10 +302,6 @@ impl Dispatcher { Message::Viewport(message) => { self.message_handlers.viewport_message_handler.process_message(message, &mut queue, ()); } - Message::Init => { - self.handle_message(AppWindowMessage::Init, false); - self.handle_message(PortfolioMessage::Init, false); - } Message::NoOp => {} Message::Batched { messages } => { messages.into_iter().for_each(|message| self.handle_message(message, false)); diff --git a/editor/src/messages/app_window/app_window_message.rs b/editor/src/messages/app_window/app_window_message.rs index 3a11c8a6d5..c02b99b5fd 100644 --- a/editor/src/messages/app_window/app_window_message.rs +++ b/editor/src/messages/app_window/app_window_message.rs @@ -3,7 +3,6 @@ use crate::messages::prelude::*; #[impl_message(Message, AppWindow)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)] pub enum AppWindowMessage { - Init, PointerLock, PointerLockMove { x: f64, y: f64 }, Close, diff --git a/editor/src/messages/app_window/app_window_message_handler.rs b/editor/src/messages/app_window/app_window_message_handler.rs index b0090e8401..21b6e9b5e9 100644 --- a/editor/src/messages/app_window/app_window_message_handler.rs +++ b/editor/src/messages/app_window/app_window_message_handler.rs @@ -1,4 +1,4 @@ -use crate::application::{Editor, Environment}; +use crate::application::{Environment, Platform}; use crate::messages::prelude::*; use crate::{application::Host, messages::app_window::AppWindowMessage}; use graphite_proc_macros::{ExtractField, message_handler_data}; @@ -10,11 +10,6 @@ pub struct AppWindowMessageHandler {} impl MessageHandler for AppWindowMessageHandler { fn process_message(&mut self, message: AppWindowMessage, responses: &mut std::collections::VecDeque, _: ()) { match message { - AppWindowMessage::Init => { - responses.add(FrontendMessage::UpdatePlatform { - platform: Editor::environment().into(), - }); - } AppWindowMessage::PointerLock => { responses.add(FrontendMessage::WindowPointerLock); } @@ -69,13 +64,11 @@ pub enum AppWindowPlatform { impl From<&Environment> for AppWindowPlatform { fn from(environment: &Environment) -> Self { - if environment.is_web() { - return AppWindowPlatform::Web; - } - match environment.host { - Host::Linux => AppWindowPlatform::Linux, - Host::Mac => AppWindowPlatform::Mac, - Host::Windows | Host::Unknown => AppWindowPlatform::Windows, + match (environment.platform, environment.host) { + (Platform::Web, _) => AppWindowPlatform::Web, + (Platform::Desktop, Host::Linux) => AppWindowPlatform::Linux, + (Platform::Desktop, Host::Mac) => AppWindowPlatform::Mac, + (Platform::Desktop, Host::Windows) => AppWindowPlatform::Windows, } } } diff --git a/editor/src/messages/input_mapper/input_mapper_message_handler.rs b/editor/src/messages/input_mapper/input_mapper_message_handler.rs index d616ac4050..d38845c6d9 100644 --- a/editor/src/messages/input_mapper/input_mapper_message_handler.rs +++ b/editor/src/messages/input_mapper/input_mapper_message_handler.rs @@ -3,7 +3,6 @@ use super::utility_types::misc::Mapping; use crate::application::Editor; use crate::messages::input_mapper::utility_types::input_keyboard::{self, Key}; use crate::messages::input_mapper::utility_types::misc::MappingEntry; -use crate::messages::portfolio::utility_types::KeyboardPlatformLayout; use crate::messages::prelude::*; #[derive(ExtractField)] @@ -49,10 +48,7 @@ impl InputMapperMessageHandler { let found_actions = all_mapping_entries.filter(|entry| entry.action.to_discriminant() == *action_to_find); // Get the `Key` for this platform's accelerator key - let platform_accel_key = match Editor::environment().host.into() { - KeyboardPlatformLayout::Standard => Key::Control, - KeyboardPlatformLayout::Mac => Key::Command, - }; + let platform_accel_key = if Editor::environment().is_mac() { Key::Command } else { Key::Control }; let entry_to_key = |entry: &MappingEntry| { // Get the modifier keys for the entry (and convert them to Key) diff --git a/editor/src/messages/input_mapper/utility_types/input_keyboard.rs b/editor/src/messages/input_mapper/utility_types/input_keyboard.rs index dfc4d7cbcd..e8c9bc533f 100644 --- a/editor/src/messages/input_mapper/utility_types/input_keyboard.rs +++ b/editor/src/messages/input_mapper/utility_types/input_keyboard.rs @@ -1,5 +1,4 @@ use crate::application::Editor; -use crate::messages::portfolio::utility_types::KeyboardPlatformLayout; use crate::messages::prelude::*; use bitflags::bitflags; use std::fmt::{self, Display, Formatter}; @@ -259,7 +258,7 @@ impl fmt::Display for Key { return write!(f, "{}", key_name.chars().skip(KEY_PREFIX.len()).collect::()); } - let keyboard_layout = || Editor::environment().host.into(); + let is_mac = Editor::environment().is_mac(); let name = match self { // Writing system keys @@ -276,21 +275,21 @@ impl fmt::Display for Key { Self::Slash => "/", // Functional keys - Self::Alt => match keyboard_layout() { - KeyboardPlatformLayout::Standard => "Alt", - KeyboardPlatformLayout::Mac => "⌥", + Self::Alt => match is_mac { + true => "⌥", + false => "Alt", }, - Self::Meta => match keyboard_layout() { - KeyboardPlatformLayout::Standard => "⊞", - KeyboardPlatformLayout::Mac => "⌘", + Self::Meta => match is_mac { + true => "⌘", + false => "⊞", }, - Self::Shift => match keyboard_layout() { - KeyboardPlatformLayout::Standard => "Shift", - KeyboardPlatformLayout::Mac => "⇧", + Self::Shift => match is_mac { + true => "⇧", + false => "Shift", }, - Self::Control => match keyboard_layout() { - KeyboardPlatformLayout::Standard => "Ctrl", - KeyboardPlatformLayout::Mac => "⌃", + Self::Control => match is_mac { + true => "⌃", + false => "Ctrl", }, Self::Backspace => "⌫", @@ -318,9 +317,9 @@ impl fmt::Display for Key { // Other keys that aren't part of the W3C spec Self::Command => "⌘", - Self::Accel => match keyboard_layout() { - KeyboardPlatformLayout::Standard => "Ctrl", - KeyboardPlatformLayout::Mac => "⌘", + Self::Accel => match is_mac { + true => "⌘", + false => "Ctrl", }, Self::MouseLeft => "Click", Self::MouseRight => "R.Click", diff --git a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs index 3464f0a07e..0271fb860d 100644 --- a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs +++ b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs @@ -1,13 +1,12 @@ +use crate::application::Editor; use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeyStates, ModifierKeys}; use crate::messages::input_mapper::utility_types::input_mouse::{MouseButton, MouseKeys, MouseState}; use crate::messages::input_mapper::utility_types::misc::FrameTimeInfo; -use crate::messages::portfolio::utility_types::KeyboardPlatformLayout; use crate::messages::prelude::*; use std::time::Duration; #[derive(ExtractField)] pub struct InputPreprocessorMessageContext<'a> { - pub keyboard_platform: KeyboardPlatformLayout, pub viewport: &'a ViewportMessageHandler, } @@ -22,11 +21,11 @@ pub struct InputPreprocessorMessageHandler { #[message_handler_data] impl<'a> MessageHandler> for InputPreprocessorMessageHandler { fn process_message(&mut self, message: InputPreprocessorMessage, responses: &mut VecDeque, context: InputPreprocessorMessageContext<'a>) { - let InputPreprocessorMessageContext { keyboard_platform, viewport } = context; + let InputPreprocessorMessageContext { viewport } = context; match message { InputPreprocessorMessage::DoubleClick { editor_mouse_state, modifier_keys } => { - self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses); + self.update_states_of_modifier_keys(modifier_keys, responses); let mouse_state = editor_mouse_state.to_mouse_state(viewport); self.mouse.position = mouse_state.position; @@ -43,7 +42,7 @@ impl<'a> MessageHandler { - self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses); + self.update_states_of_modifier_keys(modifier_keys, responses); self.keyboard.set(key as usize); if !key_repeat { responses.add(InputMapperMessage::KeyDownNoRepeat(key)); @@ -51,7 +50,7 @@ impl<'a> MessageHandler { - self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses); + self.update_states_of_modifier_keys(modifier_keys, responses); self.keyboard.unset(key as usize); if !key_repeat { responses.add(InputMapperMessage::KeyUpNoRepeat(key)); @@ -59,7 +58,7 @@ impl<'a> MessageHandler { - self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses); + self.update_states_of_modifier_keys(modifier_keys, responses); let mouse_state = editor_mouse_state.to_mouse_state(viewport); self.mouse.position = mouse_state.position; @@ -67,7 +66,7 @@ impl<'a> MessageHandler { - self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses); + self.update_states_of_modifier_keys(modifier_keys, responses); let mouse_state = editor_mouse_state.to_mouse_state(viewport); self.mouse.position = mouse_state.position; @@ -78,7 +77,7 @@ impl<'a> MessageHandler { - self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses); + self.update_states_of_modifier_keys(modifier_keys, responses); let mouse_state = editor_mouse_state.to_mouse_state(viewport); self.mouse.position = mouse_state.position; @@ -86,7 +85,7 @@ impl<'a> MessageHandler { - self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses); + self.update_states_of_modifier_keys(modifier_keys, responses); let mouse_state = editor_mouse_state.to_mouse_state(viewport); self.mouse.position = mouse_state.position; @@ -99,7 +98,7 @@ impl<'a> MessageHandler { - self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses); + self.update_states_of_modifier_keys(modifier_keys, responses); let mouse_state = editor_mouse_state.to_mouse_state(viewport); self.mouse.position = mouse_state.position; @@ -148,7 +147,7 @@ impl InputPreprocessorMessageHandler { self.mouse = new_state; } - fn update_states_of_modifier_keys(&mut self, pressed_modifier_keys: ModifierKeys, keyboard_platform: KeyboardPlatformLayout, responses: &mut VecDeque) { + fn update_states_of_modifier_keys(&mut self, pressed_modifier_keys: ModifierKeys, responses: &mut VecDeque) { let is_key_pressed = |key_to_check: ModifierKeys| pressed_modifier_keys.contains(key_to_check); // Update the state of the concrete modifier keys based on the source state @@ -157,16 +156,16 @@ impl InputPreprocessorMessageHandler { self.update_modifier_key(Key::Control, is_key_pressed(ModifierKeys::CONTROL), responses); // Update the state of either the concrete Meta or the Command keys based on which one is applicable for this platform - let meta_or_command = match keyboard_platform { - KeyboardPlatformLayout::Mac => Key::Command, - KeyboardPlatformLayout::Standard => Key::Meta, + let meta_or_command = match Editor::environment().is_mac() { + true => Key::Command, + false => Key::Meta, }; self.update_modifier_key(meta_or_command, is_key_pressed(ModifierKeys::META_OR_COMMAND), responses); // Update the state of the virtual Accel key (the primary accelerator key) based on the source state of the Control or Command key, whichever is relevant on this platform - let accel_virtual_key_state = match keyboard_platform { - KeyboardPlatformLayout::Mac => is_key_pressed(ModifierKeys::META_OR_COMMAND), - KeyboardPlatformLayout::Standard => is_key_pressed(ModifierKeys::CONTROL), + let accel_virtual_key_state = match Editor::environment().is_mac() { + true => is_key_pressed(ModifierKeys::META_OR_COMMAND), + false => is_key_pressed(ModifierKeys::CONTROL), }; self.update_modifier_key(Key::Accel, accel_virtual_key_state, responses); } @@ -206,7 +205,6 @@ mod test { let mut responses = VecDeque::new(); let context = InputPreprocessorMessageContext { - keyboard_platform: KeyboardPlatformLayout::Standard, viewport: &ViewportMessageHandler::default(), }; input_preprocessor.process_message(message, &mut responses, context); @@ -226,7 +224,6 @@ mod test { let mut responses = VecDeque::new(); let context = InputPreprocessorMessageContext { - keyboard_platform: KeyboardPlatformLayout::Standard, viewport: &ViewportMessageHandler::default(), }; input_preprocessor.process_message(message, &mut responses, context); @@ -246,7 +243,6 @@ mod test { let mut responses = VecDeque::new(); let context = InputPreprocessorMessageContext { - keyboard_platform: KeyboardPlatformLayout::Standard, viewport: &ViewportMessageHandler::default(), }; input_preprocessor.process_message(message, &mut responses, context); @@ -268,7 +264,6 @@ mod test { let mut responses = VecDeque::new(); let context = InputPreprocessorMessageContext { - keyboard_platform: KeyboardPlatformLayout::Standard, viewport: &ViewportMessageHandler::default(), }; input_preprocessor.process_message(message, &mut responses, context); @@ -289,7 +284,6 @@ mod test { let mut responses = VecDeque::new(); let context = InputPreprocessorMessageContext { - keyboard_platform: KeyboardPlatformLayout::Standard, viewport: &ViewportMessageHandler::default(), }; input_preprocessor.process_message(message, &mut responses, context); diff --git a/editor/src/messages/message.rs b/editor/src/messages/message.rs index 944e13dc56..5d4dd8bfea 100644 --- a/editor/src/messages/message.rs +++ b/editor/src/messages/message.rs @@ -39,7 +39,6 @@ pub enum Message { Viewport(ViewportMessage), // Messages - Init, Batched { messages: Box<[Message]>, }, diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 83e4e35af1..6e8ba98842 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -1,7 +1,7 @@ use super::document::utility_types::document_metadata::LayerNodeIdentifier; use super::document::utility_types::network_interface; use super::utility_types::{PanelType, PersistentData}; -use crate::application::generate_uuid; +use crate::application::{Editor, generate_uuid}; use crate::consts::{DEFAULT_DOCUMENT_NAME, DEFAULT_STROKE_WIDTH, FILE_EXTENSION}; use crate::messages::animation::TimingInformation; use crate::messages::clipboard::utility_types::ClipboardContent; @@ -101,9 +101,17 @@ impl MessageHandler> for Portfolio // Messages PortfolioMessage::Init => { + // Initialize the frontend with environment information + responses.add(FrontendMessage::UpdatePlatform { + platform: Editor::environment().into(), + }); + // Tell frontend to load persistent preferences responses.add(FrontendMessage::TriggerLoadPreferences); + // Before loading any documents, initially prepare the welcome screen buttons layout + responses.add(PortfolioMessage::RequestWelcomeScreenButtonsLayout); + // Tell frontend to load the current document responses.add(FrontendMessage::TriggerLoadFirstAutoSaveDocument); @@ -128,15 +136,13 @@ impl MessageHandler> for Portfolio shortcut: action_shortcut_manual!(Key::Shift, Key::MouseLeft), }); - // Before loading any documents, initially prepare the welcome screen buttons layout - responses.add(PortfolioMessage::RequestWelcomeScreenButtonsLayout); - // Request status bar info layout responses.add(PortfolioMessage::RequestStatusBarInfoLayout); // Tell frontend to finish loading persistent documents responses.add(FrontendMessage::TriggerLoadRestAutoSaveDocuments); + // Tell frontend to load documented passed in as launch arguments responses.add(FrontendMessage::TriggerOpenLaunchDocuments); } PortfolioMessage::DocumentPassMessage { document_id, message } => { diff --git a/editor/src/messages/portfolio/utility_types.rs b/editor/src/messages/portfolio/utility_types.rs index ba2cb9e330..a43fced4c1 100644 --- a/editor/src/messages/portfolio/utility_types.rs +++ b/editor/src/messages/portfolio/utility_types.rs @@ -1,7 +1,5 @@ use graphene_std::text::{Font, FontCache}; -use crate::application::Host; - #[derive(Debug, Default)] pub struct PersistentData { pub font_cache: FontCache, @@ -84,23 +82,6 @@ impl FontCatalogStyle { } } -#[derive(PartialEq, Eq, Clone, Copy, Default, Debug, serde::Serialize, serde::Deserialize)] -pub enum KeyboardPlatformLayout { - /// Standard keyboard mapping used by Windows and Linux - #[default] - Standard, - /// Keyboard mapping used by Macs where Command is sometimes used in favor of Control - Mac, -} -impl From for KeyboardPlatformLayout { - fn from(host: Host) -> Self { - match host { - Host::Mac => KeyboardPlatformLayout::Mac, - Host::Windows | Host::Linux | Host::Unknown => KeyboardPlatformLayout::Standard, - } - } -} - #[derive(PartialEq, Eq, Clone, Copy, Debug, Default, serde::Serialize, serde::Deserialize)] pub enum PanelType { #[default] diff --git a/editor/src/test_utils.rs b/editor/src/test_utils.rs index b6c4c34583..63291d1f1c 100644 --- a/editor/src/test_utils.rs +++ b/editor/src/test_utils.rs @@ -33,7 +33,7 @@ impl EditorTestUtils { // It isn't sufficient to guard the message dispatch here with a check if the once_cell is empty, because that isn't atomic and the time between checking and handling the dispatch can let multiple through. let _ = GLOBAL_PLATFORM.set(Platform::Windows).is_ok(); - editor.handle_message(Message::Init); + editor.handle_message(PortfolioMessage::Init); Self { editor, runtime } } diff --git a/frontend/src/editor.ts b/frontend/src/editor.ts index e33198cb9c..f49cc2a169 100644 --- a/frontend/src/editor.ts +++ b/frontend/src/editor.ts @@ -1,10 +1,12 @@ // import { panicProxy } from "@graphite/utility-functions/panic-proxy"; -import init, { wasmMemory, EditorHandle, receiveNativeMessage, editorInit } from "@graphite/../wasm/pkg/graphite_wasm"; +import type { EditorHandle } from "@graphite/../wasm/pkg/graphite_wasm"; +import init, { wasmMemory, receiveNativeMessage, constructEditor } from "@graphite/../wasm/pkg/graphite_wasm"; import { type JsMessageType } from "@graphite/messages"; import { createSubscriptionRouter, type SubscriptionRouter } from "@graphite/subscription-router"; import { operatingSystem } from "@graphite/utility-functions/platform"; +// TODO: Remove `raw`, split out `subscriptions`, and unwrap the remaining `handle` so `EditorHandle` can replace `Editor` and then it can also be renamed to `Editor` to fully remove `EditorHandle`. export type Editor = { raw: WebAssembly.Memory; handle: EditorHandle; @@ -16,10 +18,10 @@ let wasmImport: WebAssembly.Memory | undefined; // Should be called asynchronously before `createEditor()`. export async function initWasm() { - // Skip if the WASM module is already initialized + // Skip if the Wasm module is already initialized if (wasmImport !== undefined) return; - // Import the WASM module JS bindings and wrap them in the panic proxy + // Import the Wasm module JS bindings and wrap them in the panic proxy const wasm = await init(); for (const [name, f] of Object.entries(wasm)) { if (name.startsWith("__node_registry")) f(); @@ -34,24 +36,22 @@ export async function initWasm() { // Should be called after running `initWasm()` and its promise resolving. export function createEditor(): Editor { - // Raw: object containing several callable functions from `editor_api.rs` defined directly on the WASM module, not the `EditorHandle` struct (generated by wasm-bindgen) - if (!wasmImport) throw new Error("Editor WASM backend was not initialized at application startup"); + // Raw: object containing several callable functions from `editor_api.rs` defined directly on the Wasm module, not the `EditorHandle` struct (generated by wasm-bindgen) + if (!wasmImport) throw new Error("Editor Wasm backend was not initialized at application startup"); const raw: WebAssembly.Memory = wasmImport; - // Provide a random starter seed which must occur after initializing the WASM module, since WASM can't generate its own random numbers + // Provide a random starter seed which must occur after initializing the Wasm module, since Wasm can't generate its own random numbers const randomSeedFloat = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); const randomSeed = BigInt(randomSeedFloat); - editorInit(operatingSystem(), randomSeed); - // Handle: object containing many functions from `editor_api.rs` that are part of the `EditorHandle` struct (generated by wasm-bindgen) - const handle: EditorHandle = new EditorHandle((messageType: JsMessageType, messageData: Record) => { - // This callback is called by WASM when a FrontendMessage is received from the WASM wrapper `EditorHandle` + const handle: EditorHandle = constructEditor(operatingSystem(), randomSeed, (messageType: JsMessageType, messageData: Record) => { + // This callback is called by Wasm when a FrontendMessage is received from the Wasm wrapper `EditorHandle` // We pass along the first two arguments then add our own `raw` and `handle` context for the last two arguments subscriptions.handleJsMessage(messageType, messageData, raw, handle); }); - // Subscriptions: allows subscribing to messages in JS that are sent from the WASM backend + // Subscriptions: allows subscribing to messages in JS that are sent from the Wasm backend const subscriptions: SubscriptionRouter = createSubscriptionRouter(); // Check if the URL hash fragment has any demo artwork to be loaded diff --git a/frontend/src/io-managers/panic.ts b/frontend/src/io-managers/panic.ts index 3a6343289e..312af59fc9 100644 --- a/frontend/src/io-managers/panic.ts +++ b/frontend/src/io-managers/panic.ts @@ -40,7 +40,7 @@ export function githubUrl(panicDetails: string): string { Provide any further information or context that you think would be helpful in fixing the issue. Screenshots or video can be linked or attached to this issue. **Browser and OS** - ${browserVersion()}, ${operatingSystem().replace("Unknown", "YOUR OPERATING SYSTEM")} + ${browserVersion()}, ${operatingSystem()} **Stack Trace** Copied from the crash dialog in the Graphite editor: diff --git a/frontend/src/utility-functions/platform.ts b/frontend/src/utility-functions/platform.ts index 6a7b7365c1..e50e027068 100644 --- a/frontend/src/utility-functions/platform.ts +++ b/frontend/src/utility-functions/platform.ts @@ -25,18 +25,17 @@ export function browserVersion(): string { return `${match[0]} ${match[1]}`; } -export type OperatingSystem = "Windows" | "Mac" | "Linux" | "Unknown"; +export type OperatingSystem = "Windows" | "Mac" | "Linux"; export function operatingSystem(): OperatingSystem { const osTable: Record = { Windows: "Windows", Mac: "Mac", Linux: "Linux", - Unknown: "Unknown", }; const userAgentOS = Object.keys(osTable).find((key) => window.navigator.userAgent.includes(key)); - return osTable[userAgentOS || "Unknown"]; + return osTable[userAgentOS || "Windows"]; } export function isDesktop(): boolean { diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index df23da1951..95ef79a38d 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -43,7 +43,7 @@ fn calculate_hash(t: &T) -> u64 { hasher.finish() } -/// Provides a handle to access the raw WASM memory. +/// Provides a handle to access the raw Wasm memory. #[wasm_bindgen(js_name = wasmMemory)] pub fn wasm_memory() -> JsValue { wasm_bindgen::memory() @@ -80,47 +80,44 @@ impl EditorHandle { } #[cfg(not(feature = "native"))] -#[wasm_bindgen(js_name = editorInit)] -pub fn editor_init(platform: String, uuid_random_seed: u64) { - let host = match platform.as_str() { - "Linux" => Host::Linux, - "Mac" => Host::Mac, - "Windows" => Host::Windows, - _ => Host::Unknown, - }; - Editor::init(Environment { platform: Platform::Web, host }, uuid_random_seed); +#[wasm_bindgen(js_name = constructEditor)] +pub fn construct_editor(platform: String, uuid_random_seed: u64, frontend_message_handler_callback: js_sys::Function) -> EditorHandle { + Editor::init( + Environment { + platform: Platform::Web, + host: match platform.as_str() { + "Linux" => Host::Linux, + "Mac" => Host::Mac, + "Windows" => Host::Windows, + _ => unreachable!(), + }, + }, + uuid_random_seed, + ); + + let editor = Editor::new(); + let editor_handle = EditorHandle { frontend_message_handler_callback }; + if EDITOR.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor))).is_none() { + log::error!("Attempted to initialize the editor more than once"); + } + if EDITOR_HANDLE.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor_handle.clone()))).is_none() { + log::error!("Attempted to initialize the editor handle more than once"); + } + editor_handle } #[cfg(feature = "native")] -#[wasm_bindgen(js_name = editorInit)] -pub fn editor_init(_platform: String, _uuid_random_seed: u64) {} +#[wasm_bindgen(js_name = constructEditor)] +pub fn construct_editor(_platform: String, _uuid_random_seed: u64, frontend_message_handler_callback: js_sys::Function) -> EditorHandle { + let editor_handle = EditorHandle { frontend_message_handler_callback }; + if EDITOR_HANDLE.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor_handle.clone()))).is_none() { + log::error!("Attempted to initialize the editor handle more than once"); + } + editor_handle +} #[wasm_bindgen] impl EditorHandle { - #[cfg(not(feature = "native"))] - #[wasm_bindgen(constructor)] - pub fn new(frontend_message_handler_callback: js_sys::Function) -> Self { - let editor = Editor::new(); - let editor_handle = EditorHandle { frontend_message_handler_callback }; - if EDITOR.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor))).is_none() { - log::error!("Attempted to initialize the editor more than once"); - } - if EDITOR_HANDLE.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor_handle.clone()))).is_none() { - log::error!("Attempted to initialize the editor handle more than once"); - } - editor_handle - } - - #[cfg(feature = "native")] - #[wasm_bindgen(constructor)] - pub fn new(frontend_message_handler_callback: js_sys::Function) -> Self { - let editor_handle = EditorHandle { frontend_message_handler_callback }; - if EDITOR_HANDLE.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor_handle.clone()))).is_none() { - log::error!("Attempted to initialize the editor handle more than once"); - } - editor_handle - } - // Sends a message to the dispatcher in the Editor Backend #[cfg(not(feature = "native"))] fn dispatch>(&self, message: T) { @@ -197,7 +194,7 @@ impl EditorHandle { #[cfg(feature = "native")] crate::native_communcation::initialize_native_communication(); - self.dispatch(Message::Init); + self.dispatch(PortfolioMessage::Init); // Poll node graph evaluation on `requestAnimationFrame` { From 8eecbea02fc2e54624f91c36e61dbb6f154fc4ee Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Mon, 19 Jan 2026 15:59:38 +0000 Subject: [PATCH 26/28] Code review, refactoring, and test fixes --- desktop/src/app.rs | 9 +-- desktop/src/lib.rs | 8 +-- desktop/wrapper/src/lib.rs | 21 +++---- editor/src/application.rs | 31 +++++---- .../input_preprocessor_message_handler.rs | 1 - editor/src/test_utils.rs | 8 +-- frontend/src/editor.ts | 8 +-- frontend/wasm/src/editor_api.rs | 63 +++++++++---------- node-graph/libraries/core-types/src/uuid.rs | 18 ++++-- 9 files changed, 83 insertions(+), 84 deletions(-) diff --git a/desktop/src/app.rs b/desktop/src/app.rs index f06baa89f3..448a820444 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -47,12 +47,11 @@ pub(crate) struct App { } impl App { - pub(crate) fn init(wgpu_context: &WgpuContext) { - DesktopWrapper::init(wgpu_context, rand::rng().random()); + pub(crate) fn init() { Window::init(); } - pub(crate) fn new( + pub(crate) fn create( cef_context: Box, cef_view_info_sender: Sender, wgpu_context: WgpuContext, @@ -80,6 +79,8 @@ impl App { let mut persistent_data = PersistentData::default(); persistent_data.load_from_disk(); + let desktop_wrapper = DesktopWrapper::create(&wgpu_context.clone(), rand::rng().random()); + Self { render_state: None, wgpu_context, @@ -93,7 +94,7 @@ impl App { ui_scale: 1., app_event_receiver, app_event_scheduler, - desktop_wrapper: DesktopWrapper::new(), + desktop_wrapper, last_ui_update: Instant::now(), cef_context, cef_schedule: Some(Instant::now()), diff --git a/desktop/src/lib.rs b/desktop/src/lib.rs index eb7d6d5bdb..1f9df7168c 100644 --- a/desktop/src/lib.rs +++ b/desktop/src/lib.rs @@ -66,9 +66,8 @@ pub fn start() { } }; - let wgpu_context = futures::executor::block_on(gpu_context::create_wgpu_context()); - - App::init(&wgpu_context); + // Must be called before event loop initialization or native window integrations will break + App::init(); let event_loop = EventLoop::new().unwrap(); let (app_event_sender, app_event_receiver) = std::sync::mpsc::channel(); @@ -76,6 +75,7 @@ pub fn start() { let (cef_view_info_sender, cef_view_info_receiver) = std::sync::mpsc::channel(); + let wgpu_context = futures::executor::block_on(gpu_context::create_wgpu_context()); let cef_handler = cef::CefHandler::new(wgpu_context.clone(), app_event_scheduler.clone(), cef_view_info_receiver); let cef_context = match cef_context_builder.initialize(cef_handler, cli.disable_ui_acceleration) { Ok(context) => { @@ -100,7 +100,7 @@ pub fn start() { } }; - let mut app = App::new(Box::new(cef_context), cef_view_info_sender, wgpu_context, app_event_receiver, app_event_scheduler, cli.files); + let mut app = App::create(Box::new(cef_context), cef_view_info_sender, wgpu_context, app_event_receiver, app_event_scheduler, cli.files); event_loop.run_app(&mut app).unwrap(); diff --git a/desktop/wrapper/src/lib.rs b/desktop/wrapper/src/lib.rs index d2960d476f..f5f0955279 100644 --- a/desktop/wrapper/src/lib.rs +++ b/desktop/wrapper/src/lib.rs @@ -27,7 +27,10 @@ pub struct DesktopWrapper { } impl DesktopWrapper { - pub fn init(wgpu_context: &WgpuContext, uuid_random_seed: u64) { + pub fn create(wgpu_context: &WgpuContext, uuid_random_seed: u64) -> Self { + let application_io = WasmApplicationIo::new_with_context(wgpu_context.clone()); + futures::executor::block_on(graphite_editor::node_graph_executor::replace_application_io(application_io)); + #[cfg(target_os = "windows")] let host = Host::Windows; #[cfg(target_os = "macos")] @@ -35,14 +38,10 @@ impl DesktopWrapper { #[cfg(target_os = "linux")] let host = Host::Linux; let env = Environment { platform: Platform::Desktop, host }; - Editor::init(env, uuid_random_seed); - let application_io = WasmApplicationIo::new_with_context(wgpu_context.clone()); - futures::executor::block_on(graphite_editor::node_graph_executor::replace_application_io(application_io)); - } - - pub fn new() -> Self { - Self { editor: Editor::new() } + Self { + editor: Editor::create(env, uuid_random_seed), + } } pub fn dispatch(&mut self, message: DesktopWrapperMessage) -> Vec { @@ -60,12 +59,6 @@ impl DesktopWrapper { } } -impl Default for DesktopWrapper { - fn default() -> Self { - Self::new() - } -} - pub enum NodeGraphExecutionResult { HasRun(Option), NotRun, diff --git a/editor/src/application.rs b/editor/src/application.rs index bebf5b6bd2..cb0547d360 100644 --- a/editor/src/application.rs +++ b/editor/src/application.rs @@ -8,21 +8,24 @@ pub struct Editor { } impl Editor { - pub fn init(environment: Environment, uuid_random_seed: u64) { + pub fn create(environment: Environment, uuid_random_seed: u64) -> Self { ENVIRONMENT.set(environment).expect("Editor shoud only be initialized once"); - graphene_std::uuid::init_rng(uuid_random_seed); - } + graphene_std::uuid::set_uuid_seed(uuid_random_seed); - /// `Editor::init` must be called before this function - pub fn new() -> Self { Self { dispatcher: Dispatcher::new() } } #[cfg(test)] pub(crate) fn new_local_executor() -> (Self, crate::node_graph_executor::NodeRuntime) { + let _ = ENVIRONMENT.set(*Editor::environment()); + graphene_std::uuid::set_uuid_seed(0); + let (runtime, executor) = crate::node_graph_executor::NodeGraphExecutor::new_with_local_runtime(); - let dispatcher = Dispatcher::with_executor(executor); - (Self { dispatcher }, runtime) + let editor = Self { + dispatcher: Dispatcher::with_executor(executor), + }; + + (editor, runtime) } pub fn handle_message>(&mut self, message: T) -> Vec { @@ -35,17 +38,21 @@ impl Editor { self.dispatcher.poll_node_graph_evaluation(responses) } } -impl Default for Editor { - fn default() -> Self { - Self::new() - } -} static ENVIRONMENT: OnceLock = OnceLock::new(); impl Editor { + #[cfg(not(test))] pub fn environment() -> &'static Environment { ENVIRONMENT.get().expect("Editor environment accessed before initialization") } + + #[cfg(test)] + pub fn environment() -> &'static Environment { + &Environment { + platform: Platform::Desktop, + host: Host::Linux, + } + } } #[derive(Clone, Copy, Debug)] diff --git a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs index 0271fb860d..e9f5847b7b 100644 --- a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs +++ b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs @@ -187,7 +187,6 @@ impl InputPreprocessorMessageHandler { mod test { use crate::messages::input_mapper::utility_types::input_keyboard::{Key, ModifierKeys}; use crate::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, MouseKeys, ScrollDelta}; - use crate::messages::portfolio::utility_types::KeyboardPlatformLayout; use crate::messages::prelude::*; #[test] diff --git a/editor/src/test_utils.rs b/editor/src/test_utils.rs index 63291d1f1c..8c963e0b57 100644 --- a/editor/src/test_utils.rs +++ b/editor/src/test_utils.rs @@ -1,9 +1,7 @@ use crate::application::Editor; -use crate::application::set_uuid_seed; use crate::messages::input_mapper::utility_types::input_keyboard::ModifierKeys; use crate::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, MouseKeys, ScrollDelta, ViewportPosition}; use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier; -use crate::messages::portfolio::utility_types::Platform; use crate::messages::prelude::*; use crate::messages::tool::tool_messages::tool_prelude::Key; use crate::messages::tool::utility_types::ToolType; @@ -25,15 +23,11 @@ pub struct EditorTestUtils { impl EditorTestUtils { pub fn create() -> Self { let _ = env_logger::builder().is_test(true).try_init(); - set_uuid_seed(0); let (mut editor, runtime) = Editor::new_local_executor(); - // We have to set this directly instead of using `GlobalsMessage::SetPlatform` because race conditions with multiple tests can cause that message handler to set it more than once, which is a failure. - // It isn't sufficient to guard the message dispatch here with a check if the once_cell is empty, because that isn't atomic and the time between checking and handling the dispatch can let multiple through. - let _ = GLOBAL_PLATFORM.set(Platform::Windows).is_ok(); - editor.handle_message(PortfolioMessage::Init); + Self { editor, runtime } } diff --git a/frontend/src/editor.ts b/frontend/src/editor.ts index f49cc2a169..1661b78920 100644 --- a/frontend/src/editor.ts +++ b/frontend/src/editor.ts @@ -1,7 +1,7 @@ // import { panicProxy } from "@graphite/utility-functions/panic-proxy"; -import type { EditorHandle } from "@graphite/../wasm/pkg/graphite_wasm"; -import init, { wasmMemory, receiveNativeMessage, constructEditor } from "@graphite/../wasm/pkg/graphite_wasm"; +import { EditorHandle } from "@graphite/../wasm/pkg/graphite_wasm"; +import init, { wasmMemory, receiveNativeMessage } from "@graphite/../wasm/pkg/graphite_wasm"; import { type JsMessageType } from "@graphite/messages"; import { createSubscriptionRouter, type SubscriptionRouter } from "@graphite/subscription-router"; import { operatingSystem } from "@graphite/utility-functions/platform"; @@ -45,14 +45,14 @@ export function createEditor(): Editor { const randomSeed = BigInt(randomSeedFloat); // Handle: object containing many functions from `editor_api.rs` that are part of the `EditorHandle` struct (generated by wasm-bindgen) - const handle: EditorHandle = constructEditor(operatingSystem(), randomSeed, (messageType: JsMessageType, messageData: Record) => { + const handle = EditorHandle.create(operatingSystem(), randomSeed, (messageType: JsMessageType, messageData: Record) => { // This callback is called by Wasm when a FrontendMessage is received from the Wasm wrapper `EditorHandle` // We pass along the first two arguments then add our own `raw` and `handle` context for the last two arguments subscriptions.handleJsMessage(messageType, messageData, raw, handle); }); // Subscriptions: allows subscribing to messages in JS that are sent from the Wasm backend - const subscriptions: SubscriptionRouter = createSubscriptionRouter(); + const subscriptions = createSubscriptionRouter(); // Check if the URL hash fragment has any demo artwork to be loaded (async () => { diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index 95ef79a38d..27c573b2de 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -79,45 +79,42 @@ impl EditorHandle { } } -#[cfg(not(feature = "native"))] -#[wasm_bindgen(js_name = constructEditor)] -pub fn construct_editor(platform: String, uuid_random_seed: u64, frontend_message_handler_callback: js_sys::Function) -> EditorHandle { - Editor::init( - Environment { - platform: Platform::Web, - host: match platform.as_str() { - "Linux" => Host::Linux, - "Mac" => Host::Mac, - "Windows" => Host::Windows, - _ => unreachable!(), +#[wasm_bindgen] +impl EditorHandle { + #[cfg(not(feature = "native"))] + pub fn create(platform: String, uuid_random_seed: u64, frontend_message_handler_callback: js_sys::Function) -> EditorHandle { + let editor = Editor::create( + Environment { + platform: Platform::Web, + host: match platform.as_str() { + "Linux" => Host::Linux, + "Mac" => Host::Mac, + "Windows" => Host::Windows, + _ => unreachable!(), + }, }, - }, - uuid_random_seed, - ); + uuid_random_seed, + ); - let editor = Editor::new(); - let editor_handle = EditorHandle { frontend_message_handler_callback }; - if EDITOR.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor))).is_none() { - log::error!("Attempted to initialize the editor more than once"); - } - if EDITOR_HANDLE.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor_handle.clone()))).is_none() { - log::error!("Attempted to initialize the editor handle more than once"); + let editor_handle = EditorHandle { frontend_message_handler_callback }; + if EDITOR.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor))).is_none() { + log::error!("Attempted to initialize the editor more than once"); + } + if EDITOR_HANDLE.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor_handle.clone()))).is_none() { + log::error!("Attempted to initialize the editor handle more than once"); + } + editor_handle } - editor_handle -} -#[cfg(feature = "native")] -#[wasm_bindgen(js_name = constructEditor)] -pub fn construct_editor(_platform: String, _uuid_random_seed: u64, frontend_message_handler_callback: js_sys::Function) -> EditorHandle { - let editor_handle = EditorHandle { frontend_message_handler_callback }; - if EDITOR_HANDLE.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor_handle.clone()))).is_none() { - log::error!("Attempted to initialize the editor handle more than once"); + #[cfg(feature = "native")] + pub fn create(_platform: String, _uuid_random_seed: u64, frontend_message_handler_callback: js_sys::Function) -> EditorHandle { + let editor_handle = EditorHandle { frontend_message_handler_callback }; + if EDITOR_HANDLE.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor_handle.clone()))).is_none() { + log::error!("Attempted to initialize the editor handle more than once"); + } + editor_handle } - editor_handle -} -#[wasm_bindgen] -impl EditorHandle { // Sends a message to the dispatcher in the Editor Backend #[cfg(not(feature = "native"))] fn dispatch>(&self, message: T) { diff --git a/node-graph/libraries/core-types/src/uuid.rs b/node-graph/libraries/core-types/src/uuid.rs index d37dcaaa4d..3df5007e08 100644 --- a/node-graph/libraries/core-types/src/uuid.rs +++ b/node-graph/libraries/core-types/src/uuid.rs @@ -45,19 +45,27 @@ mod u64_string { mod uuid_generation { use rand_chacha::ChaCha20Rng; use rand_chacha::rand_core::{RngCore, SeedableRng}; + use std::cell::Cell; use std::sync::Mutex; static RNG: Mutex> = Mutex::new(None); + thread_local! { + pub static UUID_SEED: Cell> = const { Cell::new(None) }; + } - pub fn init_rng(random_seed: u64) { - let mut lock = RNG.lock().expect("UUID mutex poisoned"); - *lock = Some(ChaCha20Rng::seed_from_u64(random_seed)); + pub fn set_uuid_seed(random_seed: u64) { + UUID_SEED.with(|seed| seed.set(Some(random_seed))) } pub fn generate_uuid() -> u64 { let Ok(mut lock) = RNG.lock() else { panic!("UUID mutex poisoned") }; - let rng = lock.as_mut().expect("UUID generator not initialized. Forgot to call `init_rng`?"); - rng.next_u64() + if lock.is_none() { + UUID_SEED.with(|seed| { + let random_seed = seed.get().unwrap_or(42); + *lock = Some(ChaCha20Rng::seed_from_u64(random_seed)); + }) + } + lock.as_mut().map(ChaCha20Rng::next_u64).expect("UUID mutex poisoned") } } From 6c853e769c2566ae3e47cb0b83e499996913dea0 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Mon, 19 Jan 2026 15:59:38 +0000 Subject: [PATCH 27/28] fix --- desktop/src/app.rs | 4 +++- desktop/src/lib.rs | 3 ++- desktop/wrapper/src/lib.rs | 10 ++++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/desktop/src/app.rs b/desktop/src/app.rs index 448a820444..52775f6af3 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -79,7 +79,7 @@ impl App { let mut persistent_data = PersistentData::default(); persistent_data.load_from_disk(); - let desktop_wrapper = DesktopWrapper::create(&wgpu_context.clone(), rand::rng().random()); + let desktop_wrapper = DesktopWrapper::create(rand::rng().random()); Self { render_state: None, @@ -479,6 +479,8 @@ impl ApplicationHandler for App { } self.resize(); + + self.desktop_wrapper.init(self.wgpu_context.clone()); } fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) { diff --git a/desktop/src/lib.rs b/desktop/src/lib.rs index 1f9df7168c..30e3636070 100644 --- a/desktop/src/lib.rs +++ b/desktop/src/lib.rs @@ -69,13 +69,14 @@ pub fn start() { // Must be called before event loop initialization or native window integrations will break App::init(); + let wgpu_context = futures::executor::block_on(gpu_context::create_wgpu_context()); + let event_loop = EventLoop::new().unwrap(); let (app_event_sender, app_event_receiver) = std::sync::mpsc::channel(); let app_event_scheduler = event_loop.create_app_event_scheduler(app_event_sender); let (cef_view_info_sender, cef_view_info_receiver) = std::sync::mpsc::channel(); - let wgpu_context = futures::executor::block_on(gpu_context::create_wgpu_context()); let cef_handler = cef::CefHandler::new(wgpu_context.clone(), app_event_scheduler.clone(), cef_view_info_receiver); let cef_context = match cef_context_builder.initialize(cef_handler, cli.disable_ui_acceleration) { Ok(context) => { diff --git a/desktop/wrapper/src/lib.rs b/desktop/wrapper/src/lib.rs index f5f0955279..f9d6c46a6b 100644 --- a/desktop/wrapper/src/lib.rs +++ b/desktop/wrapper/src/lib.rs @@ -27,10 +27,7 @@ pub struct DesktopWrapper { } impl DesktopWrapper { - pub fn create(wgpu_context: &WgpuContext, uuid_random_seed: u64) -> Self { - let application_io = WasmApplicationIo::new_with_context(wgpu_context.clone()); - futures::executor::block_on(graphite_editor::node_graph_executor::replace_application_io(application_io)); - + pub fn create(uuid_random_seed: u64) -> Self { #[cfg(target_os = "windows")] let host = Host::Windows; #[cfg(target_os = "macos")] @@ -44,6 +41,11 @@ impl DesktopWrapper { } } + pub fn init(&self, wgpu_context: WgpuContext) { + let application_io = WasmApplicationIo::new_with_context(wgpu_context); + futures::executor::block_on(graphite_editor::node_graph_executor::replace_application_io(application_io)); + } + pub fn dispatch(&mut self, message: DesktopWrapperMessage) -> Vec { let mut executor = DesktopWrapperMessageDispatcher::new(&mut self.editor); executor.queue_desktop_wrapper_message(message); From 724e106c8c7663e7847fb6f10e114eaac0a8eff6 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Mon, 19 Jan 2026 15:59:38 +0000 Subject: [PATCH 28/28] undo renames --- desktop/src/app.rs | 4 ++-- desktop/src/lib.rs | 2 +- desktop/wrapper/src/lib.rs | 4 ++-- editor/src/application.rs | 2 +- frontend/wasm/src/editor_api.rs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/desktop/src/app.rs b/desktop/src/app.rs index 52775f6af3..bafec9c1d2 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -51,7 +51,7 @@ impl App { Window::init(); } - pub(crate) fn create( + pub(crate) fn new( cef_context: Box, cef_view_info_sender: Sender, wgpu_context: WgpuContext, @@ -79,7 +79,7 @@ impl App { let mut persistent_data = PersistentData::default(); persistent_data.load_from_disk(); - let desktop_wrapper = DesktopWrapper::create(rand::rng().random()); + let desktop_wrapper = DesktopWrapper::new(rand::rng().random()); Self { render_state: None, diff --git a/desktop/src/lib.rs b/desktop/src/lib.rs index 30e3636070..a685b2b2fa 100644 --- a/desktop/src/lib.rs +++ b/desktop/src/lib.rs @@ -101,7 +101,7 @@ pub fn start() { } }; - let mut app = App::create(Box::new(cef_context), cef_view_info_sender, wgpu_context, app_event_receiver, app_event_scheduler, cli.files); + let mut app = App::new(Box::new(cef_context), cef_view_info_sender, wgpu_context, app_event_receiver, app_event_scheduler, cli.files); event_loop.run_app(&mut app).unwrap(); diff --git a/desktop/wrapper/src/lib.rs b/desktop/wrapper/src/lib.rs index f9d6c46a6b..1080b83df6 100644 --- a/desktop/wrapper/src/lib.rs +++ b/desktop/wrapper/src/lib.rs @@ -27,7 +27,7 @@ pub struct DesktopWrapper { } impl DesktopWrapper { - pub fn create(uuid_random_seed: u64) -> Self { + pub fn new(uuid_random_seed: u64) -> Self { #[cfg(target_os = "windows")] let host = Host::Windows; #[cfg(target_os = "macos")] @@ -37,7 +37,7 @@ impl DesktopWrapper { let env = Environment { platform: Platform::Desktop, host }; Self { - editor: Editor::create(env, uuid_random_seed), + editor: Editor::new(env, uuid_random_seed), } } diff --git a/editor/src/application.rs b/editor/src/application.rs index cb0547d360..cc54caa8e3 100644 --- a/editor/src/application.rs +++ b/editor/src/application.rs @@ -8,7 +8,7 @@ pub struct Editor { } impl Editor { - pub fn create(environment: Environment, uuid_random_seed: u64) -> Self { + pub fn new(environment: Environment, uuid_random_seed: u64) -> Self { ENVIRONMENT.set(environment).expect("Editor shoud only be initialized once"); graphene_std::uuid::set_uuid_seed(uuid_random_seed); diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index 27c573b2de..192687feac 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -83,7 +83,7 @@ impl EditorHandle { impl EditorHandle { #[cfg(not(feature = "native"))] pub fn create(platform: String, uuid_random_seed: u64, frontend_message_handler_callback: js_sys::Function) -> EditorHandle { - let editor = Editor::create( + let editor = Editor::new( Environment { platform: Platform::Web, host: match platform.as_str() {