feat(mission-control): make overview panes and queue strip individually resizable

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.
This commit is contained in:
mika kuns
2026-08-05 15:29:00 +02:00
parent 83ea429b8a
commit 3ea48ff76d
2 changed files with 228 additions and 76 deletions
@@ -51,85 +51,98 @@
</Grid>
</Border>
<!-- Read-only queue strip — collapses when nothing is queued -->
<Border DockPanel.Dock="Right"
IsVisible="{Binding HasQueued}"
Width="210"
Background="{DynamicResource DeepBrush}"
BorderBrush="{DynamicResource LineBrush}"
BorderThickness="1,0,0,0">
<DockPanel LastChildFill="True" Margin="10,10">
<TextBlock DockPanel.Dock="Top" Classes="eyebrow"
Text="{loc:Tr missionControl.queue}"
<!-- Body: main content | queue splitter | queue strip (widths managed in code-behind
so the queue column collapses to zero when nothing is queued). -->
<Grid x:Name="BodyGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="0"/>
</Grid.ColumnDefinitions>
<!-- Grid / tabs / empty state -->
<Panel Grid.Column="0" Margin="6">
<!-- Overview mode: a real Grid built in code-behind so each pane is individually
resizable via GridSplitters, instead of a UniformGrid where every cell is forced
to the same size. -->
<Grid x:Name="OverviewGrid" IsVisible="{Binding !IsFocusMode}" />
<!-- Focus mode: one pane large, switched via tabs -->
<TabControl ItemsSource="{Binding Panes}"
SelectedItem="{Binding FocusedPane}"
IsVisible="{Binding IsFocusMode}">
<TabControl.ItemTemplate>
<DataTemplate x:DataType="vmm:IMissionControlPane">
<TextBlock Text="{Binding DisplayTitle}" TextTrimming="CharacterEllipsis" MaxWidth="160" />
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
<TextBlock IsVisible="{Binding !HasPanes}"
Text="{loc:Tr missionControl.empty}"
Foreground="{DynamicResource TextMuteBrush}"
LetterSpacing="1.4" Margin="0,0,0,8" />
<ScrollViewer>
<ItemsControl ItemsSource="{Binding Queued}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:QueuedTaskViewModel">
<Button Command="{Binding OpenInAppCommand}"
Padding="0" Margin="0,0,0,4"
Background="Transparent" BorderThickness="0"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
Cursor="Hand">
<Panel>
<Border Background="{DynamicResource SurfaceBrush}"
BorderBrush="{DynamicResource LineBrush}"
BorderThickness="1" CornerRadius="6" />
<Border Background="{DynamicResource RunningTintBrush}"
BorderBrush="{DynamicResource RunningTintBorderBrush}"
BorderThickness="1" CornerRadius="6"
IsVisible="{Binding IsRunning}" />
<StackPanel Margin="8,6" Spacing="2">
<TextBlock Text="{Binding Title}"
TextTrimming="CharacterEllipsis"
ToolTip.Tip="{Binding Title}"
Foreground="{DynamicResource TextDimBrush}" />
<TextBlock Classes="meta"
Text="{loc:Tr missionControl.blocked}"
IsVisible="{Binding IsBlocked}"
Foreground="{DynamicResource AmberBrush}" />
<TextBlock Classes="meta"
Text="{loc:Tr missionControl.running}"
IsVisible="{Binding IsRunning}"
Foreground="{DynamicResource StatusRunningBrush}" />
</StackPanel>
</Panel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</DockPanel>
</Border>
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Panel>
<!-- Grid / tabs / empty state -->
<Panel Margin="6">
<!-- Overview mode: the existing UniformGrid, one tile per pane -->
<ItemsControl ItemsSource="{Binding Panes}" IsVisible="{Binding !IsFocusMode}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid x:CompileBindings="False" Columns="{Binding ColumnCount}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<GridSplitter x:Name="QueueSplitter" Grid.Column="1"
IsVisible="{Binding HasQueued}"
Width="6"
Background="Transparent"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
ResizeDirection="Columns"
ResizeBehavior="PreviousAndNext"/>
<!-- Focus mode: one pane large, switched via tabs -->
<TabControl ItemsSource="{Binding Panes}"
SelectedItem="{Binding FocusedPane}"
IsVisible="{Binding IsFocusMode}">
<TabControl.ItemTemplate>
<DataTemplate x:DataType="vmm:IMissionControlPane">
<TextBlock Text="{Binding DisplayTitle}" TextTrimming="CharacterEllipsis" MaxWidth="160" />
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
<TextBlock IsVisible="{Binding !HasPanes}"
Text="{loc:Tr missionControl.empty}"
Foreground="{DynamicResource TextMuteBrush}"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Panel>
<!-- Read-only queue strip — collapses when nothing is queued -->
<Border Grid.Column="2"
IsVisible="{Binding HasQueued}"
Background="{DynamicResource DeepBrush}"
BorderBrush="{DynamicResource LineBrush}"
BorderThickness="1,0,0,0">
<DockPanel LastChildFill="True" Margin="10,10">
<TextBlock DockPanel.Dock="Top" Classes="eyebrow"
Text="{loc:Tr missionControl.queue}"
Foreground="{DynamicResource TextMuteBrush}"
LetterSpacing="1.4" Margin="0,0,0,8" />
<ScrollViewer>
<ItemsControl ItemsSource="{Binding Queued}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:QueuedTaskViewModel">
<Button Command="{Binding OpenInAppCommand}"
Padding="0" Margin="0,0,0,4"
Background="Transparent" BorderThickness="0"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
Cursor="Hand">
<Panel>
<Border Background="{DynamicResource SurfaceBrush}"
BorderBrush="{DynamicResource LineBrush}"
BorderThickness="1" CornerRadius="6" />
<Border Background="{DynamicResource RunningTintBrush}"
BorderBrush="{DynamicResource RunningTintBorderBrush}"
BorderThickness="1" CornerRadius="6"
IsVisible="{Binding IsRunning}" />
<StackPanel Margin="8,6" Spacing="2">
<TextBlock Text="{Binding Title}"
TextTrimming="CharacterEllipsis"
ToolTip.Tip="{Binding Title}"
Foreground="{DynamicResource TextDimBrush}" />
<TextBlock Classes="meta"
Text="{loc:Tr missionControl.blocked}"
IsVisible="{Binding IsBlocked}"
Foreground="{DynamicResource AmberBrush}" />
<TextBlock Classes="meta"
Text="{loc:Tr missionControl.running}"
IsVisible="{Binding IsRunning}"
Foreground="{DynamicResource StatusRunningBrush}" />
</StackPanel>
</Panel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</DockPanel>
</Border>
</Grid>
</DockPanel>
</UserControl>