Replace globals with editor environment (#3656)

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Timon
2026-01-19 17:06:02 +00:00
committed by GitHub
co-authored by Keavon Chambers
parent 07fbcd489c
commit 95d3556204
29 changed files with 193 additions and 259 deletions
+5 -10
View File
@@ -1,3 +1,4 @@
use rand::Rng;
use rfd::AsyncFileDialog; use rfd::AsyncFileDialog;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
@@ -16,7 +17,7 @@ use crate::event::{AppEvent, AppEventScheduler};
use crate::persist::PersistentData; use crate::persist::PersistentData;
use crate::render::{RenderError, RenderState}; use crate::render::{RenderError, RenderState};
use crate::window::Window; 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}; use crate::wrapper::{DesktopWrapper, NodeGraphExecutionResult, WgpuContext, serialize_frontend_messages};
pub(crate) struct App { pub(crate) struct App {
@@ -78,6 +79,8 @@ impl App {
let mut persistent_data = PersistentData::default(); let mut persistent_data = PersistentData::default();
persistent_data.load_from_disk(); persistent_data.load_from_disk();
let desktop_wrapper = DesktopWrapper::new(rand::rng().random());
Self { Self {
render_state: None, render_state: None,
wgpu_context, wgpu_context,
@@ -91,7 +94,7 @@ impl App {
ui_scale: 1., ui_scale: 1.,
app_event_receiver, app_event_receiver,
app_event_scheduler, app_event_scheduler,
desktop_wrapper: DesktopWrapper::new(), desktop_wrapper,
last_ui_update: Instant::now(), last_ui_update: Instant::now(),
cef_context, cef_context,
cef_schedule: Some(Instant::now()), cef_schedule: Some(Instant::now()),
@@ -478,14 +481,6 @@ impl ApplicationHandler for App {
self.resize(); self.resize();
self.desktop_wrapper.init(self.wgpu_context.clone()); 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) { fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) {
+3 -2
View File
@@ -66,6 +66,7 @@ pub fn start() {
} }
}; };
// Must be called before event loop initialization or native window integrations will break
App::init(); App::init();
let wgpu_context = futures::executor::block_on(gpu_context::create_wgpu_context()); let wgpu_context = futures::executor::block_on(gpu_context::create_wgpu_context());
@@ -78,9 +79,9 @@ pub fn start() {
let cef_handler = cef::CefHandler::new(wgpu_context.clone(), app_event_scheduler.clone(), cef_view_info_receiver); 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) { let cef_context = match cef_context_builder.initialize(cef_handler, cli.disable_ui_acceleration) {
Ok(c) => { Ok(context) => {
tracing::info!("CEF initialized successfully"); tracing::info!("CEF initialized successfully");
c context
} }
Err(cef::InitError::AlreadyRunning) => { Err(cef::InitError::AlreadyRunning) => {
tracing::error!("Another instance is already running, Exiting."); tracing::error!("Another instance is already running, Exiting.");
@@ -1,11 +1,10 @@
use graphene_std::Color; use graphene_std::Color;
use graphene_std::raster::Image; 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::clipboard::utility_types::ClipboardContentRaw;
use graphite_editor::messages::prelude::*; use graphite_editor::messages::prelude::*;
use super::DesktopWrapperMessageDispatcher; 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) { pub(super) fn handle_desktop_wrapper_message(dispatcher: &mut DesktopWrapperMessageDispatcher, message: DesktopWrapperMessage) {
match message { match message {
@@ -111,15 +110,6 @@ pub(super) fn handle_desktop_wrapper_message(dispatcher: &mut DesktopWrapperMess
dispatcher.queue_editor_message(message); dispatcher.queue_editor_message(message);
} }
DesktopWrapperMessage::PollNodeGraphEvaluation => dispatcher.poll_node_graph_evaluation(), 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 } => { DesktopWrapperMessage::UpdateMaximized { maximized } => {
let message = FrontendMessage::UpdateMaximized { maximized }; let message = FrontendMessage::UpdateMaximized { maximized };
dispatcher.queue_editor_message(message); dispatcher.queue_editor_message(message);
+13 -9
View File
@@ -1,5 +1,5 @@
use graph_craft::wasm_application_io::WasmApplicationIo; 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}; use graphite_editor::messages::prelude::{FrontendMessage, Message};
// TODO: Remove usage of this reexport in desktop create and remove this line // TODO: Remove usage of this reexport in desktop create and remove this line
@@ -27,8 +27,18 @@ pub struct DesktopWrapper {
} }
impl DesktopWrapper { impl DesktopWrapper {
pub fn new() -> Self { pub fn new(uuid_random_seed: u64) -> Self {
Self { editor: Editor::new() } #[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 };
Self {
editor: Editor::new(env, uuid_random_seed),
}
} }
pub fn init(&self, wgpu_context: WgpuContext) { pub fn init(&self, wgpu_context: WgpuContext) {
@@ -51,12 +61,6 @@ impl DesktopWrapper {
} }
} }
impl Default for DesktopWrapper {
fn default() -> Self {
Self::new()
}
}
pub enum NodeGraphExecutionResult { pub enum NodeGraphExecutionResult {
HasRun(Option<wgpu::Texture>), HasRun(Option<wgpu::Texture>),
NotRun, NotRun,
-7
View File
@@ -109,7 +109,6 @@ pub enum DesktopWrapperMessage {
content: Vec<u8>, content: Vec<u8>,
}, },
PollNodeGraphEvaluation, PollNodeGraphEvaluation,
UpdatePlatform(Platform),
UpdateMaximized { UpdateMaximized {
maximized: bool, maximized: bool,
}, },
@@ -163,12 +162,6 @@ pub enum SaveFileDialogContext {
File { content: Vec<u8> }, File { content: Vec<u8> },
} }
pub enum Platform {
Windows,
Mac,
Linux,
}
pub enum MenuItem { pub enum MenuItem {
Action { Action {
id: String, id: String,
+60 -9
View File
@@ -1,24 +1,31 @@
use crate::dispatcher::Dispatcher; use crate::dispatcher::Dispatcher;
use crate::messages::prelude::*; use crate::messages::prelude::*;
pub use graphene_std::uuid::*; pub use graphene_std::uuid::*;
use std::sync::OnceLock;
// TODO: serialize with serde to save the current editor state
pub struct Editor { pub struct Editor {
pub dispatcher: Dispatcher, pub dispatcher: Dispatcher,
} }
impl Editor { impl Editor {
/// Construct the editor. pub fn new(environment: Environment, uuid_random_seed: u64) -> Self {
/// Remember to provide a random seed with `editor::set_uuid_seed(seed)` before any editors can be used. ENVIRONMENT.set(environment).expect("Editor shoud only be initialized once");
pub fn new() -> Self { graphene_std::uuid::set_uuid_seed(uuid_random_seed);
Self { dispatcher: Dispatcher::new() } Self { dispatcher: Dispatcher::new() }
} }
#[cfg(test)] #[cfg(test)]
pub(crate) fn new_local_executor() -> (Self, crate::node_graph_executor::NodeRuntime) { 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 (runtime, executor) = crate::node_graph_executor::NodeGraphExecutor::new_with_local_runtime();
let dispatcher = Dispatcher::with_executor(executor); let editor = Self {
(Self { dispatcher }, runtime) dispatcher: Dispatcher::with_executor(executor),
};
(editor, runtime)
} }
pub fn handle_message<T: Into<Message>>(&mut self, message: T) -> Vec<FrontendMessage> { pub fn handle_message<T: Into<Message>>(&mut self, message: T) -> Vec<FrontendMessage> {
@@ -32,9 +39,53 @@ impl Editor {
} }
} }
impl Default for Editor { static ENVIRONMENT: OnceLock<Environment> = OnceLock::new();
fn default() -> Self { impl Editor {
Self::new() #[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)]
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 {
Windows,
Mac,
Linux,
}
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_windows(&self) -> bool {
matches!(self.host, Host::Windows)
}
pub fn is_mac(&self) -> bool {
matches!(self.host, Host::Mac)
}
pub fn is_linux(&self) -> bool {
matches!(self.host, Host::Linux)
} }
} }
+1 -8
View File
@@ -23,12 +23,11 @@ pub struct DispatcherMessageHandlers {
debug_message_handler: DebugMessageHandler, debug_message_handler: DebugMessageHandler,
defer_message_handler: DeferMessageHandler, defer_message_handler: DeferMessageHandler,
dialog_message_handler: DialogMessageHandler, dialog_message_handler: DialogMessageHandler,
globals_message_handler: GlobalsMessageHandler,
input_preprocessor_message_handler: InputPreprocessorMessageHandler, input_preprocessor_message_handler: InputPreprocessorMessageHandler,
key_mapping_message_handler: KeyMappingMessageHandler, key_mapping_message_handler: KeyMappingMessageHandler,
layout_message_handler: LayoutMessageHandler, layout_message_handler: LayoutMessageHandler,
menu_bar_message_handler: MenuBarMessageHandler, menu_bar_message_handler: MenuBarMessageHandler,
pub portfolio_message_handler: PortfolioMessageHandler, pub(crate) portfolio_message_handler: PortfolioMessageHandler,
preferences_message_handler: PreferencesMessageHandler, preferences_message_handler: PreferencesMessageHandler,
tool_message_handler: ToolMessageHandler, tool_message_handler: ToolMessageHandler,
viewport_message_handler: ViewportMessageHandler, viewport_message_handler: ViewportMessageHandler,
@@ -192,17 +191,11 @@ impl Dispatcher {
self.responses.push(message); self.responses.push(message);
} }
} }
Message::Globals(message) => {
self.message_handlers.globals_message_handler.process_message(message, &mut queue, ());
}
Message::InputPreprocessor(message) => { 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( self.message_handlers.input_preprocessor_message_handler.process_message(
message, message,
&mut queue, &mut queue,
InputPreprocessorMessageContext { InputPreprocessorMessageContext {
keyboard_platform,
viewport: &self.message_handlers.viewport_message_handler, viewport: &self.message_handlers.viewport_message_handler,
}, },
); );
@@ -1,11 +1,8 @@
use crate::messages::prelude::*; use crate::messages::prelude::*;
use super::app_window_message_handler::AppWindowPlatform;
#[impl_message(Message, AppWindow)] #[impl_message(Message, AppWindow)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum AppWindowMessage { pub enum AppWindowMessage {
UpdatePlatform { platform: AppWindowPlatform },
PointerLock, PointerLock,
PointerLockMove { x: f64, y: f64 }, PointerLockMove { x: f64, y: f64 },
Close, Close,
@@ -1,20 +1,15 @@
use crate::messages::app_window::AppWindowMessage; use crate::application::{Environment, Platform};
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::{application::Host, messages::app_window::AppWindowMessage};
use graphite_proc_macros::{ExtractField, message_handler_data}; use graphite_proc_macros::{ExtractField, message_handler_data};
#[derive(Debug, Clone, Default, ExtractField)] #[derive(Debug, Clone, Default, ExtractField)]
pub struct AppWindowMessageHandler { pub struct AppWindowMessageHandler {}
platform: AppWindowPlatform,
}
#[message_handler_data] #[message_handler_data]
impl MessageHandler<AppWindowMessage, ()> for AppWindowMessageHandler { impl MessageHandler<AppWindowMessage, ()> for AppWindowMessageHandler {
fn process_message(&mut self, message: AppWindowMessage, responses: &mut std::collections::VecDeque<Message>, _: ()) { fn process_message(&mut self, message: AppWindowMessage, responses: &mut std::collections::VecDeque<Message>, _: ()) {
match message { match message {
AppWindowMessage::UpdatePlatform { platform } => {
self.platform = platform;
responses.add(FrontendMessage::UpdatePlatform { platform: self.platform });
}
AppWindowMessage::PointerLock => { AppWindowMessage::PointerLock => {
responses.add(FrontendMessage::WindowPointerLock); responses.add(FrontendMessage::WindowPointerLock);
} }
@@ -66,3 +61,14 @@ pub enum AppWindowPlatform {
Mac, Mac,
Linux, Linux,
} }
impl From<&Environment> for AppWindowPlatform {
fn from(environment: &Environment) -> Self {
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,
}
}
}
@@ -1,4 +0,0 @@
use crate::messages::portfolio::utility_types::Platform;
use std::sync::OnceLock;
pub static GLOBAL_PLATFORM: OnceLock<Platform> = OnceLock::new();
@@ -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 },
}
@@ -1,20 +0,0 @@
use crate::messages::prelude::*;
#[derive(Debug, Default, ExtractField)]
pub struct GlobalsMessageHandler {}
#[message_handler_data]
impl MessageHandler<GlobalsMessage, ()> for GlobalsMessageHandler {
fn process_message(&mut self, message: GlobalsMessage, _responses: &mut VecDeque<Message>, _: ()) {
match message {
GlobalsMessage::SetPlatform { platform } => {
if GLOBAL_PLATFORM.get() != Some(&platform) {
GLOBAL_PLATFORM.set(platform).expect("Failed to set GLOBAL_PLATFORM");
}
}
}
}
advertise_actions!(GlobalsMessageDiscriminant;
);
}
-9
View File
@@ -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;
@@ -1,8 +1,8 @@
use super::utility_types::input_keyboard::KeysGroup; use super::utility_types::input_keyboard::KeysGroup;
use super::utility_types::misc::Mapping; 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::input_keyboard::{self, Key};
use crate::messages::input_mapper::utility_types::misc::MappingEntry; use crate::messages::input_mapper::utility_types::misc::MappingEntry;
use crate::messages::portfolio::utility_types::KeyboardPlatformLayout;
use crate::messages::prelude::*; use crate::messages::prelude::*;
#[derive(ExtractField)] #[derive(ExtractField)]
@@ -48,11 +48,7 @@ impl InputMapperMessageHandler {
let found_actions = all_mapping_entries.filter(|entry| entry.action.to_discriminant() == *action_to_find); let found_actions = all_mapping_entries.filter(|entry| entry.action.to_discriminant() == *action_to_find);
// Get the `Key` for this platform's accelerator key // 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 = if Editor::environment().is_mac() { Key::Command } else { Key::Control };
let platform_accel_key = match keyboard_layout() {
KeyboardPlatformLayout::Standard => Key::Control,
KeyboardPlatformLayout::Mac => Key::Command,
};
let entry_to_key = |entry: &MappingEntry| { let entry_to_key = |entry: &MappingEntry| {
// Get the modifier keys for the entry (and convert them to Key) // Get the modifier keys for the entry (and convert them to Key)
@@ -1,3 +1,4 @@
use crate::application::Editor;
use crate::consts::{BIG_NUDGE_AMOUNT, BRUSH_SIZE_CHANGE_KEYBOARD, NUDGE_AMOUNT}; 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::key_mapping::MappingVariant;
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeyStates}; 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::node_graph::utility_types::Direction;
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard; use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
use crate::messages::portfolio::document::utility_types::misc::GroupFolderType; use crate::messages::portfolio::document::utility_types::misc::GroupFolderType;
use crate::messages::portfolio::utility_types::KeyboardPlatformLayout;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::messages::tool::tool_messages::brush_tool::BrushToolMessageOptionsUpdate; use crate::messages::tool::tool_messages::brush_tool::BrushToolMessageOptionsUpdate;
use crate::messages::tool::tool_messages::select_tool::SelectToolPointerKeys; 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 InputMapperMessage::*;
use Key::*; 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 is_mac = Editor::environment().is_mac();
let keyboard_platform = GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout();
// NOTICE: // NOTICE:
// If a new mapping you added here isn't working (and perhaps another lower-precedence one is instead), make sure to advertise // 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), entry!(KeyDown(KeyZ); modifiers=[Accel, MouseLeft], action_dispatch=DocumentMessage::Noop),
// //
// AppWindowMessage // AppWindowMessage
entry!(KeyDown(F11); 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=(keyboard_platform != KeyboardPlatformLayout::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), entry!(KeyDown(KeyQ); modifiers=[Command], disabled=cfg!(not(target_os = "macos")), action_dispatch=AppWindowMessage::Close),
// //
// ClipboardMessage // 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; 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 // 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=[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: keyboard_platform != KeyboardPlatformLayout::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!(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(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(PageDown); modifiers=[Shift], action_dispatch=NavigationMessage::CanvasPanByViewportFraction { delta: DVec2::new(-1., 0.) }),
@@ -1,4 +1,4 @@
use crate::messages::portfolio::utility_types::KeyboardPlatformLayout; use crate::application::Editor;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use bitflags::bitflags; use bitflags::bitflags;
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
@@ -258,7 +258,7 @@ impl fmt::Display for Key {
return write!(f, "{}", key_name.chars().skip(KEY_PREFIX.len()).collect::<String>()); return write!(f, "{}", key_name.chars().skip(KEY_PREFIX.len()).collect::<String>());
} }
let keyboard_layout = || GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout(); let is_mac = Editor::environment().is_mac();
let name = match self { let name = match self {
// Writing system keys // Writing system keys
@@ -275,21 +275,21 @@ impl fmt::Display for Key {
Self::Slash => "/", Self::Slash => "/",
// Functional keys // Functional keys
Self::Alt => match keyboard_layout() { Self::Alt => match is_mac {
KeyboardPlatformLayout::Standard => "Alt", true => "⌥",
KeyboardPlatformLayout::Mac => "⌥", false => "Alt",
}, },
Self::Meta => match keyboard_layout() { Self::Meta => match is_mac {
KeyboardPlatformLayout::Standard => "⊞", true => "⌘",
KeyboardPlatformLayout::Mac => "⌘", false => "⊞",
}, },
Self::Shift => match keyboard_layout() { Self::Shift => match is_mac {
KeyboardPlatformLayout::Standard => "Shift", true => "⇧",
KeyboardPlatformLayout::Mac => "⇧", false => "Shift",
}, },
Self::Control => match keyboard_layout() { Self::Control => match is_mac {
KeyboardPlatformLayout::Standard => "Ctrl", true => "⌃",
KeyboardPlatformLayout::Mac => "⌃", false => "Ctrl",
}, },
Self::Backspace => "⌫", Self::Backspace => "⌫",
@@ -317,9 +317,9 @@ impl fmt::Display for Key {
// Other keys that aren't part of the W3C spec // Other keys that aren't part of the W3C spec
Self::Command => "⌘", Self::Command => "⌘",
Self::Accel => match keyboard_layout() { Self::Accel => match is_mac {
KeyboardPlatformLayout::Standard => "Ctrl", true => "⌘",
KeyboardPlatformLayout::Mac => "⌘", false => "Ctrl",
}, },
Self::MouseLeft => "Click", Self::MouseLeft => "Click",
Self::MouseRight => "R.Click", Self::MouseRight => "R.Click",
@@ -356,10 +356,9 @@ impl fmt::Display for KeysGroup {
.0 .0
.iter() .iter()
.map(|key| { .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); 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() key.to_string()
} else { } else {
key.to_string() + JOINER_MARK key.to_string() + JOINER_MARK
@@ -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_keyboard::{Key, KeyStates, ModifierKeys};
use crate::messages::input_mapper::utility_types::input_mouse::{MouseButton, MouseKeys, MouseState}; use crate::messages::input_mapper::utility_types::input_mouse::{MouseButton, MouseKeys, MouseState};
use crate::messages::input_mapper::utility_types::misc::FrameTimeInfo; use crate::messages::input_mapper::utility_types::misc::FrameTimeInfo;
use crate::messages::portfolio::utility_types::KeyboardPlatformLayout;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use std::time::Duration; use std::time::Duration;
#[derive(ExtractField)] #[derive(ExtractField)]
pub struct InputPreprocessorMessageContext<'a> { pub struct InputPreprocessorMessageContext<'a> {
pub keyboard_platform: KeyboardPlatformLayout,
pub viewport: &'a ViewportMessageHandler, pub viewport: &'a ViewportMessageHandler,
} }
@@ -22,11 +21,11 @@ pub struct InputPreprocessorMessageHandler {
#[message_handler_data] #[message_handler_data]
impl<'a> MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext<'a>> for InputPreprocessorMessageHandler { impl<'a> MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext<'a>> for InputPreprocessorMessageHandler {
fn process_message(&mut self, message: InputPreprocessorMessage, responses: &mut VecDeque<Message>, context: InputPreprocessorMessageContext<'a>) { fn process_message(&mut self, message: InputPreprocessorMessage, responses: &mut VecDeque<Message>, context: InputPreprocessorMessageContext<'a>) {
let InputPreprocessorMessageContext { keyboard_platform, viewport } = context; let InputPreprocessorMessageContext { viewport } = context;
match message { match message {
InputPreprocessorMessage::DoubleClick { editor_mouse_state, modifier_keys } => { 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); let mouse_state = editor_mouse_state.to_mouse_state(viewport);
self.mouse.position = mouse_state.position; self.mouse.position = mouse_state.position;
@@ -43,7 +42,7 @@ impl<'a> MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContex
} }
} }
InputPreprocessorMessage::KeyDown { key, key_repeat, modifier_keys } => { InputPreprocessorMessage::KeyDown { key, key_repeat, modifier_keys } => {
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); self.keyboard.set(key as usize);
if !key_repeat { if !key_repeat {
responses.add(InputMapperMessage::KeyDownNoRepeat(key)); responses.add(InputMapperMessage::KeyDownNoRepeat(key));
@@ -51,7 +50,7 @@ impl<'a> MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContex
responses.add(InputMapperMessage::KeyDown(key)); responses.add(InputMapperMessage::KeyDown(key));
} }
InputPreprocessorMessage::KeyUp { key, key_repeat, modifier_keys } => { InputPreprocessorMessage::KeyUp { key, key_repeat, modifier_keys } => {
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); self.keyboard.unset(key as usize);
if !key_repeat { if !key_repeat {
responses.add(InputMapperMessage::KeyUpNoRepeat(key)); responses.add(InputMapperMessage::KeyUpNoRepeat(key));
@@ -59,7 +58,7 @@ impl<'a> MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContex
responses.add(InputMapperMessage::KeyUp(key)); responses.add(InputMapperMessage::KeyUp(key));
} }
InputPreprocessorMessage::PointerDown { editor_mouse_state, modifier_keys } => { InputPreprocessorMessage::PointerDown { 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); let mouse_state = editor_mouse_state.to_mouse_state(viewport);
self.mouse.position = mouse_state.position; self.mouse.position = mouse_state.position;
@@ -67,7 +66,7 @@ impl<'a> MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContex
self.translate_mouse_event(mouse_state, true, responses); self.translate_mouse_event(mouse_state, true, responses);
} }
InputPreprocessorMessage::PointerMove { editor_mouse_state, modifier_keys } => { InputPreprocessorMessage::PointerMove { 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); let mouse_state = editor_mouse_state.to_mouse_state(viewport);
self.mouse.position = mouse_state.position; self.mouse.position = mouse_state.position;
@@ -78,7 +77,7 @@ impl<'a> MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContex
self.translate_mouse_event(mouse_state, false, responses); self.translate_mouse_event(mouse_state, false, responses);
} }
InputPreprocessorMessage::PointerUp { editor_mouse_state, modifier_keys } => { InputPreprocessorMessage::PointerUp { 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); let mouse_state = editor_mouse_state.to_mouse_state(viewport);
self.mouse.position = mouse_state.position; self.mouse.position = mouse_state.position;
@@ -86,7 +85,7 @@ impl<'a> MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContex
self.translate_mouse_event(mouse_state, false, responses); self.translate_mouse_event(mouse_state, false, responses);
} }
InputPreprocessorMessage::PointerShake { editor_mouse_state, modifier_keys } => { InputPreprocessorMessage::PointerShake { 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); let mouse_state = editor_mouse_state.to_mouse_state(viewport);
self.mouse.position = mouse_state.position; self.mouse.position = mouse_state.position;
@@ -99,7 +98,7 @@ impl<'a> MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContex
self.frame_time.advance_timestamp(Duration::from_millis(timestamp)); self.frame_time.advance_timestamp(Duration::from_millis(timestamp));
} }
InputPreprocessorMessage::WheelScroll { editor_mouse_state, modifier_keys } => { InputPreprocessorMessage::WheelScroll { 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); let mouse_state = editor_mouse_state.to_mouse_state(viewport);
self.mouse.position = mouse_state.position; self.mouse.position = mouse_state.position;
@@ -148,7 +147,7 @@ impl InputPreprocessorMessageHandler {
self.mouse = new_state; self.mouse = new_state;
} }
fn update_states_of_modifier_keys(&mut self, pressed_modifier_keys: ModifierKeys, keyboard_platform: KeyboardPlatformLayout, responses: &mut VecDeque<Message>) { fn update_states_of_modifier_keys(&mut self, pressed_modifier_keys: ModifierKeys, responses: &mut VecDeque<Message>) {
let is_key_pressed = |key_to_check: ModifierKeys| pressed_modifier_keys.contains(key_to_check); 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 // 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); 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 // 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 { let meta_or_command = match Editor::environment().is_mac() {
KeyboardPlatformLayout::Mac => Key::Command, true => Key::Command,
KeyboardPlatformLayout::Standard => Key::Meta, false => Key::Meta,
}; };
self.update_modifier_key(meta_or_command, is_key_pressed(ModifierKeys::META_OR_COMMAND), responses); 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 // 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 { let accel_virtual_key_state = match Editor::environment().is_mac() {
KeyboardPlatformLayout::Mac => is_key_pressed(ModifierKeys::META_OR_COMMAND), true => is_key_pressed(ModifierKeys::META_OR_COMMAND),
KeyboardPlatformLayout::Standard => is_key_pressed(ModifierKeys::CONTROL), false => is_key_pressed(ModifierKeys::CONTROL),
}; };
self.update_modifier_key(Key::Accel, accel_virtual_key_state, responses); self.update_modifier_key(Key::Accel, accel_virtual_key_state, responses);
} }
@@ -188,7 +187,6 @@ impl InputPreprocessorMessageHandler {
mod test { mod test {
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, ModifierKeys}; 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::input_mapper::utility_types::input_mouse::{EditorMouseState, MouseKeys, ScrollDelta};
use crate::messages::portfolio::utility_types::KeyboardPlatformLayout;
use crate::messages::prelude::*; use crate::messages::prelude::*;
#[test] #[test]
@@ -206,7 +204,6 @@ mod test {
let mut responses = VecDeque::new(); let mut responses = VecDeque::new();
let context = InputPreprocessorMessageContext { let context = InputPreprocessorMessageContext {
keyboard_platform: KeyboardPlatformLayout::Standard,
viewport: &ViewportMessageHandler::default(), viewport: &ViewportMessageHandler::default(),
}; };
input_preprocessor.process_message(message, &mut responses, context); input_preprocessor.process_message(message, &mut responses, context);
@@ -226,7 +223,6 @@ mod test {
let mut responses = VecDeque::new(); let mut responses = VecDeque::new();
let context = InputPreprocessorMessageContext { let context = InputPreprocessorMessageContext {
keyboard_platform: KeyboardPlatformLayout::Standard,
viewport: &ViewportMessageHandler::default(), viewport: &ViewportMessageHandler::default(),
}; };
input_preprocessor.process_message(message, &mut responses, context); input_preprocessor.process_message(message, &mut responses, context);
@@ -246,7 +242,6 @@ mod test {
let mut responses = VecDeque::new(); let mut responses = VecDeque::new();
let context = InputPreprocessorMessageContext { let context = InputPreprocessorMessageContext {
keyboard_platform: KeyboardPlatformLayout::Standard,
viewport: &ViewportMessageHandler::default(), viewport: &ViewportMessageHandler::default(),
}; };
input_preprocessor.process_message(message, &mut responses, context); input_preprocessor.process_message(message, &mut responses, context);
@@ -268,7 +263,6 @@ mod test {
let mut responses = VecDeque::new(); let mut responses = VecDeque::new();
let context = InputPreprocessorMessageContext { let context = InputPreprocessorMessageContext {
keyboard_platform: KeyboardPlatformLayout::Standard,
viewport: &ViewportMessageHandler::default(), viewport: &ViewportMessageHandler::default(),
}; };
input_preprocessor.process_message(message, &mut responses, context); input_preprocessor.process_message(message, &mut responses, context);
@@ -289,7 +283,6 @@ mod test {
let mut responses = VecDeque::new(); let mut responses = VecDeque::new();
let context = InputPreprocessorMessageContext { let context = InputPreprocessorMessageContext {
keyboard_platform: KeyboardPlatformLayout::Standard,
viewport: &ViewportMessageHandler::default(), viewport: &ViewportMessageHandler::default(),
}; };
input_preprocessor.process_message(message, &mut responses, context); input_preprocessor.process_message(message, &mut responses, context);
-2
View File
@@ -22,8 +22,6 @@ pub enum Message {
#[child] #[child]
Frontend(FrontendMessage), Frontend(FrontendMessage),
#[child] #[child]
Globals(GlobalsMessage),
#[child]
InputPreprocessor(InputPreprocessorMessage), InputPreprocessor(InputPreprocessorMessage),
#[child] #[child]
KeyMapping(KeyMappingMessage), KeyMapping(KeyMappingMessage),
-1
View File
@@ -8,7 +8,6 @@ pub mod debug;
pub mod defer; pub mod defer;
pub mod dialog; pub mod dialog;
pub mod frontend; pub mod frontend;
pub mod globals;
pub mod input_mapper; pub mod input_mapper;
pub mod input_preprocessor; pub mod input_preprocessor;
pub mod layout; pub mod layout;
@@ -1,3 +1,4 @@
use crate::application::Editor;
use crate::consts::{ 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_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, 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::navigation::utility_types::NavigationOperation;
use crate::messages::portfolio::document::utility_types::misc::PTZ; use crate::messages::portfolio::document::utility_types::misc::PTZ;
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface; use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
use crate::messages::portfolio::utility_types::KeyboardPlatformLayout;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo}; use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
use glam::{DAffine2, DVec2}; use glam::{DAffine2, DVec2};
@@ -176,9 +176,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
} }
NavigationMessage::CanvasPanMouseWheel { use_y_as_x } => { NavigationMessage::CanvasPanMouseWheel { use_y_as_x } => {
// On Mac, the OS already converts Shift+scroll into horizontal scrolling // 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 && !Editor::environment().is_mac() {
let delta = if use_y_as_x && keyboard_platform == KeyboardPlatformLayout::Standard {
(ipp.mouse.scroll_delta.y, 0.).into() (ipp.mouse.scroll_delta.y, 0.).into()
} else { } else {
ipp.mouse.scroll_delta.as_dvec2() ipp.mouse.scroll_delta.as_dvec2()
@@ -1,7 +1,7 @@
use super::document::utility_types::document_metadata::LayerNodeIdentifier; use super::document::utility_types::document_metadata::LayerNodeIdentifier;
use super::document::utility_types::network_interface; use super::document::utility_types::network_interface;
use super::utility_types::{PanelType, PersistentData}; 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::consts::{DEFAULT_DOCUMENT_NAME, DEFAULT_STROKE_WIDTH, FILE_EXTENSION};
use crate::messages::animation::TimingInformation; use crate::messages::animation::TimingInformation;
use crate::messages::clipboard::utility_types::ClipboardContent; use crate::messages::clipboard::utility_types::ClipboardContent;
@@ -101,9 +101,17 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
// Messages // Messages
PortfolioMessage::Init => { PortfolioMessage::Init => {
// Initialize the frontend with environment information
responses.add(FrontendMessage::UpdatePlatform {
platform: Editor::environment().into(),
});
// Tell frontend to load persistent preferences // Tell frontend to load persistent preferences
responses.add(FrontendMessage::TriggerLoadPreferences); 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 // Tell frontend to load the current document
responses.add(FrontendMessage::TriggerLoadFirstAutoSaveDocument); responses.add(FrontendMessage::TriggerLoadFirstAutoSaveDocument);
@@ -128,15 +136,13 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
shortcut: action_shortcut_manual!(Key::Shift, Key::MouseLeft), 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 // Request status bar info layout
responses.add(PortfolioMessage::RequestStatusBarInfoLayout); responses.add(PortfolioMessage::RequestStatusBarInfoLayout);
// Tell frontend to finish loading persistent documents // Tell frontend to finish loading persistent documents
responses.add(FrontendMessage::TriggerLoadRestAutoSaveDocuments); responses.add(FrontendMessage::TriggerLoadRestAutoSaveDocuments);
// Tell frontend to load documented passed in as launch arguments
responses.add(FrontendMessage::TriggerOpenLaunchDocuments); responses.add(FrontendMessage::TriggerOpenLaunchDocuments);
} }
PortfolioMessage::DocumentPassMessage { document_id, message } => { PortfolioMessage::DocumentPassMessage { document_id, message } => {
@@ -82,37 +82,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
#[default]
Standard,
/// Keyboard mapping used by Macs where Command is sometimes used in favor of Control
Mac,
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, Default, serde::Serialize, serde::Deserialize)] #[derive(PartialEq, Eq, Clone, Copy, Debug, Default, serde::Serialize, serde::Deserialize)]
pub enum PanelType { pub enum PanelType {
#[default] #[default]
-2
View File
@@ -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::preferences_dialog::{PreferencesDialogMessage, PreferencesDialogMessageContext, PreferencesDialogMessageDiscriminant, PreferencesDialogMessageHandler};
pub use crate::messages::dialog::{DialogMessage, DialogMessageContext, DialogMessageDiscriminant, DialogMessageHandler}; pub use crate::messages::dialog::{DialogMessage, DialogMessageContext, DialogMessageDiscriminant, DialogMessageHandler};
pub use crate::messages::frontend::{FrontendMessage, FrontendMessageDiscriminant}; 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::key_mapping::{KeyMappingMessage, KeyMappingMessageContext, KeyMappingMessageDiscriminant, KeyMappingMessageHandler};
pub use crate::messages::input_mapper::{InputMapperMessage, InputMapperMessageContext, InputMapperMessageDiscriminant, InputMapperMessageHandler}; pub use crate::messages::input_mapper::{InputMapperMessage, InputMapperMessageContext, InputMapperMessageDiscriminant, InputMapperMessageHandler};
pub use crate::messages::input_preprocessor::{InputPreprocessorMessage, InputPreprocessorMessageContext, InputPreprocessorMessageDiscriminant, InputPreprocessorMessageHandler}; 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}; pub use crate::messages::tool::tool_messages::text_tool::{TextToolMessage, TextToolMessageDiscriminant};
// Helper/miscellaneous // Helper/miscellaneous
pub use crate::messages::globals::global_variables::*;
pub use crate::messages::portfolio::document::utility_types::misc::DocumentId; pub use crate::messages::portfolio::document::utility_types::misc::DocumentId;
pub use graphite_proc_macros::*; pub use graphite_proc_macros::*;
pub use std::collections::{HashMap, HashSet, VecDeque}; pub use std::collections::{HashMap, HashSet, VecDeque};
+1 -7
View File
@@ -1,9 +1,7 @@
use crate::application::Editor; 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_keyboard::ModifierKeys;
use crate::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, MouseKeys, ScrollDelta, ViewportPosition}; 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::document::node_graph::document_node_definitions::DefinitionIdentifier;
use crate::messages::portfolio::utility_types::Platform;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::messages::tool::tool_messages::tool_prelude::Key; use crate::messages::tool::tool_messages::tool_prelude::Key;
use crate::messages::tool::utility_types::ToolType; use crate::messages::tool::utility_types::ToolType;
@@ -25,15 +23,11 @@ pub struct EditorTestUtils {
impl EditorTestUtils { impl EditorTestUtils {
pub fn create() -> Self { pub fn create() -> Self {
let _ = env_logger::builder().is_test(true).try_init(); let _ = env_logger::builder().is_test(true).try_init();
set_uuid_seed(0);
let (mut editor, runtime) = Editor::new_local_executor(); 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); editor.handle_message(PortfolioMessage::Init);
Self { editor, runtime } Self { editor, runtime }
} }
+1 -2
View File
@@ -16,7 +16,6 @@
import { createNodeGraphState } from "@graphite/state-providers/node-graph"; import { createNodeGraphState } from "@graphite/state-providers/node-graph";
import { createPortfolioState } from "@graphite/state-providers/portfolio"; import { createPortfolioState } from "@graphite/state-providers/portfolio";
import { createTooltipState } from "@graphite/state-providers/tooltip"; import { createTooltipState } from "@graphite/state-providers/tooltip";
import { operatingSystem } from "@graphite/utility-functions/platform";
import MainWindow from "@graphite/components/window/MainWindow.svelte"; import MainWindow from "@graphite/components/window/MainWindow.svelte";
@@ -51,7 +50,7 @@
onMount(() => { onMount(() => {
// Initialize certain setup tasks required by the editor backend to be ready for the user now that the frontend is ready // 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(() => { onDestroy(() => {
+17 -14
View File
@@ -1,8 +1,12 @@
// import { panicProxy } from "@graphite/utility-functions/panic-proxy"; // import { panicProxy } from "@graphite/utility-functions/panic-proxy";
import init, { setRandomSeed, wasmMemory, EditorHandle, receiveNativeMessage } 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 { type JsMessageType } from "@graphite/messages";
import { createSubscriptionRouter, type SubscriptionRouter } from "@graphite/subscription-router"; 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 = { export type Editor = {
raw: WebAssembly.Memory; raw: WebAssembly.Memory;
handle: EditorHandle; handle: EditorHandle;
@@ -14,10 +18,10 @@ let wasmImport: WebAssembly.Memory | undefined;
// Should be called asynchronously before `createEditor()`. // Should be called asynchronously before `createEditor()`.
export async function initWasm() { export async function initWasm() {
// Skip if the WASM module is already initialized // Skip if the Wasm module is already initialized
if (wasmImport !== undefined) return; 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(); const wasm = await init();
for (const [name, f] of Object.entries(wasm)) { for (const [name, f] of Object.entries(wasm)) {
if (name.startsWith("__node_registry")) f(); if (name.startsWith("__node_registry")) f();
@@ -28,28 +32,27 @@ export async function initWasm() {
(window as any).imageCanvases = {}; (window as any).imageCanvases = {};
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any).receiveNativeMessage = receiveNativeMessage; (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. // Should be called after running `initWasm()` and its promise resolving.
export function createEditor(): Editor { 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) // 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"); if (!wasmImport) throw new Error("Editor Wasm backend was not initialized at application startup");
const raw: WebAssembly.Memory = wasmImport; 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);
// Handle: object containing many functions from `editor_api.rs` that are part of the `EditorHandle` struct (generated by wasm-bindgen) // 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<string, unknown>) => { const handle = EditorHandle.create(operatingSystem(), randomSeed, (messageType: JsMessageType, messageData: Record<string, unknown>) => {
// This callback is called by WASM when a FrontendMessage is received from the WASM wrapper `EditorHandle` // 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 // 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.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(); const subscriptions = createSubscriptionRouter();
// Check if the URL hash fragment has any demo artwork to be loaded // Check if the URL hash fragment has any demo artwork to be loaded
(async () => { (async () => {
+1 -1
View File
@@ -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. 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** **Browser and OS**
${browserVersion()}, ${operatingSystem().replace("Unknown", "YOUR OPERATING SYSTEM")} ${browserVersion()}, ${operatingSystem()}
**Stack Trace** **Stack Trace**
Copied from the crash dialog in the Graphite editor: Copied from the crash dialog in the Graphite editor:
+2 -3
View File
@@ -25,18 +25,17 @@ export function browserVersion(): string {
return `${match[0]} ${match[1]}`; return `${match[0]} ${match[1]}`;
} }
export type OperatingSystem = "Windows" | "Mac" | "Linux" | "Unknown"; export type OperatingSystem = "Windows" | "Mac" | "Linux";
export function operatingSystem(): OperatingSystem { export function operatingSystem(): OperatingSystem {
const osTable: Record<string, OperatingSystem> = { const osTable: Record<string, OperatingSystem> = {
Windows: "Windows", Windows: "Windows",
Mac: "Mac", Mac: "Mac",
Linux: "Linux", Linux: "Linux",
Unknown: "Unknown",
}; };
const userAgentOS = Object.keys(osTable).find((key) => window.navigator.userAgent.includes(key)); const userAgentOS = Object.keys(osTable).find((key) => window.navigator.userAgent.includes(key));
return osTable[userAgentOS || "Unknown"]; return osTable[userAgentOS || "Windows"];
} }
export function isDesktop(): boolean { export function isDesktop(): boolean {
+19 -24
View File
@@ -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::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::document_metadata::LayerNodeIdentifier;
use editor::messages::portfolio::document::utility_types::network_interface::ImportOrExport; 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::prelude::*;
use editor::messages::tool::tool_messages::tool_prelude::WidgetId; use editor::messages::tool::tool_messages::tool_prelude::WidgetId;
use graph_craft::document::NodeId; use graph_craft::document::NodeId;
@@ -31,7 +31,7 @@ use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, ImageData, window};
#[cfg(not(feature = "native"))] #[cfg(not(feature = "native"))]
use crate::EDITOR; use crate::EDITOR;
#[cfg(not(feature = "native"))] #[cfg(not(feature = "native"))]
use editor::application::Editor; use editor::application::{Editor, Environment, Host, Platform};
static IMAGE_DATA_HASH: AtomicU64 = AtomicU64::new(0); static IMAGE_DATA_HASH: AtomicU64 = AtomicU64::new(0);
@@ -43,14 +43,7 @@ fn calculate_hash<T: std::hash::Hash>(t: &T) -> u64 {
hasher.finish() hasher.finish()
} }
/// Set the random seed used by the editor by calling this from JS upon initialization. /// Provides a handle to access the raw Wasm memory.
/// 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)] #[wasm_bindgen(js_name = wasmMemory)]
pub fn wasm_memory() -> JsValue { pub fn wasm_memory() -> JsValue {
wasm_bindgen::memory() wasm_bindgen::memory()
@@ -89,9 +82,20 @@ impl EditorHandle {
#[wasm_bindgen] #[wasm_bindgen]
impl EditorHandle { impl EditorHandle {
#[cfg(not(feature = "native"))] #[cfg(not(feature = "native"))]
#[wasm_bindgen(constructor)] pub fn create(platform: String, uuid_random_seed: u64, frontend_message_handler_callback: js_sys::Function) -> EditorHandle {
pub fn new(frontend_message_handler_callback: js_sys::Function) -> Self { let editor = Editor::new(
let editor = Editor::new(); Environment {
platform: Platform::Web,
host: match platform.as_str() {
"Linux" => Host::Linux,
"Mac" => Host::Mac,
"Windows" => Host::Windows,
_ => unreachable!(),
},
},
uuid_random_seed,
);
let editor_handle = EditorHandle { frontend_message_handler_callback }; let editor_handle = EditorHandle { frontend_message_handler_callback };
if EDITOR.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor))).is_none() { 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"); log::error!("Attempted to initialize the editor more than once");
@@ -103,8 +107,7 @@ impl EditorHandle {
} }
#[cfg(feature = "native")] #[cfg(feature = "native")]
#[wasm_bindgen(constructor)] pub fn create(_platform: String, _uuid_random_seed: u64, frontend_message_handler_callback: js_sys::Function) -> EditorHandle {
pub fn new(frontend_message_handler_callback: js_sys::Function) -> Self {
let editor_handle = EditorHandle { frontend_message_handler_callback }; 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() { 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"); log::error!("Attempted to initialize the editor handle more than once");
@@ -184,18 +187,10 @@ impl EditorHandle {
// ======================================================================== // ========================================================================
#[wasm_bindgen(js_name = initAfterFrontendReady)] #[wasm_bindgen(js_name = initAfterFrontendReady)]
pub fn init_after_frontend_ready(&self, platform: String) { pub fn init_after_frontend_ready(&self) {
#[cfg(feature = "native")] #[cfg(feature = "native")]
crate::native_communcation::initialize_native_communication(); 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(PortfolioMessage::Init);
// Poll node graph evaluation on `requestAnimationFrame` // Poll node graph evaluation on `requestAnimationFrame`