Desktop: Add "Disable UI Acceleration" to preferences (#3774)

* Deskltop: Add Disable UI Accelaration preference

* Fixup

* Fix typo

* Code review and update strings

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Timon 2026-02-19 01:54:01 +00:00 committed by GitHub
parent 6824f55929
commit 3f999bf231
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 310 additions and 133 deletions

View File

@ -15,6 +15,7 @@ use crate::cli::Cli;
use crate::consts::CEF_MESSAGE_LOOP_MAX_ITERATIONS; use crate::consts::CEF_MESSAGE_LOOP_MAX_ITERATIONS;
use crate::event::{AppEvent, AppEventScheduler}; use crate::event::{AppEvent, AppEventScheduler};
use crate::persist::PersistentData; use crate::persist::PersistentData;
use crate::preferences;
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}; use crate::wrapper::messages::{DesktopFrontendMessage, DesktopWrapperMessage, InputMessage, MouseKeys, MouseState};
@ -277,10 +278,10 @@ impl App {
self.persistent_data.set_document_order(ids); self.persistent_data.set_document_order(ids);
} }
DesktopFrontendMessage::PersistenceWritePreferences { preferences } => { DesktopFrontendMessage::PersistenceWritePreferences { preferences } => {
self.persistent_data.write_preferences(preferences); preferences::write(preferences);
} }
DesktopFrontendMessage::PersistenceLoadPreferences => { DesktopFrontendMessage::PersistenceLoadPreferences => {
let preferences = self.persistent_data.load_preferences(); let preferences = preferences::read();
let message = DesktopWrapperMessage::LoadPreferences { preferences }; let message = DesktopWrapperMessage::LoadPreferences { preferences };
responses.push(message); responses.push(message);
} }
@ -398,6 +399,9 @@ impl App {
window.show_all(); window.show_all();
} }
} }
DesktopFrontendMessage::Restart => {
self.exit(Some(ExitReason::Restart));
}
} }
} }
@ -663,5 +667,6 @@ impl ApplicationHandler for App {
pub(crate) enum ExitReason { pub(crate) enum ExitReason {
Shutdown, Shutdown,
Restart,
UiAccelerationFailure, UiAccelerationFailure,
} }

View File

@ -1,30 +1,28 @@
use crate::app::App;
use crate::cef::CefHandler;
use crate::cli::Cli;
use crate::consts::APP_LOCK_FILE_NAME;
use crate::event::CreateAppEventSchedulerEventLoopExt;
use clap::Parser; use clap::Parser;
use std::io::Write; use std::io::Write;
use std::process::exit; use std::process::exit;
use tracing_subscriber::EnvFilter; use tracing_subscriber::EnvFilter;
use winit::event_loop::EventLoop; use winit::event_loop::EventLoop;
pub(crate) mod consts; pub(crate) use graphite_desktop_wrapper as wrapper;
mod app; mod app;
mod cef; mod cef;
mod cli; mod cli;
mod dirs; mod dirs;
mod event; mod event;
mod gpu_context;
mod persist; mod persist;
mod preferences;
mod render; mod render;
mod window; mod window;
mod gpu_context; pub(crate) mod consts;
pub(crate) use graphite_desktop_wrapper as wrapper;
use app::App;
use cef::CefHandler;
use cli::Cli;
use event::CreateAppEventSchedulerEventLoopExt;
use crate::consts::APP_LOCK_FILE_NAME;
pub fn start() { pub fn start() {
tracing_subscriber::fmt().with_env_filter(EnvFilter::from_default_env()).init(); tracing_subscriber::fmt().with_env_filter(EnvFilter::from_default_env()).init();
@ -77,12 +75,13 @@ pub fn start() {
let (cef_view_info_sender, cef_view_info_receiver) = std::sync::mpsc::channel(); let (cef_view_info_sender, cef_view_info_receiver) = std::sync::mpsc::channel();
if cli.disable_ui_acceleration { let disable_ui_acceleration = preferences::read().disable_ui_acceleration || cli.disable_ui_acceleration;
if disable_ui_acceleration {
println!("UI acceleration is disabled"); println!("UI acceleration is disabled");
} }
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, disable_ui_acceleration) {
Ok(context) => { Ok(context) => {
tracing::info!("CEF initialized successfully"); tracing::info!("CEF initialized successfully");
context context
@ -109,16 +108,26 @@ pub fn start() {
let exit_reason = app.run(event_loop); let exit_reason = app.run(event_loop);
// If exiting due to a UI acceleration failure, update preferences to disable it for next launch
if matches!(exit_reason, app::ExitReason::UiAccelerationFailure) {
tracing::error!("Disabling UI acceleration");
preferences::modify(|prefs| {
prefs.disable_ui_acceleration = true;
});
}
// Explicitly drop the instance lock // Explicitly drop the instance lock
drop(lock); drop(lock);
match exit_reason { match exit_reason {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
app::ExitReason::UiAccelerationFailure => { app::ExitReason::Restart | app::ExitReason::UiAccelerationFailure => {
use std::os::unix::process::CommandExt; tracing::error!("Restarting application");
let mut command = std::process::Command::new(std::env::current_exe().unwrap());
tracing::error!("Restarting application without UI acceleration"); #[cfg(target_family = "unix")]
let _ = std::process::Command::new(std::env::current_exe().unwrap()).arg("--disable-ui-acceleration").exec(); let _ = std::os::unix::process::CommandExt::exec(&mut command);
#[cfg(not(target_family = "unix"))]
let _ = command.spawn();
tracing::error!("Failed to restart application"); tracing::error!("Failed to restart application");
} }
_ => {} _ => {}

View File

@ -1,4 +1,4 @@
use crate::wrapper::messages::{Document, DocumentId, Preferences}; use crate::wrapper::messages::{Document, DocumentId};
#[derive(Default, serde::Serialize, serde::Deserialize)] #[derive(Default, serde::Serialize, serde::Deserialize)]
pub(crate) struct PersistentData { pub(crate) struct PersistentData {
@ -72,22 +72,6 @@ impl PersistentData {
self.flush(); self.flush();
} }
pub(crate) fn write_preferences(&mut self, preferences: Preferences) {
let Ok(preferences) = ron::ser::to_string_pretty(&preferences, Default::default()) else {
tracing::error!("Failed to serialize preferences");
return;
};
std::fs::write(Self::preferences_file_path(), &preferences).unwrap_or_else(|e| {
tracing::error!("Failed to write preferences to disk: {e}");
});
}
pub(crate) fn load_preferences(&self) -> Option<Preferences> {
let data = std::fs::read_to_string(Self::preferences_file_path()).ok()?;
let preferences = ron::from_str(&data).ok()?;
Some(preferences)
}
fn flush(&self) { fn flush(&self) {
let data = match ron::ser::to_string_pretty(self, Default::default()) { let data = match ron::ser::to_string_pretty(self, Default::default()) {
Ok(d) => d, Ok(d) => d,
@ -129,12 +113,6 @@ impl PersistentData {
path.push(crate::consts::APP_STATE_FILE_NAME); path.push(crate::consts::APP_STATE_FILE_NAME);
path path
} }
fn preferences_file_path() -> std::path::PathBuf {
let mut path = crate::dirs::app_data_dir();
path.push(crate::consts::APP_PREFERENCES_FILE_NAME);
path
}
} }
#[derive(Default, serde::Serialize, serde::Deserialize)] #[derive(Default, serde::Serialize, serde::Deserialize)]

View File

@ -0,0 +1,33 @@
use graphite_desktop_wrapper::messages::Preferences;
pub(crate) fn write(preferences: Preferences) {
let Ok(preferences) = ron::ser::to_string_pretty(&preferences, Default::default()) else {
tracing::error!("Failed to serialize preferences");
return;
};
std::fs::write(file_path(), &preferences).unwrap_or_else(|e| {
tracing::error!("Failed to write preferences to disk: {e}");
});
}
pub(crate) fn read() -> Preferences {
let Ok(data) = std::fs::read_to_string(file_path()) else {
return Preferences::default();
};
let Ok(preferences) = ron::from_str(&data) else {
return Preferences::default();
};
preferences
}
pub(crate) fn modify(f: impl FnOnce(&mut Preferences)) {
let mut preferences = read();
f(&mut preferences);
write(preferences);
}
fn file_path() -> std::path::PathBuf {
let mut path = crate::dirs::app_data_dir();
path.push(crate::consts::APP_PREFERENCES_FILE_NAME);
path
}

View File

@ -335,7 +335,7 @@ impl RenderState {
}, },
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::TextureView(&overlays_texture_view.as_ref()), resource: wgpu::BindingResource::TextureView(overlays_texture_view.as_ref()),
}, },
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 2, binding: 2,

View File

@ -151,6 +151,9 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD
FrontendMessage::WindowShowAll => { FrontendMessage::WindowShowAll => {
dispatcher.respond(DesktopFrontendMessage::WindowShowAll); dispatcher.respond(DesktopFrontendMessage::WindowShowAll);
} }
FrontendMessage::WindowRestart => {
dispatcher.respond(DesktopFrontendMessage::Restart);
}
m => return Some(m), m => return Some(m),
} }
None None

View File

@ -1,25 +1,21 @@
use graph_craft::wasm_application_io::WasmApplicationIo; use graph_craft::wasm_application_io::WasmApplicationIo;
use graphite_editor::application::{Editor, Environment, Host, Platform}; use graphite_editor::application::{Editor, Environment, Host, Platform};
use graphite_editor::messages::prelude::{FrontendMessage, Message}; use graphite_editor::messages::prelude::{FrontendMessage, Message};
use message_dispatcher::DesktopWrapperMessageDispatcher;
use messages::{DesktopFrontendMessage, DesktopWrapperMessage};
pub use graphite_editor::consts::FILE_EXTENSION; pub use graphite_editor::consts::FILE_EXTENSION;
pub use wgpu_executor::TargetTexture; pub use wgpu_executor::TargetTexture;
pub use wgpu_executor::WgpuContext; pub use wgpu_executor::WgpuContext;
pub use wgpu_executor::WgpuContextBuilder; pub use wgpu_executor::WgpuContextBuilder;
pub use wgpu_executor::WgpuExecutor; pub use wgpu_executor::WgpuExecutor;
pub use wgpu_executor::WgpuFeatures; pub use wgpu_executor::WgpuFeatures;
pub mod messages;
use messages::{DesktopFrontendMessage, DesktopWrapperMessage};
mod message_dispatcher;
use message_dispatcher::DesktopWrapperMessageDispatcher;
mod handle_desktop_wrapper_message; mod handle_desktop_wrapper_message;
mod intercept_editor_message; mod intercept_editor_message;
mod intercept_frontend_message; mod intercept_frontend_message;
mod message_dispatcher;
pub mod messages;
pub(crate) mod utils; pub(crate) mod utils;
pub struct DesktopWrapper { pub struct DesktopWrapper {

View File

@ -74,6 +74,7 @@ pub enum DesktopFrontendMessage {
WindowHide, WindowHide,
WindowHideOthers, WindowHideOthers,
WindowShowAll, WindowShowAll,
Restart,
} }
pub enum DesktopWrapperMessage { pub enum DesktopWrapperMessage {
@ -113,7 +114,7 @@ pub enum DesktopWrapperMessage {
id: DocumentId, id: DocumentId,
}, },
LoadPreferences { LoadPreferences {
preferences: Option<Preferences>, preferences: Preferences,
}, },
MenuEvent { MenuEvent {
id: String, id: String,

View File

@ -5,6 +5,7 @@ use crate::messages::prelude::*;
pub enum AppWindowMessage { pub enum AppWindowMessage {
PointerLock, PointerLock,
PointerLockMove { x: f64, y: f64 }, PointerLockMove { x: f64, y: f64 },
Restart,
Close, Close,
Minimize, Minimize,
Maximize, Maximize,

View File

@ -40,6 +40,10 @@ impl MessageHandler<AppWindowMessage, ()> for AppWindowMessageHandler {
AppWindowMessage::ShowAll => { AppWindowMessage::ShowAll => {
responses.add(FrontendMessage::WindowShowAll); responses.add(FrontendMessage::WindowShowAll);
} }
AppWindowMessage::Restart => {
responses.add(PortfolioMessage::AutoSaveAllDocuments);
responses.add(FrontendMessage::WindowRestart);
}
} }
} }
advertise_actions!(AppWindowMessageDiscriminant; advertise_actions!(AppWindowMessageDiscriminant;

View File

@ -12,10 +12,12 @@ pub enum DialogMessage {
PreferencesDialog(PreferencesDialogMessage), PreferencesDialog(PreferencesDialogMessage),
// Messages // Messages
CloseAllDocumentsWithConfirmation, Dismiss,
CloseDialogAndThen { Close,
CloseAndThen {
followups: Vec<Message>, followups: Vec<Message>,
}, },
CloseAllDocumentsWithConfirmation,
DisplayDialogError { DisplayDialogError {
title: String, title: String,
description: String, description: String,
@ -35,4 +37,5 @@ pub enum DialogMessage {
}, },
RequestNewDocumentDialog, RequestNewDocumentDialog,
RequestPreferencesDialog, RequestPreferencesDialog,
RequestConfirmRestartDialog,
} }

View File

@ -1,6 +1,6 @@
use super::simple_dialogs::{self, AboutGraphiteDialog, DemoArtworkDialog, LicensesDialog}; use super::simple_dialogs::{self, AboutGraphiteDialog, DemoArtworkDialog, LicensesDialog};
use crate::application::GRAPHITE_GIT_COMMIT_DATE; use crate::application::GRAPHITE_GIT_COMMIT_DATE;
use crate::messages::dialog::simple_dialogs::LicensesThirdPartyDialog; use crate::messages::dialog::simple_dialogs::{ConfirmRestartDialog, LicensesThirdPartyDialog};
use crate::messages::frontend::utility_types::ExportBounds; use crate::messages::frontend::utility_types::ExportBounds;
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::prelude::*; use crate::messages::prelude::*;
@ -14,6 +14,7 @@ pub struct DialogMessageContext<'a> {
/// Stores the dialogs which require state. These are the ones that have their own message handlers, and are not the ones defined in `simple_dialogs`. /// Stores the dialogs which require state. These are the ones that have their own message handlers, and are not the ones defined in `simple_dialogs`.
#[derive(Debug, Default, Clone, ExtractField)] #[derive(Debug, Default, Clone, ExtractField)]
pub struct DialogMessageHandler { pub struct DialogMessageHandler {
on_dismiss: Option<Message>,
export_dialog: ExportDialogMessageHandler, export_dialog: ExportDialogMessageHandler,
new_document_dialog: NewDocumentDialogMessageHandler, new_document_dialog: NewDocumentDialogMessageHandler,
preferences_dialog: PreferencesDialogMessageHandler, preferences_dialog: PreferencesDialogMessageHandler,
@ -29,26 +30,38 @@ impl MessageHandler<DialogMessage, DialogMessageContext<'_>> for DialogMessageHa
DialogMessage::NewDocumentDialog(message) => self.new_document_dialog.process_message(message, responses, ()), DialogMessage::NewDocumentDialog(message) => self.new_document_dialog.process_message(message, responses, ()),
DialogMessage::PreferencesDialog(message) => self.preferences_dialog.process_message(message, responses, PreferencesDialogMessageContext { preferences }), DialogMessage::PreferencesDialog(message) => self.preferences_dialog.process_message(message, responses, PreferencesDialogMessageContext { preferences }),
DialogMessage::CloseAllDocumentsWithConfirmation => { DialogMessage::Dismiss => {
let dialog = simple_dialogs::CloseAllDocumentsDialog { if let Some(message) = self.on_dismiss.take() {
unsaved_document_names: portfolio.unsaved_document_names(), responses.add(message);
}; }
dialog.send_dialog_to_frontend(responses);
} }
DialogMessage::CloseDialogAndThen { followups } => { DialogMessage::Close => {
self.on_dismiss = None;
responses.add(FrontendMessage::DialogClose)
}
DialogMessage::CloseAndThen { followups } => {
for message in followups.into_iter() { for message in followups.into_iter() {
responses.add(message); responses.add(message);
} }
// This come after followups, so that the followups (which can cause the dialog to open) happen first, then we close it afterwards. // This come after followups, so that the followups (which can cause the dialog to open) happen first, then we close it afterwards.
// If it comes before, the dialog reopens (and appears to not close at all). // If it comes before, the dialog reopens (and appears to not close at all).
responses.add(FrontendMessage::DisplayDialogDismiss); responses.add(DialogMessage::Close);
}
DialogMessage::CloseAllDocumentsWithConfirmation => {
self.on_dismiss = Some(DialogMessage::Close.into());
let dialog = simple_dialogs::CloseAllDocumentsDialog {
unsaved_document_names: portfolio.unsaved_document_names(),
};
dialog.send_dialog_to_frontend(responses);
} }
DialogMessage::DisplayDialogError { title, description } => { DialogMessage::DisplayDialogError { title, description } => {
self.on_dismiss = None;
let dialog = simple_dialogs::ErrorDialog { title, description }; let dialog = simple_dialogs::ErrorDialog { title, description };
dialog.send_dialog_to_frontend(responses); dialog.send_dialog_to_frontend(responses);
} }
DialogMessage::RequestAboutGraphiteDialog => { DialogMessage::RequestAboutGraphiteDialog => {
self.on_dismiss = Some(DialogMessage::Close.into());
responses.add(FrontendMessage::TriggerAboutGraphiteLocalizedCommitDate { responses.add(FrontendMessage::TriggerAboutGraphiteLocalizedCommitDate {
commit_date: GRAPHITE_GIT_COMMIT_DATE.into(), commit_date: GRAPHITE_GIT_COMMIT_DATE.into(),
}); });
@ -57,6 +70,7 @@ impl MessageHandler<DialogMessage, DialogMessageContext<'_>> for DialogMessageHa
localized_commit_date, localized_commit_date,
localized_commit_year, localized_commit_year,
} => { } => {
self.on_dismiss = Some(DialogMessage::Close.into());
let dialog = AboutGraphiteDialog { let dialog = AboutGraphiteDialog {
localized_commit_date, localized_commit_date,
localized_commit_year, localized_commit_year,
@ -65,10 +79,12 @@ impl MessageHandler<DialogMessage, DialogMessageContext<'_>> for DialogMessageHa
dialog.send_dialog_to_frontend(responses); dialog.send_dialog_to_frontend(responses);
} }
DialogMessage::RequestDemoArtworkDialog => { DialogMessage::RequestDemoArtworkDialog => {
self.on_dismiss = Some(DialogMessage::Close.into());
let dialog = DemoArtworkDialog; let dialog = DemoArtworkDialog;
dialog.send_dialog_to_frontend(responses); dialog.send_dialog_to_frontend(responses);
} }
DialogMessage::RequestExportDialog => { DialogMessage::RequestExportDialog => {
self.on_dismiss = Some(DialogMessage::Close.into());
if let Some(document) = portfolio.active_document() { if let Some(document) = portfolio.active_document() {
let artboards = document let artboards = document
.metadata() .metadata()
@ -87,10 +103,10 @@ impl MessageHandler<DialogMessage, DialogMessageContext<'_>> for DialogMessageHa
self.export_dialog.artboards = artboards; self.export_dialog.artboards = artboards;
if let ExportBounds::Artboard(layer) = self.export_dialog.bounds { if let ExportBounds::Artboard(layer) = self.export_dialog.bounds
if !self.export_dialog.artboards.contains_key(&layer) { && !self.export_dialog.artboards.contains_key(&layer)
self.export_dialog.bounds = ExportBounds::AllArtwork; {
} self.export_dialog.bounds = ExportBounds::AllArtwork;
} }
self.export_dialog.has_selection = document.network_interface.selected_nodes().selected_layers(document.metadata()).next().is_some(); self.export_dialog.has_selection = document.network_interface.selected_nodes().selected_layers(document.metadata()).next().is_some();
@ -98,15 +114,17 @@ impl MessageHandler<DialogMessage, DialogMessageContext<'_>> for DialogMessageHa
} }
} }
DialogMessage::RequestLicensesDialogWithLocalizedCommitDate { localized_commit_year } => { DialogMessage::RequestLicensesDialogWithLocalizedCommitDate { localized_commit_year } => {
self.on_dismiss = Some(DialogMessage::Close.into());
let dialog = LicensesDialog { localized_commit_year }; let dialog = LicensesDialog { localized_commit_year };
dialog.send_dialog_to_frontend(responses); dialog.send_dialog_to_frontend(responses);
} }
DialogMessage::RequestLicensesThirdPartyDialogWithLicenseText { license_text } => { DialogMessage::RequestLicensesThirdPartyDialogWithLicenseText { license_text } => {
self.on_dismiss = Some(DialogMessage::Close.into());
let dialog = LicensesThirdPartyDialog { license_text }; let dialog = LicensesThirdPartyDialog { license_text };
dialog.send_dialog_to_frontend(responses); dialog.send_dialog_to_frontend(responses);
} }
DialogMessage::RequestNewDocumentDialog => { DialogMessage::RequestNewDocumentDialog => {
self.on_dismiss = Some(DialogMessage::Close.into());
self.new_document_dialog = NewDocumentDialogMessageHandler { self.new_document_dialog = NewDocumentDialogMessageHandler {
name: portfolio.generate_new_document_name(), name: portfolio.generate_new_document_name(),
infinite: false, infinite: false,
@ -115,9 +133,16 @@ impl MessageHandler<DialogMessage, DialogMessageContext<'_>> for DialogMessageHa
self.new_document_dialog.send_dialog_to_frontend(responses); self.new_document_dialog.send_dialog_to_frontend(responses);
} }
DialogMessage::RequestPreferencesDialog => { DialogMessage::RequestPreferencesDialog => {
self.preferences_dialog = PreferencesDialogMessageHandler {}; self.on_dismiss = Some(PreferencesDialogMessage::Confirm.into());
self.preferences_dialog.send_dialog_to_frontend(responses, preferences); self.preferences_dialog.send_dialog_to_frontend(responses, preferences);
} }
DialogMessage::RequestConfirmRestartDialog => {
self.on_dismiss = Some(DialogMessage::Close.into());
let dialog = ConfirmRestartDialog {
changed_settings: vec!["Disable UI Acceleration".into()],
};
dialog.send_dialog_to_frontend(responses);
}
} }
} }

View File

@ -63,7 +63,8 @@ impl MessageHandler<ExportDialogMessage, ExportDialogMessageContext<'_>> for Exp
self.send_dialog_to_frontend(responses); self.send_dialog_to_frontend(responses);
} }
advertise_actions! {ExportDialogUpdate;} advertise_actions!(ExportDialogUpdate;
);
} }
impl DialogLayoutHolder for ExportDialogMessageHandler { impl DialogLayoutHolder for ExportDialogMessageHandler {
@ -75,13 +76,13 @@ impl DialogLayoutHolder for ExportDialogMessageHandler {
TextButton::new("Export") TextButton::new("Export")
.emphasized(true) .emphasized(true)
.on_update(|_| { .on_update(|_| {
DialogMessage::CloseDialogAndThen { DialogMessage::CloseAndThen {
followups: vec![ExportDialogMessage::Submit.into()], followups: vec![ExportDialogMessage::Submit.into()],
} }
.into() .into()
}) })
.widget_instance(), .widget_instance(),
TextButton::new("Cancel").on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance(), TextButton::new("Cancel").on_update(|_| FrontendMessage::DialogClose.into()).widget_instance(),
]; ];
Layout(vec![LayoutGroup::Row { widgets }]) Layout(vec![LayoutGroup::Row { widgets }])

View File

@ -12,7 +12,7 @@ pub struct NewDocumentDialogMessageHandler {
} }
#[message_handler_data] #[message_handler_data]
impl<'a> MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHandler { impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHandler {
fn process_message(&mut self, message: NewDocumentDialogMessage, responses: &mut VecDeque<Message>, _: ()) { fn process_message(&mut self, message: NewDocumentDialogMessage, responses: &mut VecDeque<Message>, _: ()) {
match message { match message {
NewDocumentDialogMessage::Name { name } => self.name = name, NewDocumentDialogMessage::Name { name } => self.name = name,
@ -34,7 +34,11 @@ impl<'a> MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessa
responses.add(ViewportMessage::RepropagateUpdate); responses.add(ViewportMessage::RepropagateUpdate);
responses.add(DeferMessage::AfterNavigationReady { responses.add(DeferMessage::AfterNavigationReady {
messages: vec![DocumentMessage::ZoomCanvasToFitAll.into(), DocumentMessage::DeselectAllLayers.into()], messages: vec![
DocumentMessage::ZoomCanvasToFitAll.into(),
DocumentMessage::DeselectAllLayers.into(),
PortfolioMessage::AutoSaveActiveDocument.into(),
],
}); });
} }
@ -45,7 +49,8 @@ impl<'a> MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessa
self.send_dialog_to_frontend(responses); self.send_dialog_to_frontend(responses);
} }
advertise_actions! {NewDocumentDialogUpdate;} advertise_actions!(NewDocumentDialogUpdate;
);
} }
impl DialogLayoutHolder for NewDocumentDialogMessageHandler { impl DialogLayoutHolder for NewDocumentDialogMessageHandler {
@ -57,13 +62,13 @@ impl DialogLayoutHolder for NewDocumentDialogMessageHandler {
TextButton::new("OK") TextButton::new("OK")
.emphasized(true) .emphasized(true)
.on_update(|_| { .on_update(|_| {
DialogMessage::CloseDialogAndThen { DialogMessage::CloseAndThen {
followups: vec![NewDocumentDialogMessage::Submit.into()], followups: vec![NewDocumentDialogMessage::Submit.into()],
} }
.into() .into()
}) })
.widget_instance(), .widget_instance(),
TextButton::new("Cancel").on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance(), TextButton::new("Cancel").on_update(|_| FrontendMessage::DialogClose.into()).widget_instance(),
]; ];
Layout(vec![LayoutGroup::Row { widgets }]) Layout(vec![LayoutGroup::Row { widgets }])

View File

@ -3,5 +3,6 @@ use crate::messages::prelude::*;
#[impl_message(Message, DialogMessage, PreferencesDialog)] #[impl_message(Message, DialogMessage, PreferencesDialog)]
#[derive(Eq, PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)] #[derive(Eq, PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum PreferencesDialogMessage { pub enum PreferencesDialogMessage {
MayRequireRestart,
Confirm, Confirm,
} }

View File

@ -11,21 +11,34 @@ pub struct PreferencesDialogMessageContext<'a> {
/// A dialog to allow users to customize Graphite editor options /// A dialog to allow users to customize Graphite editor options
#[derive(Debug, Clone, Default, ExtractField)] #[derive(Debug, Clone, Default, ExtractField)]
pub struct PreferencesDialogMessageHandler {} pub struct PreferencesDialogMessageHandler {
unmodified_preferences: Option<PreferencesMessageHandler>,
}
#[message_handler_data] #[message_handler_data]
impl MessageHandler<PreferencesDialogMessage, PreferencesDialogMessageContext<'_>> for PreferencesDialogMessageHandler { impl MessageHandler<PreferencesDialogMessage, PreferencesDialogMessageContext<'_>> for PreferencesDialogMessageHandler {
fn process_message(&mut self, message: PreferencesDialogMessage, responses: &mut VecDeque<Message>, context: PreferencesDialogMessageContext) { fn process_message(&mut self, message: PreferencesDialogMessage, responses: &mut VecDeque<Message>, context: PreferencesDialogMessageContext) {
let PreferencesDialogMessageContext { preferences } = context; let PreferencesDialogMessageContext { preferences } = context;
match message { match message {
PreferencesDialogMessage::Confirm => {} PreferencesDialogMessage::MayRequireRestart => {
if self.unmodified_preferences.is_none() {
self.unmodified_preferences = Some(preferences.clone());
}
}
PreferencesDialogMessage::Confirm => {
if let Some(unmodified_preferences) = &self.unmodified_preferences
&& unmodified_preferences.needs_restart(preferences)
{
responses.add(DialogMessage::RequestConfirmRestartDialog);
} else {
responses.add(DialogMessage::Close);
}
}
} }
self.send_dialog_to_frontend(responses, preferences);
} }
advertise_actions! {PreferencesDialogUpdate;} advertise_actions!(PreferencesDialogUpdate;
);
} }
// This doesn't actually implement the `DialogLayoutHolder` trait like the other dialog message handlers. // This doesn't actually implement the `DialogLayoutHolder` trait like the other dialog message handlers.
@ -260,7 +273,7 @@ impl PreferencesDialogMessageHandler {
let brush_tool_description = " let brush_tool_description = "
Enable the Brush tool to support basic raster-based layer painting.\n\ Enable the Brush tool to support basic raster-based layer painting.\n\
\n\ \n\
This legacy experimental tool has performance and quality limitations and is slated for replacement in future versions of Graphite that will focus on raster graphics editing.\n\ This legacy experimental tool has performance and quality limitations and is slated for replacement in future versions of Graphite that will have a renewed focus on raster graphics editing.\n\
\n\ \n\
Content created with the Brush tool may not be compatible with future versions of Graphite. Content created with the Brush tool may not be compatible with future versions of Graphite.
" "
@ -284,6 +297,48 @@ impl PreferencesDialogMessageHandler {
rows.extend_from_slice(&[header, node_graph_wires_label, graph_wire_style, use_vello, brush_tool]); rows.extend_from_slice(&[header, node_graph_wires_label, graph_wire_style, use_vello, brush_tool]);
} }
// =============
// COMPATIBILITY
// =============
#[cfg(not(target_family = "wasm"))]
{
let header = vec![TextLabel::new("Compatibility").italic(true).widget_instance()];
let ui_acceleration_description = "
Use the CPU to draw the Graphite user interface (areas outside of the canvas) instead of the GPU. This does not affect the rendering of artwork in the canvas, which remains hardware accelerated.\n\
\n\
Disabling UI acceleration may slightly degrade performance, so this should be used as a workaround only if issues are observed with displaying the UI. This setting may become enabled automatically if Graphite launches, detects that it cannot draw the UI normally, and restarts in compatibility mode.
"
.trim();
let checkbox_id = CheckboxId::new();
let ui_acceleration = vec![
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
CheckboxInput::new(preferences.disable_ui_acceleration)
.tooltip_label("Disable UI Acceleration")
.tooltip_description(ui_acceleration_description)
.on_update(|number_input: &CheckboxInput| Message::Batched {
messages: Box::new([
PreferencesDialogMessage::MayRequireRestart.into(),
PreferencesMessage::DisableUIAcceleration {
disable_ui_acceleration: number_input.checked,
}
.into(),
]),
})
.for_label(checkbox_id)
.widget_instance(),
TextLabel::new("Disable UI Acceleration")
.tooltip_label("Disable UI Acceleration")
.tooltip_description(ui_acceleration_description)
.for_checkbox(checkbox_id)
.widget_instance(),
];
rows.extend_from_slice(&[header, ui_acceleration]);
}
Layout(rows.into_iter().map(|r| LayoutGroup::Row { widgets: r }).collect()) Layout(rows.into_iter().map(|r| LayoutGroup::Row { widgets: r }).collect())
} }
@ -307,15 +362,7 @@ impl PreferencesDialogMessageHandler {
fn layout_buttons(&self) -> Layout { fn layout_buttons(&self) -> Layout {
let widgets = vec![ let widgets = vec![
TextButton::new("OK") TextButton::new("OK").emphasized(true).on_update(|_| PreferencesDialogMessage::Confirm.into()).widget_instance(),
.emphasized(true)
.on_update(|_| {
DialogMessage::CloseDialogAndThen {
followups: vec![PreferencesDialogMessage::Confirm.into()],
}
.into()
})
.widget_instance(),
TextButton::new("Reset to Defaults").on_update(|_| PreferencesMessage::ResetToDefaults.into()).widget_instance(), TextButton::new("Reset to Defaults").on_update(|_| PreferencesMessage::ResetToDefaults.into()).widget_instance(),
]; ];

View File

@ -13,7 +13,7 @@ impl DialogLayoutHolder for AboutGraphiteDialog {
const TITLE: &'static str = "About Graphite"; const TITLE: &'static str = "About Graphite";
fn layout_buttons(&self) -> Layout { fn layout_buttons(&self) -> Layout {
let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance()]; let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DialogClose.into()).widget_instance()];
Layout(vec![LayoutGroup::Row { widgets }]) Layout(vec![LayoutGroup::Row { widgets }])
} }

View File

@ -15,13 +15,13 @@ impl DialogLayoutHolder for CloseAllDocumentsDialog {
TextButton::new("Discard All") TextButton::new("Discard All")
.emphasized(true) .emphasized(true)
.on_update(|_| { .on_update(|_| {
DialogMessage::CloseDialogAndThen { DialogMessage::CloseAndThen {
followups: vec![PortfolioMessage::CloseAllDocuments.into()], followups: vec![PortfolioMessage::CloseAllDocuments.into()],
} }
.into() .into()
}) })
.widget_instance(), .widget_instance(),
TextButton::new("Cancel").on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance(), TextButton::new("Cancel").on_update(|_| FrontendMessage::DialogClose.into()).widget_instance(),
]; ];
Layout(vec![LayoutGroup::Row { widgets }]) Layout(vec![LayoutGroup::Row { widgets }])

View File

@ -18,7 +18,7 @@ impl DialogLayoutHolder for CloseDocumentDialog {
TextButton::new("Save") TextButton::new("Save")
.emphasized(true) .emphasized(true)
.on_update(|_| { .on_update(|_| {
DialogMessage::CloseDialogAndThen { DialogMessage::CloseAndThen {
followups: vec![DocumentMessage::SaveDocument.into()], followups: vec![DocumentMessage::SaveDocument.into()],
} }
.into() .into()
@ -26,13 +26,13 @@ impl DialogLayoutHolder for CloseDocumentDialog {
.widget_instance(), .widget_instance(),
TextButton::new("Discard") TextButton::new("Discard")
.on_update(move |_| { .on_update(move |_| {
DialogMessage::CloseDialogAndThen { DialogMessage::CloseAndThen {
followups: vec![EventMessage::ToolAbort.into(), PortfolioMessage::CloseDocument { document_id }.into()], followups: vec![EventMessage::ToolAbort.into(), PortfolioMessage::CloseDocument { document_id }.into()],
} }
.into() .into()
}) })
.widget_instance(), .widget_instance(),
TextButton::new("Cancel").on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance(), TextButton::new("Cancel").on_update(|_| FrontendMessage::DialogClose.into()).widget_instance(),
]; ];
Layout(vec![LayoutGroup::Row { widgets }]) Layout(vec![LayoutGroup::Row { widgets }])

View File

@ -0,0 +1,59 @@
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::prelude::*;
/// A dialog for confirming the restart of the application when changing a preference that requires a restart to take effect.
pub struct ConfirmRestartDialog {
pub changed_settings: Vec<String>,
}
impl DialogLayoutHolder for ConfirmRestartDialog {
const ICON: &'static str = "Warning";
const TITLE: &'static str = "Restart Required";
fn layout_buttons(&self) -> Layout {
let widgets = vec![
TextButton::new("Restart Now")
.emphasized(true)
.on_update(|_| {
DialogMessage::CloseAndThen {
followups: vec![AppWindowMessage::Restart.into()],
}
.into()
})
.widget_instance(),
TextButton::new("Later").on_update(|_| FrontendMessage::DialogClose.into()).widget_instance(),
];
Layout(vec![LayoutGroup::Row { widgets }])
}
}
impl LayoutHolder for ConfirmRestartDialog {
fn layout(&self) -> Layout {
let changed_settings = "".to_string() + &self.changed_settings.join("\n");
Layout(vec![
LayoutGroup::Row {
widgets: vec![TextLabel::new("Restart to apply changes?").bold(true).multiline(true).widget_instance()],
},
LayoutGroup::Row {
widgets: vec![
TextLabel::new(
format!(
"
Settings that only take effect on next launch:\n\
{changed_settings}\n\
\n\
This only takes a few seconds. Open documents,\n\
even unsaved ones, will be automatically restored.
"
)
.trim(),
)
.multiline(true)
.widget_instance(),
],
},
])
}
}

View File

@ -20,7 +20,7 @@ impl DialogLayoutHolder for DemoArtworkDialog {
const TITLE: &'static str = "Demo Artwork"; const TITLE: &'static str = "Demo Artwork";
fn layout_buttons(&self) -> Layout { fn layout_buttons(&self) -> Layout {
let widgets = vec![TextButton::new("Close").emphasized(true).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance()]; let widgets = vec![TextButton::new("Close").emphasized(true).on_update(|_| FrontendMessage::DialogClose.into()).widget_instance()];
Layout(vec![LayoutGroup::Row { widgets }]) Layout(vec![LayoutGroup::Row { widgets }])
} }
@ -32,7 +32,7 @@ impl LayoutHolder for DemoArtworkDialog {
.chunks(4) .chunks(4)
.flat_map(|chunk| { .flat_map(|chunk| {
fn make_dialog(name: &str, filename: &str) -> Message { fn make_dialog(name: &str, filename: &str) -> Message {
DialogMessage::CloseDialogAndThen { DialogMessage::CloseAndThen {
followups: vec![ followups: vec![
FrontendMessage::TriggerFetchAndOpenDocument { FrontendMessage::TriggerFetchAndOpenDocument {
name: name.to_string(), name: name.to_string(),

View File

@ -12,7 +12,7 @@ impl DialogLayoutHolder for ErrorDialog {
const TITLE: &'static str = "Error"; const TITLE: &'static str = "Error";
fn layout_buttons(&self) -> Layout { fn layout_buttons(&self) -> Layout {
let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance()]; let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DialogClose.into()).widget_instance()];
Layout(vec![LayoutGroup::Row { widgets }]) Layout(vec![LayoutGroup::Row { widgets }])
} }

View File

@ -10,7 +10,7 @@ impl DialogLayoutHolder for LicensesDialog {
const TITLE: &'static str = "Licenses"; const TITLE: &'static str = "Licenses";
fn layout_buttons(&self) -> Layout { fn layout_buttons(&self) -> Layout {
let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance()]; let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DialogClose.into()).widget_instance()];
Layout(vec![LayoutGroup::Row { widgets }]) Layout(vec![LayoutGroup::Row { widgets }])
} }

View File

@ -10,7 +10,7 @@ impl DialogLayoutHolder for LicensesThirdPartyDialog {
const TITLE: &'static str = "Third-Party Software License Notices"; const TITLE: &'static str = "Third-Party Software License Notices";
fn layout_buttons(&self) -> Layout { fn layout_buttons(&self) -> Layout {
let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance()]; let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DialogClose.into()).widget_instance()];
Layout(vec![LayoutGroup::Row { widgets }]) Layout(vec![LayoutGroup::Row { widgets }])
} }

View File

@ -1,6 +1,7 @@
mod about_graphite_dialog; mod about_graphite_dialog;
mod close_all_documents_dialog; mod close_all_documents_dialog;
mod close_document_dialog; mod close_document_dialog;
mod confirm_restart_dialog;
mod demo_artwork_dialog; mod demo_artwork_dialog;
mod error_dialog; mod error_dialog;
mod licenses_dialog; mod licenses_dialog;
@ -9,6 +10,7 @@ mod licenses_third_party_dialog;
pub use about_graphite_dialog::AboutGraphiteDialog; pub use about_graphite_dialog::AboutGraphiteDialog;
pub use close_all_documents_dialog::CloseAllDocumentsDialog; pub use close_all_documents_dialog::CloseAllDocumentsDialog;
pub use close_document_dialog::CloseDocumentDialog; pub use close_document_dialog::CloseDocumentDialog;
pub use confirm_restart_dialog::ConfirmRestartDialog;
pub use demo_artwork_dialog::ARTWORK; pub use demo_artwork_dialog::ARTWORK;
pub use demo_artwork_dialog::DemoArtworkDialog; pub use demo_artwork_dialog::DemoArtworkDialog;
pub use error_dialog::ErrorDialog; pub use error_dialog::ErrorDialog;

View File

@ -28,7 +28,7 @@ pub enum FrontendMessage {
title: String, title: String,
icon: String, icon: String,
}, },
DisplayDialogDismiss, DialogClose,
DisplayDialogPanic { DisplayDialogPanic {
#[serde(rename = "panicInfo")] #[serde(rename = "panicInfo")]
panic_info: String, panic_info: String,
@ -376,4 +376,5 @@ pub enum FrontendMessage {
WindowHide, WindowHide,
WindowHideOthers, WindowHideOthers,
WindowShowAll, WindowShowAll,
WindowRestart,
} }

View File

@ -6,7 +6,7 @@ use crate::messages::prelude::*;
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum PreferencesMessage { pub enum PreferencesMessage {
// Management messages // Management messages
Load { preferences: Option<PreferencesMessageHandler> }, Load { preferences: PreferencesMessageHandler },
ResetToDefaults, ResetToDefaults,
// Per-preference messages // Per-preference messages
@ -17,4 +17,5 @@ pub enum PreferencesMessage {
GraphWireStyle { style: GraphWireStyle }, GraphWireStyle { style: GraphWireStyle },
ViewportZoomWheelRate { rate: f64 }, ViewportZoomWheelRate { rate: f64 },
UIScale { scale: f64 }, UIScale { scale: f64 },
DisableUIAcceleration { disable_ui_acceleration: bool },
} }

View File

@ -21,9 +21,14 @@ pub struct PreferencesMessageHandler {
pub graph_wire_style: GraphWireStyle, pub graph_wire_style: GraphWireStyle,
pub viewport_zoom_wheel_rate: f64, pub viewport_zoom_wheel_rate: f64,
pub ui_scale: f64, pub ui_scale: f64,
pub disable_ui_acceleration: bool,
} }
impl PreferencesMessageHandler { impl PreferencesMessageHandler {
pub fn needs_restart(&self, other: &Self) -> bool {
self.disable_ui_acceleration != other.disable_ui_acceleration
}
pub fn get_selection_mode(&self) -> SelectionMode { pub fn get_selection_mode(&self) -> SelectionMode {
self.selection_mode self.selection_mode
} }
@ -49,6 +54,7 @@ impl Default for PreferencesMessageHandler {
graph_wire_style: GraphWireStyle::default(), graph_wire_style: GraphWireStyle::default(),
viewport_zoom_wheel_rate: VIEWPORT_ZOOM_WHEEL_RATE, viewport_zoom_wheel_rate: VIEWPORT_ZOOM_WHEEL_RATE,
ui_scale: UI_SCALE_DEFAULT, ui_scale: UI_SCALE_DEFAULT,
disable_ui_acceleration: false,
} }
} }
} }
@ -61,9 +67,7 @@ impl MessageHandler<PreferencesMessage, PreferencesMessageContext<'_>> for Prefe
match message { match message {
// Management messages // Management messages
PreferencesMessage::Load { preferences } => { PreferencesMessage::Load { preferences } => {
if let Some(preferences) = preferences { *self = preferences;
*self = preferences;
}
responses.add(PortfolioMessage::EditorPreferences); responses.add(PortfolioMessage::EditorPreferences);
responses.add(PortfolioMessage::UpdateVelloPreference); responses.add(PortfolioMessage::UpdateVelloPreference);
@ -73,10 +77,8 @@ impl MessageHandler<PreferencesMessage, PreferencesMessageContext<'_>> for Prefe
responses.add(FrontendMessage::UpdateUIScale { scale: self.ui_scale }); responses.add(FrontendMessage::UpdateUIScale { scale: self.ui_scale });
} }
PreferencesMessage::ResetToDefaults => { PreferencesMessage::ResetToDefaults => {
refresh_dialog(responses); responses.add(PreferencesMessage::Load { preferences: Self::default() });
responses.add(KeyMappingMessage::ModifyMapping { mapping: MappingVariant::Default }); responses.add(DialogMessage::RequestPreferencesDialog);
*self = Self::default()
} }
// Per-preference messages // Per-preference messages
@ -115,6 +117,9 @@ impl MessageHandler<PreferencesMessage, PreferencesMessageContext<'_>> for Prefe
self.ui_scale = scale; self.ui_scale = scale;
responses.add(FrontendMessage::UpdateUIScale { scale: self.ui_scale }); responses.add(FrontendMessage::UpdateUIScale { scale: self.ui_scale });
} }
PreferencesMessage::DisableUIAcceleration { disable_ui_acceleration } => {
self.disable_ui_acceleration = disable_ui_acceleration;
}
} }
responses.add(FrontendMessage::TriggerSavePreferences { preferences: self.clone() }); responses.add(FrontendMessage::TriggerSavePreferences { preferences: self.clone() });
@ -123,9 +128,3 @@ impl MessageHandler<PreferencesMessage, PreferencesMessageContext<'_>> for Prefe
advertise_actions!(PreferencesMessageDiscriminant; advertise_actions!(PreferencesMessageDiscriminant;
); );
} }
fn refresh_dialog(responses: &mut VecDeque<Message>) {
responses.add(DialogMessage::CloseDialogAndThen {
followups: vec![DialogMessage::RequestPreferencesDialog.into()],
});
}

View File

@ -140,7 +140,7 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
} }
if (get(dialog).visible && key === "Escape") { if (get(dialog).visible && key === "Escape") {
dialog.dismissDialog(); editor.handle.onDialogDismiss();
} }
} }
@ -185,7 +185,7 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
const inTextInput = target === textToolInteractiveInputElement; const inTextInput = target === textToolInteractiveInputElement;
if (get(dialog).visible && !inDialog) { if (get(dialog).visible && !inDialog) {
dialog.dismissDialog(); editor.handle.onDialogDismiss();
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
} }

View File

@ -884,7 +884,7 @@ export class LayerPanelEntry {
clippable!: boolean; clippable!: boolean;
} }
export class DisplayDialogDismiss extends JsMessage {} export class DialogClose extends JsMessage {}
export class Font { export class Font {
fontFamily!: string; fontFamily!: string;
@ -1671,7 +1671,7 @@ type MessageMaker = typeof JsMessage | JSMessageFactory;
export const messageMakers: Record<string, MessageMaker> = { export const messageMakers: Record<string, MessageMaker> = {
ClearAllNodeGraphWires, ClearAllNodeGraphWires,
DisplayDialog, DisplayDialog,
DisplayDialogDismiss, DialogClose,
DisplayDialogPanic, DisplayDialogPanic,
DisplayEditableTextbox, DisplayEditableTextbox,
DisplayEditableTextboxTransform, DisplayEditableTextboxTransform,

View File

@ -2,7 +2,7 @@ import { writable } from "svelte/store";
import { type Editor } from "@graphite/editor"; import { type Editor } from "@graphite/editor";
import { type IconName } from "@graphite/icons"; import { type IconName } from "@graphite/icons";
import { DisplayDialog, DisplayDialogDismiss, UpdateDialogButtons, UpdateDialogColumn1, UpdateDialogColumn2, patchLayout, TriggerDisplayThirdPartyLicensesDialog } from "@graphite/messages"; import { DisplayDialog, DialogClose, UpdateDialogButtons, UpdateDialogColumn1, UpdateDialogColumn2, patchLayout, TriggerDisplayThirdPartyLicensesDialog } from "@graphite/messages";
import type { Layout } from "@graphite/messages"; import type { Layout } from "@graphite/messages";
export function createDialogState(editor: Editor) { export function createDialogState(editor: Editor) {
@ -76,7 +76,7 @@ export function createDialogState(editor: Editor) {
return state; return state;
}); });
}); });
editor.subscriptions.subscribeJsMessage(DisplayDialogDismiss, dismissDialog); editor.subscriptions.subscribeJsMessage(DialogClose, dismissDialog);
editor.subscriptions.subscribeJsMessage(TriggerDisplayThirdPartyLicensesDialog, async () => { editor.subscriptions.subscribeJsMessage(TriggerDisplayThirdPartyLicensesDialog, async () => {
const BACKUP_URL = "https://editor.graphite.art/third-party-licenses.txt"; const BACKUP_URL = "https://editor.graphite.art/third-party-licenses.txt";

View File

@ -384,18 +384,14 @@ impl EditorHandle {
#[wasm_bindgen(js_name = loadPreferences)] #[wasm_bindgen(js_name = loadPreferences)]
pub fn load_preferences(&self, preferences: Option<String>) { pub fn load_preferences(&self, preferences: Option<String>) {
let preferences = if let Some(preferences) = preferences { if let Some(preferences) = preferences {
let Ok(preferences) = serde_json::from_str(&preferences) else { let Ok(preferences) = serde_json::from_str(&preferences) else {
log::error!("Failed to deserialize preferences"); log::error!("Failed to deserialize preferences");
return; return;
}; };
Some(preferences) let message = PreferencesMessage::Load { preferences };
} else { self.dispatch(message);
None }
};
let message = PreferencesMessage::Load { preferences };
self.dispatch(message);
} }
#[wasm_bindgen(js_name = selectDocument)] #[wasm_bindgen(js_name = selectDocument)]
@ -602,6 +598,13 @@ impl EditorHandle {
Ok(()) Ok(())
} }
/// Dialog got dismissed
#[wasm_bindgen(js_name = onDialogDismiss)]
pub fn on_dialog_dismiss(&self) {
let message = DialogMessage::Dismiss;
self.dispatch(message);
}
/// A text box was changed /// A text box was changed
#[wasm_bindgen(js_name = updateBounds)] #[wasm_bindgen(js_name = updateBounds)]
pub fn update_bounds(&self, new_text: String) -> Result<(), JsValue> { pub fn update_bounds(&self, new_text: String) -> Result<(), JsValue> {