From f9f90a3766d441415fa4c94b245c1c38df301872 Mon Sep 17 00:00:00 2001 From: Christopher - RtF <58520035+christopher-rtf@users.noreply.github.com> Date: Thu, 2 May 2024 15:02:06 -0400 Subject: [PATCH] Update: RTL support for taskbar icon button --- .../TrayButton/Windows10/TrayButton.cs | 54 +++++++++++++++---- .../Windows11/TrayButtonNativeWindow.cs | 22 +++++++- 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/Morphic.Controls/TrayButton/Windows10/TrayButton.cs b/Morphic.Controls/TrayButton/Windows10/TrayButton.cs index fcc2b7ee..78cfe103 100644 --- a/Morphic.Controls/TrayButton/Windows10/TrayButton.cs +++ b/Morphic.Controls/TrayButton/Windows10/TrayButton.cs @@ -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 @@ -1213,13 +1213,20 @@ private void PositionTrayButton() return (taskbarRect, taskButtonContainerRect, notifyTrayRect); } - private (LegacyWindowsApi.RECT availableAreaRect, List childRects) CalculateEmptyRectsBetweenTaskButtonContainerAndNotifyTray(IntPtr taskbarHandle, System.Windows.Forms.Orientation taskbarOrientation, LegacyWindowsApi.RECT taskbarRect, LegacyWindowsApi.RECT taskButtonContainerRect, LegacyWindowsApi.RECT notifyTrayRect) + private (LegacyWindowsApi.RECT availableAreaRect, List 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 { @@ -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; @@ -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 { diff --git a/Morphic.Controls/TrayButton/Windows11/TrayButtonNativeWindow.cs b/Morphic.Controls/TrayButton/Windows11/TrayButtonNativeWindow.cs index 3760f6be..b9dadd21 100644 --- a/Morphic.Controls/TrayButton/Windows11/TrayButtonNativeWindow.cs +++ b/Morphic.Controls/TrayButton/Windows11/TrayButtonNativeWindow.cs @@ -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; @@ -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; }