Desktop: Move preferences persistence to native (#3138)

* Move preference persistence to native

* Pass preferences as struct instead of serialized to string
This commit is contained in:
Timon
2025-09-09 16:22:25 +00:00
committed by GitHub
parent 4261b7dad1
commit 50be13522b
12 changed files with 80 additions and 20 deletions
Generated
+1
View File
@@ -2365,6 +2365,7 @@ dependencies = [
"ron", "ron",
"serde", "serde",
"serde-wasm-bindgen", "serde-wasm-bindgen",
"serde_json",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
"web-sys", "web-sys",
+9
View File
@@ -214,6 +214,15 @@ impl WinitApp {
self.dispatch_desktop_wrapper_message(message); self.dispatch_desktop_wrapper_message(message);
} }
} }
DesktopFrontendMessage::PersistenceWritePreferences { preferences } => {
self.persistent_data.write_preferences(preferences);
}
DesktopFrontendMessage::PersistenceLoadPreferences => {
if let Some(preferences) = self.persistent_data.load_preferences() {
let message = DesktopWrapperMessage::LoadPreferences { preferences };
self.dispatch_desktop_wrapper_message(message);
}
}
} }
} }
+4 -1
View File
@@ -1,7 +1,10 @@
pub(crate) static APP_NAME: &str = "Graphite"; pub(crate) static APP_NAME: &str = "Graphite";
pub(crate) static APP_ID: &str = "rs.graphite.GraphiteEditor"; pub(crate) static APP_ID: &str = "rs.graphite.GraphiteEditor";
pub(crate) static APP_DIRECTORY_NAME: &str = "graphite-editor"; pub(crate) static APP_DIRECTORY_NAME: &str = "graphite-editor";
pub(crate) static APP_AUTOSAVE_DIRECTORY_NAME: &str = "documents"; pub(crate) static APP_STATE_FILE_NAME: &str = "state.ron";
pub(crate) static APP_PREFERENCES_FILE_NAME: &str = "preferences.ron";
pub(crate) static APP_DOCUMENTS_DIRECTORY_NAME: &str = "documents";
// CEF configuration constants // CEF configuration constants
pub(crate) const CEF_WINDOWLESS_FRAME_RATE: i32 = 60; pub(crate) const CEF_WINDOWLESS_FRAME_RATE: i32 = 60;
+2 -2
View File
@@ -1,7 +1,7 @@
use std::fs::create_dir_all; use std::fs::create_dir_all;
use std::path::PathBuf; use std::path::PathBuf;
use crate::consts::{APP_AUTOSAVE_DIRECTORY_NAME, APP_DIRECTORY_NAME}; use crate::consts::{APP_DIRECTORY_NAME, APP_DOCUMENTS_DIRECTORY_NAME};
pub(crate) fn ensure_dir_exists(path: &PathBuf) { pub(crate) fn ensure_dir_exists(path: &PathBuf) {
if !path.exists() { if !path.exists() {
@@ -16,7 +16,7 @@ pub(crate) fn graphite_data_dir() -> PathBuf {
} }
pub(crate) fn graphite_autosave_documents_dir() -> PathBuf { pub(crate) fn graphite_autosave_documents_dir() -> PathBuf {
let path = graphite_data_dir().join(APP_AUTOSAVE_DIRECTORY_NAME); let path = graphite_data_dir().join(APP_DOCUMENTS_DIRECTORY_NAME);
ensure_dir_exists(&path); ensure_dir_exists(&path);
path path
} }
+29 -6
View File
@@ -1,4 +1,4 @@
use graphite_desktop_wrapper::messages::{Document, DocumentId}; use graphite_desktop_wrapper::messages::{Document, DocumentId, Preferences};
#[derive(Default, serde::Serialize, serde::Deserialize)] #[derive(Default, serde::Serialize, serde::Deserialize)]
pub(crate) struct PersistentData { pub(crate) struct PersistentData {
@@ -6,6 +6,7 @@ pub(crate) struct PersistentData {
current_document: Option<DocumentId>, current_document: Option<DocumentId>,
#[serde(skip)] #[serde(skip)]
document_order: Option<Vec<DocumentId>>, document_order: Option<Vec<DocumentId>>,
preferences: Option<Preferences>,
} }
impl PersistentData { impl PersistentData {
@@ -72,21 +73,37 @@ 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::to_string(self) { let data = match ron::ser::to_string_pretty(self, Default::default()) {
Ok(d) => d, Ok(d) => d,
Err(e) => { Err(e) => {
tracing::error!("Failed to serialize persistent data: {e}"); tracing::error!("Failed to serialize persistent data: {e}");
return; return;
} }
}; };
if let Err(e) = std::fs::write(Self::persistence_file_path(), data) { if let Err(e) = std::fs::write(Self::state_file_path(), data) {
tracing::error!("Failed to write persistent data to disk: {e}"); tracing::error!("Failed to write persistent data to disk: {e}");
} }
} }
pub(crate) fn load_from_disk(&mut self) { pub(crate) fn load_from_disk(&mut self) {
let path = Self::persistence_file_path(); let path = Self::state_file_path();
let data = match std::fs::read_to_string(&path) { let data = match std::fs::read_to_string(&path) {
Ok(d) => d, Ok(d) => d,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => { Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
@@ -108,9 +125,15 @@ impl PersistentData {
*self = loaded; *self = loaded;
} }
fn persistence_file_path() -> std::path::PathBuf { fn state_file_path() -> std::path::PathBuf {
let mut path = crate::dirs::graphite_data_dir(); let mut path = crate::dirs::graphite_data_dir();
path.push(format!("{}.ron", crate::consts::APP_AUTOSAVE_DIRECTORY_NAME)); path.push(crate::consts::APP_STATE_FILE_NAME);
path
}
fn preferences_file_path() -> std::path::PathBuf {
let mut path = crate::dirs::graphite_data_dir();
path.push(crate::consts::APP_PREFERENCES_FILE_NAME);
path path
} }
} }
@@ -1,7 +1,7 @@
use graphene_std::Color; use graphene_std::Color;
use graphene_std::raster::Image; use graphene_std::raster::Image;
use graphite_editor::messages::app_window::app_window_message_handler::AppWindowPlatform; use graphite_editor::messages::app_window::app_window_message_handler::AppWindowPlatform;
use graphite_editor::messages::prelude::{AppWindowMessage, DocumentMessage, PortfolioMessage}; use graphite_editor::messages::prelude::{AppWindowMessage, DocumentMessage, PortfolioMessage, PreferencesMessage};
use crate::messages::Platform; use crate::messages::Platform;
@@ -140,5 +140,9 @@ pub(super) fn handle_desktop_wrapper_message(dispatcher: &mut DesktopWrapperMess
let message = PortfolioMessage::SelectDocument { document_id: id }; let message = PortfolioMessage::SelectDocument { document_id: id };
dispatcher.queue_editor_message(message.into()); dispatcher.queue_editor_message(message.into());
} }
DesktopWrapperMessage::LoadPreferences { preferences } => {
let message = PreferencesMessage::Load { preferences };
dispatcher.queue_editor_message(message.into());
}
} }
} }
@@ -107,6 +107,12 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD
FrontendMessage::TriggerLoadRestAutoSaveDocuments => { FrontendMessage::TriggerLoadRestAutoSaveDocuments => {
dispatcher.respond(DesktopFrontendMessage::PersistenceLoadRemainingDocuments); dispatcher.respond(DesktopFrontendMessage::PersistenceLoadRemainingDocuments);
} }
FrontendMessage::TriggerSavePreferences { preferences } => {
dispatcher.respond(DesktopFrontendMessage::PersistenceWritePreferences { preferences });
}
FrontendMessage::TriggerLoadPreferences => {
dispatcher.respond(DesktopFrontendMessage::PersistenceLoadPreferences);
}
m => return Some(m), m => return Some(m),
} }
None None
+11 -1
View File
@@ -1,8 +1,11 @@
pub use graphite_editor::messages::prelude::DocumentId; pub use graphite_editor::messages::prelude::DocumentId;
use graphite_editor::messages::prelude::FrontendMessage; use graphite_editor::messages::prelude::FrontendMessage;
pub(crate) use graphite_editor::messages::prelude::Message as EditorMessage;
use std::path::PathBuf; use std::path::PathBuf;
pub(crate) use graphite_editor::messages::prelude::Message as EditorMessage;
pub use graphite_editor::messages::prelude::PreferencesMessageHandler as Preferences;
pub enum DesktopFrontendMessage { pub enum DesktopFrontendMessage {
ToWeb(Vec<FrontendMessage>), ToWeb(Vec<FrontendMessage>),
OpenFileDialog { OpenFileDialog {
@@ -48,6 +51,10 @@ pub enum DesktopFrontendMessage {
PersistenceUpdateDocumentsList { PersistenceUpdateDocumentsList {
ids: Vec<DocumentId>, ids: Vec<DocumentId>,
}, },
PersistenceWritePreferences {
preferences: Preferences,
},
PersistenceLoadPreferences,
CloseWindow, CloseWindow,
} }
@@ -93,6 +100,9 @@ pub enum DesktopWrapperMessage {
SelectDocument { SelectDocument {
id: DocumentId, id: DocumentId,
}, },
LoadPreferences {
preferences: Preferences,
},
} }
#[derive(Clone, serde::Serialize, serde::Deserialize, Debug)] #[derive(Clone, serde::Serialize, serde::Deserialize, Debug)]
@@ -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: String }, Load { preferences: PreferencesMessageHandler },
ResetToDefaults, ResetToDefaults,
// Per-preference messages // Per-preference messages
@@ -50,15 +50,13 @@ impl MessageHandler<PreferencesMessage, ()> for PreferencesMessageHandler {
match message { match message {
// Management messages // Management messages
PreferencesMessage::Load { preferences } => { PreferencesMessage::Load { preferences } => {
if let Ok(deserialized_preferences) = serde_json::from_str::<PreferencesMessageHandler>(&preferences) { *self = preferences;
*self = deserialized_preferences;
responses.add(PortfolioMessage::EditorPreferences); responses.add(PortfolioMessage::EditorPreferences);
responses.add(PortfolioMessage::UpdateVelloPreference); responses.add(PortfolioMessage::UpdateVelloPreference);
responses.add(PreferencesMessage::ModifyLayout { responses.add(PreferencesMessage::ModifyLayout {
zoom_with_scroll: self.zoom_with_scroll, zoom_with_scroll: self.zoom_with_scroll,
}); });
}
} }
PreferencesMessage::ResetToDefaults => { PreferencesMessage::ResetToDefaults => {
refresh_dialog(responses); refresh_dialog(responses);
+1
View File
@@ -40,6 +40,7 @@ math-parser = { workspace = true }
wgpu = { workspace = true } wgpu = { workspace = true }
web-sys = { workspace = true } web-sys = { workspace = true }
ron = { workspace = true } ron = { workspace = true }
serde_json = { workspace = true }
[package.metadata.wasm-pack.profile.dev] [package.metadata.wasm-pack.profile.dev]
wasm-opt = false wasm-opt = false
+5
View File
@@ -415,6 +415,11 @@ impl EditorHandle {
#[wasm_bindgen(js_name = loadPreferences)] #[wasm_bindgen(js_name = loadPreferences)]
pub fn load_preferences(&self, preferences: String) { pub fn load_preferences(&self, preferences: String) {
let Ok(preferences) = serde_json::from_str(&preferences) else {
log::error!("Failed to deserialize preferences");
return;
};
let message = PreferencesMessage::Load { preferences }; let message = PreferencesMessage::Load { preferences };
self.dispatch(message); self.dispatch(message);