From 44e39ada35870967057e3b9d0f34f686746ade5c Mon Sep 17 00:00:00 2001 From: Timon Date: Mon, 16 Feb 2026 23:53:17 +0000 Subject: [PATCH 1/4] Deskltop: Add Disable UI Accelaration preference --- desktop/src/app.rs | 9 ++- desktop/src/lib.rs | 45 ++++++----- desktop/src/persist.rs | 24 +----- desktop/src/preferences.rs | 33 ++++++++ desktop/src/render/state.rs | 2 +- .../wrapper/src/intercept_frontend_message.rs | 3 + desktop/wrapper/src/lib.rs | 12 +-- desktop/wrapper/src/messages.rs | 3 +- .../messages/app_window/app_window_message.rs | 1 + .../app_window/app_window_message_handler.rs | 4 + editor/src/messages/dialog/dialog_message.rs | 6 +- .../messages/dialog/dialog_message_handler.rs | 29 ++++--- .../export_dialog_message_handler.rs | 5 +- .../new_document_dialog_message_handler.rs | 7 +- .../messages/dialog/preferences_dialog/mod.rs | 2 +- .../preferences_dialog_message.rs | 1 + .../preferences_dialog_message_handler.rs | 75 +++++++++++++------ .../close_all_documents_dialog.rs | 2 +- .../simple_dialogs/close_document_dialog.rs | 4 +- .../simple_dialogs/confirm_restart_dialog.rs | 35 +++++++++ .../simple_dialogs/demo_artwork_dialog.rs | 2 +- .../src/messages/dialog/simple_dialogs/mod.rs | 2 + .../src/messages/frontend/frontend_message.rs | 1 + .../preferences/preferences_message.rs | 3 +- .../preferences_message_handler.rs | 21 ++---- editor/src/messages/prelude.rs | 2 +- frontend/wasm/src/editor_api.rs | 12 +-- 27 files changed, 222 insertions(+), 123 deletions(-) create mode 100644 desktop/src/preferences.rs create mode 100644 editor/src/messages/dialog/simple_dialogs/confirm_restart_dialog.rs diff --git a/desktop/src/app.rs b/desktop/src/app.rs index a7a5e0bc7d..487bfbfa7e 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -15,6 +15,7 @@ use crate::cli::Cli; use crate::consts::CEF_MESSAGE_LOOP_MAX_ITERATIONS; use crate::event::{AppEvent, AppEventScheduler}; use crate::persist::PersistentData; +use crate::preferences; use crate::render::{RenderError, RenderState}; use crate::window::Window; use crate::wrapper::messages::{DesktopFrontendMessage, DesktopWrapperMessage, InputMessage, MouseKeys, MouseState}; @@ -277,10 +278,10 @@ impl App { self.persistent_data.set_document_order(ids); } DesktopFrontendMessage::PersistenceWritePreferences { preferences } => { - self.persistent_data.write_preferences(preferences); + preferences::write(preferences); } DesktopFrontendMessage::PersistenceLoadPreferences => { - let preferences = self.persistent_data.load_preferences(); + let preferences = preferences::read(); let message = DesktopWrapperMessage::LoadPreferences { preferences }; responses.push(message); } @@ -398,6 +399,9 @@ impl App { window.show_all(); } } + DesktopFrontendMessage::Restart => { + self.exit(Some(ExitReason::Restart)); + } } } @@ -663,5 +667,6 @@ impl ApplicationHandler for App { pub(crate) enum ExitReason { Shutdown, + Restart, UiAccelerationFailure, } diff --git a/desktop/src/lib.rs b/desktop/src/lib.rs index f113376323..f56245e03f 100644 --- a/desktop/src/lib.rs +++ b/desktop/src/lib.rs @@ -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 std::io::Write; use std::process::exit; use tracing_subscriber::EnvFilter; use winit::event_loop::EventLoop; -pub(crate) mod consts; +pub(crate) use graphite_desktop_wrapper as wrapper; mod app; mod cef; mod cli; mod dirs; mod event; +mod gpu_context; mod persist; +mod preferences; mod render; mod window; -mod gpu_context; - -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(crate) mod consts; pub fn start() { 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(); - 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"); } 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) => { tracing::info!("CEF initialized successfully"); context @@ -109,16 +108,26 @@ pub fn start() { 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 drop(lock); match exit_reason { #[cfg(target_os = "linux")] - app::ExitReason::UiAccelerationFailure => { - use std::os::unix::process::CommandExt; - - tracing::error!("Restarting application without UI acceleration"); - let _ = std::process::Command::new(std::env::current_exe().unwrap()).arg("--disable-ui-acceleration").exec(); + app::ExitReason::Restart | app::ExitReason::UiAccelerationFailure => { + tracing::error!("Restarting application"); + let mut command = std::process::Command::new(std::env::current_exe().unwrap()); + #[cfg(target_family = "unix")] + let _ = std::os::unix::process::CommandExt::exec(&mut command); + #[cfg(not(target_family = "unix"))] + let _ = command.spawn(); tracing::error!("Failed to restart application"); } _ => {} diff --git a/desktop/src/persist.rs b/desktop/src/persist.rs index 97d329f66e..eafed1722b 100644 --- a/desktop/src/persist.rs +++ b/desktop/src/persist.rs @@ -1,4 +1,4 @@ -use crate::wrapper::messages::{Document, DocumentId, Preferences}; +use crate::wrapper::messages::{Document, DocumentId}; #[derive(Default, serde::Serialize, serde::Deserialize)] pub(crate) struct PersistentData { @@ -72,22 +72,6 @@ impl PersistentData { 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 { - let data = std::fs::read_to_string(Self::preferences_file_path()).ok()?; - let preferences = ron::from_str(&data).ok()?; - Some(preferences) - } - fn flush(&self) { let data = match ron::ser::to_string_pretty(self, Default::default()) { Ok(d) => d, @@ -129,12 +113,6 @@ impl PersistentData { path.push(crate::consts::APP_STATE_FILE_NAME); 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)] diff --git a/desktop/src/preferences.rs b/desktop/src/preferences.rs new file mode 100644 index 0000000000..b5eea15eec --- /dev/null +++ b/desktop/src/preferences.rs @@ -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 +} diff --git a/desktop/src/render/state.rs b/desktop/src/render/state.rs index 81808cc7ca..11abd851e6 100644 --- a/desktop/src/render/state.rs +++ b/desktop/src/render/state.rs @@ -335,7 +335,7 @@ impl RenderState { }, wgpu::BindGroupEntry { binding: 1, - resource: wgpu::BindingResource::TextureView(&overlays_texture_view.as_ref()), + resource: wgpu::BindingResource::TextureView(overlays_texture_view.as_ref()), }, wgpu::BindGroupEntry { binding: 2, diff --git a/desktop/wrapper/src/intercept_frontend_message.rs b/desktop/wrapper/src/intercept_frontend_message.rs index e4ab2aeb2b..0d8e6b21ac 100644 --- a/desktop/wrapper/src/intercept_frontend_message.rs +++ b/desktop/wrapper/src/intercept_frontend_message.rs @@ -151,6 +151,9 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD FrontendMessage::WindowShowAll => { dispatcher.respond(DesktopFrontendMessage::WindowShowAll); } + FrontendMessage::WindowRestart => { + dispatcher.respond(DesktopFrontendMessage::Restart); + } m => return Some(m), } None diff --git a/desktop/wrapper/src/lib.rs b/desktop/wrapper/src/lib.rs index 2ef3bbb60c..8ee06c61e7 100644 --- a/desktop/wrapper/src/lib.rs +++ b/desktop/wrapper/src/lib.rs @@ -1,25 +1,21 @@ use graph_craft::wasm_application_io::WasmApplicationIo; use graphite_editor::application::{Editor, Environment, Host, Platform}; use graphite_editor::messages::prelude::{FrontendMessage, Message}; +use message_dispatcher::DesktopWrapperMessageDispatcher; +use messages::{DesktopFrontendMessage, DesktopWrapperMessage}; pub use graphite_editor::consts::FILE_EXTENSION; - pub use wgpu_executor::TargetTexture; pub use wgpu_executor::WgpuContext; pub use wgpu_executor::WgpuContextBuilder; pub use wgpu_executor::WgpuExecutor; 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 intercept_editor_message; mod intercept_frontend_message; - +mod message_dispatcher; +pub mod messages; pub(crate) mod utils; pub struct DesktopWrapper { diff --git a/desktop/wrapper/src/messages.rs b/desktop/wrapper/src/messages.rs index 84aab2b2aa..6a7105d5c7 100644 --- a/desktop/wrapper/src/messages.rs +++ b/desktop/wrapper/src/messages.rs @@ -74,6 +74,7 @@ pub enum DesktopFrontendMessage { WindowHide, WindowHideOthers, WindowShowAll, + Restart, } pub enum DesktopWrapperMessage { @@ -113,7 +114,7 @@ pub enum DesktopWrapperMessage { id: DocumentId, }, LoadPreferences { - preferences: Option, + preferences: Preferences, }, MenuEvent { id: String, diff --git a/editor/src/messages/app_window/app_window_message.rs b/editor/src/messages/app_window/app_window_message.rs index c02b99b5fd..2acf297fae 100644 --- a/editor/src/messages/app_window/app_window_message.rs +++ b/editor/src/messages/app_window/app_window_message.rs @@ -5,6 +5,7 @@ use crate::messages::prelude::*; pub enum AppWindowMessage { PointerLock, PointerLockMove { x: f64, y: f64 }, + Restart, Close, Minimize, Maximize, diff --git a/editor/src/messages/app_window/app_window_message_handler.rs b/editor/src/messages/app_window/app_window_message_handler.rs index 21b6e9b5e9..fd7b05f630 100644 --- a/editor/src/messages/app_window/app_window_message_handler.rs +++ b/editor/src/messages/app_window/app_window_message_handler.rs @@ -40,6 +40,10 @@ impl MessageHandler for AppWindowMessageHandler { AppWindowMessage::ShowAll => { responses.add(FrontendMessage::WindowShowAll); } + AppWindowMessage::Restart => { + responses.add(PortfolioMessage::AutoSaveAllDocuments); + responses.add(FrontendMessage::WindowRestart); + } } } advertise_actions!(AppWindowMessageDiscriminant; diff --git a/editor/src/messages/dialog/dialog_message.rs b/editor/src/messages/dialog/dialog_message.rs index 5a73c980c7..8e0a42d8a3 100644 --- a/editor/src/messages/dialog/dialog_message.rs +++ b/editor/src/messages/dialog/dialog_message.rs @@ -12,10 +12,11 @@ pub enum DialogMessage { PreferencesDialog(PreferencesDialogMessage), // Messages - CloseAllDocumentsWithConfirmation, - CloseDialogAndThen { + Close, + CloseAndThen { followups: Vec, }, + CloseAllDocumentsWithConfirmation, DisplayDialogError { title: String, description: String, @@ -35,4 +36,5 @@ pub enum DialogMessage { }, RequestNewDocumentDialog, RequestPreferencesDialog, + RequestConfirmRestartDialog, } diff --git a/editor/src/messages/dialog/dialog_message_handler.rs b/editor/src/messages/dialog/dialog_message_handler.rs index 6773965f11..31e628f497 100644 --- a/editor/src/messages/dialog/dialog_message_handler.rs +++ b/editor/src/messages/dialog/dialog_message_handler.rs @@ -1,6 +1,6 @@ use super::simple_dialogs::{self, AboutGraphiteDialog, DemoArtworkDialog, LicensesDialog}; 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::layout::utility_types::widget_prelude::*; use crate::messages::prelude::*; @@ -27,22 +27,23 @@ impl MessageHandler> for DialogMessageHa match message { DialogMessage::ExportDialog(message) => self.export_dialog.process_message(message, responses, ExportDialogMessageContext { portfolio }), 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, ()), - DialogMessage::CloseAllDocumentsWithConfirmation => { - let dialog = simple_dialogs::CloseAllDocumentsDialog { - unsaved_document_names: portfolio.unsaved_document_names(), - }; - dialog.send_dialog_to_frontend(responses); - } - DialogMessage::CloseDialogAndThen { followups } => { + DialogMessage::Close => responses.add(FrontendMessage::DisplayDialogDismiss), + DialogMessage::CloseAndThen { followups } => { for message in followups.into_iter() { 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. // If it comes before, the dialog reopens (and appears to not close at all). - responses.add(FrontendMessage::DisplayDialogDismiss); + responses.add(DialogMessage::Close); + } + DialogMessage::CloseAllDocumentsWithConfirmation => { + let dialog = simple_dialogs::CloseAllDocumentsDialog { + unsaved_document_names: portfolio.unsaved_document_names(), + }; + dialog.send_dialog_to_frontend(responses); } DialogMessage::DisplayDialogError { title, description } => { let dialog = simple_dialogs::ErrorDialog { title, description }; @@ -99,7 +100,6 @@ impl MessageHandler> for DialogMessageHa } DialogMessage::RequestLicensesDialogWithLocalizedCommitDate { localized_commit_year } => { let dialog = LicensesDialog { localized_commit_year }; - dialog.send_dialog_to_frontend(responses); } DialogMessage::RequestLicensesThirdPartyDialogWithLicenseText { license_text } => { @@ -115,9 +115,14 @@ impl MessageHandler> for DialogMessageHa self.new_document_dialog.send_dialog_to_frontend(responses); } DialogMessage::RequestPreferencesDialog => { - self.preferences_dialog = PreferencesDialogMessageHandler {}; + self.preferences_dialog = PreferencesDialogMessageHandler::default(); self.preferences_dialog.send_dialog_to_frontend(responses, preferences); } + DialogMessage::RequestConfirmRestartDialog => { + let dialog = ConfirmRestartDialog {}; + println!("Requesting confirm restart dialog"); + dialog.send_dialog_to_frontend(responses); + } } } diff --git a/editor/src/messages/dialog/export_dialog/export_dialog_message_handler.rs b/editor/src/messages/dialog/export_dialog/export_dialog_message_handler.rs index f4cc5a4952..c4a563bb29 100644 --- a/editor/src/messages/dialog/export_dialog/export_dialog_message_handler.rs +++ b/editor/src/messages/dialog/export_dialog/export_dialog_message_handler.rs @@ -63,7 +63,8 @@ impl MessageHandler> for Exp self.send_dialog_to_frontend(responses); } - advertise_actions! {ExportDialogUpdate;} + advertise_actions!(ExportDialogUpdate; + ); } impl DialogLayoutHolder for ExportDialogMessageHandler { @@ -75,7 +76,7 @@ impl DialogLayoutHolder for ExportDialogMessageHandler { TextButton::new("Export") .emphasized(true) .on_update(|_| { - DialogMessage::CloseDialogAndThen { + DialogMessage::CloseAndThen { followups: vec![ExportDialogMessage::Submit.into()], } .into() diff --git a/editor/src/messages/dialog/new_document_dialog/new_document_dialog_message_handler.rs b/editor/src/messages/dialog/new_document_dialog/new_document_dialog_message_handler.rs index 1b9a3d5b0f..71e0d4ae1b 100644 --- a/editor/src/messages/dialog/new_document_dialog/new_document_dialog_message_handler.rs +++ b/editor/src/messages/dialog/new_document_dialog/new_document_dialog_message_handler.rs @@ -12,7 +12,7 @@ pub struct NewDocumentDialogMessageHandler { } #[message_handler_data] -impl<'a> MessageHandler for NewDocumentDialogMessageHandler { +impl MessageHandler for NewDocumentDialogMessageHandler { fn process_message(&mut self, message: NewDocumentDialogMessage, responses: &mut VecDeque, _: ()) { match message { NewDocumentDialogMessage::Name { name } => self.name = name, @@ -45,7 +45,8 @@ impl<'a> MessageHandler for NewDocumentDialogMessa self.send_dialog_to_frontend(responses); } - advertise_actions! {NewDocumentDialogUpdate;} + advertise_actions!(NewDocumentDialogUpdate; + ); } impl DialogLayoutHolder for NewDocumentDialogMessageHandler { @@ -57,7 +58,7 @@ impl DialogLayoutHolder for NewDocumentDialogMessageHandler { TextButton::new("OK") .emphasized(true) .on_update(|_| { - DialogMessage::CloseDialogAndThen { + DialogMessage::CloseAndThen { followups: vec![NewDocumentDialogMessage::Submit.into()], } .into() diff --git a/editor/src/messages/dialog/preferences_dialog/mod.rs b/editor/src/messages/dialog/preferences_dialog/mod.rs index eb5ce03843..647e847dde 100644 --- a/editor/src/messages/dialog/preferences_dialog/mod.rs +++ b/editor/src/messages/dialog/preferences_dialog/mod.rs @@ -4,4 +4,4 @@ mod preferences_dialog_message_handler; #[doc(inline)] pub use preferences_dialog_message::{PreferencesDialogMessage, PreferencesDialogMessageDiscriminant}; #[doc(inline)] -pub use preferences_dialog_message_handler::{PreferencesDialogMessageContext, PreferencesDialogMessageHandler}; +pub use preferences_dialog_message_handler::PreferencesDialogMessageHandler; diff --git a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message.rs b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message.rs index 736fe7c10a..420f1d1f45 100644 --- a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message.rs +++ b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message.rs @@ -3,5 +3,6 @@ use crate::messages::prelude::*; #[impl_message(Message, DialogMessage, PreferencesDialog)] #[derive(Eq, PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)] pub enum PreferencesDialogMessage { + RestartRequired, Confirm, } diff --git a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs index 1955986fc6..596865b909 100644 --- a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs +++ b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs @@ -4,28 +4,29 @@ use crate::messages::portfolio::document::utility_types::wires::GraphWireStyle; use crate::messages::preferences::SelectionMode; use crate::messages::prelude::*; -#[derive(ExtractField)] -pub struct PreferencesDialogMessageContext<'a> { - pub preferences: &'a PreferencesMessageHandler, -} - /// A dialog to allow users to customize Graphite editor options #[derive(Debug, Clone, Default, ExtractField)] -pub struct PreferencesDialogMessageHandler {} +pub struct PreferencesDialogMessageHandler { + restart_requeird: bool, +} #[message_handler_data] -impl MessageHandler> for PreferencesDialogMessageHandler { - fn process_message(&mut self, message: PreferencesDialogMessage, responses: &mut VecDeque, context: PreferencesDialogMessageContext) { - let PreferencesDialogMessageContext { preferences } = context; - +impl MessageHandler for PreferencesDialogMessageHandler { + fn process_message(&mut self, message: PreferencesDialogMessage, responses: &mut VecDeque, _: ()) { match message { - PreferencesDialogMessage::Confirm => {} + PreferencesDialogMessage::RestartRequired => self.restart_requeird = true, + PreferencesDialogMessage::Confirm => { + if self.restart_requeird { + 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. @@ -284,6 +285,42 @@ impl PreferencesDialogMessageHandler { rows.extend_from_slice(&[header, node_graph_wires_label, graph_wire_style, use_vello, brush_tool]); } + // ============= + // COMPATEBILITY + // ============= + #[cfg(not(target_family = "wasm"))] + { + let header = vec![TextLabel::new("Compatebilety").italic(true).widget_instance()]; + + let ui_acceleration_description = + "Disable hardware acceleration for the user interface (viewport is not effected). This can improve stability on some systems at the cost of decreased performance."; + + let checkbox_id = CheckboxId::new(); + let ui_acceleration = vec![ + 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([ + PreferencesMessage::DisableUIAcceleration { + disable_ui_acceleration: number_input.checked, + } + .into(), + PreferencesDialogMessage::RestartRequired.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()) } @@ -307,15 +344,7 @@ impl PreferencesDialogMessageHandler { fn layout_buttons(&self) -> Layout { let widgets = vec![ - TextButton::new("OK") - .emphasized(true) - .on_update(|_| { - DialogMessage::CloseDialogAndThen { - followups: vec![PreferencesDialogMessage::Confirm.into()], - } - .into() - }) - .widget_instance(), + TextButton::new("OK").emphasized(true).on_update(|_| PreferencesDialogMessage::Confirm.into()).widget_instance(), TextButton::new("Reset to Defaults").on_update(|_| PreferencesMessage::ResetToDefaults.into()).widget_instance(), ]; diff --git a/editor/src/messages/dialog/simple_dialogs/close_all_documents_dialog.rs b/editor/src/messages/dialog/simple_dialogs/close_all_documents_dialog.rs index 545f764cc5..305535bd85 100644 --- a/editor/src/messages/dialog/simple_dialogs/close_all_documents_dialog.rs +++ b/editor/src/messages/dialog/simple_dialogs/close_all_documents_dialog.rs @@ -15,7 +15,7 @@ impl DialogLayoutHolder for CloseAllDocumentsDialog { TextButton::new("Discard All") .emphasized(true) .on_update(|_| { - DialogMessage::CloseDialogAndThen { + DialogMessage::CloseAndThen { followups: vec![PortfolioMessage::CloseAllDocuments.into()], } .into() diff --git a/editor/src/messages/dialog/simple_dialogs/close_document_dialog.rs b/editor/src/messages/dialog/simple_dialogs/close_document_dialog.rs index d535acbaa3..0a4f165002 100644 --- a/editor/src/messages/dialog/simple_dialogs/close_document_dialog.rs +++ b/editor/src/messages/dialog/simple_dialogs/close_document_dialog.rs @@ -18,7 +18,7 @@ impl DialogLayoutHolder for CloseDocumentDialog { TextButton::new("Save") .emphasized(true) .on_update(|_| { - DialogMessage::CloseDialogAndThen { + DialogMessage::CloseAndThen { followups: vec![DocumentMessage::SaveDocument.into()], } .into() @@ -26,7 +26,7 @@ impl DialogLayoutHolder for CloseDocumentDialog { .widget_instance(), TextButton::new("Discard") .on_update(move |_| { - DialogMessage::CloseDialogAndThen { + DialogMessage::CloseAndThen { followups: vec![EventMessage::ToolAbort.into(), PortfolioMessage::CloseDocument { document_id }.into()], } .into() diff --git a/editor/src/messages/dialog/simple_dialogs/confirm_restart_dialog.rs b/editor/src/messages/dialog/simple_dialogs/confirm_restart_dialog.rs new file mode 100644 index 0000000000..332f54edcf --- /dev/null +++ b/editor/src/messages/dialog/simple_dialogs/confirm_restart_dialog.rs @@ -0,0 +1,35 @@ +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 {} + +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("Cancel").on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance(), + ]; + + Layout(vec![LayoutGroup::Row { widgets }]) + } +} + +impl LayoutHolder for ConfirmRestartDialog { + fn layout(&self) -> Layout { + Layout(vec![LayoutGroup::Row { + widgets: vec![TextLabel::new("Some changes require restart to take effect").bold(true).multiline(true).widget_instance()], + }]) + } +} diff --git a/editor/src/messages/dialog/simple_dialogs/demo_artwork_dialog.rs b/editor/src/messages/dialog/simple_dialogs/demo_artwork_dialog.rs index e30a96ab1e..a413954961 100644 --- a/editor/src/messages/dialog/simple_dialogs/demo_artwork_dialog.rs +++ b/editor/src/messages/dialog/simple_dialogs/demo_artwork_dialog.rs @@ -32,7 +32,7 @@ impl LayoutHolder for DemoArtworkDialog { .chunks(4) .flat_map(|chunk| { fn make_dialog(name: &str, filename: &str) -> Message { - DialogMessage::CloseDialogAndThen { + DialogMessage::CloseAndThen { followups: vec![ FrontendMessage::TriggerFetchAndOpenDocument { name: name.to_string(), diff --git a/editor/src/messages/dialog/simple_dialogs/mod.rs b/editor/src/messages/dialog/simple_dialogs/mod.rs index a56a2c82e8..f775faec62 100644 --- a/editor/src/messages/dialog/simple_dialogs/mod.rs +++ b/editor/src/messages/dialog/simple_dialogs/mod.rs @@ -1,6 +1,7 @@ mod about_graphite_dialog; mod close_all_documents_dialog; mod close_document_dialog; +mod confirm_restart_dialog; mod demo_artwork_dialog; mod error_dialog; mod licenses_dialog; @@ -9,6 +10,7 @@ mod licenses_third_party_dialog; pub use about_graphite_dialog::AboutGraphiteDialog; pub use close_all_documents_dialog::CloseAllDocumentsDialog; pub use close_document_dialog::CloseDocumentDialog; +pub use confirm_restart_dialog::ConfirmRestartDialog; pub use demo_artwork_dialog::ARTWORK; pub use demo_artwork_dialog::DemoArtworkDialog; pub use error_dialog::ErrorDialog; diff --git a/editor/src/messages/frontend/frontend_message.rs b/editor/src/messages/frontend/frontend_message.rs index 0a03f676c5..eeef28fc4b 100644 --- a/editor/src/messages/frontend/frontend_message.rs +++ b/editor/src/messages/frontend/frontend_message.rs @@ -376,4 +376,5 @@ pub enum FrontendMessage { WindowHide, WindowHideOthers, WindowShowAll, + WindowRestart, } diff --git a/editor/src/messages/preferences/preferences_message.rs b/editor/src/messages/preferences/preferences_message.rs index 6408cd5d2f..2dcb0a80d9 100644 --- a/editor/src/messages/preferences/preferences_message.rs +++ b/editor/src/messages/preferences/preferences_message.rs @@ -6,7 +6,7 @@ use crate::messages::prelude::*; #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)] pub enum PreferencesMessage { // Management messages - Load { preferences: Option }, + Load { preferences: PreferencesMessageHandler }, ResetToDefaults, // Per-preference messages @@ -17,4 +17,5 @@ pub enum PreferencesMessage { GraphWireStyle { style: GraphWireStyle }, ViewportZoomWheelRate { rate: f64 }, UIScale { scale: f64 }, + DisableUIAcceleration { disable_ui_acceleration: bool }, } diff --git a/editor/src/messages/preferences/preferences_message_handler.rs b/editor/src/messages/preferences/preferences_message_handler.rs index 6321119c5a..c1cfe31306 100644 --- a/editor/src/messages/preferences/preferences_message_handler.rs +++ b/editor/src/messages/preferences/preferences_message_handler.rs @@ -21,6 +21,7 @@ pub struct PreferencesMessageHandler { pub graph_wire_style: GraphWireStyle, pub viewport_zoom_wheel_rate: f64, pub ui_scale: f64, + pub disable_ui_acceleration: bool, } impl PreferencesMessageHandler { @@ -49,6 +50,7 @@ impl Default for PreferencesMessageHandler { graph_wire_style: GraphWireStyle::default(), viewport_zoom_wheel_rate: VIEWPORT_ZOOM_WHEEL_RATE, ui_scale: UI_SCALE_DEFAULT, + disable_ui_acceleration: false, } } } @@ -61,9 +63,7 @@ impl MessageHandler> for Prefe match message { // Management messages PreferencesMessage::Load { preferences } => { - if let Some(preferences) = preferences { - *self = preferences; - } + *self = preferences; responses.add(PortfolioMessage::EditorPreferences); responses.add(PortfolioMessage::UpdateVelloPreference); @@ -73,10 +73,8 @@ impl MessageHandler> for Prefe responses.add(FrontendMessage::UpdateUIScale { scale: self.ui_scale }); } PreferencesMessage::ResetToDefaults => { - refresh_dialog(responses); - responses.add(KeyMappingMessage::ModifyMapping { mapping: MappingVariant::Default }); - - *self = Self::default() + responses.add(PreferencesMessage::Load { preferences: Self::default() }); + responses.add(DialogMessage::RequestPreferencesDialog); } // Per-preference messages @@ -115,6 +113,9 @@ impl MessageHandler> for Prefe self.ui_scale = 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() }); @@ -123,9 +124,3 @@ impl MessageHandler> for Prefe advertise_actions!(PreferencesMessageDiscriminant; ); } - -fn refresh_dialog(responses: &mut VecDeque) { - responses.add(DialogMessage::CloseDialogAndThen { - followups: vec![DialogMessage::RequestPreferencesDialog.into()], - }); -} diff --git a/editor/src/messages/prelude.rs b/editor/src/messages/prelude.rs index 319c125188..1e7082e5c8 100644 --- a/editor/src/messages/prelude.rs +++ b/editor/src/messages/prelude.rs @@ -12,7 +12,7 @@ pub use crate::messages::debug::{DebugMessage, DebugMessageDiscriminant, DebugMe pub use crate::messages::defer::{DeferMessage, DeferMessageDiscriminant, DeferMessageHandler}; pub use crate::messages::dialog::export_dialog::{ExportDialogMessage, ExportDialogMessageContext, ExportDialogMessageDiscriminant, ExportDialogMessageHandler}; pub use crate::messages::dialog::new_document_dialog::{NewDocumentDialogMessage, NewDocumentDialogMessageDiscriminant, NewDocumentDialogMessageHandler}; -pub use crate::messages::dialog::preferences_dialog::{PreferencesDialogMessage, PreferencesDialogMessageContext, PreferencesDialogMessageDiscriminant, PreferencesDialogMessageHandler}; +pub use crate::messages::dialog::preferences_dialog::{PreferencesDialogMessage, PreferencesDialogMessageDiscriminant, PreferencesDialogMessageHandler}; pub use crate::messages::dialog::{DialogMessage, DialogMessageContext, DialogMessageDiscriminant, DialogMessageHandler}; pub use crate::messages::frontend::{FrontendMessage, FrontendMessageDiscriminant}; pub use crate::messages::input_mapper::key_mapping::{KeyMappingMessage, KeyMappingMessageContext, KeyMappingMessageDiscriminant, KeyMappingMessageHandler}; diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index aaa17a1d22..48e8a364c8 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -384,18 +384,14 @@ impl EditorHandle { #[wasm_bindgen(js_name = loadPreferences)] pub fn load_preferences(&self, preferences: Option) { - let preferences = if let Some(preferences) = preferences { + if let Some(preferences) = preferences { let Ok(preferences) = serde_json::from_str(&preferences) else { log::error!("Failed to deserialize preferences"); return; }; - Some(preferences) - } else { - None - }; - - let message = PreferencesMessage::Load { preferences }; - self.dispatch(message); + let message = PreferencesMessage::Load { preferences }; + self.dispatch(message); + } } #[wasm_bindgen(js_name = selectDocument)] From 1fba185158290c387f4b44c83d76ac85335b8f88 Mon Sep 17 00:00:00 2001 From: Timon Date: Tue, 17 Feb 2026 17:21:23 +0000 Subject: [PATCH 2/4] Fixup --- editor/src/messages/dialog/dialog_message.rs | 1 + .../messages/dialog/dialog_message_handler.rs | 26 ++++++++++++++--- .../export_dialog_message_handler.rs | 2 +- .../new_document_dialog_message_handler.rs | 8 ++++-- .../messages/dialog/preferences_dialog/mod.rs | 2 +- .../preferences_dialog_message.rs | 2 +- .../preferences_dialog_message_handler.rs | 28 ++++++++++++++----- .../simple_dialogs/about_graphite_dialog.rs | 2 +- .../close_all_documents_dialog.rs | 2 +- .../simple_dialogs/close_document_dialog.rs | 2 +- .../simple_dialogs/confirm_restart_dialog.rs | 4 +-- .../simple_dialogs/demo_artwork_dialog.rs | 2 +- .../dialog/simple_dialogs/error_dialog.rs | 2 +- .../dialog/simple_dialogs/licenses_dialog.rs | 2 +- .../licenses_third_party_dialog.rs | 2 +- .../src/messages/frontend/frontend_message.rs | 2 +- .../preferences_message_handler.rs | 4 +++ editor/src/messages/prelude.rs | 2 +- frontend/src/io-managers/input.ts | 4 +-- frontend/src/messages.ts | 4 +-- frontend/src/state-providers/dialog.ts | 4 +-- frontend/wasm/src/editor_api.rs | 7 +++++ 22 files changed, 81 insertions(+), 33 deletions(-) diff --git a/editor/src/messages/dialog/dialog_message.rs b/editor/src/messages/dialog/dialog_message.rs index 8e0a42d8a3..c3b04e7ffc 100644 --- a/editor/src/messages/dialog/dialog_message.rs +++ b/editor/src/messages/dialog/dialog_message.rs @@ -12,6 +12,7 @@ pub enum DialogMessage { PreferencesDialog(PreferencesDialogMessage), // Messages + Dismiss, Close, CloseAndThen { followups: Vec, diff --git a/editor/src/messages/dialog/dialog_message_handler.rs b/editor/src/messages/dialog/dialog_message_handler.rs index 31e628f497..6ba420e0c5 100644 --- a/editor/src/messages/dialog/dialog_message_handler.rs +++ b/editor/src/messages/dialog/dialog_message_handler.rs @@ -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`. #[derive(Debug, Default, Clone, ExtractField)] pub struct DialogMessageHandler { + on_dismiss: Option, export_dialog: ExportDialogMessageHandler, new_document_dialog: NewDocumentDialogMessageHandler, preferences_dialog: PreferencesDialogMessageHandler, @@ -27,9 +28,17 @@ impl MessageHandler> for DialogMessageHa match message { DialogMessage::ExportDialog(message) => self.export_dialog.process_message(message, responses, ExportDialogMessageContext { portfolio }), DialogMessage::NewDocumentDialog(message) => self.new_document_dialog.process_message(message, responses, ()), - DialogMessage::PreferencesDialog(message) => self.preferences_dialog.process_message(message, responses, ()), + DialogMessage::PreferencesDialog(message) => self.preferences_dialog.process_message(message, responses, PreferencesDialogMessageContext { preferences }), - DialogMessage::Close => responses.add(FrontendMessage::DisplayDialogDismiss), + DialogMessage::Dismiss => { + if let Some(message) = self.on_dismiss.take() { + responses.add(message); + } + } + DialogMessage::Close => { + self.on_dismiss = None; + responses.add(FrontendMessage::DialogClose) + } DialogMessage::CloseAndThen { followups } => { for message in followups.into_iter() { responses.add(message); @@ -40,16 +49,19 @@ impl MessageHandler> for DialogMessageHa 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 } => { + self.on_dismiss = None; let dialog = simple_dialogs::ErrorDialog { title, description }; dialog.send_dialog_to_frontend(responses); } DialogMessage::RequestAboutGraphiteDialog => { + self.on_dismiss = Some(DialogMessage::Close.into()); responses.add(FrontendMessage::TriggerAboutGraphiteLocalizedCommitDate { commit_date: GRAPHITE_GIT_COMMIT_DATE.into(), }); @@ -58,6 +70,7 @@ impl MessageHandler> for DialogMessageHa localized_commit_date, localized_commit_year, } => { + self.on_dismiss = Some(DialogMessage::Close.into()); let dialog = AboutGraphiteDialog { localized_commit_date, localized_commit_year, @@ -66,10 +79,12 @@ impl MessageHandler> for DialogMessageHa dialog.send_dialog_to_frontend(responses); } DialogMessage::RequestDemoArtworkDialog => { + self.on_dismiss = Some(DialogMessage::Close.into()); let dialog = DemoArtworkDialog; dialog.send_dialog_to_frontend(responses); } DialogMessage::RequestExportDialog => { + self.on_dismiss = Some(DialogMessage::Close.into()); if let Some(document) = portfolio.active_document() { let artboards = document .metadata() @@ -99,14 +114,17 @@ impl MessageHandler> for DialogMessageHa } } DialogMessage::RequestLicensesDialogWithLocalizedCommitDate { localized_commit_year } => { + self.on_dismiss = Some(DialogMessage::Close.into()); let dialog = LicensesDialog { localized_commit_year }; dialog.send_dialog_to_frontend(responses); } DialogMessage::RequestLicensesThirdPartyDialogWithLicenseText { license_text } => { + self.on_dismiss = Some(DialogMessage::Close.into()); let dialog = LicensesThirdPartyDialog { license_text }; dialog.send_dialog_to_frontend(responses); } DialogMessage::RequestNewDocumentDialog => { + self.on_dismiss = Some(DialogMessage::Close.into()); self.new_document_dialog = NewDocumentDialogMessageHandler { name: portfolio.generate_new_document_name(), infinite: false, @@ -115,12 +133,12 @@ impl MessageHandler> for DialogMessageHa self.new_document_dialog.send_dialog_to_frontend(responses); } DialogMessage::RequestPreferencesDialog => { - self.preferences_dialog = PreferencesDialogMessageHandler::default(); + self.on_dismiss = Some(PreferencesDialogMessage::Confirm.into()); self.preferences_dialog.send_dialog_to_frontend(responses, preferences); } DialogMessage::RequestConfirmRestartDialog => { + self.on_dismiss = None; let dialog = ConfirmRestartDialog {}; - println!("Requesting confirm restart dialog"); dialog.send_dialog_to_frontend(responses); } } diff --git a/editor/src/messages/dialog/export_dialog/export_dialog_message_handler.rs b/editor/src/messages/dialog/export_dialog/export_dialog_message_handler.rs index c4a563bb29..5eb151d11a 100644 --- a/editor/src/messages/dialog/export_dialog/export_dialog_message_handler.rs +++ b/editor/src/messages/dialog/export_dialog/export_dialog_message_handler.rs @@ -82,7 +82,7 @@ impl DialogLayoutHolder for ExportDialogMessageHandler { .into() }) .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 }]) diff --git a/editor/src/messages/dialog/new_document_dialog/new_document_dialog_message_handler.rs b/editor/src/messages/dialog/new_document_dialog/new_document_dialog_message_handler.rs index 71e0d4ae1b..a1fd90583b 100644 --- a/editor/src/messages/dialog/new_document_dialog/new_document_dialog_message_handler.rs +++ b/editor/src/messages/dialog/new_document_dialog/new_document_dialog_message_handler.rs @@ -34,7 +34,11 @@ impl MessageHandler for NewDocumentDialogMessageHa responses.add(ViewportMessage::RepropagateUpdate); responses.add(DeferMessage::AfterNavigationReady { - messages: vec![DocumentMessage::ZoomCanvasToFitAll.into(), DocumentMessage::DeselectAllLayers.into()], + messages: vec![ + DocumentMessage::ZoomCanvasToFitAll.into(), + DocumentMessage::DeselectAllLayers.into(), + PortfolioMessage::AutoSaveActiveDocument.into(), + ], }); } @@ -64,7 +68,7 @@ impl DialogLayoutHolder for NewDocumentDialogMessageHandler { .into() }) .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 }]) diff --git a/editor/src/messages/dialog/preferences_dialog/mod.rs b/editor/src/messages/dialog/preferences_dialog/mod.rs index 647e847dde..eb5ce03843 100644 --- a/editor/src/messages/dialog/preferences_dialog/mod.rs +++ b/editor/src/messages/dialog/preferences_dialog/mod.rs @@ -4,4 +4,4 @@ mod preferences_dialog_message_handler; #[doc(inline)] pub use preferences_dialog_message::{PreferencesDialogMessage, PreferencesDialogMessageDiscriminant}; #[doc(inline)] -pub use preferences_dialog_message_handler::PreferencesDialogMessageHandler; +pub use preferences_dialog_message_handler::{PreferencesDialogMessageContext, PreferencesDialogMessageHandler}; diff --git a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message.rs b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message.rs index 420f1d1f45..93978daa09 100644 --- a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message.rs +++ b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message.rs @@ -3,6 +3,6 @@ use crate::messages::prelude::*; #[impl_message(Message, DialogMessage, PreferencesDialog)] #[derive(Eq, PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)] pub enum PreferencesDialogMessage { - RestartRequired, + MightRequireRestart, Confirm, } diff --git a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs index 596865b909..7bf3a36d9f 100644 --- a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs +++ b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs @@ -4,19 +4,31 @@ use crate::messages::portfolio::document::utility_types::wires::GraphWireStyle; use crate::messages::preferences::SelectionMode; use crate::messages::prelude::*; +#[derive(ExtractField)] +pub struct PreferencesDialogMessageContext<'a> { + pub preferences: &'a PreferencesMessageHandler, +} + /// A dialog to allow users to customize Graphite editor options #[derive(Debug, Clone, Default, ExtractField)] pub struct PreferencesDialogMessageHandler { - restart_requeird: bool, + unmodifyed_preferences: Option, } #[message_handler_data] -impl MessageHandler for PreferencesDialogMessageHandler { - fn process_message(&mut self, message: PreferencesDialogMessage, responses: &mut VecDeque, _: ()) { +impl MessageHandler> for PreferencesDialogMessageHandler { + fn process_message(&mut self, message: PreferencesDialogMessage, responses: &mut VecDeque, context: PreferencesDialogMessageContext) { + let PreferencesDialogMessageContext { preferences } = context; match message { - PreferencesDialogMessage::RestartRequired => self.restart_requeird = true, + PreferencesDialogMessage::MightRequireRestart => { + if self.unmodifyed_preferences.is_none() { + self.unmodifyed_preferences = Some(preferences.clone()); + } + } PreferencesDialogMessage::Confirm => { - if self.restart_requeird { + if let Some(unmodifyed_preferences) = &self.unmodifyed_preferences + && unmodifyed_preferences.needs_restart(preferences) + { responses.add(DialogMessage::RequestConfirmRestartDialog); } else { responses.add(DialogMessage::Close); @@ -290,23 +302,25 @@ impl PreferencesDialogMessageHandler { // ============= #[cfg(not(target_family = "wasm"))] { - let header = vec![TextLabel::new("Compatebilety").italic(true).widget_instance()]; + let header = vec![TextLabel::new("Compatibility").italic(true).widget_instance()]; let ui_acceleration_description = "Disable hardware acceleration for the user interface (viewport is not effected). This can improve stability on some systems at the cost of decreased performance."; 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::MightRequireRestart.into(), PreferencesMessage::DisableUIAcceleration { disable_ui_acceleration: number_input.checked, } .into(), - PreferencesDialogMessage::RestartRequired.into(), ]), }) .for_label(checkbox_id) diff --git a/editor/src/messages/dialog/simple_dialogs/about_graphite_dialog.rs b/editor/src/messages/dialog/simple_dialogs/about_graphite_dialog.rs index caba39ca14..f9817dcf37 100644 --- a/editor/src/messages/dialog/simple_dialogs/about_graphite_dialog.rs +++ b/editor/src/messages/dialog/simple_dialogs/about_graphite_dialog.rs @@ -13,7 +13,7 @@ impl DialogLayoutHolder for AboutGraphiteDialog { const TITLE: &'static str = "About Graphite"; 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 }]) } diff --git a/editor/src/messages/dialog/simple_dialogs/close_all_documents_dialog.rs b/editor/src/messages/dialog/simple_dialogs/close_all_documents_dialog.rs index 305535bd85..b02cb5c3d9 100644 --- a/editor/src/messages/dialog/simple_dialogs/close_all_documents_dialog.rs +++ b/editor/src/messages/dialog/simple_dialogs/close_all_documents_dialog.rs @@ -21,7 +21,7 @@ impl DialogLayoutHolder for CloseAllDocumentsDialog { .into() }) .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 }]) diff --git a/editor/src/messages/dialog/simple_dialogs/close_document_dialog.rs b/editor/src/messages/dialog/simple_dialogs/close_document_dialog.rs index 0a4f165002..9e9f56d812 100644 --- a/editor/src/messages/dialog/simple_dialogs/close_document_dialog.rs +++ b/editor/src/messages/dialog/simple_dialogs/close_document_dialog.rs @@ -32,7 +32,7 @@ impl DialogLayoutHolder for CloseDocumentDialog { .into() }) .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 }]) diff --git a/editor/src/messages/dialog/simple_dialogs/confirm_restart_dialog.rs b/editor/src/messages/dialog/simple_dialogs/confirm_restart_dialog.rs index 332f54edcf..dbc5d8a2f3 100644 --- a/editor/src/messages/dialog/simple_dialogs/confirm_restart_dialog.rs +++ b/editor/src/messages/dialog/simple_dialogs/confirm_restart_dialog.rs @@ -19,7 +19,7 @@ impl DialogLayoutHolder for ConfirmRestartDialog { .into() }) .widget_instance(), - TextButton::new("Cancel").on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance(), + TextButton::new("Later").on_update(|_| FrontendMessage::DialogClose.into()).widget_instance(), ]; Layout(vec![LayoutGroup::Row { widgets }]) @@ -29,7 +29,7 @@ impl DialogLayoutHolder for ConfirmRestartDialog { impl LayoutHolder for ConfirmRestartDialog { fn layout(&self) -> Layout { Layout(vec![LayoutGroup::Row { - widgets: vec![TextLabel::new("Some changes require restart to take effect").bold(true).multiline(true).widget_instance()], + widgets: vec![TextLabel::new("Some changes require restart to take effect").widget_instance()], }]) } } diff --git a/editor/src/messages/dialog/simple_dialogs/demo_artwork_dialog.rs b/editor/src/messages/dialog/simple_dialogs/demo_artwork_dialog.rs index a413954961..7b261b5382 100644 --- a/editor/src/messages/dialog/simple_dialogs/demo_artwork_dialog.rs +++ b/editor/src/messages/dialog/simple_dialogs/demo_artwork_dialog.rs @@ -20,7 +20,7 @@ impl DialogLayoutHolder for DemoArtworkDialog { const TITLE: &'static str = "Demo Artwork"; 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 }]) } diff --git a/editor/src/messages/dialog/simple_dialogs/error_dialog.rs b/editor/src/messages/dialog/simple_dialogs/error_dialog.rs index 9ef98916d4..a47c676acf 100644 --- a/editor/src/messages/dialog/simple_dialogs/error_dialog.rs +++ b/editor/src/messages/dialog/simple_dialogs/error_dialog.rs @@ -12,7 +12,7 @@ impl DialogLayoutHolder for ErrorDialog { const TITLE: &'static str = "Error"; 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 }]) } diff --git a/editor/src/messages/dialog/simple_dialogs/licenses_dialog.rs b/editor/src/messages/dialog/simple_dialogs/licenses_dialog.rs index e4fe579282..ed74893241 100644 --- a/editor/src/messages/dialog/simple_dialogs/licenses_dialog.rs +++ b/editor/src/messages/dialog/simple_dialogs/licenses_dialog.rs @@ -10,7 +10,7 @@ impl DialogLayoutHolder for LicensesDialog { const TITLE: &'static str = "Licenses"; 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 }]) } diff --git a/editor/src/messages/dialog/simple_dialogs/licenses_third_party_dialog.rs b/editor/src/messages/dialog/simple_dialogs/licenses_third_party_dialog.rs index 1f161cf1b6..890803bb00 100644 --- a/editor/src/messages/dialog/simple_dialogs/licenses_third_party_dialog.rs +++ b/editor/src/messages/dialog/simple_dialogs/licenses_third_party_dialog.rs @@ -10,7 +10,7 @@ impl DialogLayoutHolder for LicensesThirdPartyDialog { const TITLE: &'static str = "Third-Party Software License Notices"; 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 }]) } diff --git a/editor/src/messages/frontend/frontend_message.rs b/editor/src/messages/frontend/frontend_message.rs index eeef28fc4b..bf1535c9ad 100644 --- a/editor/src/messages/frontend/frontend_message.rs +++ b/editor/src/messages/frontend/frontend_message.rs @@ -28,7 +28,7 @@ pub enum FrontendMessage { title: String, icon: String, }, - DisplayDialogDismiss, + DialogClose, DisplayDialogPanic { #[serde(rename = "panicInfo")] panic_info: String, diff --git a/editor/src/messages/preferences/preferences_message_handler.rs b/editor/src/messages/preferences/preferences_message_handler.rs index c1cfe31306..e143c03855 100644 --- a/editor/src/messages/preferences/preferences_message_handler.rs +++ b/editor/src/messages/preferences/preferences_message_handler.rs @@ -25,6 +25,10 @@ pub struct 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 { self.selection_mode } diff --git a/editor/src/messages/prelude.rs b/editor/src/messages/prelude.rs index 1e7082e5c8..319c125188 100644 --- a/editor/src/messages/prelude.rs +++ b/editor/src/messages/prelude.rs @@ -12,7 +12,7 @@ pub use crate::messages::debug::{DebugMessage, DebugMessageDiscriminant, DebugMe pub use crate::messages::defer::{DeferMessage, DeferMessageDiscriminant, DeferMessageHandler}; pub use crate::messages::dialog::export_dialog::{ExportDialogMessage, ExportDialogMessageContext, ExportDialogMessageDiscriminant, ExportDialogMessageHandler}; pub use crate::messages::dialog::new_document_dialog::{NewDocumentDialogMessage, NewDocumentDialogMessageDiscriminant, NewDocumentDialogMessageHandler}; -pub use crate::messages::dialog::preferences_dialog::{PreferencesDialogMessage, PreferencesDialogMessageDiscriminant, PreferencesDialogMessageHandler}; +pub use crate::messages::dialog::preferences_dialog::{PreferencesDialogMessage, PreferencesDialogMessageContext, PreferencesDialogMessageDiscriminant, PreferencesDialogMessageHandler}; pub use crate::messages::dialog::{DialogMessage, DialogMessageContext, DialogMessageDiscriminant, DialogMessageHandler}; pub use crate::messages::frontend::{FrontendMessage, FrontendMessageDiscriminant}; pub use crate::messages::input_mapper::key_mapping::{KeyMappingMessage, KeyMappingMessageContext, KeyMappingMessageDiscriminant, KeyMappingMessageHandler}; diff --git a/frontend/src/io-managers/input.ts b/frontend/src/io-managers/input.ts index e1189ff8f8..560bd90a40 100644 --- a/frontend/src/io-managers/input.ts +++ b/frontend/src/io-managers/input.ts @@ -140,7 +140,7 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli } 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; if (get(dialog).visible && !inDialog) { - dialog.dismissDialog(); + editor.handle.onDialogDismiss(); e.preventDefault(); e.stopPropagation(); } diff --git a/frontend/src/messages.ts b/frontend/src/messages.ts index 92b35116f4..d6bdd57fcf 100644 --- a/frontend/src/messages.ts +++ b/frontend/src/messages.ts @@ -884,7 +884,7 @@ export class LayerPanelEntry { clippable!: boolean; } -export class DisplayDialogDismiss extends JsMessage {} +export class DialogClose extends JsMessage {} export class Font { fontFamily!: string; @@ -1671,7 +1671,7 @@ type MessageMaker = typeof JsMessage | JSMessageFactory; export const messageMakers: Record = { ClearAllNodeGraphWires, DisplayDialog, - DisplayDialogDismiss, + DialogClose, DisplayDialogPanic, DisplayEditableTextbox, DisplayEditableTextboxTransform, diff --git a/frontend/src/state-providers/dialog.ts b/frontend/src/state-providers/dialog.ts index 0e93a25537..82b070b1e3 100644 --- a/frontend/src/state-providers/dialog.ts +++ b/frontend/src/state-providers/dialog.ts @@ -2,7 +2,7 @@ import { writable } from "svelte/store"; import { type Editor } from "@graphite/editor"; 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"; export function createDialogState(editor: Editor) { @@ -76,7 +76,7 @@ export function createDialogState(editor: Editor) { return state; }); }); - editor.subscriptions.subscribeJsMessage(DisplayDialogDismiss, dismissDialog); + editor.subscriptions.subscribeJsMessage(DialogClose, dismissDialog); editor.subscriptions.subscribeJsMessage(TriggerDisplayThirdPartyLicensesDialog, async () => { const BACKUP_URL = "https://editor.graphite.art/third-party-licenses.txt"; diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index 48e8a364c8..71661bca2d 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -598,6 +598,13 @@ impl EditorHandle { 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 #[wasm_bindgen(js_name = updateBounds)] pub fn update_bounds(&self, new_text: String) -> Result<(), JsValue> { From 0ff382fc14c6b3f61a30de7dc9e4473c1cecb0a5 Mon Sep 17 00:00:00 2001 From: Timon Date: Tue, 17 Feb 2026 17:32:47 +0000 Subject: [PATCH 3/4] Fix typo --- .../preferences_dialog_message_handler.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs index 7bf3a36d9f..bc7aef10cb 100644 --- a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs +++ b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs @@ -12,7 +12,7 @@ pub struct PreferencesDialogMessageContext<'a> { /// A dialog to allow users to customize Graphite editor options #[derive(Debug, Clone, Default, ExtractField)] pub struct PreferencesDialogMessageHandler { - unmodifyed_preferences: Option, + unmodified_preferences: Option, } #[message_handler_data] @@ -21,13 +21,13 @@ impl MessageHandler { - if self.unmodifyed_preferences.is_none() { - self.unmodifyed_preferences = Some(preferences.clone()); + if self.unmodified_preferences.is_none() { + self.unmodified_preferences = Some(preferences.clone()); } } PreferencesDialogMessage::Confirm => { - if let Some(unmodifyed_preferences) = &self.unmodifyed_preferences - && unmodifyed_preferences.needs_restart(preferences) + if let Some(unmodified_preferences) = &self.unmodified_preferences + && unmodified_preferences.needs_restart(preferences) { responses.add(DialogMessage::RequestConfirmRestartDialog); } else { From bb189fbf62ce62d3ce1f4a3e3920afa32cc96068 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Wed, 18 Feb 2026 17:34:41 -0800 Subject: [PATCH 4/4] Code review and update strings --- .../messages/dialog/dialog_message_handler.rs | 14 ++++---- .../preferences_dialog_message.rs | 2 +- .../preferences_dialog_message_handler.rs | 16 +++++---- .../simple_dialogs/confirm_restart_dialog.rs | 34 ++++++++++++++++--- 4 files changed, 48 insertions(+), 18 deletions(-) diff --git a/editor/src/messages/dialog/dialog_message_handler.rs b/editor/src/messages/dialog/dialog_message_handler.rs index 6ba420e0c5..1621de1c3e 100644 --- a/editor/src/messages/dialog/dialog_message_handler.rs +++ b/editor/src/messages/dialog/dialog_message_handler.rs @@ -103,10 +103,10 @@ impl MessageHandler> for DialogMessageHa self.export_dialog.artboards = artboards; - if let ExportBounds::Artboard(layer) = self.export_dialog.bounds { - if !self.export_dialog.artboards.contains_key(&layer) { - self.export_dialog.bounds = ExportBounds::AllArtwork; - } + if let ExportBounds::Artboard(layer) = self.export_dialog.bounds + && !self.export_dialog.artboards.contains_key(&layer) + { + self.export_dialog.bounds = ExportBounds::AllArtwork; } self.export_dialog.has_selection = document.network_interface.selected_nodes().selected_layers(document.metadata()).next().is_some(); @@ -137,8 +137,10 @@ impl MessageHandler> for DialogMessageHa self.preferences_dialog.send_dialog_to_frontend(responses, preferences); } DialogMessage::RequestConfirmRestartDialog => { - self.on_dismiss = None; - let dialog = ConfirmRestartDialog {}; + self.on_dismiss = Some(DialogMessage::Close.into()); + let dialog = ConfirmRestartDialog { + changed_settings: vec!["Disable UI Acceleration".into()], + }; dialog.send_dialog_to_frontend(responses); } } diff --git a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message.rs b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message.rs index 93978daa09..db99d4f987 100644 --- a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message.rs +++ b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message.rs @@ -3,6 +3,6 @@ use crate::messages::prelude::*; #[impl_message(Message, DialogMessage, PreferencesDialog)] #[derive(Eq, PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)] pub enum PreferencesDialogMessage { - MightRequireRestart, + MayRequireRestart, Confirm, } diff --git a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs index bc7aef10cb..33755de44b 100644 --- a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs +++ b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs @@ -20,7 +20,7 @@ impl MessageHandler, context: PreferencesDialogMessageContext) { let PreferencesDialogMessageContext { preferences } = context; match message { - PreferencesDialogMessage::MightRequireRestart => { + PreferencesDialogMessage::MayRequireRestart => { if self.unmodified_preferences.is_none() { self.unmodified_preferences = Some(preferences.clone()); } @@ -273,7 +273,7 @@ impl PreferencesDialogMessageHandler { let brush_tool_description = " Enable the Brush tool to support basic raster-based layer painting.\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\ Content created with the Brush tool may not be compatible with future versions of Graphite. " @@ -298,14 +298,18 @@ impl PreferencesDialogMessageHandler { } // ============= - // COMPATEBILITY + // COMPATIBILITY // ============= #[cfg(not(target_family = "wasm"))] { let header = vec![TextLabel::new("Compatibility").italic(true).widget_instance()]; - let ui_acceleration_description = - "Disable hardware acceleration for the user interface (viewport is not effected). This can improve stability on some systems at the cost of decreased performance."; + 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![ @@ -316,7 +320,7 @@ impl PreferencesDialogMessageHandler { .tooltip_description(ui_acceleration_description) .on_update(|number_input: &CheckboxInput| Message::Batched { messages: Box::new([ - PreferencesDialogMessage::MightRequireRestart.into(), + PreferencesDialogMessage::MayRequireRestart.into(), PreferencesMessage::DisableUIAcceleration { disable_ui_acceleration: number_input.checked, } diff --git a/editor/src/messages/dialog/simple_dialogs/confirm_restart_dialog.rs b/editor/src/messages/dialog/simple_dialogs/confirm_restart_dialog.rs index dbc5d8a2f3..17856f9b8c 100644 --- a/editor/src/messages/dialog/simple_dialogs/confirm_restart_dialog.rs +++ b/editor/src/messages/dialog/simple_dialogs/confirm_restart_dialog.rs @@ -1,8 +1,10 @@ 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 {} +/// 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, +} impl DialogLayoutHolder for ConfirmRestartDialog { const ICON: &'static str = "Warning"; @@ -28,8 +30,30 @@ impl DialogLayoutHolder for ConfirmRestartDialog { impl LayoutHolder for ConfirmRestartDialog { fn layout(&self) -> Layout { - Layout(vec![LayoutGroup::Row { - widgets: vec![TextLabel::new("Some changes require restart to take effect").widget_instance()], - }]) + 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(), + ], + }, + ]) } }