You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
81 lines
2.4 KiB
Kotlin
81 lines
2.4 KiB
Kotlin
package me.eater.threedom.dom
|
|
|
|
import me.eater.threedom.event.Event
|
|
import me.eater.threedom.generated.EventNames
|
|
|
|
class EventTree(block: EventTree.() -> Unit = {}) {
|
|
private val topLevel: MutableMap<String, MutableSet<(Event<*>) -> Unit>> =
|
|
mutableMapOf()
|
|
private val listeners: MutableMap<String, MutableMap<Long, MutableSet<(Event<*>) -> Unit>>> =
|
|
mutableMapOf()
|
|
private val nodes: MutableMap<Long, MutableSet<String>> = mutableMapOf()
|
|
|
|
init {
|
|
block(this)
|
|
}
|
|
|
|
fun addTopLevelEventListener(eventName: String, handler: (Event<*>) -> Unit) {
|
|
this.topLevel.getOrPut(eventName, ::mutableSetOf).add(handler)
|
|
}
|
|
|
|
fun addEventListener(eventName: String, target: INode<*>, handler: (Event<*>) -> Unit) {
|
|
this.listeners.getOrPut(eventName, ::mutableMapOf).getOrPut(target.nodeId, ::mutableSetOf).add(handler)
|
|
this.nodes.getOrPut(target.nodeId, ::mutableSetOf).add(eventName)
|
|
}
|
|
|
|
fun removeNode(node: INode<*>) {
|
|
val events = this.nodes.remove(node.nodeId) ?: return
|
|
for (event in events) {
|
|
this.listeners[event]?.remove(node.nodeId)
|
|
}
|
|
}
|
|
|
|
fun trigger(eventName: String, target: INode<*>, event: Event<*>) {
|
|
this.topLevel[eventName]?.let {
|
|
for (handler in it) {
|
|
handler(event)
|
|
|
|
if (!event.propagate) {
|
|
return
|
|
}
|
|
}
|
|
|
|
if (!event.bubble) {
|
|
return
|
|
}
|
|
}
|
|
|
|
val map = this.listeners[eventName] ?: return
|
|
var current: INode<*>? = target
|
|
|
|
while (current != null) {
|
|
val set = map[current.nodeId]
|
|
if (set != null) {
|
|
for (eventHandler in set) {
|
|
eventHandler(event)
|
|
|
|
if (!event.propagate) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!event.bubble) {
|
|
break
|
|
}
|
|
|
|
current = current.parentNode
|
|
}
|
|
}
|
|
|
|
|
|
@Suppress("UNCHECKED_CAST")
|
|
inline fun <reified T> on(target: INode<*>, noinline block: (Event<T>) -> Unit) =
|
|
addEventListener(EventNames.getEventName<T>(), target, block as (Event<*>) -> Unit)
|
|
|
|
@Suppress("UNCHECKED_CAST")
|
|
inline fun <reified T> on(noinline block: (Event<T>) -> Unit) =
|
|
addTopLevelEventListener(EventNames.getEventName<T>(), block as (Event<*>) -> Unit)
|
|
|
|
}
|