commonMain.RootElement.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of element Show documentation
Show all versions of element Show documentation
A collection of drawing/charting utilities
package com.juul.krayon.element
import com.juul.krayon.kanvas.IsPointInPath
import com.juul.krayon.kanvas.Kanvas
import com.juul.krayon.kanvas.Transform
import com.juul.krayon.kanvas.Transform.Translate
public class RootElement : Element() {
override val tag: String get() = "root"
/**
* If set, this callback is invoked when [onClick] is called but no descendant element handles
* that event.
*
* An example of when this is useful would be implementing a deselection behavior, where clicking
* on an element selects that element and clicking anywhere else on the chart deselects it.
*/
public var onClickFallback: (() -> Unit)? by attributes.withDefault { null }
override fun draw(canvas: Kanvas) {
children.forEach { it.draw(canvas) }
}
/** Returns true if an element was found and clicked on. Returns false if no element matched. */
public fun onClick(isPointInPath: IsPointInPath, x: Float, y: Float): Boolean {
// Union types would be pretty nice here. Value is of type T where T: Element and T: Interactable
val interactable = visibilityOrderedDescendants()
.filterIsInstance>()
.filter { it.onClick != null }
.firstOrNull { interactable ->
val transform = (interactable as Element).totalTransform()
isPointInPath.isPointInPath(transform, interactable.getInteractionPath(), x, y)
}
val fallback = onClickFallback
return when {
interactable != null -> {
@Suppress("UNCHECKED_CAST") // Interactable always accepts itself as the type argument.
val onClick = interactable.onClick as (Element) -> Unit
onClick(interactable as Element)
true
}
fallback != null -> {
fallback()
true
}
else -> {
false
}
}
}
public companion object : ElementSelector {
override fun trySelect(element: Element): RootElement? = element as? RootElement
}
}
private fun Element.totalTransform(): Transform {
var element: Element? = this
var transform: Transform = Translate() // Start with a no-op/identity transform
while (element != null) {
if (element is TransformElement) {
transform = Transform.InOrder(element.transform, transform)
}
element = element.parent
}
return transform
}
private fun Element.visibilityOrderedDescendants(): Sequence = sequence {
for (child in children.asReversed()) {
yieldAll(child.visibilityOrderedDescendants())
}
yield(this@visibilityOrderedDescendants)
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy