Skip to content

Commit 3945160

Browse files
committed
Further migration to UI DSL v2.
1 parent 8082d0d commit 3945160

File tree

3 files changed

+110
-114
lines changed

3 files changed

+110
-114
lines changed

plugin/src/main/kotlin/com/mitteloupe/cag/cleanarchitecturegenerator/CreateUseCaseDialog.kt

Lines changed: 76 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,44 @@
11
package com.mitteloupe.cag.cleanarchitecturegenerator
22

33
import com.intellij.icons.AllIcons
4-
import com.intellij.openapi.fileChooser.FileChooser
54
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
65
import com.intellij.openapi.project.Project
76
import com.intellij.openapi.ui.ComboBox
7+
import com.intellij.openapi.ui.DialogPanel
88
import com.intellij.openapi.ui.DialogWrapper
9-
import com.intellij.openapi.ui.TextFieldWithBrowseButton
109
import com.intellij.openapi.ui.ValidationInfo
11-
import com.intellij.openapi.vfs.LocalFileSystem
12-
import com.intellij.ui.components.JBLabel
13-
import com.intellij.ui.components.JBTextField
14-
import com.intellij.ui.dsl.builder.Align
1510
import com.intellij.ui.dsl.builder.bindText
1611
import com.intellij.ui.dsl.builder.panel
1712
import com.intellij.ui.dsl.builder.whenItemChangedFromUi
1813
import com.intellij.util.ui.UIUtil
19-
import com.mitteloupe.cag.cleanarchitecturegenerator.form.OnChangeDocumentListener
2014
import com.mitteloupe.cag.cleanarchitecturegenerator.form.PredicateDocumentFilter
2115
import com.mitteloupe.cag.cleanarchitecturegenerator.validation.SymbolValidator
2216
import java.awt.EventQueue.invokeLater
2317
import java.io.File
2418
import javax.swing.DefaultComboBoxModel
19+
import javax.swing.JComponent
20+
import javax.swing.JLabel
2521
import javax.swing.JTextField
22+
import javax.swing.event.DocumentEvent
23+
import javax.swing.event.DocumentListener
2624
import javax.swing.text.AbstractDocument
2725

2826
private const val USE_CASE_SUFFIX = "UseCase"
2927
private const val DEFAULT_USE_CASE_NAME = "DoSomething"
3028
private const val DEFAULT_DATA_TYPE = "Unit"
3129

3230
class CreateUseCaseDialog(
33-
project: Project?,
31+
private val project: Project?,
3432
suggestedDirectory: File?
3533
) : DialogWrapper(project) {
36-
private val useCaseNameTextField = JBTextField()
37-
private val directoryField = TextFieldWithBrowseButton()
34+
private val initialDirectory = suggestedDirectory?.absolutePath.orEmpty()
35+
private lateinit var useCaseNameTextField: JComponent
36+
private var useCaseNameText: String = ""
37+
private var directoryPath: String = initialDirectory
3838
private lateinit var inputDataTypeComboBox: ComboBox<String>
39-
private val inputWarningLabel = JBLabel()
39+
private lateinit var inputWarningLabel: JLabel
4040
private lateinit var outputDataTypeComboBox: ComboBox<String>
41-
private val outputWarningLabel = JBLabel()
41+
private lateinit var outputWarningLabel: JLabel
4242
private val modelClassFinder = ModelClassFinder()
4343
private val symbolValidator = SymbolValidator()
4444
private val inputDataTypeModel = DefaultComboBoxModel<String>()
@@ -48,7 +48,7 @@ class CreateUseCaseDialog(
4848
get() = useCaseName.removeSuffix(USE_CASE_SUFFIX) + USE_CASE_SUFFIX
4949

5050
private val useCaseName: String
51-
get() = useCaseNameTextField.text.trim()
51+
get() = useCaseNameText.trim()
5252

5353
private var _inputDataType: String? = null
5454
val inputDataType: String?
@@ -59,92 +59,91 @@ class CreateUseCaseDialog(
5959
get() = _outputDataType
6060

6161
val destinationDirectory: File?
62-
get() = directoryField.text.trim().takeIf { it.isNotEmpty() }?.let { File(it) }
62+
get() = directoryPath.trim().takeIf { it.isNotEmpty() }?.let { File(it) }
6363

6464
init {
6565
title = CleanArchitectureGeneratorBundle.message("info.usecase.generator.title")
6666
init()
6767

68-
useCaseNameTextField.columns = 20
69-
directoryField.text = suggestedDirectory?.absolutePath.orEmpty()
70-
directoryField.addActionListener {
71-
val descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor()
72-
val initialDirectory = LocalFileSystem.getInstance().findFileByIoFile(File(directoryField.text))
73-
val chosen = FileChooser.chooseFile(descriptor, project, initialDirectory)
74-
if (chosen != null) {
75-
directoryField.text = chosen.path
76-
setupDataTypeComboBoxes()
77-
}
78-
}
79-
80-
useCaseNameTextField.text = DEFAULT_USE_CASE_NAME
81-
useCaseNameTextField.selectAll()
82-
83-
(useCaseNameTextField.document as AbstractDocument).documentFilter =
84-
PredicateDocumentFilter { !it.isWhitespace() }
85-
8668
setupDataTypeComboBoxes()
8769
setupWarningLabels()
8870
}
8971

90-
override fun getPreferredFocusedComponent() = useCaseNameTextField
91-
92-
override fun show() {
93-
super.show()
94-
95-
invokeLater {
96-
validateFieldOnChange()
97-
}
98-
}
99-
100-
override fun createCenterPanel() =
101-
panel {
72+
override fun createCenterPanel(): DialogPanel {
73+
return panel {
10274
row(CleanArchitectureGeneratorBundle.message("dialog.usecase.name.label")) {
10375
textField()
104-
.bindText(useCaseNameTextField::getText, useCaseNameTextField::setText)
105-
cell(
106-
JBLabel(USE_CASE_SUFFIX).apply {
107-
foreground = UIUtil.getLabelDisabledForeground()
76+
.bindText(::useCaseNameText)
77+
.onChanged { useCaseNameText = it.text }
78+
.applyToComponent {
79+
useCaseNameTextField = this
80+
invokeLater {
81+
columns = 20
82+
text = DEFAULT_USE_CASE_NAME
83+
selectAll()
84+
requestFocusInWindow()
85+
}
86+
(document as AbstractDocument).documentFilter = PredicateDocumentFilter { !it.isWhitespace() }
10887
}
109-
)
88+
label(USE_CASE_SUFFIX)
89+
.applyToComponent { foreground = UIUtil.getLabelDisabledForeground() }
11090
}
11191
row(CleanArchitectureGeneratorBundle.message("dialog.usecase.input.type.label")) {
11292
@Suppress("UnstableApiUsage")
11393
comboBox(inputDataTypeModel)
11494
.whenItemChangedFromUi { _inputDataType = it }
11595
.applyToComponent {
96+
inputDataTypeComboBox = this
11697
isEditable = true
117-
(editor.editorComponent as JTextField).validateOnChange {
118-
_inputDataType = text
98+
val textField = editor.editorComponent as JTextField
99+
textField.handleChange {
100+
println("ERAN: CHANGE! ${textField.text}")
101+
_inputDataType = textField.text
102+
inputDataType.validateDataType(inputWarningLabel)
119103
}
120-
selectedItem = DEFAULT_DATA_TYPE
121-
inputDataTypeComboBox = this
104+
invokeLater { selectedItem = DEFAULT_DATA_TYPE }
122105
}
123-
cell(inputWarningLabel)
106+
label("").applyToComponent { inputWarningLabel = this }
124107
}
125108
row(CleanArchitectureGeneratorBundle.message("dialog.usecase.output.type.label")) {
126109
@Suppress("UnstableApiUsage")
127110
comboBox(outputDataTypeModel)
128111
.whenItemChangedFromUi { _outputDataType = it }
129112
.applyToComponent {
113+
outputDataTypeComboBox = this
130114
isEditable = true
131-
(editor.editorComponent as JTextField).validateOnChange {
132-
_inputDataType = text
115+
val textField = editor.editorComponent as JTextField
116+
textField.handleChange {
117+
println("ERAN: CHANGE! ${textField.text}")
118+
_outputDataType = textField.text
119+
outputDataType.validateDataType(outputWarningLabel)
133120
}
134-
addActionListener {
135-
validateFieldOnChange()
136-
}
137-
selectedItem = DEFAULT_DATA_TYPE
138-
outputDataTypeComboBox = this
121+
invokeLater { selectedItem = DEFAULT_DATA_TYPE }
139122
}
140-
cell(outputWarningLabel)
123+
label("").applyToComponent { outputWarningLabel = this }
141124
}
142125
row(CleanArchitectureGeneratorBundle.message("dialog.usecase.directory.field.label")) {
143-
cell(directoryField)
144-
.comment(CleanArchitectureGeneratorBundle.message("dialog.usecase.directory.comment"))
145-
.align(Align.FILL)
126+
@Suppress("UnstableApiUsage")
127+
textFieldWithBrowseButton(
128+
project = project,
129+
fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(),
130+
fileChosen = {
131+
it.path.also { path ->
132+
directoryPath = path
133+
setupDataTypeComboBoxes()
134+
}
135+
}
136+
)
137+
.bindText(::directoryPath)
138+
.applyToComponent {
139+
invokeLater {
140+
text = initialDirectory
141+
}
142+
toolTipText = CleanArchitectureGeneratorBundle.message("dialog.usecase.directory.comment")
143+
}
146144
}
147145
}
146+
}
148147

149148
override fun doValidate(): ValidationInfo? =
150149
if (useCaseName.isEmpty()) {
@@ -161,11 +160,6 @@ class CreateUseCaseDialog(
161160
outputWarningLabel.clearWarning()
162161
}
163162

164-
private fun validateFieldOnChange() {
165-
inputDataType.validateDataType(inputWarningLabel)
166-
outputDataType.validateDataType(outputWarningLabel)
167-
}
168-
169163
private fun validateInputOutputTypes(): ValidationInfo? {
170164
val destinationDirectory = destinationDirectory ?: return null
171165

@@ -200,7 +194,7 @@ class CreateUseCaseDialog(
200194
return null
201195
}
202196

203-
private fun String?.validateDataType(warningLabel: JBLabel) {
197+
private fun String?.validateDataType(warningLabel: JLabel) {
204198
if (isNullOrEmpty()) {
205199
warningLabel.clearWarning()
206200
return
@@ -213,26 +207,24 @@ class CreateUseCaseDialog(
213207

214208
val destinationDirectory = destinationDirectory
215209
if (destinationDirectory != null && !symbolValidator.isValidSymbolInContext(this, destinationDirectory)) {
216-
warningLabel.showFieldWarning(
217-
CleanArchitectureGeneratorBundle.message("error.symbol.not.found")
218-
)
210+
warningLabel.showFieldWarning(CleanArchitectureGeneratorBundle.message("error.symbol.not.found"))
219211
} else {
220212
warningLabel.clearWarning()
221213
}
222214
}
223215

224-
private fun JBLabel.clearWarning() {
216+
private fun JLabel.clearWarning() {
225217
icon = null
226218
text = " "
227219
}
228220

229-
private fun JBLabel.showFieldWarning(message: String) {
221+
private fun JLabel.showFieldWarning(message: String) {
230222
icon = AllIcons.General.Warning
231223
text = message
232224
}
233225

234226
private fun setupDataTypeComboBoxes() {
235-
destinationDirectory?.let { destinationDirectory ->
227+
File(initialDirectory).let { destinationDirectory ->
236228
val modelClasses = modelClassFinder.findModelClasses(destinationDirectory)
237229
val allOptions = ModelClassFinder.PRIMITIVE_TYPES + modelClasses
238230

@@ -241,11 +233,14 @@ class CreateUseCaseDialog(
241233
}
242234
}
243235

244-
private fun JTextField.validateOnChange(onChange: JTextField.() -> Unit) {
236+
private fun JTextField.handleChange(onChange: () -> Unit) {
245237
document.addDocumentListener(
246-
OnChangeDocumentListener {
247-
onChange()
248-
validateFieldOnChange()
238+
object : DocumentListener {
239+
override fun insertUpdate(e: DocumentEvent) = onChange()
240+
241+
override fun removeUpdate(e: DocumentEvent) = onChange()
242+
243+
override fun changedUpdate(e: DocumentEvent) = onChange()
249244
}
250245
)
251246
}

plugin/src/main/kotlin/com/mitteloupe/cag/cleanarchitecturegenerator/CreateViewModelDialog.kt

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
11
package com.mitteloupe.cag.cleanarchitecturegenerator
22

3-
import com.intellij.openapi.fileChooser.FileChooser
43
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
54
import com.intellij.openapi.project.Project
65
import com.intellij.openapi.ui.DialogWrapper
7-
import com.intellij.openapi.ui.TextFieldWithBrowseButton
86
import com.intellij.openapi.ui.ValidationInfo
9-
import com.intellij.openapi.vfs.LocalFileSystem
10-
import com.intellij.ui.components.JBTextField
11-
import com.intellij.ui.dsl.builder.Align
127
import com.intellij.ui.dsl.builder.bindText
138
import com.intellij.ui.dsl.builder.panel
149
import com.intellij.util.ui.UIUtil
1510
import com.mitteloupe.cag.cleanarchitecturegenerator.form.PredicateDocumentFilter
1611
import com.mitteloupe.cag.cleanarchitecturegenerator.validation.SymbolValidator
12+
import java.awt.EventQueue.invokeLater
1713
import java.io.File
1814
import javax.swing.JComponent
1915
import javax.swing.text.AbstractDocument
@@ -22,57 +18,62 @@ private const val VIEW_MODEL_SUFFIX = "ViewModel"
2218
private const val DEFAULT_VIEW_MODEL_NAME = "My"
2319

2420
class CreateViewModelDialog(
25-
project: Project?,
26-
suggestedDirectory: File?
21+
private val project: Project?,
22+
private val suggestedDirectory: File?
2723
) : DialogWrapper(project) {
28-
private val viewModelNameTextField = JBTextField()
29-
private val directoryField = TextFieldWithBrowseButton()
24+
private var viewModelNameText: String = ""
25+
private var directoryPath: String = ""
3026
private val symbolValidator = SymbolValidator()
3127

3228
val viewModelNameWithSuffix: String
3329
get() = viewModelName.removeSuffix(VIEW_MODEL_SUFFIX) + VIEW_MODEL_SUFFIX
3430

3531
private val viewModelName: String
36-
get() = viewModelNameTextField.text.trim()
32+
get() = viewModelNameText.trim()
3733

3834
val destinationDirectory: File?
39-
get() = if (directoryField.text.isNotEmpty()) File(directoryField.text) else null
35+
get() =
36+
if (directoryPath.isNotEmpty()) {
37+
File(directoryPath)
38+
} else {
39+
null
40+
}
4041

4142
init {
4243
title = CleanArchitectureGeneratorBundle.message("info.viewmodel.generator.title")
4344
init()
44-
45-
viewModelNameTextField.columns = 20
46-
directoryField.text = suggestedDirectory?.absolutePath.orEmpty()
47-
directoryField.addActionListener {
48-
val descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor()
49-
val initialDirectory = LocalFileSystem.getInstance().findFileByIoFile(File(directoryField.text))
50-
val chosen = FileChooser.chooseFile(descriptor, project, initialDirectory)
51-
if (chosen != null) {
52-
directoryField.text = chosen.path
53-
}
54-
}
55-
56-
viewModelNameTextField.text = DEFAULT_VIEW_MODEL_NAME
57-
viewModelNameTextField.selectAll()
58-
59-
(viewModelNameTextField.document as AbstractDocument).documentFilter =
60-
PredicateDocumentFilter { !it.isWhitespace() }
6145
}
6246

6347
override fun createCenterPanel(): JComponent =
6448
panel {
6549
row(CleanArchitectureGeneratorBundle.message("dialog.viewmodel.name.label")) {
6650
textField()
67-
.bindText(viewModelNameTextField::getText, viewModelNameTextField::setText)
51+
.bindText(::viewModelNameText)
52+
.onChanged { viewModelNameText = it.text }
53+
.applyToComponent {
54+
invokeLater {
55+
columns = 20
56+
text = DEFAULT_VIEW_MODEL_NAME
57+
selectAll()
58+
}
59+
toolTipText = CleanArchitectureGeneratorBundle.message("dialog.viewmodel.directory.tooltip")
60+
(document as AbstractDocument).documentFilter = PredicateDocumentFilter { !it.isWhitespace() }
61+
}
6862
label(VIEW_MODEL_SUFFIX)
6963
.applyToComponent { foreground = UIUtil.getLabelDisabledForeground() }
7064
}
7165

7266
row(CleanArchitectureGeneratorBundle.message("dialog.viewmodel.directory.field.label")) {
73-
cell(directoryField)
74-
.comment(CleanArchitectureGeneratorBundle.message("dialog.viewmodel.directory.comment"))
75-
.align(Align.FILL)
67+
@Suppress("UnstableApiUsage")
68+
textFieldWithBrowseButton(
69+
project = project,
70+
fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor()
71+
)
72+
.bindText(::directoryPath)
73+
.applyToComponent {
74+
invokeLater { text = suggestedDirectory?.absolutePath.orEmpty() }
75+
toolTipText = CleanArchitectureGeneratorBundle.message("dialog.viewmodel.directory.tooltip")
76+
}
7677
}
7778
}
7879

0 commit comments

Comments
 (0)