net.peanuuutz.fork.ui.foundation.input.VisualIndication.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.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember
import net.peanuuutz.fork.ui.foundation.input.interaction.InteractionSource
import net.peanuuutz.fork.ui.foundation.input.interaction.collectFocusState
import net.peanuuutz.fork.ui.foundation.input.interaction.collectHoverState
import net.peanuuutz.fork.ui.foundation.input.interaction.collectPressState
import net.peanuuutz.fork.ui.ui.draw.ContentDrawScope
import net.peanuuutz.fork.ui.ui.modifier.Modifier
import net.peanuuutz.fork.ui.ui.modifier.ModifierNodeElement
import net.peanuuutz.fork.ui.ui.modifier.composed
import net.peanuuutz.fork.ui.ui.node.DrawModifierNode
import net.peanuuutz.fork.ui.ui.node.ModifierNode
import net.peanuuutz.fork.util.common.Color
@Stable
fun Modifier.visualIndication(
interactionSource: InteractionSource,
indication: VisualIndication?,
label: Any? = null
): Modifier {
return composed {
val resolvedIndication = indication ?: VisualIndication.Blank
val instance = resolvedIndication.rememberVisualIndicationInstance(
interactionSource = interactionSource,
label = label
)
this then VisualIndicationModifier(instance)
}
}
@Stable
interface VisualIndication {
@Composable
fun rememberVisualIndicationInstance(
interactionSource: InteractionSource,
label: Any?
): VisualIndicationInstance
companion object {
@Stable
val Blank: VisualIndication = object : VisualIndication {
@Composable
override fun rememberVisualIndicationInstance(
interactionSource: InteractionSource,
label: Any?
): VisualIndicationInstance {
return VisualIndicationInstance.Blank
}
override fun toString(): String {
return "VisualIndication.Blank"
}
}
@Stable
fun overlay(
onPress: Color = Color.White.copy(alpha = 0.2f),
onHover: Color = Color.White.copy(alpha = 0.1f),
onFocus: Color = onHover
): VisualIndication {
return OverlayVisualIndication(
onPress = onPress,
onHover = onHover,
onFocus = onFocus
)
}
}
}
fun interface VisualIndicationInstance {
fun ContentDrawScope.drawIndication()
companion object {
val Blank: VisualIndicationInstance = object : VisualIndicationInstance {
override fun ContentDrawScope.drawIndication() {
drawContent()
}
override fun toString(): String {
return "VisualIndicationInstance.Blank"
}
}
}
}
val LocalVisualIndication = compositionLocalOf { null }
// ======== Internal ========
private data class OverlayVisualIndication(
val onPress: Color,
val onHover: Color,
val onFocus: Color
) : VisualIndication {
@Composable
override fun rememberVisualIndicationInstance(
interactionSource: InteractionSource,
label: Any?
): VisualIndicationInstance {
val pressState = interactionSource.collectPressState(label)
val hoverState = interactionSource.collectHoverState(label)
val focusState = interactionSource.collectFocusState(label)
return remember(interactionSource) {
Instance(
pressStateProvider = pressState::value,
hoverStateProvider = hoverState::value,
focusStateProvider = focusState::value
)
}
}
private inner class Instance(
val pressStateProvider: () -> Boolean,
val hoverStateProvider: () -> Boolean,
val focusStateProvider: () -> Boolean
) : VisualIndicationInstance {
override fun ContentDrawScope.drawIndication() {
drawContent()
val color = when {
pressStateProvider() -> onPress
hoverStateProvider() -> onHover
focusStateProvider() -> onFocus
else -> Color.Transparent
}
drawRect(color)
}
}
}
private data class VisualIndicationModifier(
val instance: VisualIndicationInstance
) : ModifierNodeElement() {
override fun create(): VisualIndicationModifierNode {
return VisualIndicationModifierNode(instance)
}
override fun update(node: VisualIndicationModifierNode) {
node.instance = instance
}
}
private class VisualIndicationModifierNode(
var instance: VisualIndicationInstance
) : ModifierNode(), DrawModifierNode {
override fun ContentDrawScope.draw() {
with(instance) {
drawIndication()
}
}
}