@@ -7,8 +7,12 @@ use crate::dialogs::dialog_save_graphite_file;
77use crate :: render:: GraphicsState ;
88use crate :: render:: WgpuContext ;
99use graph_craft:: wasm_application_io:: WasmApplicationIo ;
10+ use graphene_std:: Color ;
11+ use graphene_std:: raster:: Image ;
1012use graphite_editor:: application:: Editor ;
13+ use graphite_editor:: consts:: DEFAULT_DOCUMENT_NAME ;
1114use graphite_editor:: messages:: prelude:: * ;
15+ use std:: fs;
1216use std:: sync:: Arc ;
1317use std:: sync:: mpsc:: Sender ;
1418use std:: thread;
@@ -75,7 +79,7 @@ impl WinitApp {
7579 String :: new ( )
7680 } ) ;
7781 let message = PortfolioMessage :: OpenDocumentFile {
78- document_name : path. file_name ( ) . and_then ( |s| s. to_str ( ) ) . unwrap_or ( "unknown" ) . to_string ( ) ,
82+ document_name : path. file_stem ( ) . and_then ( |s| s. to_str ( ) ) . unwrap_or ( "unknown" ) . to_string ( ) ,
7983 document_serialized_content : content,
8084 } ;
8185 let _ = event_loop_proxy. send_event ( CustomEvent :: DispatchMessage ( message. into ( ) ) ) ;
@@ -264,6 +268,75 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
264268 let Some ( event) = self . cef_context . handle_window_event ( event) else { return } ;
265269
266270 match event {
271+ // Currently not supported on wayland see https://github.com/rust-windowing/winit/issues/1881
272+ WindowEvent :: DroppedFile ( path) => {
273+ let name = path. file_stem ( ) . and_then ( |s| s. to_str ( ) ) . map ( |s| s. to_string ( ) ) ;
274+ let Some ( extension) = path. extension ( ) . and_then ( |s| s. to_str ( ) ) else {
275+ tracing:: warn!( "Unsupported file dropped: {}" , path. display( ) ) ;
276+ // Fine to early return since we don't need to do cef work in this case
277+ return ;
278+ } ;
279+ let load_string = |path : & std:: path:: PathBuf | {
280+ let Ok ( content) = fs:: read_to_string ( path) else {
281+ tracing:: error!( "Failed to read file: {}" , path. display( ) ) ;
282+ return None ;
283+ } ;
284+
285+ if content. is_empty ( ) {
286+ tracing:: warn!( "Dropped file is empty: {}" , path. display( ) ) ;
287+ return None ;
288+ }
289+ Some ( content)
290+ } ;
291+ // TODO: Consider moving this logic to the editor so we have one message to load data which is then demultiplexed in the portfolio message handler
292+ match extension {
293+ "graphite" => {
294+ let Some ( content) = load_string ( & path) else { return } ;
295+
296+ let message = PortfolioMessage :: OpenDocumentFile {
297+ document_name : name. unwrap_or ( DEFAULT_DOCUMENT_NAME . to_string ( ) ) ,
298+ document_serialized_content : content,
299+ } ;
300+ self . dispatch_message ( message. into ( ) ) ;
301+ }
302+ "svg" => {
303+ let Some ( content) = load_string ( & path) else { return } ;
304+
305+ let message = PortfolioMessage :: PasteSvg {
306+ name : path. file_stem ( ) . map ( |s| s. to_string_lossy ( ) . to_string ( ) ) ,
307+ svg : content,
308+ mouse : None ,
309+ parent_and_insert_index : None ,
310+ } ;
311+ self . dispatch_message ( message. into ( ) ) ;
312+ }
313+ _ => match image:: ImageReader :: open ( & path) {
314+ Ok ( reader) => match reader. decode ( ) {
315+ Ok ( image) => {
316+ let width = image. width ( ) ;
317+ let height = image. height ( ) ;
318+ // TODO: support loading images with more than 8 bits per channel
319+ let image_data = image. to_rgba8 ( ) ;
320+ let image = Image :: < Color > :: from_image_data ( image_data. as_raw ( ) , width, height) ;
321+
322+ let message = PortfolioMessage :: PasteImage {
323+ name,
324+ image,
325+ mouse : None ,
326+ parent_and_insert_index : None ,
327+ } ;
328+ self . dispatch_message ( message. into ( ) ) ;
329+ }
330+ Err ( e) => {
331+ tracing:: error!( "Failed to decode image: {}: {}" , path. display( ) , e) ;
332+ }
333+ } ,
334+ Err ( e) => {
335+ tracing:: error!( "Failed to open image file: {}: {}" , path. display( ) , e) ;
336+ }
337+ } ,
338+ }
339+ }
267340 WindowEvent :: CloseRequested => {
268341 tracing:: info!( "The close button was pressed; stopping" ) ;
269342 event_loop. exit ( ) ;
0 commit comments