Wizard: rework build system component

This commit is contained in:
Ilya Kirillov
2020-04-07 17:56:33 +03:00
parent c1f40907db
commit 80f09d66db
6 changed files with 130 additions and 135 deletions
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.firstStep
import com.intellij.openapi.actionSystem.ActionGroup
import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl
// needed for <=192 to work correctly
abstract class ActionToolbarImplWrapper(
place: String,
actionGroup: ActionGroup,
horizontal: Boolean
) : ActionToolbarImpl(place, actionGroup, horizontal)
@@ -0,0 +1,17 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.firstStep
import com.intellij.openapi.actionSystem.ActionGroup
import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl
import com.intellij.openapi.keymap.ex.KeymapManagerEx
// needed for <=192 to work correctly
abstract class ActionToolbarImplWrapper(
place: String,
actionGroup: ActionGroup,
horizontal: Boolean
) : ActionToolbarImpl(place, actionGroup, horizontal, KeymapManagerEx.getInstanceEx())
@@ -0,0 +1,17 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.firstStep
import com.intellij.openapi.actionSystem.ActionGroup
import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl
import com.intellij.openapi.keymap.ex.KeymapManagerEx
// needed for <=192 to work correctly
abstract class ActionToolbarImplWrapper(
place: String,
actionGroup: ActionGroup,
horizontal: Boolean
) : ActionToolbarImpl(place, actionGroup, horizontal, KeymapManagerEx.getInstanceEx())
@@ -1,35 +1,29 @@
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.firstStep package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.firstStep
import com.intellij.ide.ui.UISettings import com.intellij.icons.AllIcons
import com.intellij.openapi.Disposable import com.intellij.openapi.actionSystem.*
import com.intellij.openapi.actionSystem.ex.ActionButtonLook import com.intellij.openapi.actionSystem.ex.ActionButtonLook
import com.intellij.ui.layout.panel import com.intellij.openapi.actionSystem.impl.ActionButton
import com.intellij.util.ui.JBInsets import com.intellij.openapi.actionSystem.impl.ActionButtonWithText
import com.intellij.util.ui.JBUI import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl
import com.intellij.util.ui.UIUtil import com.intellij.openapi.util.Disposer
import com.intellij.util.ui.UIUtilities import icons.GradleIcons
import icons.OpenapiIcons
import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.tools.projectWizard.core.Context import org.jetbrains.kotlin.tools.projectWizard.core.Context
import org.jetbrains.kotlin.tools.projectWizard.core.entity.ValidationResult import org.jetbrains.kotlin.tools.projectWizard.core.entity.ValidationResult
import org.jetbrains.kotlin.tools.projectWizard.core.entity.isSpecificError import org.jetbrains.kotlin.tools.projectWizard.core.entity.isSpecificError
import org.jetbrains.kotlin.tools.projectWizard.core.entity.settings.DropDownSettingType import org.jetbrains.kotlin.tools.projectWizard.core.entity.settings.DropDownSettingType
import org.jetbrains.kotlin.tools.projectWizard.core.entity.settings.SettingReference
import org.jetbrains.kotlin.tools.projectWizard.core.entity.settings.reference import org.jetbrains.kotlin.tools.projectWizard.core.entity.settings.reference
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemPlugin 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.buildSystem.BuildSystemType
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.IdeaBasedComponentValidator import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.IdeaBasedComponentValidator
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.SettingComponent import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.SettingComponent
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.awt.Cursor
import java.awt.Dimension import java.awt.Dimension
import java.awt.Graphics import java.awt.Insets
import java.awt.Rectangle
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import javax.swing.JComponent import javax.swing.JComponent
import javax.swing.SwingConstants
import javax.swing.SwingUtilities
class BuildSystemTypeSettingComponent( class BuildSystemTypeSettingComponent(
@@ -37,24 +31,18 @@ class BuildSystemTypeSettingComponent(
) : SettingComponent<BuildSystemType, DropDownSettingType<BuildSystemType>>( ) : SettingComponent<BuildSystemType, DropDownSettingType<BuildSystemType>>(
BuildSystemPlugin::type.reference, BuildSystemPlugin::type.reference,
context context
), Disposable { ) {
override val forceLabelCenteringOffset: Int? = 3
private val buttons by lazy(LazyThreadSafetyMode.NONE) { private val toolbar by lazy(LazyThreadSafetyMode.NONE) {
read { setting.type.values.filter { setting.type.filter(this, reference, it) }.map(::BuildSystemChooseButton) } val buildSystemTypes = read { setting.type.values.filter { setting.type.filter(this, reference, it) } }
val actionGroup = DefaultActionGroup(buildSystemTypes.map(::BuildSystemTypeAction))
BuildSystemToolbar(ActionPlaces.UNKNOWN, actionGroup, true)
} }
override val component = panel { override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) {
row { toolbar
buttons.forEach { button -> button(growX) }
}
} }
override val validationIndicator: ValidationIndicator = IdeaBasedComponentValidator(this, component) override val validationIndicator: ValidationIndicator = IdeaBasedComponentValidator(Disposer.newDisposable(), component)
override fun onInit() {
super.onInit()
updateButtonsValidationState()
updateButtons()
}
override fun navigateTo(error: ValidationResult.ValidationError) { override fun navigateTo(error: ValidationResult.ValidationError) {
if (validationIndicator.validationState.isSpecificError(error)) { if (validationIndicator.validationState.isSpecificError(error)) {
@@ -62,117 +50,70 @@ class BuildSystemTypeSettingComponent(
} }
} }
override fun onValueUpdated(reference: SettingReference<*, *>?) {
super.onValueUpdated(reference)
if (reference == BuildSystemPlugin::type.reference) {
updateButtons()
}
if (reference == KotlinPlugin::projectKind.reference) {
updateButtonsValidationState()
}
}
private fun updateButtonsValidationState() {
buttons.forEach(BuildSystemChooseButton::updateValidationState)
}
private fun validateBuildSystem(buildSystem: BuildSystemType) = read { private fun validateBuildSystem(buildSystem: BuildSystemType) = read {
setting.validator.validate(this, buildSystem) setting.validator.validate(this, buildSystem)
} }
private fun updateButtons() { private inner class BuildSystemTypeAction(
buttons.forEach(BuildSystemChooseButton::updateSelectedBuildSystem) val buildSystemType: BuildSystemType
) : ToggleAction(buildSystemType.text, null, buildSystemType.icon) {
override fun isSelected(e: AnActionEvent): Boolean = value == buildSystemType
override fun setSelected(e: AnActionEvent, state: Boolean) {
if (state) {
value = buildSystemType
}
}
override fun update(e: AnActionEvent) {
super.update(e)
val validationResult = validateBuildSystem(buildSystemType)
e.presentation.isEnabled = validationResult.isOk
e.presentation.description = validationResult.safeAs<ValidationResult.ValidationError>()?.messages?.firstOrNull()
}
} }
override fun dispose() {} private inner class BuildSystemToolbar(
place: String,
actionGroup: ActionGroup,
horizontal: Boolean
) : ActionToolbarImplWrapper(place, actionGroup, horizontal) {
override fun createToolbarButton(
action: AnAction,
look: ActionButtonLook?,
place: String,
presentation: Presentation,
minimumSize: Dimension
): ActionButton = BuildSystemChooseButton(action as BuildSystemTypeAction, presentation, place, minimumSize)
}
private inner class BuildSystemChooseButton(private val buildSystemType: BuildSystemType) : JComponent() { private inner class BuildSystemChooseButton(
private val look = ActionButtonLook.SYSTEM_LOOK action: BuildSystemTypeAction,
presentation: Presentation,
init { place: String?,
font = UIUtil.getLabelFont() minimumSize: Dimension
preferredSize = Dimension(preferredSize.width, BUTTON_HEIGHT) ) : ActionButtonWithText(action, presentation, place, minimumSize) {
minimumSize = Dimension(minimumSize.width, BUTTON_HEIGHT) override fun getInsets(): Insets = super.getInsets().apply {
cursor = Cursor(Cursor.HAND_CURSOR) right += left
addMouseListener(object : MouseAdapter() { left = 0
override fun mouseClicked(e: MouseEvent?) = modify {
if (isValidBuildSystem) {
BuildSystemPlugin::type.reference.setValue(buildSystemType)
}
}
override fun mouseEntered(e: MouseEvent?) {
hovered = true
repaint()
}
override fun mouseExited(e: MouseEvent?) {
hovered = false
repaint()
}
})
} }
fun updateSelectedBuildSystem() { override fun getPreferredSize(): Dimension {
val currentBuildSystem = read { BuildSystemPlugin::type.settingValue } val old = super.getPreferredSize()
selected = currentBuildSystem == buildSystemType return Dimension(old.width + LEFT_RIGHT_PADDING * 2, old.height + TOP_BOTTOM_PADDING * 2)
repaint()
} }
fun updateValidationState() {
val validationResult = validateBuildSystem(buildSystemType)
isValidBuildSystem = validationResult.isOk
cursor = Cursor(if (isValidBuildSystem) Cursor.HAND_CURSOR else Cursor.DEFAULT_CURSOR)
toolTipText = validationResult.safeAs<ValidationResult.ValidationError>()
?.messages
?.firstOrNull()
?.takeUnless { isSelectedAndInvalid }
repaint()
}
// mostly copied from com.intellij.openapi.actionSystem.impl.ActionButtonWithText.paintComponent
override fun paintComponent(g: Graphics) {
UISettings.setupAntialiasing(g)
val fm = getFontMetrics(font)
val viewRect = Rectangle(size)
JBInsets.removeFrom(viewRect, insets)
val iconRect = Rectangle()
val textRect = Rectangle()
val text = SwingUtilities.layoutCompoundLabel(
this, fm, buildSystemType.text, null,
SwingConstants.CENTER, SwingConstants.CENTER,
SwingConstants.CENTER, SwingConstants.TRAILING,
viewRect, iconRect, textRect, 0
)
look.paintLookBackground(g, viewRect, bgColor())
g.color = if (isValidBuildSystem) foreground else UIUtil.getInactiveTextColor()
UIUtilities.drawStringUnderlineCharAt(
this, g, text, -1,
textRect.x, textRect.y + fm.ascent
)
}
private fun bgColor() = when {
selected -> JBUI.CurrentTheme.ActionButton.pressedBackground()
hovered -> JBUI.CurrentTheme.ActionButton.hoverBackground()
else -> background
}
private var selected: Boolean = false
private var hovered: Boolean = false
private var isValidBuildSystem: Boolean = true
private val isSelectedAndInvalid
get() = selected && !isValidBuildSystem
} }
companion object { companion object {
private const val BUTTON_HEIGHT = 25 private const val LEFT_RIGHT_PADDING = 10
private const val TOP_BOTTOM_PADDING = 4
} }
} }
private val BuildSystemType.icon
get() = when (this) {
BuildSystemType.GradleKotlinDsl -> KotlinIcons.GRADLE_SCRIPT
BuildSystemType.GradleGroovyDsl -> GradleIcons.Gradle
BuildSystemType.Maven -> OpenapiIcons.RepositoryLibraryLogo
BuildSystemType.Jps -> AllIcons.Nodes.Module
}
@@ -1,6 +1,7 @@
buildsystem.type.gradle.groovy=Gradle Groovy buildsystem.type.gradle.groovy=Gradle Groovy
buildsystem.type.gradle.kotlin=Gradle Kotlin buildsystem.type.gradle.kotlin=Gradle Kotlin
buildsystem.type.intellij=IntelliJ build system buildsystem.type.intellij=IntelliJ
buildsystem.type.intellij.full=IntelliJ build system
buildsystem.type.maven=Maven buildsystem.type.maven=Maven
error.template.not.found=Template with an id {0} is not found error.template.not.found=Template with an id {0} is not found
@@ -45,7 +45,7 @@ abstract class BuildSystemPlugin(context: Context) : Plugin(context) {
KotlinNewProjectWizardBundle.message( KotlinNewProjectWizardBundle.message(
"plugin.buildsystem.setting.type.error.wrong.project.kind", "plugin.buildsystem.setting.type.error.wrong.project.kind",
projectKind, projectKind,
buildSystemType.text buildSystemType.fullText
) )
) )
} else ValidationResult.OK } else ValidationResult.OK
@@ -122,10 +122,13 @@ data class BuildFileData(
@NonNls val buildFileName: String @NonNls val buildFileName: String
) )
enum class BuildSystemType(@Nls override val text: String) : DisplayableSettingItem { enum class BuildSystemType(@Nls override val text: String, val fullText: String = text) : DisplayableSettingItem {
GradleKotlinDsl(KotlinNewProjectWizardBundle.message("buildsystem.type.gradle.kotlin")), GradleKotlinDsl(KotlinNewProjectWizardBundle.message("buildsystem.type.gradle.kotlin")),
GradleGroovyDsl(KotlinNewProjectWizardBundle.message("buildsystem.type.gradle.groovy")), GradleGroovyDsl(KotlinNewProjectWizardBundle.message("buildsystem.type.gradle.groovy")),
Jps(KotlinNewProjectWizardBundle.message("buildsystem.type.intellij")), Jps(
KotlinNewProjectWizardBundle.message("buildsystem.type.intellij"),
KotlinNewProjectWizardBundle.message("buildsystem.type.intellij.full")
),
Maven(KotlinNewProjectWizardBundle.message("buildsystem.type.maven")) Maven(KotlinNewProjectWizardBundle.message("buildsystem.type.maven"))
; ;