diff --git a/CHANGELIST.md b/CHANGELIST.md
index dfd905b0..bba492fc 100644
--- a/CHANGELIST.md
+++ b/CHANGELIST.md
@@ -26,6 +26,7 @@
- Blindly assume the path exists
- Add CLI status output on runtime
- Ensure tracks are assigned in Aaru
+- Custom theme colors
### 3.2.0 (2024-06-20)
diff --git a/MPF.Frontend/Options.cs b/MPF.Frontend/Options.cs
index f550f4a9..d85d8cc6 100644
--- a/MPF.Frontend/Options.cs
+++ b/MPF.Frontend/Options.cs
@@ -92,6 +92,24 @@ namespace MPF.Frontend
set { Settings["EnablePurpMode"] = value.ToString(); }
}
+ ///
+ /// Custom color setting
+ ///
+ public string? CustomBackgroundColor
+ {
+ get { return GetStringSetting(Settings, "CustomBackgroundColor", null); }
+ set { Settings["CustomBackgroundColor"] = value; }
+ }
+
+ ///
+ /// Custom color setting
+ ///
+ public string? CustomTextColor
+ {
+ get { return GetStringSetting(Settings, "CustomTextColor", null); }
+ set { Settings["CustomTextColor"] = value; }
+ }
+
///
/// Check for updates on startup
///
diff --git a/MPF.UI/Themes/CustomTheme.cs b/MPF.UI/Themes/CustomTheme.cs
new file mode 100644
index 00000000..d565cf27
--- /dev/null
+++ b/MPF.UI/Themes/CustomTheme.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Windows.Media;
+
+namespace MPF.UI.Themes
+{
+ ///
+ /// Custom colour theme
+ ///
+ public sealed class CustomTheme : Theme
+ {
+ public CustomTheme(string? backgroundColor, string? foregroundColor)
+ {
+ // Convert hex colors to byte values
+ byte backgroundR;
+ byte backgroundG;
+ byte backgroundB;
+ byte foregroundR;
+ byte foregroundG;
+ byte foregroundB;
+ try
+ {
+ backgroundR = Convert.ToByte(backgroundColor!.Substring(0, 2), 16);
+ backgroundG = Convert.ToByte(backgroundColor!.Substring(2, 2), 16);
+ backgroundB = Convert.ToByte(backgroundColor!.Substring(4, 2), 16);
+ foregroundR = Convert.ToByte(foregroundColor!.Substring(0, 2), 16);
+ foregroundG = Convert.ToByte(foregroundColor!.Substring(2, 2), 16);
+ foregroundB = Convert.ToByte(foregroundColor!.Substring(4, 2), 16);
+ }
+ catch
+ {
+ return;
+ }
+
+ // Setup needed brushes
+ var backgroundBrush = new SolidColorBrush { Color = Color.FromArgb(0xff, backgroundR, backgroundG, backgroundB) };
+ var foregroundBrush = new SolidColorBrush { Color = Color.FromArgb(0xff, foregroundR, foregroundG, foregroundB) };
+
+ // Handle application-wide resources
+ this.ActiveBorderBrush = foregroundBrush;
+ this.ControlBrush = backgroundBrush;
+ this.ControlTextBrush = foregroundBrush;
+ this.GrayTextBrush = foregroundBrush;
+ this.WindowBrush = backgroundBrush;
+ this.WindowTextBrush = foregroundBrush;
+
+ // Handle Button-specific resources
+ this.Button_Disabled_Background = backgroundBrush;
+ this.Button_MouseOver_Background = backgroundBrush;
+ this.Button_Pressed_Background = backgroundBrush;
+ this.Button_Static_Background = backgroundBrush;
+
+ // Handle ComboBox-specific resources
+ this.ComboBox_Disabled_Background = backgroundBrush;
+ this.ComboBox_Disabled_Editable_Background = backgroundBrush;
+ this.ComboBox_Disabled_Editable_Button_Background = backgroundBrush;
+ this.ComboBox_MouseOver_Background = backgroundBrush;
+ this.ComboBox_MouseOver_Editable_Background = backgroundBrush;
+ this.ComboBox_MouseOver_Editable_Button_Background = backgroundBrush;
+ this.ComboBox_Pressed_Background = backgroundBrush;
+ this.ComboBox_Pressed_Editable_Background = backgroundBrush;
+ this.ComboBox_Pressed_Editable_Button_Background = backgroundBrush;
+ this.ComboBox_Static_Background = backgroundBrush;
+ this.ComboBox_Static_Editable_Background = backgroundBrush;
+ this.ComboBox_Static_Editable_Button_Background = backgroundBrush;
+
+ // Handle CustomMessageBox-specific resources
+ this.CustomMessageBox_Static_Background = backgroundBrush;
+
+ // Handle MenuItem-specific resources
+ this.MenuItem_SubMenu_Background = backgroundBrush;
+ this.MenuItem_SubMenu_Border = Brushes.DarkGray;
+
+ // Handle ProgressBar-specific resources
+ this.ProgressBar_Background = backgroundBrush;
+
+ // Handle ScrollViewer-specific resources
+ this.ScrollViewer_ScrollBar_Background = backgroundBrush;
+
+ // Handle TabItem-specific resources
+ this.TabItem_Selected_Background = backgroundBrush;
+ this.TabItem_Static_Background = backgroundBrush;
+ this.TabItem_Static_Border = Brushes.DarkGray;
+
+ // Handle TextBox-specific resources
+ this.TextBox_Static_Background = backgroundBrush;
+ }
+ }
+}
diff --git a/MPF.UI/Themes/PurpModeTheme.cs b/MPF.UI/Themes/PurpModeTheme.cs
deleted file mode 100644
index fe838799..00000000
--- a/MPF.UI/Themes/PurpModeTheme.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using System.Windows.Media;
-
-namespace MPF.UI.Themes
-{
- ///
- /// Default purple-mode theme
- ///
- public sealed class PurpModeTheme : Theme
- {
- public PurpModeTheme()
- {
- // Setup needed brushes
- var darkPurpBrush = new SolidColorBrush { Color = Color.FromArgb(0xff, 0x11, 0x11, 0x11) };
- var lightPurpBrush = new SolidColorBrush { Color = Color.FromArgb(0xff, 0x9a, 0x5e, 0xc0) };
-
- // Handle application-wide resources
- this.ActiveBorderBrush = lightPurpBrush;
- this.ControlBrush = darkPurpBrush;
- this.ControlTextBrush = lightPurpBrush;
- this.GrayTextBrush = lightPurpBrush;
- this.WindowBrush = darkPurpBrush;
- this.WindowTextBrush = lightPurpBrush;
-
- // Handle Button-specific resources
- this.Button_Disabled_Background = darkPurpBrush;
- this.Button_MouseOver_Background = darkPurpBrush;
- this.Button_Pressed_Background = darkPurpBrush;
- this.Button_Static_Background = darkPurpBrush;
-
- // Handle ComboBox-specific resources
- this.ComboBox_Disabled_Background = darkPurpBrush;
- this.ComboBox_Disabled_Editable_Background = darkPurpBrush;
- this.ComboBox_Disabled_Editable_Button_Background = darkPurpBrush;
- this.ComboBox_MouseOver_Background = darkPurpBrush;
- this.ComboBox_MouseOver_Editable_Background = darkPurpBrush;
- this.ComboBox_MouseOver_Editable_Button_Background = darkPurpBrush;
- this.ComboBox_Pressed_Background = darkPurpBrush;
- this.ComboBox_Pressed_Editable_Background = darkPurpBrush;
- this.ComboBox_Pressed_Editable_Button_Background = darkPurpBrush;
- this.ComboBox_Static_Background = darkPurpBrush;
- this.ComboBox_Static_Editable_Background = darkPurpBrush;
- this.ComboBox_Static_Editable_Button_Background = darkPurpBrush;
-
- // Handle CustomMessageBox-specific resources
- this.CustomMessageBox_Static_Background = darkPurpBrush;
-
- // Handle MenuItem-specific resources
- this.MenuItem_SubMenu_Background = darkPurpBrush;
- this.MenuItem_SubMenu_Border = Brushes.DarkGray;
-
- // Handle ProgressBar-specific resources
- this.ProgressBar_Background = darkPurpBrush;
-
- // Handle ScrollViewer-specific resources
- this.ScrollViewer_ScrollBar_Background = darkPurpBrush;
-
- // Handle TabItem-specific resources
- this.TabItem_Selected_Background = darkPurpBrush;
- this.TabItem_Static_Background = darkPurpBrush;
- this.TabItem_Static_Border = Brushes.DarkGray;
-
- // Handle TextBox-specific resources
- this.TextBox_Static_Background = darkPurpBrush;
- }
- }
-}
diff --git a/MPF.UI/Windows/MainWindow.xaml.cs b/MPF.UI/Windows/MainWindow.xaml.cs
index 2300d123..e5cefeef 100644
--- a/MPF.UI/Windows/MainWindow.xaml.cs
+++ b/MPF.UI/Windows/MainWindow.xaml.cs
@@ -399,14 +399,65 @@ namespace MPF.UI.Windows
if (MainViewModel.Options.EnableDarkMode)
theme = new DarkModeTheme();
else if (MainViewModel.Options.EnablePurpMode)
- theme = new PurpModeTheme();
+ theme = new CustomTheme("111111", "9A5EC0");
+ else if (IsHexColor(MainViewModel.Options.CustomBackgroundColor) && IsHexColor(MainViewModel.Options.CustomTextColor))
+ theme = new CustomTheme(MainViewModel.Options.CustomBackgroundColor, MainViewModel.Options.CustomTextColor);
else
theme = new LightModeTheme();
theme.Apply();
}
-#endregion
+ ///
+ /// Check whether a string represents a valid hex color
+ ///
+ public static bool IsHexColor(string? color)
+ {
+ if (string.IsNullOrWhiteSpace(color))
+ return false;
+
+ if (color!.Length == 7 && color[0] == '#')
+ color = color.Substring(1);
+
+ if (color.Length != 6)
+ return false;
+
+ for (int i = 0; i < color.Length; i++)
+ {
+ switch (color[i])
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ continue;
+ default:
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ #endregion
#region Event Handlers