diff --git a/lib/annotations/jdk/javax/swing/annotations.xml b/lib/annotations/jdk/javax/swing/annotations.xml index 46d4390ef7f5..1db41f6fe8d9 100644 --- a/lib/annotations/jdk/javax/swing/annotations.xml +++ b/lib/annotations/jdk/javax/swing/annotations.xml @@ -1,4 +1,7 @@ + + + diff --git a/platform/credential-store/src/PasswordSafeConfigurable.kt b/platform/credential-store/src/PasswordSafeConfigurable.kt index c167e3486c57..451e0e61c646 100644 --- a/platform/credential-store/src/PasswordSafeConfigurable.kt +++ b/platform/credential-store/src/PasswordSafeConfigurable.kt @@ -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 { - private val inKeychain = RadioButton("In native Keychain") - - private val inKeePass = RadioButton("In KeePass") private var keePassDbFile: TextFieldWithBrowseButton? = null private var isUsePgp = BooleanPropertyWithComboBoxUiManager(CollectionComboBoxModel()) - - private val rememberPasswordsUntilClosing = RadioButton("Do not save, forget passwords after restart") - - private val modeToRow = THashMap() + private val providerTypeModel = ChoicePropertyUiManager(ProviderType.KEYCHAIN) private val pgp by lazy { Pgp() } @@ -59,16 +51,10 @@ internal class PasswordSafeConfigurableUi : ConfigurableUi 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 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 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 } } row { - rememberPasswordsUntilClosing() + radioButton("Do not save, forget passwords after restart", ProviderType.MEMORY_ONLY) } } } @@ -257,13 +239,7 @@ internal class PasswordSafeConfigurableUi : ConfigurableUi 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) { diff --git a/platform/platform-impl/src/com/intellij/ui/layout/BooleanPropertyUiManager.kt b/platform/platform-impl/src/com/intellij/ui/layout/BooleanPropertyUiManager.kt index ca2580a14350..a08349bd1d8c 100644 --- a/platform/platform-impl/src/com/intellij/ui/layout/BooleanPropertyUiManager.kt +++ b/platform/platform-impl/src/com/intellij/ui/layout/BooleanPropertyUiManager.kt @@ -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() { diff --git a/platform/platform-impl/src/com/intellij/ui/layout/ChoicePropertyUiManager.kt b/platform/platform-impl/src/com/intellij/ui/layout/ChoicePropertyUiManager.kt new file mode 100644 index 000000000000..bb477bd51da0 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/layout/ChoicePropertyUiManager.kt @@ -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(defaultChoice: T) { + private val components = ArrayList>() + + private var currentSelection: ChoiceInfo? = null + + private var _selected: T = defaultChoice + var selected: T + get() = _selected + set(value) { + updateSelection(components.firstOrNull { it.id == value }) + } + + private fun updateSelection(newSelection: ChoiceInfo?) { + 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(val id: T, val model: ButtonModel, val row: Row) { + fun select(value: Boolean) { + model.isSelected = value + row.subRowsEnabled = value + } +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/ui/layout/LayoutBuilder.kt b/platform/platform-impl/src/com/intellij/ui/layout/LayoutBuilder.kt index 70cc83a40533..80e5a9feb3dc 100644 --- a/platform/platform-impl/src/com/intellij/ui/layout/LayoutBuilder.kt +++ b/platform/platform-impl/src/com/intellij/ui/layout/LayoutBuilder.kt @@ -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 buttonGroup(propertyManager: ChoicePropertyUiManager, init: LayoutBuilderWithButtonGroup.() -> 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 @PublishedApi internal constructor(builder: LayoutBuilderImpl, internal val propertyManager: ChoicePropertyUiManager) : 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) } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/ui/layout/Row.kt b/platform/platform-impl/src/com/intellij/ui/layout/Row.kt index 5e4570120b8d..6bc3944d9c94 100644 --- a/platform/platform-impl/src/com/intellij/ui/layout/Row.kt +++ b/platform/platform-impl/src/com/intellij/ui/layout/Row.kt @@ -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 LayoutBuilderWithButtonGroup.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. */ diff --git a/platform/platform-impl/src/com/intellij/ui/layout/migLayout/MigLayoutRow.kt b/platform/platform-impl/src/com/intellij/ui/layout/migLayout/MigLayoutRow.kt index 31641a42a652..d3015a96cc45 100644 --- a/platform/platform-impl/src/com/intellij/ui/layout/migLayout/MigLayoutRow.kt +++ b/platform/platform-impl/src/com/intellij/ui/layout/migLayout/MigLayoutRow.kt @@ -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 = 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)) {