From 7cb75fd3aae6d97c8b245ec6acf22ad82c7c1157 Mon Sep 17 00:00:00 2001 From: Yun Chan Date: Sat, 5 Sep 2026 06:36:26 +0900 Subject: [PATCH] fix(ui): fix SettingsWindow and dialog titlebar margins, vertical alignment, and button styling --- .../Views/DiagnoseWindow.xaml | 2 +- .../Views/FormatShiftTheme.xaml | 35 +++--- .../Views/QuickOptionsWindow.xaml | 2 +- .../Views/QuickProgressWindow.xaml | 2 +- .../UnifiedTitleBarTests.cs | 107 ++++++++++++++++++ 5 files changed, 129 insertions(+), 19 deletions(-) diff --git a/src/Everything2Everything.App/Views/DiagnoseWindow.xaml b/src/Everything2Everything.App/Views/DiagnoseWindow.xaml index 0ef9e94..3e10c7a 100644 --- a/src/Everything2Everything.App/Views/DiagnoseWindow.xaml +++ b/src/Everything2Everything.App/Views/DiagnoseWindow.xaml @@ -23,7 +23,7 @@ - + diff --git a/src/Everything2Everything.App/Views/FormatShiftTheme.xaml b/src/Everything2Everything.App/Views/FormatShiftTheme.xaml index c903571..41ada10 100644 --- a/src/Everything2Everything.App/Views/FormatShiftTheme.xaml +++ b/src/Everything2Everything.App/Views/FormatShiftTheme.xaml @@ -1,6 +1,7 @@ @@ -577,16 +578,19 @@ + + - + + + VerticalAlignment="Stretch"> @@ -608,8 +612,12 @@ - - + + @@ -626,23 +634,18 @@ - - - - + + + + + + - - - - - - - - + diff --git a/src/Everything2Everything.App/Views/QuickOptionsWindow.xaml b/src/Everything2Everything.App/Views/QuickOptionsWindow.xaml index 8adf646..ba98883 100644 --- a/src/Everything2Everything.App/Views/QuickOptionsWindow.xaml +++ b/src/Everything2Everything.App/Views/QuickOptionsWindow.xaml @@ -20,7 +20,7 @@ - + diff --git a/src/Everything2Everything.App/Views/QuickProgressWindow.xaml b/src/Everything2Everything.App/Views/QuickProgressWindow.xaml index f4d5a7a..637fd15 100644 --- a/src/Everything2Everything.App/Views/QuickProgressWindow.xaml +++ b/src/Everything2Everything.App/Views/QuickProgressWindow.xaml @@ -22,7 +22,7 @@ - + diff --git a/src/Everything2Everything.Tests/UnifiedTitleBarTests.cs b/src/Everything2Everything.Tests/UnifiedTitleBarTests.cs index 2c8c2de..e195779 100644 --- a/src/Everything2Everything.Tests/UnifiedTitleBarTests.cs +++ b/src/Everything2Everything.Tests/UnifiedTitleBarTests.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading; using System.Windows; using System.Windows.Controls; +using System.Windows.Media; using System.Xml.Linq; using Everything2Everything.App.Views; using Everything2Everything.Core; @@ -276,6 +277,112 @@ public class UnifiedTitleBarTests }); } + [Fact] + public void SettingsWindow_TitleBar_MustDisplayProperlyAlignedHeaderAndCloseButton() + { + RunOnSta(() => + { + var store = new FakeSettingsStore(); + var window = new SettingsWindow(store); + + var content = (UIElement)window.Content; + content.Measure(new Size(560, 720)); + content.Arrange(new Rect(0, 0, 560, 720)); + + var titleBar = FindLogicalChild(window); + Assert.NotNull(titleBar); + titleBar.ApplyTemplate(); + + // 1. TitleBar height must be 42px + Assert.Equal(42.0, titleBar.ActualHeight); + + // 2. PART_MainGrid must stretch to 42px + var mainGrid = (FrameworkElement)titleBar.Template.FindName("PART_MainGrid", titleBar); + Assert.NotNull(mainGrid); + Assert.Equal(42.0, mainGrid.ActualHeight); + + // 3. Find the rendered TextBlock with text "설정" + var textBlocks = FindVisualChildren(titleBar).Where(t => t.Text == "설정").ToList(); + Assert.NotEmpty(textBlocks); + var titleTb = textBlocks.First(); + + // 4. TextBlock "설정" must not be at X=0 (must have left padding/margin >= 12px) + var textPt = titleTb.TransformToAncestor(titleBar).Transform(new Point(0, 0)); + Assert.True(textPt.X >= 12.0, $"Title text X coordinate ({textPt.X}px) must be >= 12px to prevent touching window edge."); + + var closeBtn = (FrameworkElement)titleBar.Template.FindName("PART_CloseButton", titleBar); + Assert.NotNull(closeBtn); + Assert.Equal(Visibility.Visible, closeBtn.Visibility); + + // 5. Title text center Y and Close button center Y must match within 2.0px tolerance + var textCenterY = textPt.Y + titleTb.ActualHeight / 2.0; + var closeCenterY = closeBtn.TransformToAncestor(titleBar).Transform(new Point(0, closeBtn.ActualHeight / 2.0)).Y; + Assert.True(Math.Abs(textCenterY - closeCenterY) <= 2.0, + $"Title text CenterY ({textCenterY:F1}px) and CloseButton CenterY ({closeCenterY:F1}px) must match within 2px."); + }); + } + + [Fact] + public void DiagnoseWindow_TitleBar_MustHaveStandardHeightAndButtons() + { + RunOnSta(() => + { + var engine = Everything2EverythingBootstrap.CreateDefault(); + var window = new DiagnoseWindow(engine); + + var content = (UIElement)window.Content; + content.Measure(new Size(680, 580)); + content.Arrange(new Rect(0, 0, 680, 580)); + + var titleBar = FindLogicalChild(window); + Assert.NotNull(titleBar); + titleBar.ApplyTemplate(); + + // Height must be standard 42px (not hardcoded 32px) + Assert.Equal(42.0, titleBar.ActualHeight); + Assert.False(titleBar.ShowMaximize); + Assert.False(titleBar.ShowMinimize); + }); + } + + [Fact] + public void QuickOptionsWindow_TitleBar_MustHaveStandardHeightAndButtons() + { + RunOnSta(() => + { + var store = new FakeSettingsStore(); + var window = new QuickOptionsWindow(".mp4", 1, store); + + var content = (UIElement)window.Content; + content.Measure(new Size(380, 500)); + content.Arrange(new Rect(0, 0, 380, 500)); + + var titleBar = FindLogicalChild(window); + Assert.NotNull(titleBar); + titleBar.ApplyTemplate(); + + Assert.Equal(42.0, titleBar.ActualHeight); + Assert.False(titleBar.ShowMaximize); + Assert.False(titleBar.ShowMinimize); + }); + } + + + private static IEnumerable FindVisualChildren(DependencyObject parent) where T : DependencyObject + { + int count = VisualTreeHelper.GetChildrenCount(parent); + for (int i = 0; i < count; i++) + { + var child = VisualTreeHelper.GetChild(parent, i); + if (child is T typed) yield return typed; + foreach (var descendant in FindVisualChildren(child)) + { + yield return descendant; + } + } + } + + private static bool IsDescendantOf(DependencyObject? node, DependencyObject targetAncestor) { while (node != null)