Skip to content
Merged
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@

## Unreleased

### Fixed
- the help text under the IDE dropdown now takes into account whether the IDE is
already installed
- various minor alignment issues

## 2.2.1 - 2023-03-23

### Fixed
- reading an existing config would sometimes use the wrong directory on Linux
- two separate SSH sessions would spawn when connecting to a workspace through
- two separate SSH sessions would spawn when connecting to a workspace through
the main flow

## 2.2.0 - 2023-03-08
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeScreenUIManager
import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.RightGap
import com.intellij.ui.dsl.builder.panel
import com.intellij.util.ui.JBUI
import com.intellij.util.ui.components.BorderLayoutPanel
import com.jetbrains.gateway.api.GatewayUI
import java.awt.Component
Expand All @@ -30,18 +31,17 @@ class CoderGatewayConnectorWizardView : BorderLayoutPanel(), Disposable {
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()

registerStep(CoderWorkspacesStepView { nextButton.isEnabled = it })
registerStep(CoderLocateRemoteProjectStepView {
nextButton.isVisible = false
})
registerStep(CoderLocateRemoteProjectStepView { nextButton.isEnabled = it })
Copy link
Member

@johnstcn johnstcn Mar 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review: In Kotlin, you can just say it in a lambda with one parameter. TIL.


addToBottom(createBackComponent())
addToBottom(createButtons())

steps[0].apply {
onInit(model)
addToCenter(component)
updateUI()
nextButton.text = nextActionText
previousButton.text = previousActionText
nextButton.isEnabled = false
}

}
Expand Down Expand Up @@ -73,6 +73,7 @@ class CoderGatewayConnectorWizardView : BorderLayoutPanel(), Disposable {
private fun showNavigationButtons() {
nextButton.isVisible = true
previousButton.isVisible = true
nextButton.isEnabled = false
}

private fun next() {
Expand Down Expand Up @@ -101,30 +102,27 @@ class CoderGatewayConnectorWizardView : BorderLayoutPanel(), Disposable {
}
}

private fun createBackComponent(): Component {
private fun createButtons(): Component {
previousButton = JButton()
nextButton = JButton()
return panel {
separator(background = WelcomeScreenUIManager.getSeparatorColor())
indent {
row {

label("").resizableColumn().align(AlignX.FILL).gap(RightGap.SMALL)
previousButton = button("") { previous() }.align(AlignX.RIGHT).gap(RightGap.SMALL).applyToComponent { background = WelcomeScreenUIManager.getMainAssociatedComponentBackground() }.component
nextButton = button("") { next() }.align(AlignX.RIGHT).gap(RightGap.SMALL).applyToComponent { background = WelcomeScreenUIManager.getMainAssociatedComponentBackground() }.component
cell()
}
}.apply {
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
row {
label("").resizableColumn().align(AlignX.FILL).gap(RightGap.SMALL)
previousButton = button("") { previous() }
.align(AlignX.RIGHT).gap(RightGap.SMALL)
.applyToComponent { background = WelcomeScreenUIManager.getMainAssociatedComponentBackground() }.component
nextButton = button("") { next() }
.align(AlignX.RIGHT)
.applyToComponent { background = WelcomeScreenUIManager.getMainAssociatedComponentBackground() }.component
}

}.apply {
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
border = JBUI.Borders.empty(0, 16, 0, 16)
}
}

override fun dispose() {
steps.clear()
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.ui.ComboBox
import com.intellij.openapi.ui.ComponentValidator
import com.intellij.openapi.ui.ValidationInfo
import com.intellij.openapi.ui.panel.ComponentPanelBuilder
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeScreenUIManager
import com.intellij.remote.AuthType
Expand All @@ -29,12 +30,9 @@ import com.intellij.ui.AnimatedIcon
import com.intellij.ui.ColoredListCellRenderer
import com.intellij.ui.DocumentAdapter
import com.intellij.ui.components.JBTextField
import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.BottomGap
import com.intellij.ui.dsl.builder.RowLayout
import com.intellij.ui.dsl.builder.TopGap
import com.intellij.ui.dsl.builder.panel
import com.intellij.ui.dsl.builder.*
import com.intellij.util.ui.JBFont
import com.intellij.util.ui.JBUI
import com.intellij.util.ui.UIUtil
import com.intellij.util.ui.update.MergingUpdateQueue
import com.intellij.util.ui.update.Update
Expand Down Expand Up @@ -73,7 +71,7 @@ import javax.swing.ListCellRenderer
import javax.swing.SwingConstants
import javax.swing.event.DocumentEvent

class CoderLocateRemoteProjectStepView(private val disableNextAction: () -> Unit) : CoderWorkspacesWizardStep, Disposable {
class CoderLocateRemoteProjectStepView(private val setNextButtonEnabled: (Boolean) -> Unit) : CoderWorkspacesWizardStep, Disposable {
private val cs = CoroutineScope(Dispatchers.Main)
private val coderClient: CoderRestClientService = ApplicationManager.getApplication().getService(CoderRestClientService::class.java)

Expand All @@ -82,44 +80,71 @@ class CoderLocateRemoteProjectStepView(private val disableNextAction: () -> Unit
private lateinit var titleLabel: JLabel
private lateinit var wizard: CoderWorkspacesWizardModel
private lateinit var cbIDE: IDEComboBox
private lateinit var cbIDEComment: JLabel
private var tfProject = JBTextField()
private lateinit var terminalLink: LazyBrowserLink
private lateinit var ideResolvingJob: Job
private val pathValidationJobs = MergingUpdateQueue("remote-path-validation", 1000, true, tfProject)

override val component = panel {
indent {
row {
titleLabel = label("").applyToComponent {
font = JBFont.h3().asBold()
icon = CoderIcons.LOGO_16
}.component
}.bottomGap(BottomGap.MEDIUM)
row {
titleLabel = label("").applyToComponent {
font = JBFont.h3().asBold()
icon = CoderIcons.LOGO_16
}.component
}.topGap(TopGap.SMALL)
row {
label("IDE:")
cbIDE = cell(IDEComboBox(ideComboBoxModel).apply {
renderer = IDECellRenderer()
addActionListener {
setNextButtonEnabled(this.selectedItem != null)
ApplicationManager.getApplication().invokeLater {
logger.info("Selected IDE: ${this.selectedItem}")
when (this.selectedItem?.status) {
IdeStatus.ALREADY_INSTALLED ->
cbIDEComment.text =
CoderGatewayBundle.message("gateway.connector.view.coder.remoteproject.ide.installed.comment")

row {
label("IDE:")
cbIDE = cell(IDEComboBox(ideComboBoxModel).apply {
renderer = IDECellRenderer()
}).resizableColumn().align(AlignX.FILL).comment("The IDE will be downloaded from jetbrains.com").component
cell()
}.topGap(TopGap.NONE).layout(RowLayout.PARENT_GRID)
IdeStatus.DOWNLOAD ->
cbIDEComment.text =
CoderGatewayBundle.message("gateway.connector.view.coder.remoteproject.ide.download.comment")

row {
label("Project directory:")
cell(tfProject).resizableColumn().align(AlignX.FILL).component
cell()
}.topGap(TopGap.NONE).bottomGap(BottomGap.NONE).layout(RowLayout.PARENT_GRID)
row {
cell()
terminalLink = cell(
LazyBrowserLink(
CoderIcons.OPEN_TERMINAL,
"Open Terminal"
)
).component
}.topGap(TopGap.NONE).layout(RowLayout.PARENT_GRID)
}
}.apply { background = WelcomeScreenUIManager.getMainAssociatedComponentBackground() }
else ->
cbIDEComment.text =
CoderGatewayBundle.message("gateway.connector.view.coder.remoteproject.ide.none.comment")
}
}
}
}).resizableColumn().align(AlignX.FILL).component
}.topGap(TopGap.NONE).bottomGap(BottomGap.NONE).layout(RowLayout.PARENT_GRID)
row {
cell() // Empty cell for alignment.
cbIDEComment = cell(
ComponentPanelBuilder.createCommentComponent(
CoderGatewayBundle.message("gateway.connector.view.coder.remoteproject.ide.none.comment"),
false, -1, true
)
).component
}.topGap(TopGap.NONE).bottomGap(BottomGap.NONE).layout(RowLayout.PARENT_GRID)
row {
label("Project directory:")
cell(tfProject).resizableColumn().align(AlignX.FILL).component
}.topGap(TopGap.NONE).bottomGap(BottomGap.NONE).layout(RowLayout.PARENT_GRID)
row {
cell() // Empty cell for alignment.
terminalLink = cell(
LazyBrowserLink(
CoderIcons.OPEN_TERMINAL,
"Open Terminal"
)
).component
}.topGap(TopGap.NONE).layout(RowLayout.PARENT_GRID)
gap(RightGap.SMALL)
}.apply {
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
border = JBUI.Borders.empty(0, 16, 0, 16)
}

override val previousActionText = IdeBundle.message("button.back")
override val nextActionText = CoderGatewayBundle.message("gateway.connector.view.coder.remoteproject.next.text")
Expand Down Expand Up @@ -153,7 +178,7 @@ class CoderLocateRemoteProjectStepView(private val disableNextAction: () -> Unit
is SshException -> {
logger.error("Can't connect to workspace ${selectedWorkspace.name}. Reason: $e")
withContext(Dispatchers.Main) {
disableNextAction()
setNextButtonEnabled(false)
cbIDE.renderer = object : ColoredListCellRenderer<IdeWithStatus>() {
override fun customizeCellRenderer(list: JList<out IdeWithStatus>, value: IdeWithStatus?, index: Int, isSelected: Boolean, cellHasFocus: Boolean) {
background = UIUtil.getListBackground(isSelected, cellHasFocus)
Expand All @@ -167,7 +192,7 @@ class CoderLocateRemoteProjectStepView(private val disableNextAction: () -> Unit
else -> {
logger.error("Could not resolve any IDE for workspace ${selectedWorkspace.name}. Reason: $e")
withContext(Dispatchers.Main) {
disableNextAction()
setNextButtonEnabled(false)
cbIDE.renderer = object : ColoredListCellRenderer<IdeWithStatus>() {
override fun customizeCellRenderer(list: JList<out IdeWithStatus>, value: IdeWithStatus?, index: Int, isSelected: Boolean, cellHasFocus: Boolean) {
background = UIUtil.getListBackground(isSelected, cellHasFocus)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ private const val SESSION_TOKEN = "session-token"

private const val MOUSE_OVER_TEMPLATE_NAME_COLUMN_ON_ROW = "MOUSE_OVER_TEMPLATE_NAME_COLUMN_ON_ROW"

class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) : CoderWorkspacesWizardStep, Disposable {
class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : CoderWorkspacesWizardStep, Disposable {
private val cs = CoroutineScope(Dispatchers.Main)
private var localWizardModel = CoderWorkspacesWizardModel()
private val coderClient: CoderRestClientService = service()
Expand Down Expand Up @@ -122,7 +122,7 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
rowHeight = 48
setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
selectionModel.addListSelectionListener {
enableNextButtonCallback(selectedObject != null && selectedObject?.agentStatus == RUNNING && selectedObject?.agentOS == OS.LINUX)
setNextButtonEnabled(selectedObject != null && selectedObject?.agentStatus == RUNNING && selectedObject?.agentOS == OS.LINUX)
if (selectedObject?.agentStatus == RUNNING && selectedObject?.agentOS != OS.LINUX) {
notificationBanner.apply {
component.isVisible = true
Expand Down Expand Up @@ -194,55 +194,74 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
private var poller: Job? = null

override val component = panel {
indent {
row {
label(CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.header.text")).applyToComponent {
font = JBFont.h3().asBold()
icon = CoderIcons.LOGO_16
}
}.topGap(TopGap.SMALL)
row {
cell(ComponentPanelBuilder.createCommentComponent(CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.comment"), false, -1, true))
}
row {
browserLink(CoderGatewayBundle.message("gateway.connector.view.login.documentation.action"), "https://coder.com/docs/coder-oss/latest/workspaces")
}
row(CoderGatewayBundle.message("gateway.connector.view.login.url.label")) {
tfUrl = textField().resizableColumn().align(AlignX.FILL).gap(RightGap.SMALL).bindText(localWizardModel::coderURL).applyToComponent {
addActionListener {
poller?.cancel()
listTableModelOfWorkspaces.items = emptyList()
askTokenAndOpenSession(true)
}
}.component
button(CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.connect.text")) {
row {
label(CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.header.text")).applyToComponent {
font = JBFont.h3().asBold()
icon = CoderIcons.LOGO_16
}
}.topGap(TopGap.SMALL)
row {
cell(
ComponentPanelBuilder.createCommentComponent(
CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.comment"),
false,
-1,
true
)
)
}
row {
browserLink(
CoderGatewayBundle.message("gateway.connector.view.login.documentation.action"),
"https://coder.com/docs/coder-oss/latest/workspaces"
)
}
row(CoderGatewayBundle.message("gateway.connector.view.login.url.label")) {
tfUrl = textField().resizableColumn().align(AlignX.FILL).gap(RightGap.SMALL)
.bindText(localWizardModel::coderURL).applyToComponent {
addActionListener {
poller?.cancel()
listTableModelOfWorkspaces.items = emptyList()
askTokenAndOpenSession(true)
}.applyToComponent {
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
}
cell()
}
row {
cbExistingToken = checkBox(CoderGatewayBundle.message("gateway.connector.view.login.existing-token.label"))
.bindSelected(localWizardModel::useExistingToken)
.component
}
row {
cell(ComponentPanelBuilder.createCommentComponent(
CoderGatewayBundle.message("gateway.connector.view.login.existing-token.tooltip",
CoderGatewayBundle.message("gateway.connector.view.login.existing-token.label")),
false, -1, true))
}
row {
scrollCell(toolbar.createPanel().apply {
add(notificationBanner.component.apply { isVisible = false }, "South")
}).resizableColumn().align(AlignX.FILL).align(AlignY.FILL)
cell()
}.topGap(TopGap.NONE).bottomGap(BottomGap.NONE).resizableRow()
}
}.apply { background = WelcomeScreenUIManager.getMainAssociatedComponentBackground() }
}.component
button(CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.connect.text")) {
poller?.cancel()
listTableModelOfWorkspaces.items = emptyList()
askTokenAndOpenSession(true)
}.applyToComponent {
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
}
}.layout(RowLayout.PARENT_GRID)
row {
cell() // Empty cell for alignment.
cbExistingToken = checkBox(CoderGatewayBundle.message("gateway.connector.view.login.existing-token.label"))
.bindSelected(localWizardModel::useExistingToken)
.component
}.layout(RowLayout.PARENT_GRID)
row {
cell() // Empty cell for alignment.
cell(
ComponentPanelBuilder.createCommentComponent(
CoderGatewayBundle.message(
"gateway.connector.view.login.existing-token.tooltip",
CoderGatewayBundle.message("gateway.connector.view.login.existing-token.label"),
CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.connect.text")
),
false, -1, true
)
)
}.layout(RowLayout.PARENT_GRID)
row {
scrollCell(toolbar.createPanel().apply {
add(notificationBanner.component.apply { isVisible = false }, "South")
}).resizableColumn().align(AlignX.FILL).align(AlignY.FILL)
}.topGap(TopGap.NONE).bottomGap(BottomGap.NONE).resizableRow()

}.apply {
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
border = JBUI.Borders.empty(0, 16, 0, 16)
}

override val previousActionText = IdeBundle.message("button.back")
override val nextActionText = CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.next.text")
Expand Down
Loading