fix(worldclock): let the seconds time grow on wide windows
The row's seconds time was capped at 168dp everywhere, so on a tablet a larger font scale was shrunk back for no reason. The cap now grows to 45% of the window, with 168dp as the floor, and the fitted size is recomputed when the text measurer changes. A row the column has not re-seeded yet is skipped rather than crashing, and the source's KDoc is re-wrapped.
This commit is contained in:
1 parent
d70d0c78da
commit
0218339161
3 files changed
+27
-16
No files matched your search
@@ -18,6 +18,7 @@ import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -177,8 +178,14 @@ internal data class RowTimeStyle(val style: TextStyle, val maxWidth: Dp) {
|
||||
|
||||
private const val FitMargin = 0.95f
|
||||
|
||||
/** Wide enough for "12:59:59 PM" at the default scale; the city keeps the rest. */
|
||||
private val SecondsMaxWidth = 168.dp
|
||||
/**
|
||||
* The least the time may take: wide enough for "12:59:59 PM" at the default
|
||||
* scale. On a wider window the cap grows with it, to [SecondsMaxShare] of the
|
||||
* window, so a larger font scale is honoured wherever there is room for it.
|
||||
*/
|
||||
private val SecondsMinCap = 168.dp
|
||||
|
||||
private const val SecondsMaxShare = 0.45f
|
||||
|
||||
/**
|
||||
* Wide times in every format: a two-digit hour, eights, and each day period,
|
||||
@@ -189,7 +196,7 @@ private val WidestSamples: List<LocalTime> = listOf(LocalTime.of(10, 58, 58), Lo
|
||||
/**
|
||||
* Without seconds, the row's time exactly as it always was: `headlineMedium`,
|
||||
* bold, uncapped. With seconds, a size down, and shrunk further only if the
|
||||
* widest sample would not fit [SecondsMaxWidth], so at a large font scale the
|
||||
* widest sample would not fit the cap, so at a large font scale the
|
||||
* time cannot take the whole row and leave the city no width at all.
|
||||
* Measured once per format, style and density, never per tick: the digits are
|
||||
* tabular, so no time is wider than the samples.
|
||||
@@ -199,13 +206,15 @@ internal fun rememberRowTimeStyle(showSeconds: Boolean, formatTime: (LocalTime)
|
||||
val base = if (showSeconds) MaterialTheme.typography.headlineSmall else MaterialTheme.typography.headlineMedium
|
||||
val measurer = rememberTextMeasurer(cacheSize = 0)
|
||||
val density = LocalDensity.current
|
||||
return remember(showSeconds, base, formatTime, density) {
|
||||
val windowWidth = LocalConfiguration.current.screenWidthDp.dp
|
||||
val maxWidth = maxOf(SecondsMinCap, windowWidth * SecondsMaxShare)
|
||||
return remember(showSeconds, base, formatTime, density, measurer, maxWidth) {
|
||||
val style = base.copy(fontWeight = FontWeight.Bold)
|
||||
if (!showSeconds) return@remember RowTimeStyle(style, Dp.Infinity)
|
||||
val width = WidestSamples.maxOf { sample ->
|
||||
measurer.measure(formatTime(sample), style, maxLines = 1, softWrap = false).size.width
|
||||
}
|
||||
val cap = with(density) { SecondsMaxWidth.toPx() }
|
||||
val cap = with(density) { maxWidth.toPx() }
|
||||
// Through px and back, not by scaling the sp value: from Android 14
|
||||
// large font scales are non-linear, so sp and px are not proportional.
|
||||
val fitted = if (width <= cap) {
|
||||
@@ -214,7 +223,7 @@ internal fun rememberRowTimeStyle(showSeconds: Boolean, formatTime: (LocalTime)
|
||||
// A few percent spare: hinted glyph advances do not scale exactly.
|
||||
style.copy(fontSize = with(density) { (style.fontSize.toPx() * FitMargin * cap / width).toSp() })
|
||||
}
|
||||
RowTimeStyle(fitted, SecondsMaxWidth)
|
||||
RowTimeStyle(fitted, maxWidth)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -147,7 +147,9 @@ fun WorldClockScreen(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
rowHeight = 80.dp,
|
||||
) { id, position, dragHandle, isDragging ->
|
||||
val row = rowsById.getValue(id)
|
||||
// Always present while the column re-seeds in composition;
|
||||
// drawn as nothing rather than crashing if that ever lags.
|
||||
val row = rowsById[id] ?: return@ReorderableColumn
|
||||
WorldClockRow(
|
||||
row = row,
|
||||
position = position,
|
||||
|
||||
@@ -46,17 +46,17 @@ internal class WorldClockSource @Inject constructor(
|
||||
* **every tick**. [ClockChanges] restarts the tick, so `TIME_SET` and
|
||||
* `TIMEZONE_CHANGED` show at once rather than at the next minute, and the
|
||||
* restarted ticker measures its next wait against the clock as it now
|
||||
* reads. It sits
|
||||
* outside the `flatMapLatest`, so its receiver is registered once per
|
||||
* subscription rather than once per list edit, and `onStart` lets the
|
||||
* first state through without waiting for a broadcast. [refreshes] does
|
||||
* the same for what no broadcast announces: the screen sends one on every
|
||||
* resume, which covers a per-app language change (the activity is
|
||||
* recreated, the ViewModel and this flow are not) and a `delay` stretched
|
||||
* by a deep sleep (the main looper's clock stops while the device sleeps)
|
||||
* without waiting for the next minute.
|
||||
* reads. It sits outside the `flatMapLatest`, so its receiver is
|
||||
* registered once per subscription rather than once per list edit, and
|
||||
* `onStart` lets the first state through without waiting for a broadcast.
|
||||
* `SystemEventReceiver` still needs no new branch (D2).
|
||||
*
|
||||
* [refreshes] does the same for what no broadcast announces: the screen
|
||||
* sends one on every resume, which covers a per-app language change (the
|
||||
* activity is recreated, the ViewModel and this flow are not) and a
|
||||
* `delay` stretched by a deep sleep (the main looper's clock stops while
|
||||
* the device sleeps) without waiting for the next minute.
|
||||
*
|
||||
* The tick is aligned to the wall clock and only as fine as the readouts:
|
||||
* once a minute, or once a second when the user turned seconds on (#23).
|
||||
* Everything shown is truncated to that same unit, so each tick is one new
|
||||
|
||||
Reference in new issue
Block a user