@@ -7,31 +7,59 @@ import com.coder.gateway.models.WorkspaceAgentModel
77import com.coder.gateway.sdk.Arch
88import com.coder.gateway.sdk.CoderRestClientService
99import com.coder.gateway.sdk.OS
10+ import com.coder.gateway.sdk.v2.models.ProvisionerJobStatus
11+ import com.coder.gateway.sdk.v2.models.WorkspaceBuildTransition
1012import com.intellij.ide.IdeBundle
1113import com.intellij.openapi.Disposable
1214import com.intellij.openapi.application.ApplicationManager
1315import com.intellij.openapi.diagnostic.Logger
1416import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeScreenUIManager
15- import com.intellij.ui.CollectionListModel
16- import com.intellij.ui.components.JBList
1717import com.intellij.ui.dsl.builder.BottomGap
1818import com.intellij.ui.dsl.builder.TopGap
1919import com.intellij.ui.dsl.builder.panel
2020import com.intellij.ui.dsl.gridLayout.HorizontalAlign
2121import com.intellij.ui.dsl.gridLayout.VerticalAlign
22+ import com.intellij.ui.table.TableView
23+ import com.intellij.util.ui.ColumnInfo
2224import com.intellij.util.ui.JBFont
25+ import com.intellij.util.ui.JBUI
26+ import com.intellij.util.ui.ListTableModel
27+ import com.intellij.util.ui.table.IconTableCellRenderer
2328import kotlinx.coroutines.CoroutineScope
2429import kotlinx.coroutines.Dispatchers
2530import kotlinx.coroutines.cancel
2631import kotlinx.coroutines.launch
2732import kotlinx.coroutines.withContext
33+ import java.awt.Color
34+ import java.awt.Component
35+ import java.awt.Dimension
36+ import javax.swing.Icon
37+ import javax.swing.JTable
38+ import javax.swing.ListSelectionModel
39+ import javax.swing.table.DefaultTableCellRenderer
40+ import javax.swing.table.TableCellRenderer
41+
2842
2943class CoderWorkspacesStepView : CoderWorkspacesWizardStep , Disposable {
3044 private val cs = CoroutineScope (Dispatchers .Main )
3145
3246 private val coderClient: CoderRestClientService = ApplicationManager .getApplication().getService(CoderRestClientService ::class .java)
33- private var workspaces = CollectionListModel <WorkspaceAgentModel >()
34- private var workspacesView = JBList (workspaces)
47+
48+
49+ private var listTableModelOfWorkspaces = ListTableModel <WorkspaceAgentModel >(WorkspaceIconColumnInfo (" " ), WorkspaceNameColumnInfo (" Name" ), WorkspaceTemplateNameColumnInfo (" Template" ), WorkspaceStatusColumnInfo (" Status" ))
50+ private var tableOfWorkspaces = TableView (listTableModelOfWorkspaces).apply {
51+ rowSelectionAllowed = true
52+ columnSelectionAllowed = false
53+ tableHeader.reorderingAllowed = false
54+ showVerticalLines = false
55+ intercellSpacing = Dimension (0 , 0 )
56+ columnModel.getColumn(0 ).apply {
57+ maxWidth = JBUI .scale(52 )
58+ minWidth = JBUI .scale(52 )
59+ }
60+
61+ setSelectionMode(ListSelectionModel .SINGLE_SELECTION )
62+ }
3563
3664 private lateinit var wizard: CoderWorkspacesWizardModel
3765
@@ -44,7 +72,7 @@ class CoderWorkspacesStepView : CoderWorkspacesWizardStep, Disposable {
4472 }
4573 }.bottomGap(BottomGap .MEDIUM )
4674 row {
47- scrollCell(workspacesView ).resizableColumn().horizontalAlign(HorizontalAlign .FILL ).verticalAlign(VerticalAlign .FILL )
75+ scrollCell(tableOfWorkspaces ).resizableColumn().horizontalAlign(HorizontalAlign .FILL ).verticalAlign(VerticalAlign .FILL )
4876 cell()
4977 }.topGap(TopGap .NONE ).resizableRow()
5078
@@ -56,9 +84,6 @@ class CoderWorkspacesStepView : CoderWorkspacesWizardStep, Disposable {
5684
5785 override fun onInit (wizardModel : CoderWorkspacesWizardModel ) {
5886 wizard = wizardModel
59- workspaces.removeAll()
60- workspacesView.cellRenderer = WorkspaceCellRenderer ()
61-
6287 cs.launch {
6388 val workspaceList = withContext(Dispatchers .IO ) {
6489 try {
@@ -70,6 +95,7 @@ class CoderWorkspacesStepView : CoderWorkspacesWizardStep, Disposable {
7095 val workspaceName = if (shouldContainAgentName) " ${workspace.name} .${agent.name} " else workspace.name
7196 WorkspaceAgentModel (
7297 workspaceName,
98+ workspace.templateName,
7399 workspace.latestBuild.job.status,
74100 workspace.latestBuild.workspaceTransition,
75101 OS .from(agent.operatingSystem),
@@ -83,14 +109,14 @@ class CoderWorkspacesStepView : CoderWorkspacesWizardStep, Disposable {
83109 emptyList()
84110 }
85111 }
86- workspaceList.forEach {
87- workspaces.add(it)
88- }
112+
113+ // if we just run the update on the main dispatcher, the code will block because it cant get some AWT locks
114+ ApplicationManager .getApplication().invokeLater { listTableModelOfWorkspaces.updateItems(workspaceList) }
89115 }
90116 }
91117
92118 override fun onNext (wizardModel : CoderWorkspacesWizardModel ): Boolean {
93- val workspace = workspacesView.selectedValue
119+ val workspace = tableOfWorkspaces.selectedObject
94120 if (workspace != null ) {
95121 wizardModel.selectedWorkspace = workspace
96122 return true
@@ -102,6 +128,123 @@ class CoderWorkspacesStepView : CoderWorkspacesWizardStep, Disposable {
102128 cs.cancel()
103129 }
104130
131+ private class WorkspaceIconColumnInfo (columnName : String ) : ColumnInfo<WorkspaceAgentModel, String>(columnName) {
132+ override fun valueOf (workspace : WorkspaceAgentModel ? ): String? {
133+ return workspace?.agentOS?.name
134+ }
135+
136+ override fun getRenderer (item : WorkspaceAgentModel ? ): TableCellRenderer {
137+ return object : IconTableCellRenderer <String >() {
138+ override fun getText (): String {
139+ return " "
140+ }
141+
142+ override fun getIcon (value : String , table : JTable ? , row : Int ): Icon {
143+ return when (OS .from(value)) {
144+ OS .LINUX -> CoderIcons .LINUX
145+ OS .WINDOWS -> CoderIcons .WINDOWS
146+ OS .MAC -> CoderIcons .MACOS
147+ else -> CoderIcons .UNKNOWN
148+ }
149+ }
150+
151+ override fun isCenterAlignment () = true
152+ }
153+ }
154+ }
155+
156+ private class WorkspaceNameColumnInfo (columnName : String ) : ColumnInfo<WorkspaceAgentModel, String>(columnName) {
157+ override fun valueOf (workspace : WorkspaceAgentModel ? ): String? {
158+ return workspace?.name
159+ }
160+
161+ override fun getRenderer (item : WorkspaceAgentModel ? ): TableCellRenderer {
162+ return object : DefaultTableCellRenderer () {
163+ override fun getTableCellRendererComponent (table : JTable , value : Any , isSelected : Boolean , hasFocus : Boolean , row : Int , column : Int ): Component {
164+ super .getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column)
165+ if (value is String ) {
166+ text = value
167+ }
168+ font = JBFont .h3()
169+ return this
170+ }
171+ }
172+ }
173+ }
174+
175+ private class WorkspaceTemplateNameColumnInfo (columnName : String ) : ColumnInfo<WorkspaceAgentModel, String>(columnName) {
176+ override fun valueOf (workspace : WorkspaceAgentModel ? ): String? {
177+ return workspace?.templateName
178+ }
179+
180+ override fun getRenderer (item : WorkspaceAgentModel ? ): TableCellRenderer {
181+ return object : DefaultTableCellRenderer () {
182+ override fun getTableCellRendererComponent (table : JTable , value : Any , isSelected : Boolean , hasFocus : Boolean , row : Int , column : Int ): Component {
183+ super .getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column)
184+ if (value is String ) {
185+ text = value
186+ }
187+ font = JBFont .h3()
188+ return this
189+ }
190+ }
191+ }
192+ }
193+
194+ private class WorkspaceStatusColumnInfo (columnName : String ) : ColumnInfo<WorkspaceAgentModel, String>(columnName) {
195+ override fun valueOf (workspace : WorkspaceAgentModel ? ): String? {
196+ return workspace?.statusLabel()
197+ }
198+
199+ override fun getRenderer (item : WorkspaceAgentModel ? ): TableCellRenderer {
200+ return object : DefaultTableCellRenderer () {
201+ override fun getTableCellRendererComponent (table : JTable , value : Any , isSelected : Boolean , hasFocus : Boolean , row : Int , column : Int ): Component {
202+ super .getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column)
203+ if (value is String ) {
204+ text = value
205+ }
206+ font = JBFont .h3()
207+ foreground = (table.model as ListTableModel <WorkspaceAgentModel >).getRowValue(row).statusColor()
208+ return this
209+ }
210+ }
211+ }
212+
213+ private fun WorkspaceAgentModel.statusLabel () = when (this .jobStatus) {
214+ ProvisionerJobStatus .PENDING -> " ◍ Queued"
215+ ProvisionerJobStatus .RUNNING -> when (this .buildTransition) {
216+ WorkspaceBuildTransition .START -> " ⦿ Starting"
217+ WorkspaceBuildTransition .STOP -> " ◍ Stopping"
218+ WorkspaceBuildTransition .DELETE -> " ⦸ Deleting"
219+ }
220+
221+ ProvisionerJobStatus .SUCCEEDED -> when (this .buildTransition) {
222+ WorkspaceBuildTransition .START -> " ⦿ Running"
223+ WorkspaceBuildTransition .STOP -> " ◍ Stopped"
224+ WorkspaceBuildTransition .DELETE -> " ⦸ Deleted"
225+ }
226+
227+ ProvisionerJobStatus .CANCELING -> " ◍ Canceling action"
228+ ProvisionerJobStatus .CANCELED -> " ◍ Canceled action"
229+ ProvisionerJobStatus .FAILED -> " ⓧ Failed"
230+ }
231+
232+ private fun WorkspaceAgentModel.statusColor () = when (this .jobStatus) {
233+ ProvisionerJobStatus .SUCCEEDED -> if (this .buildTransition == WorkspaceBuildTransition .START ) Color .GREEN else Color .RED
234+ ProvisionerJobStatus .RUNNING -> when (this .buildTransition) {
235+ WorkspaceBuildTransition .START , WorkspaceBuildTransition .STOP , WorkspaceBuildTransition .DELETE -> Color .GRAY
236+ }
237+
238+ else -> Color .RED
239+ }
240+ }
241+
242+
243+ private fun ListTableModel<WorkspaceAgentModel>.updateItems (workspaces : Collection <WorkspaceAgentModel >) {
244+ while (this .rowCount > 0 ) this .removeRow(0 )
245+ this .addRows(workspaces)
246+ }
247+
105248 companion object {
106249 val logger = Logger .getInstance(CoderWorkspacesStepView ::class .java.simpleName)
107250 }
0 commit comments