mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IJP-1115 Kotlin UI DSL improvements / new Layout Manager
GitOrigin-RevId: ed5d7274e1a8d23ca2ed17e670d80fe457f20ee4
This commit is contained in:
committed by
intellij-monorepo-bot
parent
89821376ad
commit
4d201ad256
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user