Skip to content

Commit b100892

Browse files
authored
Add support for persistent storage of panel layouts, sizes, and active tabs (#4017)
* Add persistence to panel layouts * Fix and persist the Window > Focus Document mode * Add a Window > Reset Workspace action * workspace_layout.json -> workspace_layout.ron * Fix native app hole punch * Cleanup review pass
1 parent b099e2f commit b100892

File tree

20 files changed

+346
-107
lines changed

20 files changed

+346
-107
lines changed

desktop/src/app.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use crate::persist::PersistentData;
2121
use crate::preferences;
2222
use crate::render::{RenderError, RenderState};
2323
use crate::window::Window;
24+
use crate::workspace_layout;
2425
use crate::wrapper::messages::{DesktopFrontendMessage, DesktopWrapperMessage, InputMessage, MouseKeys, MouseState, Preferences};
2526
use crate::wrapper::{DesktopWrapper, NodeGraphExecutionResult, WgpuContext, serialize_frontend_messages};
2627

@@ -304,6 +305,15 @@ impl App {
304305
let message = DesktopWrapperMessage::LoadPreferences { preferences };
305306
responses.push(message);
306307
}
308+
DesktopFrontendMessage::PersistenceWriteWorkspaceLayout { workspace_layout: layout } => {
309+
workspace_layout::write(&layout);
310+
}
311+
DesktopFrontendMessage::PersistenceLoadWorkspaceLayout => {
312+
if let Some(workspace_layout) = workspace_layout::read() {
313+
let message = DesktopWrapperMessage::LoadWorkspaceLayout { workspace_layout };
314+
responses.push(message);
315+
}
316+
}
307317
DesktopFrontendMessage::PersistenceLoadCurrentDocument => {
308318
if let Some((id, document)) = self.persistent_data.current_document() {
309319
let message = DesktopWrapperMessage::LoadDocument {

desktop/src/consts.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub(crate) const APP_DIRECTORY_NAME: &str = "Graphite";
99
pub(crate) const APP_LOCK_FILE_NAME: &str = "instance.lock";
1010
pub(crate) const APP_STATE_FILE_NAME: &str = "state.ron";
1111
pub(crate) const APP_PREFERENCES_FILE_NAME: &str = "preferences.ron";
12+
pub(crate) const APP_WORKSPACE_LAYOUT_FILE_NAME: &str = "workspace_layout.ron";
1213
pub(crate) const APP_DOCUMENTS_DIRECTORY_NAME: &str = "documents";
1314

1415
// CEF configuration constants

desktop/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ mod persist;
2020
mod preferences;
2121
mod render;
2222
mod window;
23+
mod workspace_layout;
2324

2425
pub(crate) mod consts;
2526

desktop/src/window/win/native_handle.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ impl NativeWindowHandle {
7272

7373
// Subclass the main window.
7474
// https://learn.microsoft.com/windows/win32/api/winuser/nf-winuser-setwindowlongptra
75-
let prev_window_message_handler = unsafe { SetWindowLongPtrW(main, GWLP_WNDPROC, main_window_handle_message as isize) };
75+
let prev_window_message_handler = unsafe { SetWindowLongPtrW(main, GWLP_WNDPROC, main_window_handle_message as *const () as isize) };
7676
if prev_window_message_handler == 0 {
7777
let _ = unsafe { DestroyWindow(helper) };
7878
panic!("SetWindowLongPtrW failed");

desktop/src/workspace_layout.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
pub(crate) fn write(workspace_layout: &str) {
2+
std::fs::write(file_path(), workspace_layout).unwrap_or_else(|e| {
3+
tracing::error!("Failed to write workspace layout to disk: {e}");
4+
});
5+
}
6+
7+
pub(crate) fn read() -> Option<String> {
8+
std::fs::read_to_string(file_path()).ok()
9+
}
10+
11+
fn file_path() -> std::path::PathBuf {
12+
let mut path = crate::dirs::app_data_dir();
13+
path.push(crate::consts::APP_WORKSPACE_LAYOUT_FILE_NAME);
14+
path
15+
}

desktop/wrapper/src/handle_desktop_wrapper_message.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,15 @@ pub(super) fn handle_desktop_wrapper_message(dispatcher: &mut DesktopWrapperMess
7575
let message = PreferencesMessage::Load { preferences };
7676
dispatcher.queue_editor_message(message);
7777
}
78+
DesktopWrapperMessage::LoadWorkspaceLayout { workspace_layout } => match ron::from_str(&workspace_layout) {
79+
Ok(layout) => {
80+
let message = PortfolioMessage::LoadWorkspaceLayout { layout };
81+
dispatcher.queue_editor_message(message);
82+
}
83+
Err(e) => {
84+
tracing::error!("Failed to deserialize workspace layout: {e}");
85+
}
86+
},
7887
#[cfg(target_os = "macos")]
7988
DesktopWrapperMessage::MenuEvent { id } => {
8089
if let Some(message) = crate::utils::menu::parse_item_path(id) {

desktop/wrapper/src/intercept_frontend_message.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,16 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD
110110
FrontendMessage::TriggerLoadPreferences => {
111111
dispatcher.respond(DesktopFrontendMessage::PersistenceLoadPreferences);
112112
}
113+
FrontendMessage::TriggerSaveWorkspaceLayout { workspace_layout } => {
114+
let Ok(workspace_layout) = ron::ser::to_string_pretty(&workspace_layout, ron::ser::PrettyConfig::default()) else {
115+
tracing::error!("Failed to serialize workspace layout");
116+
return None;
117+
};
118+
dispatcher.respond(DesktopFrontendMessage::PersistenceWriteWorkspaceLayout { workspace_layout });
119+
}
120+
FrontendMessage::TriggerLoadWorkspaceLayout => {
121+
dispatcher.respond(DesktopFrontendMessage::PersistenceLoadWorkspaceLayout);
122+
}
113123
#[cfg(target_os = "macos")]
114124
FrontendMessage::UpdateLayout {
115125
layout_target: LayoutTarget::MenuBar,

desktop/wrapper/src/messages.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ pub enum DesktopFrontendMessage {
5858
preferences: Preferences,
5959
},
6060
PersistenceLoadPreferences,
61+
PersistenceWriteWorkspaceLayout {
62+
workspace_layout: String,
63+
},
64+
PersistenceLoadWorkspaceLayout,
6165
UpdateMenu {
6266
entries: Vec<MenuItem>,
6367
},
@@ -117,6 +121,9 @@ pub enum DesktopWrapperMessage {
117121
LoadPreferences {
118122
preferences: Preferences,
119123
},
124+
LoadWorkspaceLayout {
125+
workspace_layout: String,
126+
},
120127
MenuEvent {
121128
id: String,
122129
},

editor/src/dispatcher.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ impl Dispatcher {
234234
Message::MenuBar(message) => {
235235
let menu_bar_message_handler = &mut self.message_handlers.menu_bar_message_handler;
236236

237-
menu_bar_message_handler.focus_document = self.message_handlers.portfolio_message_handler.focus_document;
237+
menu_bar_message_handler.focus_document = self.message_handlers.portfolio_message_handler.workspace_panel_layout.focus_document;
238238
let layout = &self.message_handlers.portfolio_message_handler.workspace_panel_layout;
239239
menu_bar_message_handler.data_panel_open = layout.is_panel_present(PanelType::Data);
240240
menu_bar_message_handler.layers_panel_open = layout.is_panel_present(PanelType::Layers);

editor/src/messages/frontend/frontend_message.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,17 @@ pub enum FrontendMessage {
127127
TriggerLoadRestAutoSaveDocuments,
128128
TriggerOpenLaunchDocuments,
129129
TriggerLoadPreferences,
130+
TriggerLoadWorkspaceLayout,
130131
TriggerOpen,
131132
TriggerImport,
132133
TriggerSavePreferences {
133134
#[tsify(type = "unknown")]
134135
preferences: PreferencesMessageHandler,
135136
},
137+
TriggerSaveWorkspaceLayout {
138+
#[serde(rename = "workspaceLayout")]
139+
workspace_layout: WorkspacePanelLayout,
140+
},
136141
TriggerSaveActiveDocument {
137142
#[serde(rename = "documentId")]
138143
document_id: DocumentId,

0 commit comments

Comments
 (0)