From b4643d9fe2237cc4b4dccdab861c3f65bd9712fa Mon Sep 17 00:00:00 2001 From: banana Date: Sun, 3 Aug 2025 23:40:11 +0200 Subject: [PATCH] feat: Add a Favorite node group in tools menu --- .../settings/DeveloperToolConfiguration.kt | 14 +++++ .../content/DeveloperToolContentPanel.kt | 48 ++++++++++++++++- .../tool/ui/frame/menu/DeveloperToolNode.kt | 9 ++++ .../tool/ui/frame/menu/ToolsMenuTree.kt | 53 ++++++++++++++++++- 4 files changed, 121 insertions(+), 3 deletions(-) diff --git a/modules/settings/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/settings/DeveloperToolConfiguration.kt b/modules/settings/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/settings/DeveloperToolConfiguration.kt index 4c3568be..66de6dd9 100644 --- a/modules/settings/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/settings/DeveloperToolConfiguration.kt +++ b/modules/settings/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/settings/DeveloperToolConfiguration.kt @@ -30,6 +30,20 @@ class DeveloperToolConfiguration( var isResetting = false private set + private val favoriteProperty = register( + "isFavorite", + false, + PropertyType.CONFIGURATION + ) + + var isFavorite: Boolean + get() = favoriteProperty.get() + set(value) { + favoriteProperty.set(value) + } + + + // -- Initialization ------------------------------------------------------ // // -- Exposed Methods ----------------------------------------------------- // diff --git a/modules/tools/ui/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/tool/ui/frame/content/DeveloperToolContentPanel.kt b/modules/tools/ui/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/tool/ui/frame/content/DeveloperToolContentPanel.kt index ac0513cf..c20ec55e 100644 --- a/modules/tools/ui/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/tool/ui/frame/content/DeveloperToolContentPanel.kt +++ b/modules/tools/ui/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/tool/ui/frame/content/DeveloperToolContentPanel.kt @@ -1,9 +1,12 @@ package dev.turingcomplete.intellijdevelopertoolsplugin.tool.ui.frame.content import com.intellij.icons.AllIcons +import com.intellij.openapi.actionSystem.ActionToolbar.DEFAULT_MINIMUM_BUTTON_SIZE import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.DefaultActionGroup +import com.intellij.openapi.actionSystem.ToggleAction +import com.intellij.openapi.actionSystem.impl.ActionButton import com.intellij.openapi.observable.properties.AtomicProperty import com.intellij.openapi.observable.properties.ObservableMutableProperty import com.intellij.openapi.project.DumbAwareAction @@ -20,6 +23,7 @@ import com.intellij.ui.tabs.TabInfo import com.intellij.ui.tabs.TabsListener import com.intellij.util.ui.JBEmptyBorder import com.intellij.util.ui.components.BorderLayoutPanel +import dev.turingcomplete.intellijdevelopertoolsplugin.settings.DeveloperToolsApplicationSettings import dev.turingcomplete.intellijdevelopertoolsplugin.settings.DeveloperToolsApplicationSettings.Companion.generalSettings import dev.turingcomplete.intellijdevelopertoolsplugin.tool.ui.base.DeveloperUiTool import dev.turingcomplete.intellijdevelopertoolsplugin.tool.ui.common.NotBlankInputValidator @@ -90,7 +94,18 @@ open class DeveloperToolContentPanel(protected val developerToolNode: DeveloperT row { val titleComponent = buildTitle() - val actions = + val favoriteButton = ActionButton( + ToggleFavoriteAction(developerToolNode), + null, // presentation + ToggleFavoriteAction.PLACE, + DEFAULT_MINIMUM_BUTTON_SIZE + ) + cell(favoriteButton) + .gap(RightGap.SMALL) + + + + val actions = mutableListOf( dumbAwareAction("Reset") { selectedDeveloperToolInstance.get().apply { @@ -126,6 +141,37 @@ open class DeveloperToolContentPanel(protected val developerToolNode: DeveloperT return tabs.component } + private class ToggleFavoriteAction(private val toolNode: DeveloperToolNode) : + ToggleAction("Toggle Favorite", null, AllIcons.Nodes.NotFavoriteOnHover) { + + override fun isSelected(e: AnActionEvent): Boolean { + return toolNode.isFavorite + } + + override fun setSelected(e: AnActionEvent, state: Boolean) { + toolNode.isFavorite = state + e.presentation.icon = if (state) AllIcons.Nodes.Favorite else AllIcons.Nodes.NotFavoriteOnHover + + DeveloperToolsApplicationSettings.instance.generalSettings.apply { + val current = toolsMenuTreeShowGroupNodes.get() + toolsMenuTreeShowGroupNodes.set(!current) + toolsMenuTreeShowGroupNodes.set(current) + } + } + + override fun update(e: AnActionEvent) { + super.update(e) + e.presentation.icon = if (isSelected(e)) AllIcons.Nodes.Favorite else AllIcons.Nodes.NotFavoriteOnHover + } + + override fun getActionUpdateThread() = ActionUpdateThread.EDT + + companion object { + const val PLACE = "DeveloperTools.Favorite" + } + } + + private fun syncTabsSelectionVisibility() { tabs.presentation.isHideTabs = generalSettings.hideWorkbenchTabsOnSingleTab.get() && tabs.tabCount == 1 diff --git a/modules/tools/ui/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/tool/ui/frame/menu/DeveloperToolNode.kt b/modules/tools/ui/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/tool/ui/frame/menu/DeveloperToolNode.kt index b9d50950..81d32a83 100644 --- a/modules/tools/ui/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/tool/ui/frame/menu/DeveloperToolNode.kt +++ b/modules/tools/ui/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/tool/ui/frame/menu/DeveloperToolNode.kt @@ -34,6 +34,15 @@ class DeveloperToolNode( val developerTools: List get() = _developerTools + var isFavorite: Boolean + get() = settings.getDeveloperToolConfigurations(developerToolId).firstOrNull()?.isFavorite ?: false + set(value) { + settings.getDeveloperToolConfigurations(developerToolId).firstOrNull()?.let { + it.isFavorite = value + } + } + + // -- Initialization ------------------------------------------------------ // // -- Exposed Methods ----------------------------------------------------- // diff --git a/modules/tools/ui/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/tool/ui/frame/menu/ToolsMenuTree.kt b/modules/tools/ui/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/tool/ui/frame/menu/ToolsMenuTree.kt index 9fea337f..6ad17766 100644 --- a/modules/tools/ui/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/tool/ui/frame/menu/ToolsMenuTree.kt +++ b/modules/tools/ui/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/tool/ui/frame/menu/ToolsMenuTree.kt @@ -305,6 +305,15 @@ class ToolsMenuTree( val defaultGroupNodesToExpand = mutableListOf() val groupNodes = mutableMapOf() + + val favoritesGroupNode = GroupNode(DeveloperUiToolGroup().apply { + id = "favorites" + menuTitle = "⭐ Favorites" + detailTitle = "Favorites" + initiallyExpanded = true + }) + + if (toolsMenuTreeShowGroupNodes) { DeveloperUiToolGroup.EP_NAME.extensions.forEach { developerToolGroup -> val groupNode = GroupNode(developerToolGroup) @@ -346,6 +355,21 @@ class ToolsMenuTree( ) parentNode.add(developerToolNode) + if (developerToolNode.isFavorite) { + val cloneNode = + DeveloperToolNode( + developerToolId = developerToolFactoryEp.id, + project = project, + settings = settings, + parentDisposable = parentDisposable, + developerUiToolPresentation = developerUiToolFactory.getDeveloperUiToolPresentation(), + showGrouped = true, + developerUiToolCreator = developerToolCreator, + ) + favoritesGroupNode.add(cloneNode) + } + + if (developerToolFactoryEp.preferredSelected) { check(preferredSelectedDeveloperToolNode == null) { "Multiple initial selected developer tools" @@ -355,8 +379,31 @@ class ToolsMenuTree( } } + if( favoritesGroupNode.childCount > 0 ) { + rootNode.add(favoritesGroupNode) + defaultGroupNodesToExpand.add(favoritesGroupNode) + } + + if (generalSettings.toolsMenuTreeOrderAlphabetically.get()) { - TreeUtil.sortChildren(rootNode) { o1, o2 -> o1.title.compareTo(o2.title) } + fun sortNodeRecursively(node: ContentNode) { + TreeUtil.sortChildren(node) { o1, o2 -> + when { + o1.id == "favorites" -> -1 + o2.id == "favorites" -> 1 + else -> o1.title.compareTo(o2.title) + } + } + + for (i in 0 until node.childCount) { + val child = node.getChildAt(i) + if (child is ContentNode) { + sortNodeRecursively(child) + } + } + } + + sortNodeRecursively(rootNode) } return Triple(rootNode, defaultGroupNodesToExpand, preferredSelectedDeveloperToolNode) @@ -400,7 +447,9 @@ class ToolsMenuTree( val isTopLevelNode = contentNode.parent is RootNode val textAttributes = - if (toolsMenuTreeShowGroupNodes && isTopLevelNode && !contentNode.isSecondaryNode) { + if (contentNode.id == "favorites") { + REGULAR_BOLD_ATTRIBUTES + } else if (toolsMenuTreeShowGroupNodes && isTopLevelNode && !contentNode.isSecondaryNode) { REGULAR_BOLD_ATTRIBUTES } else { REGULAR_ATTRIBUTES