diff --git a/Actions/SwitchSystemAccentColorAction.cs b/Actions/SwitchSystemAccentColorAction.cs new file mode 100644 index 0000000..9b7f327 --- /dev/null +++ b/Actions/SwitchSystemAccentColorAction.cs @@ -0,0 +1,69 @@ +using ClassIsland.Core.Abstractions.Automation; +using ClassIsland.Core.Attributes; +using Microsoft.Extensions.Logging; +using Microsoft.Win32; +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using SystemTools.Settings; + +namespace SystemTools.Actions; + +[ActionInfo("SystemTools.SwitchSystemAccentColor", "切换系统强调色", "\uE790", false)] +public class SwitchSystemAccentColorAction(ILogger logger) : ActionBase +{ + private readonly ILogger _logger = logger; + + [DllImport("user32.dll", SetLastError = true)] + static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam, uint fuFlags, uint uTimeout, out UIntPtr lpdwResult); + + const uint HWND_BROADCAST = 0xFFFF; + const uint WM_SETTINGCHANGE = 0x001A; + const uint SMTO_ABORTIFHUNG = 0x0002; + + protected override async Task OnInvoke() + { + if (Settings == null || string.IsNullOrWhiteSpace(Settings.ColorHex)) return; + + try + { + var color = ParseColor(Settings.ColorHex); + // Windows 使用 ABGR 格式(低位字节是 R) + var dword = ((uint)color.A << 24) | ((uint)color.B << 16) | ((uint)color.G << 8) | color.R; + // ColorizationColor 通常使用 C4 (196) 作为 Alpha + var colorizationDword = (0xC4u << 24) | ((uint)color.B << 16) | ((uint)color.G << 8) | color.R; + + using var dwmKey = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\Windows\DWM"); + dwmKey?.SetValue("AccentColor", dword, RegistryValueKind.DWord); + dwmKey?.SetValue("ColorizationColor", colorizationDword, RegistryValueKind.DWord); + dwmKey?.SetValue("ColorizationAfterglow", colorizationDword, RegistryValueKind.DWord); + dwmKey?.SetValue("ColorPrevalence", 1, RegistryValueKind.DWord); + + using var explorerKey = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\Accent"); + explorerKey?.SetValue("AccentColorMenu", dword, RegistryValueKind.DWord); + + // 通知 Windows 刷新主题色 + SendMessageTimeout((IntPtr)HWND_BROADCAST, WM_SETTINGCHANGE, (UIntPtr)0, "ImmersiveColorSet", SMTO_ABORTIFHUNG, 5000, out _); + + _logger.LogInformation("系统强调色已切换为 {Color}", Settings.ColorHex); + } + catch (Exception ex) + { + _logger.LogError(ex, "切换系统强调色失败"); + throw; + } + + await base.OnInvoke(); + } + + private static (byte A, byte R, byte G, byte B) ParseColor(string colorHex) + { + var hex = colorHex.Trim().TrimStart('#'); + if (hex.Length == 6) hex = "FF" + hex; + if (hex.Length != 8) throw new FormatException("颜色格式无效"); + + var value = uint.Parse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture); + return ((byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)value); + } +} diff --git a/Controls/AccentColorSettingsControl.cs b/Controls/AccentColorSettingsControl.cs new file mode 100644 index 0000000..c1b5bd0 --- /dev/null +++ b/Controls/AccentColorSettingsControl.cs @@ -0,0 +1,34 @@ +using Avalonia.Controls; +using Avalonia.Layout; +using ClassIsland.Core.Abstractions.Controls; +using SystemTools.Settings; + +namespace SystemTools.Controls; + +public class AccentColorSettingsControl : ActionSettingsControlBase +{ + private readonly ColorPicker _colorPicker; + + public AccentColorSettingsControl() + { + var panel = new StackPanel { Spacing = 10, Margin = new(10) }; + panel.Children.Add(new TextBlock { Text = "切换系统强调色", FontSize = 14, FontWeight = Avalonia.Media.FontWeight.Bold }); + + _colorPicker = new ColorPicker + { + IsAlphaEnabled = false, + HorizontalAlignment = HorizontalAlignment.Left + }; + _colorPicker.ColorChanged += (_, _) => Settings.ColorHex = _colorPicker.Color.ToString(); + + panel.Children.Add(_colorPicker); + Content = panel; + } + + protected override void OnInitialized() + { + base.OnInitialized(); + if (Avalonia.Media.Color.TryParse(Settings.ColorHex, out var color)) + _colorPicker.Color = color; + } +} diff --git a/Plugin.cs b/Plugin.cs index 58ac528..a7e177f 100644 --- a/Plugin.cs +++ b/Plugin.cs @@ -258,6 +258,7 @@ private void RegisterBaseActions(IServiceCollection services) RegisterActionIfEnabled(services, config, "SystemTools.ChangeWallpaper"); RegisterActionIfEnabled(services, config, "SystemTools.SwitchTheme"); + RegisterActionIfEnabled(services, config, "SystemTools.SwitchSystemAccentColor"); // 实用工具 RegisterActionIfEnabled(services, config, @@ -491,7 +492,7 @@ private void BuildBaseActionTree() } // 系统个性化 - if (HasAnyActionEnabled(config, "SystemTools.ChangeWallpaper", "SystemTools.SwitchTheme")) + if (HasAnyActionEnabled(config, "SystemTools.ChangeWallpaper", "SystemTools.SwitchTheme", "SystemTools.SwitchSystemAccentColor")) { IActionService.ActionMenuTree["SystemTools 行动"].Add(new ActionMenuTreeGroup("系统个性化…", "\uF42F")); BuildPersonalizationMenu(config); @@ -625,6 +626,9 @@ private static bool HandleInTimePeriodRule(object? settings) return current >= start || current <= end; } + private static DateTime _lastMediaRuleCheckAt = DateTime.MinValue; + private static bool _lastMediaRuleResult; + private static bool HandleMediaMusicPlayingRule(object? settings) { if (!OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134)) @@ -632,28 +636,32 @@ private static bool HandleMediaMusicPlayingRule(object? settings) return false; } + var now = DateTime.UtcNow; + if (now - _lastMediaRuleCheckAt < TimeSpan.FromMilliseconds(800)) + { + return _lastMediaRuleResult; + } + try { var manager = WinMedia.GlobalSystemMediaTransportControlsSessionManager.RequestAsync() .AsTask().GetAwaiter().GetResult(); - if (manager == null) - return false; - - var sessions = manager.GetSessions(); - if (sessions == null || sessions.Count == 0) - return false; - - return sessions.Any(session => + var sessions = manager?.GetSessions(); + var isPlaying = sessions != null && sessions.Any(session => { var playbackInfo = session.GetPlaybackInfo(); - return playbackInfo != null && - playbackInfo.PlaybackStatus == WinMedia.GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing; + return playbackInfo?.PlaybackStatus == WinMedia.GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing; }); + + _lastMediaRuleResult = isPlaying; + _lastMediaRuleCheckAt = now; + return isPlaying; } catch { - return false; + _lastMediaRuleCheckAt = now; + return _lastMediaRuleResult; } } @@ -768,6 +776,8 @@ private void BuildPersonalizationMenu(MainConfigData config) items.Add(new ActionMenuTreeItem("SystemTools.ChangeWallpaper", "切换壁纸", "\uE9BC")); if (config.IsActionEnabled("SystemTools.SwitchTheme")) items.Add(new ActionMenuTreeItem("SystemTools.SwitchTheme", "切换主题色", "\uF42F")); + if (config.IsActionEnabled("SystemTools.SwitchSystemAccentColor")) + items.Add(new ActionMenuTreeItem("SystemTools.SwitchSystemAccentColor", "切换系统强调色", "\uE790")); if (items.Count > 0) { diff --git a/Services/AdaptiveThemeSyncService.cs b/Services/AdaptiveThemeSyncService.cs index e8f55fa..33dc6b1 100644 --- a/Services/AdaptiveThemeSyncService.cs +++ b/Services/AdaptiveThemeSyncService.cs @@ -1,6 +1,7 @@ using Avalonia.Threading; using ClassIsland.Core; using ClassIsland.Core.Abstractions.Services; +using System.Reflection; using Microsoft.Extensions.Logging; using System; using System.Drawing; @@ -56,13 +57,7 @@ private void OnTick(object? sender, EventArgs e) return; } - var themeService = IAppHost.TryGetService(); - if (themeService == null) - { - return; - } - - themeService.SetTheme(targetTheme.Value, null); + ApplyThemeLikeAppSettings(targetTheme.Value); _lastAppliedTheme = targetTheme; _logger.LogDebug("已自动匹配主题为:{Theme}", targetTheme == 1 ? "黑暗" : "明亮"); } @@ -178,6 +173,28 @@ private static bool ResolveUseTopAreaFromClassIslandSettings() return null; } + private static void ApplyThemeLikeAppSettings(int targetTheme) + { + var settingsServiceObj = IAppHost.TryGetService(); + if (settingsServiceObj != null) + { + var type = settingsServiceObj.GetType(); + if (type.Name == "SettingsService") + { + var settingsProp = type.GetProperty("Settings", BindingFlags.Instance | BindingFlags.Public); + var settings = settingsProp?.GetValue(settingsServiceObj); + var themeProp = settings?.GetType().GetProperty("Theme", BindingFlags.Instance | BindingFlags.Public); + if (themeProp?.CanWrite == true) + { + themeProp.SetValue(settings, targetTheme); + return; + } + } + } + + IAppHost.TryGetService()?.SetTheme(targetTheme, null); + } + private static string[] GetPossibleClassIslandSettingsPaths() { var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); diff --git a/Settings/AccentColorSettings.cs b/Settings/AccentColorSettings.cs new file mode 100644 index 0000000..df075ef --- /dev/null +++ b/Settings/AccentColorSettings.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace SystemTools.Settings; + +public class AccentColorSettings +{ + [JsonPropertyName("colorHex")] public string ColorHex { get; set; } = "#FF0078D4"; +} diff --git a/SettingsPage/SystemToolsSettingsViewModel.cs b/SettingsPage/SystemToolsSettingsViewModel.cs index 8d480a7..511a9d7 100644 --- a/SettingsPage/SystemToolsSettingsViewModel.cs +++ b/SettingsPage/SystemToolsSettingsViewModel.cs @@ -195,6 +195,7 @@ public void InitializeFeatureItems() ("SystemTools.Delete", "删除", "文件操作"), ("SystemTools.ChangeWallpaper", "切换壁纸", "系统个性化"), ("SystemTools.SwitchTheme", "切换主题色", "系统个性化"), + ("SystemTools.SwitchSystemAccentColor", "切换系统强调色", "系统个性化"), ("SystemTools.FullscreenClock", "沉浸式时钟", "其他工具"), ("SystemTools.KillProcess", "退出进程", "实用工具"), ("SystemTools.ScreenShot", "屏幕截图", "实用工具"),