mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 14:18:04 +08:00
Improve UX of importing vs. opening files (#3661)
* wip * fix drag and drop * fix * fix tests * fix tests * fix warning * Partial code review * add dialog * fix web * fix web * push back release candidate expiry * Code review * Reduce code duplication for pasting files in frontend --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
use graphene_std::Color;
|
||||
use graphene_std::raster::Image;
|
||||
use graphite_editor::messages::clipboard::utility_types::ClipboardContentRaw;
|
||||
use graphite_editor::messages::prelude::*;
|
||||
|
||||
@@ -14,9 +12,9 @@ pub(super) fn handle_desktop_wrapper_message(dispatcher: &mut DesktopWrapperMess
|
||||
DesktopWrapperMessage::Input(message) => {
|
||||
dispatcher.queue_editor_message(EditorMessage::InputPreprocessor(message));
|
||||
}
|
||||
DesktopWrapperMessage::OpenFileDialogResult { path, content, context } => match context {
|
||||
OpenFileDialogContext::Document => {
|
||||
dispatcher.queue_desktop_wrapper_message(DesktopWrapperMessage::OpenDocument { path, content });
|
||||
DesktopWrapperMessage::FileDialogResult { path, content, context } => match context {
|
||||
OpenFileDialogContext::Open => {
|
||||
dispatcher.queue_desktop_wrapper_message(DesktopWrapperMessage::OpenFile { path, content });
|
||||
}
|
||||
OpenFileDialogContext::Import => {
|
||||
dispatcher.queue_desktop_wrapper_message(DesktopWrapperMessage::ImportFile { path, content });
|
||||
@@ -35,78 +33,11 @@ pub(super) fn handle_desktop_wrapper_message(dispatcher: &mut DesktopWrapperMess
|
||||
}
|
||||
},
|
||||
DesktopWrapperMessage::OpenFile { path, content } => {
|
||||
let extension = path.extension().and_then(|s| s.to_str()).unwrap_or_default().to_lowercase();
|
||||
match extension.as_str() {
|
||||
"graphite" => {
|
||||
dispatcher.queue_desktop_wrapper_message(DesktopWrapperMessage::OpenDocument { path, content });
|
||||
}
|
||||
_ => {
|
||||
dispatcher.queue_desktop_wrapper_message(DesktopWrapperMessage::ImportFile { path, content });
|
||||
}
|
||||
}
|
||||
}
|
||||
DesktopWrapperMessage::OpenDocument { path, content } => {
|
||||
let Ok(content) = String::from_utf8(content) else {
|
||||
tracing::warn!("Document file is invalid: {}", path.display());
|
||||
return;
|
||||
};
|
||||
|
||||
let message = PortfolioMessage::OpenDocumentFile {
|
||||
document_name: None,
|
||||
document_path: Some(path),
|
||||
document_serialized_content: content,
|
||||
};
|
||||
let message = PortfolioMessage::OpenFile { path, content };
|
||||
dispatcher.queue_editor_message(message);
|
||||
}
|
||||
DesktopWrapperMessage::ImportFile { path, content } => {
|
||||
let extension = path.extension().and_then(|s| s.to_str()).unwrap_or_default().to_lowercase();
|
||||
match extension.as_str() {
|
||||
"svg" => {
|
||||
dispatcher.queue_desktop_wrapper_message(DesktopWrapperMessage::ImportSvg { path, content });
|
||||
}
|
||||
_ => {
|
||||
dispatcher.queue_desktop_wrapper_message(DesktopWrapperMessage::ImportImage { path, content });
|
||||
}
|
||||
}
|
||||
}
|
||||
DesktopWrapperMessage::ImportSvg { path, content } => {
|
||||
let Ok(content) = String::from_utf8(content) else {
|
||||
tracing::warn!("Svg file is invalid: {}", path.display());
|
||||
return;
|
||||
};
|
||||
|
||||
let message = PortfolioMessage::PasteSvg {
|
||||
name: path.file_stem().map(|s| s.to_string_lossy().to_string()),
|
||||
svg: content,
|
||||
mouse: None,
|
||||
parent_and_insert_index: None,
|
||||
};
|
||||
dispatcher.queue_editor_message(message);
|
||||
}
|
||||
DesktopWrapperMessage::ImportImage { path, content } => {
|
||||
let name = path.file_stem().and_then(|s| s.to_str()).map(|s| s.to_string());
|
||||
let extension = path.extension().and_then(|s| s.to_str()).unwrap_or_default().to_lowercase();
|
||||
let Some(image_format) = image::ImageFormat::from_extension(&extension) else {
|
||||
tracing::warn!("Unsupported file type: {}", path.display());
|
||||
return;
|
||||
};
|
||||
let reader = image::ImageReader::with_format(std::io::Cursor::new(content), image_format);
|
||||
let Ok(image) = reader.decode() else {
|
||||
tracing::error!("Failed to decode image: {}", path.display());
|
||||
return;
|
||||
};
|
||||
let width = image.width();
|
||||
let height = image.height();
|
||||
|
||||
// TODO: Handle Image formats with more than 8 bits per channel
|
||||
let image_data = image.to_rgba8();
|
||||
let image = Image::<Color>::from_image_data(image_data.as_raw(), width, height);
|
||||
let message = PortfolioMessage::PasteImage {
|
||||
name,
|
||||
image,
|
||||
mouse: None,
|
||||
parent_and_insert_index: None,
|
||||
};
|
||||
let message = PortfolioMessage::ImportFile { path, content };
|
||||
dispatcher.queue_editor_message(message);
|
||||
}
|
||||
DesktopWrapperMessage::PollNodeGraphEvaluation => dispatcher.poll_node_graph_evaluation(),
|
||||
|
||||
@@ -10,29 +10,17 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD
|
||||
FrontendMessage::RenderOverlays { context } => {
|
||||
dispatcher.respond(DesktopFrontendMessage::UpdateOverlays(context.take_scene()));
|
||||
}
|
||||
FrontendMessage::TriggerOpenDocument => {
|
||||
FrontendMessage::TriggerOpen => {
|
||||
dispatcher.respond(DesktopFrontendMessage::OpenFileDialog {
|
||||
title: "Open Document".to_string(),
|
||||
filters: vec![FileFilter {
|
||||
name: "Graphite".to_string(),
|
||||
extensions: vec!["graphite".to_string()],
|
||||
}],
|
||||
context: OpenFileDialogContext::Document,
|
||||
filters: vec![],
|
||||
context: OpenFileDialogContext::Open,
|
||||
});
|
||||
}
|
||||
FrontendMessage::TriggerImport => {
|
||||
dispatcher.respond(DesktopFrontendMessage::OpenFileDialog {
|
||||
title: "Import File".to_string(),
|
||||
filters: vec![
|
||||
FileFilter {
|
||||
name: "Svg".to_string(),
|
||||
extensions: vec!["svg".to_string()],
|
||||
},
|
||||
FileFilter {
|
||||
name: "Image".to_string(),
|
||||
extensions: vec!["png".to_string(), "jpg".to_string(), "jpeg".to_string(), "bmp".to_string()],
|
||||
},
|
||||
],
|
||||
filters: vec![],
|
||||
context: OpenFileDialogContext::Import,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use graph_craft::wasm_application_io::WasmApplicationIo;
|
||||
use graphite_editor::application::{Editor, Environment, Host, Platform};
|
||||
use graphite_editor::messages::prelude::{FrontendMessage, Message};
|
||||
|
||||
pub use graphite_editor::consts::FILE_EXTENSION;
|
||||
// TODO: Remove usage of this reexport in desktop create and remove this line
|
||||
pub use graphene_std::Color;
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ pub enum DesktopFrontendMessage {
|
||||
pub enum DesktopWrapperMessage {
|
||||
FromWeb(Box<EditorMessage>),
|
||||
Input(InputMessage),
|
||||
OpenFileDialogResult {
|
||||
FileDialogResult {
|
||||
path: PathBuf,
|
||||
content: Vec<u8>,
|
||||
context: OpenFileDialogContext,
|
||||
@@ -88,10 +88,6 @@ pub enum DesktopWrapperMessage {
|
||||
path: PathBuf,
|
||||
context: SaveFileDialogContext,
|
||||
},
|
||||
OpenDocument {
|
||||
path: PathBuf,
|
||||
content: Vec<u8>,
|
||||
},
|
||||
OpenFile {
|
||||
path: PathBuf,
|
||||
content: Vec<u8>,
|
||||
@@ -100,14 +96,6 @@ pub enum DesktopWrapperMessage {
|
||||
path: PathBuf,
|
||||
content: Vec<u8>,
|
||||
},
|
||||
ImportSvg {
|
||||
path: PathBuf,
|
||||
content: Vec<u8>,
|
||||
},
|
||||
ImportImage {
|
||||
path: PathBuf,
|
||||
content: Vec<u8>,
|
||||
},
|
||||
PollNodeGraphEvaluation,
|
||||
UpdateMaximized {
|
||||
maximized: bool,
|
||||
@@ -153,7 +141,7 @@ pub struct FileFilter {
|
||||
}
|
||||
|
||||
pub enum OpenFileDialogContext {
|
||||
Document,
|
||||
Open,
|
||||
Import,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user