owenclave / app/src/main/java/io/nekohasekai/sagernet/ui/compose/components/ExpressiveSegmentedButton.kt

github - dev - back to tree - upstream
package io.nekohasekai.sagernet.ui.compose.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.ButtonGroupDefaults import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.ToggleButton import androidx.compose.material3.ToggleButtonDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp /** * A genuine M3E segmented selector built on [ToggleButton] with connected * button-group shapes (leading/middle/trailing). Selecting an item morphs its * corners — the signature expressive connected-group interaction. */ @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun ExpressiveSegmentedButton( items: List, selected: T, onSelect: (T) -> Unit, label: (T) -> String, modifier: Modifier = Modifier, icon: ((T) -> ImageVector?)? = null, ) { Row( modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(ButtonGroupDefaults.ConnectedSpaceBetween), ) { items.forEachIndexed { index, item -> ToggleButton( checked = selected == item, onCheckedChange = { onSelect(item) }, modifier = Modifier.weight(1f), shapes = when (index) { 0 -> ButtonGroupDefaults.connectedLeadingButtonShapes() items.lastIndex -> ButtonGroupDefaults.connectedTrailingButtonShapes() else -> ButtonGroupDefaults.connectedMiddleButtonShapes() }, ) { val ic = icon?.invoke(item) if (ic != null) { Icon( ic, contentDescription = null, modifier = Modifier.padding(end = ToggleButtonDefaults.IconSpacing), ) } Text( label(item), maxLines = 1, overflow = TextOverflow.Ellipsis, style = MaterialTheme.typography.labelLarge, ) } } } }