Skip to content

Commit f0aefac

Browse files
committed
Impl: rewrite recent workspace view without JTree
- JTree catches all mouse actions, and it needs heavy customization to delegate the events to the children - this boilerplate can be simply avoided by using the UI DSL to dynamically build the view
1 parent 5ac66a3 commit f0aefac

File tree

2 files changed

+63
-147
lines changed

2 files changed

+63
-147
lines changed

src/main/kotlin/com/coder/gateway/views/CoderGatewayRecentWorkspaceConnectionsView.kt

+63-71
Original file line numberDiff line numberDiff line change
@@ -2,105 +2,97 @@ package com.coder.gateway.views
22

33
import com.coder.gateway.CoderGatewayBundle
44
import com.coder.gateway.icons.CoderIcons
5-
import com.coder.gateway.models.RecentWorkspaceConnection
65
import com.coder.gateway.services.CoderRecentWorkspaceConnectionsService
76
import com.intellij.openapi.components.service
7+
import com.intellij.openapi.ui.panel.ComponentPanelBuilder
88
import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeScreenUIManager
9-
import com.intellij.ui.dsl.builder.TopGap
9+
import com.intellij.ui.components.ActionLink
10+
import com.intellij.ui.components.JBScrollPane
11+
import com.intellij.ui.dsl.builder.BottomGap
1012
import com.intellij.ui.dsl.builder.panel
1113
import com.intellij.ui.dsl.gridLayout.HorizontalAlign
12-
import com.intellij.ui.dsl.gridLayout.VerticalAlign
13-
import com.intellij.ui.treeStructure.Tree
14-
import com.intellij.ui.treeStructure.treetable.ListTreeTableModel
15-
import com.intellij.ui.treeStructure.treetable.TreeColumnInfo
1614
import com.intellij.util.ui.JBFont
15+
import com.intellij.util.ui.JBUI
1716
import com.jetbrains.gateway.api.GatewayRecentConnections
17+
import com.jetbrains.gateway.api.GatewayUI
18+
import com.jetbrains.gateway.ssh.IntelliJPlatformProduct
1819
import com.jetbrains.rd.util.lifetime.Lifetime
20+
import kotlinx.coroutines.CoroutineScope
21+
import kotlinx.coroutines.Dispatchers
22+
import kotlinx.coroutines.launch
1923
import javax.swing.JComponent
20-
import javax.swing.JTree
21-
import javax.swing.tree.DefaultMutableTreeNode
22-
import javax.swing.tree.TreeNode
23-
import javax.swing.tree.TreePath
2424

2525
class CoderGatewayRecentWorkspaceConnectionsView : GatewayRecentConnections {
2626
private val recentConnectionsService = service<CoderRecentWorkspaceConnectionsService>()
27+
private val cs = CoroutineScope(Dispatchers.Main)
2728

28-
private var top = DefaultMutableTreeNode()
29-
private var recentConnectionsTeeModel = ListTreeTableModel(top, emptyArray<TreeColumnInfo>())
30-
private val recentConnectionsView = Tree(recentConnectionsTeeModel)
29+
private var contentPanel = JBScrollPane()
3130

3231
override val id = "CoderGatewayRecentConnections"
3332

3433
override val recentsIcon = CoderIcons.LOGO_16
3534

3635
override fun createRecentsView(lifetime: Lifetime): JComponent {
37-
val view = panel {
38-
indent {
39-
row {
40-
label(CoderGatewayBundle.message("gateway.connector.recentconnections.title")).applyToComponent {
41-
font = JBFont.h3().asBold()
42-
}
43-
}
44-
row {
45-
scrollCell(recentConnectionsView).resizableColumn().horizontalAlign(HorizontalAlign.FILL).verticalAlign(VerticalAlign.FILL).applyToComponent {
46-
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
47-
}
48-
cell()
49-
}.topGap(TopGap.NONE).resizableRow()
50-
}
51-
}.apply {
52-
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
53-
}
54-
55-
recentConnectionsView.apply {
56-
isRootVisible = false
57-
showsRootHandles = false
58-
rowHeight = -1
59-
val mouseListeners = mouseListeners
60-
for (mouseListener in mouseListeners) {
61-
removeMouseListener(mouseListener)
62-
}
63-
cellRenderer = CoderRecentWorkspaceConnectionsTreeRenderer()
64-
}
65-
updateTree()
66-
return view
36+
updateContentView()
37+
return contentPanel
6738
}
6839

6940
override fun getRecentsTitle() = CoderGatewayBundle.message("gateway.connector.title")
7041

7142
override fun updateRecentView() {
72-
updateTree()
43+
updateContentView()
7344
}
7445

75-
private fun updateTree() {
46+
private fun updateContentView() {
7647
val groupedConnections = recentConnectionsService.getAllRecentConnections().groupBy { it.coderWorkspaceHostname }
77-
top.removeAllChildren()
78-
groupedConnections.entries.forEach { (hostname, recentConnections) ->
79-
val hostnameTreeNode = HostnameTreeNode(hostname)
80-
recentConnections.forEach { connectionDetails ->
81-
hostnameTreeNode.add(ProjectTreeNode(connectionDetails))
82-
}
83-
top.add(hostnameTreeNode)
84-
}
85-
86-
expandAll(recentConnectionsView, TreePath(top))
87-
}
48+
contentPanel.viewport.view = panel {
49+
indent {
50+
row {
51+
label(CoderGatewayBundle.message("gateway.connector.recentconnections.title")).applyToComponent {
52+
font = JBFont.h3().asBold()
53+
}
54+
}.bottomGap(BottomGap.MEDIUM)
55+
groupedConnections.entries.forEach { (hostname, recentConnections) ->
56+
row {
57+
if (hostname != null) {
58+
label(hostname).applyToComponent {
59+
font = JBFont.h3().asBold()
60+
}.horizontalAlign(HorizontalAlign.LEFT)
61+
cell()
62+
}
63+
}
8864

89-
private fun expandAll(tree: JTree, parent: TreePath) {
90-
val node: TreeNode = parent.lastPathComponent as TreeNode
91-
if (node.childCount >= 0) {
92-
val e = node.children()
93-
while (e.hasMoreElements()) {
94-
val n: TreeNode = e.nextElement() as TreeNode
95-
val path: TreePath = parent.pathByAddingChild(n)
96-
expandAll(tree, path)
65+
group {
66+
recentConnections.forEach { connectionDetails ->
67+
val product = IntelliJPlatformProduct.fromProductCode(connectionDetails.ideProductCode!!)!!
68+
row {
69+
icon(product.icon)
70+
cell(ActionLink(connectionDetails.projectPath!!) {
71+
cs.launch {
72+
GatewayUI.getInstance().connect(
73+
mapOf(
74+
"type" to "coder",
75+
"coder_workspace_hostname" to "${connectionDetails.coderWorkspaceHostname}",
76+
"project_path" to connectionDetails.projectPath!!,
77+
"ide_product_code" to "${product.productCode}",
78+
"ide_build_number" to "${connectionDetails.ideBuildNumber}",
79+
"ide_download_link" to "${connectionDetails.downloadSource}"
80+
)
81+
)
82+
}
83+
})
84+
label("").resizableColumn().horizontalAlign(HorizontalAlign.FILL)
85+
label("Last opened: ${connectionDetails.lastOpened}").horizontalAlign(HorizontalAlign.RIGHT).applyToComponent {
86+
foreground = JBUI.CurrentTheme.ContextHelp.FOREGROUND
87+
font = ComponentPanelBuilder.getCommentFont(font)
88+
}
89+
}
90+
}
91+
}
92+
}
9793
}
94+
}.apply {
95+
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
9896
}
99-
tree.expandPath(parent)
10097
}
101-
}
102-
103-
104-
class HostnameTreeNode(val hostname: String?) : DefaultMutableTreeNode()
105-
106-
class ProjectTreeNode(val connectionDetails: RecentWorkspaceConnection) : DefaultMutableTreeNode()
98+
}

src/main/kotlin/com/coder/gateway/views/CoderRecentWorkspaceConnectionsTreeRenderer.kt

-76
This file was deleted.

0 commit comments

Comments
 (0)