From 5403cb8972c6cadff96bef82e45d0cfdc6f1dc46 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 5 May 2020 18:38:53 +0200 Subject: [PATCH] Initial support for icon description tooltips (IDEA-237109) GitOrigin-RevId: 137eb5c9b12d890b4f446b106523207f1b54e96a --- .../psi/impl/JavaDirectoryIconProvider.java | 3 +- .../psi/impl/ElementPresentationUtil.java | 9 ++-- .../psi/impl/file/PsiDirectoryImpl.java | 3 +- platform/core-ui/src/ui/CoreIconManager.java | 50 ++++++++++++++++++- .../src/ui/IconDescriptionBundleEP.java | 12 +++++ platform/core-ui/src/ui/IconWithToolTip.java | 29 +++++++++++ .../src/ui/IconWrapperWithToolTip.java | 49 ++++++++++++++++++ .../ui/IconWrapperWithToolTipComposite.java | 47 +++++++++++++++++ platform/core-ui/src/ui/LayeredIcon.java | 47 ++++++++++++++++- platform/core-ui/src/ui/RowIcon.java | 7 ++- platform/core-ui/src/util/IconUtil.java | 2 +- .../com/intellij/ide/bookmarks/Bookmark.java | 8 ++- .../src/com/intellij/ui/DeferredIconImpl.java | 10 +++- .../resources/messages/IdeBundle.properties | 46 ++++++++++++++++- .../intellij/ui/SimpleColoredComponent.java | 13 +++++ .../src/META-INF/LangExtensions.xml | 2 + .../src/META-INF/PlatformExtensionPoints.xml | 1 + .../testSrc/com/intellij/ui/IconUtilTest.java | 2 +- .../src/com/intellij/ui/IconTestUtil.java | 22 ++++++++ .../util/resources/misc/registry.properties | 6 +++ 20 files changed, 354 insertions(+), 14 deletions(-) create mode 100644 platform/core-ui/src/ui/IconDescriptionBundleEP.java create mode 100644 platform/core-ui/src/ui/IconWithToolTip.java create mode 100644 platform/core-ui/src/ui/IconWrapperWithToolTip.java create mode 100644 platform/core-ui/src/ui/IconWrapperWithToolTipComposite.java create mode 100644 platform/testFramework/src/com/intellij/ui/IconTestUtil.java diff --git a/java/java-impl/src/com/intellij/psi/impl/JavaDirectoryIconProvider.java b/java/java-impl/src/com/intellij/psi/impl/JavaDirectoryIconProvider.java index 7339a8740293..d7e84e1e9353 100644 --- a/java/java-impl/src/com/intellij/psi/impl/JavaDirectoryIconProvider.java +++ b/java/java-impl/src/com/intellij/psi/impl/JavaDirectoryIconProvider.java @@ -19,6 +19,7 @@ import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.impl.file.PsiDirectoryFactory; import com.intellij.ui.IconManager; +import com.intellij.ui.IconWithToolTip; import com.intellij.util.PlatformIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -53,7 +54,7 @@ final class JavaDirectoryIconProvider extends IconProvider implements DumbAware symbolIcon = AllIcons.Nodes.Module; } else if (isValidPackage(psiDirectory)) { - symbolIcon = PlatformIcons.PACKAGE_ICON; + symbolIcon = IconWithToolTip.tooltipOnlyIfComposite(PlatformIcons.PACKAGE_ICON); } else if (!Registry.is("ide.hide.excluded.files") && ProjectRootManager.getInstance(project).getFileIndex().isExcluded(vFile)) { symbolIcon = AllIcons.Modules.ExcludeRoot; diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/ElementPresentationUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/ElementPresentationUtil.java index 5d230a890aaf..f02ebe714a6c 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/ElementPresentationUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/ElementPresentationUtil.java @@ -12,6 +12,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.intellij.ui.IconManager; +import com.intellij.ui.IconWithToolTip; import com.intellij.ui.icons.RowIcon; import com.intellij.util.BitUtil; import com.intellij.util.PlatformIcons; @@ -142,7 +143,7 @@ public final class ElementPresentationUtil implements PlatformIcons { private static final TIntObjectHashMap BASE_ICON = new TIntObjectHashMap<>(20); static { - BASE_ICON.put(CLASS_KIND_CLASS, CLASS_ICON); + BASE_ICON.put(CLASS_KIND_CLASS, IconWithToolTip.tooltipOnlyIfComposite(CLASS_ICON)); BASE_ICON.put(CLASS_KIND_CLASS | FLAGS_ABSTRACT, ABSTRACT_CLASS_ICON); BASE_ICON.put(CLASS_KIND_ANNOTATION, ANNOTATION_TYPE_ICON); BASE_ICON.put(CLASS_KIND_ANNOTATION | FLAGS_ABSTRACT, ANNOTATION_TYPE_ICON); @@ -154,9 +155,9 @@ public final class ElementPresentationUtil implements PlatformIcons { BASE_ICON.put(CLASS_KIND_ENUM | FLAGS_ABSTRACT, ENUM_ICON); BASE_ICON.put(CLASS_KIND_EXCEPTION, EXCEPTION_CLASS_ICON); BASE_ICON.put(CLASS_KIND_EXCEPTION | FLAGS_ABSTRACT, AllIcons.Nodes.AbstractException); - BASE_ICON.put(CLASS_KIND_INTERFACE, INTERFACE_ICON); - BASE_ICON.put(CLASS_KIND_INTERFACE | FLAGS_ABSTRACT, INTERFACE_ICON); - BASE_ICON.put(CLASS_KIND_JUNIT_TEST, CLASS_ICON); + BASE_ICON.put(CLASS_KIND_INTERFACE, IconWithToolTip.tooltipOnlyIfComposite(INTERFACE_ICON)); + BASE_ICON.put(CLASS_KIND_INTERFACE | FLAGS_ABSTRACT, IconWithToolTip.tooltipOnlyIfComposite(INTERFACE_ICON)); + BASE_ICON.put(CLASS_KIND_JUNIT_TEST, IconWithToolTip.tooltipOnlyIfComposite(CLASS_ICON)); BASE_ICON.put(CLASS_KIND_JUNIT_TEST | FLAGS_ABSTRACT, ABSTRACT_CLASS_ICON); BASE_ICON.put(CLASS_KIND_RECORD, RECORD_ICON); BASE_ICON.put(CLASS_KIND_RUNNABLE, CLASS_ICON); diff --git a/platform/core-impl/src/com/intellij/psi/impl/file/PsiDirectoryImpl.java b/platform/core-impl/src/com/intellij/psi/impl/file/PsiDirectoryImpl.java index aa0675653c09..754268cea238 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/file/PsiDirectoryImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/file/PsiDirectoryImpl.java @@ -28,6 +28,7 @@ import com.intellij.psi.search.PsiElementProcessor; import com.intellij.psi.search.PsiFileSystemItemProcessor; import com.intellij.psi.util.PsiUtilCore; import com.intellij.testFramework.LightVirtualFile; +import com.intellij.ui.IconWithToolTip; import com.intellij.util.ArrayUtilRt; import com.intellij.util.IncorrectOperationException; import com.intellij.util.PlatformIcons; @@ -527,7 +528,7 @@ public class PsiDirectoryImpl extends PsiElementBase implements PsiDirectory, Qu @Override protected Icon getElementIcon(final int flags) { - return PlatformIcons.FOLDER_ICON; + return IconWithToolTip.tooltipOnlyIfComposite(PlatformIcons.FOLDER_ICON); } @Override diff --git a/platform/core-ui/src/ui/CoreIconManager.java b/platform/core-ui/src/ui/CoreIconManager.java index 78a7e6f9196b..9af616a3f7a7 100644 --- a/platform/core-ui/src/ui/CoreIconManager.java +++ b/platform/core-ui/src/ui/CoreIconManager.java @@ -1,9 +1,15 @@ // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.ui; +import com.intellij.AbstractBundle; +import com.intellij.DynamicBundle; import com.intellij.ide.IconLayerProvider; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.Iconable; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.BitUtil; import com.intellij.util.IconUtil; import com.intellij.util.SmartList; @@ -14,16 +20,20 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; import java.util.List; +import java.util.ResourceBundle; import java.util.function.Function; +import java.util.function.Supplier; public final class CoreIconManager implements IconManager { private static final List ourIconLayers = ContainerUtil.createLockFreeCopyOnWriteList(); private static final int FLAGS_LOCKED = 0x800; + private static final Logger LOG = Logger.getInstance(CoreIconManager.class); @NotNull @Override public Icon getIcon(@NotNull String path, @NotNull Class aClass) { - return IconLoader.getIcon(path, aClass); + Icon icon = IconLoader.getIcon(path, aClass); + return IconWithToolTip.create(icon, new IconDescriptionLoader(path)); } @NotNull @@ -125,4 +135,42 @@ public final class CoreIconManager implements IconManager { this.icon = icon; } } + + private static class IconDescriptionLoader implements Supplier { + private final String myPath; + private String myResult; + private boolean myCalculated; + + private IconDescriptionLoader(String path) { + myPath = path; + } + + @Override + public String get() { + if (!myCalculated) { + myResult = findIconDescription(); + myCalculated = true; + } + return myResult; + } + + private String findIconDescription() { + String basePath = StringUtil.trimStart(StringUtil.trimEnd(myPath, ".svg"), "/"); + String key = "icon." + basePath.replace('/', '.') + ".tooltip"; + Ref result = new Ref<>(); + IconDescriptionBundleEP.EP_NAME.processWithPluginDescriptor((ep, descriptor) -> { + ClassLoader classLoader = descriptor == null ? null : descriptor.getPluginClassLoader(); + if (classLoader == null) classLoader = getClass().getClassLoader(); + ResourceBundle bundle = DynamicBundle.INSTANCE.getResourceBundle(ep.qualifiedName, classLoader); + String description = AbstractBundle.messageOrNull(bundle, key); + if (description != null) { + result.set(description); + } + }); + if (result.get() == null && Registry.is("ide.icon.tooltips.trace.missing")) { + LOG.info("Icon tooltip requested but not found for " + myPath); + } + return result.get(); + } + } } diff --git a/platform/core-ui/src/ui/IconDescriptionBundleEP.java b/platform/core-ui/src/ui/IconDescriptionBundleEP.java new file mode 100644 index 000000000000..1eb255161900 --- /dev/null +++ b/platform/core-ui/src/ui/IconDescriptionBundleEP.java @@ -0,0 +1,12 @@ +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.ui; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.util.xmlb.annotations.Attribute; + +public class IconDescriptionBundleEP { + public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.iconDescriptionBundle"); + + @Attribute("qualifiedName") + public String qualifiedName; +} diff --git a/platform/core-ui/src/ui/IconWithToolTip.java b/platform/core-ui/src/ui/IconWithToolTip.java new file mode 100644 index 000000000000..cc791fc1d751 --- /dev/null +++ b/platform/core-ui/src/ui/IconWithToolTip.java @@ -0,0 +1,29 @@ +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.ui; + +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.function.Supplier; + +/** + * Icon which supports providing a tooltip. + */ +public interface IconWithToolTip extends Icon { + /** + * Returns the tooltip for the icon. + * @param composite if true, this tooltip will be combined with other tooltips (from other layers of a layered icon or parts of a row icon). + * For some icons, it only makes sense to show a tooltip if the icon is composite. + * @return + */ + @Nullable + String getToolTip(boolean composite); + + static IconWithToolTip create(Icon icon, Supplier tooltip) { + return new IconWrapperWithToolTip(icon, tooltip); + } + + static IconWithToolTip tooltipOnlyIfComposite(Icon icon) { + return new IconWrapperWithToolTipComposite(icon); + } +} diff --git a/platform/core-ui/src/ui/IconWrapperWithToolTip.java b/platform/core-ui/src/ui/IconWrapperWithToolTip.java new file mode 100644 index 000000000000..bbbd5036bb57 --- /dev/null +++ b/platform/core-ui/src/ui/IconWrapperWithToolTip.java @@ -0,0 +1,49 @@ +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.ui; + +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.util.function.Supplier; + +public class IconWrapperWithToolTip implements IconWithToolTip, RetrievableIcon { + private final Icon myIcon; + private final Supplier myToolTip; + + public IconWrapperWithToolTip(Icon icon, Supplier toolTip) { + myIcon = icon; + myToolTip = toolTip; + } + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + myIcon.paintIcon(c, g, x, y); + } + + @Override + public int getIconWidth() { + return myIcon.getIconWidth(); + } + + @Override + public int getIconHeight() { + return myIcon.getIconHeight(); + } + + @Override + public String getToolTip(boolean composite) { + return myToolTip.get(); + } + + @Override + public String toString() { + return "IconWrapperWithTooltip:" + myIcon; + } + + @Override + public @NotNull Icon retrieveIcon() { + return myIcon; + } +} + diff --git a/platform/core-ui/src/ui/IconWrapperWithToolTipComposite.java b/platform/core-ui/src/ui/IconWrapperWithToolTipComposite.java new file mode 100644 index 000000000000..0f84873bd9bc --- /dev/null +++ b/platform/core-ui/src/ui/IconWrapperWithToolTipComposite.java @@ -0,0 +1,47 @@ +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.ui; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; + +public class IconWrapperWithToolTipComposite implements IconWithToolTip, RetrievableIcon { + private final Icon myIcon; + + public IconWrapperWithToolTipComposite(Icon icon) { + myIcon = icon; + } + + @Override + public @Nullable String getToolTip(boolean composite) { + if (!composite) return null; + return myIcon instanceof IconWithToolTip ? ((IconWithToolTip)myIcon).getToolTip(true) : null; + } + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + myIcon.paintIcon(c, g, x, y); + } + + @Override + public int getIconWidth() { + return myIcon.getIconWidth(); + } + + @Override + public int getIconHeight() { + return myIcon.getIconHeight(); + } + + @Override + public @NotNull Icon retrieveIcon() { + return myIcon; + } + + @Override + public String toString() { + return "IconWrapperWithTooltipComposite:" + myIcon.toString(); + } +} diff --git a/platform/core-ui/src/ui/LayeredIcon.java b/platform/core-ui/src/ui/LayeredIcon.java index 99ca0776591e..2fd5fb090f07 100644 --- a/platform/core-ui/src/ui/LayeredIcon.java +++ b/platform/core-ui/src/ui/LayeredIcon.java @@ -10,6 +10,7 @@ import com.intellij.util.IconUtil; import com.intellij.util.ui.JBCachingScalableIcon; import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; @@ -18,7 +19,7 @@ import java.util.Arrays; import static com.intellij.ui.scale.ScaleType.OBJ_SCALE; import static com.intellij.ui.scale.ScaleType.USR_SCALE; -public class LayeredIcon extends JBCachingScalableIcon implements DarkIconProvider, CompositeIcon { +public class LayeredIcon extends JBCachingScalableIcon implements DarkIconProvider, CompositeIcon, IconWithToolTip { private static final Logger LOG = Logger.getInstance("#com.intellij.ui.LayeredIcon"); private final Icon[] myIcons; private Icon[] myScaledIcons; @@ -309,4 +310,48 @@ public class LayeredIcon extends JBCachingScalableIcon implements D public String toString() { return "Layered icon "+getIconWidth()+"x"+getIconHeight()+". myIcons=" + Arrays.asList(myIcons); } + + @Override + public String getToolTip(boolean composite) { + return combineIconTooltips(myIcons); + } + + @Nullable + static String combineIconTooltips(Icon[] icons) { + // If a layered icon contains only a single non-null layer and other layers are null, its tooltip is not a composite one. + Icon singleIcon = null; + for (Icon icon : icons) { + if (icon != null) { + if (singleIcon != null) { + return buildCompositeTooltip(icons); + } + singleIcon = icon; + } + } + if (singleIcon != null) { + return singleIcon instanceof IconWithToolTip ? ((IconWithToolTip) singleIcon).getToolTip(false) : null; + } + return null; + } + + @Nullable + private static String buildCompositeTooltip(Icon[] icons) { + StringBuilder result = null; + for (int i = 0; i < icons.length; i++) { + // first layer is the actual object (noun), other layers are modifiers (adjectives), so put first object in last position + Icon icon = i == icons.length - 1 ? icons[0] : icons[i + 1]; + if (icon instanceof IconWithToolTip) { + String toolTip = ((IconWithToolTip)icon).getToolTip(true); + if (toolTip != null) { + if (result == null) { + result = new StringBuilder(toolTip); + } + else { + result.append(" ").append(toolTip); + } + } + } + } + return result != null ? result.toString() : null; + } } diff --git a/platform/core-ui/src/ui/RowIcon.java b/platform/core-ui/src/ui/RowIcon.java index de7f17927077..fdd5ee8b11c3 100644 --- a/platform/core-ui/src/ui/RowIcon.java +++ b/platform/core-ui/src/ui/RowIcon.java @@ -17,7 +17,7 @@ import java.util.List; import static com.intellij.ui.scale.ScaleType.OBJ_SCALE; import static java.lang.Math.ceil; -public class RowIcon extends JBCachingScalableIcon implements com.intellij.ui.icons.RowIcon { +public class RowIcon extends JBCachingScalableIcon implements com.intellij.ui.icons.RowIcon, IconWithToolTip { private final com.intellij.ui.icons.RowIcon.Alignment myAlignment; private int myWidth; @@ -202,4 +202,9 @@ public class RowIcon extends JBCachingScalableIcon implements com.intel public String toString() { return "Row icon. myIcons=" + Arrays.asList(myIcons); } + + @Override + public String getToolTip(boolean composite) { + return LayeredIcon.combineIconTooltips(myIcons); + } } diff --git a/platform/core-ui/src/util/IconUtil.java b/platform/core-ui/src/util/IconUtil.java index 4682faa8b40b..f531032c9cf9 100644 --- a/platform/core-ui/src/util/IconUtil.java +++ b/platform/core-ui/src/util/IconUtil.java @@ -188,7 +188,7 @@ public class IconUtil { } FileType fileType = vFile.getFileType(); if (vFile.isDirectory() && !(fileType instanceof DirectoryFileType)) { - return PlatformIcons.FOLDER_ICON; + return IconWithToolTip.tooltipOnlyIfComposite(PlatformIcons.FOLDER_ICON); } return fileType.getIcon(); } diff --git a/platform/lang-impl/src/com/intellij/ide/bookmarks/Bookmark.java b/platform/lang-impl/src/com/intellij/ide/bookmarks/Bookmark.java index 56aea2f0b907..254c9a1c5b5f 100644 --- a/platform/lang-impl/src/com/intellij/ide/bookmarks/Bookmark.java +++ b/platform/lang-impl/src/com/intellij/ide/bookmarks/Bookmark.java @@ -38,6 +38,7 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.reference.SoftReference; import com.intellij.ui.ColorUtil; +import com.intellij.ui.IconWithToolTip; import com.intellij.ui.JBColor; import com.intellij.ui.RetrievableIcon; import com.intellij.util.IconUtil; @@ -484,7 +485,7 @@ public final class Bookmark implements Navigatable, Comparable { } } - private static class MyCheckedIcon extends JBCachingScalableIcon implements RetrievableIcon { + private static class MyCheckedIcon extends JBCachingScalableIcon implements RetrievableIcon, IconWithToolTip { @NotNull @Override public Icon retrieveIcon() { @@ -515,6 +516,11 @@ public final class Bookmark implements Navigatable, Comparable { public MyCheckedIcon copy() { return new MyCheckedIcon(); } + + @Override + public String getToolTip(boolean composite) { + return IdeBundle.message("tooltip.bookmarked"); + } } private static boolean darkBackground() { diff --git a/platform/lang-impl/src/com/intellij/ui/DeferredIconImpl.java b/platform/lang-impl/src/com/intellij/ui/DeferredIconImpl.java index d8798f3dcaec..da3ac142dccd 100644 --- a/platform/lang-impl/src/com/intellij/ui/DeferredIconImpl.java +++ b/platform/lang-impl/src/com/intellij/ui/DeferredIconImpl.java @@ -36,7 +36,7 @@ import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; -public final class DeferredIconImpl extends JBCachingScalableIcon> implements DeferredIcon, RetrievableIcon { +public final class DeferredIconImpl extends JBCachingScalableIcon> implements DeferredIcon, RetrievableIcon, IconWithToolTip { private static final Logger LOG = Logger.getInstance(DeferredIconImpl.class); private static final int MIN_AUTO_UPDATE_MILLIS = 950; private static final RepaintScheduler ourRepaintScheduler = new RepaintScheduler(); @@ -320,6 +320,14 @@ public final class DeferredIconImpl extends JBCachingScalableIcon 0 && System.currentTimeMillis() - myLastCalcTime > Math.max(MIN_AUTO_UPDATE_MILLIS, 10 * myLastTimeSpent)) { myDone = false; diff --git a/platform/platform-api/resources/messages/IdeBundle.properties b/platform/platform-api/resources/messages/IdeBundle.properties index a70f58b9bb7a..94c3b0276789 100644 --- a/platform/platform-api/resources/messages/IdeBundle.properties +++ b/platform/platform-api/resources/messages/IdeBundle.properties @@ -2082,4 +2082,48 @@ button.convert.anyway=Convert anyway button.reload.anyway=Reload anyway dialog.title.file.0.can.t.be.reloaded=File ''{0}'' can''t be reloaded in the ''{1}'' encoding.{2} dialog.title.file.0.most.likely.isn.t.stored=File ''{0}'' most likely isn''t stored in the ''{1}'' encoding.{2} -dumb.service.indexing.paused.due.to=Indexing paused due to {0} \ No newline at end of file +dumb.service.indexing.paused.due.to=Indexing paused due to {0} +tooltip.bookmarked=bookmarked + +icon.fileTypes.archive.tooltip=archive file + +icon.modules.excludedGeneratedRoot.tooltip=excluded generated sources root +icon.modules.excludeRoot.tooltip=excluded directory +icon.modules.generatedFolder.tooltip=generated folder +icon.modules.generatedSourceRoot.tooltip=generated sources root +icon.modules.generatedTestRoot.tooltip=generated test sources root +icon.modules.output.tooltip=output root +icon.modules.resourcesRoot.tooltip=resources root +icon.modules.sourceRoot.tooltip=sources root +icon.modules.testResourcesRoot.tooltip=test resources root +icon.modules.testRoot.tooltip=test sources root +icon.modules.unloadedModule.tooltip=unloaded module + +icon.nodes.abstractClass.tooltip=abstract class +icon.nodes.abstractException.tooltip=abstract exception +icon.nodes.abstractMethod.tooltip=abstract method +icon.nodes.annotationtype.tooltip=annotation type +icon.nodes.anonymousClass.tooltip=anonymous class +icon.nodes.c_plocal.tooltip=package-local +icon.nodes.c_private.tooltip=private +icon.nodes.c_protected.tooltip=protected +icon.nodes.c_public.tooltip=public +icon.nodes.class.tooltip=class +icon.nodes.classInitializer.tooltip=class initializer +icon.nodes.enum.tooltip=enum +icon.nodes.excludedFromCompile.tooltip=excluded from compilation +icon.nodes.field.tooltip=field +icon.nodes.finalMark.tooltip=final +icon.nodes.folder.tooltip=folder +icon.nodes.ideaModule.tooltip=IntelliJ module file +icon.nodes.interface.tooltip=interface +icon.nodes.junitTestMark.tooltip=unit test +icon.nodes.locked.tooltip=read-only +icon.nodes.method.tooltip=method +icon.nodes.Module.tooltip=module +icon.nodes.moduleGroup.tooltip=module group +icon.nodes.package.tooltip=package +icon.nodes.ppJdk.tooltip=JDK +icon.nodes.ppLib.tooltip=external library +icon.nodes.runnableMark.tooltip=runnable +icon.nodes.staticMark.tooltip=static diff --git a/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java b/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java index 84ad3022c8ec..10fa3d09cf9d 100644 --- a/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java +++ b/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java @@ -8,6 +8,7 @@ import com.intellij.openapi.ui.GraphicsConfig; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.NlsContexts; import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.paint.EffectPainter; import com.intellij.ui.scale.JBUIScale; @@ -24,6 +25,7 @@ import javax.swing.*; import javax.swing.border.Border; import javax.swing.tree.TreeCellRenderer; import java.awt.*; +import java.awt.event.MouseEvent; import java.awt.font.FontRenderContext; import java.awt.font.TextAttribute; import java.awt.font.TextLayout; @@ -1078,6 +1080,17 @@ public class SimpleColoredComponent extends JComponent implements Accessible, Co } } + @Override + public String getToolTipText(MouseEvent event) { + if (myIcon instanceof IconWithToolTip && findFragmentAt(event.getX()) == FRAGMENT_ICON && Registry.is("ide.icon.tooltips")) { + String iconToolTip = ((IconWithToolTip)myIcon).getToolTip(false); + if (iconToolTip != null) { + return iconToolTip; + } + } + return super.getToolTipText(event); + } + @Override public AccessibleContext getAccessibleContext() { if (accessibleContext == null) { diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index e545a643bd16..f112968f86c5 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -1276,6 +1276,8 @@ + + diff --git a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml index 964ab30794ad..73423a1a3f44 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml @@ -380,5 +380,6 @@ + diff --git a/platform/platform-tests/testSrc/com/intellij/ui/IconUtilTest.java b/platform/platform-tests/testSrc/com/intellij/ui/IconUtilTest.java index 278eb7f0fb7a..ab584395c98c 100644 --- a/platform/platform-tests/testSrc/com/intellij/ui/IconUtilTest.java +++ b/platform/platform-tests/testSrc/com/intellij/ui/IconUtilTest.java @@ -281,7 +281,7 @@ public class IconUtilTest extends HeavyPlatformTestCase { UIUtil.dispatchAllInvocationEvents(); List icons = autopsyIconsFrom(icon); - assertOneElement(ContainerUtil.filter(icons, ic -> ic == PlatformIcons.LOCKED_ICON)); + assertOneElement(ContainerUtil.filter(icons, ic -> ic == IconTestUtil.unwrapRetrievableIcon(PlatformIcons.LOCKED_ICON))); } finally { WriteCommandAction.runWriteCommandAction(getProject(), diff --git a/platform/testFramework/src/com/intellij/ui/IconTestUtil.java b/platform/testFramework/src/com/intellij/ui/IconTestUtil.java new file mode 100644 index 000000000000..7f94ef702292 --- /dev/null +++ b/platform/testFramework/src/com/intellij/ui/IconTestUtil.java @@ -0,0 +1,22 @@ +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.ui; + +import com.intellij.openapi.util.IconLoader; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +public class IconTestUtil { + @Nullable + public static String getIconPath(Icon icon) { + icon = unwrapRetrievableIcon(icon); + return ((IconLoader.CachedImageIcon)icon).getOriginalPath(); + } + + public static Icon unwrapRetrievableIcon(Icon icon) { + while (icon instanceof RetrievableIcon) { + icon = ((RetrievableIcon)icon).retrieveIcon(); + } + return icon; + } +} diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 9dddf0b8cb40..16c6bff8766d 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -1805,4 +1805,10 @@ indexing.progress.indicator.power.description=Indexing progress indicator speed- ide.background.tasks=true ide.background.tasks.description=Start backgroundable tasks in background without showing modal dialog with progress bar +ide.icon.tooltips=true +ide.icon.tooltips.description=Show tooltips on icons explaining their meaning +ide.icon.tooltips.trace.missing=false +ide.icon.tooltips.trace.missing.description=Log a message when an icon tooltip is requested but isn't available + + # TODO please use EP com.intellij.registryKey for plugin/product specific keys \ No newline at end of file