From 2ee3df5473654a1d6048fc4a9b3c51b624cbc7cc Mon Sep 17 00:00:00 2001 From: vineethkuttan <66076509+vineethkuttan@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:18:24 +0530 Subject: [PATCH 1/4] Clamp Tooltip Position To Monitor --- .../Fabric/Composition/TooltipService.cpp | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/TooltipService.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/TooltipService.cpp index cfa5fa66f93..2e85af9af1c 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/TooltipService.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/TooltipService.cpp @@ -135,6 +135,39 @@ LRESULT CALLBACK TooltipWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM l return DefWindowProc(hwnd, message, wparam, lparam); } +POINT ClampTooltipPositionToMonitor(const POINT &anchorPt, int tooltipWidth, int tooltipHeight, int margin) noexcept { + POINT result = {anchorPt.x - tooltipWidth / 2, anchorPt.y - tooltipHeight - margin}; + + HMONITOR hMonitor = MonitorFromPoint(anchorPt, MONITOR_DEFAULTTONEAREST); + MONITORINFO monitorInfo = {}; + monitorInfo.cbSize = sizeof(monitorInfo); + if (!GetMonitorInfo(hMonitor, &monitorInfo)) { + return result; + } + + const RECT &bounds = monitorInfo.rcWork; + + if (result.y < bounds.top) { + result.y = anchorPt.y + margin; + } + + if (result.y + tooltipHeight > bounds.bottom) { + result.y = bounds.bottom - tooltipHeight; + } + if (result.y < bounds.top) { + result.y = bounds.top; + } + + if (result.x + tooltipWidth > bounds.right) { + result.x = bounds.right - tooltipWidth; + } + if (result.x < bounds.left) { + result.x = bounds.left; + } + + return result; +} + void RegisterTooltipWndClass() noexcept { static bool registered = false; if (registered) { @@ -334,14 +367,17 @@ void TooltipTracker::ShowTooltip(const winrt::Microsoft::ReactNative::ComponentV POINT pt = {static_cast(m_pos.X * scaleFactor), static_cast(m_pos.Y * scaleFactor)}; ClientToScreen(parentHwnd, &pt); + POINT tooltipPos = ClampTooltipPositionToMonitor( + pt, tooltipData->width, tooltipData->height, static_cast(toolTipPlacementMargin * scaleFactor)); + RegisterTooltipWndClass(); HINSTANCE hInstance = GetModuleHandle(NULL); m_hwndTip = CreateWindow( c_tooltipWindowClassName, L"Tooltip", WS_POPUP, - pt.x - tooltipData->width / 2, - static_cast(pt.y - tooltipData->height - (toolTipPlacementMargin * scaleFactor)), + tooltipPos.x, + tooltipPos.y, tooltipData->width, tooltipData->height, parentHwnd, From 1e2c3946537235c950a185a08980dcdbee76cbb3 Mon Sep 17 00:00:00 2001 From: vineethkuttan <66076509+vineethkuttan@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:18:36 +0530 Subject: [PATCH 2/4] Change files --- ...ative-windows-c28bfe9f-8c81-4316-aa52-c66fc1dc98d9.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/react-native-windows-c28bfe9f-8c81-4316-aa52-c66fc1dc98d9.json diff --git a/change/react-native-windows-c28bfe9f-8c81-4316-aa52-c66fc1dc98d9.json b/change/react-native-windows-c28bfe9f-8c81-4316-aa52-c66fc1dc98d9.json new file mode 100644 index 00000000000..c1d814cea64 --- /dev/null +++ b/change/react-native-windows-c28bfe9f-8c81-4316-aa52-c66fc1dc98d9.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Clamp Tooltip Position To Monitor", + "packageName": "react-native-windows", + "email": "66076509+vineethkuttan@users.noreply.github.com", + "dependentChangeType": "patch" +} From 91e45f3d6b0aaa6931f87320cc0abb6b9e246e51 Mon Sep 17 00:00:00 2001 From: vineethkuttan <66076509+vineethkuttan@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:46:58 +0530 Subject: [PATCH 3/4] review changes --- .../Fabric/Composition/TooltipService.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/TooltipService.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/TooltipService.cpp index 2e85af9af1c..d8788d36221 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/TooltipService.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/TooltipService.cpp @@ -135,7 +135,7 @@ LRESULT CALLBACK TooltipWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM l return DefWindowProc(hwnd, message, wparam, lparam); } -POINT ClampTooltipPositionToMonitor(const POINT &anchorPt, int tooltipWidth, int tooltipHeight, int margin) noexcept { +POINT ClampTooltipPositionToMonitor(const POINT &anchorPt, int &tooltipWidth, int &tooltipHeight, int margin) noexcept { POINT result = {anchorPt.x - tooltipWidth / 2, anchorPt.y - tooltipHeight - margin}; HMONITOR hMonitor = MonitorFromPoint(anchorPt, MONITOR_DEFAULTTONEAREST); @@ -147,10 +147,19 @@ POINT ClampTooltipPositionToMonitor(const POINT &anchorPt, int tooltipWidth, int const RECT &bounds = monitorInfo.rcWork; + // Cap the tooltip size to the work area so the position clamping below is guaranteed to keep + // the whole tooltip on-screen, even for unusually long tooltip text. + tooltipWidth = std::min(tooltipWidth, static_cast(bounds.right - bounds.left)); + tooltipHeight = std::min(tooltipHeight, static_cast(bounds.bottom - bounds.top)); + + result = {anchorPt.x - tooltipWidth / 2, anchorPt.y - tooltipHeight - margin}; + + // Flip below the anchor if there isn't enough room above it within the work area. if (result.y < bounds.top) { result.y = anchorPt.y + margin; } + // Clamp vertically so the tooltip never extends past the work area. if (result.y + tooltipHeight > bounds.bottom) { result.y = bounds.bottom - tooltipHeight; } @@ -158,6 +167,7 @@ POINT ClampTooltipPositionToMonitor(const POINT &anchorPt, int tooltipWidth, int result.y = bounds.top; } + // Clamp horizontally so the tooltip never extends past the work area. if (result.x + tooltipWidth > bounds.right) { result.x = bounds.right - tooltipWidth; } From 5871051a18ac8d883a3b6885161672bed4460120 Mon Sep 17 00:00:00 2001 From: vineethkuttan <66076509+vineethkuttan@users.noreply.github.com> Date: Tue, 18 Aug 2026 14:04:59 +0530 Subject: [PATCH 4/4] Change files --- ...ct-native-windows-f4eab9a1-89da-4344-bb29-9257605a9422.json} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename change/{react-native-windows-c28bfe9f-8c81-4316-aa52-c66fc1dc98d9.json => react-native-windows-f4eab9a1-89da-4344-bb29-9257605a9422.json} (88%) diff --git a/change/react-native-windows-c28bfe9f-8c81-4316-aa52-c66fc1dc98d9.json b/change/react-native-windows-f4eab9a1-89da-4344-bb29-9257605a9422.json similarity index 88% rename from change/react-native-windows-c28bfe9f-8c81-4316-aa52-c66fc1dc98d9.json rename to change/react-native-windows-f4eab9a1-89da-4344-bb29-9257605a9422.json index c1d814cea64..40f56c77078 100644 --- a/change/react-native-windows-c28bfe9f-8c81-4316-aa52-c66fc1dc98d9.json +++ b/change/react-native-windows-f4eab9a1-89da-4344-bb29-9257605a9422.json @@ -1,5 +1,5 @@ { - "type": "patch", + "type": "prerelease", "comment": "Clamp Tooltip Position To Monitor", "packageName": "react-native-windows", "email": "66076509+vineethkuttan@users.noreply.github.com",