diff --git a/app/src/main/java/com/droiddeck/launcher/input/PcKeys.kt b/app/src/main/java/com/droiddeck/launcher/input/PcKeys.kt index f3ab1d4c6..24959e5a2 100644 --- a/app/src/main/java/com/droiddeck/launcher/input/PcKeys.kt +++ b/app/src/main/java/com/droiddeck/launcher/input/PcKeys.kt @@ -51,6 +51,15 @@ val PC_KEY_ROWS: List> = listOf( val PC_MODIFIERS: Set = setOf(42, 54, 29, 97, 56, 100, 125) +// Sticky modifiers are excluded: hiding a locked modifier would leave it pressed forever. +val PC_KEY_HIDDEN_GROUPS: Map> = mapOf( + "function" to ((59..68).toSet() + setOf(1, 87, 88)), + "nav" to setOf(110, 111, 102, 107, 104, 109), + "arrows" to setOf(103, 105, 108, 106), +) + +val PC_KEY_GROUP_IDS: Map = mapOf(0 to "function", 1 to "nav", 2 to "arrows") + val PC_KEYS: Map = PC_KEY_ROWS.flatten().associateBy { it.code } fun pcKeyLabel(context: Context, code: Int): String { diff --git a/app/src/main/java/com/droiddeck/launcher/session/SessionPrefs.kt b/app/src/main/java/com/droiddeck/launcher/session/SessionPrefs.kt index fd54f5c39..48bdeffde 100644 --- a/app/src/main/java/com/droiddeck/launcher/session/SessionPrefs.kt +++ b/app/src/main/java/com/droiddeck/launcher/session/SessionPrefs.kt @@ -348,6 +348,32 @@ object SessionPrefs { prefs(context).edit().putInt("hudOpacity", percent.coerceIn(HUD_OPACITY_RANGE)).apply() } + const val PC_KEYBOARD_SIZE_DEFAULT = 47 + val PC_KEYBOARD_SIZE_RANGE = 25..70 + const val PC_KEYBOARD_OPACITY_DEFAULT = 100 + val PC_KEYBOARD_OPACITY_RANGE = 30..100 + + fun pcKeyboardSize(context: Context): Int = + prefs(context).getInt("pcKeyboardSize", PC_KEYBOARD_SIZE_DEFAULT).takeIf { it in PC_KEYBOARD_SIZE_RANGE } ?: PC_KEYBOARD_SIZE_DEFAULT + + fun setPcKeyboardSize(context: Context, percent: Int) { + prefs(context).edit().putInt("pcKeyboardSize", percent.coerceIn(PC_KEYBOARD_SIZE_RANGE)).apply() + } + + fun pcKeyboardOpacity(context: Context): Int = + prefs(context).getInt("pcKeyboardOpacity", PC_KEYBOARD_OPACITY_DEFAULT).takeIf { it in PC_KEYBOARD_OPACITY_RANGE } ?: PC_KEYBOARD_OPACITY_DEFAULT + + fun setPcKeyboardOpacity(context: Context, percent: Int) { + prefs(context).edit().putInt("pcKeyboardOpacity", percent.coerceIn(PC_KEYBOARD_OPACITY_RANGE)).apply() + } + + fun pcKeyboardHiddenKeys(context: Context): Set = + idList(prefs(context).getString("pcKeyboardHiddenKeys", null)).toSet() + + fun setPcKeyboardHiddenKeys(context: Context, hidden: Set) { + prefs(context).edit().putString("pcKeyboardHiddenKeys", hidden.joinToString(",")).apply() + } + fun droidDeckHudConfig(context: Context): String? = prefs(context).getString("__KEEP_droidDeckHud__", null) fun setDroidDeckHudConfig(context: Context, config: String) { diff --git a/app/src/main/java/com/droiddeck/launcher/session/SettingsReset.kt b/app/src/main/java/com/droiddeck/launcher/session/SettingsReset.kt index 07d330e10..c47bdd798 100644 --- a/app/src/main/java/com/droiddeck/launcher/session/SettingsReset.kt +++ b/app/src/main/java/com/droiddeck/launcher/session/SettingsReset.kt @@ -17,7 +17,7 @@ object SettingsReset { "storesInstallLanguage", "hud", "hudDeck", "hudFps", "hudBattery", "hudGraph", "hudColor", "hudOpacity", "__KEEP_droidDeckHud__", "__KEEP_droidDeckHudPos__", "backHold", "backActionsInverted", "backDoubleTapMs", "touch", "osc", "shape", "steamController", "gamepadLeft", "gamepadRight", "pipAutoEnter", "unfoldedControls", "unfoldedControlsWithController", - "stretch16x9", "picturePosition", "railLabels", + "stretch16x9", "picturePosition", "railLabels", "pcKeyboardSize", "pcKeyboardOpacity", "pcKeyboardHiddenKeys", "gpuDriverMode", "androidDriver", "linuxDriver", "linuxDriver.steam", "linuxDriver.desktop", "tuSysmem", "glThread", "noGlError", "zinkLazy", "mangoapp", "gpuClockPin", "gamescopeRealtime", "ramCapMb", "vramCapMb", "zramClient", "directAudio", "mic", "wifiDiscovery", "clientCpusOverride", "clientCpus", "gameCpus", diff --git a/app/src/main/java/com/droiddeck/launcher/ui/PcKeyboard.kt b/app/src/main/java/com/droiddeck/launcher/ui/PcKeyboard.kt index 6c68c9cfc..5c4072703 100644 --- a/app/src/main/java/com/droiddeck/launcher/ui/PcKeyboard.kt +++ b/app/src/main/java/com/droiddeck/launcher/ui/PcKeyboard.kt @@ -1,6 +1,7 @@ package com.droiddeck.launcher.ui import com.droiddeck.launcher.R +import com.droiddeck.launcher.input.PC_KEY_HIDDEN_GROUPS import com.droiddeck.launcher.input.PC_KEY_ROWS import com.droiddeck.launcher.input.PC_MODIFIERS import com.droiddeck.launcher.input.PcKey @@ -35,6 +36,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.key import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateMapOf import androidx.compose.runtime.mutableStateOf @@ -45,12 +47,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.key.nativeKeyCode import androidx.compose.ui.input.key.onPreviewKeyEvent import androidx.compose.ui.input.key.type import androidx.compose.ui.input.key.KeyEventType import androidx.compose.ui.input.key.key import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -98,10 +102,17 @@ fun PcKeyboard( } LaunchedEffect(Unit) { runCatching { firstKey.requestFocus() } } + val prefsContext = LocalContext.current + remember { PcKeyboardPrefs.load(prefsContext) } + val hiddenCodes = PcKeyboardPrefs.hiddenKeys.flatMap { PC_KEY_HIDDEN_GROUPS[it].orEmpty() }.toSet() + val rows = PC_KEY_ROWS.map { row -> row.filter { it.code !in hiddenCodes } }.filter { it.isNotEmpty() } + BoxWithConstraints(Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) { - val rowHeight = ((maxHeight * 0.47f - 24.dp - 3.dp * 6 - 10.dp) / 6).coerceIn(24.dp, 44.dp) + val rowHeight = ((maxHeight * (PcKeyboardPrefs.size / 100f) - 24.dp - 3.dp * rows.size - 10.dp) / rows.size) + .coerceIn(20.dp, 56.dp) Box( Modifier.fillMaxWidth() + .graphicsLayer { alpha = PcKeyboardPrefs.opacity / 100f } .background(Color(0xE6101418), RoundedCornerShape(topStart = 14.dp, topEnd = 14.dp)) .border(1.dp, pal.line2, RoundedCornerShape(topStart = 14.dp, topEnd = 14.dp)) .pointerInput(Unit) { awaitEachGesture { awaitFirstDown().consume(); waitForUpOrCancellation()?.consume() } } @@ -115,7 +126,7 @@ fun PcKeyboard( Spacer(Modifier.width(8.dp)) HeaderButton(stringResource(R.string.pckb_hide), onClose) } - PC_KEY_ROWS.forEachIndexed { r, row -> + rows.forEachIndexed { r, row -> Row(horizontalArrangement = Arrangement.spacedBy(3.dp), modifier = Modifier.fillMaxWidth().height(rowHeight)) { row.forEachIndexed { i, key -> val state = mods[key.code] ?: MOD_OFF @@ -126,11 +137,13 @@ fun PcKeyboard( key.label.length == 1 && key.label[0].isLetter() && (shifted xor capsOn) -> key.label.uppercase() else -> key.label } - KeyCap( - label, key.weight, lit, locked = state == MOD_LOCKED, - modifier = if (r == 0 && i == 0) Modifier.focusRequester(firstKey) else Modifier, - onDown = { press(key) }, onUp = { release(key) }, - ) + key(key.code) { + KeyCap( + label, key.weight, lit, locked = state == MOD_LOCKED, + modifier = if (r == 0 && i == 0) Modifier.focusRequester(firstKey) else Modifier, + onDown = { press(key) }, onUp = { release(key) }, + ) + } } } } @@ -164,8 +177,11 @@ private fun RowScope.KeyCap( awaitEachGesture { awaitFirstDown().consume() down = true; onDown() - waitForUpOrCancellation()?.consume() - down = false; onUp() + try { + waitForUpOrCancellation()?.consume() + } finally { + down = false; onUp() + } } } .onPreviewKeyEvent { e -> diff --git a/app/src/main/java/com/droiddeck/launcher/ui/PcKeyboardPrefs.kt b/app/src/main/java/com/droiddeck/launcher/ui/PcKeyboardPrefs.kt new file mode 100644 index 000000000..3b31e0b99 --- /dev/null +++ b/app/src/main/java/com/droiddeck/launcher/ui/PcKeyboardPrefs.kt @@ -0,0 +1,38 @@ +package com.droiddeck.launcher.ui + +import android.content.Context +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import com.droiddeck.launcher.input.PC_KEY_HIDDEN_GROUPS +import com.droiddeck.launcher.session.SessionPrefs + +internal object PcKeyboardPrefs { + var size by mutableStateOf(SessionPrefs.PC_KEYBOARD_SIZE_DEFAULT) + private set + var opacity by mutableStateOf(SessionPrefs.PC_KEYBOARD_OPACITY_DEFAULT) + private set + var hiddenKeys by mutableStateOf(emptySet()) + private set + + fun load(context: Context) { + size = SessionPrefs.pcKeyboardSize(context) + opacity = SessionPrefs.pcKeyboardOpacity(context) + hiddenKeys = SessionPrefs.pcKeyboardHiddenKeys(context).filter { it in PC_KEY_HIDDEN_GROUPS }.toSet() + } + + fun setSize(context: Context, percent: Int) { + size = percent + SessionPrefs.setPcKeyboardSize(context, percent) + } + + fun setOpacity(context: Context, percent: Int) { + opacity = percent + SessionPrefs.setPcKeyboardOpacity(context, percent) + } + + fun toggleHiddenKey(context: Context, group: String, on: Boolean) { + hiddenKeys = if (on) hiddenKeys + group else hiddenKeys - group + SessionPrefs.setPcKeyboardHiddenKeys(context, hiddenKeys) + } +} diff --git a/app/src/main/java/com/droiddeck/launcher/ui/SessionOverlay.kt b/app/src/main/java/com/droiddeck/launcher/ui/SessionOverlay.kt index 4ecf45256..f61ecd98a 100644 --- a/app/src/main/java/com/droiddeck/launcher/ui/SessionOverlay.kt +++ b/app/src/main/java/com/droiddeck/launcher/ui/SessionOverlay.kt @@ -154,6 +154,7 @@ import com.droiddeck.launcher.gpu.SteamVr import com.droiddeck.launcher.gpu.ScreenEffects import com.droiddeck.launcher.session.SessionPrefs import com.droiddeck.launcher.session.ComponentsManager +import com.droiddeck.launcher.input.PC_KEY_GROUP_IDS import com.droiddeck.launcher.input.SecondScreenDisplay import com.droiddeck.launcher.input.SecondScreenMode import com.droiddeck.launcher.input.TouchGestureConfig.Companion.MOUSE_BEHAVIOR_CLICK @@ -683,6 +684,28 @@ internal fun SessionDrawer(open: Boolean, requestedPage: SessionDrawerPage, cont host.open = null; a.onKeyboard() } } + val keyboardContext = LocalContext.current + remember { PcKeyboardPrefs.load(keyboardContext) } + SliderRow( + stringResource(R.string.pckb_size), stringResource(R.string.pckb_size_hint), + PcKeyboardPrefs.size, SessionPrefs.PC_KEYBOARD_SIZE_RANGE, step = 1, + format = { "$it%" }, modifier = focus.track(page, "pckb-size"), + onChange = { PcKeyboardPrefs.setSize(keyboardContext, it) }) + SliderRow( + stringResource(R.string.pckb_opacity), stringResource(R.string.pckb_opacity_hint), + PcKeyboardPrefs.opacity, SessionPrefs.PC_KEYBOARD_OPACITY_RANGE, step = 5, + format = { "$it%" }, modifier = focus.track(page, "pckb-opacity"), + onChange = { PcKeyboardPrefs.setOpacity(keyboardContext, it) }) + MultiRow( + host, "pckb-hidden", stringResource(R.string.pckb_hidden_keys), + stringResource(R.string.pckb_hidden_keys_hint), + listOf( + 0 to stringResource(R.string.pckb_group_function), + 1 to stringResource(R.string.pckb_group_nav), + 2 to stringResource(R.string.pckb_group_arrows), + ), + PC_KEY_GROUP_IDS.filterValues { it in PcKeyboardPrefs.hiddenKeys }.keys, + onToggle = { id, on -> PcKeyboardPrefs.toggleHiddenKey(keyboardContext, PC_KEY_GROUP_IDS.getValue(id), on) }) } } SessionDrawerPage.SECOND_SCREEN -> { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 00b2c3a79..e429091d7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1396,6 +1396,15 @@ Cancel queued Android keyboard Hide ✕ + Size on screen + Share of the screen the keyboard takes, header included. + Opacity + Lower it to see the game behind the keyboard. + Hidden keys + Keys you rarely use stay off the keyboard. + Esc and function row + Ins / Del / Home / End / PgUp / PgDn + Arrow keys Launch %1$s Choose a screen. Secondary: %1$s. The secondary display is no longer available.