From ac0190ce6ea1845dc922bd1aebaa003aeb409e1b Mon Sep 17 00:00:00 2001 From: 22rw <88563451+22rw@users.noreply.github.com> Date: Thu, 13 Apr 2023 10:48:56 +0200 Subject: [PATCH 1/3] Create ColorUtils.cs The ColorUtils class is used to determine two things: the system theme (light/dark) and the system accent color. This information can later be used to style the context menu base on the users preferences. --- SteamShutdown/ColorUtils.cs | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 SteamShutdown/ColorUtils.cs diff --git a/SteamShutdown/ColorUtils.cs b/SteamShutdown/ColorUtils.cs new file mode 100644 index 0000000..5ac2672 --- /dev/null +++ b/SteamShutdown/ColorUtils.cs @@ -0,0 +1,41 @@ +using Microsoft.Win32; +using System; +using System.Drawing; + +namespace SteamShutdown +{ + public static class ColorUtils + { + public static bool SystemUsesLightTheme() + { + int sult = (Int32)Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", null); + return sult == 1; + } + + public static Color GetAccentColor() + { + const String DWM_KEY = @"Software\Microsoft\Windows\DWM"; + using (RegistryKey dwmKey = Registry.CurrentUser.OpenSubKey(DWM_KEY, RegistryKeyPermissionCheck.ReadSubTree)) + { + const String KEY_EX_MSG = "The \"HKCU\\" + DWM_KEY + "\" registry key does not exist."; + if (dwmKey is null) throw new InvalidOperationException(KEY_EX_MSG); + + Object accentColorObj = dwmKey.GetValue("AccentColor"); + if (accentColorObj is Int32 accentColorDword) + { + return ParseDWordColor(accentColorDword); + } + else + { + const String VALUE_EX_MSG = "The \"HKCU\\" + DWM_KEY + "\\AccentColor\" registry key value could not be parsed as an ABGR color."; + throw new InvalidOperationException(VALUE_EX_MSG); + } + } + } + + private static Color ParseDWordColor(Int32 color) + { + return Color.FromArgb((color >> 24) & 0xFF, (color >> 0) & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF); + } + } +} From 18ed7f1f9106b05c8d5bc2463e71734dfb1deded Mon Sep 17 00:00:00 2001 From: 22rw <88563451+22rw@users.noreply.github.com> Date: Thu, 13 Apr 2023 11:00:06 +0200 Subject: [PATCH 2/3] Create DarkRenderer.cs The DarkRenderer is used to render the context menu strip with darkmode friendly styling. Applying the renderer by using the ToolStrip.Renderer property results in a dark context menu. --- SteamShutdown/DarkRenderer.cs | 111 ++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 SteamShutdown/DarkRenderer.cs diff --git a/SteamShutdown/DarkRenderer.cs b/SteamShutdown/DarkRenderer.cs new file mode 100644 index 0000000..4a50ded --- /dev/null +++ b/SteamShutdown/DarkRenderer.cs @@ -0,0 +1,111 @@ +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; + +namespace SteamShutdown +{ + internal class DarkColorTable : ProfessionalColorTable + { + private Color _backColor = Color.FromArgb(43, 43, 43), + _itemSelectedColor = Color.FromArgb(65, 65, 65), + _borderColor = Color.FromArgb(128, 128, 128), + _separatorColor = Color.FromArgb(128, 128, 128), + _btnPressedColor = Color.FromArgb(110, 160, 230); + + //Border color + public override Color ToolStripBorder => _borderColor; + public override Color MenuBorder => _borderColor; + + //Background color + public override Color ToolStripDropDownBackground => _backColor; + public override Color MenuStripGradientBegin => _backColor; + public override Color MenuStripGradientEnd => _backColor; + public override Color CheckBackground => _backColor; + public override Color CheckSelectedBackground => _backColor; + public override Color CheckPressedBackground => _backColor; + public override Color ImageMarginGradientBegin => _backColor; + public override Color ImageMarginGradientMiddle => _backColor; + public override Color ImageMarginGradientEnd => _backColor; + + //Seperator color + public override Color SeparatorDark => _separatorColor; + public override Color SeparatorLight => _separatorColor; + + //MenuItem selected color + public override Color MenuItemSelected => _itemSelectedColor; + public override Color MenuItemBorder => _itemSelectedColor; + + //Item pressed color + public override Color ButtonPressedBorder => _btnPressedColor; + public override Color ButtonPressedGradientBegin => _btnPressedColor; + public override Color ButtonPressedGradientMiddle => _btnPressedColor; + public override Color ButtonPressedGradientEnd => _btnPressedColor; + + //StatusStrip color + public override Color StatusStripGradientBegin => _backColor; + public override Color StatusStripGradientEnd => _backColor; + } + + public class DarkRenderer : ToolStripProfessionalRenderer + { + private Color _accentColor; + + public DarkRenderer(Color accentColor) : base(new DarkColorTable()) + { + _accentColor = accentColor; + } + + //Make sure the text is rendered with white text color + protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e) + { + e.TextColor = Color.White; + base.OnRenderItemText(e); + } + + //Rendering the arrow for the actions submenu + protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e) + { + e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; + + //Rectangle for the arrows bounding box, a smaller box for a cleaner menu look + var r = new Rectangle(e.ArrowRectangle.Location, e.ArrowRectangle.Size); + r.Inflate(-3, -6); + + //The arrow consists of two lines, defined by 3 points + e.Graphics.DrawLines(Pens.White, new Point[3] { + new Point(r.Left, r.Top), + new Point(r.Right, r.Top + r.Height /2), + new Point(r.Left, r.Top+ r.Height) + }); + } + + //Rendering the checkmark and its surrounding box + protected override void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e) + { + if (e != null) + { + e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; + + var rectImage = new Rectangle(e.ImageRectangle.Location, e.ImageRectangle.Size); + rectImage.X += 4; + //rectImage.Inflate(-1, -1); + + using (var p = new Pen(_accentColor, 1)) + e.Graphics.DrawRectangle(p, rectImage); + + rectImage.Width -= 6; + rectImage.Height -= 8; + + rectImage.X += 3; + rectImage.Y += 4; + + using (var p = new Pen(Color.White, 1)) + { + e.Graphics.DrawLines(p, new Point[] { new Point(rectImage.Left, rectImage.Bottom - (int)(rectImage.Height / 2)), new Point(rectImage.Left + (int)(rectImage.Width / 3), rectImage.Bottom), new Point(rectImage.Right, rectImage.Top) }); + } + } + else + base.OnRenderItemCheck(e); + } + } +} From fab10f98d9353338158c4ccdc31ae1ba8aee892a Mon Sep 17 00:00:00 2001 From: 22rw <88563451+22rw@users.noreply.github.com> Date: Thu, 13 Apr 2023 11:06:43 +0200 Subject: [PATCH 3/3] Updated NotifyIcon to use DarkRenderer If the system is using darkmode, the DarkRenderer will be applied to the ToolStrip's Renderer property. --- SteamShutdown/CustomApplicationContext.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/SteamShutdown/CustomApplicationContext.cs b/SteamShutdown/CustomApplicationContext.cs index 7bd0f25..7882ebd 100644 --- a/SteamShutdown/CustomApplicationContext.cs +++ b/SteamShutdown/CustomApplicationContext.cs @@ -1,4 +1,4 @@ -using SteamShutdown.Actions; +using SteamShutdown.Actions; using System; using System.Linq; using System.Reflection; @@ -171,6 +171,10 @@ private void InitializeContext() }; NotifyIcon.MouseUp += NotifyIcon_MouseUp; NotifyIcon.ContextMenuStrip.Opening += ContextMenuStrip_Opening; + NotifyIcon.ContextMenuStrip.RenderMode = ToolStripRenderMode.Professional; + if(!ColorUtils.SystemUsesLightTheme()) + NotifyIcon.ContextMenuStrip.Renderer = new DarkRenderer(ColorUtils.GetAccentColor()); + NotifyIcon.ShowBalloonTip(2000, "Hello", "You find me in the taskbar.", ToolTipIcon.Info); }