Replace the UniformGrid (all tiles forced to equal size) with a real Grid + GridSplitters built in code-behind per pane count/column count, so panes resize individually. The queue strip's fixed 210px column becomes a drag-resizable Grid column (min 160px) that collapses to 0 when nothing is queued, replacing the old dock-based fixed width.
171 lines
6.2 KiB
C#
171 lines
6.2 KiB
C#
using System;
|
|
using System.Collections.Specialized;
|
|
using System.ComponentModel;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Interactivity;
|
|
using Avalonia.Layout;
|
|
using Avalonia.Media;
|
|
using Avalonia.Platform.Storage;
|
|
using ClaudeDo.Ui.ViewModels;
|
|
|
|
namespace ClaudeDo.Ui.Views.MissionControl;
|
|
|
|
public partial class MissionControlView : UserControl
|
|
{
|
|
private const double SplitterSize = 6;
|
|
private const double DefaultQueueWidth = 210;
|
|
private const double MinQueueWidth = 160;
|
|
private const double MinPaneWidth = 220;
|
|
private const double MinPaneHeight = 140;
|
|
|
|
private MissionControlViewModel? _vm;
|
|
private GridLength _queueWidth = new(DefaultQueueWidth, GridUnitType.Pixel);
|
|
|
|
public MissionControlView()
|
|
{
|
|
InitializeComponent();
|
|
DataContextChanged += OnDataContextChanged;
|
|
QueueSplitter.DragCompleted += (_, _) => _queueWidth = BodyGrid.ColumnDefinitions[2].Width;
|
|
}
|
|
|
|
// Ad-hoc ConPTY session: the view owns the folder picker, the VM only takes the chosen path.
|
|
private async void OnNewSessionClicked(object? sender, RoutedEventArgs e)
|
|
{
|
|
if (DataContext is not MissionControlViewModel vm) return;
|
|
var topLevel = TopLevel.GetTopLevel(this);
|
|
if (topLevel is null) return;
|
|
|
|
var folders = await topLevel.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
|
{
|
|
Title = "Choose a directory",
|
|
AllowMultiple = false,
|
|
});
|
|
if (folders.Count == 0) return;
|
|
|
|
await vm.OpenAdHocConPtySessionAsync(folders[0].Path.LocalPath);
|
|
}
|
|
|
|
private void OnDataContextChanged(object? sender, EventArgs e)
|
|
{
|
|
if (_vm is not null)
|
|
{
|
|
_vm.Panes.CollectionChanged -= OnPanesChanged;
|
|
_vm.PropertyChanged -= OnViewModelPropertyChanged;
|
|
}
|
|
|
|
_vm = DataContext as MissionControlViewModel;
|
|
|
|
if (_vm is not null)
|
|
{
|
|
_vm.Panes.CollectionChanged += OnPanesChanged;
|
|
_vm.PropertyChanged += OnViewModelPropertyChanged;
|
|
}
|
|
|
|
RebuildOverviewGrid();
|
|
UpdateQueueColumn();
|
|
}
|
|
|
|
private void OnPanesChanged(object? sender, NotifyCollectionChangedEventArgs e) => RebuildOverviewGrid();
|
|
|
|
private void OnViewModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
|
{
|
|
if (e.PropertyName == nameof(MissionControlViewModel.ColumnCount))
|
|
RebuildOverviewGrid();
|
|
else if (e.PropertyName == nameof(MissionControlViewModel.HasQueued))
|
|
UpdateQueueColumn();
|
|
}
|
|
|
|
// The queue column is Pixel-widthed (so the splitter can drag it) — when nothing is
|
|
// queued it collapses to 0 instead of leaving a blank gap, and remembers the last
|
|
// dragged width for the rest of this window's lifetime (not persisted beyond it).
|
|
private void UpdateQueueColumn()
|
|
{
|
|
var queueColumn = BodyGrid.ColumnDefinitions[2];
|
|
if (_vm?.HasQueued == true)
|
|
{
|
|
queueColumn.MinWidth = MinQueueWidth;
|
|
queueColumn.Width = _queueWidth;
|
|
}
|
|
else
|
|
{
|
|
queueColumn.MinWidth = 0;
|
|
queueColumn.Width = new GridLength(0, GridUnitType.Pixel);
|
|
}
|
|
}
|
|
|
|
// Replaces the old UniformGrid with a real Grid + GridSplitters so panes resize
|
|
// individually. Column splitters span every row (resize that whole column vs. its
|
|
// neighbor); row splitters span every column. Rebuilt from scratch whenever the pane
|
|
// count or column count changes — per-pane sizes are intentionally not preserved across
|
|
// a rebuild (session-only resizing, reset to the default split when panes come/go).
|
|
private void RebuildOverviewGrid()
|
|
{
|
|
OverviewGrid.Children.Clear();
|
|
OverviewGrid.ColumnDefinitions.Clear();
|
|
OverviewGrid.RowDefinitions.Clear();
|
|
|
|
var panes = _vm?.Panes;
|
|
if (panes is not { Count: > 0 }) return;
|
|
|
|
var columns = Math.Max(1, _vm!.ColumnCount);
|
|
var rows = (int)Math.Ceiling(panes.Count / (double)columns);
|
|
|
|
for (var c = 0; c < columns; c++)
|
|
{
|
|
if (c > 0) OverviewGrid.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Auto));
|
|
OverviewGrid.ColumnDefinitions.Add(new ColumnDefinition(new GridLength(1, GridUnitType.Star)) { MinWidth = MinPaneWidth });
|
|
}
|
|
|
|
for (var r = 0; r < rows; r++)
|
|
{
|
|
if (r > 0) OverviewGrid.RowDefinitions.Add(new RowDefinition(GridLength.Auto));
|
|
OverviewGrid.RowDefinitions.Add(new RowDefinition(new GridLength(1, GridUnitType.Star)) { MinHeight = MinPaneHeight });
|
|
}
|
|
|
|
var totalGridRows = rows * 2 - 1;
|
|
var totalGridCols = columns * 2 - 1;
|
|
|
|
for (var c = 1; c < columns; c++)
|
|
{
|
|
var splitter = new GridSplitter
|
|
{
|
|
Width = SplitterSize,
|
|
Background = Brushes.Transparent,
|
|
HorizontalAlignment = HorizontalAlignment.Stretch,
|
|
VerticalAlignment = VerticalAlignment.Stretch,
|
|
ResizeDirection = GridResizeDirection.Columns,
|
|
ResizeBehavior = GridResizeBehavior.PreviousAndNext,
|
|
};
|
|
Grid.SetColumn(splitter, c * 2 - 1);
|
|
Grid.SetRow(splitter, 0);
|
|
Grid.SetRowSpan(splitter, totalGridRows);
|
|
OverviewGrid.Children.Add(splitter);
|
|
}
|
|
|
|
for (var r = 1; r < rows; r++)
|
|
{
|
|
var splitter = new GridSplitter
|
|
{
|
|
Height = SplitterSize,
|
|
Background = Brushes.Transparent,
|
|
HorizontalAlignment = HorizontalAlignment.Stretch,
|
|
VerticalAlignment = VerticalAlignment.Stretch,
|
|
ResizeDirection = GridResizeDirection.Rows,
|
|
ResizeBehavior = GridResizeBehavior.PreviousAndNext,
|
|
};
|
|
Grid.SetColumn(splitter, 0);
|
|
Grid.SetColumnSpan(splitter, totalGridCols);
|
|
Grid.SetRow(splitter, r * 2 - 1);
|
|
OverviewGrid.Children.Add(splitter);
|
|
}
|
|
|
|
for (var i = 0; i < panes.Count; i++)
|
|
{
|
|
var presenter = new ContentControl { Content = panes[i] };
|
|
Grid.SetColumn(presenter, (i % columns) * 2);
|
|
Grid.SetRow(presenter, (i / columns) * 2);
|
|
OverviewGrid.Children.Add(presenter);
|
|
}
|
|
}
|
|
}
|