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,92 @@ 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
+
842
+ @ Override
843
+ public void drawOn (GC gc , int imageWidth , int imageHeight ) {
844
+ gc .setAdvanced (true );
845
+ gc .drawImage (image , imageWidth - imageBounds .width , (imageHeight - imageBounds .height ) / 2 );
846
+ gc .setAntialias (SWT .ON );
847
+ int x = imageWidth - 16 ;
848
+ int y = imageHeight / 2 - 8 ;
849
+ if ((style & SWT .CHECK ) != 0 ) {
850
+ drawCheck (gc , foregroundColor , backgroundColor , x , y );
851
+ }
852
+ else {
853
+ drawRadio (gc , foregroundColor , backgroundColor , x , y );
854
+ }
855
+ }
856
+ };
857
+ imageSelected = new Image (image .getDevice (), drawer ,
858
+ Math .max (imageBounds .width , 16 ), Math .max (imageBounds .height , 16 ));
859
+ }
860
+
861
+ private void drawCheck (GC gc , Color foregroundColor , Color backgroundColor , int x , int y ) {
862
+ int [] points = new int [] { x + 4 , y + 10 , x + 6 , y + 12 , x + 12 , y + 6 };
863
+ gc .setLineStyle (SWT .LINE_SOLID );
864
+ gc .setForeground (backgroundColor );
865
+ gc .setLineCap (SWT .CAP_ROUND );
866
+ gc .setLineJoin (SWT .JOIN_ROUND );
867
+ gc .setAlpha (127 );
868
+ gc .setLineWidth (6 );
869
+ gc .drawPolyline (points );
870
+ gc .setLineJoin (SWT .JOIN_MITER );
871
+ gc .setAlpha (255 );
872
+ gc .setLineWidth (3 );
873
+ gc .drawPolyline (points );
874
+ gc .setForeground (foregroundColor );
875
+ gc .setLineWidth (1 );
876
+ gc .setLineCap (SWT .CAP_FLAT );
877
+ gc .drawPolyline (points );
878
+ }
879
+
880
+ private void drawRadio (GC gc , Color foregroundColor , Color backgroundColor , int x , int y ) {
881
+ gc .setBackground (backgroundColor );
882
+ gc .setAlpha (127 );
883
+ gc .fillOval (x + 4 , y + 5 , 8 , 8 );
884
+ gc .setAlpha (255 );
885
+ gc .fillOval (x + 5 , y + 6 , 6 , 6 );
886
+ gc .setBackground (foregroundColor );
887
+ gc .fillOval (x + 6 , y + 7 , 4 , 4 );
888
+ }
889
+
890
+ private Color increaseContrast (Color color ) {
891
+ return (color .getRed () + color .getGreen () + color .getBlue () > 127 * 3 ) ? display .getSystemColor (SWT .COLOR_WHITE ) : color ;
892
+ }
893
+
794
894
private long getMenuItemIconBitmapHandle (Image image ) {
795
895
if (image == null ) {
796
896
return 0 ;
797
897
}
798
898
if (hBitmap != 0 ) OS .DeleteObject (hBitmap );
799
- int zoom = adaptZoomForMenuItem (getZoom ());
899
+ int zoom = adaptZoomForMenuItem (getZoom (), image );
800
900
return Display .create32bitDIB (image , zoom );
801
901
}
802
902
803
- private int adaptZoomForMenuItem (int currentZoom ) {
903
+ private long getMenuItemIconSelectedBitmapHandle () {
904
+ Image image = imageSelected ;
905
+ if (image == null ) {
906
+ return 0 ;
907
+ }
908
+ if (hBitmapSelected != 0 ) OS .DeleteObject (hBitmapSelected );
909
+ int zoom = adaptZoomForMenuItem (getZoom (), image );
910
+ return hBitmapSelected = Display .create32bitDIB (image , zoom );
911
+ }
912
+
913
+ private int adaptZoomForMenuItem (int currentZoom , Image image ) {
804
914
int primaryMonitorZoomAtAppStartUp = getPrimaryMonitorZoomAtStartup ();
805
915
/*
806
916
* Windows has inconsistent behavior when setting the size of MenuItem image and
@@ -985,6 +1095,14 @@ public void setSelection (boolean selected) {
985
1095
if (!success ) error (SWT .ERROR_CANNOT_SET_SELECTION );
986
1096
info .fState &= ~OS .MFS_CHECKED ;
987
1097
if (selected ) info .fState |= OS .MFS_CHECKED ;
1098
+
1099
+ if (selected && CUSTOM_SELECTION_IMAGE > 1 && hBitmap != 0 && imageSelected == null ) {
1100
+ initCustomSelectedImage ();
1101
+ info .fMask |= OS .MIIM_CHECKMARKS ;
1102
+ info .hbmpUnchecked = hBitmap ;
1103
+ info .hbmpChecked = getMenuItemIconSelectedBitmapHandle ();
1104
+ }
1105
+
988
1106
success = OS .SetMenuItemInfo (hMenu , id , false , info );
989
1107
if (!success ) {
990
1108
/*
@@ -1350,12 +1468,9 @@ private static void handleDPIChange(Widget widget, int newZoom, float scalingFac
1350
1468
if (!(widget instanceof MenuItem menuItem )) {
1351
1469
return ;
1352
1470
}
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 );
1471
+ // Refresh the image(s)
1472
+ if (menuItem .getImage () != null ) {
1473
+ ((MenuItem )menuItem ).updateImage ();
1359
1474
}
1360
1475
// Refresh the sub menu
1361
1476
Menu subMenu = menuItem .getMenu ();
0 commit comments