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() uiComponent.text = newValue.toString()
} }
@@ -5,12 +5,15 @@
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components 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.ValuesReadingContext
import org.jetbrains.kotlin.tools.projectWizard.core.entity.* 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.DynamicComponent
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.FocusableComponent 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.panel
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ErrorAwareComponent 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.SettingComponent
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator
import java.awt.BorderLayout import java.awt.BorderLayout
@@ -21,13 +24,19 @@ abstract class UIComponent<V : Any>(
labelText: String? = null, labelText: String? = null,
private val validator: SettingValidator<V>? = null, private val validator: SettingValidator<V>? = null,
private val onValueUpdate: (V) -> Unit = {} private val onValueUpdate: (V) -> Unit = {}
) : DynamicComponent(valuesReadingContext), ErrorAwareComponent, FocusableComponent { ) : DynamicComponent(valuesReadingContext), ErrorAwareComponent, FocusableComponent, Disposable {
private val validationIndicator = if (validator != null) private val validationIndicator by lazy(LazyThreadSafetyMode.NONE) {
ValidationIndicator(defaultText = labelText, showText = true) if (validator != null)
else null IdeaBasedComponentValidator(this, getValidatorTarget())
else null
}
override fun dispose() {}
protected abstract val uiComponent: JComponent protected abstract val uiComponent: JComponent
protected open fun getValidatorTarget() = uiComponent
abstract fun updateUiValue(newValue: V) abstract fun updateUiValue(newValue: V)
abstract fun getUiValue(): V? abstract fun getUiValue(): V?
@@ -47,7 +56,7 @@ abstract class UIComponent<V : Any>(
final override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) { final override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) {
panel { panel {
validationIndicator?.let { add(it, BorderLayout.NORTH) } labelText?.let { add(label("$it:"), BorderLayout.NORTH) }
add(uiComponent, BorderLayout.CENTER) add(uiComponent, BorderLayout.CENTER)
} }
} }
@@ -62,7 +71,7 @@ abstract class UIComponent<V : Any>(
fun validate(value: V) { fun validate(value: V) {
if (validator == null) return if (validator == null) return
if (validationIndicator == null) return if (validationIndicator == null) return
validationIndicator.validationState = validator.validate(valuesReadingContext, value) validationIndicator?.updateValidationState(validator.validate(valuesReadingContext, value))
} }
override fun focusOn() { 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.DisplayableSettingItem
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module 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.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.SettingComponent
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator
import java.awt.BorderLayout import java.awt.BorderLayout
import javax.swing.BorderFactory import javax.swing.BorderFactory
import javax.swing.JComponent import javax.swing.JComponent
@@ -66,11 +66,9 @@ class ModulesEditorComponent(
} }
} }
override val validationIndicator = ValidationIndicator(showText = true) { error -> override val validationIndicator = AlwaysShownValidationIndicator(showText = true) { error ->
val module = error.target as? Module ?: return@ValidationIndicator val module = error.target as? Module ?: return@AlwaysShownValidationIndicator
tree.selectModule(module) tree.selectModule(module)
selectSettingWithError(error) 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) { override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) {
panel { panel {
add( add(
panel { panel {
add(validationIndicator, BorderLayout.WEST)
add(settingLabel, BorderLayout.CENTER) add(settingLabel, BorderLayout.CENTER)
}, },
BorderLayout.WEST BorderLayout.WEST
@@ -36,7 +36,7 @@ abstract class SettingComponent<V : Any, T: SettingType<V>>(
private fun updateValidationState() { private fun updateValidationState() {
val value = value val value = value
if (validationIndicator != null && value != null) { 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 package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting
import com.intellij.icons.AllIcons import com.intellij.icons.AllIcons
import com.intellij.ui.JBColor import com.intellij.openapi.Disposable
import com.intellij.util.ui.EmptyIcon 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.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.tools.projectWizard.wizard.ui.label
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.awt.BorderLayout import java.awt.BorderLayout
import java.awt.Cursor import java.awt.Cursor
import java.awt.event.MouseEvent import java.awt.event.MouseEvent
import java.awt.event.MouseListener import java.awt.event.MouseListener
import javax.swing.BorderFactory import javax.swing.BorderFactory
import javax.swing.Icon import javax.swing.JComponent
import javax.swing.JPanel import javax.swing.JPanel
class ValidationIndicator( interface ValidationIndicator {
defaultText: String? = null, 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 showText: Boolean,
private val onClickAction: ((ValidationResult.ValidationError) -> Unit)? = null private val onClickAction: ((ValidationResult.ValidationError) -> Unit)? = null
) : JPanel(BorderLayout()) { ) : JPanel(BorderLayout()), ValidationIndicator {
private val textLabel = defaultText?.let { label("$it: ") } private val errorLabel = label(" ")
private val errorLabel = label(" ").apply {
foreground = JBColor.red
}
private var currentError: ValidationResult.ValidationError? = null private var currentError: ValidationResult.ValidationError? = null
init { init {
border = BorderFactory.createEmptyBorder(3, 3, 3, 3) errorLabel.icon = AllIcons.General.Error
textLabel?.let { add(it, BorderLayout.WEST) } background = JBUI.CurrentTheme.Validator.errorBackgroundColor()
border =
BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(JBUI.CurrentTheme.Validator.errorBorderColor()),
JBUI.Borders.empty(4, 8)
)
add(errorLabel, BorderLayout.CENTER) add(errorLabel, BorderLayout.CENTER)
if (onClickAction != null) { if (onClickAction != null) {
@@ -47,32 +69,17 @@ class ValidationIndicator(
} }
} }
private fun setIcon(icon: Icon?) { override fun updateValidationState(newState: ValidationResult) {
if (textLabel != null) textLabel.icon = icon isVisible = !newState.isOk
else errorLabel.icon = icon ?: EmptyIcon.ICON_16 when (newState) {
} ValidationResult.OK -> {
currentError = null
private fun String.linkifyErrorText() = }
if (onClickAction == null) this is ValidationResult.ValidationError -> {
else "<u>$this</u>".asHtml() val errors = newState.messages.firstOrNull().orEmpty()
if (showText) errorLabel.text = errors
var validationState: ValidationResult = ValidationResult.OK currentError = newState
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
}
} }
} }
}
} }