[Editor] Improve Parameter<T> binding to backing properties

This commit is contained in:
2021-02-20 11:36:04 +01:00
parent 53c24c9b99
commit 878d6de678
5 changed files with 51 additions and 42 deletions

View File

@@ -12,17 +12,31 @@ abstract class Parameter<T>(
private val autocommit: Boolean = false, private val autocommit: Boolean = false,
private val onCommit: (oldValue: T, newValue: T, submit: () -> Unit) -> Unit = { _, _, submit -> submit() } private val onCommit: (oldValue: T, newValue: T, submit: () -> Unit) -> Unit = { _, _, submit -> submit() }
) { ) {
private var other: Property<T>? = null
val keyProperty = ReadOnlyStringWrapper(key) val keyProperty = ReadOnlyStringWrapper(key)
val key by keyProperty val key by keyProperty
val editableProperty: BooleanProperty = SimpleBooleanProperty(editable) val editableProperty: BooleanProperty = SimpleBooleanProperty(editable)
var editable by editableProperty
var editable by editableProperty
val valueProperty: ObjectProperty<T> = SimpleObjectProperty(initialValue) val valueProperty: ObjectProperty<T> = SimpleObjectProperty(initialValue)
var value by valueProperty var value by valueProperty
fun commit() = onCommit(value, editorValueProperty.value, if (autocommit) NOOP else this::submit) fun commit() = onCommit(value, editorValueProperty.value, if (autocommit) NOOP else this::submit)
fun bindBidirectional(other: Property<T>) {
unbindBidirectional()
this.value = other.value
this.other = other.apply { bindBidirectional(valueProperty) }
}
fun unbindBidirectional() {
this.other?.unbindBidirectional(valueProperty)
this.other = null
}
private fun submit() { private fun submit() {
value = editorValueProperty.value value = editorValueProperty.value
} }
@@ -30,7 +44,17 @@ abstract class Parameter<T>(
protected fun init() { protected fun init() {
if (autocommit) { if (autocommit) {
editorValueProperty.bindBidirectional(valueProperty) editorValueProperty.bindBidirectional(valueProperty)
valueProperty.addListener { _, oldValue, newValue -> onCommit(oldValue, newValue, NOOP) } valueProperty.addListener { _, oldValue, newValue ->
// Disclaimer:
// This ugly hack enforces the onCommit() listener to be fired, when
// `other` Property has been updated with current value.
// Without that hack, even if `other` is bidirectionally bound to `valueProperty`,
// it is updated right after the onCommit() listener, which is not very useful.
other?.value = newValue
onCommit(oldValue, newValue, NOOP)
}
} else { } else {
editorValueProperty.value = value editorValueProperty.value = value
valueProperty.addListener { _, _, v -> editorValueProperty.value = v } valueProperty.addListener { _, _, v -> editorValueProperty.value = v }

View File

@@ -9,10 +9,6 @@ import org.springframework.stereotype.Component
@Component @Component
class ColorLayerParametersBinder : LayerParametersBinder<ColorLayer> { class ColorLayerParametersBinder : LayerParametersBinder<ColorLayer> {
private var red: IntegerParameter? = null
private var green: IntegerParameter? = null
private var blue: IntegerParameter? = null
private var alpha: IntegerParameter? = null
override fun bind( override fun bind(
layer: ColorLayer, layer: ColorLayer,
@@ -21,24 +17,15 @@ class ColorLayerParametersBinder : LayerParametersBinder<ColorLayer> {
onCommit: () -> Unit onCommit: () -> Unit
) { ) {
val red = IntegerParameter("red", 100, 0, 100, autocommit = true) { _, _, _ -> onCommit() } val red = IntegerParameter("red", 100, 0, 100, autocommit = true) { _, _, _ -> onCommit() }
.apply { valueProperty.bindBidirectional(layer.redProperty) }
val green = IntegerParameter("green", 100, 0, 100, autocommit = true) { _, _, _ -> onCommit() } val green = IntegerParameter("green", 100, 0, 100, autocommit = true) { _, _, _ -> onCommit() }
.apply { valueProperty.bindBidirectional(layer.greenProperty) }
val blue = IntegerParameter("blue", 100, 0, 100, autocommit = true) { _, _, _ -> onCommit() } val blue = IntegerParameter("blue", 100, 0, 100, autocommit = true) { _, _, _ -> onCommit() }
.apply { valueProperty.bindBidirectional(layer.blueProperty) }
val alpha = IntegerParameter("alpha", 100, 0, 100, autocommit = true) { _, _, _ -> onCommit() } val alpha = IntegerParameter("alpha", 100, 0, 100, autocommit = true) { _, _, _ -> onCommit() }
.apply { valueProperty.bindBidirectional(layer.alphaProperty) }
red.bindBidirectional(layer.redProperty)
green.bindBidirectional(layer.greenProperty)
blue.bindBidirectional(layer.blueProperty)
alpha.bindBidirectional(layer.alphaProperty)
parameters.addAll(red, green, blue, alpha) parameters.addAll(red, green, blue, alpha)
} }
override fun unbind(layer: ColorLayer, parameters: ObservableList<Parameter<*>>) {
red?.valueProperty?.unbindBidirectional(layer.redProperty)
green?.valueProperty?.unbindBidirectional(layer.greenProperty)
blue?.valueProperty?.unbindBidirectional(layer.blueProperty)
alpha?.valueProperty?.unbindBidirectional(layer.alphaProperty)
}
} }

View File

@@ -3,7 +3,6 @@ package com.bartlomiejpluta.base.editor.map.parameter.layer
import com.bartlomiejpluta.base.editor.common.parameter.model.GraphicAssetParameter import com.bartlomiejpluta.base.editor.common.parameter.model.GraphicAssetParameter
import com.bartlomiejpluta.base.editor.common.parameter.model.IntegerParameter import com.bartlomiejpluta.base.editor.common.parameter.model.IntegerParameter
import com.bartlomiejpluta.base.editor.common.parameter.model.Parameter import com.bartlomiejpluta.base.editor.common.parameter.model.Parameter
import com.bartlomiejpluta.base.editor.image.asset.ImageAsset
import com.bartlomiejpluta.base.editor.map.model.layer.ImageLayer import com.bartlomiejpluta.base.editor.map.model.layer.ImageLayer
import com.bartlomiejpluta.base.editor.project.model.Project import com.bartlomiejpluta.base.editor.project.model.Project
import javafx.collections.ObservableList import javafx.collections.ObservableList
@@ -11,8 +10,6 @@ import org.springframework.stereotype.Component
@Component @Component
class ImageLayerParametersBinder : LayerParametersBinder<ImageLayer> { class ImageLayerParametersBinder : LayerParametersBinder<ImageLayer> {
private var image: GraphicAssetParameter<ImageAsset>? = null
private var opacity: IntegerParameter? = null
override fun bind( override fun bind(
layer: ImageLayer, layer: ImageLayer,
@@ -20,20 +17,18 @@ class ImageLayerParametersBinder : LayerParametersBinder<ImageLayer> {
project: Project, project: Project,
onCommit: () -> Unit onCommit: () -> Unit
) { ) {
image = GraphicAssetParameter("image", layer.imageAsset, true, project.images) { _, _, submit -> val image = GraphicAssetParameter("image", layer.imageAsset, true, project.images) { _, _, submit ->
onCommit() onCommit()
submit() submit()
}.apply { valueProperty.bindBidirectional(layer.imageAssetProperty) } }
opacity = IntegerParameter("opacity", 100, 0, 100, autocommit = true) { _, _, _ -> val opacity = IntegerParameter("opacity", 100, 0, 100, autocommit = true) { _, _, _ ->
onCommit() onCommit()
}.apply { valueProperty.bindBidirectional(layer.opacityProperty) } }
image.bindBidirectional(layer.imageAssetProperty)
opacity.bindBidirectional(layer.opacityProperty)
parameters.addAll(image, opacity) parameters.addAll(image, opacity)
} }
override fun unbind(layer: ImageLayer, parameters: ObservableList<Parameter<*>>) {
image?.valueProperty?.unbindBidirectional(layer.imageAssetProperty)
opacity?.valueProperty?.unbindBidirectional(layer.opacityProperty)
}
} }

View File

@@ -7,5 +7,4 @@ import javafx.collections.ObservableList
interface LayerParametersBinder<T : Layer> { interface LayerParametersBinder<T : Layer> {
fun bind(layer: T, parameters: ObservableList<Parameter<*>>, project: Project, onCommit: () -> Unit) fun bind(layer: T, parameters: ObservableList<Parameter<*>>, project: Project, onCommit: () -> Unit)
fun unbind(layer: T, parameters: ObservableList<Parameter<*>>)
} }

View File

@@ -24,17 +24,21 @@ class MapLayerParameters : View() {
private val parameters = observableListOf<Parameter<*>>() private val parameters = observableListOf<Parameter<*>>()
init { init {
editorStateVM.selectedLayerProperty.addListener { _, previousLayer, layer -> editorStateVM.selectedLayerProperty.addListener { _, _, layer ->
when (previousLayer) { parameters.forEach(Parameter<*>::unbindBidirectional)
is ColorLayer -> colorLayerParametersBinder.unbind(previousLayer, parameters)
is ImageLayer -> imageLayerParametersBinder.unbind(previousLayer, parameters)
}
parameters.clear() parameters.clear()
when (layer) { when (layer) {
is ColorLayer -> colorLayerParametersBinder.bind(layer, parameters, projectContext.project!!) { fire(RedrawMapRequestEvent) } is ColorLayer -> colorLayerParametersBinder.bind(layer, parameters, projectContext.project!!) {
is ImageLayer -> imageLayerParametersBinder.bind(layer, parameters, projectContext.project!!) { fire(RedrawMapRequestEvent) } fire(
RedrawMapRequestEvent
)
}
is ImageLayer -> imageLayerParametersBinder.bind(layer, parameters, projectContext.project!!) {
fire(
RedrawMapRequestEvent
)
}
} }
} }
} }