RunDashboard: edit types shown in dashboard

- Use one list with 'Add' button instead of two lists
- Make Run Dashboard types list hideable
This commit is contained in:
Konstantin Aleev
2017-09-20 18:10:15 +03:00
parent d260064418
commit fefbf336d6
4 changed files with 214 additions and 169 deletions
@@ -18,7 +18,6 @@ package com.intellij.execution.impl
import com.intellij.execution.*
import com.intellij.execution.configuration.ConfigurationFactoryEx
import com.intellij.execution.configurations.*
import com.intellij.execution.dashboard.RunDashboardManager
import com.intellij.execution.impl.RunConfigurable.Companion.collectNodesRecursively
import com.intellij.execution.impl.RunConfigurableNodeKind.*
import com.intellij.icons.AllIcons
@@ -32,7 +31,6 @@ import com.intellij.openapi.options.*
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.LabeledComponent.create
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.ui.VerticalFlowLayout
import com.intellij.openapi.util.Comparing
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.Pair
@@ -43,13 +41,11 @@ import com.intellij.openapi.wm.IdeFocusManager
import com.intellij.openapi.wm.IdeFocusManager.getGlobalInstance
import com.intellij.ui.*
import com.intellij.ui.RowsDnDSupport.RefinedDropSupport.Position.*
import com.intellij.ui.components.JBLabel
import com.intellij.ui.components.labels.ActionLink
import com.intellij.ui.treeStructure.Tree
import com.intellij.util.ArrayUtilRt
import com.intellij.util.IconUtil
import com.intellij.util.PlatformIcons
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.containers.HashMap
import com.intellij.util.containers.nullize
import com.intellij.util.ui.*
@@ -57,11 +53,13 @@ import com.intellij.util.ui.tree.TreeUtil
import gnu.trove.THashSet
import gnu.trove.TObjectIntHashMap
import net.miginfocom.swing.MigLayout
import java.awt.*
import java.awt.BorderLayout
import java.awt.FlowLayout
import java.awt.GridBagConstraints
import java.awt.GridBagLayout
import java.awt.event.KeyEvent
import java.util.*
import java.util.function.ToIntFunction
import java.util.stream.Collectors
import javax.swing.*
import javax.swing.border.EmptyBorder
import javax.swing.event.DocumentEvent
@@ -99,9 +97,7 @@ open class RunConfigurable @JvmOverloads constructor(private val myProject: Proj
private var toolbarDecorator: ToolbarDecorator? = null
private var isFolderCreating: Boolean = false
private val toolbarAddAction = MyToolbarAddAction()
private val runDashboardTypes = ContainerUtil.newHashSet<String>()
private val hiddenRunDashboardTypesList = RunDashboardTypesList(myProject)
private val shownRunDashboardTypesList = RunDashboardTypesList(myProject)
private val runDashboardTypesPanel = RunDashboardTypesPanel(myProject)
companion object {
fun collectNodesRecursively(parentNode: DefaultMutableTreeNode,
@@ -496,7 +492,8 @@ open class RunConfigurable @JvmOverloads constructor(private val myProject: Proj
if (Registry.`is`("ide.run.dashboard.types.configuration") || ApplicationManager.getApplication().isInternal) {
val wrapper = JPanel(BorderLayout())
wrapper.add(createRunDashboardTypesPanel(), BorderLayout.CENTER)
wrapper.add(runDashboardTypesPanel, BorderLayout.CENTER)
runDashboardTypesPanel.addChangeListener(this::defaultsSettingsChanged)
wrapper.add(settingsWrapper, BorderLayout.SOUTH)
rightPanel.add(wrapper, BorderLayout.SOUTH)
@@ -543,7 +540,7 @@ open class RunConfigurable @JvmOverloads constructor(private val myProject: Proj
private fun defaultsSettingsChanged() {
isModified = !Comparing.equal(recentsLimit.text, recentsLimit.getClientProperty(INITIAL_VALUE_KEY)) ||
!Comparing.equal(confirmation.isSelected, confirmation.getClientProperty(INITIAL_VALUE_KEY)) ||
!Comparing.equal(runDashboardTypes, RunDashboardManager.getInstance(myProject).types)
runDashboardTypesPanel.isModified()
}
private fun createSettingsPanel(): JPanel {
@@ -565,82 +562,6 @@ open class RunConfigurable @JvmOverloads constructor(private val myProject: Proj
return bottomPanel
}
private fun createRunDashboardTypesPanel(): JComponent {
updateRunDashboardTypes()
val buttonsPanel = JPanel(VerticalFlowLayout())
buttonsPanel.add(JButton(ExecutionBundle.message("run.dashboard.configurable.show")).apply {
addActionListener { changeRunDashboardTypes(false, true) }
})
buttonsPanel.add(JButton(ExecutionBundle.message("run.dashboard.configurable.hide")).apply {
addActionListener { changeRunDashboardTypes(false, false) }
})
buttonsPanel.add(JButton(ExecutionBundle.message("run.dashboard.configurable.show.all")).apply {
addActionListener { changeRunDashboardTypes(true, true) }
})
buttonsPanel.add(JButton(ExecutionBundle.message("run.dashboard.configurable.hide.all")).apply {
addActionListener { changeRunDashboardTypes(true, false) }
})
val gridBag = GridBag().setDefaultWeightX(0, 0.5).setDefaultWeightX(1, 0.0).setDefaultWeightX(2, 0.5)
val treesPanel = JPanel(GridBagLayout())
val hiddenLabel = JBLabel(ExecutionBundle.message("run.dashboard.configurable.hidden.label"))
val shownLabel = JBLabel(ExecutionBundle.message("run.dashboard.configurable.shown.label"))
val hiddenSize = hiddenLabel.minimumSize
val shownSize = shownLabel.minimumSize
if (hiddenSize.width < shownSize.width) {
val size = Dimension(shownSize.width, hiddenSize.height)
hiddenLabel.minimumSize = size
hiddenLabel.preferredSize = size
hiddenLabel.maximumSize = size
}
else {
val size = Dimension(hiddenSize.width, shownSize.height)
shownLabel.minimumSize = size
shownLabel.preferredSize = size
shownLabel.maximumSize = size
}
treesPanel.add(hiddenLabel, gridBag.nextLine().next().anchor(GridBagConstraints.WEST))
treesPanel.add(shownLabel, gridBag.next().next().anchor(GridBagConstraints.WEST))
treesPanel.add(hiddenRunDashboardTypesList.component, gridBag.nextLine().next().weighty(1.0).fillCell())
treesPanel.add(buttonsPanel, gridBag.next().anchor(GridBagConstraints.CENTER))
treesPanel.add(shownRunDashboardTypesList.component, gridBag.next().weighty(1.0).fillCell())
return treesPanel
}
private fun updateRunDashboardTypes() {
hiddenRunDashboardTypesList.updateModel(runDashboardTypes, false)
shownRunDashboardTypesList.updateModel(runDashboardTypes, true)
val hiddenSize = hiddenRunDashboardTypesList.component.preferredSize
val shownSize = shownRunDashboardTypesList.component.preferredSize
if (hiddenSize.width < shownSize.width) {
hiddenRunDashboardTypesList.component.preferredSize = Dimension(shownSize.width, hiddenSize.height)
}
else {
shownRunDashboardTypesList.component.preferredSize = Dimension(hiddenSize.width, shownSize.height)
}
}
private fun changeRunDashboardTypes(all: Boolean, show: Boolean) {
val list = if (show) hiddenRunDashboardTypesList else shownRunDashboardTypesList
val types = if (all) list.allTypes else list.selectedTypes
val ids = types.stream().map(ConfigurationType::getId).collect(Collectors.toSet<String>())
if (show) {
runDashboardTypes.addAll(ids)
}
else {
runDashboardTypes.removeAll(ids)
}
updateRunDashboardTypes()
defaultsSettingsChanged()
}
private val selectedConfigurationType: ConfigurationType?
get() {
val configurationTypeNode = selectedConfigurationTypeNode
@@ -684,8 +605,7 @@ open class RunConfigurable @JvmOverloads constructor(private val myProject: Proj
confirmation.isSelected = config.isRestartRequiresConfirmation
confirmation.putClientProperty(INITIAL_VALUE_KEY, confirmation.isSelected)
runDashboardTypes.clear()
runDashboardTypes.addAll(RunDashboardManager.getInstance(myProject).types)
runDashboardTypesPanel.reset()
for (each in additionalSettings) {
each.first.reset()
@@ -725,10 +645,7 @@ open class RunConfigurable @JvmOverloads constructor(private val myProject: Proj
manager.config.isRestartRequiresConfirmation = confirmation.isSelected
confirmation.putClientProperty(INITIAL_VALUE_KEY, confirmation.isSelected)
val dashboardManager = RunDashboardManager.getInstance(myProject)
if (!Comparing.equal(runDashboardTypes, dashboardManager.types)) {
dashboardManager.types = runDashboardTypes
}
runDashboardTypesPanel.apply()
for (configurable in storedComponents.values) {
if (configurable.isModified) {
@@ -1106,7 +1023,9 @@ open class RunConfigurable @JvmOverloads constructor(private val myProject: Proj
configurationTypes.add(null)
}
val popup = NewRunConfigurationPopup.createAddPopup(configurationTypes, "$hiddenCount items more (irrelevant)...",
val popup = NewRunConfigurationPopup.createAddPopup(configurationTypes,
ExecutionBundle.message("show.irrelevant.configurations.action.name",
hiddenCount),
{ factory -> createNewConfiguration(factory) }, selectedConfigurationType,
{ showAddPopup(false) }, true)
//new TreeSpeedSearch(myTree);
@@ -1,69 +0,0 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.execution.impl
import com.intellij.execution.configurations.ConfigurationType
import com.intellij.openapi.project.Project
import com.intellij.ui.CollectionListModel
import com.intellij.ui.ColoredListCellRenderer
import com.intellij.ui.ListSpeedSearch
import com.intellij.ui.SpeedSearchComparator
import com.intellij.ui.components.JBList
import com.intellij.ui.components.JBScrollPane
import com.intellij.ui.speedSearch.SpeedSearchUtil
import javax.swing.JComponent
import javax.swing.JList
/**
* @author Konstantin Aleev
*/
internal class RunDashboardTypesList(private val myProject: Project) {
private val listModel = CollectionListModel<ConfigurationType>()
private val list = JBList<ConfigurationType>(listModel)
val component: JComponent = JBScrollPane(list)
val selectedTypes: Collection<ConfigurationType>
get() = list.selectedValuesList
val allTypes: Collection<ConfigurationType>
get() = listModel.items
init {
val search = ListSpeedSearch(list, { it.displayName })
search.comparator = SpeedSearchComparator(false)
list.visibleRowCount = 20
list.cellRenderer = object : ColoredListCellRenderer<ConfigurationType>() {
override fun customizeCellRenderer(list: JList<out ConfigurationType>,
value: ConfigurationType?,
index: Int,
selected: Boolean,
hasFocus: Boolean) {
if (value != null) {
append(value.displayName)
icon = value.icon
}
SpeedSearchUtil.applySpeedSearchHighlighting(list, this, true, selected)
}
}
}
fun updateModel(types: Set<String>, include: Boolean) {
listModel.removeAll()
listModel.add(
RunManagerImpl.getInstanceImpl(myProject).configurationFactoriesWithoutUnknown.filterNot { types.contains(it.id) xor include })
}
}
@@ -0,0 +1,196 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.execution.impl
import com.intellij.execution.ExecutionBundle
import com.intellij.execution.RunManager
import com.intellij.execution.configurations.ConfigurationType
import com.intellij.execution.dashboard.RunDashboardManager
import com.intellij.ide.util.PropertiesComponent
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.DefaultActionGroup
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.openapi.util.Comparing
import com.intellij.openapi.util.Conditions
import com.intellij.openapi.util.SystemInfo
import com.intellij.ui.*
import com.intellij.ui.components.JBLabel
import com.intellij.ui.components.JBList
import com.intellij.ui.speedSearch.SpeedSearchUtil
import com.intellij.util.containers.stream
import com.intellij.util.ui.UIUtil
import org.jetbrains.annotations.NonNls
import java.awt.BorderLayout
import javax.swing.BorderFactory
import javax.swing.JList
import javax.swing.JPanel
import javax.swing.event.ListDataEvent
import javax.swing.event.ListDataListener
@NonNls private val EXPAND_PROPERTY_KEY = "ExpandRunDashboardTypesPanel"
private val IGNORE_CASE_DISPLAY_NAME_COMPARATOR = Comparator<ConfigurationType> { t1, t2 ->
t1.displayName.compareTo(t2.displayName, ignoreCase = true)
}
/**
* @author Konstantin Aleev
*/
internal class RunDashboardTypesPanel(private val myProject: Project) : JPanel(BorderLayout()) {
private val listModel = CollectionListModel<ConfigurationType>()
private val list = JBList<ConfigurationType>(listModel)
init {
val search = ListSpeedSearch(list, { it.displayName })
search.comparator = SpeedSearchComparator(false)
list.visibleRowCount = 5
list.cellRenderer = object : ColoredListCellRenderer<ConfigurationType>() {
override fun customizeCellRenderer(list: JList<out ConfigurationType>,
value: ConfigurationType?,
index: Int,
selected: Boolean,
hasFocus: Boolean) {
if (value != null) {
append(value.displayName)
icon = value.icon
}
SpeedSearchUtil.applySpeedSearchHighlighting(list, this, true, selected)
}
}
val toolbarDecorator = ToolbarDecorator.createDecorator(list)
if (!SystemInfo.isMac) {
toolbarDecorator.setAsUsualTopToolbar()
}
toolbarDecorator.setAddAction { showAddPopup(it, true) }
toolbarDecorator.setRemoveAction { list.selectedValuesList.forEach { listModel.remove(it) } }
toolbarDecorator.setMoveUpAction(null)
toolbarDecorator.setMoveDownAction(null)
val listPanel = JPanel(BorderLayout())
listPanel.add(toolbarDecorator.createPanel(), BorderLayout.CENTER)
val descriptionLabel = JBLabel(ExecutionBundle.message("run.dashboard.configurable.types.panel.description")).apply {
border = BorderFactory.createEmptyBorder(TitledSeparator.TOP_INSET, 20, 0, 0)
componentStyle = UIUtil.ComponentStyle.SMALL
}
listPanel.add(descriptionLabel, BorderLayout.SOUTH)
val hideableDecorator = object : HideableDecorator(this,
ExecutionBundle.message("run.dashboard.configurable.types.panel.title"),
false) {
override fun on() {
super.on()
storeState()
}
override fun off() {
super.off()
storeState()
}
private fun storeState() {
PropertiesComponent.getInstance().setValue(EXPAND_PROPERTY_KEY, isExpanded.toString())
}
}
hideableDecorator.setOn(PropertiesComponent.getInstance().getBoolean(EXPAND_PROPERTY_KEY, true))
hideableDecorator.setContentComponent(listPanel)
}
private fun showAddPopup(button: AnActionButton, showApplicableTypesOnly: Boolean) {
val allTypes = RunManager.getInstance(myProject).configurationFactoriesWithoutUnknown.filter { !listModel.contains(it) }
val configurationTypes = getTypesToShow(showApplicableTypesOnly, allTypes).toMutableList()
configurationTypes.sortWith(IGNORE_CASE_DISPLAY_NAME_COMPARATOR)
val hiddenCount = allTypes.size - configurationTypes.size
val actionGroup = DefaultActionGroup(null, false)
configurationTypes.forEach({
actionGroup.add(object : AnAction(it.displayName, null, it.icon) {
override fun actionPerformed(e: AnActionEvent?) {
listModel.add(it)
listModel.sort(IGNORE_CASE_DISPLAY_NAME_COMPARATOR)
list.selectedIndex = listModel.getElementIndex(it)
}
})
})
if (hiddenCount > 0) {
actionGroup.add(object : AnAction(ExecutionBundle.message("show.irrelevant.configurations.action.name", hiddenCount)) {
override fun actionPerformed(e: AnActionEvent?) {
showAddPopup(button, false)
}
})
}
val popup = JBPopupFactory.getInstance().createActionGroupPopup(
ExecutionBundle.message("run.dashboard.configurable.add.configuration.type"),
actionGroup,
SimpleDataContext.getProjectContext(myProject),
false,
false,
false,
null,
-1,
Conditions.alwaysTrue<AnAction>())
popup.show(button.preferredPopupPoint!!)
}
private fun getTypesToShow(showApplicableTypesOnly: Boolean, allTypes: List<ConfigurationType>): List<ConfigurationType> {
if (showApplicableTypesOnly) {
val applicableTypes = allTypes.filter { it.configurationFactories.stream().anyMatch { it.isApplicable(myProject) } }
if (applicableTypes.size < (allTypes.size - 3)) {
return applicableTypes
}
}
return allTypes
}
fun addChangeListener(onChange: () -> Unit) {
listModel.addListDataListener(object : ListDataListener {
override fun contentsChanged(e: ListDataEvent?) {
onChange()
}
override fun intervalRemoved(e: ListDataEvent?) {
onChange()
}
override fun intervalAdded(e: ListDataEvent?) {
onChange()
}
})
}
fun isModified() = !Comparing.equal(listModel.items.mapTo(HashSet(), { it.id }), RunDashboardManager.getInstance(myProject).types)
fun reset() {
listModel.removeAll()
val types = RunDashboardManager.getInstance(myProject).types
listModel.add(RunManagerImpl.getInstanceImpl(myProject).configurationFactoriesWithoutUnknown.filter { types.contains(it.id) })
listModel.sort(IGNORE_CASE_DISPLAY_NAME_COMPARATOR)
}
fun apply() {
val dashboardManager = RunDashboardManager.getInstance(myProject)
val types = listModel.items.mapTo(HashSet(), { it.id })
if (!Comparing.equal(types, dashboardManager.types)) {
dashboardManager.types = types
}
}
}
@@ -58,6 +58,8 @@ run.configuration.startup.connection.rab.title=Startup/Connection
add.new.run.configuration.action2.name=Add New Configuration
add.new.run.configuration.action.name=Add New ''{0}'' Configuration
show.irrelevant.configurations.action.name={0} items more (irrelevant)...
remove.run.configuration.action.name=Remove Configuration
no.parameters=No parameters
@@ -410,9 +412,6 @@ run.dashboard.failed.group.name=Failed
run.dashboard.group.configurations.title=Group Configurations
run.dashboard.group.configurations.label=Group Name:
run.dashboard.configurable.show=Show >
run.dashboard.configurable.hide=< Hide
run.dashboard.configurable.show.all=Show All >
run.dashboard.configurable.hide.all=< Hide All
run.dashboard.configurable.shown.label=Run Dashboard
run.dashboard.configurable.hidden.label=Run/Debug Tool Window
run.dashboard.configurable.add.configuration.type=Add Configuration Type
run.dashboard.configurable.types.panel.title=Run Dashboard Types
run.dashboard.configurable.types.panel.description=<html><body>To manage run configuration in Run Dashboard add its type.<body></html>