improvements for tablet layouts with new parser

This commit is contained in:
Helium314 2023-12-04 12:11:53 +01:00
parent 8edf006a32
commit 2232bc3848
11 changed files with 78 additions and 59 deletions

View file

@ -73,7 +73,7 @@ Features that may go unnoticed
* Reduce space between keys, with option to use old values, https://github.com/Helium314/openboard/pull/8
* Fix number row not split in split keyboard view, https://github.com/Helium314/openboard/pull/27
* Fix issue with spell checker incorrectly flagging words before a period as wrong on newer Android versions, https://github.com/openboard-team/openboard/pull/679
* maybe not properly fixed, this causes some other issues
* maybe not properly fixed, this causes some other issues, https://github.com/Helium314/openboard/issues/55
* Fix always-dark settings on some Android versions, https://github.com/Helium314/openboard/pull/69
* Fix bug with space before word being deleted in some apps / input fields, https://github.com/Helium314/openboard/commit/ce0bf06545c4547d3fc5791cc769508db0a89e87
* Allow using auto theme on some devices with Android 9
@ -82,11 +82,14 @@ Features that may go unnoticed
* Updated translations
* Open dictionary files with the app
* Add more options to the language switch key
* New keyboard parser (no need to use `tools:make-keyboard-text:makeText` any more)
* Can use simple text files or JSON files as used by [FlorisBoard](https://github.com/florisboard/florisboard/tree/master/app/src/main/assets/ime/keyboard/org.florisboard.layouts/layouts)
## The rough plan/todo before "full" release
* Finish keyboard parsing upgrades
* In the end, layouts should be defined in either simple text files, or json files, ideally in same format as used by [FlorisBoard](https://github.com/florisboard/florisboard/tree/master/app/src/main/assets/ime/keyboard/org.florisboard.layouts/layouts)
* Users should be allowed to add their own layouts
* Determine symbol popup keys from symbols layout instead of hardcoding them to the layout (still allow layout to override or add popup keys)
* Allow users more control over popup keys (which hint label to show, which popup keys (language, layout, symbols) to show first or at all)
* Overhaul the view system
* Have a fixed height common to all views (keyboard, emoji, clipboard)
* Should allow for more flexible one-handed mode (though the actual one-handed mode changes may be implemented later)

View file

@ -18,6 +18,7 @@ punctuation !fixedColumnOrder!7 ٕ|ٕ ٔ|ٔ ْ|ْ ٍ|ٍ ٌ|ٌ ً|ً ّ|ّ
alphabet: أ‌ب‌ج
symbol: ٣٢١؟
comma: ،
question: ؟
[number_row]
١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩ ٠

View file

@ -11,6 +11,7 @@ punctuation !fixedColumnOrder!7 ٕ|ٕ ْ|ْ ّ|ّ ٌ|ٌ ٍ|ٍ ً|ً ٔ|ٔ
alphabet: ا‌ب‌پ
symbol: ۳۲۱؟
comma: ،
question: ؟
[number_row]
۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹ ۰

View file

@ -25,6 +25,7 @@ alphabet: اب‌پ
comma: ،
symbol: ۳۲۱؟
period: ۔
question: ؟
[number_row]
۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹ ۰

View file

@ -25,4 +25,4 @@ $$$
:
؛ ;
!
؟ ? ¿
؟

View file

@ -978,6 +978,7 @@ public class Key implements Comparable<Key> {
public static KeyParams newSpacer(final KeyboardParams params, final float relativeWidth) {
final KeyParams spacer = new KeyParams(params);
spacer.mRelativeWidth = relativeWidth;
spacer.mRelativeHeight = params.mDefaultRelativeRowHeight;
return spacer;
}

View file

@ -63,30 +63,14 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
return this
// todo: further plan
// migrate other languages/layouts to this style
// test whether the layouts really are the same
// comparing params with both parsers looks good, see list of detected differences below
// still need to check moreKeys, there will be many more differences that might just be minor
// write down a list of differences, ask users to open issues if they don't like them
// some keyboard_layout_set have supportedScript that is enum synced with script id in ScriptUtils
// that's one more reason for using language tags...
// but currently it's still read from xml outside the keyboard parser, so that's fine for now
// issues:
// armenian bottom row (and some more): functional keys could be narrower
// rtl parentheses hint label (urdu and more)
// urdu and others: no labels because the moreKeys are languageMoreKeys -> need the moreKeys setting soon (at least setting to show first language moreKey if no symbol)
// setting: symbol morekey(s): layout default, take from symbols, layout default but fill from symbols if empty
// setting: hint label: from symbol morekey only, symbol but language if none, first choice on long press
// (later): setting which moreKeys to prefer (default: symbol or important language, always symbol, always language)
// (later): setting whether to show duplicate moreKeys (describe properly what it actually does)
// and have some setting to enable configuring this per locale (in language settings -> potentially should not be a dialog any more but a fragment?)
// label flags are horrible to "decompile" when viewing a layout
// migrate pcqwerty to this style
// this will be more complicated...
// linked shift keys might be easy
// distance below number row with high row, but reduced key height?
// consider settings key needs to disappear if device is locked
// ignore number row setting?
// ignore number row setting to avoid duplicate number row?
// and test tablet layout!
// do the moreKeys thing (only now, because pc layout may change stuff)
// migrate keypad layouts to this style
// will need more configurable layout definition -> another parser, or do it with compatible jsons
// make the remove duplicate moreKey thing an option?
@ -100,9 +84,16 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
// write another parser, it should already consider split
// add a setting to display all emojis (and use emojiv2 or emojicompat or whatever is necessary)
// mention in subtitle that they may not be displayed properly, depending on the app you're writing in
// now all layouts should be using the new parser -> throw an error instead of falling back to old parser
// more settings for localized number row, so it can be different in shift or symbols
// migrate moreKeys and moreSuggestions to this style?
// at least they should not make use of the KeyTextsSet/Table (and of the XmlKeyboardParser?)
// setting which moreKeys to prefer (default: symbol or important language, always symbol, always language)
// setting whether to show duplicate moreKeys (describe properly what it actually does)
// some keyboard_layout_set have supportedScript that is enum synced with script id in ScriptUtils
// that's one more reason for using language tags...
// currently it's still read from xml outside the keyboard parser, but should still go to some other place
// maybe use scriptUtils to determine, just make sure it's correct (also for hindi and serbian!)
// remove the old parser
// then finally the spanish/german/swiss/nordic layouts can be removed and replaced by some hasExtraKeys parameter
// also the eo check could then be removed
@ -155,7 +146,7 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
// autoXScale: com key, action keys, some on phone layout, some non-latin languages
// autoScale: only one single letter in khmer layout (includes autoXScale)
// preserveCase: action key + more keys, com key, shift keys
// shiftedLetterActivated: period and some keys on pcqwerty, tablet only
// shiftedLetterActivated: period and some keys on pcqwerty, tablet only (wtf, when enabled can't open moreKeys -> remove? or what would be the use?)
// fromCustomActionLabel: action key with customLabelActionKeyStyle -> check parser where to get this info
// followFunctionalTextColor: number mode keys, action key
// keepBackgroundAspectRatio: lxx and rounded action more keys, lxx no-border action and emoji, moreKeys keyboard view
@ -189,18 +180,6 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
return@forEachIndexed
}
xmlRow2.forEachIndexed { index1, xmlParams ->
// todo: compare tablet layouts (how to best force it for both parsers?)
// just rename the sw600 folders to sw 360
// ->
// to shift symbols label should be ~ [ <
// last symbols row should be \ = * " ' : ; ! ? (but is * " ' : ; ! ? ! ?)
// last shift symbols row should have inverted ! and ?
// some different label flags
// ar: last symbols row should be \ = * " ' : ; ! ؟ (but is * « » : ; ! ؟ ! ?)
// ar: layout should not have ! and ? added (just empty space here...)
// ru, sr (both), others don't have a right shift key (come on...)
// but bulgarian (default) has -> not even per language
// armenian (and probably other 4 row layouts) messed up (delete key should be in first for, not x from bottom)
val keyParams = row[index1]
if (keyParams.mLabel != xmlParams.mLabel)
// currency keys (shift symbol) arranged differently

View file

@ -5,6 +5,7 @@ import android.content.res.Resources
import android.util.Log
import android.view.inputmethod.EditorInfo
import android.widget.Toast
import androidx.annotation.StringRes
import org.dslul.openboard.inputmethod.keyboard.Key
import org.dslul.openboard.inputmethod.keyboard.Key.KeyParams
import org.dslul.openboard.inputmethod.keyboard.KeyboardId
@ -12,7 +13,6 @@ import org.dslul.openboard.inputmethod.keyboard.KeyboardTheme
import org.dslul.openboard.inputmethod.keyboard.internal.KeyboardIconsSet
import org.dslul.openboard.inputmethod.keyboard.internal.KeyboardParams
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.floris.KeyData
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.floris.toTextKey
import org.dslul.openboard.inputmethod.latin.R
import org.dslul.openboard.inputmethod.latin.common.Constants
import org.dslul.openboard.inputmethod.latin.common.splitOnWhitespace
@ -75,28 +75,29 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
Toast.makeText(context, message, Toast.LENGTH_LONG).show()
}
}
val functionalKeysReversed = parseFunctionalKeys().reversed()
val functionalKeysReversed = parseFunctionalKeys(R.string.key_def_functional).reversed()
val functionalKeysTop = parseFunctionalKeys(R.string.key_def_functional_top_row)
// keyboard parsed bottom-up because the number of rows is not fixed, but the functional keys
// are always added to the rows near the bottom
keysInRows.add(getBottomRowAndAdjustBaseKeys(baseKeys))
baseKeys.reversed().forEachIndexed { i, it ->
val row: List<KeyData> = if (i == 0) {
val row: List<KeyData> = if (i == 0 && isTablet()) {
// add bottom row extra keys
// todo: question mark might be different -> get it from localeKeyTexts
// also, maybe check device dimension int instead of getting this from resources, then using language labels is easier
// and in shift symbols it should be inverted question/exclamation marks
it + context.getString(R.string.key_def_extra_bottom_right)
.split(",").mapNotNull { if (it.isBlank()) null else it.trim().toTextKey(labelFlags = Key.LABEL_FLAGS_FONT_DEFAULT) }
val tabletExtraKeys = params.mLocaleKeyTexts.getTabletExtraKeys(params.mId.mElementId)
tabletExtraKeys.first + it + tabletExtraKeys.second
} else {
it
}
// parse functional keys for this row (if any)
val outerFunctionalKeyDefs = if (i == baseKeys.lastIndex && functionalKeysTop.isNotEmpty()) functionalKeysTop.first()
else emptyList<String>() to emptyList()
val functionalKeysDefs = if (i < functionalKeysReversed.size) functionalKeysReversed[i]
else emptyList<String>() to emptyList()
val functionalKeysLeft = functionalKeysDefs.first.map { getFunctionalKeyParams(it) }
val functionalKeysRight = functionalKeysDefs.second.map { getFunctionalKeyParams(it) }
// if we have a top row and top row entries from normal functional key defs, use top row as outer keys
val functionalKeysLeft = outerFunctionalKeyDefs.first.map { getFunctionalKeyParams(it) } + functionalKeysDefs.first.map { getFunctionalKeyParams(it) }
val functionalKeysRight = functionalKeysDefs.second.map { getFunctionalKeyParams(it) } + outerFunctionalKeyDefs.second.map { getFunctionalKeyParams(it) }
val paramsRow = ArrayList<KeyParams>(functionalKeysLeft)
// determine key width, maybe scale factor for keys, and spacers to add
@ -152,7 +153,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
// so the symbols keyboard is higher than the normal one
// not a new issue, but should be solved in this migration
// how? possibly scale all keyboards to height of main alphabet? (consider suggestion strip)
keysInRows.forEach { key -> key.forEach { it.mRelativeHeight *= heightRescale } }
keysInRows.forEach { row -> row.forEach { it.mRelativeHeight *= heightRescale } }
}
return keysInRows
@ -184,8 +185,8 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
lastNormalRow.add(lastNormalRow.indexOfLast { it.mBackgroundType == Key.BACKGROUND_TYPE_NORMAL } + 1, KeyParams.newSpacer(params, spacerWidth))
}
private fun parseFunctionalKeys(): List<Pair<List<String>, List<String>>> =
context.getString(R.string.key_def_functional).split("\n").mapNotNull { line ->
private fun parseFunctionalKeys(@StringRes id: Int): List<Pair<List<String>, List<String>>> =
context.getString(id).split("\n").mapNotNull { line ->
if (line.isBlank()) return@mapNotNull null
val p = line.split(";")
splitFunctionalKeyDefs(p.first()) to splitFunctionalKeyDefs(p.last())
@ -491,6 +492,17 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
moreKeys.add("$replacementText|${iconPrefixRemoved.substringAfter("|")}")
}
}
// remove emoji shortcut on enter in tablet mode (like original, because bottom row always has an emoji key)
// (probably not necessary, but whatever)
if (isTablet() && moreKeys.remove("!icon/emoji_action_key|!code/key_emoji")) {
val i = moreKeys.indexOfFirst { it.startsWith("!fixedColumnOrder") }
if (i > -1) {
val n = moreKeys[i].substringAfter("!fixedColumnOrder!").toIntOrNull()
if (n != null)
moreKeys[i] = moreKeys[i].replace(n.toString(), (n - 1).toString())
}
// remove emoji on enter, because tablet layout has a separate emoji key
}
return moreKeys.toTypedArray()
}
@ -531,7 +543,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
if (elementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED)
return params.mLocaleKeyTexts.labelSymbol
if (elementId == KeyboardId.ELEMENT_SYMBOLS)
return params.mLocaleKeyTexts.labelShiftSymbol
return params.mLocaleKeyTexts.getShiftSymbolLabel(isTablet())
if (elementId == KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED || elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED
|| elementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED || elementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED)
return "!icon/shift_key_shifted"
@ -569,8 +581,8 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
for (i in moreKeys.indices)
moreKeys[i] = moreKeys[i].rtlLabel(params) // for parentheses
}
if (context.resources.getInteger(R.integer.config_screen_metrics) >= 3 && moreKeys.contains("!") && moreKeys.contains("?")) {
// we have a tablet, remove ! and ? keys and reduce number in autoColumnOrder
if (isTablet() && moreKeys.contains("!") && moreKeys.contains("?")) {
// remove ! and ? keys and reduce number in autoColumnOrder
// this makes use of removal of empty moreKeys in MoreKeySpec.insertAdditionalMoreKeys
moreKeys[moreKeys.indexOf("!")] = ""
moreKeys[moreKeys.indexOf("?")] = ""
@ -581,6 +593,8 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
return moreKeys
}
private fun isTablet() = context.resources.getInteger(R.integer.config_screen_metrics) >= 3
companion object {
private val TAG = KeyboardParser::class.simpleName

View file

@ -3,6 +3,7 @@ package org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser
import android.content.Context
import org.dslul.openboard.inputmethod.keyboard.Key
import org.dslul.openboard.inputmethod.keyboard.KeyboardId
import org.dslul.openboard.inputmethod.keyboard.internal.KeyboardParams
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.floris.KeyData
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.floris.toTextKey
@ -20,12 +21,13 @@ class LocaleKeyTexts(dataStream: InputStream?, locale: Locale) {
private set
var labelAlphabet = "ABC"
private set
var labelShiftSymbol = "= \\\\ <"
private set
private var labelShiftSymbol = "= \\\\ <"
private var labelShiftSymbolTablet = "~ [ <"
var labelComma = ","
private set
var labelPeriod = "."
private set
private var labelQuestion = "?"
val currencyKey = getCurrencyKey(locale)
private var numberKeys = ((1..9) + 0).map { it.toString() }
private val numbersMoreKeys = arrayOf(
@ -51,8 +53,8 @@ class LocaleKeyTexts(dataStream: InputStream?, locale: Locale) {
moreKeys["\""] = arrayOf("!fixedColumnOrder!5", "", "", "", "«", "»")
if ("!" !in moreKeys)
moreKeys["!"] = arrayOf("¡")
if ("?" !in moreKeys)
moreKeys["?"] = arrayOf("¿")
if (labelQuestion !in moreKeys)
moreKeys[labelQuestion] = if (labelQuestion == "?") arrayOf("¿") else arrayOf("?", "¿")
if ("punctuation" !in moreKeys)
moreKeys["punctuation"] = arrayOf("${Key.MORE_KEYS_AUTO_COLUMN_ORDER}8", "\\,", "?", "!", "#", ")", "(", "/", ";", "'", "@", ":", "-", "\"", "+", "\\%", "&")
}
@ -80,6 +82,19 @@ class LocaleKeyTexts(dataStream: InputStream?, locale: Locale) {
}
}
/** Pair(extraKeysLeft, extraKeysRight) */
// todo: they should be optional, or will unexpectedly appear on custom layouts
fun getTabletExtraKeys(elementId: Int): Pair<List<KeyData>, List<KeyData>> {
val flags = Key.LABEL_FLAGS_FONT_DEFAULT
return when (elementId) {
KeyboardId.ELEMENT_SYMBOLS -> listOf("\\".toTextKey(labelFlags = flags), "=".toTextKey(labelFlags = flags)) to emptyList()
KeyboardId.ELEMENT_SYMBOLS_SHIFTED -> emptyList<KeyData>() to listOf("¡".toTextKey(labelFlags = flags), "¿".toTextKey(labelFlags = flags))
else -> emptyList<KeyData>() to listOf("!".toTextKey(labelFlags = flags), labelQuestion.toTextKey(labelFlags = flags)) // assume alphabet
}
}
fun getShiftSymbolLabel(isTablet: Boolean) = if (isTablet) labelShiftSymbolTablet else labelShiftSymbol
// need tp provide a copy because some functions like MoreKeySpec.insertAdditionalMoreKeys may modify the array
fun getMoreKeys(label: String): Array<String>? = moreKeys[label]?.copyOf()
@ -125,8 +140,10 @@ class LocaleKeyTexts(dataStream: InputStream?, locale: Locale) {
"symbol" -> labelSymbol = split.last()
"alphabet" -> labelAlphabet = split.last()
"shift_symbol" -> labelShiftSymbol = split.last() // never used, but could be...
"shift_symbol_tablet" -> labelShiftSymbolTablet = split.last() // never used, but could be...
"comma" -> labelComma = split.last()
"period" -> labelPeriod = split.last()
"question" -> labelQuestion = split.last()
}
}

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="key_def_functional">";delete 10%
<string name="key_def_functional">"
;action 10%
shift 10%; shift"</string>
<string name="key_def_extra_bottom_right">"!, ?"</string>
<string name="key_def_functional_top_row" translatable="false">";delete 10%"</string>
<string name="key_def_bottom_row">"symbol, comma, space, period, emoji_com"</string>
</resources>

View file

@ -5,12 +5,14 @@
the left side, keys after are on the right side. Use comma to separate keys on the same side.
Valid key names are at the bottom of SimpleLayoutParser (todo: location may change)
Width (in percent of keyboard width) can be appended to the key name, defaults to default key width
Applied from bottom
-->
<string name="key_def_functional" translatable="false">"shift 15%; delete 15%"</string>
<!--
Extra keys to be inserted at the right side into row above space (only labels, comma-separated)
Functional key definitions for a single row, on top of the keyboard.
This is to consider that some keyboards have more than 3 rows (relevant for tablet layout)
-->
<string name="key_def_extra_bottom_right" translatable="false">""</string>
<string name="key_def_functional_top_row" translatable="false">""</string>
<!--
Bottom row definition is similar to functional key definition, but only one row, and not
split into two groups. Space bar will be adjusted in code for language and emoji keys,