From 0208fad0577e5a2901a84e8af8ec22abc8a1482c Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Thu, 16 Jul 2020 19:56:00 +0300 Subject: [PATCH] Wizard: do not allow to choose Frontend Application template & JS Library at the same time #KT-40371 fixed --- .../wizard/ui/components/DropDownComponent.kt | 32 ++++++++++++- .../wizard/ui/components/UIComponent.kt | 4 ++ .../ui/secondStep/ModuleSettingsComponent.kt | 47 +++++++++++-------- .../JsModuleConfigurator.kt | 9 ++++ .../NativeConsoleApplicationTemplate.kt | 5 +- .../templates/SimpleJsClientTemplate.kt | 21 +++++++-- .../tools/projectWizard/templates/Template.kt | 5 +- 7 files changed, 94 insertions(+), 29 deletions(-) diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/components/DropDownComponent.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/components/DropDownComponent.kt index 99e37df1c1b..7ffc49ccf77 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/components/DropDownComponent.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/components/DropDownComponent.kt @@ -1,5 +1,6 @@ package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.ui.ComboBox import com.intellij.ui.ColoredListCellRenderer import com.intellij.ui.SimpleTextAttributes @@ -13,13 +14,14 @@ import java.awt.event.ItemEvent import javax.swing.DefaultComboBoxModel import javax.swing.Icon import javax.swing.JList +import javax.swing.SwingUtilities class DropDownComponent( context: Context, - initialValues: List = emptyList(), + private val initialValues: List = emptyList(), initiallySelectedValue: T? = null, labelText: String? = null, - filter: (T) -> Boolean = { true }, + private val filter: (T) -> Boolean = { true }, private val validator: SettingValidator = settingValidator { ValidationResult.OK }, private val iconProvider: (T) -> Icon? = { null }, onValueUpdate: (T) -> Unit = {} @@ -68,6 +70,32 @@ class DropDownComponent( } } + fun filterValues() { + val selectedItem = model.selectedItem + + safeUpdateUi { + model.removeAllElements() + initialValues.filter(filter).forEach(model::addElement) + when { + model.getIndexOf(selectedItem) != -1 -> { + model.selectedItem = selectedItem + } + model.size != 0 -> { + uiComponent.selectedIndex = 0 + uiComponent.selectedItem?.let { + ApplicationManager.getApplication().invokeLater { + @Suppress("UNCHECKED_CAST") + forceValueUpdate(it as T) + } + } + } + } + } + uiComponent.repaint() + } + + private val model get() = uiComponent.model as DefaultComboBoxModel + val valuesCount get() = uiComponent.model.size 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 b32cdf77fe9..e79b9ed7775 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 @@ -51,6 +51,10 @@ abstract class UIComponent( validate(value) } + protected fun forceValueUpdate(newValue: V) { + onValueUpdate(newValue) + } + override fun onInit() { super.onInit() getUiValue()?.let(::validate) 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 a2b35627683..1e5acce72b3 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 @@ -1,12 +1,11 @@ package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep -import com.intellij.ui.components.JBLabel import com.intellij.util.ui.JBUI -import com.intellij.util.ui.UIUtil import org.jetbrains.kotlin.idea.projectWizard.UiEditorUsageStats import org.jetbrains.kotlin.tools.projectWizard.core.Context import org.jetbrains.kotlin.tools.projectWizard.core.Reader import org.jetbrains.kotlin.tools.projectWizard.core.entity.StringValidators +import org.jetbrains.kotlin.tools.projectWizard.core.entity.settings.SettingReference import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.CommonTargetConfigurator import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.getConfiguratorSettings import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.moduleType @@ -59,13 +58,18 @@ class ModuleSettingsComponent( settingsList.setComponents(moduleSettingComponents) } - private fun createTemplatesListComponentForModule(module: Module) = - read { availableTemplatesFor(module) }.takeIf { it.isNotEmpty() }?.let { templates -> - ModuleTemplateComponent(context, module, templates, uiEditorUsagesStats) { - updateModule(module) - component.updateUI() - } + @OptIn(ExperimentalStdlibApi::class) + private fun createTemplatesListComponentForModule(module: Module): ModuleTemplateComponent? { + val templates = read { availableTemplatesFor(module) }.takeIf { it.isNotEmpty() } ?: return null + val templatesWithNoneTemplate = buildList { + add(NoneTemplate) + addAll(templates) } + return ModuleTemplateComponent(context, module, templatesWithNoneTemplate, uiEditorUsagesStats) { + updateModule(module) + component.updateUI() + } + } } private class ModuleNameComponent(context: Context, private val module: Module) : TitledComponent(context) { @@ -117,11 +121,9 @@ private class ModuleTemplateComponent( @OptIn(ExperimentalStdlibApi::class) private val dropDown = DropDownComponent( context, - initialValues = buildList { - add(NoneTemplate) - addAll(templates) - }, + initialValues = templates, initiallySelectedValue = module.template ?: NoneTemplate, + filter = { template: Template -> read { template.isApplicableTo(this, module) } }, labelText = null, ) { value -> module.template = value.takeIf { it != NoneTemplate } @@ -150,19 +152,24 @@ private class ModuleTemplateComponent( addToBottom(templateDescriptionLabel) } - override val title: String = KotlinNewProjectWizardUIBundle.message("module.settings.template") - - private object NoneTemplate : Template() { - override val title = KotlinNewProjectWizardUIBundle.message("module.settings.template.none") - override val description: String = "" - override val moduleTypes: Set = ModuleType.ALL - override val id: String = "none" + override fun onValueUpdated(reference: SettingReference<*, *>?) { + super.onValueUpdated(reference) + dropDown.filterValues() } + + override val title: String = KotlinNewProjectWizardUIBundle.message("module.settings.template") +} + +private object NoneTemplate : Template() { + override val title = KotlinNewProjectWizardUIBundle.message("module.settings.template.none") + override val description: String = "" + override val moduleTypes: Set = ModuleType.ALL + override val id: String = "none" } fun Reader.availableTemplatesFor(module: Module) = TemplatesPlugin::templates.propertyValue.values.filter { template -> - module.configurator.moduleType in template.moduleTypes && template.isApplicableTo(module) + module.configurator.moduleType in template.moduleTypes } diff --git a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/moduleConfigurators/JsModuleConfigurator.kt b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/moduleConfigurators/JsModuleConfigurator.kt index b9f40b7ff68..d9e0d05f9db 100644 --- a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/moduleConfigurators/JsModuleConfigurator.kt +++ b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/moduleConfigurators/JsModuleConfigurator.kt @@ -10,6 +10,7 @@ import org.jetbrains.annotations.NonNls import org.jetbrains.kotlin.tools.projectWizard.KotlinNewProjectWizardBundle import org.jetbrains.kotlin.tools.projectWizard.core.Reader import org.jetbrains.kotlin.tools.projectWizard.core.entity.settings.ModuleConfiguratorSetting +import org.jetbrains.kotlin.tools.projectWizard.core.entity.settings.ModuleConfiguratorSettingReference import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.BuildSystemIR import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.KotlinBuildSystemPluginIR import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.GradleIRListBuilder @@ -19,6 +20,7 @@ import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleType import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModulesToIrConversionData import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.ModuleKind +import org.jetbrains.kotlin.tools.projectWizard.templates.SimpleJsClientTemplate interface JSConfigurator : ModuleConfiguratorWithModuleType, ModuleConfiguratorWithSettings { override val moduleType: ModuleType get() = ModuleType.js @@ -60,6 +62,13 @@ interface JSConfigurator : ModuleConfiguratorWithModuleType, ModuleConfiguratorW GenerationPhase.PROJECT_GENERATION ) { defaultValue = value(JsTargetKind.APPLICATION) + filter = filter@{ reference, kindCandidate -> + when { + reference !is ModuleConfiguratorSettingReference<*, *> -> false + kindCandidate == JsTargetKind.LIBRARY && reference.module?.template is SimpleJsClientTemplate -> false + else -> true + } + } } val cssSupport by booleanSetting( diff --git a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/templates/NativeConsoleApplicationTemplate.kt b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/templates/NativeConsoleApplicationTemplate.kt index 278c16e2137..df78dccaea4 100644 --- a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/templates/NativeConsoleApplicationTemplate.kt +++ b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/templates/NativeConsoleApplicationTemplate.kt @@ -25,7 +25,10 @@ class NativeConsoleApplicationTemplate : Template() { override val moduleTypes: Set = setOf(ModuleType.native) override val id: String = "nativeConsoleApp" - override fun isApplicableTo(module: Module): Boolean = + override fun isApplicableTo( + reader: Reader, + module: Module + ): Boolean = module.configurator.safeAs()?.isDesktopTarget == true override fun updateTargetIr(module: ModuleIR, targetConfigurationIR: TargetConfigurationIR): TargetConfigurationIR = diff --git a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/templates/SimpleJsClientTemplate.kt b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/templates/SimpleJsClientTemplate.kt index 7c55b0564cb..4749c0c70e1 100644 --- a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/templates/SimpleJsClientTemplate.kt +++ b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/templates/SimpleJsClientTemplate.kt @@ -17,8 +17,6 @@ import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.* import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.multiplatform.DefaultTargetConfigurationIR import org.jetbrains.kotlin.tools.projectWizard.library.MavenArtifact import org.jetbrains.kotlin.tools.projectWizard.library.NpmArtifact -import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.JsBrowserTargetConfigurator -import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.JsSingleplatformModuleConfigurator import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleSubType import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleType @@ -29,7 +27,9 @@ import org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors.Templa import org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors.interceptTemplate import org.jetbrains.kotlin.tools.projectWizard.KotlinNewProjectWizardBundle import org.jetbrains.kotlin.tools.projectWizard.Versions +import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.* import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin +import org.jetbrains.kotlin.tools.projectWizard.templates.withSettingsOf class SimpleJsClientTemplate : Template() { override val title: String = KotlinNewProjectWizardBundle.message("module.template.js.simple.title") @@ -40,9 +40,20 @@ class SimpleJsClientTemplate : Template() { @NonNls override val id: String = "simpleJsClient" - override fun isApplicableTo(module: Module): Boolean = - module.configurator == JsBrowserTargetConfigurator - || module.configurator == JsSingleplatformModuleConfigurator + override fun isApplicableTo( + reader: Reader, + module: Module + ): Boolean = when (module.configurator) { + JsBrowserTargetConfigurator -> true + JsSingleplatformModuleConfigurator -> { + with(reader) { + withSettingsOf(module, module.configurator) { + JSConfigurator.kind.reference.notRequiredSettingValue == JsTargetKind.APPLICATION + } + } + } + else -> false + } val renderEngine by enumSetting( KotlinNewProjectWizardBundle.message("module.template.js.simple.setting.rendering.engine"), diff --git a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/templates/Template.kt b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/templates/Template.kt index 83c2be4a7fd..d046eeedb07 100644 --- a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/templates/Template.kt +++ b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/templates/Template.kt @@ -77,7 +77,10 @@ abstract class Template : SettingsOwner, EntitiesOwnerDescriptor, DisplayableSet override val text: String get() = title - open fun isApplicableTo(module: Module): Boolean = true + open fun isApplicableTo( + reader: Reader, + module: Module + ): Boolean = true open val settings: List> = emptyList() open val interceptionPoints: List> = emptyList()