@@ -21,6 +21,7 @@ public MainWindow()
2121 {
2222 InitializeComponent ( ) ;
2323 Closing += OnWindowClosing ;
24+
2425 Loaded += async ( _ , _ ) =>
2526 {
2627 if ( DataContext is MainWindowViewModel vm )
@@ -294,6 +295,105 @@ private void OnCompositionSpriteDoubleTapped(object? sender, TappedEventArgs e)
294295 }
295296 }
296297
298+ // ── Sprite drag-drop: drag from sprite list → drop on composition cell ──
299+
300+ private SpriteViewModel ? _dragSprite ;
301+ private Popup ? _dragAdorner ;
302+ private bool _spriteDragActive ;
303+
304+ private void OnRightSpritePointerPressed ( object ? sender , PointerPressedEventArgs e )
305+ {
306+ if ( sender is not Border border || border . DataContext is not SpriteViewModel svm )
307+ return ;
308+
309+ var props = e . GetCurrentPoint ( border ) . Properties ;
310+ if ( ! props . IsLeftButtonPressed ) return ;
311+
312+ _dragSprite = svm ;
313+ _spriteDragActive = false ;
314+ e . Pointer . Capture ( border ) ;
315+ e . Handled = true ;
316+ }
317+
318+ private void OnRightSpritePointerMoved ( object ? sender , PointerEventArgs e )
319+ {
320+ if ( _dragSprite == null ) return ;
321+
322+ if ( ! _spriteDragActive )
323+ {
324+ _spriteDragActive = true ;
325+ // Create floating adorner showing the sprite image
326+ var img = new Image
327+ {
328+ Source = _dragSprite . Bitmap ,
329+ Width = 32 ,
330+ Height = 32 ,
331+ Stretch = Stretch . None
332+ } ;
333+ RenderOptions . SetBitmapInterpolationMode ( img , Avalonia . Media . Imaging . BitmapInterpolationMode . None ) ;
334+ _dragAdorner = new Popup
335+ {
336+ IsLightDismissEnabled = false ,
337+ Placement = PlacementMode . Pointer ,
338+ PlacementTarget = this ,
339+ Child = new Border
340+ {
341+ Background = Brushes . Transparent ,
342+ Opacity = 0.85 ,
343+ IsHitTestVisible = false ,
344+ Child = img
345+ }
346+ } ;
347+ ( ( Panel ) this . Content ! ) . Children . Add ( _dragAdorner ) ;
348+ _dragAdorner . Open ( ) ;
349+ }
350+
351+ // Reposition by re-opening at pointer
352+ if ( _dragAdorner != null )
353+ {
354+ _dragAdorner . Close ( ) ;
355+ _dragAdorner . Open ( ) ;
356+ }
357+ }
358+
359+ private void OnRightSpritePointerReleased ( object ? sender , PointerReleasedEventArgs e )
360+ {
361+ if ( _dragSprite == null ) return ;
362+
363+ var draggedSprite = _dragSprite ;
364+ _dragSprite = null ;
365+ _spriteDragActive = false ;
366+
367+ // Close and remove adorner
368+ if ( _dragAdorner != null )
369+ {
370+ _dragAdorner . Close ( ) ;
371+ if ( this . Content is Panel panel )
372+ panel . Children . Remove ( _dragAdorner ) ;
373+ _dragAdorner = null ;
374+ }
375+
376+ e . Pointer . Capture ( null ) ;
377+
378+ // Hit-test: find the composition cell under the pointer
379+ var pos = e . GetPosition ( this ) ;
380+ var hit = this . InputHitTest ( pos ) ;
381+ var visual = hit as Control ;
382+ SpriteViewModel ? targetSlot = null ;
383+ while ( visual != null )
384+ {
385+ if ( visual . DataContext is SpriteViewModel svm && svm . SlotIndex >= 0 )
386+ {
387+ targetSlot = svm ;
388+ break ;
389+ }
390+ visual = visual . Parent as Control ;
391+ }
392+
393+ if ( targetSlot != null && DataContext is MainWindowViewModel vm )
394+ vm . AssignSpriteToSlot ( targetSlot , draggedSprite . SpriteId ) ;
395+ }
396+
297397 private void OnClientItemDoubleTapped ( object ? sender , TappedEventArgs e )
298398 {
299399 if ( DataContext is MainWindowViewModel vm )
0 commit comments