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 class TitledComponent(context: Context) : DynamicComponent(context) {
|
||||||
abstract val title: String?
|
abstract val title: String?
|
||||||
|
open fun shouldBeShow(): Boolean = true
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FocusableComponent {
|
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
|
uiEditorUsagesStats: UiEditorUsageStats
|
||||||
) : DynamicComponent(context) {
|
) : DynamicComponent(context) {
|
||||||
private val settingsList = TitledComponentsList(emptyList(), context).asSubComponent()
|
private val settingsList = TitledComponentsList(emptyList(), context).asSubComponent()
|
||||||
|
private val moduleDependenciesComponent = ModuleDependenciesComponent(context)
|
||||||
|
|
||||||
override val component: JComponent = borderPanel {
|
override val component: JComponent = borderPanel {
|
||||||
addToCenter(settingsList.component)
|
addToCenter(settingsList.component)
|
||||||
@@ -43,6 +44,7 @@ class ModuleSettingsComponent(
|
|||||||
|
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
private fun updateModule(module: Module) {
|
private fun updateModule(module: Module) {
|
||||||
|
moduleDependenciesComponent.module = module
|
||||||
val moduleSettingComponents = buildList {
|
val moduleSettingComponents = buildList {
|
||||||
add(ModuleNameComponent(context, module))
|
add(ModuleNameComponent(context, module))
|
||||||
createTemplatesListComponentForModule(module)?.let(::add)
|
createTemplatesListComponentForModule(module)?.let(::add)
|
||||||
@@ -50,6 +52,7 @@ class ModuleSettingsComponent(
|
|||||||
module.template?.let { template ->
|
module.template?.let { template ->
|
||||||
addAll(template.settings(module).map { it.createSettingComponent(context) })
|
addAll(template.settings(module).map { it.createSettingComponent(context) })
|
||||||
}
|
}
|
||||||
|
add(moduleDependenciesComponent)
|
||||||
}
|
}
|
||||||
|
|
||||||
settingsList.setComponents(moduleSettingComponents)
|
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 {
|
companion object {
|
||||||
private fun createComponentsPanel(components: List<TitledComponent>) = panel {
|
private fun createComponentsPanel(components: List<TitledComponent>) = panel {
|
||||||
components.forEach { component ->
|
components.forEach { component ->
|
||||||
|
if (!component.shouldBeShow()) return@forEach
|
||||||
row(component.title?.let { "$it:" }.orEmpty()) {
|
row(component.title?.let { "$it:" }.orEmpty()) {
|
||||||
component.component(growX)
|
component.component(growX)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -50,5 +50,8 @@ sealed class ModuleDependencyType(
|
|||||||
|
|
||||||
fun getPossibleDependencyType(from: Module, to: Module): ModuleDependencyType? =
|
fun getPossibleDependencyType(from: Module, to: Module): ModuleDependencyType? =
|
||||||
ALL.firstOrNull { it.accepts(from, to) }
|
ALL.firstOrNull { it.accepts(from, to) }
|
||||||
|
|
||||||
|
fun isDependencyPossible(from: Module, to: Module): Boolean =
|
||||||
|
getPossibleDependencyType(from, to) != null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user