Merged
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
1 change: 0 additions & 1 deletion platform/openide.util.ui/manifest.mf
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,4 +2,3 @@ Manifest-Version: 1.0
OpenIDE-Module: org.openide.util.ui
OpenIDE-Module-Localizing-Bundle: org/openide/util/Bundle.properties
OpenIDE-Module-Specification-Version: 9.34

3 changes: 1 addition & 2 deletions platform/openide.util.ui/nbproject/project.properties
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,8 +16,7 @@
# under the License.

javac.compilerargs=-Xlint -Xlint:-serial -Xlint:-processing
javac.source=1.8
javac.target=1.8
javac.release=11
module.jar.dir=lib


Expand Down
87 changes: 71 additions & 16 deletions platform/openide.util.ui/src/org/openide/util/ImageUtilities.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,6 +34,7 @@
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.MultiResolutionImage;
import java.awt.image.RGBImageFilter;
import java.awt.image.WritableRaster;
import java.io.IOException;
Expand All@@ -46,6 +47,7 @@
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
Expand DownExpand Up@@ -322,10 +324,9 @@ public static final Image mergeImages(Image image1, Image image2, int x, int y)
* @return icon corresponding icon
*/
public static final Icon image2Icon(Image image) {
/* Make sure to always return a ToolTipImage, to take advantage of its rendering tweaks for
HiDPI screens. */
return (image instanceof ToolTipImage)
ToolTipImage ret = (image instanceof ToolTipImage)
? (ToolTipImage) image : assignToolTipToImageInternal(image, "");
return ret.asImageIconIfRequiredForRetina();
}

/**
Expand DownExpand Up@@ -376,8 +377,9 @@ methods in this class may be called from any thread, while JLabel's methods and
// so let's try second most used one type, it satisfies AbstractButton, JCheckbox. Not all cases are
// covered, however.
icon.paintIcon(dummyIconComponentButton, g, 0, 0);
} finally {
g.dispose();
}
g.dispose();
return image;
}

Expand DownExpand Up@@ -1049,17 +1051,12 @@ private static final class IconImageIcon extends ImageIcon {
it volatile instead, to be completely sure that the class is still thread-safe. */
private volatile Icon delegate;

private IconImageIcon(Icon delegate) {
super(icon2Image(delegate));
IconImageIcon(ToolTipImage delegate) {
super(delegate);
Parameters.notNull("delegate", delegate);
this.delegate = delegate;
}

private static ImageIcon create(Icon delegate) {
return (delegate instanceof ImageIcon)
? (ImageIcon) delegate : new IconImageIcon(delegate);
}

@Override
public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
delegate.paintIcon(c, g, x, y);
Expand DownExpand Up@@ -1089,16 +1086,24 @@ private void readObjectNoData() throws ObjectStreamException {
}

/**
* Image with tool tip text (for icons with badges)
* Image with tool tip text (for icons with badges).
*
* <p>On MacOS, HiDPI (Retina) support in JMenuItem.setIcon(Icon) requires the Icon argument to
* be an instance of ImageIcon wrapping a MultiResolutionImage (see
* com.apple.laf.ScreenMenuIcon.setIcon, com.apple.laf.AquaIcon.getImageForIcon, and
* sun.lwawt.macosx.CImage.Creator.createFromImage). Thus we have this class implement
* MultiResolutionImage, and use asImageIcon when needed via asImageIconIfRequiredForRetina.
*/
private static class ToolTipImage extends BufferedImage implements Icon {
private static class ToolTipImage extends BufferedImage implements Icon, MultiResolutionImage {
final String toolTipText;
// May be null.
final Icon delegateIcon;
// May be null.
final URL url;
// May be null.
ImageIcon imageIconVersion;
// May be null.
volatile BufferedImage doubleSizeVariant;

public static ToolTipImage createNew(String toolTipText, Image image, URL url) {
ImageUtilities.ensureLoaded(image);
Expand DownExpand Up@@ -1137,9 +1142,16 @@ public ToolTipImage(
}

public synchronized ImageIcon asImageIcon() {
if (imageIconVersion == null)
imageIconVersion = IconImageIcon.create(this);
return imageIconVersion;
if (imageIconVersion == null) {
imageIconVersion = new IconImageIcon(this);
}
return imageIconVersion;
}

public Icon asImageIconIfRequiredForRetina() {
/* We could choose to do this only on MacOS, but doing it on all platforms will lower
the chance of undetected platform-specific bugs. */
return delegateIcon != null ? asImageIcon() : this;
}

/**
Expand DownExpand Up@@ -1237,6 +1249,49 @@ Image.UndefinedProperty rather than null (see Javadoc spec for this method), but
}
return super.getProperty(name, observer);
}

private Image getDoubleSizeVariant() {
if (delegateIcon == null) {
return null;
}
BufferedImage ret = doubleSizeVariant;
if (ret == null) {
int SCALE = 2;
ColorModel model = getColorModel();
int w = delegateIcon.getIconWidth() * SCALE;
int h = delegateIcon.getIconHeight() * SCALE;
ret = new BufferedImage(
model,
model.createCompatibleWritableRaster(w, h),
model.isAlphaPremultiplied(), null);
Graphics g = ret.createGraphics();
try {
((Graphics2D) g).transform(AffineTransform.getScaleInstance(SCALE, SCALE));
delegateIcon.paintIcon(dummyIconComponentLabel, g, 0, 0);
} finally {
g.dispose();
}
doubleSizeVariant = ret;
}
return ret;
}

@Override
public Image getResolutionVariant(double destImageWidth, double destImageHeight) {
if (destImageWidth <= getWidth(null) && destImageHeight <= getHeight(null)) {
/* Returning "this" should be safe here, as the same is done in
sun.awt.image.MultiResolutionToolkitImage. */
return this;
}
Image ds = getDoubleSizeVariant();
return ds != null ? ds : this;
}

@Override
public List<Image> getResolutionVariants() {
Image ds = getDoubleSizeVariant();
return ds == null ? List.of(this) : List.of(this, ds);
}
}

private static final class DisabledButtonFilter extends RGBImageFilter {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,7 +55,7 @@
import java.awt.datatransfer.Clipboard;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.InvalidDnDOperationException;
import java.awt.dnd.peer.DragSourceContextPeer;
//import java.awt.dnd.peer.DragSourceContextPeer;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
Expand All@@ -64,6 +64,7 @@
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
/*
import java.awt.peer.ButtonPeer;
import java.awt.peer.CanvasPeer;
import java.awt.peer.CheckboxMenuItemPeer;
Expand All@@ -86,6 +87,7 @@
import java.awt.peer.TextAreaPeer;
import java.awt.peer.TextFieldPeer;
import java.awt.peer.WindowPeer;
*/
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.util.ArrayList;
Expand DownExpand Up@@ -320,28 +322,12 @@ public Toolkit getToolkit() {
return customToolkit;
}
}

private static class NoCustomCursorToolkit extends Toolkit {
public FontMetrics getFontMetrics(Font font) {
return Toolkit.getDefaultToolkit().getFontMetrics( font );
}

protected TextFieldPeer createTextField(TextField target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected ListPeer createList(java.awt.List target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected MenuBarPeer createMenuBar(MenuBar target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException {
throw new IllegalStateException("Method not implemented");
}

public boolean prepareImage(Image image, int width, int height, ImageObserver observer) {
return Toolkit.getDefaultToolkit().prepareImage( image, width, height, observer );
}
Expand All@@ -350,30 +336,14 @@ public int checkImage(Image image, int width, int height, ImageObserver observer
return Toolkit.getDefaultToolkit().checkImage( image, width, height, observer );
}

protected PopupMenuPeer createPopupMenu(PopupMenu target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public PrintJob getPrintJob(Frame frame, String jobtitle, Properties props) {
return Toolkit.getDefaultToolkit().getPrintJob( frame, jobtitle, props );
}

protected ButtonPeer createButton(Button target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image createImage(ImageProducer producer) {
return Toolkit.getDefaultToolkit().createImage( producer );
}

protected CanvasPeer createCanvas(Canvas target) {
throw new IllegalStateException("Method not implemented");
}

protected ScrollbarPeer createScrollbar(Scrollbar target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image getImage(String filename) {
return Toolkit.getDefaultToolkit().getImage( filename );
}
Expand All@@ -382,14 +352,6 @@ public Image createImage(String filename) {
return Toolkit.getDefaultToolkit().createImage( filename );
}

protected MenuPeer createMenu(Menu target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected MenuItemPeer createMenuItem(MenuItem target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Map mapInputMethodHighlight(InputMethodHighlight highlight) throws HeadlessException {
return Toolkit.getDefaultToolkit().mapInputMethodHighlight( highlight );
}
Expand All@@ -402,58 +364,10 @@ public Image getImage(URL url) {
return Toolkit.getDefaultToolkit().getImage( url );
}

protected CheckboxPeer createCheckbox(Checkbox target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image createImage(URL url) {
return Toolkit.getDefaultToolkit().createImage( url );
}

protected TextAreaPeer createTextArea(TextArea target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FileDialogPeer createFileDialog(FileDialog target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected ScrollPanePeer createScrollPane(ScrollPane target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected DialogPeer createDialog(Dialog target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected PanelPeer createPanel(Panel target) {
throw new IllegalStateException("Method not implemented");
}

protected ChoicePeer createChoice(Choice target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FramePeer createFrame(Frame target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected LabelPeer createLabel(Label target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FontPeer getFontPeer(String name, int style) {
throw new IllegalStateException("Method not implemented");
}

protected CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected WindowPeer createWindow(Window target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public void sync() {
Toolkit.getDefaultToolkit().sync();
}
Expand DownExpand Up@@ -503,10 +417,6 @@ public Dimension getBestCursorSize(int preferredWidth, int preferredHeight) thro
return new Dimension(0,0);
}

protected DesktopPeer createDesktopPeer(Desktop target) throws HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}

@Override
public boolean isModalityTypeSupported(ModalityType modalityType) {
throw new UnsupportedOperationException("Not supported yet.");
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
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
1 change: 0 additions & 1 deletion platform/openide.util.ui/manifest.mf
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,4 +2,3 @@ Manifest-Version: 1.0
OpenIDE-Module: org.openide.util.ui
OpenIDE-Module-Localizing-Bundle: org/openide/util/Bundle.properties
OpenIDE-Module-Specification-Version: 9.34

3 changes: 1 addition & 2 deletions platform/openide.util.ui/nbproject/project.properties
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,8 +16,7 @@
# under the License.

javac.compilerargs=-Xlint -Xlint:-serial -Xlint:-processing
javac.source=1.8
javac.target=1.8
javac.release=11
module.jar.dir=lib


Expand Down
87 changes: 71 additions & 16 deletions platform/openide.util.ui/src/org/openide/util/ImageUtilities.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,6 +34,7 @@
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.MultiResolutionImage;
import java.awt.image.RGBImageFilter;
import java.awt.image.WritableRaster;
import java.io.IOException;
Expand All@@ -46,6 +47,7 @@
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
Expand DownExpand Up@@ -322,10 +324,9 @@ public static final Image mergeImages(Image image1, Image image2, int x, int y)
* @return icon corresponding icon
*/
public static final Icon image2Icon(Image image) {
/* Make sure to always return a ToolTipImage, to take advantage of its rendering tweaks for
HiDPI screens. */
return (image instanceof ToolTipImage)
ToolTipImage ret = (image instanceof ToolTipImage)
? (ToolTipImage) image : assignToolTipToImageInternal(image, "");
return ret.asImageIconIfRequiredForRetina();
}

/**
Expand DownExpand Up@@ -376,8 +377,9 @@ methods in this class may be called from any thread, while JLabel's methods and
// so let's try second most used one type, it satisfies AbstractButton, JCheckbox. Not all cases are
// covered, however.
icon.paintIcon(dummyIconComponentButton, g, 0, 0);
} finally {
g.dispose();
}
g.dispose();
return image;
}

Expand DownExpand Up@@ -1049,17 +1051,12 @@ private static final class IconImageIcon extends ImageIcon {
it volatile instead, to be completely sure that the class is still thread-safe. */
private volatile Icon delegate;

private IconImageIcon(Icon delegate) {
super(icon2Image(delegate));
IconImageIcon(ToolTipImage delegate) {
super(delegate);
Parameters.notNull("delegate", delegate);
this.delegate = delegate;
}

private static ImageIcon create(Icon delegate) {
return (delegate instanceof ImageIcon)
? (ImageIcon) delegate : new IconImageIcon(delegate);
}

@Override
public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
delegate.paintIcon(c, g, x, y);
Expand DownExpand Up@@ -1089,16 +1086,24 @@ private void readObjectNoData() throws ObjectStreamException {
}

/**
* Image with tool tip text (for icons with badges)
* Image with tool tip text (for icons with badges).
*
* <p>On MacOS, HiDPI (Retina) support in JMenuItem.setIcon(Icon) requires the Icon argument to
* be an instance of ImageIcon wrapping a MultiResolutionImage (see
* com.apple.laf.ScreenMenuIcon.setIcon, com.apple.laf.AquaIcon.getImageForIcon, and
* sun.lwawt.macosx.CImage.Creator.createFromImage). Thus we have this class implement
* MultiResolutionImage, and use asImageIcon when needed via asImageIconIfRequiredForRetina.
*/
private static class ToolTipImage extends BufferedImage implements Icon {
private static class ToolTipImage extends BufferedImage implements Icon, MultiResolutionImage {
final String toolTipText;
// May be null.
final Icon delegateIcon;
// May be null.
final URL url;
// May be null.
ImageIcon imageIconVersion;
// May be null.
volatile BufferedImage doubleSizeVariant;

public static ToolTipImage createNew(String toolTipText, Image image, URL url) {
ImageUtilities.ensureLoaded(image);
Expand DownExpand Up@@ -1137,9 +1142,16 @@ public ToolTipImage(
}

public synchronized ImageIcon asImageIcon() {
if (imageIconVersion == null)
imageIconVersion = IconImageIcon.create(this);
return imageIconVersion;
if (imageIconVersion == null) {
imageIconVersion = new IconImageIcon(this);
}
return imageIconVersion;
}

public Icon asImageIconIfRequiredForRetina() {
/* We could choose to do this only on MacOS, but doing it on all platforms will lower
the chance of undetected platform-specific bugs. */
return delegateIcon != null ? asImageIcon() : this;
}

/**
Expand DownExpand Up@@ -1237,6 +1249,49 @@ Image.UndefinedProperty rather than null (see Javadoc spec for this method), but
}
return super.getProperty(name, observer);
}

private Image getDoubleSizeVariant() {
if (delegateIcon == null) {
return null;
}
BufferedImage ret = doubleSizeVariant;
if (ret == null) {
int SCALE = 2;
ColorModel model = getColorModel();
int w = delegateIcon.getIconWidth() * SCALE;
int h = delegateIcon.getIconHeight() * SCALE;
ret = new BufferedImage(
model,
model.createCompatibleWritableRaster(w, h),
model.isAlphaPremultiplied(), null);
Graphics g = ret.createGraphics();
try {
((Graphics2D) g).transform(AffineTransform.getScaleInstance(SCALE, SCALE));
delegateIcon.paintIcon(dummyIconComponentLabel, g, 0, 0);
} finally {
g.dispose();
}
doubleSizeVariant = ret;
}
return ret;
}

@Override
public Image getResolutionVariant(double destImageWidth, double destImageHeight) {
if (destImageWidth <= getWidth(null) && destImageHeight <= getHeight(null)) {
/* Returning "this" should be safe here, as the same is done in
sun.awt.image.MultiResolutionToolkitImage. */
return this;
}
Image ds = getDoubleSizeVariant();
return ds != null ? ds : this;
}

@Override
public List<Image> getResolutionVariants() {
Image ds = getDoubleSizeVariant();
return ds == null ? List.of(this) : List.of(this, ds);
}
}

private static final class DisabledButtonFilter extends RGBImageFilter {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,7 +55,7 @@
import java.awt.datatransfer.Clipboard;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.InvalidDnDOperationException;
import java.awt.dnd.peer.DragSourceContextPeer;
//import java.awt.dnd.peer.DragSourceContextPeer;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
Expand All@@ -64,6 +64,7 @@
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
/*
import java.awt.peer.ButtonPeer;
import java.awt.peer.CanvasPeer;
import java.awt.peer.CheckboxMenuItemPeer;
Expand All@@ -86,6 +87,7 @@
import java.awt.peer.TextAreaPeer;
import java.awt.peer.TextFieldPeer;
import java.awt.peer.WindowPeer;
*/
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.util.ArrayList;
Expand DownExpand Up@@ -320,28 +322,12 @@ public Toolkit getToolkit() {
return customToolkit;
}
}

private static class NoCustomCursorToolkit extends Toolkit {
public FontMetrics getFontMetrics(Font font) {
return Toolkit.getDefaultToolkit().getFontMetrics( font );
}

protected TextFieldPeer createTextField(TextField target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected ListPeer createList(java.awt.List target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected MenuBarPeer createMenuBar(MenuBar target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException {
throw new IllegalStateException("Method not implemented");
}

public boolean prepareImage(Image image, int width, int height, ImageObserver observer) {
return Toolkit.getDefaultToolkit().prepareImage( image, width, height, observer );
}
Expand All@@ -350,30 +336,14 @@ public int checkImage(Image image, int width, int height, ImageObserver observer
return Toolkit.getDefaultToolkit().checkImage( image, width, height, observer );
}

protected PopupMenuPeer createPopupMenu(PopupMenu target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public PrintJob getPrintJob(Frame frame, String jobtitle, Properties props) {
return Toolkit.getDefaultToolkit().getPrintJob( frame, jobtitle, props );
}

protected ButtonPeer createButton(Button target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image createImage(ImageProducer producer) {
return Toolkit.getDefaultToolkit().createImage( producer );
}

protected CanvasPeer createCanvas(Canvas target) {
throw new IllegalStateException("Method not implemented");
}

protected ScrollbarPeer createScrollbar(Scrollbar target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image getImage(String filename) {
return Toolkit.getDefaultToolkit().getImage( filename );
}
Expand All@@ -382,14 +352,6 @@ public Image createImage(String filename) {
return Toolkit.getDefaultToolkit().createImage( filename );
}

protected MenuPeer createMenu(Menu target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected MenuItemPeer createMenuItem(MenuItem target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Map mapInputMethodHighlight(InputMethodHighlight highlight) throws HeadlessException {
return Toolkit.getDefaultToolkit().mapInputMethodHighlight( highlight );
}
Expand All@@ -402,58 +364,10 @@ public Image getImage(URL url) {
return Toolkit.getDefaultToolkit().getImage( url );
}

protected CheckboxPeer createCheckbox(Checkbox target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image createImage(URL url) {
return Toolkit.getDefaultToolkit().createImage( url );
}

protected TextAreaPeer createTextArea(TextArea target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FileDialogPeer createFileDialog(FileDialog target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected ScrollPanePeer createScrollPane(ScrollPane target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected DialogPeer createDialog(Dialog target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected PanelPeer createPanel(Panel target) {
throw new IllegalStateException("Method not implemented");
}

protected ChoicePeer createChoice(Choice target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FramePeer createFrame(Frame target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected LabelPeer createLabel(Label target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FontPeer getFontPeer(String name, int style) {
throw new IllegalStateException("Method not implemented");
}

protected CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected WindowPeer createWindow(Window target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public void sync() {
Toolkit.getDefaultToolkit().sync();
}
Expand DownExpand Up@@ -503,10 +417,6 @@ public Dimension getBestCursorSize(int preferredWidth, int preferredHeight) thro
return new Dimension(0,0);
}

protected DesktopPeer createDesktopPeer(Desktop target) throws HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}

@Override
public boolean isModalityTypeSupported(ModalityType modalityType) {
throw new UnsupportedOperationException("Not supported yet.");
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
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
1 change: 0 additions & 1 deletion platform/openide.util.ui/manifest.mf
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,4 +2,3 @@ Manifest-Version: 1.0
OpenIDE-Module: org.openide.util.ui
OpenIDE-Module-Localizing-Bundle: org/openide/util/Bundle.properties
OpenIDE-Module-Specification-Version: 9.34

3 changes: 1 addition & 2 deletions platform/openide.util.ui/nbproject/project.properties
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,8 +16,7 @@
# under the License.

javac.compilerargs=-Xlint -Xlint:-serial -Xlint:-processing
javac.source=1.8
javac.target=1.8
javac.release=11
module.jar.dir=lib


Expand Down
87 changes: 71 additions & 16 deletions platform/openide.util.ui/src/org/openide/util/ImageUtilities.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,6 +34,7 @@
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.MultiResolutionImage;
import java.awt.image.RGBImageFilter;
import java.awt.image.WritableRaster;
import java.io.IOException;
Expand All@@ -46,6 +47,7 @@
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
Expand DownExpand Up@@ -322,10 +324,9 @@ public static final Image mergeImages(Image image1, Image image2, int x, int y)
* @return icon corresponding icon
*/
public static final Icon image2Icon(Image image) {
/* Make sure to always return a ToolTipImage, to take advantage of its rendering tweaks for
HiDPI screens. */
return (image instanceof ToolTipImage)
ToolTipImage ret = (image instanceof ToolTipImage)
? (ToolTipImage) image : assignToolTipToImageInternal(image, "");
return ret.asImageIconIfRequiredForRetina();
}

/**
Expand DownExpand Up@@ -376,8 +377,9 @@ methods in this class may be called from any thread, while JLabel's methods and
// so let's try second most used one type, it satisfies AbstractButton, JCheckbox. Not all cases are
// covered, however.
icon.paintIcon(dummyIconComponentButton, g, 0, 0);
} finally {
g.dispose();
}
g.dispose();
return image;
}

Expand DownExpand Up@@ -1049,17 +1051,12 @@ private static final class IconImageIcon extends ImageIcon {
it volatile instead, to be completely sure that the class is still thread-safe. */
private volatile Icon delegate;

private IconImageIcon(Icon delegate) {
super(icon2Image(delegate));
IconImageIcon(ToolTipImage delegate) {
super(delegate);
Parameters.notNull("delegate", delegate);
this.delegate = delegate;
}

private static ImageIcon create(Icon delegate) {
return (delegate instanceof ImageIcon)
? (ImageIcon) delegate : new IconImageIcon(delegate);
}

@Override
public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
delegate.paintIcon(c, g, x, y);
Expand DownExpand Up@@ -1089,16 +1086,24 @@ private void readObjectNoData() throws ObjectStreamException {
}

/**
* Image with tool tip text (for icons with badges)
* Image with tool tip text (for icons with badges).
*
* <p>On MacOS, HiDPI (Retina) support in JMenuItem.setIcon(Icon) requires the Icon argument to
* be an instance of ImageIcon wrapping a MultiResolutionImage (see
* com.apple.laf.ScreenMenuIcon.setIcon, com.apple.laf.AquaIcon.getImageForIcon, and
* sun.lwawt.macosx.CImage.Creator.createFromImage). Thus we have this class implement
* MultiResolutionImage, and use asImageIcon when needed via asImageIconIfRequiredForRetina.
*/
private static class ToolTipImage extends BufferedImage implements Icon {
private static class ToolTipImage extends BufferedImage implements Icon, MultiResolutionImage {
final String toolTipText;
// May be null.
final Icon delegateIcon;
// May be null.
final URL url;
// May be null.
ImageIcon imageIconVersion;
// May be null.
volatile BufferedImage doubleSizeVariant;

public static ToolTipImage createNew(String toolTipText, Image image, URL url) {
ImageUtilities.ensureLoaded(image);
Expand DownExpand Up@@ -1137,9 +1142,16 @@ public ToolTipImage(
}

public synchronized ImageIcon asImageIcon() {
if (imageIconVersion == null)
imageIconVersion = IconImageIcon.create(this);
return imageIconVersion;
if (imageIconVersion == null) {
imageIconVersion = new IconImageIcon(this);
}
return imageIconVersion;
}

public Icon asImageIconIfRequiredForRetina() {
/* We could choose to do this only on MacOS, but doing it on all platforms will lower
the chance of undetected platform-specific bugs. */
return delegateIcon != null ? asImageIcon() : this;
}

/**
Expand DownExpand Up@@ -1237,6 +1249,49 @@ Image.UndefinedProperty rather than null (see Javadoc spec for this method), but
}
return super.getProperty(name, observer);
}

private Image getDoubleSizeVariant() {
if (delegateIcon == null) {
return null;
}
BufferedImage ret = doubleSizeVariant;
if (ret == null) {
int SCALE = 2;
ColorModel model = getColorModel();
int w = delegateIcon.getIconWidth() * SCALE;
int h = delegateIcon.getIconHeight() * SCALE;
ret = new BufferedImage(
model,
model.createCompatibleWritableRaster(w, h),
model.isAlphaPremultiplied(), null);
Graphics g = ret.createGraphics();
try {
((Graphics2D) g).transform(AffineTransform.getScaleInstance(SCALE, SCALE));
delegateIcon.paintIcon(dummyIconComponentLabel, g, 0, 0);
} finally {
g.dispose();
}
doubleSizeVariant = ret;
}
return ret;
}

@Override
public Image getResolutionVariant(double destImageWidth, double destImageHeight) {
if (destImageWidth <= getWidth(null) && destImageHeight <= getHeight(null)) {
/* Returning "this" should be safe here, as the same is done in
sun.awt.image.MultiResolutionToolkitImage. */
return this;
}
Image ds = getDoubleSizeVariant();
return ds != null ? ds : this;
}

@Override
public List<Image> getResolutionVariants() {
Image ds = getDoubleSizeVariant();
return ds == null ? List.of(this) : List.of(this, ds);
}
}

private static final class DisabledButtonFilter extends RGBImageFilter {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,7 +55,7 @@
import java.awt.datatransfer.Clipboard;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.InvalidDnDOperationException;
import java.awt.dnd.peer.DragSourceContextPeer;
//import java.awt.dnd.peer.DragSourceContextPeer;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
Expand All@@ -64,6 +64,7 @@
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
/*
import java.awt.peer.ButtonPeer;
import java.awt.peer.CanvasPeer;
import java.awt.peer.CheckboxMenuItemPeer;
Expand All@@ -86,6 +87,7 @@
import java.awt.peer.TextAreaPeer;
import java.awt.peer.TextFieldPeer;
import java.awt.peer.WindowPeer;
*/
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.util.ArrayList;
Expand DownExpand Up@@ -320,28 +322,12 @@ public Toolkit getToolkit() {
return customToolkit;
}
}

private static class NoCustomCursorToolkit extends Toolkit {
public FontMetrics getFontMetrics(Font font) {
return Toolkit.getDefaultToolkit().getFontMetrics( font );
}

protected TextFieldPeer createTextField(TextField target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected ListPeer createList(java.awt.List target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected MenuBarPeer createMenuBar(MenuBar target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException {
throw new IllegalStateException("Method not implemented");
}

public boolean prepareImage(Image image, int width, int height, ImageObserver observer) {
return Toolkit.getDefaultToolkit().prepareImage( image, width, height, observer );
}
Expand All@@ -350,30 +336,14 @@ public int checkImage(Image image, int width, int height, ImageObserver observer
return Toolkit.getDefaultToolkit().checkImage( image, width, height, observer );
}

protected PopupMenuPeer createPopupMenu(PopupMenu target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public PrintJob getPrintJob(Frame frame, String jobtitle, Properties props) {
return Toolkit.getDefaultToolkit().getPrintJob( frame, jobtitle, props );
}

protected ButtonPeer createButton(Button target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image createImage(ImageProducer producer) {
return Toolkit.getDefaultToolkit().createImage( producer );
}

protected CanvasPeer createCanvas(Canvas target) {
throw new IllegalStateException("Method not implemented");
}

protected ScrollbarPeer createScrollbar(Scrollbar target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image getImage(String filename) {
return Toolkit.getDefaultToolkit().getImage( filename );
}
Expand All@@ -382,14 +352,6 @@ public Image createImage(String filename) {
return Toolkit.getDefaultToolkit().createImage( filename );
}

protected MenuPeer createMenu(Menu target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected MenuItemPeer createMenuItem(MenuItem target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Map mapInputMethodHighlight(InputMethodHighlight highlight) throws HeadlessException {
return Toolkit.getDefaultToolkit().mapInputMethodHighlight( highlight );
}
Expand All@@ -402,58 +364,10 @@ public Image getImage(URL url) {
return Toolkit.getDefaultToolkit().getImage( url );
}

protected CheckboxPeer createCheckbox(Checkbox target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image createImage(URL url) {
return Toolkit.getDefaultToolkit().createImage( url );
}

protected TextAreaPeer createTextArea(TextArea target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FileDialogPeer createFileDialog(FileDialog target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected ScrollPanePeer createScrollPane(ScrollPane target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected DialogPeer createDialog(Dialog target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected PanelPeer createPanel(Panel target) {
throw new IllegalStateException("Method not implemented");
}

protected ChoicePeer createChoice(Choice target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FramePeer createFrame(Frame target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected LabelPeer createLabel(Label target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FontPeer getFontPeer(String name, int style) {
throw new IllegalStateException("Method not implemented");
}

protected CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected WindowPeer createWindow(Window target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public void sync() {
Toolkit.getDefaultToolkit().sync();
}
Expand DownExpand Up@@ -503,10 +417,6 @@ public Dimension getBestCursorSize(int preferredWidth, int preferredHeight) thro
return new Dimension(0,0);
}

protected DesktopPeer createDesktopPeer(Desktop target) throws HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}

@Override
public boolean isModalityTypeSupported(ModalityType modalityType) {
throw new UnsupportedOperationException("Not supported yet.");
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
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
1 change: 0 additions & 1 deletion platform/openide.util.ui/manifest.mf
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,4 +2,3 @@ Manifest-Version: 1.0
OpenIDE-Module: org.openide.util.ui
OpenIDE-Module-Localizing-Bundle: org/openide/util/Bundle.properties
OpenIDE-Module-Specification-Version: 9.34

3 changes: 1 addition & 2 deletions platform/openide.util.ui/nbproject/project.properties
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,8 +16,7 @@
# under the License.

javac.compilerargs=-Xlint -Xlint:-serial -Xlint:-processing
javac.source=1.8
javac.target=1.8
javac.release=11
module.jar.dir=lib


Expand Down
87 changes: 71 additions & 16 deletions platform/openide.util.ui/src/org/openide/util/ImageUtilities.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,6 +34,7 @@
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.MultiResolutionImage;
import java.awt.image.RGBImageFilter;
import java.awt.image.WritableRaster;
import java.io.IOException;
Expand All@@ -46,6 +47,7 @@
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
Expand DownExpand Up@@ -322,10 +324,9 @@ public static final Image mergeImages(Image image1, Image image2, int x, int y)
* @return icon corresponding icon
*/
public static final Icon image2Icon(Image image) {
/* Make sure to always return a ToolTipImage, to take advantage of its rendering tweaks for
HiDPI screens. */
return (image instanceof ToolTipImage)
ToolTipImage ret = (image instanceof ToolTipImage)
? (ToolTipImage) image : assignToolTipToImageInternal(image, "");
return ret.asImageIconIfRequiredForRetina();
}

/**
Expand DownExpand Up@@ -376,8 +377,9 @@ methods in this class may be called from any thread, while JLabel's methods and
// so let's try second most used one type, it satisfies AbstractButton, JCheckbox. Not all cases are
// covered, however.
icon.paintIcon(dummyIconComponentButton, g, 0, 0);
} finally {
g.dispose();
}
g.dispose();
return image;
}

Expand DownExpand Up@@ -1049,17 +1051,12 @@ private static final class IconImageIcon extends ImageIcon {
it volatile instead, to be completely sure that the class is still thread-safe. */
private volatile Icon delegate;

private IconImageIcon(Icon delegate) {
super(icon2Image(delegate));
IconImageIcon(ToolTipImage delegate) {
super(delegate);
Parameters.notNull("delegate", delegate);
this.delegate = delegate;
}

private static ImageIcon create(Icon delegate) {
return (delegate instanceof ImageIcon)
? (ImageIcon) delegate : new IconImageIcon(delegate);
}

@Override
public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
delegate.paintIcon(c, g, x, y);
Expand DownExpand Up@@ -1089,16 +1086,24 @@ private void readObjectNoData() throws ObjectStreamException {
}

/**
* Image with tool tip text (for icons with badges)
* Image with tool tip text (for icons with badges).
*
* <p>On MacOS, HiDPI (Retina) support in JMenuItem.setIcon(Icon) requires the Icon argument to
* be an instance of ImageIcon wrapping a MultiResolutionImage (see
* com.apple.laf.ScreenMenuIcon.setIcon, com.apple.laf.AquaIcon.getImageForIcon, and
* sun.lwawt.macosx.CImage.Creator.createFromImage). Thus we have this class implement
* MultiResolutionImage, and use asImageIcon when needed via asImageIconIfRequiredForRetina.
*/
private static class ToolTipImage extends BufferedImage implements Icon {
private static class ToolTipImage extends BufferedImage implements Icon, MultiResolutionImage {
final String toolTipText;
// May be null.
final Icon delegateIcon;
// May be null.
final URL url;
// May be null.
ImageIcon imageIconVersion;
// May be null.
volatile BufferedImage doubleSizeVariant;

public static ToolTipImage createNew(String toolTipText, Image image, URL url) {
ImageUtilities.ensureLoaded(image);
Expand DownExpand Up@@ -1137,9 +1142,16 @@ public ToolTipImage(
}

public synchronized ImageIcon asImageIcon() {
if (imageIconVersion == null)
imageIconVersion = IconImageIcon.create(this);
return imageIconVersion;
if (imageIconVersion == null) {
imageIconVersion = new IconImageIcon(this);
}
return imageIconVersion;
}

public Icon asImageIconIfRequiredForRetina() {
/* We could choose to do this only on MacOS, but doing it on all platforms will lower
the chance of undetected platform-specific bugs. */
return delegateIcon != null ? asImageIcon() : this;
}

/**
Expand DownExpand Up@@ -1237,6 +1249,49 @@ Image.UndefinedProperty rather than null (see Javadoc spec for this method), but
}
return super.getProperty(name, observer);
}

private Image getDoubleSizeVariant() {
if (delegateIcon == null) {
return null;
}
BufferedImage ret = doubleSizeVariant;
if (ret == null) {
int SCALE = 2;
ColorModel model = getColorModel();
int w = delegateIcon.getIconWidth() * SCALE;
int h = delegateIcon.getIconHeight() * SCALE;
ret = new BufferedImage(
model,
model.createCompatibleWritableRaster(w, h),
model.isAlphaPremultiplied(), null);
Graphics g = ret.createGraphics();
try {
((Graphics2D) g).transform(AffineTransform.getScaleInstance(SCALE, SCALE));
delegateIcon.paintIcon(dummyIconComponentLabel, g, 0, 0);
} finally {
g.dispose();
}
doubleSizeVariant = ret;
}
return ret;
}

@Override
public Image getResolutionVariant(double destImageWidth, double destImageHeight) {
if (destImageWidth <= getWidth(null) && destImageHeight <= getHeight(null)) {
/* Returning "this" should be safe here, as the same is done in
sun.awt.image.MultiResolutionToolkitImage. */
return this;
}
Image ds = getDoubleSizeVariant();
return ds != null ? ds : this;
}

@Override
public List<Image> getResolutionVariants() {
Image ds = getDoubleSizeVariant();
return ds == null ? List.of(this) : List.of(this, ds);
}
}

private static final class DisabledButtonFilter extends RGBImageFilter {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,7 +55,7 @@
import java.awt.datatransfer.Clipboard;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.InvalidDnDOperationException;
import java.awt.dnd.peer.DragSourceContextPeer;
//import java.awt.dnd.peer.DragSourceContextPeer;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
Expand All@@ -64,6 +64,7 @@
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
/*
import java.awt.peer.ButtonPeer;
import java.awt.peer.CanvasPeer;
import java.awt.peer.CheckboxMenuItemPeer;
Expand All@@ -86,6 +87,7 @@
import java.awt.peer.TextAreaPeer;
import java.awt.peer.TextFieldPeer;
import java.awt.peer.WindowPeer;
*/
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.util.ArrayList;
Expand DownExpand Up@@ -320,28 +322,12 @@ public Toolkit getToolkit() {
return customToolkit;
}
}

private static class NoCustomCursorToolkit extends Toolkit {
public FontMetrics getFontMetrics(Font font) {
return Toolkit.getDefaultToolkit().getFontMetrics( font );
}

protected TextFieldPeer createTextField(TextField target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected ListPeer createList(java.awt.List target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected MenuBarPeer createMenuBar(MenuBar target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException {
throw new IllegalStateException("Method not implemented");
}

public boolean prepareImage(Image image, int width, int height, ImageObserver observer) {
return Toolkit.getDefaultToolkit().prepareImage( image, width, height, observer );
}
Expand All@@ -350,30 +336,14 @@ public int checkImage(Image image, int width, int height, ImageObserver observer
return Toolkit.getDefaultToolkit().checkImage( image, width, height, observer );
}

protected PopupMenuPeer createPopupMenu(PopupMenu target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public PrintJob getPrintJob(Frame frame, String jobtitle, Properties props) {
return Toolkit.getDefaultToolkit().getPrintJob( frame, jobtitle, props );
}

protected ButtonPeer createButton(Button target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image createImage(ImageProducer producer) {
return Toolkit.getDefaultToolkit().createImage( producer );
}

protected CanvasPeer createCanvas(Canvas target) {
throw new IllegalStateException("Method not implemented");
}

protected ScrollbarPeer createScrollbar(Scrollbar target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image getImage(String filename) {
return Toolkit.getDefaultToolkit().getImage( filename );
}
Expand All@@ -382,14 +352,6 @@ public Image createImage(String filename) {
return Toolkit.getDefaultToolkit().createImage( filename );
}

protected MenuPeer createMenu(Menu target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected MenuItemPeer createMenuItem(MenuItem target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Map mapInputMethodHighlight(InputMethodHighlight highlight) throws HeadlessException {
return Toolkit.getDefaultToolkit().mapInputMethodHighlight( highlight );
}
Expand All@@ -402,58 +364,10 @@ public Image getImage(URL url) {
return Toolkit.getDefaultToolkit().getImage( url );
}

protected CheckboxPeer createCheckbox(Checkbox target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image createImage(URL url) {
return Toolkit.getDefaultToolkit().createImage( url );
}

protected TextAreaPeer createTextArea(TextArea target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FileDialogPeer createFileDialog(FileDialog target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected ScrollPanePeer createScrollPane(ScrollPane target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected DialogPeer createDialog(Dialog target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected PanelPeer createPanel(Panel target) {
throw new IllegalStateException("Method not implemented");
}

protected ChoicePeer createChoice(Choice target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FramePeer createFrame(Frame target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected LabelPeer createLabel(Label target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FontPeer getFontPeer(String name, int style) {
throw new IllegalStateException("Method not implemented");
}

protected CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected WindowPeer createWindow(Window target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public void sync() {
Toolkit.getDefaultToolkit().sync();
}
Expand DownExpand Up@@ -503,10 +417,6 @@ public Dimension getBestCursorSize(int preferredWidth, int preferredHeight) thro
return new Dimension(0,0);
}

protected DesktopPeer createDesktopPeer(Desktop target) throws HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}

@Override
public boolean isModalityTypeSupported(ModalityType modalityType) {
throw new UnsupportedOperationException("Not supported yet.");
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
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
1 change: 0 additions & 1 deletion platform/openide.util.ui/manifest.mf
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,4 +2,3 @@ Manifest-Version: 1.0
OpenIDE-Module: org.openide.util.ui
OpenIDE-Module-Localizing-Bundle: org/openide/util/Bundle.properties
OpenIDE-Module-Specification-Version: 9.34

3 changes: 1 addition & 2 deletions platform/openide.util.ui/nbproject/project.properties
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,8 +16,7 @@
# under the License.

javac.compilerargs=-Xlint -Xlint:-serial -Xlint:-processing
javac.source=1.8
javac.target=1.8
javac.release=11
module.jar.dir=lib


Expand Down
87 changes: 71 additions & 16 deletions platform/openide.util.ui/src/org/openide/util/ImageUtilities.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,6 +34,7 @@
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.MultiResolutionImage;
import java.awt.image.RGBImageFilter;
import java.awt.image.WritableRaster;
import java.io.IOException;
Expand All@@ -46,6 +47,7 @@
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
Expand DownExpand Up@@ -322,10 +324,9 @@ public static final Image mergeImages(Image image1, Image image2, int x, int y)
* @return icon corresponding icon
*/
public static final Icon image2Icon(Image image) {
/* Make sure to always return a ToolTipImage, to take advantage of its rendering tweaks for
HiDPI screens. */
return (image instanceof ToolTipImage)
ToolTipImage ret = (image instanceof ToolTipImage)
? (ToolTipImage) image : assignToolTipToImageInternal(image, "");
return ret.asImageIconIfRequiredForRetina();
}

/**
Expand DownExpand Up@@ -376,8 +377,9 @@ methods in this class may be called from any thread, while JLabel's methods and
// so let's try second most used one type, it satisfies AbstractButton, JCheckbox. Not all cases are
// covered, however.
icon.paintIcon(dummyIconComponentButton, g, 0, 0);
} finally {
g.dispose();
}
g.dispose();
return image;
}

Expand DownExpand Up@@ -1049,17 +1051,12 @@ private static final class IconImageIcon extends ImageIcon {
it volatile instead, to be completely sure that the class is still thread-safe. */
private volatile Icon delegate;

private IconImageIcon(Icon delegate) {
super(icon2Image(delegate));
IconImageIcon(ToolTipImage delegate) {
super(delegate);
Parameters.notNull("delegate", delegate);
this.delegate = delegate;
}

private static ImageIcon create(Icon delegate) {
return (delegate instanceof ImageIcon)
? (ImageIcon) delegate : new IconImageIcon(delegate);
}

@Override
public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
delegate.paintIcon(c, g, x, y);
Expand DownExpand Up@@ -1089,16 +1086,24 @@ private void readObjectNoData() throws ObjectStreamException {
}

/**
* Image with tool tip text (for icons with badges)
* Image with tool tip text (for icons with badges).
*
* <p>On MacOS, HiDPI (Retina) support in JMenuItem.setIcon(Icon) requires the Icon argument to
* be an instance of ImageIcon wrapping a MultiResolutionImage (see
* com.apple.laf.ScreenMenuIcon.setIcon, com.apple.laf.AquaIcon.getImageForIcon, and
* sun.lwawt.macosx.CImage.Creator.createFromImage). Thus we have this class implement
* MultiResolutionImage, and use asImageIcon when needed via asImageIconIfRequiredForRetina.
*/
private static class ToolTipImage extends BufferedImage implements Icon {
private static class ToolTipImage extends BufferedImage implements Icon, MultiResolutionImage {
final String toolTipText;
// May be null.
final Icon delegateIcon;
// May be null.
final URL url;
// May be null.
ImageIcon imageIconVersion;
// May be null.
volatile BufferedImage doubleSizeVariant;

public static ToolTipImage createNew(String toolTipText, Image image, URL url) {
ImageUtilities.ensureLoaded(image);
Expand DownExpand Up@@ -1137,9 +1142,16 @@ public ToolTipImage(
}

public synchronized ImageIcon asImageIcon() {
if (imageIconVersion == null)
imageIconVersion = IconImageIcon.create(this);
return imageIconVersion;
if (imageIconVersion == null) {
imageIconVersion = new IconImageIcon(this);
}
return imageIconVersion;
}

public Icon asImageIconIfRequiredForRetina() {
/* We could choose to do this only on MacOS, but doing it on all platforms will lower
the chance of undetected platform-specific bugs. */
return delegateIcon != null ? asImageIcon() : this;
}

/**
Expand DownExpand Up@@ -1237,6 +1249,49 @@ Image.UndefinedProperty rather than null (see Javadoc spec for this method), but
}
return super.getProperty(name, observer);
}

private Image getDoubleSizeVariant() {
if (delegateIcon == null) {
return null;
}
BufferedImage ret = doubleSizeVariant;
if (ret == null) {
int SCALE = 2;
ColorModel model = getColorModel();
int w = delegateIcon.getIconWidth() * SCALE;
int h = delegateIcon.getIconHeight() * SCALE;
ret = new BufferedImage(
model,
model.createCompatibleWritableRaster(w, h),
model.isAlphaPremultiplied(), null);
Graphics g = ret.createGraphics();
try {
((Graphics2D) g).transform(AffineTransform.getScaleInstance(SCALE, SCALE));
delegateIcon.paintIcon(dummyIconComponentLabel, g, 0, 0);
} finally {
g.dispose();
}
doubleSizeVariant = ret;
}
return ret;
}

@Override
public Image getResolutionVariant(double destImageWidth, double destImageHeight) {
if (destImageWidth <= getWidth(null) && destImageHeight <= getHeight(null)) {
/* Returning "this" should be safe here, as the same is done in
sun.awt.image.MultiResolutionToolkitImage. */
return this;
}
Image ds = getDoubleSizeVariant();
return ds != null ? ds : this;
}

@Override
public List<Image> getResolutionVariants() {
Image ds = getDoubleSizeVariant();
return ds == null ? List.of(this) : List.of(this, ds);
}
}

private static final class DisabledButtonFilter extends RGBImageFilter {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,7 +55,7 @@
import java.awt.datatransfer.Clipboard;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.InvalidDnDOperationException;
import java.awt.dnd.peer.DragSourceContextPeer;
//import java.awt.dnd.peer.DragSourceContextPeer;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
Expand All@@ -64,6 +64,7 @@
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
/*
import java.awt.peer.ButtonPeer;
import java.awt.peer.CanvasPeer;
import java.awt.peer.CheckboxMenuItemPeer;
Expand All@@ -86,6 +87,7 @@
import java.awt.peer.TextAreaPeer;
import java.awt.peer.TextFieldPeer;
import java.awt.peer.WindowPeer;
*/
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.util.ArrayList;
Expand DownExpand Up@@ -320,28 +322,12 @@ public Toolkit getToolkit() {
return customToolkit;
}
}

private static class NoCustomCursorToolkit extends Toolkit {
public FontMetrics getFontMetrics(Font font) {
return Toolkit.getDefaultToolkit().getFontMetrics( font );
}

protected TextFieldPeer createTextField(TextField target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected ListPeer createList(java.awt.List target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected MenuBarPeer createMenuBar(MenuBar target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException {
throw new IllegalStateException("Method not implemented");
}

public boolean prepareImage(Image image, int width, int height, ImageObserver observer) {
return Toolkit.getDefaultToolkit().prepareImage( image, width, height, observer );
}
Expand All@@ -350,30 +336,14 @@ public int checkImage(Image image, int width, int height, ImageObserver observer
return Toolkit.getDefaultToolkit().checkImage( image, width, height, observer );
}

protected PopupMenuPeer createPopupMenu(PopupMenu target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public PrintJob getPrintJob(Frame frame, String jobtitle, Properties props) {
return Toolkit.getDefaultToolkit().getPrintJob( frame, jobtitle, props );
}

protected ButtonPeer createButton(Button target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image createImage(ImageProducer producer) {
return Toolkit.getDefaultToolkit().createImage( producer );
}

protected CanvasPeer createCanvas(Canvas target) {
throw new IllegalStateException("Method not implemented");
}

protected ScrollbarPeer createScrollbar(Scrollbar target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image getImage(String filename) {
return Toolkit.getDefaultToolkit().getImage( filename );
}
Expand All@@ -382,14 +352,6 @@ public Image createImage(String filename) {
return Toolkit.getDefaultToolkit().createImage( filename );
}

protected MenuPeer createMenu(Menu target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected MenuItemPeer createMenuItem(MenuItem target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Map mapInputMethodHighlight(InputMethodHighlight highlight) throws HeadlessException {
return Toolkit.getDefaultToolkit().mapInputMethodHighlight( highlight );
}
Expand All@@ -402,58 +364,10 @@ public Image getImage(URL url) {
return Toolkit.getDefaultToolkit().getImage( url );
}

protected CheckboxPeer createCheckbox(Checkbox target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image createImage(URL url) {
return Toolkit.getDefaultToolkit().createImage( url );
}

protected TextAreaPeer createTextArea(TextArea target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FileDialogPeer createFileDialog(FileDialog target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected ScrollPanePeer createScrollPane(ScrollPane target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected DialogPeer createDialog(Dialog target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected PanelPeer createPanel(Panel target) {
throw new IllegalStateException("Method not implemented");
}

protected ChoicePeer createChoice(Choice target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FramePeer createFrame(Frame target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected LabelPeer createLabel(Label target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FontPeer getFontPeer(String name, int style) {
throw new IllegalStateException("Method not implemented");
}

protected CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected WindowPeer createWindow(Window target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public void sync() {
Toolkit.getDefaultToolkit().sync();
}
Expand DownExpand Up@@ -503,10 +417,6 @@ public Dimension getBestCursorSize(int preferredWidth, int preferredHeight) thro
return new Dimension(0,0);
}

protected DesktopPeer createDesktopPeer(Desktop target) throws HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}

@Override
public boolean isModalityTypeSupported(ModalityType modalityType) {
throw new UnsupportedOperationException("Not supported yet.");
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
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
1 change: 0 additions & 1 deletion platform/openide.util.ui/manifest.mf
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,4 +2,3 @@ Manifest-Version: 1.0
OpenIDE-Module: org.openide.util.ui
OpenIDE-Module-Localizing-Bundle: org/openide/util/Bundle.properties
OpenIDE-Module-Specification-Version: 9.34

3 changes: 1 addition & 2 deletions platform/openide.util.ui/nbproject/project.properties
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,8 +16,7 @@
# under the License.

javac.compilerargs=-Xlint -Xlint:-serial -Xlint:-processing
javac.source=1.8
javac.target=1.8
javac.release=11
module.jar.dir=lib


Expand Down
87 changes: 71 additions & 16 deletions platform/openide.util.ui/src/org/openide/util/ImageUtilities.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,6 +34,7 @@
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.MultiResolutionImage;
import java.awt.image.RGBImageFilter;
import java.awt.image.WritableRaster;
import java.io.IOException;
Expand All@@ -46,6 +47,7 @@
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
Expand DownExpand Up@@ -322,10 +324,9 @@ public static final Image mergeImages(Image image1, Image image2, int x, int y)
* @return icon corresponding icon
*/
public static final Icon image2Icon(Image image) {
/* Make sure to always return a ToolTipImage, to take advantage of its rendering tweaks for
HiDPI screens. */
return (image instanceof ToolTipImage)
ToolTipImage ret = (image instanceof ToolTipImage)
? (ToolTipImage) image : assignToolTipToImageInternal(image, "");
return ret.asImageIconIfRequiredForRetina();
}

/**
Expand DownExpand Up@@ -376,8 +377,9 @@ methods in this class may be called from any thread, while JLabel's methods and
// so let's try second most used one type, it satisfies AbstractButton, JCheckbox. Not all cases are
// covered, however.
icon.paintIcon(dummyIconComponentButton, g, 0, 0);
} finally {
g.dispose();
}
g.dispose();
return image;
}

Expand DownExpand Up@@ -1049,17 +1051,12 @@ private static final class IconImageIcon extends ImageIcon {
it volatile instead, to be completely sure that the class is still thread-safe. */
private volatile Icon delegate;

private IconImageIcon(Icon delegate) {
super(icon2Image(delegate));
IconImageIcon(ToolTipImage delegate) {
super(delegate);
Parameters.notNull("delegate", delegate);
this.delegate = delegate;
}

private static ImageIcon create(Icon delegate) {
return (delegate instanceof ImageIcon)
? (ImageIcon) delegate : new IconImageIcon(delegate);
}

@Override
public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
delegate.paintIcon(c, g, x, y);
Expand DownExpand Up@@ -1089,16 +1086,24 @@ private void readObjectNoData() throws ObjectStreamException {
}

/**
* Image with tool tip text (for icons with badges)
* Image with tool tip text (for icons with badges).
*
* <p>On MacOS, HiDPI (Retina) support in JMenuItem.setIcon(Icon) requires the Icon argument to
* be an instance of ImageIcon wrapping a MultiResolutionImage (see
* com.apple.laf.ScreenMenuIcon.setIcon, com.apple.laf.AquaIcon.getImageForIcon, and
* sun.lwawt.macosx.CImage.Creator.createFromImage). Thus we have this class implement
* MultiResolutionImage, and use asImageIcon when needed via asImageIconIfRequiredForRetina.
*/
private static class ToolTipImage extends BufferedImage implements Icon {
private static class ToolTipImage extends BufferedImage implements Icon, MultiResolutionImage {
final String toolTipText;
// May be null.
final Icon delegateIcon;
// May be null.
final URL url;
// May be null.
ImageIcon imageIconVersion;
// May be null.
volatile BufferedImage doubleSizeVariant;

public static ToolTipImage createNew(String toolTipText, Image image, URL url) {
ImageUtilities.ensureLoaded(image);
Expand DownExpand Up@@ -1137,9 +1142,16 @@ public ToolTipImage(
}

public synchronized ImageIcon asImageIcon() {
if (imageIconVersion == null)
imageIconVersion = IconImageIcon.create(this);
return imageIconVersion;
if (imageIconVersion == null) {
imageIconVersion = new IconImageIcon(this);
}
return imageIconVersion;
}

public Icon asImageIconIfRequiredForRetina() {
/* We could choose to do this only on MacOS, but doing it on all platforms will lower
the chance of undetected platform-specific bugs. */
return delegateIcon != null ? asImageIcon() : this;
}

/**
Expand DownExpand Up@@ -1237,6 +1249,49 @@ Image.UndefinedProperty rather than null (see Javadoc spec for this method), but
}
return super.getProperty(name, observer);
}

private Image getDoubleSizeVariant() {
if (delegateIcon == null) {
return null;
}
BufferedImage ret = doubleSizeVariant;
if (ret == null) {
int SCALE = 2;
ColorModel model = getColorModel();
int w = delegateIcon.getIconWidth() * SCALE;
int h = delegateIcon.getIconHeight() * SCALE;
ret = new BufferedImage(
model,
model.createCompatibleWritableRaster(w, h),
model.isAlphaPremultiplied(), null);
Graphics g = ret.createGraphics();
try {
((Graphics2D) g).transform(AffineTransform.getScaleInstance(SCALE, SCALE));
delegateIcon.paintIcon(dummyIconComponentLabel, g, 0, 0);
} finally {
g.dispose();
}
doubleSizeVariant = ret;
}
return ret;
}

@Override
public Image getResolutionVariant(double destImageWidth, double destImageHeight) {
if (destImageWidth <= getWidth(null) && destImageHeight <= getHeight(null)) {
/* Returning "this" should be safe here, as the same is done in
sun.awt.image.MultiResolutionToolkitImage. */
return this;
}
Image ds = getDoubleSizeVariant();
return ds != null ? ds : this;
}

@Override
public List<Image> getResolutionVariants() {
Image ds = getDoubleSizeVariant();
return ds == null ? List.of(this) : List.of(this, ds);
}
}

private static final class DisabledButtonFilter extends RGBImageFilter {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,7 +55,7 @@
import java.awt.datatransfer.Clipboard;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.InvalidDnDOperationException;
import java.awt.dnd.peer.DragSourceContextPeer;
//import java.awt.dnd.peer.DragSourceContextPeer;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
Expand All@@ -64,6 +64,7 @@
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
/*
import java.awt.peer.ButtonPeer;
import java.awt.peer.CanvasPeer;
import java.awt.peer.CheckboxMenuItemPeer;
Expand All@@ -86,6 +87,7 @@
import java.awt.peer.TextAreaPeer;
import java.awt.peer.TextFieldPeer;
import java.awt.peer.WindowPeer;
*/
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.util.ArrayList;
Expand DownExpand Up@@ -320,28 +322,12 @@ public Toolkit getToolkit() {
return customToolkit;
}
}

private static class NoCustomCursorToolkit extends Toolkit {
public FontMetrics getFontMetrics(Font font) {
return Toolkit.getDefaultToolkit().getFontMetrics( font );
}

protected TextFieldPeer createTextField(TextField target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected ListPeer createList(java.awt.List target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected MenuBarPeer createMenuBar(MenuBar target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException {
throw new IllegalStateException("Method not implemented");
}

public boolean prepareImage(Image image, int width, int height, ImageObserver observer) {
return Toolkit.getDefaultToolkit().prepareImage( image, width, height, observer );
}
Expand All@@ -350,30 +336,14 @@ public int checkImage(Image image, int width, int height, ImageObserver observer
return Toolkit.getDefaultToolkit().checkImage( image, width, height, observer );
}

protected PopupMenuPeer createPopupMenu(PopupMenu target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public PrintJob getPrintJob(Frame frame, String jobtitle, Properties props) {
return Toolkit.getDefaultToolkit().getPrintJob( frame, jobtitle, props );
}

protected ButtonPeer createButton(Button target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image createImage(ImageProducer producer) {
return Toolkit.getDefaultToolkit().createImage( producer );
}

protected CanvasPeer createCanvas(Canvas target) {
throw new IllegalStateException("Method not implemented");
}

protected ScrollbarPeer createScrollbar(Scrollbar target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image getImage(String filename) {
return Toolkit.getDefaultToolkit().getImage( filename );
}
Expand All@@ -382,14 +352,6 @@ public Image createImage(String filename) {
return Toolkit.getDefaultToolkit().createImage( filename );
}

protected MenuPeer createMenu(Menu target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected MenuItemPeer createMenuItem(MenuItem target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Map mapInputMethodHighlight(InputMethodHighlight highlight) throws HeadlessException {
return Toolkit.getDefaultToolkit().mapInputMethodHighlight( highlight );
}
Expand All@@ -402,58 +364,10 @@ public Image getImage(URL url) {
return Toolkit.getDefaultToolkit().getImage( url );
}

protected CheckboxPeer createCheckbox(Checkbox target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image createImage(URL url) {
return Toolkit.getDefaultToolkit().createImage( url );
}

protected TextAreaPeer createTextArea(TextArea target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FileDialogPeer createFileDialog(FileDialog target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected ScrollPanePeer createScrollPane(ScrollPane target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected DialogPeer createDialog(Dialog target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected PanelPeer createPanel(Panel target) {
throw new IllegalStateException("Method not implemented");
}

protected ChoicePeer createChoice(Choice target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FramePeer createFrame(Frame target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected LabelPeer createLabel(Label target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FontPeer getFontPeer(String name, int style) {
throw new IllegalStateException("Method not implemented");
}

protected CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected WindowPeer createWindow(Window target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public void sync() {
Toolkit.getDefaultToolkit().sync();
}
Expand DownExpand Up@@ -503,10 +417,6 @@ public Dimension getBestCursorSize(int preferredWidth, int preferredHeight) thro
return new Dimension(0,0);
}

protected DesktopPeer createDesktopPeer(Desktop target) throws HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}

@Override
public boolean isModalityTypeSupported(ModalityType modalityType) {
throw new UnsupportedOperationException("Not supported yet.");
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
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
1 change: 0 additions & 1 deletion platform/openide.util.ui/manifest.mf
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,4 +2,3 @@ Manifest-Version: 1.0
OpenIDE-Module: org.openide.util.ui
OpenIDE-Module-Localizing-Bundle: org/openide/util/Bundle.properties
OpenIDE-Module-Specification-Version: 9.34

3 changes: 1 addition & 2 deletions platform/openide.util.ui/nbproject/project.properties
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,8 +16,7 @@
# under the License.

javac.compilerargs=-Xlint -Xlint:-serial -Xlint:-processing
javac.source=1.8
javac.target=1.8
javac.release=11
module.jar.dir=lib


Expand Down
87 changes: 71 additions & 16 deletions platform/openide.util.ui/src/org/openide/util/ImageUtilities.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,6 +34,7 @@
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.MultiResolutionImage;
import java.awt.image.RGBImageFilter;
import java.awt.image.WritableRaster;
import java.io.IOException;
Expand All@@ -46,6 +47,7 @@
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
Expand DownExpand Up@@ -322,10 +324,9 @@ public static final Image mergeImages(Image image1, Image image2, int x, int y)
* @return icon corresponding icon
*/
public static final Icon image2Icon(Image image) {
/* Make sure to always return a ToolTipImage, to take advantage of its rendering tweaks for
HiDPI screens. */
return (image instanceof ToolTipImage)
ToolTipImage ret = (image instanceof ToolTipImage)
? (ToolTipImage) image : assignToolTipToImageInternal(image, "");
return ret.asImageIconIfRequiredForRetina();
}

/**
Expand DownExpand Up@@ -376,8 +377,9 @@ methods in this class may be called from any thread, while JLabel's methods and
// so let's try second most used one type, it satisfies AbstractButton, JCheckbox. Not all cases are
// covered, however.
icon.paintIcon(dummyIconComponentButton, g, 0, 0);
} finally {
g.dispose();
}
g.dispose();
return image;
}

Expand DownExpand Up@@ -1049,17 +1051,12 @@ private static final class IconImageIcon extends ImageIcon {
it volatile instead, to be completely sure that the class is still thread-safe. */
private volatile Icon delegate;

private IconImageIcon(Icon delegate) {
super(icon2Image(delegate));
IconImageIcon(ToolTipImage delegate) {
super(delegate);
Parameters.notNull("delegate", delegate);
this.delegate = delegate;
}

private static ImageIcon create(Icon delegate) {
return (delegate instanceof ImageIcon)
? (ImageIcon) delegate : new IconImageIcon(delegate);
}

@Override
public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
delegate.paintIcon(c, g, x, y);
Expand DownExpand Up@@ -1089,16 +1086,24 @@ private void readObjectNoData() throws ObjectStreamException {
}

/**
* Image with tool tip text (for icons with badges)
* Image with tool tip text (for icons with badges).
*
* <p>On MacOS, HiDPI (Retina) support in JMenuItem.setIcon(Icon) requires the Icon argument to
* be an instance of ImageIcon wrapping a MultiResolutionImage (see
* com.apple.laf.ScreenMenuIcon.setIcon, com.apple.laf.AquaIcon.getImageForIcon, and
* sun.lwawt.macosx.CImage.Creator.createFromImage). Thus we have this class implement
* MultiResolutionImage, and use asImageIcon when needed via asImageIconIfRequiredForRetina.
*/
private static class ToolTipImage extends BufferedImage implements Icon {
private static class ToolTipImage extends BufferedImage implements Icon, MultiResolutionImage {
final String toolTipText;
// May be null.
final Icon delegateIcon;
// May be null.
final URL url;
// May be null.
ImageIcon imageIconVersion;
// May be null.
volatile BufferedImage doubleSizeVariant;

public static ToolTipImage createNew(String toolTipText, Image image, URL url) {
ImageUtilities.ensureLoaded(image);
Expand DownExpand Up@@ -1137,9 +1142,16 @@ public ToolTipImage(
}

public synchronized ImageIcon asImageIcon() {
if (imageIconVersion == null)
imageIconVersion = IconImageIcon.create(this);
return imageIconVersion;
if (imageIconVersion == null) {
imageIconVersion = new IconImageIcon(this);
}
return imageIconVersion;
}

public Icon asImageIconIfRequiredForRetina() {
/* We could choose to do this only on MacOS, but doing it on all platforms will lower
the chance of undetected platform-specific bugs. */
return delegateIcon != null ? asImageIcon() : this;
}

/**
Expand DownExpand Up@@ -1237,6 +1249,49 @@ Image.UndefinedProperty rather than null (see Javadoc spec for this method), but
}
return super.getProperty(name, observer);
}

private Image getDoubleSizeVariant() {
if (delegateIcon == null) {
return null;
}
BufferedImage ret = doubleSizeVariant;
if (ret == null) {
int SCALE = 2;
ColorModel model = getColorModel();
int w = delegateIcon.getIconWidth() * SCALE;
int h = delegateIcon.getIconHeight() * SCALE;
ret = new BufferedImage(
model,
model.createCompatibleWritableRaster(w, h),
model.isAlphaPremultiplied(), null);
Graphics g = ret.createGraphics();
try {
((Graphics2D) g).transform(AffineTransform.getScaleInstance(SCALE, SCALE));
delegateIcon.paintIcon(dummyIconComponentLabel, g, 0, 0);
} finally {
g.dispose();
}
doubleSizeVariant = ret;
}
return ret;
}

@Override
public Image getResolutionVariant(double destImageWidth, double destImageHeight) {
if (destImageWidth <= getWidth(null) && destImageHeight <= getHeight(null)) {
/* Returning "this" should be safe here, as the same is done in
sun.awt.image.MultiResolutionToolkitImage. */
return this;
}
Image ds = getDoubleSizeVariant();
return ds != null ? ds : this;
}

@Override
public List<Image> getResolutionVariants() {
Image ds = getDoubleSizeVariant();
return ds == null ? List.of(this) : List.of(this, ds);
}
}

private static final class DisabledButtonFilter extends RGBImageFilter {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,7 +55,7 @@
import java.awt.datatransfer.Clipboard;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.InvalidDnDOperationException;
import java.awt.dnd.peer.DragSourceContextPeer;
//import java.awt.dnd.peer.DragSourceContextPeer;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
Expand All@@ -64,6 +64,7 @@
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
/*
import java.awt.peer.ButtonPeer;
import java.awt.peer.CanvasPeer;
import java.awt.peer.CheckboxMenuItemPeer;
Expand All@@ -86,6 +87,7 @@
import java.awt.peer.TextAreaPeer;
import java.awt.peer.TextFieldPeer;
import java.awt.peer.WindowPeer;
*/
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.util.ArrayList;
Expand DownExpand Up@@ -320,28 +322,12 @@ public Toolkit getToolkit() {
return customToolkit;
}
}

private static class NoCustomCursorToolkit extends Toolkit {
public FontMetrics getFontMetrics(Font font) {
return Toolkit.getDefaultToolkit().getFontMetrics( font );
}

protected TextFieldPeer createTextField(TextField target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected ListPeer createList(java.awt.List target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected MenuBarPeer createMenuBar(MenuBar target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException {
throw new IllegalStateException("Method not implemented");
}

public boolean prepareImage(Image image, int width, int height, ImageObserver observer) {
return Toolkit.getDefaultToolkit().prepareImage( image, width, height, observer );
}
Expand All@@ -350,30 +336,14 @@ public int checkImage(Image image, int width, int height, ImageObserver observer
return Toolkit.getDefaultToolkit().checkImage( image, width, height, observer );
}

protected PopupMenuPeer createPopupMenu(PopupMenu target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public PrintJob getPrintJob(Frame frame, String jobtitle, Properties props) {
return Toolkit.getDefaultToolkit().getPrintJob( frame, jobtitle, props );
}

protected ButtonPeer createButton(Button target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image createImage(ImageProducer producer) {
return Toolkit.getDefaultToolkit().createImage( producer );
}

protected CanvasPeer createCanvas(Canvas target) {
throw new IllegalStateException("Method not implemented");
}

protected ScrollbarPeer createScrollbar(Scrollbar target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image getImage(String filename) {
return Toolkit.getDefaultToolkit().getImage( filename );
}
Expand All@@ -382,14 +352,6 @@ public Image createImage(String filename) {
return Toolkit.getDefaultToolkit().createImage( filename );
}

protected MenuPeer createMenu(Menu target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected MenuItemPeer createMenuItem(MenuItem target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Map mapInputMethodHighlight(InputMethodHighlight highlight) throws HeadlessException {
return Toolkit.getDefaultToolkit().mapInputMethodHighlight( highlight );
}
Expand All@@ -402,58 +364,10 @@ public Image getImage(URL url) {
return Toolkit.getDefaultToolkit().getImage( url );
}

protected CheckboxPeer createCheckbox(Checkbox target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image createImage(URL url) {
return Toolkit.getDefaultToolkit().createImage( url );
}

protected TextAreaPeer createTextArea(TextArea target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FileDialogPeer createFileDialog(FileDialog target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected ScrollPanePeer createScrollPane(ScrollPane target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected DialogPeer createDialog(Dialog target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected PanelPeer createPanel(Panel target) {
throw new IllegalStateException("Method not implemented");
}

protected ChoicePeer createChoice(Choice target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FramePeer createFrame(Frame target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected LabelPeer createLabel(Label target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FontPeer getFontPeer(String name, int style) {
throw new IllegalStateException("Method not implemented");
}

protected CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected WindowPeer createWindow(Window target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public void sync() {
Toolkit.getDefaultToolkit().sync();
}
Expand DownExpand Up@@ -503,10 +417,6 @@ public Dimension getBestCursorSize(int preferredWidth, int preferredHeight) thro
return new Dimension(0,0);
}

protected DesktopPeer createDesktopPeer(Desktop target) throws HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}

@Override
public boolean isModalityTypeSupported(ModalityType modalityType) {
throw new UnsupportedOperationException("Not supported yet.");
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
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
1 change: 0 additions & 1 deletion platform/openide.util.ui/manifest.mf
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,4 +2,3 @@ Manifest-Version: 1.0
OpenIDE-Module: org.openide.util.ui
OpenIDE-Module-Localizing-Bundle: org/openide/util/Bundle.properties
OpenIDE-Module-Specification-Version: 9.34

3 changes: 1 addition & 2 deletions platform/openide.util.ui/nbproject/project.properties
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,8 +16,7 @@
# under the License.

javac.compilerargs=-Xlint -Xlint:-serial -Xlint:-processing
javac.source=1.8
javac.target=1.8
javac.release=11
module.jar.dir=lib


Expand Down
87 changes: 71 additions & 16 deletions platform/openide.util.ui/src/org/openide/util/ImageUtilities.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,6 +34,7 @@
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.MultiResolutionImage;
import java.awt.image.RGBImageFilter;
import java.awt.image.WritableRaster;
import java.io.IOException;
Expand All@@ -46,6 +47,7 @@
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
Expand DownExpand Up@@ -322,10 +324,9 @@ public static final Image mergeImages(Image image1, Image image2, int x, int y)
* @return icon corresponding icon
*/
public static final Icon image2Icon(Image image) {
/* Make sure to always return a ToolTipImage, to take advantage of its rendering tweaks for
HiDPI screens. */
return (image instanceof ToolTipImage)
ToolTipImage ret = (image instanceof ToolTipImage)
? (ToolTipImage) image : assignToolTipToImageInternal(image, "");
return ret.asImageIconIfRequiredForRetina();
}

/**
Expand DownExpand Up@@ -376,8 +377,9 @@ methods in this class may be called from any thread, while JLabel's methods and
// so let's try second most used one type, it satisfies AbstractButton, JCheckbox. Not all cases are
// covered, however.
icon.paintIcon(dummyIconComponentButton, g, 0, 0);
} finally {
g.dispose();
}
g.dispose();
return image;
}

Expand DownExpand Up@@ -1049,17 +1051,12 @@ private static final class IconImageIcon extends ImageIcon {
it volatile instead, to be completely sure that the class is still thread-safe. */
private volatile Icon delegate;

private IconImageIcon(Icon delegate) {
super(icon2Image(delegate));
IconImageIcon(ToolTipImage delegate) {
super(delegate);
Parameters.notNull("delegate", delegate);
this.delegate = delegate;
}

private static ImageIcon create(Icon delegate) {
return (delegate instanceof ImageIcon)
? (ImageIcon) delegate : new IconImageIcon(delegate);
}

@Override
public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
delegate.paintIcon(c, g, x, y);
Expand DownExpand Up@@ -1089,16 +1086,24 @@ private void readObjectNoData() throws ObjectStreamException {
}

/**
* Image with tool tip text (for icons with badges)
* Image with tool tip text (for icons with badges).
*
* <p>On MacOS, HiDPI (Retina) support in JMenuItem.setIcon(Icon) requires the Icon argument to
* be an instance of ImageIcon wrapping a MultiResolutionImage (see
* com.apple.laf.ScreenMenuIcon.setIcon, com.apple.laf.AquaIcon.getImageForIcon, and
* sun.lwawt.macosx.CImage.Creator.createFromImage). Thus we have this class implement
* MultiResolutionImage, and use asImageIcon when needed via asImageIconIfRequiredForRetina.
*/
private static class ToolTipImage extends BufferedImage implements Icon {
private static class ToolTipImage extends BufferedImage implements Icon, MultiResolutionImage {
final String toolTipText;
// May be null.
final Icon delegateIcon;
// May be null.
final URL url;
// May be null.
ImageIcon imageIconVersion;
// May be null.
volatile BufferedImage doubleSizeVariant;

public static ToolTipImage createNew(String toolTipText, Image image, URL url) {
ImageUtilities.ensureLoaded(image);
Expand DownExpand Up@@ -1137,9 +1142,16 @@ public ToolTipImage(
}

public synchronized ImageIcon asImageIcon() {
if (imageIconVersion == null)
imageIconVersion = IconImageIcon.create(this);
return imageIconVersion;
if (imageIconVersion == null) {
imageIconVersion = new IconImageIcon(this);
}
return imageIconVersion;
}

public Icon asImageIconIfRequiredForRetina() {
/* We could choose to do this only on MacOS, but doing it on all platforms will lower
the chance of undetected platform-specific bugs. */
return delegateIcon != null ? asImageIcon() : this;
}

/**
Expand DownExpand Up@@ -1237,6 +1249,49 @@ Image.UndefinedProperty rather than null (see Javadoc spec for this method), but
}
return super.getProperty(name, observer);
}

private Image getDoubleSizeVariant() {
if (delegateIcon == null) {
return null;
}
BufferedImage ret = doubleSizeVariant;
if (ret == null) {
int SCALE = 2;
ColorModel model = getColorModel();
int w = delegateIcon.getIconWidth() * SCALE;
int h = delegateIcon.getIconHeight() * SCALE;
ret = new BufferedImage(
model,
model.createCompatibleWritableRaster(w, h),
model.isAlphaPremultiplied(), null);
Graphics g = ret.createGraphics();
try {
((Graphics2D) g).transform(AffineTransform.getScaleInstance(SCALE, SCALE));
delegateIcon.paintIcon(dummyIconComponentLabel, g, 0, 0);
} finally {
g.dispose();
}
doubleSizeVariant = ret;
}
return ret;
}

@Override
public Image getResolutionVariant(double destImageWidth, double destImageHeight) {
if (destImageWidth <= getWidth(null) && destImageHeight <= getHeight(null)) {
/* Returning "this" should be safe here, as the same is done in
sun.awt.image.MultiResolutionToolkitImage. */
return this;
}
Image ds = getDoubleSizeVariant();
return ds != null ? ds : this;
}

@Override
public List<Image> getResolutionVariants() {
Image ds = getDoubleSizeVariant();
return ds == null ? List.of(this) : List.of(this, ds);
}
}

private static final class DisabledButtonFilter extends RGBImageFilter {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,7 +55,7 @@
import java.awt.datatransfer.Clipboard;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.InvalidDnDOperationException;
import java.awt.dnd.peer.DragSourceContextPeer;
//import java.awt.dnd.peer.DragSourceContextPeer;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
Expand All@@ -64,6 +64,7 @@
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
/*
import java.awt.peer.ButtonPeer;
import java.awt.peer.CanvasPeer;
import java.awt.peer.CheckboxMenuItemPeer;
Expand All@@ -86,6 +87,7 @@
import java.awt.peer.TextAreaPeer;
import java.awt.peer.TextFieldPeer;
import java.awt.peer.WindowPeer;
*/
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.util.ArrayList;
Expand DownExpand Up@@ -320,28 +322,12 @@ public Toolkit getToolkit() {
return customToolkit;
}
}

private static class NoCustomCursorToolkit extends Toolkit {
public FontMetrics getFontMetrics(Font font) {
return Toolkit.getDefaultToolkit().getFontMetrics( font );
}

protected TextFieldPeer createTextField(TextField target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected ListPeer createList(java.awt.List target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected MenuBarPeer createMenuBar(MenuBar target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException {
throw new IllegalStateException("Method not implemented");
}

public boolean prepareImage(Image image, int width, int height, ImageObserver observer) {
return Toolkit.getDefaultToolkit().prepareImage( image, width, height, observer );
}
Expand All@@ -350,30 +336,14 @@ public int checkImage(Image image, int width, int height, ImageObserver observer
return Toolkit.getDefaultToolkit().checkImage( image, width, height, observer );
}

protected PopupMenuPeer createPopupMenu(PopupMenu target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public PrintJob getPrintJob(Frame frame, String jobtitle, Properties props) {
return Toolkit.getDefaultToolkit().getPrintJob( frame, jobtitle, props );
}

protected ButtonPeer createButton(Button target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image createImage(ImageProducer producer) {
return Toolkit.getDefaultToolkit().createImage( producer );
}

protected CanvasPeer createCanvas(Canvas target) {
throw new IllegalStateException("Method not implemented");
}

protected ScrollbarPeer createScrollbar(Scrollbar target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image getImage(String filename) {
return Toolkit.getDefaultToolkit().getImage( filename );
}
Expand All@@ -382,14 +352,6 @@ public Image createImage(String filename) {
return Toolkit.getDefaultToolkit().createImage( filename );
}

protected MenuPeer createMenu(Menu target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected MenuItemPeer createMenuItem(MenuItem target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Map mapInputMethodHighlight(InputMethodHighlight highlight) throws HeadlessException {
return Toolkit.getDefaultToolkit().mapInputMethodHighlight( highlight );
}
Expand All@@ -402,58 +364,10 @@ public Image getImage(URL url) {
return Toolkit.getDefaultToolkit().getImage( url );
}

protected CheckboxPeer createCheckbox(Checkbox target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public Image createImage(URL url) {
return Toolkit.getDefaultToolkit().createImage( url );
}

protected TextAreaPeer createTextArea(TextArea target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FileDialogPeer createFileDialog(FileDialog target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected ScrollPanePeer createScrollPane(ScrollPane target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected DialogPeer createDialog(Dialog target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected PanelPeer createPanel(Panel target) {
throw new IllegalStateException("Method not implemented");
}

protected ChoicePeer createChoice(Choice target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FramePeer createFrame(Frame target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected LabelPeer createLabel(Label target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected FontPeer getFontPeer(String name, int style) {
throw new IllegalStateException("Method not implemented");
}

protected CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

protected WindowPeer createWindow(Window target) throws HeadlessException {
throw new IllegalStateException("Method not implemented");
}

public void sync() {
Toolkit.getDefaultToolkit().sync();
}
Expand DownExpand Up@@ -503,10 +417,6 @@ public Dimension getBestCursorSize(int preferredWidth, int preferredHeight) thro
return new Dimension(0,0);
}

protected DesktopPeer createDesktopPeer(Desktop target) throws HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}

@Override
public boolean isModalityTypeSupported(ModalityType modalityType) {
throw new UnsupportedOperationException("Not supported yet.");
Expand Down