diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/Component.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/Component.kt index 8e69e3e612f..1c8bf8d18d1 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/Component.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/Component.kt @@ -47,4 +47,8 @@ abstract class DynamicComponent(private val valuesReadingContext: ValuesReadingC reader(valuesReadingContext) open fun onValueUpdated(reference: SettingReference<*, *>?) {} +} + +interface FocusableComponent { + fun focusOn() {} } \ No newline at end of file diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/components/PathFieldComponent.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/components/PathFieldComponent.kt index 13816dbd500..c93c01ef2bd 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/components/PathFieldComponent.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/components/PathFieldComponent.kt @@ -46,4 +46,8 @@ class PathFieldComponent( } override fun getUiValue(): Path = Paths.get(uiComponent.text.trim()) + + override fun focusOn() { + uiComponent.textField.requestFocus() + } } \ No newline at end of file diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/components/UIComponent.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/components/UIComponent.kt index 2f82f3116da..3f939d2b870 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/components/UIComponent.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/components/UIComponent.kt @@ -6,11 +6,12 @@ package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext -import org.jetbrains.kotlin.tools.projectWizard.core.entity.Setting -import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingType -import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingValidator +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.panel +import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ErrorAwareComponent +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.JComponent @@ -20,7 +21,7 @@ abstract class UIComponent( labelText: String? = null, private val validator: SettingValidator? = null, private val onValueUpdate: (V) -> Unit = {} -) : DynamicComponent(valuesReadingContext) { +) : DynamicComponent(valuesReadingContext), ErrorAwareComponent, FocusableComponent { private val validationIndicator = if (validator != null) ValidationIndicator(defaultText = labelText, showText = true) else null @@ -63,6 +64,14 @@ abstract class UIComponent( if (validationIndicator == null) return validationIndicator.validationState = validator.validate(valuesReadingContext, value) } + + override fun focusOn() { + uiComponent.requestFocus() + } + + override fun findComponentWithError(error: ValidationResult.ValidationError): FocusableComponent? = takeIf { + getUiValue()?.let { validator?.validate?.invoke(valuesReadingContext, it)?.isSpecificError(error) } == true + } } fun UIComponent.valueForSetting(setting: Setting>): V? = diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/ModuleSettingsComponent.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/ModuleSettingsComponent.kt index 7d3b3f717bd..dbab011fbee 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/ModuleSettingsComponent.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/ModuleSettingsComponent.kt @@ -4,6 +4,7 @@ import com.intellij.ui.components.JBTabbedPane import org.jetbrains.kotlin.idea.projectWizard.UiEditorUsageStats import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext import org.jetbrains.kotlin.tools.projectWizard.core.entity.StringValidators +import org.jetbrains.kotlin.tools.projectWizard.core.entity.ValidationResult import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.configuratorSettings import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.moduleType import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleType @@ -11,6 +12,7 @@ import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module.Companion.ALLOWED_SPECIAL_CHARS_IN_MODULE_NAMES import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.ModuleKind 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.components.TextFieldComponent import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.panel import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.SettingsList @@ -33,6 +35,13 @@ class ModuleSettingsComponent( add("Module Settings", moduleConfiguratorSettingsList.component) } + fun selectSettingWithError(error: ValidationResult.ValidationError) { + val componentWithError = nameField.findComponentWithError(error) + ?: moduleConfiguratorSettingsList.findComponentWithError(error)?.also { tabPanel.selectedIndex = 1 } + ?: templateComponent.findComponentWithError(error)?.also { tabPanel.selectedIndex = 0 } + componentWithError?.focusOn() + } + private val nameField = TextFieldComponent( valuesReadingContext, labelText = "Name", diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/SecondStepWizardComponent.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/SecondStepWizardComponent.kt index b1e3f6f73ac..b0170affd63 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/SecondStepWizardComponent.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/SecondStepWizardComponent.kt @@ -2,6 +2,7 @@ package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep import org.jetbrains.kotlin.idea.projectWizard.UiEditorUsageStats import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext +import org.jetbrains.kotlin.tools.projectWizard.core.entity.ValidationResult 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.Sourceset @@ -17,7 +18,9 @@ class SecondStepWizardComponent( uiEditorUsagesStats: UiEditorUsageStats ) : WizardStepComponent(wizard.valuesReadingContext) { private val moduleEditorSubStep = - ModulesEditorSubStep(wizard.valuesReadingContext, uiEditorUsagesStats, ::onNodeSelected).asSubComponent() + ModulesEditorSubStep(wizard.valuesReadingContext, uiEditorUsagesStats, ::onNodeSelected) { + templatesSubStep.selectSettingWithError(it) + }.asSubComponent() private val templatesSubStep = ModuleSettingsSubStep(wizard, uiEditorUsagesStats).asSubComponent() override val component = splitterFor( @@ -34,12 +37,14 @@ class SecondStepWizardComponent( class ModulesEditorSubStep( valuesReadingContext: ValuesReadingContext, uiEditorUsagesStats: UiEditorUsageStats, - onNodeSelected: (data: DisplayableSettingItem?) -> Unit + onNodeSelected: (data: DisplayableSettingItem?) -> Unit, + selectSettingWithError: (ValidationResult.ValidationError) -> Unit ) : SubStep(valuesReadingContext) { private val moduleSettingComponent = ModulesEditorComponent( valuesReadingContext, uiEditorUsagesStats, - onNodeSelected + onNodeSelected, + selectSettingWithError ).asSubComponent() override fun buildContent(): JComponent = panel { @@ -74,6 +79,10 @@ class ModuleSettingsSubStep( changeComponent() } + fun selectSettingWithError(error: ValidationResult.ValidationError) { + moduleSettingsComponent.selectSettingWithError(error) + } + private fun changeComponent() { panel.removeAll() val component = when (selectedNode) { diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/TemplatesComponent.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/TemplatesComponent.kt index 82975343089..f12eaf582a8 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/TemplatesComponent.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/TemplatesComponent.kt @@ -10,6 +10,7 @@ import com.intellij.util.ui.UIUtil import org.jetbrains.kotlin.idea.KotlinIcons import org.jetbrains.kotlin.idea.projectWizard.UiEditorUsageStats import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext +import org.jetbrains.kotlin.tools.projectWizard.core.entity.ValidationResult import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.moduleType import org.jetbrains.kotlin.tools.projectWizard.plugins.templates.TemplatesPlugin import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module @@ -17,6 +18,8 @@ import org.jetbrains.kotlin.tools.projectWizard.templates.Template import org.jetbrains.kotlin.tools.projectWizard.templates.settings import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.* import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.Component +import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ErrorAwareComponent +import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.SettingComponent import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.SettingsList import java.awt.* import javax.swing.JComponent @@ -25,7 +28,7 @@ import javax.swing.JPanel class TemplatesComponent( valuesReadingContext: ValuesReadingContext, uiEditorUsagesStats: UiEditorUsageStats -) : DynamicComponent(valuesReadingContext) { +) : DynamicComponent(valuesReadingContext), ErrorAwareComponent { private val chooseTemplateComponent: ChooseTemplateComponent = ChooseTemplateComponent(valuesReadingContext) { template -> uiEditorUsagesStats.moduleTemplatesSet++ @@ -49,6 +52,9 @@ class TemplatesComponent( } } + override fun findComponentWithError(error: ValidationResult.ValidationError): SettingComponent<*, *>? = + templateSettingsComponent.findComponentWithError(error) + private fun switchState(selectedTemplate: Template?) { panel.removeAll() when (selectedTemplate) { @@ -243,7 +249,7 @@ class TemplateDescriptionComponent( private class TemplateSettingsComponent( valuesReadingContext: ValuesReadingContext, removeTemplate: () -> Unit -) : DynamicComponent(valuesReadingContext) { +) : DynamicComponent(valuesReadingContext), ErrorAwareComponent { private val templateDescriptionComponent = TemplateDescriptionComponent( needRemoveButton = true, nonDefaultBackgroundColor = UIUtil.getEditorPaneBackground(), @@ -256,6 +262,10 @@ private class TemplateSettingsComponent( component.bordered() } + override fun findComponentWithError(error: ValidationResult.ValidationError): SettingComponent<*, *>? = + settings.findComponentWithError(error) + + fun setTemplate(module: Module, selectedTemplate: Template) { settings.setSettings(selectedTemplate.settings(module)) templateDescriptionComponent.updateSelectedTemplate(selectedTemplate) diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/modulesEditor/ModulesEditorComponent.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/modulesEditor/ModulesEditorComponent.kt index cbca7123eec..1971f21609a 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/modulesEditor/ModulesEditorComponent.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/modulesEditor/ModulesEditorComponent.kt @@ -4,6 +4,7 @@ import com.intellij.ui.JBColor import org.jetbrains.kotlin.idea.projectWizard.UiEditorUsageStats import org.jetbrains.kotlin.tools.projectWizard.core.entity.ListSettingType import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext +import org.jetbrains.kotlin.tools.projectWizard.core.entity.ValidationResult import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ProjectKind @@ -20,7 +21,8 @@ import javax.swing.JComponent class ModulesEditorComponent( valuesReadingContext: ValuesReadingContext, uiEditorUsagesStats: UiEditorUsageStats, - oneEntrySelected: (data: DisplayableSettingItem?) -> Unit + oneEntrySelected: (data: DisplayableSettingItem?) -> Unit, + selectSettingWithError: (ValidationResult.ValidationError) -> Unit ) : SettingComponent, ListSettingType>(KotlinPlugin::modules.reference, valuesReadingContext) { private val tree: ModulesEditorTree = ModulesEditorTree( @@ -64,7 +66,11 @@ class ModulesEditorComponent( } } - override val validationIndicator = ValidationIndicator(showText = true).apply { + override val validationIndicator = ValidationIndicator(showText = true) { error -> + val module = error.target as? Module ?: return@ValidationIndicator + tree.selectModule(module) + selectSettingWithError(error) + }.apply { background = tree.background } } diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/modulesEditor/ModulesEditorTree.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/modulesEditor/ModulesEditorTree.kt index 7a4aeb022d1..c878d50e7d8 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/modulesEditor/ModulesEditorTree.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/modulesEditor/ModulesEditorTree.kt @@ -7,12 +7,14 @@ import com.intellij.ui.treeStructure.Tree 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.Sourceset +import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.path import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.icon import org.jetbrains.kotlin.utils.addToStdlib.safeAs import javax.swing.JComponent import javax.swing.JTree import javax.swing.tree.DefaultMutableTreeNode import javax.swing.tree.DefaultTreeModel +import javax.swing.tree.TreePath import javax.swing.tree.TreeSelectionModel class ModulesEditorTree( @@ -32,6 +34,17 @@ class ModulesEditorTree( openPaths.forEach(::expandPath) } + fun selectModule(moduleToSelect: Module) { + for (value in (model.root as DefaultMutableTreeNode).depthFirstEnumeration().iterator()) { + val node = value as? DefaultMutableTreeNode ?: continue + val module = node.userObject as? Module ?: continue + if (module.path == moduleToSelect.path) { + selectionPath = TreePath(node.path) + break + } + } + } + val selectedSettingItem get() = selectedNode?.userObject?.safeAs() diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/SettingComponent.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/SettingComponent.kt index 28eb7b0f0ea..9f47e3c5815 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/SettingComponent.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/SettingComponent.kt @@ -4,11 +4,12 @@ import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext import org.jetbrains.kotlin.tools.projectWizard.core.entity.* import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.Displayable import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.DynamicComponent +import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.FocusableComponent abstract class SettingComponent>( val reference: SettingReference, val valuesReadingContext: ValuesReadingContext -) : DynamicComponent(valuesReadingContext), Displayable { +) : DynamicComponent(valuesReadingContext), Displayable, FocusableComponent { var value: V? get() = reference.value set(value) { diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/SettingsList.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/SettingsList.kt index c70e97ba983..b69242fb41d 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/SettingsList.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/SettingsList.kt @@ -3,18 +3,25 @@ package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting import com.intellij.ui.components.panels.VerticalLayout import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingReference -import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.Component +import org.jetbrains.kotlin.tools.projectWizard.core.entity.ValidationResult +import org.jetbrains.kotlin.tools.projectWizard.core.entity.isSpecificError 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.PanelWithStatusText import javax.swing.JComponent +interface ErrorAwareComponent { + fun findComponentWithError(error: ValidationResult.ValidationError): FocusableComponent? +} + class SettingsList( settings: List>, private val valuesReadingContext: ValuesReadingContext -) : DynamicComponent(valuesReadingContext) { +) : DynamicComponent(valuesReadingContext), ErrorAwareComponent { private val panel = PanelWithStatusText(VerticalLayout(5), "This module has no settings to configure") - private var settingComponents: List = emptyList() + var settingComponents: List> = emptyList() + private set init { setSettings(settings) @@ -35,4 +42,11 @@ class SettingsList( } settingComponents.forEach { setting -> setting.onInit(); panel.add(setting.component) } } + + override fun findComponentWithError(error: ValidationResult.ValidationError) = + settingComponents.firstOrNull { component -> + val value = component.value ?: return@firstOrNull false + val result = component.setting.validator.validate(valuesReadingContext, value) + result isSpecificError error + } } \ No newline at end of file diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/UIComponentDelegatingSettingComponent.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/UIComponentDelegatingSettingComponent.kt index 0e77dfe8d36..f9b1926ecf0 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/UIComponentDelegatingSettingComponent.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/UIComponentDelegatingSettingComponent.kt @@ -29,5 +29,9 @@ abstract class UIComponentDelegatingSettingComponent value?.let(uiComponent::updateUiValue) } + override fun focusOn() { + uiComponent.focusOn() + } + override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) { uiComponent.component } } \ No newline at end of file diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/ValidationIndicator.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/ValidationIndicator.kt index 3178f36db77..9e87a74f4fc 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/ValidationIndicator.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/ValidationIndicator.kt @@ -2,28 +2,49 @@ package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting import com.intellij.icons.AllIcons import com.intellij.ui.JBColor -import com.intellij.ui.components.JBLabel import com.intellij.util.ui.EmptyIcon 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 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.JPanel class ValidationIndicator( defaultText: String? = null, - private val showText: Boolean + 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 } + private var currentError: ValidationResult.ValidationError? = null + + init { border = BorderFactory.createEmptyBorder(3, 3, 3, 3) textLabel?.let { add(it, BorderLayout.WEST) } add(errorLabel, BorderLayout.CENTER) + + if (onClickAction != null) { + cursor = Cursor(Cursor.HAND_CURSOR) + addMouseListener(object : MouseListener { + override fun mouseReleased(p0: MouseEvent?) {} + override fun mouseEntered(p0: MouseEvent?) {} + override fun mouseExited(p0: MouseEvent?) {} + override fun mousePressed(p0: MouseEvent?) {} + + override fun mouseClicked(p0: MouseEvent?) { + currentError?.let(onClickAction) + } + }) + } } private fun setIcon(icon: Icon?) { @@ -31,6 +52,9 @@ class ValidationIndicator( else errorLabel.icon = icon ?: EmptyIcon.ICON_16 } + private fun String.linkifyErrorText() = + if (onClickAction == null) this + else "$this".asHtml() var validationState: ValidationResult = ValidationResult.OK set(value) { @@ -40,12 +64,14 @@ class ValidationIndicator( 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 + if (showText) errorLabel.text = errors.linkifyErrorText() + currentError = value } } } diff --git a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/core/entity/Setting.kt b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/core/entity/Setting.kt index 430b604b9e0..a3ee9346643 100644 --- a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/core/entity/Setting.kt +++ b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/core/entity/Setting.kt @@ -354,7 +354,7 @@ class ListSettingType(private val parser: Parser) : SettingType values.fold(ValidationResult.OK as ValidationResult) { result, value -> result and when (value) { - is Validatable<*> -> (value.validator as SettingValidator).validate(this, value) + is Validatable<*> -> (value.validator as SettingValidator).validate(this, value).withTargetIfNull(value) else -> ValidationResult.OK } } diff --git a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/core/entity/SettingValidator.kt b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/core/entity/SettingValidator.kt index 37783228665..3370f0627ea 100644 --- a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/core/entity/SettingValidator.kt +++ b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/core/entity/SettingValidator.kt @@ -55,8 +55,8 @@ sealed class ValidationResult { override val isOk = true } - class ValidationError(val messages: List) : ValidationResult() { - constructor(message: String) : this(listOf(message)) + data class ValidationError(val messages: List, val target: Any? = null) : ValidationResult() { + constructor(message: String, target: Any? = null) : this(listOf(message), target) override val isOk = false } @@ -73,6 +73,19 @@ sealed class ValidationResult { } } +infix fun ValidationResult.isSpecificError(error: ValidationResult.ValidationError) = + this is ValidationResult.ValidationError && messages.firstOrNull() == error.messages.firstOrNull() + +fun ValidationResult.withTarget(target: Any) = when (this) { + ValidationResult.OK -> this + is ValidationResult.ValidationError -> copy(target = target) +} + +fun ValidationResult.withTargetIfNull(target: Any) = when (this) { + ValidationResult.OK -> this + is ValidationResult.ValidationError -> if (this.target == null) copy(target = target) else this +} + fun ValidationResult.toResult() = when (this) { ValidationResult.OK -> UNIT_SUCCESS is ValidationResult.ValidationError -> Failure(messages.map { ValidationError(it) }) @@ -85,7 +98,7 @@ interface Validatable { fun > ValuesReadingContext.validateList(list: List) = settingValidator { list.fold(ValidationResult.OK as ValidationResult) { result, value -> - result and value.validator.validate(this, value) + result and value.validator.validate(this, value).withTarget(value) } }