![JAR search and dependency download from the Maven repository](/logo.png)
net.peanuuutz.fork.ui.preset.Checkbox.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.preset
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.NonRestartableComposable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import kotlinx.coroutines.CoroutineScope
import net.peanuuutz.fork.ui.foundation.draw.BorderStroke
import net.peanuuutz.fork.ui.foundation.draw.background
import net.peanuuutz.fork.ui.foundation.draw.border
import net.peanuuutz.fork.ui.foundation.draw.paint
import net.peanuuutz.fork.ui.foundation.draw.painter.EmptyPainter
import net.peanuuutz.fork.ui.foundation.input.LocalVisualIndication
import net.peanuuutz.fork.ui.foundation.input.ToggleState
import net.peanuuutz.fork.ui.foundation.input.focusable
import net.peanuuutz.fork.ui.foundation.input.hoverable
import net.peanuuutz.fork.ui.foundation.input.interaction.MutableInteractionSource
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.toggleable
import net.peanuuutz.fork.ui.foundation.input.visualIndication
import net.peanuuutz.fork.ui.foundation.layout.Box
import net.peanuuutz.fork.ui.foundation.layout.PaddingValues
import net.peanuuutz.fork.ui.foundation.layout.minSize
import net.peanuuutz.fork.ui.foundation.layout.padding
import net.peanuuutz.fork.ui.preset.theme.Theme
import net.peanuuutz.fork.ui.ui.draw.ContentScale
import net.peanuuutz.fork.ui.ui.draw.Painter
import net.peanuuutz.fork.ui.ui.modifier.Modifier
import net.peanuuutz.fork.ui.ui.modifier.conditional
import net.peanuuutz.fork.ui.ui.unit.IntSize
import net.peanuuutz.fork.ui.ui.unit.isSpecified
@Composable
fun Checkbox(
isChecked: Boolean,
onValueChanged: (candidate: Boolean) -> Unit,
modifier: Modifier = Modifier,
isEnabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
checkboxStyle: CheckboxStyle = Theme.checkbox,
checkmarkPadding: PaddingValues = CheckboxDefaults.CheckmarkPadding,
checkmarkScale: ContentScale = ContentScale.Unchanged
) {
Checkbox(
state = ToggleState(isChecked),
onToggle = { onValueChanged(!isChecked) },
modifier = modifier,
isEnabled = isEnabled,
interactionSource = interactionSource,
checkboxStyle = checkboxStyle,
checkmarkPadding = checkmarkPadding,
checkmarkScale = checkmarkScale
)
}
@Composable
fun Checkbox(
state: ToggleState,
onToggle: CoroutineScope.() -> Unit,
modifier: Modifier = Modifier,
isEnabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
checkboxStyle: CheckboxStyle = Theme.checkbox,
checkmarkPadding: PaddingValues = CheckboxDefaults.CheckmarkPadding,
checkmarkScale: ContentScale = ContentScale.Unchanged
) {
val isHovered by interactionSource.collectHoverState()
val isFocused by interactionSource.collectFocusState()
val isSelected = isHovered || isFocused
val border by checkboxStyle.border(
isEnabled = isEnabled,
isSelected = isSelected,
state = state
)
val background by checkboxStyle.background(
isEnabled = isEnabled,
isSelected = isSelected,
state = state
)
val checkmark by checkboxStyle.checkmark(
isEnabled = isEnabled,
state = state
)
Box(
modifier = modifier
.minSize(CheckboxDefaults.MinSize)
.conditional(isEnabled) {
this
.toggleable(
interactionSource = interactionSource,
onToggle = onToggle
)
.hoverable(interactionSource)
.focusable(interactionSource)
.visualIndication(
interactionSource = interactionSource,
indication = LocalVisualIndication.current
)
}
.border(border)
.background(background)
.padding(checkmarkPadding)
.paint(
painter = checkmark,
contentScale = checkmarkScale
)
)
}
object CheckboxDefaults {
val MinSize: IntSize = IntSize(8, 8)
val DefaultSize: IntSize = IntSize(20, 20)
val CheckmarkPadding: PaddingValues = PaddingValues(1)
}
@Stable
interface CheckboxStyle {
@Composable
fun border(
isEnabled: Boolean,
isSelected: Boolean,
state: ToggleState
): State
@Composable
fun background(
isEnabled: Boolean,
isSelected: Boolean,
state: ToggleState
): State
@Composable
fun checkmark(
isEnabled: Boolean,
state: ToggleState
): State
@Stable
abstract class Delegated(
val delegate: CheckboxStyle
) : CheckboxStyle by delegate
}
val Theme.checkbox: CheckboxStyle
@ReadOnlyComposable
@Composable
get() = LocalCheckbox.current
@NonRestartableComposable
@Composable
fun CheckboxStyleProvider(
checkboxStyle: CheckboxStyle,
content: @Composable () -> Unit
) {
CompositionLocalProvider(
LocalCheckbox provides checkboxStyle,
content = content
)
}
@Stable
class DefaultCheckboxStyle(
val border: BorderStroke,
val background: Painter,
val checkedBorder: BorderStroke,
val checkedBackground: Painter,
val selectedBorder: BorderStroke,
val selectedBackground: Painter,
val selectedCheckedBorder: BorderStroke,
val selectedCheckedBackground: Painter,
val checkmark: Painter,
val disabledBorder: BorderStroke,
val disabledBackground: Painter
) : CheckboxStyle {
private val checkmarkPlaceholder: Painter = kotlin.run {
val checkmarkSize = checkmark.size
if (checkmarkSize.isSpecified()) {
EmptyPainter(checkmarkSize)
} else {
EmptyPainter.Placeholder
}
}
@Composable
override fun border(isEnabled: Boolean, isSelected: Boolean, state: ToggleState): State {
val borderStroke = when {
!isEnabled -> disabledBorder
!isSelected -> if (state == ToggleState.Off) border else checkedBorder
else -> if (state == ToggleState.Off) selectedBorder else selectedCheckedBorder
}
return rememberUpdatedState(borderStroke)
}
@Composable
override fun background(isEnabled: Boolean, isSelected: Boolean, state: ToggleState): State {
val painter = when {
!isEnabled -> disabledBackground
!isSelected -> if (state == ToggleState.Off) background else checkedBackground
else -> if (state == ToggleState.Off) selectedBackground else selectedCheckedBackground
}
return rememberUpdatedState(painter)
}
@Composable
override fun checkmark(isEnabled: Boolean, state: ToggleState): State {
val painter = if (state == ToggleState.Off) checkmarkPlaceholder else checkmark
return rememberUpdatedState(painter)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is DefaultCheckboxStyle) return false
if (border != other.border) return false
if (background != other.background) return false
if (checkedBorder != other.checkedBorder) return false
if (checkedBackground != other.checkedBackground) return false
if (selectedBorder != other.selectedBorder) return false
if (selectedBackground != other.selectedBackground) return false
if (selectedCheckedBorder != other.selectedCheckedBorder) return false
if (selectedCheckedBackground != other.selectedCheckedBackground) return false
if (checkmark != other.checkmark) return false
if (disabledBorder != other.disabledBorder) return false
if (disabledBackground != other.disabledBackground) return false
return true
}
override fun hashCode(): Int {
var result = border.hashCode()
result = 31 * result + background.hashCode()
result = 31 * result + checkedBorder.hashCode()
result = 31 * result + checkedBackground.hashCode()
result = 31 * result + selectedBorder.hashCode()
result = 31 * result + selectedBackground.hashCode()
result = 31 * result + selectedCheckedBorder.hashCode()
result = 31 * result + selectedCheckedBackground.hashCode()
result = 31 * result + checkmark.hashCode()
result = 31 * result + disabledBorder.hashCode()
result = 31 * result + disabledBackground.hashCode()
return result
}
override fun toString(): String {
return "DefaultCheckboxStyle(border=$border, background=$background, " +
"checkedBorder=$checkedBorder, checkedBackground=$checkedBackground, " +
"selectedBorder=$selectedBorder, selectedBackground=$selectedBackground, " +
"selectedCheckedBorder=$selectedCheckedBorder, selectedCheckedBackground=$selectedCheckedBackground, " +
"checkmark=$checkmark, " +
"disabledBorder=$disabledBorder, disabledBackground=$disabledBackground)"
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy