@@ -47,7 +47,9 @@ public class MenuItem extends Item {
4747 /* Image margin. */
4848 final static int MARGIN_WIDTH = 1 ;
4949 final static int MARGIN_HEIGHT = 1 ;
50-
50+ final static int LEFT_TEXT_MARGIN = 5 ;
51+ final static int IMAGE_TEXT_GAP = 3 ;
52+ final static int WINDOWS_OVERHEAD = 5 ;
5153 static {
5254 DPIZoomChangeRegistry .registerHandler (MenuItem ::handleDPIChange , MenuItem .class );
5355 }
@@ -1121,10 +1123,32 @@ LRESULT wmCommandChild (long wParam, long lParam) {
11211123 return null ;
11221124}
11231125
1124- LRESULT wmDrawChild (long wParam , long lParam ) {
1125- DRAWITEMSTRUCT struct = new DRAWITEMSTRUCT ();
1126- OS .MoveMemory (struct , lParam , DRAWITEMSTRUCT .sizeof );
1127- if (image != null ) {
1126+ @ Override
1127+ GC createNewGC (long hDC , GCData data ) {
1128+ if (getDisplay ().isRescalingAtRuntime ()) {
1129+ return super .createNewGC (hDC , data );
1130+ } else {
1131+ data .nativeZoom = getMonitorZoom ();
1132+ return GC .win32_new (hDC , data );
1133+ }
1134+ }
1135+
1136+ private int getMonitorZoom () {
1137+ return getMenu ().getShell ().getMonitor ().zoom ;
1138+ }
1139+
1140+ private int getMenuZoom () {
1141+ if (getDisplay ().isRescalingAtRuntime ()) {
1142+ return super .getZoom ();
1143+ } else {
1144+ return DPIUtil .getZoomForAutoscaleProperty (getMonitorZoom ());
1145+ }
1146+ }
1147+
1148+ LRESULT wmDrawChild (long wParam , long lParam ) {
1149+ DRAWITEMSTRUCT struct = new DRAWITEMSTRUCT ();
1150+ OS .MoveMemory (struct , lParam , DRAWITEMSTRUCT .sizeof );
1151+ if ((text != null || image != null )) {
11281152 GCData data = new GCData ();
11291153 data .device = display ;
11301154 GC gc = createNewGC (struct .hDC , data );
@@ -1134,14 +1158,50 @@ LRESULT wmDrawChild (long wParam, long lParam) {
11341158 * coordinate. The fix is to ignore this value when
11351159 * the item is in a menu bar.
11361160 */
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 ();
1161+ int x = (parent .style & SWT .BAR ) == 0 ? MARGIN_WIDTH * 2 : struct .left ;
1162+ int zoom = getMenuZoom ();
1163+ Rectangle menuItemArea = null ;
1164+ if (text != null ) {
1165+ this .getParent ().redraw ();
1166+ int flags = SWT .DRAW_DELIMITER ;
1167+ boolean isInactive = ((struct .itemState & OS .ODS_INACTIVE ) != 0 );
1168+ boolean isSelected = ((struct .itemState & OS .ODS_SELECTED ) != 0 );
1169+ boolean isNoAccel = ((struct .itemState & OS .ODS_NOACCEL ) != 0 );
1170+
1171+ String drawnText = "" ;
1172+ if (isNoAccel ) {
1173+ drawnText = this .text .replace ("&" , "" );
1174+ } else {
1175+ drawnText = this .text ;
1176+ flags |= SWT .DRAW_MNEMONIC ;
1177+ }
1178+ Rectangle menuItemBounds = this .getBounds ();
1179+
1180+ int fillMenuWidth = DPIUtil .scaleDown (menuItemBounds .width , zoom );
1181+ int fillMenuHeight = DPIUtil .scaleDown (menuItemBounds .height , zoom );
1182+ menuItemArea = new Rectangle (DPIUtil .scaleDown (x , zoom ), DPIUtil .scaleDown (struct .top , zoom ), fillMenuWidth , fillMenuHeight );
1183+
1184+ gc .setForeground (isInactive ? display .getSystemColor (SWT .COLOR_GRAY ) : display .getSystemColor (SWT .COLOR_WHITE ));
1185+ gc .setBackground (isSelected ? display .getSystemColor (SWT .COLOR_DARK_GRAY ) : parent .getBackground ());
1186+ gc .fillRectangle (menuItemArea );
1187+
1188+ int xPositionText = LEFT_TEXT_MARGIN + DPIUtil .scaleDown (x , zoom ) + (this .image != null ? this .image .getBounds ().width + IMAGE_TEXT_GAP : 0 );
1189+ int yPositionText = DPIUtil .scaleDown (struct .top , zoom );
1190+ gc .drawText (drawnText , xPositionText , yPositionText , flags );
1191+ }
1192+ if (image != null ) {
1193+ Image image = getEnabled () ? this .image : new Image (display , this .image , SWT .IMAGE_DISABLE );
1194+ int gap = (menuItemArea .height - image .getBounds ().height )/2 ;
1195+ gc .drawImage (image , LEFT_TEXT_MARGIN + DPIUtil .scaleDown (x , zoom ), gap + DPIUtil .scaleDown (struct .top , zoom ));
1196+ if (this .image != image ) {
1197+ image .dispose ();
1198+ }
1199+ }
1200+ gc .dispose ();
1201+ }
1202+ if (parent .foreground != -1 ) {
1203+ OS .SetTextColor (struct .hDC , parent .foreground );
11431204 }
1144- if (parent .foreground != -1 ) OS .SetTextColor (struct .hDC , parent .foreground );
11451205 return null ;
11461206}
11471207
@@ -1151,17 +1211,18 @@ LRESULT wmMeasureChild (long wParam, long lParam) {
11511211
11521212 if ((parent .style & SWT .BAR ) != 0 ) {
11531213 if (parent .needsMenuCallback ()) {
1214+ Point point = calculateRenderedTextSize ();
1215+ int menuZoom = getDisplay ().isRescalingAtRuntime () ? super .getZoom () : getMonitorZoom ();
1216+ struct .itemHeight = DPIUtil .scaleUp (point .y , menuZoom );
11541217 /*
11551218 * Weirdness in Windows. Setting `HBMMENU_CALLBACK` causes
11561219 * item sizes to mean something else. It seems that it is
11571220 * the size of left margin before the text. At the same time,
11581221 * if menu item has a mnemonic, it's always drawn at a fixed
11591222 * position. I have tested on Win7, Win8.1, Win10 and found
11601223 * 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.
11631224 */
1164- struct .itemWidth = DPIUtil .scaleUp (5 , nativeZoom );
1225+ struct .itemWidth = DPIUtil .scaleUp (LEFT_TEXT_MARGIN + point . x - WINDOWS_OVERHEAD + ( this . image != null ? this . image . getBounds (). width + IMAGE_TEXT_GAP : 0 ), menuZoom );
11651226 OS .MoveMemory (lParam , struct , MEASUREITEMSTRUCT .sizeof );
11661227 return null ;
11671228 }
@@ -1205,6 +1266,34 @@ LRESULT wmMeasureChild (long wParam, long lParam) {
12051266 return null ;
12061267}
12071268
1269+ private Point calculateRenderedTextSize () {
1270+ GC gc = new GC (this .getMenu ().getShell ());
1271+ String textWithoutMnemonicCharacter = getText ().replace ("&" , "" );
1272+ Point points = gc .textExtent (textWithoutMnemonicCharacter );
1273+ gc .dispose ();
1274+
1275+ if (getDisplay ().isRescalingAtRuntime ()) {
1276+ return points ;
1277+ } else {
1278+ int primaryMonitorZoom = this .getDisplay ().getDeviceZoom ();
1279+ int adjustedPrimaryMonitorZoom = DPIUtil .getZoomForAutoscaleProperty (primaryMonitorZoom );
1280+ if (primaryMonitorZoom != adjustedPrimaryMonitorZoom ) {
1281+ // Windows will use a font matching the native primary monitor zoom for calculating the size in pixels,
1282+ // GC will use the native primary monitor zoom to scale down from pixels to points in this scenario
1283+ // Therefore we need to make sure adjust the points as if it would have been scaled down by the
1284+ // native primary monitor zoom.
1285+ // Example:
1286+ // Primary monitor on 150% with int200: native zoom 150%, adjusted zoom 100%
1287+ // Pixel height of font in this example is 15px
1288+ // GC calculated height of 15px, scales down with adjusted zoom of 100% and returns 15pt -> should be 10pt
1289+ // this calculation is corrected by the following line
1290+ // This is the only place, where the GC needs to use the native zoom to do that, therefore it is fixed only here
1291+ points = DPIUtil .scaleDown (DPIUtil .scaleUp (points , adjustedPrimaryMonitorZoom ), primaryMonitorZoom );
1292+ }
1293+ return points ;
1294+ }
1295+ }
1296+
12081297private static final class MenuItemToolTip extends ToolTip {
12091298
12101299 public MenuItemToolTip (Shell parent ) {
0 commit comments