Refactor persistence to combine document handling and workspace layout (#4031)

* Unify editor state persistence

* Review

* Fix

* Remove redundant DocumentDetails

* LoadDocumentContent indirection
This commit is contained in:
Timon
2026-04-19 11:31:21 +02:00
committed by GitHub
parent 2a2a60883d
commit 6c5e3c97f8
36 changed files with 562 additions and 699 deletions

View File

@@ -295,7 +295,7 @@ impl Dispatcher {
document_id,
document,
input: &self.message_handlers.input_preprocessor_message_handler,
persistent_data: &self.message_handlers.portfolio_message_handler.persistent_data,
cached_data: &self.message_handlers.portfolio_message_handler.cached_data,
node_graph: &self.message_handlers.portfolio_message_handler.executor,
preferences: &self.message_handlers.preferences_message_handler,
viewport: &self.message_handlers.viewport_message_handler,

View File

@@ -1,7 +1,7 @@
use super::IconName;
use super::utility_types::{DocumentDetails, MouseCursorIcon, OpenDocument};
use super::utility_types::{MouseCursorIcon, PersistedState};
use crate::messages::app_window::app_window_message_handler::AppWindowPlatform;
use crate::messages::frontend::utility_types::EyedropperPreviewImage;
use crate::messages::frontend::utility_types::{DocumentInfo, EyedropperPreviewImage};
use crate::messages::input_mapper::utility_types::misc::ActionShortcut;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::utility_types::{
@@ -113,7 +113,15 @@ pub enum FrontendMessage {
font: Font,
url: String,
},
TriggerPersistenceRemoveDocument {
TriggerPersistenceReadState,
TriggerPersistenceReadDocument {
#[serde(rename = "documentId")]
document_id: DocumentId,
},
TriggerPersistenceWriteState {
state: PersistedState,
},
TriggerPersistenceDeleteDocument {
#[serde(rename = "documentId")]
document_id: DocumentId,
},
@@ -121,26 +129,15 @@ pub enum FrontendMessage {
#[serde(rename = "documentId")]
document_id: DocumentId,
document: String,
details: DocumentDetails,
},
TriggerLoadAutoSaveDocuments,
TriggerOpenLaunchDocuments,
TriggerLoadPreferences,
TriggerLoadWorkspaceLayout,
TriggerOpen,
TriggerImport,
TriggerSavePreferences {
#[cfg_attr(feature = "wasm", tsify(type = "unknown"))]
preferences: PreferencesMessageHandler,
},
TriggerSaveWorkspaceLayout {
#[serde(rename = "workspaceLayout")]
workspace_layout: WorkspacePanelLayout,
},
TriggerSaveActiveDocument {
#[serde(rename = "documentId")]
document_id: DocumentId,
},
TriggerTextCommit,
TriggerVisitLink {
url: String,
@@ -291,7 +288,7 @@ pub enum FrontendMessage {
},
UpdateOpenDocumentsList {
#[serde(rename = "openDocuments")]
open_documents: Vec<OpenDocument>,
open_documents: Vec<DocumentInfo>,
},
UpdateWirePathInProgress {
#[serde(rename = "wirePath")]

View File

@@ -1,29 +1,12 @@
use std::path::PathBuf;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::utility_types::WorkspacePanelLayout;
use crate::messages::prelude::*;
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct OpenDocument {
pub id: DocumentId,
pub details: DocumentDetails,
}
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct DocumentDetails {
pub name: String,
pub path: Option<PathBuf>,
#[serde(alias = "isSaved")]
pub is_saved: bool,
#[serde(alias = "isAutoSaved")]
pub is_auto_saved: bool,
}
#[cfg_attr(feature = "wasm", derive(tsify::Tsify), tsify(large_number_types_as_bigints))]
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct PersistedDocumentInfo {
pub struct DocumentInfo {
pub id: DocumentId,
pub name: String,
#[serde(default)]
@@ -34,8 +17,10 @@ pub struct PersistedDocumentInfo {
#[cfg_attr(feature = "wasm", derive(tsify::Tsify), tsify(large_number_types_as_bigints))]
#[derive(Clone, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct PersistedState {
pub documents: Vec<PersistedDocumentInfo>,
pub documents: Vec<DocumentInfo>,
pub current_document: Option<DocumentId>,
#[serde(default)]
pub workspace_layout: Option<WorkspacePanelLayout>,
}
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]

View File

@@ -21,7 +21,7 @@ use crate::messages::portfolio::document::properties_panel::properties_panel_mes
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis, PTZ};
use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, InputConnector, NodeTemplate};
use crate::messages::portfolio::utility_types::{PanelType, PersistentData};
use crate::messages::portfolio::utility_types::{CachedData, PanelType};
use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::graph_modification_utils::{self, get_blend_mode, get_fill, get_opacity};
use crate::messages::tool::tool_messages::select_tool::SelectToolPointerKeys;
@@ -52,7 +52,7 @@ use std::time::Duration;
pub struct DocumentMessageContext<'a> {
pub document_id: DocumentId,
pub ipp: &'a InputPreprocessorMessageHandler,
pub persistent_data: &'a PersistentData,
pub cached_data: &'a CachedData,
pub executor: &'a mut NodeGraphExecutor,
pub current_tool: &'a ToolType,
pub preferences: &'a PreferencesMessageHandler,
@@ -193,7 +193,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
let DocumentMessageContext {
document_id,
ipp,
persistent_data,
cached_data,
executor,
viewport,
current_tool,
@@ -231,7 +231,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
selection_network_path: &self.selection_network_path,
document_name: self.name.as_str(),
executor,
persistent_data,
cached_data,
properties_panel_open,
};
self.properties_panel_message_handler.process_message(message, responses, context);

View File

@@ -8,7 +8,7 @@ use crate::messages::portfolio::document::utility_types::network_interface::{
DocumentNodeMetadata, DocumentNodePersistentMetadata, InputMetadata, NodeNetworkInterface, NodeNetworkMetadata, NodeNetworkPersistentMetadata, NodeTemplate, NodeTypePersistentMetadata,
NumberInputSettings, Vec2InputSettings, WidgetOverride,
};
use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::portfolio::utility_types::CachedData;
use crate::messages::prelude::Message;
use crate::node_graph_executor::NodeGraphExecutor;
use glam::DVec2;
@@ -29,7 +29,7 @@ use serde_json::Value;
use std::collections::{HashMap, VecDeque};
pub struct NodePropertiesContext<'a> {
pub persistent_data: &'a PersistentData,
pub cached_data: &'a CachedData,
pub responses: &'a mut VecDeque<Message>,
pub executor: &'a mut NodeGraphExecutor,
pub network_interface: &'a mut NodeNetworkInterface,

View File

@@ -5,7 +5,7 @@ use super::utility_types::FrontendGraphDataType;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeNetworkInterface};
use crate::messages::portfolio::utility_types::{FontCatalogStyle, PersistentData};
use crate::messages::portfolio::utility_types::{CachedData, FontCatalogStyle};
use crate::messages::prelude::*;
use choice::enum_choice;
use dyn_any::DynAny;
@@ -793,7 +793,7 @@ pub fn array_of_vec2_widget(parameter_widgets_info: ParameterWidgetsInfo, text_p
pub fn font_inputs(parameter_widgets_info: ParameterWidgetsInfo) -> (Vec<WidgetInstance>, Option<Vec<WidgetInstance>>) {
let ParameterWidgetsInfo {
persistent_data,
cached_data,
document_node,
node_id,
index,
@@ -813,7 +813,7 @@ pub fn font_inputs(parameter_widgets_info: ParameterWidgetsInfo) -> (Vec<WidgetI
first_widgets.extend_from_slice(&[
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
DropdownInput::new(vec![
persistent_data
cached_data
.font_catalog
.0
.iter()
@@ -866,7 +866,7 @@ pub fn font_inputs(parameter_widgets_info: ParameterWidgetsInfo) -> (Vec<WidgetI
})
.collect::<Vec<_>>(),
])
.selected_index(persistent_data.font_catalog.0.iter().position(|family| family.name == font.font_family).map(|i| i as u32))
.selected_index(cached_data.font_catalog.0.iter().position(|family| family.name == font.font_family).map(|i| i as u32))
.virtual_scrolling(true)
.widget_instance(),
]);
@@ -876,7 +876,7 @@ pub fn font_inputs(parameter_widgets_info: ParameterWidgetsInfo) -> (Vec<WidgetI
second_row.extend_from_slice(&[
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
DropdownInput::new({
persistent_data
cached_data
.font_catalog
.0
.iter()
@@ -914,7 +914,7 @@ pub fn font_inputs(parameter_widgets_info: ParameterWidgetsInfo) -> (Vec<WidgetI
.unwrap_or_default()
})
.selected_index(
persistent_data
cached_data
.font_catalog
.0
.iter()
@@ -2208,7 +2208,7 @@ pub fn math_properties(node_id: NodeId, context: &mut NodePropertiesContext) ->
}
pub struct ParameterWidgetsInfo<'a> {
persistent_data: &'a PersistentData,
cached_data: &'a CachedData,
network_interface: &'a NodeNetworkInterface,
selection_network_path: &'a [NodeId],
document_node: Option<&'a DocumentNode>,
@@ -2231,7 +2231,7 @@ impl<'a> ParameterWidgetsInfo<'a> {
let document_node = context.network_interface.document_node(&node_id, context.selection_network_path);
ParameterWidgetsInfo {
persistent_data: context.persistent_data,
cached_data: context.cached_data,
network_interface: context.network_interface,
selection_network_path: context.selection_network_path,
document_node,

View File

@@ -3,7 +3,7 @@ use graphene_std::uuid::NodeId;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::document_node_definitions::NodePropertiesContext;
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::portfolio::utility_types::CachedData;
use crate::messages::prelude::*;
use crate::node_graph_executor::NodeGraphExecutor;
@@ -13,7 +13,7 @@ pub struct PropertiesPanelMessageContext<'a> {
pub selection_network_path: &'a [NodeId],
pub document_name: &'a str,
pub executor: &'a mut NodeGraphExecutor,
pub persistent_data: &'a PersistentData,
pub cached_data: &'a CachedData,
pub properties_panel_open: bool,
}
@@ -28,7 +28,7 @@ impl MessageHandler<PropertiesPanelMessage, PropertiesPanelMessageContext<'_>> f
selection_network_path,
document_name,
executor,
persistent_data,
cached_data,
properties_panel_open,
} = context;
@@ -46,7 +46,7 @@ impl MessageHandler<PropertiesPanelMessage, PropertiesPanelMessageContext<'_>> f
}
let mut node_properties_context = NodePropertiesContext {
persistent_data,
cached_data,
responses,
network_interface,
selection_network_path,

View File

@@ -3,8 +3,11 @@ mod portfolio_message_handler;
pub mod document;
pub mod document_migration;
pub mod persistent_state;
pub mod utility_types;
#[doc(inline)]
pub use persistent_state::{PersistentStateMessage, PersistentStateMessageContext, PersistentStateMessageHandler};
#[doc(inline)]
pub use portfolio_message::{PortfolioMessage, PortfolioMessageDiscriminant};
#[doc(inline)]

View File

@@ -0,0 +1,7 @@
mod persistent_state_message;
mod persistent_state_message_handler;
#[doc(inline)]
pub use persistent_state_message::{PersistentStateMessage, PersistentStateMessageDiscriminant};
#[doc(inline)]
pub use persistent_state_message_handler::{PersistentStateMessageContext, PersistentStateMessageHandler};

View File

@@ -0,0 +1,30 @@
use crate::messages::frontend::utility_types::PersistedState;
use crate::messages::prelude::*;
#[impl_message(Message, PortfolioMessage, PersistentState)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum PersistentStateMessage {
ReadState,
WriteState,
LoadState {
state: PersistedState,
},
ReadDocument {
#[serde(rename = "documentId")]
document_id: DocumentId,
},
WriteDocument {
#[serde(rename = "documentId")]
document_id: DocumentId,
document: String,
},
DeleteDocument {
#[serde(rename = "documentId")]
document_id: DocumentId,
},
LoadDocument {
#[serde(rename = "documentId")]
document_id: DocumentId,
document: String,
},
}

View File

@@ -0,0 +1,54 @@
use crate::messages::frontend::utility_types::PersistedState;
use crate::messages::portfolio::utility_types::WorkspacePanelLayout;
use crate::messages::prelude::*;
#[derive(Default, Debug, Clone, ExtractField)]
pub struct PersistentStateMessageHandler {
loaded: bool,
}
#[derive(ExtractField)]
pub struct PersistentStateMessageContext {
pub persisted_state: PersistedState,
}
#[message_handler_data]
impl MessageHandler<PersistentStateMessage, PersistentStateMessageContext> for PersistentStateMessageHandler {
fn process_message(&mut self, message: PersistentStateMessage, responses: &mut VecDeque<Message>, context: PersistentStateMessageContext) {
let PersistentStateMessageContext { persisted_state: state } = context;
match message {
PersistentStateMessage::ReadState => {
responses.add(FrontendMessage::TriggerPersistenceReadState);
}
PersistentStateMessage::WriteState => {
if !self.loaded && (state.documents.is_empty() && (state.workspace_layout == Some(WorkspacePanelLayout::default()) || state.workspace_layout.is_none())) {
return;
}
self.loaded = true;
responses.add(FrontendMessage::TriggerPersistenceWriteState { state });
}
PersistentStateMessage::LoadState { state } => {
self.loaded = true;
responses.add(PortfolioMessage::LoadPersistedState { state });
}
PersistentStateMessage::ReadDocument { document_id } => {
responses.add(FrontendMessage::TriggerPersistenceReadDocument { document_id });
}
PersistentStateMessage::WriteDocument { document_id, document } => {
responses.add(FrontendMessage::TriggerPersistenceWriteDocument { document_id, document });
}
PersistentStateMessage::DeleteDocument { document_id } => {
responses.add(FrontendMessage::TriggerPersistenceDeleteDocument { document_id });
}
PersistentStateMessage::LoadDocument { document_id, document } => {
responses.add(PortfolioMessage::LoadDocumentContent {
document_id,
document_serialized_content: document,
});
}
}
}
advertise_actions!(PersistentStateMessageDiscriminant;);
}

View File

@@ -1,6 +1,7 @@
use super::document::utility_types::document_metadata::LayerNodeIdentifier;
use super::utility_types::{DockingSplitDirection, PanelGroupId, PanelType, WorkspacePanelLayout};
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
use super::persistent_state::PersistentStateMessage;
use super::utility_types::{DockingSplitDirection, PanelGroupId, PanelType};
use crate::messages::frontend::utility_types::{ExportBounds, FileType, PersistedState};
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
use crate::messages::portfolio::utility_types::FontCatalog;
use crate::messages::prelude::*;
@@ -15,6 +16,8 @@ pub enum PortfolioMessage {
// Sub-messages
#[child]
Document(DocumentMessage),
#[child]
PersistentState(PersistentStateMessage),
// Messages
Init,
@@ -61,8 +64,12 @@ pub enum PortfolioMessage {
LoadDocumentResources {
document_id: DocumentId,
},
LoadWorkspaceLayout {
layout: WorkspacePanelLayout,
LoadPersistedState {
state: PersistedState,
},
LoadDocumentContent {
document_id: DocumentId,
document_serialized_content: String,
},
MoveAllPanelTabs {
source_group: PanelGroupId,
@@ -93,15 +100,13 @@ pub enum PortfolioMessage {
document_path: Option<PathBuf>,
document_serialized_content: String,
},
OpenDocumentFileWithId {
LoadDocument {
document_id: DocumentId,
document_name: Option<String>,
document_path: Option<PathBuf>,
document_is_auto_saved: bool,
document_is_saved: bool,
document_serialized_content: String,
to_front: bool,
select_after_open: bool,
},
OpenImage {
name: Option<String>,
@@ -186,7 +191,6 @@ pub enum PortfolioMessage {
UpdateDocumentWidgets,
UpdateOpenDocumentsList,
UpdateWorkspacePanelLayout,
SaveWorkspaceLayout,
ResetWorkspaceLayout,
ResetPanelGroupSizes {
/// Path of child indices from the root to the split node whose children's sizes should be reset to defaults.

View File

@@ -1,12 +1,13 @@
use super::document::utility_types::document_metadata::LayerNodeIdentifier;
use super::document::utility_types::network_interface;
use super::utility_types::{PanelLayoutSubdivision, PanelType, PersistentData, WorkspacePanelLayout};
use super::persistent_state::{PersistentStateMessage, PersistentStateMessageContext, PersistentStateMessageHandler};
use super::utility_types::{CachedData, PanelLayoutSubdivision, PanelType, WorkspacePanelLayout};
use crate::application::{Editor, generate_uuid};
use crate::consts::{DEFAULT_DOCUMENT_NAME, DEFAULT_STROKE_WIDTH, FILE_EXTENSION};
use crate::messages::animation::TimingInformation;
use crate::messages::clipboard::utility_types::ClipboardContent;
use crate::messages::dialog::simple_dialogs;
use crate::messages::frontend::utility_types::{DocumentDetails, OpenDocument};
use crate::messages::frontend::utility_types::{DocumentInfo, PersistedState};
use crate::messages::input_mapper::utility_types::input_keyboard::Key;
use crate::messages::input_mapper::utility_types::macros::{action_shortcut, action_shortcut_manual};
use crate::messages::layout::utility_types::widget_prelude::*;
@@ -50,10 +51,12 @@ pub struct PortfolioMessageContext<'a> {
#[derive(Debug, Default, ExtractField)]
pub struct PortfolioMessageHandler {
pub documents: HashMap<DocumentId, DocumentMessageHandler>,
unloaded_documents: HashMap<DocumentId, DocumentInfo>,
document_ids: VecDeque<DocumentId>,
pub(crate) active_document_id: Option<DocumentId>,
persistent_state: PersistentStateMessageHandler,
pub cached_data: CachedData,
copy_buffer: [Vec<CopyBufferEntry>; INTERNAL_CLIPBOARD_COUNT as usize],
pub persistent_data: PersistentData,
pub executor: NodeGraphExecutor,
pub selection_mode: SelectionMode,
pub reset_node_definitions_on_open: bool,
@@ -82,7 +85,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
let document_inputs = DocumentMessageContext {
document_id,
ipp,
persistent_data: &self.persistent_data,
cached_data: &self.cached_data,
executor: &mut self.executor,
current_tool,
preferences,
@@ -94,9 +97,17 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
document.process_message(message, responses, document_inputs)
}
}
PortfolioMessage::PersistentState(message) => {
let context = PersistentStateMessageContext {
persisted_state: self.persisted_state_snapshot(),
};
self.persistent_state.process_message(message, responses, context);
}
// Messages
PortfolioMessage::Init => {
responses.add(PersistentStateMessage::ReadState);
// Initialize the frontend with environment information
responses.add(FrontendMessage::UpdatePlatform {
platform: Editor::environment().into(),
@@ -104,14 +115,10 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
// Tell frontend to load persistent preferences
responses.add(FrontendMessage::TriggerLoadPreferences);
responses.add(FrontendMessage::TriggerLoadWorkspaceLayout);
// Before loading any documents, initially prepare the welcome screen buttons layout
responses.add(PortfolioMessage::RequestWelcomeScreenButtonsLayout);
// Tell frontend to load persistent auto-saved documents (placed early so IndexedDB reads overlap with subsequent UI setup)
responses.add(FrontendMessage::TriggerLoadAutoSaveDocuments);
// Tell frontend to load documents passed in as launch arguments
responses.add(FrontendMessage::TriggerOpenLaunchDocuments);
@@ -147,7 +154,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
let document_inputs = DocumentMessageContext {
document_id,
ipp,
persistent_data: &self.persistent_data,
cached_data: &self.cached_data,
executor: &mut self.executor,
current_tool,
preferences,
@@ -168,25 +175,21 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
}
}
PortfolioMessage::AutoSaveAllDocuments => {
for (document_id, document) in self.documents.iter_mut() {
if !document.is_auto_saved() {
for document_id in self.document_ids.iter() {
if let Some(document) = self.documents.get_mut(document_id)
&& !document.is_auto_saved()
{
document.set_auto_save_state(true);
responses.add(PortfolioMessage::AutoSaveDocument { document_id: *document_id });
}
}
}
PortfolioMessage::AutoSaveDocument { document_id } => {
let document = self.documents.get(&document_id).unwrap();
responses.add(FrontendMessage::TriggerPersistenceWriteDocument {
let Some(document) = self.document(document_id) else { return };
responses.add(PersistentStateMessage::WriteDocument {
document_id,
document: document.serialize_document(),
details: DocumentDetails {
name: document.name.clone(),
path: document.path.clone(),
is_saved: document.is_saved(),
is_auto_saved: document.is_auto_saved(),
},
})
});
}
PortfolioMessage::CloseActiveDocumentWithConfirmation => {
if let Some(document_id) = self.active_document_id {
@@ -206,7 +209,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
}
for document_id in &self.document_ids {
responses.add(FrontendMessage::TriggerPersistenceRemoveDocument { document_id: *document_id });
responses.add(PersistentStateMessage::DeleteDocument { document_id: *document_id });
}
responses.add(PortfolioMessage::DestroyAllDocuments);
@@ -221,7 +224,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
}
PortfolioMessage::CloseDocument { document_id } => {
// Is this the last document?
if self.documents.len() == 1 && self.document_ids[0] == document_id {
if self.document_ids.len() == 1 && self.document_ids[0] == document_id {
// Clear UI layouts that assume the existence of a document
responses.add(PropertiesPanelMessage::Clear);
responses.add(DocumentMessage::ClearLayersPanel);
@@ -231,13 +234,17 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
// Actually delete the document (delay to delete document is required to let the document and properties panel messages above get processed)
responses.add(PortfolioMessage::DeleteDocument { document_id });
responses.add(FrontendMessage::TriggerPersistenceRemoveDocument { document_id });
responses.add(PersistentStateMessage::DeleteDocument { document_id });
// Send the new list of document tab names
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
}
PortfolioMessage::CloseDocumentWithConfirmation { document_id } => {
let target_document = self.documents.get(&document_id).unwrap();
let Some(target_document) = self.document(document_id) else {
responses.add(EventMessage::ToolAbort);
responses.add(PortfolioMessage::CloseDocument { document_id });
return;
};
if target_document.is_saved() {
responses.add(EventMessage::ToolAbort);
responses.add(PortfolioMessage::CloseDocument { document_id });
@@ -335,6 +342,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
PortfolioMessage::DeleteDocument { document_id } => {
let document_index = self.document_index(document_id);
self.documents.remove(&document_id);
self.unloaded_documents.remove(&document_id);
self.document_ids.remove(document_index);
if self.document_ids.is_empty() {
@@ -354,12 +362,14 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
PortfolioMessage::DestroyAllDocuments => {
// Empty the list of internal document data
self.documents.clear();
self.unloaded_documents.clear();
self.document_ids.clear();
self.active_document_id = None;
responses.add(MenuBarMessage::SendLayout);
responses.add(PersistentStateMessage::WriteState);
}
PortfolioMessage::FontCatalogLoaded { catalog } => {
self.persistent_data.font_catalog = catalog;
self.cached_data.font_catalog = catalog;
if let Some(document_id) = self.active_document_id {
responses.add(PortfolioMessage::LoadDocumentResources { document_id });
@@ -370,23 +380,26 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
responses.add(PortfolioMessage::LoadFontData { font });
}
PortfolioMessage::LoadFontData { font } => {
if let Some(style) = self.persistent_data.font_catalog.find_font_style_in_catalog(&font) {
if let Some(style) = self.cached_data.font_catalog.find_font_style_in_catalog(&font) {
let font = Font::new(font.font_family, style.to_named_style());
if !self.persistent_data.font_cache.loaded_font(&font) {
if !self.cached_data.font_cache.loaded_font(&font) {
responses.add(FrontendMessage::TriggerFontDataLoad { font, url: style.url });
}
}
}
PortfolioMessage::FontLoaded { font_family, font_style, data } => {
let font = Font::new(font_family, font_style);
self.persistent_data.font_cache.insert(font, data);
self.executor.update_font_cache(self.persistent_data.font_cache.clone());
self.cached_data.font_cache.insert(font, data);
self.executor.update_font_cache(self.cached_data.font_cache.clone());
for document_id in self.document_ids.iter() {
let node_to_inspect = self.node_to_inspect();
let Some(document) = self.documents.get_mut(document_id) else {
if self.unloaded_documents.contains_key(document_id) {
continue;
}
log::error!("Tried to render non-existent document");
continue;
};
@@ -407,16 +420,10 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
active: document.render_mode != graphene_std::vector::style::RenderMode::SvgPreview,
});
if let Ok(message) = self.executor.submit_node_graph_evaluation(
self.documents.get_mut(document_id).expect("Tried to render non-existent document"),
*document_id,
physical_resolution,
scale,
timing_information,
node_to_inspect,
true,
pointer_position,
) {
if let Ok(message) = self
.executor
.submit_node_graph_evaluation(document, *document_id, physical_resolution, scale, timing_information, node_to_inspect, true, pointer_position)
{
responses.add_front(message);
}
}
@@ -431,7 +438,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
}
PortfolioMessage::EditorPreferences => self.executor.update_editor_preferences(preferences.editor_preferences()),
PortfolioMessage::LoadDocumentResources { document_id } => {
let catalog = &self.persistent_data.font_catalog;
let catalog = &self.cached_data.font_catalog;
if catalog.0.is_empty() {
responses.add_front(FrontendMessage::TriggerFontCatalogLoad);
@@ -442,16 +449,52 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
document.load_layer_resources(responses);
}
}
PortfolioMessage::LoadWorkspaceLayout { layout } => {
self.workspace_panel_layout = layout;
responses.add(PortfolioMessage::UpdateWorkspacePanelLayout);
PortfolioMessage::LoadPersistedState { state } => {
if let Some(layout) = state.workspace_layout {
self.workspace_panel_layout = layout;
responses.add(PortfolioMessage::UpdateWorkspacePanelLayout);
}
// Refresh all visible panels since the layout may have changed
for group_id in self.workspace_panel_layout.root.all_group_ids() {
if let Some(panel_type) = self.workspace_panel_layout.panel_group(group_id).and_then(|g| g.active_panel_type()) {
self.refresh_panel_content(panel_type, responses);
let PersistedState {
documents,
current_document,
workspace_layout: _,
} = state;
for info in documents {
if !self.document_ids.contains(&info.id) {
self.document_ids.push_back(info.id);
}
if !self.documents.contains_key(&info.id) {
self.unloaded_documents.insert(info.id, info);
}
}
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
let select_document_id = current_document.filter(|id| self.document_ids.contains(id)).or_else(|| self.document_ids.front().copied());
if let Some(document_id) = select_document_id {
responses.add(PortfolioMessage::SelectDocument { document_id });
}
}
PortfolioMessage::LoadDocumentContent {
document_id,
document_serialized_content,
} => {
let Some(info) = self.unloaded_documents.remove(&document_id) else {
log::error!("Tried to load content for non existent document");
return;
};
responses.add(PortfolioMessage::LoadDocument {
document_id,
document_name: Some(info.name),
document_path: info.path,
document_is_auto_saved: true,
document_is_saved: info.is_saved,
document_serialized_content,
});
responses.add(PortfolioMessage::SelectDocument { document_id });
}
PortfolioMessage::NewDocumentWithName { name } => {
let mut new_document = DocumentMessageHandler::default();
@@ -465,7 +508,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
responses.add(NavigationMessage::CanvasPan { delta: (0., 0.).into() });
}
self.load_document(new_document, document_id, responses, false);
self.load_document(new_document, document_id, responses);
responses.add(PortfolioMessage::SelectDocument { document_id });
}
PortfolioMessage::MoveAllPanelTabs {
@@ -515,7 +558,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
responses.add(MenuBarMessage::SendLayout);
responses.add(PortfolioMessage::UpdateWorkspacePanelLayout);
responses.add(PortfolioMessage::SaveWorkspaceLayout);
// Refresh the new active tab
if let Some(panel_type) = self.workspace_panel_layout.panel_group(target_group).and_then(|g| g.active_panel_type()) {
@@ -565,7 +607,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
responses.add(MenuBarMessage::SendLayout);
responses.add(PortfolioMessage::UpdateWorkspacePanelLayout);
responses.add(PortfolioMessage::SaveWorkspaceLayout);
// Refresh the moved panel's content in its new location
self.refresh_panel_content(panel_type, responses);
@@ -661,30 +702,28 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
document_path,
document_serialized_content,
} => {
responses.add(PortfolioMessage::OpenDocumentFileWithId {
document_id: DocumentId(generate_uuid()),
let document_id = DocumentId(generate_uuid());
responses.add(PortfolioMessage::LoadDocument {
document_id,
document_name,
document_path,
document_is_auto_saved: false,
document_is_saved: true,
document_serialized_content,
to_front: false,
select_after_open: true,
});
responses.add(PortfolioMessage::SelectDocument { document_id });
}
PortfolioMessage::ToggleResetNodesToDefinitionsOnOpen => {
self.reset_node_definitions_on_open = !self.reset_node_definitions_on_open;
responses.add(MenuBarMessage::SendLayout);
}
PortfolioMessage::OpenDocumentFileWithId {
PortfolioMessage::LoadDocument {
document_id,
document_name,
document_path,
document_is_auto_saved,
document_is_saved,
document_serialized_content,
to_front,
select_after_open,
} => {
// Upgrade the document being opened to use fresh copies of all nodes
let reset_node_definitions_on_open = reset_node_definitions_on_open || document_migration_reset_node_definition(&document_serialized_content);
@@ -777,11 +816,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
}
// Load the document into the portfolio so it opens in the editor
self.load_document(document, document_id, responses, to_front);
if select_after_open {
responses.add(PortfolioMessage::SelectDocument { document_id });
}
self.load_document(document, document_id, responses);
}
PortfolioMessage::OpenImage { name, image } => {
responses.add(PortfolioMessage::NewDocumentWithName {
@@ -1126,7 +1161,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
mouse,
parent_and_insert_index,
} => {
if self.documents.is_empty() {
if self.document_ids.is_empty() {
responses.add(PortfolioMessage::OpenImage { name, image });
} else {
responses.add(DocumentMessage::PasteImage {
@@ -1144,7 +1179,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
mouse,
parent_and_insert_index,
} => {
if self.documents.is_empty() {
if self.document_ids.is_empty() {
responses.add(PortfolioMessage::OpenSvg { name, svg });
} else {
responses.add(DocumentMessage::PasteSvg {
@@ -1200,7 +1235,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
}
responses.add(PortfolioMessage::UpdateWorkspacePanelLayout);
responses.add(PortfolioMessage::SaveWorkspaceLayout);
}
}
PortfolioMessage::RequestWelcomeScreenButtonsLayout => {
@@ -1280,7 +1314,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
// Send the layout update first so the frontend mounts the new panel component before it receives content
responses.add(PortfolioMessage::UpdateWorkspacePanelLayout);
responses.add(PortfolioMessage::SaveWorkspaceLayout);
if let Some(panel_type) = self.workspace_panel_layout.panel_group(group).and_then(|g| g.active_panel_type()) {
self.refresh_panel_content(panel_type, responses);
@@ -1315,7 +1348,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
responses.add(MenuBarMessage::SendLayout);
responses.add(PortfolioMessage::UpdateWorkspacePanelLayout);
responses.add(PortfolioMessage::SaveWorkspaceLayout);
// Refresh the new panel group's active tab
if let Some(panel_type) = self.workspace_panel_layout.panel_group(new_id).and_then(|g| g.active_panel_type()) {
@@ -1340,13 +1372,32 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
node_graph_open = document.is_graph_overlay_open();
}
if self.unloaded_documents.contains_key(&document_id) {
let already_selected = self.active_document_id == Some(document_id);
self.active_document_id = Some(document_id);
responses.add(MenuBarMessage::SendLayout);
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
responses.add(FrontendMessage::UpdateActiveDocument { document_id });
if !already_selected {
responses.add(PersistentStateMessage::ReadDocument { document_id });
}
return;
}
if !self.documents.contains_key(&document_id) {
warn!("Tried to read non existent document");
return;
}
// Set the new active document ID
self.active_document_id = Some(document_id);
responses.add(MenuBarMessage::SendLayout);
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
responses.add(FrontendMessage::UpdateActiveDocument { document_id });
responses.add(FrontendMessage::TriggerSaveActiveDocument { document_id });
responses.add(ToolMessage::InitTools);
responses.add(NodeGraphMessage::Init);
responses.add(OverlaysMessage::Draw);
@@ -1382,7 +1433,8 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
artboard_name,
artboard_count,
} => {
let document = self.active_document_id.and_then(|id| self.documents.get_mut(&id)).expect("Tried to render non-existent document");
let document_id = self.active_document_id.expect("Tried to render non-existent document");
let document = self.documents.get_mut(&document_id).expect("Tried to render non-existent document");
let export_config = ExportConfig {
name,
file_type,
@@ -1392,7 +1444,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
artboard_count,
..Default::default()
};
let result = self.executor.submit_document_export(document, self.active_document_id.unwrap(), export_config);
let result = self.executor.submit_document_export(document, document_id, export_config);
if let Err(description) = result {
responses.add(DialogMessage::DisplayDialogError {
@@ -1491,7 +1543,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
responses.add(MenuBarMessage::SendLayout);
responses.add(PortfolioMessage::UpdateWorkspacePanelLayout);
responses.add(PortfolioMessage::SaveWorkspaceLayout);
}
PortfolioMessage::TogglePropertiesPanelOpen => {
if self.workspace_panel_layout.focus_document {
@@ -1536,11 +1587,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
false => self.workspace_panel_layout.clone(),
};
responses.add(FrontendMessage::UpdateWorkspacePanelLayout { panel_layout });
}
PortfolioMessage::SaveWorkspaceLayout => {
responses.add(FrontendMessage::TriggerSaveWorkspaceLayout {
workspace_layout: self.workspace_panel_layout.clone(),
});
responses.add(PersistentStateMessage::WriteState);
}
PortfolioMessage::ResetWorkspaceLayout => {
// Destroy layouts for all currently visible non-document panels
@@ -1562,7 +1609,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
}
responses.add(PortfolioMessage::UpdateWorkspacePanelLayout);
responses.add(PortfolioMessage::SaveWorkspaceLayout);
responses.add(MenuBarMessage::SendLayout);
}
PortfolioMessage::ResetPanelGroupSizes { split_path } => {
@@ -1578,7 +1624,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
node.recalculate_default_sizes();
responses.add(PortfolioMessage::UpdateWorkspacePanelLayout);
responses.add(PortfolioMessage::SaveWorkspaceLayout);
}
PortfolioMessage::SetPanelGroupSizes { split_path, sizes } => {
// Walk the tree to the target split node using the path
@@ -1596,29 +1641,16 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
}
}
responses.add(PortfolioMessage::SaveWorkspaceLayout);
responses.add(PortfolioMessage::UpdateWorkspacePanelLayout);
}
PortfolioMessage::UpdateOpenDocumentsList => {
// Send the list of document tab names
let open_documents = self
.document_ids
.iter()
.filter_map(|id| {
self.documents.get(id).map(|document| OpenDocument {
id: *id,
details: DocumentDetails {
name: document.name.clone(),
path: document.path.clone(),
is_saved: document.is_saved(),
is_auto_saved: document.is_auto_saved(),
},
})
})
.collect::<Vec<_>>();
let open_documents = self.document_ids.iter().filter_map(|id| self.document_details(*id)).collect::<Vec<_>>();
let no_open_documents = open_documents.is_empty();
responses.add(FrontendMessage::UpdateOpenDocumentsList { open_documents });
responses.add(PersistentStateMessage::WriteState);
if no_open_documents {
responses.add(PortfolioMessage::RequestWelcomeScreenButtonsLayout);
@@ -1682,11 +1714,11 @@ impl PortfolioMessageHandler {
}
pub fn active_document(&self) -> Option<&DocumentMessageHandler> {
self.active_document_id.and_then(|id| self.documents.get(&id))
self.active_document_id.and_then(|id| self.document(id))
}
pub fn active_document_mut(&mut self) -> Option<&mut DocumentMessageHandler> {
self.active_document_id.and_then(|id| self.documents.get_mut(&id))
self.active_document_id.and_then(|id| self.document_mut(id))
}
pub fn active_document_id(&self) -> Option<DocumentId> {
@@ -1694,12 +1726,29 @@ impl PortfolioMessageHandler {
}
pub fn unsaved_document_names(&self) -> Vec<String> {
self.documents.values().filter(|document| !document.is_saved()).map(|document| document.name.clone()).collect()
self.document_ids
.iter()
.filter_map(|id| self.document_details(*id))
.filter(|details| !details.is_saved)
.map(|details| details.name)
.collect()
}
pub fn persisted_state_snapshot(&self) -> PersistedState {
let documents = self.document_ids.iter().filter_map(|id| self.document_details(*id)).collect::<Vec<_>>();
PersistedState {
documents,
current_document: self.active_document_id,
workspace_layout: Some(self.workspace_panel_layout.clone()),
}
}
pub fn generate_new_document_name(&self) -> String {
let mut doc_title_numbers = self
.ordered_document_iterator()
.document_ids
.iter()
.filter_map(|id| self.document_details(*id))
.filter_map(|doc| {
doc.name
.rsplit_once(DEFAULT_DOCUMENT_NAME)
@@ -1744,12 +1793,12 @@ impl PortfolioMessageHandler {
}
}
fn load_document(&mut self, mut new_document: DocumentMessageHandler, document_id: DocumentId, responses: &mut VecDeque<Message>, to_front: bool) {
if to_front {
self.document_ids.push_front(document_id);
} else {
fn load_document(&mut self, mut new_document: DocumentMessageHandler, document_id: DocumentId, responses: &mut VecDeque<Message>) {
let is_new_document = !self.document_ids.contains(&document_id);
if is_new_document {
self.document_ids.push_back(document_id);
}
self.unloaded_documents.remove(&document_id);
new_document.update_layers_panel_control_bar_widgets(
self.workspace_panel_layout.is_panel_visible(PanelType::Layers) && !self.workspace_panel_layout.focus_document,
responses,
@@ -1768,12 +1817,14 @@ impl PortfolioMessageHandler {
// TODO: Remove this and find a way to fix the issue where creating a new document when the node graph is open causes the transform in the new document to be incorrect
responses.add(DocumentMessage::GraphViewOverlay { open: false });
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
if is_new_document {
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
}
}
/// Returns an iterator over the open documents in order.
pub fn ordered_document_iterator(&self) -> impl Iterator<Item = &DocumentMessageHandler> {
self.document_ids.iter().map(|id| self.documents.get(id).expect("Document id was not found in the document hashmap"))
self.document_ids.iter().filter_map(|id| self.document(*id))
}
fn document_index(&self, document_id: DocumentId) -> usize {
@@ -1781,7 +1832,10 @@ impl PortfolioMessageHandler {
}
pub fn poll_node_graph_evaluation(&mut self, responses: &mut VecDeque<Message>) -> Result<(), String> {
let Some(active_document) = self.active_document_id.and_then(|id| self.documents.get_mut(&id)) else {
let Some(document_id) = self.active_document_id else {
return Err("No active document".to_string());
};
let Some(active_document) = self.documents.get_mut(&document_id) else {
return Err("No active document".to_string());
};
@@ -1801,6 +1855,19 @@ impl PortfolioMessageHandler {
result
}
fn document_details(&self, document_id: DocumentId) -> Option<DocumentInfo> {
if let Some(document) = self.documents.get(&document_id) {
Some(DocumentInfo {
id: document_id,
name: document.name.clone(),
path: document.path.clone(),
is_saved: document.is_saved(),
})
} else {
self.unloaded_documents.get(&document_id).cloned()
}
}
/// Get the ID of the selected node that should be used as the current source for the Data panel.
pub fn node_to_inspect(&self) -> Option<NodeId> {
// Skip if the Data panel is not open
@@ -1808,7 +1875,7 @@ impl PortfolioMessageHandler {
return None;
}
let document = self.documents.get(&self.active_document_id?)?;
let document = self.document(self.active_document_id?)?;
let selected_nodes = document.network_interface.selected_nodes().0;
// Skip if there is not exactly one selected node
@@ -1854,7 +1921,6 @@ impl PortfolioMessageHandler {
responses.add(MenuBarMessage::SendLayout);
responses.add(PortfolioMessage::UpdateWorkspacePanelLayout);
responses.add(PortfolioMessage::SaveWorkspaceLayout);
}
/// Destroy the stored layout for a panel that is no longer the active tab.

View File

@@ -3,7 +3,7 @@ use graphene_std::raster::Image;
use graphene_std::text::{Font, FontCache};
#[derive(Debug, Default)]
pub struct PersistentData {
pub struct CachedData {
pub font_cache: FontCache,
pub font_catalog: FontCatalog,
}

View File

@@ -27,6 +27,7 @@ pub use crate::messages::portfolio::document::node_graph::{NodeGraphMessage, Nod
pub use crate::messages::portfolio::document::overlays::{OverlaysMessage, OverlaysMessageContext, OverlaysMessageDiscriminant, OverlaysMessageHandler};
pub use crate::messages::portfolio::document::properties_panel::{PropertiesPanelMessage, PropertiesPanelMessageDiscriminant, PropertiesPanelMessageHandler};
pub use crate::messages::portfolio::document::{DocumentMessage, DocumentMessageContext, DocumentMessageDiscriminant, DocumentMessageHandler};
pub use crate::messages::portfolio::persistent_state::{PersistentStateMessage, PersistentStateMessageContext, PersistentStateMessageDiscriminant, PersistentStateMessageHandler};
pub use crate::messages::portfolio::{PortfolioMessage, PortfolioMessageContext, PortfolioMessageDiscriminant, PortfolioMessageHandler};
pub use crate::messages::preferences::{PreferencesMessage, PreferencesMessageDiscriminant, PreferencesMessageHandler};
pub use crate::messages::tool::transform_layer::{TransformLayerMessage, TransformLayerMessageDiscriminant, TransformLayerMessageHandler};

View File

@@ -4,7 +4,7 @@ use super::utility_types::{ToolActionMessageContext, ToolFsmState, tool_message_
use crate::application::generate_uuid;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::overlays::utility_types::OverlayProvider;
use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::portfolio::utility_types::CachedData;
use crate::messages::prelude::*;
use crate::messages::tool::transform_layer::transform_layer_message_handler::TransformLayerMessageContext;
use crate::messages::tool::utility_types::{HintData, ToolType};
@@ -18,7 +18,7 @@ pub struct ToolMessageContext<'a> {
pub document_id: DocumentId,
pub document: &'a mut DocumentMessageHandler,
pub input: &'a InputPreprocessorMessageHandler,
pub persistent_data: &'a PersistentData,
pub cached_data: &'a CachedData,
pub node_graph: &'a NodeGraphExecutor,
pub preferences: &'a PreferencesMessageHandler,
pub viewport: &'a ViewportMessageHandler,
@@ -39,7 +39,7 @@ impl MessageHandler<ToolMessage, ToolMessageContext<'_>> for ToolMessageHandler
document_id,
document,
input,
persistent_data,
cached_data,
node_graph,
preferences,
viewport,
@@ -126,7 +126,7 @@ impl MessageHandler<ToolMessage, ToolMessageContext<'_>> for ToolMessageHandler
node_graph,
preferences,
viewport,
persistent_data,
cached_data,
};
if let Some(tool_abort_message) = tool.event_to_message_map().tool_abort {
@@ -217,7 +217,7 @@ impl MessageHandler<ToolMessage, ToolMessageContext<'_>> for ToolMessageHandler
tool_data.tools.get(active_tool).unwrap().activate(responses);
// Register initial properties
tool_data.tools.get(active_tool).unwrap().refresh_options(responses, persistent_data);
tool_data.tools.get(active_tool).unwrap().refresh_options(responses, cached_data);
// Notify the frontend about the initial active tool
tool_data.send_layout(responses, LayoutTarget::ToolShelf, preferences.brush_tool);
@@ -234,7 +234,7 @@ impl MessageHandler<ToolMessage, ToolMessageContext<'_>> for ToolMessageHandler
node_graph,
preferences,
viewport,
persistent_data,
cached_data,
};
// Set initial hints and cursor
@@ -258,7 +258,7 @@ impl MessageHandler<ToolMessage, ToolMessageContext<'_>> for ToolMessageHandler
ToolMessage::RefreshToolOptions => {
let tool_data = &mut self.tool_state.tool_data;
tool_data.tools.get(&tool_data.active_tool_type).unwrap().refresh_options(responses, persistent_data);
tool_data.tools.get(&tool_data.active_tool_type).unwrap().refresh_options(responses, cached_data);
}
ToolMessage::RefreshToolShelf => {
let tool_data = &mut self.tool_state.tool_data;
@@ -346,7 +346,7 @@ impl MessageHandler<ToolMessage, ToolMessageContext<'_>> for ToolMessageHandler
node_graph,
preferences,
viewport,
persistent_data,
cached_data,
};
if matches!(tool_message, ToolMessage::UpdateHints) {
if graph_view_overlay_open {

View File

@@ -605,7 +605,7 @@ impl Fsm for SelectToolFsmState {
document,
input,
viewport,
persistent_data,
cached_data,
..
} = tool_action_data;
@@ -632,7 +632,7 @@ impl Fsm for SelectToolFsmState {
overlay_context.outline(document.metadata().layer_with_free_points_outline(layer), layer_to_viewport, None);
if is_layer_fed_by_node_of_name(layer, &document.network_interface, &DefinitionIdentifier::ProtoNode(graphene_std::text::text::IDENTIFIER)) {
let transformed_quad = layer_to_viewport * text_bounding_box(layer, document, &persistent_data.font_cache);
let transformed_quad = layer_to_viewport * text_bounding_box(layer, document, &cached_data.font_cache);
overlay_context.dashed_quad(transformed_quad, None, None, Some(7.), Some(5.), None);
}
}

View File

@@ -7,7 +7,7 @@ use crate::messages::portfolio::document::node_graph::document_node_definitions:
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::InputConnector;
use crate::messages::portfolio::utility_types::{FontCatalog, FontCatalogStyle, PersistentData};
use crate::messages::portfolio::utility_types::{CachedData, FontCatalog, FontCatalogStyle};
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
use crate::messages::tool::common_functionality::graph_modification_utils::{self, is_layer_fed_by_node_of_name};
@@ -230,8 +230,8 @@ fn create_text_widgets(tool: &TextTool, font_catalog: &FontCatalog) -> Vec<Widge
}
impl ToolRefreshOptions for TextTool {
fn refresh_options(&self, responses: &mut VecDeque<Message>, persistent_data: &PersistentData) {
self.send_layout(responses, LayoutTarget::ToolOptions, &persistent_data.font_catalog);
fn refresh_options(&self, responses: &mut VecDeque<Message>, cached_data: &CachedData) {
self.send_layout(responses, LayoutTarget::ToolOptions, &cached_data.font_catalog);
}
}
@@ -302,7 +302,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Text
}
}
self.send_layout(responses, LayoutTarget::ToolOptions, &context.persistent_data.font_catalog);
self.send_layout(responses, LayoutTarget::ToolOptions, &context.cached_data.font_catalog);
}
fn actions(&self) -> ActionList {
@@ -573,11 +573,11 @@ impl Fsm for TextToolFsmState {
document,
global_tool_data,
input,
persistent_data,
cached_data,
viewport,
..
} = transition_data;
let font_cache = &persistent_data.font_cache;
let font_cache = &cached_data.font_cache;
let fill_color = COLOR_OVERLAY_BLUE_05;
let ToolMessage::Text(event) = event else { return self };

View File

@@ -9,7 +9,7 @@ use crate::messages::input_mapper::utility_types::macros::action_shortcut;
use crate::messages::input_mapper::utility_types::misc::ActionShortcut;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::overlays::utility_types::OverlayProvider;
use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::portfolio::utility_types::CachedData;
use crate::messages::preferences::PreferencesMessageHandler;
use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::shapes::shape_utility::ShapeType;
@@ -24,7 +24,7 @@ pub struct ToolActionMessageContext<'a> {
pub document_id: DocumentId,
pub global_tool_data: &'a DocumentToolData,
pub input: &'a InputPreprocessorMessageHandler,
pub persistent_data: &'a PersistentData,
pub cached_data: &'a CachedData,
pub shape_editor: &'a mut ShapeState,
pub node_graph: &'a NodeGraphExecutor,
pub preferences: &'a PreferencesMessageHandler,
@@ -37,11 +37,11 @@ impl<T> ToolCommon for T where T: for<'a, 'b> MessageHandler<ToolMessage, &'b mu
type Tool = dyn ToolCommon + Send + Sync;
pub trait ToolRefreshOptions {
fn refresh_options(&self, responses: &mut VecDeque<Message>, _persistent_data: &PersistentData);
fn refresh_options(&self, responses: &mut VecDeque<Message>, _cached_data: &CachedData);
}
impl<T: LayoutHolder> ToolRefreshOptions for T {
fn refresh_options(&self, responses: &mut VecDeque<Message>, _persistent_data: &PersistentData) {
fn refresh_options(&self, responses: &mut VecDeque<Message>, _cached_data: &CachedData) {
self.send_layout(responses, LayoutTarget::ToolOptions);
}
}

View File

@@ -36,15 +36,15 @@ impl EditorTestUtils {
async fn run<'a>(editor: &'a mut Editor, runtime: &'a mut NodeRuntime) -> Result<Instrumented, String> {
let portfolio = &mut editor.dispatcher.message_handlers.portfolio_message_handler;
let document_id = portfolio.active_document_id.unwrap();
let exector = &mut portfolio.executor;
let document = portfolio.documents.get_mut(&document_id).unwrap();
let (executor, documents) = (&mut portfolio.executor, &mut portfolio.documents);
let document = documents.get_mut(&document_id).unwrap();
let instrumented = match exector.update_node_graph_instrumented(document) {
let instrumented = match executor.update_node_graph_instrumented(document) {
Ok(instrumented) => instrumented,
Err(e) => return Err(format!("update_node_graph_instrumented failed\n\n{e}")),
};
if let Err(e) = exector.submit_current_node_graph_evaluation(document, document_id, UVec2::ONE, 1., Default::default(), DVec2::ZERO) {
if let Err(e) = executor.submit_current_node_graph_evaluation(document, document_id, UVec2::ONE, 1., Default::default(), DVec2::ZERO) {
return Err(format!("submit_current_node_graph_evaluation failed\n\n{e}"));
}
runtime.run().await;