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
+19 -22
View File
@@ -66,43 +66,40 @@
question "which world am I in?" and "can I edit?" cannot disagree. Clicking the
highlighted half re-enters it, which is how a creator gets back to the layers
during a session. -->
<Border Background="{StaticResource Brush.SurfaceContainerLowest}" CornerRadius="4"
Padding="2" Height="32" VerticalAlignment="Center" Margin="0,0,12,0"
<!-- The segmented mode switcher (creator mock 2026-11-12): pill track, two equal halves,
active half on a lighter pill with off-white bold text and a coral icon. Hover tints
only the inactive half. -->
<Border Background="{StaticResource Brush.SegmentTrack}" CornerRadius="12" Padding="4"
VerticalAlignment="Center" Margin="0,0,12,0"
Visibility="{Binding IsBuildWorld, Converter={StaticResource BoolToVis}}">
<StackPanel Orientation="Horizontal">
<!-- Build Scenes carries a LAYOUT glyph while it is the active world and a PADLOCK
once Record / Go Live is: the door is still there, it is just behind the
session. Clicking it swaps the glyph back and takes the creator into the
layers (creator ruling 2026-11-12). -->
<Button Style="{StaticResource WorldSegmentButton}" Tag="{Binding IsBuildWorld}"
Command="{Binding EnterBuildWorldCommand}" Padding="12,0"
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Style="{StaticResource SegmentHalf}"
Tag="{Binding IsBuildWorld}" Command="{Binding EnterBuildWorldCommand}"
ToolTip="Edit layers, scenes and webcam placement">
<StackPanel Orientation="Horizontal">
<Path Width="14" Height="14" Stretch="Uniform" VerticalAlignment="Center"
Fill="{Binding Foreground, RelativeSource={RelativeSource AncestorType=Button}}"
Visibility="{Binding IsBuildWorld, Converter={StaticResource BoolToVis}}"
<Path Style="{StaticResource SegmentIcon}" Tag="{Binding IsBuildWorld}"
Data="M3,4h8v6H3V4zM13,4h8v6h-8V4zM3,12h8v8H3V12zM13,12h8v8h-8V12z"/>
<TextBlock Text="Build Scenes" FontSize="12" FontWeight="Bold" Margin="6,0,0,0"
<TextBlock Text="Build scenes" FontSize="12" Margin="6,0,0,0"
VerticalAlignment="Center"
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=Button}}"/>
</StackPanel>
</Button>
<!-- Record / Go Live: a record dot in a ring, the same "armed" language the REC sign
uses, so the two halves are read apart at a glance. -->
<Button Style="{StaticResource WorldSegmentButton}" Tag="{Binding IsOutputWorld}"
Command="{Binding EnterOutputWorldCommand}" Padding="12,0"
<Button Grid.Column="1" Style="{StaticResource SegmentHalf}"
Tag="{Binding IsOutputWorld}" Command="{Binding EnterOutputWorldCommand}"
ToolTip="Choose the output and start the session">
<StackPanel Orientation="Horizontal">
<Path Width="14" Height="14" Stretch="Uniform" VerticalAlignment="Center"
Fill="{Binding Foreground, RelativeSource={RelativeSource AncestorType=Button}}"
<Path Style="{StaticResource SegmentIcon}" Tag="{Binding IsOutputWorld}"
Data="M12,2a10,10 0 1,0 0,20a10,10 0 1,0 0,-20zM12,4a8,8 0 1,1 0,16a8,8 0 1,1 0,-16zM12,7.5a4.5,4.5 0 1,1 0,9a4.5,4.5 0 1,1 0,-9z"/>
<TextBlock Text="Record / Go Live" FontSize="12" FontWeight="Bold" Margin="6,0,0,0"
<TextBlock Text="Record / go live" FontSize="12" Margin="6,0,0,0"
VerticalAlignment="Center"
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=Button}}"/>
</StackPanel>
</Button>
</StackPanel>
</Grid>
</Border>
<!-- IDLE: the mode switch + the action(s) of the armed world. The three
controls share one height (build 77 feedback) so they read as one