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))
override val component = panel { BuildSystemToolbar(ActionPlaces.UNKNOWN, actionGroup, true)
row {
buttons.forEach { button -> button(growX) }
} }
override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) {
toolbar
} }
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 dispose() {} override fun setSelected(e: AnActionEvent, state: Boolean) {
if (state) {
private inner class BuildSystemChooseButton(private val buildSystemType: BuildSystemType) : JComponent() { value = buildSystemType
private val look = ActionButtonLook.SYSTEM_LOOK
init {
font = UIUtil.getLabelFont()
preferredSize = Dimension(preferredSize.width, BUTTON_HEIGHT)
minimumSize = Dimension(minimumSize.width, BUTTON_HEIGHT)
cursor = Cursor(Cursor.HAND_CURSOR)
addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent?) = modify {
if (isValidBuildSystem) {
BuildSystemPlugin::type.reference.setValue(buildSystemType)
} }
} }
override fun mouseEntered(e: MouseEvent?) { override fun update(e: AnActionEvent) {
hovered = true super.update(e)
repaint()
}
override fun mouseExited(e: MouseEvent?) {
hovered = false
repaint()
}
})
}
fun updateSelectedBuildSystem() {
val currentBuildSystem = read { BuildSystemPlugin::type.settingValue }
selected = currentBuildSystem == buildSystemType
repaint()
}
fun updateValidationState() {
val validationResult = validateBuildSystem(buildSystemType) val validationResult = validateBuildSystem(buildSystemType)
isValidBuildSystem = validationResult.isOk e.presentation.isEnabled = validationResult.isOk
cursor = Cursor(if (isValidBuildSystem) Cursor.HAND_CURSOR else Cursor.DEFAULT_CURSOR) e.presentation.description = validationResult.safeAs<ValidationResult.ValidationError>()?.messages?.firstOrNull()
toolTipText = validationResult.safeAs<ValidationResult.ValidationError>() }
?.messages
?.firstOrNull()
?.takeUnless { isSelectedAndInvalid }
repaint()
} }
private inner class BuildSystemToolbar(
// mostly copied from com.intellij.openapi.actionSystem.impl.ActionButtonWithText.paintComponent place: String,
override fun paintComponent(g: Graphics) { actionGroup: ActionGroup,
UISettings.setupAntialiasing(g) horizontal: Boolean
) : ActionToolbarImplWrapper(place, actionGroup, horizontal) {
val fm = getFontMetrics(font) override fun createToolbarButton(
val viewRect = Rectangle(size) action: AnAction,
JBInsets.removeFrom(viewRect, insets) look: ActionButtonLook?,
place: String,
val iconRect = Rectangle() presentation: Presentation,
val textRect = Rectangle() minimumSize: Dimension
val text = SwingUtilities.layoutCompoundLabel( ): ActionButton = BuildSystemChooseButton(action as BuildSystemTypeAction, presentation, place, minimumSize)
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 { private inner class BuildSystemChooseButton(
selected -> JBUI.CurrentTheme.ActionButton.pressedBackground() action: BuildSystemTypeAction,
hovered -> JBUI.CurrentTheme.ActionButton.hoverBackground() presentation: Presentation,
else -> background place: String?,
minimumSize: Dimension
) : ActionButtonWithText(action, presentation, place, minimumSize) {
override fun getInsets(): Insets = super.getInsets().apply {
right += left
left = 0
} }
private var selected: Boolean = false override fun getPreferredSize(): Dimension {
private var hovered: Boolean = false val old = super.getPreferredSize()
private var isValidBuildSystem: Boolean = true return Dimension(old.width + LEFT_RIGHT_PADDING * 2, old.height + TOP_BOTTOM_PADDING * 2)
}
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"))
; ;