Add tab and shift-tab switching support to oxr-panel
This commit is contained in:
@@ -6,6 +6,7 @@ import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.*
|
||||
import org.openrndr.math.Matrix44
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.math.mod_
|
||||
import org.openrndr.panel.elements.*
|
||||
import org.openrndr.panel.layout.Layouter
|
||||
import org.openrndr.panel.style.*
|
||||
@@ -95,7 +96,11 @@ class ControlManager : Extension {
|
||||
|
||||
val dropInput = DropInput()
|
||||
|
||||
|
||||
|
||||
|
||||
inner class KeyboardInput {
|
||||
private var lastTarget: Element? = null
|
||||
var target: Element? = null
|
||||
set(value) {
|
||||
if (value != field) {
|
||||
@@ -104,6 +109,9 @@ class ControlManager : Extension {
|
||||
value?.keyboard?.focusGained?.trigger(FocusEvent())
|
||||
field = value
|
||||
field?.pseudoClasses?.add(ElementPseudoClass("active"))
|
||||
value?.let {
|
||||
lastTarget = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +126,26 @@ class ControlManager : Extension {
|
||||
}
|
||||
checkForManualRedraw()
|
||||
}
|
||||
|
||||
if (!event.propagationCancelled) {
|
||||
if (event.key == KEY_TAB) {
|
||||
val focusableControls = body?.findAllVisible { it.handlesKeyboardFocus } ?: emptyList()
|
||||
|
||||
val index = target?.let { focusableControls.indexOf(it) } ?: lastTarget?.let { focusableControls.indexOf(it) } ?: -1
|
||||
if (focusableControls.isNotEmpty()) {
|
||||
|
||||
target = if (target != null) {
|
||||
if (KeyModifier.SHIFT in event.modifiers) {
|
||||
focusableControls[(index - 1).mod_(focusableControls.size)]
|
||||
} else {
|
||||
focusableControls[(index + 1).mod_(focusableControls.size)]
|
||||
}
|
||||
} else {
|
||||
lastTarget ?: focusableControls[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun release(event: KeyEvent) {
|
||||
@@ -210,7 +238,6 @@ class ControlManager : Extension {
|
||||
element.children.forEach { traverse(it, depth + 1) }
|
||||
}
|
||||
|
||||
|
||||
if (!event.propagationCancelled && event.position in element.screenArea && element.computedStyle.display != Display.NONE) {
|
||||
candidates.add(Pair(element, depth))
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import kotlin.math.round
|
||||
|
||||
class Button : Element(ElementType("button")) {
|
||||
|
||||
override val handlesKeyboardFocus = true
|
||||
var label: String = "OK"
|
||||
|
||||
class ButtonEvent(val source: Button)
|
||||
|
||||
@@ -10,7 +10,9 @@ import org.openrndr.math.Vector2
|
||||
import org.openrndr.panel.collections.ObservableCopyOnWriteArrayList
|
||||
import org.openrndr.panel.collections.ObservableHashSet
|
||||
import org.openrndr.panel.style.CompoundSelector
|
||||
import org.openrndr.panel.style.Display
|
||||
import org.openrndr.panel.style.StyleSheet
|
||||
import org.openrndr.panel.style.display
|
||||
import org.openrndr.shape.Rectangle
|
||||
|
||||
import java.util.*
|
||||
@@ -27,6 +29,7 @@ open class Element(val type: ElementType) {
|
||||
|
||||
var scrollTop = 0.0
|
||||
open val handlesDoubleClick = false
|
||||
open val handlesKeyboardFocus = false
|
||||
|
||||
open val widthHint: Double?
|
||||
get() {
|
||||
@@ -223,9 +226,34 @@ open class Element(val type: ElementType) {
|
||||
else -> p.children[index + 1]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun findNext(premise: (Element) -> Boolean) : Element? {
|
||||
return parent?.let { p ->
|
||||
val index = p.children.indexOf(this)
|
||||
val siblingCount = p.children.size
|
||||
for (i in index + 1 until siblingCount) {
|
||||
if (premise(p.children[i])) {
|
||||
return p.children[i]
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
fun findPrevious(premise: (Element) -> Boolean) : Element? {
|
||||
return parent?.let { p ->
|
||||
val index = p.children.indexOf(this)
|
||||
for (i in index-1 downTo 0) {
|
||||
if (premise(p.children[i])) {
|
||||
return p.children[i]
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun move(steps: Int) {
|
||||
parent?.let { p ->
|
||||
if (steps != 0) {
|
||||
@@ -283,7 +311,34 @@ fun Element.enable() {
|
||||
|
||||
fun Element.isDisabled(): Boolean = disabled in pseudoClasses
|
||||
|
||||
fun Element.findAll(predicate: (Element) -> Boolean) : List<Element> {
|
||||
val results = mutableListOf<Element>()
|
||||
visit {
|
||||
if (predicate(this)) {
|
||||
results.add(this)
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
fun Element.findAllVisible(predicate: (Element) -> Boolean) : List<Element> {
|
||||
val results = mutableListOf<Element>()
|
||||
visitVisible {
|
||||
if (predicate(this)) {
|
||||
results.add(this)
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
fun Element.visit(function: Element.() -> Unit) {
|
||||
this.function()
|
||||
children.forEach { it.visit(function) }
|
||||
}
|
||||
|
||||
fun Element.visitVisible(function: Element.() -> Unit) {
|
||||
if (this.computedStyle.display != Display.NONE) {
|
||||
this.function()
|
||||
children.forEach { it.visitVisible(function) }
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,9 @@ data class Range(val min: Double, val max: Double) {
|
||||
}
|
||||
|
||||
class Slider : Element(ElementType("slider")) {
|
||||
|
||||
override val handlesKeyboardFocus = true
|
||||
|
||||
var label = ""
|
||||
var precision = 3
|
||||
var value: Double
|
||||
|
||||
Reference in New Issue
Block a user