diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/NewProjectWizardModuleBuilder.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/NewProjectWizardModuleBuilder.kt index 385891159a8..02d6abfd11e 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/NewProjectWizardModuleBuilder.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/NewProjectWizardModuleBuilder.kt @@ -61,11 +61,8 @@ class NewProjectWizardModuleBuilder : EmptyModuleBuilder() { companion object { const val MODULE_BUILDER_ID = "kotlin.newProjectWizard.builder" private const val DEFAULT_GROUP_ID = "me.user" - private val projectNameValidator = StringValidators.shouldBeValidIdentifier( - KotlinNewProjectWizardBundle.message("editor.entity.project.name"), - setOf('-', '_') - ) - private val INVALID_PROJECT_NAME_MESSAGE = KotlinNewProjectWizardBundle.message("error.invalid.project.name") + private val projectNameValidator = StringValidators.shouldBeValidIdentifier("Project name", setOf('-', '_')) + private const val INVALID_PROJECT_NAME_MESSAGE = "Invalid project name" } override fun isAvailable(): Boolean = ExperimentalFeatures.NewWizard.isEnabled @@ -96,7 +93,7 @@ class NewProjectWizardModuleBuilder : EmptyModuleBuilder() { phases = GenerationPhase.startingFrom(GenerationPhase.FIRST_STEP) ).onFailure { errors -> val errorMessages = errors.joinToString(separator = "\n") { it.message } - Messages.showErrorDialog(project, errorMessages, KotlinNewProjectWizardBundle.message("error.following")) + Messages.showErrorDialog(project, errorMessages, "The following errors arose during project generation") }.isSuccess if (success) { val projectCreationStats = ProjectCreationStats( @@ -186,11 +183,16 @@ abstract class WizardStep(protected val wizard: IdeWizard, private val phase: Ge override fun updateDataModel() = Unit // model is updated on every UI action override fun validate(): Boolean = when (val result = wizard.context.read { with(wizard) { validate(setOf(phase)) } }) { - is Success<*> -> true - is Failure -> { - throw ConfigurationException(result.asHtml(), KotlinNewProjectWizardBundle.message("error.validation")) + ValidationResult.OK -> true + is ValidationResult.ValidationError -> { + handleErrors(result) + false } } + + protected open fun handleErrors(error: ValidationResult.ValidationError) { + throw ConfigurationException(error.asHtml(), "Validation Error") + } } private class PomWizardStep( @@ -256,4 +258,8 @@ class ModuleNewWizardSecondStep( override fun _init() { component.onInit() } + + override fun handleErrors(error: ValidationResult.ValidationError) { + component.navigateTo(error) + } } \ No newline at end of file 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 eb451b7e060..4ffe40af2eb 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 @@ -5,12 +5,13 @@ import org.jetbrains.kotlin.tools.projectWizard.core.Context import org.jetbrains.kotlin.tools.projectWizard.core.Reader import org.jetbrains.kotlin.tools.projectWizard.core.SettingsWriter import org.jetbrains.kotlin.tools.projectWizard.core.Writer +import org.jetbrains.kotlin.tools.projectWizard.core.entity.ValidationResult import org.jetbrains.kotlin.tools.projectWizard.core.entity.settings.PluginSettingPropertyReference import org.jetbrains.kotlin.tools.projectWizard.core.entity.settings.SettingReference import org.jetbrains.kotlin.tools.projectWizard.core.entity.settings.SettingType import org.jetbrains.kotlin.tools.projectWizard.core.entity.settings.reference -abstract class Component : Displayable { +abstract class Component : Displayable, ErrorNavigatable { private val subComponents = mutableListOf() open fun onInit() { @@ -20,6 +21,10 @@ abstract class Component : Displayable { protected fun C.asSubComponent(): C = also { this@Component.subComponents += it } + + override fun navigateTo(error: ValidationResult.ValidationError) { + subComponents.forEach { it.navigateTo(error) } + } } abstract class DynamicComponent(private val context: Context) : Component() { @@ -66,4 +71,8 @@ abstract class TitledComponent(context: Context) : DynamicComponent(context) { interface FocusableComponent { fun focusOn() {} +} + +interface ErrorNavigatable { + fun navigateTo(error: ValidationResult.ValidationError) } \ 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 d5041d2cd98..a050b21ec06 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 @@ -17,7 +17,6 @@ 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 java.awt.BorderLayout import javax.swing.JComponent @@ -27,7 +26,7 @@ abstract class UIComponent( labelText: String? = null, private val validator: SettingValidator? = null, private val onValueUpdate: (V) -> Unit = {} -) : DynamicComponent(context), ErrorAwareComponent, FocusableComponent, Disposable { +) : DynamicComponent(context), FocusableComponent, Disposable { private val validationIndicator by lazy(LazyThreadSafetyMode.NONE) { if (validator != null) IdeaBasedComponentValidator(this, getValidatorTarget()) @@ -83,10 +82,14 @@ abstract class UIComponent( uiComponent.requestFocus() } - override fun findComponentWithError(error: ValidationResult.ValidationError): FocusableComponent? = takeIf { - read { + + override fun navigateTo(error: ValidationResult.ValidationError) { + val errorInThisComponent = read { getUiValue()?.let { validator?.validate?.invoke(this, it)?.isSpecificError(error) } == true } + if (errorInThisComponent) { + focusOn() + } } } 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 9026f363bba..c0293f21fca 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 @@ -65,7 +65,7 @@ class ModuleSettingsComponent( } private class ModuleNameComponent(context: Context, module: Module) : TitledComponent(context) { - override val component: JComponent = TextFieldComponent( + private val textField = TextFieldComponent( context, labelText = null, initialValue = module.name, @@ -73,7 +73,10 @@ private class ModuleNameComponent(context: Context, module: Module) : TitledComp ) { value -> module.name = value context.write { eventManager.fireListeners(null) } - }.component + }.asSubComponent() + + override val component: JComponent + get() = textField.component override val title: String = "Name" @@ -91,7 +94,7 @@ private class ModuleTemplateComponent( onTemplateChanged: () -> Unit ) : TitledComponent(context) { @OptIn(ExperimentalStdlibApi::class) - override val component = DropDownComponent( + private val dropDown = DropDownComponent( context, initialValues = buildList { add(NoneTemplate) @@ -102,7 +105,10 @@ private class ModuleTemplateComponent( ) { value -> module.template = value.takeIf { it != NoneTemplate } onTemplateChanged() - }.component + }.asSubComponent() + + override val component: JComponent + get() = dropDown.component override val title: String = "Template" 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 ca8b56ed1cf..69ce4c0d8d9 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 @@ -24,6 +24,11 @@ class SecondStepWizardComponent( addToCenter(moduleSettingsComponent.component) } + override fun navigateTo(error: ValidationResult.ValidationError) { + moduleEditorComponent.navigateTo(error) + moduleSettingsComponent.navigateTo(error) + } + private fun onNodeSelected(data: DisplayableSettingItem?) { moduleSettingsComponent.selectedNode = data } 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 deleted file mode 100644 index 037376aa1a2..00000000000 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/TemplatesComponent.kt +++ /dev/null @@ -1,281 +0,0 @@ -package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep - -import com.intellij.openapi.ui.DialogWrapper -import com.intellij.openapi.ui.Messages -import com.intellij.openapi.ui.ex.MessagesEx -import com.intellij.ui.JBSplitter -import com.intellij.ui.SimpleTextAttributes -import com.intellij.util.ui.StatusText -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.Context -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 -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.TitledComponentsList -import java.awt.* -import javax.swing.JComponent -import javax.swing.JPanel - -class TemplatesComponent( - context: Context, - uiEditorUsagesStats: UiEditorUsageStats -) : DynamicComponent(context), ErrorAwareComponent { - private val chooseTemplateComponent: ChooseTemplateComponent = - ChooseTemplateComponent(context) { template -> - uiEditorUsagesStats.moduleTemplatesSet++ - module?.template = template - switchState(template) - }.asSubComponent() - - private val templateSettingsComponent = TemplateSettingsComponent(context) { - if (MessagesEx.showOkCancelDialog( - component, - "Do you want to remove selected template from module", - "Remove selected template", - "Remove", - "Cancel", - null - ) == Messages.OK - ) { - uiEditorUsagesStats.moduleTemplatesRemoved++ - module?.template = null - switchState(null) - } - }.asSubComponent() - - override fun findComponentWithError(error: ValidationResult.ValidationError): SettingComponent<*, *>? = - templateSettingsComponent.findComponentWithError(error) - - private fun switchState(selectedTemplate: Template?) { - panel.removeAll() - when (selectedTemplate) { - null -> panel.add(chooseTemplateComponent.component, BorderLayout.CENTER) - else -> { - if (module != null) { - templateSettingsComponent.setTemplate(module!!, selectedTemplate) - } - panel.add(templateSettingsComponent.component, BorderLayout.CENTER) - } - } - panel.updateUI() - } - - val panel = panel { - add(chooseTemplateComponent.component, BorderLayout.CENTER) - } - - override val component: JComponent = panel - - var module: Module? = null - set(value) { - field = value - switchState(value?.template) - chooseTemplateComponent.selectedModule = value - } -} - -class ChooseTemplateComponent( - context: Context, - private val onTemplateChosen: (Template) -> Unit -) : DynamicComponent(context) { - private enum class State(val text: String) { - MODULE_SELECTED_AND_TEMPLATES_AVAILABLE("You can configure a template for selected module"), - MODULE_SELECTED_AND_NO_TEMPLATES_AVAILABLE("No templates available for selected module"), - NO_MODULE_SELECTED("Please select a module to configure") - } - - override val component: JPanel = object : JPanel() { - override fun paint(g: Graphics?) { - super.paint(g) - statusText.paint(this, g) - } - } - - private val statusText = object : StatusText(component) { - override fun isStatusVisible() = true - } - - private val allTemplates - get() = read { - TemplatesPlugin::templates.propertyValue - } - - var selectedModule: Module? = null - set(value) { - field = value - onStateUpdated() - } - - init { - onStateUpdated() - } - - private val availableTemplates - get() = allTemplates.values.filter { template -> - selectedModule!!.configurator.moduleType in template.moduleTypes - && template.isApplicableTo(selectedModule!!) - } - - private val state: State - get() = when { - selectedModule == null -> State.NO_MODULE_SELECTED - availableTemplates.isEmpty() -> State.MODULE_SELECTED_AND_NO_TEMPLATES_AVAILABLE - else -> State.MODULE_SELECTED_AND_TEMPLATES_AVAILABLE - } - - private fun onStateUpdated() { - statusText.clear() - statusText.appendText(state.text) - if (state == State.MODULE_SELECTED_AND_TEMPLATES_AVAILABLE) { - statusText.appendSecondaryText("Configure", SimpleTextAttributes.LINK_ATTRIBUTES) { - showDialog() - } - } - } - - - private fun showDialog() { - val availableTemplates = availableTemplates - if (availableTemplates.isEmpty()) { - MessagesEx.error(null, "No templates available for the selected module") - } else { - val dialog = TemplateListDialog(availableTemplates, component) - if (dialog.showAndGet()) { - onTemplateChosen(dialog.selectedTemplate ?: return) - } - } - } -} - -private class TemplateListDialog( - values: List