apply "locked" small patch icon only once

This commit is contained in:
Alexey Kudravtsev
2015-10-15 13:34:11 +03:00
parent deadab4ca1
commit ada6698a52
5 changed files with 361 additions and 263 deletions
@@ -1,4 +1,3 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
@@ -17,9 +16,14 @@
package com.intellij.ui;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;
import javax.swing.*;
import java.awt.*;
import java.util.Arrays;
import java.util.List;
public class RowIcon implements Icon {
private final Alignment myAlignment;
@@ -46,6 +50,13 @@ public class RowIcon implements Icon {
recalculateSize();
}
@TestOnly
@NotNull
Icon[] getAllIcons() {
List<Icon> icons = ContainerUtil.packNullables(myIcons);
return icons.toArray(new Icon[icons.size()]);
}
public int hashCode() {
return myIcons.length > 0 ? myIcons[0].hashCode() : 0;
}
@@ -27,7 +27,6 @@ import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.vfs.VFileProperty;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.WritingAccessProvider;
import com.intellij.ui.IconDeferrer;
import com.intellij.ui.JBColor;
import com.intellij.ui.LayeredIcon;
@@ -53,11 +52,11 @@ public class IconUtil {
Boolean was = project.getUserData(PROJECT_WAS_EVER_INITIALIZED);
if (was == null) {
if (project.isInitialized()) {
was = Boolean.valueOf(true);
was = true;
project.putUserData(PROJECT_WAS_EVER_INITIALIZED, was);
}
else {
was = Boolean.valueOf(false);
was = false;
}
}
@@ -94,7 +93,8 @@ public class IconUtil {
return new ImageIcon(img);
}
public static Icon cropIcon(@NotNull Icon icon, Rectangle area) {
@NotNull
public static Icon cropIcon(@NotNull Icon icon, @NotNull Rectangle area) {
if (!new Rectangle(icon.getIconWidth(), icon.getIconHeight()).contains(area)) {
return icon;
}
@@ -153,10 +153,6 @@ public class IconUtil {
icon = patcher.patchIcon(icon, file, flags, project);
}
if ((flags & Iconable.ICON_FLAG_READ_STATUS) != 0 &&
(!file.isWritable() || !WritingAccessProvider.isPotentiallyWritable(file, project))) {
icon = new LayeredIcon(icon, PlatformIcons.LOCKED_ICON);
}
if (file.is(VFileProperty.SYMLINK)) {
icon = new LayeredIcon(icon, PlatformIcons.SYMLINK_ICON);
}
@@ -234,72 +230,88 @@ public class IconUtil {
}
}
@NotNull
public static Icon getAddIcon() {
return getToolbarDecoratorIcon("add.png");
}
@NotNull
public static Icon getRemoveIcon() {
return getToolbarDecoratorIcon("remove.png");
}
@NotNull
public static Icon getMoveUpIcon() {
return getToolbarDecoratorIcon("moveUp.png");
}
@NotNull
public static Icon getMoveDownIcon() {
return getToolbarDecoratorIcon("moveDown.png");
}
@NotNull
public static Icon getEditIcon() {
return getToolbarDecoratorIcon("edit.png");
}
@NotNull
public static Icon getAddClassIcon() {
return getToolbarDecoratorIcon("addClass.png");
}
@NotNull
public static Icon getAddPatternIcon() {
return getToolbarDecoratorIcon("addPattern.png");
}
@NotNull
public static Icon getAddJiraPatternIcon() {
return getToolbarDecoratorIcon("addJira.png");
}
@NotNull
public static Icon getAddYouTrackPatternIcon() {
return getToolbarDecoratorIcon("addYouTrack.png");
}
@NotNull
public static Icon getAddBlankLineIcon() {
return getToolbarDecoratorIcon("addBlankLine.png");
}
@NotNull
public static Icon getAddPackageIcon() {
return getToolbarDecoratorIcon("addPackage.png");
}
@NotNull
public static Icon getAddLinkIcon() {
return getToolbarDecoratorIcon("addLink.png");
}
@NotNull
public static Icon getAddFolderIcon() {
return getToolbarDecoratorIcon("addFolder.png");
}
@NotNull
public static Icon getAnalyzeIcon() {
return getToolbarDecoratorIcon("analyze.png");
}
public static void paintInCenterOf(@NotNull Component c, Graphics g, Icon icon) {
public static void paintInCenterOf(@NotNull Component c, @NotNull Graphics g, @NotNull Icon icon) {
final int x = (c.getWidth() - icon.getIconWidth()) / 2;
final int y = (c.getHeight() - icon.getIconHeight()) / 2;
icon.paintIcon(c, g, x, y);
}
public static Icon getToolbarDecoratorIcon(String name) {
@NotNull
private static Icon getToolbarDecoratorIcon(@NotNull String name) {
return IconLoader.getIcon(getToolbarDecoratorIconsFolder() + name);
}
@NotNull
private static String getToolbarDecoratorIconsFolder() {
return "/toolbarDecorator/" + (SystemInfo.isMac ? "mac/" : "");
}
@@ -322,6 +334,7 @@ public class IconUtil {
return result;
}
@NotNull
public static Icon toSize(@NotNull Icon icon, int width, int height) {
return new IconSizeWrapper(icon, width, height);
}
@@ -359,7 +372,7 @@ public class IconUtil {
private final Icon mySrc;
private final Rectangle myCrop;
private CropIcon(@NotNull Icon src, Rectangle crop) {
private CropIcon(@NotNull Icon src, @NotNull Rectangle crop) {
mySrc = src;
myCrop = crop;
}
@@ -380,12 +393,14 @@ public class IconUtil {
}
}
@NotNull
public static Icon scale(@NotNull final Icon source, double _scale) {
final int hiDPIscale;
if (source instanceof ImageIcon) {
Image image = ((ImageIcon)source).getImage();
hiDPIscale = RetinaImage.isAppleHiDPIScaledImage(image) || image instanceof JBHiDPIScaledImage ? 2 : 1;
} else {
}
else {
hiDPIscale = 1;
}
final double scale = Math.min(32, Math.max(.1, _scale));
@@ -400,7 +415,8 @@ public class IconUtil {
g2d.setTransform(transform);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
source.paintIcon(c, g2d, 0, 0);
} finally {
}
finally {
g2d.dispose();
}
}
@@ -415,7 +431,6 @@ public class IconUtil {
return (int)(source.getIconHeight() * scale) / hiDPIscale;
}
};
}
@NotNull
@@ -465,7 +480,7 @@ public class IconUtil {
}
@NotNull
public static Icon textToIcon(final String text, final Component component, final float fontSize) {
public static Icon textToIcon(@NotNull final String text, @NotNull final Component component, final float fontSize) {
final Font font = JBFont.create(JBUI.Fonts.label().deriveFont(fontSize));
FontMetrics metrics = component.getFontMetrics(font);
final int width = metrics.stringWidth(text) + JBUI.scale(4);
@@ -479,7 +494,8 @@ public class IconUtil {
GraphicsUtil.setupAntialiasing(g);
g.setFont(font);
UIUtil.drawStringWithHighlighting(g, text, x + JBUI.scale(2), y + height - JBUI.scale(1), JBColor.foreground(), JBColor.background());
} finally {
}
finally {
g.dispose();
}
}
@@ -496,6 +512,7 @@ public class IconUtil {
};
}
@NotNull
public static Icon addText(@NotNull Icon base, @NotNull String text) {
LayeredIcon icon = new LayeredIcon(2);
icon.setIcon(base, 0);
@@ -35,6 +35,7 @@ import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.ide.PooledThreadExecutor;
import javax.swing.*;
@@ -371,6 +372,12 @@ public class DeferredIconImpl<T> implements DeferredIcon {
equalIcons(myDelegateIcon, ((DeferredIconImpl)icon).myDelegateIcon);
}
@TestOnly
@NotNull
Icon getDelegateIcon() {
return myDelegateIcon;
}
@Override
public String toString() {
return "Deferred. Base=" + myDelegateIcon;
@@ -1,246 +0,0 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.editor.impl;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.PlatformTestCase;
import com.intellij.ui.DeferredIcon;
import com.intellij.util.FileContentUtilCore;
import com.intellij.util.IconUtil;
import com.intellij.util.TimeoutUtil;
import javax.swing.*;
import java.awt.*;
import java.awt.image.ImageObserver;
import java.io.IOException;
import java.text.AttributedCharacterIterator;
public class IconUtilTest extends PlatformTestCase {
public void testIconDeferrerDoesNotDeferIconsAdInfinitum() throws IOException {
VirtualFile file = createTempFile("txt", null, "hkjh", CharsetToolkit.UTF8_CHARSET);
Icon icon = IconUtil.getIcon(file, Iconable.ICON_FLAG_VISIBILITY, getProject());
assertTrue(icon instanceof DeferredIcon);
Graphics g = new Graphics() {
@Override
public Graphics create() {
return this;
}
@Override
public void translate(int x, int y) {
}
@Override
public Color getColor() {
return null;
}
@Override
public void setColor(Color c) {
}
@Override
public void setPaintMode() {
}
@Override
public void setXORMode(Color c1) {
}
@Override
public Font getFont() {
return null;
}
@Override
public void setFont(Font font) {
}
@Override
public FontMetrics getFontMetrics(Font f) {
return null;
}
@Override
public Rectangle getClipBounds() {
return null;
}
@Override
public void clipRect(int x, int y, int width, int height) {
}
@Override
public void setClip(int x, int y, int width, int height) {
}
@Override
public Shape getClip() {
return null;
}
@Override
public void setClip(Shape clip) {
}
@Override
public void copyArea(int x, int y, int width, int height, int dx, int dy) {
}
@Override
public void drawLine(int x1, int y1, int x2, int y2) {
}
@Override
public void fillRect(int x, int y, int width, int height) {
}
@Override
public void clearRect(int x, int y, int width, int height) {
}
@Override
public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
}
@Override
public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
}
@Override
public void drawOval(int x, int y, int width, int height) {
}
@Override
public void fillOval(int x, int y, int width, int height) {
}
@Override
public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
}
@Override
public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
}
@Override
public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) {
}
@Override
public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) {
}
@Override
public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) {
}
@Override
public void drawString(String str, int x, int y) {
}
@Override
public void drawString(AttributedCharacterIterator iterator, int x, int y) {
}
@Override
public boolean drawImage(Image img, int x, int y, ImageObserver observer) {
return false;
}
@Override
public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) {
return false;
}
@Override
public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) {
return false;
}
@Override
public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) {
return false;
}
@Override
public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) {
return false;
}
@Override
public boolean drawImage(Image img,
int dx1,
int dy1,
int dx2,
int dy2,
int sx1,
int sy1,
int sx2,
int sy2,
Color bgcolor,
ImageObserver observer) {
return false;
}
@Override
public void dispose() {
}
};
icon.paintIcon(new JLabel(), g, 0, 0); // force to eval
TimeoutUtil.sleep(1000); // give chance to evaluate
Icon icon2 = IconUtil.getIcon(file, Iconable.ICON_FLAG_VISIBILITY, getProject());
assertSame(icon, icon2);
FileContentUtilCore.reparseFiles(file);
Icon icon3 = IconUtil.getIcon(file, Iconable.ICON_FLAG_VISIBILITY, getProject());
assertNotSame(icon2, icon3);
}
@Override
protected boolean isRunInWriteAction() {
return false;
}
}
@@ -0,0 +1,309 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ui;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.PlatformTestCase;
import com.intellij.testFramework.PsiTestUtil;
import com.intellij.util.FileContentUtilCore;
import com.intellij.util.IconUtil;
import com.intellij.util.PlatformIcons;
import com.intellij.util.TimeoutUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.image.ImageObserver;
import java.io.File;
import java.io.IOException;
import java.text.AttributedCharacterIterator;
import java.util.Collections;
import java.util.List;
public class IconUtilTest extends PlatformTestCase {
public void testIconDeferrerDoesNotDeferIconsAdInfinitum() throws IOException {
VirtualFile file = createTempFile("txt", null, "hkjh", CharsetToolkit.UTF8_CHARSET);
Icon icon = IconUtil.getIcon(file, Iconable.ICON_FLAG_VISIBILITY, getProject());
assertTrue(icon instanceof DeferredIcon);
Graphics g = createMockGraphics();
icon.paintIcon(new JLabel(), g, 0, 0); // force to eval
TimeoutUtil.sleep(1000); // give chance to evaluate
Icon icon2 = IconUtil.getIcon(file, Iconable.ICON_FLAG_VISIBILITY, getProject());
assertSame(icon, icon2);
FileContentUtilCore.reparseFiles(file);
Icon icon3 = IconUtil.getIcon(file, Iconable.ICON_FLAG_VISIBILITY, getProject());
assertNotSame(icon2, icon3);
}
@NotNull
private static Graphics createMockGraphics() {
return new Graphics() {
@Override
public Graphics create() {
return this;
}
@Override
public void translate(int x, int y) {
}
@Override
public Color getColor() {
return null;
}
@Override
public void setColor(Color c) {
}
@Override
public void setPaintMode() {
}
@Override
public void setXORMode(Color c1) {
}
@Override
public Font getFont() {
return null;
}
@Override
public void setFont(Font font) {
}
@Override
public FontMetrics getFontMetrics(Font f) {
return null;
}
@Override
public Rectangle getClipBounds() {
return null;
}
@Override
public void clipRect(int x, int y, int width, int height) {
}
@Override
public void setClip(int x, int y, int width, int height) {
}
@Override
public Shape getClip() {
return null;
}
@Override
public void setClip(Shape clip) {
}
@Override
public void copyArea(int x, int y, int width, int height, int dx, int dy) {
}
@Override
public void drawLine(int x1, int y1, int x2, int y2) {
}
@Override
public void fillRect(int x, int y, int width, int height) {
}
@Override
public void clearRect(int x, int y, int width, int height) {
}
@Override
public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
}
@Override
public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
}
@Override
public void drawOval(int x, int y, int width, int height) {
}
@Override
public void fillOval(int x, int y, int width, int height) {
}
@Override
public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
}
@Override
public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
}
@Override
public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) {
}
@Override
public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) {
}
@Override
public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) {
}
@Override
public void drawString(String str, int x, int y) {
}
@Override
public void drawString(AttributedCharacterIterator iterator, int x, int y) {
}
@Override
public boolean drawImage(Image img, int x, int y, ImageObserver observer) {
return false;
}
@Override
public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) {
return false;
}
@Override
public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) {
return false;
}
@Override
public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) {
return false;
}
@Override
public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) {
return false;
}
@Override
public boolean drawImage(Image img,
int dx1,
int dy1,
int dx2,
int dy2,
int sx1,
int sy1,
int sx2,
int sy2,
Color bgcolor,
ImageObserver observer) {
return false;
}
@Override
public void dispose() {
}
};
}
public void testLockedPatchSmallIconAppliedOnlyOnce() throws IOException {
File dir = createTempDir("my");
VirtualFile sourceRoot = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(dir);
VirtualFile file = createChildData(sourceRoot, "X.java");
PsiTestUtil.addSourceRoot(getModule(), sourceRoot);
WriteCommandAction.runWriteCommandAction(getProject(),
(ThrowableComputable<Void,IOException>)() -> {
file.setBinaryContent("class X {}".getBytes(CharsetToolkit.UTF8_CHARSET));
file.setWritable(false);
return null;
});
UIUtil.dispatchAllInvocationEvents(); // write actions
UIUtil.dispatchAllInvocationEvents();
try {
Icon icon = IconUtil.getIcon(file, -1, getProject());
icon.paintIcon(new JLabel(), createMockGraphics(), 0, 0); // force to eval
TimeoutUtil.sleep(1000); // give chance to evaluate
UIUtil.dispatchAllInvocationEvents();
UIUtil.dispatchAllInvocationEvents();
List<Icon> icons = autopsyIconsFrom(icon);
assertOneElement(ContainerUtil.filter(icons, ic -> ic == PlatformIcons.LOCKED_ICON));
}
finally {
WriteCommandAction.runWriteCommandAction(getProject(),
(ThrowableComputable<Void,IOException>)() -> {
file.setWritable(true);
return null;
});
}
}
@NotNull
private static List<Icon> autopsyIconsFrom(@NotNull Icon icon) {
if (icon instanceof DeferredIconImpl) {
return autopsyIconsFrom(((DeferredIconImpl)icon).getDelegateIcon());
}
if (icon instanceof LayeredIcon) {
return ContainerUtil.flatten(ContainerUtil.map(((LayeredIcon)icon).getAllLayers(), IconUtilTest::autopsyIconsFrom));
}
if (icon instanceof RowIcon) {
return ContainerUtil.flatten(ContainerUtil.map(((RowIcon)icon).getAllIcons(), IconUtilTest::autopsyIconsFrom));
}
return Collections.singletonList(icon);
}
@Override
protected boolean isRunInWriteAction() {
return false;
}
}