mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 14:23:28 +07:00
[DBE-23393] Added table heatmap coloring to DataGrip
Merge-request: IJ-MR-165372 Merged-by: Anvar Ramazanov <anvar.ramazanov@jetbrains.com> (cherry picked from commit 9e8bd85a80dcf8ac319559af40af0ca3aeba9f13) IJ-MR-167231 GitOrigin-RevId: 62fa8f59ab384db634f50579ab552928c515335e
This commit is contained in:
committed by
intellij-monorepo-bot
parent
73c5e818bc
commit
0e416b3d97
@@ -403,4 +403,18 @@ action.Console.TableResult.SaveLobAsAction.AmbiguousQueryResult.text=ambiguous q
|
||||
action.Console.TableResult.LoadFullCellAction.Confirmation.title=Warning
|
||||
action.Console.TableResult.LoadFullCellAction.Confirmation.text=One of the loaded cells contains a large amount of data, which may affect performance. Do you want to continue?
|
||||
data.loaders.waiting=Waiting for the loaders to be ready
|
||||
data.loaders.plugin.ad.notification=Get loader for the file ''{0}''
|
||||
data.loaders.plugin.ad.notification=Get loader for the file ''{0}''
|
||||
# Different coloring modes from TableHeatmapColorLayer.ColoringMode enum.
|
||||
datagrid.coloring.mode.off=Off
|
||||
datagrid.coloring.mode.sequential=Sequential
|
||||
datagrid.coloring.mode.diverging=Diverging
|
||||
# Action group controlling the table heatmap-coloring mode.
|
||||
group.TableViewColoringGroup.text=Table Coloring Options
|
||||
action.ToggleTableViewColoringModeOffAction.text=Off
|
||||
action.ToggleTableViewColoringModeSequentialAction.text=Sequential
|
||||
action.ToggleTableViewColoringModeDivergingAction.text=Diverging
|
||||
action.ToggleTableViewColorPerColumnAction.text=Each Column Independently
|
||||
action.ToggleTableViewColorPerTableAction.text=Whole Table
|
||||
action.ToggleTableViewColorBooleanColumnsAction.text=Color Boolean Columns
|
||||
separator.TableViewColoring.mode=Table Color Mapping
|
||||
separator.TableViewColoring.options=Options
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.intellij.database.extractors;
|
||||
|
||||
import com.intellij.database.datagrid.*;
|
||||
import com.intellij.database.run.ui.DataAccessType;
|
||||
import com.intellij.database.run.ui.grid.editors.GridCellEditorHelper;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.CommonClassNames;
|
||||
@@ -182,6 +183,10 @@ public class ObjectFormatterUtil {
|
||||
CommonClassNames.JAVA_LANG_BOOLEAN.equals(className)) && ((JdbcGridColumn)column).getSize() <= 1;
|
||||
}
|
||||
|
||||
public static boolean isBooleanCell(@NotNull CoreGrid<GridRow, GridColumn> grid, @NotNull ModelIndex<GridRow> row, @NotNull ModelIndex<GridColumn> column) {
|
||||
return isBooleanColumn(Objects.requireNonNull(grid.getDataModel(DataAccessType.DATABASE_DATA).getColumn(column)), GridCellEditorHelper.get(grid).guessJdbcTypeForEditing(grid, row, column));
|
||||
}
|
||||
|
||||
public static boolean isNumericCell(@NotNull CoreGrid<GridRow, GridColumn> grid, @NotNull ModelIndex<GridRow> row, @NotNull ModelIndex<GridColumn> column) {
|
||||
return isNumericValue(GridCellEditorHelper.get(grid).guessJdbcTypeForEditing(grid, row, column));
|
||||
}
|
||||
|
||||
@@ -331,6 +331,23 @@
|
||||
icon="com.intellij.grid.core.impl.icons.GridCoreImplIcons.ColumnFilter" />
|
||||
<action id="Console.TableResult.ToggleRecordView" class="com.intellij.database.actions.ToggleEditMaximizedViewAction"
|
||||
icon="com.intellij.grid.core.impl.icons.GridCoreImplIcons.SingleRecordView"/>
|
||||
<group id="TableViewColoringGroup" icon="com.intellij.grid.core.impl.icons.GridCoreImplIcons.TableHeatmap" popup="true">
|
||||
<separator key="separator.TableViewColoring.mode"/>
|
||||
<action id="ToggleTableViewColoringModeOffAction"
|
||||
class="com.intellij.database.run.actions.ToggleTableViewColoringModeOffAction"/>
|
||||
<action id="ToggleTableViewColoringModeSequentialAction"
|
||||
class="com.intellij.database.run.actions.ToggleTableViewColoringModeSequentialAction"/>
|
||||
<action id="ToggleTableViewColoringModeDivergingAction"
|
||||
class="com.intellij.database.run.actions.ToggleTableViewColoringModeDivergingAction"/>
|
||||
<separator key="separator.TableViewColoring.options"/>
|
||||
<action id="ToggleTableViewColorPerColumnAction"
|
||||
class="com.intellij.database.run.actions.ToggleTableViewColorPerColumnAction"/>
|
||||
<action id="ToggleTableViewColorPerTableAction"
|
||||
class="com.intellij.database.run.actions.ToggleTableViewColorPerTableAction"/>
|
||||
<separator/>
|
||||
<action id="ToggleTableViewColorBooleanColumnsAction"
|
||||
class="com.intellij.database.run.actions.ToggleTableViewColorBooleanColumnsAction"/>
|
||||
</group>
|
||||
</group>
|
||||
<reference ref="Console.TableResult.ChooseExtractor"/>
|
||||
<group id="Console.TableResult.ChooseExtractor.Group">
|
||||
|
||||
66
grid/impl/src/datagrid/color/MultipleGradient.kt
Normal file
66
grid/impl/src/datagrid/color/MultipleGradient.kt
Normal file
@@ -0,0 +1,66 @@
|
||||
package com.intellij.database.datagrid.color
|
||||
|
||||
import java.awt.Color
|
||||
|
||||
/**
|
||||
* Inspired by [java.awt.MultipleGradientPaintContext]
|
||||
* Used for getting multiple gradient values.
|
||||
* <pre>{@code
|
||||
* @Suppress("UseJBColor")
|
||||
* val multipleGradient by lazy { MultipleGradient(floatArrayOf(0f, 0.5f, 1f), arrayOf(Color.BLUE, Color.ORANGE, Color.RED)) }
|
||||
*
|
||||
* fun foo() {
|
||||
* multipleGradient.getColor(0.3f)
|
||||
* }
|
||||
* }</pre>
|
||||
*/
|
||||
class MultipleGradient(private val fractions: FloatArray, colors: Array<Color>) {
|
||||
|
||||
private var normalizedIntervals = Array(fractions.size - 1) { index ->
|
||||
fractions[index + 1] - fractions[index]
|
||||
}
|
||||
|
||||
private var gradients = Array(normalizedIntervals.size){ index ->
|
||||
interpolate(colors[index].rgb, colors[index + 1].rgb)
|
||||
}
|
||||
|
||||
/** Position should be in range [0,1] or will be truncated to this range. */
|
||||
fun getColor(position: Float): Color {
|
||||
val pos = position.coerceIn(0f, 1f)
|
||||
for (i in gradients.indices) {
|
||||
if (pos < fractions[i + 1]) {
|
||||
val delta = pos - fractions[i]
|
||||
val index = ((delta / normalizedIntervals[i]) * GRADIENT_SIZE_INDEX).toInt()
|
||||
return gradients[i][index]
|
||||
}
|
||||
}
|
||||
return gradients[gradients.size - 1][GRADIENT_SIZE_INDEX]
|
||||
}
|
||||
|
||||
private fun interpolate(rgb1: Int, rgb2: Int) : Array<Color> {
|
||||
val a1 = rgb1 shr 24 and 0xff
|
||||
val r1 = rgb1 shr 16 and 0xff
|
||||
val g1 = rgb1 shr 8 and 0xff
|
||||
val b1 = rgb1 and 0xff
|
||||
|
||||
val da = ((rgb2 shr 24) and 0xff) - a1
|
||||
val dr = ((rgb2 shr 16) and 0xff) - r1
|
||||
val dg = ((rgb2 shr 8) and 0xff) - g1
|
||||
val db = ((rgb2) and 0xff) - b1
|
||||
|
||||
val stepSize = 1.0f / GRADIENT_SIZE
|
||||
|
||||
return Array(GRADIENT_SIZE) { i ->
|
||||
val intColor = ((((a1 + i * da * stepSize) + 0.5).toInt() shl 24)) or
|
||||
((((r1 + i * dr * stepSize) + 0.5).toInt() shl 16)) or
|
||||
((((g1 + i * dg * stepSize) + 0.5).toInt() shl 8)) or ((((b1 + i * db * stepSize) + 0.5).toInt()))
|
||||
@Suppress("UseJBColor") // Gradient is theme-aware.
|
||||
Color(intColor)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val GRADIENT_SIZE: Int = 256
|
||||
const val GRADIENT_SIZE_INDEX: Int = GRADIENT_SIZE - 1
|
||||
}
|
||||
}
|
||||
280
grid/impl/src/datagrid/color/TableHeatmapColorLayer.kt
Normal file
280
grid/impl/src/datagrid/color/TableHeatmapColorLayer.kt
Normal file
@@ -0,0 +1,280 @@
|
||||
package com.intellij.database.datagrid.color
|
||||
|
||||
import com.intellij.database.DataGridBundle
|
||||
import com.intellij.database.datagrid.DataGrid
|
||||
import com.intellij.database.datagrid.GridColumn
|
||||
import com.intellij.database.datagrid.GridModel
|
||||
import com.intellij.database.datagrid.GridRequestSource
|
||||
import com.intellij.database.datagrid.GridRow
|
||||
import com.intellij.database.datagrid.ModelIndex
|
||||
import com.intellij.database.datagrid.ModelIndexSet
|
||||
import com.intellij.database.datagrid.mutating.ColumnDescriptor
|
||||
import com.intellij.database.extractors.ObjectFormatterUtil
|
||||
import com.intellij.database.run.ui.DataAccessType
|
||||
import com.intellij.database.run.ui.table.TableResultView
|
||||
import com.intellij.ide.util.PropertiesComponent
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.ui.JBColor
|
||||
import java.awt.Color
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Adds and controls table heatmap-styled coloring.
|
||||
*
|
||||
* Usage: {code}TableHeatmapColorLayer.installOn(dataGrid){code}
|
||||
*/
|
||||
class TableHeatmapColorLayer private constructor(private val dataGrid: DataGrid, private val tableResultView: TableResultView, private val useOldColors: Boolean) : ColorLayer, GridModel.Listener<GridRow, GridColumn>, Disposable {
|
||||
|
||||
enum class ColoringMode(val title: String) {
|
||||
OFF(DataGridBundle.message("datagrid.coloring.mode.off")),
|
||||
SEQUENTIAL(DataGridBundle.message("datagrid.coloring.mode.sequential")),
|
||||
DIVERGING(DataGridBundle.message("datagrid.coloring.mode.diverging"));
|
||||
|
||||
companion object {
|
||||
fun getByName(name: String?) : ColoringMode = entries.firstOrNull { it.name == name } ?: DIVERGING
|
||||
}
|
||||
}
|
||||
|
||||
private class ColumnRange(val min: Double, val max: Double)
|
||||
|
||||
private val columns = mutableMapOf<ModelIndex<GridColumn>, ColumnRange>()
|
||||
private var globalMax = -Double.MAX_VALUE
|
||||
private var globalMin = Double.MAX_VALUE
|
||||
|
||||
var coloringMode: ColoringMode = getColoringMode()
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
setColoringMode(value)
|
||||
tableResultView.showHorizontalLines = value != ColoringMode.OFF
|
||||
tableResultView.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
/** When true, the coloring separate for every numeric column, when false, the coloring is global. */
|
||||
var perColumn : Boolean = isPerColumnColoringEnabled()
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
tableResultView.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
var colorBooleanColumns: Boolean = isColorBooleanColumnsEnabled()
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
tableResultView.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
dataGrid.getDataModel(DataAccessType.DATA_WITH_MUTATIONS).addListener(this, this)
|
||||
updateRanges()
|
||||
}
|
||||
|
||||
override fun dispose(): Unit = Unit
|
||||
|
||||
override fun columnsAdded(columns: ModelIndexSet<GridColumn>?): Unit = Unit
|
||||
override fun columnsRemoved(columns: ModelIndexSet<GridColumn>?): Unit = Unit
|
||||
override fun rowsAdded(rows: ModelIndexSet<GridRow>?): Unit = Unit
|
||||
override fun rowsRemoved(rows: ModelIndexSet<GridRow>?): Unit = Unit
|
||||
override fun cellsUpdated(
|
||||
rows: ModelIndexSet<GridRow>?,
|
||||
columns: ModelIndexSet<GridColumn>?,
|
||||
place: GridRequestSource.RequestPlace?,
|
||||
): Unit = Unit
|
||||
|
||||
override fun afterLastRowAdded(): Unit = updateRanges()
|
||||
|
||||
private fun updateRanges() {
|
||||
val dataModel = dataGrid.getDataModel(DataAccessType.DATA_WITH_MUTATIONS)
|
||||
|
||||
if (dataModel.rowCount == 0 || dataModel.columnCount == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
dataModel.columnIndices.asIterable().forEach { columnIndex ->
|
||||
val column = dataModel.getColumn(columnIndex)
|
||||
val firstRow = dataModel.rowIndices.first()
|
||||
|
||||
// ColumnDescriptor.Attribute.HIGHLIGHTED currently is the only normal way do extract index column from coloring.
|
||||
if (column == null ||
|
||||
column.attributes.contains(ColumnDescriptor.Attribute.HIGHLIGHTED) ||
|
||||
!ObjectFormatterUtil.isNumericCell(dataGrid, firstRow, columnIndex)) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
var min = Double.MAX_VALUE
|
||||
var max = -Double.MAX_VALUE
|
||||
dataModel.rowIndices.asIterable().forEach { rowIndex ->
|
||||
val value = dataModel.getValueAt(rowIndex, columnIndex).toString().toDoubleOrNull()
|
||||
if (value != null) {
|
||||
if (min > value) min = value
|
||||
if (max < value) max = value
|
||||
}
|
||||
}
|
||||
|
||||
if (max > globalMax) globalMax = max
|
||||
if (min < globalMin) globalMin = min
|
||||
|
||||
if (min != Double.MAX_VALUE && max != -Double.MAX_VALUE) {
|
||||
columns[columnIndex] = ColumnRange(min, max)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getColor(x: Double, minX: Double, maxX: Double): Color? {
|
||||
if (minX == maxX) return null // Protection from the situation with one-row table or column of same values.
|
||||
|
||||
val p = (x - minX) / (maxX - minX)
|
||||
return when (coloringMode) { // Currently, this is white to blue.
|
||||
ColoringMode.SEQUENTIAL -> {
|
||||
//if (JBColor.isBright())
|
||||
// Color.getHSBColor(240 / 360f, p.toFloat() * 0.6f, 1f)
|
||||
//else
|
||||
// Color.getHSBColor(240 / 360f, 220 / 360f, p.toFloat() * 0.7f)
|
||||
if (JBColor.isBright())
|
||||
getSimpleGradientLight(useOldColors).getColor(p.toFloat())
|
||||
else
|
||||
getSimpleGradientDark(useOldColors).getColor(p.toFloat())
|
||||
}
|
||||
|
||||
// Multiple color gradient orange - blue - red.
|
||||
ColoringMode.DIVERGING -> {
|
||||
if (JBColor.isBright())
|
||||
getMultipleGradientLight(useOldColors).getColor(p.toFloat())
|
||||
else
|
||||
getMultipleGradientDark(useOldColors).getColor(p.toFloat())
|
||||
}
|
||||
|
||||
// Should not be called, but we will return our legacy colors for now.
|
||||
else -> {
|
||||
@Suppress("UseJBColor") if (JBColor.isBright()) Color((255 * p).roundToInt(), 0, (255 * (1 - p)).roundToInt(), 255).brighter()
|
||||
else Color((255 * p).roundToInt(), 0, (255 * (1 - p)).roundToInt(), 255)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCellBackground(row: ModelIndex<GridRow>, column: ModelIndex<GridColumn>, grid: DataGrid, color: Color?): Color? {
|
||||
if (coloringMode == ColoringMode.OFF) return null
|
||||
|
||||
val dataModel = dataGrid.getDataModel(DataAccessType.DATA_WITH_MUTATIONS)
|
||||
|
||||
if (colorBooleanColumns && ObjectFormatterUtil.isBooleanCell(grid, row, column)) {
|
||||
return if (dataModel.getValueAt(row, column).toString().lowercase() == "true") {
|
||||
getColor(0.6, 0.0, 1.0)
|
||||
}
|
||||
else {
|
||||
getColor(0.0, 0.0, 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
val columnRange = columns[column] ?: return null
|
||||
|
||||
val doubleValue = dataModel.getValueAt(row, column).toString().toDoubleOrNull() ?: return null
|
||||
|
||||
return if (perColumn) getColor(doubleValue, columnRange.min, columnRange.max)
|
||||
else getColor(doubleValue, globalMin, globalMax)
|
||||
}
|
||||
|
||||
override fun getRowHeaderBackground(row: ModelIndex<GridRow>, grid: DataGrid, color: Color?): Color? = null
|
||||
|
||||
override fun getColumnHeaderBackground(column: ModelIndex<GridColumn>, grid: DataGrid, color: Color?): Color? = null
|
||||
|
||||
override fun getPriority(): Int = 0
|
||||
|
||||
companion object {
|
||||
@Suppress("UseJBColor")
|
||||
val simpleGradientLightOld: MultipleGradient by lazy {
|
||||
MultipleGradient(floatArrayOf(0f, 1f), arrayOf(Color(93, 143, 244),
|
||||
Color(212, 226, 255)))
|
||||
}
|
||||
|
||||
@Suppress("UseJBColor")
|
||||
val simpleGradientLight: MultipleGradient by lazy {
|
||||
MultipleGradient(floatArrayOf(0f, 1f), arrayOf(Color(54, 150, 80),
|
||||
Color(242, 252, 243)))
|
||||
}
|
||||
|
||||
fun getSimpleGradientLight(useOldColors: Boolean): MultipleGradient = if (useOldColors) simpleGradientLightOld else simpleGradientLight
|
||||
|
||||
@Suppress("UseJBColor")
|
||||
val simpleGradientDarkOld: MultipleGradient by lazy {
|
||||
MultipleGradient(floatArrayOf(0f, 1f), arrayOf(Color(54, 106, 207),
|
||||
Color(37, 50, 77)))
|
||||
}
|
||||
|
||||
@Suppress("UseJBColor")
|
||||
val simpleGradientDark: MultipleGradient by lazy {
|
||||
MultipleGradient(floatArrayOf(0f, 1f), arrayOf(Color(87, 150, 92),
|
||||
Color(37, 54, 39)))
|
||||
}
|
||||
|
||||
fun getSimpleGradientDark(useOldColors: Boolean): MultipleGradient = if (useOldColors) simpleGradientDarkOld else simpleGradientDark
|
||||
|
||||
// Test 3-colors gradient for diverging coloring.
|
||||
@Suppress("UseJBColor")
|
||||
val multipleGradientLightOld: MultipleGradient by lazy {
|
||||
MultipleGradient(floatArrayOf(0f, 0.5f, 1f), arrayOf(Color(88, 140, 243),
|
||||
Color.WHITE,
|
||||
Color(228, 108, 120)))
|
||||
}
|
||||
|
||||
@Suppress("UseJBColor")
|
||||
val multipleGradientLight: MultipleGradient by lazy {
|
||||
MultipleGradient(floatArrayOf(0f, 0.5f, 1f), arrayOf(Color(54, 150, 80),
|
||||
Color.WHITE,
|
||||
Color(228, 108, 120)))
|
||||
}
|
||||
|
||||
fun getMultipleGradientLight(useOldColors: Boolean): MultipleGradient = if (useOldColors) multipleGradientLightOld else multipleGradientLight
|
||||
|
||||
@Suppress("UseJBColor")
|
||||
val multipleGradientDarkOld: MultipleGradient by lazy {
|
||||
MultipleGradient(floatArrayOf(0f, 0.5f, 1f), arrayOf(Color(54, 106, 207, ),
|
||||
Color(30, 31, 34),
|
||||
Color(189, 87, 87)))
|
||||
}
|
||||
|
||||
@Suppress("UseJBColor")
|
||||
val multipleGradientDark: MultipleGradient by lazy {
|
||||
MultipleGradient(floatArrayOf(0f, 0.5f, 1f), arrayOf(Color(87, 150, 92),
|
||||
Color(30, 31, 34),
|
||||
Color(189, 87, 87)))
|
||||
}
|
||||
|
||||
fun getMultipleGradientDark(useOldColors: Boolean): MultipleGradient = if (useOldColors) multipleGradientDarkOld else multipleGradientDark
|
||||
|
||||
val HEATMAP_OLD_COLORS : Key<Boolean> = Key.create<Boolean>("HEATMAP_OLD_COLORS")
|
||||
|
||||
private const val COLOR_BOOLEAN_COLUMNS = "table.view.heatmap.color.boolean.columns"
|
||||
private const val PER_COLUMN_COLORING = "table.view.heatmap.color.per.columns"
|
||||
private const val COLORING_MODE = "table.view.heatmap.coloring.mode"
|
||||
|
||||
fun getColoringMode(): ColoringMode = ColoringMode.getByName(PropertiesComponent.getInstance().getValue(COLORING_MODE))
|
||||
fun setColoringMode(mode: ColoringMode): Unit = PropertiesComponent.getInstance().setValue(COLORING_MODE, mode.name)
|
||||
|
||||
fun isPerColumnColoringEnabled(): Boolean = PropertiesComponent.getInstance().getBoolean(PER_COLUMN_COLORING, false)
|
||||
fun setPerColumnColoringEnabled(value: Boolean): Unit = PropertiesComponent.getInstance().setValue(PER_COLUMN_COLORING, value)
|
||||
|
||||
fun isColorBooleanColumnsEnabled(): Boolean = PropertiesComponent.getInstance().getBoolean(COLOR_BOOLEAN_COLUMNS, true)
|
||||
fun setColorBooleanColumnsEnabled(value: Boolean): Unit = PropertiesComponent.getInstance().setValue(COLOR_BOOLEAN_COLUMNS, value, true)
|
||||
|
||||
fun installOn(dataGrid: DataGrid): TableHeatmapColorLayer? {
|
||||
val gridColorModel = dataGrid.colorModel as? GridColorModelImpl ?: return null
|
||||
val tableResultView = dataGrid.resultView as? TableResultView ?: return null
|
||||
val useOldColors = dataGrid.getUserData(HEATMAP_OLD_COLORS) == true
|
||||
val tableHeatmapColorLayer = TableHeatmapColorLayer(dataGrid, tableResultView, useOldColors)
|
||||
Disposer.register(dataGrid, tableHeatmapColorLayer)
|
||||
gridColorModel.addLayer(tableHeatmapColorLayer)
|
||||
tableResultView.repaint()
|
||||
|
||||
tableResultView.showHorizontalLines = true
|
||||
|
||||
return tableHeatmapColorLayer
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,10 @@ package com.intellij.database.datagrid
|
||||
|
||||
import com.intellij.database.DatabaseDataKeys
|
||||
import com.intellij.ide.DataManager
|
||||
import com.intellij.ide.util.PropertiesComponent
|
||||
import com.intellij.openapi.actionSystem.PlatformCoreDataKeys
|
||||
import com.intellij.openapi.fileEditor.FileEditor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.util.containers.TreeTraversal
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import java.awt.Component
|
||||
@@ -65,3 +67,10 @@ private fun preferredRowHeight(table: JTable, row: Int): Int {
|
||||
}
|
||||
return height
|
||||
}
|
||||
|
||||
const val COLORED_BY_DEFAULT: String = "datagrid.heatmap.switchedByDefault"
|
||||
|
||||
fun setHeatmapColoringEnable(project: Project, value: Boolean) {
|
||||
PropertiesComponent.getInstance(project).setValue(COLORED_BY_DEFAULT, value, true)
|
||||
}
|
||||
|
||||
|
||||
38
grid/impl/src/run/actions/ToggleTableViewBaseAction.kt
Normal file
38
grid/impl/src/run/actions/ToggleTableViewBaseAction.kt
Normal file
@@ -0,0 +1,38 @@
|
||||
package com.intellij.database.run.actions
|
||||
|
||||
import com.intellij.database.datagrid.GridUtil
|
||||
import com.intellij.database.datagrid.color.GridColorModelImpl
|
||||
import com.intellij.openapi.actionSystem.ActionUpdateThread
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.project.DumbAwareToggleAction
|
||||
import com.intellij.database.datagrid.color.TableHeatmapColorLayer
|
||||
|
||||
abstract class ToggleTableViewBaseAction : DumbAwareToggleAction() {
|
||||
override fun update(e: AnActionEvent) {
|
||||
val dataGrid = GridUtil.getDataGrid(e.dataContext)
|
||||
e.presentation.isEnabled = dataGrid != null
|
||||
if (e.presentation.isEnabled) {
|
||||
super.update(e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT
|
||||
|
||||
protected fun getOrCreateHeatMapColorLayer(e: AnActionEvent) : TableHeatmapColorLayer? {
|
||||
val heatmapColorLayer = getHeatMapColorLayer(e)
|
||||
if (heatmapColorLayer != null)
|
||||
return heatmapColorLayer
|
||||
|
||||
val grid = GridUtil.getDataGrid(e.dataContext) ?: return null
|
||||
return TableHeatmapColorLayer.installOn(grid)
|
||||
}
|
||||
|
||||
protected fun getHeatMapColorLayer(e: AnActionEvent): TableHeatmapColorLayer? {
|
||||
return getGridColorModel(e)?.getLayer(TableHeatmapColorLayer::class.java) as? TableHeatmapColorLayer
|
||||
}
|
||||
|
||||
private fun getGridColorModel(e: AnActionEvent): GridColorModelImpl? {
|
||||
val grid = GridUtil.getDataGrid(e.dataContext) ?: return null
|
||||
return grid.colorModel as? GridColorModelImpl
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.intellij.database.run.actions
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.database.datagrid.color.TableHeatmapColorLayer
|
||||
|
||||
class ToggleTableViewColorBooleanColumnsAction : ToggleTableViewBaseAction() {
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
super.update(e)
|
||||
if (e.presentation.isEnabled) {
|
||||
e.presentation.isEnabled = getHeatMapColorLayer(e) != null
|
||||
}
|
||||
}
|
||||
|
||||
override fun isSelected(e: AnActionEvent): Boolean {
|
||||
val heatmapLayer = getHeatMapColorLayer(e) ?: return false
|
||||
return heatmapLayer.colorBooleanColumns
|
||||
}
|
||||
|
||||
override fun setSelected(e: AnActionEvent, state: Boolean) {
|
||||
val heatmapLayer = getHeatMapColorLayer(e) ?: return
|
||||
heatmapLayer.colorBooleanColumns = state
|
||||
|
||||
// Persisting to restore on next table creation.
|
||||
TableHeatmapColorLayer.setColorBooleanColumnsEnabled(state)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.intellij.database.run.actions
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.database.datagrid.color.TableHeatmapColorLayer
|
||||
|
||||
class ToggleTableViewColorPerColumnAction : ToggleTableViewBaseAction() {
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
super.update(e)
|
||||
if (e.presentation.isEnabled) {
|
||||
e.presentation.isEnabled = getHeatMapColorLayer(e) != null
|
||||
}
|
||||
}
|
||||
|
||||
override fun isSelected(e: AnActionEvent): Boolean {
|
||||
val heatmapLayer = getHeatMapColorLayer(e) ?: return false
|
||||
return heatmapLayer.perColumn
|
||||
}
|
||||
|
||||
override fun setSelected(e: AnActionEvent, state: Boolean) {
|
||||
val heatmapLayer = getHeatMapColorLayer(e) ?: return
|
||||
heatmapLayer.perColumn = state
|
||||
|
||||
TableHeatmapColorLayer.setPerColumnColoringEnabled(heatmapLayer.perColumn)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.intellij.database.run.actions
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.database.datagrid.color.TableHeatmapColorLayer
|
||||
|
||||
class ToggleTableViewColorPerTableAction : ToggleTableViewBaseAction() {
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
super.update(e)
|
||||
if (e.presentation.isEnabled) {
|
||||
e.presentation.isEnabled = getHeatMapColorLayer(e) != null
|
||||
}
|
||||
}
|
||||
|
||||
override fun isSelected(e: AnActionEvent): Boolean {
|
||||
val heatmapLayer = getHeatMapColorLayer(e) ?: return false
|
||||
return !heatmapLayer.perColumn
|
||||
}
|
||||
|
||||
override fun setSelected(e: AnActionEvent, state: Boolean) {
|
||||
val heatmapLayer = getHeatMapColorLayer(e) ?: return
|
||||
heatmapLayer.perColumn = !state
|
||||
|
||||
TableHeatmapColorLayer.setPerColumnColoringEnabled(heatmapLayer.perColumn)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.intellij.database.run.actions
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.database.datagrid.color.TableHeatmapColorLayer
|
||||
import com.intellij.database.datagrid.setHeatmapColoringEnable
|
||||
|
||||
class ToggleTableViewColoringModeDivergingAction : ToggleTableViewBaseAction() {
|
||||
override fun isSelected(e: AnActionEvent): Boolean {
|
||||
return getHeatMapColorLayer(e)?.coloringMode == TableHeatmapColorLayer.ColoringMode.DIVERGING
|
||||
}
|
||||
|
||||
override fun setSelected(e: AnActionEvent, state: Boolean) {
|
||||
if (state) {
|
||||
getOrCreateHeatMapColorLayer(e)?.coloringMode = TableHeatmapColorLayer.ColoringMode.DIVERGING
|
||||
TableHeatmapColorLayer.setColoringMode(TableHeatmapColorLayer.ColoringMode.DIVERGING)
|
||||
e.project?.let { setHeatmapColoringEnable(it, true) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.intellij.database.run.actions
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.database.datagrid.color.TableHeatmapColorLayer
|
||||
import com.intellij.database.datagrid.setHeatmapColoringEnable
|
||||
|
||||
/** Enables or disables heatmap-styled coloring of table cells. */
|
||||
class ToggleTableViewColoringModeOffAction : ToggleTableViewBaseAction() {
|
||||
override fun isSelected(e: AnActionEvent): Boolean {
|
||||
val heatmapLayer = getHeatMapColorLayer(e) ?: return true
|
||||
return heatmapLayer.coloringMode == TableHeatmapColorLayer.ColoringMode.OFF
|
||||
}
|
||||
|
||||
override fun setSelected(e: AnActionEvent, state: Boolean) {
|
||||
if (state) {
|
||||
getHeatMapColorLayer(e)?.coloringMode = TableHeatmapColorLayer.ColoringMode.OFF
|
||||
TableHeatmapColorLayer.setColoringMode(TableHeatmapColorLayer.ColoringMode.OFF)
|
||||
e.project?.let { setHeatmapColoringEnable(it, false) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.intellij.database.run.actions
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.database.datagrid.color.TableHeatmapColorLayer
|
||||
import com.intellij.database.datagrid.setHeatmapColoringEnable
|
||||
|
||||
class ToggleTableViewColoringModeSequentialAction : ToggleTableViewBaseAction() {
|
||||
override fun isSelected(e: AnActionEvent): Boolean {
|
||||
return getHeatMapColorLayer(e)?.coloringMode == TableHeatmapColorLayer.ColoringMode.SEQUENTIAL
|
||||
}
|
||||
|
||||
override fun setSelected(e: AnActionEvent, state: Boolean) {
|
||||
if (state) {
|
||||
getOrCreateHeatMapColorLayer(e)?.coloringMode = TableHeatmapColorLayer.ColoringMode.SEQUENTIAL
|
||||
TableHeatmapColorLayer.setColoringMode(TableHeatmapColorLayer.ColoringMode.SEQUENTIAL)
|
||||
e.project?.let { setHeatmapColoringEnable(it, true) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,7 +232,7 @@ class PyDataView(private val project: Project) : DumbAware {
|
||||
companion object {
|
||||
private const val DATA_VIEWER_ID = "SciView"
|
||||
|
||||
const val COLORED_BY_DEFAULT: String = "python.debugger.dataView.coloredByDefault"
|
||||
const val COLORED_BY_DEFAULT: String = "datagrid.heatmap.switchedByDefault"
|
||||
const val AUTO_RESIZE: String = "python.debugger.dataView.autoresize"
|
||||
private const val HELP_ID = "reference.toolWindows.PyDataView"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user