feat(ui): drag-reorder Mission Control panes by their header
This commit is contained in:
@@ -1,8 +1,46 @@
|
||||
using System.Linq;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.VisualTree;
|
||||
using ClaudeDo.Ui.ViewModels;
|
||||
using ClaudeDo.Ui.ViewModels.Islands;
|
||||
|
||||
namespace ClaudeDo.Ui.Views.MissionControl;
|
||||
|
||||
public partial class MissionControlView : UserControl
|
||||
{
|
||||
public MissionControlView() => InitializeComponent();
|
||||
// Shared with MonitorPaneView (the drag source).
|
||||
public static readonly DataFormat<string> PaneFormat =
|
||||
DataFormat.CreateStringApplicationFormat("claudedo-monitor-pane");
|
||||
|
||||
public MissionControlView()
|
||||
{
|
||||
InitializeComponent();
|
||||
AddHandler(DragDrop.DragOverEvent, OnPaneDragOver);
|
||||
AddHandler(DragDrop.DropEvent, OnPaneDrop);
|
||||
}
|
||||
|
||||
private void OnPaneDragOver(object? sender, DragEventArgs e)
|
||||
{
|
||||
e.DragEffects = (e.DataTransfer?.Contains(PaneFormat) ?? false)
|
||||
? DragDropEffects.Move
|
||||
: DragDropEffects.None;
|
||||
}
|
||||
|
||||
private void OnPaneDrop(object? sender, DragEventArgs e)
|
||||
{
|
||||
if (DataContext is not MissionControlViewModel vm) return;
|
||||
var draggedId = e.DataTransfer?.TryGetValue(PaneFormat);
|
||||
if (string.IsNullOrEmpty(draggedId)) return;
|
||||
if (e.Source is not Avalonia.Visual src) return;
|
||||
|
||||
var targetPane = src.FindAncestorOfType<MonitorPaneView>();
|
||||
if (targetPane?.DataContext is not TaskMonitorViewModel target) return;
|
||||
|
||||
var dragged = vm.Monitors.FirstOrDefault(m => m.SubscribedTaskId == draggedId);
|
||||
if (dragged is null) return;
|
||||
|
||||
vm.MoveMonitor(dragged, target);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
x:DataType="vm:TaskMonitorViewModel"
|
||||
x:Class="ClaudeDo.Ui.Views.MissionControl.MonitorPaneView">
|
||||
<Border Classes="monitor-pane"
|
||||
DragDrop.AllowDrop="True"
|
||||
Classes.mon-review="{Binding IsWaitingForReview}"
|
||||
Classes.mon-done="{Binding IsDone}"
|
||||
Classes.mon-roadblock="{Binding HasRoadblock}"
|
||||
@@ -17,7 +18,8 @@
|
||||
<Border DockPanel.Dock="Top"
|
||||
Background="{DynamicResource Surface2Brush}"
|
||||
BorderBrush="{DynamicResource LineBrush}"
|
||||
BorderThickness="0,0,0,1" Padding="6,3">
|
||||
BorderThickness="0,0,0,1" Padding="6,3"
|
||||
PointerPressed="OnHeaderPressed">
|
||||
<StackPanel Orientation="Horizontal" Spacing="2"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Button Classes="title-ctrl"
|
||||
|
||||
@@ -1,8 +1,26 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.VisualTree;
|
||||
using ClaudeDo.Ui.ViewModels.Islands;
|
||||
|
||||
namespace ClaudeDo.Ui.Views.MissionControl;
|
||||
|
||||
public partial class MonitorPaneView : UserControl
|
||||
{
|
||||
public MonitorPaneView() => InitializeComponent();
|
||||
|
||||
private async void OnHeaderPressed(object? sender, PointerPressedEventArgs e)
|
||||
{
|
||||
if (DataContext is not TaskMonitorViewModel m || m.SubscribedTaskId is not { } id) return;
|
||||
if (e.Source is not Avalonia.Visual src) return;
|
||||
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) return;
|
||||
|
||||
// Don't start a drag when the press landed on a header action button.
|
||||
var button = src as Button ?? src.FindAncestorOfType<Button>();
|
||||
if (button is not null) return;
|
||||
|
||||
var data = new DataTransfer();
|
||||
data.Add(DataTransferItem.Create(MissionControlView.PaneFormat, id));
|
||||
await DragDrop.DoDragDropAsync(e, data, DragDropEffects.Move);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user