From 80f09d66db38fef36d5de56c04fd97c07b7bba5c Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Tue, 7 Apr 2020 17:56:33 +0300 Subject: [PATCH] Wizard: rework build system component --- .../ui/firstStep/ActionToolbarImplWrapper.kt | 16 ++ .../firstStep/ActionToolbarImplWrapper.kt.191 | 17 ++ .../firstStep/ActionToolbarImplWrapper.kt.192 | 17 ++ .../BuildSystemTypeSettingComponent.kt | 203 +++++++----------- .../KotlinNewProjectWizardBundle.properties | 3 +- .../plugins/buildSystem/BuildSystemPlugin.kt | 9 +- 6 files changed, 130 insertions(+), 135 deletions(-) create mode 100644 idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/ActionToolbarImplWrapper.kt create mode 100644 idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/ActionToolbarImplWrapper.kt.191 create mode 100644 idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/ActionToolbarImplWrapper.kt.192 diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/ActionToolbarImplWrapper.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/ActionToolbarImplWrapper.kt new file mode 100644 index 00000000000..ad9afdb0442 --- /dev/null +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/ActionToolbarImplWrapper.kt @@ -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) \ No newline at end of file diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/ActionToolbarImplWrapper.kt.191 b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/ActionToolbarImplWrapper.kt.191 new file mode 100644 index 00000000000..13cf4d2a92c --- /dev/null +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/ActionToolbarImplWrapper.kt.191 @@ -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()) \ No newline at end of file diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/ActionToolbarImplWrapper.kt.192 b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/ActionToolbarImplWrapper.kt.192 new file mode 100644 index 00000000000..13cf4d2a92c --- /dev/null +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/ActionToolbarImplWrapper.kt.192 @@ -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()) \ No newline at end of file diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/BuildSystemTypeSettingComponent.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/BuildSystemTypeSettingComponent.kt index 12571c3a610..27b4c5cb63b 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/BuildSystemTypeSettingComponent.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/BuildSystemTypeSettingComponent.kt @@ -1,35 +1,29 @@ package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.firstStep -import com.intellij.ide.ui.UISettings -import com.intellij.openapi.Disposable +import com.intellij.icons.AllIcons +import com.intellij.openapi.actionSystem.* import com.intellij.openapi.actionSystem.ex.ActionButtonLook -import com.intellij.ui.layout.panel -import com.intellij.util.ui.JBInsets -import com.intellij.util.ui.JBUI -import com.intellij.util.ui.UIUtil -import com.intellij.util.ui.UIUtilities +import com.intellij.openapi.actionSystem.impl.ActionButton +import com.intellij.openapi.actionSystem.impl.ActionButtonWithText +import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl +import com.intellij.openapi.util.Disposer +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.entity.ValidationResult 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.SettingReference 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.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.SettingComponent import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator import org.jetbrains.kotlin.utils.addToStdlib.safeAs -import java.awt.Cursor import java.awt.Dimension -import java.awt.Graphics -import java.awt.Rectangle -import java.awt.event.MouseAdapter -import java.awt.event.MouseEvent +import java.awt.Insets import javax.swing.JComponent -import javax.swing.SwingConstants -import javax.swing.SwingUtilities class BuildSystemTypeSettingComponent( @@ -37,24 +31,18 @@ class BuildSystemTypeSettingComponent( ) : SettingComponent>( BuildSystemPlugin::type.reference, context -), Disposable { - override val forceLabelCenteringOffset: Int? = 3 - private val buttons by lazy(LazyThreadSafetyMode.NONE) { - read { setting.type.values.filter { setting.type.filter(this, reference, it) }.map(::BuildSystemChooseButton) } +) { + + private val toolbar by lazy(LazyThreadSafetyMode.NONE) { + 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 { - row { - buttons.forEach { button -> button(growX) } - } + override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) { + toolbar } - override val validationIndicator: ValidationIndicator = IdeaBasedComponentValidator(this, component) - - override fun onInit() { - super.onInit() - updateButtonsValidationState() - updateButtons() - } + override val validationIndicator: ValidationIndicator = IdeaBasedComponentValidator(Disposer.newDisposable(), component) override fun navigateTo(error: ValidationResult.ValidationError) { 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 { setting.validator.validate(this, buildSystem) } - private fun updateButtons() { - buttons.forEach(BuildSystemChooseButton::updateSelectedBuildSystem) + private inner class BuildSystemTypeAction( + 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()?.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 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?) { - hovered = true - repaint() - } - - override fun mouseExited(e: MouseEvent?) { - hovered = false - repaint() - } - }) + private inner class BuildSystemChooseButton( + action: BuildSystemTypeAction, + presentation: Presentation, + place: String?, + minimumSize: Dimension + ) : ActionButtonWithText(action, presentation, place, minimumSize) { + override fun getInsets(): Insets = super.getInsets().apply { + right += left + left = 0 } - fun updateSelectedBuildSystem() { - val currentBuildSystem = read { BuildSystemPlugin::type.settingValue } - selected = currentBuildSystem == buildSystemType - repaint() + override fun getPreferredSize(): Dimension { + val old = super.getPreferredSize() + return Dimension(old.width + LEFT_RIGHT_PADDING * 2, old.height + TOP_BOTTOM_PADDING * 2) } - - fun updateValidationState() { - val validationResult = validateBuildSystem(buildSystemType) - isValidBuildSystem = validationResult.isOk - cursor = Cursor(if (isValidBuildSystem) Cursor.HAND_CURSOR else Cursor.DEFAULT_CURSOR) - toolTipText = validationResult.safeAs() - ?.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 { - private const val BUTTON_HEIGHT = 25 + private const val LEFT_RIGHT_PADDING = 10 + private const val TOP_BOTTOM_PADDING = 4 } -} \ No newline at end of file +} + +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 + } \ No newline at end of file diff --git a/libraries/tools/new-project-wizard/resources/messages/KotlinNewProjectWizardBundle.properties b/libraries/tools/new-project-wizard/resources/messages/KotlinNewProjectWizardBundle.properties index 00a4b445ba2..3f6dd049379 100644 --- a/libraries/tools/new-project-wizard/resources/messages/KotlinNewProjectWizardBundle.properties +++ b/libraries/tools/new-project-wizard/resources/messages/KotlinNewProjectWizardBundle.properties @@ -1,6 +1,7 @@ buildsystem.type.gradle.groovy=Gradle Groovy 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 error.template.not.found=Template with an id {0} is not found diff --git a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/plugins/buildSystem/BuildSystemPlugin.kt b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/plugins/buildSystem/BuildSystemPlugin.kt index a57309c43aa..c527c485223 100644 --- a/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/plugins/buildSystem/BuildSystemPlugin.kt +++ b/libraries/tools/new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/plugins/buildSystem/BuildSystemPlugin.kt @@ -45,7 +45,7 @@ abstract class BuildSystemPlugin(context: Context) : Plugin(context) { KotlinNewProjectWizardBundle.message( "plugin.buildsystem.setting.type.error.wrong.project.kind", projectKind, - buildSystemType.text + buildSystemType.fullText ) ) } else ValidationResult.OK @@ -122,10 +122,13 @@ data class BuildFileData( @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")), 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")) ;