Replace globals with editor environment (#3656)
Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
07fbcd489c
commit
95d3556204
|
|
@ -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 {
|
||||
|
|
@ -78,6 +79,8 @@ impl App {
|
|||
let mut persistent_data = PersistentData::default();
|
||||
persistent_data.load_from_disk();
|
||||
|
||||
let desktop_wrapper = DesktopWrapper::new(rand::rng().random());
|
||||
|
||||
Self {
|
||||
render_state: None,
|
||||
wgpu_context,
|
||||
|
|
@ -91,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()),
|
||||
|
|
@ -478,14 +481,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) {
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ 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());
|
||||
|
|
@ -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_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.");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,8 +27,18 @@ pub struct DesktopWrapper {
|
|||
}
|
||||
|
||||
impl DesktopWrapper {
|
||||
pub fn new() -> Self {
|
||||
Self { editor: Editor::new() }
|
||||
pub fn new(uuid_random_seed: u64) -> Self {
|
||||
#[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) {
|
||||
|
|
@ -51,12 +61,6 @@ impl DesktopWrapper {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for DesktopWrapper {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub enum NodeGraphExecutionResult {
|
||||
HasRun(Option<wgpu::Texture>),
|
||||
NotRun,
|
||||
|
|
|
|||
|
|
@ -109,7 +109,6 @@ pub enum DesktopWrapperMessage {
|
|||
content: Vec<u8>,
|
||||
},
|
||||
PollNodeGraphEvaluation,
|
||||
UpdatePlatform(Platform),
|
||||
UpdateMaximized {
|
||||
maximized: bool,
|
||||
},
|
||||
|
|
@ -163,12 +162,6 @@ pub enum SaveFileDialogContext {
|
|||
File { content: Vec<u8> },
|
||||
}
|
||||
|
||||
pub enum Platform {
|
||||
Windows,
|
||||
Mac,
|
||||
Linux,
|
||||
}
|
||||
|
||||
pub enum MenuItem {
|
||||
Action {
|
||||
id: String,
|
||||
|
|
|
|||
|
|
@ -1,24 +1,31 @@
|
|||
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 {
|
||||
/// Construct the editor.
|
||||
/// Remember to provide a random seed with `editor::set_uuid_seed(seed)` before any editors can be used.
|
||||
pub fn new() -> 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);
|
||||
|
||||
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<T: Into<Message>>(&mut self, message: T) -> Vec<FrontendMessage> {
|
||||
|
|
@ -32,9 +39,53 @@ impl Editor {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for Editor {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
static ENVIRONMENT: OnceLock<Environment> = 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)]
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,12 +23,11 @@ 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,
|
||||
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,
|
||||
|
|
@ -192,17 +191,11 @@ 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,
|
||||
viewport: &self.message_handlers.viewport_message_handler,
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
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 },
|
||||
PointerLock,
|
||||
PointerLockMove { x: f64, y: f64 },
|
||||
Close,
|
||||
|
|
|
|||
|
|
@ -1,20 +1,15 @@
|
|||
use crate::messages::app_window::AppWindowMessage;
|
||||
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};
|
||||
|
||||
#[derive(Debug, Clone, Default, ExtractField)]
|
||||
pub struct AppWindowMessageHandler {
|
||||
platform: AppWindowPlatform,
|
||||
}
|
||||
pub struct AppWindowMessageHandler {}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<AppWindowMessage, ()> for AppWindowMessageHandler {
|
||||
fn process_message(&mut self, message: AppWindowMessage, responses: &mut std::collections::VecDeque<Message>, _: ()) {
|
||||
match message {
|
||||
AppWindowMessage::UpdatePlatform { platform } => {
|
||||
self.platform = platform;
|
||||
responses.add(FrontendMessage::UpdatePlatform { platform: self.platform });
|
||||
}
|
||||
AppWindowMessage::PointerLock => {
|
||||
responses.add(FrontendMessage::WindowPointerLock);
|
||||
}
|
||||
|
|
@ -66,3 +61,14 @@ pub enum AppWindowPlatform {
|
|||
Mac,
|
||||
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;
|
||||
);
|
||||
}
|
||||
|
|
@ -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::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)]
|
||||
|
|
@ -48,11 +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 keyboard_layout = || GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout();
|
||||
let platform_accel_key = match keyboard_layout() {
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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.) }),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::messages::portfolio::utility_types::KeyboardPlatformLayout;
|
||||
use crate::application::Editor;
|
||||
use crate::messages::prelude::*;
|
||||
use bitflags::bitflags;
|
||||
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>());
|
||||
}
|
||||
|
||||
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 {
|
||||
// Writing system keys
|
||||
|
|
@ -275,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 => "⌫",
|
||||
|
||||
|
|
@ -317,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",
|
||||
|
|
@ -356,10 +356,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
|
||||
|
|
|
|||
|
|
@ -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<InputPreprocessorMessage, InputPreprocessorMessageContext<'a>> for InputPreprocessorMessageHandler {
|
||||
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 {
|
||||
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<InputPreprocessorMessage, InputPreprocessorMessageContex
|
|||
}
|
||||
}
|
||||
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);
|
||||
if !key_repeat {
|
||||
responses.add(InputMapperMessage::KeyDownNoRepeat(key));
|
||||
|
|
@ -51,7 +50,7 @@ impl<'a> MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContex
|
|||
responses.add(InputMapperMessage::KeyDown(key));
|
||||
}
|
||||
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);
|
||||
if !key_repeat {
|
||||
responses.add(InputMapperMessage::KeyUpNoRepeat(key));
|
||||
|
|
@ -59,7 +58,7 @@ impl<'a> MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContex
|
|||
responses.add(InputMapperMessage::KeyUp(key));
|
||||
}
|
||||
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);
|
||||
self.mouse.position = mouse_state.position;
|
||||
|
|
@ -67,7 +66,7 @@ impl<'a> MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContex
|
|||
self.translate_mouse_event(mouse_state, true, responses);
|
||||
}
|
||||
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);
|
||||
self.mouse.position = mouse_state.position;
|
||||
|
|
@ -78,7 +77,7 @@ impl<'a> MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContex
|
|||
self.translate_mouse_event(mouse_state, false, responses);
|
||||
}
|
||||
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);
|
||||
self.mouse.position = mouse_state.position;
|
||||
|
|
@ -86,7 +85,7 @@ impl<'a> MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContex
|
|||
self.translate_mouse_event(mouse_state, false, responses);
|
||||
}
|
||||
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);
|
||||
self.mouse.position = mouse_state.position;
|
||||
|
|
@ -99,7 +98,7 @@ impl<'a> MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContex
|
|||
self.frame_time.advance_timestamp(Duration::from_millis(timestamp));
|
||||
}
|
||||
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);
|
||||
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<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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
|
@ -188,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]
|
||||
|
|
@ -206,7 +204,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 +223,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 +242,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 +263,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 +283,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);
|
||||
|
|
|
|||
|
|
@ -22,8 +22,6 @@ pub enum Message {
|
|||
#[child]
|
||||
Frontend(FrontendMessage),
|
||||
#[child]
|
||||
Globals(GlobalsMessage),
|
||||
#[child]
|
||||
InputPreprocessor(InputPreprocessorMessage),
|
||||
#[child]
|
||||
KeyMapping(KeyMappingMessage),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<NavigationMessage, NavigationMessageContext<'_>> 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()
|
||||
|
|
|
|||
|
|
@ -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<PortfolioMessage, PortfolioMessageContext<'_>> 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<PortfolioMessage, PortfolioMessageContext<'_>> 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 } => {
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
pub enum PanelType {
|
||||
#[default]
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(() => {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
// 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 { 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;
|
||||
|
|
@ -14,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();
|
||||
|
|
@ -28,28 +32,27 @@ 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.
|
||||
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
|
||||
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)
|
||||
const handle: EditorHandle = new EditorHandle((messageType: JsMessageType, messageData: Record<string, unknown>) => {
|
||||
// This callback is called by WASM when a FrontendMessage is received from the WASM wrapper `EditorHandle`
|
||||
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`
|
||||
// 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();
|
||||
// Subscriptions: allows subscribing to messages in JS that are sent from the Wasm backend
|
||||
const subscriptions = createSubscriptionRouter();
|
||||
|
||||
// Check if the URL hash fragment has any demo artwork to be loaded
|
||||
(async () => {
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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<string, OperatingSystem> = {
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -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,14 +43,7 @@ fn calculate_hash<T: std::hash::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.
|
||||
/// Provides a handle to access the raw Wasm memory.
|
||||
#[wasm_bindgen(js_name = wasmMemory)]
|
||||
pub fn wasm_memory() -> JsValue {
|
||||
wasm_bindgen::memory()
|
||||
|
|
@ -89,9 +82,20 @@ impl EditorHandle {
|
|||
#[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();
|
||||
pub fn create(platform: String, uuid_random_seed: u64, frontend_message_handler_callback: js_sys::Function) -> EditorHandle {
|
||||
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 };
|
||||
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");
|
||||
|
|
@ -103,8 +107,7 @@ impl EditorHandle {
|
|||
}
|
||||
|
||||
#[cfg(feature = "native")]
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(frontend_message_handler_callback: js_sys::Function) -> Self {
|
||||
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");
|
||||
|
|
@ -184,18 +187,10 @@ 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);
|
||||
|
||||
// Poll node graph evaluation on `requestAnimationFrame`
|
||||
|
|
|
|||
Loading…
Reference in New Issue