diff --git a/src/ClaudeDo.Ui/Views/Islands/TasksIslandView.axaml b/src/ClaudeDo.Ui/Views/Islands/TasksIslandView.axaml index 6838c30..54a4bd1 100644 --- a/src/ClaudeDo.Ui/Views/Islands/TasksIslandView.axaml +++ b/src/ClaudeDo.Ui/Views/Islands/TasksIslandView.axaml @@ -95,13 +95,7 @@ @@ -120,13 +114,7 @@ @@ -159,13 +147,7 @@ diff --git a/src/ClaudeDo.Ui/Views/Islands/TasksIslandView.axaml.cs b/src/ClaudeDo.Ui/Views/Islands/TasksIslandView.axaml.cs index 0bdd650..464b377 100644 --- a/src/ClaudeDo.Ui/Views/Islands/TasksIslandView.axaml.cs +++ b/src/ClaudeDo.Ui/Views/Islands/TasksIslandView.axaml.cs @@ -1,28 +1,43 @@ +using System; using System.Linq; using Avalonia; using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Layout; using Avalonia.Media; using Avalonia.Threading; using Avalonia.VisualTree; +using ClaudeDo.Ui.ViewModels; using ClaudeDo.Ui.ViewModels.Islands; using ClaudeDo.Ui.ViewModels.Modals; +using ClaudeDo.Ui.Views.Controls; +using ClaudeDo.Ui.Views.MissionControl; using ClaudeDo.Ui.Views.Modals; namespace ClaudeDo.Ui.Views.Islands; public partial class TasksIslandView : UserControl { - // Public so the Mission Control window can accept the same drag payload (drop-to-queue). - public static readonly DataFormat TaskRowFormat = - DataFormat.CreateStringApplicationFormat("claudedo-task-row"); + private readonly TaskDragController _drag = new(); + + // Custom-drag gesture state. The drag is ARMED on press and BEGINS once the pointer moves + // past the threshold, so a plain click still selects the row. + private const double DragThreshold = 4; + private Point _pressPoint; + private TaskRowViewModel? _pressRow; + private Control? _pressControl; + private bool _dragArmed; + private bool _dragging; public TasksIslandView() { InitializeComponent(); AddHandler(PointerPressedEvent, OnTunnelPointerPressed, RoutingStrategies.Tunnel); + AddHandler(PointerMovedEvent, OnPointerMovedDrag, RoutingStrategies.Tunnel); + AddHandler(PointerReleasedEvent, OnPointerReleasedDrag, RoutingStrategies.Tunnel); + AddHandler(PointerCaptureLostEvent, OnPointerCaptureLost); DataContextChanged += (_, _) => { if (DataContext is TasksIslandViewModel vm) @@ -103,9 +118,15 @@ public partial class TasksIslandView : UserControl return await tcs.Task; } - private async void OnTunnelPointerPressed(object? sender, PointerPressedEventArgs e) + // ── Custom ghost drag ──────────────────────────────────────────────────── + // Replaces both the OLE DoDragDropAsync reorder and the OLE drop-to-queue path: a hand-built + // drag (pointer capture + a transparent topmost ghost window) is the only way to get a + // translucent follower that crosses from this window into the separate Mission Control window. + + private void OnTunnelPointerPressed(object? sender, PointerPressedEventArgs e) { - if (DataContext is not TasksIslandViewModel vm) return; + ResetPressState(); + if (DataContext is not TasksIslandViewModel) return; if (e.Source is not Visual src) return; var button = src as Button ?? src.FindAncestorOfType