1313using System . Linq ;
1414using System . Threading ;
1515using System . Threading . Tasks ;
16+ using Windows . ApplicationModel . DataTransfer ;
17+ using Windows . Foundation ;
18+ using Windows . Graphics ;
19+ using Windows . Storage ;
1620// ReSharper disable CheckNamespace
1721
1822#nullable enable
@@ -23,10 +27,36 @@ public sealed partial class PluginManagerPage
2327 public static PluginManagerPageContext Context { get ; } = new ( ) ;
2428 public static PluginManagerPage This { get ; private set ; } = null ! ;
2529
30+ private readonly DispatcherTimer _fileDragIndicatorTimer = new ( )
31+ {
32+ Interval = TimeSpan . FromMilliseconds ( 50 )
33+ } ;
34+ private bool _isWinUiFileDragActive ;
35+ private bool _isDropIndicatorVisible ;
36+
2637 public PluginManagerPage ( )
2738 {
2839 This = this ;
2940 InitializeComponent ( ) ;
41+ _fileDragIndicatorTimer . Tick += OnFileDragIndicatorTimerTick ;
42+ Loaded += OnPluginManagerPageLoaded ;
43+ Unloaded += OnPluginManagerPageUnloaded ;
44+ }
45+
46+ private void OnPluginManagerPageLoaded ( object sender , RoutedEventArgs e )
47+ {
48+ WindowUtility . FileDropEvent += OnNativeFileDrop ;
49+ WindowUtility . SetFileDropEnabled ( true ) ;
50+ _fileDragIndicatorTimer . Start ( ) ;
51+ }
52+
53+ private void OnPluginManagerPageUnloaded ( object sender , RoutedEventArgs e )
54+ {
55+ _fileDragIndicatorTimer . Stop ( ) ;
56+ _isWinUiFileDragActive = false ;
57+ SetImportDropIndicator ( false ) ;
58+ WindowUtility . SetFileDropEnabled ( false ) ;
59+ WindowUtility . FileDropEvent -= OnNativeFileDrop ;
3060 }
3161
3262 private void OnListViewRightClickUpdate ( object sender , RightTappedRoutedEventArgs e )
@@ -63,10 +93,8 @@ private void OnRestoreUninstalledPlugin(object sender, RoutedEventArgs e)
6393
6494 private async void OnClickImportButton ( object sender , RoutedEventArgs e )
6595 {
66- int imported = 0 ;
6796 try
6897 {
69- ImportBoxButton . IsEnabled = false ;
7098 Dictionary < string , string > supportedFiles = new ( )
7199 {
72100 { Locale . Current . Lang ? . _PluginManagerPage ? . FileDialogFileFilter1 ?? "" , "*.zip;manifest.json" }
@@ -79,6 +107,117 @@ private async void OnClickImportButton(object sender, RoutedEventArgs e)
79107 return ;
80108 }
81109
110+ await ImportPlugins ( selectedFiles ) ;
111+ }
112+ catch ( Exception ex )
113+ {
114+ ErrorSender . SendException ( ex . WrapPluginException ( "No plugin has been imported due to following error:" ) ) ;
115+ }
116+ }
117+
118+ private void OnDragEnterImportBox ( object sender , DragEventArgs e )
119+ {
120+ if ( ImportBoxButton . IsEnabled && e . DataView . Contains ( StandardDataFormats . StorageItems ) )
121+ {
122+ _isWinUiFileDragActive = true ;
123+ SetImportDropIndicator ( true ) ;
124+ e . AcceptedOperation = DataPackageOperation . Copy ;
125+ }
126+ }
127+
128+ private void OnDragLeaveImportBox ( object sender , DragEventArgs e )
129+ {
130+ Point position = e . GetPosition ( ImportBoxButton ) ;
131+ if ( position . X >= 0 && position . X <= ImportBoxButton . ActualWidth &&
132+ position . Y >= 0 && position . Y <= ImportBoxButton . ActualHeight )
133+ {
134+ return ;
135+ }
136+
137+ _isWinUiFileDragActive = false ;
138+ SetImportDropIndicator ( false ) ;
139+ }
140+
141+ private void OnDragOverImportBox ( object sender , DragEventArgs e )
142+ {
143+ if ( ImportBoxButton . IsEnabled && e . DataView . Contains ( StandardDataFormats . StorageItems ) )
144+ {
145+ _isWinUiFileDragActive = true ;
146+ SetImportDropIndicator ( true ) ;
147+ e . AcceptedOperation = DataPackageOperation . Copy ;
148+ }
149+ }
150+
151+ private async void OnDropImportBox ( object sender , DragEventArgs e )
152+ {
153+ _isWinUiFileDragActive = false ;
154+ SetImportDropIndicator ( false ) ;
155+ try
156+ {
157+ IReadOnlyList < IStorageItem > storageItems = await e . DataView . GetStorageItemsAsync ( ) ;
158+ string [ ] selectedFiles = storageItems . OfType < StorageFile > ( ) . Select ( file => file . Path ) . ToArray ( ) ;
159+ await ImportPlugins ( selectedFiles ) ;
160+ }
161+ catch ( Exception ex )
162+ {
163+ ErrorSender . SendException ( ex . WrapPluginException ( "No plugin has been imported due to following error:" ) ) ;
164+ }
165+ }
166+
167+ private async void OnNativeFileDrop ( string [ ] selectedFiles , PointInt32 dropPoint )
168+ {
169+ if ( ! IsPointInDropArea ( dropPoint ) )
170+ {
171+ return ;
172+ }
173+
174+ await ImportPlugins ( selectedFiles ) ;
175+ }
176+
177+ private void OnFileDragIndicatorTimerTick ( object ? sender , object e )
178+ {
179+ bool isExternalDragOverDropArea =
180+ WindowUtility . TryGetExternalDragPosition ( out PointInt32 dragPosition ) &&
181+ IsPointInDropArea ( dragPosition ) ;
182+ SetImportDropIndicator ( _isWinUiFileDragActive || isExternalDragOverDropArea ) ;
183+ }
184+
185+ private bool IsPointInDropArea ( PointInt32 point )
186+ {
187+ Rect dropAreaBounds = ImportBoxButton . TransformToVisual ( null )
188+ . TransformBounds ( new Rect ( 0 ,
189+ 0 ,
190+ ImportBoxButton . ActualWidth ,
191+ ImportBoxButton . ActualHeight ) ) ;
192+ double scaleFactor = WindowUtility . CurrentWindowMonitorScaleFactor ;
193+ return point . X >= dropAreaBounds . Left * scaleFactor &&
194+ point . X <= dropAreaBounds . Right * scaleFactor &&
195+ point . Y >= dropAreaBounds . Top * scaleFactor &&
196+ point . Y <= dropAreaBounds . Bottom * scaleFactor ;
197+ }
198+
199+ private void SetImportDropIndicator ( bool isVisible )
200+ {
201+ if ( _isDropIndicatorVisible == isVisible )
202+ {
203+ return ;
204+ }
205+
206+ _isDropIndicatorVisible = isVisible ;
207+ ImportDropIndicator . Opacity = isVisible ? 1 : 0 ;
208+ }
209+
210+ private async Task ImportPlugins ( string [ ] selectedFiles )
211+ {
212+ if ( selectedFiles . Length == 0 )
213+ {
214+ return ;
215+ }
216+
217+ int imported = 0 ;
218+ try
219+ {
220+ ImportBoxButton . IsEnabled = false ;
82221 List < Exception > exceptions = [ ] ;
83222
84223 foreach ( string filePath in selectedFiles )
0 commit comments