summaryrefslogtreecommitdiff
path: root/platform/platform-impl/src/com/intellij/ui/dsl/builder/textField.kt
blob: ab53eb7516a7c3a7e2cf216dc4f797ddc2c36874 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// 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.builder

import com.intellij.openapi.observable.properties.GraphProperty
import com.intellij.openapi.observable.properties.ObservableMutableProperty
import com.intellij.openapi.observable.util.lockOrSkip
import com.intellij.openapi.observable.util.transform
import com.intellij.openapi.observable.util.whenTextChanged
import com.intellij.openapi.ui.validation.DialogValidation
import com.intellij.openapi.ui.validation.forTextComponent
import com.intellij.openapi.ui.validation.trimParameter
import com.intellij.ui.dsl.ValidationException
import com.intellij.ui.dsl.builder.impl.CellImpl.Companion.installValidationRequestor
import com.intellij.ui.dsl.catchValidationException
import com.intellij.ui.dsl.stringToInt
import com.intellij.ui.dsl.validateIntInRange
import com.intellij.ui.layout.*
import com.intellij.util.containers.map2Array
import org.jetbrains.annotations.ApiStatus
import java.util.concurrent.atomic.AtomicBoolean
import javax.swing.JTextField
import javax.swing.text.JTextComponent
import kotlin.reflect.KMutableProperty0

/**
 * Columns for text components with tiny width. Used for [Row.intTextField] by default
 */
const val COLUMNS_TINY = 6

/**
 * Columns for text components with short width (used instead of deprecated [GrowPolicy.SHORT_TEXT])
 */
const val COLUMNS_SHORT = 18

/**
 * Columns for text components with medium width (used instead of deprecated [GrowPolicy.MEDIUM_TEXT])
 */
const val COLUMNS_MEDIUM = 25

const val COLUMNS_LARGE = 36

@Deprecated("Please, recompile code", level = DeprecationLevel.HIDDEN)
@ApiStatus.ScheduledForRemoval
fun <T : JTextComponent> Cell<T>.bindText(property: GraphProperty<String>) = bindText(property)

fun <T : JTextComponent> Cell<T>.bindText(property: ObservableMutableProperty<String>): Cell<T> {
  installValidationRequestor(property)
  return applyToComponent { bind(property) }
}

fun <T : JTextComponent> Cell<T>.bindText(prop: KMutableProperty0<String>): Cell<T> {
  return bindText(prop.toMutableProperty())
}

fun <T : JTextComponent> Cell<T>.bindText(getter: () -> String, setter: (String) -> Unit): Cell<T> {
  return bindText(MutableProperty(getter, setter))
}

@Deprecated("Please, recompile code", level = DeprecationLevel.HIDDEN)
@ApiStatus.ScheduledForRemoval
fun <T : JTextComponent> Cell<T>.bindIntText(property: GraphProperty<Int>): Cell<T> = bindIntText(property)

fun <T : JTextComponent> Cell<T>.bindIntText(property: ObservableMutableProperty<Int>): Cell<T> {
  installValidationRequestor(property)
  return applyToComponent {
    bind(property.transform({ it.toString() }, { component.getValidatedIntValue(it) }))
  }
}

fun <T : JTextComponent> Cell<T>.bindIntText(prop: KMutableProperty0<Int>): Cell<T> {
  return bindIntText(prop.toMutableProperty())
}

fun <T : JTextComponent> Cell<T>.bindIntText(getter: () -> Int, setter: (Int) -> Unit): Cell<T> {
  return bindIntText(MutableProperty(getter, setter))
}

fun <T : JTextComponent> Cell<T>.text(text: String): Cell<T> {
  component.text = text
  return this
}

/**
 * Minimal width of text field in chars
 *
 * @see COLUMNS_TINY
 * @see COLUMNS_SHORT
 * @see COLUMNS_MEDIUM
 * @see COLUMNS_LARGE
 */
fun <T : JTextField> Cell<T>.columns(columns: Int) = apply {
  component.columns(columns)
}

fun <T : JTextField> T.columns(columns: Int) = apply {
  this.columns = columns
}

@Throws(ValidationException::class)
private fun JTextComponent.getValidatedIntValue(value: String): Int {
  val result = stringToInt(value)
  val range = getClientProperty(DSL_INT_TEXT_RANGE_PROPERTY) as? IntRange
  range?.let { validateIntInRange(result, it) }
  return result
}

private fun JTextComponent.bind(property: ObservableMutableProperty<String>) {
  text = property.get()
  // See: IDEA-238573 removed cyclic update of UI components that bound with properties
  val mutex = AtomicBoolean()
  property.afterChange {
    mutex.lockOrSkip {
      text = it
    }
  }
  whenTextChanged {
    mutex.lockOrSkip {
      // Catch transformed GraphProperties, e.g. for intTextField
      catchValidationException {
        property.set(text)
      }
    }
  }
}

private fun <T : JTextComponent> Cell<T>.bindText(prop: MutableProperty<String>): Cell<T> {
  return bind(JTextComponent::getText, JTextComponent::setText, prop)
}

private fun <T : JTextComponent> Cell<T>.bindIntText(prop: MutableProperty<Int>): Cell<T> {
  return bindText({ prop.get().toString() },
                  { value -> catchValidationException { prop.set(component.getValidatedIntValue(value)) } })
}

fun <T : JTextComponent> Cell<T>.trimmedTextValidation(vararg validations: DialogValidation.WithParameter<() -> String>) =
  textValidation(*validations.map2Array { it.trimParameter() })

fun <T : JTextComponent> Cell<T>.textValidation(vararg validations: DialogValidation.WithParameter<() -> String>) =
  validation(*validations.map2Array { it.forTextComponent() })