Creator ruling 2026-11-12: the action is not part of the output row, and it is not to be placed at all until they say where. So Start Recording / Private Test Stream / Review & Go Live are out of the bar, along with the divider that had been separating them. What the middle of the bar is now: the world segment in Build, and the output line (Output [YouTube] [Local MP4] One output at a time) in the Record / Go Live world. The view-model commands (StartRecordingCommand, TestStreamCommand, GoLiveCommand) stay, unused by XAML, because they are wherever the action lands next. UiShellTests now asserts the bar contains none of the three action labels, so re-adding one by accident fails the gate. Scope justification: Assets/starting-background.jpg is outside this unit — the creator's replacement art, still awaiting their visual check.
209 lines
15 KiB
XML
209 lines
15 KiB
XML
<UserControl x:Class="ytLive.Controls.TopBar"
|
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
xmlns:helpers="clr-namespace:ytLive.Helpers"
|
|
x:Name="TopBarRoot">
|
|
<Grid>
|
|
<Grid.ColumnDefinitions>
|
|
<!-- 352 = the layers panel's right edge in the work area below: window margin 8 +
|
|
360px column = 368, minus this bar's own 16px padding. EDIT is right-aligned in
|
|
this column so the two edges line up exactly (creator ruling 2026-11-01). -->
|
|
<ColumnDefinition Width="352"/>
|
|
<ColumnDefinition Width="*"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<!-- BRAND + EDIT. The gear moved to the preview's right rail, first tab (2026-11-01):
|
|
app-level actions live with the other pull-outs now. -->
|
|
<Grid Grid.Column="0">
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="*"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<!-- LOGO / BRAND — left-click opens the in-app About overlay; right-click opens the app
|
|
menu (Report Bug / Feature Request / About). App Settings is no longer in this
|
|
menu: the gear rail tab opens the Settings drawer directly (creator ruling
|
|
2026-11-01). -->
|
|
<Button Grid.Column="0" Background="Transparent" BorderThickness="0" Cursor="Hand"
|
|
HorizontalAlignment="Left"
|
|
VerticalAlignment="Center" ToolTip="About LlamaCasty — right-click for the menu"
|
|
Command="{Binding OpenAboutCommand}"
|
|
PreviewMouseRightButtonDown="BrandWordmark_PreviewMouseRightButtonDown">
|
|
<Button.ContextMenu>
|
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
|
<MenuItem Header="Report Bug" Command="{Binding OpenBugCommand}"/>
|
|
<MenuItem Header="Feature Request" Command="{Binding OpenFeatureCommand}"/>
|
|
<MenuItem Header="About" Command="{Binding OpenAboutCommand}"/>
|
|
</ContextMenu>
|
|
</Button.ContextMenu>
|
|
<Button.Template>
|
|
<ControlTemplate TargetType="Button">
|
|
<ContentPresenter/>
|
|
</ControlTemplate>
|
|
</Button.Template>
|
|
<!-- Wordmark: "LlamaCas" + "ty" in YouTube red (creator ruling 2026-10-30 — never
|
|
re-colour it). The Run elements MUST stay adjacent: whitespace between inline
|
|
Runs renders as a space and splits the name into "LlamaCas ty". -->
|
|
<TextBlock FontSize="22" FontWeight="Bold" VerticalAlignment="Center">
|
|
<Run Text="LlamaCas" Foreground="{StaticResource Brush.OnSurface}"/><Run Text="ty" Foreground="{StaticResource Brush.Youtube}"/><Run
|
|
x:Name="BuildIdStamp" Text="{x:Static helpers:BuildStamp.Label}" FontSize="9"
|
|
BaselineAlignment="Superscript" Foreground="{StaticResource Brush.OnSurfaceVariant}"
|
|
ToolTipService.ToolTip="{x:Static helpers:BuildStamp.Label}"/><Run Text=" " FontSize="4"/>
|
|
</TextBlock>
|
|
</Button>
|
|
|
|
</Grid>
|
|
|
|
<!-- Session surface (top bar redesign): the bar is ONE surface rendered by
|
|
the FIRST decision — Record or Stream. Each world is the whole bar; the
|
|
other world never exists on screen. While running, the bar drops to a
|
|
reality line + End so nothing mid-session can be re-armed. -->
|
|
<StackPanel Grid.Column="1" Orientation="Horizontal"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center">
|
|
<!-- THE decision (creator ruling 2026-11-12): [ Build Scenes ][ Record / Go Live ].
|
|
Build Scenes is the new edit mode — there is no separate EDIT button, so the
|
|
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"
|
|
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"
|
|
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}}"
|
|
Data="M3,4h8v6H3V4zM13,4h8v6h-8V4zM3,12h8v8H3V12zM13,12h8v8h-8V12z"/>
|
|
|
|
<TextBlock Text="Build Scenes" FontSize="12" FontWeight="Bold" 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"
|
|
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}}"
|
|
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"
|
|
VerticalAlignment="Center"
|
|
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=Button}}"/>
|
|
</StackPanel>
|
|
</Button>
|
|
</StackPanel>
|
|
</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
|
|
family — segments host, Start/Go Live, and Test all sit at 32. -->
|
|
<!-- The Output world REPLACES the segment (creator ruling 2026-11-12): choosing Record / Go
|
|
Live takes the dual button away and puts the output line in its place. This chevron is
|
|
the way back to Build Scenes — present for the whole world, running or not, so editing
|
|
during a show (unit 1) still has an entry point and the bar has no dead end. -->
|
|
<Button Style="{StaticResource IconButton}" Width="28" Height="32" Margin="0,0,6,0"
|
|
Command="{Binding EnterBuildWorldCommand}" ToolTip="Back to Build Scenes"
|
|
Visibility="{Binding IsOutputWorld, Converter={StaticResource BoolToVis}}">
|
|
<Path Data="M15.41,7.41L14,6l-6,6 6,6 1.41,-1.41L10.83,12z" Width="14" Height="14"
|
|
Stretch="Uniform" Fill="{StaticResource Brush.OnSurface}"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
|
</Button>
|
|
<!-- THE OUTPUT LINE (creator ruling 2026-11-12): which single output. Nothing else
|
|
belongs in it — the action is not part of this row. -->
|
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center"
|
|
Visibility="{Binding ShowOutputStage, Converter={StaticResource BoolToVis}}">
|
|
<TextBlock Text="Output" FontSize="12" FontWeight="Bold" VerticalAlignment="Center"
|
|
Foreground="{StaticResource Brush.OnSurfaceVariant}" Margin="0,0,8,0"/>
|
|
<Border Background="{StaticResource Brush.SurfaceContainerLowest}" CornerRadius="4" Padding="2" Height="32" VerticalAlignment="Center">
|
|
<StackPanel Orientation="Horizontal">
|
|
<ToggleButton Style="{StaticResource RecordPillToggle}"
|
|
IsChecked="{Binding OnAirPillOn}">
|
|
<StackPanel Orientation="Horizontal">
|
|
<Path Width="14" Height="14" Stretch="Uniform" VerticalAlignment="Center"
|
|
Fill="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ToggleButton}}"
|
|
Data="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/>
|
|
<TextBlock Text="YouTube" FontSize="12" FontWeight="Bold" Margin="6,0,0,0"
|
|
VerticalAlignment="Center"
|
|
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ToggleButton}}"/>
|
|
</StackPanel>
|
|
</ToggleButton>
|
|
<ToggleButton Style="{StaticResource RecordPillToggle}"
|
|
IsChecked="{Binding RecordPillOn}">
|
|
<StackPanel Orientation="Horizontal">
|
|
<Path Width="14" Height="14" Stretch="Uniform" VerticalAlignment="Center"
|
|
Fill="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ToggleButton}}"
|
|
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="Local MP4" FontSize="12" FontWeight="Bold" Margin="6,0,0,0"
|
|
VerticalAlignment="Center"
|
|
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ToggleButton}}"/>
|
|
</StackPanel>
|
|
</ToggleButton>
|
|
</StackPanel>
|
|
</Border>
|
|
<TextBlock Text="One output at a time" FontSize="11" VerticalAlignment="Center"
|
|
Foreground="{StaticResource Brush.OnSurfaceVariant}" Margin="8,0,0,0"/>
|
|
</StackPanel>
|
|
|
|
<!-- RUNNING: one reality line (dot + word + elapsed) and the stop -->
|
|
<StackPanel Orientation="Horizontal"
|
|
Visibility="{Binding RunningVisible, Converter={StaticResource BoolToVis}}">
|
|
<Ellipse Width="12" Height="12" VerticalAlignment="Center"
|
|
Fill="{Binding RunningDotBrush}"/>
|
|
<TextBlock Text="{Binding RunningText}" FontSize="16" FontWeight="Bold"
|
|
Foreground="White" Margin="8,0,0,0" VerticalAlignment="Center"/>
|
|
<TextBlock Text="{Binding LiveElapsedText}" Foreground="White" FontSize="14"
|
|
FontFamily="Consolas" VerticalAlignment="Center" Margin="8,0,0,0"/>
|
|
<!-- TASK 32·2: measured facts only — elapsed seconds and which attempt. No countdown:
|
|
YouTube's cutoff is not a constant we can promise. -->
|
|
<TextBlock x:Name="ReconnectDetail" Text="{Binding ReconnectDetailText}"
|
|
Foreground="{StaticResource Brush.Attention}" FontSize="13"
|
|
FontFamily="Consolas" VerticalAlignment="Center" Margin="10,0,0,0"
|
|
Visibility="{Binding IsReconnecting, Converter={StaticResource BoolToVis}}"/>
|
|
<Button Content="{Binding EndButtonText}" Style="{StaticResource YtButton}"
|
|
Background="{StaticResource Brush.Primary}" Margin="14,0,0,0"
|
|
Command="{Binding EndStreamCommand}"/>
|
|
</StackPanel>
|
|
</StackPanel>
|
|
|
|
<!-- Account zone: lives only in the Stream world. Signed out → Sign In;
|
|
signed in → a big avatar of the streaming account (build 77 feedback:
|
|
"make the user icon much bigger"). Record world has no YouTube
|
|
identity at all. -->
|
|
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
|
|
<Button Content="Sign In" Style="{StaticResource YtButtonSecondary}"
|
|
Margin="0,0,8,0" Command="{Binding SignInCommand}"
|
|
ToolTip="Sign in to YouTube to go live."
|
|
Visibility="{Binding ShowSignInButton, Converter={StaticResource BoolToVis}}"/>
|
|
<Button Style="{StaticResource IconButton}" Margin="0,0,0,0"
|
|
ToolTip="{Binding AccountDisplayName}"
|
|
Visibility="{Binding ShowAvatarButton, Converter={StaticResource BoolToVis}}">
|
|
<Button.ContextMenu>
|
|
<ContextMenu>
|
|
<MenuItem Header="Change Account" Command="{Binding ChangeAccountCommand}"/>
|
|
<Separator/>
|
|
<MenuItem Header="Logout" Command="{Binding LogoutCommand}"/>
|
|
</ContextMenu>
|
|
</Button.ContextMenu>
|
|
<Border Width="40" Height="40" CornerRadius="20" Background="{StaticResource Brush.SurfaceContainer}"
|
|
ClipToBounds="True" BorderBrush="{StaticResource Brush.Outline}" BorderThickness="1">
|
|
<Grid>
|
|
<Image x:Name="AvatarImage" Stretch="UniformToFill"/>
|
|
<TextBlock x:Name="AvatarInitial" Foreground="{StaticResource Brush.OnSurface}" FontSize="20"
|
|
FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"
|
|
Visibility="Collapsed"/>
|
|
</Grid>
|
|
</Border>
|
|
</Button>
|
|
</StackPanel>
|
|
</Grid>
|
|
</UserControl> |