Optimise non-fixed length RollingNumber (#1751)
* Try Optimise non-fixed length RollingNumber * Apply RollingNumber to SliderPref * Revert Debug * RollingNumber handle negative value * Fix typo --------- Co-authored-by: Dude so hot <djohn@fbi.gov>
This commit is contained in:
1 parent
34de943c9f
commit
eac7048360
3 files changed
+62
-57
No files matched your search
@@ -8,16 +8,19 @@ import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.movableContentOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.takeOrElse
|
||||
import androidx.compose.ui.layout.layout
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
@@ -30,6 +33,7 @@ import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.toSize
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.atan2
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.floor
|
||||
@@ -102,7 +106,7 @@ private const val ZERO_RADIAN = -(PI / 2 - GAP / 2).toFloat()
|
||||
// null is mapped to (0, - 1.528f * radius)
|
||||
private const val NULL_NODE_DISTANCE = 1.528f
|
||||
|
||||
// Input: Concept space: 1 2 3 4 6 6 7 8 9 0 null
|
||||
// Input: Concept space: 1 2 3 4 5 6 7 8 9 0 null
|
||||
// Output: Position in 2d euclidean space partial circle
|
||||
private fun conceptSpaceToIntermediate(value: Int?): Offset {
|
||||
if (value != null) {
|
||||
@@ -138,67 +142,70 @@ private fun normalize(degree: Float) = (degree - 2 * PI * floor(degree / (2 * PI
|
||||
private fun numberOffsetToUIOffset(heightPx: Int, number: Float) = IntOffset(0, -(heightPx * 2 * number).toInt())
|
||||
|
||||
@Composable
|
||||
fun RollingNumber(number: Int, style: TextStyle = LocalTextStyle.current, length: Int? = null) {
|
||||
val string = remember(number) { "$number" }
|
||||
fun RollingNumber(
|
||||
number: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = Color.Unspecified,
|
||||
style: TextStyle = LocalTextStyle.current,
|
||||
length: Int? = null,
|
||||
) {
|
||||
val isNegative = number < 0
|
||||
val textColor = color.takeOrElse { style.color.takeOrElse { LocalContentColor.current } }
|
||||
val string = remember(number) { "${abs(number)}" }
|
||||
val transition = updateTransition(string)
|
||||
val (styleNoSpacing, spacing) = extractSpacingFromTextStyle(style)
|
||||
val (styleNoSpacing, spacing) = extractSpacingFromTextStyle(style.merge(color = textColor))
|
||||
val size = rememberTextStyleNumberMaxSize(styleNoSpacing)
|
||||
Row(
|
||||
modifier = Modifier.clipToBounds().padding(horizontal = spacing).layout { measurable, constraints ->
|
||||
LazyRow(
|
||||
modifier = modifier.clipToBounds().padding(horizontal = spacing).layout { measurable, constraints ->
|
||||
val placeable = measurable.measure(constraints)
|
||||
layout(width = placeable.width, height = size.height.roundToPx()) {
|
||||
placeable.place(0, 0)
|
||||
}
|
||||
},
|
||||
reverseLayout = true,
|
||||
horizontalArrangement = Arrangement.spacedBy(spacing, Alignment.CenterHorizontally),
|
||||
) {
|
||||
val content = remember {
|
||||
val meta = @Composable { reversed: Int ->
|
||||
val max = length ?: with(transition) { max(currentState.length, targetState.length) }
|
||||
val rotate by transition.animateOffset { str ->
|
||||
val len = str.length
|
||||
val absent = max - len
|
||||
val where = max - reversed - 1
|
||||
val v = if (where < absent) null else str[where - absent].digitToInt()
|
||||
conceptSpaceToIntermediate(v)
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier.offset {
|
||||
val number = intermediateToNumberOffset(rotate)
|
||||
numberOffsetToUIOffset(size.height.roundToPx(), number)
|
||||
},
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
repeat(10) { i ->
|
||||
Text(
|
||||
text = "$i",
|
||||
modifier = Modifier.size(size),
|
||||
style = styleNoSpacing,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
Text(
|
||||
text = "",
|
||||
modifier = Modifier.size(size),
|
||||
style = styleNoSpacing,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
val max = length ?: with(transition) { max(currentState.length, targetState.length) }
|
||||
items(max, key = { it }) { reversed ->
|
||||
val rotate by transition.animateOffset { str ->
|
||||
val len = str.length
|
||||
val absent = max - len
|
||||
val where = max - reversed - 1
|
||||
val v = if (where < absent) null else str[where - absent].digitToInt()
|
||||
conceptSpaceToIntermediate(v)
|
||||
}
|
||||
(0 until MAX_NUMBER).map {
|
||||
if (length != null) {
|
||||
{ meta(it) }
|
||||
} else {
|
||||
movableContentOf { meta(it) }
|
||||
Column(
|
||||
modifier = Modifier.animateItem().offset {
|
||||
val number = intermediateToNumberOffset(rotate)
|
||||
numberOffsetToUIOffset(size.height.roundToPx(), number)
|
||||
},
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
repeat(10) { i ->
|
||||
Text(
|
||||
text = "$i",
|
||||
modifier = Modifier.size(size),
|
||||
style = styleNoSpacing,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
Text(
|
||||
text = "",
|
||||
modifier = Modifier.size(size),
|
||||
style = styleNoSpacing,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
val max = length ?: with(transition) { max(currentState.length, targetState.length) }
|
||||
check(max <= MAX_NUMBER)
|
||||
repeat(max) { index ->
|
||||
content[max - index - 1]()
|
||||
if (isNegative) {
|
||||
item(key = -1) {
|
||||
Text(
|
||||
text = "-",
|
||||
modifier = Modifier.size(size).animateItem(),
|
||||
style = styleNoSpacing,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val MAX_NUMBER = 16
|
||||
@@ -107,7 +107,7 @@ fun SwitchPreference(title: String, summary: String? = null, value: KMutableProp
|
||||
fun IntSliderPreference(maxValue: Int, minValue: Int = 0, step: Int = maxValue - minValue - 1, showTicks: Boolean = true, title: String, summary: String? = null, value: KMutableProperty0<Int>, enabled: Boolean = true) {
|
||||
var v by remember { mutableIntStateOf(value.get()) }
|
||||
fun set(float: Float) = value.set(float.roundToInt().also { v = it })
|
||||
SliderPref(title = title, summary = summary, defaultValue = v.toFloat(), onValueChangeFinished = ::set, valueRange = minValue.toFloat()..maxValue.toFloat(), showValue = true, showInteger = true, steps = step, showTicks = showTicks, enabled = enabled)
|
||||
SliderPref(title = title, summary = summary, defaultValue = v.toFloat(), onValueChangeFinished = ::set, valueRange = minValue.toFloat()..maxValue.toFloat(), showValue = true, steps = step, showTicks = showTicks, enabled = enabled)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.SliderDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
@@ -18,7 +17,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.jamal.composeprefs3.ui.roundToDP
|
||||
import com.hippo.ehviewer.ui.main.RollingNumber
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
@@ -30,7 +29,6 @@ fun SliderPref(
|
||||
onValueChangeFinished: ((Float) -> Unit)? = null,
|
||||
valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
|
||||
showValue: Boolean = false,
|
||||
showInteger: Boolean = false,
|
||||
showTicks: Boolean = true,
|
||||
steps: Int = 0,
|
||||
textColor: Color = MaterialTheme.colorScheme.onBackground,
|
||||
@@ -73,10 +71,10 @@ fun SliderPref(
|
||||
},
|
||||
)
|
||||
if (showValue) {
|
||||
Text(
|
||||
text = if (showInteger) value.roundToInt().toString() else roundToDP(value, 2).toString(),
|
||||
RollingNumber(
|
||||
number = value.roundToInt(),
|
||||
color = textColor,
|
||||
modifier = Modifier.weight(0.5f).padding(start = 8.dp),
|
||||
modifier = Modifier.weight(0.5f),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user