Add sequence editor control and DoubleListParameter

This commit is contained in:
Edwin Jakobs
2020-03-26 19:36:14 +01:00
parent 8d810226da
commit d88f258f9c
4 changed files with 114 additions and 28 deletions

View File

@@ -155,8 +155,7 @@ class GUI : Extension {
this.background = Color.RGBa(ColorRGBa.GRAY.copy(a = 0.99)) this.background = Color.RGBa(ColorRGBa.GRAY.copy(a = 0.99))
this.overflow = Overflow.Scroll this.overflow = Overflow.Scroll
//<editor-fold desc="1) setup control style">
/* 1) setup control style */
descendant(has type "colorpicker-button") { descendant(has type "colorpicker-button") {
this.width = 175.px this.width = 175.px
} }
@@ -181,6 +180,12 @@ class GUI : Extension {
this.width = 175.px this.width = 175.px
this.height = 175.px this.height = 175.px
} }
descendant(has type "sequence-editor") {
this.width = 175.px
this.height = 100.px
}
//</editor-fold>
} }
styleSheet(has class_ "randomize-strong") { styleSheet(has class_ "randomize-strong") {
@@ -254,7 +259,7 @@ class GUI : Extension {
header.mouse.pressed.subscribe { header.mouse.pressed.subscribe {
it.cancelPropagation() it.cancelPropagation()
} }
header.mouse.clicked.subscribe { {
if (KeyModifier.CTRL in it.modifiers) { if (KeyModifier.CTRL in it.modifiers) {
collapsible.classes.remove(collapseClass) collapsible.classes.remove(collapseClass)
@@ -279,7 +284,7 @@ class GUI : Extension {
} }
} }
} }
collapseBorder.mouse.pressed.subscribe { {
it.cancelPropagation() it.cancelPropagation()
} }
@@ -294,7 +299,7 @@ class GUI : Extension {
} }
it.cancelPropagation() it.cancelPropagation()
} }
sidebar.mouse.scrolled.subscribe { {
sidebarState().scrollTop = sidebar.scrollTop sidebarState().scrollTop = sidebar.scrollTop
} }
if (sidebarState().collapsed) { if (sidebarState().collapsed) {
@@ -316,18 +321,19 @@ class GUI : Extension {
program.extend(panel) program.extend(panel)
} }
/* 2) control creation. create control, set label, set range, setup event-handler, load values */
//<editor-fold desc="2) Control creation">
private fun Div.addControl(compartment: LabeledObject, parameter: Parameter): Element { private fun Div.addControl(compartment: LabeledObject, parameter: Parameter): Element {
val obj = compartment.obj val obj = compartment.obj
return when (parameter.parameterType) { return when (parameter.parameterType) {
/* 2) control creation. create control, set label, set range, setup event-handler, load values */
ParameterType.Int -> { ParameterType.Int -> {
slider { slider {
label = parameter.label label = parameter.label
range = Range(parameter.intRange!!.first.toDouble(), parameter.intRange!!.last.toDouble()) range = Range(parameter.intRange!!.first.toDouble(), parameter.intRange!!.last.toDouble())
precision = 0 precision = 0
events.valueChanged.subscribe { {
setAndPersist(compartment.label, parameter.property as KMutableProperty1<Any, Int>, obj, it.newValue.toInt()) setAndPersist(compartment.label, parameter.property as KMutableProperty1<Any, Int>, obj, it.newValue.toInt())
(parameter.property as KMutableProperty1<Any, Int>).set(obj, value.toInt()) (parameter.property as KMutableProperty1<Any, Int>).set(obj, value.toInt())
onChangeListener?.invoke(parameter.property!!.name, it.newValue) onChangeListener?.invoke(parameter.property!!.name, it.newValue)
@@ -343,7 +349,7 @@ class GUI : Extension {
label = parameter.label label = parameter.label
range = Range(parameter.doubleRange!!.start, parameter.doubleRange!!.endInclusive) range = Range(parameter.doubleRange!!.start, parameter.doubleRange!!.endInclusive)
precision = parameter.precision!! precision = parameter.precision!!
events.valueChanged.subscribe { {
setAndPersist(compartment.label, parameter.property as KMutableProperty1<Any, Double>, obj, it.newValue) setAndPersist(compartment.label, parameter.property as KMutableProperty1<Any, Double>, obj, it.newValue)
onChangeListener?.invoke(parameter.property!!.name, it.newValue) onChangeListener?.invoke(parameter.property!!.name, it.newValue)
} }
@@ -358,7 +364,7 @@ class GUI : Extension {
ParameterType.Action -> { ParameterType.Action -> {
button { button {
label = parameter.label label = parameter.label
events.clicked.subscribe { {
/* the `obj` we pass in here is the receiver */ /* the `obj` we pass in here is the receiver */
parameter.function!!.call(obj) parameter.function!!.call(obj)
onChangeListener?.invoke(parameter.function!!.name, null) onChangeListener?.invoke(parameter.function!!.name, null)
@@ -368,7 +374,7 @@ class GUI : Extension {
ParameterType.Boolean -> { ParameterType.Boolean -> {
toggle { toggle {
label = parameter.label label = parameter.label
events.valueChanged.subscribe { {
value = it.newValue value = it.newValue
setAndPersist(compartment.label, parameter.property as KMutableProperty1<Any, Boolean>, obj, it.newValue) setAndPersist(compartment.label, parameter.property as KMutableProperty1<Any, Boolean>, obj, it.newValue)
onChangeListener?.invoke(parameter.property!!.name, it.newValue) onChangeListener?.invoke(parameter.property!!.name, it.newValue)
@@ -382,7 +388,7 @@ class GUI : Extension {
ParameterType.Text -> { ParameterType.Text -> {
textfield { textfield {
label = parameter.label label = parameter.label
events.valueChanged.subscribe { {
setAndPersist(compartment.label, parameter.property as KMutableProperty1<Any, String>, obj, it.newValue) setAndPersist(compartment.label, parameter.property as KMutableProperty1<Any, String>, obj, it.newValue)
onChangeListener?.invoke(parameter.property!!.name, it.newValue) onChangeListener?.invoke(parameter.property!!.name, it.newValue)
} }
@@ -394,7 +400,7 @@ class GUI : Extension {
ParameterType.Color -> { ParameterType.Color -> {
colorpickerButton { colorpickerButton {
label = parameter.label label = parameter.label
events.valueChanged.subscribe { {
setAndPersist( setAndPersist(
compartment.label, compartment.label,
parameter.property as KMutableProperty1<Any, ColorRGBa>, parameter.property as KMutableProperty1<Any, ColorRGBa>,
@@ -423,7 +429,7 @@ class GUI : Extension {
showVector = parameter.showVector!! showVector = parameter.showVector!!
invertY = parameter.invertY!! invertY = parameter.invertY!!
events.valueChanged.subscribe { {
setAndPersist( setAndPersist(
compartment.label, compartment.label,
parameter.property as KMutableProperty1<Any, Vector2>, parameter.property as KMutableProperty1<Any, Vector2>,
@@ -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<Any, MutableList<Double>>,
obj,
it.newValue.toMutableList()
)
onChangeListener?.invoke(parameter.property!!.name, it.newValue)
}
getPersistedOrDefault(
compartment.label,
parameter.property as KMutableProperty1<Any, MutableList<Double>>,
obj
)?.let {
value = it
}
}
}
} }
} }
//</editor-fold>
private val trackedObjects = mutableMapOf<LabeledObject, TrackedObjectBinding>() private val trackedObjects = mutableMapOf<LabeledObject, TrackedObjectBinding>()
@@ -452,6 +487,7 @@ class GUI : Extension {
var booleanValue: Boolean? = null, var booleanValue: Boolean? = null,
var colorValue: ColorRGBa? = null, var colorValue: ColorRGBa? = null,
var vectorValue: Vector2? = null, var vectorValue: Vector2? = null,
var doubleListValue: MutableList<Double>? = null,
var textValue: String? = null) var textValue: String? = null)
@@ -472,6 +508,7 @@ class GUI : Extension {
ParameterType.Text -> ParameterValue(textValue = k.property.qget(lo.obj) as String) ParameterType.Text -> ParameterValue(textValue = k.property.qget(lo.obj) as String)
ParameterType.Boolean -> ParameterValue(booleanValue = k.property.qget(lo.obj) as Boolean) ParameterType.Boolean -> ParameterValue(booleanValue = k.property.qget(lo.obj) as Boolean)
ParameterType.XY -> ParameterValue(vectorValue = k.property.qget(lo.obj) as Vector2) ParameterType.XY -> ParameterValue(vectorValue = k.property.qget(lo.obj) as Vector2)
ParameterType.DoubleList -> ParameterValue(doubleListValue = k.property.qget(lo.obj) as MutableList<Double>)
}) })
}) })
} }
@@ -509,6 +546,9 @@ class GUI : Extension {
ParameterType.XY -> parameterValue.vectorValue?.let { ParameterType.XY -> parameterValue.vectorValue?.let {
parameter.property.qset(lo.obj, it) parameter.property.qset(lo.obj, it)
} }
ParameterType.DoubleList -> parameterValue.doubleListValue?.let {
parameter.property.qset(lo.obj, it)
}
ParameterType.Boolean -> parameterValue.booleanValue?.let { ParameterType.Boolean -> parameterValue.booleanValue?.let {
parameter.property.qset(lo.obj, it) parameter.property.qset(lo.obj, it)
} }
@@ -538,11 +578,12 @@ class GUI : Extension {
ParameterType.Color -> { ParameterType.Color -> {
(control as ColorpickerButton).color = (parameter.property as KMutableProperty1<Any, ColorRGBa>).get(labeledObject.obj) (control as ColorpickerButton).color = (parameter.property as KMutableProperty1<Any, ColorRGBa>).get(labeledObject.obj)
} }
ParameterType.XY -> { ParameterType.XY -> {
(control as XYPad).value = (parameter.property as KMutableProperty1<Any, Vector2>).get(labeledObject.obj) (control as XYPad).value = (parameter.property as KMutableProperty1<Any, Vector2>).get(labeledObject.obj)
} }
ParameterType.DoubleList -> {
(control as SequenceEditor).value = (parameter.property as KMutableProperty1<Any, MutableList<Double>>).get(labeledObject.obj)
}
ParameterType.Boolean -> { ParameterType.Boolean -> {
(control as Toggle).value = (parameter.property as KMutableProperty1<Any, Boolean>).get(labeledObject.obj) (control as Toggle).value = (parameter.property as KMutableProperty1<Any, Boolean>).get(labeledObject.obj)
} }

View File

@@ -5,9 +5,17 @@ import org.openrndr.KeyModifier
import org.openrndr.color.ColorRGBa import org.openrndr.color.ColorRGBa
import org.openrndr.draw.Drawer import org.openrndr.draw.Drawer
import org.openrndr.draw.LineCap import org.openrndr.draw.LineCap
import org.openrndr.draw.isolated
import org.openrndr.events.Event import org.openrndr.events.Event
import org.openrndr.math.Vector2 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.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.abs
import kotlin.math.round import kotlin.math.round
import kotlin.math.roundToInt import kotlin.math.roundToInt
@@ -15,13 +23,17 @@ import kotlin.math.roundToInt
class SequenceEditor : Element(ElementType("sequence-editor")) { class SequenceEditor : Element(ElementType("sequence-editor")) {
var value = mutableListOf(0.0) var value = mutableListOf(0.0)
var label = "sequence"
var precision = 2 var precision = 2
var maximumSequenceLength = 16 var maximumSequenceLength = 16
var minimumSequenceLength = 1 var minimumSequenceLength = 1
var range: ClosedRange<Double> = -1.0..1.0
private var selectedIndex: Int? = null private var selectedIndex: Int? = null
private var tooltip: Tooltip? = null private var tooltip: Tooltip? = null
private val footerHeight = 20.0
class ValueChangedEvent(val source: SequenceEditor, class ValueChangedEvent(val source: SequenceEditor,
val oldValue: List<Double>, val oldValue: List<Double>,
val newValue: List<Double>) val newValue: List<Double>)
@@ -35,7 +47,7 @@ class SequenceEditor : Element(ElementType("sequence-editor")) {
init { init {
fun query(position: Vector2): Vector2 { fun query(position: Vector2): Vector2 {
val x = (position.x - layout.screenX) / layout.screenWidth 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) return Vector2(x, y)
} }
@@ -68,7 +80,7 @@ class SequenceEditor : Element(ElementType("sequence-editor")) {
if (value.size < maximumSequenceLength) { if (value.size < maximumSequenceLength) {
val q = query(it.position) val q = query(it.position)
val oldValue = value.map { it } 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)) events.valueChanged.trigger(ValueChangedEvent(this, oldValue, value))
} }
} }
@@ -108,7 +120,7 @@ class SequenceEditor : Element(ElementType("sequence-editor")) {
val readIndex = index.roundToInt() - 1 val readIndex = index.roundToInt() - 1
if (readIndex >= 0 && readIndex < value.size) { if (readIndex >= 0 && readIndex < value.size) {
val value = String.format("%.0${precision}f", value[readIndex]) 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() requestRedraw()
} }
} }
@@ -121,7 +133,7 @@ class SequenceEditor : Element(ElementType("sequence-editor")) {
val writeIndex = index - 1 val writeIndex = index - 1
if (writeIndex >= 0 && writeIndex < value.size) { if (writeIndex >= 0 && writeIndex < value.size) {
val oldValue = value.map { it } 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)) events.valueChanged.trigger(ValueChangedEvent(this, oldValue, value))
} }
requestRedraw() requestRedraw()
@@ -130,21 +142,47 @@ class SequenceEditor : Element(ElementType("sequence-editor")) {
} }
override fun draw(drawer: Drawer) { override fun draw(drawer: Drawer) {
drawer.stroke = (ColorRGBa.BLACK.opacify(0.25)) val controlArea = Rectangle(0.0, 0.0, layout.screenWidth, layout.screenHeight - footerHeight)
drawer.strokeWeight = (1.0)
drawer.lineSegment(0.0, layout.screenHeight / 2.0, layout.screenWidth, layout.screenHeight / 2.0) 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) { for (i in value.indices) {
val dx = layout.screenWidth / (value.size + 1) 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) val x = dx * (i + 1)
drawer.lineCap = LineCap.ROUND drawer.lineCap = LineCap.ROUND
drawer.lineSegment(x, layout.screenHeight / 2.0, x, layout.screenHeight / 2.0 + height) drawer.stroke = computedStyle.effectiveColor
drawer.circle(x, layout.screenHeight / 2.0 + height, 5.0) 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) tooltip?.draw(drawer)
} }
} }

View File

@@ -105,6 +105,10 @@ fun defaultStyles(
marginBottom = 15.px marginBottom = 15.px
marginLeft = 5.px marginLeft = 5.px
marginRight = 5.px marginRight = 5.px
color = controlTextColor
and(has state "active") {
color = controlActiveColor
}
}, },
styleSheet(has type "colorpicker") { styleSheet(has type "colorpicker") {
@@ -116,6 +120,8 @@ fun defaultStyles(
marginRight = 5.px marginRight = 5.px
}, },
styleSheet(has type "xy-pad") { styleSheet(has type "xy-pad") {
display = Display.BLOCK display = Display.BLOCK
background = Color.RGBa(ColorRGBa.GRAY) background = Color.RGBa(ColorRGBa.GRAY)

View File

@@ -37,7 +37,8 @@ class Tooltip(val parent: Element, val position: Vector2, val message: String) {
drawer.translate(position) drawer.translate(position)
drawer.translate(10.0, 0.0) 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.fill = ColorRGBa.GRAY
drawer.rectangle(0.0, 0.0, maxX + 20.0, maxY) drawer.rectangle(0.0, 0.0, maxX + 20.0, maxY)
drawer.fill = ColorRGBa.BLACK drawer.fill = ColorRGBa.BLACK