From 208b5dc85471ca1539ddded9372fdfde868f1f4e Mon Sep 17 00:00:00 2001 From: "Klare, Heiko" Date: Fri, 23 Jan 2026 12:46:33 +0100 Subject: [PATCH 1/6] [Win32] Make ImageList retrieval from Display consider zoom When requesting an ImageList from a Display, the zoom is currently not considered when scanning through the existing image lists for a matching one. This can lead to an image list with a wrong zoom being used, such that the contained image handles are not properly fitting in size and thus need to be scaled. This changes includes the zoom into the identification of whether one of a Display's image lists fits to the requested properties or not. To this end, the whole validation logic is moved to the ImageList class itself, such that it's style and zoom do not need to be exposed unnecessarily. --- .../org/eclipse/swt/internal/ImageList.java | 9 ++--- .../org/eclipse/swt/widgets/Display.java | 36 +++++++------------ 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/ImageList.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/ImageList.java index b21c849346f..b6da1704a08 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/ImageList.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/ImageList.java @@ -331,10 +331,6 @@ public Image get (int index) { return images [index]; } -public int getStyle () { - return style; -} - public long getHandle(int targetZoom) { if (!zoomToHandle.containsKey(targetZoom)) { int scaledWidth = DPIUtil.pointToPixel(DPIUtil.pixelToPoint(width, this.zoom), targetZoom); @@ -374,6 +370,11 @@ public Point getImageSize() { return Win32DPIUtils.pixelToPointAsSize(new Point (cx [0], cy [0]), zoom); } +public boolean isFittingFor(int style, int width, int height, int zoom) { + Point imageSize = getImageSize(); + return this.style == style && imageSize.x == width && imageSize.y == height && this.zoom == zoom; +} + public int indexOf (Image image) { int count = OS.ImageList_GetImageCount (handle); for (int i=0; i Date: Wed, 1 Oct 2025 15:31:12 +0200 Subject: [PATCH 2/6] [GTK] Reports wrong mouse coordinates when starting a drag #2119 Control: don't send MouseMove events with stateMask set for BUTTON1 before the MouseDown event was sent These events are already added to the dragDetectionQueue, so there is no need to send/post them now. --- .../Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java | 1 + 1 file changed, 1 insertion(+) 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 64ad85edbd4..c762cbce982 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 @@ -5091,6 +5091,7 @@ boolean sendMouseEvent (int type, int button, int count, int detail, boolean sen flushQueueOnDnd(); } else { dragDetectionQueue.add(event); + return true; } break; case SWT.MouseUp: From e15246c9e8b7d1fd8fa35080659ed8e46926a09d 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: Sat, 24 Jan 2026 17:47:13 +0200 Subject: [PATCH 3/6] Use try-with-resources --- .../junit/Test_org_eclipse_swt_custom_BusyIndicator.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_BusyIndicator.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_BusyIndicator.java index c963cba8cc4..f26b80ef1bb 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_BusyIndicator.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_BusyIndicator.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Christoph Läubrich and others. + * Copyright (c) 2024, 2026 Christoph Läubrich and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -95,8 +95,7 @@ public void testShowWhile() { @Test @Timeout(value = 30) public void testShowWhileWithFuture() { - ExecutorService executor = Executors.newSingleThreadExecutor(); - try { + try (ExecutorService executor = Executors.newSingleThreadExecutor()){ Shell shell = new Shell(); Display display = shell.getDisplay(); Cursor busyCursor = display.getSystemCursor(SWT.CURSOR_WAIT); @@ -129,8 +128,6 @@ public void testShowWhileWithFuture() { shell.dispose(); while (!display.isDisposed() && display.readAndDispatch()) { } - } finally { - executor.shutdownNow(); } } From 8c1409a4141c744d6bd1247afbba43d0f33ce37a 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: Sat, 24 Jan 2026 18:10:13 +0200 Subject: [PATCH 4/6] Update tycho-build to 5.0.2 --- .mvn/extensions.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml index 5f188e157a5..a10e798528a 100644 --- a/.mvn/extensions.xml +++ b/.mvn/extensions.xml @@ -4,6 +4,6 @@ org.eclipse.tycho tycho-build - 5.0.1 + 5.0.2 \ No newline at end of file From f6c5753df9887773e2f3bffa60b7b29a7ef81360 Mon Sep 17 00:00:00 2001 From: "Klare, Heiko" Date: Fri, 23 Jan 2026 17:48:11 +0100 Subject: [PATCH 5/6] [Win32] Initialize nativeZoom of TrayItem The Tray implementation uses the default constructor of the Widget class. When using that constructor, the nativeZoom of the widget is not initialized. In case of a Tray, contained TrayItems inherit that zoom and will fail to get an image set because the zoom is zero. With this change, a TrayItem will initialize its native zoom based on the zoom of the context device. --- .../Eclipse SWT/win32/org/eclipse/swt/widgets/TrayItem.java | 1 + 1 file changed, 1 insertion(+) diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TrayItem.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TrayItem.java index 0498981c9a8..7c9286443fe 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TrayItem.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TrayItem.java @@ -79,6 +79,7 @@ public class TrayItem extends Item { public TrayItem (Tray parent, int style) { super (parent, style); this.parent = parent; + this.nativeZoom = display.getDeviceZoom(); parent.createItem (this, parent.getItemCount ()); createUpdateWidget (true); } From 1bafbaee692c7bb20a6343ecdb84a3f9e5a86014 Mon Sep 17 00:00:00 2001 From: "Klare, Heiko" Date: Fri, 23 Jan 2026 09:30:51 +0100 Subject: [PATCH 6/6] [Win32] Avoid duplicate application of gray/disabled image adaptation When an image is created with SWT.IMAGE_GRAY or SWT.IMAGE_DISABLE, an adaptation of the loaded/created image data is performed to make it look grayed/disabled. The according functionality is currently applied by callers of loadImageData() right afterwards. Since for every consumption of an image data/handle the loadImageData() method needs to be called, this ensures that the image data is always adapted correctly. In the implementation for an ImageGcDrawer, the adaptation for gray/disabled is currently executed within the loadImageData() method. The same applies to implementation for an existing handle and a plain image, as they will simply scale existing image data (which already have the gray/disabled adaptation applied). Since consumers of that method perform the adaptation as well, this leads to an unnecessary duplicate application of the adaptation and to inconsistent behavior throughout the implementations of loadImageData(). This change streamlines the implementation in the Image class by properly assigning the responsibility for applying gray/disabled adaptations as follows: - the copy constructor accepting the gray/disabled flag applies the style to copied handles, just like it does now - on retrieval of new image data or handles for zoom/sizes, the loadImageData() and loadImageDataAtExactSize() method of the provider classes are responsible for delivering data with the styling being applied This relieves all consumers of those methods from taking care of applying the style and properly assign it to those two methods and the implementation. --- .../win32/org/eclipse/swt/graphics/Image.java | 46 +++++++++++++------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java index c9336d0925e..bd6373392ab 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java @@ -238,8 +238,7 @@ private boolean isReusable(int width, int height) { private Optional createHandleAtExactSize(int width, int height) { Optional imageData = imageProvider.loadImageDataAtExactSize(width, height); if (imageData.isPresent()) { - ImageData adaptedData = adaptImageDataIfDisabledOrGray(imageData.get()); - temporaryHandleContainer = init(adaptedData, -1); + temporaryHandleContainer = init(imageData.get(), -1); return Optional.of(temporaryHandleContainer); } return Optional.empty(); @@ -253,8 +252,7 @@ private InternalImageHandle getOrCreateImageHandleAtClosestSize(int widthHint, i InternalImageHandle bestFittingHandle = imageHandleManager.get(imageZoom); if (bestFittingHandle == null) { ImageData bestFittingImageData = imageProvider.loadImageData(imageZoom).element(); - ImageData adaptedData = adaptImageDataIfDisabledOrGray(bestFittingImageData); - bestFittingHandle = temporaryHandleContainer = init(adaptedData, -1); + bestFittingHandle = temporaryHandleContainer = init(bestFittingImageData, -1); } return bestFittingHandle; } @@ -771,6 +769,10 @@ public Image(Device device, ImageGcDrawer imageGcDrawer, int width, int height) init(); } +private ElementAtZoom adaptImageDataIfDisabledOrGray(ElementAtZoom dataAtZoom) { + return new ElementAtZoom<>(adaptImageDataIfDisabledOrGray(dataAtZoom.element()), dataAtZoom.zoom()); +} + private ImageData adaptImageDataIfDisabledOrGray(ImageData data) { ImageData returnImageData = null; switch (this.styleFlag) { @@ -2058,6 +2060,10 @@ protected boolean isPersistentImageHandleRequriedForImageData() { return false; } + /** + * Returns image data at the best-fitting available zoom for the given zoom. + * The returned data will have a potential gray/disable style applied. + */ protected abstract ElementAtZoom loadImageData(int zoom); abstract ImageData newImageData(int zoom); @@ -2071,6 +2077,10 @@ ElementAtZoom getClosestAvailableImageData(int zoom) { return new ElementAtZoom<>(imageData, closestZoom); } + /** + * Returns image data at the exact requested size if available. + * The returned data will have a potential gray/disable style applied. + */ protected Optional loadImageDataAtExactSize(int width, int height) { return Optional.empty(); // exact size not available } @@ -2174,7 +2184,6 @@ protected DestroyableImageHandle newImageHandle(ZoomContext zoomContext) { private DestroyableImageHandle initializeHandleFromSource(ZoomContext zoomContext) { ElementAtZoom imageDataAtZoom = loadImageData(zoomContext.targetZoom()); ImageData imageData = DPIUtil.scaleImageData(device, imageDataAtZoom.element(), zoomContext.targetZoom(), imageDataAtZoom.zoom()); - imageData = adaptImageDataIfDisabledOrGray(imageData); return newImageHandle(imageData, zoomContext); } } @@ -2202,7 +2211,8 @@ protected Rectangle getBounds(int zoom) { @Override protected ElementAtZoom loadImageData(int zoom) { - return new ElementAtZoom<>(imageDataAtBaseZoom, baseZoom); + ImageData adaptedImageData = adaptImageDataIfDisabledOrGray(imageDataAtBaseZoom); + return new ElementAtZoom<>(adaptedImageData, baseZoom); } @Override @@ -2233,7 +2243,8 @@ protected ElementAtZoom loadImageData(int zoom) { ImageData scaledMask = DPIUtil.scaleImageData(device, maskAt100, zoom, 100); scaledMask = ImageData.convertMask(scaledMask); ImageData mergedData = applyMask(scaledSource, scaledMask); - return new ElementAtZoom<>(mergedData, zoom); + ImageData adaptedData = adaptImageDataIfDisabledOrGray(mergedData); + return new ElementAtZoom<>(adaptedData, zoom); } @Override @@ -2260,7 +2271,8 @@ private ImageDataLoaderStreamProviderWrapper(byte[] inputStreamData) { @Override protected ElementAtZoom loadImageData(int zoom) { - return ImageDataLoader.loadByZoom(new ByteArrayInputStream(inputStreamData), FileFormat.DEFAULT_ZOOM, zoom); + ElementAtZoom imageDataAtZoom = ImageDataLoader.loadByZoom(new ByteArrayInputStream(inputStreamData), FileFormat.DEFAULT_ZOOM, zoom); + return adaptImageDataIfDisabledOrGray(imageDataAtZoom); } @Override @@ -2278,7 +2290,8 @@ AbstractImageProviderWrapper createCopy(Image image) { protected Optional loadImageDataAtExactSize(int targetWidth, int targetHeight) { if (ImageDataLoader.isDynamicallySizable(new ByteArrayInputStream(this.inputStreamData))) { ImageData imageDataAtSize = ImageDataLoader.loadBySize(new ByteArrayInputStream(this.inputStreamData), targetWidth, targetHeight); - return Optional.of(imageDataAtSize); + ImageData adaptedImageDataAtSize = adaptImageDataIfDisabledOrGray(imageDataAtSize); + return Optional.of(adaptedImageDataAtSize); } return Optional.empty(); } @@ -2470,7 +2483,6 @@ protected DestroyableImageHandle newImageHandle(ZoomContext zoomContext) { private DestroyableImageHandle initializeHandleFromSource(int zoom) { ElementAtZoom imageDataAtZoom = loadImageData(zoom); ImageData imageData = DPIUtil.scaleImageData (device, imageDataAtZoom.element(), zoom, imageDataAtZoom.zoom()); - imageData = adaptImageDataIfDisabledOrGray(imageData); return init(imageData, zoom); } @@ -2497,7 +2509,8 @@ protected ElementAtZoom loadImageData(int zoom) { // Load at appropriate zoom via loader if (fileForZoom.zoom() != zoom && ImageDataLoader.canLoadAtZoom(fileForZoom.element(), fileForZoom.zoom(), zoom)) { ElementAtZoom imageDataAtZoom = ImageDataLoader.loadByZoom(fileForZoom.element(), fileForZoom.zoom(), zoom); - return new ElementAtZoom<>(imageDataAtZoom.element(), zoom); + ImageData adaptedImageData = adaptImageDataIfDisabledOrGray(imageDataAtZoom.element()); + return new ElementAtZoom<>(adaptedImageData, zoom); } // Load at file zoom (native or via loader) and rescale @@ -2517,7 +2530,7 @@ protected ElementAtZoom loadImageData(int zoom) { temporaryImageHandle.destroy(); } } - return imageDataAtZoom; + return adaptImageDataIfDisabledOrGray(imageDataAtZoom); } @Override @@ -2727,7 +2740,8 @@ protected Optional loadImageDataAtExactSize(int targetWidth, int targ String fileName = DPIUtil.validateAndGetImagePathAtZoom(this.provider, 100).element(); if (ImageDataLoader.isDynamicallySizable(fileName)) { ImageData imageDataAtSize = ImageDataLoader.loadBySize(fileName, targetWidth, targetHeight); - return Optional.of(imageDataAtSize); + ImageData adaptedImageDataAtSize = adaptImageDataIfDisabledOrGray(imageDataAtSize); + return Optional.of(adaptedImageDataAtSize); } return Optional.empty(); } @@ -2740,7 +2754,8 @@ private class ImageDataProviderWrapper extends BaseImageProviderWrapper loadImageData(int zoom) { - return DPIUtil.validateAndGetImageDataAtZoom (provider, zoom); + ElementAtZoom imageDataAtZoom = DPIUtil.validateAndGetImageDataAtZoom (provider, zoom); + return adaptImageDataIfDisabledOrGray(imageDataAtZoom); } @Override @@ -2756,7 +2771,8 @@ protected Optional loadImageDataAtExactSize(int targetWidth, int targ SWT.error(SWT.ERROR_INVALID_ARGUMENT, null, " ImageDataAtSizeProvider returned null for width=" + targetWidth + ", height=" + targetHeight); } - return Optional.of(imageData); + ImageData adaptedImageData = adaptImageDataIfDisabledOrGray(imageData); + return Optional.of(adaptedImageData); } return Optional.empty(); }