Skip to content

Commit 8af9ec1

Browse files
authored
Tweak IDE help text and alignment (#209)
* Tweak header alignment to match previous step * Change IDE help text based on installed status * Standardize panel width Before we used an empty cell which created a gap on the right but that caused the IDE dropdown to change sizes based on its content. I removed `indent` and added an empty border so we get consistent padding on both sides although I have no idea if this is the idiomatic way to do this. * Call out button name in existing token help message Just to help make it clear the checkbox applies to when the connect button is pressed and not the table below. * Align existing token checkbox with URL text box To help make it clear it applies to the connect action and not to whatever happens to be displayed below in the table. * Disable next button instead of removing it UI moving around can be confusing. * Add help text and alignment fixes to changelog
1 parent db299db commit 8af9ec1

File tree

5 files changed

+154
-104
lines changed

5 files changed

+154
-104
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@
44

55
## Unreleased
66

7+
### Fixed
8+
- the help text under the IDE dropdown now takes into account whether the IDE is
9+
already installed
10+
- various minor alignment issues
11+
712
## 2.2.1 - 2023-03-23
813

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

1419
## 2.2.0 - 2023-03-08

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

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeScreenUIManager
99
import com.intellij.ui.dsl.builder.AlignX
1010
import com.intellij.ui.dsl.builder.RightGap
1111
import com.intellij.ui.dsl.builder.panel
12+
import com.intellij.util.ui.JBUI
1213
import com.intellij.util.ui.components.BorderLayoutPanel
1314
import com.jetbrains.gateway.api.GatewayUI
1415
import java.awt.Component
@@ -30,18 +31,17 @@ class CoderGatewayConnectorWizardView : BorderLayoutPanel(), Disposable {
3031
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
3132

3233
registerStep(CoderWorkspacesStepView { nextButton.isEnabled = it })
33-
registerStep(CoderLocateRemoteProjectStepView {
34-
nextButton.isVisible = false
35-
})
34+
registerStep(CoderLocateRemoteProjectStepView { nextButton.isEnabled = it })
3635

37-
addToBottom(createBackComponent())
36+
addToBottom(createButtons())
3837

3938
steps[0].apply {
4039
onInit(model)
4140
addToCenter(component)
4241
updateUI()
4342
nextButton.text = nextActionText
4443
previousButton.text = previousActionText
44+
nextButton.isEnabled = false
4545
}
4646

4747
}
@@ -73,6 +73,7 @@ class CoderGatewayConnectorWizardView : BorderLayoutPanel(), Disposable {
7373
private fun showNavigationButtons() {
7474
nextButton.isVisible = true
7575
previousButton.isVisible = true
76+
nextButton.isEnabled = false
7677
}
7778

7879
private fun next() {
@@ -101,30 +102,27 @@ class CoderGatewayConnectorWizardView : BorderLayoutPanel(), Disposable {
101102
}
102103
}
103104

104-
private fun createBackComponent(): Component {
105+
private fun createButtons(): Component {
105106
previousButton = JButton()
106107
nextButton = JButton()
107108
return panel {
108109
separator(background = WelcomeScreenUIManager.getSeparatorColor())
109-
indent {
110-
row {
111-
112-
label("").resizableColumn().align(AlignX.FILL).gap(RightGap.SMALL)
113-
previousButton = button("") { previous() }.align(AlignX.RIGHT).gap(RightGap.SMALL).applyToComponent { background = WelcomeScreenUIManager.getMainAssociatedComponentBackground() }.component
114-
nextButton = button("") { next() }.align(AlignX.RIGHT).gap(RightGap.SMALL).applyToComponent { background = WelcomeScreenUIManager.getMainAssociatedComponentBackground() }.component
115-
cell()
116-
}
117-
}.apply {
118-
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
110+
row {
111+
label("").resizableColumn().align(AlignX.FILL).gap(RightGap.SMALL)
112+
previousButton = button("") { previous() }
113+
.align(AlignX.RIGHT).gap(RightGap.SMALL)
114+
.applyToComponent { background = WelcomeScreenUIManager.getMainAssociatedComponentBackground() }.component
115+
nextButton = button("") { next() }
116+
.align(AlignX.RIGHT)
117+
.applyToComponent { background = WelcomeScreenUIManager.getMainAssociatedComponentBackground() }.component
119118
}
120-
121119
}.apply {
122120
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
121+
border = JBUI.Borders.empty(0, 16, 0, 16)
123122
}
124123
}
125124

126125
override fun dispose() {
127126
steps.clear()
128127
}
129128
}
130-

src/main/kotlin/com/coder/gateway/views/steps/CoderLocateRemoteProjectStepView.kt

Lines changed: 63 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import com.intellij.openapi.diagnostic.Logger
2020
import com.intellij.openapi.ui.ComboBox
2121
import com.intellij.openapi.ui.ComponentValidator
2222
import com.intellij.openapi.ui.ValidationInfo
23+
import com.intellij.openapi.ui.panel.ComponentPanelBuilder
2324
import com.intellij.openapi.util.Disposer
2425
import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeScreenUIManager
2526
import com.intellij.remote.AuthType
@@ -29,12 +30,9 @@ import com.intellij.ui.AnimatedIcon
2930
import com.intellij.ui.ColoredListCellRenderer
3031
import com.intellij.ui.DocumentAdapter
3132
import com.intellij.ui.components.JBTextField
32-
import com.intellij.ui.dsl.builder.AlignX
33-
import com.intellij.ui.dsl.builder.BottomGap
34-
import com.intellij.ui.dsl.builder.RowLayout
35-
import com.intellij.ui.dsl.builder.TopGap
36-
import com.intellij.ui.dsl.builder.panel
33+
import com.intellij.ui.dsl.builder.*
3734
import com.intellij.util.ui.JBFont
35+
import com.intellij.util.ui.JBUI
3836
import com.intellij.util.ui.UIUtil
3937
import com.intellij.util.ui.update.MergingUpdateQueue
4038
import com.intellij.util.ui.update.Update
@@ -73,7 +71,7 @@ import javax.swing.ListCellRenderer
7371
import javax.swing.SwingConstants
7472
import javax.swing.event.DocumentEvent
7573

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

@@ -82,44 +80,71 @@ class CoderLocateRemoteProjectStepView(private val disableNextAction: () -> Unit
8280
private lateinit var titleLabel: JLabel
8381
private lateinit var wizard: CoderWorkspacesWizardModel
8482
private lateinit var cbIDE: IDEComboBox
83+
private lateinit var cbIDEComment: JLabel
8584
private var tfProject = JBTextField()
8685
private lateinit var terminalLink: LazyBrowserLink
8786
private lateinit var ideResolvingJob: Job
8887
private val pathValidationJobs = MergingUpdateQueue("remote-path-validation", 1000, true, tfProject)
8988

9089
override val component = panel {
91-
indent {
92-
row {
93-
titleLabel = label("").applyToComponent {
94-
font = JBFont.h3().asBold()
95-
icon = CoderIcons.LOGO_16
96-
}.component
97-
}.bottomGap(BottomGap.MEDIUM)
90+
row {
91+
titleLabel = label("").applyToComponent {
92+
font = JBFont.h3().asBold()
93+
icon = CoderIcons.LOGO_16
94+
}.component
95+
}.topGap(TopGap.SMALL)
96+
row {
97+
label("IDE:")
98+
cbIDE = cell(IDEComboBox(ideComboBoxModel).apply {
99+
renderer = IDECellRenderer()
100+
addActionListener {
101+
setNextButtonEnabled(this.selectedItem != null)
102+
ApplicationManager.getApplication().invokeLater {
103+
logger.info("Selected IDE: ${this.selectedItem}")
104+
when (this.selectedItem?.status) {
105+
IdeStatus.ALREADY_INSTALLED ->
106+
cbIDEComment.text =
107+
CoderGatewayBundle.message("gateway.connector.view.coder.remoteproject.ide.installed.comment")
98108

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

107-
row {
108-
label("Project directory:")
109-
cell(tfProject).resizableColumn().align(AlignX.FILL).component
110-
cell()
111-
}.topGap(TopGap.NONE).bottomGap(BottomGap.NONE).layout(RowLayout.PARENT_GRID)
112-
row {
113-
cell()
114-
terminalLink = cell(
115-
LazyBrowserLink(
116-
CoderIcons.OPEN_TERMINAL,
117-
"Open Terminal"
118-
)
119-
).component
120-
}.topGap(TopGap.NONE).layout(RowLayout.PARENT_GRID)
121-
}
122-
}.apply { background = WelcomeScreenUIManager.getMainAssociatedComponentBackground() }
113+
else ->
114+
cbIDEComment.text =
115+
CoderGatewayBundle.message("gateway.connector.view.coder.remoteproject.ide.none.comment")
116+
}
117+
}
118+
}
119+
}).resizableColumn().align(AlignX.FILL).component
120+
}.topGap(TopGap.NONE).bottomGap(BottomGap.NONE).layout(RowLayout.PARENT_GRID)
121+
row {
122+
cell() // Empty cell for alignment.
123+
cbIDEComment = cell(
124+
ComponentPanelBuilder.createCommentComponent(
125+
CoderGatewayBundle.message("gateway.connector.view.coder.remoteproject.ide.none.comment"),
126+
false, -1, true
127+
)
128+
).component
129+
}.topGap(TopGap.NONE).bottomGap(BottomGap.NONE).layout(RowLayout.PARENT_GRID)
130+
row {
131+
label("Project directory:")
132+
cell(tfProject).resizableColumn().align(AlignX.FILL).component
133+
}.topGap(TopGap.NONE).bottomGap(BottomGap.NONE).layout(RowLayout.PARENT_GRID)
134+
row {
135+
cell() // Empty cell for alignment.
136+
terminalLink = cell(
137+
LazyBrowserLink(
138+
CoderIcons.OPEN_TERMINAL,
139+
"Open Terminal"
140+
)
141+
).component
142+
}.topGap(TopGap.NONE).layout(RowLayout.PARENT_GRID)
143+
gap(RightGap.SMALL)
144+
}.apply {
145+
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
146+
border = JBUI.Borders.empty(0, 16, 0, 16)
147+
}
123148

124149
override val previousActionText = IdeBundle.message("button.back")
125150
override val nextActionText = CoderGatewayBundle.message("gateway.connector.view.coder.remoteproject.next.text")
@@ -153,7 +178,7 @@ class CoderLocateRemoteProjectStepView(private val disableNextAction: () -> Unit
153178
is SshException -> {
154179
logger.error("Can't connect to workspace ${selectedWorkspace.name}. Reason: $e")
155180
withContext(Dispatchers.Main) {
156-
disableNextAction()
181+
setNextButtonEnabled(false)
157182
cbIDE.renderer = object : ColoredListCellRenderer<IdeWithStatus>() {
158183
override fun customizeCellRenderer(list: JList<out IdeWithStatus>, value: IdeWithStatus?, index: Int, isSelected: Boolean, cellHasFocus: Boolean) {
159184
background = UIUtil.getListBackground(isSelected, cellHasFocus)
@@ -167,7 +192,7 @@ class CoderLocateRemoteProjectStepView(private val disableNextAction: () -> Unit
167192
else -> {
168193
logger.error("Could not resolve any IDE for workspace ${selectedWorkspace.name}. Reason: $e")
169194
withContext(Dispatchers.Main) {
170-
disableNextAction()
195+
setNextButtonEnabled(false)
171196
cbIDE.renderer = object : ColoredListCellRenderer<IdeWithStatus>() {
172197
override fun customizeCellRenderer(list: JList<out IdeWithStatus>, value: IdeWithStatus?, index: Int, isSelected: Boolean, cellHasFocus: Boolean) {
173198
background = UIUtil.getListBackground(isSelected, cellHasFocus)

src/main/kotlin/com/coder/gateway/views/steps/CoderWorkspacesStepView.kt

Lines changed: 66 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ private const val SESSION_TOKEN = "session-token"
8989

9090
private const val MOUSE_OVER_TEMPLATE_NAME_COLUMN_ON_ROW = "MOUSE_OVER_TEMPLATE_NAME_COLUMN_ON_ROW"
9191

92-
class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) : CoderWorkspacesWizardStep, Disposable {
92+
class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : CoderWorkspacesWizardStep, Disposable {
9393
private val cs = CoroutineScope(Dispatchers.Main)
9494
private var localWizardModel = CoderWorkspacesWizardModel()
9595
private val coderClient: CoderRestClientService = service()
@@ -122,7 +122,7 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
122122
rowHeight = 48
123123
setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
124124
selectionModel.addListSelectionListener {
125-
enableNextButtonCallback(selectedObject != null && selectedObject?.agentStatus == RUNNING && selectedObject?.agentOS == OS.LINUX)
125+
setNextButtonEnabled(selectedObject != null && selectedObject?.agentStatus == RUNNING && selectedObject?.agentOS == OS.LINUX)
126126
if (selectedObject?.agentStatus == RUNNING && selectedObject?.agentOS != OS.LINUX) {
127127
notificationBanner.apply {
128128
component.isVisible = true
@@ -194,55 +194,74 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
194194
private var poller: Job? = null
195195

196196
override val component = panel {
197-
indent {
198-
row {
199-
label(CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.header.text")).applyToComponent {
200-
font = JBFont.h3().asBold()
201-
icon = CoderIcons.LOGO_16
202-
}
203-
}.topGap(TopGap.SMALL)
204-
row {
205-
cell(ComponentPanelBuilder.createCommentComponent(CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.comment"), false, -1, true))
206-
}
207-
row {
208-
browserLink(CoderGatewayBundle.message("gateway.connector.view.login.documentation.action"), "https://coder.com/docs/coder-oss/latest/workspaces")
209-
}
210-
row(CoderGatewayBundle.message("gateway.connector.view.login.url.label")) {
211-
tfUrl = textField().resizableColumn().align(AlignX.FILL).gap(RightGap.SMALL).bindText(localWizardModel::coderURL).applyToComponent {
212-
addActionListener {
213-
poller?.cancel()
214-
listTableModelOfWorkspaces.items = emptyList()
215-
askTokenAndOpenSession(true)
216-
}
217-
}.component
218-
button(CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.connect.text")) {
197+
row {
198+
label(CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.header.text")).applyToComponent {
199+
font = JBFont.h3().asBold()
200+
icon = CoderIcons.LOGO_16
201+
}
202+
}.topGap(TopGap.SMALL)
203+
row {
204+
cell(
205+
ComponentPanelBuilder.createCommentComponent(
206+
CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.comment"),
207+
false,
208+
-1,
209+
true
210+
)
211+
)
212+
}
213+
row {
214+
browserLink(
215+
CoderGatewayBundle.message("gateway.connector.view.login.documentation.action"),
216+
"https://coder.com/docs/coder-oss/latest/workspaces"
217+
)
218+
}
219+
row(CoderGatewayBundle.message("gateway.connector.view.login.url.label")) {
220+
tfUrl = textField().resizableColumn().align(AlignX.FILL).gap(RightGap.SMALL)
221+
.bindText(localWizardModel::coderURL).applyToComponent {
222+
addActionListener {
219223
poller?.cancel()
220224
listTableModelOfWorkspaces.items = emptyList()
221225
askTokenAndOpenSession(true)
222-
}.applyToComponent {
223-
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
224226
}
225-
cell()
226-
}
227-
row {
228-
cbExistingToken = checkBox(CoderGatewayBundle.message("gateway.connector.view.login.existing-token.label"))
229-
.bindSelected(localWizardModel::useExistingToken)
230-
.component
231-
}
232-
row {
233-
cell(ComponentPanelBuilder.createCommentComponent(
234-
CoderGatewayBundle.message("gateway.connector.view.login.existing-token.tooltip",
235-
CoderGatewayBundle.message("gateway.connector.view.login.existing-token.label")),
236-
false, -1, true))
237-
}
238-
row {
239-
scrollCell(toolbar.createPanel().apply {
240-
add(notificationBanner.component.apply { isVisible = false }, "South")
241-
}).resizableColumn().align(AlignX.FILL).align(AlignY.FILL)
242-
cell()
243-
}.topGap(TopGap.NONE).bottomGap(BottomGap.NONE).resizableRow()
244-
}
245-
}.apply { background = WelcomeScreenUIManager.getMainAssociatedComponentBackground() }
227+
}.component
228+
button(CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.connect.text")) {
229+
poller?.cancel()
230+
listTableModelOfWorkspaces.items = emptyList()
231+
askTokenAndOpenSession(true)
232+
}.applyToComponent {
233+
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
234+
}
235+
}.layout(RowLayout.PARENT_GRID)
236+
row {
237+
cell() // Empty cell for alignment.
238+
cbExistingToken = checkBox(CoderGatewayBundle.message("gateway.connector.view.login.existing-token.label"))
239+
.bindSelected(localWizardModel::useExistingToken)
240+
.component
241+
}.layout(RowLayout.PARENT_GRID)
242+
row {
243+
cell() // Empty cell for alignment.
244+
cell(
245+
ComponentPanelBuilder.createCommentComponent(
246+
CoderGatewayBundle.message(
247+
"gateway.connector.view.login.existing-token.tooltip",
248+
CoderGatewayBundle.message("gateway.connector.view.login.existing-token.label"),
249+
CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.connect.text")
250+
),
251+
false, -1, true
252+
)
253+
)
254+
}.layout(RowLayout.PARENT_GRID)
255+
row {
256+
scrollCell(toolbar.createPanel().apply {
257+
add(notificationBanner.component.apply { isVisible = false }, "South")
258+
}).resizableColumn().align(AlignX.FILL).align(AlignY.FILL)
259+
}.topGap(TopGap.NONE).bottomGap(BottomGap.NONE).resizableRow()
260+
261+
}.apply {
262+
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
263+
border = JBUI.Borders.empty(0, 16, 0, 16)
264+
}
246265

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

0 commit comments

Comments
 (0)