diff --git a/platform/platform-impl/src/com/intellij/ui/dsl/CellBuilder.kt b/platform/platform-impl/src/com/intellij/ui/dsl/CellBuilder.kt index 7ed72633a27a..c61630d330f9 100644 --- a/platform/platform-impl/src/com/intellij/ui/dsl/CellBuilder.kt +++ b/platform/platform-impl/src/com/intellij/ui/dsl/CellBuilder.kt @@ -1,108 +1,25 @@ // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.ui.dsl -import com.intellij.openapi.ui.panel.ComponentPanelBuilder -import com.intellij.openapi.util.NlsContexts -import com.intellij.ui.dsl.gridLayout.HorizontalAlign -import com.intellij.ui.dsl.gridLayout.VerticalAlign import com.intellij.ui.layout.* -import com.intellij.util.SmartList import org.jetbrains.annotations.ApiStatus import javax.swing.AbstractButton import javax.swing.JComponent import kotlin.reflect.KMutableProperty0 -@DslMarker -private annotation class CellBuilderMarker - @ApiStatus.Experimental -@CellBuilderMarker -open class CellBuilder( - private val builder: PanelBuilder, - val component: T, - val viewComponent: JComponent = component) { +interface CellBuilder : CellBuilderBase> { - var horizontalAlign = HorizontalAlign.LEFT - private set - var verticalAlign = VerticalAlign.CENTER - private set - var comment: JComponent? = null - private set - var rightGap = 0 - private set - private var applyIfEnabled = false + fun applyToComponent(task: T.() -> Unit): CellBuilder - fun applyToComponent(task: T.() -> Unit): CellBuilder { - component.task() - return this - } - - fun enabled(isEnabled: Boolean): CellBuilder { - viewComponent.isEnabled = isEnabled - return this - } + fun enabled(isEnabled: Boolean): CellBuilder /** * If this method is called, the value of the component will be stored to the backing property only if the component is enabled */ - fun applyIfEnabled(): CellBuilder { - applyIfEnabled = true - return this - } + fun applyIfEnabled(): CellBuilder - fun alignHorizontal(horizontalAlign: HorizontalAlign): CellBuilder { - this.horizontalAlign = horizontalAlign - return this - } - - fun alignVertical(verticalAlign: VerticalAlign): CellBuilder { - this.verticalAlign = verticalAlign - return this - } - - /** - * Separates next cell in current row with [SpacingConfiguration.horizontalUnrelatedGap]. Should not be used for last cell in a row - */ - fun rightUnrelatedGap(): CellBuilder { - rightGap = builder.spacing.horizontalUnrelatedGap - return this - } - - fun comment(@NlsContexts.DetailedDescription comment: String, - maxLineLength: Int = ComponentPanelBuilder.MAX_COMMENT_WIDTH): CellBuilder { - this.comment = ComponentPanelBuilder.createCommentComponent(comment, true, maxLineLength, true) - return this - } - - private fun shouldSaveOnApply(): Boolean { - return !(applyIfEnabled && !viewComponent.isEnabled) - } - - private fun onApply(callback: () -> Unit): CellBuilder { - builder.applyCallbacks.getOrPut(component) { SmartList() }.add(callback) - return this - } - - private fun onReset(callback: () -> Unit): CellBuilder { - builder.resetCallbacks.getOrPut(component) { SmartList() }.add(callback) - return this - } - - private fun onIsModified(callback: () -> Boolean): CellBuilder { - builder.isModifiedCallbacks.getOrPut(component) { SmartList() }.add(callback) - return this - } - - fun bind( - componentGet: (T) -> V, - componentSet: (T, V) -> Unit, - modelBinding: PropertyBinding - ): CellBuilder { - onApply { if (shouldSaveOnApply()) modelBinding.set(componentGet(component)) } - onReset { componentSet(component, modelBinding.get()) } - onIsModified { shouldSaveOnApply() && componentGet(component) != modelBinding.get() } - return this - } + fun bind(componentGet: (T) -> V, componentSet: (T, V) -> Unit, modelBinding: PropertyBinding): CellBuilder } fun CellBuilder.bind(modelBinding: PropertyBinding): CellBuilder { diff --git a/platform/platform-impl/src/com/intellij/ui/dsl/CellBuilderBase.kt b/platform/platform-impl/src/com/intellij/ui/dsl/CellBuilderBase.kt new file mode 100644 index 000000000000..3a511cb8acb7 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/dsl/CellBuilderBase.kt @@ -0,0 +1,36 @@ +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.ui.dsl + +import com.intellij.openapi.ui.panel.ComponentPanelBuilder +import com.intellij.openapi.util.NlsContexts +import com.intellij.ui.dsl.gridLayout.HorizontalAlign +import com.intellij.ui.dsl.gridLayout.VerticalAlign +import org.jetbrains.annotations.ApiStatus +import javax.swing.JComponent + +@DslMarker +private annotation class CellBuilderMarker + +@ApiStatus.Experimental +@CellBuilderMarker +interface CellBuilderBase> { + + val horizontalAlign: HorizontalAlign + val verticalAlign: VerticalAlign + val rightGap: Int + val comment: JComponent? + + fun alignHorizontal(horizontalAlign: HorizontalAlign): CellBuilderBase + + fun alignVertical(verticalAlign: VerticalAlign): CellBuilderBase + + fun comment(@NlsContexts.DetailedDescription comment: String, + maxLineLength: Int = ComponentPanelBuilder.MAX_COMMENT_WIDTH): CellBuilderBase + + /** + * Separates next cell in current row with [SpacingConfiguration.horizontalUnrelatedGap]. + * Should not be used for last cell in a row + */ + fun rightUnrelatedGap(): CellBuilderBase + +} diff --git a/platform/platform-impl/src/com/intellij/ui/dsl/PanelBuilder.kt b/platform/platform-impl/src/com/intellij/ui/dsl/PanelBuilder.kt index e68b9f0ad24c..cbac5b2fe2ed 100644 --- a/platform/platform-impl/src/com/intellij/ui/dsl/PanelBuilder.kt +++ b/platform/platform-impl/src/com/intellij/ui/dsl/PanelBuilder.kt @@ -1,112 +1,7 @@ // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.ui.dsl -import com.intellij.openapi.diagnostic.Logger -import com.intellij.openapi.ui.DialogPanel -import com.intellij.ui.dsl.gridLayout.Gaps -import com.intellij.ui.dsl.gridLayout.JBGridLayout -import com.intellij.ui.dsl.gridLayout.builders.RowsGridBuilder import org.jetbrains.annotations.ApiStatus -import org.jetbrains.annotations.Nls -import javax.swing.* -import javax.swing.text.JTextComponent @ApiStatus.Experimental -class PanelBuilder { - - companion object { - private val LOG = Logger.getInstance(PanelBuilder::class.java) - - private val DEFAULT_VERTICAL_GAP_COMPONENTS = setOf(JTextComponent::class, AbstractButton::class, JSpinner::class, JLabel::class) - } - - val spacing = createIntelliJSpacingConfiguration() - - val applyCallbacks: MutableMap Unit>> = linkedMapOf() - val resetCallbacks: MutableMap Unit>> = linkedMapOf() - val isModifiedCallbacks: MutableMap Boolean>> = linkedMapOf() - - private val rows = mutableListOf() - - fun row(@Nls label: String, init: RowBuilder.() -> Unit): RowBuilder { - return row(JLabel(label), init) - } - - fun row(label: JLabel? = null, init: RowBuilder.() -> Unit): RowBuilder { - val result = RowBuilder(this, label) - result.init() - rows.add(result) - return result - } - - internal fun build(): DialogPanel { - val result = DialogPanel(layout = JBGridLayout()) - val columnsCount = rows.maxOf { it.cells.size } - val rowsGridBuilder = RowsGridBuilder(result) - - for ((y, row) in rows.withIndex()) { - for ((x, cell) in row.cells.withIndex()) { - val width: Int - var rightGap = cell.rightGap - if (x == row.cells.size - 1) { - width = columnsCount - x - if (cell.rightGap > 0) { - LOG.warn("Right gap is set for last cell and will be ignored: rightGap = $rightGap") - rightGap = 0 - } - } - else { - width = 1 - } - - val insets = cell.component.insets - val verticalGap = getDefaultVerticalGap(cell.component) - rowsGridBuilder.cell(cell.component, width = width, horizontalAlign = cell.horizontalAlign, verticalAlign = cell.verticalAlign, - gaps = Gaps(top = verticalGap, bottom = verticalGap, right = rightGap), - visualPaddings = Gaps(top = insets.top, left = insets.left, bottom = insets.bottom, right = insets.right)) - } - - rowsGridBuilder.row() - createCommentRow(rowsGridBuilder, row, columnsCount) - } - - return result - } - - /** - * Returns default top and bottom gap for [component] - */ - private fun getDefaultVerticalGap(component: JComponent): Int { - return if (DEFAULT_VERTICAL_GAP_COMPONENTS.any { clazz -> clazz.isInstance(component) }) spacing.verticalComponentGap else 0 - } - - /** - * Appends comment (currently one comment for a row is supported, can be fixed later) - */ - private fun createCommentRow(builder: RowsGridBuilder, row: RowBuilder, columnsCount: Int) { - for ((column, cell) in row.cells.withIndex()) { - cell.comment?.let { - createComment(builder, column, columnsCount, row.label != null, it, cell.viewComponent) - return - } - } - } - - private fun createComment(builder: RowsGridBuilder, - column: Int, - columnsCount: Int, - labeledRow: Boolean, - comment: JComponent, - anchorComponent: JComponent) { - if (labeledRow && column == 1) { - builder.cell(comment, width = columnsCount) - } - else { - val gaps = Gaps(left = if (anchorComponent is JToggleButton) spacing.horizontalIndent else 0, - bottom = spacing.verticalCommentBottomGap) - builder.skip(column) - builder.cell(comment, columnsCount - column, gaps = gaps) - } - builder.row() - } -} +interface PanelBuilder: PanelBuilderBase, CellBuilderBase diff --git a/platform/platform-impl/src/com/intellij/ui/dsl/PanelBuilderBase.kt b/platform/platform-impl/src/com/intellij/ui/dsl/PanelBuilderBase.kt new file mode 100644 index 000000000000..8bc0b7cc0454 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/dsl/PanelBuilderBase.kt @@ -0,0 +1,14 @@ +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.ui.dsl + +import org.jetbrains.annotations.ApiStatus +import org.jetbrains.annotations.Nls +import javax.swing.JLabel + +@ApiStatus.Experimental +interface PanelBuilderBase { + + fun row(@Nls label: String, init: RowBuilder.() -> Unit): RowBuilder + + fun row(label: JLabel? = null, init: RowBuilder.() -> Unit): RowBuilder +} diff --git a/platform/platform-impl/src/com/intellij/ui/dsl/RowBuilder.kt b/platform/platform-impl/src/com/intellij/ui/dsl/RowBuilder.kt index f8677b6e7667..7dee5c07cdec 100644 --- a/platform/platform-impl/src/com/intellij/ui/dsl/RowBuilder.kt +++ b/platform/platform-impl/src/com/intellij/ui/dsl/RowBuilder.kt @@ -1,41 +1,26 @@ // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.ui.dsl -import com.intellij.BundleBase import com.intellij.openapi.util.NlsContexts import com.intellij.ui.components.JBCheckBox import org.jetbrains.annotations.ApiStatus import java.awt.event.ActionEvent import javax.swing.JButton import javax.swing.JComponent -import javax.swing.JLabel @DslMarker private annotation class RowBuilderMarker @ApiStatus.Experimental @RowBuilderMarker -class RowBuilder(val builder: PanelBuilder, val label: JLabel?) { +interface RowBuilder { - internal val cells = mutableListOf>() + fun cell(component: T): CellBuilder - init { - label?.let { cell(it) } - } + fun panel(init: PanelBuilder.() -> Unit): PanelBuilder - fun cell(component: T): CellBuilder { - val result = CellBuilder(builder, component) - cells.add(result) - return result - } + fun checkBox(@NlsContexts.Checkbox text: String): CellBuilder - fun checkBox(@NlsContexts.Checkbox text: String): CellBuilder { - return cell(JBCheckBox(text)) - } + fun button(@NlsContexts.Button text: String, actionListener: (event: ActionEvent) -> Unit): CellBuilder - fun button(@NlsContexts.Button text: String, actionListener: (event: ActionEvent) -> Unit): CellBuilder { - val button = JButton(BundleBase.replaceMnemonicAmpersand(text)) - button.addActionListener(actionListener) - return cell(button) - } } diff --git a/platform/platform-impl/src/com/intellij/ui/dsl/SpacingConfiguration.kt b/platform/platform-impl/src/com/intellij/ui/dsl/SpacingConfiguration.kt index 4b05ce78d149..4328663fbd11 100644 --- a/platform/platform-impl/src/com/intellij/ui/dsl/SpacingConfiguration.kt +++ b/platform/platform-impl/src/com/intellij/ui/dsl/SpacingConfiguration.kt @@ -1,7 +1,6 @@ // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.ui.dsl -import com.intellij.util.ui.JBUI import org.jetbrains.annotations.ApiStatus @ApiStatus.Experimental @@ -28,15 +27,3 @@ interface SpacingConfiguration { val verticalCommentBottomGap: Int } - -// https://jetbrains.github.io/ui/controls/input_field/#spacing -@ApiStatus.Experimental -fun createIntelliJSpacingConfiguration(): SpacingConfiguration { - return object : SpacingConfiguration { - - override val horizontalUnrelatedGap = JBUI.scale(16) - override val horizontalIndent = JBUI.scale(20) - override val verticalComponentGap = JBUI.scale(6) - override val verticalCommentBottomGap = JBUI.scale(6) - } -} diff --git a/platform/platform-impl/src/com/intellij/ui/dsl/impl/CellBuilderBaseImpl.kt b/platform/platform-impl/src/com/intellij/ui/dsl/impl/CellBuilderBaseImpl.kt new file mode 100644 index 000000000000..9515b6fb38b7 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/dsl/impl/CellBuilderBaseImpl.kt @@ -0,0 +1,50 @@ +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.ui.dsl.impl + +import com.intellij.openapi.ui.panel.ComponentPanelBuilder +import com.intellij.openapi.util.NlsContexts +import com.intellij.ui.dsl.CellBuilderBase +import com.intellij.ui.dsl.SpacingConfiguration +import com.intellij.ui.dsl.gridLayout.HorizontalAlign +import com.intellij.ui.dsl.gridLayout.VerticalAlign +import org.jetbrains.annotations.ApiStatus +import javax.swing.JComponent + +@ApiStatus.Experimental +internal open class CellBuilderBaseImpl>(private val dialogPanelConfig: DialogPanelConfig) : CellBuilderBase { + + override var horizontalAlign = HorizontalAlign.LEFT + protected set + + override var verticalAlign = VerticalAlign.CENTER + protected set + + override var rightGap = 0 + protected set + + override var comment: JComponent? = null + protected set + + override fun alignHorizontal(horizontalAlign: HorizontalAlign): CellBuilderBase { + this.horizontalAlign = horizontalAlign + return this + } + + override fun alignVertical(verticalAlign: VerticalAlign): CellBuilderBase { + this.verticalAlign = verticalAlign + return this + } + + override fun comment(@NlsContexts.DetailedDescription comment: String, maxLineLength: Int): CellBuilderBase { + this.comment = ComponentPanelBuilder.createCommentComponent(comment, true, maxLineLength, true) + return this + } + + /** + * Separates next cell in current row with [SpacingConfiguration.horizontalUnrelatedGap]. Should not be used for last cell in a row + */ + override fun rightUnrelatedGap(): CellBuilderBase { + rightGap = dialogPanelConfig.spacing.horizontalUnrelatedGap + return this + } +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/ui/dsl/impl/CellBuilderImpl.kt b/platform/platform-impl/src/com/intellij/ui/dsl/impl/CellBuilderImpl.kt new file mode 100644 index 000000000000..c39d957dc0ee --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/dsl/impl/CellBuilderImpl.kt @@ -0,0 +1,81 @@ +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.ui.dsl.impl + +import com.intellij.openapi.util.NlsContexts +import com.intellij.ui.dsl.CellBuilder +import com.intellij.ui.dsl.gridLayout.HorizontalAlign +import com.intellij.ui.dsl.gridLayout.VerticalAlign +import com.intellij.ui.layout.* +import com.intellij.util.SmartList +import org.jetbrains.annotations.ApiStatus +import javax.swing.JComponent + +@ApiStatus.Experimental +internal class CellBuilderImpl( + private val dialogPanelConfig: DialogPanelConfig, + val component: T, + val viewComponent: JComponent = component) : CellBuilderBaseImpl>(dialogPanelConfig), CellBuilder { + + private var applyIfEnabled = false + + override fun alignHorizontal(horizontalAlign: HorizontalAlign): CellBuilder { + super.alignHorizontal(horizontalAlign) + return this + } + + override fun alignVertical(verticalAlign: VerticalAlign): CellBuilder { + super.alignVertical(verticalAlign) + return this + } + + override fun comment(@NlsContexts.DetailedDescription comment: String, maxLineLength: Int): CellBuilder { + super.comment(comment, maxLineLength) + return this + } + + override fun rightUnrelatedGap(): CellBuilder { + super.rightUnrelatedGap() + return this + } + + override fun applyToComponent(task: T.() -> Unit): CellBuilder { + component.task() + return this + } + + override fun enabled(isEnabled: Boolean): CellBuilder { + viewComponent.isEnabled = isEnabled + return this + } + + override fun applyIfEnabled(): CellBuilder { + applyIfEnabled = true + return this + } + + override fun bind(componentGet: (T) -> V, componentSet: (T, V) -> Unit, modelBinding: PropertyBinding): CellBuilder { + onApply { if (shouldSaveOnApply()) modelBinding.set(componentGet(component)) } + onReset { componentSet(component, modelBinding.get()) } + onIsModified { shouldSaveOnApply() && componentGet(component) != modelBinding.get() } + return this + } + + private fun shouldSaveOnApply(): Boolean { + return !(applyIfEnabled && !viewComponent.isEnabled) + } + + private fun onApply(callback: () -> Unit): CellBuilder { + dialogPanelConfig.applyCallbacks.getOrPut(component) { SmartList() }.add(callback) + return this + } + + private fun onReset(callback: () -> Unit): CellBuilder { + dialogPanelConfig.resetCallbacks.getOrPut(component) { SmartList() }.add(callback) + return this + } + + private fun onIsModified(callback: () -> Boolean): CellBuilder { + dialogPanelConfig.isModifiedCallbacks.getOrPut(component) { SmartList() }.add(callback) + return this + } +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/ui/dsl/impl/DialogPanelConfig.kt b/platform/platform-impl/src/com/intellij/ui/dsl/impl/DialogPanelConfig.kt new file mode 100644 index 000000000000..e7452f783710 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/dsl/impl/DialogPanelConfig.kt @@ -0,0 +1,28 @@ +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.ui.dsl.impl + +import com.intellij.ui.dsl.SpacingConfiguration +import com.intellij.util.ui.JBUI +import org.jetbrains.annotations.ApiStatus +import javax.swing.JComponent + +@ApiStatus.Experimental +internal class DialogPanelConfig { + + val spacing = createIntelliJSpacingConfiguration() + + val applyCallbacks: MutableMap Unit>> = linkedMapOf() + val resetCallbacks: MutableMap Unit>> = linkedMapOf() + val isModifiedCallbacks: MutableMap Boolean>> = linkedMapOf() +} + +// https://jetbrains.github.io/ui/controls/input_field/#spacing +private fun createIntelliJSpacingConfiguration(): SpacingConfiguration { + return object : SpacingConfiguration { + + override val horizontalUnrelatedGap = JBUI.scale(16) + override val horizontalIndent = JBUI.scale(20) + override val verticalComponentGap = JBUI.scale(6) + override val verticalCommentBottomGap = JBUI.scale(6) + } +} diff --git a/platform/platform-impl/src/com/intellij/ui/dsl/impl/PanelBuilderImpl.kt b/platform/platform-impl/src/com/intellij/ui/dsl/impl/PanelBuilderImpl.kt new file mode 100644 index 000000000000..8fc499e6ec05 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/dsl/impl/PanelBuilderImpl.kt @@ -0,0 +1,141 @@ +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.ui.dsl.impl + +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.ui.DialogPanel +import com.intellij.openapi.util.NlsContexts +import com.intellij.ui.dsl.CellBuilderBase +import com.intellij.ui.dsl.PanelBuilder +import com.intellij.ui.dsl.RowBuilder +import com.intellij.ui.dsl.gridLayout.Gaps +import com.intellij.ui.dsl.gridLayout.HorizontalAlign +import com.intellij.ui.dsl.gridLayout.JBGrid +import com.intellij.ui.dsl.gridLayout.VerticalAlign +import com.intellij.ui.dsl.gridLayout.builders.RowsGridBuilder +import org.jetbrains.annotations.ApiStatus +import org.jetbrains.annotations.Nls +import javax.swing.* +import javax.swing.text.JTextComponent + +@ApiStatus.Experimental +internal class PanelBuilderImpl(private val dialogPanelConfig: DialogPanelConfig) : CellBuilderBaseImpl( + dialogPanelConfig), PanelBuilder { + + companion object { + private val LOG = Logger.getInstance(PanelBuilderImpl::class.java) + + private val DEFAULT_VERTICAL_GAP_COMPONENTS = setOf(JTextComponent::class, AbstractButton::class, JSpinner::class, JLabel::class) + } + + private val rows = mutableListOf() + + override fun row(@Nls label: String, init: RowBuilder.() -> Unit): RowBuilder { + return row(JLabel(label), init) + } + + override fun row(label: JLabel?, init: RowBuilder.() -> Unit): RowBuilder { + val result = RowBuilderImpl(dialogPanelConfig, label) + result.init() + rows.add(result) + return result + } + + override fun alignHorizontal(horizontalAlign: HorizontalAlign): CellBuilderBase { + super.alignHorizontal(horizontalAlign) + return this + } + + override fun alignVertical(verticalAlign: VerticalAlign): CellBuilderBase { + super.alignVertical(verticalAlign) + return this + } + + override fun comment(@NlsContexts.DetailedDescription comment: String, maxLineLength: Int): CellBuilderBase { + super.comment(comment, maxLineLength) + return this + } + + override fun rightUnrelatedGap(): CellBuilderBase { + super.rightUnrelatedGap() + return this + } + + fun build(panel: DialogPanel, grid: JBGrid) { + val columnsCount = rows.maxOf { it.cells.size } + val rowsGridBuilder = RowsGridBuilder(panel, grid = grid) + + for ((rowIndex, row) in rows.withIndex()) { + for ((cellIndex, cell) in row.cells.withIndex()) { + val width: Int + var rightGap = cell.rightGap + if (cellIndex == row.cells.size - 1) { + width = columnsCount - cellIndex + if (cell.rightGap > 0) { + LOG.warn("Right gap is set for last cell and will be ignored: rightGap = $rightGap") + rightGap = 0 + } + } + else { + width = 1 + } + + // todo + if (cell is CellBuilderImpl<*>) { + val insets = cell.component.insets + val verticalGap = getDefaultVerticalGap(cell.component) + val gaps = Gaps(top = verticalGap, bottom = verticalGap, right = rightGap) + val visualPaddings = Gaps(top = insets.top, left = insets.left, bottom = insets.bottom, right = insets.right) + rowsGridBuilder.cell(cell.component, width = width, horizontalAlign = cell.horizontalAlign, verticalAlign = cell.verticalAlign, + gaps = gaps, + visualPaddings = visualPaddings) + } + } + + rowsGridBuilder.row() + createCommentRow(rowsGridBuilder, row, columnsCount) + } + } + + /** + * Returns default top and bottom gap for [component] + */ + private fun getDefaultVerticalGap(component: JComponent): Int { + return if (DEFAULT_VERTICAL_GAP_COMPONENTS.any { clazz -> + clazz.isInstance(component) + }) dialogPanelConfig.spacing.verticalComponentGap + else 0 + } + + /** + * Appends comment (currently one comment for a row is supported, can be fixed later) + */ + private fun createCommentRow(builder: RowsGridBuilder, row: RowBuilderImpl, columnsCount: Int) { + for ((column, cell) in row.cells.withIndex()) { + cell.comment?.let { + val additionalHorizontalIndent = if (cell is CellBuilderImpl<*> && cell.viewComponent is JToggleButton) + dialogPanelConfig.spacing.horizontalIndent + else + 0 + createComment(builder, column, columnsCount, row.label != null, it, additionalHorizontalIndent) + return + } + } + } + + private fun createComment(builder: RowsGridBuilder, + column: Int, + columnsCount: Int, + labeledRow: Boolean, + comment: JComponent, + additionalHorizontalIndent: Int) { + if (labeledRow && column == 1) { + builder.cell(comment, width = columnsCount) + } + else { + val gaps = Gaps(left = additionalHorizontalIndent, bottom = dialogPanelConfig.spacing.verticalCommentBottomGap) + builder.skip(column) + builder.cell(comment, columnsCount - column, gaps = gaps) + } + builder.row() + } +} diff --git a/platform/platform-impl/src/com/intellij/ui/dsl/impl/RowBuilderImpl.kt b/platform/platform-impl/src/com/intellij/ui/dsl/impl/RowBuilderImpl.kt new file mode 100644 index 000000000000..e2576460e585 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/dsl/impl/RowBuilderImpl.kt @@ -0,0 +1,51 @@ +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.ui.dsl.impl + +import com.intellij.BundleBase +import com.intellij.openapi.util.NlsContexts +import com.intellij.ui.components.JBCheckBox +import com.intellij.ui.dsl.CellBuilder +import com.intellij.ui.dsl.CellBuilderBase +import com.intellij.ui.dsl.PanelBuilder +import com.intellij.ui.dsl.RowBuilder +import org.jetbrains.annotations.ApiStatus +import java.awt.event.ActionEvent +import javax.swing.JButton +import javax.swing.JComponent +import javax.swing.JLabel + +@ApiStatus.Experimental +internal class RowBuilderImpl(private val dialogPanelConfig: DialogPanelConfig, val label: JLabel?) : RowBuilder { + + val cells: List> + get() = _cells + + private val _cells = mutableListOf>() + + init { + label?.let { cell(it) } + } + + override fun cell(component: T): CellBuilder { + val result = CellBuilderImpl(dialogPanelConfig, component) + _cells.add(result) + return result + } + + override fun panel(init: PanelBuilder.() -> Unit): PanelBuilder { + val result = PanelBuilderImpl(dialogPanelConfig) + result.init() + _cells.add(result) + return result + } + + override fun checkBox(@NlsContexts.Checkbox text: String): CellBuilder { + return cell(JBCheckBox(text)) + } + + override fun button(@NlsContexts.Button text: String, actionListener: (event: ActionEvent) -> Unit): CellBuilder { + val button = JButton(BundleBase.replaceMnemonicAmpersand(text)) + button.addActionListener(actionListener) + return cell(button) + } +} diff --git a/platform/platform-impl/src/com/intellij/ui/dsl/panel.kt b/platform/platform-impl/src/com/intellij/ui/dsl/panel.kt index 712cff18b9ad..8b7d279b6d3b 100644 --- a/platform/platform-impl/src/com/intellij/ui/dsl/panel.kt +++ b/platform/platform-impl/src/com/intellij/ui/dsl/panel.kt @@ -2,26 +2,38 @@ package com.intellij.ui.dsl import com.intellij.openapi.ui.DialogPanel +import com.intellij.ui.dsl.gridLayout.JBGridLayout +import com.intellij.ui.dsl.impl.DialogPanelConfig +import com.intellij.ui.dsl.impl.PanelBuilderImpl import org.jetbrains.annotations.ApiStatus +/* +todo +remove first/last gaps +panel in ShelfProjectConfigurable +*/ + @ApiStatus.Experimental -fun panel(init: PanelBuilder.() -> Unit): DialogPanel { - val builder = PanelBuilder() +fun panel(init: PanelBuilderBase.() -> Unit): DialogPanel { // Dialog panel content supports only PanelBuilderBase, no CellBuilderBase + val dialogPanelConfig = DialogPanelConfig() + val builder = PanelBuilderImpl(dialogPanelConfig) builder.init() - val panel = builder.build() - initPanel(builder, panel) - return panel + val layout = JBGridLayout() + val result = DialogPanel(layout = layout) + builder.build(result, layout.rootGrid) + initPanel(dialogPanelConfig, result) + return result } -private fun initPanel(builder: PanelBuilder, panel: DialogPanel) { +private fun initPanel(dialogPanelConfig: DialogPanelConfig, panel: DialogPanel) { /* todo panel.preferredFocusedComponent = builder.builder.preferredFocusedComponent panel.validateCallbacks = builder.builder.validateCallbacks panel.componentValidateCallbacks = builder.builder.componentValidateCallbacks panel.customValidationRequestors = builder.builder.customValidationRequestors */ - panel.applyCallbacks = builder.applyCallbacks - panel.resetCallbacks = builder.resetCallbacks - panel.isModifiedCallbacks = builder.isModifiedCallbacks + panel.applyCallbacks = dialogPanelConfig.applyCallbacks + panel.resetCallbacks = dialogPanelConfig.resetCallbacks + panel.isModifiedCallbacks = dialogPanelConfig.isModifiedCallbacks }