net.peanuuutz.fork.ui.foundation.input.PointerIcon.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fork-ui Show documentation
Show all versions of fork-ui Show documentation
Comprehensive API designed for Minecraft modders
The newest version!
/*
* Copyright 2020 The Android Open Source Project
* Modifications Copyright 2022 Peanuuutz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.peanuuutz.fork.ui.foundation.input
import androidx.compose.runtime.Stable
import kotlinx.coroutines.isActive
import net.peanuuutz.fork.ui.ui.context.pointer.PointerEvent
import net.peanuuutz.fork.ui.ui.context.pointer.PointerIcon
import net.peanuuutz.fork.ui.ui.context.pointer.PointerService
import net.peanuuutz.fork.ui.ui.modifier.Modifier
import net.peanuuutz.fork.ui.ui.modifier.ModifierNodeElement
import net.peanuuutz.fork.ui.ui.modifier.input.SuspendingPointerInputModifierNode
import net.peanuuutz.fork.ui.ui.node.BranchingModifierNode
import net.peanuuutz.fork.ui.ui.node.PointerEventPass
import net.peanuuutz.fork.ui.ui.node.PointerEventPass.In
import net.peanuuutz.fork.ui.ui.node.PointerEventPass.Out
import net.peanuuutz.fork.ui.ui.node.PointerInputModifierNode
import net.peanuuutz.fork.ui.ui.node.requireOwner
@Stable
fun Modifier.pointerIconOnHover(
pointerIcon: PointerIcon,
overrideDescendants: Boolean = false,
isEnabled: Boolean = true
): Modifier {
if (!isEnabled) {
return this
}
val element = PointerIconModifier(
pointerIcon = pointerIcon,
overrideDescendants = overrideDescendants
)
return this then element
}
// ======== Internal ========
private data class PointerIconModifier(
val pointerIcon: PointerIcon,
val overrideDescendants: Boolean
) : ModifierNodeElement() {
override fun create(): PointerIconModifierNode {
return PointerIconModifierNode(
pointerIcon = pointerIcon,
overrideDescendants = overrideDescendants
)
}
override fun update(node: PointerIconModifierNode) {
node.pointerIcon = pointerIcon
node.overrideDescendants = overrideDescendants
}
}
private class PointerIconModifierNode(
var pointerIcon: PointerIcon,
var overrideDescendants: Boolean
) : BranchingModifierNode(), PointerInputModifierNode {
private val pointerService: PointerService?
get() = requireOwner().pointerService
private val pointerInputHandler: SuspendingPointerInputModifierNode = branch {
SuspendingPointerInputModifierNode {
receive {
var isInside = isHovered
while (listenerContext.isActive) {
val pass = if (!overrideDescendants) In else Out
val event = listen(pass)
val isInsideBefore = isInside
isInside = event.isCaptured
if (isInside && !isInsideBefore) {
pointerService?.pushIcon(pointerIcon)
isHovered = true
} else if (!isInside && isInsideBefore) {
pointerService?.popIcon()
isHovered = false
}
}
}
}
}
private var isHovered: Boolean = false
override fun onDetach() {
if (isHovered) {
isHovered = false
pointerService?.popIcon()
}
}
override fun onPointerEvent(pass: PointerEventPass, pointerEvent: PointerEvent) {
pointerInputHandler.onPointerEvent(pass, pointerEvent)
}
}