Skip to content

Commit

Permalink
Merge pull request #394 from raisingthefloor/christopher/win10-widget…
Browse files Browse the repository at this point in the history
…-fix

Add taskbar widget repositioning check (Win10)
  • Loading branch information
christopher-rtf authored Aug 22, 2024
2 parents 4747b9f + d39aa19 commit ce04ac5
Showing 1 changed file with 52 additions and 0 deletions.
52 changes: 52 additions & 0 deletions Morphic.Controls/TrayButton/Windows10/TrayButton.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<DateTimeOffset> _widgetPositionChangeHistory = new();

[Flags]
private enum TrayButtonVisualStateFlags
{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -560,6 +570,9 @@ public void Dispose()
_mouseHook.Dispose();
}

_trayButtonWidgetPositionCheckupTimer?.Dispose();
_trayButtonWidgetPositionCheckupTimer = null;

Microsoft.Win32.SystemEvents.DisplaySettingsChanged -= SystemEvents_DisplaySettingsChanged;

this.DestroyTooltipWindow();
Expand Down Expand Up @@ -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)
{
Expand Down

0 comments on commit ce04ac5

Please # to comment.