19
19
import org .eclipse .swt .graphics .*;
20
20
import org .eclipse .swt .internal .*;
21
21
import org .eclipse .swt .internal .win32 .*;
22
+ import org .eclipse .swt .internal .win32 .version .*;
22
23
23
24
/**
24
25
* Instances of this class represent a selectable user interface object
42
43
public class MenuItem extends Item {
43
44
Menu parent , menu ;
44
45
long hBitmap ;
46
+ Image imageSelected ;
47
+ long hBitmapSelected ;
45
48
int id , accelerator , userId ;
46
49
ToolTip itemToolTip ;
47
50
/* Image margin. */
@@ -53,6 +56,11 @@ public class MenuItem extends Item {
53
56
// value in wmMeasureChild is increased by a fixed value (in points) when wmDrawChild is called
54
57
// This static is used to mitigate this increase
55
58
private final static int WINDOWS_OVERHEAD = 6 ;
59
+ // Workaround for: selection indicator is missing for menu item with image on Win11 (#501)
60
+ // 0= off/system behavior; 1= no image if selected; 2= with overlay marker (default)
61
+ private final static int CUSTOM_SELECTION_IMAGE = (OsVersion .IS_WIN11_21H2 ) ?
62
+ Integer .getInteger ("org.eclipse.swt.internal.win32.menu.customSelectionImage" , 2 ) : 0 ;
63
+
56
64
static {
57
65
DPIZoomChangeRegistry .registerHandler (MenuItem ::handleDPIChange , MenuItem .class );
58
66
}
@@ -543,6 +551,12 @@ void releaseWidget () {
543
551
super .releaseWidget ();
544
552
if (hBitmap != 0 ) OS .DeleteObject (hBitmap );
545
553
hBitmap = 0 ;
554
+ if (hBitmapSelected != 0 ) OS .DeleteObject (hBitmapSelected );
555
+ hBitmapSelected = 0 ;
556
+ if (imageSelected != null ) {
557
+ imageSelected .dispose ();
558
+ imageSelected = null ;
559
+ }
546
560
if (accelerator != 0 ) {
547
561
parent .destroyAccelerators ();
548
562
}
@@ -774,14 +788,34 @@ public void setImage (Image image) {
774
788
if (this .image == image ) return ;
775
789
if ((style & SWT .SEPARATOR ) != 0 ) return ;
776
790
super .setImage (image );
791
+ if (imageSelected != null ) {
792
+ imageSelected .dispose ();
793
+ imageSelected = null ;
794
+ }
795
+ if ((style & (SWT .CHECK | SWT .RADIO )) != 0 && CUSTOM_SELECTION_IMAGE > 1
796
+ && image != null && getSelection ()) {
797
+ initCustomSelectedImage ();
798
+ }
799
+ updateImage ();
800
+ }
801
+
802
+ private void updateImage () {
777
803
MENUITEMINFO info = new MENUITEMINFO ();
778
804
info .cbSize = MENUITEMINFO .sizeof ;
779
805
info .fMask = OS .MIIM_BITMAP ;
780
806
if (parent .needsMenuCallback ()) {
781
807
info .hbmpItem = OS .HBMMENU_CALLBACK ;
782
808
} else {
783
809
if (OS .IsAppThemed ()) {
784
- info .hbmpItem = hBitmap = getMenuItemIconBitmapHandle (image );
810
+ hBitmap = getMenuItemIconBitmapHandle (image );
811
+ if ((style & (SWT .CHECK | SWT .RADIO )) != 0 && CUSTOM_SELECTION_IMAGE > 0 ) {
812
+ info .fMask |= OS .MIIM_CHECKMARKS ;
813
+ info .hbmpUnchecked = hBitmap ;
814
+ info .hbmpChecked = getMenuItemIconSelectedBitmapHandle ();
815
+ }
816
+ else {
817
+ info .hbmpItem = hBitmap ;
818
+ }
785
819
} else {
786
820
info .hbmpItem = image != null ? OS .HBMMENU_CALLBACK : 0 ;
787
821
}
@@ -791,16 +825,84 @@ public void setImage (Image image) {
791
825
parent .redraw ();
792
826
}
793
827
828
+ private void initCustomSelectedImage () {
829
+ Image image = this .image ;
830
+ if (image == null ) {
831
+ return ;
832
+ }
833
+ Rectangle imageBounds = image .getBounds ();
834
+ Color foregroundColor = increaseContrast ((display .menuBarForegroundPixel != -1 ) ? Color .win32_new (this .display , display .menuBarForegroundPixel ) : parent .getForeground ());
835
+ Color backgroundColor = increaseContrast ((display .menuBarBackgroundPixel != -1 ) ? Color .win32_new (this .display , display .menuBarBackgroundPixel ) : parent .getBackground ());
836
+ ImageGcDrawer drawer = new ImageGcDrawer () {
837
+ @ Override
838
+ public int getGcStyle () {
839
+ return SWT .TRANSPARENT ;
840
+ }
841
+ @ Override
842
+ public void drawOn (GC gc , int imageWidth , int imageHeight ) {
843
+ gc .setAdvanced (true );
844
+ gc .drawImage (image , imageWidth - imageBounds .width , (imageHeight - imageBounds .height ) / 2 );
845
+ int x0 = imageWidth - 16 ;
846
+ int y0 = imageHeight / 2 - 8 ;
847
+ if (((style & SWT .CHECK ) != 0 )) {
848
+ int [] points = new int [] { x0 + 4 , y0 + 10 , x0 + 6 , y0 + 12 , x0 + 12 , y0 + 6 };
849
+ gc .setAntialias (SWT .ON );
850
+ gc .setLineStyle (SWT .LINE_SOLID );
851
+ gc .setForeground (backgroundColor );
852
+ gc .setLineCap (SWT .CAP_ROUND );
853
+ gc .setLineJoin (SWT .JOIN_ROUND );
854
+ gc .setAlpha (127 );
855
+ gc .setLineWidth (6 );
856
+ gc .drawPolyline (points );
857
+ gc .setLineJoin (SWT .JOIN_MITER );
858
+ gc .setAlpha (255 );
859
+ gc .setLineWidth (3 );
860
+ gc .drawPolyline (points );
861
+ gc .setForeground (foregroundColor );
862
+ gc .setLineWidth (1 );
863
+ gc .setLineCap (SWT .CAP_FLAT );
864
+ gc .drawPolyline (points );
865
+ }
866
+ else {
867
+ gc .setAntialias (SWT .ON );
868
+ gc .setBackground (backgroundColor );
869
+ gc .setAlpha (127 );
870
+ gc .fillOval (x0 + 4 , y0 + 5 , 8 , 8 );
871
+ gc .setAlpha (255 );
872
+ gc .fillOval (x0 + 5 , y0 + 6 , 6 , 6 );
873
+ gc .setBackground (foregroundColor );
874
+ gc .fillOval (x0 + 6 , y0 + 7 , 4 , 4 );
875
+ }
876
+ }
877
+ };
878
+ imageSelected = new Image (image .getDevice (), drawer ,
879
+ Math .max (imageBounds .width , 16 ), Math .max (imageBounds .height , 16 ));
880
+ }
881
+
882
+ private Color increaseContrast (Color color ) {
883
+ return (color .getRed () + color .getGreen () + color .getBlue () > 127 * 3 ) ? display .getSystemColor (SWT .COLOR_WHITE ) : color ;
884
+ }
885
+
794
886
private long getMenuItemIconBitmapHandle (Image image ) {
795
887
if (image == null ) {
796
888
return 0 ;
797
889
}
798
890
if (hBitmap != 0 ) OS .DeleteObject (hBitmap );
799
- int zoom = adaptZoomForMenuItem (getZoom ());
891
+ int zoom = adaptZoomForMenuItem (getZoom (), image );
800
892
return Display .create32bitDIB (image , zoom );
801
893
}
802
894
803
- private int adaptZoomForMenuItem (int currentZoom ) {
895
+ private long getMenuItemIconSelectedBitmapHandle () {
896
+ Image image = imageSelected ;
897
+ if (image == null ) {
898
+ return 0 ;
899
+ }
900
+ if (hBitmapSelected != 0 ) OS .DeleteObject (hBitmapSelected );
901
+ int zoom = adaptZoomForMenuItem (getZoom (), image );
902
+ return hBitmapSelected = Display .create32bitDIB (image , zoom );
903
+ }
904
+
905
+ private int adaptZoomForMenuItem (int currentZoom , Image image ) {
804
906
int primaryMonitorZoomAtAppStartUp = getPrimaryMonitorZoomAtStartup ();
805
907
/*
806
908
* Windows has inconsistent behavior when setting the size of MenuItem image and
@@ -985,6 +1087,14 @@ public void setSelection (boolean selected) {
985
1087
if (!success ) error (SWT .ERROR_CANNOT_SET_SELECTION );
986
1088
info .fState &= ~OS .MFS_CHECKED ;
987
1089
if (selected ) info .fState |= OS .MFS_CHECKED ;
1090
+
1091
+ if (selected && CUSTOM_SELECTION_IMAGE > 1 && hBitmap != 0 && imageSelected == null ) {
1092
+ initCustomSelectedImage ();
1093
+ info .fMask |= OS .MIIM_CHECKMARKS ;
1094
+ info .hbmpUnchecked = hBitmap ;
1095
+ info .hbmpChecked = getMenuItemIconSelectedBitmapHandle ();
1096
+ }
1097
+
988
1098
success = OS .SetMenuItemInfo (hMenu , id , false , info );
989
1099
if (!success ) {
990
1100
/*
@@ -1350,12 +1460,9 @@ private static void handleDPIChange(Widget widget, int newZoom, float scalingFac
1350
1460
if (!(widget instanceof MenuItem menuItem )) {
1351
1461
return ;
1352
1462
}
1353
- // Refresh the image
1354
- Image menuItemImage = menuItem .getImage ();
1355
- if (menuItemImage != null ) {
1356
- Image currentImage = menuItemImage ;
1357
- menuItem .image = null ;
1358
- menuItem .setImage (currentImage );
1463
+ // Refresh the image(s)
1464
+ if (menuItem .getImage () != null ) {
1465
+ ((MenuItem )menuItem ).updateImage ();
1359
1466
}
1360
1467
// Refresh the sub menu
1361
1468
Menu subMenu = menuItem .getMenu ();
0 commit comments