From 71ed51b651e4abd5d6711c49ba21c2cac1c6302a Mon Sep 17 00:00:00 2001 From: Ioan Faur Date: Thu, 23 Jun 2022 00:11:25 +0300 Subject: [PATCH 1/2] Fix: progress icon spin in combo box - the progress icon in the IDE selection combo box was not spinning while IDEs were loading. - there were two reasons: 1. the combobox did not enable animated icons in the renderer 2. the cell renderer implementation did not repaint the icon - resolves #4 --- .../steps/CoderLocateRemoteProjectStepView.kt | 26 ++++++++++++------- .../messages/CoderGatewayBundle.properties | 3 ++- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/com/coder/gateway/views/steps/CoderLocateRemoteProjectStepView.kt b/src/main/kotlin/com/coder/gateway/views/steps/CoderLocateRemoteProjectStepView.kt index 936bc459..8f5e8663 100644 --- a/src/main/kotlin/com/coder/gateway/views/steps/CoderLocateRemoteProjectStepView.kt +++ b/src/main/kotlin/com/coder/gateway/views/steps/CoderLocateRemoteProjectStepView.kt @@ -14,6 +14,7 @@ import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeScreenUIManager import com.intellij.remote.AuthType import com.intellij.remote.RemoteCredentialsHolder import com.intellij.ui.AnimatedIcon +import com.intellij.ui.ColoredListCellRenderer import com.intellij.ui.components.JBTextField import com.intellij.ui.dsl.builder.BottomGap import com.intellij.ui.dsl.builder.RowLayout @@ -21,7 +22,6 @@ import com.intellij.ui.dsl.builder.TopGap import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.gridLayout.HorizontalAlign import com.intellij.util.ui.JBFont -import com.intellij.util.ui.JBUI import com.intellij.util.ui.UIUtil import com.jetbrains.gateway.api.GatewayUI import com.jetbrains.gateway.ssh.CachingProductsJsonWrapper @@ -47,7 +47,6 @@ class CoderLocateRemoteProjectStepView : CoderWorkspacesWizardStep, Disposable { private val cs = CoroutineScope(Dispatchers.Main) private val coderClient: CoderRestClientService = ApplicationManager.getApplication().getService(CoderRestClientService::class.java) - private val spinner = JLabel("", AnimatedIcon.Default(), SwingConstants.LEFT) private var ideComboBoxModel = DefaultComboBoxModel() private lateinit var titleLabel: JLabel @@ -127,7 +126,6 @@ class CoderLocateRemoteProjectStepView : CoderWorkspacesWizardStep, Disposable { if (idesWithStatus.isEmpty()) { logger.warn("Could not resolve any IDE for workspace ${selectedWorkspace.name}, probably $workspaceOS is not supported by Gateway") } else { - cbIDE.remove(spinner) ideComboBoxModel.addAll(idesWithStatus) cbIDE.selectedIndex = 0 } @@ -162,18 +160,28 @@ class CoderLocateRemoteProjectStepView : CoderWorkspacesWizardStep, Disposable { } private class IDEComboBox(model: ComboBoxModel) : ComboBox(model) { + + init { + putClientProperty(AnimatedIcon.ANIMATION_IN_RENDERER_ALLOWED, true) + } + override fun getSelectedItem(): IdeWithStatus? { return super.getSelectedItem() as IdeWithStatus? } } private class IDECellRenderer : ListCellRenderer { + private val loadingComponentRenderer: ListCellRenderer = object : ColoredListCellRenderer() { + override fun customizeCellRenderer(list: JList, value: IdeWithStatus?, index: Int, isSelected: Boolean, cellHasFocus: Boolean) { + background = UIUtil.getListBackground(isSelected, cellHasFocus) + icon = AnimatedIcon.Default.INSTANCE + append(CoderGatewayBundle.message("gateway.connector.view.coder.remoteproject.loading.text")) + } + } + override fun getListCellRendererComponent(list: JList?, ideWithStatus: IdeWithStatus?, index: Int, isSelected: Boolean, cellHasFocus: Boolean): Component { return if (ideWithStatus == null && index == -1) { - JPanel().apply { - layout = FlowLayout(FlowLayout.LEFT) - add(JLabel("Retrieving products...", AnimatedIcon.Default(), SwingConstants.LEFT)) - } + loadingComponentRenderer.getListCellRendererComponent(list, null, -1, isSelected, cellHasFocus) } else if (ideWithStatus != null) { JPanel().apply { layout = FlowLayout(FlowLayout.LEFT) @@ -181,10 +189,10 @@ class CoderLocateRemoteProjectStepView : CoderWorkspacesWizardStep, Disposable { add(JLabel("${ideWithStatus.product.productCode} ${ideWithStatus.presentableVersion} ${ideWithStatus.buildNumber} | ${ideWithStatus.status.name.toLowerCase()}").apply { foreground = UIUtil.getLabelDisabledForeground() }) - background = JBUI.CurrentTheme.List.background(isSelected, cellHasFocus) + background = UIUtil.getListBackground(isSelected, cellHasFocus) } } else { - JPanel() + panel { } } } } diff --git a/src/main/resources/messages/CoderGatewayBundle.properties b/src/main/resources/messages/CoderGatewayBundle.properties index 5fef708c..82414753 100644 --- a/src/main/resources/messages/CoderGatewayBundle.properties +++ b/src/main/resources/messages/CoderGatewayBundle.properties @@ -10,8 +10,9 @@ gateway.connector.view.login.token.label=Session Token: gateway.connector.view.coder.auth.next.text=Connect gateway.connector.view.login.cli.downloader.dialog.title=Authenticate and setup coder gateway.connector.view.coder.workspaces.next.text=Select IDE and Project -gateway.connector.view.coder.remoteproject.next.text=Download and Start IDE gateway.connector.view.coder.workspaces.choose.text=Choose a workspace +gateway.connector.view.coder.remoteproject.loading.text=Retrieving products... +gateway.connector.view.coder.remoteproject.next.text=Download and Start IDE gateway.connector.view.coder.remoteproject.choose.text=Choose IDE and project for workspace {0} gateway.connector.recentconnections.title=Recent Coder Workspaces gateway.connector.recentconnections.remove.button.tooltip=Remove from Recent Connections From ad50f82aa0866ec489e5211b24894ca930034a71 Mon Sep 17 00:00:00 2001 From: Ioan Faur Date: Thu, 23 Jun 2022 23:05:23 +0300 Subject: [PATCH 2/2] Fix: report error if supported IDEs could not be resolved - the IDEs combobox is no longer stuck with the text "Retrieving products.." if an exception happens in the code. - instead, exceptions are handled a new custom combo box renderer is installed that tells the user that an error was encountered while retrieving the IDEs. - resolves #10 --- .../steps/CoderLocateRemoteProjectStepView.kt | 47 ++++++++++++------- .../messages/CoderGatewayBundle.properties | 1 + 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/main/kotlin/com/coder/gateway/views/steps/CoderLocateRemoteProjectStepView.kt b/src/main/kotlin/com/coder/gateway/views/steps/CoderLocateRemoteProjectStepView.kt index 8f5e8663..f5caa95e 100644 --- a/src/main/kotlin/com/coder/gateway/views/steps/CoderLocateRemoteProjectStepView.kt +++ b/src/main/kotlin/com/coder/gateway/views/steps/CoderLocateRemoteProjectStepView.kt @@ -110,24 +110,35 @@ class CoderLocateRemoteProjectStepView : CoderWorkspacesWizardStep, Disposable { cs.launch { logger.info("Retrieving available IDE's for ${selectedWorkspace.name} workspace...") - val workspaceOS = withContext(Dispatchers.IO) { - RemoteCredentialsHolder().apply { - setHost("coder.${selectedWorkspace.name}") - userName = "coder" - authType = AuthType.OPEN_SSH - }.guessOs - } - logger.info("Resolved OS and Arch for ${selectedWorkspace.name} is: $workspaceOS") - val idesWithStatus = IntelliJPlatformProduct.values() - .filter { it.showInGateway } - .flatMap { CachingProductsJsonWrapper.getAvailableIdes(it, workspaceOS) } - .map { ide -> IdeWithStatus(ide.product, ide.buildNumber, IdeStatus.DOWNLOAD, ide.downloadLink, ide.presentableVersion) } - - if (idesWithStatus.isEmpty()) { - logger.warn("Could not resolve any IDE for workspace ${selectedWorkspace.name}, probably $workspaceOS is not supported by Gateway") - } else { - ideComboBoxModel.addAll(idesWithStatus) - cbIDE.selectedIndex = 0 + try { + val workspaceOS = withContext(Dispatchers.IO) { + RemoteCredentialsHolder().apply { + setHost("coder.${selectedWorkspace.name}") + userName = "coder" + authType = AuthType.OPEN_SSH + }.guessOs + } + logger.info("Resolved OS and Arch for ${selectedWorkspace.name} is: $workspaceOS") + val idesWithStatus = IntelliJPlatformProduct.values() + .filter { it.showInGateway } + .flatMap { CachingProductsJsonWrapper.getAvailableIdes(it, workspaceOS) } + .map { ide -> IdeWithStatus(ide.product, ide.buildNumber, IdeStatus.DOWNLOAD, ide.downloadLink, ide.presentableVersion) } + + if (idesWithStatus.isEmpty()) { + logger.warn("Could not resolve any IDE for workspace ${selectedWorkspace.name}, probably $workspaceOS is not supported by Gateway") + } else { + ideComboBoxModel.addAll(idesWithStatus) + cbIDE.selectedIndex = 0 + } + } catch (e: Exception) { + logger.error("Could not resolve any IDE for workspace ${selectedWorkspace.name}. Reason: $e") + cbIDE.renderer = object : ColoredListCellRenderer() { + override fun customizeCellRenderer(list: JList, value: IdeWithStatus?, index: Int, isSelected: Boolean, cellHasFocus: Boolean) { + background = UIUtil.getListBackground(isSelected, cellHasFocus) + icon = UIUtil.getBalloonErrorIcon() + append(CoderGatewayBundle.message("gateway.connector.view.coder.remoteproject.ide.error.text", selectedWorkspace.name)) + } + } } } } diff --git a/src/main/resources/messages/CoderGatewayBundle.properties b/src/main/resources/messages/CoderGatewayBundle.properties index 82414753..2fd16be0 100644 --- a/src/main/resources/messages/CoderGatewayBundle.properties +++ b/src/main/resources/messages/CoderGatewayBundle.properties @@ -12,6 +12,7 @@ gateway.connector.view.login.cli.downloader.dialog.title=Authenticate and setup gateway.connector.view.coder.workspaces.next.text=Select IDE and Project gateway.connector.view.coder.workspaces.choose.text=Choose a workspace gateway.connector.view.coder.remoteproject.loading.text=Retrieving products... +gateway.connector.view.coder.remoteproject.ide.error.text=Could not retrieve any IDE for workspace {0} because an error was encountered gateway.connector.view.coder.remoteproject.next.text=Download and Start IDE gateway.connector.view.coder.remoteproject.choose.text=Choose IDE and project for workspace {0} gateway.connector.recentconnections.title=Recent Coder Workspaces