Skip to content

Commit

Permalink
Merge pull request #387 from raisingthefloor/christopher/taskbar-rtl
Browse files Browse the repository at this point in the history
RTL support for taskbar icon button
  • Loading branch information
christopher-rtf authored May 2, 2024
2 parents 4846530 + f9f90a3 commit ff405e2
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 11 deletions.
54 changes: 45 additions & 9 deletions Morphic.Controls/TrayButton/Windows10/TrayButton.cs
Original file line number Diff line number Diff line change
Expand Up @@ -862,7 +862,7 @@ private void Paint(IntPtr hWnd)

if (_iconHandle != IntPtr.Zero && iconWidthAndHeight > 0)
{
var drawIconSuccess = LegacyWindowsApi.DrawIconEx(bufferedPaintDc, xLeft, yTop, _iconHandle, iconWidthAndHeight, iconWidthAndHeight, 0 /* not animated */, IntPtr.Zero /* no triple-buffering */, LegacyWindowsApi.DrawIconFlags.DI_NORMAL);
var drawIconSuccess = LegacyWindowsApi.DrawIconEx(bufferedPaintDc, xLeft, yTop, _iconHandle, iconWidthAndHeight, iconWidthAndHeight, 0 /* not animated */, IntPtr.Zero /* no triple-buffering */, LegacyWindowsApi.DrawIconFlags.DI_NORMAL | LegacyWindowsApi.DrawIconFlags.DI_NOMIRROR);
if (drawIconSuccess == false)
{
// failed; abort
Expand Down Expand Up @@ -1213,13 +1213,20 @@ private void PositionTrayButton()
return (taskbarRect, taskButtonContainerRect, notifyTrayRect);
}

private (LegacyWindowsApi.RECT availableAreaRect, List<LegacyWindowsApi.RECT> childRects) CalculateEmptyRectsBetweenTaskButtonContainerAndNotifyTray(IntPtr taskbarHandle, System.Windows.Forms.Orientation taskbarOrientation, LegacyWindowsApi.RECT taskbarRect, LegacyWindowsApi.RECT taskButtonContainerRect, LegacyWindowsApi.RECT notifyTrayRect)
private (LegacyWindowsApi.RECT availableAreaRect, List<LegacyWindowsApi.RECT> childRects) CalculateEmptyRectsBetweenTaskButtonContainerAndNotifyTray(IntPtr taskbarHandle, System.Windows.Forms.Orientation taskbarOrientation, bool isRightToLeft, LegacyWindowsApi.RECT taskbarRect, LegacyWindowsApi.RECT taskButtonContainerRect, LegacyWindowsApi.RECT notifyTrayRect)
{
// calculate the total "free area" rectangle (the area between the task button container and the notify tray where we want to place our tray button)
LegacyWindowsApi.RECT freeAreaAvailableRect;
if (taskbarOrientation == System.Windows.Forms.Orientation.Horizontal)
{
freeAreaAvailableRect = new LegacyWindowsApi.RECT(new System.Windows.Rect(taskButtonContainerRect.Right, taskbarRect.Top, Math.Max(notifyTrayRect.Left - taskButtonContainerRect.Right, 0), Math.Max(taskbarRect.Bottom - taskbarRect.Top, 0)));
if (isRightToLeft == false)
{
freeAreaAvailableRect = new LegacyWindowsApi.RECT(new System.Windows.Rect(taskButtonContainerRect.Right, taskbarRect.Top, Math.Max(notifyTrayRect.Left - taskButtonContainerRect.Right, 0), Math.Max(taskbarRect.Bottom - taskbarRect.Top, 0)));
}
else
{
freeAreaAvailableRect = new LegacyWindowsApi.RECT(new System.Windows.Rect(notifyTrayRect.Right, taskbarRect.Top, Math.Max(taskButtonContainerRect.Left - notifyTrayRect.Right, 0), Math.Max(taskbarRect.Bottom - taskbarRect.Top, 0)));
}
}
else
{
Expand Down Expand Up @@ -1318,8 +1325,19 @@ private void PositionTrayButton()
taskbarOrientation = System.Windows.Forms.Orientation.Vertical;
}

// if the taskbar is horizontal, determine if it's LeftToRight (standard) or RightToLeft (for Arabic, Hebrew, etc.)
bool isRightToLeft = false;
if (taskbarOrientation == System.Windows.Forms.Orientation.Horizontal)
{
var centerXOfTaskbar = taskbarRect.Left + ((taskbarRect.Right - taskbarRect.Left) / 2);
if (notifyTrayRect.Right < centerXOfTaskbar)
{
isRightToLeft = true;
}
}

// calculate all of the free rects between the task button container and notify tray
var calculateEmptyRectsResult = this.CalculateEmptyRectsBetweenTaskButtonContainerAndNotifyTray(taskbarHandle, taskbarOrientation, taskbarRect, taskButtonContainerRect, notifyTrayRect);
var calculateEmptyRectsResult = this.CalculateEmptyRectsBetweenTaskButtonContainerAndNotifyTray(taskbarHandle, taskbarOrientation, isRightToLeft, taskbarRect, taskButtonContainerRect, notifyTrayRect);
var freeAreaChildRects = calculateEmptyRectsResult.childRects;
var freeAreaAvailableRect = calculateEmptyRectsResult.availableAreaRect;

Expand Down Expand Up @@ -1399,24 +1417,42 @@ private void PositionTrayButton()
}
}

// if our current (already-used-by-us) rect was not available, choose the leftmost/topmost space available
// if our current (already-used-by-us) rect was not available, choose the leftmost/topmost space available; note that "rightmost" is actually leftmost when the system is using an RTL orientation (e.g. Arabic, Hebrew)
if (newRect is null)
{
if (taskbarOrientation == System.Windows.Forms.Orientation.Horizontal)
{
// horizontal taskbar: find the leftmost rect in the available space (which we'll then carve the "rightmost" section out of)
// OBSERVATION: leftmost is actually rightmost in RTL layouts (e.g. Arabic, Hebrew)
LegacyWindowsApi.RECT leftmostRect = freeAreaAvailableRect;

foreach (var freeAreaChildRect in freeAreaChildRects)
{
if (freeAreaChildRect.Left < leftmostRect.Right)
if (isRightToLeft == false)
{
leftmostRect.Right = freeAreaChildRect.Left;
if (freeAreaChildRect.Left < leftmostRect.Right)
{
leftmostRect.Right = freeAreaChildRect.Left;
}
}
else
{
if (freeAreaChildRect.Right > leftmostRect.Left)
{
leftmostRect.Left = freeAreaChildRect.Right;
}
}
}

// choose the rightmost space in the leftmostRect area; expand our tray button towards the left if/as necessary
newRect = new LegacyWindowsApi.RECT(new System.Windows.Rect(leftmostRect.Right - trayButtonWidth, leftmostRect.Bottom - trayButtonHeight, trayButtonWidth, trayButtonHeight));
// choose the rightmost space in the leftmostRect area (or leftmost for RTL layouts); expand our tray button towards the left (right for RTL) if/as necessary
if (isRightToLeft == false)
{
newRect = new LegacyWindowsApi.RECT(new System.Windows.Rect(leftmostRect.Right - trayButtonWidth, leftmostRect.Bottom - trayButtonHeight, trayButtonWidth, trayButtonHeight));
}
else
{
newRect = new LegacyWindowsApi.RECT(new System.Windows.Rect(leftmostRect.Left, leftmostRect.Bottom - trayButtonHeight, trayButtonWidth, trayButtonHeight));
}
}
else
{
Expand Down
22 changes: 20 additions & 2 deletions Morphic.Controls/TrayButton/Windows11/TrayButtonNativeWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,17 @@ internal static Windows.Win32.Foundation.RECT CalculateCenterRectInsideRect(Wind
taskbarOrientation = System.Windows.Forms.Orientation.Vertical;
}

// if the taskbar is horizontal, determine if it's LeftToRight (standard) or RightToLeft (for Arabic, Hebrew, etc.)
bool isRightToLeft = false;
if (taskbarOrientation == System.Windows.Forms.Orientation.Horizontal)
{
var centerXOfTaskbar = taskbarRect.X + (taskbarRect.Width / 2);
if (notifyTrayRect.right < centerXOfTaskbar)
{
isRightToLeft = true;
}
}

// establish the appropriate size for our tray button (i.e. same height/width as taskbar, and with an aspect ratio of 8:10)
int trayButtonHeight;
int trayButtonWidth;
Expand Down Expand Up @@ -1130,12 +1141,19 @@ internal static Windows.Win32.Foundation.RECT CalculateCenterRectInsideRect(Wind
trayButtonHeight = (int)((Double)trayButtonWidth * 0.8);
}

// choose a space in the rightmost/bottommost position of the taskbar
// choose a space in the rightmost/bottommost position of the taskbar; note that "rightmost" is actually leftmost when the system is using an RTL orientation (e.g. Arabic, Hebrew)
int trayButtonX;
int trayButtonY;
if (taskbarOrientation == System.Windows.Forms.Orientation.Horizontal)
{
trayButtonX = notifyTrayRect.left - trayButtonWidth;
if (isRightToLeft == false)
{
trayButtonX = notifyTrayRect.left - trayButtonWidth;
}
else
{
trayButtonX = notifyTrayRect.right;
}
// NOTE: if we have any issues with positioning, try to replace taskbarRect.bottom with taskButtoncontainerRect.bottom (if we chose option #1 for our size calculations above)
trayButtonY = taskbarRect.bottom - trayButtonHeight;
}
Expand Down

0 comments on commit ff405e2

Please # to comment.