From fbbf3c8c88785c59ac39a012655fe3cfd9e09c67 Mon Sep 17 00:00:00 2001 From: banana Date: Sun, 3 Aug 2025 19:19:39 +0200 Subject: [PATCH] feat: Add actions to "move left" and "move right" a tab in workbench list --- .../DeveloperToolsInstanceSettings.kt | 18 +++++ .../content/DeveloperToolContentPanel.kt | 81 ++++++++++++++++++- 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/modules/settings/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/settings/DeveloperToolsInstanceSettings.kt b/modules/settings/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/settings/DeveloperToolsInstanceSettings.kt index b7be7426..4010e899 100644 --- a/modules/settings/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/settings/DeveloperToolsInstanceSettings.kt +++ b/modules/settings/src/main/kotlin/dev/turingcomplete/intellijdevelopertoolsplugin/settings/DeveloperToolsInstanceSettings.kt @@ -38,6 +38,9 @@ abstract class DeveloperToolsInstanceSettings : PersistentStateComponent? = null private set + private val tabOrders = + ConcurrentHashMap>() + // -- Initialization ------------------------------------------------------ // // -- Exposed Methods ----------------------------------------------------- // @@ -70,6 +73,13 @@ abstract class DeveloperToolsInstanceSettings : PersistentStateComponent = + tabOrders[toolId]?.toList() ?: emptyList() + + fun setToolTabOrder(toolId: String, order: List) { + tabOrders[toolId] = order.toMutableList() + } + override fun getState(): InstanceState { val stateDeveloperToolsConfigurations = developerToolsConfigurations @@ -86,6 +96,7 @@ abstract class DeveloperToolsInstanceSettings : PersistentStateComponent + tabOrders[toolId] = order.toMutableList() + } } // -- Private Methods ----------------------------------------------------- // @@ -181,6 +197,8 @@ abstract class DeveloperToolsInstanceSettings : PersistentStateComponent? = null, + @get:XCollection(style = v2, elementName = "tabOrder") + var tabOrders: Map>? = null, ) // -- Inner Type ---------------------------------------------------------- // 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..e5c465ba 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 @@ -118,6 +118,9 @@ open class DeveloperToolContentPanel(protected val developerToolNode: DeveloperT tabs = JBTabsFactory.createTabs(developerToolNode.project, developerToolNode.parentDisposable) developerToolNode.developerTools.forEach { addWorkbench(it) } + + restoreTabOrder() + selectedDeveloperToolInstance = AtomicProperty(tabs.selectedInfo!!.castedObject()) tabs.addListener(createTabsChangedListener(), developerToolNode.parentDisposable) @@ -142,6 +145,10 @@ open class DeveloperToolContentPanel(protected val developerToolNode: DeveloperT newDeveloperToolInstance.instance.activated() } } + + override fun tabsMoved() { + saveTabOrder() + } } private fun addWorkbench(developerToolContainer: DeveloperToolContainer) { @@ -163,7 +170,11 @@ open class DeveloperToolContentPanel(protected val developerToolNode: DeveloperT tabs.addTab(tabInfo) tabs.select(tabInfo, false) tabs.setPopupGroup( - DefaultActionGroup(createRenameWorkbenchAction()), + DefaultActionGroup( + createRenameWorkbenchAction(), + createMoveTabAction(direction = -1), + createMoveTabAction(direction = 1), + ), DeveloperToolContentPanel::class.java.name, true, ) @@ -194,6 +205,37 @@ open class DeveloperToolContentPanel(protected val developerToolNode: DeveloperT override fun getActionUpdateThread() = ActionUpdateThread.BGT } + private fun createMoveTabAction(direction: Int) = + object : DumbAwareAction( + if (direction < 0) "Move Left" else "Move Right", + null, + if (direction < 0) AllIcons.General.ArrowLeft else AllIcons.General.ArrowRight + ) { + + override fun update(e: AnActionEvent) { + val selectedInfo = tabs.selectedInfo + val currentIndex = tabs.tabs.indexOf(selectedInfo) + val targetIndex = currentIndex + direction + e.presentation.isEnabled = selectedInfo != null && targetIndex in 0 until tabs.tabCount + } + + override fun actionPerformed(e: AnActionEvent) { + val selectedInfo = tabs.selectedInfo ?: return + val currentIndex = tabs.tabs.indexOf(selectedInfo) + val targetIndex = currentIndex + direction + + if (targetIndex in 0 until tabs.tabCount) { + tabs.removeTab(selectedInfo) + tabs.addTab(selectedInfo, targetIndex) + tabs.select(selectedInfo, true) + saveTabOrder() + } + } + + override fun getActionUpdateThread() = ActionUpdateThread.BGT + } + + private fun createDestroyWorkbenchAction(developerUiTool: DeveloperUiTool, tabInfo: TabInfo) = DestroyWorkbenchAction( { @@ -215,6 +257,43 @@ open class DeveloperToolContentPanel(protected val developerToolNode: DeveloperT override fun getActionUpdateThread() = ActionUpdateThread.BGT } + private fun restoreTabOrder() { + val savedOrder = developerToolNode.settings.getToolTabOrder(developerToolNode.id) + if (savedOrder.isEmpty()) { + return + } + + val existingTabs = mutableMapOf() + tabs.tabs.forEach { tab -> + val container = tab.`object` as DeveloperToolContainer + existingTabs[container.configuration.id.toString()] = tab + } + + val orderedTabs = ArrayList() + + savedOrder.forEach { id -> + existingTabs[id]?.let { tab -> + orderedTabs.add(tab) + existingTabs.remove(id) + } + } + + orderedTabs.addAll(existingTabs.values) + + tabs.removeAllTabs() + orderedTabs.forEach { tab -> + tabs.addTab(tab) + } + } + + private fun saveTabOrder() { + val order = tabs.tabs.map { + (it.`object` as DeveloperToolContainer).configuration.id.toString() + } + developerToolNode.settings.setToolTabOrder(developerToolNode.id, order) + } + + // -- Inner Type ---------------------------------------------------------- // private class DestroyWorkbenchAction(