Skip to content

Commit 30f11e9

Browse files
ShahzaibIbrahimHeikoKlare
authored andcommitted
MenuItem not aligned correctly during DPI change
When switched to Dark Theme, using gc to draw menu item, moving the responsibility from OS due to wrong scaling of menu bar vertically. Also changing the text color from a grayish tone to white. Also when ALT key is pressed mnemonics are underlined and work as a toggle, the behavior which was missing from dark theme previously.
1 parent fd55950 commit 30f11e9

File tree

3 files changed

+110
-16
lines changed

3 files changed

+110
-16
lines changed

bundles/org.eclipse.swt/Eclipse SWT PI/win32/org/eclipse/swt/internal/win32/OS.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,7 @@ public class OS extends C {
897897
public static final int MFS_CHECKED = 0x8;
898898
public static final int MFS_DISABLED = 0x3;
899899
public static final int MFS_GRAYED = 0x3;
900+
public static final int MFT_OWNERDRAW = 0x100;
900901
public static final int MFT_RADIOCHECK = 0x200;
901902
public static final int MFT_RIGHTJUSTIFY = 0x4000;
902903
public static final int MFT_RIGHTORDER = 0x2000;
@@ -1019,6 +1020,8 @@ public class OS extends C {
10191020
public static final int OBJ_PEN = 0x1;
10201021
public static final int OBM_CHECKBOXES = 0x7ff7;
10211022
public static final int ODS_SELECTED = 0x1;
1023+
public static final int ODS_NOACCEL = 0x0100;
1024+
public static final int ODS_INACTIVE = 0x80;
10221025
public static final int ODT_MENU = 0x1;
10231026
public static final int OIC_BANG = 0x7F03;
10241027
public static final int OIC_HAND = 0x7F01;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ void createItem (MenuItem item, int index) {
358358
info.fMask = OS.MIIM_ID | OS.MIIM_TYPE | OS.MIIM_DATA;
359359
info.wID = item.id;
360360
info.dwItemData = item.id;
361-
info.fType = item.widgetStyle ();
361+
info.fType = (style & SWT.BAR) != 0 && needsMenuCallback() ? OS.MFT_OWNERDRAW : item.widgetStyle ();
362362
info.dwTypeData = pszText;
363363
boolean success = OS.InsertMenuItem (handle, index, true, info);
364364
if (pszText != 0) OS.HeapFree (hHeap, 0, pszText);

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

Lines changed: 106 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@ public class MenuItem extends Item {
4747
/* Image margin. */
4848
final static int MARGIN_WIDTH = 1;
4949
final static int MARGIN_HEIGHT = 1;
50-
50+
private final static int LEFT_TEXT_MARGIN = 7;
51+
private final static int IMAGE_TEXT_GAP = 3;
52+
// There is a weird behavior in the Windows API with menus in OWENERDRAW mode that the returned
53+
// value in wmMeasureChild is increased by a fixed value (in points) when wmDrawChild is called
54+
// This static is used to mitigate this increase
55+
private final static int WINDOWS_OVERHEAD = 6;
5156
static {
5257
DPIZoomChangeRegistry.registerHandler(MenuItem::handleDPIChange, MenuItem.class);
5358
}
@@ -1121,10 +1126,32 @@ LRESULT wmCommandChild (long wParam, long lParam) {
11211126
return null;
11221127
}
11231128

1124-
LRESULT wmDrawChild (long wParam, long lParam) {
1125-
DRAWITEMSTRUCT struct = new DRAWITEMSTRUCT ();
1126-
OS.MoveMemory (struct, lParam, DRAWITEMSTRUCT.sizeof);
1127-
if (image != null) {
1129+
@Override
1130+
GC createNewGC(long hDC, GCData data) {
1131+
if (getDisplay().isRescalingAtRuntime()) {
1132+
return super.createNewGC(hDC, data);
1133+
} else {
1134+
data.nativeZoom = getMonitorZoom();
1135+
return GC.win32_new(hDC, data);
1136+
}
1137+
}
1138+
1139+
private int getMonitorZoom() {
1140+
return getMenu().getShell().getMonitor().zoom;
1141+
}
1142+
1143+
private int getMenuZoom() {
1144+
if (getDisplay().isRescalingAtRuntime()) {
1145+
return super.getZoom();
1146+
} else {
1147+
return DPIUtil.getZoomForAutoscaleProperty(getMonitorZoom());
1148+
}
1149+
}
1150+
1151+
LRESULT wmDrawChild(long wParam, long lParam) {
1152+
DRAWITEMSTRUCT struct = new DRAWITEMSTRUCT();
1153+
OS.MoveMemory(struct, lParam, DRAWITEMSTRUCT.sizeof);
1154+
if ((text != null || image != null)) {
11281155
GCData data = new GCData();
11291156
data.device = display;
11301157
GC gc = createNewGC(struct.hDC, data);
@@ -1134,14 +1161,50 @@ LRESULT wmDrawChild (long wParam, long lParam) {
11341161
* coordinate. The fix is to ignore this value when
11351162
* the item is in a menu bar.
11361163
*/
1137-
int x = (parent.style & SWT.BAR) != 0 ? MARGIN_WIDTH * 2 : struct.left;
1138-
Image image = getEnabled () ? this.image : new Image (display, this.image, SWT.IMAGE_DISABLE);
1139-
int zoom = getZoom();
1140-
gc.drawImage (image, DPIUtil.scaleDown(x, zoom), DPIUtil.scaleDown(struct.top + MARGIN_HEIGHT, zoom));
1141-
if (this.image != image) image.dispose ();
1142-
gc.dispose ();
1164+
int x = (parent.style & SWT.BAR) == 0 ? MARGIN_WIDTH * 2 : struct.left;
1165+
int zoom = getMenuZoom();
1166+
Rectangle menuItemArea = null;
1167+
if (text != null) {
1168+
this.getParent().redraw();
1169+
int flags = SWT.DRAW_DELIMITER;
1170+
boolean isInactive = ((struct.itemState & OS.ODS_INACTIVE) != 0);
1171+
boolean isSelected = ((struct.itemState & OS.ODS_SELECTED) != 0);
1172+
boolean isNoAccel = ((struct.itemState & OS.ODS_NOACCEL) != 0);
1173+
1174+
String drawnText = "";
1175+
if(isNoAccel) {
1176+
drawnText = this.text.replace("&", "");
1177+
} else {
1178+
drawnText = this.text;
1179+
flags |= SWT.DRAW_MNEMONIC;
1180+
}
1181+
Rectangle menuItemBounds = this.getBounds();
1182+
1183+
int fillMenuWidth = DPIUtil.scaleDown(menuItemBounds.width, zoom);
1184+
int fillMenuHeight = DPIUtil.scaleDown(menuItemBounds.height, zoom);
1185+
menuItemArea = new Rectangle(DPIUtil.scaleDown(x, zoom), DPIUtil.scaleDown(struct.top, zoom), fillMenuWidth, fillMenuHeight);
1186+
1187+
gc.setForeground(isInactive ? display.getSystemColor(SWT.COLOR_GRAY) : display.getSystemColor(SWT.COLOR_WHITE));
1188+
gc.setBackground(isSelected ? display.getSystemColor(SWT.COLOR_DARK_GRAY) : parent.getBackground());
1189+
gc.fillRectangle(menuItemArea);
1190+
1191+
int xPositionText = LEFT_TEXT_MARGIN + DPIUtil.scaleDown(x, zoom) + (this.image != null ? this.image.getBounds().width + IMAGE_TEXT_GAP : 0);
1192+
int yPositionText = DPIUtil.scaleDown(struct.top, zoom) + MARGIN_HEIGHT;
1193+
gc.drawText(drawnText, xPositionText, yPositionText, flags);
1194+
}
1195+
if (image != null) {
1196+
Image image = getEnabled() ? this.image : new Image(display, this.image, SWT.IMAGE_DISABLE);
1197+
int gap = (menuItemArea.height - image.getBounds().height) / 2;
1198+
gc.drawImage(image, LEFT_TEXT_MARGIN + DPIUtil.scaleDown(x, zoom), gap + DPIUtil.scaleDown(struct.top, zoom));
1199+
if (this.image != image) {
1200+
image.dispose();
1201+
}
1202+
}
1203+
gc.dispose();
1204+
}
1205+
if (parent.foreground != -1) {
1206+
OS.SetTextColor(struct.hDC, parent.foreground);
11431207
}
1144-
if (parent.foreground != -1) OS.SetTextColor (struct.hDC, parent.foreground);
11451208
return null;
11461209
}
11471210

@@ -1151,17 +1214,19 @@ LRESULT wmMeasureChild (long wParam, long lParam) {
11511214

11521215
if ((parent.style & SWT.BAR) != 0) {
11531216
if (parent.needsMenuCallback()) {
1217+
Point point = calculateRenderedTextSize();
1218+
int menuZoom = getDisplay().isRescalingAtRuntime() ? super.getZoom() : getMonitorZoom();
1219+
struct.itemHeight = DPIUtil.scaleUp(point.y, menuZoom);
11541220
/*
11551221
* Weirdness in Windows. Setting `HBMMENU_CALLBACK` causes
11561222
* item sizes to mean something else. It seems that it is
11571223
* the size of left margin before the text. At the same time,
11581224
* if menu item has a mnemonic, it's always drawn at a fixed
11591225
* position. I have tested on Win7, Win8.1, Win10 and found
11601226
* that value of 5 works well in matching text to mnemonic.
1161-
* NOTE: autoScaleUpUsingNativeDPI() is used to avoid problems
1162-
* with applications that disable automatic scaling.
11631227
*/
1164-
struct.itemWidth = DPIUtil.scaleUp(5, nativeZoom);
1228+
int horizontalSpaceImage = this.image != null ? this.image.getBounds().width + IMAGE_TEXT_GAP: 0;
1229+
struct.itemWidth = DPIUtil.scaleUp(LEFT_TEXT_MARGIN + point.x - WINDOWS_OVERHEAD + horizontalSpaceImage, menuZoom);
11651230
OS.MoveMemory (lParam, struct, MEASUREITEMSTRUCT.sizeof);
11661231
return null;
11671232
}
@@ -1205,6 +1270,32 @@ LRESULT wmMeasureChild (long wParam, long lParam) {
12051270
return null;
12061271
}
12071272

1273+
private Point calculateRenderedTextSize() {
1274+
GC gc = new GC(this.getMenu().getShell());
1275+
String textWithoutMnemonicCharacter = getText().replace("&", "");
1276+
Point points = gc.textExtent(textWithoutMnemonicCharacter);
1277+
gc.dispose();
1278+
1279+
if (!getDisplay().isRescalingAtRuntime()) {
1280+
int primaryMonitorZoom = this.getDisplay().getDeviceZoom();
1281+
int adjustedPrimaryMonitorZoom = DPIUtil.getZoomForAutoscaleProperty(primaryMonitorZoom);
1282+
if (primaryMonitorZoom != adjustedPrimaryMonitorZoom) {
1283+
// Windows will use a font matching the native primary monitor zoom for calculating the size in pixels,
1284+
// GC will use the native primary monitor zoom to scale down from pixels to points in this scenario
1285+
// Therefore we need to make sure adjust the points as if it would have been scaled down by the
1286+
// native primary monitor zoom.
1287+
// Example:
1288+
// Primary monitor on 150% with int200: native zoom 150%, adjusted zoom 100%
1289+
// Pixel height of font in this example is 15px
1290+
// GC calculated height of 15px, scales down with adjusted zoom of 100% and returns 15pt -> should be 10pt
1291+
// this calculation is corrected by the following line
1292+
// This is the only place, where the GC needs to use the native zoom to do that, therefore it is fixed only here
1293+
points = DPIUtil.scaleDown(DPIUtil.scaleUp(points, adjustedPrimaryMonitorZoom), primaryMonitorZoom);
1294+
}
1295+
}
1296+
return points;
1297+
}
1298+
12081299
private static final class MenuItemToolTip extends ToolTip {
12091300

12101301
public MenuItemToolTip(Shell parent) {

0 commit comments

Comments
 (0)