refactor: extract SceneThumbnailStrip out of MainWindow.xaml — Phase 2, Commit 16

SceneThumbnailStrip.xaml(.cs) takes the Row 0 scene-picker strip (ItemsControl
over Scenes with staged/live border highlight); Thumbnail_MouseLeftButtonDown
moved into the control, transitioning via DataContext.TransitionToScene.

Zero behavior change; build 0 warnings; 246 pass, only the 2 known failures.
Docs: ViewModels/index.md Phase 2 tracker updated in the same commit.
This commit is contained in:
2026-08-30 19:39:51 -07:00
parent c5e3466df0
commit 73397cfea7
5 changed files with 78 additions and 59 deletions
+54
View File
@@ -0,0 +1,54 @@
<UserControl x:Class="ytLive.Controls.SceneThumbnailStrip"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Border Background="#0d1117" CornerRadius="4" Margin="0,0,0,4" Padding="4">
<ItemsControl ItemsSource="{Binding Scenes}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1" Columns="5"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border CornerRadius="4" Margin="2" Padding="2"
BorderThickness="2"
BorderBrush="Transparent"
Cursor="Hand"
MouseLeftButtonDown="Thumbnail_MouseLeftButtonDown">
<Border.Style>
<Style TargetType="Border">
<Setter Property="BorderBrush" Value="#333344"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsStaged}" Value="True">
<Setter Property="BorderBrush" Value="#e94560"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsLive}" Value="True">
<Setter Property="BorderBrush" Value="#22c55e"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" CornerRadius="3"
Background="#0a0a1a" ClipToBounds="True">
<Image Source="{Binding Snapshot}"
Stretch="UniformToFill"
RenderOptions.BitmapScalingMode="LowQuality"/>
</Border>
<TextBlock Grid.Row="1" Foreground="#a0a0b0" FontSize="10"
HorizontalAlignment="Center"
Margin="0,2,0,0"
TextTrimming="CharacterEllipsis">
<Run Text="{Binding Name, Mode=OneWay}"/><Run Text=" "/><Run Text="{Binding HotkeyLabel, Mode=OneWay}" Foreground="#6a6a7a" FontWeight="SemiBold"/>
</TextBlock>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</UserControl>
+21
View File
@@ -0,0 +1,21 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using ytLive.Models;
using ytLive.ViewModels;
namespace ytLive.Controls;
public partial class SceneThumbnailStrip : UserControl
{
public SceneThumbnailStrip()
{
InitializeComponent();
}
private void Thumbnail_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is Border { DataContext: Scene scene } && DataContext is MainViewModel vm)
vm.TransitionToScene(scene);
}
}
+1 -51
View File
@@ -93,57 +93,7 @@
</Grid.ColumnDefinitions>
<!-- ═══ THUMBNAIL STRIP ═══ -->
<Border Grid.Row="0" Grid.ColumnSpan="2" Background="#0d1117" CornerRadius="4" Margin="0,0,0,4"
Padding="4">
<ItemsControl ItemsSource="{Binding Scenes}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1" Columns="5"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border CornerRadius="4" Margin="2" Padding="2"
BorderThickness="2"
BorderBrush="Transparent"
Cursor="Hand"
MouseLeftButtonDown="Thumbnail_MouseLeftButtonDown">
<Border.Style>
<Style TargetType="Border">
<Setter Property="BorderBrush" Value="#333344"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsStaged}" Value="True">
<Setter Property="BorderBrush" Value="#e94560"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsLive}" Value="True">
<Setter Property="BorderBrush" Value="#22c55e"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" CornerRadius="3"
Background="#0a0a1a" ClipToBounds="True">
<Image Source="{Binding Snapshot}"
Stretch="UniformToFill"
RenderOptions.BitmapScalingMode="LowQuality"/>
</Border>
<TextBlock Grid.Row="1" Foreground="#a0a0b0" FontSize="10"
HorizontalAlignment="Center"
Margin="0,2,0,0"
TextTrimming="CharacterEllipsis">
<Run Text="{Binding Name, Mode=OneWay}"/><Run Text=" "/><Run Text="{Binding HotkeyLabel, Mode=OneWay}" Foreground="#6a6a7a" FontWeight="SemiBold"/>
</TextBlock>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
<controls:SceneThumbnailStrip Grid.Row="0" Grid.ColumnSpan="2"/>
<!-- LEFT PANEL: one panel, two states.
Offline (edit mode): LAYERS + PROPERTIES
-7
View File
@@ -164,13 +164,6 @@ public partial class MainWindow : Window
}
}
// ─── Preview image selection / move / resize ───
private void Thumbnail_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is Border { DataContext: Scene scene })
_viewModel.TransitionToScene(scene);
}
private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
+2 -1
View File
@@ -57,7 +57,8 @@ Progress (Commit N/12 · tag):
- [x] **Commit 12 · `refactor-commit-12`** — **BottomBar** → `Controls/BottomBar.xaml(.cs)` (gear menu + stream stats + quality dropdown; `GearButton_Click` moved to the control's code-behind).
- [x] **Commit 13 · `refactor-commit-13`** — **HealthBanner** → `Controls/HealthBanner.xaml` (Row 1). Also promoted the 7 value↔visibility converters out of `MainWindow.Window.Resources` into `Themes/Controls.xaml` so extracted controls can resolve them (app-level, not window-level).
- [x] **Commit 14 · `refactor-commit-14`** — **TopBar** → `Controls/TopBar.xaml(.cs)` (Row 0: logo, REC/ON-AIR pills + signs, avatar, Sign In/Start/End buttons). Avatar fetch moved (async-preserved) into the control; window exposes `_topBar.RefreshAvatar()` invoked on `IsConnected`/`AccountAvatarUrl` changes.
- [x] **Commit 15 · `refactor-commit-15`** — **PreviewPane** → `Controls/PreviewPane.xaml(.cs)` (Row 1 Col 1 center preview pane). Moved the 1920×1080 canvas (OverlayCanvas/SelectionOverlay/BrandFlashLayer/SocialBarElement), text pull-out, and the live-controls row (TRAX/game/mic meters + sliders + speakers + socials button). Code-behind moved: preview drag/select/resize/hit-test (Preview_Mouse*, HitHandle, HitElement), webcam-context-menu handlers (WebcamMenu_*), social-bar glow (SocialBar_*, SocialsButton_*, SocialGlowTick), audio sliders/speakers (VolumeSlider_*, GameVolumeSlider_*, MicSpeaker_*, GameSpeaker_*, SetSliderValueFromClick, FindParent), TraxButton_*. Window keeps the source-list left panel + thumbnail strip + edit UI, and routes selection-overlay via `_previewPane.UpdateSelectionOverlay(bool)` plus `_previewPane.IsClickInsidePreview/Drawer`. `BackgroundMenu_*` stayed in the window (used by the left-panel source context menu). Pending: thumbnail strip, left scene/chat panel, overlay host.
- [x] **Commit 15 · `refactor-commit-15`** — **PreviewPane** → `Controls/PreviewPane.xaml(.cs)` (Row 1 Col 1 center preview pane). Moved the 1920×1080 canvas (OverlayCanvas/SelectionOverlay/BrandFlashLayer/SocialBarElement), text pull-out, and the live-controls row (TRAX/game/mic meters + sliders + speakers + socials button). Code-behind moved: preview drag/select/resize/hit-test (Preview_Mouse*, HitHandle, HitElement), webcam-context-menu handlers (WebcamMenu_*), social-bar glow (SocialBar_*, SocialsButton_*, SocialGlowTick), audio sliders/speakers (VolumeSlider_*, GameVolumeSlider_*, MicSpeaker_*, GameSpeaker_*, SetSliderValueFromClick, FindParent), TraxButton_*. Window keeps the source-list left panel + thumbnail strip + edit UI, and routes selection-overlay via `_previewPane.UpdateSelectionOverlay(bool)` plus `_previewPane.IsClickInsidePreview/Drawer`. `BackgroundMenu_*` stayed in the window (used by the left-panel source context menu).
- [x] **Commit 16 · `refactor-commit-16`** — **SceneThumbnailStrip** → `Controls/SceneThumbnailStrip.xaml(.cs)` (Row 0 scene-picker strip). `Thumbnail_MouseLeftButtonDown` moved into the control (transitions via `DataContext.TransitionToScene`). Pending: left scene/chat panel, overlay host.
The partial `.cs` files land next to `MainViewModel.cs` in this folder as the split
proceeds; each partial carries its own `using`s and re-declares nothing from core.