Wizard: Add initial version of the new project wizard
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":libraries:tools:new-project-wizard"))
|
||||
implementation(project(":idea:ide-common"))
|
||||
implementation(project(":idea:idea-core"))
|
||||
implementation(project(":idea:idea-jvm"))
|
||||
compileOnly(project(":kotlin-reflect-api"))
|
||||
|
||||
compileOnly(intellijCoreDep())
|
||||
compileOnly(intellijDep())
|
||||
compileOnly(intellijPluginDep("gradle"))
|
||||
|
||||
excludeInAndroidStudio(rootProject) {
|
||||
compileOnly(intellijPluginDep("maven"))
|
||||
}
|
||||
|
||||
Platform[191].orLower {
|
||||
compileOnly(intellijDep()) { includeJars("java-api", "java-impl") }
|
||||
}
|
||||
|
||||
Platform[192].orHigher {
|
||||
compileOnly(intellijPluginDep("java")) { includeJars("java-api", "java-impl") }
|
||||
testCompileOnly(intellijPluginDep("java")) { includeJars("java-api", "java-impl") }
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
projectTest {
|
||||
dependsOn(":dist")
|
||||
workingDir = rootDir
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.PluginsCreator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingReference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.service.Service
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.StructurePlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemPlugin
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class IdeWizard(
|
||||
createPlugins: PluginsCreator,
|
||||
initialServices: List<Service>
|
||||
) : Wizard(createPlugins, initialServices) {
|
||||
private val allSettings = plugins.flatMap { it.declaredSettings }
|
||||
|
||||
init {
|
||||
context.settingContext.initPluginSettings(allSettings)
|
||||
}
|
||||
|
||||
var projectPath by setting(StructurePlugin::projectPath.reference)
|
||||
var projectName by setting(StructurePlugin::name.reference)
|
||||
|
||||
var groupId by setting(StructurePlugin::groupId.reference)
|
||||
var artifactId by setting(StructurePlugin::artifactId.reference)
|
||||
var buildSystemType by setting(BuildSystemPlugin::type.reference)
|
||||
|
||||
private fun <V : Any, T : SettingType<V>> setting(reference: SettingReference<V, T>) =
|
||||
object : ReadWriteProperty<Any?, V?> {
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: V?) {
|
||||
context.settingContext[reference] = value ?: return
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): V? =
|
||||
context.settingContext[reference]
|
||||
}
|
||||
}
|
||||
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix
|
||||
import com.intellij.jarRepository.JarRepositoryManager
|
||||
import com.intellij.openapi.module.ModifiableModuleModel
|
||||
import com.intellij.openapi.module.ModuleTypeId
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.DependencyScope
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||
import com.intellij.openapi.roots.OrderRootType
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.util.PathUtil
|
||||
import org.jetbrains.idea.maven.utils.library.RepositoryLibraryProperties
|
||||
import org.jetbrains.kotlin.config.ResourceKotlinRootType
|
||||
import org.jetbrains.kotlin.config.SourceKotlinRootType
|
||||
import org.jetbrains.kotlin.config.TestResourceKotlinRootType
|
||||
import org.jetbrains.kotlin.config.TestSourceKotlinRootType
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.service.JpsService
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.library.MavenArtifact
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.SourcesetType
|
||||
import java.nio.file.Path
|
||||
import com.intellij.openapi.module.Module as IdeaModule
|
||||
|
||||
class IdeaJpsService(
|
||||
private val project: Project,
|
||||
private val modulesModel: ModifiableModuleModel
|
||||
) : JpsService {
|
||||
override fun importProject(path: Path, modulesIrs: List<ModuleIR>): TaskResult<Unit> = runWriteAction {
|
||||
ProjectImporter(project, modulesModel, path, modulesIrs).import()
|
||||
}
|
||||
}
|
||||
|
||||
private class ProjectImporter(
|
||||
private val project: Project,
|
||||
private val modulesModel: ModifiableModuleModel,
|
||||
private val path: Path,
|
||||
private val modulesIrs: List<ModuleIR>
|
||||
) {
|
||||
private val librariesPath: Path
|
||||
get() = path / "libs"
|
||||
|
||||
fun import() = modulesIrs.mapSequence { convertModule(it) } andThen
|
||||
safe { modulesModel.commit() }
|
||||
|
||||
|
||||
private fun convertModule(moduleIr: ModuleIR): TaskResult<IdeaModule> {
|
||||
val module = modulesModel.newModule(
|
||||
(moduleIr.path / "${moduleIr.name}.iml").toString(),
|
||||
ModuleTypeId.JAVA_MODULE
|
||||
)
|
||||
val rootModel = ModuleRootManager.getInstance(module).modifiableModel
|
||||
val contentRoot = rootModel.addContentEntry(moduleIr.path.url)
|
||||
|
||||
SourcesetType.ALL.forEach { sourceset ->
|
||||
val isTest = sourceset == SourcesetType.test
|
||||
contentRoot.addSourceFolder(
|
||||
(moduleIr.path / "src" / sourceset.name / "kotlin").url,
|
||||
if (isTest) TestSourceKotlinRootType else SourceKotlinRootType
|
||||
)
|
||||
contentRoot.addSourceFolder(
|
||||
(moduleIr.path / "src" / sourceset.name / "resources").url,
|
||||
if (isTest) TestResourceKotlinRootType else ResourceKotlinRootType
|
||||
)
|
||||
}
|
||||
|
||||
rootModel.inheritSdk()
|
||||
rootModel.commit()
|
||||
addLibrariesToTheModule(moduleIr, module)
|
||||
return Success(module)
|
||||
}
|
||||
|
||||
private fun addLibrariesToTheModule(moduleIr: ModuleIR, module: IdeaModule) {
|
||||
moduleIr.irs.forEach { ir ->
|
||||
when {
|
||||
ir is LibraryDependencyIR && !ir.isKotlinStdlib -> {
|
||||
attachLibraryToModule(ir, module)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun attachLibraryToModule(
|
||||
libraryDependency: LibraryDependencyIR,
|
||||
module: IdeaModule
|
||||
) {
|
||||
val artifact = libraryDependency.artifact as? MavenArtifact ?: return
|
||||
val libraryProperties = RepositoryLibraryProperties(
|
||||
artifact.groupId,
|
||||
artifact.artifactId,
|
||||
libraryDependency.version.toString()
|
||||
)
|
||||
val classesRoots = downloadLibraryAndGetItsClasses(libraryProperties)
|
||||
|
||||
ModuleRootModificationUtil.addModuleLibrary(
|
||||
module,
|
||||
if (classesRoots.size > 1) libraryProperties.artifactId else null,
|
||||
OrderEntryFix.refreshAndConvertToUrls(classesRoots),
|
||||
emptyList(),
|
||||
when (libraryDependency.dependencyType) {
|
||||
DependencyType.MAIN -> DependencyScope.COMPILE
|
||||
DependencyType.TEST -> DependencyScope.TEST
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun downloadLibraryAndGetItsClasses(libraryProperties: RepositoryLibraryProperties) =
|
||||
JarRepositoryManager.loadDependenciesModal(
|
||||
project,
|
||||
libraryProperties,
|
||||
false,
|
||||
false,
|
||||
librariesPath.toString(),
|
||||
null
|
||||
).asSequence()
|
||||
.filter { it.type == OrderRootType.CLASSES }
|
||||
.map { PathUtil.getLocalPath(it.file) }
|
||||
.toList()
|
||||
}
|
||||
|
||||
private val Path.url
|
||||
get() = VfsUtil.pathToUrl(toString())
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard
|
||||
|
||||
import com.intellij.ide.projectWizard.ProjectSettingsStep
|
||||
import com.intellij.ide.util.projectWizard.*
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.module.ModifiableModuleModel
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.module.ModuleType
|
||||
import com.intellij.openapi.options.ConfigurationException
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ui.configuration.ModulesProvider
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.util.SystemProperties
|
||||
import org.jetbrains.kotlin.idea.framework.KotlinModuleSettingStep
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.Failure
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.Success
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.isSuccess
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.onFailure
|
||||
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.Plugins
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.projectTemplates.ProjectTemplatesPlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.PomWizardStepComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.firstStep.FirstWizardStepComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep.SecondStepWizardComponent
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import java.nio.file.Paths
|
||||
import javax.swing.JComponent
|
||||
import com.intellij.openapi.module.Module as IdeaModule
|
||||
|
||||
|
||||
class NewProjectWizardModuleBuilder : ModuleBuilder() {
|
||||
private val wizard = IdeWizard(Plugins.allPlugins, listOf(IdeaAndroidService()))
|
||||
|
||||
companion object {
|
||||
const val MODULE_BUILDER_ID = "kotlin.newProjectWizard.builder"
|
||||
}
|
||||
|
||||
private var wizardContext: WizardContext? = null
|
||||
|
||||
override fun getModuleType(): ModuleType<*> = NewProjectWizardModuleType()
|
||||
override fun createWizardSteps(
|
||||
wizardContext: WizardContext,
|
||||
modulesProvider: ModulesProvider
|
||||
): Array<ModuleWizardStep> {
|
||||
this.wizardContext = wizardContext
|
||||
return arrayOf(ModuleNewWizardSecondStep(wizard))
|
||||
}
|
||||
|
||||
override fun commit(
|
||||
project: Project,
|
||||
model: ModifiableModuleModel?,
|
||||
modulesProvider: ModulesProvider?
|
||||
): List<IdeaModule>? {
|
||||
val modulesModel = model ?: ModuleManager.getInstance(project).modifiableModel
|
||||
val success = wizard.apply(
|
||||
services = listOf(
|
||||
IdeaMavenService(project),
|
||||
IdeaGradleService(project),
|
||||
IdeaJpsService(project, modulesModel),
|
||||
IdeaFileSystemService(),
|
||||
IdeaAndroidService()
|
||||
),
|
||||
phases = GenerationPhase.startingFrom(GenerationPhase.FIRST_STEP)
|
||||
).onFailure { errors ->
|
||||
val errorMessages = errors.joinToString(separator = "\n") { it.message }
|
||||
Messages.showErrorDialog(project, errorMessages, "The following errors arose during project generation")
|
||||
}.isSuccess
|
||||
return when {
|
||||
!success -> null
|
||||
wizard.buildSystemType == BuildSystemType.Jps -> runWriteAction {
|
||||
modulesModel.modules.toList().onEach { setupModule(it) }
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun modifySettingsStep(settingsStep: SettingsStep): ModuleWizardStep {
|
||||
updateProjectNameAndPomDate(settingsStep)
|
||||
|
||||
return when (wizard.buildSystemType) {
|
||||
BuildSystemType.Jps -> {
|
||||
KotlinModuleSettingStep(
|
||||
JvmPlatforms.defaultJvmPlatform,
|
||||
this,
|
||||
settingsStep,
|
||||
wizardContext
|
||||
)
|
||||
}
|
||||
else -> PomWizardStep(settingsStep, wizard)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateProjectNameAndPomDate(settingsStep: SettingsStep) {
|
||||
val suggestedProjectName = with(wizard.valuesReadingContext) {
|
||||
ProjectTemplatesPlugin::template.settingValue.suggestedProjectName.decapitalize()
|
||||
}
|
||||
settingsStep.moduleNameLocationSettings?.apply {
|
||||
val projectParentDirectory = moduleContentRoot.let { Paths.get(it).parent.toString() }
|
||||
moduleName = ProjectWizardUtil.findNonExistingFileName(projectParentDirectory, suggestedProjectName, "")
|
||||
}
|
||||
|
||||
settingsStep.safeAs<ProjectSettingsStep>()?.bindModuleSettings()
|
||||
|
||||
wizard.artifactId = suggestedProjectName
|
||||
wizard.groupId = SystemProperties.getUserName()?.let { "me.$it" } ?: suggestedProjectName
|
||||
}
|
||||
|
||||
override fun getCustomOptionsStep(context: WizardContext?, parentDisposable: Disposable?) =
|
||||
ModuleNewWizardFirstStep(wizard)
|
||||
|
||||
override fun setName(name: String) {
|
||||
wizard.projectName = name
|
||||
}
|
||||
|
||||
|
||||
override fun setModuleFilePath(path: String) = Unit
|
||||
|
||||
override fun setContentEntryPath(moduleRootPath: String) {
|
||||
wizard.projectPath = Paths.get(moduleRootPath)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class WizardStep(protected val wizard: IdeWizard, private val phase: GenerationPhase) : ModuleWizardStep() {
|
||||
override fun updateDataModel() = Unit // model is updated on every UI action
|
||||
override fun validate(): Boolean =
|
||||
when (val result = with(wizard.valuesReadingContext) { with(wizard) { validate(setOf(phase)) } }) {
|
||||
is Success<*> -> true
|
||||
is Failure -> {
|
||||
val messages = result.errors.joinToString(separator = "\n") { it.message }
|
||||
throw ConfigurationException(messages)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PomWizardStep(
|
||||
originalSettingStep: SettingsStep,
|
||||
wizard: IdeWizard
|
||||
) : WizardStep(wizard, GenerationPhase.PROJECT_GENERATION) {
|
||||
private val pomWizardStepComponent = PomWizardStepComponent(wizard.valuesReadingContext)
|
||||
|
||||
init {
|
||||
originalSettingStep.addSettingsComponent(component)
|
||||
pomWizardStepComponent.onInit()
|
||||
|
||||
}
|
||||
|
||||
override fun getComponent(): JComponent = pomWizardStepComponent.component
|
||||
}
|
||||
|
||||
|
||||
class ModuleNewWizardFirstStep(wizard: IdeWizard) : WizardStep(wizard, GenerationPhase.FIRST_STEP) {
|
||||
private val component = FirstWizardStepComponent(wizard)
|
||||
override fun getComponent(): JComponent = component.component
|
||||
|
||||
init {
|
||||
runPreparePhase()
|
||||
component.onInit()
|
||||
}
|
||||
|
||||
private fun runPreparePhase() = ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||
{
|
||||
wizard.apply(emptyList(), setOf(GenerationPhase.PREPARE)) { task ->
|
||||
ProgressManager.getInstance().progressIndicator.text = task.title ?: ""
|
||||
}
|
||||
},
|
||||
"",
|
||||
true,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
class ModuleNewWizardSecondStep(wizard: IdeWizard) : WizardStep(wizard, GenerationPhase.SECOND_STEP) {
|
||||
private val component = SecondStepWizardComponent(wizard)
|
||||
override fun getComponent(): JComponent = component.component
|
||||
|
||||
override fun _init() {
|
||||
component.onInit()
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard
|
||||
|
||||
import com.intellij.openapi.module.ModuleType
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
import javax.swing.Icon
|
||||
|
||||
class NewProjectWizardModuleType: ModuleType<NewProjectWizardModuleBuilder>(NewProjectWizardModuleBuilder.MODULE_BUILDER_ID) {
|
||||
override fun getName(): String = "New Kotlin Project Wizard"
|
||||
override fun getDescription(): String = name
|
||||
override fun getNodeIcon(isOpened: Boolean): Icon = KotlinIcons.SMALL_LOGO
|
||||
override fun createModuleBuilder(): NewProjectWizardModuleBuilder = NewProjectWizardModuleBuilder()
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard
|
||||
|
||||
import com.intellij.openapi.actionSystem.ActionPlaces
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.android.sdk.AndroidSdkData
|
||||
import org.jetbrains.idea.maven.project.MavenProjectsManager
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.TaskResult
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.safe
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.service.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.ModuleIR
|
||||
import org.jetbrains.plugins.gradle.action.ImportProjectFromScriptAction
|
||||
import java.nio.file.Path
|
||||
|
||||
class IdeaFileSystemService : FileSystemService {
|
||||
override fun createDirectory(path: Path): TaskResult<Unit> = safe {
|
||||
runWriteAction<Unit> {
|
||||
VfsUtil.createDirectoryIfMissing(path.toString())
|
||||
}
|
||||
}
|
||||
|
||||
override fun createFile(path: Path, text: String): TaskResult<Unit> = safe {
|
||||
runWriteAction {
|
||||
val directoryPath = path.parent
|
||||
val directory = VfsUtil.createDirectoryIfMissing(directoryPath.toFile().toString())!!
|
||||
val virtualFile = directory.createChildData(this, path.fileName.toString())
|
||||
VfsUtil.saveText(virtualFile, text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IdeaGradleService(private val project: Project) : GradleService {
|
||||
|
||||
// We have to call action directly as there is no common way
|
||||
// to import Gradle project in all IDEAs from 183 to 193
|
||||
override fun importProject(
|
||||
path: Path,
|
||||
modulesIrs: List<ModuleIR>
|
||||
): TaskResult<Unit> = safe {
|
||||
val virtualFile = LocalFileSystem.getInstance().findFileByPath(path.toString())!!
|
||||
val dataContext = SimpleDataContext.getSimpleContext(
|
||||
mapOf(
|
||||
CommonDataKeys.PROJECT.name to project,
|
||||
CommonDataKeys.VIRTUAL_FILE.name to virtualFile
|
||||
),
|
||||
null
|
||||
)
|
||||
val action = ImportProjectFromScriptAction()
|
||||
val event = AnActionEvent.createFromAnAction(action, null, ActionPlaces.UNKNOWN, dataContext)
|
||||
action.actionPerformed(event)
|
||||
}
|
||||
}
|
||||
|
||||
class IdeaMavenService(private val project: Project) : MavenService {
|
||||
override fun importProject(
|
||||
path: Path,
|
||||
modulesIrs: List<ModuleIR>
|
||||
): TaskResult<Unit> = safe {
|
||||
val mavenProjectManager = MavenProjectsManager.getInstance(project)
|
||||
|
||||
val rootFile = LocalFileSystem.getInstance().findFileByPath(path.toString())!!
|
||||
mavenProjectManager.addManagedFilesOrUnignore(rootFile.findAllPomFiles())
|
||||
}
|
||||
|
||||
private fun VirtualFile.findAllPomFiles(): List<VirtualFile> {
|
||||
val result = mutableListOf<VirtualFile>()
|
||||
|
||||
fun VirtualFile.find() {
|
||||
when {
|
||||
!isDirectory && name == "pom.xml" -> result += this
|
||||
isDirectory -> children.forEach(VirtualFile::find)
|
||||
}
|
||||
}
|
||||
|
||||
find()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class IdeaAndroidService : AndroidService {
|
||||
override fun isValidAndroidSdk(path: Path): Boolean =
|
||||
//todo use android plugin for that?
|
||||
AndroidServiceImpl().isValidAndroidSdk(path)
|
||||
}
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
|
||||
|
||||
abstract class Component : Displayable {
|
||||
private val subComponents = mutableListOf<Component>()
|
||||
|
||||
open fun onInit() {
|
||||
subComponents.forEach(Component::onInit)
|
||||
}
|
||||
|
||||
protected fun <C : Component> C.asSubComponent(): C = also {
|
||||
this@Component.subComponents += it
|
||||
}
|
||||
}
|
||||
|
||||
abstract class DynamicComponent(private val valuesReadingContext: ValuesReadingContext) : Component() {
|
||||
//TODO do not use it in future
|
||||
protected val context = valuesReadingContext.context
|
||||
protected val eventManager
|
||||
get() = valuesReadingContext.context.eventManager
|
||||
|
||||
private var isInitialized: Boolean = false
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
isInitialized = true
|
||||
}
|
||||
|
||||
var <V : Any, T : SettingType<V>> SettingReference<V, T>.value: V?
|
||||
get() = with(valuesReadingContext) { notRequiredSettingValue() }
|
||||
set(value) {
|
||||
with(context) { settingContext[this@value] = value!! }
|
||||
}
|
||||
|
||||
inline val <V : Any, reified T : SettingType<V>> PluginSettingPropertyReference<V, T>.value: V?
|
||||
get() = reference.value
|
||||
|
||||
init {
|
||||
valuesReadingContext.context.eventManager.addSettingUpdaterEventListener { reference ->
|
||||
if (isInitialized) onValueUpdated(reference)
|
||||
}
|
||||
}
|
||||
open fun onValueUpdated(reference: SettingReference<*, *>?) {}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui
|
||||
|
||||
import com.intellij.util.ui.HtmlPanel
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import java.awt.Font
|
||||
import javax.swing.event.HyperlinkEvent
|
||||
|
||||
class DescriptionPanel(initialText: String? = null) : HtmlPanel() {
|
||||
private var bodyText: String? = initialText
|
||||
|
||||
fun updateText(text: String) {
|
||||
bodyText = text.asHtml()
|
||||
update()
|
||||
}
|
||||
|
||||
override fun getBody() = bodyText.orEmpty()
|
||||
|
||||
override fun getBodyFont(): Font = UIUtil.getButtonFont().deriveFont(Font.PLAIN)
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui
|
||||
|
||||
import javax.swing.JComponent
|
||||
|
||||
interface Displayable {
|
||||
val component: JComponent
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui
|
||||
|
||||
import com.intellij.ui.components.panels.VerticalLayout
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.StructurePlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.StringSettingComponent
|
||||
import javax.swing.Box
|
||||
import javax.swing.JSeparator
|
||||
import javax.swing.SwingConstants
|
||||
|
||||
class PomWizardStepComponent(valuesReadingContext: ValuesReadingContext) : WizardStepComponent(valuesReadingContext) {
|
||||
private val groupIdComponent = StringSettingComponent(
|
||||
StructurePlugin::groupId.reference,
|
||||
valuesReadingContext,
|
||||
showLabel = true
|
||||
).asSubComponent()
|
||||
|
||||
private val artifactIdComponent = StringSettingComponent(
|
||||
StructurePlugin::artifactId.reference,
|
||||
valuesReadingContext,
|
||||
showLabel = true
|
||||
).asSubComponent()
|
||||
|
||||
private val versionComponent = StringSettingComponent(
|
||||
StructurePlugin::version.reference,
|
||||
valuesReadingContext,
|
||||
showLabel = true
|
||||
).asSubComponent()
|
||||
|
||||
|
||||
override val component = panel(VerticalLayout(0)) {
|
||||
add(Box.createVerticalStrut(10))
|
||||
add(JSeparator(SwingConstants.HORIZONTAL))
|
||||
add(Box.createVerticalStrut(10))
|
||||
add(groupIdComponent.component)
|
||||
add(artifactIdComponent.component)
|
||||
add(versionComponent.component)
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui
|
||||
|
||||
import com.intellij.ui.ColoredListCellRenderer
|
||||
import com.intellij.ui.components.JBList
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ignore
|
||||
import javax.swing.DefaultListModel
|
||||
import javax.swing.JList
|
||||
import javax.swing.ListModel
|
||||
import javax.swing.ListSelectionModel
|
||||
import javax.swing.event.ListDataEvent
|
||||
import javax.swing.event.ListDataListener
|
||||
|
||||
abstract class AbstractSingleSelectableListWithIcon<V>(initialValues: List<V> = emptyList()) : JBList<V>() {
|
||||
protected abstract fun ColoredListCellRenderer<V>.render(value: V)
|
||||
protected open fun onSelected(value: V?) {}
|
||||
|
||||
protected val model: DefaultListModel<V>
|
||||
get() = super.getModel() as DefaultListModel<V>
|
||||
|
||||
fun updateValues(newValues: List<V>) {
|
||||
setModel(createDefaultListModel(newValues))
|
||||
if (newValues.isNotEmpty()) {
|
||||
selectedIndex = 0
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
selectionMode = ListSelectionModel.SINGLE_SELECTION
|
||||
updateValues(initialValues)
|
||||
|
||||
selectionModel.addListSelectionListener { e ->
|
||||
if (e.valueIsAdjusting) return@addListSelectionListener
|
||||
val selectedValue = when (selectedIndex) {
|
||||
-1 -> null
|
||||
else -> this.model.getElementAt(selectedIndex)
|
||||
}
|
||||
onSelected(selectedValue)
|
||||
}
|
||||
|
||||
cellRenderer = object : ColoredListCellRenderer<V>() {
|
||||
override fun customizeCellRenderer(
|
||||
list: JList<out V>,
|
||||
value: V?,
|
||||
index: Int,
|
||||
selected: Boolean,
|
||||
hasFocus: Boolean
|
||||
) {
|
||||
render(value ?: return)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ImmutableSingleSelectableListWithIcon<V>(
|
||||
values: List<V>,
|
||||
private val renderValue: ColoredListCellRenderer<V>.(V) -> Unit,
|
||||
emptyMessage: String? = null,
|
||||
private val onValueSelected: (V?) -> Unit = {}
|
||||
) : AbstractSingleSelectableListWithIcon<V>(values) {
|
||||
override fun ColoredListCellRenderer<V>.render(value: V) = renderValue(value)
|
||||
override fun onSelected(value: V?) = onValueSelected(value)
|
||||
|
||||
init {
|
||||
emptyMessage?.let { text ->
|
||||
emptyText.text = text
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.JComponent
|
||||
|
||||
abstract class SubStep(
|
||||
val valuesReadingContext: ValuesReadingContext
|
||||
) : DynamicComponent(valuesReadingContext) {
|
||||
protected abstract fun buildContent(): JComponent
|
||||
|
||||
final override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) {
|
||||
panel {
|
||||
add(buildContent(), BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
|
||||
abstract class WizardStepComponent(
|
||||
valuesReadingContext: ValuesReadingContext
|
||||
) : DynamicComponent(valuesReadingContext)
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components
|
||||
|
||||
import com.intellij.openapi.ui.ComboBox
|
||||
import com.intellij.ui.ColoredListCellRenderer
|
||||
import com.intellij.ui.SimpleTextAttributes
|
||||
import com.intellij.ui.layout.selectedValueIs
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingValidator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.ValidationResult
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.settingValidator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.Component
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.panel
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.JComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import java.awt.event.ItemEvent
|
||||
import javax.swing.DefaultComboBoxModel
|
||||
import javax.swing.Icon
|
||||
import javax.swing.JList
|
||||
|
||||
class DropDownComponent<T : DisplayableSettingItem>(
|
||||
private val valuesReadingContext: ValuesReadingContext,
|
||||
initialValues: List<T> = emptyList(),
|
||||
labelText: String? = null,
|
||||
private val validator: SettingValidator<T> = settingValidator { ValidationResult.OK },
|
||||
private val iconProvider: (T) -> Icon? = { null },
|
||||
private val onAnyValueUpdate: (T) -> Unit = {}
|
||||
) : Component() {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
|
||||
private val validationIndicator = ValidationIndicator(defaultText = labelText, showText = true)
|
||||
|
||||
private var allowFireEventWhenValueUpdated: Boolean = true
|
||||
|
||||
private fun withoutActionFiring(action: () -> Unit) {
|
||||
val initialAllowFireEventWhenValueUpdated = allowFireEventWhenValueUpdated
|
||||
allowFireEventWhenValueUpdated = false
|
||||
action()
|
||||
allowFireEventWhenValueUpdated = initialAllowFireEventWhenValueUpdated
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private val comboBox = ComboBox<T>(initialValues.toTypedArray<DisplayableSettingItem>() as Array<T>).apply {
|
||||
renderer = object : ColoredListCellRenderer<T>() {
|
||||
override fun customizeCellRenderer(
|
||||
list: JList<out T>,
|
||||
value: T?,
|
||||
index: Int,
|
||||
selected: Boolean,
|
||||
hasFocus: Boolean
|
||||
) {
|
||||
if (value == null) return
|
||||
icon = iconProvider(value)
|
||||
append(value.text)
|
||||
value.greyText?.let {
|
||||
append(" $it", SimpleTextAttributes.GRAYED_ATTRIBUTES)
|
||||
}
|
||||
if (!this@apply.selectedValueIs(value).invoke()) {
|
||||
validator.validate(valuesReadingContext, value)
|
||||
.safeAs<ValidationResult.ValidationError>()
|
||||
?.messages
|
||||
?.firstOrNull()
|
||||
?.let { error ->
|
||||
append(" $error.", SimpleTextAttributes.ERROR_ATTRIBUTES)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addItemListener { e ->
|
||||
if (e?.stateChange == ItemEvent.SELECTED) {
|
||||
value = e.item as T
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateValues(newValues: List<T>) = withoutActionFiring {
|
||||
val oldValue = comboBox.selectedItem
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
comboBox.model = DefaultComboBoxModel(newValues.toTypedArray<DisplayableSettingItem>() as Array<T>)
|
||||
|
||||
if (newValues.isNotEmpty() && oldValue !in newValues) {
|
||||
value = newValues.first()
|
||||
}
|
||||
}
|
||||
|
||||
override val component: JComponent = panel {
|
||||
add(validationIndicator, BorderLayout.NORTH)
|
||||
add(comboBox, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
var value: T
|
||||
get() = comboBox.selectedItem as T
|
||||
set(value) {
|
||||
comboBox.selectedItem = value
|
||||
fireValueChanged(value)
|
||||
}
|
||||
|
||||
private fun fireValueChanged(newValue: T) {
|
||||
validate(newValue)
|
||||
if (allowFireEventWhenValueUpdated) {
|
||||
onAnyValueUpdate(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
initialValues.firstOrNull()?.let { first ->
|
||||
value = first
|
||||
validate(first)
|
||||
}
|
||||
}
|
||||
|
||||
fun validate(value: T = this.value) {
|
||||
validationIndicator.validationState = validator.validate(valuesReadingContext, value)
|
||||
}
|
||||
}
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components
|
||||
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
|
||||
import com.intellij.openapi.ui.TextBrowseFolderListener
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.StringValidator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.ValidationResult
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.Component
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.panel
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.textField
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.JComponent
|
||||
|
||||
class PathFieldComponent(
|
||||
private val valuesReadingContext: ValuesReadingContext,
|
||||
private val initialText: String = "",
|
||||
labelText: String? = "",
|
||||
private val validator: StringValidator = org.jetbrains.kotlin.tools.projectWizard.core.entity.settingValidator { ValidationResult.OK },
|
||||
private val onSuccessValueUpdate: (String) -> Unit = {},
|
||||
private val onAnyValueUpdate: (String) -> Unit = {}
|
||||
) : Component() {
|
||||
private val validationIndicator = ValidationIndicator(defaultText = labelText, showText = true)
|
||||
private val pathField = TextFieldWithBrowseButton(
|
||||
textField(initialText, this::fireValueChanged)
|
||||
).apply {
|
||||
addBrowseFolderListener(
|
||||
TextBrowseFolderListener(
|
||||
FileChooserDescriptorFactory.createSingleFolderDescriptor(),
|
||||
null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override val component: JComponent = panel {
|
||||
add(validationIndicator, BorderLayout.NORTH)
|
||||
add(pathField, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
|
||||
var value: String
|
||||
get() = pathField.text
|
||||
set(value) {
|
||||
pathField.text = value
|
||||
fireValueChanged(value)
|
||||
}
|
||||
|
||||
private fun fireValueChanged(text: String) {
|
||||
validate(text)
|
||||
if (validationIndicator.validationState == ValidationResult.OK) {
|
||||
onSuccessValueUpdate(text)
|
||||
}
|
||||
onAnyValueUpdate(text)
|
||||
}
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
validate(initialText)
|
||||
}
|
||||
|
||||
private fun validate(text: String) {
|
||||
validationIndicator.validationState = validator.validate(valuesReadingContext, text)
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.StringValidator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.ValidationResult
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.settingValidator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.Component
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.panel
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.textField
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.JComponent
|
||||
|
||||
class TextFieldComponent(
|
||||
private val valuesReadingContext: ValuesReadingContext,
|
||||
private val initialText: String = "",
|
||||
labelText: String? = "",
|
||||
private val validator: StringValidator = settingValidator { ValidationResult.OK },
|
||||
private val onSuccessValueUpdate: (String) -> Unit = {},
|
||||
private val onAnyValueUpdate: (String) -> Unit = {}
|
||||
) : Component() {
|
||||
private val validationIndicator = ValidationIndicator(defaultText = labelText, showText = true)
|
||||
private val textField = textField(initialText, this::fireValueChanged)
|
||||
|
||||
override val component: JComponent = panel {
|
||||
add(validationIndicator, BorderLayout.NORTH)
|
||||
add(textField, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
|
||||
var value: String
|
||||
get() = textField.text
|
||||
set(value) {
|
||||
textField.text = value
|
||||
fireValueChanged(value)
|
||||
}
|
||||
|
||||
private fun fireValueChanged(text: String) {
|
||||
validate(text)
|
||||
if (validationIndicator.validationState == ValidationResult.OK) {
|
||||
onSuccessValueUpdate(text)
|
||||
}
|
||||
onAnyValueUpdate(text)
|
||||
}
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
validate(initialText)
|
||||
}
|
||||
|
||||
private fun validate(text: String) {
|
||||
validationIndicator.validationState = validator.validate(valuesReadingContext, text)
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.firstStep
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import icons.GradleIcons
|
||||
import icons.MavenIcons
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.DropDownSettingType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingReference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemPlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components.DropDownComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.panel
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.SettingComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator
|
||||
import java.awt.BorderLayout
|
||||
|
||||
|
||||
class BuildSystemTypeSettingComponent(
|
||||
valuesReadingContext: ValuesReadingContext
|
||||
) : SettingComponent<BuildSystemType, DropDownSettingType<BuildSystemType>>(
|
||||
BuildSystemPlugin::type.reference,
|
||||
valuesReadingContext
|
||||
) {
|
||||
override val validationIndicator: ValidationIndicator? = null
|
||||
private val listComponent = DropDownComponent(
|
||||
valuesReadingContext,
|
||||
setting.type.values,
|
||||
labelText = "Build System",
|
||||
validator = setting.validator,
|
||||
iconProvider = BuildSystemType::icon,
|
||||
onAnyValueUpdate = { value = it }
|
||||
).asSubComponent()
|
||||
|
||||
override val component = panel {
|
||||
add(listComponent.component, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
override fun onValueUpdated(reference: SettingReference<*, *>?) {
|
||||
super.onValueUpdated(reference)
|
||||
if (reference == KotlinPlugin::projectKind.reference) {
|
||||
listComponent.component.updateUI()
|
||||
}
|
||||
listComponent.validate()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private val BuildSystemType.icon
|
||||
get() = when (this) {
|
||||
BuildSystemType.GradleKotlinDsl -> GradleIcons.Gradle
|
||||
BuildSystemType.GradleGroovyDsl -> GradleIcons.Gradle
|
||||
BuildSystemType.Maven -> MavenIcons.MavenLogo
|
||||
BuildSystemType.Jps -> AllIcons.Nodes.Module
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.firstStep
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingReference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.projectTemplates.ProjectTemplatesPlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.projectTemplates.ProjectTemplate
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.IdeWizard
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.*
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.JComponent
|
||||
|
||||
class FirstWizardStepComponent(wizard: IdeWizard) : WizardStepComponent(wizard.valuesReadingContext) {
|
||||
private val buildSystemSubStep = BuildSystemSubStep(wizard.valuesReadingContext).asSubComponent()
|
||||
private val templatesSubStep = TemplatesSubStep(wizard.valuesReadingContext).asSubComponent()
|
||||
|
||||
override val component: JComponent = panel {
|
||||
add(templatesSubStep.component, BorderLayout.CENTER)
|
||||
add(buildSystemSubStep.component, BorderLayout.SOUTH)
|
||||
}
|
||||
}
|
||||
|
||||
class BuildSystemSubStep(valuesReadingContext: ValuesReadingContext) :
|
||||
SubStep(valuesReadingContext) {
|
||||
private val buildSystemSetting = BuildSystemTypeSettingComponent(valuesReadingContext).asSubComponent()
|
||||
|
||||
override fun buildContent(): JComponent = panel {
|
||||
bordered(needBottomEmptyBorder = false)
|
||||
add(buildSystemSetting.component, BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
|
||||
class TemplatesSubStep(valuesReadingContext: ValuesReadingContext) :
|
||||
SubStep(valuesReadingContext) {
|
||||
private val projectTemplateSettingComponent =
|
||||
ProjectTemplateSettingComponent(valuesReadingContext) { projectTemplate ->
|
||||
templateDescriptionComponent.setTemplate(projectTemplate)
|
||||
}.asSubComponent()
|
||||
|
||||
private val templateDescriptionComponent = TemplateDescriptionComponent().asSubComponent()
|
||||
|
||||
override fun buildContent(): JComponent = panel {
|
||||
add(projectTemplateSettingComponent.component, BorderLayout.CENTER)
|
||||
add(templateDescriptionComponent.component, BorderLayout.SOUTH)
|
||||
}
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
applySelectedTemplate()
|
||||
}
|
||||
|
||||
private fun applySelectedTemplate() {
|
||||
projectTemplateSettingComponent.value?.setsValues?.forEach { (setting, value) ->
|
||||
// TODO do not use settingContext directly
|
||||
context.settingContext[setting] = value
|
||||
}
|
||||
}
|
||||
|
||||
override fun onValueUpdated(reference: SettingReference<*, *>?) {
|
||||
super.onValueUpdated(reference)
|
||||
if (reference == ProjectTemplatesPlugin::template.reference) {
|
||||
applySelectedTemplate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TemplateDescriptionComponent : Component() {
|
||||
private val descriptionPanel = DescriptionPanel()
|
||||
|
||||
fun setTemplate(template: ProjectTemplate) {
|
||||
descriptionPanel.updateText(template.htmlDescription)
|
||||
}
|
||||
|
||||
override val component: JComponent = panel {
|
||||
bordered()
|
||||
add(descriptionPanel, BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.firstStep
|
||||
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.DropDownSettingType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.projectTemplates.ProjectTemplatesPlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.projectTemplates.ProjectTemplate
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.ImmutableSingleSelectableListWithIcon
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.UiConstants
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.bordered
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.panel
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.SettingComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.BorderFactory
|
||||
import javax.swing.JComponent
|
||||
|
||||
class ProjectTemplateSettingComponent(
|
||||
valuesReadingContext: ValuesReadingContext,
|
||||
private val onSelected: (ProjectTemplate) -> Unit
|
||||
) : SettingComponent<ProjectTemplate, DropDownSettingType<ProjectTemplate>>(
|
||||
ProjectTemplatesPlugin::template.reference,
|
||||
valuesReadingContext
|
||||
) {
|
||||
override val validationIndicator: ValidationIndicator? get() = null
|
||||
|
||||
private val list = ImmutableSingleSelectableListWithIcon(
|
||||
setting.type.values,
|
||||
renderValue = { value ->
|
||||
icon = KotlinIcons.SMALL_LOGO
|
||||
append(value.title)
|
||||
},
|
||||
onValueSelected = {
|
||||
onValueSelected(it!!)
|
||||
}
|
||||
).apply {
|
||||
border = BorderFactory.createEmptyBorder(
|
||||
UiConstants.GAP_BORDER_SIZE,
|
||||
UiConstants.GAP_BORDER_SIZE,
|
||||
UiConstants.GAP_BORDER_SIZE,
|
||||
UiConstants.GAP_BORDER_SIZE
|
||||
)
|
||||
}
|
||||
|
||||
private fun onValueSelected(selected: ProjectTemplate) {
|
||||
value = selected
|
||||
onSelected(selected)
|
||||
}
|
||||
|
||||
override val component: JComponent = panel {
|
||||
bordered(needTopEmptyBorder = false, needInnerEmptyBorder = false)
|
||||
add(list, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
if (setting.type.values.isNotEmpty()) {
|
||||
list.selectedIndex = 0
|
||||
onValueSelected(setting.type.values.first())
|
||||
}
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep
|
||||
|
||||
import com.intellij.ui.components.panels.VerticalLayout
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.StringValidators
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.ModuleConfigurator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.configuratorSettings
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components.DropDownComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components.TextFieldComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.SettingsList
|
||||
import javax.swing.BorderFactory
|
||||
import javax.swing.JComponent
|
||||
|
||||
class ModuleSettingsComponent(valuesReadingContext: ValuesReadingContext) : DynamicComponent(valuesReadingContext) {
|
||||
private val validateModuleName =
|
||||
StringValidators.shouldNotBeBlank("Module name") and
|
||||
StringValidators.shouldBeValidIdentifier("Module Name")
|
||||
|
||||
private val moduleConfigurator = DropDownComponent<ModuleConfigurator>(
|
||||
valuesReadingContext,
|
||||
iconProvider = { it.icon },
|
||||
labelText = "Consider as"
|
||||
) { value ->
|
||||
module?.configurator = value
|
||||
}
|
||||
|
||||
private val moduleConfiguratorSettingsList = SettingsList(emptyList(), valuesReadingContext)
|
||||
|
||||
private val nameField = TextFieldComponent(
|
||||
valuesReadingContext,
|
||||
labelText = "Name",
|
||||
onAnyValueUpdate = { value ->
|
||||
module?.name = value
|
||||
eventManager.fireListeners(null)
|
||||
},
|
||||
validator = validateModuleName
|
||||
).asSubComponent()
|
||||
|
||||
|
||||
override val component: JComponent = panel(VerticalLayout(5)) {
|
||||
border = BorderFactory.createEmptyBorder(
|
||||
UiConstants.GAP_BORDER_SIZE,
|
||||
UiConstants.GAP_BORDER_SIZE,
|
||||
UiConstants.GAP_BORDER_SIZE,
|
||||
UiConstants.GAP_BORDER_SIZE
|
||||
)
|
||||
add(nameField.component)
|
||||
add(moduleConfigurator.component)
|
||||
add(moduleConfiguratorSettingsList.component)
|
||||
}
|
||||
|
||||
var module: Module? = null
|
||||
set(value) {
|
||||
field = value
|
||||
if (value != null) {
|
||||
updateModule(value)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateModule(module: Module) {
|
||||
nameField.value = module.name
|
||||
val newValues = ModuleConfigurator.BY_MODULE_KIND.getValue(module.configurator.moduleKind)
|
||||
.filter { configurator ->
|
||||
if (configurator.moduleKind == ModuleKind.target)
|
||||
configurator.moduleType == module.configurator.moduleType
|
||||
else false
|
||||
}
|
||||
|
||||
nameField.component.isVisible = module.kind != ModuleKind.target
|
||||
|| module.configurator.moduleType != ModuleType.common
|
||||
|
||||
moduleConfiguratorSettingsList.setSettings(module.configuratorSettings)
|
||||
|
||||
moduleConfigurator.apply {
|
||||
updateValues(newValues)
|
||||
component.isVisible = newValues.size > 1
|
||||
value = module.configurator
|
||||
component.updateUI()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep
|
||||
|
||||
import com.intellij.util.ui.StatusText
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.Component
|
||||
import java.awt.Graphics
|
||||
import javax.swing.JPanel
|
||||
|
||||
class NothingSelectedComponent : Component() {
|
||||
override val component: JPanel = object : JPanel() {
|
||||
override fun paint(g: Graphics?) {
|
||||
super.paint(g)
|
||||
statusText.paint(this, g)
|
||||
}
|
||||
}
|
||||
|
||||
private val statusText = object : StatusText(component) {
|
||||
override fun isStatusVisible() = true
|
||||
|
||||
init {
|
||||
appendText("Neither Module nor Sourceset is selected")
|
||||
}
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Sourceset
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.IdeWizard
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep.modulesEditor.ModulesEditorComponent
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.BorderFactory
|
||||
import javax.swing.JComponent
|
||||
|
||||
class SecondStepWizardComponent(wizard: IdeWizard) : WizardStepComponent(wizard.valuesReadingContext) {
|
||||
private val moduleEditorSubStep =
|
||||
ModulesEditorSubStep(wizard.valuesReadingContext, ::onNodeSelected).asSubComponent()
|
||||
private val templatesSubStep = ModuleSettingsSubStep(wizard).asSubComponent()
|
||||
|
||||
override val component = splitterFor(
|
||||
moduleEditorSubStep.component,
|
||||
templatesSubStep.component
|
||||
)
|
||||
|
||||
private fun onNodeSelected(data: DisplayableSettingItem?) {
|
||||
templatesSubStep.selectedNode = data
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ModulesEditorSubStep(
|
||||
valuesReadingContext: ValuesReadingContext,
|
||||
onNodeSelected: (data: DisplayableSettingItem?) -> Unit
|
||||
) : SubStep(valuesReadingContext) {
|
||||
private val moduleSettingComponent = ModulesEditorComponent(
|
||||
valuesReadingContext,
|
||||
onNodeSelected
|
||||
).asSubComponent()
|
||||
|
||||
override fun buildContent(): JComponent = panel {
|
||||
bordered(needInnerEmptyBorder = false, needLineBorder = false)
|
||||
border = BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createEmptyBorder(0, 0, 0, UiConstants.GAP_BORDER_SIZE),
|
||||
border
|
||||
)
|
||||
add(moduleSettingComponent.component, BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ModuleSettingsSubStep(wizard: IdeWizard) : SubStep(wizard.valuesReadingContext) {
|
||||
private val sourcesetSettingsComponent = SourcesetSettingsComponent(wizard.valuesReadingContext).asSubComponent()
|
||||
private val moduleSettingsComponent = ModuleSettingsComponent(valuesReadingContext).asSubComponent()
|
||||
private val nothingSelectedComponent = NothingSelectedComponent().asSubComponent()
|
||||
|
||||
private val panel = panel {
|
||||
bordered()
|
||||
add(nothingSelectedComponent.component, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
var selectedNode: DisplayableSettingItem? = null
|
||||
set(value) {
|
||||
field = value
|
||||
sourcesetSettingsComponent.sourceset = value as? Sourceset
|
||||
moduleSettingsComponent.module = value as? Module
|
||||
changeComponent()
|
||||
}
|
||||
|
||||
private fun changeComponent() {
|
||||
panel.removeAll()
|
||||
val component = when (selectedNode) {
|
||||
is Sourceset -> sourcesetSettingsComponent
|
||||
is Module -> moduleSettingsComponent
|
||||
else -> nothingSelectedComponent
|
||||
}
|
||||
panel.add(component.component, BorderLayout.CENTER)
|
||||
panel.updateUI()
|
||||
}
|
||||
|
||||
override fun buildContent() = panel
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
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.ValuesReadingContext
|
||||
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(
|
||||
valuesReadingContext: ValuesReadingContext
|
||||
) : DynamicComponent(valuesReadingContext) {
|
||||
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 = panel {
|
||||
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() = panel {
|
||||
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
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep
|
||||
|
||||
import com.intellij.ui.components.JBTabbedPane
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Sourceset
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.DynamicComponent
|
||||
|
||||
class SourcesetSettingsComponent(valuesReadingContext: ValuesReadingContext) : DynamicComponent(valuesReadingContext) {
|
||||
private val templatesComponent = TemplatesComponent(valuesReadingContext).asSubComponent()
|
||||
private val dependenciesComponent = SourcesetDependenciesSettingsComponent(valuesReadingContext).asSubComponent()
|
||||
override val component = JBTabbedPane().apply {
|
||||
add("Template", templatesComponent.component)
|
||||
add("Dependencies", dependenciesComponent.component)
|
||||
}
|
||||
|
||||
var sourceset: Sourceset? = null
|
||||
set(value) {
|
||||
field = value
|
||||
dependenciesComponent.sourceset = value
|
||||
templatesComponent.sourceset = value
|
||||
}
|
||||
}
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep
|
||||
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.ui.ex.MessagesEx
|
||||
import com.intellij.ui.JBSplitter
|
||||
import com.intellij.ui.SimpleTextAttributes
|
||||
import com.intellij.util.ui.StatusText
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.templates.TemplatesPlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Sourceset
|
||||
import org.jetbrains.kotlin.tools.projectWizard.templates.Template
|
||||
import org.jetbrains.kotlin.tools.projectWizard.templates.settings
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.Component
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.SettingsList
|
||||
import java.awt.*
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JPanel
|
||||
|
||||
class TemplatesComponent(valuesReadingContext: ValuesReadingContext) : DynamicComponent(valuesReadingContext) {
|
||||
private val chooseTemplateComponent: ChooseTemplateComponent =
|
||||
ChooseTemplateComponent(valuesReadingContext) { template ->
|
||||
sourceset?.template = template
|
||||
switchState(template)
|
||||
}
|
||||
|
||||
private val templateSettingsComponent = TemplateSettingsComponent(valuesReadingContext) {
|
||||
if (MessagesEx.showOkCancelDialog(
|
||||
component,
|
||||
"Do you want to remove selected template from module",
|
||||
"Remove selected template",
|
||||
"Remove",
|
||||
"Cancel",
|
||||
null
|
||||
) == Messages.OK
|
||||
) {
|
||||
switchState(null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun switchState(selectedTemplate: Template?) {
|
||||
panel.removeAll()
|
||||
when (selectedTemplate) {
|
||||
null -> panel.add(chooseTemplateComponent.component, BorderLayout.CENTER)
|
||||
else -> {
|
||||
if (sourceset != null) {
|
||||
templateSettingsComponent.setTemplate(sourceset!!, selectedTemplate)
|
||||
}
|
||||
panel.add(templateSettingsComponent.component, BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
panel.updateUI()
|
||||
}
|
||||
|
||||
val panel = panel {
|
||||
add(chooseTemplateComponent.component, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
override val component: JComponent = panel
|
||||
|
||||
var sourceset: Sourceset? = null
|
||||
set(value) {
|
||||
field = value
|
||||
switchState(value?.template)
|
||||
chooseTemplateComponent.selectedModule = value
|
||||
}
|
||||
}
|
||||
|
||||
class ChooseTemplateComponent(
|
||||
private val valuesReadingContext: ValuesReadingContext,
|
||||
private val onTemplateChosen: (Template) -> Unit
|
||||
) : DynamicComponent(valuesReadingContext) {
|
||||
private enum class State(val text: String) {
|
||||
MODULE_SELECTED_AND_TEMPLATES_AVAILABLE("You can configure a template for selected module"),
|
||||
MODULE_SELECTED_AND_NO_TEMPLATES_AVAILABLE("No templates available for selected module"),
|
||||
NO_MODULE_SELECTED("Please select a module to configure")
|
||||
}
|
||||
|
||||
override val component: JPanel = object : JPanel() {
|
||||
override fun paint(g: Graphics?) {
|
||||
super.paint(g)
|
||||
statusText.paint(this, g)
|
||||
}
|
||||
}
|
||||
|
||||
private val statusText = object : StatusText(component) {
|
||||
override fun isStatusVisible() = true
|
||||
}
|
||||
|
||||
private val allTemplates
|
||||
get() = with(valuesReadingContext) {
|
||||
TemplatesPlugin::templates.propertyValue
|
||||
}
|
||||
|
||||
var selectedModule: Sourceset? = null
|
||||
set(value) {
|
||||
field = value
|
||||
onStateUpdated()
|
||||
}
|
||||
|
||||
init {
|
||||
onStateUpdated()
|
||||
}
|
||||
|
||||
private val availableTemplates
|
||||
get() = allTemplates.values.filter { template ->
|
||||
selectedModule!!.containingModuleType in template.moduleTypes
|
||||
&& selectedModule!!.sourcesetType in template.sourcesetTypes
|
||||
}
|
||||
|
||||
private val state: State
|
||||
get() = when {
|
||||
selectedModule == null -> State.NO_MODULE_SELECTED
|
||||
availableTemplates.isEmpty() -> State.MODULE_SELECTED_AND_NO_TEMPLATES_AVAILABLE
|
||||
else -> State.MODULE_SELECTED_AND_TEMPLATES_AVAILABLE
|
||||
}
|
||||
|
||||
private fun onStateUpdated() {
|
||||
statusText.clear()
|
||||
statusText.appendText(state.text)
|
||||
if (state == State.MODULE_SELECTED_AND_TEMPLATES_AVAILABLE) {
|
||||
statusText.appendSecondaryText("Configure", SimpleTextAttributes.LINK_ATTRIBUTES) {
|
||||
showDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun showDialog() {
|
||||
val availableTemplates = availableTemplates
|
||||
if (availableTemplates.isEmpty()) {
|
||||
MessagesEx.error(null, "No templates available for the selected module")
|
||||
} else {
|
||||
val dialog = TemplateListDialog(availableTemplates, component)
|
||||
if (dialog.showAndGet()) {
|
||||
onTemplateChosen(dialog.selectedTemplate ?: return)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TemplateListDialog(
|
||||
values: List<Template>,
|
||||
parent: JComponent
|
||||
) : DialogWrapper(parent, false) {
|
||||
private val templateDescriptionComponent = TemplateDescriptionComponent(
|
||||
needRemoveButton = false
|
||||
).apply {
|
||||
component.bordered(needTopEmptyBorder = false, needBottomEmptyBorder = false)
|
||||
}
|
||||
private val templatesList = ImmutableSingleSelectableListWithIcon(
|
||||
values = values,
|
||||
renderValue = { value ->
|
||||
icon = when {
|
||||
value.moduleTypes.size == 1 -> value.moduleTypes.single().icon
|
||||
else -> KotlinIcons.MPP
|
||||
}
|
||||
append(value.title)
|
||||
},
|
||||
onValueSelected = { templateDescriptor ->
|
||||
templateDescriptionComponent.updateSelectedTemplate(templateDescriptor)
|
||||
}
|
||||
).bordered(needTopEmptyBorder = false, needBottomEmptyBorder = false)
|
||||
|
||||
val selectedTemplate: Template?
|
||||
get() = templatesList.selectedValue
|
||||
|
||||
init {
|
||||
title = "Choose a template to configure"
|
||||
init()
|
||||
if (values.isNotEmpty()) {
|
||||
templateDescriptionComponent.updateSelectedTemplate(values.first())
|
||||
}
|
||||
}
|
||||
|
||||
override fun createCenterPanel() = panel(BorderLayout()) {
|
||||
preferredSize = Dimension(700, 300)
|
||||
val splitter = JBSplitter(false, .3f).apply {
|
||||
firstComponent = templatesList
|
||||
secondComponent = templateDescriptionComponent.component
|
||||
orientation = false
|
||||
}
|
||||
add(splitter, BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
|
||||
class TemplateDescriptionComponent(
|
||||
needRemoveButton: Boolean = false,
|
||||
private val nonDefaultBackgroundColor: Color? = null,
|
||||
onRemoveButtonClicked: () -> Unit = {}
|
||||
) : Component() {
|
||||
private val removeButton = if (needRemoveButton) {
|
||||
hyperlinkLabel("Remove template", onClick = onRemoveButtonClicked)
|
||||
} else null
|
||||
|
||||
private val titleLabel = label("", bold = true) {
|
||||
font = UIUtil.getListFont().let { font ->
|
||||
Font(font.family, Font.BOLD, (font.size * 1.3).toInt())
|
||||
}
|
||||
}
|
||||
|
||||
private val title = panel {
|
||||
nonDefaultBackgroundColor?.let { background = it }
|
||||
add(titleLabel, BorderLayout.CENTER)
|
||||
removeButton?.let { add(it, BorderLayout.EAST) }
|
||||
}
|
||||
|
||||
private val descriptionLabel = DescriptionPanel()
|
||||
|
||||
override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) {
|
||||
panel {
|
||||
nonDefaultBackgroundColor?.let { background = it }
|
||||
add(title, BorderLayout.NORTH)
|
||||
add(descriptionLabel, BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setComponentsVisibility(isVisible: Boolean) {
|
||||
titleLabel.isVisible = isVisible
|
||||
descriptionLabel.isVisible = isVisible
|
||||
}
|
||||
|
||||
fun updateSelectedTemplate(template: Template?) {
|
||||
setComponentsVisibility(template != null)
|
||||
if (template != null) {
|
||||
titleLabel.text = template.title
|
||||
descriptionLabel.updateText(template.htmlDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TemplateSettingsComponent(
|
||||
valuesReadingContext: ValuesReadingContext,
|
||||
removeTemplate: () -> Unit
|
||||
) : DynamicComponent(valuesReadingContext) {
|
||||
private val templateDescriptionComponent = TemplateDescriptionComponent(
|
||||
needRemoveButton = true,
|
||||
nonDefaultBackgroundColor = UIUtil.getEditorPaneBackground(),
|
||||
onRemoveButtonClicked = removeTemplate
|
||||
).apply {
|
||||
component.bordered(needTopEmptyBorder = false, needBottomEmptyBorder = false)
|
||||
}
|
||||
|
||||
private val settings = SettingsList(emptyList(), valuesReadingContext).apply {
|
||||
component.bordered()
|
||||
}
|
||||
|
||||
fun setTemplate(sourceset: Sourceset, selectedTemplate: Template) {
|
||||
settings.setSettings(selectedTemplate.settings(sourceset))
|
||||
templateDescriptionComponent.updateSelectedTemplate(selectedTemplate)
|
||||
}
|
||||
|
||||
override val component: JComponent = splitterFor(
|
||||
templateDescriptionComponent.component,
|
||||
settings.component,
|
||||
vertical = true
|
||||
).apply {
|
||||
(this as JBSplitter).proportion = .7f
|
||||
}
|
||||
}
|
||||
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep.modulesEditor
|
||||
|
||||
import com.intellij.openapi.ui.popup.ListPopup
|
||||
import com.intellij.openapi.ui.popup.PopupStep
|
||||
import com.intellij.openapi.ui.popup.util.BaseListPopupStep
|
||||
import com.intellij.ui.popup.PopupFactoryImpl
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.buildList
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleSubType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.fullText
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.asHtml
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.htmlText
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.icon
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import javax.swing.Icon
|
||||
|
||||
class CreateModuleOrTargetPopup private constructor(
|
||||
private val target: Module?,
|
||||
private val allowMultiplatform: Boolean,
|
||||
private val allowAndroid: Boolean,
|
||||
private val allowIos: Boolean,
|
||||
private val createTarget: (TargetConfigurator) -> Unit,
|
||||
private val createModule: (ModuleConfigurator) -> Unit
|
||||
) {
|
||||
|
||||
private fun TargetConfigurator.needToShow(): Boolean {
|
||||
val allTargets = target?.subModules ?: return false
|
||||
return canCoexistsWith(allTargets.map { it.configurator as TargetConfigurator })
|
||||
}
|
||||
|
||||
private fun DisplayableSettingItem.needToShow(): Boolean = when (this) {
|
||||
is TargetConfigurator -> needToShow()
|
||||
is TargetConfiguratorGroupWithSubItems -> subItems.any { it.needToShow() }
|
||||
else -> false
|
||||
}
|
||||
|
||||
private inner class ChooseModuleOrMppModuleStep : BaseListPopupStep<ModuleConfigurator>(
|
||||
"Module Type",
|
||||
buildList {
|
||||
if (allowMultiplatform) +MppModuleConfigurator
|
||||
+JvmSinglePlatformModuleConfigurator
|
||||
if (allowAndroid) +AndroidSinglePlatformModuleConfigurator
|
||||
|
||||
//todo ios support
|
||||
//if (allowIos) +IOSSinglePlatformModuleConfigurator
|
||||
}
|
||||
) {
|
||||
override fun getIconFor(value: ModuleConfigurator): Icon = value.icon
|
||||
override fun getTextFor(value: ModuleConfigurator): String = value.htmlText
|
||||
|
||||
override fun onChosen(selectedValue: ModuleConfigurator?, finalChoice: Boolean): PopupStep<*>? =
|
||||
when (selectedValue) {
|
||||
null -> PopupStep.FINAL_CHOICE
|
||||
else -> {
|
||||
createModule(selectedValue)
|
||||
PopupStep.FINAL_CHOICE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class ChooseTargetTypeStep(
|
||||
targetConfiguratorGroup: TargetConfiguratorGroupWithSubItems,
|
||||
showTitle: Boolean
|
||||
) : BaseListPopupStep<DisplayableSettingItem>(
|
||||
"Target".takeIf { showTitle },
|
||||
targetConfiguratorGroup.subItems.filter { it.needToShow() }
|
||||
) {
|
||||
override fun getIconFor(value: DisplayableSettingItem): Icon? = when (value) {
|
||||
is DisplayableTargetConfiguratorGroup -> value.moduleType.icon
|
||||
is ModuleConfigurator -> value.moduleType.icon
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun getTextFor(value: DisplayableSettingItem): String = value.htmlText
|
||||
|
||||
override fun onChosen(selectedValue: DisplayableSettingItem?, finalChoice: Boolean): PopupStep<*>? {
|
||||
when {
|
||||
finalChoice && selectedValue is TargetConfigurator -> createTarget(selectedValue)
|
||||
selectedValue is TargetConfiguratorGroupWithSubItems ->
|
||||
return ChooseTargetTypeStep(selectedValue, showTitle = false)
|
||||
}
|
||||
return PopupStep.FINAL_CHOICE
|
||||
}
|
||||
}
|
||||
|
||||
private fun create(): ListPopup? = when (target?.kind) {
|
||||
ModuleKind.target -> null
|
||||
ModuleKind.multiplatform -> ChooseTargetTypeStep(TargetConfigurationGroups.FIRST, showTitle = true)
|
||||
else -> when {
|
||||
allowMultiplatform || allowAndroid || allowIos -> ChooseModuleOrMppModuleStep()
|
||||
else -> {
|
||||
createModule(JvmSinglePlatformModuleConfigurator)
|
||||
null
|
||||
}
|
||||
}
|
||||
}?.let { PopupFactoryImpl.getInstance().createListPopup(it) }
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
target: Module?,
|
||||
allowMultiplatform: Boolean,
|
||||
allowAndroid: Boolean,
|
||||
allowIos: Boolean,
|
||||
createTarget: (TargetConfigurator) -> Unit,
|
||||
createModule: (ModuleConfigurator) -> Unit
|
||||
) = CreateModuleOrTargetPopup(
|
||||
target = target,
|
||||
allowMultiplatform = allowMultiplatform,
|
||||
allowAndroid = allowAndroid,
|
||||
allowIos = allowIos,
|
||||
createTarget = createTarget,
|
||||
createModule = createModule
|
||||
).create()
|
||||
}
|
||||
}
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep.modulesEditor
|
||||
|
||||
import com.intellij.ui.JBColor
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.ListSettingType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ProjectKind
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.panel
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.SettingComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.BorderFactory
|
||||
import javax.swing.JComponent
|
||||
|
||||
|
||||
class ModulesEditorComponent(
|
||||
valuesReadingContext: ValuesReadingContext,
|
||||
oneEntrySelected: (data: DisplayableSettingItem?) -> Unit
|
||||
) : SettingComponent<List<Module>, ListSettingType<Module>>(KotlinPlugin::modules.reference, valuesReadingContext) {
|
||||
private val tree: ModulesEditorTree =
|
||||
ModulesEditorTree(
|
||||
onSelected = { oneEntrySelected(it) },
|
||||
addModule = { component ->
|
||||
val isMppProject = KotlinPlugin::projectKind.value == ProjectKind.Multiplatform
|
||||
moduleCreator.create(
|
||||
target = null, // The empty tree case
|
||||
allowMultiplatform = isMppProject,
|
||||
allowAndroid = isMppProject,
|
||||
allowIos = isMppProject,
|
||||
allModules = value ?: emptyList(),
|
||||
createModule = { model.add(it) }
|
||||
)?.showInCenterOf(component)
|
||||
}
|
||||
)
|
||||
|
||||
private val model = TargetsModel(tree, ::value)
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
model.update()
|
||||
}
|
||||
|
||||
private val moduleCreator = NewModuleCreator()
|
||||
|
||||
private val toolbarDecorator = ModulesEditorToolbarDecorator(
|
||||
tree = tree,
|
||||
moduleCreator = moduleCreator,
|
||||
model = model,
|
||||
getModules = { value ?: emptyList() },
|
||||
isMultiplatformProject = { KotlinPlugin::projectKind.value == ProjectKind.Multiplatform }
|
||||
)
|
||||
|
||||
override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) {
|
||||
panel {
|
||||
border = BorderFactory.createLineBorder(JBColor.border())
|
||||
add(toolbarDecorator.createToolPanel(), BorderLayout.CENTER)
|
||||
add(validationIndicator, BorderLayout.SOUTH)
|
||||
}
|
||||
}
|
||||
|
||||
override val validationIndicator = ValidationIndicator(showText = true).apply {
|
||||
background = tree.background
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep.modulesEditor
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.actionSystem.ActionToolbarPosition
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.ui.ToolbarDecorator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.AndroidSinglePlatformModuleConfigurator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.IOSSinglePlatformModuleConfigurator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.MppModuleConfigurator
|
||||
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.createPanelWithPopupHandler
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import javax.swing.JComponent
|
||||
|
||||
class ModulesEditorToolbarDecorator(
|
||||
private val tree: ModulesEditorTree,
|
||||
private val moduleCreator: NewModuleCreator,
|
||||
private val model: TargetsModel,
|
||||
private val getModules: () -> List<Module>,
|
||||
private val isMultiplatformProject: () -> Boolean
|
||||
) {
|
||||
private val toolbarDecorator: ToolbarDecorator = ToolbarDecorator.createDecorator(tree).apply {
|
||||
setToolbarPosition(ActionToolbarPosition.TOP)
|
||||
setAddAction { button ->
|
||||
val allModules = getModules().withAllSubModules(includeSourcesets = false)
|
||||
val target = tree.selectedSettingItem?.safeAs<Module>()
|
||||
val isRootModule = target == null
|
||||
val popup = moduleCreator.create(
|
||||
target = tree.selectedSettingItem?.safeAs(),
|
||||
allowMultiplatform = isRootModule
|
||||
&& allModules.none { it.configurator == MppModuleConfigurator },
|
||||
allowAndroid = isRootModule
|
||||
&& isMultiplatformProject()
|
||||
&& allModules.none { it.configurator == AndroidSinglePlatformModuleConfigurator },
|
||||
allowIos = isRootModule
|
||||
&& isMultiplatformProject()
|
||||
&& allModules.none { it.configurator == IOSSinglePlatformModuleConfigurator },
|
||||
allModules = allModules,
|
||||
createModule = model::add
|
||||
)
|
||||
popup?.show(button.preferredPopupPoint!!)
|
||||
}
|
||||
setAddActionUpdater { event ->
|
||||
event.presentation.apply {
|
||||
isEnabled = when (val selected = tree.selectedSettingItem) {
|
||||
is Module -> selected.configurator.canContainSubModules
|
||||
is Sourceset -> false
|
||||
null -> true
|
||||
else -> false
|
||||
}
|
||||
text = "Add" + when (tree.selectedSettingItem?.safeAs<Module>()?.kind) {
|
||||
ModuleKind.multiplatform -> " Target"
|
||||
ModuleKind.singleplatform -> " Module"
|
||||
ModuleKind.target -> ""
|
||||
null -> ""
|
||||
}
|
||||
}
|
||||
event.presentation.isEnabled
|
||||
}
|
||||
setRemoveAction {
|
||||
val moduleKindText = selectedModuleKindText ?: "Module"
|
||||
if (Messages.showOkCancelDialog(
|
||||
tree,
|
||||
buildString {
|
||||
appendln("Do you want to remove selected $moduleKindText?")
|
||||
if (tree.selectedSettingItem.safeAs<Module>()?.kind != ModuleKind.target) {
|
||||
appendln("This will also remove all submodules.")
|
||||
}
|
||||
appendln()
|
||||
appendln("This action cannot be undone.")
|
||||
},
|
||||
"Remove selected $moduleKindText?",
|
||||
"Remove",
|
||||
"Cancel",
|
||||
AllIcons.General.QuestionDialog
|
||||
) == Messages.OK
|
||||
) {
|
||||
model.removeSelected()
|
||||
}
|
||||
}
|
||||
setRemoveActionUpdater { event ->
|
||||
event.presentation.apply {
|
||||
isEnabled = tree.selectedSettingItem is Module
|
||||
text = "Remove" + selectedModuleKindText?.let { " $it" }.orEmpty()
|
||||
}
|
||||
event.presentation.isEnabled
|
||||
}
|
||||
|
||||
setMoveDownAction(null)
|
||||
setMoveUpAction(null)
|
||||
}
|
||||
|
||||
private val selectedModuleKindText
|
||||
get() = tree.selectedSettingItem.safeAs<Module>()?.kindText
|
||||
|
||||
fun createToolPanel(): JComponent = toolbarDecorator
|
||||
.createPanelWithPopupHandler(tree)
|
||||
.apply {
|
||||
border = null
|
||||
}
|
||||
}
|
||||
|
||||
private val Module.kindText
|
||||
get() = when (kind) {
|
||||
ModuleKind.multiplatform -> "Module"
|
||||
ModuleKind.singleplatform -> "Module"
|
||||
ModuleKind.target -> "Target"
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep.modulesEditor
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.ui.ColoredTreeCellRenderer
|
||||
import com.intellij.ui.SimpleTextAttributes
|
||||
import com.intellij.ui.treeStructure.Tree
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Sourceset
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.icon
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JTree
|
||||
import javax.swing.tree.DefaultMutableTreeNode
|
||||
import javax.swing.tree.DefaultTreeModel
|
||||
import javax.swing.tree.TreeSelectionModel
|
||||
|
||||
class ModulesEditorTree(
|
||||
private val onSelected: (DisplayableSettingItem?) -> Unit,
|
||||
private val addModule: (JComponent) -> Unit
|
||||
) : Tree(DefaultMutableTreeNode(PROJECT_USER_OBJECT)) {
|
||||
val model: DefaultTreeModel get() = super.getModel() as DefaultTreeModel
|
||||
|
||||
@Suppress("ClassName")
|
||||
object PROJECT_USER_OBJECT
|
||||
|
||||
fun reload() {
|
||||
val openPaths = (0 until rowCount).mapNotNull { row ->
|
||||
getPathForRow(row).takeIf { isExpanded(it) }
|
||||
}
|
||||
model.reload()
|
||||
openPaths.forEach(::expandPath)
|
||||
}
|
||||
|
||||
val selectedSettingItem
|
||||
get() = selectedNode?.userObject?.safeAs<DisplayableSettingItem>()
|
||||
|
||||
val selectedNode
|
||||
get() = getSelectedNodes(DefaultMutableTreeNode::class.java) {
|
||||
it.userObject is DisplayableSettingItem
|
||||
}.singleOrNull()
|
||||
|
||||
|
||||
init {
|
||||
getSelectionModel().selectionMode = TreeSelectionModel.SINGLE_TREE_SELECTION
|
||||
setShowsRootHandles(true)
|
||||
|
||||
addTreeSelectionListener {
|
||||
onSelected(selectedSettingItem)
|
||||
}
|
||||
|
||||
emptyText.clear()
|
||||
emptyText.appendText("No modules created")
|
||||
emptyText.appendSecondaryText(
|
||||
"Add a module to the project",
|
||||
SimpleTextAttributes.LINK_PLAIN_ATTRIBUTES
|
||||
) {
|
||||
addModule(this)
|
||||
}
|
||||
|
||||
setCellRenderer(object : ColoredTreeCellRenderer() {
|
||||
override fun customizeCellRenderer(
|
||||
tree: JTree,
|
||||
value: Any?,
|
||||
selected: Boolean,
|
||||
expanded: Boolean,
|
||||
leaf: Boolean,
|
||||
row: Int,
|
||||
hasFocus: Boolean
|
||||
) {
|
||||
if (value?.safeAs<DefaultMutableTreeNode>()?.userObject == PROJECT_USER_OBJECT) {
|
||||
icon = AllIcons.Nodes.Project
|
||||
append("Project")
|
||||
return
|
||||
}
|
||||
val setting = (value as? DefaultMutableTreeNode)?.userObject as? DisplayableSettingItem
|
||||
?: return
|
||||
icon = when (setting) {
|
||||
is Module -> setting.icon
|
||||
is Sourceset -> AllIcons.Nodes.Module
|
||||
else -> null
|
||||
}
|
||||
append(setting.text)
|
||||
setting.greyText?.let { greyText ->
|
||||
append(" ")
|
||||
append("($greyText)", SimpleTextAttributes.GRAYED_ATTRIBUTES)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep.modulesEditor
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.TargetConfigurator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.*
|
||||
|
||||
class NewModuleCreator {
|
||||
private fun suggestName(name: String, modules: List<Module>): String {
|
||||
val names = modules.map(Module::name).toSet()
|
||||
if (name !in names) return name
|
||||
var index = 1
|
||||
while ("${name}_$index" in names) {
|
||||
index++
|
||||
}
|
||||
return "${name}_$index"
|
||||
}
|
||||
|
||||
private fun newTarget(
|
||||
configurator: TargetConfigurator,
|
||||
allTargets: List<Module>
|
||||
): Module = MultiplatformTargetModule(
|
||||
suggestName(
|
||||
configurator.suggestedModuleName ?: configurator.moduleType.name,
|
||||
allTargets
|
||||
),
|
||||
configurator,
|
||||
SourcesetType.values().map { sourcesetType ->
|
||||
Sourceset(
|
||||
sourcesetType,
|
||||
configurator.moduleType,
|
||||
template = null,
|
||||
dependencies = emptyList()
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
fun create(
|
||||
target: Module?,
|
||||
allowMultiplatform: Boolean,
|
||||
allowAndroid: Boolean,
|
||||
allowIos: Boolean,
|
||||
allModules: List<Module>,
|
||||
createModule: (Module) -> Unit
|
||||
) = CreateModuleOrTargetPopup.create(
|
||||
target = target,
|
||||
allowMultiplatform = allowMultiplatform,
|
||||
allowAndroid = allowAndroid,
|
||||
allowIos = allowIos,
|
||||
createTarget = { targetConfigurator ->
|
||||
createModule(newTarget(targetConfigurator, target?.subModules.orEmpty()))
|
||||
},
|
||||
createModule = { configurator ->
|
||||
val name = suggestName(configurator.suggestedModuleName ?: "module", allModules)
|
||||
val sourcesets = when (configurator.moduleKind) {
|
||||
ModuleKind.multiplatform -> emptyList()
|
||||
else -> SourcesetType.values().map { sourcesetType ->
|
||||
Sourceset(
|
||||
sourcesetType,
|
||||
ModuleType.jvm,
|
||||
template = null,
|
||||
dependencies = emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
val createdModule = Module(
|
||||
name,
|
||||
configurator.moduleKind,
|
||||
configurator,
|
||||
sourcesets,
|
||||
emptyList()
|
||||
)
|
||||
createModule(createdModule)
|
||||
}
|
||||
)
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep.modulesEditor
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.ModuleKind
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import javax.swing.tree.DefaultMutableTreeNode
|
||||
import javax.swing.tree.TreePath
|
||||
import kotlin.reflect.KMutableProperty0
|
||||
|
||||
class TargetsModel(
|
||||
private val tree: ModulesEditorTree,
|
||||
private val value: KMutableProperty0<List<Module>?>
|
||||
) {
|
||||
private val root get() = tree.model.root as DefaultMutableTreeNode
|
||||
|
||||
private fun addToTheTree(module: Module, modifyValue: Boolean, parent: DefaultMutableTreeNode = root) {
|
||||
val pathsToExpand = mutableListOf<TreePath>()
|
||||
|
||||
fun add(moduleToAdd: Module, parentToAdd: DefaultMutableTreeNode) {
|
||||
DefaultMutableTreeNode(moduleToAdd).apply {
|
||||
val userObject = parentToAdd.userObject
|
||||
when {
|
||||
userObject is Module && userObject.kind == ModuleKind.singleplatform -> {
|
||||
val indexOfLastModule = parent.children()
|
||||
.toList()
|
||||
.indexOfLast {
|
||||
it.safeAs<DefaultMutableTreeNode>()?.userObject is Module
|
||||
}
|
||||
if (indexOfLastModule == -1) parent.insert(this, 0)
|
||||
else parent.insert(this, indexOfLastModule)
|
||||
}
|
||||
else -> parentToAdd.add(this)
|
||||
}
|
||||
pathsToExpand += TreePath(path)
|
||||
|
||||
moduleToAdd.subModules.forEach { subModule ->
|
||||
add(subModule, this)
|
||||
}
|
||||
moduleToAdd.sourcesets.forEach { sourceset ->
|
||||
val sourcesetNode = DefaultMutableTreeNode(sourceset)
|
||||
add(sourcesetNode)
|
||||
pathsToExpand += TreePath(sourcesetNode.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add(module, parent)
|
||||
|
||||
if (modifyValue) {
|
||||
when (val parentModule = parent.userObject) {
|
||||
ModulesEditorTree.PROJECT_USER_OBJECT -> value.set(value.get().orEmpty() + module)
|
||||
is Module -> parentModule.subModules += module
|
||||
}
|
||||
}
|
||||
tree.reload()
|
||||
pathsToExpand.forEach(tree::expandPath)
|
||||
}
|
||||
|
||||
fun add(module: Module) {
|
||||
addToTheTree(module, modifyValue = true, parent = tree.selectedNode ?: root)
|
||||
}
|
||||
|
||||
fun update() {
|
||||
root.removeAllChildren()
|
||||
value.get()?.forEach { addToTheTree(it, modifyValue = false) }
|
||||
if (value.get()?.isEmpty() == true) {
|
||||
tree.reload()
|
||||
}
|
||||
}
|
||||
|
||||
fun removeSelected() {
|
||||
val selectedNode = tree.selectedNode?.takeIf { it.userObject is Module } ?: return
|
||||
when (val parent = selectedNode.parent.safeAs<DefaultMutableTreeNode>()?.userObject) {
|
||||
ModulesEditorTree.PROJECT_USER_OBJECT -> {
|
||||
val index = selectedNode.parent.getIndex(selectedNode)
|
||||
selectedNode.removeFromParent()
|
||||
value.set(value.get()?.toMutableList().also { it?.removeAt(index) })
|
||||
}
|
||||
is Module -> {
|
||||
parent.subModules = parent.subModules.filterNot { it === selectedNode.userObject }
|
||||
selectedNode.removeFromParent()
|
||||
}
|
||||
}
|
||||
tree.reload()
|
||||
}
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting
|
||||
|
||||
import com.intellij.openapi.ui.ComboBox
|
||||
import com.intellij.ui.components.JBCheckBox
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.version.Version
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components.DropDownComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components.PathFieldComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components.TextFieldComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.label
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.panel
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.event.ItemEvent
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import javax.swing.DefaultComboBoxModel
|
||||
import javax.swing.JComponent
|
||||
|
||||
|
||||
sealed class DefaultSettingComponent<V : Any, T : SettingType<V>>(
|
||||
reference: SettingReference<V, T>,
|
||||
valuesReadingContext: ValuesReadingContext
|
||||
) : SettingComponent<V, T>(reference, valuesReadingContext) {
|
||||
companion object {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <V : Any, T : SettingType<V>> create(
|
||||
setting: SettingReference<V, T>,
|
||||
valuesReadingContext: ValuesReadingContext
|
||||
): DefaultSettingComponent<V, T> = when (setting.type) {
|
||||
VersionSettingType::class ->
|
||||
VersionSettingComponent(
|
||||
setting as SettingReference<Version, VersionSettingType>,
|
||||
valuesReadingContext
|
||||
) as DefaultSettingComponent<V, T>
|
||||
BooleanSettingType::class ->
|
||||
BooleanSettingComponent(
|
||||
setting as SettingReference<Boolean, BooleanSettingType>,
|
||||
valuesReadingContext
|
||||
) as DefaultSettingComponent<V, T>
|
||||
DropDownSettingType::class ->
|
||||
DropdownSettingComponent(
|
||||
setting as SettingReference<DisplayableSettingItem, DropDownSettingType<DisplayableSettingItem>>,
|
||||
valuesReadingContext
|
||||
) as DefaultSettingComponent<V, T>
|
||||
StringSettingType::class ->
|
||||
StringSettingComponent(
|
||||
setting as SettingReference<String, StringSettingType>,
|
||||
valuesReadingContext
|
||||
) as DefaultSettingComponent<V, T>
|
||||
PathSettingType::class ->
|
||||
PathSettingComponent(
|
||||
setting as SettingReference<Path, PathSettingType>,
|
||||
valuesReadingContext
|
||||
) as DefaultSettingComponent<V, T>
|
||||
else -> TODO(setting.type.qualifiedName!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class VersionSettingComponent(
|
||||
reference: SettingReference<Version, VersionSettingType>,
|
||||
valuesReadingContext: ValuesReadingContext
|
||||
) : DefaultSettingComponent<Version, VersionSettingType>(reference, valuesReadingContext) {
|
||||
private val settingLabel = label(setting.title)
|
||||
private val comboBox = ComboBox<Version>().apply {
|
||||
addItemListener { e ->
|
||||
if (e?.stateChange == ItemEvent.SELECTED) {
|
||||
value = e.item as Version
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
val values = setting.defaultValue?.let(::listOf).orEmpty()
|
||||
comboBox.model = DefaultComboBoxModel<Version>(values.toTypedArray())
|
||||
|
||||
if (values.isNotEmpty()) {
|
||||
value = values.first()
|
||||
}
|
||||
}
|
||||
|
||||
override val validationIndicator = ValidationIndicator(showText = false)
|
||||
override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) {
|
||||
panel {
|
||||
add(
|
||||
panel {
|
||||
add(validationIndicator, BorderLayout.WEST)
|
||||
add(settingLabel, BorderLayout.CENTER)
|
||||
},
|
||||
BorderLayout.WEST
|
||||
)
|
||||
add(comboBox, BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DropdownSettingComponent(
|
||||
reference: SettingReference<DisplayableSettingItem, DropDownSettingType<DisplayableSettingItem>>,
|
||||
valuesReadingContext: ValuesReadingContext
|
||||
) : DefaultSettingComponent<DisplayableSettingItem, DropDownSettingType<DisplayableSettingItem>>(
|
||||
reference,
|
||||
valuesReadingContext
|
||||
) {
|
||||
private val dropDownComponent = DropDownComponent(
|
||||
valuesReadingContext,
|
||||
setting.type.values,
|
||||
labelText = setting.title,
|
||||
onAnyValueUpdate = { newValue ->
|
||||
value = newValue
|
||||
}
|
||||
).asSubComponent()
|
||||
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
val valuesFiltered = setting.type.values.filter { setting.type.filter(reference, it) }
|
||||
dropDownComponent.updateValues(valuesFiltered)
|
||||
if (valuesFiltered.isNotEmpty()) {
|
||||
value = valuesFiltered.first()
|
||||
}
|
||||
}
|
||||
|
||||
override val validationIndicator: ValidationIndicator? = null
|
||||
override val component: JComponent = dropDownComponent.component
|
||||
}
|
||||
|
||||
class BooleanSettingComponent(
|
||||
reference: SettingReference<Boolean, BooleanSettingType>,
|
||||
valuesReadingContext: ValuesReadingContext
|
||||
) : DefaultSettingComponent<Boolean, BooleanSettingType>(reference, valuesReadingContext) {
|
||||
private val checkBox = JBCheckBox(setting.title).apply {
|
||||
addChangeListener {
|
||||
value = this@apply.isSelected
|
||||
}
|
||||
}
|
||||
|
||||
override fun onValueUpdated(reference: SettingReference<*, *>?) {
|
||||
super.onValueUpdated(reference)
|
||||
checkBox.isSelected = value ?: false
|
||||
}
|
||||
|
||||
override val validationIndicator = ValidationIndicator(showText = false)
|
||||
|
||||
override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) {
|
||||
panel {
|
||||
// TODO add validation indicator
|
||||
add(checkBox, BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class StringSettingComponent(
|
||||
reference: SettingReference<String, StringSettingType>,
|
||||
valuesReadingContext: ValuesReadingContext,
|
||||
showLabel: Boolean = true
|
||||
) : DefaultSettingComponent<String, StringSettingType>(reference, valuesReadingContext) {
|
||||
private val textFieldComponent = TextFieldComponent(
|
||||
valuesReadingContext,
|
||||
setting.defaultValue.orEmpty(),
|
||||
labelText = if (showLabel) setting.title else null,
|
||||
validator = setting.validator,
|
||||
onAnyValueUpdate = {
|
||||
value = it
|
||||
}
|
||||
).asSubComponent()
|
||||
|
||||
override val validationIndicator: ValidationIndicator? = null
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
textFieldComponent.value = value.orEmpty()
|
||||
}
|
||||
|
||||
override val component = textFieldComponent.component
|
||||
}
|
||||
|
||||
class PathSettingComponent(
|
||||
reference: SettingReference<Path, PathSettingType>,
|
||||
valuesReadingContext: ValuesReadingContext
|
||||
) : DefaultSettingComponent<Path, PathSettingType>(reference, valuesReadingContext) {
|
||||
private val pathFieldComponent = PathFieldComponent(
|
||||
valuesReadingContext,
|
||||
"",
|
||||
setting.title,
|
||||
validator = settingValidator { path ->
|
||||
setting.validator.validate(this, Paths.get(path))
|
||||
},
|
||||
onAnyValueUpdate = {
|
||||
value = Paths.get(it)
|
||||
}
|
||||
).asSubComponent()
|
||||
|
||||
override val validationIndicator: ValidationIndicator? = null
|
||||
|
||||
override val component = pathFieldComponent.component
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.Displayable
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.DynamicComponent
|
||||
|
||||
abstract class SettingComponent<V : Any, T: SettingType<V>>(
|
||||
val reference: SettingReference<V, T>,
|
||||
private val valuesReadingContext: ValuesReadingContext
|
||||
) : DynamicComponent(valuesReadingContext), Displayable {
|
||||
var value: V?
|
||||
get() = reference.value
|
||||
set(value) {
|
||||
reference.value = value
|
||||
}
|
||||
|
||||
val setting: Setting<V, T>
|
||||
get() = with(valuesReadingContext.context) {
|
||||
with(reference) { getSetting() }
|
||||
}
|
||||
|
||||
abstract val validationIndicator: ValidationIndicator?
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
updateValidationState()
|
||||
}
|
||||
|
||||
override fun onValueUpdated(reference: SettingReference<*, *>?) {
|
||||
component.isVisible = setting.isActive(valuesReadingContext)
|
||||
updateValidationState()
|
||||
}
|
||||
|
||||
private fun updateValidationState() {
|
||||
val value = value
|
||||
if (validationIndicator != null && value != null) {
|
||||
validationIndicator!!.validationState = setting.validator.validate(valuesReadingContext, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting
|
||||
|
||||
import com.intellij.ui.components.panels.VerticalLayout
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingReference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.Component
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.DynamicComponent
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JPanel
|
||||
|
||||
class SettingsList(
|
||||
settings: List<SettingReference<*, *>>,
|
||||
private val valuesReadingContext: ValuesReadingContext
|
||||
) : DynamicComponent(valuesReadingContext) {
|
||||
private val panel = JPanel(VerticalLayout(5))
|
||||
private var settingComponents: List<Component> = emptyList()
|
||||
|
||||
init {
|
||||
setSettings(settings)
|
||||
}
|
||||
|
||||
override val component: JComponent = panel
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
settingComponents.forEach { it.onInit() }
|
||||
}
|
||||
|
||||
fun setSettings(settings: List<SettingReference<*, *>>) {
|
||||
panel.removeAll()
|
||||
settingComponents = settings.map { setting ->
|
||||
DefaultSettingComponent.create(setting, valuesReadingContext)
|
||||
}
|
||||
settingComponents.forEach { setting -> setting.onInit(); panel.add(setting.component) }
|
||||
}
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.ui.JBColor
|
||||
import com.intellij.ui.components.JBLabel
|
||||
import com.intellij.util.ui.EmptyIcon
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.ValidationResult
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.label
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.BorderFactory
|
||||
import javax.swing.Icon
|
||||
import javax.swing.JPanel
|
||||
|
||||
class ValidationIndicator(
|
||||
defaultText: String? = null,
|
||||
private val showText: Boolean
|
||||
) : JPanel(BorderLayout()) {
|
||||
private val textLabel = defaultText?.let { label("$it: ") }
|
||||
private val errorLabel = label(" ").apply {
|
||||
foreground = JBColor.red
|
||||
}
|
||||
|
||||
init {
|
||||
border = BorderFactory.createEmptyBorder(3, 3, 3, 3)
|
||||
textLabel?.let { add(it, BorderLayout.WEST) }
|
||||
add(errorLabel, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
private fun setIcon(icon: Icon?) {
|
||||
if (textLabel != null) textLabel.icon = icon
|
||||
else errorLabel.icon = icon ?: EmptyIcon.ICON_16
|
||||
}
|
||||
|
||||
|
||||
var validationState: ValidationResult = ValidationResult.OK
|
||||
set(value) {
|
||||
field = value
|
||||
when (value) {
|
||||
ValidationResult.OK -> {
|
||||
setIcon(null)
|
||||
errorLabel.text = " "
|
||||
toolTipText = null
|
||||
}
|
||||
is ValidationResult.ValidationError -> {
|
||||
setIcon(AllIcons.General.Error)
|
||||
val errors = value.messages.firstOrNull().orEmpty()
|
||||
toolTipText = errors
|
||||
if (showText) errorLabel.text = errors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
import com.intellij.openapi.actionSystem.ActionPlaces
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
||||
import com.intellij.ui.JBColor
|
||||
import com.intellij.ui.JBSplitter
|
||||
import com.intellij.ui.PopupHandler
|
||||
import com.intellij.ui.ToolbarDecorator
|
||||
import com.intellij.ui.components.JBLabel
|
||||
import com.intellij.ui.components.JBTextField
|
||||
import com.intellij.util.ui.JBUI
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.UiConstants.GAP_BORDER_SIZE
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.ModuleConfigurator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.SimpleTargetConfigurator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.TargetConfigurator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleSubType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.*
|
||||
import java.awt.*
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import javax.swing.*
|
||||
import javax.swing.event.DocumentEvent
|
||||
import javax.swing.event.DocumentListener
|
||||
|
||||
internal inline fun label(text: String, bold: Boolean = false, init: JBLabel.() -> Unit = {}) = JBLabel().apply {
|
||||
font = UIUtil.getButtonFont().deriveFont(if (bold) Font.BOLD else Font.PLAIN)
|
||||
this.text = text
|
||||
init()
|
||||
}
|
||||
|
||||
inline fun panel(layout: LayoutManager = BorderLayout(), init: JPanel.() -> Unit = {}) = JPanel(layout).apply(init)
|
||||
|
||||
fun textField(defaultValue: String?, onUpdated: (value: String) -> Unit) = JBTextField(defaultValue).apply {
|
||||
document.addDocumentListener(object : DocumentListener {
|
||||
override fun insertUpdate(e: DocumentEvent?) = onUpdated(this@apply.text)
|
||||
override fun removeUpdate(e: DocumentEvent?) = onUpdated(this@apply.text)
|
||||
override fun changedUpdate(e: DocumentEvent?) = onUpdated(this@apply.text)
|
||||
})
|
||||
}
|
||||
|
||||
internal fun hyperlinkLabel(
|
||||
text: String,
|
||||
onClick: () -> Unit
|
||||
) = DescriptionPanel().apply {
|
||||
cursor = Cursor(Cursor.HAND_CURSOR)
|
||||
addMouseListener(object : MouseAdapter() {
|
||||
override fun mouseClicked(e: MouseEvent?) = onClick()
|
||||
})
|
||||
updateText("<a href='javascript:void(0)'>$text</a>".asHtml())
|
||||
}
|
||||
|
||||
internal fun String.asHtml() = "<html><body>$this</body></html>"
|
||||
|
||||
|
||||
val DisplayableSettingItem.htmlText
|
||||
get() = (text + greyText?.let { " <i>($greyText)</i>" }.orEmpty()).asHtml()
|
||||
|
||||
fun splitterFor(
|
||||
vararg components: JComponent,
|
||||
vertical: Boolean = false
|
||||
) = components.reduce { left, right ->
|
||||
JBSplitter(vertical, 1f / components.size).apply {
|
||||
firstComponent = left
|
||||
secondComponent = right
|
||||
dividerWidth = 1
|
||||
}
|
||||
}
|
||||
|
||||
val ModuleType.icon: Icon
|
||||
get() = when (this) {
|
||||
ModuleType.jvm -> KotlinIcons.SMALL_LOGO
|
||||
ModuleType.js -> KotlinIcons.JS
|
||||
ModuleType.native -> KotlinIcons.NATIVE
|
||||
ModuleType.common -> KotlinIcons.SMALL_LOGO
|
||||
}
|
||||
|
||||
|
||||
val Module.icon: Icon
|
||||
get() = when (kind) {
|
||||
ModuleKind.target -> configurator.moduleType.icon
|
||||
ModuleKind.multiplatform -> AllIcons.Nodes.Module
|
||||
ModuleKind.singleplatform -> AllIcons.Nodes.Module
|
||||
}
|
||||
|
||||
|
||||
val ModuleSubType.icon: Icon
|
||||
get() = moduleType.icon
|
||||
|
||||
val ModuleConfigurator.icon: Icon
|
||||
get() = when (this) {
|
||||
is SimpleTargetConfigurator -> moduleSubType.icon
|
||||
is TargetConfigurator -> moduleType.icon
|
||||
else -> AllIcons.Nodes.Module
|
||||
}
|
||||
|
||||
fun ToolbarDecorator.createPanelWithPopupHandler(popupTarget: JComponent) = createPanel().apply toolbarApply@{
|
||||
val actionGroup = DefaultActionGroup().apply {
|
||||
ToolbarDecorator.findAddButton(this@toolbarApply)?.let(this::add)
|
||||
ToolbarDecorator.findRemoveButton(this@toolbarApply)?.let(this::add)
|
||||
}
|
||||
PopupHandler.installPopupHandler(
|
||||
popupTarget,
|
||||
actionGroup,
|
||||
ActionPlaces.UNKNOWN,
|
||||
ActionManager.getInstance()
|
||||
)
|
||||
}
|
||||
|
||||
fun <C : JComponent> C.bordered(
|
||||
needTopEmptyBorder: Boolean = true,
|
||||
needBottomEmptyBorder: Boolean = true,
|
||||
needInnerEmptyBorder: Boolean = true,
|
||||
needLineBorder: Boolean = true
|
||||
) = apply {
|
||||
val lineBorder = if (needLineBorder) BorderFactory.createLineBorder(JBColor.border()) else null
|
||||
border = BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createEmptyBorder(
|
||||
if (needTopEmptyBorder) GAP_BORDER_SIZE else 0,
|
||||
0,
|
||||
if (needBottomEmptyBorder) GAP_BORDER_SIZE else 0,
|
||||
0
|
||||
),
|
||||
when {
|
||||
needInnerEmptyBorder -> BorderFactory.createCompoundBorder(
|
||||
lineBorder,
|
||||
BorderFactory.createEmptyBorder(GAP_BORDER_SIZE, GAP_BORDER_SIZE, GAP_BORDER_SIZE, GAP_BORDER_SIZE)
|
||||
)
|
||||
else -> lineBorder
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Copied from com.intellij.ide.projectWizard.ProjectSettingsStep.addField
|
||||
fun JPanel.addFieldWithLabel(label: String, field: JComponent) {
|
||||
val jLabel = JBLabel(label)
|
||||
jLabel.labelFor = field
|
||||
jLabel.verticalAlignment = SwingConstants.TOP
|
||||
add(
|
||||
jLabel,
|
||||
GridBagConstraints(
|
||||
0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST,
|
||||
GridBagConstraints.VERTICAL, JBUI.insets(5, 0, 5, 0), 4, 0
|
||||
)
|
||||
)
|
||||
add(
|
||||
field,
|
||||
GridBagConstraints(
|
||||
1, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER,
|
||||
GridBagConstraints.HORIZONTAL, JBUI.insetsBottom(5), 0, 0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
object UiConstants {
|
||||
const val GAP_BORDER_SIZE = 5
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.library.LibraryArtifact
|
||||
import org.jetbrains.kotlin.tools.projectWizard.library.MavenArtifact
|
||||
import org.jetbrains.kotlin.tools.projectWizard.library.NpmArtifact
|
||||
|
||||
fun LibraryArtifact.render() = when (this) {
|
||||
is MavenArtifact -> "$groupId:$artifactId"
|
||||
is NpmArtifact -> name
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user