Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion desktop/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ impl App {
let wake = Arc::new(move || {
wake_scheduler.schedule(AppEvent::DesktopWrapperMessage(DesktopWrapperMessage::Wake));
});
let desktop_wrapper = DesktopWrapper::new(rand::rng().random(), Arc::new(resource_storage), wgpu_context.clone(), wake);
let desktop_wrapper = DesktopWrapper::new(rand::rng().random(), Arc::new(resource_storage), dirs::app_autosave_documents_dir(), wgpu_context.clone(), wake);

Self {
render_state: None,
Expand Down
5 changes: 3 additions & 2 deletions desktop/wrapper/src/intercept_frontend_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,14 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD
if let Some(path) = path {
dispatcher.respond(DesktopFrontendMessage::WriteFile { path, content });
} else {
let extension = std::path::Path::new(&name).extension().and_then(|extension| extension.to_str()).unwrap_or("*").to_string();
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
dispatcher.respond(DesktopFrontendMessage::SaveFileDialog {
title: "Save Document".to_string(),
default_filename: name,
default_folder: folder,
filters: vec![FileFilter {
name: "Graphite".to_string(),
extensions: vec!["graphite".to_string()],
name: "Graphite Document".to_string(),
extensions: vec![extension],
}],
context: SaveFileDialogContext::Document { document_id, content },
});
Expand Down
4 changes: 2 additions & 2 deletions desktop/wrapper/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub struct DesktopWrapper {
}

impl DesktopWrapper {
pub fn new(uuid_random_seed: u64, resource_storage: Arc<dyn ResourceStorage>, wgpu_context: WgpuContext, schedule_wake: Wake) -> Self {
pub fn new(uuid_random_seed: u64, resource_storage: Arc<dyn ResourceStorage>, working_copy_root: std::path::PathBuf, wgpu_context: WgpuContext, schedule_wake: Wake) -> Self {
#[cfg(target_os = "windows")]
let host = Host::Windows;
#[cfg(target_os = "macos")]
Expand All @@ -37,7 +37,7 @@ impl DesktopWrapper {
let application_io = PlatformApplicationIo::new_with_context(wgpu_context);

Self {
editor: Editor::new(env, uuid_random_seed, resource_storage, application_io, schedule_wake),
editor: Editor::new(env, uuid_random_seed, resource_storage, Some(working_copy_root), application_io, schedule_wake),
}
}

Expand Down
3 changes: 3 additions & 0 deletions editor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ gpu = ["interpreted-executor/gpu", "dep:wgpu-executor"]
# Local dependencies
graphite-proc-macros = { workspace = true }
graph-craft = { workspace = true }
graph-storage = { workspace = true, features = ["conversion"] }
document-format = { workspace = true }
document-container = { workspace = true }
graphene-hash = { workspace = true }
interpreted-executor = { workspace = true }
graphene-std = { workspace = true } # NOTE: `core-types` should not be added here because `graphene-std` re-exports its contents
Expand Down
11 changes: 9 additions & 2 deletions editor/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,18 @@ pub struct Editor {
}

impl Editor {
pub fn new(environment: Environment, uuid_random_seed: u64, resource_storage: Arc<dyn ResourceStorage>, mut application_io: PlatformApplicationIo, wake: Wake) -> Self {
pub fn new(
environment: Environment,
uuid_random_seed: u64,
resource_storage: Arc<dyn ResourceStorage>,
working_copy_root: Option<std::path::PathBuf>,
mut application_io: PlatformApplicationIo,
wake: Wake,
) -> Self {
ENVIRONMENT.set(environment).expect("Editor shoud only be initialized once");
graphene_std::uuid::set_uuid_seed(uuid_random_seed);

let mut dispatcher = Dispatcher::new(resource_storage);
let mut dispatcher = Dispatcher::new(resource_storage, working_copy_root);
dispatcher.message_handlers.future_message_handler.set_wake(wake);
application_io.inject_resource_proxy(dispatcher.message_handlers.resource_storage_message_handler.resources());
crate::node_graph_executor::replace_application_io(application_io);
Expand Down
3 changes: 3 additions & 0 deletions editor/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ pub const COLOR_OVERLAY_BLACK_75: &str = "#000000bf";

// DOCUMENT
pub const FILE_EXTENSION: &str = "graphite";
/// New document container format. Save writes a `.gdd` with the legacy `.graphite` embedded as a fallback
/// during the dual-write soak; `.graphite` stays a supported open/import input.
pub const GDD_FILE_EXTENSION: &str = "gdd";
Comment thread
TrueDoctor marked this conversation as resolved.
pub const DEFAULT_DOCUMENT_NAME: &str = "Untitled Document";
pub const MAX_UNDO_HISTORY_LEN: usize = 100; // TODO: Add this to user preferences
pub const AUTO_SAVE_TIMEOUT_SECONDS: u64 = 1;
Expand Down
4 changes: 3 additions & 1 deletion editor/src/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,10 @@ const DEBUG_MESSAGE_BLOCK_LIST: &[MessageDiscriminant] = &[
const DEBUG_MESSAGE_ENDING_BLOCK_LIST: &[&str] = &["PointerMove", "PointerOutsideViewport", "Overlays", "Draw", "CurrentTime", "Time"];

impl Dispatcher {
pub fn new(resource_storage: Arc<dyn ResourceStorage>) -> Self {
pub fn new(resource_storage: Arc<dyn ResourceStorage>, working_copy_root: Option<std::path::PathBuf>) -> Self {
let mut s = Self::default();
s.message_handlers.resource_storage_message_handler = ResourceStorageMessageHandler::new(resource_storage);
s.message_handlers.portfolio_message_handler.set_working_copy_root(working_copy_root);
s
}

Expand Down Expand Up @@ -271,6 +272,7 @@ impl Dispatcher {
menu_bar_message_handler.properties_panel_open = layout.is_panel_present(PanelType::Properties);
menu_bar_message_handler.message_logging_verbosity = self.message_handlers.debug_message_handler.message_logging_verbosity;
menu_bar_message_handler.reset_node_definitions_on_open = self.message_handlers.portfolio_message_handler.reset_node_definitions_on_open;
menu_bar_message_handler.show_storage_preferences = self.message_handlers.preferences_message_handler.show_storage_preferences;

if let Some(document) = self
.message_handlers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,62 @@ impl PreferencesDialogMessageHandler {
rows.extend_from_slice(&[header, node_graph_wires_label, graph_wire_style, brush_tool]);
}

// =========
// DOCUMENTS
// =========
// Soak-only `.gdd` storage options, hidden unless enabled from the developer debug menu.
if preferences.show_storage_preferences {
let header = vec![TextLabel::new("Documents").italic(true).widget_instance()];

let save_as_gdd_description = "
Save documents in the new .gdd container format (with the legacy .graphite file embedded as a recovery fallback) instead of a plain .graphite file. The .gdd format is still being validated, so this is opt-in for now.\n\
\n\
*Default: Off.*
"
.trim();
let save_as_gdd_checkbox_id = CheckboxId::new();
let save_as_gdd = vec![
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
CheckboxInput::new(preferences.save_as_gdd)
.tooltip_label("Save as .gdd")
.tooltip_description(save_as_gdd_description)
.on_update(|checkbox_input: &CheckboxInput| PreferencesMessage::SaveAsGdd { enabled: checkbox_input.checked }.into())
.for_label(save_as_gdd_checkbox_id)
.widget_instance(),
TextLabel::new("Save as .gdd")
.tooltip_label("Save as .gdd")
.tooltip_description(save_as_gdd_description)
.for_checkbox(save_as_gdd_checkbox_id)
.widget_instance(),
];

let validate_description = "
Validate every document save, open, and undo by round-tripping it through the .gdd storage format and comparing against the legacy path, logging any mismatch. Useful for debugging the .gdd format during its soak, but the per-edit round-trip has a performance cost.\n\
\n\
*Default: Off.*
"
.trim();
let validate_checkbox_id = CheckboxId::new();
let validate_storage_round_trip = vec![
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
CheckboxInput::new(preferences.validate_storage_round_trip)
.tooltip_label("Validate Storage Round-Trip")
.tooltip_description(validate_description)
.on_update(|checkbox_input: &CheckboxInput| PreferencesMessage::ValidateStorageRoundTrip { enabled: checkbox_input.checked }.into())
.for_label(validate_checkbox_id)
.widget_instance(),
TextLabel::new("Validate Storage Round-Trip")
.tooltip_label("Validate Storage Round-Trip")
.tooltip_description(validate_description)
.for_checkbox(validate_checkbox_id)
.widget_instance(),
];

rows.extend_from_slice(&[header, save_as_gdd, validate_storage_round_trip]);
}

// =============
// COMPATIBILITY
// =============
Expand Down
10 changes: 10 additions & 0 deletions editor/src/messages/future/future_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,10 @@ pub struct FutureMessageContext {}

#[derive(ExtractField)]
pub struct FutureMessageHandler {
#[cfg_attr(test, expect(dead_code))]
spawner: Arc<dyn MessageSpawner>,
wake: Wake,
#[cfg_attr(test, expect(dead_code))]
results_sender: UnboundedSender<Message>,
results_receiver: UnboundedReceiver<Message>,
}
Expand Down Expand Up @@ -119,7 +121,15 @@ impl MessageHandler<FutureMessage, FutureMessageContext> for FutureMessageHandle
fn process_message(&mut self, message: FutureMessage, _responses: &mut VecDeque<Message>, _context: FutureMessageContext) {
match message {
FutureMessage::Await { future } => {
#[cfg(not(test))]
self.spawner.spawn(future.into_future(), self.results_sender.clone(), self.wake.clone());

// For tests, block on the future to ensure the result is available when validating editor state afterwards.
#[cfg(test)]
{
let message = futures::executor::block_on(future.into_future());
Comment thread
TrueDoctor marked this conversation as resolved.
_responses.push_back(message);
}
}
FutureMessage::Wake => {
// Tick-only message: the dispatcher's top-of-tick drain handles the real work.
Expand Down
6 changes: 6 additions & 0 deletions editor/src/messages/menu_bar/menu_bar_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub struct MenuBarMessageHandler {
pub has_selection_history: (bool, bool),
pub message_logging_verbosity: MessageLoggingVerbosity,
pub reset_node_definitions_on_open: bool,
pub show_storage_preferences: bool,
pub make_path_editable_is_allowed: bool,
pub data_panel_open: bool,
pub layers_panel_open: bool,
Expand Down Expand Up @@ -50,6 +51,7 @@ impl LayoutHolder for MenuBarMessageHandler {
let message_logging_verbosity_names = self.message_logging_verbosity == MessageLoggingVerbosity::Names;
let message_logging_verbosity_contents = self.message_logging_verbosity == MessageLoggingVerbosity::Contents;
let reset_node_definitions_on_open = self.reset_node_definitions_on_open;
let show_storage_preferences = self.show_storage_preferences;
let make_path_editable_is_allowed = self.make_path_editable_is_allowed;

let about = MenuListEntry::new("About Graphite…")
Expand Down Expand Up @@ -718,6 +720,10 @@ impl LayoutHolder for MenuBarMessageHandler {
.label("Reset Nodes to Definitions on Open")
.icon(if reset_node_definitions_on_open { "CheckboxChecked" } else { "CheckboxUnchecked" })
.on_commit(|_| PortfolioMessage::ToggleResetNodesToDefinitionsOnOpen.into()),
MenuListEntry::new("Show Storage Preferences")
.label("Show Storage Preferences")
.icon(if show_storage_preferences { "CheckboxChecked" } else { "CheckboxUnchecked" })
.on_commit(|_| PreferencesMessage::ToggleShowStoragePreferences.into()),
],
vec![
MenuListEntry::new("Print Trace Logs")
Expand Down
Loading
Loading