All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.RootElement.kt Maven / Gradle / Ivy

There is a newer version: 0.19.1
Show newest version
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