From 046c67c03521e3fd5f0a8f17628b62a2177c7ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=8A?= =?UTF-8?q?=D1=80=20=D0=9A=D1=83=D1=80=D1=82=D0=B0=D0=BA=D0=BE=D0=B2?= Date: Fri, 6 Feb 2026 09:41:37 +0200 Subject: [PATCH 1/5] [Gtk] One more usless inPixel method removed --- .../gtk/org/eclipse/swt/widgets/Composite.java | 2 +- .../Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java | 5 ----- .../Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java | 8 ++++---- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Composite.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Composite.java index 0a0bbb4be00..cf0cba3c5d6 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Composite.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Composite.java @@ -1447,7 +1447,7 @@ void printWidget (GC gc, long drawable, int depth, int x, int y) { for (int i=children.length-1; i>=0; --i) { Control child = children [i]; if (child.getVisible ()) { - Point location = child.getLocationInPixels (); + Point location = child.getLocation (); child.printWidget (gc, drawable, depth, x + location.x, y + location.y); } } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java index c3cb9bc850a..656b19e191f 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java @@ -1226,11 +1226,6 @@ int setBounds (int x, int y, int width, int height, boolean move, boolean resize * */ public Point getLocation () { - checkWidget(); - return getLocationInPixels(); -} - -Point getLocationInPixels () { checkWidget(); long topHandle = topHandle (); GtkAllocation allocation = new GtkAllocation (); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java index 728d728efa9..0b31200b270 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java @@ -1270,7 +1270,7 @@ public boolean getFullScreen () { } @Override -Point getLocationInPixels () { +public Point getLocation() { checkWidget (); // Bug in GTK: when shell is moved and then hidden, its location does not get updated. // Move it before getting its location. @@ -3044,7 +3044,7 @@ public void setVisible (boolean visible) { opened = true; if (!moved) { moved = true; - Point location = getLocationInPixels(); + Point location = getLocation(); oldX = location.x; oldY = location.y; sendEvent (SWT.Move); @@ -3495,7 +3495,7 @@ Point getWindowOrigin () { * window trims etc. from the window manager. That's why getLocation () * is not safe to use for coordinate mappings after the shell has been made visible. */ - return getLocationInPixels (); + return getLocation (); } return super.getWindowOrigin( ); } @@ -3503,7 +3503,7 @@ Point getWindowOrigin () { @Override Point getSurfaceOrigin () { if (!mapped) { - return getLocationInPixels (); + return getLocation (); } return super.getSurfaceOrigin( ); } From f522c1a2dcaa4a3157f8ec2fdd543462d7d8cde9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 16:47:54 +0000 Subject: [PATCH 2/5] Initial plan From b4e38b0a75e9fe3d2cf54355b60556b8e2f81dd8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 16:55:30 +0000 Subject: [PATCH 3/5] Add GTK4 native API for custom menu widgets Co-authored-by: akurtakov <574788+akurtakov@users.noreply.github.com> --- .../Eclipse SWT PI/gtk/library/gtk4.c | 16 +++ .../org/eclipse/swt/internal/gtk4/GTK4.java | 7 ++ .../gtk/org/eclipse/swt/widgets/MenuItem.java | 101 +++++++++++++++++- 3 files changed, 121 insertions(+), 3 deletions(-) diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4.c b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4.c index 92f105cd0c6..8595323a425 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4.c +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4.c @@ -2160,6 +2160,22 @@ JNIEXPORT jlong JNICALL GTK4_NATIVE(gtk_1popover_1menu_1new_1from_1model_1full) } #endif +#ifndef NO_gtk_1popover_1menu_1add_1child +JNIEXPORT jboolean JNICALL GTK4_NATIVE(gtk_1popover_1menu_1add_1child) + (JNIEnv *env, jclass that, jlong arg0, jlong arg1, jbyteArray arg2) +{ + jbyte *lparg2=NULL; + jboolean rc = 0; + GTK4_NATIVE_ENTER(env, that, gtk_1popover_1menu_1add_1child_FUNC); + if (arg2) if ((lparg2 = (*env)->GetByteArrayElements(env, arg2, NULL)) == NULL) goto fail; + rc = (jboolean)gtk_popover_menu_add_child((GtkPopoverMenu *)arg0, (GtkWidget *)arg1, (const char *)lparg2); +fail: + if (arg2 && lparg2) (*env)->ReleaseByteArrayElements(env, arg2, lparg2, 0); + GTK4_NATIVE_EXIT(env, that, gtk_1popover_1menu_1add_1child_FUNC); + return rc; +} +#endif + #ifndef NO_gtk_1popover_1menu_1set_1menu_1model JNIEXPORT void JNICALL GTK4_NATIVE(gtk_1popover_1menu_1set_1menu_1model) (JNIEnv *env, jclass that, jlong arg0, jlong arg1) diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk4/GTK4.java b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk4/GTK4.java index afdfa87e44a..be6107292fc 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk4/GTK4.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk4/GTK4.java @@ -551,6 +551,13 @@ public class GTK4 { /** @param popover cast=(GtkPopover *) */ public static final native void gtk_popover_set_has_arrow(long popover, boolean has_arrow); + /** + * @param popover cast=(GtkPopoverMenu *) + * @param child cast=(GtkWidget *) + * @param id cast=(const char *) + */ + public static final native boolean gtk_popover_menu_add_child(long popover, long child, byte[] id); + /* GtkPopoverMenuBar */ /** @param model cast=(GMenuModel *) */ public static final native long gtk_popover_menu_bar_new_from_model(long model); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/MenuItem.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/MenuItem.java index edc9eb543ee..87711aeff08 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/MenuItem.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/MenuItem.java @@ -69,6 +69,10 @@ public class MenuItem extends Item { long modelHandle, actionHandle, shortcutHandle; Section section; String actionName; + + /* GTK4 custom widget support for icons */ + long buttonHandle; // Custom button widget for the menu item + String customId; // Unique ID for custom widget registration /** * Constructs a new instance of this class given its parent @@ -300,6 +304,64 @@ void createHandle(int index) { break; } + /* + * GTK4: Create custom widgets for menu items to support icons. + * GTK4 PopoverMenu does not support icons via the standard GIO MenuItem API. + * We work around this by: + * 1. Setting a "custom" attribute on the GMenuItem model + * 2. Creating a custom widget (button with box containing image+label) + * 3. Registering the custom widget with the parent menu's PopoverMenu + */ + if ((style & SWT.SEPARATOR) == 0) { + // Create unique ID for this menu item's custom widget + customId = "swt-item-" + this.hashCode(); + byte[] idBytes = Converter.javaStringToCString(customId); + + // Set "custom" attribute on the GMenuItem model + byte[] customAttr = Converter.javaStringToCString("custom"); + byte[] stringFormat = Converter.javaStringToCString("s"); + OS.g_menu_item_set_attribute(handle, customAttr, stringFormat, idBytes); + + // Create custom widget structure: Button -> Box -> [Image + Label] + buttonHandle = GTK.gtk_button_new(); + if (buttonHandle == 0) error(SWT.ERROR_NO_HANDLES); + + boxHandle = gtk_box_new(GTK.GTK_ORIENTATION_HORIZONTAL, false, 6); + if (boxHandle == 0) error(SWT.ERROR_NO_HANDLES); + + // Create image handle (initially empty, will be populated by setImage) + imageHandle = GTK.gtk_image_new(); + if (imageHandle == 0) error(SWT.ERROR_NO_HANDLES); + GTK4.gtk_box_append(boxHandle, imageHandle); + + // Create label handle (will be populated by setText) + labelHandle = GTK.gtk_label_new_with_mnemonic(null); + if (labelHandle == 0) error(SWT.ERROR_NO_HANDLES); + GTK.gtk_label_set_xalign(labelHandle, 0); + GTK.gtk_widget_set_halign(labelHandle, GTK.GTK_ALIGN_FILL); + GTK4.gtk_box_append(boxHandle, labelHandle); + + // Put the box into the button + GTK4.gtk_button_set_child(buttonHandle, boxHandle); + + // Style the button to look like a menu item + byte[] modelClass = Converter.javaStringToCString("model"); + byte[] flatClass = Converter.javaStringToCString("flat"); + GTK.gtk_widget_add_css_class(buttonHandle, modelClass); + GTK.gtk_widget_add_css_class(buttonHandle, flatClass); + + // Register custom widget with parent PopoverMenu + // Note: The parent menu must be a PopoverMenu for this to work + if (parent.handle != 0) { + GTK4.gtk_popover_menu_add_child(parent.handle, buttonHandle, idBytes); + } + + // Connect button click to action + if (actionHandle != 0) { + OS.g_signal_connect_closure(buttonHandle, OS.clicked, display.getClosure(CLICKED), false); + } + } + Section selectedSection = parent.sections.getLast(); for (Section section : parent.sections) { @@ -1028,9 +1090,6 @@ public void setID (int id) { */ @Override public void setImage (Image image) { - //TODO: GTK4 Menu images with text are no longer supported - if (GTK.GTK4) return; - checkWidget(); if (this.image == image) return; if ((style & SWT.SEPARATOR) != 0) return; @@ -1041,6 +1100,38 @@ public void setImage (Image image) { } private void _setImage (Image image) { + if (GTK.GTK4) { + // GTK4: Update the image in the custom widget + if (image != null) { + ImageList imageList = parent.imageList; + if (imageList == null) imageList = parent.imageList = new ImageList(); + int imageIndex = imageList.indexOf(image); + if (imageIndex == -1) { + imageIndex = imageList.add(image); + } else { + imageList.put(imageIndex, image); + } + + // Use paintable approach similar to ToolItem + long pixbuf = ImageList.createPixbuf(image); + long texture = GDK.gdk_texture_new_for_pixbuf(pixbuf); + OS.g_object_unref(pixbuf); + + if (imageHandle != 0) { + GTK4.gtk_image_set_from_paintable(imageHandle, texture); + gtk_widget_show(imageHandle); + } + } else { + // Clear the image + if (imageHandle != 0) { + GTK4.gtk_image_clear(imageHandle); + gtk_widget_hide(imageHandle); + } + } + return; + } + + // GTK3 implementation if (image != null) { ImageList imageList = parent.imageList; if (imageList == null) imageList = parent.imageList = new ImageList (); @@ -1268,6 +1359,10 @@ public void setText (String string) { if (GTK.GTK4) { OS.g_menu_item_set_label(handle, buffer); + // Update custom widget label if it exists + if (labelHandle != 0 && GTK.GTK_IS_LABEL(labelHandle)) { + GTK.gtk_label_set_text_with_mnemonic(labelHandle, buffer); + } MaskKeysym maskKeysym = getMaskKeysym(); if (maskKeysym != null) { OS.g_menu_item_set_attribute(handle, From af995a5bff6fcfae730195fb2ac098b13dc8b57d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 16:57:56 +0000 Subject: [PATCH 4/5] Fix g_menu_item_set_attribute call for custom ID Co-authored-by: akurtakov <574788+akurtakov@users.noreply.github.com> --- .../Eclipse SWT/gtk/org/eclipse/swt/widgets/MenuItem.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/MenuItem.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/MenuItem.java index 87711aeff08..8242c2b149b 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/MenuItem.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/MenuItem.java @@ -318,9 +318,15 @@ void createHandle(int index) { byte[] idBytes = Converter.javaStringToCString(customId); // Set "custom" attribute on the GMenuItem model + // Allocate native memory for the ID string + long idPtr = OS.g_malloc(idBytes.length); + C.memmove(idPtr, idBytes, idBytes.length); + byte[] customAttr = Converter.javaStringToCString("custom"); byte[] stringFormat = Converter.javaStringToCString("s"); - OS.g_menu_item_set_attribute(handle, customAttr, stringFormat, idBytes); + OS.g_menu_item_set_attribute(handle, customAttr, stringFormat, idPtr); + + // Note: Don't free idPtr yet, as the menu model may still reference it // Create custom widget structure: Button -> Box -> [Image + Label] buttonHandle = GTK.gtk_button_new(); From 9614f13219004e4ff758b8f1bd0d58c4bcd5fce5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 11:06:04 +0000 Subject: [PATCH 5/5] Fix GTK critical error by only creating custom widgets for POP_UP menus Co-authored-by: akurtakov <574788+akurtakov@users.noreply.github.com> --- .../Eclipse SWT/gtk/org/eclipse/swt/widgets/MenuItem.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/MenuItem.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/MenuItem.java index 8242c2b149b..c2b7eef14a0 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/MenuItem.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/MenuItem.java @@ -311,8 +311,11 @@ void createHandle(int index) { * 1. Setting a "custom" attribute on the GMenuItem model * 2. Creating a custom widget (button with box containing image+label) * 3. Registering the custom widget with the parent menu's PopoverMenu + * + * Note: This only works for POP_UP menus, as BAR and DROP_DOWN menus + * don't have a GtkPopoverMenu widget that we can register with. */ - if ((style & SWT.SEPARATOR) == 0) { + if ((style & SWT.SEPARATOR) == 0 && (parent.style & SWT.POP_UP) != 0) { // Create unique ID for this menu item's custom widget customId = "swt-item-" + this.hashCode(); byte[] idBytes = Converter.javaStringToCString(customId); @@ -357,7 +360,7 @@ void createHandle(int index) { GTK.gtk_widget_add_css_class(buttonHandle, flatClass); // Register custom widget with parent PopoverMenu - // Note: The parent menu must be a PopoverMenu for this to work + // Only POP_UP menus have a GtkPopoverMenu widget we can register with if (parent.handle != 0) { GTK4.gtk_popover_menu_add_child(parent.handle, buttonHandle, idBytes); }