IJP-1115 Kotlin UI DSL improvements / new Layout Manager

GitOrigin-RevId: ed5d7274e1a8d23ca2ed17e670d80fe457f20ee4
This commit is contained in:
Pavel Porvatov
2021-08-06 09:32:16 +00:00
committed by intellij-monorepo-bot
parent 89821376ad
commit 4d201ad256
12 changed files with 433 additions and 236 deletions
@@ -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<out T : JComponent>(
private val builder: PanelBuilder,
val component: T,
val viewComponent: JComponent = component) {
interface CellBuilder<T : JComponent> : CellBuilderBase<CellBuilder<T>> {
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<T>
fun applyToComponent(task: T.() -> Unit): CellBuilder<T> {
component.task()
return this
}
fun enabled(isEnabled: Boolean): CellBuilder<T> {
viewComponent.isEnabled = isEnabled
return this
}
fun enabled(isEnabled: Boolean): CellBuilder<T>
/**
* 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<T> {
applyIfEnabled = true
return this
}
fun applyIfEnabled(): CellBuilder<T>
fun alignHorizontal(horizontalAlign: HorizontalAlign): CellBuilder<T> {
this.horizontalAlign = horizontalAlign
return this
}
fun alignVertical(verticalAlign: VerticalAlign): CellBuilder<T> {
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<T> {
rightGap = builder.spacing.horizontalUnrelatedGap
return this
}
fun comment(@NlsContexts.DetailedDescription comment: String,
maxLineLength: Int = ComponentPanelBuilder.MAX_COMMENT_WIDTH): CellBuilder<T> {
this.comment = ComponentPanelBuilder.createCommentComponent(comment, true, maxLineLength, true)
return this
}
private fun shouldSaveOnApply(): Boolean {
return !(applyIfEnabled && !viewComponent.isEnabled)
}
private fun onApply(callback: () -> Unit): CellBuilder<T> {
builder.applyCallbacks.getOrPut(component) { SmartList() }.add(callback)
return this
}
private fun onReset(callback: () -> Unit): CellBuilder<T> {
builder.resetCallbacks.getOrPut(component) { SmartList() }.add(callback)
return this
}
private fun onIsModified(callback: () -> Boolean): CellBuilder<T> {
builder.isModifiedCallbacks.getOrPut(component) { SmartList() }.add(callback)
return this
}
fun <V> bind(
componentGet: (T) -> V,
componentSet: (T, V) -> Unit,
modelBinding: PropertyBinding<V>
): CellBuilder<T> {
onApply { if (shouldSaveOnApply()) modelBinding.set(componentGet(component)) }
onReset { componentSet(component, modelBinding.get()) }
onIsModified { shouldSaveOnApply() && componentGet(component) != modelBinding.get() }
return this
}
fun <V> bind(componentGet: (T) -> V, componentSet: (T, V) -> Unit, modelBinding: PropertyBinding<V>): CellBuilder<T>
}
fun <T : AbstractButton> CellBuilder<T>.bind(modelBinding: PropertyBinding<Boolean>): CellBuilder<T> {
@@ -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<T : CellBuilderBase<T>> {
val horizontalAlign: HorizontalAlign
val verticalAlign: VerticalAlign
val rightGap: Int
val comment: JComponent?
fun alignHorizontal(horizontalAlign: HorizontalAlign): CellBuilderBase<T>
fun alignVertical(verticalAlign: VerticalAlign): CellBuilderBase<T>
fun comment(@NlsContexts.DetailedDescription comment: String,
maxLineLength: Int = ComponentPanelBuilder.MAX_COMMENT_WIDTH): CellBuilderBase<T>
/**
* Separates next cell in current row with [SpacingConfiguration.horizontalUnrelatedGap].
* Should not be used for last cell in a row
*/
fun rightUnrelatedGap(): CellBuilderBase<T>
}
@@ -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<JComponent?, MutableList<() -> Unit>> = linkedMapOf()
val resetCallbacks: MutableMap<JComponent?, MutableList<() -> Unit>> = linkedMapOf()
val isModifiedCallbacks: MutableMap<JComponent?, MutableList<() -> Boolean>> = linkedMapOf()
private val rows = mutableListOf<RowBuilder>()
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<PanelBuilder>
@@ -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
}
@@ -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<CellBuilder<JComponent>>()
fun <T : JComponent> cell(component: T): CellBuilder<T>
init {
label?.let { cell(it) }
}
fun panel(init: PanelBuilder.() -> Unit): PanelBuilder
fun <T : JComponent> cell(component: T): CellBuilder<T> {
val result = CellBuilder(builder, component)
cells.add(result)
return result
}
fun checkBox(@NlsContexts.Checkbox text: String): CellBuilder<JBCheckBox>
fun checkBox(@NlsContexts.Checkbox text: String): CellBuilder<JBCheckBox> {
return cell(JBCheckBox(text))
}
fun button(@NlsContexts.Button text: String, actionListener: (event: ActionEvent) -> Unit): CellBuilder<JButton>
fun button(@NlsContexts.Button text: String, actionListener: (event: ActionEvent) -> Unit): CellBuilder<JButton> {
val button = JButton(BundleBase.replaceMnemonicAmpersand(text))
button.addActionListener(actionListener)
return cell(button)
}
}
@@ -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)
}
}
@@ -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<T : CellBuilderBase<T>>(private val dialogPanelConfig: DialogPanelConfig) : CellBuilderBase<T> {
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<T> {
this.horizontalAlign = horizontalAlign
return this
}
override fun alignVertical(verticalAlign: VerticalAlign): CellBuilderBase<T> {
this.verticalAlign = verticalAlign
return this
}
override fun comment(@NlsContexts.DetailedDescription comment: String, maxLineLength: Int): CellBuilderBase<T> {
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<T> {
rightGap = dialogPanelConfig.spacing.horizontalUnrelatedGap
return this
}
}
@@ -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<T : JComponent>(
private val dialogPanelConfig: DialogPanelConfig,
val component: T,
val viewComponent: JComponent = component) : CellBuilderBaseImpl<CellBuilder<T>>(dialogPanelConfig), CellBuilder<T> {
private var applyIfEnabled = false
override fun alignHorizontal(horizontalAlign: HorizontalAlign): CellBuilder<T> {
super.alignHorizontal(horizontalAlign)
return this
}
override fun alignVertical(verticalAlign: VerticalAlign): CellBuilder<T> {
super.alignVertical(verticalAlign)
return this
}
override fun comment(@NlsContexts.DetailedDescription comment: String, maxLineLength: Int): CellBuilder<T> {
super.comment(comment, maxLineLength)
return this
}
override fun rightUnrelatedGap(): CellBuilder<T> {
super.rightUnrelatedGap()
return this
}
override fun applyToComponent(task: T.() -> Unit): CellBuilder<T> {
component.task()
return this
}
override fun enabled(isEnabled: Boolean): CellBuilder<T> {
viewComponent.isEnabled = isEnabled
return this
}
override fun applyIfEnabled(): CellBuilder<T> {
applyIfEnabled = true
return this
}
override fun <V> bind(componentGet: (T) -> V, componentSet: (T, V) -> Unit, modelBinding: PropertyBinding<V>): CellBuilder<T> {
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<T> {
dialogPanelConfig.applyCallbacks.getOrPut(component) { SmartList() }.add(callback)
return this
}
private fun onReset(callback: () -> Unit): CellBuilder<T> {
dialogPanelConfig.resetCallbacks.getOrPut(component) { SmartList() }.add(callback)
return this
}
private fun onIsModified(callback: () -> Boolean): CellBuilder<T> {
dialogPanelConfig.isModifiedCallbacks.getOrPut(component) { SmartList() }.add(callback)
return this
}
}
@@ -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<JComponent?, MutableList<() -> Unit>> = linkedMapOf()
val resetCallbacks: MutableMap<JComponent?, MutableList<() -> Unit>> = linkedMapOf()
val isModifiedCallbacks: MutableMap<JComponent?, MutableList<() -> 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)
}
}
@@ -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<PanelBuilder>(
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<RowBuilderImpl>()
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<PanelBuilder> {
super.alignHorizontal(horizontalAlign)
return this
}
override fun alignVertical(verticalAlign: VerticalAlign): CellBuilderBase<PanelBuilder> {
super.alignVertical(verticalAlign)
return this
}
override fun comment(@NlsContexts.DetailedDescription comment: String, maxLineLength: Int): CellBuilderBase<PanelBuilder> {
super.comment(comment, maxLineLength)
return this
}
override fun rightUnrelatedGap(): CellBuilderBase<PanelBuilder> {
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()
}
}
@@ -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<CellBuilderBase<*>>
get() = _cells
private val _cells = mutableListOf<CellBuilderBase<*>>()
init {
label?.let { cell(it) }
}
override fun <T : JComponent> cell(component: T): CellBuilder<T> {
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<JBCheckBox> {
return cell(JBCheckBox(text))
}
override fun button(@NlsContexts.Button text: String, actionListener: (event: ActionEvent) -> Unit): CellBuilder<JButton> {
val button = JButton(BundleBase.replaceMnemonicAmpersand(text))
button.addActionListener(actionListener)
return cell(button)
}
}
@@ -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
}