mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IconManager.getIcon should return the same instance for the same input args as soon as cache is valid (not invalidated due time- or size- or access- eviction policy)
GitOrigin-RevId: 1a39cd16087772d27eb42a5f8c76b852159e8656
This commit is contained in:
committed by
intellij-monorepo-bot
parent
afdaf033e5
commit
c82f50d8df
@@ -1,4 +1,6 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:Suppress("LiftReturnOrAssignment")
|
||||
|
||||
package com.intellij.ui.icons
|
||||
|
||||
import com.intellij.AbstractBundle
|
||||
@@ -8,7 +10,9 @@ import com.intellij.icons.AllIcons
|
||||
import com.intellij.ide.IconLayerProvider
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.*
|
||||
import com.intellij.openapi.util.Iconable
|
||||
import com.intellij.openapi.util.findIconUsingDeprecatedImplementation
|
||||
import com.intellij.openapi.util.findIconUsingNewImplementation
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.ui.*
|
||||
@@ -27,6 +31,7 @@ import java.util.function.Function
|
||||
import java.util.function.Supplier
|
||||
import javax.swing.Icon
|
||||
|
||||
@Suppress("DeprecatedCallableAddReplaceWith")
|
||||
@ApiStatus.Internal
|
||||
class CoreIconManager : IconManager, CoreAwareIconManager {
|
||||
override fun getPlatformIcon(id: PlatformIcons): Icon {
|
||||
@@ -88,19 +93,25 @@ class CoreIconManager : IconManager, CoreAwareIconManager {
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Use getIcon(path, classLoader)")
|
||||
override fun getIcon(path: String, aClass: Class<*>): Icon {
|
||||
val icon = IconLoader.getIcon(path, aClass)
|
||||
val tooltip = IconDescriptionLoader(path)
|
||||
return if (icon is ScalableIcon) ScalableIconWrapperWithToolTip(icon, tooltip) else IconWrapperWithToolTip(
|
||||
icon, tooltip)
|
||||
return findIconUsingDeprecatedImplementation(originalPath = path,
|
||||
classLoader = aClass.classLoader,
|
||||
aClass = aClass,
|
||||
toolTip = IconDescriptionLoader(path))!!
|
||||
}
|
||||
|
||||
override fun getIcon(path: String, classLoader: ClassLoader): Icon {
|
||||
return findIconUsingNewImplementation(path = path, classLoader = classLoader, toolTip = IconDescriptionLoader(path))!!
|
||||
}
|
||||
|
||||
override fun loadRasterizedIcon(path: String, classLoader: ClassLoader, cacheKey: Int, flags: Int): Icon {
|
||||
assert(!path.isEmpty() && path[0] != '/')
|
||||
return IconWithToolTipImpl(originalPath = path, resolver = createRasterizedImageDataLoader(path = path,
|
||||
classLoader = classLoader,
|
||||
cacheKey = cacheKey,
|
||||
imageFlags = flags))
|
||||
assert(!path.startsWith('/'))
|
||||
return CachedImageIcon(
|
||||
originalPath = path,
|
||||
resolver = createRasterizedImageDataLoader(path = path, classLoader = classLoader, cacheKey = cacheKey, imageFlags = flags),
|
||||
toolTip = IconDescriptionLoader(path),
|
||||
)
|
||||
}
|
||||
|
||||
override fun createEmptyIcon(icon: Icon): Icon = EmptyIcon.create(icon)
|
||||
@@ -170,22 +181,6 @@ class CoreIconManager : IconManager, CoreAwareIconManager {
|
||||
override fun withIconBadge(icon: Icon, color: Paint): Icon = BadgeIcon(icon, color)
|
||||
}
|
||||
|
||||
private class IconWithToolTipImpl(originalPath: String,
|
||||
resolver: ImageDataLoader) : CachedImageIcon(originalPath = originalPath,
|
||||
resolver = resolver), IconWithToolTip {
|
||||
@NlsSafe
|
||||
private var result: String? = null
|
||||
private var isTooltipCalculated = false
|
||||
|
||||
override fun getToolTip(composite: Boolean): @NlsContexts.Tooltip String? {
|
||||
if (!isTooltipCalculated) {
|
||||
result = findIconDescription(originalPath!!)
|
||||
isTooltipCalculated = true
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
private class IconLayer(@JvmField val flagMask: Int, @JvmField val icon: Icon) {
|
||||
init {
|
||||
BitUtil.assertOneBitMask(flagMask)
|
||||
@@ -211,7 +206,7 @@ private const val FLAGS_LOCKED = 0x800
|
||||
// a reflective path is not supported, a result is not cached
|
||||
private fun createRasterizedImageDataLoader(path: String, classLoader: ClassLoader, cacheKey: Int, imageFlags: Int): ImageDataLoader {
|
||||
val startTime = StartUpMeasurer.getCurrentTimeIfEnabled()
|
||||
val patchedPath = IconLoader.patchPath(path, classLoader)
|
||||
val patchedPath = CachedImageIcon.patchPath(originalPath = path, classLoader = classLoader)
|
||||
val classLoaderWeakRef = WeakReference(classLoader)
|
||||
val resolver = if (patchedPath == null) {
|
||||
RasterizedImageDataLoader(path = path,
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.ui.icons;
|
||||
|
||||
import com.intellij.openapi.util.NlsContexts;
|
||||
import com.intellij.openapi.util.ScalableIcon;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
final class ScalableIconWrapperWithToolTip extends IconWrapperWithToolTip implements ScalableIcon {
|
||||
ScalableIconWrapperWithToolTip(@SuppressWarnings("TypeMayBeWeakened") @NotNull ScalableIcon icon, @NotNull Supplier<@NlsContexts.Tooltip String> toolTip) {
|
||||
super(icon, toolTip);
|
||||
}
|
||||
|
||||
private ScalableIconWrapperWithToolTip(IconWrapperWithToolTip another) {
|
||||
super(another);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getScale() {
|
||||
return ((ScalableIcon)retrieveIcon()).getScale();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Icon scale(float scaleFactor) {
|
||||
return ((ScalableIcon)retrieveIcon()).scale(scaleFactor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ScalableIconWrapperWithToolTip replaceBy(@NotNull IconReplacer replacer) {
|
||||
return new ScalableIconWrapperWithToolTip(super.replaceBy(replacer));
|
||||
}
|
||||
}
|
||||
@@ -600,7 +600,7 @@ public final class UITheme {
|
||||
}
|
||||
|
||||
if (value.endsWith(".png") || value.endsWith(".svg")) {
|
||||
Icon icon = ImageDataByPathLoader.Companion.findIcon(value, classLoader, null);
|
||||
Icon icon = ImageDataByPathLoader.Companion.findIconByPath(value, classLoader, null, null);
|
||||
if (icon != null) {
|
||||
return icon;
|
||||
}
|
||||
|
||||
@@ -52,8 +52,11 @@ interface IconManager {
|
||||
|
||||
fun getPlatformIcon(id: PlatformIcons): Icon
|
||||
|
||||
@Deprecated("Use getIcon(path, classLoader)")
|
||||
fun getIcon(path: String, aClass: Class<*>): Icon
|
||||
|
||||
fun getIcon(path: String, classLoader: ClassLoader): Icon
|
||||
|
||||
/**
|
||||
* Path must be specified without a leading slash, in a format for [ClassLoader.getResourceAsStream]
|
||||
*/
|
||||
@@ -98,6 +101,8 @@ private object DummyIconManager : IconManager {
|
||||
|
||||
override fun getIcon(path: String, aClass: Class<*>): Icon = DummyIconImpl(path)
|
||||
|
||||
override fun getIcon(path: String, classLoader: ClassLoader): Icon = DummyIconImpl(path)
|
||||
|
||||
override fun loadRasterizedIcon(path: String, classLoader: ClassLoader, cacheKey: Int, flags: Int): Icon = DummyIconImpl(path)
|
||||
|
||||
override fun createLayeredIcon(instance: Iconable, icon: Icon, flags: Int): RowIcon {
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:Suppress("ReplaceGetOrSet", "ReplacePutWithAssignment", "DeprecatedCallableAddReplaceWith")
|
||||
@file:Suppress("ReplaceGetOrSet", "ReplacePutWithAssignment", "DeprecatedCallableAddReplaceWith", "LiftReturnOrAssignment")
|
||||
|
||||
package com.intellij.openapi.util
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine
|
||||
import com.intellij.diagnostic.StartUpMeasurer
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.openapi.util.IconLoader.getReflectiveIcon
|
||||
import com.intellij.openapi.util.IconLoader.isReflectivePath
|
||||
import com.intellij.openapi.util.IconLoader.patchPath
|
||||
import com.intellij.ui.*
|
||||
import com.intellij.ui.icons.*
|
||||
import com.intellij.ui.icons.CachedImageIcon.Companion.pathTransform
|
||||
import com.intellij.ui.icons.CachedImageIcon.Companion.pathTransformGlobalModCount
|
||||
import com.intellij.ui.icons.ImageDataByPathLoader.Companion.findIcon
|
||||
import com.intellij.ui.paint.PaintUtil
|
||||
import com.intellij.ui.scale.DerivedScaleType
|
||||
import com.intellij.ui.scale.JBUIScale.sysScale
|
||||
@@ -25,7 +22,7 @@ import com.intellij.util.*
|
||||
import com.intellij.util.SVGLoader.SvgElementColorPatcherProvider
|
||||
import com.intellij.util.containers.CollectionFactory
|
||||
import com.intellij.util.ui.*
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
import org.jetbrains.annotations.NonNls
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.xxh3.Xxh3
|
||||
@@ -38,6 +35,7 @@ import java.lang.ref.WeakReference
|
||||
import java.net.MalformedURLException
|
||||
import java.net.URL
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.function.Function
|
||||
import java.util.function.Supplier
|
||||
import javax.swing.Icon
|
||||
@@ -50,7 +48,10 @@ private val LOG: Logger
|
||||
|
||||
private val LOOKUP = MethodHandles.lookup()
|
||||
|
||||
private val iconCache = ConcurrentHashMap<Pair<String, ClassLoader?>, CachedImageIcon>(100, 0.9f, 2)
|
||||
private val iconCache = Caffeine.newBuilder()
|
||||
.expireAfterAccess(1, TimeUnit.HOURS)
|
||||
.maximumSize(256)
|
||||
.build<Pair<String, ClassLoader?>, CachedImageIcon>()
|
||||
|
||||
// contains mapping between icons and disabled icons
|
||||
private val iconToDisabledIcon = ConcurrentHashMap<() -> RGBImageFilter, MutableMap<Icon, Icon>>()
|
||||
@@ -122,10 +123,10 @@ object IconLoader {
|
||||
@TestOnly
|
||||
@JvmStatic
|
||||
fun clearCacheInTests() {
|
||||
iconCache.clear()
|
||||
iconCache.invalidateAll()
|
||||
iconToDisabledIcon.clear()
|
||||
clearImageCache()
|
||||
pathTransformGlobalModCount.incrementAndGet()
|
||||
com.intellij.ui.icons.CachedImageIcon.pathTransformGlobalModCount.incrementAndGet()
|
||||
}
|
||||
|
||||
@Deprecated("Use {@link #getIcon(String, ClassLoader)}", level = DeprecationLevel.ERROR)
|
||||
@@ -136,7 +137,7 @@ object IconLoader {
|
||||
|
||||
@JvmStatic
|
||||
fun getReflectiveIcon(path: String, classLoader: ClassLoader): Icon? {
|
||||
return try {
|
||||
try {
|
||||
var dotIndex = path.lastIndexOf('.')
|
||||
val fieldName = path.substring(dotIndex + 1)
|
||||
val builder = StringBuilder(path.length + 20)
|
||||
@@ -161,11 +162,11 @@ object IconLoader {
|
||||
builder.insert(0, if (path.startsWith("AllIcons.")) "com.intellij.icons." else "icons.")
|
||||
}
|
||||
val aClass = classLoader.loadClass(builder.toString())
|
||||
LOOKUP.findStaticGetter(aClass, fieldName, Icon::class.java).invoke() as Icon
|
||||
return LOOKUP.findStaticGetter(aClass, fieldName, Icon::class.java).invoke() as Icon
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
LOG.warn("Cannot get reflective icon (path=$path)", e)
|
||||
null
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +179,7 @@ object IconLoader {
|
||||
|
||||
@JvmStatic
|
||||
fun getIcon(path: String, aClass: Class<*>): Icon {
|
||||
return findIcon(originalPath = path, aClass = aClass, classLoader = aClass.classLoader, handleNotFound = null, deferUrlResolve = true)
|
||||
return findIcon(originalPath = path, aClass = aClass, classLoader = aClass.classLoader, deferUrlResolve = true)
|
||||
?: throw IllegalStateException("Icon cannot be found in '$path', class='${aClass.name}'")
|
||||
}
|
||||
|
||||
@@ -227,16 +228,12 @@ object IconLoader {
|
||||
*/
|
||||
@JvmStatic
|
||||
fun findIcon(path: String, aClass: Class<*>): Icon? {
|
||||
return findIcon(originalPath = path, originalClassLoader = aClass.classLoader, cache = iconCache)
|
||||
return findIconUsingNewImplementation(path, aClass.classLoader)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun findIcon(path: String, aClass: Class<*>, deferUrlResolve: Boolean, strict: Boolean): Icon? {
|
||||
return findIcon(originalPath = path,
|
||||
aClass = aClass,
|
||||
classLoader = aClass.classLoader,
|
||||
handleNotFound = if (strict) HandleNotFound.THROW_EXCEPTION else HandleNotFound.IGNORE,
|
||||
deferUrlResolve = deferUrlResolve)
|
||||
return findIcon(originalPath = path, aClass = aClass, classLoader = aClass.classLoader, strict, deferUrlResolve = deferUrlResolve)
|
||||
}
|
||||
|
||||
fun isReflectivePath(path: String): Boolean {
|
||||
@@ -251,27 +248,22 @@ object IconLoader {
|
||||
}
|
||||
|
||||
val key = Pair<String, ClassLoader?>(url.toString(), null)
|
||||
return if (storeToCache) {
|
||||
iconCache.computeIfAbsent(key) { CachedImageIcon(url = url, useCacheOnLoad = true) }
|
||||
if (storeToCache) {
|
||||
return iconCache.get(key) { CachedImageIcon(url = url, useCacheOnLoad = true) }
|
||||
}
|
||||
else {
|
||||
iconCache.get(key) ?: CachedImageIcon(url = url, useCacheOnLoad = false)
|
||||
return iconCache.getIfPresent(key) ?: CachedImageIcon(url = url, useCacheOnLoad = false)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun patchPath(originalPath: String, classLoader: ClassLoader): Pair<String, ClassLoader>? {
|
||||
return pathTransform.get().patchPath(originalPath, classLoader)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun findIcon(path: String, classLoader: ClassLoader): Icon? {
|
||||
return findIcon(originalPath = path, originalClassLoader = classLoader, cache = iconCache)
|
||||
return findIconUsingNewImplementation(path, classLoader)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun findResolvedIcon(path: String, classLoader: ClassLoader): Icon? {
|
||||
val icon = findIcon(originalPath = path, originalClassLoader = classLoader, cache = iconCache)
|
||||
val icon = findIconUsingNewImplementation(path, classLoader)
|
||||
return if (icon is com.intellij.ui.icons.CachedImageIcon && icon.getRealIcon() === com.intellij.ui.icons.CachedImageIcon.EMPTY_ICON) null else icon
|
||||
}
|
||||
|
||||
@@ -345,7 +337,7 @@ object IconLoader {
|
||||
return getDisabledIcon(icon = icon, disableFilter = null)
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Internal
|
||||
fun getDisabledIcon(icon: Icon, disableFilter: (() -> RGBImageFilter)?): Icon {
|
||||
if (!com.intellij.ui.icons.CachedImageIcon.isActivated) {
|
||||
return icon
|
||||
@@ -365,7 +357,7 @@ object IconLoader {
|
||||
return replaceCachedImageIcons(icon) { patchColorsInCacheImageIcon(imageIcon = it, colorPatcher = colorPatcher, isDark = null) }!!
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Internal
|
||||
fun patchColorsInCacheImageIcon(imageIcon: com.intellij.ui.icons.CachedImageIcon, colorPatcher: SvgElementColorPatcherProvider, isDark: Boolean?): Icon {
|
||||
var result = imageIcon
|
||||
if (isDark != null) {
|
||||
@@ -402,7 +394,7 @@ object IconLoader {
|
||||
/**
|
||||
* Creates a new icon with the low-level CachedImageIcon changing
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
@Internal
|
||||
fun replaceCachedImageIcons(icon: Icon, cachedImageIconReplacer: (com.intellij.ui.icons.CachedImageIcon) -> Icon): Icon? {
|
||||
val replacer: IconReplacer = object : IconReplacer {
|
||||
override fun replaceIcon(icon: Icon?): Icon? {
|
||||
@@ -530,7 +522,7 @@ object IconLoader {
|
||||
}
|
||||
|
||||
fun detachClassLoader(classLoader: ClassLoader) {
|
||||
iconCache.entries.removeIf { (key, icon): Map.Entry<Pair<String, ClassLoader?>, com.intellij.ui.icons.CachedImageIcon> ->
|
||||
iconCache.asMap().entries.removeIf { (key, icon) ->
|
||||
icon.detachClassLoader(classLoader) || key.second === classLoader
|
||||
}
|
||||
}
|
||||
@@ -585,11 +577,11 @@ private fun updateTransform(updater: Function<in IconTransform, IconTransform>)
|
||||
var prev: IconTransform
|
||||
var next: IconTransform
|
||||
do {
|
||||
prev = pathTransform.get()
|
||||
prev = CachedImageIcon.pathTransform.get()
|
||||
next = updater.apply(prev)
|
||||
}
|
||||
while (!pathTransform.compareAndSet(prev, next))
|
||||
pathTransformGlobalModCount.incrementAndGet()
|
||||
while (!CachedImageIcon.pathTransform.compareAndSet(prev, next))
|
||||
CachedImageIcon.pathTransformGlobalModCount.incrementAndGet()
|
||||
if (prev != next) {
|
||||
iconToDisabledIcon.clear()
|
||||
colorPatchCache.clear()
|
||||
@@ -604,15 +596,33 @@ private fun updateTransform(updater: Function<in IconTransform, IconTransform>)
|
||||
private fun findIcon(originalPath: String,
|
||||
aClass: Class<*>?,
|
||||
classLoader: ClassLoader,
|
||||
handleNotFound: HandleNotFound?,
|
||||
strict: Boolean = STRICT_LOCAL.get(),
|
||||
deferUrlResolve: Boolean): Icon? {
|
||||
var effectiveClassLoader = classLoader
|
||||
if (!deferUrlResolve) {
|
||||
return findIcon(originalPath = originalPath, originalClassLoader = effectiveClassLoader, cache = iconCache)
|
||||
if (deferUrlResolve) {
|
||||
return findIconUsingDeprecatedImplementation(originalPath = originalPath,
|
||||
classLoader = classLoader,
|
||||
aClass = aClass,
|
||||
strict = strict)
|
||||
}
|
||||
else {
|
||||
return findIconUsingNewImplementation(originalPath, classLoader)
|
||||
}
|
||||
}
|
||||
|
||||
@Internal
|
||||
fun findIconUsingNewImplementation(path: String, classLoader: ClassLoader, toolTip: Supplier<String?>? = null): Icon? {
|
||||
return ImageDataByPathLoader.findIconByPath(path = path, classLoader = classLoader, cache = iconCache.asMap(), toolTip = toolTip)
|
||||
}
|
||||
|
||||
@Internal
|
||||
fun findIconUsingDeprecatedImplementation(originalPath: String,
|
||||
classLoader: ClassLoader,
|
||||
aClass: Class<*>?,
|
||||
toolTip: Supplier<String?>? = null,
|
||||
strict: Boolean = STRICT_LOCAL.get()): Icon? {
|
||||
var effectiveClassLoader = classLoader
|
||||
val startTime = StartUpMeasurer.getCurrentTimeIfEnabled()
|
||||
val patchedPath = patchPath(originalPath = originalPath, classLoader = effectiveClassLoader)
|
||||
val patchedPath = CachedImageIcon.patchPath(originalPath = originalPath, classLoader = effectiveClassLoader)
|
||||
val path = patchedPath?.first ?: originalPath
|
||||
if (patchedPath?.second != null) {
|
||||
effectiveClassLoader = patchedPath.second
|
||||
@@ -624,16 +634,11 @@ private fun findIcon(originalPath: String,
|
||||
}
|
||||
else {
|
||||
val key = Pair(originalPath, effectiveClassLoader)
|
||||
var cachedIcon = iconCache.get(key)
|
||||
var cachedIcon = iconCache.getIfPresent(key)
|
||||
if (cachedIcon == null) {
|
||||
cachedIcon = iconCache.computeIfAbsent(key) { k ->
|
||||
val effectiveHandleNotFound = handleNotFound
|
||||
?: if (STRICT_LOCAL.get()) HandleNotFound.THROW_EXCEPTION else HandleNotFound.IGNORE
|
||||
val resolver = ImageDataByPathResourceLoader(path = path,
|
||||
ownerClass = aClass,
|
||||
classLoader = k.second,
|
||||
handleNotFound = effectiveHandleNotFound)
|
||||
CachedImageIcon(originalPath = originalPath, resolver = resolver)
|
||||
cachedIcon = iconCache.get(key) { k ->
|
||||
val resolver = ImageDataByPathResourceLoader(path = path, ownerClass = aClass, classLoader = k.second, strict = strict)
|
||||
CachedImageIcon(originalPath = originalPath, resolver = resolver, toolTip = toolTip)
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -658,7 +663,7 @@ private abstract class LazyIcon : ScaleContextSupport(), CopyableIcon, Retrievab
|
||||
@Volatile
|
||||
private var icon: Icon? = null
|
||||
|
||||
private var transformModCount = pathTransformGlobalModCount.get()
|
||||
private var transformModCount = CachedImageIcon.pathTransformGlobalModCount.get()
|
||||
|
||||
override fun isComplex(): Boolean = true
|
||||
|
||||
@@ -677,7 +682,7 @@ private abstract class LazyIcon : ScaleContextSupport(), CopyableIcon, Retrievab
|
||||
@Synchronized
|
||||
fun getOrComputeIcon(): Icon {
|
||||
var icon = icon
|
||||
val newTransformModCount = pathTransformGlobalModCount.get()
|
||||
val newTransformModCount = CachedImageIcon.pathTransformGlobalModCount.get()
|
||||
if (icon != null && wasComputed && transformModCount == newTransformModCount) {
|
||||
return icon
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.net.URL
|
||||
import java.util.concurrent.ConcurrentMap
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import java.util.function.Supplier
|
||||
import javax.swing.Icon
|
||||
import javax.swing.ImageIcon
|
||||
|
||||
@@ -37,7 +38,9 @@ open class CachedImageIcon protected constructor(
|
||||
private val localFilterSupplier: (() -> RGBImageFilter)? = null,
|
||||
private val colorPatcher: SVGLoader.SvgElementColorPatcherProvider? = null,
|
||||
private val useStroke: Boolean = false,
|
||||
) : AbstractScaleContextAware<ScaleContext>(ScaleContext.create()), CopyableIcon, ScalableIcon, DarkIconProvider, MenuBarIconProvider {
|
||||
private val toolTip: Supplier<String?>? = null,
|
||||
) : AbstractScaleContextAware<ScaleContext>(ScaleContext.create()), CopyableIcon, ScalableIcon, DarkIconProvider, MenuBarIconProvider,
|
||||
IconWithToolTip {
|
||||
companion object {
|
||||
@JvmField
|
||||
internal var isActivated: Boolean = !GraphicsEnvironment.isHeadless()
|
||||
@@ -45,6 +48,10 @@ open class CachedImageIcon protected constructor(
|
||||
@JvmField
|
||||
internal val pathTransformGlobalModCount: AtomicInteger = AtomicInteger()
|
||||
|
||||
fun patchPath(originalPath: String, classLoader: ClassLoader): Pair<String, ClassLoader>? {
|
||||
return pathTransform.get().patchPath(originalPath, classLoader)
|
||||
}
|
||||
|
||||
@JvmField
|
||||
internal val pathTransform: AtomicReference<IconTransform> = AtomicReference(
|
||||
IconTransform(StartupUiUtil.isUnderDarcula(), arrayOf<IconPathPatcher>(DeprecatedDuplicatesIconPathPatcher()), null)
|
||||
@@ -84,10 +91,8 @@ open class CachedImageIcon protected constructor(
|
||||
pathTransformModCount = pathTransformGlobalModCount.get()
|
||||
}
|
||||
|
||||
constructor(originalPath: String?, resolver: ImageDataLoader?) : this(originalPath = originalPath,
|
||||
resolver = resolver,
|
||||
isDarkOverridden = null,
|
||||
colorPatcher = null)
|
||||
constructor(originalPath: String?, resolver: ImageDataLoader?, toolTip: Supplier<String?>?) :
|
||||
this(originalPath = originalPath, resolver = resolver, isDarkOverridden = null, colorPatcher = null, toolTip = toolTip)
|
||||
|
||||
init {
|
||||
// for instance, ShadowPainter updates the context from and outside
|
||||
@@ -96,6 +101,8 @@ open class CachedImageIcon protected constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getToolTip(composite: Boolean): String? = toolTip?.get()
|
||||
|
||||
override fun paintIcon(c: Component?, g: Graphics, x: Int, y: Int) {
|
||||
getRealIcon(ScaleContext.create(if (g is Graphics2D) g else null)).paintIcon(c, g, x, y)
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.ui.icons;
|
||||
|
||||
import com.intellij.openapi.util.NlsContexts;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -15,5 +14,5 @@ public interface IconWithToolTip extends 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.
|
||||
*/
|
||||
@NlsContexts.Tooltip @Nullable String getToolTip(boolean composite);
|
||||
@Nullable String getToolTip(boolean composite);
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.NonNls
|
||||
import java.awt.Image
|
||||
import java.net.URL
|
||||
import java.util.function.Supplier
|
||||
import javax.swing.Icon
|
||||
|
||||
@ApiStatus.Internal
|
||||
@@ -16,31 +17,32 @@ class ImageDataByPathLoader private constructor(private val path: String,
|
||||
private val classLoader: ClassLoader,
|
||||
private val original: ImageDataByPathLoader?) : ImageDataLoader {
|
||||
companion object {
|
||||
// cache is not used - image data resolved using cache in any case
|
||||
fun findIcon(@NonNls originalPath: String,
|
||||
originalClassLoader: ClassLoader,
|
||||
cache: MutableMap<Pair<String, ClassLoader?>, CachedImageIcon>?): Icon? {
|
||||
fun findIconByPath(@NonNls path: String,
|
||||
classLoader: ClassLoader,
|
||||
cache: MutableMap<Pair<String, ClassLoader?>, CachedImageIcon>?,
|
||||
toolTip: Supplier<String?>? = null): Icon? {
|
||||
val startTime = StartUpMeasurer.getCurrentTimeIfEnabled()
|
||||
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val originalPath = normalizePath(originalPath)
|
||||
val patched = IconLoader.patchPath(originalPath, originalClassLoader)
|
||||
val path = patched?.first ?: originalPath
|
||||
val classLoader = patched?.second ?: originalClassLoader
|
||||
val originalPath = normalizePath(path)
|
||||
val patched = CachedImageIcon.patchPath(originalPath, classLoader)
|
||||
val effectivePath = patched?.first ?: originalPath
|
||||
val effectiveClassLoader = patched?.second ?: classLoader
|
||||
val icon: Icon? = when {
|
||||
IconLoader.isReflectivePath(path) -> IconLoader.getReflectiveIcon(path, classLoader)
|
||||
IconLoader.isReflectivePath(effectivePath) -> IconLoader.getReflectiveIcon(effectivePath, effectiveClassLoader)
|
||||
cache == null -> createIcon(originalPath = originalPath,
|
||||
originalClassLoader = originalClassLoader,
|
||||
originalClassLoader = effectiveClassLoader,
|
||||
patched = patched,
|
||||
path = path,
|
||||
classLoader = classLoader)
|
||||
path = effectivePath,
|
||||
classLoader = effectiveClassLoader,
|
||||
toolTip = toolTip)
|
||||
else -> {
|
||||
cache.computeIfAbsent(Pair(originalPath, originalClassLoader)) {
|
||||
cache.computeIfAbsent(Pair(originalPath, effectiveClassLoader)) {
|
||||
createIcon(originalPath = it.first,
|
||||
originalClassLoader = it.second!!,
|
||||
patched = patched,
|
||||
path = path,
|
||||
classLoader = classLoader)
|
||||
path = effectivePath,
|
||||
classLoader = effectiveClassLoader,
|
||||
toolTip = toolTip)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,10 +56,11 @@ class ImageDataByPathLoader private constructor(private val path: String,
|
||||
originalClassLoader: ClassLoader,
|
||||
patched: Pair<String, ClassLoader?>?,
|
||||
path: String,
|
||||
classLoader: ClassLoader): CachedImageIcon {
|
||||
val loader = ImageDataByPathLoader(originalPath, originalClassLoader, null)
|
||||
val resolver = if (patched == null) loader else ImageDataByPathLoader(path, classLoader, loader)
|
||||
return CachedImageIcon(originalPath = null, resolver = resolver)
|
||||
classLoader: ClassLoader,
|
||||
toolTip: Supplier<String?>? = null): CachedImageIcon {
|
||||
val loader = ImageDataByPathLoader(path = originalPath, classLoader = originalClassLoader, original = null)
|
||||
val resolver = if (patched == null) loader else ImageDataByPathLoader(path = path, classLoader = classLoader, original = loader)
|
||||
return CachedImageIcon(originalPath = null, resolver = resolver, toolTip = toolTip)
|
||||
}
|
||||
|
||||
private fun normalizePath(patchedPath: String): String {
|
||||
|
||||
@@ -11,18 +11,6 @@ import java.net.URL
|
||||
|
||||
private val UNRESOLVED_URL = URL("file:///unresolved")
|
||||
|
||||
internal enum class HandleNotFound {
|
||||
THROW_EXCEPTION {
|
||||
override fun handle(message: String) {
|
||||
throw RuntimeException(message)
|
||||
}
|
||||
},
|
||||
IGNORE;
|
||||
|
||||
open fun handle(message: String) {
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
class ImageDataByUrlLoader private constructor(
|
||||
private val ownerClass: Class<*>? = null,
|
||||
@@ -65,7 +53,7 @@ class ImageDataByUrlLoader private constructor(
|
||||
internal class ImageDataByPathResourceLoader(
|
||||
private val ownerClass: Class<*>? = null,
|
||||
private val classLoader: ClassLoader? = null,
|
||||
private val handleNotFound: HandleNotFound,
|
||||
private val strict: Boolean,
|
||||
private val path: String,
|
||||
) : ImageDataLoader {
|
||||
@Volatile
|
||||
@@ -74,7 +62,7 @@ internal class ImageDataByPathResourceLoader(
|
||||
var result = field
|
||||
if (result === UNRESOLVED_URL) {
|
||||
result = try {
|
||||
resolveUrl(path = path, classLoader = classLoader, ownerClass = ownerClass, handleNotFound = handleNotFound)
|
||||
resolveUrl(path = path, classLoader = classLoader, ownerClass = ownerClass, strict = strict)
|
||||
}
|
||||
finally {
|
||||
field = result
|
||||
@@ -106,7 +94,7 @@ internal class ImageDataByPathResourceLoader(
|
||||
private fun resolveUrl(path: String?,
|
||||
classLoader: ClassLoader?,
|
||||
ownerClass: Class<*>?,
|
||||
handleNotFound: HandleNotFound): URL? {
|
||||
strict: Boolean): URL? {
|
||||
var effectivePath = path
|
||||
var url: URL? = null
|
||||
if (effectivePath != null) {
|
||||
@@ -120,8 +108,8 @@ private fun resolveUrl(path: String?,
|
||||
url = findUrl(path = effectivePath, urlProvider = ownerClass::getResource)
|
||||
}
|
||||
}
|
||||
if (url == null) {
|
||||
handleNotFound.handle("Can't find icon in '$effectivePath' near $classLoader")
|
||||
if (url == null && strict) {
|
||||
throw RuntimeException("Can't find icon in '$effectivePath' near $classLoader")
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user