Answer a question card as one act, and mark the press that made it
Picking an option marked nothing until the answer had crossed the tunnel, been recorded and come back as an event, so the card sat unchanged for most of a second after a tap. What the reader has picked is now the card's own state and shows at once; a Submit at the foot sends every question the tool is waiting on, greyed until all of them have an answer and a spinner while the request is out. Several questions are paged rather than stacked, with the count and a pair of arrows on the right, because three questions with four described options each is several screens and the reader scrolls past the one they are answering to reach the button that sends it. A permission ask keeps its single tap -- two bare words are not worth a submit step -- and marks what was pressed until the request settles, so the mark either stands on the recorded answer or goes away with the failure. Chevron draws all four directions from one description of the shape, since the pager needed two more of them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a4d512af26
commit
8dcd2cb708
7 files changed
+299
-127
No files matched your search
@@ -11,14 +11,25 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
/** Which way a [Chevron] points. */
|
||||
enum class Pointing {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
/**
|
||||
* A chevron, pointing up or down.
|
||||
* A chevron, pointing whichever of the four ways is asked for.
|
||||
*
|
||||
* Drawn rather than set in a font: a chevron from an icon font is one of the glyphs a system font
|
||||
* may simply not have, and the reader who gets an empty box instead is never the one who wrote it.
|
||||
*
|
||||
* One composable for both directions rather than two that differ by a minus sign -- the pair would
|
||||
* drift, and the drift would be a bug in exactly one direction.
|
||||
* One composable for all four directions rather than one per axis that differ by which coordinate
|
||||
* gets the minus sign -- the copies would drift, and the drift would be a bug in exactly one
|
||||
* direction. The shape is written once in its own coordinates, where x runs across the opening and
|
||||
* y runs from the open side to the tip, and [Pointing] is only a table of how those two map onto
|
||||
* the box.
|
||||
*
|
||||
* It draws no label of its own, so every caller owes it a `contentDescription`: this is the whole
|
||||
* of what assistive technology has to go on, and it is also the answer to "what was that arrow for"
|
||||
@@ -26,28 +37,36 @@ import androidx.compose.ui.unit.dp
|
||||
*/
|
||||
@Composable
|
||||
fun Chevron(
|
||||
pointingUp: Boolean,
|
||||
pointing: Pointing,
|
||||
modifier: Modifier = Modifier,
|
||||
colour: Color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
) {
|
||||
Canvas(modifier.width(20.dp).height(10.dp)) {
|
||||
val sideways = pointing == Pointing.Left || pointing == Pointing.Right
|
||||
Canvas(
|
||||
modifier
|
||||
.width(if (sideways) CHEVRON_DEPTH else CHEVRON_SPAN)
|
||||
.height(if (sideways) CHEVRON_SPAN else CHEVRON_DEPTH)
|
||||
) {
|
||||
val inset = 2.dp.toPx()
|
||||
val point = if (pointingUp) inset else size.height - inset
|
||||
val ends = if (pointingUp) size.height - inset else inset
|
||||
val wide = size.width - inset
|
||||
val tall = size.height - inset
|
||||
fun at(across: Float, along: Float) =
|
||||
when (pointing) {
|
||||
Pointing.Up -> Offset(lerp(inset, wide, across), lerp(tall, inset, along))
|
||||
Pointing.Down -> Offset(lerp(inset, wide, across), lerp(inset, tall, along))
|
||||
Pointing.Left -> Offset(lerp(wide, inset, along), lerp(inset, tall, across))
|
||||
Pointing.Right -> Offset(lerp(inset, wide, along), lerp(inset, tall, across))
|
||||
}
|
||||
val stroke = 2.dp.toPx()
|
||||
drawLine(
|
||||
colour,
|
||||
Offset(inset, ends),
|
||||
Offset(size.width / 2, point),
|
||||
strokeWidth = stroke,
|
||||
cap = StrokeCap.Round,
|
||||
)
|
||||
drawLine(
|
||||
colour,
|
||||
Offset(size.width / 2, point),
|
||||
Offset(size.width - inset, ends),
|
||||
strokeWidth = stroke,
|
||||
cap = StrokeCap.Round,
|
||||
)
|
||||
drawLine(colour, at(0f, 0f), at(0.5f, 1f), strokeWidth = stroke, cap = StrokeCap.Round)
|
||||
drawLine(colour, at(0.5f, 1f), at(1f, 0f), strokeWidth = stroke, cap = StrokeCap.Round)
|
||||
}
|
||||
}
|
||||
|
||||
private fun lerp(from: Float, to: Float, fraction: Float) = from + (to - from) * fraction
|
||||
|
||||
/** How far the chevron opens, across the direction it points. */
|
||||
private val CHEVRON_SPAN = 20.dp
|
||||
|
||||
/** How far it reaches in the direction it points. */
|
||||
private val CHEVRON_DEPTH = 10.dp
|
||||
Reference in new issue
Block a user