Files
LlamaCasty/SocialsDialog.xaml
T
gramps c82636e1a9 feat(socials): bar appearance — text colour, background plate, opacity, text shadow
In the Socials drawer, under the show/hide switch: text colour, an optional background plate
with its own colour and opacity, and a text drop shadow. The bar had been hard-wired
white-on-transparent since it shipped.

- `SocialsConfig` gains five additive properties (BarTextColor, BarBackgroundEnabled,
  BarBackgroundColor, BarBackgroundOpacity, BarTextShadow).
- Persistence: five new Socials columns, added by the per-column idempotent migration pattern
  (a half-migrated DB heals instead of failing), written in Save and read back in Load with
  defaults for pre-existing rows.
- `SocialBarRenderer.Render(entries, SocialBarStyle?)` — the look is a separate value type so the
  renderer stays a pure "entries + look" function and the preview can never read a half-applied
  config mid-edit. A malformed hex falls back to white/black instead of throwing inside a render
  tick.
- The plate hugs the entries (rounded 20px, padding) so it reads as a caption bar, not a band
  across the frame. Off by default, because transparent is what creators have today.
- The colour pickers reuse `BorderColorPalette` — one catalogue of the app's colours, per the
  TASK 60/63 rule.

Guard: SocialBarAppearanceTests paints the real strip and inspects pixels — default is white with
no plate, a red text colour reaches the glyphs, a 55% blue plate yields half-transparent blue
pixels, and the shadow adds dark pixels that were not there before.
2026-10-30 08:47:50 -07:00

164 lines
11 KiB
XML

<UserControl x:Class="ytLive.SocialsDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Resources>
<ResourceDictionary>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</ResourceDictionary>
</UserControl.Resources>
<Grid Margin="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Social Media Site Promotion" FontSize="18" FontWeight="Bold"
Foreground="{StaticResource Brush.OnSurface}" Margin="0,0,0,12"/>
<!-- Show/hide the bar on the stream + how it is painted -->
<StackPanel Grid.Row="1" Margin="0,0,0,10">
<CheckBox IsChecked="{Binding BarEnabled}" Style="{StaticResource YtCheckBox}">
<TextBlock Text="Show the social bar on the stream" Foreground="{StaticResource Brush.OnSurface}" FontSize="13"/>
</CheckBox>
<StackPanel Margin="18,6,0,0" IsEnabled="{Binding BarEnabled}">
<DockPanel Margin="0,2,0,0">
<TextBlock DockPanel.Dock="Left" Text="Text colour" VerticalAlignment="Center"
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" Width="96"/>
<ComboBox Style="{StaticResource YtComboBox}" Height="24" FontSize="11"
ItemsSource="{Binding ColorOptions}" DisplayMemberPath="Name"
SelectedValuePath="Hex" SelectedValue="{Binding BarTextColor, Mode=TwoWay}"/>
</DockPanel>
<CheckBox IsChecked="{Binding BarBackgroundEnabled}" Style="{StaticResource YtCheckBox}" Margin="0,8,0,0">
<TextBlock Text="Background plate" Foreground="{StaticResource Brush.OnSurface}" FontSize="12"/>
</CheckBox>
<StackPanel Margin="18,4,0,0" IsEnabled="{Binding BarBackgroundEnabled}">
<DockPanel>
<TextBlock DockPanel.Dock="Left" Text="Plate colour" VerticalAlignment="Center"
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" Width="96"/>
<ComboBox Style="{StaticResource YtComboBox}" Height="24" FontSize="11"
ItemsSource="{Binding ColorOptions}" DisplayMemberPath="Name"
SelectedValuePath="Hex" SelectedValue="{Binding BarBackgroundColor, Mode=TwoWay}"/>
</DockPanel>
<DockPanel Margin="0,6,0,0">
<TextBlock DockPanel.Dock="Left" Text="Opacity" VerticalAlignment="Center"
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" Width="96"/>
<TextBlock DockPanel.Dock="Right" VerticalAlignment="Center" Width="42" TextAlignment="Right"
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11"
Text="{Binding BarBackgroundOpacity, StringFormat={}{0:P0}}"/>
<Slider Minimum="0" Maximum="1" SmallChange="0.05" LargeChange="0.1"
Value="{Binding BarBackgroundOpacity, Mode=TwoWay}" VerticalAlignment="Center"/>
</DockPanel>
</StackPanel>
<CheckBox IsChecked="{Binding BarTextShadow}" Style="{StaticResource YtCheckBox}" Margin="0,8,0,0">
<TextBlock Text="Text drop shadow" Foreground="{StaticResource Brush.OnSurface}" FontSize="12"/>
</CheckBox>
</StackPanel>
</StackPanel>
<!-- Sign-in gate: the first slot is always your YouTube channel -->
<Border Grid.Row="2" Background="{StaticResource Brush.SurfaceContainer}" CornerRadius="5" Padding="10,8" Margin="0,0,0,10"
Visibility="{Binding ShowSignInBanner, Converter={StaticResource BoolToVis}}">
<StackPanel>
<TextBlock Text="Sign in to YouTube first — the first slot is your channel."
Foreground="{StaticResource Brush.OnSurface}" FontSize="12" TextWrapping="Wrap"/>
<Button Content="Sign in to YouTube" Command="{Binding SignInCommand}"
Style="{StaticResource YtButtonSecondary}" HorizontalAlignment="Left"
Margin="0,8,0,0" Padding="12,4" FontSize="11"/>
</StackPanel>
</Border>
<TextBlock Grid.Row="2" Text="Signing in… open the browser to authorize, then come back here."
Foreground="{StaticResource Brush.Error}" FontSize="12" Margin="0,0,0,10"
Visibility="{Binding IsBusy, Converter={StaticResource BoolToVis}}"/>
<!-- The six fixed slots -->
<ScrollViewer Grid.Row="3" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Slots}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0,6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="34"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- Service icon: logo, lock (Premium) or do-not (empty/invalid) -->
<Border Grid.Column="0" Width="30" Height="30" CornerRadius="15"
Background="{StaticResource Brush.SurfaceContainer}" HorizontalAlignment="Left" VerticalAlignment="Top">
<Grid>
<Path Data="{Binding LogoData}" Fill="White" Stretch="Uniform"
Width="15" Height="15" HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility="{Binding ShowLogo, Converter={StaticResource BoolToVis}}"/>
<Path Data="{Binding LockedIconData}" Fill="{StaticResource Brush.OnSurfaceVariant}" Stretch="Uniform"
Width="15" Height="15" HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility="{Binding IsLocked, Converter={StaticResource BoolToVis}}"/>
<Path Data="{Binding DoNotIconData}" Fill="{StaticResource Brush.OnSurfaceDisabled}" Stretch="Uniform"
Width="15" Height="15" HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility="{Binding ShowDoNot, Converter={StaticResource BoolToVis}}"/>
</Grid>
</Border>
<!-- Handle text / edit box / status -->
<Grid Grid.Column="1" Margin="10,0,8,0">
<TextBlock Text="{Binding DisplayText}" Foreground="{StaticResource Brush.OnSurface}" FontSize="14"
VerticalAlignment="Top" Margin="0,6,0,0"
TextTrimming="CharacterEllipsis"
Visibility="{Binding ShowDisplay, Converter={StaticResource BoolToVis}}"/>
<TextBox Text="{Binding EditText, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource YtTextBox}" FontSize="13"
KeyDown="SocialEdit_KeyDown" LostFocus="SocialEdit_LostFocus"
Visibility="{Binding ShowEditBox, Converter={StaticResource BoolToVis}}"/>
<TextBlock Text="{Binding Error}" Foreground="{StaticResource Brush.Error}" FontSize="11"
TextWrapping="Wrap" Margin="0,32,0,0"
Visibility="{Binding ShowError, Converter={StaticResource BoolToVis}}"/>
<TextBlock Text="Checking…" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" Margin="0,32,0,0"
Visibility="{Binding ShowBusy, Converter={StaticResource BoolToVis}}"/>
</Grid>
<!-- Row actions -->
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Top"
Margin="0,3,0,0">
<Button Content="Sign in" Click="SignInButton_Click"
Style="{StaticResource YtButtonSecondary}" Padding="10,3"
FontSize="11" Margin="0,0,6,0"
Visibility="{Binding ShowSignInButton, Converter={StaticResource BoolToVis}}"/>
<Button Content="+" Click="SlotAdd_Click" Tag="{Binding Index}"
Style="{StaticResource IconButton}" Width="26" Height="26"
ToolTip="Add a social" FontSize="16" FontWeight="Bold"
Visibility="{Binding ShowAddButton, Converter={StaticResource BoolToVis}}"/>
<Button Content="&#x270F;&#xFE0F;" Click="SlotEdit_Click" Tag="{Binding Index}"
Style="{StaticResource IconButton}" Width="26" Height="26"
ToolTip="Edit" FontSize="13"
Visibility="{Binding ShowEditButton, Converter={StaticResource BoolToVis}}"/>
<Button Content="&#x1F5D1;&#xFE0F;" Click="SlotDelete_Click" Tag="{Binding Index}"
Style="{StaticResource IconButton}" Width="26" Height="26"
ToolTip="Remove" FontSize="12" Foreground="{StaticResource Brush.Error}"
Visibility="{Binding ShowDeleteButton, Converter={StaticResource BoolToVis}}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<!-- Actions -->
<StackPanel Grid.Row="4" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,12,0,0">
<Button Content="Cancel" Command="{Binding CancelCommand}"
Style="{StaticResource YtButtonSecondary}" Margin="0,0,8,0" IsCancel="True"/>
<Button Content="Save" Command="{Binding SaveCommand}" Style="{StaticResource YtButton}" MinWidth="110"/>
</StackPanel>
</Grid>
</UserControl>