From 7a4c7848d632d050f1082f57a8a39bc772045fa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Wed, 22 Oct 2025 18:46:14 +0200 Subject: [PATCH 1/2] Detect the desired size from the path or Query parameters. Currently Icons can be organized in folders like /icons/16x16, /icons/32x32 and so on for high resolution support. Currently if one want to replace such structure with SVG it is required to scale down the SVG to 16x16 pixel document size as otherwise they get rendered at there native size (what usually is much larger). As it is not really desirable to restrict the size of the SVG design for technical reasons, JFace now can detect two cases: 1) the SVG is places in a folder with "classic" folder layout the size is extracted and passed down as a hint for dynamic sizable icons 2) one can additionally add a query parameter, e.g. if I have an icon like /icons/obj16/search.svg and I have two places where I want to use it one for a toolbar (16x16) and once for a Wizard Images (usually 128x128) I can use the url bundle:/example.id/icons/obj16/search.svg?size=16x16 and bundle:/example.id/icons/obj16/search.svg?size=128x128 to accomplish this task without the need to even store two SVGs --- .../org.eclipse.jface/META-INF/MANIFEST.MF | 4 +- .../jface/resource/URLHintProvider.java | 50 +++++ .../jface/resource/URLImageDescriptor.java | 13 +- .../org.eclipse.jface.snippets/.classpath | 2 +- .../.settings/org.eclipse.jdt.core.prefs | 6 +- .../Snippet083SVGImageSizeHints.java | 166 ++++++++++++++ .../META-INF/MANIFEST.MF | 2 +- .../icons/imagetests/16x16/test-icon.svg | 6 + .../icons/imagetests/32x32/test-icon.svg | 6 + .../icons/imagetests/test-icon.svg | 6 + .../jface/tests/images/AllImagesTests.java | 3 +- .../tests/images/URLHintProviderTest.java | 202 ++++++++++++++++++ 12 files changed, 456 insertions(+), 10 deletions(-) create mode 100644 bundles/org.eclipse.jface/src/org/eclipse/jface/resource/URLHintProvider.java create mode 100644 examples/org.eclipse.jface.snippets/Eclipse JFace Snippets/org/eclipse/jface/snippets/resources/Snippet083SVGImageSizeHints.java create mode 100644 tests/org.eclipse.jface.tests/icons/imagetests/16x16/test-icon.svg create mode 100644 tests/org.eclipse.jface.tests/icons/imagetests/32x32/test-icon.svg create mode 100644 tests/org.eclipse.jface.tests/icons/imagetests/test-icon.svg create mode 100644 tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/images/URLHintProviderTest.java diff --git a/bundles/org.eclipse.jface/META-INF/MANIFEST.MF b/bundles/org.eclipse.jface/META-INF/MANIFEST.MF index b2c427bf1af..142f59fae71 100644 --- a/bundles/org.eclipse.jface/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.jface/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.jface;singleton:=true -Bundle-Version: 3.38.100.qualifier +Bundle-Version: 3.39.0 Bundle-Vendor: %providerName Bundle-Localization: plugin Export-Package: org.eclipse.jface, @@ -34,7 +34,7 @@ Export-Package: org.eclipse.jface, org.eclipse.jface.window, org.eclipse.jface.wizard, org.eclipse.jface.wizard.images -Require-Bundle: org.eclipse.swt;bundle-version="[3.126.0,4.0.0)";visibility:=reexport, +Require-Bundle: org.eclipse.swt;bundle-version="[3.132.0,3.134.0)";visibility:=reexport, org.eclipse.core.commands;bundle-version="[3.4.0,4.0.0)";visibility:=reexport, org.eclipse.equinox.common;bundle-version="[3.18.0,4.0.0)", org.eclipse.equinox.bidi;bundle-version="[0.10.0,2.0.0)";resolution:=optional diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/URLHintProvider.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/URLHintProvider.java new file mode 100644 index 00000000000..4d5c176cf32 --- /dev/null +++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/URLHintProvider.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2025 Christoph Läubrich and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.jface.resource; + +import java.net.URL; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.swt.graphics.Point; + +class URLHintProvider implements Supplier { + + private static final Pattern QUERY_PATTERN = Pattern.compile("&size=(\\d+)x(\\d+)"); //$NON-NLS-1$ + private static final Pattern PATH_PATTERN = Pattern.compile("/(\\d+)x(\\d+)/"); //$NON-NLS-1$ + + private URL url; + + public URLHintProvider(URL url) { + this.url = url; + } + + @Override + public Point get() { + String query = url.getQuery(); + Matcher matcher; + if (query != null && !query.isEmpty()) { + matcher = QUERY_PATTERN.matcher("&" + query); //$NON-NLS-1$ + } else { + String path = url.getPath(); + matcher = PATH_PATTERN.matcher(path); + } + if (matcher.find()) { + return new Point(Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2))); + } + return null; + } + +} diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/URLImageDescriptor.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/URLImageDescriptor.java index 646ca5cb90f..66a3ea04a49 100644 --- a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/URLImageDescriptor.java +++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/URLImageDescriptor.java @@ -25,6 +25,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.function.Function; +import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -42,6 +43,7 @@ import org.eclipse.swt.graphics.ImageDataProvider; import org.eclipse.swt.graphics.ImageFileNameProvider; import org.eclipse.swt.graphics.ImageLoader; +import org.eclipse.swt.graphics.Point; import org.eclipse.swt.internal.DPIUtil.ElementAtZoom; import org.eclipse.swt.internal.NativeImageLoader; import org.eclipse.swt.internal.image.FileFormat; @@ -133,7 +135,7 @@ private static R getZoomedImageSource(URL url, String urlString, int zoom, F private static ImageData getImageData(URL url, int fileZoom, int targetZoom) { try (InputStream in = getStream(url)) { if (in != null) { - return loadImageFromStream(new BufferedInputStream(in), fileZoom, targetZoom); + return loadImageFromStream(new BufferedInputStream(in), fileZoom, targetZoom, new URLHintProvider(url)); } } catch (SWTException e) { if (e.code != SWT.ERROR_INVALID_IMAGE) { @@ -147,7 +149,13 @@ private static ImageData getImageData(URL url, int fileZoom, int targetZoom) { } @SuppressWarnings("restriction") - private static ImageData loadImageFromStream(InputStream stream, int fileZoom, int targetZoom) { + private static ImageData loadImageFromStream(InputStream stream, int fileZoom, int targetZoom, + Supplier hintProvider) { + Point hintSize = hintProvider.get(); + if (hintSize != null) { + return NativeImageLoader.load(stream, new ImageLoader(), hintSize.x * targetZoom / fileZoom, + hintSize.y * targetZoom / fileZoom); + } return NativeImageLoader.load(new ElementAtZoom<>(stream, fileZoom), new ImageLoader(), targetZoom).get(0) .element(); } @@ -171,6 +179,7 @@ private static InputStream getStream(URL url) { } return url.openStream(); } catch (IOException e) { + e.printStackTrace(); if (InternalPolicy.DEBUG_LOG_URL_IMAGE_DESCRIPTOR_MISSING_2x) { String path = url.getPath(); if (path.endsWith("@2x.png") || path.endsWith("@1.5x.png")) { //$NON-NLS-1$ //$NON-NLS-2$ diff --git a/examples/org.eclipse.jface.snippets/.classpath b/examples/org.eclipse.jface.snippets/.classpath index d47c0b22004..59a38942740 100644 --- a/examples/org.eclipse.jface.snippets/.classpath +++ b/examples/org.eclipse.jface.snippets/.classpath @@ -1,6 +1,6 @@ - + diff --git a/examples/org.eclipse.jface.snippets/.settings/org.eclipse.jdt.core.prefs b/examples/org.eclipse.jface.snippets/.settings/org.eclipse.jdt.core.prefs index 19621f56db4..99fa2aea988 100644 --- a/examples/org.eclipse.jface.snippets/.settings/org.eclipse.jdt.core.prefs +++ b/examples/org.eclipse.jface.snippets/.settings/org.eclipse.jdt.core.prefs @@ -8,9 +8,9 @@ org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annota org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.compliance=21 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate @@ -118,7 +118,7 @@ org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning org.eclipse.jdt.core.compiler.release=enabled -org.eclipse.jdt.core.compiler.source=17 +org.eclipse.jdt.core.compiler.source=21 org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 org.eclipse.jdt.core.formatter.align_type_members_on_columns=false org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16 diff --git a/examples/org.eclipse.jface.snippets/Eclipse JFace Snippets/org/eclipse/jface/snippets/resources/Snippet083SVGImageSizeHints.java b/examples/org.eclipse.jface.snippets/Eclipse JFace Snippets/org/eclipse/jface/snippets/resources/Snippet083SVGImageSizeHints.java new file mode 100644 index 00000000000..cfc5a79ae94 --- /dev/null +++ b/examples/org.eclipse.jface.snippets/Eclipse JFace Snippets/org/eclipse/jface/snippets/resources/Snippet083SVGImageSizeHints.java @@ -0,0 +1,166 @@ +/******************************************************************************* + * Copyright (c) 2025 Christoph Läubrich and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.jface.snippets.resources; + +import java.net.URL; + +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; + +/** + * A snippet to demonstrate SVG image size hints using path-based and + * query-parameter-based size detection. + * + *

+ * This demonstrates two ways to control SVG rendering size: + *

    + *
  1. Path-based hints: Place SVG in folders like /icons/16x16/ or + * /icons/32x32/
  2. + *
  3. Query parameter hints: Add ?size=WIDTHxHEIGHT to the URL
  4. + *
+ *

+ * + *

+ * This allows using a single SVG file at different sizes without creating + * multiple scaled versions or restricting the SVG design size. + *

+ */ +public class Snippet083SVGImageSizeHints { + + public static void main(String[] args) { + Display display = new Display(); + Shell shell = new Shell(display); + shell.setText("SVG Image Size Hints Demo"); + GridLayoutFactory.fillDefaults().numColumns(2).margins(10, 10).spacing(10, 10).applyTo(shell); + + addSection(shell, "Path-Based Size Hints", """ + SVGs placed in folders with size patterns (e.g., /icons/16x16/, /icons/32x32/) + are automatically rendered at that size. + + Example: bundle://plugin.id/icons/16x16/icon.svg + """); + + addSection(shell, "Query Parameter Size Hints", """ + You can specify size using query parameters for maximum flexibility. + This allows using the same SVG at different sizes. + + Example: bundle://plugin.id/icons/icon.svg?size=16x16 + Example: bundle://plugin.id/icons/icon.svg?size=128x128 + """); + + addSection(shell, "Zoom Support", """ + Both methods work with high-DPI displays. The hint specifies the base size, + and JFace automatically scales for 150% and 200% zoom levels. + + 16x16 at 100% zoom → 16x16 pixels + 16x16 at 150% zoom → 24x24 pixels + 16x16 at 200% zoom → 32x32 pixels + """); + + // Demonstrate with actual images from test resources if available + try { + URL svgUrl = Snippet083SVGImageSizeHints.class + .getResource("/org/eclipse/jface/snippets/resources/test-demo.svg"); + if (svgUrl != null) { + addImageDemo(shell, "Default (no hint)", svgUrl.toString(), display); + addImageDemo(shell, "With ?size=32x32", svgUrl.toString() + "?size=32x32", display); + addImageDemo(shell, "With ?size=64x64", svgUrl.toString() + "?size=64x64", display); + } + } catch (Exception e) { + // Demo images not available, that's okay + Label note = new Label(shell, SWT.WRAP); + note.setText("Note: Visual demo requires test SVG files. See URLHintProviderTest for examples."); + GridDataFactory.fillDefaults().span(2, 1).grab(true, false).applyTo(note); + } + + addSection(shell, "Use Cases", """ + 1. Toolbar icons: Use ?size=16x16 for consistent toolbar sizing + 2. Wizard images: Use ?size=128x128 for large wizard graphics + 3. View icons: Place in /icons/16x16/ folder structure + 4. Multi-resolution: One SVG serves all sizes without duplication + """); + + addSection(shell, "Code Example", """ + // Using query parameter + URL iconUrl = new URL("bundle://my.plugin/icons/search.svg?size=16x16"); + ImageDescriptor desc = ImageDescriptor.createFromURL(iconUrl); + Image icon = desc.createImage(); + + // Using path-based hint + URL iconUrl = FileLocator.find(bundle, new Path("icons/16x16/search.svg")); + ImageDescriptor desc = ImageDescriptor.createFromURL(iconUrl); + """); + + shell.pack(); + shell.open(); + + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) { + display.sleep(); + } + } + display.dispose(); + } + + private static void addSection(Shell shell, String title, String text) { + Label titleLabel = new Label(shell, SWT.BOLD); + titleLabel.setText(title); + GridDataFactory.fillDefaults().span(2, 1).grab(true, false).applyTo(titleLabel); + + Label textLabel = new Label(shell, SWT.WRAP); + textLabel.setText(text.trim()); + GridDataFactory.fillDefaults().span(2, 1).grab(true, false).hint(600, SWT.DEFAULT).applyTo(textLabel); + + Label separator = new Label(shell, SWT.SEPARATOR | SWT.HORIZONTAL); + GridDataFactory.fillDefaults().span(2, 1).grab(true, false).applyTo(separator); + } + + private static void addImageDemo(Shell shell, String description, String urlString, Display display) { + try { + URL url = new URL(urlString); + ImageDescriptor descriptor = ImageDescriptor.createFromURL(url); + Image image = descriptor.createImage(display); + + Label label = new Label(shell, SWT.NONE); + label.setText(description + ":"); + GridDataFactory.fillDefaults().applyTo(label); + + Canvas canvas = new Canvas(shell, SWT.BORDER); + canvas.addPaintListener(e -> { + if (!image.isDisposed()) { + e.gc.drawImage(image, 0, 0); + } + }); + GridDataFactory.swtDefaults().hint(image.getBounds().width + 2, image.getBounds().height + 2) + .applyTo(canvas); + + canvas.addDisposeListener(e -> { + if (!image.isDisposed()) { + image.dispose(); + } + }); + } catch (Exception e) { + Label error = new Label(shell, SWT.NONE); + error.setText(description + ": Error loading image"); + GridDataFactory.fillDefaults().span(2, 1).applyTo(error); + } + } +} diff --git a/examples/org.eclipse.jface.snippets/META-INF/MANIFEST.MF b/examples/org.eclipse.jface.snippets/META-INF/MANIFEST.MF index 5d7848897ab..6a368779768 100644 --- a/examples/org.eclipse.jface.snippets/META-INF/MANIFEST.MF +++ b/examples/org.eclipse.jface.snippets/META-INF/MANIFEST.MF @@ -15,5 +15,5 @@ Export-Package: org.eclipse.jface.snippets.dialogs, org.eclipse.jface.snippets.viewers, org.eclipse.jface.snippets.window, org.eclipse.jface.snippets.wizard -Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-RequiredExecutionEnvironment: JavaSE-21 Automatic-Module-Name: org.eclipse.jface.snippets diff --git a/tests/org.eclipse.jface.tests/icons/imagetests/16x16/test-icon.svg b/tests/org.eclipse.jface.tests/icons/imagetests/16x16/test-icon.svg new file mode 100644 index 00000000000..61178e6910f --- /dev/null +++ b/tests/org.eclipse.jface.tests/icons/imagetests/16x16/test-icon.svg @@ -0,0 +1,6 @@ + + + + + S + diff --git a/tests/org.eclipse.jface.tests/icons/imagetests/32x32/test-icon.svg b/tests/org.eclipse.jface.tests/icons/imagetests/32x32/test-icon.svg new file mode 100644 index 00000000000..61178e6910f --- /dev/null +++ b/tests/org.eclipse.jface.tests/icons/imagetests/32x32/test-icon.svg @@ -0,0 +1,6 @@ + + + + + S + diff --git a/tests/org.eclipse.jface.tests/icons/imagetests/test-icon.svg b/tests/org.eclipse.jface.tests/icons/imagetests/test-icon.svg new file mode 100644 index 00000000000..4c8ecc392a1 --- /dev/null +++ b/tests/org.eclipse.jface.tests/icons/imagetests/test-icon.svg @@ -0,0 +1,6 @@ + + + + + S + diff --git a/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/images/AllImagesTests.java b/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/images/AllImagesTests.java index 7761d2aa846..d4fb86cacd9 100644 --- a/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/images/AllImagesTests.java +++ b/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/images/AllImagesTests.java @@ -21,7 +21,8 @@ @Suite @SelectClasses({ ImageRegistryTest.class, ResourceManagerTest.class, FileImageDescriptorTest.class, - UrlImageDescriptorTest.class, DecorationOverlayIconTest.class, DeferredImageDescriptorTest.class }) + UrlImageDescriptorTest.class, URLHintProviderTest.class, DecorationOverlayIconTest.class, + DeferredImageDescriptorTest.class }) public class AllImagesTests { public static void main(String[] args) { diff --git a/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/images/URLHintProviderTest.java b/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/images/URLHintProviderTest.java new file mode 100644 index 00000000000..ae5b207e386 --- /dev/null +++ b/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/images/URLHintProviderTest.java @@ -0,0 +1,202 @@ +/******************************************************************************* + * Copyright (c) 2025 Christoph Läubrich and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.jface.tests.images; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.net.URL; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.ImageData; +import org.junit.jupiter.api.Test; + +/** + * Tests for URLHintProvider functionality that detects desired image sizes from + * URL paths and query parameters. + * + *

+ * Note: SVG size hint support requires SWT 3.132+ with native SVG loading + * capabilities. Tests verify that hints are detected and passed to the loader, + * but actual rendering depends on platform SVG support. + *

+ */ +public class URLHintProviderTest { + + @Test + public void testPathBasedHintDetection16x16() throws Exception { + URL url = URLHintProviderTest.class.getResource("/icons/imagetests/16x16/test-icon.svg"); + assertNotNull(url, "Test SVG not found"); + + ImageDescriptor descriptor = ImageDescriptor.createFromURL(url); + ImageData imageData = descriptor.getImageData(100); + + assertNotNull(imageData, "ImageData should not be null"); + // If SVG size hints are supported, should be 16x16, otherwise native 128x128 + assertEquals(16, imageData.width, "Width should be 16 based on path hint"); + assertEquals(16, imageData.height, "Height should be 16 based on path hint"); + } + + @Test + public void testPathBasedHintDetection32x32() throws Exception { + URL url = URLHintProviderTest.class.getResource("/icons/imagetests/32x32/test-icon.svg"); + assertNotNull(url, "Test SVG not found"); + + ImageDescriptor descriptor = ImageDescriptor.createFromURL(url); + ImageData imageData = descriptor.getImageData(100); + + assertNotNull(imageData, "ImageData should not be null"); + assertEquals(32, imageData.width, "Width should be 32 based on path hint"); + assertEquals(32, imageData.height, "Height should be 32 based on path hint"); + } + + @Test + public void testPathBasedHintDetectionZoom200() throws Exception { + URL url = URLHintProviderTest.class.getResource("/icons/imagetests/16x16/test-icon.svg"); + assertNotNull(url, "Test SVG not found"); + + ImageDescriptor descriptor = ImageDescriptor.createFromURL(url); + ImageData imageData = descriptor.getImageData(200); + + assertNotNull(imageData, "ImageData should not be null"); + assertEquals(32, imageData.width, "Width should be 32 (16*2) at 200% zoom"); + assertEquals(32, imageData.height, "Height should be 32 (16*2) at 200% zoom"); + } + + @Test + public void testPathBasedHintDetectionZoom150() throws Exception { + URL url = URLHintProviderTest.class.getResource("/icons/imagetests/16x16/test-icon.svg"); + assertNotNull(url, "Test SVG not found"); + + ImageDescriptor descriptor = ImageDescriptor.createFromURL(url); + ImageData imageData = descriptor.getImageData(150); + + assertNotNull(imageData, "ImageData should not be null"); + assertEquals(24, imageData.width, "Width should be 24 (16*1.5) at 150% zoom"); + assertEquals(24, imageData.height, "Height should be 24 (16*1.5) at 150% zoom"); + } + + @Test + public void testQueryParameterHintDetection() throws Exception { + URL baseUrl = URLHintProviderTest.class.getResource("/icons/imagetests/test-icon.svg"); + assertNotNull(baseUrl, "Test SVG not found"); + + // Create URL with query parameter - using jar: protocol from class loader + String urlString = baseUrl.toExternalForm(); + URL url = new URL(urlString + "?size=16x16"); + + ImageDescriptor descriptor = ImageDescriptor.createFromURL(url); + ImageData imageData = descriptor.getImageData(100); + + assertNotNull(imageData, "ImageData should not be null"); + assertEquals(16, imageData.width, "Width should be 16 based on query parameter"); + assertEquals(16, imageData.height, "Height should be 16 based on query parameter"); + } + + @Test + public void testQueryParameterHintDetection64x64() throws Exception { + URL baseUrl = URLHintProviderTest.class.getResource("/icons/imagetests/test-icon.svg"); + assertNotNull(baseUrl, "Test SVG not found"); + + String urlString = baseUrl.toExternalForm(); + URL url = new URL(urlString + "?size=64x64"); + ImageDescriptor descriptor = ImageDescriptor.createFromURL(url); + ImageData imageData = descriptor.getImageData(100); + + assertNotNull(imageData, "ImageData should not be null"); + assertEquals(64, imageData.width, "Width should be 64 based on query parameter"); + assertEquals(64, imageData.height, "Height should be 64 based on query parameter"); + } + + @Test + public void testQueryParameterWithZoom200() throws Exception { + URL baseUrl = URLHintProviderTest.class.getResource("/icons/imagetests/test-icon.svg"); + assertNotNull(baseUrl, "Test SVG not found"); + + String urlString = baseUrl.toExternalForm(); + URL url = new URL(urlString + "?size=16x16"); + + ImageDescriptor descriptor = ImageDescriptor.createFromURL(url); + ImageData imageData = descriptor.getImageData(200); + + assertNotNull(imageData, "ImageData should not be null"); + assertEquals(32, imageData.width, "Width should be 32 (16*2) at 200% zoom"); + assertEquals(32, imageData.height, "Height should be 32 (16*2) at 200% zoom"); + } + + @Test + public void testQueryParameterRectangularSize() throws Exception { + URL baseUrl = URLHintProviderTest.class.getResource("/icons/imagetests/test-icon.svg"); + assertNotNull(baseUrl, "Test SVG not found"); + + String urlString = baseUrl.toExternalForm(); + URL url = new URL(urlString + "?size=48x32"); + + ImageDescriptor descriptor = ImageDescriptor.createFromURL(url); + ImageData imageData = descriptor.getImageData(100); + + assertNotNull(imageData, "ImageData should not be null"); + assertEquals(48, imageData.width, "Width should be 48 based on query parameter"); + assertEquals(32, imageData.height, "Height should be 32 based on query parameter"); + } + + @Test + public void testNoHintDefaultSize() throws Exception { + URL url = URLHintProviderTest.class.getResource("/icons/imagetests/test-icon.svg"); + assertNotNull(url, "Test SVG not found"); + + ImageDescriptor descriptor = ImageDescriptor.createFromURL(url); + ImageData imageData = descriptor.getImageData(100); + + assertNotNull(imageData, "ImageData should not be null"); + // Without hint, SVG should render at its native viewBox size (128x128) + assertEquals(128, imageData.width, "Width should be 128 (native SVG size)"); + assertEquals(128, imageData.height, "Height should be 128 (native SVG size)"); + } + + @Test + public void testQueryParameterPrecedenceOverPath() throws Exception { + // Query parameter should take precedence over path hint + URL baseUrl = URLHintProviderTest.class.getResource("/icons/imagetests/16x16/test-icon.svg"); + assertNotNull(baseUrl, "Test SVG not found"); + + String urlString = baseUrl.toExternalForm(); + URL url = new URL(urlString + "?size=64x64"); + + ImageDescriptor descriptor = ImageDescriptor.createFromURL(url); + ImageData imageData = descriptor.getImageData(100); + + assertNotNull(imageData, "ImageData should not be null"); + assertEquals(64, imageData.width, "Width should be 64 from query parameter, not 16 from path"); + assertEquals(64, imageData.height, "Height should be 64 from query parameter, not 16 from path"); + } + + @Test + public void testQueryParameterWithMultipleParams() throws Exception { + URL baseUrl = URLHintProviderTest.class.getResource("/icons/imagetests/test-icon.svg"); + assertNotNull(baseUrl, "Test SVG not found"); + + String urlString = baseUrl.toExternalForm(); + // Test with multiple query parameters + URL url = new URL(urlString + "?foo=bar&size=24x24&other=value"); + + ImageDescriptor descriptor = ImageDescriptor.createFromURL(url); + ImageData imageData = descriptor.getImageData(100); + + assertNotNull(imageData, "ImageData should not be null"); + assertEquals(24, imageData.width, "Width should be 24 from query parameter"); + assertEquals(24, imageData.height, "Height should be 24 from query parameter"); + } + +} From f7e12988ca50662724f6ca31ecfc63aaa4751ef9 Mon Sep 17 00:00:00 2001 From: Eclipse Platform Bot Date: Tue, 9 Dec 2025 07:01:35 +0000 Subject: [PATCH 2/2] Version bump(s) for 4.39 stream --- tests/org.eclipse.jface.tests/META-INF/MANIFEST.MF | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/org.eclipse.jface.tests/META-INF/MANIFEST.MF b/tests/org.eclipse.jface.tests/META-INF/MANIFEST.MF index b8dc172d3e3..8cadbfa2c6c 100644 --- a/tests/org.eclipse.jface.tests/META-INF/MANIFEST.MF +++ b/tests/org.eclipse.jface.tests/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.jface.tests -Bundle-Version: 1.4.1100.qualifier +Bundle-Version: 1.4.1200.qualifier Automatic-Module-Name: org.eclipse.jface.tests Bundle-RequiredExecutionEnvironment: JavaSE-17 Require-Bundle: org.junit;bundle-version="4.12.0",