diff --git a/App.xaml.cs b/App.xaml.cs
index 3acd1ea..274c24d 100644
--- a/App.xaml.cs
+++ b/App.xaml.cs
@@ -10,7 +10,6 @@ public partial class App : System.Windows.Application
{
base.OnStartup(e);
- ThemeService.Apply();
controller = new LauncherController();
controller.Start();
diff --git a/MainWindow.xaml b/MainWindow.xaml
index e03eb30..9cabaf2 100644
--- a/MainWindow.xaml
+++ b/MainWindow.xaml
@@ -129,6 +129,7 @@
+
@@ -136,10 +137,14 @@
PreviewKeyDown="HotkeyBox_PreviewKeyDown"/>
-
+
-
-
+
+
+
+ ();
ItemDisplayModeBox.ItemsSource = Enum.GetValues();
+ ThemeSchemeBox.ItemsSource = Enum.GetValues();
TypeBox.ItemsSource = Enum.GetValues();
VersionText.Text = $"Version {AppInfo.Version} Build {AppInfo.Build}";
RefreshView();
@@ -53,6 +54,7 @@ public partial class MainWindow : Window
HotkeyBox.Text = controller.Config.Hotkey;
DisplayModeBox.SelectedItem = controller.Config.DisplayMode;
+ ThemeSchemeBox.SelectedItem = controller.Config.ThemeScheme;
LoadStartupState();
StatusText.Text = "Config file: config.json beside TaskbarLauncher.exe";
if (itemToSelect is not null && SelectTreeItem(itemToSelect))
@@ -324,6 +326,9 @@ public partial class MainWindow : Window
controller.Config.DisplayMode = DisplayModeBox.SelectedItem is LauncherDisplayMode mode
? mode
: LauncherDisplayMode.CompactList;
+ controller.Config.ThemeScheme = ThemeSchemeBox.SelectedItem is LauncherThemeScheme scheme
+ ? scheme
+ : LauncherThemeScheme.MarkHaven;
ApplyCurrentItem();
}
@@ -1007,6 +1012,16 @@ public partial class MainWindow : Window
UpdatePreview();
}
+ private void ThemeSchemeBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (controller is null || ThemeSchemeBox.SelectedItem is not LauncherThemeScheme scheme)
+ {
+ return;
+ }
+
+ ThemeService.Apply(scheme);
+ }
+
private void EnsureAutomaticIconPath()
{
if (TypeBox.SelectedItem is LauncherItemType.Menu)
diff --git a/Models/LauncherConfig.cs b/Models/LauncherConfig.cs
index 49eac7b..19f96d7 100644
--- a/Models/LauncherConfig.cs
+++ b/Models/LauncherConfig.cs
@@ -6,6 +6,8 @@ public sealed class LauncherConfig
public LauncherDisplayMode DisplayMode { get; set; } = LauncherDisplayMode.CompactList;
+ public LauncherThemeScheme ThemeScheme { get; set; } = LauncherThemeScheme.MarkHaven;
+
public List Items { get; set; } = [];
}
@@ -42,3 +44,11 @@ public enum LauncherDisplayMode
LargeIconWithText,
CompactList
}
+
+public enum LauncherThemeScheme
+{
+ MarkHaven,
+ MGETOrange,
+ System,
+ WindowsClassic
+}
diff --git a/Services/ConfigService.cs b/Services/ConfigService.cs
index 7b41f36..1f1b8b0 100644
--- a/Services/ConfigService.cs
+++ b/Services/ConfigService.cs
@@ -36,7 +36,7 @@ public static class ConfigService
string json = File.ReadAllText(ConfigPath);
LauncherConfig config = JsonSerializer.Deserialize(json, JsonOptions) ?? CreateDefaultConfig();
- NormalizeDisplayModes(config);
+ NormalizeConfig(config);
Save(config);
return config;
}
@@ -74,7 +74,7 @@ public static class ConfigService
Directory.CreateDirectory(destinationFolder);
}
- NormalizeDisplayModes(config);
+ NormalizeConfig(config);
string json = JsonSerializer.Serialize(config, JsonOptions);
File.WriteAllText(destinationPath, json);
return destinationPath;
@@ -155,7 +155,7 @@ public static class ConfigService
]
};
- NormalizeDisplayModes(config);
+ NormalizeConfig(config);
return config;
}
@@ -179,7 +179,7 @@ public static class ConfigService
}
ValidateConfig(config);
- NormalizeDisplayModes(config);
+ NormalizeConfig(config);
return config;
}
@@ -214,8 +214,18 @@ public static class ConfigService
}
}
- private static void NormalizeDisplayModes(LauncherConfig config)
+ private static void NormalizeConfig(LauncherConfig config)
{
+ if (!Enum.IsDefined(config.DisplayMode))
+ {
+ config.DisplayMode = LauncherDisplayMode.CompactList;
+ }
+
+ if (!Enum.IsDefined(config.ThemeScheme))
+ {
+ config.ThemeScheme = LauncherThemeScheme.MarkHaven;
+ }
+
foreach (LauncherItem item in config.Items)
{
NormalizeDisplayModes(item);
diff --git a/Services/LauncherController.cs b/Services/LauncherController.cs
index 4029094..8976adc 100644
--- a/Services/LauncherController.cs
+++ b/Services/LauncherController.cs
@@ -20,6 +20,7 @@ public sealed class LauncherController : IDisposable
public void Start()
{
Config = ConfigService.Load();
+ ThemeService.Apply(Config.ThemeScheme);
CreateTrayIcon();
RegisterHotkey();
}
@@ -27,6 +28,7 @@ public sealed class LauncherController : IDisposable
public void ReloadConfig()
{
Config = ConfigService.Load();
+ ThemeService.Apply(Config.ThemeScheme);
RegisterHotkey();
popup?.Close();
popup = null;
diff --git a/Services/ThemeService.cs b/Services/ThemeService.cs
index 37cf7b9..3be8958 100644
--- a/Services/ThemeService.cs
+++ b/Services/ThemeService.cs
@@ -1,6 +1,7 @@
using System.Windows;
using System.Windows.Media;
using Microsoft.Win32;
+using TaskbarLauncher.Models;
using MediaColor = System.Windows.Media.Color;
namespace TaskbarLauncher.Services;
@@ -10,42 +11,32 @@ public static class ThemeService
private const string PersonalizeKey = @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize";
private const string DwmKey = @"Software\Microsoft\Windows\DWM";
- public static void Apply()
+ public static void Apply(LauncherThemeScheme scheme = LauncherThemeScheme.MarkHaven)
{
bool isLight = IsLightTheme();
- MediaColor accent = GetAccentColor();
- MediaColor orange = MediaColor.FromRgb(0xD8, 0x78, 0x1D);
- MediaColor window = isLight ? MediaColor.FromRgb(0xF6, 0xF1, 0xEA) : MediaColor.FromRgb(0x18, 0x16, 0x14);
- MediaColor surface = isLight ? MediaColor.FromRgb(0xFF, 0xFC, 0xF8) : MediaColor.FromRgb(0x22, 0x1F, 0x1B);
- MediaColor panel = isLight ? MediaColor.FromRgb(0xFF, 0xF7, 0xEE) : MediaColor.FromRgb(0x2A, 0x24, 0x1E);
- MediaColor control = isLight ? MediaColor.FromRgb(0xFA, 0xF1, 0xE7) : MediaColor.FromRgb(0x32, 0x2B, 0x24);
- MediaColor hover = isLight ? MediaColor.FromRgb(0xFF, 0xED, 0xDA) : MediaColor.FromRgb(0x42, 0x34, 0x28);
- MediaColor text = isLight ? MediaColor.FromRgb(0x24, 0x1D, 0x17) : MediaColor.FromRgb(0xF8, 0xF2, 0xEA);
- MediaColor muted = isLight ? MediaColor.FromRgb(0x75, 0x68, 0x5C) : MediaColor.FromRgb(0xC3, 0xB5, 0xA6);
- MediaColor border = isLight ? MediaColor.FromRgb(0xE4, 0xD1, 0xBC) : MediaColor.FromRgb(0x5B, 0x4B, 0x3C);
- MediaColor accentSoft = Blend(accent, surface, isLight ? 0.14 : 0.24);
- MediaColor orangeSoft = Blend(orange, surface, isLight ? 0.16 : 0.30);
+ ThemePalette palette = GetPalette(scheme, isLight);
+ MediaColor accent = palette.Accent;
MediaColor selectedText = IsDark(accent) ? System.Windows.Media.Colors.White : System.Windows.Media.Colors.Black;
- SetBrush("AppWindowBrush", window);
- SetBrush("AppSurfaceBrush", surface);
- SetBrush("AppPanelBrush", panel);
- SetBrush("AppControlBrush", control);
- SetBrush("AppControlHoverBrush", hover);
- SetBrush("AppTextBrush", text);
- SetBrush("AppMutedTextBrush", muted);
- SetBrush("AppBorderBrush", border);
+ SetBrush("AppWindowBrush", palette.Window);
+ SetBrush("AppSurfaceBrush", palette.Surface);
+ SetBrush("AppPanelBrush", palette.Panel);
+ SetBrush("AppControlBrush", palette.Control);
+ SetBrush("AppControlHoverBrush", palette.Hover);
+ SetBrush("AppTextBrush", palette.Text);
+ SetBrush("AppMutedTextBrush", palette.Muted);
+ SetBrush("AppBorderBrush", palette.Border);
SetBrush("AppAccentBrush", accent);
- SetBrush("AppAccentSoftBrush", accentSoft);
- SetBrush("AppOrangeBrush", orange);
- SetBrush("AppOrangeSoftBrush", orangeSoft);
+ SetBrush("AppAccentSoftBrush", palette.AccentSoft);
+ SetBrush("AppOrangeBrush", palette.Orange);
+ SetBrush("AppOrangeSoftBrush", palette.OrangeSoft);
SetBrush("AppSelectedTextBrush", selectedText);
- SetBrush(System.Windows.SystemColors.WindowBrushKey, surface);
- SetBrush(System.Windows.SystemColors.WindowTextBrushKey, text);
- SetBrush(System.Windows.SystemColors.ControlBrushKey, control);
- SetBrush(System.Windows.SystemColors.ControlTextBrushKey, text);
- SetBrush(System.Windows.SystemColors.ControlDarkBrushKey, border);
+ SetBrush(System.Windows.SystemColors.WindowBrushKey, palette.Surface);
+ SetBrush(System.Windows.SystemColors.WindowTextBrushKey, palette.Text);
+ SetBrush(System.Windows.SystemColors.ControlBrushKey, palette.Control);
+ SetBrush(System.Windows.SystemColors.ControlTextBrushKey, palette.Text);
+ SetBrush(System.Windows.SystemColors.ControlDarkBrushKey, palette.Border);
SetBrush(System.Windows.SystemColors.HighlightBrushKey, accent);
SetBrush(System.Windows.SystemColors.HighlightTextBrushKey, selectedText);
}
@@ -70,6 +61,101 @@ public static class ThemeService
return MediaColor.FromRgb(0x00, 0x78, 0xD4);
}
+ private static ThemePalette GetPalette(LauncherThemeScheme scheme, bool isLight)
+ {
+ MediaColor windowsAccent = GetAccentColor();
+ MediaColor orange = MediaColor.FromRgb(0xD8, 0x78, 0x1D);
+
+ return scheme switch
+ {
+ LauncherThemeScheme.System => CreatePalette(
+ isLight,
+ windowsAccent,
+ orange,
+ window: LightDark(isLight, 0xF7F7F7, 0x202020),
+ surface: LightDark(isLight, 0xFFFFFF, 0x2B2B2B),
+ panel: LightDark(isLight, 0xF3F3F3, 0x333333),
+ control: LightDark(isLight, 0xF3F3F3, 0x333333),
+ hover: LightDark(isLight, 0xEAF3FF, 0x3B4A5C),
+ text: LightDark(isLight, 0x1A1A1A, 0xF3F3F3),
+ muted: LightDark(isLight, 0x666666, 0xB8B8B8),
+ border: LightDark(isLight, 0xC8C8C8, 0x555555)),
+
+ LauncherThemeScheme.WindowsClassic => CreatePalette(
+ isLight,
+ windowsAccent,
+ orange,
+ window: LightDark(isLight, 0xF0F0F0, 0x1F1F1F),
+ surface: LightDark(isLight, 0xFAFAFA, 0x2D2D30),
+ panel: LightDark(isLight, 0xFFFFFF, 0x252526),
+ control: LightDark(isLight, 0xF5F5F5, 0x333337),
+ hover: LightDark(isLight, 0xEDEDED, 0x3E3E42),
+ text: LightDark(isLight, 0x111111, 0xF1F1F1),
+ muted: LightDark(isLight, 0x5F5F5F, 0xBDBDBD),
+ border: LightDark(isLight, 0xD0D0D0, 0x565656)),
+
+ LauncherThemeScheme.MGETOrange => CreatePalette(
+ isLight,
+ orange,
+ orange,
+ window: LightDark(isLight, 0xF6F1EA, 0x181614),
+ surface: LightDark(isLight, 0xFFFCF8, 0x221F1B),
+ panel: LightDark(isLight, 0xFFF7EE, 0x2A241E),
+ control: LightDark(isLight, 0xFAF1E7, 0x322B24),
+ hover: LightDark(isLight, 0xFFEDDA, 0x423428),
+ text: LightDark(isLight, 0x241D17, 0xF8F2EA),
+ muted: LightDark(isLight, 0x75685C, 0xC3B5A6),
+ border: LightDark(isLight, 0xE4D1BC, 0x5B4B3C)),
+
+ _ => CreatePalette(
+ isLight,
+ LightDark(isLight, 0x146B52, 0x58D0A3),
+ LightDark(isLight, 0xB56B08, 0xF4BD54),
+ window: LightDark(isLight, 0xEEF1F0, 0x111816),
+ surface: LightDark(isLight, 0xFFFFFF, 0x18201E),
+ panel: LightDark(isLight, 0xFBFCFB, 0x15201D),
+ control: LightDark(isLight, 0xEEF3F0, 0x1A2A24),
+ hover: LightDark(isLight, 0xE2F3EC, 0x183B30),
+ text: LightDark(isLight, 0x1B2824, 0xEDF5F2),
+ muted: LightDark(isLight, 0x66746F, 0x9CAAA5),
+ border: LightDark(isLight, 0xDCE3E0, 0x33423D))
+ };
+ }
+
+ private static ThemePalette CreatePalette(
+ bool isLight,
+ MediaColor accent,
+ MediaColor orange,
+ MediaColor window,
+ MediaColor surface,
+ MediaColor panel,
+ MediaColor control,
+ MediaColor hover,
+ MediaColor text,
+ MediaColor muted,
+ MediaColor border)
+ {
+ return new ThemePalette(
+ window,
+ surface,
+ panel,
+ control,
+ hover,
+ text,
+ muted,
+ border,
+ accent,
+ Blend(accent, surface, isLight ? 0.14 : 0.24),
+ orange,
+ Blend(orange, surface, isLight ? 0.16 : 0.30));
+ }
+
+ private static MediaColor LightDark(bool isLight, int lightRgb, int darkRgb)
+ {
+ int rgb = isLight ? lightRgb : darkRgb;
+ return MediaColor.FromRgb((byte)(rgb >> 16), (byte)(rgb >> 8), (byte)rgb);
+ }
+
private static MediaColor Blend(MediaColor foreground, MediaColor background, double foregroundAmount)
{
double amount = Math.Clamp(foregroundAmount, 0, 1);
@@ -98,4 +184,18 @@ public static class ThemeService
double luminance = (0.299 * color.R) + (0.587 * color.G) + (0.114 * color.B);
return luminance < 140;
}
+
+ private sealed record ThemePalette(
+ MediaColor Window,
+ MediaColor Surface,
+ MediaColor Panel,
+ MediaColor Control,
+ MediaColor Hover,
+ MediaColor Text,
+ MediaColor Muted,
+ MediaColor Border,
+ MediaColor Accent,
+ MediaColor AccentSoft,
+ MediaColor Orange,
+ MediaColor OrangeSoft);
}