From c9730e1a11b06ecf44ea8a36a94ebeb10357224b Mon Sep 17 00:00:00 2001 From: ww-rm Date: Fri, 3 Oct 2025 23:45:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=A0=87=E9=A2=98=E6=A0=8F?= =?UTF-8?q?=E7=9A=AE=E8=82=A4=E9=A2=9C=E8=89=B2=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SpineViewer/App.xaml.cs | 14 ++++-- SpineViewer/Natives/Dwmapi.cs | 48 +++++++++++++++++++ SpineViewer/Utils/JsonHelper.cs | 4 +- .../MainWindow/SpineObjectListViewModel.cs | 4 +- SpineViewer/Views/AboutDialog.xaml.cs | 13 ++++- SpineViewer/Views/DiagnosticsDialog.xaml.cs | 13 ++++- .../CustomFFmpegExporterDialog.xaml.cs | 13 ++++- .../FFmpegVideoExporterDialog.xaml.cs | 13 ++++- .../FrameExporterDialog.xaml.cs | 13 ++++- .../FrameSequenceExporterDialog.xaml.cs | 13 ++++- SpineViewer/Views/MainWindow.xaml.cs | 12 ++++- SpineViewer/Views/PreferenceDialog.xaml.cs | 13 ++++- SpineViewer/Views/ProgressDialog.xaml.cs | 12 ++++- 13 files changed, 166 insertions(+), 19 deletions(-) create mode 100644 SpineViewer/Natives/Dwmapi.cs diff --git a/SpineViewer/App.xaml.cs b/SpineViewer/App.xaml.cs index 5411bf8..e7812a2 100644 --- a/SpineViewer/App.xaml.cs +++ b/SpineViewer/App.xaml.cs @@ -1,6 +1,7 @@ using Microsoft.Win32; using NLog; using SpineViewer.Natives; +using SpineViewer.Resources; using SpineViewer.ViewModels.MainWindow; using SpineViewer.Views; using System.Collections.Frozen; @@ -12,6 +13,7 @@ using System.IO; using System.IO.Pipes; using System.Reflection; using System.Windows; +using System.Windows.Interop; namespace SpineViewer { @@ -231,8 +233,8 @@ namespace SpineViewer } catch (Exception ex) { - _logger.Error("Failed to query autorun registry key, {0}", ex.Message); _logger.Trace(ex.ToString()); + _logger.Error("Failed to query autorun registry key, {0}", ex.Message); return false; } } @@ -259,8 +261,8 @@ namespace SpineViewer } catch (Exception ex) { - _logger.Error("Failed to set autorun registry key, {0}", ex.Message); _logger.Trace(ex.ToString()); + _logger.Error("Failed to set autorun registry key, {0}", ex.Message); } } } @@ -343,8 +345,8 @@ namespace SpineViewer } catch (Exception ex) { - _logger.Error("Failed to switch language to {0}, {1}", value, ex.Message); _logger.Trace(ex.ToString()); + _logger.Error("Failed to switch language to {0}, {1}", value, ex.Message); } } } @@ -360,17 +362,19 @@ namespace SpineViewer { Resources.MergedDictionaries.Add(new() { Source = new(uri, UriKind.Relative) }); Resources.MergedDictionaries.Add(new() { Source = new("Resources/Theme.xaml", UriKind.Relative) }); + var hwnd = new WindowInteropHelper(Current.MainWindow).Handle; + Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText); + Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region); _skin = value; } catch (Exception ex) { - _logger.Error("Failed to switch skin to {0}, {1}", value, ex.Message); _logger.Trace(ex.ToString()); + _logger.Error("Failed to switch skin to {0}, {1}", value, ex.Message); } } } private AppSkin _skin = AppSkin.Light; - } public enum AppLanguage diff --git a/SpineViewer/Natives/Dwmapi.cs b/SpineViewer/Natives/Dwmapi.cs new file mode 100644 index 0000000..29fe400 --- /dev/null +++ b/SpineViewer/Natives/Dwmapi.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Media; + +namespace SpineViewer.Natives +{ + /// + /// dwmapi.dll 包装类 + /// + public static class Dwmapi + { + private const uint DWMWA_USE_IMMERSIVE_DARK_MODE = 20; + private const uint DWMWA_CAPTION_COLOR = 35; + private const uint DWMWA_TEXT_COLOR = 36; + private const uint DWMWA_COLOR_DEFAULT = 0xFFFFFFFF; + + [DllImport("dwmapi.dll")] + private static extern int DwmSetWindowAttribute(IntPtr hwnd, uint dwAttribute, ref int pvAttribute, int cbAttribute); + + [DllImport("dwmapi.dll")] + private static extern int DwmSetWindowAttribute(IntPtr hwnd, uint dwAttribute, ref uint pvAttribute, int cbAttribute); + + public static bool SetWindowCaptionColor(IntPtr hwnd, Color color) + { + int c = color.R | (color.G << 8) | (color.B << 16); + return 0 == DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, ref c, sizeof(uint)); + } + + public static bool SetWindowTextColor(IntPtr hwnd, Color color) + { + int c = color.R | (color.G << 8) | (color.B << 16); + return 0 == DwmSetWindowAttribute(hwnd, DWMWA_TEXT_COLOR, ref c, sizeof(uint)); + } + + public static bool SetWindowDarkMode(IntPtr hwnd, bool darkMode) + { + int b = darkMode ? 1 : 0; + uint c = DWMWA_COLOR_DEFAULT; + DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, ref c, sizeof(uint)); + DwmSetWindowAttribute(hwnd, DWMWA_TEXT_COLOR, ref c, sizeof(uint)); + return 0 == DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, ref b, sizeof(int)); + } + } +} diff --git a/SpineViewer/Utils/JsonHelper.cs b/SpineViewer/Utils/JsonHelper.cs index 96b3fb0..6efb3d2 100644 --- a/SpineViewer/Utils/JsonHelper.cs +++ b/SpineViewer/Utils/JsonHelper.cs @@ -66,8 +66,8 @@ namespace SpineViewer.Utils } catch (Exception ex) { - _logger.Error("Failed to read json file {0}, {1}", path, ex.Message); _logger.Trace(ex.ToString()); + _logger.Error("Failed to read json file {0}, {1}", path, ex.Message); MessagePopupService.Error($"Failed to read json file {path}, {ex.ToString()}"); } } @@ -88,8 +88,8 @@ namespace SpineViewer.Utils } catch (Exception ex) { - _logger.Error("Failed to save json file {0}, {1}", path, ex.Message); _logger.Trace(ex.ToString()); + _logger.Error("Failed to save json file {0}, {1}", path, ex.Message); MessagePopupService.Error($"Failed to save json file {path}, {ex.ToString()}"); return false; } diff --git a/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs b/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs index a161261..0615ac4 100644 --- a/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs @@ -218,8 +218,8 @@ namespace SpineViewer.ViewModels.MainWindow } catch (Exception ex) { - _logger.Error("Failed to reload spine {0}, {1}", sp.SkelPath, ex.Message); _logger.Trace(ex.ToString()); + _logger.Error("Failed to reload spine {0}, {1}", sp.SkelPath, ex.Message); } } } @@ -279,8 +279,8 @@ namespace SpineViewer.ViewModels.MainWindow catch (Exception ex) { error++; - _logger.Error("Failed to reload spine {0}, {1}", sp.SkelPath, ex.Message); _logger.Trace(ex.ToString()); + _logger.Error("Failed to reload spine {0}, {1}", sp.SkelPath, ex.Message); } } } diff --git a/SpineViewer/Views/AboutDialog.xaml.cs b/SpineViewer/Views/AboutDialog.xaml.cs index decc20e..f390768 100644 --- a/SpineViewer/Views/AboutDialog.xaml.cs +++ b/SpineViewer/Views/AboutDialog.xaml.cs @@ -1,4 +1,6 @@ -using System; +using SpineViewer.Natives; +using SpineViewer.Resources; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -8,6 +10,7 @@ using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; +using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; @@ -22,6 +25,14 @@ namespace SpineViewer.Views public AboutDialog() { InitializeComponent(); + SourceInitialized += AboutDialog_SourceInitialized; + } + + private void AboutDialog_SourceInitialized(object? sender, EventArgs e) + { + var hwnd = new WindowInteropHelper(this).Handle; + Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText); + Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region); } } } diff --git a/SpineViewer/Views/DiagnosticsDialog.xaml.cs b/SpineViewer/Views/DiagnosticsDialog.xaml.cs index 2dcee92..72ca5aa 100644 --- a/SpineViewer/Views/DiagnosticsDialog.xaml.cs +++ b/SpineViewer/Views/DiagnosticsDialog.xaml.cs @@ -1,4 +1,6 @@ -using System; +using SpineViewer.Natives; +using SpineViewer.Resources; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -8,6 +10,7 @@ using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; +using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; @@ -22,6 +25,14 @@ namespace SpineViewer.Views public DiagnosticsDialog() { InitializeComponent(); + SourceInitialized += DiagnosticsDialog_SourceInitialized; + } + + private void DiagnosticsDialog_SourceInitialized(object? sender, EventArgs e) + { + var hwnd = new WindowInteropHelper(this).Handle; + Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText); + Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region); } } } diff --git a/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml.cs b/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml.cs index 7eceb61..eda8ae4 100644 --- a/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml.cs +++ b/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml.cs @@ -1,4 +1,6 @@ -using SpineViewer.Services; +using SpineViewer.Natives; +using SpineViewer.Resources; +using SpineViewer.Services; using SpineViewer.ViewModels.Exporters; using System; using System.Collections.Generic; @@ -10,6 +12,7 @@ using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; +using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; @@ -24,6 +27,14 @@ namespace SpineViewer.Views.ExporterDialogs public CustomFFmpegExporterDialog() { InitializeComponent(); + SourceInitialized += CustomFFmpegExporterDialog_SourceInitialized; + } + + private void CustomFFmpegExporterDialog_SourceInitialized(object? sender, EventArgs e) + { + var hwnd = new WindowInteropHelper(this).Handle; + Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText); + Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region); } private void ButtonOK_Click(object sender, RoutedEventArgs e) diff --git a/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml.cs b/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml.cs index 5528eb2..b4dbaaa 100644 --- a/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml.cs +++ b/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml.cs @@ -1,4 +1,6 @@ -using SpineViewer.Services; +using SpineViewer.Natives; +using SpineViewer.Resources; +using SpineViewer.Services; using SpineViewer.ViewModels.Exporters; using System; using System.Collections.Generic; @@ -10,6 +12,7 @@ using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; +using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; @@ -24,6 +27,14 @@ namespace SpineViewer.Views.ExporterDialogs public FFmpegVideoExporterDialog() { InitializeComponent(); + SourceInitialized += FFmpegVideoExporterDialog_SourceInitialized; + } + + private void FFmpegVideoExporterDialog_SourceInitialized(object? sender, EventArgs e) + { + var hwnd = new WindowInteropHelper(this).Handle; + Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText); + Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region); } private void ButtonOK_Click(object sender, RoutedEventArgs e) diff --git a/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml.cs b/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml.cs index 5fb3c65..b34058f 100644 --- a/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml.cs +++ b/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml.cs @@ -1,4 +1,6 @@ -using SpineViewer.Services; +using SpineViewer.Natives; +using SpineViewer.Resources; +using SpineViewer.Services; using SpineViewer.ViewModels.Exporters; using System; using System.Collections.Generic; @@ -10,6 +12,7 @@ using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; +using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; @@ -24,6 +27,14 @@ namespace SpineViewer.Views.ExporterDialogs public FrameExporterDialog() { InitializeComponent(); + SourceInitialized += FrameExporterDialog_SourceInitialized; + } + + private void FrameExporterDialog_SourceInitialized(object? sender, EventArgs e) + { + var hwnd = new WindowInteropHelper(this).Handle; + Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText); + Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region); } private void ButtonOK_Click(object sender, RoutedEventArgs e) diff --git a/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml.cs b/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml.cs index 6c6590e..1ed0c47 100644 --- a/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml.cs +++ b/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml.cs @@ -1,4 +1,6 @@ -using SpineViewer.Services; +using SpineViewer.Natives; +using SpineViewer.Resources; +using SpineViewer.Services; using SpineViewer.ViewModels.Exporters; using System; using System.Collections.Generic; @@ -10,6 +12,7 @@ using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; +using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; @@ -24,6 +27,14 @@ namespace SpineViewer.Views.ExporterDialogs public FrameSequenceExporterDialog() { InitializeComponent(); + SourceInitialized += FrameSequenceExporterDialog_SourceInitialized; + } + + private void FrameSequenceExporterDialog_SourceInitialized(object? sender, EventArgs e) + { + var hwnd = new WindowInteropHelper(this).Handle; + Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText); + Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region); } private void ButtonOK_Click(object sender, RoutedEventArgs e) diff --git a/SpineViewer/Views/MainWindow.xaml.cs b/SpineViewer/Views/MainWindow.xaml.cs index 6c01547..59fe17e 100644 --- a/SpineViewer/Views/MainWindow.xaml.cs +++ b/SpineViewer/Views/MainWindow.xaml.cs @@ -15,6 +15,7 @@ using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Reflection.Metadata; +using System.Runtime.InteropServices; using System.Text; using System.Windows; using System.Windows.Controls; @@ -65,11 +66,11 @@ public partial class MainWindow : Window // XXX: hc 的 NotifyIcon 的 Text 似乎没法双向绑定 _notifyIcon.Text = _vm.Title; + SourceInitialized += MainWindow_SourceInitialized; Loaded += MainWindow_Loaded; ContentRendered += MainWindow_ContentRendered; Closing += MainWindow_Closing; Closed += MainWindow_Closed; - _vm.SpineObjectListViewModel.RequestSelectionChanging += SpinesListView_RequestSelectionChanging; _vm.SFMLRendererViewModel.RequestSelectionChanging += SpinesListView_RequestSelectionChanging; @@ -178,6 +179,13 @@ public partial class MainWindow : Window #region MainWindow 事件处理 + private void MainWindow_SourceInitialized(object? sender, EventArgs e) + { + var hwnd = new WindowInteropHelper(this).Handle; + Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText); + Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region); + } + private void MainWindow_Loaded(object sender, RoutedEventArgs e) { var vm = _vm.SFMLRendererViewModel; @@ -728,6 +736,7 @@ public partial class MainWindow : Window } #endregion + private void DebugMenuItem_Click(object sender, RoutedEventArgs e) { @@ -737,7 +746,6 @@ public partial class MainWindow : Window _logger.Warn("Warn"); _logger.Error("Error"); _logger.Fatal("Fatal"); - return; #endif } diff --git a/SpineViewer/Views/PreferenceDialog.xaml.cs b/SpineViewer/Views/PreferenceDialog.xaml.cs index 50af2d5..849f47b 100644 --- a/SpineViewer/Views/PreferenceDialog.xaml.cs +++ b/SpineViewer/Views/PreferenceDialog.xaml.cs @@ -1,4 +1,6 @@ -using SpineViewer.Services; +using SpineViewer.Natives; +using SpineViewer.Resources; +using SpineViewer.Services; using SpineViewer.ViewModels.Exporters; using System; using System.Collections.Generic; @@ -10,6 +12,7 @@ using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; +using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; @@ -24,6 +27,14 @@ namespace SpineViewer.Views public PreferenceDialog() { InitializeComponent(); + SourceInitialized += PreferenceDialog_SourceInitialized; + } + + private void PreferenceDialog_SourceInitialized(object? sender, EventArgs e) + { + var hwnd = new WindowInteropHelper(this).Handle; + Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText); + Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region); } private void ButtonOK_Click(object sender, RoutedEventArgs e) diff --git a/SpineViewer/Views/ProgressDialog.xaml.cs b/SpineViewer/Views/ProgressDialog.xaml.cs index 33c44a1..fde5c03 100644 --- a/SpineViewer/Views/ProgressDialog.xaml.cs +++ b/SpineViewer/Views/ProgressDialog.xaml.cs @@ -1,4 +1,6 @@ -using SpineViewer.ViewModels; +using SpineViewer.Natives; +using SpineViewer.Resources; +using SpineViewer.ViewModels; using System; using System.Collections.Generic; using System.Linq; @@ -25,9 +27,17 @@ namespace SpineViewer.Views public ProgressDialog() { InitializeComponent(); + SourceInitialized += ProgressDialog_SourceInitialized; Loaded += ProgressWindow_Loaded; } + private void ProgressDialog_SourceInitialized(object? sender, EventArgs e) + { + var hwnd = new WindowInteropHelper(this).Handle; + Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText); + Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region); + } + private void ProgressWindow_Loaded(object sender, RoutedEventArgs e) { var hwnd = new WindowInteropHelper(this).Handle;