From e2241aef885a6314d6d6ef940642952b06cf2796 Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Fri, 20 Mar 2020 00:02:21 +0300 Subject: [PATCH] Wizard: fix layout --- .../projectWizard/wizard/ui/Component.kt | 2 + .../ui/firstStep/FirstWizardStepComponent.kt | 40 +++++-- .../ProjectTemplateSettingComponent.kt | 4 +- .../secondStep/ModuleDependenciesComponent.kt | 4 + .../ui/secondStep/ModuleSettingsComponent.kt | 7 +- .../secondStep/SecondStepWizardComponent.kt | 5 + .../wizard/ui/setting/TitledComponentsList.kt | 113 ++++++++++++++---- .../tools/projectWizard/wizard/ui/ui.kt | 4 +- 8 files changed, 137 insertions(+), 42 deletions(-) diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/Component.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/Component.kt index 558c526ef02..cb5539cd9ce 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/Component.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/Component.kt @@ -70,6 +70,8 @@ abstract class DynamicComponent(private val context: Context) : Component() { } abstract class TitledComponent(context: Context) : DynamicComponent(context) { + open val needCentering: Boolean = true + open val maximumWidth: Int? = null abstract val title: String? open fun shouldBeShow(): Boolean = true } diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/FirstWizardStepComponent.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/FirstWizardStepComponent.kt index 759b4e47b3c..3522dfa138f 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/FirstWizardStepComponent.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/FirstWizardStepComponent.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.* import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep.modulesEditor.ModulesEditorComponent import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.TitledComponentsList import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.createSettingComponent +import java.awt.Cursor import java.awt.event.MouseAdapter import java.awt.event.MouseEvent import javax.swing.JComponent @@ -52,7 +53,9 @@ class ProjectSettingsComponent(ideWizard: IdeWizard) : DynamicComponent(ideWizar projectTemplateComponent, buildSystemSetting ), - context + context, + stretchY = true, + useBigYGap = true ).asSubComponent() override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) { @@ -63,8 +66,6 @@ class ProjectSettingsComponent(ideWizard: IdeWizard) : DynamicComponent(ideWizar row { buildSystemAdditionalSettingsComponent.component(growX) } - }.apply { - border = JBUI.Borders.empty(10) } } @@ -152,10 +153,24 @@ private class PomSettingsComponent(context: Context) : TitledComponentsList( StructurePlugin::artifactId.reference.createSettingComponent(context), StructurePlugin::version.reference.createSettingComponent(context) ), - context + context, + stretchY = true ) class KotlinJpsRuntimeComponent(ideWizard: IdeWizard) : DynamicComponent(ideWizard.context) { + private val componentList = TitledComponentsList( + listOf( + JdkComponent(ideWizard), + KotlinRuntimeComponentComponent(ideWizard) + ), + ideWizard.context, + stretchY = true + ).asSubComponent() + + override val component: JComponent = componentList.component +} + +private class JdkComponent(ideWizard: IdeWizard) : TitledComponent(ideWizard.context) { private val javaModuleBuilder = JavaModuleBuilder() private val jdkComboBox = JdkComboBox( ProjectSdksModel().apply { reset(null) }, @@ -166,14 +181,14 @@ class KotlinJpsRuntimeComponent(ideWizard: IdeWizard) : DynamicComponent(ideWiza ideWizard.jpsData.jdk = selectedJdk } } - override val component: JComponent = panel { - row("Project JDK:") { - borderPanel { addToCenter(jdkComboBox) }.addBorder(JBUI.Borders.empty(0, 7))(growX) - } - row("Kotlin Runtime:") { - ideWizard.jpsData.libraryOptionsPanel.simplePanel(growX) - } - } + + override val title: String = "Project JDK" + override val component: JComponent = jdkComboBox +} + +private class KotlinRuntimeComponentComponent(ideWizard: IdeWizard) : TitledComponent(ideWizard.context) { + override val title: String = "Kotlin Runtime" + override val component: JComponent = ideWizard.jpsData.libraryOptionsPanel.simplePanel } @Suppress("SpellCheckingInspection") @@ -182,6 +197,7 @@ private class HideableSection(text: String, private var component: JComponent) : private var isExpanded = false init { + titledSeparator.label.cursor = Cursor(Cursor.HAND_CURSOR) updateComponent(component) titledSeparator.addMouseListener(object : MouseAdapter() { override fun mouseReleased(e: MouseEvent) = update(!isExpanded) diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/ProjectTemplateSettingComponent.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/ProjectTemplateSettingComponent.kt index 7c59653749c..a007ffd41da 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/ProjectTemplateSettingComponent.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/firstStep/ProjectTemplateSettingComponent.kt @@ -13,7 +13,6 @@ import org.jetbrains.kotlin.tools.projectWizard.projectTemplates.ProjectTemplate import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.* 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.JComponent class ProjectTemplateSettingComponent( @@ -23,6 +22,7 @@ class ProjectTemplateSettingComponent( context ) { override val validationIndicator: ValidationIndicator? get() = null + override val needCentering: Boolean = false private val templateDescriptionComponent = TemplateDescriptionComponent().asSubComponent() private val list = ImmutableSingleSelectableListWithIcon( @@ -37,7 +37,7 @@ class ProjectTemplateSettingComponent( override val component: JComponent = borderPanel { addToCenter(borderPanel { addToCenter(list) }.addBorder(JBUI.Borders.empty(0,/*left*/ 3, 0, /*right*/ 3))) - addToBottom(templateDescriptionComponent.component.addBorder(JBUI.Borders.empty(/*top*/5,/*left*/ 3, 0, 0))) + addToBottom(templateDescriptionComponent.component.addBorder(JBUI.Borders.empty(/*top*/8,/*left*/ 3, /*bottom*/10, 0))) } private fun applySelectedTemplate() = modify { diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/ModuleDependenciesComponent.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/ModuleDependenciesComponent.kt index 4efdadd06be..3110a65e823 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/ModuleDependenciesComponent.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/ModuleDependenciesComponent.kt @@ -7,6 +7,7 @@ import com.intellij.ui.ColoredListCellRenderer import com.intellij.ui.SimpleTextAttributes import com.intellij.ui.ToolbarDecorator import com.intellij.ui.popup.PopupFactoryImpl +import com.intellij.util.ui.JBUI import org.jetbrains.kotlin.tools.projectWizard.core.Context import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.withAllSubModules @@ -22,6 +23,8 @@ class ModuleDependenciesComponent( ) : TitledComponent(context) { override val title: String = "Module dependencies" private val dependenciesList = ModuleDependenciesList() + override val needCentering: Boolean = false + override val maximumWidth: Int = 500 private val toolbarDecorator: ToolbarDecorator = ToolbarDecorator.createDecorator(dependenciesList).apply { setToolbarPosition(ActionToolbarPosition.BOTTOM) @@ -72,6 +75,7 @@ class ModuleDependenciesComponent( override val component: JPanel = toolbarDecorator.createPanelWithPopupHandler(dependenciesList).apply { preferredSize = Dimension(preferredSize.width, 200) + addBorder(JBUI.Borders.empty(0, 3)) } } diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/ModuleSettingsComponent.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/ModuleSettingsComponent.kt index 72081f826ba..3c565e70dc9 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/ModuleSettingsComponent.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/ModuleSettingsComponent.kt @@ -1,6 +1,5 @@ package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep -import com.intellij.util.ui.JBUI import org.jetbrains.kotlin.idea.projectWizard.UiEditorUsageStats import org.jetbrains.kotlin.tools.projectWizard.core.Context import org.jetbrains.kotlin.tools.projectWizard.core.Reader @@ -15,7 +14,6 @@ import org.jetbrains.kotlin.tools.projectWizard.templates.Template import org.jetbrains.kotlin.tools.projectWizard.templates.settings import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.DynamicComponent import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.TitledComponent -import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.borderPanel 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.TitledComponentsList @@ -29,10 +27,7 @@ class ModuleSettingsComponent( private val settingsList = TitledComponentsList(emptyList(), context).asSubComponent() private val moduleDependenciesComponent = ModuleDependenciesComponent(context) - override val component: JComponent = borderPanel { - addToCenter(settingsList.component) - border = JBUI.Borders.empty(20) - } + override val component: JComponent = settingsList.component var module: Module? = null set(value) { diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/SecondStepWizardComponent.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/SecondStepWizardComponent.kt index cf824516d88..3deda8cb26b 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/SecondStepWizardComponent.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/secondStep/SecondStepWizardComponent.kt @@ -1,5 +1,6 @@ package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep +import com.intellij.util.ui.JBUI import org.jetbrains.kotlin.idea.projectWizard.UiEditorUsageStats import org.jetbrains.kotlin.tools.projectWizard.core.Context import org.jetbrains.kotlin.tools.projectWizard.core.entity.ValidationResult @@ -9,6 +10,10 @@ 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 java.awt.Dimension +import javax.swing.Box +import javax.swing.BoxLayout +import javax.swing.JPanel class SecondStepWizardComponent( wizard: IdeWizard, diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/TitledComponentsList.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/TitledComponentsList.kt index 8804a1c4fdc..b9eb7e5be40 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/TitledComponentsList.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/setting/TitledComponentsList.kt @@ -1,20 +1,24 @@ package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting -import com.intellij.ui.layout.panel import com.intellij.util.ui.components.BorderLayoutPanel 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.settings.SettingReference -import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.DynamicComponent -import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.FocusableComponent -import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.TitledComponent +import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.* +import javax.swing.JComponent +import javax.swing.Spring +import javax.swing.SpringLayout open class TitledComponentsList( private var components: List, - private val context: Context + context: Context, + private val stretchY: Boolean = false, + private val globalMaxWidth: Int? = null, + useBigYGap: Boolean = false, ) : DynamicComponent(context) { private val ui = BorderLayoutPanel() + private val yGap = if (useBigYGap) yGapBig else yGapSmall + init { ui.addToCenter(createComponentsPanel(components)) } @@ -37,23 +41,92 @@ open class TitledComponentsList( ui.addToCenter(createComponentsPanel(newComponents)) } + private fun createComponentsPanel(components: List) = customPanel(SpringLayout()) { + if (components.isEmpty()) return@customPanel + val layout = this.layout as SpringLayout - fun setSettings(settings: List>) { - setComponents( - settings.map { setting -> - DefaultSettingComponent.create(setting, context, needLabel = false) + fun JComponent.constraints() = layout.getConstraints(this) + + val componentsWithLabels = components.mapNotNull { component -> + if (!component.shouldBeShow()) return@mapNotNull null + val label = label(component.title?.let { "$it:" }.orEmpty()) + TitledComponentData( + label.also { add(it) }.constraints(), + component.component.also { add(it) }.constraints(), + component.needCentering, + component.maximumWidth + ) + } + + fun TitledComponentData.centerConstraint() = + if (needCentering) component.height * .5f - label.height * .5f + else 4.asSpring() + + val labelWidth = componentsWithLabels.fold(componentsWithLabels.first().label.width) { spring, row -> + Spring.max(spring, row.label.width) + } + + componentsWithLabels.forEach { (label, component, _, componentMaxWidth) -> + label.width = labelWidth + val maxWidth = componentMaxWidth ?: globalMaxWidth + if (maxWidth == null) { + component[SpringLayout.EAST] = layout.getConstraint(SpringLayout.EAST, this) - xPanelPadding.asSpring() + } else { + component.width = maxWidth.asSpring() } - ) + } + + var lastLabel: SpringLayout.Constraints? = null + var lastComponent: SpringLayout.Constraints? = null + + for (data in componentsWithLabels) { + val (label, component) = data + label.x = xPanelPadding.asSpring() + component.x = label[SpringLayout.EAST] + xGap + + if (lastComponent != null && lastLabel != null) { + val constraint = lastComponent[SpringLayout.SOUTH] + yGap + label.y = constraint + data.centerConstraint() + component.y = constraint + } else { + label.y = data.centerConstraint() + yPanelPadding + component.y = yPanelPadding.asSpring() + } + + lastLabel = label + lastComponent = component + } + + if (stretchY) { + constraints()[SpringLayout.SOUTH] = lastComponent!![SpringLayout.SOUTH] + yGap + } } companion object { - private fun createComponentsPanel(components: List) = panel { - components.forEach { component -> - if (!component.shouldBeShow()) return@forEach - row(component.title?.let { "$it:" }.orEmpty()) { - component.component(growX) - } - } - } + private const val xGap = 5 + private const val yGapSmall = 6 + private const val yGapBig = 12 + private const val xPanelPadding = 16 + private const val yPanelPadding = 16 } -} \ No newline at end of file + + private data class TitledComponentData( + val label: SpringLayout.Constraints, + val component: SpringLayout.Constraints, + val needCentering: Boolean, + val maximumWidth: Int?, + ) +} + +private operator fun Spring.plus(other: Spring) = Spring.sum(this, other) +private operator fun Spring.plus(gap: Int) = Spring.sum(this, Spring.constant(gap)) +private operator fun Spring.minus(other: Spring) = this + Spring.minus(other) +private operator fun Spring.unaryMinus() = Spring.minus(this) +private operator fun Spring.times(by: Float) = Spring.scale(this, by) +private fun Int.asSpring() = Spring.constant(this) +private operator fun SpringLayout.Constraints.get(edgeName: String) = getConstraint(edgeName) +private operator fun SpringLayout.Constraints.set(edgeName: String, spring: Spring) { + setConstraint(edgeName, spring) +} + + diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/ui.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/ui.kt index 504a19615e4..baec689cfb4 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/ui.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/ui/ui.kt @@ -35,12 +35,12 @@ 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) + font = UIUtil.getLabelFont().deriveFont(if (bold) Font.BOLD else Font.PLAIN) this.text = text init() } -inline fun customPanel(layout: LayoutManager = BorderLayout(), init: JPanel.() -> Unit = {}) = JPanel(layout).apply(init) +inline fun customPanel(layout: LayoutManager? = BorderLayout(), init: JPanel.() -> Unit = {}) = JPanel(layout).apply(init) inline fun borderPanel(init: BorderLayoutPanel.() -> Unit = {}) = BorderLayoutPanel().apply(init)