From 045d5cac5259906bc9c53faef1f619c1f27adcb1 Mon Sep 17 00:00:00 2001 From: mntone Date: Thu, 6 Aug 2020 22:27:41 +0900 Subject: [PATCH 1/4] Improve notification icon on light theme. --- source/SylphyHorn/.assets/tasktray-light.ico | Bin 0 -> 12926 bytes source/SylphyHorn/ApplicationPreparation.cs | 7 +++- source/SylphyHorn/SylphyHorn.csproj | 1 + source/SylphyHorn/UI/DynamicInfoTrayIcon.cs | 26 +++++++++----- source/SylphyHorn/UI/TaskTrayIcon.cs | 36 +++++++++++++++---- 5 files changed, 55 insertions(+), 15 deletions(-) create mode 100644 source/SylphyHorn/.assets/tasktray-light.ico diff --git a/source/SylphyHorn/.assets/tasktray-light.ico b/source/SylphyHorn/.assets/tasktray-light.ico new file mode 100644 index 0000000000000000000000000000000000000000..d30ab0829cf130c3f9a7818984e52cbfe72e8dd7 GIT binary patch literal 12926 zcmeHNyKWRg5Nu04WSPK$B^+4d$jBgs%o6`VOh`t61c8HdNXTap5)vWA2OuIu1pWgq zK-d=2F4L?^S8mtr%whpPJY$Wzr@E?UuD#ydJK1c_F5Bj&r8u)km&_iR+1A#uetpI4 zO~SUfhxPMyv!@$oJ3GVr(>1d$ac8q({WAG{ylHkTd89Ns4tNT-n|#js=+pUXX6|LU zwlpFK`?)UGlovSAVLeq3e$y|z%)MBmCo|@Fj_E9}om4GX;IIx=)UZ}@j04Y7RKEWN z>(fbn9$Yp1dT4h0o7v;we?+FCQ<`J*$mV9VlHBv3|-R0M4%+ zip%@CkK*L~!DSC%(8M)x6(bkE%#9qb#XNGG-|zi?(brY!AD=T`19&Yef(t#?%vBiI zIj`aJUNt%UU%0&Q7wJ7`m!n=jfg!So!nj__l^z%(dnk471#hr+mC%9S1%B6}!|>!n=jfg!So!nj__ zl^z%(dnkyhW;pWaWMF?W`!xbjxmRNow5c{gl@JCHtWEO;K- z+QR30e{4SQ^~Z0gOz7al-k$sCnpv8=Y2tIuzW+l;8?6Ja1GOE<-Jhr8wfjZh)O~XN z=&ROI>l|Q;UxTq8IbzfUNAx=9kzccBJ#xgTSGdBWZfvde3Ud#ID{O4PSW|k1wf$TF z)pen;c#Vy%m0n@(`fJypx-JwpcD==#(kqOwKdy5gy=&I2M~)cvz!CXOkz2b~UW}D| wz&&d$?USpYbzkUn{ta&(xX>L)-xbKwnazE^n|JqpzMpqbrcCJIBR5IlPs2>sv;Y7A literal 0 HcmV?d00001 diff --git a/source/SylphyHorn/ApplicationPreparation.cs b/source/SylphyHorn/ApplicationPreparation.cs index 830f1b6..a649706 100644 --- a/source/SylphyHorn/ApplicationPreparation.cs +++ b/source/SylphyHorn/ApplicationPreparation.cs @@ -11,6 +11,8 @@ using SylphyHorn.Services; using SylphyHorn.UI; using SylphyHorn.UI.Bindings; +using System.Runtime.InteropServices; +using Windows.System; namespace SylphyHorn { @@ -114,17 +116,20 @@ public TaskTrayIcon CreateTaskTrayIcon() if (this._taskTrayIcon == null) { const string iconUri = "pack://application:,,,/SylphyHorn;Component/.assets/tasktray.ico"; + const string lightIconUri = "pack://application:,,,/SylphyHorn;Component/.assets/tasktray-light.ico"; if (!Uri.TryCreate(iconUri, UriKind.Absolute, out var uri)) return null; + if (!Uri.TryCreate(lightIconUri, UriKind.Absolute, out var lightUri)) return null; var icon = IconHelper.GetIconFromResource(uri); + var lightIcon = IconHelper.GetIconFromResource(lightUri); var menus = new[] { new TaskTrayIconItem(Resources.TaskTray_Menu_Settings, ShowSettings, () => Application.Args.CanSettings), new TaskTrayIconItem(Resources.TaskTray_Menu_Exit, this._shutdownAction), }; - this._taskTrayIcon = new TaskTrayIcon(icon, menus); + this._taskTrayIcon = new TaskTrayIcon(icon, lightIcon, menus); } return this._taskTrayIcon; diff --git a/source/SylphyHorn/SylphyHorn.csproj b/source/SylphyHorn/SylphyHorn.csproj index a2a7cce..c7bdb6c 100644 --- a/source/SylphyHorn/SylphyHorn.csproj +++ b/source/SylphyHorn/SylphyHorn.csproj @@ -367,6 +367,7 @@ + Always Designer diff --git a/source/SylphyHorn/UI/DynamicInfoTrayIcon.cs b/source/SylphyHorn/UI/DynamicInfoTrayIcon.cs index d4604f2..9c333a8 100644 --- a/source/SylphyHorn/UI/DynamicInfoTrayIcon.cs +++ b/source/SylphyHorn/UI/DynamicInfoTrayIcon.cs @@ -1,6 +1,7 @@ using System; using System.Drawing; using System.Windows.Controls; +using MetroRadiance.Platform; using SylphyHorn.Interop; namespace SylphyHorn.UI @@ -12,11 +13,12 @@ public class DynamicInfoTrayIcon : IDisposable private const float _verticalFontSize = 6; private readonly FontFamily _fontFamily; - private readonly Brush _brush; + private readonly Color? _color; private Font _font; + private Brush _brush; private Orientation _lastOrientation; - public DynamicInfoTrayIcon(int totalDesktopCount, FontFamily fontFamily = null, Color? color = null) + public DynamicInfoTrayIcon(int totalDesktopCount, Theme theme, FontFamily fontFamily = null, Color? color = null) { var currentOrientation = this._lastOrientation = GetOrientation(totalDesktopCount); var fontSize = GetFontSize(currentOrientation); @@ -24,12 +26,8 @@ public DynamicInfoTrayIcon(int totalDesktopCount, FontFamily fontFamily = null, this._fontFamily = fontFamily ?? new FontFamily(_defaultFontFamilyName); this._font = new Font(this._fontFamily, fontSize, FontStyle.Bold); - if (!color.HasValue) - { - color = Color.White; - } - - this._brush = new SolidBrush(color.Value); + this._color = color; + this._brush = new SolidBrush(color.GetValueOrDefault(GetForegroundColor(theme))); } public Icon GetDesktopInfoIcon(int currentDesktop, int totalDesktopCount) @@ -59,6 +57,12 @@ private void UpdateFontSize(Orientation newOrientation) this._font = new Font(this._fontFamily, fontSize, FontStyle.Bold); } + public void UpdateBrush(Theme theme) + { + this._brush?.Dispose(); + this._brush = new SolidBrush(this._color.GetValueOrDefault(GetForegroundColor(theme))); + } + // consolidate two methods below? private Bitmap DrawHorizontalInfo(int currentDesktop, int totalDesktopCount) { @@ -71,6 +75,7 @@ private Bitmap DrawHorizontalInfo(int currentDesktop, int totalDesktopCount) using (var graphics = Graphics.FromImage(bitmap)) { + graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; graphics.DrawString(stringToDraw, this._font, this._brush, offset); } @@ -114,6 +119,11 @@ private static float GetFontSize(Orientation orientation) return orientation == Orientation.Horizontal ? _horizontalFontSize : _verticalFontSize; } + private static Color GetForegroundColor(Theme theme) + { + return theme == Theme.Light ? Color.Black : Color.White; + } + private static PointF GetHorizontalStringOffset() { return new PointF(-2, 0); diff --git a/source/SylphyHorn/UI/TaskTrayIcon.cs b/source/SylphyHorn/UI/TaskTrayIcon.cs index 4000305..278d1aa 100644 --- a/source/SylphyHorn/UI/TaskTrayIcon.cs +++ b/source/SylphyHorn/UI/TaskTrayIcon.cs @@ -1,7 +1,9 @@ using System; using System.Drawing; using System.Linq; +using System.Windows; using System.Windows.Forms; +using MetroRadiance.Platform; using SylphyHorn.Properties; using SylphyHorn.Serialization; using WindowsDesktop; @@ -12,19 +14,22 @@ public class TaskTrayIcon : IDisposable { private Icon _icon; private readonly Icon _defaultIcon; + private readonly Icon _lightIcon; private readonly TaskTrayIconItem[] _items; private NotifyIcon _notifyIcon; private DynamicInfoTrayIcon _infoIcon; public string Text { get; set; } = ProductInfo.Title; - public TaskTrayIcon(Icon icon, TaskTrayIconItem[] items) + public TaskTrayIcon(Icon icon, Icon lightIcon, TaskTrayIconItem[] items) { this._defaultIcon = icon; + this._lightIcon = lightIcon; - this._icon = icon; + this._icon = WindowsTheme.SystemTheme.Current == Theme.Light ? this._lightIcon : this._defaultIcon; this._items = items; + WindowsTheme.SystemTheme.Changed += this.OnSystemThemeChanged; VirtualDesktop.CurrentChanged += this.OnCurrentDesktopChanged; } @@ -65,12 +70,14 @@ public void Reload(VirtualDesktop desktop = null) { this.UpdateWithDesktopInfo(desktop ?? VirtualDesktop.Current); } - else if (this._icon != this._defaultIcon) + else if (this._icon != this._defaultIcon && this._icon != this._lightIcon) { this._infoIcon?.Dispose(); this._infoIcon = null; - this.ChangeIcon(this._defaultIcon); + this.ChangeIcon(WindowsTheme.SystemTheme.Current == Theme.Light + ? this._lightIcon + : this._defaultIcon); } } @@ -82,7 +89,7 @@ private void UpdateWithDesktopInfo(VirtualDesktop currentDesktop) if (this._infoIcon == null) { - this._infoIcon = new DynamicInfoTrayIcon(totalDesktopCount); + this._infoIcon = new DynamicInfoTrayIcon(totalDesktopCount, WindowsTheme.SystemTheme.Current); } this.ChangeIcon(this._infoIcon.GetDesktopInfoIcon(currentDesktopIndex, totalDesktopCount)); @@ -93,9 +100,24 @@ private void OnCurrentDesktopChanged(object sender, VirtualDesktopChangedEventAr this.Reload(e.NewDesktop); } + private void OnSystemThemeChanged(object sender, Theme e) + { + if (Settings.General.TrayShowDesktop) + { + this._infoIcon.UpdateBrush(e); + this.UpdateWithDesktopInfo(VirtualDesktop.Current); + } + else + { + this.ChangeIcon(e == Theme.Light + ? this._lightIcon + : this._defaultIcon); + } + } + private void ChangeIcon(Icon newIcon) { - if (this._icon != this._defaultIcon) + if (this._icon != this._defaultIcon && this._icon != this._lightIcon) { this._icon?.Dispose(); } @@ -107,8 +129,10 @@ private void ChangeIcon(Icon newIcon) public void Dispose() { this._notifyIcon?.Dispose(); + this._lightIcon?.Dispose(); this._icon?.Dispose(); + WindowsTheme.SystemTheme.Changed -= this.OnSystemThemeChanged; VirtualDesktop.CurrentChanged -= this.OnCurrentDesktopChanged; } } From 179f97b156954d3d55bea0fb9fc58482691358df Mon Sep 17 00:00:00 2001 From: mntone Date: Fri, 7 Aug 2020 11:26:01 +0900 Subject: [PATCH 2/4] Improve notification icon rendering. --- source/SylphyHorn/ApplicationPreparation.cs | 3 + source/SylphyHorn/Interop/ColorHelper.cs | 12 + source/SylphyHorn/Interop/IconHelper.cs | 68 +++- source/SylphyHorn/SylphyHorn.csproj | 8 + source/SylphyHorn/UI/DynamicInfoTrayIcon.cs | 203 ++++++----- source/SylphyHorn/UI/TaskTrayIcon.cs | 41 ++- source/SylphyHorn/UI/TaskTrayTestWindow.xaml | 345 ++++++++++++++++++ .../SylphyHorn/UI/TaskTrayTestWindow.xaml.cs | 151 ++++++++ 8 files changed, 716 insertions(+), 115 deletions(-) create mode 100644 source/SylphyHorn/Interop/ColorHelper.cs create mode 100644 source/SylphyHorn/UI/TaskTrayTestWindow.xaml create mode 100644 source/SylphyHorn/UI/TaskTrayTestWindow.xaml.cs diff --git a/source/SylphyHorn/ApplicationPreparation.cs b/source/SylphyHorn/ApplicationPreparation.cs index a649706..abd5e87 100644 --- a/source/SylphyHorn/ApplicationPreparation.cs +++ b/source/SylphyHorn/ApplicationPreparation.cs @@ -127,6 +127,9 @@ public TaskTrayIcon CreateTaskTrayIcon() { new TaskTrayIconItem(Resources.TaskTray_Menu_Settings, ShowSettings, () => Application.Args.CanSettings), new TaskTrayIconItem(Resources.TaskTray_Menu_Exit, this._shutdownAction), +#if DEBUG + new TaskTrayIconItem("Tasktray Icon Test", () => new TaskTrayTestWindow().Show()), +#endif }; this._taskTrayIcon = new TaskTrayIcon(icon, lightIcon, menus); diff --git a/source/SylphyHorn/Interop/ColorHelper.cs b/source/SylphyHorn/Interop/ColorHelper.cs new file mode 100644 index 0000000..e1a643f --- /dev/null +++ b/source/SylphyHorn/Interop/ColorHelper.cs @@ -0,0 +1,12 @@ +using System.Windows.Media; + +namespace SylphyHorn.Interop +{ + public static class ColorHelper + { + public static System.Drawing.Color ToGDIColor(this Color color) + { + return System.Drawing.Color.FromArgb(color.A, color.R, color.G, color.B); + } + } +} diff --git a/source/SylphyHorn/Interop/IconHelper.cs b/source/SylphyHorn/Interop/IconHelper.cs index a50d74c..f793730 100644 --- a/source/SylphyHorn/Interop/IconHelper.cs +++ b/source/SylphyHorn/Interop/IconHelper.cs @@ -1,6 +1,7 @@ using System; using System.Drawing; -using System.Windows; +using System.IO; +using System.Windows.Media.Imaging; using MetroRadiance.Interop; namespace SylphyHorn.Interop @@ -20,6 +21,35 @@ public static Icon GetIconFromResource(Uri uri) } } + public static RenderTargetBitmap ToRenderTargetBitmap(this System.Windows.Media.DrawingVisual drawingVisual, Size size, Dpi? dpi = null) + { + var imageDpi = dpi ?? new Dpi(96, 96); + var imageSize = ScaleSizeByDpi(size, imageDpi); + var renderTarget = new RenderTargetBitmap(imageSize.Width, imageSize.Height, imageDpi.X, imageDpi.Y, System.Windows.Media.PixelFormats.Default); + renderTarget.Render(drawingVisual); + return renderTarget; + } + + public static Bitmap ToBitmap(this System.Windows.Media.DrawingVisual drawingVisual, Size size, Dpi? dpi = null, Color? transparentColor = null) + { + var renderTarget = drawingVisual.ToRenderTargetBitmap(size, dpi); + var encoder = new BmpBitmapEncoder(); + var frame = BitmapFrame.Create(renderTarget); + encoder.Frames.Add(frame); + + using (var stream = new MemoryStream()) + { + encoder.Save(stream); + + var bitmap = new Bitmap(stream, useIcm: true); + if (transparentColor.HasValue) + { + bitmap.MakeTransparent(transparentColor.Value); + } + return bitmap; + } + } + public static Icon ToIcon(this Bitmap bitmap) { var iconHandle = bitmap.GetHicon(); @@ -28,21 +58,43 @@ public static Icon ToIcon(this Bitmap bitmap) return icon; } +#if DEBUG + internal static BitmapSource ToBitmapSource(this Icon icon) + { + using (var bitmap = icon.ToBitmap()) + using (var stream = new MemoryStream()) + { + bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png); + stream.Seek(0, SeekOrigin.Begin); + + var bitmapSource = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); + return bitmapSource; + } + } +#endif + private static Icon ScaleIconToDpi(Icon targetIcon) { - var dpi = GetMonitorDpi(); + var dpi = GetSystemDpi(); - return new Icon(targetIcon, new System.Drawing.Size((int)(16 * dpi.ScaleX), (int)(16 * dpi.ScaleY))); + return new Icon(targetIcon, new Size((int)(16 * dpi.ScaleX), (int)(16 * dpi.ScaleY))); } - public static System.Windows.Size GetIconSize() + public static Size GetIconSize() { - var dpi = GetMonitorDpi(); + return new Size( + (int)System.Windows.SystemParameters.SmallIconWidth, + (int)System.Windows.SystemParameters.SmallIconHeight); + } - return new System.Windows.Size(SystemParameters.SmallIconWidth * dpi.ScaleX, SystemParameters.SmallIconHeight * dpi.ScaleY); + private static Size ScaleSizeByDpi(Size size, Dpi dpi) + { + return new Size( + (int)Math.Round(size.Width * dpi.ScaleX), + (int)Math.Round(size.Height * dpi.ScaleY)); } - - private static Dpi GetMonitorDpi() + + public static Dpi GetSystemDpi() { return PerMonitorDpi.GetDpi(IntPtr.Zero); } diff --git a/source/SylphyHorn/SylphyHorn.csproj b/source/SylphyHorn/SylphyHorn.csproj index c7bdb6c..5324de9 100644 --- a/source/SylphyHorn/SylphyHorn.csproj +++ b/source/SylphyHorn/SylphyHorn.csproj @@ -230,6 +230,7 @@ + @@ -282,6 +283,9 @@ SettingsWindow.xaml + + TaskTrayTestWindow.xaml + Designer MSBuild:Compile @@ -324,6 +328,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + diff --git a/source/SylphyHorn/UI/DynamicInfoTrayIcon.cs b/source/SylphyHorn/UI/DynamicInfoTrayIcon.cs index 9c333a8..628b5df 100644 --- a/source/SylphyHorn/UI/DynamicInfoTrayIcon.cs +++ b/source/SylphyHorn/UI/DynamicInfoTrayIcon.cs @@ -1,112 +1,140 @@ using System; -using System.Drawing; +using System.Globalization; +using System.Windows; using System.Windows.Controls; +using System.Windows.Media; +using MetroRadiance.Interop; using MetroRadiance.Platform; using SylphyHorn.Interop; namespace SylphyHorn.UI { - public class DynamicInfoTrayIcon : IDisposable + public class DynamicInfoTrayIcon { private const string _defaultFontFamilyName = "Segoe UI"; - private const float _horizontalFontSize = 7; - private const float _verticalFontSize = 6; + private const double _horizontalFontSize = 9; + private const double _verticalFontSize = 8; + private const double _verticalSpacing = -0.5; + private const double _triggerFontSizeInEffectivePixels = 14.0; + private const double _minFontSize = 4.0; - private readonly FontFamily _fontFamily; - private readonly Color? _color; - private Font _font; - private Brush _brush; - private Orientation _lastOrientation; + private static readonly SolidColorBrush _lightForegroundBrush = new SolidColorBrush(ImmersiveColor.GetColorByTypeName(ImmersiveColorNames.SystemTextLightTheme)); + private static readonly SolidColorBrush _lightBackgroundBrush = new SolidColorBrush(ImmersiveColor.GetColorByTypeName(ImmersiveColorNames.SystemBackgroundLightTheme)); + private static readonly SolidColorBrush _darkForegroundBrush = new SolidColorBrush(ImmersiveColor.GetColorByTypeName(ImmersiveColorNames.SystemTextDarkTheme)); + private static readonly SolidColorBrush _darkBackgroundBrush = new SolidColorBrush(ImmersiveColor.GetColorByTypeName(ImmersiveColorNames.SystemBackgroundDarkTheme)); - public DynamicInfoTrayIcon(int totalDesktopCount, Theme theme, FontFamily fontFamily = null, Color? color = null) - { - var currentOrientation = this._lastOrientation = GetOrientation(totalDesktopCount); - var fontSize = GetFontSize(currentOrientation); + private static readonly Typeface _defaultFont = new Typeface(new FontFamily(_defaultFontFamilyName), FontStyles.Normal, FontWeights.Bold, FontStretches.Normal); - this._fontFamily = fontFamily ?? new FontFamily(_defaultFontFamilyName); - this._font = new Font(this._fontFamily, fontSize, FontStyle.Bold); + private SolidColorBrush _foregroundBrush; + private SolidColorBrush _backgroundBrush; + private Dpi? _dpi; - this._color = color; - this._brush = new SolidBrush(color.GetValueOrDefault(GetForegroundColor(theme))); + public DynamicInfoTrayIcon(Theme theme, bool colorPrevalence, Dpi? dpi = null) + { + (this._foregroundBrush, this._backgroundBrush) = GetThemeBrushes(theme, colorPrevalence); + this._dpi = dpi; } - public Icon GetDesktopInfoIcon(int currentDesktop, int totalDesktopCount) + public System.Drawing.Icon GetDesktopInfoIcon(int currentDesktop, int totalDesktopCount) { - var currentOrientation = GetOrientation(totalDesktopCount); - - if (currentOrientation != this._lastOrientation) - { - this.UpdateFontSize(currentOrientation); - } - - this._lastOrientation = currentOrientation; - - using (var iconBitmap = currentOrientation == Orientation.Horizontal - ? this.DrawHorizontalInfo(currentDesktop, totalDesktopCount) - : this.DrawVerticalInfo(currentDesktop, totalDesktopCount)) + using (var iconBitmap = this.DrawInfo(currentDesktop, totalDesktopCount)) { return iconBitmap.ToIcon(); } } - private void UpdateFontSize(Orientation newOrientation) - { - var fontSize = GetFontSize(newOrientation); - - this._font?.Dispose(); - this._font = new Font(this._fontFamily, fontSize, FontStyle.Bold); - } - - public void UpdateBrush(Theme theme) + public void UpdateBrush(Theme theme, bool colorPrevalence) { - this._brush?.Dispose(); - this._brush = new SolidBrush(this._color.GetValueOrDefault(GetForegroundColor(theme))); + (this._foregroundBrush, this._backgroundBrush) = GetThemeBrushes(theme, colorPrevalence); } // consolidate two methods below? - private Bitmap DrawHorizontalInfo(int currentDesktop, int totalDesktopCount) + private System.Drawing.Bitmap DrawInfo(int currentDesktop, int totalDesktopCount) { var iconSize = IconHelper.GetIconSize(); - var bitmap = new Bitmap((int)iconSize.Width, (int)iconSize.Height); - - var stringToDraw = $"{currentDesktop}/{totalDesktopCount}"; + var dpi = this._dpi ?? GetDpi(); + var scale = dpi.X / 96.0; - var offset = GetHorizontalStringOffset(); - - using (var graphics = Graphics.FromImage(bitmap)) + var drawingVisual = new DrawingVisual(); + using (var context = drawingVisual.RenderOpen()) { - graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; - graphics.DrawString(stringToDraw, this._font, this._brush, offset); + context.DrawRectangle(this._backgroundBrush, null, new Rect(0.0, 0.0, iconSize.Width, iconSize.Height)); + + var currentOrientation = GetOrientation(totalDesktopCount); + if (currentOrientation == Orientation.Horizontal) + { + this.DrawHorizontalInfo(context, iconSize, scale, currentDesktop, totalDesktopCount); + } + else + { + this.DrawVerticalInfo(context, iconSize, scale, currentDesktop, totalDesktopCount); + } } - return bitmap; + return drawingVisual.ToBitmap( + iconSize, + dpi, + this._backgroundBrush.Color.ToGDIColor()); } - private Bitmap DrawVerticalInfo(int currentDesktop, int totalDesktops) + private void DrawHorizontalInfo(DrawingContext context, System.Drawing.Size size, double scale, int currentDesktop, int totalDesktopCount) { - var iconSize = IconHelper.GetIconSize(); - var bitmap = new Bitmap((int)iconSize.Width, (int)iconSize.Height); + var stringToDraw = $"{currentDesktop}/{totalDesktopCount}"; + var formattedText = this.GetFormattedTextFromText(stringToDraw, _horizontalFontSize, size, scale); + formattedText.LineHeight = Math.Min(_horizontalFontSize, size.Height); - var firstOffset = GetFirstVerticalStringOffset(currentDesktop); - var secondOffset = GetSecondVerticalStringOffset(totalDesktops, bitmap.Height); + var offsetY = Math.Floor(0.5 * (size.Height - formattedText.Extent)); + context.DrawText(formattedText, new Point(0, offsetY)); + } + + private void DrawVerticalInfo(DrawingContext context, System.Drawing.Size size, double scale, int currentDesktop, int totalDesktopCount, double? currentFontSize = null) + { + var fontSize = currentFontSize ?? _verticalFontSize; + var scaleable = (fontSize - 1) >= _minFontSize; + var lineHeight = Math.Min(fontSize, 0.5 * size.Height); var firstString = currentDesktop.ToString(); - var secondString = totalDesktops.ToString(); + var firstFormattedText = this.GetFormattedTextFromText(firstString, fontSize, size, scale); + firstFormattedText.LineHeight = lineHeight; + if (scaleable && firstFormattedText.MinWidth > size.Width) + { + DrawVerticalInfo(context, size, scale, currentDesktop, totalDesktopCount, fontSize - 1); + return; + } - using (var graphics = Graphics.FromImage(bitmap)) + var secondString = totalDesktopCount.ToString(); + var secondFormattedText = this.GetFormattedTextFromText(secondString, fontSize, size, scale); + secondFormattedText.LineHeight = lineHeight; + if (scaleable && secondFormattedText.MinWidth > size.Width) { - graphics.DrawString(firstString, this._font, this._brush, firstOffset); - graphics.DrawString(secondString, this._font, this._brush, secondOffset); + DrawVerticalInfo(context, size, scale, currentDesktop, totalDesktopCount, fontSize - 1); + return; } - return bitmap; + var offsetY1 = Math.Floor(0.5 * size.Height - _verticalSpacing - firstFormattedText.Extent); + context.DrawText(firstFormattedText, new Point(0, offsetY1)); + + var offsetY2 = Math.Ceiling(0.5 * size.Height + _verticalSpacing); + context.DrawText(secondFormattedText, new Point(0, offsetY2)); } - public void Dispose() + private FormattedText GetFormattedTextFromText(string text, double fontSize, System.Drawing.Size size, double scale = 1) { - this._fontFamily?.Dispose(); - this._font?.Dispose(); - this._brush?.Dispose(); + var formattedText = new FormattedText( + text, + CultureInfo.CurrentUICulture, + FlowDirection.LeftToRight, + _defaultFont, + fontSize, + this._foregroundBrush, + null, + fontSize * scale >= _triggerFontSizeInEffectivePixels ? TextFormattingMode.Ideal : TextFormattingMode.Display, + scale); + formattedText.MaxLineCount = 1; + formattedText.MaxTextWidth = size.Width; + formattedText.TextAlignment = TextAlignment.Center; + formattedText.Trimming = TextTrimming.None; + return formattedText; } private static Orientation GetOrientation(int totalDesktopCount) @@ -114,44 +142,21 @@ private static Orientation GetOrientation(int totalDesktopCount) return totalDesktopCount >= 10 ? Orientation.Vertical : Orientation.Horizontal; } - private static float GetFontSize(Orientation orientation) - { - return orientation == Orientation.Horizontal ? _horizontalFontSize : _verticalFontSize; - } - - private static Color GetForegroundColor(Theme theme) + private static (SolidColorBrush Foreground, SolidColorBrush Background) GetThemeBrushes(Theme theme, bool colorPrevalence) { - return theme == Theme.Light ? Color.Black : Color.White; + return colorPrevalence + ? (_darkForegroundBrush, new SolidColorBrush(ImmersiveColor.GetColorByTypeName(ImmersiveColorNames.SystemAccentDark1))) + : theme == Theme.Light + ? (_lightForegroundBrush, _lightBackgroundBrush) + : (_darkForegroundBrush, _darkBackgroundBrush); } - private static PointF GetHorizontalStringOffset() + private static Dpi GetDpi() { - return new PointF(-2, 0); - } - - private static PointF GetFirstVerticalStringOffset(int value) - { - var offset = new PointF(-2, -2); - - if (value < 10) - { - offset.X += 7; - } - else if (value < 100) - { - offset.X += 4; - } - - return offset; - } - - private static PointF GetSecondVerticalStringOffset(int value, int bitmapHeight) - { - var offset = GetFirstVerticalStringOffset(value); - - offset.Y += bitmapHeight / 2f; - - return offset; + var dpi = IconHelper.GetSystemDpi(); + var maxDpi = Math.Max(dpi.X, dpi.Y); + var newDpi = new Dpi(maxDpi, maxDpi); + return newDpi; } } } diff --git a/source/SylphyHorn/UI/TaskTrayIcon.cs b/source/SylphyHorn/UI/TaskTrayIcon.cs index 278d1aa..4289c11 100644 --- a/source/SylphyHorn/UI/TaskTrayIcon.cs +++ b/source/SylphyHorn/UI/TaskTrayIcon.cs @@ -6,6 +6,7 @@ using MetroRadiance.Platform; using SylphyHorn.Properties; using SylphyHorn.Serialization; +using SylphyHorn.Services; using WindowsDesktop; namespace SylphyHorn.UI @@ -30,6 +31,8 @@ public TaskTrayIcon(Icon icon, Icon lightIcon, TaskTrayIconItem[] items) this._items = items; WindowsTheme.SystemTheme.Changed += this.OnSystemThemeChanged; + WindowsTheme.Accent.Changed += this.OnAccentChanged; + WindowsTheme.ColorPrevalence.Changed += this.OnColorPrevalenceChanged; VirtualDesktop.CurrentChanged += this.OnCurrentDesktopChanged; } @@ -68,11 +71,10 @@ public void Reload(VirtualDesktop desktop = null) { if (Settings.General.TrayShowDesktop) { - this.UpdateWithDesktopInfo(desktop ?? VirtualDesktop.Current); + VisualHelper.InvokeOnUIDispatcher(() => this.UpdateWithDesktopInfo(desktop ?? VirtualDesktop.Current)); } else if (this._icon != this._defaultIcon && this._icon != this._lightIcon) { - this._infoIcon?.Dispose(); this._infoIcon = null; this.ChangeIcon(WindowsTheme.SystemTheme.Current == Theme.Light @@ -89,7 +91,9 @@ private void UpdateWithDesktopInfo(VirtualDesktop currentDesktop) if (this._infoIcon == null) { - this._infoIcon = new DynamicInfoTrayIcon(totalDesktopCount, WindowsTheme.SystemTheme.Current); + this._infoIcon = new DynamicInfoTrayIcon( + WindowsTheme.SystemTheme.Current, + WindowsTheme.ColorPrevalence.Current); } this.ChangeIcon(this._infoIcon.GetDesktopInfoIcon(currentDesktopIndex, totalDesktopCount)); @@ -100,12 +104,31 @@ private void OnCurrentDesktopChanged(object sender, VirtualDesktopChangedEventAr this.Reload(e.NewDesktop); } + private void OnAccentChanged(object sender, System.Windows.Media.Color e) + { + var colorPrevalence = WindowsTheme.ColorPrevalence.Current; + if (Settings.General.TrayShowDesktop && colorPrevalence) + { + this._infoIcon.UpdateBrush(WindowsTheme.SystemTheme.Current, colorPrevalence); + VisualHelper.InvokeOnUIDispatcher(() => this.UpdateWithDesktopInfo(VirtualDesktop.Current)); + } + } + + private void OnColorPrevalenceChanged(object sender, bool e) + { + if (Settings.General.TrayShowDesktop) + { + this._infoIcon.UpdateBrush(WindowsTheme.SystemTheme.Current, e); + VisualHelper.InvokeOnUIDispatcher(() => this.UpdateWithDesktopInfo(VirtualDesktop.Current)); + } + } + private void OnSystemThemeChanged(object sender, Theme e) { if (Settings.General.TrayShowDesktop) { - this._infoIcon.UpdateBrush(e); - this.UpdateWithDesktopInfo(VirtualDesktop.Current); + this._infoIcon.UpdateBrush(e, WindowsTheme.ColorPrevalence.Current); + VisualHelper.InvokeOnUIDispatcher(() => this.UpdateWithDesktopInfo(VirtualDesktop.Current)); } else { @@ -128,12 +151,14 @@ private void ChangeIcon(Icon newIcon) public void Dispose() { + WindowsTheme.SystemTheme.Changed -= this.OnSystemThemeChanged; + WindowsTheme.Accent.Changed -= this.OnAccentChanged; + WindowsTheme.ColorPrevalence.Changed -= this.OnColorPrevalenceChanged; + VirtualDesktop.CurrentChanged -= this.OnCurrentDesktopChanged; + this._notifyIcon?.Dispose(); this._lightIcon?.Dispose(); this._icon?.Dispose(); - - WindowsTheme.SystemTheme.Changed -= this.OnSystemThemeChanged; - VirtualDesktop.CurrentChanged -= this.OnCurrentDesktopChanged; } } diff --git a/source/SylphyHorn/UI/TaskTrayTestWindow.xaml b/source/SylphyHorn/UI/TaskTrayTestWindow.xaml new file mode 100644 index 0000000..e3766e9 --- /dev/null +++ b/source/SylphyHorn/UI/TaskTrayTestWindow.xaml @@ -0,0 +1,345 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/SylphyHorn/UI/TaskTrayTestWindow.xaml.cs b/source/SylphyHorn/UI/TaskTrayTestWindow.xaml.cs new file mode 100644 index 0000000..37f3abd --- /dev/null +++ b/source/SylphyHorn/UI/TaskTrayTestWindow.xaml.cs @@ -0,0 +1,151 @@ +using MetroRadiance.Platform; +using MetroRadiance.UI.Controls; +using SylphyHorn.Interop; +using System; +using System.Collections.ObjectModel; +using System.Windows; +using System.Windows.Input; +using System.Windows.Media; + +namespace SylphyHorn.UI +{ + partial class TaskTrayTestWindow + { + public TaskTrayTestWindow() + { + this.InitializeComponent(); + + this.ThemeMode = BlurWindowThemeMode.Dark; + this.LightRadioButton.Click += (sender, e) => this.ThemeMode = BlurWindowThemeMode.Light; + this.DarkRadioButton.Click += (sender, e) => this.ThemeMode = BlurWindowThemeMode.Dark; + this.AccentRadioButton.Click += (sender, e) => this.ThemeMode = BlurWindowThemeMode.Accent; + + this.DataContext = new TaskTrayIconsTestViewModel(Theme.Dark, false); + } + + protected override void OnThemeModeChanged(DependencyPropertyChangedEventArgs e) + { + var themeMode = (BlurWindowThemeMode)e.NewValue; + this.DataContext = new TaskTrayIconsTestViewModel( + themeMode == BlurWindowThemeMode.Light ? Theme.Light : Theme.Dark, + themeMode == BlurWindowThemeMode.Accent); + + switch (themeMode) + { + case BlurWindowThemeMode.Dark: + this.LightRadioButton.IsChecked = false; + this.DarkRadioButton.IsChecked = true; + this.AccentRadioButton.IsChecked = false; + break; + + case BlurWindowThemeMode.Accent: + this.LightRadioButton.IsChecked = false; + this.DarkRadioButton.IsChecked = false; + this.AccentRadioButton.IsChecked = true; + break; + + default: + this.LightRadioButton.IsChecked = true; + this.DarkRadioButton.IsChecked = false; + this.AccentRadioButton.IsChecked = false; + break; + } + } + + protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) + { + base.OnMouseLeftButtonDown(e); + + var position = e.GetPosition(this); + var result = VisualTreeHelper.HitTest(this.CaptionBar, position); + if (result != null) + { + this.DragMove(); + } + } + + private class TaskTrayIconTestViewModel + { + public TaskTrayIconTestViewModel(double scale, Theme theme, bool colorPrevalence) + { + this.Header = $"{100.0 * scale}%"; + this.Width = Math.Round(scale * 16.0); + this.Height = Math.Round(scale * 16.0); + + var colorName = theme == Theme.Light + ? ImmersiveColorNames.SystemBackgroundLightTheme + : ImmersiveColorNames.SystemBackgroundDarkTheme; + this.OpacityMask = new SolidColorBrush(ImmersiveColor.GetColorByTypeName(colorName)); + + var dpi = (uint)(96.0 * scale); + var generator = new DynamicInfoTrayIcon( + theme, + colorPrevalence, + new MetroRadiance.Interop.Dpi(dpi, dpi)); + this.Icon1By3 = generator.GetDesktopInfoIcon(1, 3).ToBitmapSource(); + this.Icon2By7 = generator.GetDesktopInfoIcon(2, 7).ToBitmapSource(); + this.Icon9By9 = generator.GetDesktopInfoIcon(9, 9).ToBitmapSource(); + this.Icon5By10 = generator.GetDesktopInfoIcon(5, 10).ToBitmapSource(); + this.Icon2By100 = generator.GetDesktopInfoIcon(2, 100).ToBitmapSource(); + this.Icon14By100 = generator.GetDesktopInfoIcon(14, 100).ToBitmapSource(); + this.Icon99By100 = generator.GetDesktopInfoIcon(99, 100).ToBitmapSource(); + this.Icon5By1000 = generator.GetDesktopInfoIcon(5, 1000).ToBitmapSource(); + this.Icon52By1000 = generator.GetDesktopInfoIcon(52, 1000).ToBitmapSource(); + this.Icon834By1000 = generator.GetDesktopInfoIcon(834, 1000).ToBitmapSource(); + this.Icon999By1000 = generator.GetDesktopInfoIcon(999, 1000).ToBitmapSource(); + this.Icon1000By1024 = generator.GetDesktopInfoIcon(1000, 1024).ToBitmapSource(); + this.Icon100000By16777216 = generator.GetDesktopInfoIcon(100000, 16777216).ToBitmapSource(); + } + + public string Header { get; } + public double Width { get; } + public double Height { get; } + public SolidColorBrush OpacityMask { get; } + + public ImageSource Icon1By3 { get; } + public ImageSource Icon2By7 { get; } + public ImageSource Icon9By9 { get; } + public ImageSource Icon5By10 { get; } + public ImageSource Icon2By100 { get; } + public ImageSource Icon14By100 { get; } + public ImageSource Icon99By100 { get; } + public ImageSource Icon5By1000 { get; } + public ImageSource Icon52By1000 { get; } + public ImageSource Icon834By1000 { get; } + public ImageSource Icon999By1000 { get; } + public ImageSource Icon1000By1024 { get; } + public ImageSource Icon100000By16777216 { get; } + } + + private class TaskTrayIconsTestViewModel + { + public TaskTrayIconsTestViewModel(Theme theme, bool colorPrevalence) + { + this.Scales = new Collection() + { + new TaskTrayIconTestViewModel(1, theme, colorPrevalence), + new TaskTrayIconTestViewModel(1.25, theme, colorPrevalence), + new TaskTrayIconTestViewModel(1.5, theme, colorPrevalence), + new TaskTrayIconTestViewModel(1.75, theme, colorPrevalence), + new TaskTrayIconTestViewModel(2, theme, colorPrevalence), + new TaskTrayIconTestViewModel(2.25, theme, colorPrevalence), + new TaskTrayIconTestViewModel(2.5, theme, colorPrevalence), + new TaskTrayIconTestViewModel(2.75, theme, colorPrevalence), + new TaskTrayIconTestViewModel(3, theme, colorPrevalence), + }; + } + + public double HorizontalFontSize { get; } = 9; + + public double VerticalFontSize { get; } = 8; + + public double Vertical1000FontSize { get; } = 7; + + public FontFamily FontFamily { get; } = new FontFamily("Segoe UI"); + + public FontWeight FontWeight { get; } = FontWeights.Bold; + + public Collection Scales { get; } + } + } +} From 5b8d6f309625b8b940661a2ced294df7205f42a4 Mon Sep 17 00:00:00 2001 From: mntone Date: Sat, 8 Aug 2020 10:38:03 +0900 Subject: [PATCH 3/4] Add feature to show the desktop index on tooltip. --- .../SylphyHorn/Properties/Resources.Designer.cs | 9 +++++++++ source/SylphyHorn/Properties/Resources.ja.resx | 3 +++ source/SylphyHorn/Properties/Resources.resx | 3 +++ source/SylphyHorn/UI/TaskTrayIcon.cs | 15 ++++++++++++--- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/source/SylphyHorn/Properties/Resources.Designer.cs b/source/SylphyHorn/Properties/Resources.Designer.cs index 5d5a7d5..3522f5d 100644 --- a/source/SylphyHorn/Properties/Resources.Designer.cs +++ b/source/SylphyHorn/Properties/Resources.Designer.cs @@ -617,5 +617,14 @@ public static string TaskTray_Menu_Settings { return ResourceManager.GetString("TaskTray_Menu_Settings", resourceCulture); } } + + /// + /// Desktop {0}/{1} に類似しているローカライズされた文字列を検索します。 + /// + public static string TaskTray_TooltipText_DesktopCount { + get { + return ResourceManager.GetString("TaskTray_TooltipText_DesktopCount", resourceCulture); + } + } } } diff --git a/source/SylphyHorn/Properties/Resources.ja.resx b/source/SylphyHorn/Properties/Resources.ja.resx index 2df6bfd..2eb16e3 100644 --- a/source/SylphyHorn/Properties/Resources.ja.resx +++ b/source/SylphyHorn/Properties/Resources.ja.resx @@ -303,4 +303,7 @@ 設定 + + デスクトップ {0}/{1} + \ No newline at end of file diff --git a/source/SylphyHorn/Properties/Resources.resx b/source/SylphyHorn/Properties/Resources.resx index c1c5467..a37a914 100644 --- a/source/SylphyHorn/Properties/Resources.resx +++ b/source/SylphyHorn/Properties/Resources.resx @@ -303,4 +303,7 @@ &Settings (S) + + Desktop {0}/{1} + \ No newline at end of file diff --git a/source/SylphyHorn/UI/TaskTrayIcon.cs b/source/SylphyHorn/UI/TaskTrayIcon.cs index 4289c11..0a635ee 100644 --- a/source/SylphyHorn/UI/TaskTrayIcon.cs +++ b/source/SylphyHorn/UI/TaskTrayIcon.cs @@ -20,8 +20,6 @@ public class TaskTrayIcon : IDisposable private NotifyIcon _notifyIcon; private DynamicInfoTrayIcon _infoIcon; - public string Text { get; set; } = ProductInfo.Title; - public TaskTrayIcon(Icon icon, Icon lightIcon, TaskTrayIconItem[] items) { this._defaultIcon = icon; @@ -47,7 +45,7 @@ public void Show() this._notifyIcon = new NotifyIcon() { - Text = this.Text, + Text = ProductInfo.Title, Icon = this._icon, Visible = true, ContextMenu = new ContextMenu(menus), @@ -89,6 +87,12 @@ private void UpdateWithDesktopInfo(VirtualDesktop currentDesktop) var currentDesktopIndex = Array.IndexOf(desktops, currentDesktop) + 1; var totalDesktopCount = desktops.Length; + var text = string.Format( + Resources.TaskTray_TooltipText_DesktopCount + "\n" + ProductInfo.Title, + currentDesktopIndex, + totalDesktopCount); + this.ChangeText(text); + if (this._infoIcon == null) { this._infoIcon = new DynamicInfoTrayIcon( @@ -138,6 +142,11 @@ private void OnSystemThemeChanged(object sender, Theme e) } } + private void ChangeText(string newText) + { + this._notifyIcon.Text = newText; + } + private void ChangeIcon(Icon newIcon) { if (this._icon != this._defaultIcon && this._icon != this._lightIcon) From af5baac6caff943cf95348d227b4b8e23532517a Mon Sep 17 00:00:00 2001 From: mntone Date: Sat, 8 Aug 2020 10:42:32 +0900 Subject: [PATCH 4/4] Fix issue where correct info wasn't rarely shown. --- source/SylphyHorn/UI/TaskTrayIcon.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/SylphyHorn/UI/TaskTrayIcon.cs b/source/SylphyHorn/UI/TaskTrayIcon.cs index 0a635ee..2daeaa2 100644 --- a/source/SylphyHorn/UI/TaskTrayIcon.cs +++ b/source/SylphyHorn/UI/TaskTrayIcon.cs @@ -32,6 +32,7 @@ public TaskTrayIcon(Icon icon, Icon lightIcon, TaskTrayIconItem[] items) WindowsTheme.Accent.Changed += this.OnAccentChanged; WindowsTheme.ColorPrevalence.Changed += this.OnColorPrevalenceChanged; VirtualDesktop.CurrentChanged += this.OnCurrentDesktopChanged; + VirtualDesktop.Destroyed += this.OnDesktopDestroyed; } public void Show() @@ -108,6 +109,11 @@ private void OnCurrentDesktopChanged(object sender, VirtualDesktopChangedEventAr this.Reload(e.NewDesktop); } + private void OnDesktopDestroyed(object sender, VirtualDesktopDestroyEventArgs e) + { + this.Reload(); + } + private void OnAccentChanged(object sender, System.Windows.Media.Color e) { var colorPrevalence = WindowsTheme.ColorPrevalence.Current; @@ -164,6 +170,7 @@ public void Dispose() WindowsTheme.Accent.Changed -= this.OnAccentChanged; WindowsTheme.ColorPrevalence.Changed -= this.OnColorPrevalenceChanged; VirtualDesktop.CurrentChanged -= this.OnCurrentDesktopChanged; + VirtualDesktop.Destroyed -= this.OnDesktopDestroyed; this._notifyIcon?.Dispose(); this._lightIcon?.Dispose();