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.draw.*
|
||||||
import org.openrndr.math.Matrix44
|
import org.openrndr.math.Matrix44
|
||||||
import org.openrndr.math.Vector2
|
import org.openrndr.math.Vector2
|
||||||
|
import org.openrndr.math.mod_
|
||||||
import org.openrndr.panel.elements.*
|
import org.openrndr.panel.elements.*
|
||||||
import org.openrndr.panel.layout.Layouter
|
import org.openrndr.panel.layout.Layouter
|
||||||
import org.openrndr.panel.style.*
|
import org.openrndr.panel.style.*
|
||||||
@@ -95,7 +96,11 @@ class ControlManager : Extension {
|
|||||||
|
|
||||||
val dropInput = DropInput()
|
val dropInput = DropInput()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
inner class KeyboardInput {
|
inner class KeyboardInput {
|
||||||
|
private var lastTarget: Element? = null
|
||||||
var target: Element? = null
|
var target: Element? = null
|
||||||
set(value) {
|
set(value) {
|
||||||
if (value != field) {
|
if (value != field) {
|
||||||
@@ -104,6 +109,9 @@ class ControlManager : Extension {
|
|||||||
value?.keyboard?.focusGained?.trigger(FocusEvent())
|
value?.keyboard?.focusGained?.trigger(FocusEvent())
|
||||||
field = value
|
field = value
|
||||||
field?.pseudoClasses?.add(ElementPseudoClass("active"))
|
field?.pseudoClasses?.add(ElementPseudoClass("active"))
|
||||||
|
value?.let {
|
||||||
|
lastTarget = it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,6 +126,26 @@ class ControlManager : Extension {
|
|||||||
}
|
}
|
||||||
checkForManualRedraw()
|
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) {
|
fun release(event: KeyEvent) {
|
||||||
@@ -210,7 +238,6 @@ class ControlManager : Extension {
|
|||||||
element.children.forEach { traverse(it, depth + 1) }
|
element.children.forEach { traverse(it, depth + 1) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!event.propagationCancelled && event.position in element.screenArea && element.computedStyle.display != Display.NONE) {
|
if (!event.propagationCancelled && event.position in element.screenArea && element.computedStyle.display != Display.NONE) {
|
||||||
candidates.add(Pair(element, depth))
|
candidates.add(Pair(element, depth))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import kotlin.math.round
|
|||||||
|
|
||||||
class Button : Element(ElementType("button")) {
|
class Button : Element(ElementType("button")) {
|
||||||
|
|
||||||
|
override val handlesKeyboardFocus = true
|
||||||
var label: String = "OK"
|
var label: String = "OK"
|
||||||
|
|
||||||
class ButtonEvent(val source: Button)
|
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.ObservableCopyOnWriteArrayList
|
||||||
import org.openrndr.panel.collections.ObservableHashSet
|
import org.openrndr.panel.collections.ObservableHashSet
|
||||||
import org.openrndr.panel.style.CompoundSelector
|
import org.openrndr.panel.style.CompoundSelector
|
||||||
|
import org.openrndr.panel.style.Display
|
||||||
import org.openrndr.panel.style.StyleSheet
|
import org.openrndr.panel.style.StyleSheet
|
||||||
|
import org.openrndr.panel.style.display
|
||||||
import org.openrndr.shape.Rectangle
|
import org.openrndr.shape.Rectangle
|
||||||
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -27,6 +29,7 @@ open class Element(val type: ElementType) {
|
|||||||
|
|
||||||
var scrollTop = 0.0
|
var scrollTop = 0.0
|
||||||
open val handlesDoubleClick = false
|
open val handlesDoubleClick = false
|
||||||
|
open val handlesKeyboardFocus = false
|
||||||
|
|
||||||
open val widthHint: Double?
|
open val widthHint: Double?
|
||||||
get() {
|
get() {
|
||||||
@@ -223,9 +226,34 @@ open class Element(val type: ElementType) {
|
|||||||
else -> p.children[index + 1]
|
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) {
|
fun move(steps: Int) {
|
||||||
parent?.let { p ->
|
parent?.let { p ->
|
||||||
if (steps != 0) {
|
if (steps != 0) {
|
||||||
@@ -283,7 +311,34 @@ fun Element.enable() {
|
|||||||
|
|
||||||
fun Element.isDisabled(): Boolean = disabled in pseudoClasses
|
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) {
|
fun Element.visit(function: Element.() -> Unit) {
|
||||||
this.function()
|
this.function()
|
||||||
children.forEach { it.visit(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")) {
|
class Slider : Element(ElementType("slider")) {
|
||||||
|
|
||||||
|
override val handlesKeyboardFocus = true
|
||||||
|
|
||||||
var label = ""
|
var label = ""
|
||||||
var precision = 3
|
var precision = 3
|
||||||
var value: Double
|
var value: Double
|
||||||
|
|||||||
Reference in New Issue
Block a user