11package com.mitteloupe.cag.cleanarchitecturegenerator
22
33import com.intellij.icons.AllIcons
4- import com.intellij.openapi.fileChooser.FileChooser
54import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
65import com.intellij.openapi.project.Project
76import com.intellij.openapi.ui.ComboBox
7+ import com.intellij.openapi.ui.DialogPanel
88import com.intellij.openapi.ui.DialogWrapper
9- import com.intellij.openapi.ui.TextFieldWithBrowseButton
109import 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
1510import com.intellij.ui.dsl.builder.bindText
1611import com.intellij.ui.dsl.builder.panel
1712import com.intellij.ui.dsl.builder.whenItemChangedFromUi
1813import com.intellij.util.ui.UIUtil
19- import com.mitteloupe.cag.cleanarchitecturegenerator.form.OnChangeDocumentListener
2014import com.mitteloupe.cag.cleanarchitecturegenerator.form.PredicateDocumentFilter
2115import com.mitteloupe.cag.cleanarchitecturegenerator.validation.SymbolValidator
2216import java.awt.EventQueue.invokeLater
2317import java.io.File
2418import javax.swing.DefaultComboBoxModel
19+ import javax.swing.JComponent
20+ import javax.swing.JLabel
2521import javax.swing.JTextField
22+ import javax.swing.event.DocumentEvent
23+ import javax.swing.event.DocumentListener
2624import javax.swing.text.AbstractDocument
2725
2826private const val USE_CASE_SUFFIX = " UseCase"
2927private const val DEFAULT_USE_CASE_NAME = " DoSomething"
3028private const val DEFAULT_DATA_TYPE = " Unit"
3129
3230class 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 }
0 commit comments