Skip to content

[win32] Add workaround for missing selection indicator for menu item with image on Win11 (#501) #2143

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,7 @@ public class OS extends C {
public static final int MF_SEPARATOR = 0x800;
public static final int MF_SYSMENU = 0x2000;
public static final int MF_UNCHECKED = 0x0;
public static final int MIIM_CHECKMARKS = 0x8;
public static final int MIIM_BITMAP = 0x80;
public static final int MIIM_DATA = 0x20;
public static final int MIIM_FTYPE = 0x100;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.*;
import org.eclipse.swt.internal.win32.*;
import org.eclipse.swt.internal.win32.version.*;

/**
* Instances of this class represent a selectable user interface object
Expand All @@ -42,6 +43,8 @@
public class MenuItem extends Item {
Menu parent, menu;
long hBitmap;
Image imageSelected;
long hBitmapSelected;
int id, accelerator, userId;
ToolTip itemToolTip;
/* Image margin. */
Expand All @@ -53,6 +56,11 @@ public class MenuItem extends Item {
// value in wmMeasureChild is increased by a fixed value (in points) when wmDrawChild is called
// This static is used to mitigate this increase
private final static int WINDOWS_OVERHEAD = 6;
// Workaround for: selection indicator is missing for menu item with image on Win11 (#501)
// 0= off/system behavior; 1= no image if selected; 2= with overlay marker (default)
private final static int CUSTOM_SELECTION_IMAGE = (OsVersion.IS_WIN11_21H2) ?
Integer.getInteger("org.eclipse.swt.internal.win32.menu.customSelectionImage", 2) : 0;

static {
DPIZoomChangeRegistry.registerHandler(MenuItem::handleDPIChange, MenuItem.class);
}
Expand Down Expand Up @@ -543,6 +551,12 @@ void releaseWidget () {
super.releaseWidget ();
if (hBitmap != 0) OS.DeleteObject (hBitmap);
hBitmap = 0;
if (hBitmapSelected != 0) OS.DeleteObject (hBitmapSelected);
hBitmapSelected = 0;
if (imageSelected != null) {
imageSelected.dispose();
imageSelected = null;
}
if (accelerator != 0) {
parent.destroyAccelerators ();
}
Expand Down Expand Up @@ -774,14 +788,34 @@ public void setImage (Image image) {
if (this.image == image) return;
if ((style & SWT.SEPARATOR) != 0) return;
super.setImage (image);
if (imageSelected != null) {
imageSelected.dispose();
imageSelected = null;
}
if ((style & (SWT.CHECK | SWT.RADIO)) != 0 && CUSTOM_SELECTION_IMAGE > 1
&& image != null && getSelection()) {
initCustomSelectedImage();
}
updateImage();
}

private void updateImage () {
MENUITEMINFO info = new MENUITEMINFO ();
info.cbSize = MENUITEMINFO.sizeof;
info.fMask = OS.MIIM_BITMAP;
if (parent.needsMenuCallback()) {
info.hbmpItem = OS.HBMMENU_CALLBACK;
} else {
if (OS.IsAppThemed ()) {
info.hbmpItem = hBitmap = getMenuItemIconBitmapHandle(image);
hBitmap = getMenuItemIconBitmapHandle(image);
if ((style & (SWT.CHECK | SWT.RADIO)) != 0 && CUSTOM_SELECTION_IMAGE > 0) {
info.fMask |= OS.MIIM_CHECKMARKS;
info.hbmpUnchecked = hBitmap;
info.hbmpChecked = getMenuItemIconSelectedBitmapHandle();
}
else {
info.hbmpItem = hBitmap;
}
} else {
info.hbmpItem = image != null ? OS.HBMMENU_CALLBACK : 0;
}
Expand All @@ -791,16 +825,84 @@ public void setImage (Image image) {
parent.redraw ();
}

private void initCustomSelectedImage() {
Image image = this.image;
if (image == null) {
return;
}
Rectangle imageBounds = image.getBounds();
Color foregroundColor = increaseContrast((display.menuBarForegroundPixel != -1) ? Color.win32_new (this.display, display.menuBarForegroundPixel) : parent.getForeground());
Color backgroundColor = increaseContrast((display.menuBarBackgroundPixel != -1) ? Color.win32_new (this.display, display.menuBarBackgroundPixel) : parent.getBackground());
ImageGcDrawer drawer = new ImageGcDrawer() {
@Override
public int getGcStyle() {
return SWT.TRANSPARENT;
}
@Override
public void drawOn(GC gc, int imageWidth, int imageHeight) {
gc.setAdvanced(true);
gc.drawImage(image, imageWidth - imageBounds.width, (imageHeight - imageBounds.height) / 2);
int x0 = imageWidth - 16;
int y0 = imageHeight / 2 - 8;
if (((style & SWT.CHECK) != 0)) {
int[] points = new int[] { x0 + 4, y0 + 10, x0 + 6, y0 + 12, x0 + 12, y0 + 6 };
gc.setAntialias(SWT.ON);
gc.setLineStyle(SWT.LINE_SOLID);
gc.setForeground(backgroundColor);
gc.setLineCap(SWT.CAP_ROUND);
gc.setLineJoin(SWT.JOIN_ROUND);
gc.setAlpha(127);
gc.setLineWidth(6);
gc.drawPolyline(points);
gc.setLineJoin(SWT.JOIN_MITER);
gc.setAlpha(255);
gc.setLineWidth(3);
gc.drawPolyline(points);
gc.setForeground(foregroundColor);
gc.setLineWidth(1);
gc.setLineCap(SWT.CAP_FLAT);
gc.drawPolyline(points);
}
else {
gc.setAntialias(SWT.ON);
gc.setBackground(backgroundColor);
gc.setAlpha(127);
gc.fillOval(x0 + 4, y0 + 5, 8, 8);
gc.setAlpha(255);
gc.fillOval(x0 + 5, y0 + 6, 6, 6);
gc.setBackground(foregroundColor);
gc.fillOval(x0 + 6, y0 + 7, 4, 4);
}
}
};
imageSelected = new Image(image.getDevice(), drawer,
Math.max(imageBounds.width, 16), Math.max(imageBounds.height, 16));
}

private Color increaseContrast(Color color) {
return (color.getRed() + color.getGreen() + color.getBlue() > 127 * 3) ? display.getSystemColor(SWT.COLOR_WHITE) : color;
}

private long getMenuItemIconBitmapHandle(Image image) {
if (image == null) {
return 0;
}
if (hBitmap != 0) OS.DeleteObject (hBitmap);
int zoom = adaptZoomForMenuItem(getZoom());
int zoom = adaptZoomForMenuItem(getZoom(), image);
return Display.create32bitDIB (image, zoom);
}

private int adaptZoomForMenuItem(int currentZoom) {
private long getMenuItemIconSelectedBitmapHandle() {
Image image = imageSelected;
if (image == null) {
return 0;
}
if (hBitmapSelected != 0) OS.DeleteObject (hBitmapSelected);
int zoom = adaptZoomForMenuItem(getZoom(), image);
return hBitmapSelected = Display.create32bitDIB (image, zoom);
}

private int adaptZoomForMenuItem(int currentZoom, Image image) {
int primaryMonitorZoomAtAppStartUp = getPrimaryMonitorZoomAtStartup();
/*
* Windows has inconsistent behavior when setting the size of MenuItem image and
Expand Down Expand Up @@ -985,6 +1087,14 @@ public void setSelection (boolean selected) {
if (!success) error (SWT.ERROR_CANNOT_SET_SELECTION);
info.fState &= ~OS.MFS_CHECKED;
if (selected) info.fState |= OS.MFS_CHECKED;

if (selected && CUSTOM_SELECTION_IMAGE > 1 && hBitmap != 0 && imageSelected == null) {
initCustomSelectedImage();
info.fMask |= OS.MIIM_CHECKMARKS;
info.hbmpUnchecked = hBitmap;
info.hbmpChecked = getMenuItemIconSelectedBitmapHandle();
}

success = OS.SetMenuItemInfo (hMenu, id, false, info);
if (!success) {
/*
Expand Down Expand Up @@ -1350,12 +1460,9 @@ private static void handleDPIChange(Widget widget, int newZoom, float scalingFac
if (!(widget instanceof MenuItem menuItem)) {
return;
}
// Refresh the image
Image menuItemImage = menuItem.getImage();
if (menuItemImage != null) {
Image currentImage = menuItemImage;
menuItem.image = null;
menuItem.setImage (currentImage);
// Refresh the image(s)
if (menuItem.getImage() != null) {
((MenuItem)menuItem).updateImage();
}
// Refresh the sub menu
Menu subMenu = menuItem.getMenu();
Expand Down
Loading