11package to.bitkit.ui.components
22
33import androidx.annotation.DrawableRes
4+ import androidx.compose.foundation.focusable
45import androidx.compose.foundation.layout.Box
56import androidx.compose.foundation.layout.BoxScope
67import androidx.compose.foundation.layout.BoxWithConstraints
@@ -12,10 +13,19 @@ import androidx.compose.foundation.lazy.grid.items
1213import androidx.compose.material3.Icon
1314import androidx.compose.material3.Text
1415import androidx.compose.runtime.Composable
16+ import androidx.compose.runtime.LaunchedEffect
1517import androidx.compose.runtime.getValue
18+ import androidx.compose.runtime.remember
1619import androidx.compose.ui.Alignment
1720import androidx.compose.ui.Modifier
21+ import androidx.compose.ui.focus.FocusRequester
22+ import androidx.compose.ui.focus.focusRequester
1823import androidx.compose.ui.hapticfeedback.HapticFeedbackType
24+ import androidx.compose.ui.input.key.Key
25+ import androidx.compose.ui.input.key.KeyEventType
26+ import androidx.compose.ui.input.key.key
27+ import androidx.compose.ui.input.key.onPreviewKeyEvent
28+ import androidx.compose.ui.input.key.type
1929import androidx.compose.ui.platform.LocalHapticFeedback
2030import androidx.compose.ui.platform.testTag
2131import androidx.compose.ui.res.painterResource
@@ -63,7 +73,20 @@ fun NumberPad(
6373 availableHeight : Dp = defaultHeight,
6474 errorKey : String? = null,
6575) {
66- BoxWithConstraints (modifier = modifier) {
76+ val focusRequester = remember { FocusRequester () }
77+ LaunchedEffect (Unit ) { focusRequester.requestFocus() }
78+
79+ BoxWithConstraints (
80+ modifier = modifier
81+ .focusRequester(focusRequester)
82+ .onPreviewKeyEvent { keyEvent ->
83+ if (keyEvent.type != KeyEventType .KeyDown ) return @onPreviewKeyEvent false
84+ val mapped = mapHardwareKey(keyEvent.key, type) ? : return @onPreviewKeyEvent false
85+ onPress(mapped)
86+ true
87+ }
88+ .focusable()
89+ ) {
6790 val buttonHeight = when {
6891 constraints.hasFixedHeight -> maxHeight / ROWS
6992 else -> (availableHeight / ROWS ).coerceIn(minButtonHeight, idealButtonHeight)
@@ -151,6 +174,27 @@ fun NumberPad(
151174
152175enum class NumberPadType { SIMPLE , INTEGER , DECIMAL }
153176
177+ private val hardwareKeyMap = mapOf (
178+ Key .Zero to " 0" , Key .NumPad0 to " 0" ,
179+ Key .One to " 1" , Key .NumPad1 to " 1" ,
180+ Key .Two to " 2" , Key .NumPad2 to " 2" ,
181+ Key .Three to " 3" , Key .NumPad3 to " 3" ,
182+ Key .Four to " 4" , Key .NumPad4 to " 4" ,
183+ Key .Five to " 5" , Key .NumPad5 to " 5" ,
184+ Key .Six to " 6" , Key .NumPad6 to " 6" ,
185+ Key .Seven to " 7" , Key .NumPad7 to " 7" ,
186+ Key .Eight to " 8" , Key .NumPad8 to " 8" ,
187+ Key .Nine to " 9" , Key .NumPad9 to " 9" ,
188+ Key .Backspace to KEY_DELETE , Key .Delete to KEY_DELETE ,
189+ Key .Period to KEY_DECIMAL , Key .NumPadDot to KEY_DECIMAL ,
190+ )
191+
192+ private fun mapHardwareKey (key : Key , type : NumberPadType ): String? {
193+ val mapped = hardwareKeyMap[key] ? : return null
194+ if (mapped == KEY_DECIMAL && type != NumberPadType .DECIMAL ) return null
195+ return mapped
196+ }
197+
154198@Composable
155199fun NumberPadKeyButton (
156200 text : String ,
0 commit comments