From e9b5ffb972c335cf9f4e66850135980ed809577d Mon Sep 17 00:00:00 2001 From: Lars Vogel Date: Mon, 28 Sep 2026 10:19:12 +0200 Subject: [PATCH] Draw the ColorCellEditor swatch with an ImageGcDrawer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The swatch was a 4-bit ImageData filled pixel by pixel, so on HiDPI displays SWT scales it up from 100 percent. Draw it with a GC instead, so it is rendered at the actual zoom. GDI+ keeps the background transparent on Windows, and the border is drawn as two fills so it stays aligned with the fill at fractional zoom. This needs SWT 3.130. Assisted-by: multiple AI agents and layers of automated tooling 🤖 --- .../org.eclipse.jface/META-INF/MANIFEST.MF | 2 +- .../jface/viewers/ColorCellEditor.java | 74 ++++++++----------- 2 files changed, 33 insertions(+), 43 deletions(-) diff --git a/bundles/org.eclipse.jface/META-INF/MANIFEST.MF b/bundles/org.eclipse.jface/META-INF/MANIFEST.MF index 3d6990d7426..a08e60a7a2e 100644 --- a/bundles/org.eclipse.jface/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.jface/META-INF/MANIFEST.MF @@ -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.130.0,4.0.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/viewers/ColorCellEditor.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/ColorCellEditor.java index 32fdae46d71..e812a46c426 100644 --- a/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/ColorCellEditor.java +++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/ColorCellEditor.java @@ -19,14 +19,14 @@ import org.eclipse.swt.graphics.FontMetrics; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.ImageData; -import org.eclipse.swt.graphics.PaletteData; +import org.eclipse.swt.graphics.ImageGcDrawer; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.ColorDialog; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Layout; import org.eclipse.swt.widgets.Table; @@ -133,55 +133,47 @@ public ColorCellEditor(Composite parent, int style) { } /** - * Creates and returns the color image data for the given control - * and RGB value. The image's size is either the control's item extent - * or the cell editor's default extent, which is 16 pixels square. - * - * @param w the control - * @param color the color + * Creates the swatch image for the given color, sized to the item height of + * the control. */ - private ImageData createColorImage(Control w, RGB color) { + private Image createColorImage(Control w, RGB color) { GC gc = new GC(w); FontMetrics fm = gc.getFontMetrics(); - int size = fm.getAscent(); + int ascent = fm.getAscent(); gc.dispose(); int indent = 6; int extent = DEFAULT_EXTENT; - if (w instanceof Table) { - extent = ((Table) w).getItemHeight() - 1; - } else if (w instanceof Tree) { - extent = ((Tree) w).getItemHeight() - 1; - } - - if (size > extent) { - size = extent; + if (w instanceof Table table) { + extent = table.getItemHeight() - 1; + } else if (w instanceof Tree tree) { + extent = tree.getItemHeight() - 1; } - int width = indent + size; - int height = extent; - - int xoffset = indent; - int yoffset = (height - size) / 2; - - RGB black = new RGB(0, 0, 0); - PaletteData dataPalette = new PaletteData(black, black, color); - ImageData data = new ImageData(width, height, 4, dataPalette); - data.transparentPixel = 0; - - int end = size - 1; - for (int y = 0; y < size; y++) { - for (int x = 0; x < size; x++) { - if (x == 0 || y == 0 || x == end || y == end) { - data.setPixel(x + xoffset, y + yoffset, 1); - } else { - data.setPixel(x + xoffset, y + yoffset, 2); - } + int size = Math.min(ascent, extent); + int yoffset = (extent - size) / 2; + Display display = w.getDisplay(); + + ImageGcDrawer drawer = new ImageGcDrawer() { + @Override + public void drawOn(GC imageGc, int imageWidth, int imageHeight) { + // GDI+ is needed on Windows to keep the unpainted area transparent + imageGc.setAdvanced(true); + // Border as two fills, a stroke does not align with the fill at + // fractional zoom + imageGc.setBackground(display.getSystemColor(SWT.COLOR_BLACK)); + imageGc.fillRectangle(indent, yoffset, size, size); + imageGc.setBackground(new Color(display, color)); + imageGc.fillRectangle(indent + 1, yoffset + 1, size - 2, size - 2); } - } - return data; + @Override + public int getGcStyle() { + return SWT.TRANSPARENT; + } + }; + return new Image(display, drawer, indent + size, extent); } @Override @@ -230,9 +222,7 @@ protected void updateContents(Object value) { image.dispose(); } - ImageData id = createColorImage(colorLabel.getParent().getParent(), rgb); - ImageData mask = id.getTransparencyMask(); - image = new Image(colorLabel.getDisplay(), id, mask); + image = createColorImage(colorLabel.getParent().getParent(), rgb); colorLabel.setImage(image); rgbLabel