Wizard: add module dependencies to UI
This commit is contained in:
+1
@@ -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 {
|
||||
|
||||
+140
@@ -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
|
||||
}
|
||||
+3
@@ -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)
|
||||
|
||||
-137
@@ -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
|
||||
}
|
||||
-20
@@ -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
|
||||
}
|
||||
}
|
||||
+1
@@ -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)
|
||||
}
|
||||
|
||||
+3
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user