diff --git a/src/SheetMe.Designer/App.xaml.cs b/src/SheetMe.Designer/App.xaml.cs index f0f8c44..b46d635 100644 --- a/src/SheetMe.Designer/App.xaml.cs +++ b/src/SheetMe.Designer/App.xaml.cs @@ -171,6 +171,11 @@ public partial class App : Application return Diagnostics.DbSmoke.RunTableSearch(args[1], args[2]); } + if (args.Length >= 2 && args[0] == "--dialog-shots") + { + return Diagnostics.DialogShots.Run(args[1]); + } + MessageBox.Show($"알 수 없는 진단 옵션입니다: {args[0]}", "서식생성기", MessageBoxButton.OK, MessageBoxImage.Warning); return 2; diff --git a/src/SheetMe.Designer/Diagnostics/DialogShots.cs b/src/SheetMe.Designer/Diagnostics/DialogShots.cs new file mode 100644 index 0000000..6e9d3d0 --- /dev/null +++ b/src/SheetMe.Designer/Diagnostics/DialogShots.cs @@ -0,0 +1,189 @@ +using System.IO; +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using SheetMe.Core.Models; +using SheetMe.Data.Stores; +using SheetMe.Designer.Services; +using SheetMe.Designer.ViewModels; + +namespace SheetMe.Designer.Diagnostics; + +/// +/// 별도 창(대화상자) 오프스크린 스냅샷 — --dialog-shots <출력폴더>. +/// +/// 창 10종을 다크·라이트 양 테마로 열어 PNG 로 남긴다. 목적은 하나다: +/// 테마 대비 결함을 사람 손 없이 재현 가능하게 확인한다. +/// 실입력(마우스·키보드) 주입은 화면 잠금·세션 격리 상태에서 OS 가 거부하지만 +/// 이 경로는 창을 화면 밖에 띄워 RenderTargetBitmap 으로 찍으므로 그 제약을 받지 않는다. +/// +/// 창은 화면 밖(-10000)에 Show() 로 띄운다 — 레이아웃이 실제로 돌아야 템플릿·트리거가 +/// 적용된 상태로 찍힌다(Measure/Arrange 만으로는 Window 크롬이 구성되지 않는다). +/// 모든 창은 찍은 뒤 즉시 닫는다. +/// +public static class DialogShots +{ + #region Methods + public static int Run(string outputDirectory) + { + try + { + Directory.CreateDirectory(outputDirectory); + // 기본 ShutdownMode 는 OnLastWindowClose 라, 찍고 닫는 순간 마지막 창이 사라져 앱이 종료된다 + // (그 뒤 Application.Current 가 null 이 되어 테마 교체에서 NRE). 명시 종료로 바꾼다. + Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown; + var lines = new List(); + + foreach (var light in new[] { false, true }) + { + ThemeManager.Apply(light); + var suffix = light ? "light" : "dark"; + foreach (var (name, factory) in Factories()) + { + try + { + var window = factory(); + Capture(window, Path.Combine(outputDirectory, $"{name}-{suffix}.png")); + // 창 배경이 실제로 무엇으로 해석됐는지 함께 남긴다 — 픽셀만 보면 원인을 못 가린다. + // WPF 는 암시 스타일을 요소의 '정확한 타입'으로만 찾으므로, Window 를 상속한 + // 대화상자에는 + + diff --git a/src/SheetMe.Designer/Views/ColorPickerWindow.cs b/src/SheetMe.Designer/Views/ColorPickerWindow.cs index 1b92bbc..d6354e7 100644 --- a/src/SheetMe.Designer/Views/ColorPickerWindow.cs +++ b/src/SheetMe.Designer/Views/ColorPickerWindow.cs @@ -54,6 +54,11 @@ public sealed class ColorPickerWindow : Window private ColorPickerWindow(string? initialHex) { Title = "색상 선택"; + // 암시 Window 스타일은 파생 클래스에 적용되지 않는다 — 직접 걸지 않으면 다크에서 흰 창이 된다 + if (Application.Current?.TryFindResource("ThemedWindow") is Style themed) + { + Style = themed; + } Width = 300; SizeToContent = SizeToContent.Height; WindowStartupLocation = WindowStartupLocation.CenterOwner; diff --git a/src/SheetMe.Designer/Views/FontManagerDialogView.xaml b/src/SheetMe.Designer/Views/FontManagerDialogView.xaml index 5bda3bc..ef22180 100644 --- a/src/SheetMe.Designer/Views/FontManagerDialogView.xaml +++ b/src/SheetMe.Designer/Views/FontManagerDialogView.xaml @@ -2,7 +2,8 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="폰트 일괄 변경" Width="620" Height="560" MinWidth="520" MinHeight="420" - WindowStartupLocation="CenterOwner" ShowInTaskbar="False"> + WindowStartupLocation="CenterOwner" ShowInTaskbar="False" + Style="{StaticResource ThemedWindow}"> diff --git a/src/SheetMe.Designer/Views/MainView.xaml b/src/SheetMe.Designer/Views/MainView.xaml index 20b1c8d..f3af249 100644 --- a/src/SheetMe.Designer/Views/MainView.xaml +++ b/src/SheetMe.Designer/Views/MainView.xaml @@ -16,7 +16,8 @@ Width="1440" Height="920" MinWidth="940" MinHeight="640" WindowStyle="None" WindowStartupLocation="CenterScreen" Background="{DynamicResource B.AppBg}" FontFamily="Malgun Gothic" FontSize="13" - Loaded="OnWindowLoaded" StateChanged="OnWindowStateChanged" Closing="OnWindowClosing"> + Loaded="OnWindowLoaded" StateChanged="OnWindowStateChanged" Closing="OnWindowClosing" + Style="{StaticResource ThemedWindow}">