mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-171404 allow UI scale exceed 2x
- UI scale can exceed 2x now - JBUI.ScaleType.OBJ_SCALE is introduced - JBUI.Scaler & JBUI.ScaleContext is introduced - JBUI.JBIcon and subclasses are refactored - CachedImageIcon is simplified - ShadowPainter scales shadow now - Transitioning from int/float to double where applicable - double [x, y, width, height] is rounded this way [floor, floor, ceil, ceil] - JBDimension is backed by double size - JBDimension rescales itself via new methods: size()/width()/width2D()
This commit is contained in:
@@ -18,7 +18,7 @@ package com.intellij.ui;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.ScalableIcon;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.util.ui.JBUI.CachingScalableJBIcon;
|
||||
import org.intellij.lang.annotations.MagicConstant;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -26,7 +26,12 @@ import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class LayeredIcon extends JBUI.UpdatingScalableJBIcon<LayeredIcon> {
|
||||
import static com.intellij.util.ui.JBUI.ScaleType.OBJ_SCALE;
|
||||
import static com.intellij.util.ui.JBUI.ScaleType.USR_SCALE;
|
||||
import static java.lang.Math.ceil;
|
||||
import static java.lang.Math.floor;
|
||||
|
||||
public class LayeredIcon extends CachingScalableJBIcon<LayeredIcon> {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.ui.LayeredIcon");
|
||||
private final Icon[] myIcons;
|
||||
private Icon[] myScaledIcons;
|
||||
@@ -96,8 +101,8 @@ public class LayeredIcon extends JBUI.UpdatingScalableJBIcon<LayeredIcon> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public LayeredIcon withJBUIPreScaled(boolean preScaled) {
|
||||
super.withJBUIPreScaled(preScaled);
|
||||
public LayeredIcon withIconPreScaled(boolean preScaled) {
|
||||
super.withIconPreScaled(preScaled);
|
||||
updateSize();
|
||||
return this;
|
||||
}
|
||||
@@ -218,13 +223,13 @@ public class LayeredIcon extends JBUI.UpdatingScalableJBIcon<LayeredIcon> {
|
||||
|
||||
@Override
|
||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||
if (updateJBUIScale()) updateSize();
|
||||
if (getScaleContext().update()) updateSize();
|
||||
Icon[] icons = myScaledIcons();
|
||||
for (int i = 0; i < icons.length; i++) {
|
||||
Icon icon = icons[i];
|
||||
if (icon == null || myDisabledLayers[i]) continue;
|
||||
int xOffset = x + scaleVal(myXShift + myHShifts(i), Scale.INSTANCE);
|
||||
int yOffset = y + scaleVal(myYShift + myVShifts(i), Scale.INSTANCE);
|
||||
int xOffset = (int)floor(x + scaleVal(myXShift + myHShifts(i), OBJ_SCALE));
|
||||
int yOffset = (int)floor(y + scaleVal(myYShift + myVShifts(i), OBJ_SCALE));
|
||||
icon.paintIcon(c, g, xOffset, yOffset);
|
||||
}
|
||||
}
|
||||
@@ -239,26 +244,26 @@ public class LayeredIcon extends JBUI.UpdatingScalableJBIcon<LayeredIcon> {
|
||||
|
||||
@Override
|
||||
public int getIconWidth() {
|
||||
if (myWidth <= 1 || updateJBUIScale()) {
|
||||
if (getScaleContext().update() || myWidth <= 1) {
|
||||
updateSize();
|
||||
}
|
||||
return scaleVal(myWidth, Scale.INSTANCE);
|
||||
return (int)ceil(scaleVal(myWidth, OBJ_SCALE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconHeight() {
|
||||
if (myHeight <= 1 || updateJBUIScale()) {
|
||||
if (getScaleContext().update() || myHeight <= 1) {
|
||||
updateSize();
|
||||
}
|
||||
return scaleVal(myHeight, Scale.INSTANCE);
|
||||
return (int)ceil(scaleVal(myHeight, OBJ_SCALE));
|
||||
}
|
||||
|
||||
private int myHShifts(int i) {
|
||||
return scaleVal(myHShifts[i], Scale.JBUI);
|
||||
return (int)floor(scaleVal(myHShifts[i], USR_SCALE));
|
||||
}
|
||||
|
||||
private int myVShifts(int i) {
|
||||
return scaleVal(myVShifts[i], Scale.JBUI);
|
||||
return (int)floor(scaleVal(myVShifts[i], USR_SCALE));
|
||||
}
|
||||
|
||||
protected void updateSize() {
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.intellij.openapi.util.ScalableIcon;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.util.ui.JBUI.CachingScalableJBIcon;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
@@ -28,7 +29,10 @@ import java.awt.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class RowIcon extends JBUI.UpdatingScalableJBIcon<RowIcon> {
|
||||
import static com.intellij.util.ui.JBUI.ScaleType.OBJ_SCALE;
|
||||
import static java.lang.Math.ceil;
|
||||
|
||||
public class RowIcon extends CachingScalableJBIcon<RowIcon> {
|
||||
private final Alignment myAlignment;
|
||||
|
||||
private int myWidth;
|
||||
@@ -123,7 +127,7 @@ public class RowIcon extends JBUI.UpdatingScalableJBIcon<RowIcon> {
|
||||
|
||||
@Override
|
||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||
if (updateJBUIScale()) updateSize();
|
||||
if (getScaleContext().update()) updateSize();
|
||||
int _x = x;
|
||||
int _y = y;
|
||||
for (Icon icon : myScaledIcons()) {
|
||||
@@ -144,14 +148,14 @@ public class RowIcon extends JBUI.UpdatingScalableJBIcon<RowIcon> {
|
||||
|
||||
@Override
|
||||
public int getIconWidth() {
|
||||
if (updateJBUIScale()) updateSize();
|
||||
return scaleVal(myWidth, Scale.INSTANCE);
|
||||
if (getScaleContext().update()) updateSize();
|
||||
return (int)ceil(scaleVal(myWidth, OBJ_SCALE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconHeight() {
|
||||
if (updateJBUIScale()) updateSize();
|
||||
return scaleVal(myHeight, Scale.INSTANCE);
|
||||
if (getScaleContext().update()) updateSize();
|
||||
return (int)ceil(scaleVal(myHeight, OBJ_SCALE));
|
||||
}
|
||||
|
||||
private void updateSize() {
|
||||
|
||||
@@ -30,8 +30,8 @@ import com.intellij.ui.JBColor;
|
||||
import com.intellij.ui.LayeredIcon;
|
||||
import com.intellij.ui.RowIcon;
|
||||
import com.intellij.util.ui.*;
|
||||
import com.intellij.util.ui.JBUI.JBUIScaleUpdatable;
|
||||
import com.intellij.util.ui.JBUI.ScaleType;
|
||||
import com.intellij.util.ui.JBUI.ScaleContext;
|
||||
import com.intellij.util.ui.JBUI.ScaleContextAware;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -40,6 +40,9 @@ import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import static com.intellij.util.ui.JBUI.ScaleType.USR_SCALE;
|
||||
import static java.lang.Math.ceil;
|
||||
|
||||
|
||||
/**
|
||||
* @author max
|
||||
@@ -69,28 +72,35 @@ public class IconUtil {
|
||||
return icon;
|
||||
}
|
||||
|
||||
final int w = Math.min(icon.getIconWidth(), maxWidth);
|
||||
final int h = Math.min(icon.getIconHeight(), maxHeight);
|
||||
Image image = toImage(icon);
|
||||
if (image == null) return icon;
|
||||
|
||||
final BufferedImage image = GraphicsEnvironment
|
||||
.getLocalGraphicsEnvironment()
|
||||
.getDefaultScreenDevice()
|
||||
.getDefaultConfiguration()
|
||||
.createCompatibleImage(icon.getIconWidth(), icon.getIconHeight(), Transparency.TRANSLUCENT);
|
||||
final Graphics2D g = image.createGraphics();
|
||||
icon.paintIcon(new JPanel(), g, 0, 0);
|
||||
g.dispose();
|
||||
double scale = 1f;
|
||||
if (image instanceof JBHiDPIScaledImage) {
|
||||
scale = ((JBHiDPIScaledImage)image).getScale();
|
||||
image = ((JBHiDPIScaledImage)image).getDelegate();
|
||||
}
|
||||
BufferedImage bi = ImageUtil.toBufferedImage(image);
|
||||
final Graphics2D g = bi.createGraphics();
|
||||
|
||||
int imageWidth = ImageUtil.getRealWidth(image);
|
||||
int imageHeight = ImageUtil.getRealHeight(image);
|
||||
|
||||
final int w = Math.min(imageWidth, maxWidth);
|
||||
final int h = Math.min(imageHeight, maxHeight);
|
||||
maxWidth = maxWidth == Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)ceil(maxWidth * scale);
|
||||
maxHeight = maxHeight == Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)ceil(maxHeight * scale);
|
||||
|
||||
final BufferedImage img = UIUtil.createImage(g, w, h, Transparency.TRANSLUCENT);
|
||||
final int offX = icon.getIconWidth() > maxWidth ? (icon.getIconWidth() - maxWidth) / 2 : 0;
|
||||
final int offY = icon.getIconHeight() > maxHeight ? (icon.getIconHeight() - maxHeight) / 2 : 0;
|
||||
final int offX = imageWidth > maxWidth ? (imageWidth - maxWidth) / 2 : 0;
|
||||
final int offY = imageHeight > maxHeight ? (imageHeight - maxHeight) / 2 : 0;
|
||||
for (int col = 0; col < w; col++) {
|
||||
for (int row = 0; row < h; row++) {
|
||||
img.setRGB(col, row, image.getRGB(col + offX, row + offY));
|
||||
img.setRGB(col, row, bi.getRGB(col + offX, row + offY));
|
||||
}
|
||||
}
|
||||
|
||||
return new ImageIcon(img);
|
||||
g.dispose();
|
||||
return new JBImageIcon(RetinaImage.createFrom(img, scale, null));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -466,8 +476,8 @@ public class IconUtil {
|
||||
@NotNull
|
||||
public static Icon scale(@NotNull Icon icon, @Nullable Component ancestor, float scale) {
|
||||
if (icon instanceof ScalableIcon) {
|
||||
if (icon instanceof JBUI.JBUIScaleUpdatable) {
|
||||
((JBUI.JBUIScaleUpdatable)icon).updateJBUIScale(ancestor != null ? ancestor.getGraphicsConfiguration() : null);
|
||||
if (icon instanceof ScaleContextAware) {
|
||||
((ScaleContextAware)icon).updateScaleContext(ancestor != null ? ScaleContext.create(ancestor) : null);
|
||||
}
|
||||
return ((ScalableIcon)icon).scale(scale);
|
||||
}
|
||||
@@ -491,11 +501,11 @@ public class IconUtil {
|
||||
public static Icon scaleByFont(@NotNull Icon icon, @Nullable Component ancestor, float fontSize) {
|
||||
float scale = JBUI.getFontScale(fontSize);
|
||||
if (icon instanceof ScalableIcon) {
|
||||
if (icon instanceof JBUIScaleUpdatable) {
|
||||
JBUI.JBUIScaleUpdatable jbuiIcon = (JBUI.JBUIScaleUpdatable)icon;
|
||||
jbuiIcon.updateJBUIScale(ancestor != null ? ancestor.getGraphicsConfiguration() : null);
|
||||
if (icon instanceof ScaleContextAware) {
|
||||
ScaleContextAware ctxIcon = (ScaleContextAware)icon;
|
||||
ctxIcon.updateScaleContext(ancestor != null ? ScaleContext.create(ancestor) : null);
|
||||
// take into account the user scale of the icon
|
||||
float usrScale = jbuiIcon.getJBUIScale(ScaleType.USR);
|
||||
double usrScale = ctxIcon.getScaleContext().getScale(USR_SCALE);
|
||||
scale /= usrScale;
|
||||
}
|
||||
return ((ScalableIcon)icon).scale(scale);
|
||||
|
||||
+3
-1
@@ -39,6 +39,8 @@ import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
import static java.lang.Math.ceil;
|
||||
|
||||
public class FileStatusColorsTable extends JBTable {
|
||||
|
||||
private JBPopupMenu mySetColorMenu;
|
||||
@@ -269,7 +271,7 @@ public class FileStatusColorsTable extends JBTable {
|
||||
final int iconWidth = getIconWidth();
|
||||
g.setColor(myColor);
|
||||
|
||||
final int size = scaleVal(COLOR_HEIGHT);
|
||||
final int size = (int)ceil(scaleVal(COLOR_HEIGHT));
|
||||
final int y = j + (iconHeight - size) / 2;
|
||||
|
||||
g.fillRect(i, y, iconWidth, size);
|
||||
|
||||
@@ -54,6 +54,7 @@ import com.intellij.util.IconUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.PlatformIcons;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.util.ui.JBUI.ScaleType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -66,6 +67,9 @@ import java.awt.geom.Rectangle2D;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import static com.intellij.util.ui.JBUI.ScaleType.OBJ_SCALE;
|
||||
import static java.lang.Math.ceil;
|
||||
|
||||
public class Bookmark implements Navigatable, Comparable<Bookmark> {
|
||||
public static final Icon DEFAULT_ICON = new MyCheckedIcon();
|
||||
|
||||
@@ -384,12 +388,12 @@ public class Bookmark implements Navigatable, Comparable<Bookmark> {
|
||||
|
||||
@Override
|
||||
public int getIconWidth() {
|
||||
return scaleVal(DEFAULT_ICON.getIconWidth(), Scale.INSTANCE);
|
||||
return (int)ceil(scaleVal(DEFAULT_ICON.getIconWidth(), OBJ_SCALE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconHeight() {
|
||||
return scaleVal(DEFAULT_ICON.getIconHeight(), Scale.INSTANCE);
|
||||
return (int)ceil(scaleVal(DEFAULT_ICON.getIconHeight(), OBJ_SCALE));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -422,12 +426,12 @@ public class Bookmark implements Navigatable, Comparable<Bookmark> {
|
||||
|
||||
@Override
|
||||
public int getIconWidth() {
|
||||
return scaleVal(PlatformIcons.CHECK_ICON.getIconWidth(), Scale.INSTANCE);
|
||||
return (int)ceil(scaleVal(PlatformIcons.CHECK_ICON.getIconWidth(), OBJ_SCALE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconHeight() {
|
||||
return scaleVal(PlatformIcons.CHECK_ICON.getIconHeight(), Scale.INSTANCE);
|
||||
return (int)ceil(scaleVal(PlatformIcons.CHECK_ICON.getIconHeight(), OBJ_SCALE));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -34,6 +34,7 @@ import com.intellij.util.concurrency.AppExecutorUtil;
|
||||
import com.intellij.util.containers.TransferToEDTQueue;
|
||||
import com.intellij.util.ui.EmptyIcon;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.util.ui.JBUI.CachingScalableJBIcon;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -46,7 +47,7 @@ import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class DeferredIconImpl<T> extends JBUI.UpdatingScalableJBIcon<DeferredIconImpl<T>> implements DeferredIcon, RetrievableIcon {
|
||||
public class DeferredIconImpl<T> extends CachingScalableJBIcon<DeferredIconImpl<T>> implements DeferredIcon, RetrievableIcon {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.ui.DeferredIconImpl");
|
||||
private static final int MIN_AUTO_UPDATE_MILLIS = 950;
|
||||
private static final RepaintScheduler ourRepaintScheduler = new RepaintScheduler();
|
||||
@@ -90,11 +91,12 @@ public class DeferredIconImpl<T> extends JBUI.UpdatingScalableJBIcon<DeferredIco
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScale(float scale) {
|
||||
public Icon scale(float scale) {
|
||||
if (getScale() != scale && myDelegateIcon instanceof ScalableIcon) {
|
||||
myScaledDelegateIcon = ((ScalableIcon)myDelegateIcon).scale(scale);
|
||||
super.setScale(scale);
|
||||
super.scale(scale);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private static class Holder {
|
||||
|
||||
@@ -18,10 +18,7 @@ package com.intellij.ui;
|
||||
import com.intellij.openapi.ui.popup.IconButton;
|
||||
import com.intellij.openapi.util.Pass;
|
||||
import com.intellij.ui.awt.RelativePoint;
|
||||
import com.intellij.util.ui.BaseButtonBehavior;
|
||||
import com.intellij.util.ui.CenteredIcon;
|
||||
import com.intellij.util.ui.TimedDeadzone;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.intellij.util.ui.*;
|
||||
|
||||
import javax.accessibility.*;
|
||||
import javax.swing.*;
|
||||
@@ -48,6 +45,8 @@ public class InplaceButton extends JComponent implements ActiveComponent, Access
|
||||
private int myYTransform = 0;
|
||||
private boolean myFill;
|
||||
|
||||
private JBDimension mySize;
|
||||
|
||||
private boolean myHoveringEnabled;
|
||||
|
||||
public InplaceButton(String tooltip, final Icon icon, final ActionListener listener) {
|
||||
@@ -129,7 +128,11 @@ public class InplaceButton extends JComponent implements ActiveComponent, Access
|
||||
height = Math.max(height, hovered.getIconHeight());
|
||||
|
||||
|
||||
setPreferredSize(new Dimension(width, height));
|
||||
JBDimension size = JBDimension.create(new Dimension(width, height), true);
|
||||
if (mySize != null && !mySize.size().equals(size)) {
|
||||
invalidate();
|
||||
}
|
||||
mySize = size;
|
||||
|
||||
myIcon = regular;
|
||||
myRegular = new CenteredIcon(regular, width, height);
|
||||
@@ -137,6 +140,12 @@ public class InplaceButton extends JComponent implements ActiveComponent, Access
|
||||
myInactive = new CenteredIcon(inactive, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
if (mySize == null || isPreferredSizeSet()) return super.getPreferredSize();
|
||||
return mySize.size();
|
||||
}
|
||||
|
||||
public InplaceButton setFillBg(boolean fill) {
|
||||
myFill = fill;
|
||||
return this;
|
||||
|
||||
@@ -22,6 +22,9 @@ import org.jetbrains.annotations.NotNull;
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
import static java.lang.Math.ceil;
|
||||
import static java.lang.Math.floor;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
@@ -67,10 +70,10 @@ public class SizedIcon extends JBUI.CachingScalableJBIcon {
|
||||
@Override
|
||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||
Icon icon = myScaledIcon();
|
||||
int dx = scaleVal(myWidth) - icon.getIconWidth();
|
||||
int dy = scaleVal(myHeight) - icon.getIconHeight();
|
||||
double dx = scaleVal(myWidth) - icon.getIconWidth();
|
||||
double dy = scaleVal(myHeight) - icon.getIconHeight();
|
||||
if (dx > 0 || dy > 0) {
|
||||
icon.paintIcon(c, g, x + dx / 2, y + dy / 2);
|
||||
icon.paintIcon(c, g, x + (int)floor(dx / 2), y + (int)floor(dy / 2));
|
||||
}
|
||||
else {
|
||||
icon.paintIcon(c, g, x, y);
|
||||
@@ -78,10 +81,10 @@ public class SizedIcon extends JBUI.CachingScalableJBIcon {
|
||||
}
|
||||
|
||||
public int getIconWidth() {
|
||||
return scaleVal(myWidth);
|
||||
return (int)ceil(scaleVal(myWidth));
|
||||
}
|
||||
|
||||
public int getIconHeight() {
|
||||
return scaleVal(myHeight);
|
||||
return (int)ceil(scaleVal(myHeight));
|
||||
}
|
||||
}
|
||||
|
||||
+7
-9
@@ -449,7 +449,7 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar, QuickAct
|
||||
}
|
||||
} : myButtonLook,
|
||||
myPlace, myPresentationFactory.getPresentation(action),
|
||||
myMinimumButtonSize);
|
||||
myMinimumButtonSize.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -721,7 +721,7 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar, QuickAct
|
||||
int xOffset = 0;
|
||||
int yOffset = 0;
|
||||
// Calculate max size of a row. It's not possible to make more then 3 column toolbar
|
||||
final int maxRowHeight = Math.max(heightToFit, componentCount * myMinimumButtonSize.height / 3);
|
||||
final int maxRowHeight = Math.max(heightToFit, componentCount * myMinimumButtonSize.height() / 3);
|
||||
for (int i = 0; i < componentCount; i++) {
|
||||
if (yOffset + maxHeight > maxRowHeight) { // place component at new row
|
||||
yOffset = 0;
|
||||
@@ -750,7 +750,7 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar, QuickAct
|
||||
int xOffset = 0;
|
||||
int yOffset = 0;
|
||||
// Calculate max size of a row. It's not possible to make more then 3 row toolbar
|
||||
final int maxRowWidth = Math.max(widthToFit, componentCount * myMinimumButtonSize.width / 3);
|
||||
final int maxRowWidth = Math.max(widthToFit, componentCount * myMinimumButtonSize.width() / 3);
|
||||
for (int i = 0; i < componentCount; i++) {
|
||||
final Dimension d = dims[i];
|
||||
if (xOffset + d.width > maxRowWidth) { // place component at new row
|
||||
@@ -778,7 +778,7 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar, QuickAct
|
||||
int xOffset = 0;
|
||||
int yOffset = 0;
|
||||
// Calculate max size of a row. It's not possible to make more then 3 column toolbar
|
||||
final int maxRowHeight = Math.max(heightToFit, componentCount * myMinimumButtonSize.height / 3);
|
||||
final int maxRowHeight = Math.max(heightToFit, componentCount * myMinimumButtonSize.height() / 3);
|
||||
for (int i = 0; i < componentCount; i++) {
|
||||
final Dimension d = dims[i];
|
||||
if (yOffset + d.height > maxRowHeight) { // place component at new row
|
||||
@@ -841,7 +841,6 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar, QuickAct
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
final ArrayList<Rectangle> bounds = new ArrayList<>();
|
||||
if (myMinimumButtonSize != null) myMinimumButtonSize.update();
|
||||
calculateBounds(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE), bounds);
|
||||
if (bounds.isEmpty()) return JBUI.emptySize();
|
||||
int xLeft = Integer.MAX_VALUE;
|
||||
@@ -882,7 +881,7 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar, QuickAct
|
||||
}
|
||||
if (myLayoutPolicy == AUTO_LAYOUT_POLICY) {
|
||||
final Insets i = getInsets();
|
||||
return new Dimension(AllIcons.Ide.Link.getIconWidth() + i.left + i.right, myMinimumButtonSize.height + i.top + i.bottom);
|
||||
return new Dimension(AllIcons.Ide.Link.getIconWidth() + i.left + i.right, myMinimumButtonSize.height() + i.top + i.bottom);
|
||||
}
|
||||
else {
|
||||
return super.getMinimumSize();
|
||||
@@ -929,8 +928,7 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar, QuickAct
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
mySize.update();
|
||||
return mySize;
|
||||
return mySize.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -975,7 +973,7 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar, QuickAct
|
||||
|
||||
@Override
|
||||
public void setMinimumButtonSize(@NotNull final Dimension size) {
|
||||
myMinimumButtonSize = JBDimension.create(size, false);
|
||||
myMinimumButtonSize = JBDimension.create(size, true);
|
||||
for (int i = getComponentCount() - 1; i >= 0; i--) {
|
||||
final Component component = getComponent(i);
|
||||
if (component instanceof ActionButton) {
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
package com.intellij.openapi.ui.impl;
|
||||
|
||||
import com.intellij.util.IconUtil;
|
||||
import com.intellij.util.ui.JBUI.ScaleContext;
|
||||
import com.intellij.util.ui.JBUI.ScaleContextSupport;
|
||||
import com.intellij.util.ui.JBUI.ScaleContextAware;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -25,7 +28,7 @@ import java.awt.image.BufferedImage;
|
||||
/**
|
||||
* @author Konstantin Bulenkov
|
||||
*/
|
||||
public class ShadowPainter {
|
||||
public class ShadowPainter extends ScaleContextSupport<ScaleContext> {
|
||||
private final Icon myTop;
|
||||
private final Icon myTopRight;
|
||||
private final Icon myRight;
|
||||
@@ -34,18 +37,27 @@ public class ShadowPainter {
|
||||
private final Icon myBottomLeft;
|
||||
private final Icon myLeft;
|
||||
private final Icon myTopLeft;
|
||||
|
||||
private Icon myCroppedTop = null;
|
||||
private Icon myCroppedRight = null;
|
||||
private Icon myCroppedBottom = null;
|
||||
private Icon myCroppedLeft = null;
|
||||
|
||||
@Nullable
|
||||
private Color myBorderColor;
|
||||
|
||||
public ShadowPainter(Icon top, Icon topRight, Icon right, Icon bottomRight, Icon bottom, Icon bottomLeft, Icon left, Icon topLeft) {
|
||||
myTop = IconUtil.cropIcon(top, 1, Integer.MAX_VALUE);
|
||||
super(ScaleContext.create());
|
||||
myTop = top;
|
||||
myTopRight = topRight;
|
||||
myRight = IconUtil.cropIcon(right, Integer.MAX_VALUE, 1);
|
||||
myRight = right;
|
||||
myBottomRight = bottomRight;
|
||||
myBottom = IconUtil.cropIcon(bottom, 1, Integer.MAX_VALUE);
|
||||
myBottom = bottom;
|
||||
myBottomLeft = bottomLeft;
|
||||
myLeft = IconUtil.cropIcon(left, Integer.MAX_VALUE, 1);
|
||||
myLeft = left;
|
||||
myTopLeft = topLeft;
|
||||
|
||||
updateIcons(null);
|
||||
}
|
||||
|
||||
public ShadowPainter(Icon top, Icon topRight, Icon right, Icon bottomRight, Icon bottom, Icon bottomLeft, Icon left, Icon topLeft, @Nullable Color borderColor) {
|
||||
@@ -58,10 +70,7 @@ public class ShadowPainter {
|
||||
}
|
||||
|
||||
public BufferedImage createShadow(final JComponent c, final int width, final int height) {
|
||||
final GraphicsConfiguration graphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment().
|
||||
getDefaultScreenDevice().getDefaultConfiguration();
|
||||
|
||||
final BufferedImage image = graphicsConfiguration.createCompatibleImage(width, height, Transparency.TRANSLUCENT);
|
||||
final BufferedImage image = c.getGraphicsConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
|
||||
final Graphics2D g = image.createGraphics();
|
||||
|
||||
paintShadow(c, g, 0, 0, width, height);
|
||||
@@ -70,12 +79,31 @@ public class ShadowPainter {
|
||||
return image;
|
||||
}
|
||||
|
||||
public void paintShadow(Component c, Graphics2D g, int x, int y, int width, int height) {
|
||||
final int leftSize = myLeft.getIconWidth();
|
||||
final int rightSize = myRight.getIconWidth();
|
||||
final int bottomSize = myBottom.getIconHeight();
|
||||
final int topSize = myTop.getIconHeight();
|
||||
private void updateIcons(ScaleContext ctx) {
|
||||
updateIcon(myTop, ctx, () -> myCroppedTop = IconUtil.cropIcon(myTop, 1, Integer.MAX_VALUE));
|
||||
updateIcon(myTopRight, ctx, null);
|
||||
updateIcon(myRight, ctx, () -> myCroppedRight = IconUtil.cropIcon(myRight, Integer.MAX_VALUE, 1));
|
||||
updateIcon(myBottomRight, ctx, null);
|
||||
updateIcon(myBottom, ctx, () -> myCroppedBottom = IconUtil.cropIcon(myBottom, 1, Integer.MAX_VALUE));
|
||||
updateIcon(myBottomLeft, ctx, null);
|
||||
updateIcon(myLeft, ctx, () -> myCroppedLeft = IconUtil.cropIcon(myLeft, Integer.MAX_VALUE, 1));
|
||||
updateIcon(myTopLeft, ctx, null);
|
||||
}
|
||||
|
||||
private void updateIcon(Icon icon, ScaleContext ctx, Runnable r) {
|
||||
if (icon instanceof ScaleContextAware) ((ScaleContextAware)icon).updateScaleContext(ctx);
|
||||
if (r != null) r.run();
|
||||
}
|
||||
|
||||
public void paintShadow(Component c, Graphics2D g, int x, int y, int width, int height) {
|
||||
ScaleContext ctx = ScaleContext.create(c);
|
||||
if (updateScaleContext(ctx)) {
|
||||
updateIcons(ctx);
|
||||
}
|
||||
final int leftSize = myCroppedLeft.getIconWidth();
|
||||
final int rightSize = myCroppedRight.getIconWidth();
|
||||
final int bottomSize = myCroppedBottom.getIconHeight();
|
||||
final int topSize = myCroppedTop.getIconHeight();
|
||||
|
||||
myTopLeft.paintIcon(c, g, x, y);
|
||||
myTopRight.paintIcon(c, g, x + width - myTopRight.getIconWidth(), y);
|
||||
@@ -83,16 +111,16 @@ public class ShadowPainter {
|
||||
myBottomLeft.paintIcon(c, g, x, y + height - myBottomLeft.getIconHeight());
|
||||
|
||||
for (int _x = myTopLeft.getIconWidth(); _x < width - myTopRight.getIconWidth(); _x++) {
|
||||
myTop.paintIcon(c, g, _x + x, y);
|
||||
myCroppedTop.paintIcon(c, g, _x + x, y);
|
||||
}
|
||||
for (int _x = myBottomLeft.getIconWidth(); _x < width - myBottomLeft.getIconWidth(); _x++) {
|
||||
myBottom.paintIcon(c, g, _x + x, y + height - bottomSize);
|
||||
myCroppedBottom.paintIcon(c, g, _x + x, y + height - bottomSize);
|
||||
}
|
||||
for (int _y = myTopLeft.getIconHeight(); _y < height - myBottomLeft.getIconHeight(); _y++) {
|
||||
myLeft.paintIcon(c, g, x, _y + y);
|
||||
myCroppedLeft.paintIcon(c, g, x, _y + y);
|
||||
}
|
||||
for (int _y = myTopRight.getIconHeight(); _y < height - myBottomRight.getIconHeight(); _y++) {
|
||||
myRight.paintIcon(c, g, x + width - rightSize, _y + y);
|
||||
myCroppedRight.paintIcon(c, g, x + width - rightSize, _y + y);
|
||||
}
|
||||
|
||||
if (myBorderColor != null) {
|
||||
|
||||
@@ -40,7 +40,7 @@ public final class FontComboBox extends ComboBox {
|
||||
private static final FontInfoRenderer RENDERER = new FontInfoRenderer();
|
||||
|
||||
private Model myModel;
|
||||
private JBDimension myPrefSize;
|
||||
private final JBDimension mySize;
|
||||
|
||||
public FontComboBox() {
|
||||
this(false);
|
||||
@@ -52,9 +52,10 @@ public final class FontComboBox extends ComboBox {
|
||||
|
||||
public FontComboBox(boolean withAllStyles, boolean filterNonLatin, boolean noFontItem) {
|
||||
super(new Model(withAllStyles, filterNonLatin, noFontItem));
|
||||
Dimension size = getPreferredSize();
|
||||
Dimension size = super.getPreferredSize();
|
||||
size.width = size.height * 8;
|
||||
myPrefSize = JBDimension.create(size, false);
|
||||
// preScaled=true as 'size' reflects already scaled font
|
||||
mySize = JBDimension.create(size, true);
|
||||
setSwingPopup(true);
|
||||
setRenderer(RENDERER);
|
||||
getModel().addListDataListener(new ListDataListener() {
|
||||
@@ -77,9 +78,8 @@ public final class FontComboBox extends ComboBox {
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
if (isPreferredSizeSet() || myPrefSize == null) return super.getPreferredSize();
|
||||
myPrefSize.update();
|
||||
return myPrefSize;
|
||||
if (isPreferredSizeSet()) return super.getPreferredSize();
|
||||
return mySize.size();
|
||||
}
|
||||
|
||||
public boolean isMonospacedOnly() {
|
||||
|
||||
@@ -21,15 +21,14 @@ import com.intellij.openapi.util.registry.RegistryValue;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.reference.SoftReference;
|
||||
import com.intellij.ui.RetrievableIcon;
|
||||
import com.intellij.util.ConcurrencyUtil;
|
||||
import com.intellij.util.ImageLoader;
|
||||
import com.intellij.util.ReflectionUtil;
|
||||
import com.intellij.util.RetinaImage;
|
||||
import com.intellij.util.*;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.ImageUtil;
|
||||
import com.intellij.util.ui.JBImageIcon;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.util.ui.JBUI.ScaleType;
|
||||
import com.intellij.util.ui.JBUI.ScaleContext;
|
||||
import com.intellij.util.ui.JBUI.RasterJBIcon;
|
||||
import com.intellij.util.ui.JBUI.BaseScaleContext.UpdateListener;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -47,6 +46,8 @@ import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import static com.intellij.util.ui.JBUI.ScaleType.*;
|
||||
|
||||
public final class IconLoader {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.IconLoader");
|
||||
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
|
||||
@@ -380,7 +381,7 @@ public final class IconLoader {
|
||||
return icon;
|
||||
}
|
||||
|
||||
private static final class CachedImageIcon extends JBUI.UpdatingJBIcon implements ScalableIcon {
|
||||
private static final class CachedImageIcon extends RasterJBIcon implements ScalableIcon {
|
||||
private volatile Object myRealIcon;
|
||||
private String myOriginalPath;
|
||||
private ClassLoader myClassLoader;
|
||||
@@ -393,6 +394,16 @@ public final class IconLoader {
|
||||
private ImageFilter[] myFilters;
|
||||
private final MyScaledIconsCache myScaledIconsCache = new MyScaledIconsCache();
|
||||
|
||||
{
|
||||
// For instance, ShadowPainter updates the context from outside.
|
||||
getScaleContext().addUpdateListener(new UpdateListener() {
|
||||
@Override
|
||||
public void contextUpdated() {
|
||||
myRealIcon = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private CachedImageIcon(@NotNull CachedImageIcon icon) {
|
||||
myRealIcon = null; // to be computed
|
||||
myOriginalPath = icon.myOriginalPath;
|
||||
@@ -419,29 +430,22 @@ public final class IconLoader {
|
||||
return myFilters[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateJBUIScale(Graphics2D g) {
|
||||
if (needUpdateJBUIScale(g)) {
|
||||
getRealIcon(g); // force update
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private synchronized ImageIcon getRealIcon() {
|
||||
return getRealIcon(null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private synchronized ImageIcon getRealIcon(@Nullable Graphics g) {
|
||||
if (!isValid() || needUpdateJBUIScale((Graphics2D)g)) {
|
||||
private synchronized ImageIcon getRealIcon(ScaleContext ctx) {
|
||||
if (updateScaleContext(ctx)) {
|
||||
myRealIcon = null;
|
||||
}
|
||||
if (!isValid()) {
|
||||
if (isLoaderDisabled()) return EMPTY_ICON;
|
||||
myRealIcon = null;
|
||||
dark = USE_DARK_ICONS;
|
||||
super.updateJBUIScale((Graphics2D)g);
|
||||
setGlobalFilter(IMAGE_FILTER);
|
||||
if (!isValid()) myScaledIconsCache.clear();
|
||||
myScaledIconsCache.clear();
|
||||
if (numberOfPatchers != ourPatchers.size()) {
|
||||
numberOfPatchers = ourPatchers.size();
|
||||
Pair<String, Class> patchedPath = patchPath(myOriginalPath);
|
||||
@@ -467,7 +471,7 @@ public final class IconLoader {
|
||||
if (icon != null) return icon;
|
||||
}
|
||||
|
||||
icon = myScaledIconsCache.getOrLoadIcon(getJBUIScale(ScaleType.PIX));
|
||||
icon = myScaledIconsCache.getOrScaleIcon(1f);
|
||||
|
||||
if (icon != null) {
|
||||
if (icon.getIconWidth() < 50 && icon.getIconHeight() < 50) {
|
||||
@@ -488,7 +492,10 @@ public final class IconLoader {
|
||||
|
||||
@Override
|
||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||
getRealIcon(g).paintIcon(c, g, x, y);
|
||||
// Component is preferable to Graphics as a scale provider, as it lets the context stick
|
||||
// to the comp's actual scale via the update method.
|
||||
ScaleContext ctx = c != null ? ScaleContext.create(c) : ScaleContext.create((Graphics2D)g);
|
||||
getRealIcon(ctx).paintIcon(c, g, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -517,7 +524,7 @@ public final class IconLoader {
|
||||
|
||||
getRealIcon(); // force state update & cache reset
|
||||
|
||||
Icon icon = myScaledIconsCache.getOrScaleIcon(getJBUIScale(ScaleType.PIX), scale);
|
||||
Icon icon = myScaledIconsCache.getOrScaleIcon(scale);
|
||||
if (icon != null) {
|
||||
return icon;
|
||||
}
|
||||
@@ -530,85 +537,58 @@ public final class IconLoader {
|
||||
return icon;
|
||||
}
|
||||
|
||||
private class MyScaledIconsCache {
|
||||
// Map {false -> image}, {true -> image@2x}
|
||||
private Map<Boolean, SoftReference<Image>> origImagesCache = Collections.synchronizedMap(new HashMap<Boolean, SoftReference<Image>>(2));
|
||||
private Image loadFromUrl(ScaleContext ctx) {
|
||||
return ImageLoader.loadFromUrl(myUrl, true, myFilters, ctx);
|
||||
}
|
||||
|
||||
private class MyScaledIconsCache {
|
||||
private static final int SCALED_ICONS_CACHE_LIMIT = 5;
|
||||
|
||||
// Map {pixel scale -> icon}
|
||||
private Map<Float, SoftReference<ImageIcon>> scaledIconsCache = Collections.synchronizedMap(new LinkedHashMap<Float, SoftReference<ImageIcon>>(SCALED_ICONS_CACHE_LIMIT) {
|
||||
private Map<Double, SoftReference<ImageIcon>> scaledIconsCache = Collections.synchronizedMap(new LinkedHashMap<Double, SoftReference<ImageIcon>>(SCALED_ICONS_CACHE_LIMIT) {
|
||||
@Override
|
||||
public boolean removeEldestEntry(Map.Entry<Float, SoftReference<ImageIcon>> entry) {
|
||||
public boolean removeEldestEntry(Map.Entry<Double, SoftReference<ImageIcon>> entry) {
|
||||
return size() > SCALED_ICONS_CACHE_LIMIT;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieves the orig image (1x, 2x) based on the pixScale.
|
||||
* Retrieves the orig icon scaled by the provided scale.
|
||||
*/
|
||||
private Image getOrLoadOrigImage(boolean needRetinaImage) {
|
||||
Image image = SoftReference.dereference(origImagesCache.get(needRetinaImage));
|
||||
if (image != null) return image;
|
||||
public ImageIcon getOrScaleIcon(final float scale) {
|
||||
updateScale(OBJ_SCALE.of(scale));
|
||||
|
||||
image = ImageLoader.loadFromUrl(myUrl, false, myFilters, needRetinaImage ? 2f : 1f);
|
||||
if (image == null) return null;
|
||||
origImagesCache.put(needRetinaImage, new SoftReference<Image>(image));
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the orig icon based on the pixScale, then scale it by the instanceScale.
|
||||
*/
|
||||
public ImageIcon getOrScaleIcon(float pixScale, float instanceScale) {
|
||||
final float effectiveScale = pixScale * instanceScale;
|
||||
ImageIcon icon = SoftReference.dereference(scaledIconsCache.get(effectiveScale));
|
||||
ImageIcon icon = SoftReference.dereference(scaledIconsCache.get(getScale(PIX_SCALE)));
|
||||
if (icon != null) {
|
||||
return icon;
|
||||
}
|
||||
|
||||
Image image;
|
||||
if (svg) {
|
||||
image = doWithTmpRegValue("ide.svg.icon", true, new Callable<Image>() {
|
||||
@Override
|
||||
public Image call() {
|
||||
return ImageLoader.loadFromUrl(myUrl, true, myFilters, effectiveScale);
|
||||
return loadFromUrl(getScaleContext());
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
boolean needRetinaImage = JBUI.isHiDPI(effectiveScale);
|
||||
image = getOrLoadOrigImage(needRetinaImage);
|
||||
if (image == null) return null;
|
||||
|
||||
if (!UIUtil.isJreHiDPIEnabled() && needRetinaImage) {
|
||||
instanceScale = effectiveScale / 2f; // the image is 2x raw BufferedImage, compensate it
|
||||
}
|
||||
|
||||
image = ImageUtil.scaleImage(image, instanceScale);
|
||||
image = loadFromUrl(getScaleContext());
|
||||
}
|
||||
icon = checkIcon(image, myUrl);
|
||||
|
||||
if (icon != null && (icon.getIconWidth() * icon.getIconHeight() * 4) < ImageLoader.CACHED_IMAGE_MAX_SIZE) {
|
||||
scaledIconsCache.put(effectiveScale, new SoftReference<ImageIcon>(icon));
|
||||
scaledIconsCache.put(getScale(PIX_SCALE), new SoftReference<ImageIcon>(icon));
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the orig icon based on the pixScale.
|
||||
*/
|
||||
public ImageIcon getOrLoadIcon(float pixScale) {
|
||||
return getOrScaleIcon(pixScale, 1f);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
scaledIconsCache.clear();
|
||||
origImagesCache.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class LazyIcon extends JBUI.UpdatingJBIcon {
|
||||
public abstract static class LazyIcon extends RasterJBIcon {
|
||||
private boolean myWasComputed;
|
||||
private Icon myIcon;
|
||||
private boolean isDarkVariant = USE_DARK_ICONS;
|
||||
@@ -617,7 +597,10 @@ public final class IconLoader {
|
||||
|
||||
@Override
|
||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||
final Icon icon = getOrComputeIcon(g);
|
||||
if (updateScaleContext(ScaleContext.create((Graphics2D)g))) {
|
||||
myIcon = null;
|
||||
}
|
||||
final Icon icon = getOrComputeIcon();
|
||||
if (icon != null) {
|
||||
icon.paintIcon(c, g, x, y);
|
||||
}
|
||||
@@ -636,13 +619,11 @@ public final class IconLoader {
|
||||
}
|
||||
|
||||
protected final synchronized Icon getOrComputeIcon() {
|
||||
return getOrComputeIcon(null);
|
||||
}
|
||||
|
||||
protected final synchronized Icon getOrComputeIcon(@Nullable Graphics g) {
|
||||
if (!myWasComputed || isDarkVariant != USE_DARK_ICONS || needUpdateJBUIScale((Graphics2D)g) || filter != IMAGE_FILTER || numberOfPatchers != ourPatchers.size()) {
|
||||
if (!myWasComputed || isDarkVariant != USE_DARK_ICONS ||
|
||||
myIcon == null ||
|
||||
filter != IMAGE_FILTER || numberOfPatchers != ourPatchers.size())
|
||||
{
|
||||
isDarkVariant = USE_DARK_ICONS;
|
||||
updateJBUIScale((Graphics2D)g);
|
||||
filter = IMAGE_FILTER;
|
||||
myWasComputed = true;
|
||||
numberOfPatchers = ourPatchers.size();
|
||||
@@ -662,7 +643,7 @@ public final class IconLoader {
|
||||
Icon icon = getOrComputeIcon();
|
||||
if (icon != null) {
|
||||
if (icon instanceof CachedImageIcon) {
|
||||
Image img = ((CachedImageIcon)icon).myScaledIconsCache.getOrLoadOrigImage(false);
|
||||
Image img = ((CachedImageIcon)icon).loadFromUrl(ScaleContext.create(USR_SCALE.of(1d), SYS_SCALE.of(1d)));
|
||||
if (img != null) {
|
||||
icon = new ImageIcon(img);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.ImageUtil;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.util.ui.JBUI.ScaleContext;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.imgscalr.Scalr;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -44,6 +45,8 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import static com.intellij.util.ui.JBUI.ScaleType.*;
|
||||
|
||||
public class ImageLoader implements Serializable {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.util.ImageLoader");
|
||||
|
||||
@@ -56,29 +59,29 @@ public class ImageLoader implements Serializable {
|
||||
|
||||
SVG {
|
||||
@Override
|
||||
public Image load(URL url, InputStream is, float scale) throws IOException {
|
||||
public Image load(URL url, InputStream is, double scale) throws IOException {
|
||||
return SVGLoader.load(url, is, scale);
|
||||
}
|
||||
},
|
||||
|
||||
UNDEFINED;
|
||||
|
||||
public Image load(URL url, InputStream stream, float scale) throws IOException {
|
||||
return ImageLoader.load(stream, (int)scale);
|
||||
public Image load(URL url, InputStream stream, double scale) throws IOException {
|
||||
return ImageLoader.load(stream, scale);
|
||||
}
|
||||
}
|
||||
|
||||
public final String path;
|
||||
public final @Nullable Class cls; // resource class if present
|
||||
public final float scale; // initial scale factor
|
||||
public final double scale; // initial scale factor
|
||||
public final Type type;
|
||||
public final boolean original; // path is not altered
|
||||
|
||||
public ImageDesc(String path, Class cls, float scale, Type type) {
|
||||
public ImageDesc(String path, Class cls, double scale, Type type) {
|
||||
this(path, cls, scale, type, false);
|
||||
}
|
||||
|
||||
public ImageDesc(String path, Class cls, float scale, Type type, boolean original) {
|
||||
public ImageDesc(String path, Class cls, double scale, Type type, boolean original) {
|
||||
this.path = path;
|
||||
this.cls = cls;
|
||||
this.scale = scale;
|
||||
@@ -150,50 +153,44 @@ public class ImageLoader implements Serializable {
|
||||
public static ImageDescList create(@NotNull String file,
|
||||
@Nullable Class cls,
|
||||
boolean dark,
|
||||
boolean retina,
|
||||
boolean allowFloatScaling)
|
||||
{
|
||||
return create(file, cls, dark, retina, allowFloatScaling, JBUI.pixScale());
|
||||
}
|
||||
|
||||
public static ImageDescList create(@NotNull String file,
|
||||
@Nullable Class cls,
|
||||
boolean dark,
|
||||
boolean retina,
|
||||
boolean allowFloatScaling,
|
||||
float pixScale)
|
||||
ScaleContext ctx)
|
||||
{
|
||||
ImageDescList vars = new ImageDescList();
|
||||
|
||||
boolean ideSvgIconSupport = Registry.is("ide.svg.icon");
|
||||
|
||||
// Prefer retina images for HiDPI scale, because downscaling
|
||||
// retina images provides a better result than upscaling non-retina images.
|
||||
boolean retina = JBUI.isHiDPI(ctx.getScale(PIX_SCALE));
|
||||
|
||||
if (retina || dark || ideSvgIconSupport) {
|
||||
final String name = FileUtil.getNameWithoutExtension(file);
|
||||
final String ext = FileUtilRt.getExtension(file);
|
||||
|
||||
pixScale = adjustScaleFactor(allowFloatScaling, pixScale);
|
||||
double scale = adjustScaleFactor(allowFloatScaling, ctx.getScale(PIX_SCALE));
|
||||
|
||||
if (ideSvgIconSupport && dark) {
|
||||
vars.add(new ImageDesc(name + "_dark.svg", cls, pixScale, ImageDesc.Type.SVG));
|
||||
vars.add(new ImageDesc(name + "_dark.svg", cls, scale, ImageDesc.Type.SVG));
|
||||
}
|
||||
|
||||
if (ideSvgIconSupport) {
|
||||
vars.add(new ImageDesc(name + ".svg", cls, pixScale, ImageDesc.Type.SVG));
|
||||
vars.add(new ImageDesc(name + ".svg", cls, scale, ImageDesc.Type.SVG));
|
||||
}
|
||||
|
||||
if (dark && retina) {
|
||||
vars.add(new ImageDesc(name + "@2x_dark." + ext, cls, 2f, ImageDesc.Type.PNG));
|
||||
vars.add(new ImageDesc(name + "@2x_dark." + ext, cls, 2d, ImageDesc.Type.PNG));
|
||||
}
|
||||
|
||||
if (dark) {
|
||||
vars.add(new ImageDesc(name + "_dark." + ext, cls, 1f, ImageDesc.Type.PNG));
|
||||
vars.add(new ImageDesc(name + "_dark." + ext, cls, 1d, ImageDesc.Type.PNG));
|
||||
}
|
||||
|
||||
if (retina) {
|
||||
vars.add(new ImageDesc(name + "@2x." + ext, cls, 2f, ImageDesc.Type.PNG));
|
||||
vars.add(new ImageDesc(name + "@2x." + ext, cls, 2d, ImageDesc.Type.PNG));
|
||||
}
|
||||
}
|
||||
vars.add(new ImageDesc(file, cls, 1f, ImageDesc.Type.PNG, true));
|
||||
vars.add(new ImageDesc(file, cls, 1d, ImageDesc.Type.PNG, true));
|
||||
return vars;
|
||||
}
|
||||
}
|
||||
@@ -226,12 +223,12 @@ public class ImageLoader implements Serializable {
|
||||
});
|
||||
}
|
||||
|
||||
public ImageConverterChain withRetina() {
|
||||
public ImageConverterChain withHiDPI(final ScaleContext ctx) {
|
||||
return with(new ImageConverter() {
|
||||
@Override
|
||||
public Image convert(Image source, ImageDesc desc) {
|
||||
if (source != null && UIUtil.isJreHiDPIEnabled() && desc.scale > 1) {
|
||||
return RetinaImage.createFrom(source, (int)desc.scale, ourComponent);
|
||||
if (source != null && UIUtil.isJreHiDPI(ctx)) {
|
||||
return RetinaImage.createFrom(source, ctx.getScale(SYS_SCALE), ourComponent);
|
||||
}
|
||||
return source;
|
||||
}
|
||||
@@ -280,51 +277,40 @@ public class ImageLoader implements Serializable {
|
||||
|
||||
@Nullable
|
||||
public static Image loadFromUrl(@NotNull URL url, boolean allowFloatScaling, ImageFilter filter) {
|
||||
return loadFromUrl(url, allowFloatScaling, new ImageFilter[] {filter}, JBUI.pixScale());
|
||||
return loadFromUrl(url, allowFloatScaling, new ImageFilter[] {filter}, ScaleContext.create());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an image by the passed url in scale (1x, 2x, ...) possibly closed to the passed JBUI pix scale,
|
||||
* then simply returns it in the JRE-managed HiDPI mode, otherwise scales the image
|
||||
* according to the passed scale and returns.
|
||||
* Loads an image of available resolution (1x, 2x, ...) and scales to address the provided scale context.
|
||||
* Then wraps the image with {@link JBHiDPIScaledImage} if necessary.
|
||||
*/
|
||||
@Nullable
|
||||
public static Image loadFromUrl(@NotNull URL url, boolean allowFloatScaling, ImageFilter[] filters, float pixScale) {
|
||||
final float scaleFactor = adjustScaleFactor(allowFloatScaling, pixScale); // valid for Retina as well
|
||||
|
||||
public static Image loadFromUrl(@NotNull URL url, final boolean allowFloatScaling, ImageFilter[] filters, final ScaleContext ctx) {
|
||||
// We can't check all 3rd party plugins and convince the authors to add @2x icons.
|
||||
// In IDE-managed HiDPI mode with (scaleFactor > 1.0) we should scale images manually.
|
||||
// Note we never scale images in JRE-managed HiDPI mode because scaling is handled by JRE.
|
||||
// In IDE-managed HiDPI mode with scale > 1.0 we scale images manually.
|
||||
|
||||
final boolean scaleImages = (scaleFactor > 1.0f && !UIUtil.isJreHiDPIEnabled());
|
||||
|
||||
// Prefer retina images for HiDPI scale, because downscaling
|
||||
// retina images provides a better result than upscaling non-retina images.
|
||||
final boolean loadRetinaImages = JBUI.isHiDPI(scaleFactor);
|
||||
|
||||
return ImageDescList.create(url.toString(), null, UIUtil.isUnderDarcula(), loadRetinaImages, allowFloatScaling, pixScale).load(
|
||||
return ImageDescList.create(url.toString(), null, UIUtil.isUnderDarcula(), allowFloatScaling, ctx).load(
|
||||
ImageConverterChain.create().
|
||||
withFilter(filters).
|
||||
withRetina().
|
||||
with(new ImageConverter() {
|
||||
public Image convert(Image source, ImageDesc desc) {
|
||||
if (source != null && scaleImages && desc.type != ImageDesc.Type.SVG) {
|
||||
if (desc.path.contains("@2x"))
|
||||
return scaleImage(source, scaleFactor / 2.0f); // divide by 2.0 as Retina images are 2x the resolution.
|
||||
else
|
||||
return scaleImage(source, scaleFactor);
|
||||
if (source != null && desc.type != ImageDesc.Type.SVG) {
|
||||
double scale = adjustScaleFactor(allowFloatScaling, ctx.getScale(PIX_SCALE));
|
||||
if (desc.scale > 1) scale /= desc.scale; // compensate the image original scale
|
||||
source = scaleImage(source, scale);
|
||||
}
|
||||
return source;
|
||||
}
|
||||
}));
|
||||
}).
|
||||
withHiDPI(ctx));
|
||||
}
|
||||
|
||||
private static float adjustScaleFactor(boolean allowFloatScaling, float scale) {
|
||||
private static double adjustScaleFactor(boolean allowFloatScaling, double scale) {
|
||||
return allowFloatScaling ? scale : JBUI.isHiDPI(scale) ? 2f : 1f;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Image scaleImage(Image image, float scale) {
|
||||
public static Image scaleImage(Image image, double scale) {
|
||||
if (scale == 1.0) return image;
|
||||
|
||||
if (image instanceof JBHiDPIScaledImage) {
|
||||
@@ -343,22 +329,6 @@ public class ImageLoader implements Serializable {
|
||||
return Scalr.resize(ImageUtil.toBufferedImage(image), Scalr.Method.QUALITY, width, height);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Image loadFromUrl(URL url, boolean dark, boolean retina) {
|
||||
return loadFromUrl(url, dark, retina, (ImageFilter[])null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Image loadFromUrl(URL url, boolean dark, boolean retina, ImageFilter filter) {
|
||||
return loadFromUrl(url, dark, retina, new ImageFilter[] {filter});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Image loadFromUrl(URL url, boolean dark, boolean retina, ImageFilter[] filters) {
|
||||
return ImageDescList.create(url.toString(), null, dark, retina, true).
|
||||
load(ImageConverterChain.create().withFilter(filters).withRetina());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Image loadFromResource(@NonNls @NotNull String s) {
|
||||
Class callerClass = ReflectionUtil.getGrandCallerClass();
|
||||
@@ -368,8 +338,9 @@ public class ImageLoader implements Serializable {
|
||||
|
||||
@Nullable
|
||||
public static Image loadFromResource(@NonNls @NotNull String path, @NotNull Class aClass) {
|
||||
return ImageDescList.create(path, aClass, UIUtil.isUnderDarcula(), JBUI.isHiDPI(JBUI.pixScale()), true).
|
||||
load(ImageConverterChain.create().withRetina());
|
||||
ScaleContext ctx = ScaleContext.create();
|
||||
return ImageDescList.create(path, aClass, UIUtil.isUnderDarcula(), true, ctx).
|
||||
load(ImageConverterChain.create().withHiDPI(ctx));
|
||||
}
|
||||
|
||||
public static Image loadFromStream(@NotNull final InputStream inputStream) {
|
||||
@@ -383,10 +354,10 @@ public class ImageLoader implements Serializable {
|
||||
public static Image loadFromStream(@NotNull final InputStream inputStream, final int scale, ImageFilter filter) {
|
||||
Image image = load(inputStream, scale);
|
||||
ImageDesc desc = new ImageDesc("", null, scale, ImageDesc.Type.UNDEFINED);
|
||||
return ImageConverterChain.create().withFilter(filter).withRetina().convert(image, desc);
|
||||
return ImageConverterChain.create().withFilter(filter).withHiDPI(ScaleContext.create()).convert(image, desc);
|
||||
}
|
||||
|
||||
private static Image load(@NotNull final InputStream inputStream, final int scale) {
|
||||
private static Image load(@NotNull final InputStream inputStream, double scale) {
|
||||
if (scale <= 0) throw new IllegalArgumentException("Scale must be 1 or greater");
|
||||
try {
|
||||
BufferExposingByteArrayOutputStream outputStream = new BufferExposingByteArrayOutputStream();
|
||||
|
||||
@@ -25,15 +25,24 @@ import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ImageObserver;
|
||||
|
||||
import static java.lang.Math.ceil;
|
||||
|
||||
/**
|
||||
* @author Konstantin Bulenkov
|
||||
* @author tav
|
||||
*/
|
||||
public class JBHiDPIScaledImage extends BufferedImage {
|
||||
private final @Nullable Image myImage;
|
||||
private final int myUserWidth;
|
||||
private final int myUserHeight;
|
||||
private final float myScale;
|
||||
private final double myUserWidth;
|
||||
private final double myUserHeight;
|
||||
private final double myScale;
|
||||
|
||||
/**
|
||||
* @see #JBHiDPIScaledImage(double, double, int)
|
||||
*/
|
||||
public JBHiDPIScaledImage(int width, int height, int type) {
|
||||
this((double)width, (double)height, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a scaled HiDPI-aware BufferedImage, targeting the system default scale.
|
||||
@@ -42,10 +51,17 @@ public class JBHiDPIScaledImage extends BufferedImage {
|
||||
* @param height the height in user coordinate space
|
||||
* @param type the type
|
||||
*/
|
||||
public JBHiDPIScaledImage(int width, int height, int type) {
|
||||
public JBHiDPIScaledImage(double width, double height, int type) {
|
||||
this((GraphicsConfiguration)null, width, height, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #JBHiDPIScaledImage(Graphics2D, double, double, int)
|
||||
*/
|
||||
public JBHiDPIScaledImage(@Nullable Graphics2D g, int width, int height, int type) {
|
||||
this(g, (double)width, (double)height, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a scaled HiDPI-aware BufferedImage, targeting the graphics scale.
|
||||
*
|
||||
@@ -54,7 +70,7 @@ public class JBHiDPIScaledImage extends BufferedImage {
|
||||
* @param height the height in user coordinate space
|
||||
* @param type the type
|
||||
*/
|
||||
public JBHiDPIScaledImage(@Nullable Graphics2D g, int width, int height, int type) {
|
||||
public JBHiDPIScaledImage(@Nullable Graphics2D g, double width, double height, int type) {
|
||||
super((int)(width * JBUI.sysScale(g)), (int)(height * JBUI.sysScale(g)), type);
|
||||
myImage = null;
|
||||
myUserWidth = width;
|
||||
@@ -62,6 +78,13 @@ public class JBHiDPIScaledImage extends BufferedImage {
|
||||
myScale = JBUI.sysScale(g);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #JBHiDPIScaledImage(GraphicsConfiguration, double, double, int)
|
||||
*/
|
||||
public JBHiDPIScaledImage(@Nullable GraphicsConfiguration gc, int width, int height, int type) {
|
||||
this(gc, (double)width, (double)height, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a scaled HiDPI-aware BufferedImage, targeting the graphics config.
|
||||
*
|
||||
@@ -70,7 +93,7 @@ public class JBHiDPIScaledImage extends BufferedImage {
|
||||
* @param height the height in user coordinate space
|
||||
* @param type the type
|
||||
*/
|
||||
public JBHiDPIScaledImage(@Nullable GraphicsConfiguration gc, int width, int height, int type) {
|
||||
public JBHiDPIScaledImage(@Nullable GraphicsConfiguration gc, double width, double height, int type) {
|
||||
super((int)(width * JBUI.sysScale(gc)), (int)(height * JBUI.sysScale(gc)), type);
|
||||
myImage = null;
|
||||
myUserWidth = width;
|
||||
@@ -78,6 +101,13 @@ public class JBHiDPIScaledImage extends BufferedImage {
|
||||
myScale = JBUI.sysScale(gc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #JBHiDPIScaledImage(Image, double, double, int)
|
||||
*/
|
||||
public JBHiDPIScaledImage(@NotNull Image image, int width, int height, int type) {
|
||||
this(image, (double)width, (double)height, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a HiDPI-aware BufferedImage wrapper for the provided scaled raw image.
|
||||
* The wrapper image will represent the scaled raw image in user coordinate space.
|
||||
@@ -87,7 +117,7 @@ public class JBHiDPIScaledImage extends BufferedImage {
|
||||
* @param height the height in user coordinate space
|
||||
* @param type the type
|
||||
*/
|
||||
public JBHiDPIScaledImage(@NotNull Image image, int width, int height, int type) {
|
||||
public JBHiDPIScaledImage(@NotNull Image image, double width, double height, int type) {
|
||||
super(1, 1, type); // a dummy wrapper
|
||||
myImage = image;
|
||||
myUserWidth = width;
|
||||
@@ -95,7 +125,7 @@ public class JBHiDPIScaledImage extends BufferedImage {
|
||||
myScale = myUserWidth > 0 ? myImage.getWidth(null) / myUserWidth : 1f;
|
||||
}
|
||||
|
||||
public float getScale() {
|
||||
public double getScale() {
|
||||
return myScale;
|
||||
}
|
||||
|
||||
@@ -105,7 +135,7 @@ public class JBHiDPIScaledImage extends BufferedImage {
|
||||
* @param scaleFactor the scale factor
|
||||
* @return scaled instance
|
||||
*/
|
||||
public JBHiDPIScaledImage scale(float scaleFactor) {
|
||||
public JBHiDPIScaledImage scale(double scaleFactor) {
|
||||
Image img = myImage == null ? this: myImage;
|
||||
|
||||
int w = (int)(scaleFactor * getRealWidth(null));
|
||||
@@ -114,15 +144,16 @@ public class JBHiDPIScaledImage extends BufferedImage {
|
||||
|
||||
Image scaled = Scalr.resize(ImageUtil.toBufferedImage(img), Scalr.Method.QUALITY, w, h);
|
||||
|
||||
int newUserWidth = (int)(w / this.myScale);
|
||||
int newUserHeight = (int)(h / this.myScale);
|
||||
double newUserWidth = w / this.myScale;
|
||||
double newUserHeight = h / this.myScale;
|
||||
|
||||
if (myImage != null) {
|
||||
return new JBHiDPIScaledImage(scaled, newUserWidth, newUserHeight, getType());
|
||||
}
|
||||
JBHiDPIScaledImage newImg = new JBHiDPIScaledImage(newUserWidth, newUserHeight, getType());
|
||||
Graphics2D g = newImg.createGraphics();
|
||||
g.drawImage(scaled, 0, 0, newUserWidth, newUserHeight, 0, 0, scaled.getWidth(null), scaled.getHeight(null), null);
|
||||
g.drawImage(scaled, 0, 0, (int)ceil(newUserWidth), (int)ceil(newUserHeight),
|
||||
0, 0, scaled.getWidth(null), scaled.getHeight(null), null);
|
||||
g.dispose();
|
||||
return newImg;
|
||||
}
|
||||
@@ -182,7 +213,7 @@ public class JBHiDPIScaledImage extends BufferedImage {
|
||||
* @return the width
|
||||
*/
|
||||
public int getUserWidth(ImageObserver observer) {
|
||||
return myImage != null ? myUserWidth : (int)(super.getWidth(observer) / myScale);
|
||||
return myImage != null ? (int)ceil(myUserWidth) : (int)ceil(super.getWidth(observer) / myScale);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,7 +223,7 @@ public class JBHiDPIScaledImage extends BufferedImage {
|
||||
* @return the height
|
||||
*/
|
||||
public int getUserHeight(ImageObserver observer) {
|
||||
return myImage != null ? myUserHeight : (int)(super.getHeight(observer) / myScale);
|
||||
return myImage != null ? (int)ceil(myUserHeight) : (int)ceil(super.getHeight(observer) / myScale);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -61,11 +61,11 @@ public class RetinaImage { // [tav] todo: create HiDPIImage class
|
||||
* @return the Retina-aware wrapper
|
||||
*/
|
||||
@NotNull
|
||||
public static Image createFrom(Image image, final float scale, ImageObserver observer) {
|
||||
public static Image createFrom(Image image, final double scale, ImageObserver observer) {
|
||||
int w = image.getWidth(observer);
|
||||
int h = image.getHeight(observer);
|
||||
|
||||
Image hidpi = new JBHiDPIScaledImage(image, (int)(w / scale), (int)(h / scale), BufferedImage.TYPE_INT_ARGB);
|
||||
Image hidpi = new JBHiDPIScaledImage(image, w / scale, h / scale, BufferedImage.TYPE_INT_ARGB);
|
||||
if (SystemInfo.isAppleJvm) {
|
||||
Graphics2D g = (Graphics2D)hidpi.getGraphics();
|
||||
g.scale(1f / scale, 1f / scale);
|
||||
|
||||
@@ -41,8 +41,8 @@ import java.util.List;
|
||||
public class SVGLoader {
|
||||
private TranscoderInput input;
|
||||
private BufferedImage img;
|
||||
private float width;
|
||||
private float height;
|
||||
private double width;
|
||||
private double height;
|
||||
|
||||
private enum SizeAttr {
|
||||
width,
|
||||
@@ -121,7 +121,7 @@ public class SVGLoader {
|
||||
return load(null, stream, scale);
|
||||
}
|
||||
|
||||
public static Image load(@Nullable URL url, @NotNull InputStream stream , float scale) throws IOException {
|
||||
public static Image load(@Nullable URL url, @NotNull InputStream stream , double scale) throws IOException {
|
||||
try {
|
||||
return new SVGLoader(url, stream, scale).createImage();
|
||||
}
|
||||
@@ -130,7 +130,7 @@ public class SVGLoader {
|
||||
}
|
||||
}
|
||||
|
||||
private SVGLoader(@Nullable URL url, InputStream stream, float scale) throws IOException {
|
||||
private SVGLoader(@Nullable URL url, InputStream stream, double scale) throws IOException {
|
||||
Document document = null;
|
||||
String uri = null;
|
||||
try {
|
||||
|
||||
@@ -20,6 +20,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import static java.lang.Math.ceil;
|
||||
|
||||
/**
|
||||
* @author Konstantin Bulenkov
|
||||
*/
|
||||
@@ -79,7 +81,7 @@ public class ColorIcon extends EmptyIcon {
|
||||
}
|
||||
|
||||
private int getColorSize() {
|
||||
return scaleVal(myColorSize);
|
||||
return (int)ceil(scaleVal(myColorSize));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -27,6 +27,9 @@ import java.beans.PropertyChangeListener;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.intellij.util.ui.JBUI.ScaleType.PIX_SCALE;
|
||||
import static java.lang.Math.ceil;
|
||||
|
||||
/**
|
||||
* @author max
|
||||
* @author Konstantin Bulenkov
|
||||
@@ -114,13 +117,13 @@ public class EmptyIcon extends JBUI.CachingScalableJBIcon<EmptyIcon> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmptyIcon withJBUIPreScaled(boolean preScaled) {
|
||||
if (myUseCache && isJBUIPreScaled() != preScaled) {
|
||||
public EmptyIcon withIconPreScaled(boolean preScaled) {
|
||||
if (myUseCache && isIconPreScaled() != preScaled) {
|
||||
Integer key = key(width, height);
|
||||
if (key != null) cache.remove(key); // rather useless to keep it in cache
|
||||
return create(width, height, preScaled);
|
||||
}
|
||||
return (EmptyIcon)super.withJBUIPreScaled(preScaled);
|
||||
return (EmptyIcon)super.withIconPreScaled(preScaled);
|
||||
}
|
||||
|
||||
private static EmptyIcon create(int width, int height, boolean preScaled) {
|
||||
@@ -128,7 +131,7 @@ public class EmptyIcon extends JBUI.CachingScalableJBIcon<EmptyIcon> {
|
||||
EmptyIcon icon = key != null ? cache.get(key) : null;
|
||||
if (icon == null) {
|
||||
icon = new EmptyIcon(width, height, true);
|
||||
icon.setJBUIPreScaled(preScaled);
|
||||
icon.setIconPreScaled(preScaled);
|
||||
if (key != null) cache.put(key, icon);
|
||||
}
|
||||
return icon;
|
||||
@@ -141,12 +144,12 @@ public class EmptyIcon extends JBUI.CachingScalableJBIcon<EmptyIcon> {
|
||||
|
||||
@Override
|
||||
public int getIconWidth() {
|
||||
return scaleVal(width);
|
||||
return (int)ceil(scaleVal(width));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconHeight() {
|
||||
return scaleVal(height);
|
||||
return (int)ceil(scaleVal(height));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -160,17 +163,17 @@ public class EmptyIcon extends JBUI.CachingScalableJBIcon<EmptyIcon> {
|
||||
|
||||
final EmptyIcon icon = (EmptyIcon)o;
|
||||
|
||||
if (scaleVal(height, Scale.EFFECTIVE) != icon.scaleVal(icon.height, Scale.EFFECTIVE)) return false;
|
||||
if (scaleVal(width, Scale.EFFECTIVE) != icon.scaleVal(icon.width, Scale.EFFECTIVE)) return false;
|
||||
if (scaleVal(height, PIX_SCALE) != icon.scaleVal(icon.height, PIX_SCALE)) return false;
|
||||
if (scaleVal(width, PIX_SCALE) != icon.scaleVal(icon.width, PIX_SCALE)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = scaleVal(width, Scale.EFFECTIVE);
|
||||
result = 31 * result + scaleVal(height, Scale.EFFECTIVE);
|
||||
return result;
|
||||
double result = scaleVal(width, PIX_SCALE);
|
||||
result = 31 * result + scaleVal(height, PIX_SCALE);
|
||||
return (int)result;
|
||||
}
|
||||
|
||||
public EmptyIconUIResource asUIResource() {
|
||||
|
||||
@@ -32,13 +32,14 @@ public class ImageUtil {
|
||||
|
||||
public static BufferedImage toBufferedImage(@NotNull Image image, boolean inUserSize) {
|
||||
if (image instanceof JBHiDPIScaledImage) {
|
||||
Image img = ((JBHiDPIScaledImage)image).getDelegate();
|
||||
float scale = ((JBHiDPIScaledImage)image).getScale();
|
||||
JBHiDPIScaledImage jbImage = (JBHiDPIScaledImage)image;
|
||||
Image img = jbImage.getDelegate();
|
||||
if (img != null) {
|
||||
image = img;
|
||||
if (inUserSize) {
|
||||
image = scaleImage(image, 1 / scale);
|
||||
double scale = jbImage.getScale();
|
||||
img = scaleImage(img, 1 / scale);
|
||||
}
|
||||
image = img;
|
||||
}
|
||||
}
|
||||
if (image instanceof BufferedImage) {
|
||||
@@ -116,7 +117,7 @@ public class ImageUtil {
|
||||
/**
|
||||
* Scales the image taking into account its HiDPI awareness.
|
||||
*/
|
||||
public static Image scaleImage(Image image, float scale) {
|
||||
public static Image scaleImage(Image image, double scale) {
|
||||
return ImageLoader.scaleImage(image, scale);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,36 +15,75 @@
|
||||
*/
|
||||
package com.intellij.util.ui;
|
||||
|
||||
import com.intellij.util.ui.JBUI.Scaler;
|
||||
|
||||
import javax.swing.plaf.UIResource;
|
||||
import java.awt.*;
|
||||
|
||||
import static java.lang.Math.ceil;
|
||||
|
||||
/**
|
||||
* @author Konstantin Bulenkov
|
||||
* @author tav
|
||||
*/
|
||||
public class JBDimension extends Dimension {
|
||||
float myJBUIScale = JBUI.scale(1f);
|
||||
protected Size2D size2D;
|
||||
private MyScaler scaler = new MyScaler();
|
||||
|
||||
private static class Size2D {
|
||||
double width;
|
||||
double height;
|
||||
|
||||
Size2D(double width, double height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
int intWidth() {
|
||||
return (int)ceil(width);
|
||||
}
|
||||
|
||||
int intHeight() {
|
||||
return (int)ceil(height);
|
||||
}
|
||||
|
||||
Size2D copy() {
|
||||
return new Size2D(width, height);
|
||||
}
|
||||
|
||||
void set(double width, double height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
}
|
||||
|
||||
public JBDimension(int width, int height) {
|
||||
this(width, height, true);
|
||||
this(width, height, false);
|
||||
}
|
||||
|
||||
private JBDimension(int width, int height, boolean applyScale) {
|
||||
super(applyScale ? scale(width) : width, applyScale ? scale(height) : height);
|
||||
public JBDimension(int width, int height, boolean preScaled) {
|
||||
this((double)width, (double)height, preScaled);
|
||||
}
|
||||
|
||||
private static int scale(int size) {
|
||||
return size == -1 ? -1 : JBUI.scale(size);
|
||||
private JBDimension(double width, double height, boolean preScaled) {
|
||||
size2D = new Size2D(preScaled ? width : scale(width), preScaled ? height : scale(height));
|
||||
|
||||
set(size2D);
|
||||
}
|
||||
|
||||
public static JBDimension create(Dimension from) {
|
||||
return create(from, true);
|
||||
private double scale(double size) {
|
||||
return Math.max(-1, JBUI.scale((float)size));
|
||||
}
|
||||
|
||||
public static JBDimension create(Dimension from, boolean applyScale) {
|
||||
public static JBDimension create(Dimension from, boolean preScaled) {
|
||||
if (from instanceof JBDimension) {
|
||||
return ((JBDimension)from);
|
||||
}
|
||||
return new JBDimension(from.width, from.height, applyScale);
|
||||
return new JBDimension(from.width, from.height, preScaled);
|
||||
}
|
||||
|
||||
public static JBDimension create(Dimension from) {
|
||||
return create(from, false);
|
||||
}
|
||||
|
||||
public JBDimensionUIResource asUIResource() {
|
||||
@@ -54,30 +93,126 @@ public class JBDimension extends Dimension {
|
||||
public static class JBDimensionUIResource extends JBDimension implements UIResource {
|
||||
public JBDimensionUIResource(JBDimension size) {
|
||||
super(0, 0);
|
||||
width = size.width;
|
||||
height = size.height;
|
||||
set(size.width, size.height);
|
||||
|
||||
size2D = size.size2D.copy();
|
||||
}
|
||||
}
|
||||
|
||||
public JBDimension withWidth(int width) {
|
||||
JBDimension size = new JBDimension(0, 0);
|
||||
size.width = scale(width);
|
||||
size.height = height;
|
||||
size.size2D.set(scale(width), size2D.height);
|
||||
|
||||
size.set(size.size2D.intWidth(), height);
|
||||
return size;
|
||||
}
|
||||
|
||||
public JBDimension withHeight(int height) {
|
||||
JBDimension size = new JBDimension(0, 0);
|
||||
size.width = width;
|
||||
size.height = scale(height);
|
||||
size.size2D.set(size2D.width, scale(height));
|
||||
|
||||
size.set(width, size.size2D.intHeight());
|
||||
return size;
|
||||
}
|
||||
|
||||
// [tav] todo: may lose precision
|
||||
protected void set(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
protected void set(Size2D size2d) {
|
||||
set(size2d.intWidth(), size2d.intHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the size according to current {@link JBUI.ScaleType#USR_SCALE} if necessary.
|
||||
* @return whether the size has been updated
|
||||
*/
|
||||
public boolean update() {
|
||||
if (!scaler.needUpdate()) return false;
|
||||
|
||||
size2D.set(scaler.scaleVal(size2D.width), scaler.scaleVal(size2D.height));
|
||||
|
||||
set(size2D);
|
||||
|
||||
scaler.update();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return this JBDimension with updated size
|
||||
*/
|
||||
public JBDimension size() {
|
||||
update();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return new JBDimension with updated size
|
||||
*/
|
||||
public JBDimension newSize() {
|
||||
update();
|
||||
JBDimension d = new JBDimension(size2D.width, size2D.height, true);
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return updated width
|
||||
*/
|
||||
public int width() {
|
||||
update();
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return updated height
|
||||
*/
|
||||
public int height() {
|
||||
update();
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return updated double width
|
||||
*/
|
||||
public double width2d() {
|
||||
update();
|
||||
return size2D.width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return updated double height
|
||||
*/
|
||||
public double height2d() {
|
||||
update();
|
||||
return size2D.height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (!(obj instanceof JBDimension)) return false;
|
||||
|
||||
JBDimension that = (JBDimension)obj;
|
||||
return size2D.equals(that.size2D);
|
||||
}
|
||||
}
|
||||
|
||||
class MyScaler extends Scaler {
|
||||
public MyScaler() {
|
||||
super(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double currentScale() {
|
||||
return JBUI.scale(1f);
|
||||
}
|
||||
|
||||
public boolean needUpdate() {
|
||||
return initialScale != JBUI.scale(1f);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
float scale = JBUI.scale(1f);
|
||||
width = (int)(width * scale / myJBUIScale);
|
||||
height = (int)(height * scale / myJBUIScale);
|
||||
myJBUIScale = scale;
|
||||
setPreScaled(true); // updates initialScale
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -29,6 +29,7 @@ import com.intellij.util.*;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.JBIterable;
|
||||
import com.intellij.util.containers.JBTreeTraverser;
|
||||
import com.intellij.util.ui.JBUI.ScaleContext;
|
||||
import com.intellij.util.ui.accessibility.ScreenReader;
|
||||
import org.intellij.lang.annotations.JdkConstants;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
@@ -380,6 +381,13 @@ public class UIUtil {
|
||||
return isJreHiDPI(comp != null ? comp.getGraphicsConfiguration() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the JRE-managed HiDPI mode is enabled and the provided system scale context is HiDPI.
|
||||
*/
|
||||
public static boolean isJreHiDPI(@Nullable ScaleContext ctx) {
|
||||
return isJreHiDPIEnabled() && JBUI.isHiDPI(JBUI.sysScale(ctx));
|
||||
}
|
||||
|
||||
private static Boolean jreHiDPI;
|
||||
private static boolean jreHiDPI_earlierVersion;
|
||||
|
||||
|
||||
+3
-3
@@ -331,9 +331,9 @@ class StructureFilterPopupComponent extends FilterPopupComponent<VcsLogFileFilte
|
||||
}
|
||||
|
||||
@Override
|
||||
public CheckboxColorIcon withJBUIPreScaled(boolean preScaled) {
|
||||
mySizedIcon = (SizedIcon)mySizedIcon.withJBUIPreScaled(preScaled);
|
||||
return (CheckboxColorIcon)super.withJBUIPreScaled(preScaled);
|
||||
public CheckboxColorIcon withIconPreScaled(boolean preScaled) {
|
||||
mySizedIcon = (SizedIcon)mySizedIcon.withIconPreScaled(preScaled);
|
||||
return (CheckboxColorIcon)super.withIconPreScaled(preScaled);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+8
-5
@@ -22,6 +22,9 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import static java.lang.Math.ceil;
|
||||
import static java.lang.Math.floor;
|
||||
|
||||
/**
|
||||
* @author Alexander Lobas
|
||||
*/
|
||||
@@ -72,22 +75,22 @@ public final class ColorIcon extends EmptyIcon {
|
||||
iconWidth + coloredComponent.getIpad().left + coloredComponent.getIconTextGap(), component.getHeight());
|
||||
}
|
||||
|
||||
int x = left + (iconWidth - scaleVal(myColorSize)) / 2;
|
||||
int y = top + (iconHeight - scaleVal(myColorSize)) / 2;
|
||||
int x = left + (int)floor((iconWidth - scaleVal(myColorSize)) / 2);
|
||||
int y = top + (int)floor((iconHeight - scaleVal(myColorSize)) / 2);
|
||||
|
||||
g.setColor(myColor);
|
||||
g.fillRect(x, y, scaleVal(myColorSize), scaleVal(myColorSize));
|
||||
g.fillRect(x, y, (int)ceil(scaleVal(myColorSize)), (int)ceil(scaleVal(myColorSize)));
|
||||
|
||||
if (myShowRedLine) {
|
||||
Graphics2D g2d = (Graphics2D)g;
|
||||
Object hint = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g.setColor(JBColor.red);
|
||||
g.drawLine(x, y + scaleVal(myColorSize), x + scaleVal(myColorSize), y);
|
||||
g.drawLine(x, y + (int)floor(scaleVal(myColorSize)), x + (int)floor(scaleVal(myColorSize)), y);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, hint);
|
||||
}
|
||||
|
||||
g.setColor(Color.BLACK);
|
||||
g.drawRect(x, y, scaleVal(myColorSize), scaleVal(myColorSize));
|
||||
g.drawRect(x, y, (int)ceil(scaleVal(myColorSize)), (int)ceil(scaleVal(myColorSize)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user