From d88f258f9c6401354d006d2e2ecad54f9d73f503 Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Thu, 26 Mar 2020 19:36:14 +0100 Subject: [PATCH] Add sequence editor control and DoubleListParameter --- orx-gui/src/main/kotlin/Gui.kt | 71 +++++++++++++++---- .../openrndr/panel/elements/SequenceEditor.kt | 62 ++++++++++++---- .../org/openrndr/panel/style/DefaultStyles.kt | 6 ++ .../org/openrndr/panel/tools/Tooltip.kt | 3 +- 4 files changed, 114 insertions(+), 28 deletions(-) diff --git a/orx-gui/src/main/kotlin/Gui.kt b/orx-gui/src/main/kotlin/Gui.kt index 3ecda134..fdbdb0ba 100644 --- a/orx-gui/src/main/kotlin/Gui.kt +++ b/orx-gui/src/main/kotlin/Gui.kt @@ -155,8 +155,7 @@ class GUI : Extension { this.background = Color.RGBa(ColorRGBa.GRAY.copy(a = 0.99)) this.overflow = Overflow.Scroll - - /* 1) setup control style */ + // descendant(has type "colorpicker-button") { this.width = 175.px } @@ -181,6 +180,12 @@ class GUI : Extension { this.width = 175.px this.height = 175.px } + + descendant(has type "sequence-editor") { + this.width = 175.px + this.height = 100.px + } + // } styleSheet(has class_ "randomize-strong") { @@ -254,7 +259,7 @@ class GUI : Extension { header.mouse.pressed.subscribe { it.cancelPropagation() } - header.mouse.clicked.subscribe { + { if (KeyModifier.CTRL in it.modifiers) { collapsible.classes.remove(collapseClass) @@ -279,7 +284,7 @@ class GUI : Extension { } } } - collapseBorder.mouse.pressed.subscribe { + { it.cancelPropagation() } @@ -294,7 +299,7 @@ class GUI : Extension { } it.cancelPropagation() } - sidebar.mouse.scrolled.subscribe { + { sidebarState().scrollTop = sidebar.scrollTop } if (sidebarState().collapsed) { @@ -316,18 +321,19 @@ class GUI : Extension { program.extend(panel) } + /* 2) control creation. create control, set label, set range, setup event-handler, load values */ + // private fun Div.addControl(compartment: LabeledObject, parameter: Parameter): Element { val obj = compartment.obj return when (parameter.parameterType) { - /* 2) control creation. create control, set label, set range, setup event-handler, load values */ ParameterType.Int -> { slider { label = parameter.label range = Range(parameter.intRange!!.first.toDouble(), parameter.intRange!!.last.toDouble()) precision = 0 - events.valueChanged.subscribe { + { setAndPersist(compartment.label, parameter.property as KMutableProperty1, obj, it.newValue.toInt()) (parameter.property as KMutableProperty1).set(obj, value.toInt()) onChangeListener?.invoke(parameter.property!!.name, it.newValue) @@ -343,7 +349,7 @@ class GUI : Extension { label = parameter.label range = Range(parameter.doubleRange!!.start, parameter.doubleRange!!.endInclusive) precision = parameter.precision!! - events.valueChanged.subscribe { + { setAndPersist(compartment.label, parameter.property as KMutableProperty1, obj, it.newValue) onChangeListener?.invoke(parameter.property!!.name, it.newValue) } @@ -358,7 +364,7 @@ class GUI : Extension { ParameterType.Action -> { button { label = parameter.label - events.clicked.subscribe { + { /* the `obj` we pass in here is the receiver */ parameter.function!!.call(obj) onChangeListener?.invoke(parameter.function!!.name, null) @@ -368,7 +374,7 @@ class GUI : Extension { ParameterType.Boolean -> { toggle { label = parameter.label - events.valueChanged.subscribe { + { value = it.newValue setAndPersist(compartment.label, parameter.property as KMutableProperty1, obj, it.newValue) onChangeListener?.invoke(parameter.property!!.name, it.newValue) @@ -382,7 +388,7 @@ class GUI : Extension { ParameterType.Text -> { textfield { label = parameter.label - events.valueChanged.subscribe { + { setAndPersist(compartment.label, parameter.property as KMutableProperty1, obj, it.newValue) onChangeListener?.invoke(parameter.property!!.name, it.newValue) } @@ -394,7 +400,7 @@ class GUI : Extension { ParameterType.Color -> { colorpickerButton { label = parameter.label - events.valueChanged.subscribe { + { setAndPersist( compartment.label, parameter.property as KMutableProperty1, @@ -423,7 +429,7 @@ class GUI : Extension { showVector = parameter.showVector!! invertY = parameter.invertY!! - events.valueChanged.subscribe { + { setAndPersist( compartment.label, parameter.property as KMutableProperty1, @@ -434,8 +440,37 @@ class GUI : Extension { } } } + + ParameterType.DoubleList -> { + sequenceEditor { + range = parameter.doubleRange!! + label = parameter.label + minimumSequenceLength = parameter.sizeRange!!.start + maximumSequenceLength = parameter.sizeRange!!.endInclusive + precision = parameter.precision!! + + events.valueChanged.listen { + setAndPersist( + compartment.label, + parameter.property as KMutableProperty1>, + obj, + it.newValue.toMutableList() + ) + onChangeListener?.invoke(parameter.property!!.name, it.newValue) + } + getPersistedOrDefault( + compartment.label, + parameter.property as KMutableProperty1>, + obj + )?.let { + value = it + } + } + } + } } + // private val trackedObjects = mutableMapOf() @@ -452,6 +487,7 @@ class GUI : Extension { var booleanValue: Boolean? = null, var colorValue: ColorRGBa? = null, var vectorValue: Vector2? = null, + var doubleListValue: MutableList? = null, var textValue: String? = null) @@ -472,6 +508,7 @@ class GUI : Extension { ParameterType.Text -> ParameterValue(textValue = k.property.qget(lo.obj) as String) ParameterType.Boolean -> ParameterValue(booleanValue = k.property.qget(lo.obj) as Boolean) ParameterType.XY -> ParameterValue(vectorValue = k.property.qget(lo.obj) as Vector2) + ParameterType.DoubleList -> ParameterValue(doubleListValue = k.property.qget(lo.obj) as MutableList) }) }) } @@ -509,6 +546,9 @@ class GUI : Extension { ParameterType.XY -> parameterValue.vectorValue?.let { parameter.property.qset(lo.obj, it) } + ParameterType.DoubleList -> parameterValue.doubleListValue?.let { + parameter.property.qset(lo.obj, it) + } ParameterType.Boolean -> parameterValue.booleanValue?.let { parameter.property.qset(lo.obj, it) } @@ -538,11 +578,12 @@ class GUI : Extension { ParameterType.Color -> { (control as ColorpickerButton).color = (parameter.property as KMutableProperty1).get(labeledObject.obj) } - ParameterType.XY -> { (control as XYPad).value = (parameter.property as KMutableProperty1).get(labeledObject.obj) } - + ParameterType.DoubleList -> { + (control as SequenceEditor).value = (parameter.property as KMutableProperty1>).get(labeledObject.obj) + } ParameterType.Boolean -> { (control as Toggle).value = (parameter.property as KMutableProperty1).get(labeledObject.obj) } diff --git a/orx-panel/src/main/kotlin/org/openrndr/panel/elements/SequenceEditor.kt b/orx-panel/src/main/kotlin/org/openrndr/panel/elements/SequenceEditor.kt index db998aaf..6711e8ea 100644 --- a/orx-panel/src/main/kotlin/org/openrndr/panel/elements/SequenceEditor.kt +++ b/orx-panel/src/main/kotlin/org/openrndr/panel/elements/SequenceEditor.kt @@ -5,9 +5,17 @@ import org.openrndr.KeyModifier import org.openrndr.color.ColorRGBa import org.openrndr.draw.Drawer import org.openrndr.draw.LineCap +import org.openrndr.draw.isolated import org.openrndr.events.Event import org.openrndr.math.Vector2 +import org.openrndr.math.map +import org.openrndr.panel.style.Color +import org.openrndr.panel.style.color +import org.openrndr.panel.style.effectiveColor import org.openrndr.panel.tools.Tooltip +import org.openrndr.shape.Rectangle +import org.openrndr.text.Cursor +import org.openrndr.text.Writer import kotlin.math.abs import kotlin.math.round import kotlin.math.roundToInt @@ -15,13 +23,17 @@ import kotlin.math.roundToInt class SequenceEditor : Element(ElementType("sequence-editor")) { var value = mutableListOf(0.0) + var label = "sequence" var precision = 2 var maximumSequenceLength = 16 var minimumSequenceLength = 1 + var range: ClosedRange = -1.0..1.0 private var selectedIndex: Int? = null private var tooltip: Tooltip? = null + private val footerHeight = 20.0 + class ValueChangedEvent(val source: SequenceEditor, val oldValue: List, val newValue: List) @@ -35,7 +47,7 @@ class SequenceEditor : Element(ElementType("sequence-editor")) { init { fun query(position: Vector2): Vector2 { val x = (position.x - layout.screenX) / layout.screenWidth - val y = 1.0 - ((position.y - layout.screenY) / (layout.screenHeight * 0.5)) + val y = 1.0 - ((position.y - layout.screenY) / ((layout.screenHeight - footerHeight) * 0.5)) return Vector2(x, y) } @@ -68,7 +80,7 @@ class SequenceEditor : Element(ElementType("sequence-editor")) { if (value.size < maximumSequenceLength) { val q = query(it.position) val oldValue = value.map { it } - value.add(index.toInt(), q.y) + value.add(index.toInt(), q.y.map(-1.0, 1.0, range.start, range.endInclusive)) events.valueChanged.trigger(ValueChangedEvent(this, oldValue, value)) } } @@ -108,7 +120,7 @@ class SequenceEditor : Element(ElementType("sequence-editor")) { val readIndex = index.roundToInt() - 1 if (readIndex >= 0 && readIndex < value.size) { val value = String.format("%.0${precision}f", value[readIndex]) - tooltip = Tooltip(this@SequenceEditor, it.position - Vector2(layout.screenX, layout.screenY), "index: ${index.roundToInt()}, $value") + tooltip = Tooltip(this@SequenceEditor, it.position - Vector2(layout.screenX, layout.screenY), "$value") requestRedraw() } } @@ -121,7 +133,7 @@ class SequenceEditor : Element(ElementType("sequence-editor")) { val writeIndex = index - 1 if (writeIndex >= 0 && writeIndex < value.size) { val oldValue = value.map { it } - value[writeIndex] = q.y.coerceIn(-1.0, 1.0) + value[writeIndex] = q.y.coerceIn(-1.0, 1.0).map(-1.0, 1.0, range.start, range.endInclusive) events.valueChanged.trigger(ValueChangedEvent(this, oldValue, value)) } requestRedraw() @@ -130,21 +142,47 @@ class SequenceEditor : Element(ElementType("sequence-editor")) { } override fun draw(drawer: Drawer) { - drawer.stroke = (ColorRGBa.BLACK.opacify(0.25)) - drawer.strokeWeight = (1.0) - drawer.lineSegment(0.0, layout.screenHeight / 2.0, layout.screenWidth, layout.screenHeight / 2.0) + val controlArea = Rectangle(0.0, 0.0, layout.screenWidth, layout.screenHeight - footerHeight) + + drawer.stroke = computedStyle.effectiveColor?.opacify(0.25) + drawer.strokeWeight = (1.0) + + + val zeroHeight = 0.0.map(range.start, range.endInclusive, -1.0, 1.0).coerceIn(-1.0, 1.0) * controlArea.height / -2.0 + drawer.lineSegment(0.0, controlArea.height / 2.0 + zeroHeight, layout.screenWidth, controlArea.height / 2.0 + zeroHeight) + + drawer.strokeWeight = 7.0 + drawer.fill = computedStyle.effectiveColor - drawer.strokeWeight = 1.0 - drawer.stroke = ColorRGBa.WHITE for (i in value.indices) { val dx = layout.screenWidth / (value.size + 1) - val height = -value[i] * layout.screenHeight / 2.0 + val height = -value[i].map(range.start, range.endInclusive, -1.0, 1.0).coerceIn(-1.0, 1.0) * controlArea.height / 2.0 val x = dx * (i + 1) drawer.lineCap = LineCap.ROUND - drawer.lineSegment(x, layout.screenHeight / 2.0, x, layout.screenHeight / 2.0 + height) - drawer.circle(x, layout.screenHeight / 2.0 + height, 5.0) + drawer.stroke = computedStyle.effectiveColor + drawer.lineSegment(x, controlArea.height / 2.0 + zeroHeight, x, controlArea.height / 2.0 + height) + + drawer.stroke = computedStyle.effectiveColor?.shade(1.1) + drawer.fill = ColorRGBa.PINK + drawer.circle(x, controlArea.height / 2.0 + height, 7.0) } + + drawer.isolated { + drawer.translate(0.0, controlArea.height) + drawer.fill = computedStyle.effectiveColor + (root() as? Body)?.controlManager?.fontManager?.let { + val font = it.font(computedStyle) + val writer = Writer(drawer) + drawer.fontMap = (font) + drawer.fill = computedStyle.effectiveColor + writer.cursor = Cursor(0.0, 4.0) + writer.box = Rectangle(0.0, 4.0, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY) + writer.newLine() + writer.text(label) + } + } + tooltip?.draw(drawer) } } diff --git a/orx-panel/src/main/kotlin/org/openrndr/panel/style/DefaultStyles.kt b/orx-panel/src/main/kotlin/org/openrndr/panel/style/DefaultStyles.kt index 629b5ec1..b7ea7f7b 100644 --- a/orx-panel/src/main/kotlin/org/openrndr/panel/style/DefaultStyles.kt +++ b/orx-panel/src/main/kotlin/org/openrndr/panel/style/DefaultStyles.kt @@ -105,6 +105,10 @@ fun defaultStyles( marginBottom = 15.px marginLeft = 5.px marginRight = 5.px + color = controlTextColor + and(has state "active") { + color = controlActiveColor + } }, styleSheet(has type "colorpicker") { @@ -116,6 +120,8 @@ fun defaultStyles( marginRight = 5.px }, + + styleSheet(has type "xy-pad") { display = Display.BLOCK background = Color.RGBa(ColorRGBa.GRAY) diff --git a/orx-panel/src/main/kotlin/org/openrndr/panel/tools/Tooltip.kt b/orx-panel/src/main/kotlin/org/openrndr/panel/tools/Tooltip.kt index 9c5ec6b9..d33b6b3c 100644 --- a/orx-panel/src/main/kotlin/org/openrndr/panel/tools/Tooltip.kt +++ b/orx-panel/src/main/kotlin/org/openrndr/panel/tools/Tooltip.kt @@ -37,7 +37,8 @@ class Tooltip(val parent: Element, val position: Vector2, val message: String) { drawer.translate(position) drawer.translate(10.0, 0.0) - drawer.stroke = null + drawer.strokeWeight = 0.5 + drawer.stroke = ColorRGBa.WHITE.opacify(0.25) drawer.fill = ColorRGBa.GRAY drawer.rectangle(0.0, 0.0, maxX + 20.0, maxY) drawer.fill = ColorRGBa.BLACK