mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
UI DSL — to not require use radio buttons directly (part 1)
there are some issues regarding enabled state if some nested component has own enabled state (so, should stay disabled if containing row is enabled)
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
<root>
|
||||
<item name='javax.swing.ButtonGroup javax.swing.ButtonModel getSelection()'>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
</item>
|
||||
<item name='javax.swing.JComponent void paint(java.awt.Graphics) 0'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
|
||||
@@ -22,12 +22,10 @@ import com.intellij.openapi.ui.MessageDialogBuilder
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.ui.CollectionComboBoxModel
|
||||
import com.intellij.ui.components.RadioButton
|
||||
import com.intellij.ui.layout.*
|
||||
import com.intellij.util.io.exists
|
||||
import com.intellij.util.io.isDirectory
|
||||
import com.intellij.util.text.nullize
|
||||
import gnu.trove.THashMap
|
||||
import java.io.File
|
||||
import java.nio.file.Paths
|
||||
import javax.swing.JPanel
|
||||
@@ -41,16 +39,10 @@ internal class PasswordSafeConfigurable(private val settings: PasswordSafeSettin
|
||||
}
|
||||
|
||||
internal class PasswordSafeConfigurableUi : ConfigurableUi<PasswordSafeSettings> {
|
||||
private val inKeychain = RadioButton("In native Keychain")
|
||||
|
||||
private val inKeePass = RadioButton("In KeePass")
|
||||
private var keePassDbFile: TextFieldWithBrowseButton? = null
|
||||
|
||||
private var isUsePgp = BooleanPropertyWithComboBoxUiManager(CollectionComboBoxModel<PgpKey>())
|
||||
|
||||
private val rememberPasswordsUntilClosing = RadioButton("Do not save, forget passwords after restart")
|
||||
|
||||
private val modeToRow = THashMap<ProviderType, Row>()
|
||||
private val providerTypeModel = ChoicePropertyUiManager(ProviderType.KEYCHAIN)
|
||||
|
||||
private val pgp by lazy { Pgp() }
|
||||
|
||||
@@ -59,16 +51,10 @@ internal class PasswordSafeConfigurableUi : ConfigurableUi<PasswordSafeSettings>
|
||||
private val secureRandom = lazy { createSecureRandom() }
|
||||
|
||||
override fun reset(settings: PasswordSafeSettings) {
|
||||
when (settings.providerType) {
|
||||
ProviderType.MEMORY_ONLY -> rememberPasswordsUntilClosing.isSelected = true
|
||||
ProviderType.KEYCHAIN -> inKeychain.isSelected = true
|
||||
ProviderType.KEEPASS -> inKeePass.isSelected = true
|
||||
else -> throw IllegalStateException("Unknown provider type: ${settings.providerType}")
|
||||
}
|
||||
providerTypeModel.selected = settings.providerType
|
||||
|
||||
@Suppress("IfThenToElvis")
|
||||
keePassDbFile?.text = settings.keepassDb ?: getDefaultKeePassDbFile().toString()
|
||||
updateEnabledState()
|
||||
|
||||
val secretKeys = pgp.listKeys()
|
||||
isUsePgp.listModel.replaceAll(secretKeys)
|
||||
@@ -76,7 +62,7 @@ internal class PasswordSafeConfigurableUi : ConfigurableUi<PasswordSafeSettings>
|
||||
val currentKeyId = settings.state.pgpKeyId
|
||||
isUsePgp.selected = (if (currentKeyId == null) null else secretKeys.firstOrNull { it.keyId == currentKeyId }) ?: secretKeys.firstOrNull()
|
||||
isUsePgp.value = !secretKeys.isEmpty() && currentKeyId != null
|
||||
isUsePgp.isEnabled = !secretKeys.isEmpty()
|
||||
isUsePgp.isEnabled = providerTypeModel.selected == ProviderType.KEEPASS && !secretKeys.isEmpty()
|
||||
}
|
||||
|
||||
override fun isModified(settings: PasswordSafeSettings): Boolean {
|
||||
@@ -190,23 +176,19 @@ internal class PasswordSafeConfigurableUi : ConfigurableUi<PasswordSafeSettings>
|
||||
|
||||
private fun getNewDbFileAsString() = keePassDbFile!!.text.trim().nullize()
|
||||
|
||||
private fun updateEnabledState() {
|
||||
modeToRow[ProviderType.KEEPASS]?.subRowsEnabled = getNewProviderType() == ProviderType.KEEPASS
|
||||
}
|
||||
|
||||
override fun getComponent(): JPanel {
|
||||
return panel {
|
||||
row { label("Save passwords:") }
|
||||
|
||||
buttonGroup({ updateEnabledState() }) {
|
||||
buttonGroup(providerTypeModel) {
|
||||
if (SystemInfo.isLinux || isMacOsCredentialStoreSupported) {
|
||||
row {
|
||||
inKeychain()
|
||||
radioButton("In native Keychain", ProviderType.KEYCHAIN)
|
||||
}
|
||||
}
|
||||
|
||||
modeToRow[ProviderType.KEEPASS] = row {
|
||||
inKeePass()
|
||||
row {
|
||||
radioButton("In KeePass", ProviderType.KEEPASS)
|
||||
row("Database:") {
|
||||
val fileChooserDescriptor = FileChooserDescriptorFactory.createSingleLocalFileDescriptor().withFileFilter {
|
||||
it.isDirectory || it.name.endsWith(".kdbx")
|
||||
@@ -237,7 +219,7 @@ internal class PasswordSafeConfigurableUi : ConfigurableUi<PasswordSafeSettings>
|
||||
}
|
||||
}
|
||||
row {
|
||||
rememberPasswordsUntilClosing()
|
||||
radioButton("Do not save, forget passwords after restart", ProviderType.MEMORY_ONLY)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -257,13 +239,7 @@ internal class PasswordSafeConfigurableUi : ConfigurableUi<PasswordSafeSettings>
|
||||
|
||||
private fun getNewPgpKey() = isUsePgp.selected
|
||||
|
||||
private fun getNewProviderType(): ProviderType {
|
||||
return when {
|
||||
rememberPasswordsUntilClosing.isSelected -> ProviderType.MEMORY_ONLY
|
||||
inKeePass.isSelected -> ProviderType.KEEPASS
|
||||
else -> ProviderType.KEYCHAIN
|
||||
}
|
||||
}
|
||||
private fun getNewProviderType() = providerTypeModel.selected
|
||||
|
||||
private inner class ClearKeePassDatabaseAction : DumbAwareAction("Clear") {
|
||||
override fun actionPerformed(event: AnActionEvent) {
|
||||
|
||||
@@ -38,7 +38,10 @@ open class BooleanPropertyUiManager {
|
||||
updateDependentComponentState()
|
||||
})
|
||||
|
||||
updateCheckBoxEnabledState()
|
||||
// do not modify initial enabled state of component - maybe disabled as part of inactive choice group
|
||||
if (!isEnabled) {
|
||||
updateCheckBoxEnabledState()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateDependentComponentState() {
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. 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.layout
|
||||
|
||||
import java.awt.event.ActionListener
|
||||
import javax.swing.ButtonModel
|
||||
import javax.swing.JRadioButton
|
||||
|
||||
class ChoicePropertyUiManager<T : Any>(defaultChoice: T) {
|
||||
private val components = ArrayList<ChoiceInfo<T>>()
|
||||
|
||||
private var currentSelection: ChoiceInfo<T>? = null
|
||||
|
||||
private var _selected: T = defaultChoice
|
||||
var selected: T
|
||||
get() = _selected
|
||||
set(value) {
|
||||
updateSelection(components.firstOrNull { it.id == value })
|
||||
}
|
||||
|
||||
private fun updateSelection(newSelection: ChoiceInfo<T>?) {
|
||||
currentSelection?.select(false)
|
||||
|
||||
currentSelection = newSelection ?: return
|
||||
newSelection.select(true)
|
||||
}
|
||||
|
||||
internal fun addRadioButton(component: JRadioButton, id: T, row: Row) {
|
||||
val isSelected = id == _selected
|
||||
component.isSelected = isSelected
|
||||
val info = ChoiceInfo(id, component.model, row)
|
||||
components.add(info)
|
||||
if (isSelected) {
|
||||
currentSelection = info
|
||||
}
|
||||
else {
|
||||
row.subRowsEnabled = false
|
||||
}
|
||||
|
||||
component.addActionListener(ActionListener {
|
||||
if (component.isSelected) {
|
||||
updateSelection(info)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private data class ChoiceInfo<T>(val id: T, val model: ButtonModel, val row: Row) {
|
||||
fun select(value: Boolean) {
|
||||
model.isSelected = value
|
||||
row.subRowsEnabled = value
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,8 @@ import java.awt.event.ActionListener
|
||||
import javax.swing.ButtonGroup
|
||||
import javax.swing.JLabel
|
||||
|
||||
class LayoutBuilder @PublishedApi internal constructor(@PublishedApi internal val builder: LayoutBuilderImpl, val buttonGroup: ButtonGroup? = null) {
|
||||
inline fun row(label: String, init: Row.() -> Unit): Row = row(label = Label(label), init = init)
|
||||
open class LayoutBuilder @PublishedApi internal constructor(@PublishedApi internal val builder: LayoutBuilderImpl, val buttonGroup: ButtonGroup? = null) {
|
||||
inline fun row(label: String, init: Row.() -> Unit) = row(label = Label(label), init = init)
|
||||
|
||||
inline fun row(label: JLabel? = null, separated: Boolean = false, init: Row.() -> Unit): Row {
|
||||
val row = builder.newRow(label, buttonGroup, separated)
|
||||
@@ -54,6 +54,10 @@ class LayoutBuilder @PublishedApi internal constructor(@PublishedApi internal va
|
||||
return group
|
||||
}
|
||||
|
||||
inline fun <T : Any> buttonGroup(propertyManager: ChoicePropertyUiManager<T>, init: LayoutBuilderWithButtonGroup<T>.() -> Unit) {
|
||||
LayoutBuilderWithButtonGroup(builder, propertyManager).init()
|
||||
}
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@PublishedApi
|
||||
@Deprecated("", replaceWith = ReplaceWith("builder"), level = DeprecationLevel.ERROR)
|
||||
@@ -61,6 +65,9 @@ class LayoutBuilder @PublishedApi internal constructor(@PublishedApi internal va
|
||||
get() = builder
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
class LayoutBuilderWithButtonGroup<T : Any> @PublishedApi internal constructor(builder: LayoutBuilderImpl, internal val propertyManager: ChoicePropertyUiManager<T>) : LayoutBuilder(builder)
|
||||
|
||||
fun FileChooserDescriptor.chooseFile(event: AnActionEvent, fileChosen: (chosenFile: VirtualFile) -> Unit) {
|
||||
FileChooser.chooseFile(this, event.getData(PlatformDataKeys.PROJECT), event.getData(PlatformDataKeys.CONTEXT_COMPONENT), null, fileChosen)
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
package com.intellij.ui.layout
|
||||
|
||||
import com.intellij.ui.components.Label
|
||||
import com.intellij.ui.components.RadioButton
|
||||
import com.intellij.util.ui.UIUtil.ComponentStyle
|
||||
import com.intellij.util.ui.UIUtil.FontColor
|
||||
import javax.swing.JComponent
|
||||
@@ -23,6 +24,12 @@ abstract class Row : Cell() {
|
||||
label(gapLeft = gapLeft)
|
||||
}
|
||||
|
||||
fun <T : Any> LayoutBuilderWithButtonGroup<T>.radioButton(text: String, id: T) {
|
||||
val component = RadioButton(text)
|
||||
propertyManager.addRadioButton(component, id, this@Row)
|
||||
component()
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the right alignment for the component if the cell is larger than the component plus its gaps.
|
||||
*/
|
||||
|
||||
@@ -70,7 +70,7 @@ internal class MigLayoutRow(private val parent: MigLayoutRow?,
|
||||
private val spacing: SpacingConfiguration
|
||||
get() = builder.spacing
|
||||
|
||||
override var enabled: Boolean = true
|
||||
override var enabled = true
|
||||
set(value) {
|
||||
if (field == value) {
|
||||
return
|
||||
@@ -82,7 +82,7 @@ internal class MigLayoutRow(private val parent: MigLayoutRow?,
|
||||
}
|
||||
}
|
||||
|
||||
override var visible: Boolean = true
|
||||
override var visible = true
|
||||
set(value) {
|
||||
if (field == value) {
|
||||
return
|
||||
@@ -94,7 +94,7 @@ internal class MigLayoutRow(private val parent: MigLayoutRow?,
|
||||
}
|
||||
}
|
||||
|
||||
override var subRowsEnabled: Boolean = true
|
||||
override var subRowsEnabled = true
|
||||
set(value) {
|
||||
if (field == value) {
|
||||
return
|
||||
@@ -104,7 +104,7 @@ internal class MigLayoutRow(private val parent: MigLayoutRow?,
|
||||
subRows?.forEach { it.enabled = value }
|
||||
}
|
||||
|
||||
override var subRowsVisible: Boolean = true
|
||||
override var subRowsVisible = true
|
||||
set(value) {
|
||||
if (field == value) {
|
||||
return
|
||||
@@ -132,10 +132,14 @@ internal class MigLayoutRow(private val parent: MigLayoutRow?,
|
||||
if (isSeparated) {
|
||||
val separatorRow = MigLayoutRow(this, componentConstraints, builder, indent = indent, noGrid = true)
|
||||
configureSeparatorRow(separatorRow, title)
|
||||
separatorRow.enabled = subRowsEnabled
|
||||
separatorRow.visible = subRowsVisible
|
||||
row.getOrCreateSubRowsList().add(separatorRow)
|
||||
}
|
||||
|
||||
subRows.add(row)
|
||||
row.enabled = subRowsEnabled
|
||||
row.visible = subRowsVisible
|
||||
|
||||
if (label != null) {
|
||||
row.addComponent(label)
|
||||
@@ -195,6 +199,13 @@ internal class MigLayoutRow(private val parent: MigLayoutRow?,
|
||||
internal fun addComponent(component: JComponent, cc: Lazy<CC> = lazy { CC() }, gapLeft: Int = 0, growPolicy: GrowPolicy? = null, comment: String? = null) {
|
||||
components.add(component)
|
||||
|
||||
if (!visible) {
|
||||
component.isVisible = false
|
||||
}
|
||||
if (!enabled) {
|
||||
component.isEnabled = false
|
||||
}
|
||||
|
||||
if (!shareCellWithPreviousComponentIfNeed(component, cc)) {
|
||||
// increase column index if cell mode not enabled or it is a first component of cell
|
||||
if (componentIndexWhenCellModeWasEnabled == -1 || componentIndexWhenCellModeWasEnabled == (components.size - 1)) {
|
||||
|
||||
Reference in New Issue
Block a user