@@ -47,7 +47,12 @@ public class MenuItem extends Item {
47
47
/* Image margin. */
48
48
final static int MARGIN_WIDTH = 1 ;
49
49
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 ;
51
56
static {
52
57
DPIZoomChangeRegistry .registerHandler (MenuItem ::handleDPIChange , MenuItem .class );
53
58
}
@@ -1121,10 +1126,32 @@ LRESULT wmCommandChild (long wParam, long lParam) {
1121
1126
return null ;
1122
1127
}
1123
1128
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 )) {
1128
1155
GCData data = new GCData ();
1129
1156
data .device = display ;
1130
1157
GC gc = createNewGC (struct .hDC , data );
@@ -1134,14 +1161,50 @@ LRESULT wmDrawChild (long wParam, long lParam) {
1134
1161
* coordinate. The fix is to ignore this value when
1135
1162
* the item is in a menu bar.
1136
1163
*/
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 );
1143
1207
}
1144
- if (parent .foreground != -1 ) OS .SetTextColor (struct .hDC , parent .foreground );
1145
1208
return null ;
1146
1209
}
1147
1210
@@ -1151,17 +1214,19 @@ LRESULT wmMeasureChild (long wParam, long lParam) {
1151
1214
1152
1215
if ((parent .style & SWT .BAR ) != 0 ) {
1153
1216
if (parent .needsMenuCallback ()) {
1217
+ Point point = calculateRenderedTextSize ();
1218
+ int menuZoom = getDisplay ().isRescalingAtRuntime () ? super .getZoom () : getMonitorZoom ();
1219
+ struct .itemHeight = DPIUtil .scaleUp (point .y , menuZoom );
1154
1220
/*
1155
1221
* Weirdness in Windows. Setting `HBMMENU_CALLBACK` causes
1156
1222
* item sizes to mean something else. It seems that it is
1157
1223
* the size of left margin before the text. At the same time,
1158
1224
* if menu item has a mnemonic, it's always drawn at a fixed
1159
1225
* position. I have tested on Win7, Win8.1, Win10 and found
1160
1226
* 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.
1163
1227
*/
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 );
1165
1230
OS .MoveMemory (lParam , struct , MEASUREITEMSTRUCT .sizeof );
1166
1231
return null ;
1167
1232
}
@@ -1205,6 +1270,32 @@ LRESULT wmMeasureChild (long wParam, long lParam) {
1205
1270
return null ;
1206
1271
}
1207
1272
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
+
1208
1299
private static final class MenuItemToolTip extends ToolTip {
1209
1300
1210
1301
public MenuItemToolTip (Shell parent ) {
0 commit comments