fix(text): show selected formatting and style controls
Use category-filtered font and selected-item dropdowns, separate horizontal and vertical alignment, and reuse the border-color palette templates for text color. Persist vertical alignment additively. Follow the established static text-source pattern: https://obsproject.com/kb/text-sources
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Xunit;
|
||||
using ytLive.Controls;
|
||||
using ytLive.Models;
|
||||
using ytLive.ViewModels;
|
||||
|
||||
namespace ytLive.Tests;
|
||||
|
||||
[Collection("RealApp")]
|
||||
public sealed class TextLayerDropdownTests
|
||||
{
|
||||
private readonly RealAppHost _app;
|
||||
|
||||
public TextLayerDropdownTests(RealAppHost app) => _app = app;
|
||||
|
||||
[Fact]
|
||||
public void Text_Controls_Show_Defaults_Update_And_Restore_Selected_Values()
|
||||
{
|
||||
_app.Run(Run);
|
||||
}
|
||||
|
||||
private static void Run()
|
||||
{
|
||||
var dbPath = Path.Combine(Path.GetTempPath(), $"ytLlive-text-controls-{Guid.NewGuid():N}.db");
|
||||
MainViewModel.LayoutPathOverride = dbPath;
|
||||
MainWindow? window = null;
|
||||
try
|
||||
{
|
||||
window = new MainWindow();
|
||||
var vm = (MainViewModel)window.DataContext;
|
||||
vm.EditingActive = true;
|
||||
vm.StagedScene = vm.Scenes.Single(scene => SceneCatalog.Is(scene.Name, SceneCatalog.Live));
|
||||
vm.AddSourceCommand.Execute(SourceType.TextOverlay);
|
||||
var source = vm.StagedScene!.Elements.OfType<Source>()
|
||||
.Single(item => item.Type == SourceType.TextOverlay);
|
||||
vm.SelectedElement = source;
|
||||
|
||||
window.Show();
|
||||
window.UpdateLayout();
|
||||
|
||||
var panel = (LeftPanel)window.FindName("LeftPanel")!;
|
||||
var fontType = (ComboBox)panel.FindName("TextLayerFontTypePicker")!;
|
||||
var font = (ComboBox)panel.FindName("TextLayerFontPicker")!;
|
||||
var size = (ComboBox)panel.FindName("TextLayerSizePicker")!;
|
||||
var colorField = (TextBox)panel.FindName("TextLayerColorEditor")!;
|
||||
var color = (ComboBox)panel.FindName("TextLayerColorPicker")!;
|
||||
var horizontal = (ComboBox)panel.FindName("TextLayerHorizontalAlignmentPicker")!;
|
||||
var vertical = (ComboBox)panel.FindName("TextLayerVerticalAlignmentPicker")!;
|
||||
|
||||
Assert.Equal("Sans Serif", fontType.SelectedItem);
|
||||
Assert.Equal("Sans Serif", fontType.Text);
|
||||
Assert.Equal("Segoe UI", font.SelectedItem);
|
||||
Assert.Equal("Segoe UI", font.Text);
|
||||
Assert.Equal(48d, size.SelectedItem);
|
||||
Assert.Equal("48", size.Text);
|
||||
Assert.Equal("#FFFFFF", colorField.Text);
|
||||
Assert.Equal(source.TextColorEntry, color.SelectedItem);
|
||||
Assert.Equal(TextLayerAlignment.Left, horizontal.SelectedItem);
|
||||
Assert.Equal("Left", horizontal.Text);
|
||||
Assert.Equal(TextLayerVerticalAlignment.Top, vertical.SelectedItem);
|
||||
Assert.Equal("Top", vertical.Text);
|
||||
|
||||
Assert.Same(Application.Current.Resources["YtComboBox"], color.Style);
|
||||
Assert.Same(panel.Resources["BorderColorTemplates"], color.ItemTemplateSelector);
|
||||
Assert.Equal(10, ((FrameworkElement)fontType.Parent).Margin.Left);
|
||||
Assert.Equal(10, ((FrameworkElement)color.Parent).Margin.Left);
|
||||
Assert.Equal(10, ((FrameworkElement)horizontal.Parent).Margin.Left);
|
||||
Assert.Equal(10, ((FrameworkElement)vertical.Parent).Margin.Left);
|
||||
|
||||
fontType.SelectedItem = "Serif";
|
||||
window.UpdateLayout();
|
||||
Assert.Equal("Serif", vm.SelectedTextFontType);
|
||||
Assert.Equal("Segoe UI", source.TextFontFamily);
|
||||
Assert.NotEmpty(font.Items);
|
||||
var serifFont = font.Items.Cast<string>().First();
|
||||
font.SelectedItem = serifFont;
|
||||
size.SelectedItem = 36d;
|
||||
color.SelectedItem = source.TextColorOptions.Single(item => item.Hex == "#FF0000");
|
||||
horizontal.SelectedItem = TextLayerAlignment.Center;
|
||||
vertical.SelectedItem = TextLayerVerticalAlignment.Bottom;
|
||||
window.UpdateLayout();
|
||||
|
||||
Assert.Equal(serifFont, source.TextFontFamily);
|
||||
Assert.Equal(36, source.TextFontSize);
|
||||
Assert.Equal("#FF0000", source.TextColor);
|
||||
Assert.Equal(TextLayerAlignment.Center, source.TextAlignment);
|
||||
Assert.Equal(TextLayerVerticalAlignment.Bottom, source.TextVerticalAlignment);
|
||||
var raster = Assert.IsType<ytLive.Services.VideoFrame>(vm.ResolveOutputFrame(source));
|
||||
Assert.True(FirstInkRow(raster) > 50,
|
||||
"bottom alignment did not place the text lower inside its box");
|
||||
Assert.Equal("Serif", fontType.SelectedItem);
|
||||
Assert.Equal(serifFont, font.SelectedItem);
|
||||
Assert.Equal(serifFont, font.Text);
|
||||
Assert.Equal(36d, size.SelectedItem);
|
||||
Assert.Equal("36", size.Text);
|
||||
Assert.Equal("#FF0000", colorField.Text);
|
||||
Assert.Equal(source.TextColorEntry, color.SelectedItem);
|
||||
Assert.Equal(TextLayerAlignment.Center, horizontal.SelectedItem);
|
||||
Assert.Equal("Center", horizontal.Text);
|
||||
Assert.Equal(TextLayerVerticalAlignment.Bottom, vertical.SelectedItem);
|
||||
Assert.Equal("Bottom", vertical.Text);
|
||||
|
||||
vm.SaveLayoutNow();
|
||||
window.Close();
|
||||
window = null;
|
||||
SqliteConnection.ClearAllPools();
|
||||
|
||||
window = new MainWindow();
|
||||
var reloadedVm = (MainViewModel)window.DataContext;
|
||||
reloadedVm.EditingActive = true;
|
||||
reloadedVm.StagedScene = reloadedVm.Scenes.Single(scene => SceneCatalog.Is(scene.Name, SceneCatalog.Live));
|
||||
var reloaded = reloadedVm.StagedScene.Elements.OfType<Source>()
|
||||
.Single(item => item.Type == SourceType.TextOverlay);
|
||||
reloadedVm.SelectedElement = reloaded;
|
||||
window.Show();
|
||||
window.UpdateLayout();
|
||||
|
||||
var reloadedPanel = (LeftPanel)window.FindName("LeftPanel")!;
|
||||
Assert.Equal(reloadedVm.SelectedTextFontType, ((ComboBox)reloadedPanel.FindName("TextLayerFontTypePicker")!).SelectedItem);
|
||||
Assert.Equal(reloaded.TextFontFamily, ((ComboBox)reloadedPanel.FindName("TextLayerFontPicker")!).SelectedItem);
|
||||
Assert.Equal(reloaded.TextFontFamily, ((ComboBox)reloadedPanel.FindName("TextLayerFontPicker")!).Text);
|
||||
Assert.Equal(reloaded.TextFontSize, ((ComboBox)reloadedPanel.FindName("TextLayerSizePicker")!).SelectedItem);
|
||||
Assert.Equal(reloaded.TextFontSize.ToString(), ((ComboBox)reloadedPanel.FindName("TextLayerSizePicker")!).Text);
|
||||
Assert.Equal(reloaded.TextColor, ((TextBox)reloadedPanel.FindName("TextLayerColorEditor")!).Text);
|
||||
Assert.Equal(reloaded.TextColorEntry, ((ComboBox)reloadedPanel.FindName("TextLayerColorPicker")!).SelectedItem);
|
||||
Assert.Equal(reloaded.TextAlignment, ((ComboBox)reloadedPanel.FindName("TextLayerHorizontalAlignmentPicker")!).SelectedItem);
|
||||
Assert.Equal(reloaded.TextAlignment.ToString(), ((ComboBox)reloadedPanel.FindName("TextLayerHorizontalAlignmentPicker")!).Text);
|
||||
Assert.Equal(reloaded.TextVerticalAlignment, ((ComboBox)reloadedPanel.FindName("TextLayerVerticalAlignmentPicker")!).SelectedItem);
|
||||
Assert.Equal(reloaded.TextVerticalAlignment.ToString(), ((ComboBox)reloadedPanel.FindName("TextLayerVerticalAlignmentPicker")!).Text);
|
||||
}
|
||||
finally
|
||||
{
|
||||
window?.Close();
|
||||
MainViewModel.LayoutPathOverride = null;
|
||||
SqliteConnection.ClearAllPools();
|
||||
try { File.Delete(dbPath); } catch { /* best-effort cleanup */ }
|
||||
}
|
||||
}
|
||||
|
||||
private static int FirstInkRow(ytLive.Services.VideoFrame frame)
|
||||
{
|
||||
for (var y = 0; y < frame.Height; y++)
|
||||
for (var x = 0; x < frame.Width; x++)
|
||||
if (frame.BgraPixels[(y * frame.Width + x) * 4 + 3] > 0)
|
||||
return y;
|
||||
return frame.Height;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user