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 1d2ca0f0620..558c526ef02 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 @@ -71,6 +71,7 @@ abstract class DynamicComponent(private val context: Context) : Component() { abstract class TitledComponent(context: Context) : DynamicComponent(context) { abstract val title: String? + open fun shouldBeShow(): Boolean = true } interface FocusableComponent { diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/ModuleDependenciesComponent.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/ModuleDependenciesComponent.kt new file mode 100644 index 00000000000..4efdadd06be --- /dev/null +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/ModuleDependenciesComponent.kt @@ -0,0 +1,140 @@ +package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep + +import com.intellij.openapi.actionSystem.ActionToolbarPosition +import com.intellij.openapi.ui.popup.PopupStep +import com.intellij.openapi.ui.popup.util.BaseListPopupStep +import com.intellij.ui.ColoredListCellRenderer +import com.intellij.ui.SimpleTextAttributes +import com.intellij.ui.ToolbarDecorator +import com.intellij.ui.popup.PopupFactoryImpl +import org.jetbrains.kotlin.tools.projectWizard.core.Context +import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin +import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.withAllSubModules +import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.* +import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.* +import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import java.awt.Dimension +import javax.swing.Icon +import javax.swing.JPanel + +class ModuleDependenciesComponent( + context: Context +) : TitledComponent(context) { + override val title: String = "Module dependencies" + private val dependenciesList = ModuleDependenciesList() + + private val toolbarDecorator: ToolbarDecorator = ToolbarDecorator.createDecorator(dependenciesList).apply { + setToolbarPosition(ActionToolbarPosition.BOTTOM) + setAddAction { button -> + AddModulesPopUp.create( + possibleDependencies(), + dependenciesList::addDependency + ).show(button.preferredPopupPoint!!) + } + setAddActionName("Add module dependency") + setAddActionUpdater { e -> + e.presentation.apply { + isEnabled = possibleDependencies().isNotEmpty() + } + e.presentation.isEnabled + } + setRemoveAction { + dependenciesList.removeSelected() + } + setAddActionName("Remove module dependency") + setMoveDownAction(null) + setMoveUpAction(null) + } + + var module: Module? = null + set(value) { + field = value + dependenciesList.module = value + } + + private fun possibleDependencies(): List = + read { KotlinPlugin::modules.settingValue }.withAllSubModules().toMutableList().apply { + module?.let(::remove) + removeAll( + module + ?.dependencies + ?.filterIsInstance() + ?.map(ModuleReference.ByModule::module) + .orEmpty() + ) + }.filter { to -> + ModuleDependencyType.isDependencyPossible(module!!, to) + } + + override fun shouldBeShow(): Boolean = module?.let { + it.dependencies.isEmpty() && possibleDependencies().isEmpty() + } != true + + override val component: JPanel = toolbarDecorator.createPanelWithPopupHandler(dependenciesList).apply { + preferredSize = Dimension(preferredSize.width, 200) + } +} + +private class AddModulesPopUp( + modules: List, + private val onChosenCallBack: (Module) -> Unit +) : BaseListPopupStep(null, modules) { + override fun getIconFor(value: Module?): Icon? = value?.icon + override fun getTextFor(value: Module): String = value.text + + + override fun onChosen(selectedValue: Module?, finalChoice: Boolean): PopupStep<*>? { + if (selectedValue != null) { + onChosenCallBack(selectedValue) + } + return PopupStep.FINAL_CHOICE + } + + companion object { + fun create(modules: List, onChosen: (Module) -> Unit) = + PopupFactoryImpl.getInstance().createListPopup(AddModulesPopUp(modules, onChosen)) + } +} + +private class ModuleDependenciesList : AbstractSingleSelectableListWithIcon() { + init { + setEmptyText("There is no module dependencies") + } + + override fun ColoredListCellRenderer.render(value: Module) { + renderModule(value) + } + + var module: Module? = null + set(value) { + field = value + updateValues( + value?.dependencies?.mapNotNull { it.safeAs()?.module } ?: return + ) + updateUI() + } + + + fun addDependency(dependency: Module) { + model.addElement(dependency) + module?.let { it.dependencies += ModuleReference.ByModule(dependency) } + } + + fun removeSelected() { + val index = selectedIndex + model.removeElementAt(selectedIndex) + module?.dependencies?.removeAt(index) + if (model.size() > 0) { + selectedIndex = index.coerceAtMost(model.size - 1) + } + } +} + +private fun ColoredListCellRenderer.renderModule(module: Module) { + append(module.path.asString()) + append(" ") + module.greyText?.let { + append(it, SimpleTextAttributes.GRAYED_ATTRIBUTES) + } + icon = module.icon +} \ No newline at end of file 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 c0293f21fca..72081f826ba 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 @@ -27,6 +27,7 @@ class ModuleSettingsComponent( uiEditorUsagesStats: UiEditorUsageStats ) : DynamicComponent(context) { private val settingsList = TitledComponentsList(emptyList(), context).asSubComponent() + private val moduleDependenciesComponent = ModuleDependenciesComponent(context) override val component: JComponent = borderPanel { addToCenter(settingsList.component) @@ -43,6 +44,7 @@ class ModuleSettingsComponent( @OptIn(ExperimentalStdlibApi::class) private fun updateModule(module: Module) { + moduleDependenciesComponent.module = module val moduleSettingComponents = buildList { add(ModuleNameComponent(context, module)) createTemplatesListComponentForModule(module)?.let(::add) @@ -50,6 +52,7 @@ class ModuleSettingsComponent( module.template?.let { template -> addAll(template.settings(module).map { it.createSettingComponent(context) }) } + add(moduleDependenciesComponent) } settingsList.setComponents(moduleSettingComponents) diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/SourcesetDependenciesSettingsComponent.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/SourcesetDependenciesSettingsComponent.kt deleted file mode 100644 index 65c478be465..00000000000 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/SourcesetDependenciesSettingsComponent.kt +++ /dev/null @@ -1,137 +0,0 @@ -package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep - -import com.intellij.openapi.actionSystem.ActionToolbarPosition -import com.intellij.openapi.ui.DialogWrapper -import com.intellij.ui.ColoredListCellRenderer -import com.intellij.ui.SimpleTextAttributes -import com.intellij.ui.ToolbarDecorator -import org.jetbrains.kotlin.tools.projectWizard.core.Context -import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.AndroidSinglePlatformModuleConfigurator -import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.IOSSinglePlatformModuleConfigurator -import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin -import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.withAllSubModules -import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.* -import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.* -import org.jetbrains.kotlin.utils.addToStdlib.safeAs -import java.awt.BorderLayout -import java.awt.Dimension -import javax.swing.JComponent - -class SourcesetDependenciesSettingsComponent( - context: Context -) : DynamicComponent(context) { - private val sourcesetDependenciesList = SourcesetDependenciesList() - - private val toolbarDecorator: ToolbarDecorator = ToolbarDecorator.createDecorator(sourcesetDependenciesList).apply { - setToolbarPosition(ActionToolbarPosition.RIGHT) - setAddAction { - val dialog = ChooseModuleDialog(allModulesExcludingCurrent, component) - if (dialog.showAndGet()) { - dialog.selectedModule?.let(sourcesetDependenciesList::addDependency) - } - } - setRemoveAction { - sourcesetDependenciesList.removeSelected() - } - setMoveDownAction(null) - setMoveUpAction(null) - } - - var sourceset: Sourceset? = null - set(value) { - field = value - sourcesetDependenciesList.sourceset = value - } - - - private val allModulesExcludingCurrent: List - get() { - val currentModule = sourceset?.parent - - return KotlinPlugin::modules.value!! - .withAllSubModules() - .filter { module -> - if (sourceset in module.sourcesets) return@filter false - if (currentModule?.kind == ModuleKind.target && currentModule in module.subModules) - return@filter false - - if (module.configurator == AndroidSinglePlatformModuleConfigurator - || module.configurator == IOSSinglePlatformModuleConfigurator - ) return@filter false - true - } - } - - override val component = customPanel { - add(toolbarDecorator.createPanelWithPopupHandler(sourcesetDependenciesList), BorderLayout.CENTER) - } -} - -private class ChooseModuleDialog(modules: List, parent: JComponent) : DialogWrapper(parent, false) { - private val list = ImmutableSingleSelectableListWithIcon( - values = modules, - emptyMessage = "There are no dependencies to add", - renderValue = { value -> - renderModule(value) - } - ).bordered(needTopEmptyBorder = false, needBottomEmptyBorder = false) - - override fun createCenterPanel() = customPanel { - preferredSize = Dimension(preferredSize.width, 300) - add( - label("Please select the modules to add as dependencies:") - .bordered( - needLineBorder = false, - needInnerEmptyBorder = false, - needTopEmptyBorder = false - ), - BorderLayout.NORTH - ) - add(list, BorderLayout.CENTER) - } - - val selectedModule: Module? - get() = list.selectedValue - - init { - title = "Choose Modules" - init() - } -} - -private class SourcesetDependenciesList : AbstractSingleSelectableListWithIcon() { - override fun ColoredListCellRenderer.render(value: Module) { - renderModule(value) - } - - var sourceset: Sourceset? = null - set(value) { - field = value - updateValues( - value?.dependencies?.mapNotNull { it.safeAs()?.module } ?: return - ) - updateUI() - } - - - fun addDependency(dependency: Module) { - model.addElement(dependency) - sourceset?.let { it.dependencies += ModuleBasedSourcesetDependency(dependency) } - } - - fun removeSelected() { - val index = selectedIndex - model.removeElementAt(selectedIndex) - sourceset?.let { it.dependencies = it.dependencies.toMutableList().also { it.removeAt(index) } } - if (model.size() > 0) { - selectedIndex = index.coerceAtMost(model.size - 1) - } - } -} - -private fun ColoredListCellRenderer.renderModule(module: Module) { - append(module.path.asString()) - append(" ") - append("(${module.greyText})", SimpleTextAttributes.GRAYED_ATTRIBUTES) - icon = module.icon -} \ No newline at end of file diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/SourcesetSettingsComponent.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/SourcesetSettingsComponent.kt deleted file mode 100644 index 677f471ccd3..00000000000 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/SourcesetSettingsComponent.kt +++ /dev/null @@ -1,20 +0,0 @@ -package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep - -import com.intellij.ui.components.JBTabbedPane -import org.jetbrains.kotlin.tools.projectWizard.core.Context -import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Sourceset -import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.DynamicComponent - -class SourcesetSettingsComponent(context: Context) : DynamicComponent(context) { - private val dependenciesComponent = SourcesetDependenciesSettingsComponent(context).asSubComponent() - - override val component = JBTabbedPane().apply { - add("Dependencies", dependenciesComponent.component) - } - - var sourceset: Sourceset? = null - set(value) { - field = value - dependenciesComponent.sourceset = value - } -} \ No newline at end of file diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/TitledComponentsList.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/TitledComponentsList.kt index a172d0e6d06..8804a1c4fdc 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/TitledComponentsList.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/TitledComponentsList.kt @@ -49,6 +49,7 @@ open class TitledComponentsList( companion object { private fun createComponentsPanel(components: List) = panel { components.forEach { component -> + if (!component.shouldBeShow()) return@forEach row(component.title?.let { "$it:" }.orEmpty()) { component.component(growX) } diff --git a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/settings/buildsystem/ModuleDependency.kt b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/settings/buildsystem/ModuleDependency.kt index 951bdea5c7e..af6d00b7fd9 100644 --- a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/settings/buildsystem/ModuleDependency.kt +++ b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/settings/buildsystem/ModuleDependency.kt @@ -50,5 +50,8 @@ sealed class ModuleDependencyType( fun getPossibleDependencyType(from: Module, to: Module): ModuleDependencyType? = ALL.firstOrNull { it.accepts(from, to) } + + fun isDependencyPossible(from: Module, to: Module): Boolean = + getPossibleDependencyType(from, to) != null } } \ No newline at end of file