move that special handling for emoji and language switch keys into layouts

for that, a new keyboard_state_selector is added
this allows modifying functional key layouts without having to manually adjust those keys
This commit is contained in:
Helium314 2024-06-15 21:54:47 +02:00
parent 17f30ad6d6
commit c064ba199c
6 changed files with 61 additions and 22 deletions

View file

@ -84,6 +84,7 @@ Features that may go unnoticed, and further potentially useful information
* Select text and press shift to switch between uppercase, lowercase and capitalize words
* You can add dictionaries by opening the file
* This only works with _content-uris_ and not with _file-uris_, meaning that it may not work with some file explorers.
* Not really a feature, but you can restart the keyboard by going to the settings and swiping it away from recents
* _Debug mode / debug APK_
* Long-press a suggestion in the suggestion strip twice to show the source dictionary.
* When using debug APK, you can find _Debug Settings_ within the _Advanced Preferences_, though the usefulness is limited except for dumping dictionaries into the log.

View file

@ -11,9 +11,9 @@
"email": { "label": "@", "groupId": 1, "type": "function" },
"uri": { "label": "/", "groupId": 1, "type": "function" }
},
{ "label": "language_switch" },
{ "label": "emoji" },
{ "label": "numpad" },
{ "$": "keyboard_state_selector", "languageKeyEnabled": { "$": "keyboard_state_selector", "alphabet": { "label": "language_switch" }}},
{ "$": "keyboard_state_selector", "emojiKeyEnabled": { "$": "keyboard_state_selector", "alphabet": { "label": "emoji" }}},
{ "$": "keyboard_state_selector", "symbols": { "label": "numpad" }},
{ "label": "space" },
{ "label": "period", "labelFlags": 1073741824 },
{ "label": "action", "width": 0.15 }

View file

@ -198,16 +198,7 @@ class KeyboardParser(private val params: KeyboardParams, private val context: Co
val functionalKeysBottom = allFunctionalKeys.lastOrNull() ?: return
if (!params.mId.isAlphaOrSymbolKeyboard || functionalKeysBottom.isEmpty() || functionalKeysBottom.any { it.isKeyPlaceholder() })
return
if (!Settings.getInstance().current.mHasCustomFunctionalLayout) {
// remove keys that should only exist on specific layouts or depend on setting (emoji, numpad, language switch)
if (!Settings.getInstance().current.mShowsEmojiKey || !params.mId.isAlphabetKeyboard)
functionalKeysBottom.removeFirst { it.label == toolbarKeyStrings[ToolbarKey.EMOJI] }
if (!Settings.getInstance().current.isLanguageSwitchKeyEnabled || !params.mId.isAlphabetKeyboard)
functionalKeysBottom.removeFirst { it.label == KeyLabel.LANGUAGE_SWITCH }
if (params.mId.mElementId != KeyboardId.ELEMENT_SYMBOLS)
functionalKeysBottom.removeFirst { it.label == KeyLabel.NUMPAD }
}
// replace comma / period if 2 keys in normal bottom row
// replace comma / period if 2 keys in normal bottom row
if (baseKeys.last().size == 2) {
functionalKeysBottom.replaceFirst(
{ it.label == KeyLabel.COMMA || it.groupId == KeyData.GROUP_COMMA},

View file

@ -11,6 +11,7 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.floris.CaseSelector
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.CharWidthSelector
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KanaSelector
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyData
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyboardStateSelector
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.LayoutDirectionSelector
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.MultiTextKeyData
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.ShiftStateSelector
@ -171,6 +172,7 @@ object RawKeyboardParser {
subclass(CaseSelector::class, CaseSelector.serializer())
subclass(ShiftStateSelector::class, ShiftStateSelector.serializer())
subclass(VariationSelector::class, VariationSelector.serializer())
subclass(KeyboardStateSelector::class, KeyboardStateSelector.serializer())
subclass(LayoutDirectionSelector::class, LayoutDirectionSelector.serializer())
subclass(CharWidthSelector::class, CharWidthSelector.serializer())
subclass(KanaSelector::class, KanaSelector.serializer())

View file

@ -9,7 +9,6 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import helium314.keyboard.keyboard.KeyboardId
import helium314.keyboard.keyboard.internal.KeyboardParams
import helium314.keyboard.latin.common.StringUtils
// taken from FlorisBoard, small modifications
// popup not nullable (maybe change back, but currently that's necessary for number keys)
@ -131,9 +130,7 @@ class ShiftStateSelector(
}
/**
* Allows to select an [AbstractKeyData] based on the current variation. Note that this type of selector only really
* makes sense in a text context, though technically speaking it can be used anywhere, so this implementation allows
* for any [AbstractKeyData] to be used here. The JSON class identifier for this selector is `variation_selector`.
* Allows to select an [AbstractKeyData] based on the current variation. The JSON class identifier for this selector is `variation_selector`.
*
* Example usage in a layout JSON file:
* ```
@ -155,6 +152,12 @@ class ShiftStateSelector(
* [default] will be used instead.
* @property password The key data to use if [KeyboardId.passwordInput] return true. If this value is
* null, [default] will be used instead.
* @property date The key data to use if [KeyboardId.MODE_DATE] is active. If this value is null,
* null, [default] will be used instead.
* @property time The key data to use if [KeyboardId.MODE_TIME] is active. If this value is null,
* null, [default] will be used instead.
* @property datetime The key data to use if [KeyboardId.MODE_DATETIME] is active. If this value is null,
* null, [default] will be used instead.
*/
@Serializable
@SerialName("variation_selector")
@ -170,16 +173,13 @@ data class VariationSelector(
) : AbstractKeyData {
override fun compute(params: KeyboardParams): KeyData? {
return when {
// todo: what is normal and all?
// KeyVariation.ALL -> default
// KeyVariation.NORMAL -> normal ?: default
params.mId.passwordInput() -> password ?: default
params.mId.mMode == KeyboardId.MODE_EMAIL -> email ?: default
params.mId.mMode == KeyboardId.MODE_URL -> uri ?: default
params.mId.mMode == KeyboardId.MODE_DATE -> date ?: default
params.mId.mMode == KeyboardId.MODE_TIME -> time ?: default
params.mId.mMode == KeyboardId.MODE_DATETIME -> datetime ?: default
else -> default
else -> normal ?: default
}?.compute(params)
}
@ -188,6 +188,49 @@ data class VariationSelector(
}
}
/**
* Allows to select an [AbstractKeyData] based on states saved in [KeyboardId].
* The JSON class identifier for this selector is `keyboard_state_selector`.
* Note that the conditions are checked in order as given below, and the first non-null AbstractKeyData is selected.
*
* @property emojiKeyEnabled The key data to use if [KeyboardId.mEmojiKeyEnabled] is true.
* @property languageKeyEnabled The key data to use if [KeyboardId.mLanguageSwitchKeyEnabled] is true.
* @property symbols The key data to use if [KeyboardId.mElementId] is [KeyboardId.ELEMENT_SYMBOLS].
* @property moreSymbols The key data to use if [KeyboardId.mElementId] is [KeyboardId.ELEMENT_SYMBOLS_SHIFTED].
* @property alphabet The key data to use if [KeyboardId.isAlphabetKeyboard] is true.
* @property default The default key data which should be used in case none of the other conditions have a matching non-null
* AbstractKeyData. Can be null, in this case no key is displayed.
*/
@Serializable
@SerialName("keyboard_state_selector")
class KeyboardStateSelector(
val emojiKeyEnabled: AbstractKeyData? = null,
val languageKeyEnabled: AbstractKeyData? = null,
val symbols: AbstractKeyData? = null,
val moreSymbols: AbstractKeyData? = null,
val alphabet: AbstractKeyData? = null,
val default: AbstractKeyData? = null,
) : AbstractKeyData {
override fun compute(params: KeyboardParams): KeyData? {
if (params.mId.mEmojiKeyEnabled)
emojiKeyEnabled?.compute(params)?.let { return it }
if (params.mId.mLanguageSwitchKeyEnabled)
languageKeyEnabled?.compute(params)?.let { return it }
if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS)
symbols?.compute(params)?.let { return it }
if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED)
moreSymbols?.compute(params)?.let { return it }
if (params.mId.isAlphabetKeyboard)
alphabet?.compute(params)?.let { return it }
return default?.compute(params)
}
override fun asString(isForDisplay: Boolean): String {
return ""
}
}
/**
* Allows to select an [AbstractKeyData] based on the current layout direction. Note that this type of selector only
* really makes sense in a text context, though technically speaking it can be used anywhere, so this implementation

View file

@ -33,7 +33,9 @@ If the layout has exactly 2 keys in the bottom row, these keys will replace comm
* there are also selector classes, which allow to change keys conditionally, see the [dvorak layout](https://github.com/Helium314/HeliBoard/blob/main/app/src/main/assets/layouts/dvorak.json) for an example:
* `case_selector`: keys for `lower` and `upper` (both mandatory), similar to `shift_state_selector`
* `shift_state_selector`: keys for `unshifted`, `shifted`, `shiftedManual`, `shiftedAutomatic`, `capsLock`, `manualOrLocked`, `default` (all optional)
* `variation_selector`: keys for `datetime`, `time`, `date`, `password`, `normal`, `uri`, `email`, `default` (all optional)
* `variation_selector`: keys for input types `datetime`, `time`, `date`, `password`, `normal`, `uri`, `email`, `default` (all optional)
* `keyboard_state_selector`: keys for `emojiKeyEnabled`, `languageKeyEnabled`, `symbols`, `moreSymbols`, `alphabet`, `default` (all optional)
* the `keyEnabled` keys will be used if the corresponding setting is enabled, `symbols`, `moreSymbols`, `alphabet` will be used when the said keyboard view is active
* `layout_direction_selector`: keys for `ltr` and `rtl` (both mandatory)
### Properties
* A (non-selector) key can have the following properties: