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.overflow = Overflow.Scroll
/* 1) setup control style */
//<editor-fold desc="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
}
//</editor-fold>
}
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 */
//<editor-fold desc="2) Control creation">
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<Any, Int>, obj, it.newValue.toInt())
(parameter.property as KMutableProperty1<Any, Int>).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<Any, Double>, 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<Any, Boolean>, 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<Any, String>, 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<Any, ColorRGBa>,
@@ -423,7 +429,7 @@ class GUI : Extension {
showVector = parameter.showVector!!
invertY = parameter.invertY!!
events.valueChanged.subscribe {
{
setAndPersist(
compartment.label,
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>()
@@ -452,6 +487,7 @@ class GUI : Extension {
var booleanValue: Boolean? = null,
var colorValue: ColorRGBa? = null,
var vectorValue: Vector2? = null,
var doubleListValue: MutableList<Double>? = 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<Double>)
})
})
}
@@ -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<Any, ColorRGBa>).get(labeledObject.obj)
}
ParameterType.XY -> {
(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 -> {
(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.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<Double> = -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<Double>,
val newValue: List<Double>)
@@ -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)
}
}

View File

@@ -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)

View File

@@ -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