Add tab and shift-tab switching support to oxr-panel

This commit is contained in:
Edwin Jakobs
2020-04-04 19:42:40 +02:00
parent c292a63ca4
commit 185c02176f
4 changed files with 88 additions and 2 deletions

View File

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

View File

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

View File

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

View File

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