parse number and alpha/symbol layouts using the same function

This commit is contained in:
Helium314 2024-05-11 23:25:47 +02:00
parent 9bb64d561f
commit 38228f8b70
10 changed files with 129 additions and 189 deletions

View file

@ -3,13 +3,13 @@
{ "label": "1", "type": "numeric" },
{ "label": "2", "type": "numeric" },
{ "label": "3", "type": "numeric" },
{ "label": "-", "popup": { "main": { "label": "+" } }, "labelFlags": 1073742336 }
{ "label": "-", "type": "function", "popup": { "main": { "label": "+" } }, "labelFlags": 64 }
],
[
{ "label": "4", "type": "numeric" },
{ "label": "5", "type": "numeric" },
{ "label": "6", "type": "numeric" },
{ "label": "space" }
{ "label": "space", "type": "function" }
],
[
{ "label": "7", "type": "numeric" },
@ -19,15 +19,15 @@
],
[
{ "$": "variation_selector",
"default": { "label": "," },
"date": { "label": "." },
"time": { "label": ".", "popup": { "relevant": [
"default": { "label": ",", "type": "numeric" },
"date": { "label": ".", "type": "numeric" },
"time": { "label": ".", "type": "numeric", "popup": { "relevant": [
{ "label": "!fixedColumnOrder!2" },
{ "label": "!hasLabels!" },
{ "label": "AM" },
{ "label": "PM" }
] } },
"datetime": { "label": ".", "popup": { "relevant": [
"datetime": { "label": ".", "type": "numeric", "popup": { "relevant": [
{ "label": "!fixedColumnOrder!2" },
{ "label": "!hasLabels!" },
{ "label": "AM" },
@ -36,10 +36,10 @@
},
{ "label": "0", "type": "numeric" },
{ "$": "variation_selector",
"default": { "label": "." },
"date": { "label": "/" },
"time": { "label": ":" },
"datetime": { "label": "/ :|/", "popup": { "relevant": [
"default": { "label": ".", "type": "numeric" },
"date": { "label": "/", "type": "numeric" },
"time": { "label": ":", "type": "numeric" },
"datetime": { "label": "/ :|/", "type": "numeric", "popup": { "relevant": [
{ "label": "!noPanelAutoPopupKey!" },
{ "label": "," }
] } }

View file

@ -1,6 +1,6 @@
[
[
{ "label": "+", "popup": {
{ "label": "+", "type": "function", "popup": {
"relevant": [
{ "label": "(" },
{ "label": "<" },
@ -10,10 +10,10 @@
{ "label": "1", "type": "numeric" },
{ "label": "2", "type": "numeric" },
{ "label": "3", "type": "numeric" },
{ "label": "%", "popup": { "main": { "label": "$$$"} }, "labelFlags": 512 }
{ "label": "%", "type": "function", "popup": { "main": { "label": "$$$"} }, "labelFlags": 512 }
],
[
{ "label": "-", "popup": {
{ "label": "-", "type": "function", "popup": {
"relevant": [
{ "label": ")" },
{ "label": ">" },
@ -23,10 +23,10 @@
{ "label": "4", "type": "numeric" },
{ "label": "5", "type": "numeric" },
{ "label": "6", "type": "numeric" },
{ "label": "space" }
{ "label": "space", "type": "function" }
],
[
{ "label": "*", "popup": {
{ "label": "*", "type": "function", "popup": {
"relevant": [
{ "label": "/" },
{ "label": "×" },
@ -40,11 +40,11 @@
],
[
{ "label": "alpha" },
{ "label": "comma" },
{ "label": "symbol" },
{ "label": "comma", "width": 0.1 },
{ "label": "symbol", "type": "character", "width": 0.12 },
{ "label": "0", "type": "numeric" },
{ "label": "=", "popup": { "relevant": [ { "label": "≠"}, { "label": "≈"} ] } },
{ "label": "period" },
{ "label": "enter" }
{ "label": "=", "width": 0.12, "popup": { "relevant": [ { "label": "≠"}, { "label": "≈"} ] } },
{ "label": "period", "width": 0.1 },
{ "label": "action" }
]
]

View file

@ -1,46 +1,46 @@
[
[
{ "label": "(", "popup": { "relevant": [ { "label": "[" }, { "label": "{" } ] } },
{ "label": ")", "popup": { "relevant": [ { "label": "]" }, { "label": "}" } ] } },
{ "label": ":" },
{ "label": "1", "type": "numeric" },
{ "label": "2", "type": "numeric" },
{ "label": "3", "type": "numeric" },
{ "label": "+", "popup": { "main": { "label": "±" } } },
{ "label": "-", "popup": { "main": { "label": "~" } } },
{ "label": "space" }
{ "label": "(", "type": "function", "popup": { "relevant": [ { "label": "[" }, { "label": "{" } ] } },
{ "label": ")", "type": "function", "popup": { "relevant": [ { "label": "]" }, { "label": "}" } ] } },
{ "label": ":", "type": "function" },
{ "label": "1", "type": "numeric", "width": 0.3 },
{ "label": "2", "type": "numeric", "width": 0.3 },
{ "label": "3", "type": "numeric", "width": 0.3 },
{ "label": "+", "type": "function", "popup": { "main": { "label": "±" } } },
{ "label": "-", "type": "function", "popup": { "main": { "label": "~" } } },
{ "label": "space", "type": "function" }
],
[
{ "label": "!" },
{ "label": "?" },
{ "label": ";" },
{ "label": "4", "type": "numeric" },
{ "label": "5", "type": "numeric" },
{ "label": "6", "type": "numeric" },
{ "label": "*", "popup": { "main": { "label": "×" } } },
{ "label": "/", "popup": { "main": { "label": "÷" } } },
{ "label": "!", "type": "function" },
{ "label": "?", "type": "function" },
{ "label": ";", "type": "function" },
{ "label": "4", "type": "numeric", "width": 0.3 },
{ "label": "5", "type": "numeric", "width": 0.3 },
{ "label": "6", "type": "numeric", "width": 0.3 },
{ "label": "*", "type": "function", "popup": { "main": { "label": "×" } } },
{ "label": "/", "type": "function", "popup": { "main": { "label": "÷" } } },
{ "label": "delete" }
],
[
{ "label": "|" },
{ "label": "$$$" },
{ "label": "&" },
{ "label": "7", "type": "numeric" },
{ "label": "8", "type": "numeric" },
{ "label": "9", "type": "numeric" },
{ "label": "#" },
{ "label": "%", "popup": { "main": { "label": "‰" } } },
{ "label": "|", "type": "function" },
{ "label": "$$$", "type": "function" },
{ "label": "&", "type": "function" },
{ "label": "7", "type": "numeric", "width": 0.3 },
{ "label": "8", "type": "numeric", "width": 0.3 },
{ "label": "9", "type": "numeric", "width": 0.3 },
{ "label": "#", "type": "function" },
{ "label": "%", "type": "function", "popup": { "main": { "label": "‰" } } },
{ "label": "action" }
],
[
{ "label": "alpha" },
{ "label": "<", "popup": { "main": { "label": "≤" } } },
{ "label": ">", "popup": { "main": { "label": "≥" } } },
{ "label": "comma" },
{ "label": "0", "type": "numeric" },
{ "label": "period" },
{ "label": "=", "popup": { "relevant": [ { "label": "≠"}, { "label": "≈"} ] } },
{ "label": "=", "popup": { "relevant": [ { "label": "≠"}, { "label": "≈"} ] } },
{ "label": "<", "type": "function", "popup": { "main": { "label": "≤" } } },
{ "label": ">", "type": "function", "popup": { "main": { "label": "≥" } } },
{ "label": "comma", "type": "numeric", "width": 0.3 },
{ "label": "0", "type": "numeric", "width": 0.3 },
{ "label": "period", "type": "numeric", "width": 0.3 },
{ "label": "=", "type": "function", "popup": { "relevant": [ { "label": "≠"}, { "label": "≈"} ] } },
{ "label": "=", "type": "function", "popup": { "relevant": [ { "label": "≠"}, { "label": "≈"} ] } },
{ "label": "symbol" }
]
]

View file

@ -3,13 +3,13 @@
{ "label": "1", "type": "numeric" },
{ "label": "2", "type": "numeric", "popup": { "main": { "label": "ABC" } } },
{ "label": "3", "type": "numeric", "popup": { "main": { "label": "DEF" } } },
{ "label": "-", "popup": { "main": { "label": "+" } }, "labelFlags": 1073742336 }
{ "label": "-", "type": "function", "labelFlags": 1073742400, "popup": { "main": { "label": "+" } } }
],
[
{ "label": "4", "type": "numeric", "popup": { "main": { "label": "GHI" } } },
{ "label": "5", "type": "numeric", "popup": { "main": { "label": "JKL" } } },
{ "label": "6", "type": "numeric", "popup": { "main": { "label": "MNO" } } },
{ "label": "space" }
{ "label": "space", "type": "function" }
],
[
{ "label": "7", "type": "numeric", "popup": { "main": { "label": "PQRS" } } },
@ -18,9 +18,9 @@
{ "label": "delete" }
],
[
{ "label": "|!code/key_switch_alpha_symbol", "labelFlags": 524432 },
{ "label": "|!code/key_switch_alpha_symbol", "type": "numeric", "labelFlags": 524432 },
{ "label": "0 +|0", "type": "numeric", "popup": { "relevant": [ { "label": "!noPanelAutoPopupKey!" }, { "label": "+" } ] } },
{ "label": "." },
{ "label": ".", "type": "numeric", "labelFlags": 64 },
{ "label": "action" }
]
]

View file

@ -3,24 +3,24 @@
{ "label": "(", "type": "numeric" },
{ "label": "/", "type": "numeric" },
{ "label": ")", "type": "numeric" },
{ "label": "-", "popup": { "main": { "label": "+" } } }
{ "label": "-", "type": "function", "labelFlags": 1073742400, "popup": { "main": { "label": "+" } } }
],
[
{ "label": "N", "type": "numeric" },
{ "label": "!string/label_pause_key|,", "type": "numeric" },
{ "label": "!string/label_pause_key", "code": 44, "type": "numeric" },
{ "label": ",", "type": "numeric" },
{ "label": "space" }
{ "label": "space", "type": "function" }
],
[
{ "label": "|*", "type": "numeric" },
{ "label": "!string/label_wait_key|;", "type": "numeric" },
{ "label": "!string/label_wait_key", "code": 59, "type": "numeric" },
{ "label": "\\#", "type": "numeric" },
{ "label": "delete" }
],
[
{ "label": "123|!code/key_switch_alpha_symbol", "labelFlags": 524432 },
{ "label": "123|!code/key_switch_alpha_symbol", "type": "numeric", "labelFlags": 524432 },
{ "label": "+", "type": "numeric" },
{ "label": "." },
{ "label": ".", "type": "numeric" },
{ "label": "action" }
]
]

View file

@ -224,9 +224,10 @@ public class KeyboardParams {
R.styleable.Keyboard_keyboardRightPadding, width, width, 0);
mBaseWidth = mOccupiedWidth - mLeftPadding - mRightPadding;
final float defaultKeyWidthFactor = context.getResources().getInteger(R.integer.config_screen_metrics) > 2
? 0.9f : 1f;
mDefaultKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
final float defaultKeyWidthFactor = context.getResources().getInteger(R.integer.config_screen_metrics) > 2 ? 0.9f : 1f;
mDefaultKeyWidth = mId.isNumberLayout()
? 0.17f
: keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
1, 1, defaultKeyWidthFactor / DEFAULT_KEYBOARD_COLUMNS);
mDefaultAbsoluteKeyWidth = (int) (mDefaultKeyWidth * mBaseWidth);

View file

@ -48,11 +48,13 @@ import java.io.File
*/
abstract class KeyboardParser(private val params: KeyboardParams, private val context: Context) {
private val infos = layoutInfos(params)
private val defaultLabelFlags = if (params.mId.isAlphabetKeyboard) {
params.mLocaleKeyboardInfos.labelFlags
} else if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS || params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) {
Key.LABEL_FLAGS_DISABLE_HINT_LABEL // reproduce the no-hints in symbol layouts, todo: add setting
} else 0
private val defaultLabelFlags = when {
params.mId.isAlphabetKeyboard -> params.mLocaleKeyboardInfos.labelFlags
// reproduce the no-hints in symbol layouts
// todo: add setting? or put it in TextKeyData to happen only if no label flags specified explicitly?
params.mId.isAlphaOrSymbolKeyboard -> Key.LABEL_FLAGS_DISABLE_HINT_LABEL
else -> 0
}
abstract fun parseCoreLayout(layoutContent: String): MutableList<List<KeyData>>
@ -66,16 +68,10 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
else
params.mTouchPositionCorrection.load(context.resources.getStringArray(infos.touchPositionCorrectionData))
val baseKeys: MutableList<List<KeyData>> = parseCoreLayout(layoutContent)
val keysInRows = if (params.mId.isAlphaOrSymbolKeyboard) {
createAlphaSymbolRows(baseKeys)
} else if (params.mId.isNumberLayout) {
createNumericRows(baseKeys)
} else {
throw(UnsupportedOperationException("creating KeyboardId ${params.mId.mElementId} not supported"))
}
// rescale height if we have more than 4 rows
val heightRescale = if (keysInRows.size > 4) 4f / keysInRows.size else 1f
val baseKeys = parseCoreLayout(layoutContent)
val keysInRows = createRows(baseKeys)
// rescale height if we have anything but the usual 4 rows
val heightRescale = if (keysInRows.size != 4) 4f / keysInRows.size else 1f
if (heightRescale != 1f) {
keysInRows.forEach { row -> row.forEach { it.mHeight *= heightRescale } }
}
@ -86,7 +82,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
// this should be ready for customizable functional layouts, but needs cleanup
// todo (later): remove this as part of adding a cache for parsed layouts
private fun getFunctionalKeyLayoutText(): String {
if (!params.mId.isAlphaOrSymbolKeyboard) throw IllegalStateException("functional key layout only for aloha and symbol layouts")
if (params.mId.isNumberLayout) return "[]" // empty list
val layouts = Settings.getLayoutsDir(context).list() ?: emptyArray()
if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) {
if ("functional_keys_symbols_shifted.json" in layouts)
@ -102,15 +98,21 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
return context.readAssetsLayoutFile(fileName)
}
private fun createAlphaSymbolRows(baseKeys: MutableList<List<KeyData>>): ArrayList<ArrayList<KeyParams>> {
private fun createRows(baseKeys: MutableList<List<KeyData>>): ArrayList<ArrayList<KeyParams>> {
// add padding for number layouts in landscape mode (maybe do it some other way later)
if (params.mId.isNumberLayout && params.mId.mElementId != KeyboardId.ELEMENT_NUMPAD
&& context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
params.mLeftPadding = (params.mOccupiedWidth * 0.1f).toInt()
params.mRightPadding = (params.mOccupiedWidth * 0.1f).toInt()
params.mBaseWidth = params.mOccupiedWidth - params.mLeftPadding - params.mRightPadding
}
addNumberRowOrPopupKeys(baseKeys)
if (params.mId.isAlphabetKeyboard)
addSymbolPopupKeys(baseKeys)
if (params.mId.mNumberRowEnabled)
baseKeys.add(
0,
params.mLocaleKeyboardInfos.getNumberRow()
.map { it.copy(newLabelFlags = Key.LABEL_FLAGS_DISABLE_HINT_LABEL or defaultLabelFlags) })
if (params.mId.isAlphaOrSymbolKeyboard && params.mId.mNumberRowEnabled)
baseKeys.add(0, params.mLocaleKeyboardInfos.getNumberRow()
.map { it.copy(newLabelFlags = Key.LABEL_FLAGS_DISABLE_HINT_LABEL or defaultLabelFlags) })
val allFunctionalKeys = JsonKeyboardParser(params, context).parseCoreLayout(getFunctionalKeyLayoutText())
adjustBottomFunctionalRowAndBaseKeys(allFunctionalKeys, baseKeys)
@ -126,7 +128,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
val functionalKeys = mutableListOf<Pair<List<KeyParams>, List<KeyParams>>>()
val baseKeyParams = baseKeys.mapIndexed { i, it ->
val row: List<KeyData> = if (i == baseKeys.lastIndex - 1 && Settings.getInstance().isTablet) {
val row: List<KeyData> = if (params.mId.isAlphaOrSymbolKeyboard && i == baseKeys.lastIndex - 1 && Settings.getInstance().isTablet) {
// add bottom row extra keys
// todo (later): this can make very customized layouts look awkward
// decide when to (not) add it
@ -142,13 +144,14 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
val functionalKeysFromBottom = functionalKeysBottom.getOrNull(i - bottomIndexOffset) ?: emptyList()
functionalKeys.add(getFunctionalKeysBySide(functionalKeysFromTop, functionalKeysFromBottom))
row.map { key ->
row.mapNotNull { key ->
val extraFlags = if (key.label.length > 2 && key.label.codePointCount(0, key.label.length) > 2 && !isEmoji(key.label))
Key.LABEL_FLAGS_AUTO_X_SCALE
else 0
val keyData = key.processFunctionalKeys() ?: return@mapNotNull null // all keys could actually be functional keys...
if (DebugFlags.DEBUG_ENABLED)
Log.d(TAG, "adding key ${key.label}, ${key.code}")
key.toKeyParams(params, defaultLabelFlags or extraFlags)
Log.d(TAG, "adding key ${keyData.label}, ${keyData.code}")
keyData.toKeyParams(params, defaultLabelFlags or extraFlags)
}
}
return setReasonableWidths(baseKeyParams, functionalKeys)
@ -247,7 +250,6 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
}
// replace comma / period if 2 keys in normal bottom row
if (baseKeys.last().size == 2) {
Log.i("test", "$functionalKeysBottom")
functionalKeysBottom.replaceFirst(
{ it.label == KeyLabel.COMMA || it.groupId == KeyData.GROUP_COMMA},
{ baseKeys.last()[0].copy(newGroupId = 1, newType = baseKeys.last()[0].type ?: it.type) }
@ -256,7 +258,6 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
{ it.label == KeyLabel.PERIOD || it.groupId == KeyData.GROUP_PERIOD},
{ baseKeys.last()[1].copy(newGroupId = 2, newType = baseKeys.last()[1].type ?: it.type) }
)
Log.i("test", "$functionalKeysBottom")
baseKeys.removeLast()
}
// add those extra keys depending on layout (remove later)
@ -304,14 +305,11 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
// this is not nice in here, but otherwise we'd need context, and defaultLabelFlags and infos for toKeyParams
// improve it later, but currently this messy way is still ok
private fun KeyData.processFunctionalKeys(): KeyData? {
if (label == KeyLabel.PERIOD) {
// todo: why defaultLabelFlags exactly here? is this for armenian or bengali period labels? try removing also check in holo theme
return copy(newLabelFlags = labelFlags or defaultLabelFlags)
}
if (label == KeyLabel.SHIFT && !infos.hasShiftKey) return null
if (label != KeyLabel.ACTION) return this
return copy(
private fun KeyData.processFunctionalKeys(): KeyData? = when (label) {
// todo: why defaultLabelFlags exactly here? is this for armenian or bengali period labels? try removing also check in holo theme
KeyLabel.PERIOD -> copy(newLabelFlags = labelFlags or defaultLabelFlags)
KeyLabel.SHIFT -> if (infos.hasShiftKey) this else null
KeyLabel.ACTION -> copy(
// todo: evaluating the label should actually only happen in toKeyParams
// this label change already makes it necessary to provide the background in here too, because toKeyParams can't use action as label
newLabel = "${getActionKeyLabel()}|${getActionKeyCode()}",
@ -319,6 +317,15 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
// the label change is messing with toKeyParams, so we need to supply the appropriate BG type here
newType = type ?: KeyType.ENTER_EDITING
)
else -> {
// this is ugly...
if (label.length > 8 && label.startsWith("!string/")) {
val id = context.resources.getIdentifier(label.substringAfter("!string/"), "string", context.packageName)
Log.i("test", "id of $label: $id")
if (id != 0) copy(newLabel = getInLocale(id))
else this
} else this
}
}
private fun addNumberRowOrPopupKeys(baseKeys: MutableList<List<KeyData>>) {
@ -363,87 +370,6 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
}
}
private fun createNumericRows(baseKeys: MutableList<List<KeyData>>): ArrayList<ArrayList<KeyParams>> {
val keysInRows = ArrayList<ArrayList<KeyParams>>()
if (context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && params.mId.mElementId != KeyboardId.ELEMENT_NUMPAD) {
// add padding here instead of using xml (actually this is not good... todo (later))
params.mLeftPadding = (params.mOccupiedWidth * 0.1f).toInt()
params.mRightPadding = (params.mOccupiedWidth * 0.1f).toInt()
params.mBaseWidth = params.mOccupiedWidth - params.mLeftPadding - params.mRightPadding
}
baseKeys.forEachIndexed { i, row ->
val paramsRow = ArrayList<KeyParams>()
row.forEach { key ->
var keyParams: KeyParams? = null
// try parsing a functional key
val functionalKeyName = when (key.label) {
// todo (later): maybe add special popupKeys for phone and number layouts?
"." -> if (params.mId.mElementId == KeyboardId.ELEMENT_NUMPAD) KeyLabel.PERIOD else "."
"," -> if (params.mId.mElementId == KeyboardId.ELEMENT_NUMPAD) KeyLabel.COMMA else ","
else -> key.label
}
if (functionalKeyName.length > 1 && key.type != KeyType.NUMERIC) {
try {
keyParams = key.copy(newLabel = functionalKeyName).processFunctionalKeys()!!.toKeyParams(params)
} catch (_: Throwable) {} // just use normal label
}
if (keyParams == null) {
keyParams = if (key.type == KeyType.NUMERIC) {
val labelFlags = when (params.mId.mElementId) {
KeyboardId.ELEMENT_PHONE -> Key.LABEL_FLAGS_ALIGN_LABEL_OFF_CENTER or Key.LABEL_FLAGS_HAS_HINT_LABEL or Key.LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO
KeyboardId.ELEMENT_PHONE_SYMBOLS -> 0
else -> Key.LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO
}
key.toKeyParams(params, labelFlags or defaultLabelFlags)
} else if (key.label.length == 1 && (params.mId.mElementId == KeyboardId.ELEMENT_PHONE || params.mId.mElementId == KeyboardId.ELEMENT_NUMBER))
key.toKeyParams(params, additionalLabelFlags = Key.LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO or defaultLabelFlags)
else
key.toKeyParams(params, additionalLabelFlags = defaultLabelFlags)
}
if (key.type != KeyType.NUMERIC && keyParams.mBackgroundType != Key.BACKGROUND_TYPE_ACTION)
keyParams.mBackgroundType = Key.BACKGROUND_TYPE_FUNCTIONAL
if (params.mId.mElementId == KeyboardId.ELEMENT_PHONE && key.popup.main?.getPopupLabel(params)?.length?.let { it > 1 } == true) {
keyParams.mPopupKeys = null // the ABC and stuff labels should not create popupKeys
}
if (keyParams.mLabel?.length?.let { it > 1 } == true && keyParams.mLabel?.startsWith("!string/") == true) {
// resolve string label
val id = context.resources.getIdentifier(keyParams.mLabel?.substringAfter("!string/"), "string", context.packageName)
if (id != 0)
keyParams.mLabel = getInLocale(id)
}
paramsRow.add(keyParams)
if (DebugFlags.DEBUG_ENABLED)
Log.d(TAG, "adding key ${keyParams.mLabel}, ${keyParams.mCode}")
}
if (i == baseKeys.lastIndex) { // bottom row needs some adjustments
val n = row.indexOfFirst { it.type == KeyType.NUMERIC }
if (n != -1) {
// make sure the keys next to 0 have normal background
paramsRow.getOrNull(n - 1)?.mBackgroundType = Key.BACKGROUND_TYPE_NORMAL
paramsRow.getOrNull(n + 1)?.mBackgroundType = Key.BACKGROUND_TYPE_NORMAL
// make those keys same width as numeric keys except in numpad layout
// but determine from row size instead of from elementId, in case user wants to adjust numpad layout
if (row.size == baseKeys[0].size) {
paramsRow.getOrNull(n - 1)?.mWidth = paramsRow[n].mWidth
paramsRow.getOrNull(n + 1)?.mWidth = paramsRow[n].mWidth
} else if (row.size == baseKeys[0].size + 2) {
// numpad last row -> make sure the keys next to 0 fit nicely
paramsRow.getOrNull(n - 1)?.mWidth = paramsRow[n].mWidth * 0.55f
paramsRow.getOrNull(n - 2)?.mWidth = paramsRow[n].mWidth * 0.45f
paramsRow.getOrNull(n + 1)?.mWidth = paramsRow[n].mWidth * 0.55f
paramsRow.getOrNull(n + 2)?.mWidth = paramsRow[n].mWidth * 0.45f
}
}
}
val widthSum = paramsRow.sumOf { it.mWidth }
paramsRow.forEach { it.mWidth /= widthSum }
keysInRows.add(paramsRow)
}
return keysInRows
}
private fun getActionKeyLabel(): String {
if (params.mId.isMultiLine && (params.mId.mElementId == KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED || params.mId.mElementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED))
return "!icon/enter_key"

View file

@ -43,6 +43,7 @@ enum class KeyType {
"space" -> NAVIGATION
"action" -> ENTER_EDITING
"shift" -> LOCK
"normal" -> CHARACTER
else -> valueOf(string.uppercase())
}
}

View file

@ -144,11 +144,21 @@ sealed interface KeyData : AbstractKeyData {
override fun compute(params: KeyboardParams): KeyData {
require(groupId <= GROUP_ENTER) { "only groups up to GROUP_ENTER are supported" }
require(label.isNotEmpty() || type == KeyType.PLACEHOLDER || code != KeyCode.UNSPECIFIED) { "non-placeholder key has no code and no label" }
require(width >= 0f || width == -1f) { "illegal width $width" }
val newLabel = label.convertFlorisLabel()
val newCode = code.checkAndConvertCode()
val newLabelFlags = if (labelFlags == 0 && params.mId.isNumberLayout) {
if (type == KeyType.NUMERIC) {
when (params.mId.mElementId) {
KeyboardId.ELEMENT_PHONE -> Key.LABEL_FLAGS_ALIGN_LABEL_OFF_CENTER or Key.LABEL_FLAGS_HAS_HINT_LABEL or Key.LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO
KeyboardId.ELEMENT_PHONE_SYMBOLS -> 0
else -> Key.LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO
}
} else 0
} else labelFlags
if (newCode != code || newLabel != label)
return copy(newCode = newCode, newLabel = newLabel)
if (newCode != code || newLabel != label || labelFlags != newLabelFlags)
return copy(newCode = newCode, newLabel = newLabel, newLabelFlags = newLabelFlags)
return this
}
@ -258,7 +268,7 @@ sealed interface KeyData : AbstractKeyData {
private fun getDefaultWidth(params: KeyboardParams): Float {
return if (label == KeyLabel.SPACE && params.mId.isAlphaOrSymbolKeyboard) -1f
else if (type == KeyType.NUMERIC && params.mId.isNumberLayout) 0.17f // todo (later) consider making this -1?
else if (type == KeyType.NUMERIC && params.mId.isNumberLayout) -1f
else params.mDefaultKeyWidth
}

View file

@ -38,12 +38,13 @@ If the layout has exactly 2 keys in the bottom row, these keys will replace comm
### Properties
* A (non-selector) key can have the following properties:
* `type`: only specific values, HeliBoard mostly uses this to determine background color and type, determined automatically by default
* `character`: normal key color
* `normal`: normal key color
* `function`: functional key color
* `space`: space bar color
* `action`: action key color
* `unspecified`: no background color
* `placeholder`: no background color, no label, and pressing the key does nothing
* `numeric`: normal key color, only in number layouts: sets default width to `-1` and sets default label flags if none specified
* There are some more values, but they do nothing
* `code`: code point that is entered when the key is pressed, determined from the label by default, not available for `multi_text_key`
* There are special negative values available, e.g. the ones used by functional keys, see [KeyCode.kt](/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt). There are several not yet supported key codes in there, you can see in the function `checkAndConvertCode` which ones are working.
@ -57,9 +58,10 @@ If the layout has exactly 2 keys in the bottom row, these keys will replace comm
* `width`: width of the key in units of screen width, e.g. a key with `"width": 0.1` has a width of 10% of the screen, defaults to `0`
* A special value is `-1`, which means the key expands to the available space not already used by other keys (e.g. the space bar)
* `0` is interpreted as follows
* `-1` on the `space` key in alphabet or symbols layouts
* `0.17` for keys with `"type": numeric` in number layouts
* Otherwise the default width is used, which is `0.1` for phones and `0.09` for tablets
* `-1` on the `space` key in alphabet or symbols layouts, and for keys with `"type": numeric` in number layouts
* `0.17` for number layouts
* `0.1` for phones
* `0.09` for tablets
* If the sum of widths in a row is greater than 1, keys are rescaled to fit on the screen
* `labelFlags`: allows specific effects, see [here](app/src/main/res/values/attrs.xml) in the section _keyLabelFlags_ for names and numeric values