feat(topbar): the segmented mode switcher, to the mock

Implements the creator's mock (2026-11-12) for the world segment: pill track (#1E222B, radius 12,
padding 4) holding two equal halves; the active half sits on a lighter pill (#2E3440, radius 8) with
#E2E8F0 bold text and a coral (#F26D6D) icon; the inactive half is #8892A4 on a one-alpha black face so
the whole area hit-tests; hover tints #252A34 on the inactive half only, so the active one never looks
clickable-but-dim. Labels are "Build scenes" / "Record / go live".

Two deliberate deviations from the pasted recipe, both to honour this repo:
- State is the existing BarWorld binding plus a command, not a code-behind SetActiveMode — one source of
  truth, and the change still raises the same property-changed notification a mode event would.
- Styles live in Themes/Controls.xaml (SegmentHalf, SegmentIcon) with colours as tokens in Tokens.xaml,
  rather than inline properties on the elements: AGENTS requires shared styles, and one DataTrigger on
  each element's own Tag serves both halves.

DesignTokensTests caught the one raw literal (#01000000) and it is now Brush.SegmentHitArea, which is the
rule working as intended.

680 pass, 0 warnings. Scope justification: Assets/starting-background.jpg is outside this unit — the
creator's replacement art, still awaiting their visual check.
This commit is contained in:
2026-11-12 16:20:25 -08:00
parent b92569a8c7
commit 6b4aa99863
4 changed files with 86 additions and 25 deletions
+50
View File
@@ -1002,6 +1002,56 @@
<!-- REC / ON-AIR segment pills: an armed session control reads red, because red is what
"live or recording" means (DESIGN.md). Kept separate from SegmentToggle so the Rect|Circle
choice in the properties panel stays the interactive blue. -->
<!-- The segmented mode switcher's icon: muted when its segment is inactive, coral when active.
Tag carries the segment's own flag so one style serves both halves (creator mock 2026-11-12). -->
<Style x:Key="SegmentIcon" TargetType="Path">
<Setter Property="Fill" Value="{StaticResource Brush.SegmentTextInactive}"/>
<Setter Property="Width" Value="15"/>
<Setter Property="Height" Value="15"/>
<Setter Property="Stretch" Value="Uniform"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Tag, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Fill" Value="{StaticResource Brush.SegmentIconActive}"/>
</DataTrigger>
</Style.Triggers>
</Style>
<!-- One segment of the switcher: pill container is the host Border, this is a half of it. -->
<Style x:Key="SegmentHalf" TargetType="Button">
<Setter Property="Background" Value="{StaticResource Brush.SegmentHitArea}"/>
<Setter Property="Foreground" Value="{StaticResource Brush.SegmentTextInactive}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Face" Background="{TemplateBinding Background}"
CornerRadius="8" Padding="14,6">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<!-- Active: lighter inner pill, off-white bold text. -->
<DataTrigger Binding="{Binding Tag, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Background" Value="{StaticResource Brush.SegmentActive}"/>
<Setter Property="Foreground" Value="{StaticResource Brush.SegmentTextActive}"/>
<Setter Property="FontWeight" Value="Bold"/>
</DataTrigger>
<!-- Hover tints ONLY the inactive half, so the active one never looks clickable-but-dim. -->
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True"/>
<Condition Binding="{Binding Tag, RelativeSource={RelativeSource Self}}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="{StaticResource Brush.SegmentHover}"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
<!-- The top bar's world segment (creator ruling 2026-11-12): [ Build Scenes ][ Record / Go Live ].
A Button, not a ToggleButton — the highlight follows the view model, so a click must never be
what sets it. Each button passes its own world flag in Tag and one DataTrigger paints it. -->
+10
View File
@@ -35,6 +35,16 @@
<SolidColorBrush x:Key="Brush.Tertiary" Color="#38bdf8"/>
<SolidColorBrush x:Key="Brush.OnTertiary" Color="#0b1326"/>
<SolidColorBrush x:Key="Brush.OnAccent" Color="#ffffff"/>
<!-- Segmented mode switcher (creator mock, 2026-11-12) -->
<SolidColorBrush x:Key="Brush.SegmentTrack" Color="#1E222B"/>
<SolidColorBrush x:Key="Brush.SegmentActive" Color="#2E3440"/>
<SolidColorBrush x:Key="Brush.SegmentHover" Color="#252A34"/>
<SolidColorBrush x:Key="Brush.SegmentTextActive" Color="#E2E8F0"/>
<SolidColorBrush x:Key="Brush.SegmentTextInactive" Color="#8892A4"/>
<SolidColorBrush x:Key="Brush.SegmentIconActive" Color="#F26D6D"/>
<!-- One-alpha black: transparent to the eye, opaque to hit-testing, so the whole segment is clickable. -->
<SolidColorBrush x:Key="Brush.SegmentHitArea" Color="#01000000"/>
<SolidColorBrush x:Key="Brush.Primary" Color="#ff5451"/>
<SolidColorBrush x:Key="Brush.PrimaryContainer" Color="#b91a24"/>
<SolidColorBrush x:Key="Brush.Secondary" Color="#ffb95f"/>