@@ -8,13 +8,14 @@ use std::sync::mpsc::{Receiver, Sender};
88use std:: thread;
99use std:: time:: { Duration , Instant } ;
1010use winit:: application:: ApplicationHandler ;
11- use winit:: data_transfer:: { DataTransferSendBuilder , TypeHint } ;
11+ use winit:: data_transfer:: TypeHint ;
1212use winit:: dpi:: PhysicalSize ;
1313use winit:: event:: { ElementState , MouseButton , StartCause , WindowEvent } ;
1414use winit:: event_loop:: run_on_demand:: EventLoopExtRunOnDemand ;
15- use winit:: event_loop:: { ActiveEventLoop , AsyncRequestSerial , ControlFlow , DndAction , EventLoop } ;
15+ use winit:: event_loop:: { ActiveEventLoop , ControlFlow , DndAction , EventLoop } ;
1616use winit:: window:: WindowId ;
1717
18+ use crate :: data_transfer:: { self , TransferReads } ;
1819use crate :: dirs;
1920use crate :: event:: { AppEvent , AppEventScheduler } ;
2021use crate :: input:: InputState ;
@@ -24,7 +25,7 @@ use crate::render::{RenderError, RenderState};
2425use crate :: spawner:: TokioSpawner ;
2526use crate :: ui:: { Acceleration , InputEvent , UiCommand , UiConfig , UiInstance } ;
2627use crate :: window:: Window ;
27- use crate :: wrapper:: messages:: { DesktopFrontendMessage , DesktopWrapperMessage , IngestAction } ;
28+ use crate :: wrapper:: messages:: { DataTransfer , DesktopFrontendMessage , DesktopWrapperMessage , IngestAction } ;
2829use crate :: wrapper:: { DesktopWrapper , FolderStore , MmapResourceStorage , WgpuContext , WgpuExecutor , serialize_frontend_messages} ;
2930
3031pub ( crate ) struct App {
@@ -36,8 +37,7 @@ pub(crate) struct App {
3637 window_maximized : bool ,
3738 window_fullscreen : bool ,
3839 window_pending_drag : bool ,
39- pending_dnd_fetch : Option < AsyncRequestSerial > ,
40- pending_clipboard_fetch : Option < AsyncRequestSerial > ,
40+ transfer_reads : TransferReads ,
4141 input_state : InputState ,
4242 app_event_receiver : Receiver < AppEvent > ,
4343 app_event_scheduler : AppEventScheduler ,
@@ -133,8 +133,7 @@ impl App {
133133 window_maximized : false ,
134134 window_fullscreen : false ,
135135 window_pending_drag : false ,
136- pending_dnd_fetch : None ,
137- pending_clipboard_fetch : None ,
136+ transfer_reads : TransferReads :: default ( ) ,
138137 input_state : InputState :: new ( ) ,
139138 app_event_receiver,
140139 app_event_scheduler,
@@ -256,16 +255,10 @@ impl App {
256255 futures:: executor:: block_on ( dialog. pick_file ( ) ) . into_iter ( ) . collect ( )
257256 } ;
258257
259- for handle in handles {
260- let path = handle. path ( ) . to_path_buf ( ) ;
261- match fs:: read ( & path) {
262- Ok ( content) => {
263- let action = action. clone ( ) ;
264- let message = DesktopWrapperMessage :: IngestFile { path, content, action } ;
265- app_event_scheduler. schedule ( AppEvent :: DesktopWrapperMessage ( message) ) ;
266- }
267- Err ( e) => tracing:: error!( "Failed to read file {}: {}" , path. display( ) , e) ,
268- }
258+ let files = data_transfer:: read_files ( handles. iter ( ) . map ( |handle| handle. path ( ) . to_path_buf ( ) ) ) ;
259+ if !files. is_empty ( ) {
260+ let message = DesktopWrapperMessage :: Ingest { content : DataTransfer ( files) , action } ;
261+ app_event_scheduler. schedule ( AppEvent :: DesktopWrapperMessage ( message) ) ;
269262 }
270263 } ) ;
271264 }
@@ -380,8 +373,8 @@ impl App {
380373 DesktopFrontendMessage :: ClipboardRead => {
381374 self . app_event_scheduler . schedule ( AppEvent :: ClipboardRead ) ;
382375 }
383- DesktopFrontendMessage :: ClipboardWrite { content, svg } => {
384- self . app_event_scheduler . schedule ( AppEvent :: ClipboardWrite { content, svg } ) ;
376+ DesktopFrontendMessage :: ClipboardWrite ( content) => {
377+ self . app_event_scheduler . schedule ( AppEvent :: ClipboardWrite ( content) ) ;
385378 }
386379 DesktopFrontendMessage :: PointerLock => {
387380 self . input_state . lock_pointer ( ) ;
@@ -502,31 +495,15 @@ impl App {
502495 }
503496 }
504497 AppEvent :: ClipboardRead => {
505- let result = event_loop. clipboard ( ) . and_then ( |id| {
506- let Some ( id) = id else { return Ok ( None ) } ;
507- let data_transfer = event_loop. data_transfer ( id) ?;
508- if !data_transfer. has_type ( & TypeHint :: Plaintext ) {
509- return Ok ( None ) ;
510- }
511- event_loop. fetch_data_transfer ( id, & TypeHint :: Plaintext ) . map ( Some )
512- } ) ;
513- match result {
514- Ok ( Some ( serial) ) => self . pending_clipboard_fetch = Some ( serial) ,
515- Ok ( None ) => self . dispatch_desktop_wrapper_message ( DesktopWrapperMessage :: ClipboardReadResult { content : None } ) ,
516- Err ( e) => {
517- tracing:: error!( "Failed to read from clipboard: {e}" ) ;
518- self . dispatch_desktop_wrapper_message ( DesktopWrapperMessage :: ClipboardReadResult { content : None } ) ;
519- }
498+ let read = event_loop
499+ . clipboard ( )
500+ . and_then ( |id| id. map_or ( Ok ( ( ) ) , |id| self . transfer_reads . start ( event_loop, id, IngestAction :: Paste ) ) ) ;
501+ if let Err ( e) = read {
502+ tracing:: error!( "Failed to read from clipboard: {e}" ) ;
520503 }
521504 }
522- AppEvent :: ClipboardWrite { content, svg } => {
523- let has_svg = svg. is_some ( ) ;
524- let mut builder = DataTransferSendBuilder :: new ( ( content, svg) ) . with_type ( TypeHint :: Plaintext , |( content, _) , _| Some ( content. clone ( ) ) ) ;
525- if has_svg {
526- builder = builder. with_type ( TypeHint :: Image { extension_hint : Some ( "svg" ) } , |( _, svg) , _| svg. as_ref ( ) . map ( |svg| svg. as_bytes ( ) . to_vec ( ) ) ) ;
527- }
528- let send_data = builder. build ( ) ;
529- if let Err ( e) = event_loop. set_clipboard ( send_data) {
505+ AppEvent :: ClipboardWrite ( content) => {
506+ if let Err ( e) = event_loop. set_clipboard ( data_transfer:: send ( content) ) {
530507 tracing:: error!( "Failed to write to clipboard: {e}" ) ;
531508 }
532509 }
@@ -559,18 +536,16 @@ impl App {
559536 }
560537 let app_event_scheduler = self . app_event_scheduler . clone ( ) ;
561538 let _ = thread:: spawn ( move || {
562- for path in paths {
539+ for path in & paths {
563540 tracing:: info!( "Opening file: {}" , path. display( ) ) ;
564- if let Ok ( content) = fs:: read ( & path) {
565- let message = DesktopWrapperMessage :: IngestFile {
566- path,
567- content,
568- action : IngestAction :: Open ,
569- } ;
570- app_event_scheduler. schedule ( AppEvent :: DesktopWrapperMessage ( message) ) ;
571- } else {
572- tracing:: error!( "Failed to read file: {}" , path. display( ) ) ;
573- }
541+ }
542+ let files = data_transfer:: read_files ( paths) ;
543+ if !files. is_empty ( ) {
544+ let message = DesktopWrapperMessage :: Ingest {
545+ content : DataTransfer ( files) ,
546+ action : IngestAction :: Open ,
547+ } ;
548+ app_event_scheduler. schedule ( AppEvent :: DesktopWrapperMessage ( message) ) ;
574549 }
575550 } ) ;
576551 }
@@ -670,57 +645,25 @@ impl ApplicationHandler for App {
670645 }
671646 }
672647 WindowEvent :: DragEntered { id, .. } => {
673- let accepts = event_loop. data_transfer ( id) . is_ok_and ( |data_transfer| data_transfer. has_type ( & TypeHint :: UriList ) ) ;
648+ let accepts = event_loop. data_transfer ( id) . is_ok_and ( |data_transfer| {
649+ data_transfer. has_type ( & TypeHint :: UriList ) || data_transfer. has_type ( & TypeHint :: Image { extension_hint : None } ) || data_transfer. has_type ( & TypeHint :: Plaintext )
650+ } ) ;
674651 let actions: & [ DndAction ] = if accepts { & [ DndAction :: Copy ] } else { & [ ] } ;
675652 if let Err ( e) = event_loop. set_valid_dnd_actions ( id, actions) {
676653 tracing:: error!( "Failed to set valid drag and drop actions: {e}" ) ;
677654 }
678655 }
679- WindowEvent :: DragDropped { id, .. } => match event_loop. fetch_data_transfer ( id, & TypeHint :: UriList ) {
680- Ok ( serial) => self . pending_dnd_fetch = Some ( serial) ,
681- Err ( e) => tracing:: error!( "Failed to fetch dropped data: {e}" ) ,
682- } ,
683- WindowEvent :: DataTransferReceived { serial, ref value, .. } if self . pending_clipboard_fetch == Some ( serial) => match value. try_as_string ( ) {
684- Ok ( content) => {
685- self . pending_clipboard_fetch = None ;
686- let message = DesktopWrapperMessage :: ClipboardReadResult { content : Some ( content) } ;
687- self . app_event_scheduler . schedule ( AppEvent :: DesktopWrapperMessage ( message) ) ;
656+ WindowEvent :: DragDropped { id, .. } => {
657+ if let Err ( e) = self . transfer_reads . start ( event_loop, id, IngestAction :: Import ) {
658+ tracing:: error!( "Failed to fetch dropped data: {e}" ) ;
688659 }
689- Err ( e) if e. kind ( ) == std:: io:: ErrorKind :: WouldBlock => { }
690- Err ( e) => {
691- self . pending_clipboard_fetch = None ;
692- tracing:: error!( "Failed to read from clipboard: {e}" ) ;
693- let message = DesktopWrapperMessage :: ClipboardReadResult { content : None } ;
660+ }
661+ WindowEvent :: DataTransferReceived { serial, ref value, .. } => {
662+ if let Some ( ( content, action) ) = self . transfer_reads . receive ( serial, value. as_ref ( ) ) {
663+ let message = DesktopWrapperMessage :: Ingest { content, action } ;
694664 self . app_event_scheduler . schedule ( AppEvent :: DesktopWrapperMessage ( message) ) ;
695665 }
696- } ,
697- WindowEvent :: DataTransferReceived { serial, ref value, .. } if self . pending_dnd_fetch == Some ( serial) => match value. try_as_uris ( ) {
698- Ok ( uris) => {
699- self . pending_dnd_fetch = None ;
700- let paths = uris. iter ( ) . filter_map ( |uri| url:: Url :: parse ( uri) . ok ( ) ?. to_file_path ( ) . ok ( ) ) ;
701- for path in paths {
702- match fs:: read ( & path) {
703- Ok ( content) => {
704- let message = DesktopWrapperMessage :: IngestFile {
705- path,
706- content,
707- action : IngestAction :: Import ,
708- } ;
709- self . app_event_scheduler . schedule ( AppEvent :: DesktopWrapperMessage ( message) ) ;
710- }
711- Err ( e) => {
712- tracing:: error!( "Failed to read dropped file {}: {}" , path. display( ) , e) ;
713- return ;
714- }
715- } ;
716- }
717- }
718- Err ( e) if e. kind ( ) == std:: io:: ErrorKind :: WouldBlock => { }
719- Err ( e) => {
720- self . pending_dnd_fetch = None ;
721- tracing:: error!( "Failed to read dropped data: {e}" ) ;
722- }
723- } ,
666+ }
724667
725668 WindowEvent :: PointerMoved { .. } | WindowEvent :: PointerLeft { position : Some ( _) , .. } | WindowEvent :: PointerEntered { .. }
726669 if !self . input_state . pointer_locked ( ) && self . window_pending_drag =>
0 commit comments