From d39aa193298e3bb4af78e212278b7cb3959ed5ea Mon Sep 17 00:00:00 2001 From: Christopher - RtF <58520035+christopher-rtf@users.noreply.github.com> Date: Thu, 22 Aug 2024 10:44:14 -0700 Subject: [PATCH] Add taskbar widget repositioning check (Win10) --- .../TrayButton/Windows10/TrayButton.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/Morphic.Controls/TrayButton/Windows10/TrayButton.cs b/Morphic.Controls/TrayButton/Windows10/TrayButton.cs index 78cfe103..071ffe55 100644 --- a/Morphic.Controls/TrayButton/Windows10/TrayButton.cs +++ b/Morphic.Controls/TrayButton/Windows10/TrayButton.cs @@ -225,9 +225,16 @@ private class TrayButtonNativeWindow : System.Windows.Forms.NativeWindow, IDispo private string? _tooltipText = null; private bool _tooltipInfoAdded = false; + // NOTE: this timer is used to reposition the tray button when the screen resolution changes (and it keeps watch at an accelerated pace, to make sure the taskbar has stopped moving around) private System.Threading.Timer? _trayButtonPositionCheckupTimer; private int _trayButtonPositionCheckupTimerCounter = 0; + // NOTE: this timer is used to reposition the tray button when adjacent taskbar widgets (e.g. Windows 10 weather) change in size + private System.Threading.Timer? _trayButtonWidgetPositionCheckupTimer; + // + private TimeSpan _widgetPositionCheckupInterval = new TimeSpan(0, 0, 0, 1, 0); // NOTE: this is a failsafe mechanism; if our position changes more than this many times per minute, we will back off the widget reposition timer (to avoid a potential super-glitchy user experience, with a taskbar button that won't stop moving; this may also help avoid the unnecessary movement of adjacent widgets) + private Queue _widgetPositionChangeHistory = new(); + [Flags] private enum TrayButtonVisualStateFlags { @@ -311,6 +318,9 @@ public void Initialize(IntPtr taskbarHandle) //{ this.PositionTrayButton(); //} + + // NOTE: due to the weather, news and other taskbar widgets introduced in late versions of Windows 10, we need to re-validate and re-position the taskbar icon when adjacent widgets overlay its position + _trayButtonWidgetPositionCheckupTimer = new System.Threading.Timer(TrayButtonWidgetPositionCheckup, null, _widgetPositionCheckupInterval, _widgetPositionCheckupInterval); } // NOTE: this function is somewhat redundant and is provided to support Windows 11; we should refactor all of this code to handle window messages centrally @@ -560,6 +570,9 @@ public void Dispose() _mouseHook.Dispose(); } + _trayButtonWidgetPositionCheckupTimer?.Dispose(); + _trayButtonWidgetPositionCheckupTimer = null; + Microsoft.Win32.SystemEvents.DisplaySettingsChanged -= SystemEvents_DisplaySettingsChanged; this.DestroyTooltipWindow(); @@ -788,6 +801,45 @@ private void TrayButtonPositionCheckup(object? state) } } } + // + private void TrayButtonWidgetPositionCheckup(object? state) + { + const int NUM_CHANGE_HISTORY_ENTRIES_TO_AVERAGE = 20; + const int WIDGET_POSITION_INTERVAL_BACKOFF_MULTIPLIER = 2; + + // check the current and desired positions of the notify tray icon + var calculateResult = this.CalculateCurrentAndTargetRectOfTrayButton(); + if (calculateResult is not null) + { + if (calculateResult.Value.changeToRect is not null) + { + // record the repositioning event's timestamp + var currentRepositioningEventTimestamp = DateTimeOffset.UtcNow; + _widgetPositionChangeHistory.Enqueue(currentRepositioningEventTimestamp); + while (_widgetPositionChangeHistory.Count > NUM_CHANGE_HISTORY_ENTRIES_TO_AVERAGE) + { + _ = _widgetPositionChangeHistory.Dequeue(); + } + + // reposition the tray button + this.PositionTrayButton(); + + // determine if our repositioning is happening too frequently; if so, then increase its check interval (multiplied by WIDGET_POSITION_INTERVAL_BACKOFF_MULTIPLIER) + var oldestChange = _widgetPositionChangeHistory.Peek(); + var numberOfHistoryEvents = _widgetPositionChangeHistory.Count; + var totalDuration = currentRepositioningEventTimestamp.Subtract(oldestChange); + if (numberOfHistoryEvents == NUM_CHANGE_HISTORY_ENTRIES_TO_AVERAGE) + { + TimeSpan averageIntervalPerChange = totalDuration / numberOfHistoryEvents; + if (averageIntervalPerChange < _widgetPositionCheckupInterval * WIDGET_POSITION_INTERVAL_BACKOFF_MULTIPLIER) + { + _widgetPositionCheckupInterval *= WIDGET_POSITION_INTERVAL_BACKOFF_MULTIPLIER; + _trayButtonWidgetPositionCheckupTimer?.Change(_widgetPositionCheckupInterval, _widgetPositionCheckupInterval); + } + } + } + } + } private LegacyWindowsApi.POINT? ConvertMouseMessageLParamToScreenPoint(IntPtr lParam) {