Wizard: add module dependencies to UI

This commit is contained in:
Ilya Kirillov
2020-03-19 18:44:06 +03:00
parent cca4384371
commit 9435b386ce
7 changed files with 148 additions and 157 deletions
@@ -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 {
@@ -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<Module> =
read { KotlinPlugin::modules.settingValue }.withAllSubModules().toMutableList().apply {
module?.let(::remove)
removeAll(
module
?.dependencies
?.filterIsInstance<ModuleReference.ByModule>()
?.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<Module>,
private val onChosenCallBack: (Module) -> Unit
) : BaseListPopupStep<Module>(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<Module>, onChosen: (Module) -> Unit) =
PopupFactoryImpl.getInstance().createListPopup(AddModulesPopUp(modules, onChosen))
}
}
private class ModuleDependenciesList : AbstractSingleSelectableListWithIcon<Module>() {
init {
setEmptyText("There is no module dependencies")
}
override fun ColoredListCellRenderer<Module>.render(value: Module) {
renderModule(value)
}
var module: Module? = null
set(value) {
field = value
updateValues(
value?.dependencies?.mapNotNull { it.safeAs<ModuleReference.ByModule>()?.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<Module>.renderModule(module: Module) {
append(module.path.asString())
append(" ")
module.greyText?.let {
append(it, SimpleTextAttributes.GRAYED_ATTRIBUTES)
}
icon = module.icon
}
@@ -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)
@@ -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<Module>
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<Module>, 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<Module>() {
override fun ColoredListCellRenderer<Module>.render(value: Module) {
renderModule(value)
}
var sourceset: Sourceset? = null
set(value) {
field = value
updateValues(
value?.dependencies?.mapNotNull { it.safeAs<ModuleBasedSourcesetDependency>()?.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<Module>.renderModule(module: Module) {
append(module.path.asString())
append(" ")
append("(${module.greyText})", SimpleTextAttributes.GRAYED_ATTRIBUTES)
icon = module.icon
}
@@ -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
}
}
@@ -49,6 +49,7 @@ open class TitledComponentsList(
companion object {
private fun createComponentsPanel(components: List<TitledComponent>) = panel {
components.forEach { component ->
if (!component.shouldBeShow()) return@forEach
row(component.title?.let { "$it:" }.orEmpty()) {
component.component(growX)
}
@@ -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
}
}