Skip to content

Commit 03834e3

Browse files
committed
Fit Table Tooltip in a single monitor for Win32
This commit contributes to the fitting of the table tooltip inside a single monitor if it spans over multiple monitor to avoid any infinite loop because of rescaling triggered by any DPI_CHANGED events. contributes to #62 and #128
1 parent 89270c2 commit 03834e3

File tree

6 files changed

+114
-105
lines changed

6 files changed

+114
-105
lines changed

bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/CoordinateSystemMapper.java

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ interface CoordinateSystemMapper {
3535

3636
Rectangle translateToDisplayCoordinates(Rectangle rect, int zoom);
3737

38+
Rectangle getContainingMonitorBoundsInPixels(Point point);
39+
3840
void setCursorLocation(int x, int y);
3941

4042
Point getCursorLocation();

bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java

+19
Original file line numberDiff line numberDiff line change
@@ -1724,6 +1724,25 @@ public Point getCursorLocation () {
17241724
return coordinateSystemMapper.getCursorLocation();
17251725
}
17261726

1727+
Rectangle fitRectangleBoundsIntoMonitor(RECT rect) {
1728+
Rectangle monitorBounds = coordinateSystemMapper.getContainingMonitorBoundsInPixels(getCursorLocation());
1729+
int rectWidth = rect.right - rect.left;
1730+
int rectHeight = rect.bottom - rect.top;
1731+
if (rect.left < monitorBounds.x) {
1732+
rect.left = monitorBounds.x;
1733+
}
1734+
int monitorBoundsRightEnd = monitorBounds.x + monitorBounds.width;
1735+
if (rect.right > monitorBoundsRightEnd) {
1736+
if (rectWidth <= monitorBounds.width) {
1737+
rect.left = monitorBoundsRightEnd - rectWidth;
1738+
} else {
1739+
rect.left = monitorBounds.x;
1740+
}
1741+
rectWidth = monitorBoundsRightEnd - rect.left;
1742+
}
1743+
return new Rectangle(rect.left, rect.top, rectWidth, rectHeight);
1744+
}
1745+
17271746
Point getCursorLocationInPixels () {
17281747
POINT pt = new POINT ();
17291748
OS.GetCursorPos (pt);

bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/MultiZoomCoordinateSystemMapper.java

+8
Original file line numberDiff line numberDiff line change
@@ -271,4 +271,12 @@ private int getApplicableMonitorZoom(Monitor monitor) {
271271
return DPIUtil.getZoomForAutoscaleProperty(monitor.zoom);
272272
}
273273

274+
@Override
275+
public Rectangle getContainingMonitorBoundsInPixels(Point point) {
276+
Monitor monitor = point instanceof MonitorAwarePoint monitorAwarePoint
277+
? monitorAwarePoint.getMonitor()
278+
:getContainingMonitorForPoints(point.x, point.y);
279+
return getMonitorClientAreaInPixels(monitor);
280+
}
281+
274282
}

bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/SingleZoomCoordinateSystemMapper.java

+13
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,17 @@ public Point getCursorLocation() {
105105
public void setCursorLocation(int x, int y) {
106106
display.setCursorLocationInPixels(DPIUtil.autoScaleUp(x), DPIUtil.autoScaleUp(y));
107107
}
108+
109+
@Override
110+
public Rectangle getContainingMonitorBoundsInPixels(Point point) {
111+
int zoom = DPIUtil.getDeviceZoom();
112+
point = DPIUtil.scaleUp(point, zoom);
113+
for (Monitor monitor : display.getMonitors()) {
114+
Rectangle monitorBounds = DPIUtil.scaleUp(monitor.getBounds(), zoom);
115+
if (monitorBounds.contains(point)) {
116+
return monitorBounds;
117+
}
118+
}
119+
return null;
120+
}
108121
}

bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Table.java

+68-63
Original file line numberDiff line numberDiff line change
@@ -7158,82 +7158,87 @@ LRESULT wmNotifyToolTip (NMHDR hdr, long wParam, long lParam) {
71587158
case OS.TTN_SHOW: {
71597159
LRESULT result = super.wmNotify (hdr, wParam, lParam);
71607160
if (result != null) return result;
7161-
if (hdr.code != OS.TTN_SHOW) tipRequested = true;
7162-
long code = callWindowProc (handle, OS.WM_NOTIFY, wParam, lParam);
7163-
if (hdr.code != OS.TTN_SHOW) tipRequested = false;
71647161
if (toolTipText != null) break;
7165-
if (isCustomToolTip ()) {
7166-
LVHITTESTINFO pinfo = new LVHITTESTINFO ();
7167-
int pos = OS.GetMessagePos ();
7168-
POINT pt = new POINT();
7169-
OS.POINTSTOPOINT (pt, pos);
7170-
OS.ScreenToClient (handle, pt);
7171-
pinfo.x = pt.x;
7172-
pinfo.y = pt.y;
7173-
/*
7174-
* Bug in Windows. When LVM_SUBITEMHITTEST is used to hittest
7175-
* a point that is above the table, instead of returning -1 to
7176-
* indicate that the hittest failed, a negative index is returned.
7177-
* The fix is to consider any value that is negative a failure.
7178-
*/
7179-
if (OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo) >= 0) {
7180-
TableItem item = _getItem (pinfo.iItem);
7181-
long hDC = OS.GetDC (handle);
7182-
long oldFont = 0, newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
7183-
if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont);
7184-
long hFont = item.fontHandle (pinfo.iSubItem);
7185-
if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
7186-
Event event = sendMeasureItemEvent (item, pinfo.iItem, pinfo.iSubItem, hDC);
7187-
if (!isDisposed () && !item.isDisposed ()) {
7188-
RECT itemRect = new RECT ();
7189-
Rectangle boundsInPixels = DPIUtil.scaleUp(event.getBounds(), getZoom());
7190-
OS.SetRect (itemRect, boundsInPixels.x, boundsInPixels.y, boundsInPixels.x + boundsInPixels.width, boundsInPixels.y + boundsInPixels.height);
7191-
if (hdr.code == OS.TTN_SHOW) {
7192-
RECT toolRect = toolTipRect (itemRect);
7193-
OS.MapWindowPoints (handle, 0, toolRect, 2);
7194-
long hwndToolTip = OS.SendMessage (handle, OS.LVM_GETTOOLTIPS, 0, 0);
7195-
int flags = OS.SWP_NOACTIVATE | OS.SWP_NOZORDER;
7196-
int width = toolRect.right - toolRect.left, height = toolRect.bottom - toolRect.top;
7197-
OS.SetWindowPos (hwndToolTip, 0, toolRect.left , toolRect.top, width, height, flags);
7198-
} else {
7199-
NMTTDISPINFO lpnmtdi = new NMTTDISPINFO ();
7200-
OS.MoveMemory (lpnmtdi, lParam, NMTTDISPINFO.sizeof);
7201-
if (lpnmtdi.lpszText != 0) {
7202-
OS.MoveMemory (lpnmtdi.lpszText, new char [1], 2);
7203-
OS.MoveMemory (lParam, lpnmtdi, NMTTDISPINFO.sizeof);
7204-
}
7205-
RECT cellRect = item.getBounds (pinfo.iItem, pinfo.iSubItem, true, true, true, true, hDC);
7206-
RECT clientRect = new RECT ();
7207-
OS.GetClientRect (handle, clientRect);
7208-
if (itemRect.right > cellRect.right || itemRect.right > clientRect.right) {
7209-
//TEMPORARY CODE
7210-
String string = " ";
7162+
if (isCustomToolTip () || hdr.code == OS.TTN_SHOW) {
7163+
result = positionTooltip(hdr, lParam);
7164+
}
7165+
return result;
7166+
}
7167+
}
7168+
return null;
7169+
}
7170+
7171+
private LRESULT positionTooltip(NMHDR hdr, long lParam) {
7172+
LRESULT result = null;
7173+
LVHITTESTINFO pinfo = new LVHITTESTINFO ();
7174+
int pos = OS.GetMessagePos ();
7175+
POINT pt = new POINT();
7176+
OS.POINTSTOPOINT (pt, pos);
7177+
OS.ScreenToClient (handle, pt);
7178+
pinfo.x = pt.x;
7179+
pinfo.y = pt.y;
7180+
/*
7181+
* Bug in Windows. When LVM_SUBITEMHITTEST is used to hittest
7182+
* a point that is above the table, instead of returning -1 to
7183+
* indicate that the hittest failed, a negative index is returned.
7184+
* The fix is to consider any value that is negative a failure.
7185+
*/
7186+
if (OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo) >= 0) {
7187+
TableItem item = _getItem (pinfo.iItem);
7188+
long hDC = OS.GetDC (handle);
7189+
long oldFont = 0, newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
7190+
if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont);
7191+
long hFont = item.fontHandle (pinfo.iSubItem);
7192+
if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
7193+
Event event = sendMeasureItemEvent (item, pinfo.iItem, pinfo.iSubItem, hDC);
7194+
if (!isDisposed () && !item.isDisposed ()) {
7195+
RECT itemRect = new RECT ();
7196+
Rectangle boundsInPixels = DPIUtil.scaleUp(event.getBounds(), getZoom());
7197+
OS.SetRect (itemRect, boundsInPixels.x, boundsInPixels.y, boundsInPixels.x + boundsInPixels.width, boundsInPixels.y + boundsInPixels.height);
7198+
if (hdr.code == OS.TTN_SHOW) {
7199+
RECT toolRect = isCustomToolTip() ? toolTipRect (itemRect) : itemRect;
7200+
OS.MapWindowPoints (handle, 0, toolRect, 2);
7201+
long hwndToolTip = OS.SendMessage (handle, OS.LVM_GETTOOLTIPS, 0, 0);
7202+
int flags = OS.SWP_NOACTIVATE | OS.SWP_NOZORDER;
7203+
Rectangle adjustedTooltipBounds = getDisplay().fitRectangleBoundsIntoMonitor(toolRect);
7204+
OS.SetWindowPos(hwndToolTip, 0, adjustedTooltipBounds.x, adjustedTooltipBounds.y,
7205+
adjustedTooltipBounds.width, adjustedTooltipBounds.height, flags);
7206+
result = LRESULT.ONE;
7207+
} else {
7208+
NMTTDISPINFO lpnmtdi = new NMTTDISPINFO ();
7209+
OS.MoveMemory (lpnmtdi, lParam, NMTTDISPINFO.sizeof);
7210+
if (lpnmtdi.lpszText != 0) {
7211+
OS.MoveMemory (lpnmtdi.lpszText, new char [1], 2);
7212+
OS.MoveMemory (lParam, lpnmtdi, NMTTDISPINFO.sizeof);
7213+
}
7214+
RECT cellRect = item.getBounds (pinfo.iItem, pinfo.iSubItem, true, true, true, true, hDC);
7215+
RECT clientRect = new RECT ();
7216+
OS.GetClientRect (handle, clientRect);
7217+
if (itemRect.right > cellRect.right || itemRect.right > clientRect.right) {
7218+
//TEMPORARY CODE
7219+
String string = " ";
72117220
// String string = null;
72127221
// if (pinfo.iSubItem == 0) {
72137222
// string = item.text;
72147223
// } else {
72157224
// String [] strings = item.strings;
72167225
// if (strings != null) string = strings [pinfo.iSubItem];
72177226
// }
7218-
if (string != null) {
7219-
Shell shell = getShell ();
7220-
char [] chars = new char [string.length () + 1];
7221-
string.getChars (0, string.length (), chars, 0);
7222-
shell.setToolTipText (lpnmtdi, chars);
7223-
OS.MoveMemory (lParam, lpnmtdi, NMTTDISPINFO.sizeof);
7224-
}
7225-
}
7226-
}
7227+
if (string != null) {
7228+
Shell shell = getShell ();
7229+
char [] chars = new char [string.length () + 1];
7230+
string.getChars (0, string.length (), chars, 0);
7231+
shell.setToolTipText (lpnmtdi, chars);
7232+
OS.MoveMemory (lParam, lpnmtdi, NMTTDISPINFO.sizeof);
72277233
}
7228-
if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
7229-
if (newFont != 0) OS.SelectObject (hDC, oldFont);
7230-
OS.ReleaseDC (handle, hDC);
72317234
}
72327235
}
7233-
return new LRESULT (code);
72347236
}
7237+
if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
7238+
if (newFont != 0) OS.SelectObject (hDC, oldFont);
7239+
OS.ReleaseDC (handle, hDC);
72357240
}
7236-
return null;
7241+
return result;
72377242
}
72387243

72397244
LRESULT wmNotifyToolTip (NMTTCUSTOMDRAW nmcd, long lParam) {

bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Tree.java

+4-42
Original file line numberDiff line numberDiff line change
@@ -8175,15 +8175,10 @@ private LRESULT positionTooltip(NMHDR hdr, long wParam, long lParam, boolean man
81758175
// triggers additional display messages to SWT, creating an infinite loop
81768176
// of positioning and re-scaling events.
81778177
// Refer: https://github.com/eclipse-platform/eclipse.platform.swt/issues/557
8178-
Point cursorLocation = display.getCursorLocation();
8179-
Rectangle monitorBounds = cursorLocation instanceof MonitorAwarePoint monitorAwarePoint
8180-
? getContainingMonitorBoundsInMultiZoomCoordinateSystem(monitorAwarePoint)
8181-
: getContainingMonitorBoundsInSingleZoomCoordinateSystem(cursorLocation);
8182-
if (monitorBounds != null) {
8183-
Rectangle adjustedTooltipBounds = fitTooltipBoundsIntoMonitor(toolRect, monitorBounds);
8184-
OS.SetWindowPos (hdr.hwndFrom, 0, adjustedTooltipBounds.x, adjustedTooltipBounds.y, adjustedTooltipBounds.width, adjustedTooltipBounds.height, flags);
8185-
result = LRESULT.ONE;
8186-
}
8178+
8179+
Rectangle adjustedTooltipBounds = getDisplay().fitRectangleBoundsIntoMonitor(toolRect);
8180+
OS.SetWindowPos (hdr.hwndFrom, 0, adjustedTooltipBounds.x, adjustedTooltipBounds.y, adjustedTooltipBounds.width, adjustedTooltipBounds.height, flags);
8181+
result = LRESULT.ONE;
81878182
} else if (!managedTooltip) {
81888183
// If managedTooltip is false and the cursor is not over the valid part of the
81898184
// target cell, Windows may still try to display the default tooltip. Since we
@@ -8196,39 +8191,6 @@ private LRESULT positionTooltip(NMHDR hdr, long wParam, long lParam, boolean man
81968191
return result;
81978192
}
81988193

8199-
/**
8200-
* Adjust the tool tip to fit in a single monitor either by shifting its position or by adjusting it's width.
8201-
*/
8202-
private Rectangle fitTooltipBoundsIntoMonitor(RECT tooltipBounds, Rectangle monitorBounds) {
8203-
int tooltipWidth = tooltipBounds.right - tooltipBounds.left;
8204-
int tooltipHeight = tooltipBounds.bottom - tooltipBounds.top;
8205-
if (tooltipBounds.left < monitorBounds.x) {
8206-
tooltipBounds.left = monitorBounds.x;
8207-
}
8208-
int monitorBoundsRightEnd = monitorBounds.x + monitorBounds.width;
8209-
if (tooltipBounds.right > monitorBoundsRightEnd) {
8210-
if (tooltipWidth <= monitorBounds.width) {
8211-
tooltipBounds.left = monitorBoundsRightEnd - tooltipWidth;
8212-
} else {
8213-
tooltipBounds.left = monitorBounds.x;
8214-
}
8215-
tooltipWidth = monitorBoundsRightEnd - tooltipBounds.left;
8216-
}
8217-
return new Rectangle(tooltipBounds.left, tooltipBounds.top, tooltipWidth, tooltipHeight);
8218-
}
8219-
8220-
private Rectangle getContainingMonitorBoundsInSingleZoomCoordinateSystem(Point point) {
8221-
int zoom = getZoom();
8222-
point = DPIUtil.scaleUp(point, zoom);
8223-
for (Monitor monitor : display.getMonitors()) {
8224-
Rectangle monitorBounds = DPIUtil.scaleUp(monitor.getBounds(), zoom);
8225-
if (monitorBounds.contains(point)) {
8226-
return monitorBounds;
8227-
}
8228-
}
8229-
return null;
8230-
}
8231-
82328194
private Rectangle getContainingMonitorBoundsInMultiZoomCoordinateSystem(MonitorAwarePoint point) {
82338195
Monitor monitor = point.getMonitor();
82348196
return new Rectangle(monitor.x, monitor.y, DPIUtil.scaleUp(monitor.width, monitor.zoom),

0 commit comments

Comments
 (0)