Wizard: use IntelliJ validator in UI instead of custom one

This commit is contained in:
Ilya Kirillov
2020-02-12 19:46:43 +03:00
parent 1431649c4d
commit 72f92d7a3b
6 changed files with 70 additions and 55 deletions
@@ -41,7 +41,9 @@ class PathFieldComponent(
)
}
override fun updateUiValue(newValue: Path)= safeUpdateUi {
override fun getValidatorTarget(): JComponent = uiComponent.textField
override fun updateUiValue(newValue: Path) = safeUpdateUi {
uiComponent.text = newValue.toString()
}
@@ -5,12 +5,15 @@
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components
import com.intellij.openapi.Disposable
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.DynamicComponent
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.FocusableComponent
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.label
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.panel
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ErrorAwareComponent
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.IdeaBasedComponentValidator
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.SettingComponent
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator
import java.awt.BorderLayout
@@ -21,13 +24,19 @@ abstract class UIComponent<V : Any>(
labelText: String? = null,
private val validator: SettingValidator<V>? = null,
private val onValueUpdate: (V) -> Unit = {}
) : DynamicComponent(valuesReadingContext), ErrorAwareComponent, FocusableComponent {
private val validationIndicator = if (validator != null)
ValidationIndicator(defaultText = labelText, showText = true)
else null
) : DynamicComponent(valuesReadingContext), ErrorAwareComponent, FocusableComponent, Disposable {
private val validationIndicator by lazy(LazyThreadSafetyMode.NONE) {
if (validator != null)
IdeaBasedComponentValidator(this, getValidatorTarget())
else null
}
override fun dispose() {}
protected abstract val uiComponent: JComponent
protected open fun getValidatorTarget() = uiComponent
abstract fun updateUiValue(newValue: V)
abstract fun getUiValue(): V?
@@ -47,7 +56,7 @@ abstract class UIComponent<V : Any>(
final override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) {
panel {
validationIndicator?.let { add(it, BorderLayout.NORTH) }
labelText?.let { add(label("$it:"), BorderLayout.NORTH) }
add(uiComponent, BorderLayout.CENTER)
}
}
@@ -62,7 +71,7 @@ abstract class UIComponent<V : Any>(
fun validate(value: V) {
if (validator == null) return
if (validationIndicator == null) return
validationIndicator.validationState = validator.validate(valuesReadingContext, value)
validationIndicator?.updateValidationState(validator.validate(valuesReadingContext, value))
}
override fun focusOn() {
@@ -11,8 +11,8 @@ import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ProjectKind
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.panel
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.AlwaysShownValidationIndicator
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.SettingComponent
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator
import java.awt.BorderLayout
import javax.swing.BorderFactory
import javax.swing.JComponent
@@ -66,11 +66,9 @@ class ModulesEditorComponent(
}
}
override val validationIndicator = ValidationIndicator(showText = true) { error ->
val module = error.target as? Module ?: return@ValidationIndicator
override val validationIndicator = AlwaysShownValidationIndicator(showText = true) { error ->
val module = error.target as? Module ?: return@AlwaysShownValidationIndicator
tree.selectModule(module)
selectSettingWithError(error)
}.apply {
background = tree.background
}
}
@@ -73,12 +73,11 @@ class VersionSettingComponent(
}
}
override val validationIndicator = ValidationIndicator(showText = false)
override val validationIndicator = null
override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) {
panel {
add(
panel {
add(validationIndicator, BorderLayout.WEST)
add(settingLabel, BorderLayout.CENTER)
},
BorderLayout.WEST
@@ -36,7 +36,7 @@ abstract class SettingComponent<V : Any, T: SettingType<V>>(
private fun updateValidationState() {
val value = value
if (validationIndicator != null && value != null) {
validationIndicator!!.validationState = setting.validator.validate(valuesReadingContext, value)
validationIndicator!!.updateValidationState(setting.validator.validate(valuesReadingContext, value))
}
}
}
@@ -1,35 +1,57 @@
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting
import com.intellij.icons.AllIcons
import com.intellij.ui.JBColor
import com.intellij.util.ui.EmptyIcon
import com.intellij.openapi.Disposable
import com.intellij.openapi.ui.ComponentValidator
import com.intellij.openapi.ui.ValidationInfo
import com.intellij.util.ui.JBUI
import org.jetbrains.kotlin.tools.projectWizard.core.entity.ValidationResult
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.asHtml
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.label
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.awt.BorderLayout
import java.awt.Cursor
import java.awt.event.MouseEvent
import java.awt.event.MouseListener
import javax.swing.BorderFactory
import javax.swing.Icon
import javax.swing.JComponent
import javax.swing.JPanel
class ValidationIndicator(
defaultText: String? = null,
interface ValidationIndicator {
fun updateValidationState(newState: ValidationResult)
}
class IdeaBasedComponentValidator(
parentDisposable: Disposable,
private val jComponent: JComponent
) : ValidationIndicator {
private val validator = ComponentValidator(parentDisposable).installOn(jComponent)
override fun updateValidationState(newState: ValidationResult) {
validator.updateInfo(
newState.safeAs<ValidationResult.ValidationError>()
?.messages
?.firstOrNull()
?.let { ValidationInfo(it, jComponent) }
)
}
}
class AlwaysShownValidationIndicator(
private val showText: Boolean,
private val onClickAction: ((ValidationResult.ValidationError) -> Unit)? = null
) : JPanel(BorderLayout()) {
private val textLabel = defaultText?.let { label("$it: ") }
private val errorLabel = label(" ").apply {
foreground = JBColor.red
}
) : JPanel(BorderLayout()), ValidationIndicator {
private val errorLabel = label(" ")
private var currentError: ValidationResult.ValidationError? = null
init {
border = BorderFactory.createEmptyBorder(3, 3, 3, 3)
textLabel?.let { add(it, BorderLayout.WEST) }
errorLabel.icon = AllIcons.General.Error
background = JBUI.CurrentTheme.Validator.errorBackgroundColor()
border =
BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(JBUI.CurrentTheme.Validator.errorBorderColor()),
JBUI.Borders.empty(4, 8)
)
add(errorLabel, BorderLayout.CENTER)
if (onClickAction != null) {
@@ -47,32 +69,17 @@ class ValidationIndicator(
}
}
private fun setIcon(icon: Icon?) {
if (textLabel != null) textLabel.icon = icon
else errorLabel.icon = icon ?: EmptyIcon.ICON_16
}
private fun String.linkifyErrorText() =
if (onClickAction == null) this
else "<u>$this</u>".asHtml()
var validationState: ValidationResult = ValidationResult.OK
set(value) {
field = value
when (value) {
ValidationResult.OK -> {
setIcon(null)
errorLabel.text = " "
toolTipText = null
currentError = null
}
is ValidationResult.ValidationError -> {
setIcon(AllIcons.General.Error)
val errors = value.messages.firstOrNull().orEmpty()
toolTipText = errors
if (showText) errorLabel.text = errors.linkifyErrorText()
currentError = value
}
override fun updateValidationState(newState: ValidationResult) {
isVisible = !newState.isOk
when (newState) {
ValidationResult.OK -> {
currentError = null
}
is ValidationResult.ValidationError -> {
val errors = newState.messages.firstOrNull().orEmpty()
if (showText) errorLabel.text = errors
currentError = newState
}
}
}
}