commonMain.io.github.lyxnx.compose.pine.Checkbox.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pine Show documentation
Show all versions of pine Show documentation
Jetpack Compose Pine Theme
package io.github.lyxnx.compose.pine
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.snap
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.selection.triStateToggleable
import androidx.compose.material3.Icon
import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.state.ToggleableState
import androidx.compose.ui.unit.dp
import compose.icons.TablerIcons
import compose.icons.tablericons.Outline
import compose.icons.tablericons.outline.Check
import compose.icons.tablericons.outline.Minus
import io.github.lyxnx.compose.ui.dropShadow
import io.github.lyxnx.compose.ui.ifTrue
/**
* A Pine Theme dual state checkbox (on/off)
*
* @param checked whether this checkbox is checked
* @param onCheckChanged action to perform when clicking - this is passed the inverse of the [checked] parameter
* @param modifier the modifier to be applied to the checkbox
* @param enabled whether the checkbox is enabled
* @param colors any custom colors to be used for the checkbox
* @param interactionSource interaction source used to dispatch interaction events to
*/
@Composable
public fun Checkbox(
checked: Boolean,
onCheckChanged: ((Boolean) -> Unit)?,
modifier: Modifier = Modifier,
enabled: Boolean = true,
colors: CheckboxColors = CheckboxDefaults.checkboxColors(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
) {
val state = remember(checked) {
if (checked) ToggleableState.On else ToggleableState.Off
}
TriStateCheckbox(
state = state,
onClick = if (onCheckChanged != null) {
{
onCheckChanged(!checked)
}
} else null,
modifier = modifier,
enabled = enabled,
colors = colors,
interactionSource = interactionSource
)
}
/**
* A Pine Theme tri state checkbox
*
* @param state the checkbox state
* @param onClick action to perform when clicking
* @param modifier the modifier to be applied to the checkbox
* @param enabled whether the checkbox is enabled
* @param colors any custom colors to be used for the checkbox
* @param interactionSource interaction source used to dispatch interaction events to
*/
@Composable
public fun TriStateCheckbox(
state: ToggleableState,
onClick: (() -> Unit)?,
modifier: Modifier = Modifier,
enabled: Boolean = true,
colors: CheckboxColors = CheckboxDefaults.triStateCheckboxColors(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
) {
val isFocused by interactionSource.collectIsFocusedAsState()
val backgroundColor by colors.backgroundColor(interactionSource, enabled, state)
val borderColor by colors.borderColor(interactionSource, enabled, state)
val indicatorColor by colors.indicatorColor(interactionSource, enabled, state)
val focusShadowColor by colors.shadowColor(state)
val transition = updateTransition(state)
val checkDrawFraction = transition.animateFloat(
transitionSpec = {
when {
initialState == ToggleableState.Off -> tween(100)
targetState == ToggleableState.Off -> snap(100)
else -> spring()
}
}
) {
when (it) {
ToggleableState.On -> 0.75f
ToggleableState.Off -> 0f
ToggleableState.Indeterminate -> 0.75f
}
}
Box(
modifier
.ifTrue(onClick != null) {
Modifier.minimumInteractiveComponentSize()
}
.ifTrue(isFocused) {
dropShadow(
color = focusShadowColor,
shape = PineTheme.shapes.small,
spread = 2.dp
)
}
.clip(PineTheme.shapes.small)
.ifTrue(onClick != null) {
triStateToggleable(
state = state,
onClick = onClick!!,
enabled = enabled,
role = Role.Checkbox,
interactionSource = interactionSource,
indication = LocalIndication.current
)
}
.border(1.dp, borderColor, PineTheme.shapes.small)
.background(backgroundColor)
.requiredSize(24.dp),
contentAlignment = Alignment.Center
) {
if (state == ToggleableState.On || state == ToggleableState.Indeterminate) {
Icon(
painter = rememberVectorPainter(if (state == ToggleableState.On) TablerIcons.Outline.Check else TablerIcons.Outline.Minus),
contentDescription = null,
tint = indicatorColor,
modifier = Modifier.fillMaxSize(checkDrawFraction.value)
)
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy