diff --git a/platform/platform-impl/src/com/intellij/ide/ProjectWindowCustomizerService.kt b/platform/platform-impl/src/com/intellij/ide/ProjectWindowCustomizerService.kt index 3d808f8130da..a2a75db24849 100644 --- a/platform/platform-impl/src/com/intellij/ide/ProjectWindowCustomizerService.kt +++ b/platform/platform-impl/src/com/intellij/ide/ProjectWindowCustomizerService.kt @@ -14,6 +14,7 @@ import com.intellij.openapi.components.serviceAsync import com.intellij.openapi.components.serviceIfCreated import com.intellij.openapi.extensions.ExtensionNotApplicableException import com.intellij.openapi.project.Project +import com.intellij.openapi.project.ProjectNameListener import com.intellij.openapi.project.ex.ProjectManagerEx import com.intellij.openapi.startup.ProjectActivity import com.intellij.openapi.util.registry.Registry @@ -47,11 +48,16 @@ private class ProjectWindowCustomizerIconCache(private val project: Project) { busConnection.subscribe(LafManagerListener.TOPIC, LafManagerListener { cachedIcon.drop() }) + busConnection.subscribe(ProjectNameListener.TOPIC, object: ProjectNameListener { + override fun nameChanged(newName: String) { + cachedIcon.drop() + } + }) } private fun getIconRaw(): Icon { val path = ProjectWindowCustomizerService.projectPath(project) ?: "" - return RecentProjectsManagerBase.getInstanceEx().getProjectIcon(path = path, isProjectValid = true, iconSize = 20) + return RecentProjectsManagerBase.getInstanceEx().getProjectIcon(path = path, isProjectValid = true, iconSize = 20, name = project.name) } } diff --git a/platform/platform-impl/src/com/intellij/ide/RecentProjectIconHelper.kt b/platform/platform-impl/src/com/intellij/ide/RecentProjectIconHelper.kt index 95281dc56892..237700a6f2c0 100644 --- a/platform/platform-impl/src/com/intellij/ide/RecentProjectIconHelper.kt +++ b/platform/platform-impl/src/com/intellij/ide/RecentProjectIconHelper.kt @@ -83,8 +83,8 @@ internal class RecentProjectIconHelper { } @JvmStatic - fun generateProjectIcon(path: @SystemIndependent String, isProjectValid: Boolean, size: Int = unscaledProjectIconSize()): Icon { - val generatedProjectIcon = generateProjectIcon(path, isProjectValid, size, null) + fun generateProjectIcon(path: @SystemIndependent String, isProjectValid: Boolean, size: Int = unscaledProjectIconSize(), projectName: String? = null): Icon { + val generatedProjectIcon = generateProjectIcon(path, isProjectValid, size, null, projectName) projectIconCache.put(Pair(path, size), ProjectIcon(icon = generatedProjectIcon, isProjectValid = isProjectValid, @@ -93,8 +93,8 @@ internal class RecentProjectIconHelper { return generatedProjectIcon } - fun generateProjectIcon(path: @SystemIndependent String, isProjectValid: Boolean, size: Int = unscaledProjectIconSize(), colorIndex: Int?): Icon { - val name = getProjectName(path, RecentProjectsManagerBase.getInstanceEx()) + fun generateProjectIcon(path: @SystemIndependent String, isProjectValid: Boolean, size: Int = unscaledProjectIconSize(), colorIndex: Int?, projectName: String? = null): Icon { + val name = projectName ?: getProjectName(path, RecentProjectsManagerBase.getInstanceEx()) val palette = if (colorIndex != null) ChangeProjectIconPalette(colorIndex) else ProjectIconPalette var generatedProjectIcon: Icon = AvatarIcon(targetSize = size, @@ -122,13 +122,13 @@ internal class RecentProjectIconHelper { } } - fun getProjectIcon(path: @SystemIndependent String, isProjectValid: Boolean = true, iconSize: Int = unscaledProjectIconSize()): Icon { + fun getProjectIcon(path: @SystemIndependent String, isProjectValid: Boolean = true, iconSize: Int = unscaledProjectIconSize(), name: String? = null): Icon { if (!RecentProjectsManagerBase.isFileSystemPath(path)) { return EmptyIcon.create(iconSize) } return IconDeferrer.getInstance().defer(EmptyIcon.create(iconSize), Triple(path, isProjectValid, iconSize)) { - getCustomIcon(path = it.first, isProjectValid = it.second, iconSize) ?: getGeneratedProjectIcon(path = it.first, isProjectValid = it.second, iconSize) + getCustomIcon(path = it.first, isProjectValid = it.second, iconSize) ?: getGeneratedProjectIcon(path = it.first, isProjectValid = it.second, iconSize, name) } } @@ -173,17 +173,21 @@ private fun getCustomIcon(path: @SystemIndependent String, isProjectValid: Boole return iconWrapper.icon } -private fun getGeneratedProjectIcon(path: @SystemIndependent String, isProjectValid: Boolean, size: Int = unscaledProjectIconSize()): Icon { +private fun getGeneratedProjectIcon(path: @SystemIndependent String, isProjectValid: Boolean, size: Int = unscaledProjectIconSize(), name: String? = null): Icon { val projectIcon = projectIconCache.get(Pair(path, size)) - if (projectIcon != null && isCachedIcon(projectIcon, isProjectValid)) { + if (projectIcon != null && isCachedIcon(projectIcon, isProjectValid, name = name)) { return projectIcon.icon } - return RecentProjectIconHelper.generateProjectIcon(path, isProjectValid, size) + return RecentProjectIconHelper.generateProjectIcon(path, isProjectValid, size, projectName = name) } -private fun isCachedIcon(icon: ProjectIcon, isProjectValid: Boolean, timestamp: Long? = null): Boolean { - val isCached = icon.isProjectValid == isProjectValid && icon.lastUsedProjectIconSize == userScaledProjectIconSize() - return if (timestamp == null) isCached else isCached && icon.timestamp == timestamp +private fun isCachedIcon(icon: ProjectIcon, isProjectValid: Boolean, timestamp: Long? = null, name: String? = null): Boolean { + val isNameChanged = (icon.icon as? AvatarIcon)?.avatarName?.let { it != name } ?: false + val isTimestampChanged = timestamp?.let { icon.timestamp != it } ?: false + + return icon.isProjectValid == isProjectValid + && icon.lastUsedProjectIconSize == userScaledProjectIconSize() + && !isNameChanged && !isTimestampChanged } private data class ProjectIcon( diff --git a/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.kt b/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.kt index dd86fd2669ab..96eeb4141c31 100644 --- a/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.kt +++ b/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.kt @@ -221,8 +221,8 @@ open class RecentProjectsManagerBase(coroutineScope: CoroutineScope) : return projectIconHelper.getProjectIcon(path, isProjectValid) } - fun getProjectIcon(path: String, isProjectValid: Boolean, iconSize: Int): Icon { - return projectIconHelper.getProjectIcon(path, isProjectValid, iconSize) + fun getProjectIcon(path: String, isProjectValid: Boolean, iconSize: Int, name: String? = null): Icon { + return projectIconHelper.getProjectIcon(path, isProjectValid, iconSize, name) } @Suppress("OVERRIDE_DEPRECATION") diff --git a/platform/platform-impl/src/com/intellij/openapi/project/ProjectNameListener.kt b/platform/platform-impl/src/com/intellij/openapi/project/ProjectNameListener.kt new file mode 100644 index 000000000000..db11a4471b86 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/project/ProjectNameListener.kt @@ -0,0 +1,17 @@ +// 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.openapi.project + +import com.intellij.util.messages.Topic +import java.util.* + +/** + * Represents a listener for name changes of Project + */ +interface ProjectNameListener: EventListener { + fun nameChanged(newName: String) + + companion object { + @Topic.ProjectLevel + val TOPIC = Topic(ProjectNameListener::class.java) + } +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.kt b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.kt index 31485b8615c3..2a6d319fe6eb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.kt +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.kt @@ -23,6 +23,7 @@ import com.intellij.openapi.diagnostic.debug import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.project.Project import com.intellij.openapi.project.ProjectManager +import com.intellij.openapi.project.ProjectNameListener import com.intellij.openapi.project.ex.ProjectEx import com.intellij.openapi.project.ex.ProjectManagerEx import com.intellij.openapi.startup.StartupManager @@ -175,7 +176,9 @@ open class ProjectImpl(parent: ComponentManagerImpl, filePath: Path, projectName } cachedName = value + if (!ApplicationManager.getApplication().isUnitTestMode) { + messageBus.syncPublisher(ProjectNameListener.TOPIC).nameChanged(value) StartupManager.getInstance(this).runAfterOpened { ApplicationManager.getApplication().invokeLater(Runnable { val frame = WindowManager.getInstance().getFrame(this) ?: return@Runnable diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/projectActions/ChangeProjectColorActionGroup.kt b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/projectActions/ChangeProjectColorActionGroup.kt index fa0b4964290e..302e286a14ff 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/projectActions/ChangeProjectColorActionGroup.kt +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/projectActions/ChangeProjectColorActionGroup.kt @@ -18,16 +18,17 @@ import com.intellij.util.ui.JBPoint class ChangeProjectColorActionGroup: DefaultActionGroup(), DumbAware { override fun getChildren(e: AnActionEvent?): Array { val projectPath = e?.project?.let { ProjectWindowCustomizerService.projectPath(it) } ?: return emptyArray() + val projectName = e.project?.name - return arrayOf(ChangeProjectColorAction(projectPath, IdeBundle.message("action.ChangeProjectColorAction.Amber.title"), 0), - ChangeProjectColorAction(projectPath, IdeBundle.message("action.ChangeProjectColorAction.Rust.title"), 1), - ChangeProjectColorAction(projectPath, IdeBundle.message("action.ChangeProjectColorAction.Olive.title"), 2), - ChangeProjectColorAction(projectPath, IdeBundle.message("action.ChangeProjectColorAction.Grass.title"), 8), - ChangeProjectColorAction(projectPath, IdeBundle.message("action.ChangeProjectColorAction.Ocean.title"), 7), - ChangeProjectColorAction(projectPath, IdeBundle.message("action.ChangeProjectColorAction.Sky.title"), 3), - ChangeProjectColorAction(projectPath, IdeBundle.message("action.ChangeProjectColorAction.Cobalt.title"), 4), - ChangeProjectColorAction(projectPath, IdeBundle.message("action.ChangeProjectColorAction.Violet.title"), 6), - ChangeProjectColorAction(projectPath, IdeBundle.message("action.ChangeProjectColorAction.Plum.title"), 5), + return arrayOf(ChangeProjectColorAction(projectPath, IdeBundle.message("action.ChangeProjectColorAction.Amber.title"), 0, projectName), + ChangeProjectColorAction(projectPath, IdeBundle.message("action.ChangeProjectColorAction.Rust.title"), 1, projectName), + ChangeProjectColorAction(projectPath, IdeBundle.message("action.ChangeProjectColorAction.Olive.title"), 2, projectName), + ChangeProjectColorAction(projectPath, IdeBundle.message("action.ChangeProjectColorAction.Grass.title"), 8, projectName), + ChangeProjectColorAction(projectPath, IdeBundle.message("action.ChangeProjectColorAction.Ocean.title"), 7, projectName), + ChangeProjectColorAction(projectPath, IdeBundle.message("action.ChangeProjectColorAction.Sky.title"), 3, projectName), + ChangeProjectColorAction(projectPath, IdeBundle.message("action.ChangeProjectColorAction.Cobalt.title"), 4, projectName), + ChangeProjectColorAction(projectPath, IdeBundle.message("action.ChangeProjectColorAction.Violet.title"), 6, projectName), + ChangeProjectColorAction(projectPath, IdeBundle.message("action.ChangeProjectColorAction.Plum.title"), 5, projectName), Separator(), ChooseCustomProjectColorAction() ) @@ -40,7 +41,7 @@ class ChangeProjectColorActionGroup: DefaultActionGroup(), DumbAware { } } -class ChangeProjectColorAction(val projectPath: String, val name: @NlsSafe String, val index: Int): +class ChangeProjectColorAction(val projectPath: String, val name: @NlsSafe String, val index: Int, val projectName: String?): AnAction(name, "", RecentProjectIconHelper.generateProjectIcon(projectPath, true, size = 16, colorIndex = index)), DumbAware { override fun getActionUpdateThread() = ActionUpdateThread.EDT diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/projectActions/ChangeProjectIconAction.kt b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/projectActions/ChangeProjectIconAction.kt index 0e4c426d8ffd..25929af6dc90 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/projectActions/ChangeProjectIconAction.kt +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/projectActions/ChangeProjectIconAction.kt @@ -95,7 +95,7 @@ internal class ChangeProjectIconAction : RecentProjectsWelcomeScreenActionBase() if (selectedItem is RecentProjectItem) { return selectedItem.projectPath } - return event.project?.basePath + return event.project?.let { ProjectWindowCustomizerService.projectPath(it) } } override fun update(event: AnActionEvent) {