⎇ 5227fbf1abfb2a437968ebc63692560c3a929230
keyboard/src/main/java/com/rizoma/trazo/ui/KeyCell.kt
Ñawpa willay
ch'uya
Trazo: arreglar cursor al reposicionar + mayúsculas en R↑ y columna reordenada
1package com.rizoma.trazo.ui
2
3import androidx.compose.foundation.background
4import androidx.compose.foundation.border
5import androidx.compose.foundation.gestures.awaitEachGesture
6import androidx.compose.foundation.gestures.awaitFirstDown
7import androidx.compose.foundation.layout.Box
8import androidx.compose.foundation.layout.fillMaxSize
9import androidx.compose.foundation.layout.padding
10import androidx.compose.foundation.shape.RoundedCornerShape
11import androidx.compose.material3.Text
12import androidx.compose.runtime.Composable
13import androidx.compose.ui.Alignment
14import androidx.compose.ui.Modifier
15import androidx.compose.ui.draw.clip
16import androidx.compose.ui.graphics.Color
17import androidx.compose.ui.input.pointer.pointerInput
18import androidx.compose.ui.platform.LocalDensity
19import androidx.compose.ui.text.font.FontWeight
20import androidx.compose.ui.unit.dp
21import androidx.compose.ui.unit.sp
22import com.rizoma.trazo.model.Dir
23import com.rizoma.trazo.model.KeyDef
24import com.rizoma.trazo.model.directionOf
25import com.rizoma.trazo.ui.theme.NeonVerde
26import com.rizoma.trazo.ui.theme.Tecla
27import com.rizoma.trazo.ui.theme.Tenue
28
29private val hintAlignments = mapOf(
30 Dir.N to Alignment.TopCenter,
31 Dir.NE to Alignment.TopEnd,
32 Dir.E to Alignment.CenterEnd,
33 Dir.SE to Alignment.BottomEnd,
34 Dir.S to Alignment.BottomCenter,
35 Dir.SW to Alignment.BottomStart,
36 Dir.W to Alignment.CenterStart,
37 Dir.NW to Alignment.TopStart,
38)
39
40@Composable
41fun KeyCell(
42 key: KeyDef,
43 shifted: Boolean,
44 onChar: (Char) -> Unit,
45 onShift: () -> Unit = {},
46 shiftAccent: Color = Tenue,
47 modifier: Modifier = Modifier,
48) {
49 val tapRadiusPx = with(LocalDensity.current) { 22.dp.toPx() }
50
51 Box(
52 modifier
53 .padding(3.dp)
54 .clip(RoundedCornerShape(14.dp))
55 .background(Tecla)
56 .border(1.dp, NeonVerde.copy(alpha = 0.10f), RoundedCornerShape(14.dp))
57 .pointerInput(key, shifted) {
58 awaitEachGesture {
59 val down = awaitFirstDown()
60 var current = down.position
61 while (true) {
62 val event = awaitPointerEvent()
63 val change = event.changes.firstOrNull { it.id == down.id } ?: event.changes.first()
64 current = change.position
65 change.consume()
66 if (!change.pressed) break
67 }
68 val dx = current.x - down.position.x
69 val dy = current.y - down.position.y
70 val dir = directionOf(dx, dy, tapRadiusPx)
71 if (key.shiftDir != null && dir == key.shiftDir) onShift()
72 else key.charFor(dir)?.let { raw -> onChar(raw) }
73 }
74 },
75 ) {
76 // Pistas de swipe (tenues) en los bordes.
77 key.swipes.forEach { (dir, ch) ->
78 val align = hintAlignments[dir] ?: return@forEach
79 Text(
80 text = glyph(ch, shifted),
81 color = Tenue,
82 fontSize = 11.sp,
83 modifier = Modifier.align(align).padding(horizontal = 6.dp, vertical = 3.dp),
84 )
85 }
86 // Pista de mayúsculas (R↑): coloreada según el estado del shift.
87 key.shiftDir?.let { d ->
88 hintAlignments[d]?.let { align ->
89 Text(
90 text = "⇧",
91 color = shiftAccent,
92 fontSize = 13.sp,
93 fontWeight = FontWeight.Bold,
94 modifier = Modifier.align(align).padding(horizontal = 6.dp, vertical = 3.dp),
95 )
96 }
97 }
98 // Carácter central.
99 Text(
100 text = glyph(key.center, shifted),
101 color = androidx.compose.ui.graphics.Color.White,
102 fontSize = 22.sp,
103 fontWeight = FontWeight.Medium,
104 modifier = Modifier.align(Alignment.Center),
105 )
106 }
107}
108
109private fun glyph(c: Char, shifted: Boolean): String =
110 if (shifted) c.uppercaseChar().toString() else c.toString()