Switch most latin layouts to use the simple parser (#276)

And do some tweaks so it works better
This commit is contained in:
Helium314 2023-11-19 21:16:23 +01:00 committed by GitHub
parent da6dcece7f
commit cbfa934721
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
69 changed files with 1448 additions and 177 deletions

View file

@ -0,0 +1,8 @@
[morekeys]
a á â ä à æ ã å ā
e é è ê ë ę ė ē
i í ì ï î į ī ij
o ó ô ö ò õ œ ø ō
u ú û ü ù ū
n ñ ń
y ý ij

View file

@ -0,0 +1,24 @@
[morekeys]
a á â ä à æ ã å ā ą ª ă ả ạ ằ ắ ẳ ẵ ặ ầ ấ ẩ ẫ ậ
e é è ê ë ę ė ē ə ě ẻ ẽ ẹ ề ế ể ễ ệ ĕ
i í ì ï î į ī ij ı ĩ ỉ ị ĭ
o ó ô ö ò õ œ ø ō º ő ỏ ọ ồ ố ổ ỗ ộ ơ ờ ớ ở ỡ ợ ŏ
u ú û ü ù ū ů ũ ű ų µ ủ ụ ư ừ ứ ử ữ ự ŭ
n ñ ń ň ņ ʼn ŋ
y ý ij ÿ y ŷ þ ỳ ỷ ỹ ỵ
s ş ß ś š ẞ ș ŝ ſ
g ğ ġ ģ g\' ĝ
c ç ć č ċ ĉ
z ž ź ż
l l·l ł ĺ ļ ľ ŀ
punctuation !autoColumnOrder!9 \, ? ! · # ) ( / ; ' @ : - " + \% & ¡ ¿
d ď ð đ
r ř ŕ ŗ
t ť ț ţ ŧ þ
k ķ ĸ
v w ŵ
h ĥ ħ
w w ŵ
q q
x x
j ĵ

View file

@ -0,0 +1,12 @@
[morekeys]
a â ä á
e ə é
i ı î ï ì í į ī
o ö ô œ ò ó õ ø ō
u ü û ù ú ū
s ş ß ś š
g ğ
n ň ñ
c ç ć č
y ý
z ž

View file

@ -0,0 +1,13 @@
[morekeys]
a à á ä â ã å ą æ ā ª
e è é ë ê ę ė ē
i í ï ì î į ī
o ò ó ö ô õ ø œ ō º
u ú ü ù û ū
n ñ ń
c ç ć č
l l·l ł
punctuation !autoColumnOrder!9 \, ? ! · # ) ( / ; ' @ : - " + \% &
[extra_keys]
2: ç

View file

@ -0,0 +1,16 @@
[morekeys]
a á à â ä æ ã å ā
e é ě è ê ë ę ė ē
i í î ï ì į ī
o ó ö ô ò õ œ ø ō
u ú ů û ü ù ū
s š ß ś
n ň ñ ń
c č ç ć
y ý ÿ
d ď
r ř
t ť
z ž ź ż
'
" ” „ “ » «

View file

@ -0,0 +1,18 @@
[morekeys]
a å æ á ä à â ã ā
e é ë
i í ï
o ø ö ó ô ò õ œ ō
u ú ü û ù ū
s ß ś š
n ñ ń
y ý ÿ
d ð
l ł
'
" ” „ “ » «
[extra_keys]
1: å
2: æ ä
2: ø ö

View file

@ -0,0 +1,14 @@
[morekeys]
a ä % â à á æ ã å ā
e é è ê ë ė
o ö % ô ò ó õ œ ø ō
u ü % û ù ú ū
s ß % ẞ ś š
n ñ ń
'
" ” „ “ » «
[extra_keys]
1: ü è
2: ö é
2: ä à

View file

@ -0,0 +1,14 @@
[morekeys]
a ä â à á æ ã å ā
e é è ê ë ė
o ö ô ò ó õ œ ø ō
u ü û ù ú ū
s ß % ś š
n ñ ń
'
" ” „ “ » «
[extra_keys]
1: ü è
2: ö é
2: ä à

View file

@ -0,0 +1,15 @@
[morekeys]
a ä â à á æ ã å ā
e é è ê ë ė
o ö ô ò ó õ œ ø ō
u ü û ù ú ū
s ß % ś š
n ñ ń
'
" ” „ “ » «
[extra_keys]
1: ü è
2: ö é
2: ä à
3: ß

View file

@ -0,0 +1,9 @@
[morekeys]
a à á â ä æ ã å ā
e é è ê ë ē
i í î ï ī ì
o ó ô ö ò œ ø ō õ
u ú û ü ù ū
s ß
n ñ
c ç

View file

@ -0,0 +1,22 @@
[morekeys]
a á à â ä æ ã å ā ă ą ª
e é ě è ê ë ę ė ē
i í î ï ĩ ì į ī ı ij
o ó ö ô ò õ œ ø ō ő º
u ú ů û ü ù ū ũ ű ų µ
s ß š ś ș ş
n ñ ń ņ ň ʼn ŋ
c ć č ç ċ
ŭ y ý ŷ ÿ þ
d ð ď đ
r ř ŕ ŗ
t ť ț ţ ŧ
z ź ż ž
k ķ ĸ
l ĺ ļ ľ ŀ ł
g ğ ġ ģ
v w ŵ
h ĥ ħ
ĝ w ŵ
ŝ q
ĉ x

View file

@ -0,0 +1,12 @@
[morekeys]
a á à ä â ã å ą æ ā ª
e é è ë ê ę ė ē
i í ï ì î į ī
o ó ò ö ô õ ø œ ō º
u ú ü ù û ū
n ñ ń
c ç ć č
punctuation !autoColumnOrder!9 \, ? ! # ) ( / ; ¡ ' @ : - " + \% & ¿
[extra_keys]
2: ñ

View file

@ -0,0 +1,24 @@
[morekeys]
a ä ā à á â ã å æ ą
e ē è ė é ê ë ę ě
i ī ì į í î ï ı
o ö õ ò ó ô œ ő ø
u ü ū ų ù ú û ů ű
s š ß ś ş
n ņ ñ ń
c č ç ć
y ý ÿ
d ď
r ŗ ř ŕ
t ţ ť
z ž ż ź
k ķ
l ļ ł ĺ ľ
g ģ ğ
'
" ” „ “
[extra_keys]
1: ü
2: ö õ
2: ä

View file

@ -0,0 +1,11 @@
[morekeys]
a á à ä â ã å ą æ ā ª
e é è ë ê ę ė ē
i í ï ì î į ī
o ó ò ö ô õ ø œ ō º
u ú ü ù û ū
n ñ ń
c ç ć č
[extra_keys]
2: ñ

View file

@ -0,0 +1,11 @@
[morekeys]
a ä å æ à á â ã ā
o ö ø ô ò ó õ œ ō
u ü
s š ß ś
z ž ź ż
[extra_keys]
1: å
2: ö ø
2: ä æ

View file

@ -0,0 +1,11 @@
[morekeys]
a á à ä â ã å ą æ ā ª
e é è ë ê ę ė ē
i í ï ì î į ī
o ó ò ö ô õ ø œ ō º
u ú ü ù û ū
n ñ ń
c ç ć č
[extra_keys]
2: ñ

View file

@ -0,0 +1,13 @@
[morekeys]
a à â % æ á ä ã å ā ª
e é è ê ë % ę ė ē
i î % ï ì í į ī
o ô œ % ö ò ó õ ø ō º
u ù û % ü ú ū
c ç % ć č
y % ÿ
[extra_keys]
1: è ü
2: é ö
2: à ä

View file

@ -0,0 +1,11 @@
[morekeys]
a á à ä â ã å ą æ ā ª
e é è ë ê ę ė ē
i í ï ì î į ī
o ó ò ö ô õ ø œ ō º
u ú ü ù û ū
n ñ ń
c ç ć č
[extra_keys]
2: ñ

View file

@ -0,0 +1,8 @@
[morekeys]
s š ś ß
n ñ ń
z ž ź ż
c č ć ç
d đ
'
" “ „ ” » «

View file

@ -0,0 +1,8 @@
[morekeys]
a á
e é
i í
o ó ö ő
u ú ü ű
'
" “ „ ” » «

View file

@ -0,0 +1,11 @@
[morekeys]
a á ä æ å à â ã ā
e é ë è ê ę ė ē
i í ï î ì į ī
o ó ö ô ò õ œ ø ō
u ú ü û ù ū
y ý ÿ
d ð
t þ
'
" ” „ “

View file

@ -0,0 +1,11 @@
[morekeys]
a à á â ä æ ã å ā ª
e è é ê ë ę ė ē ə
i ì í î ï į ī
o ò ó ô ö õ œ ø ō º
u ù ú û ü ū
[extra_keys]
1: ü è
2: ö é
2: ä à

View file

@ -0,0 +1,19 @@
[morekeys]
a ą ä ā à á â ã å æ
e ė ę ē è é ê ë ě
i į ī ì í î ï ı
o ö õ ò ó ô œ ő ø
u ū ų ü ū ù ú û ů ű
s š ß ś ş
n ņ ñ ń
c č ç ć
y ý ÿ
d ď
r ŗ ř ŕ
t ţ ť
z ž ż ź
k ķ
l ļ ł ĺ ľ
g ģ ğ
'
" ” „ “

View file

@ -0,0 +1,19 @@
[morekeys]
a ā à á â ã ä å æ ą
e ē ė è é ê ë ę ě
i ī į ì í î ï ı
o ò ó ô õ ö œ ő ø
u ū ų ù ú û ü ů ű
s š ß ś ş
n ņ ñ ń
c č ç ć
y ý ÿ
d ď
r ŗ ř ŕ
t ţ ť
z ž ż ź
k ķ
l ļ ł ĺ ľ
g ģ ğ
'
" ” „ “

View file

@ -0,0 +1,20 @@
[morekeys]
a á â ä à æ ã å ā ą ª ă
e é è ê ë ę ė ē ə ě
i í ì ï î į ī ij ı ĩ
o ó ô ö ò õ œ ø ō º ő
u ú û ü ù ū ů ũ ű ų
n ñ ń ň ņ ʼn ŋ
y ý ij ÿ ŷ
s ş ß ś š ș
g ğ ġ ģ
c ç ć č ċ
z ž ź ż
l ł ĺ ļ ľ ŀ
punctuation !autoColumnOrder!9 \, ? ! # ) ( / ; ' @ : - " + \% &
d ď ð đ
r ř ŕ ŗ
t ť ț ţ ŧ þ
k ķ ĸ
h ĥ
w ŵ

View file

@ -0,0 +1,12 @@
[morekeys]
a å æ ä à á â ã ā
e é è ê ë ę ė ē
o ø ö ô ò ó õ œ ō
u ü û ù ú ū
'
" “ „ ”
[extra_keys]
1: å
2: ø ö
2: æ ä

View file

@ -0,0 +1,10 @@
[morekeys]
a á ä â à æ ã å ā
e é ë ê è ę ė ē
i í ï ì î į ī ij
o ó ö ô ò õ œ ø ō
u ú ü û ù ū
n ñ ń
y ij
'
" “ „ ”

View file

@ -0,0 +1,11 @@
[morekeys]
a ą á à â ä æ ã å ā
e ę è é ê ë ė ē
o ó ö ô ò õ œ ø ō
s ś ß š
n ń ñ
c ć ç č
z ż ź ž
l ł
'
" “ „ ”

View file

@ -0,0 +1,7 @@
[morekeys]
a á ã à â ä å æ ª
e é ê è ę ė ē ë
i í î ì ï į ī
o ó õ ô ò ö œ ø ō º
u ú ü ù û ū
c ç č ć

View file

@ -0,0 +1,2 @@
[morekeys]
o ò ó ö ô õ œ ø

View file

@ -0,0 +1,7 @@
[morekeys]
a ă â ã à á ä æ å ā
i î ï ì í į ī
s ș ß ś š
t ț
'
" “ „ ”

View file

@ -0,0 +1,19 @@
[morekeys]
a á ä ā à â ã å æ ą
e é ě ē ė è ê ë ę
i í ī į ì î ï ı
o ô ó ö ò õ œ ő ø
u ú ů ü ū ų ù û ű
s š ß ś ş
n ň ņ ñ ń
c č ç ć
y ý ÿ
d ď
r ŕ ř ŗ
t ť ţ
z ž ż ź
k ķ
l ľ ĺ ļ ł
g ģ ğ
'
" ” „ “ » «

View file

@ -0,0 +1,7 @@
[morekeys]
s š
c č ć
d đ
z ž
'
" ” „ “ » «

View file

@ -0,0 +1,14 @@
[morekeys]
e è
i ì
s š %
c č ć %
d đ %
z ž %
[extra_keys]
1: š
2: č
2: ć
3: đ
3: ž

View file

@ -0,0 +1,22 @@
[morekeys]
a ä å æ á à â ą ã
c ç ć č
d ð ď
e é è ê ë ę
i í ì î ï
l ł
n ń ñ ň
o ö ø œ ó ò ô õ ō
r ř
s ś š ş ß
t ť þ
u ü ú ù û ū
y ý ÿ
z ź ž ż
'
" » «
[extra_keys]
1: å
2: ö ø œ
2: ä æ

View file

@ -0,0 +1,10 @@
[morekeys]
a à á â ä æ ã å ā
e è é ê ë ē
i î ï í ī ì
o ô ö ò ó œ ø ō õ
u û ü ù ú ū
s ß
n ñ
c ç
g g\'

View file

@ -0,0 +1,12 @@
[morekeys]
a â ä á
e ə é
i ı î ï ì í į ī
o ö ô œ ò ó õ ø ō
u ü û ù ú ū
s ş ß ś š
g ğ
n ň ñ
c ç ć č
y ý
z ž

View file

@ -0,0 +1,12 @@
[morekeys]
a â ä á
e ə é
i ı î ï ì í į ī
o ö ô œ ò ó õ ø ō
u ü û ù ú ū
s ş ß ś š
g ğ
n ň ñ
c ç ć č
y ý
z ž

View file

@ -0,0 +1,8 @@
[morekeys]
a à á ả ã ạ ă ằ ắ ẳ ẵ ặ â ầ ấ ẩ ẫ ậ
e è é ẻ ẽ ẹ ê ề ế ể ễ ệ
i ì í ỉ ĩ ị
o ò ó ỏ õ ọ ô ồ ố ổ ỗ ộ ơ ờ ớ ở ỡ ợ
u ù ú ủ ũ ụ ư ừ ứ ử ữ ự
y ỳ ý ỷ ỹ ỵ
d đ

View file

@ -0,0 +1,9 @@
[morekeys]
a à á â ä æ ã å ā
e é è ê ë ē
i í î ï ī ì
o ó ô ö ò œ ø ō õ
u ú û ü ù ū
s ß
n ñ
c ç

View file

@ -0,0 +1,20 @@
[morekeys]
a à á â ã ä å æ ā ă ą ª
e è é ê ë ē ĕ ė ę ě
i ì í î ï ĩ ī ĭ į ı ij
o ò ó ô õ ö ø ō ŏ ő œ º
u ù ú û ü ũ ū ŭ ů ű ų
s ß ś ŝ ş š ſ
n ñ ń ņ ň ʼn ŋ
c ç ć ĉ ċ č
y ý ŷ ÿ ij
d ď đ ð
r ŕ ŗ ř
t þ ţ ť ŧ
z ź ż ž
k ķ ĸ
l ĺ ļ ľ ŀ ł
g ĝ ğ ġ ģ
h ĥ
j ĵ
w ŵ

View file

@ -0,0 +1,29 @@
b %
é \ è
p |
o =
v [
d ]
l <
j >
z {
w }
a @
u #
i $$$
e _
c &
t -
s +
r (
n )
m /
y *
x "
k '
q :
g ;
h !
f ?

View file

@ -0,0 +1,29 @@
ŝ %
ĝ \
e |
r =
t [
ŭ ]
u <
i >
o {
p }
a @
s #
d $$$
f _
g &
h -
j +
k (
l )
ĵ
z *
ĉ "
c '
v :
b ;
n !
m ?

View file

@ -0,0 +1,32 @@
w %
l \
r |
b =
z [
; ]
q <
u >
d {
j }
s @
h #
n $$$
t _
,
.
a -
e (
o )
i /
m *
v "
c '
g :
p ;
x !
k ?
f
y

View file

@ -11,7 +11,7 @@ p }
a @
s #
d $
d $$$
f _
g &
h -

View file

@ -0,0 +1,28 @@
q %
w \
e |
r =
t [
z ]
u <
i >
o {
p }
a @
s #
d $$$
f _
g &
h -
j +
k (
l )
y *
x "
c '
v :
b ;
n !
m ?

View file

@ -0,0 +1,29 @@
q %
d \
r |
w =
b [
j ]
f <
u >
p {
; }
a @
s #
h $$$
t _
g &
y -
n +
e (
o )
i /
z *
x "
m '
d :
v ;
k !
l ?

View file

@ -131,7 +131,7 @@ public class Key implements Comparable<Key> {
private static final int MORE_KEYS_FLAGS_NEEDS_DIVIDERS = 0x20000000;
private static final int MORE_KEYS_FLAGS_NO_PANEL_AUTO_MORE_KEY = 0x10000000;
// TODO: Rename these specifiers to !autoOrder! and !fixedOrder! respectively.
private static final String MORE_KEYS_AUTO_COLUMN_ORDER = "!autoColumnOrder!";
public static final String MORE_KEYS_AUTO_COLUMN_ORDER = "!autoColumnOrder!";
private static final String MORE_KEYS_FIXED_COLUMN_ORDER = "!fixedColumnOrder!";
private static final String MORE_KEYS_HAS_LABELS = "!hasLabels!";
private static final String MORE_KEYS_NEEDS_DIVIDERS = "!needsDividers!";
@ -943,7 +943,7 @@ public class Key implements Comparable<Key> {
// for creating keys that might get modified later
public static class KeyParams {
// params for building
private boolean isSpacer;
public boolean isSpacer;
private final KeyboardParams mKeyboardParams; // for reading gaps and keyboard width / height
public float mRelativeWidth;
public float mRelativeHeight; // also should allow negative values, indicating absolute height is defined
@ -975,8 +975,10 @@ public class Key implements Comparable<Key> {
return keyParams;
}
public static KeyParams newSpacer(final KeyboardParams params) {
return new KeyParams(params);
public static KeyParams newSpacer(final KeyboardParams params, final float relativeWidth) {
final KeyParams spacer = new KeyParams(params);
spacer.mRelativeWidth = relativeWidth;
return spacer;
}
public Key createKey() {
@ -987,7 +989,7 @@ public class Key implements Comparable<Key> {
public void setDimensionsFromRelativeSize(final float newX, final float newY) {
if (mRelativeHeight == 0)
mRelativeHeight = mKeyboardParams.mDefaultRelativeRowHeight;
if (mRelativeWidth == 0)
if (!isSpacer && mRelativeWidth == 0)
mRelativeWidth = mKeyboardParams.mDefaultRelativeKeyWidth;
if (mRelativeHeight < 0)
// todo (later): deal with it properly when it needs to be adjusted, i.e. when changing moreKeys or moreSuggestions
@ -998,7 +1000,7 @@ public class Key implements Comparable<Key> {
mFullHeight = mRelativeHeight * mKeyboardParams.mBaseHeight;
}
private static int getMoreKeysColumnAndFlags(final KeyboardParams params, final String[] moreKeys) {
private static int getMoreKeysColumnAndFlagsAndSetNullInArray(final KeyboardParams params, final String[] moreKeys) {
// Get maximum column order number and set a relevant mode value.
int moreKeysColumnAndFlags = MORE_KEYS_MODE_MAX_COLUMN_WITH_AUTO_ORDER | params.mMaxMoreKeysKeyboardColumn;
int value;
@ -1066,7 +1068,7 @@ public class Key implements Comparable<Key> {
final Locale localeForUpcasing = params.mId.getLocale();
int actionFlags = style.getFlags(keyAttr, R.styleable.Keyboard_Key_keyActionFlags);
String[] moreKeys = style.getStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys);
mMoreKeysColumnAndFlags = getMoreKeysColumnAndFlags(params, moreKeys);
mMoreKeysColumnAndFlags = getMoreKeysColumnAndFlagsAndSetNullInArray(params, moreKeys);
final String[] additionalMoreKeys;
if ((mLabelFlags & LABEL_FLAGS_DISABLE_ADDITIONAL_MORE_KEYS) != 0) {
@ -1172,7 +1174,7 @@ public class Key implements Comparable<Key> {
mLabelFlags = labelFlags;
mRelativeWidth = relativeWidth;
mRelativeHeight = params.mDefaultRelativeRowHeight;
mMoreKeysColumnAndFlags = getMoreKeysColumnAndFlags(params, layoutMoreKeys);
mMoreKeysColumnAndFlags = getMoreKeysColumnAndFlagsAndSetNullInArray(params, layoutMoreKeys);
mIconId = KeySpecParser.getIconId(keySpec);
final boolean needsToUpcase = needsToUpcase(mLabelFlags, params.mId.mElementId);
@ -1184,10 +1186,10 @@ public class Key implements Comparable<Key> {
languageMoreKeys = null;
} else {
// same style as additionalMoreKeys (i.e. moreKeys with the % placeholder(s))
// todo: read from assets or xml, and cache the results for quick reading again
languageMoreKeys = null; // todo: getLanguageMoreKeys(keySpec, mKeyboardParams.mId.getLocale());
languageMoreKeys = params.mLocaleKeyTexts.getMoreKeys(keySpec);
}
final String[] finalMoreKeys = MoreKeySpec.insertAdditionalMoreKeys(languageMoreKeys, layoutMoreKeys);
if (finalMoreKeys != null) {
actionFlags |= ACTION_FLAGS_ENABLE_LONG_PRESS;
mMoreKeys = new MoreKeySpec[finalMoreKeys.length];
@ -1314,7 +1316,7 @@ public class Key implements Comparable<Key> {
if (moreKeySpecs != null) {
String[] moreKeys = MoreKeySpec.splitKeySpecs(moreKeySpecs);
mMoreKeysColumnAndFlags = getMoreKeysColumnAndFlags(params, moreKeys);
mMoreKeysColumnAndFlags = getMoreKeysColumnAndFlagsAndSetNullInArray(params, moreKeys);
moreKeys = MoreKeySpec.insertAdditionalMoreKeys(moreKeys, null);
int actionFlags = 0;

View file

@ -20,6 +20,7 @@ import android.view.inputmethod.InputMethodSubtype;
import org.dslul.openboard.inputmethod.keyboard.internal.KeyboardBuilder;
import org.dslul.openboard.inputmethod.keyboard.internal.KeyboardParams;
import org.dslul.openboard.inputmethod.keyboard.internal.UniqueKeysCache;
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.LocaleKeyTextsKt;
import org.dslul.openboard.inputmethod.latin.InputAttributes;
import org.dslul.openboard.inputmethod.latin.R;
import org.dslul.openboard.inputmethod.latin.RichInputMethodSubtype;
@ -127,6 +128,7 @@ public final class KeyboardLayoutSet {
public static void onSystemLocaleChanged() {
clearKeyboardCache();
LocaleKeyTextsKt.clearCache();
}
public static void onKeyboardThemeChanged() {

View file

@ -13,8 +13,11 @@ import org.dslul.openboard.inputmethod.keyboard.Key
import org.dslul.openboard.inputmethod.keyboard.Key.KeyParams
import org.dslul.openboard.inputmethod.keyboard.Keyboard
import org.dslul.openboard.inputmethod.keyboard.KeyboardId
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.MORE_KEYS_ALL
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.MORE_KEYS_MORE
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.SimpleKeyboardParser
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.XmlKeyboardParser
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.addLocaleKeyTextsToParams
import org.dslul.openboard.inputmethod.latin.R
import org.dslul.openboard.inputmethod.latin.common.Constants
import org.dslul.openboard.inputmethod.latin.settings.Settings
@ -46,47 +49,25 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
fun loadSimpleKeyboard(id: KeyboardId): KeyboardBuilder<KP> {
mParams.mId = id
keysInRows = SimpleKeyboardParser(mParams, mContext).parseFromAssets("qwerty")
useRelative()
addLocaleKeyTextsToParams(mContext, mParams)
when (Settings.getInstance().current.mShowMoreKeys) {
MORE_KEYS_ALL -> mParams.mLocaleKeyTexts.addFile(mContext.assets.open("language_key_texts/all_more_keys.txt"))
MORE_KEYS_MORE -> mParams.mLocaleKeyTexts.addFile(mContext.assets.open("language_key_texts/more_more_keys.txt"))
}
keysInRows = SimpleKeyboardParser(mParams, mContext).parseFromAssets(id.mSubtype.keyboardLayoutSetName)
determineAbsoluteValues()
return this
// todo: further plan to make is actually useful
// create languageMoreKeys list from stuff in keyboard-text tools
// probably use files in assets, and cache them in a weak hash map with localestring as key
// or better 2 letter code, and join codes when combining languageMoreKeys for multiple locales
// or maybe locale tag, but that's super annoying for api < 24(?)
// or no caching if loading and combining is fast anyway (need to test)
// the locale morekeys then should be a map label -> moreKeys
// the whole moreKeys map for the current keyboard could be in mParams to simplify access when creating keys
// file format? it's easy to switch, but still... text like above? json?
// or use resources? could look like donottranslate-more-keys files
// should be possible with configuration and contextThemeWrapper, but probably more complicated than simple files
// also would be a bit annoying as it would require to have empty base strings for all possible keys
// test first whether something like morekeys_&#x1002;, or morekeys_&#x00F8; or better morekeys_ø actually works
// if not, definitely don't use resources
// consider the % placeholder, this should still be used and documented
// though maybe has issues when merging languages?
// how to deal with unnecessary moreKeys?
// e.g. german should have ö as moreKey on o, but swiss german layout has ö as separate key
// still have ö on o (like now), or remove it? or make it optional?
// is this handled by KeyboardParams.removeRedundantMoreKeys?
// not only moreKeys, also currency key and some labels keys should be translated, though not necessarily in that map
// need some placeholder for currency key, like $$$
// have an explicit all-more-keys definition, which is created from a script merging all available moreKeys
// only letter forms and nothing else, right?
// maybe some most-but-not-all? e.g. only all that occur for more than one language
// migrate latin layouts to this style (need to make exception for pcqwerty!)
// finalize simple layout format
// keep like now: nice, because simple and allows defining any number of moreKeys
// rows of letters, separated with space: very straightforward, but moreKeys are annoying and only one possible
// consider the current layout maybe doesn't have the correct moreKeys
// where to actually get the current keyboard layout name, so it can be used to select the correct file?
// maybe KeyboardLayoutSet will need to be replaced
// need to solve the scaling issue with number row and 5 row keyboards
// allow users to switch to old style (keep it until all layouts are switched)
// really helps to find differences
// add a text that issues / unwanted differences should be reported, as the setting will be removed at some point
// label flags to do (top part is for latin!)
// todo: further plan
// add a parser for more complex layouts, and slowly extend it with whatever is needed
// initially it's just alternative key for shifted layout
// so dvorak and azerty and colemak and others can be migrated
// try to make the format compatible with florisboard
// migrate symbol layouts to this style
// better before user-defined layouts
// should be straightforward to do
// allow users to define their own layouts
// need to solve the scaling issue with number row and 5 row keyboards
// write up how things work for users, also regarding language more keys
// readme, maybe also some "help" button in a dialog
// some sort of proper UI, or simply text input?
@ -98,9 +79,22 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
// need to somehow test for this
// is that autoColumnOrder thing a workaround for that?
// still would crash for a single huge label
// potential keyspec parsing issues:
// MoreKeySpec constructor does things like KeySpecParser.getLabel and others
// these work with special characters like | and \ doing things depending on their position
// if used wrongly, things can crash
// -> maybe disable this style of parsing when creating MoreKeySpec of a user-provided layout
// or also for the simple layouts, because there is no need to have it in layouts
// does the same issue apply to normal key labels?
// popup and (single key) long press preview rescale the label on x only, which may deform emojis
// migrate symbol layouts to this style
// does glide typing work with multiple letters on one key? if not, users should be notified
// maybe allow users to define their own symbol and shift-symbol layouts
// allow users to import layouts, which essentially just fills the text from a file
// can be json too, but need to have a (close to) final definition first
// make the remove duplicate moreKey thing an option?
// why is it on for serbian (latin), but not for german (german)?
// only nordic and serbian_qwertz layouts have it disabled, default is enabled
// -> add the option, but disable it by default for all layouts
// migrate emoji layouts to this style
// emojis are defined in that string array, should be simple to handle
// parsing could be done into a single row, which is then split as needed
@ -113,27 +107,23 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
// migrate keypad layouts to this style
// will need more configurable layout definition -> another parser
// migrate moreKeys and moreSuggestions to this style?
// at least they should not make use of the KeyTextsSet/Table and of the XmlKeyboardParser
// at least they should not make use of the KeyTextsSet/Table (and of the XmlKeyboardParser?)
// migrate other languages to this style
// may be difficult in some cases, like additional row, or no shift key, or pc qwerty layout
// also the (integrated) number row might cause issues
// at least some of these layouts will need more complicated definition, not just a simple text file
// remove all the keyboard layout related xmls if possible
// rows_, rowkeys_, row_, kbd_ maybe keyboard_layout_set, keys_, keystyle_, key_
// and the texts_table and its source tools
// some languages also change symbol view, e.g. fa changes symbols row 3
// add more layouts before doing this? or just keep the layout conversion script
// todo: label flags
// alignHintLabelToBottom -> what does it do?
// fontNormal -> check / compare turkish layout
// fontDefault -> check exclamation and question keys
// hasShiftedLetterHint, shiftedLetterActivated -> what is the effect on period key?
// labelFlags should be set correctly
// alignHintLabelToBottom: on lxx and rounded themes
// alignHintLabelToBottom: on lxx and rounded themes, but did not find what it actually does...
// alignIconToBottom: space_key_for_number_layout
// alignLabelOffCenter: number keys in phone layout
// fontNormal: turkish (rows 1 and 2 only), .com, emojis, numModeKeyStyle, a bunch of non-latin languages
// fontMonoSpace: unused (not really: fontDefault is monospace + normal)
// -> switches to normal typeface, only relevant for holo which has bold
// fontMonoSpace: unused
// fontDefault: keyExclamationQuestion, a bunch of "normal" keys in fontNormal layouts like thai
// -> switches to default defined typeface, useful e.g. if row has fontNormal
// followKeyLargeLetterRatio: number keys in number/phone/numpad layouts
// followKeyLetterRatio: mode keys in number layouts, some keys in some non-latin layouts
// followKeyLabelRatio: enter key, some keys in phone layout (same as followKeyLetterRatio + followKeyLargeLetterRatio)
@ -154,12 +144,18 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
// maybe remove some of the flags? or keep supporting them?
// for pcqwerty: hasShiftedLetterHint -> hasShiftedLetterHint|shiftedLetterActivated when shift is enabled, need to consider if the flag is used
// actually period key also has shifted letter hint
return this
}
fun loadFromXml(xmlId: Int, id: KeyboardId): KeyboardBuilder<KP> {
mParams.mId = id
if (Settings.getInstance().current.mUseNewKeyboardParsing
&& id.isAlphabetKeyboard
&& this::class == KeyboardBuilder::class // otherwise this will apply to moreKeys and moreSuggestions
&& SimpleKeyboardParser.hasLayoutFile(mParams.mId.mSubtype.keyboardLayoutSetName)
) {
loadSimpleKeyboard(id)
return this
}
// loading a keyboard should set default params like mParams.readAttributes(mContext, attrs);
// attrs may be null, then default values are used (looks good for "normal" keyboards)
try {
@ -167,10 +163,10 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
keysInRows = keyboardParser.parseKeyboard()
}
} catch (e: XmlPullParserException) {
Log.w(BUILDER_TAG, "keyboard XML parse error", e)
Log.w(TAG, "keyboard XML parse error", e)
throw IllegalArgumentException(e.message, e)
} catch (e: IOException) {
Log.w(BUILDER_TAG, "keyboard XML parse error", e)
Log.w(TAG, "keyboard XML parse error", e)
throw RuntimeException(e.message, e)
}
return this
@ -194,12 +190,11 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
return Keyboard(mParams)
}
// resize keyboard using relative params
// determine key size and positions using relative width and height
// ideally this should not change anything
// but it does a little, depending on how float -> int is done (cast or round, and when to sum up gaps and width)
// still should not be more than a pixel difference
// keep it around for a while, for testing
private fun useRelative() {
private fun determineAbsoluteValues() {
var currentY = mParams.mTopPadding.toFloat()
for (row in keysInRows) {
if (row.isEmpty()) continue
@ -229,10 +224,8 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
val currentKeyXPos = row[i].xPos
if (currentKeyXPos > currentX) {
// insert spacer
val spacer = KeyParams.newSpacer(mParams)
spacer.mRelativeWidth = (currentKeyXPos - currentX) / mParams.mBaseWidth
val spacer = KeyParams.newSpacer(mParams, (currentKeyXPos - currentX) / mParams.mBaseWidth)
spacer.yPos = row[i].yPos
spacer.mRelativeHeight = row[i].mRelativeHeight
row.add(i, spacer)
i++
currentX += currentKeyXPos - currentX
@ -242,9 +235,7 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
}
if (currentX < mParams.mOccupiedWidth) {
// insert spacer
val spacer = KeyParams.newSpacer(mParams)
spacer.mRelativeWidth = (mParams.mOccupiedWidth - currentX) / mParams.mBaseWidth
spacer.mRelativeHeight = row.last().mRelativeHeight
val spacer = KeyParams.newSpacer(mParams, (mParams.mOccupiedWidth - currentX) / mParams.mBaseWidth)
spacer.yPos = row.last().yPos
row.add(spacer)
}
@ -253,7 +244,6 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
private fun addSplit() {
val spacerRelativeWidth = Settings.getInstance().current.mSpacerRelativeWidth
// adjust gaps for the whole keyboard, so it's the same for all rows
// todo: maybe remove? not sure if narrower gaps are desirable
mParams.mRelativeHorizontalGap *= 1f / (1f + spacerRelativeWidth)
mParams.mHorizontalGap = (mParams.mRelativeHorizontalGap * mParams.mId.mWidth).toInt()
var maxWidthBeforeSpacer = 0f
@ -262,11 +252,9 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
fillGapsWithSpacers(row)
val y = row.first().yPos // all have the same y, so this is fine
val relativeWidthSum = row.sumOf { it.mRelativeWidth } // sum up relative widths
val spacer = KeyParams.newSpacer(mParams)
spacer.mRelativeWidth = spacerRelativeWidth
spacer.mRelativeHeight = row.first().mRelativeHeight
val spacer = KeyParams.newSpacer(mParams, spacerRelativeWidth)
// insert spacer before first key that starts right of the center (also consider gap)
var insertIndex = row.indexOfFirst { it.xPos > mParams.mOccupiedWidth / 2 }
var insertIndex = row.indexOfFirst { it.xPos + it.mFullWidth / 3 > mParams.mOccupiedWidth / 2 }
.takeIf { it > -1 } ?: (row.size / 2) // fallback should never be needed, but better than having an error
if (row.any { it.mCode == Constants.CODE_SPACE }) {
val spaceLeft = row.single { it.mCode == Constants.CODE_SPACE }
@ -378,9 +366,6 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
startRow()
for (keyParams in row) {
endKey(keyParams.createKey())
// todo (later): markAsBottomKey if in bottom row?
// this is not done in original parsing style, but why not?
// just test it (with different bottom paddings)
}
endRow()
}
@ -388,6 +373,6 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
}
companion object {
private const val BUILDER_TAG = "Keyboard.Builder"
private const val TAG = "Keyboard.Builder"
}
}

View file

@ -16,6 +16,7 @@ import androidx.annotation.Nullable;
import org.dslul.openboard.inputmethod.keyboard.Key;
import org.dslul.openboard.inputmethod.keyboard.KeyboardId;
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.LocaleKeyTexts;
import org.dslul.openboard.inputmethod.latin.R;
import org.dslul.openboard.inputmethod.latin.common.Constants;
import org.dslul.openboard.inputmethod.latin.settings.Settings;
@ -84,6 +85,8 @@ public class KeyboardParams {
@NonNull
private final UniqueKeysCache mUniqueKeysCache;
public boolean mAllowRedundantMoreKeys;
@NonNull
public LocaleKeyTexts mLocaleKeyTexts;
public int mMostCommonKeyHeight = 0;
public int mMostCommonKeyWidth = 0;
@ -219,8 +222,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;
mDefaultRelativeKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
1, 1, 1f / DEFAULT_KEYBOARD_COLUMNS);
1, 1, defaultKeyWidthFactor / DEFAULT_KEYBOARD_COLUMNS);
mDefaultKeyWidth = (int) (mDefaultRelativeKeyWidth * mBaseWidth);
// todo: maybe settings should not be accessed from here?

View file

@ -52,7 +52,7 @@ public final class KeyboardTextsSet {
mResourceLocale = SubtypeLocaleUtils.NO_LANGUAGE.equals(locale.toString()) ? null : locale;
mResourcePackageName = resourcePackageName;
mTextsTables.clear();
if (Settings.getInstance().getCurrent().mShowAllMoreKeys) {
if (Settings.getInstance().getCurrent().mShowMoreKeys > 0) {
mTextsTables.add(KeyboardTextsTable.getTextsTable(new Locale(SubtypeLocaleUtils.NO_LANGUAGE)));
return;
}

View file

@ -0,0 +1,228 @@
// SPDX-License-Identifier: GPL-3.0-only
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.internal.KeyboardParams
import org.dslul.openboard.inputmethod.latin.common.splitOnWhitespace
import org.dslul.openboard.inputmethod.latin.settings.Settings
import java.io.InputStream
import java.util.Locale
import kotlin.math.round
class LocaleKeyTexts(dataStream: InputStream?) {
private val moreKeys = hashMapOf<String, Array<String>>()
private val extraKeys = Array<MutableList<Pair<String, Array<String>?>>?>(5) { null }
var labelSymbols = "\\?123"
var labelAlphabet = "ABC"
var labelShiftSymbols = "=\\<"
init {
readStream(dataStream, false)
// set default quote moreKeys if necessary
// should this also be done with punctuation moreKeys??
if ("\'" !in moreKeys)
moreKeys["\'"] = arrayOf("", "", "", "", "")
if ("\"" !in moreKeys)
moreKeys["\""] = arrayOf("", "", "", "«", "»")
if ("!" !in moreKeys)
moreKeys["!"] = arrayOf("¡")
if ("?" !in moreKeys)
moreKeys["?"] = arrayOf("¿")
}
private fun readStream(stream: InputStream?, onlyMoreKeys: Boolean) {
if (stream == null) return
stream.reader().use { reader ->
var mode = READER_MODE_NONE
val colonSpaceRegex = ":\\s+".toRegex()
reader.forEachLine { l ->
val line = l.trim()
when (line) {
"[morekeys]" -> { mode = READER_MODE_MORE_KEYS; return@forEachLine }
"[extra_keys]" -> { mode = READER_MODE_EXTRA_KEYS; return@forEachLine }
"[labels]" -> { mode = READER_MODE_LABELS; return@forEachLine }
}
when (mode) {
READER_MODE_MORE_KEYS -> addMoreKeys(line.splitOnWhitespace())
READER_MODE_EXTRA_KEYS -> if (!onlyMoreKeys) addExtraKey(line.split(colonSpaceRegex, 1))
READER_MODE_LABELS -> if (!onlyMoreKeys) addLabel(line.split(colonSpaceRegex, 1))
}
}
}
}
// need tp provide a copy because some functions like MoreKeySpec.insertAdditionalMoreKeys may modify the array
fun getMoreKeys(label: String): Array<String>? = moreKeys[label]?.copyOf()
fun getExtraKeys(row: Int): List<Pair<String, Array<String>?>>? =
if (row > extraKeys.size) null
else extraKeys[row]
fun addFile(dataStream: InputStream?) {
readStream(dataStream, true)
}
private fun addMoreKeys(split: List<String>) {
if (split.size == 1) return
val existingMoreKeys = moreKeys[split.first()]
if (existingMoreKeys == null)
moreKeys[split.first()] = Array(split.size - 1) { split[it + 1] }
else
moreKeys[split.first()] = mergeMoreKeys(existingMoreKeys, split.drop(1))
}
private fun addExtraKey(split: List<String>) {
if (split.size < 2) return
val row = split.first().toIntOrNull() ?: return
val keys = split.last().splitOnWhitespace()
val morekeys = if (keys.size == 1) null else Array(keys.size - 1) { keys[it + 1] }
if (extraKeys[row] == null)
extraKeys[row] = mutableListOf()
extraKeys[row]?.add(keys.first() to morekeys)
}
private fun addLabel(split: List<String>) {
if (split.size < 2) return
when (split.first()) {
"symbols" -> labelSymbols = split.last()
"alphabet" -> labelAlphabet = split.last()
"shift_symbols" -> labelShiftSymbols = split.last()
}
}
}
private fun mergeMoreKeys(original: Array<String>, added: List<String>): Array<String> {
val markerIndexInOriginal = original.indexOf("%")
val markerIndexInAddedIndex = added.indexOf("%")
val moreKeys = mutableSetOf<String>()
if (markerIndexInOriginal != -1 && markerIndexInAddedIndex != -1) {
// add original and then added until %
original.forEachIndexed { index, s -> if (index < markerIndexInOriginal) moreKeys.add(s) }
added.forEachIndexed { index, s -> if (index < markerIndexInAddedIndex) moreKeys.add(s) }
// add % and remaining moreKeys
original.forEachIndexed { index, s -> if (index >= markerIndexInOriginal) moreKeys.add(s) }
added.forEachIndexed { index, s -> if (index > markerIndexInAddedIndex) moreKeys.add(s) }
} else if (markerIndexInOriginal != -1) {
// add original until %, then added, then remaining original
original.forEachIndexed { index, s -> if (index <= markerIndexInOriginal) moreKeys.add(s) }
moreKeys.addAll(added)
original.forEachIndexed { index, s -> if (index > markerIndexInOriginal) moreKeys.add(s) }
} else if (markerIndexInAddedIndex != -1) {
// add added until %, then original, then remaining added
added.forEachIndexed { index, s -> if (index <= markerIndexInAddedIndex) moreKeys.add(s) }
moreKeys.addAll(original)
added.forEachIndexed { index, s -> if (index > markerIndexInAddedIndex) moreKeys.add(s) }
} else {
// use original, then added
moreKeys.addAll(original)
moreKeys.addAll(added)
}
// in fact this is only special treatment for the punctuation moreKeys
if (moreKeys.any { it.startsWith(Key.MORE_KEYS_AUTO_COLUMN_ORDER) }) {
val originalColumnCount = original.firstOrNull { it.startsWith(Key.MORE_KEYS_AUTO_COLUMN_ORDER) }
?.substringAfter(Key.MORE_KEYS_AUTO_COLUMN_ORDER)?.toIntOrNull()
val l = moreKeys.filterNot { it.startsWith(Key.MORE_KEYS_AUTO_COLUMN_ORDER) }
if (originalColumnCount != null && moreKeys.size <= 20 // not for too wide layout
&& originalColumnCount == round((original.size - 1 + 0.1f) / 2f).toInt()) { // +0.1 f against rounding issues
// we had 2 rows, and want it again
return (l + "${Key.MORE_KEYS_AUTO_COLUMN_ORDER}${round(l.size / 2f).toInt()}").toTypedArray()
}
// just drop autoColumnOrder otherwise (maybe not? depends on arising issues)
return l.toTypedArray()
}
return moreKeys.toTypedArray()
}
fun addLocaleKeyTextsToParams(context: Context, params: KeyboardParams) {
val locales = Settings.getInstance().current.mSecondaryLocales + params.mId.locale
params.mLocaleKeyTexts = moreKeysAndLabels.getOrPut(locales.joinToString { it.toString() }) {
val lkt = LocaleKeyTexts(getStreamForLocale(params.mId.locale, context))
locales.forEach { locale ->
if (locale == params.mId.locale) return@forEach
lkt.addFile(getStreamForLocale(locale, context))
}
lkt
}
}
private fun getStreamForLocale(locale: Locale, context: Context) =
try {
if (locale.toString() == "zz") context.assets.open("language_key_texts/more_more_keys.txt")
else context.assets.open("language_key_texts/${locale.toString().lowercase()}.txt")
} catch (_: Exception) {
try {
context.assets.open("language_key_texts/${locale.language.lowercase()}.txt")
} catch (_: Exception) {
null
}
}
fun clearCache() = moreKeysAndLabels.clear()
// cache the texts, so they don't need to be read over and over
private val moreKeysAndLabels = hashMapOf<String, LocaleKeyTexts>()
private const val READER_MODE_NONE = 0
private const val READER_MODE_MORE_KEYS = 1
private const val READER_MODE_EXTRA_KEYS = 2
private const val READER_MODE_LABELS = 3
// probably could be improved and extended, currently this is what's done in key_styles_currency.xml
fun getCurrencyKey(locale: Locale): Pair<String, Array<String>> {
if (locale.country.matches(euroCountries))
return euro
if (locale.toString().matches(euroLocales))
return euro
if (locale.language.matches("ca|eu|lb|mt".toRegex()))
return euro
if (locale.language.matches("fa|iw|ko|lo|mn|ne|th|uk|vi".toRegex()))
return genericCurrencyKey(getCurrency(locale))
if (locale.language == "hy")
return dram
if (locale.language == "tr")
return lira
if (locale.language == "ru")
return ruble
if (locale.country == "LK" || locale.country == "BD")
return genericCurrencyKey(getCurrency(locale))
if (locale.country == "IN" || locale.language.matches("hi|kn|ml|mr|ta|te".toRegex()))
return rupee
if (locale.country == "GB")
return pound
return genericCurrencyKey("$")
}
private fun genericCurrencyKey(currency: String) = currency to genericCurrencyMoreKeys
private val genericCurrencyMoreKeys = arrayOf("$", "¢", "£", "", "¥", "")
private fun getCurrency(locale: Locale): String {
if (locale.country == "BD") return ""
if (locale.country == "LK") return "රු"
return when (locale.language) {
"fa" -> ""
"iw" -> ""
"ko" -> ""
"lo" -> ""
"mn" -> ""
"ne" -> "रु."
"si" -> "රු"
"th" -> "฿"
"uk" -> ""
"vi" -> ""
else -> "$"
}
}
// needs at least 4 moreKeys for working shift-symbol keyboard
private val euro = "" to arrayOf("¢", "£", "$", "¥", "")
private val dram = "֏" to arrayOf("", "$", "", "¥", "£")
private val rupee = "" to arrayOf("¢", "£", "", "¥", "")
private val pound = "£" to arrayOf("¢", "$", "", "¥", "")
private val ruble = "" to arrayOf("", "$", "£", "¥")
private val lira = "" to arrayOf("", "$", "£", "¥")
private val euroCountries = "AD|AT|BE|BG|HR|CY|CZ|DA|EE|FI|FR|DE|GR|HU|IE|IT|XK|LV|LT|LU|MT|MO|ME|NL|PL|PT|RO|SM|SK|SI|ES|VA".toRegex()
private val euroLocales = "bg|ca|cs|da|de|el|en|es|et|eu|fi|fr|ga|gl|hr|hu|it|lb|lt|lv|mt|nl|pl|pt|ro|sk|sl|sq|sr|sv".toRegex()
const val MORE_KEYS_ALL = 2;
const val MORE_KEYS_MORE = 1;
const val MORE_KEYS_NORMAL = 0;

View file

@ -2,6 +2,7 @@
package org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser
import android.content.Context
import android.content.res.Resources
import android.view.inputmethod.EditorInfo
import org.dslul.openboard.inputmethod.keyboard.Key
import org.dslul.openboard.inputmethod.keyboard.Key.KeyParams
@ -10,7 +11,9 @@ 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.latin.R
import org.dslul.openboard.inputmethod.latin.common.splitOnWhitespace
import org.dslul.openboard.inputmethod.latin.utils.InputTypeUtils
import org.dslul.openboard.inputmethod.latin.utils.RunInLocale
import org.dslul.openboard.inputmethod.latin.utils.sumOf
/**
@ -27,14 +30,25 @@ import org.dslul.openboard.inputmethod.latin.utils.sumOf
*/
class SimpleKeyboardParser(private val params: KeyboardParams, private val context: Context) {
fun parseFromAssets(layoutName: String) =
parse(context.assets.open("layouts/$layoutName.txt").reader().readText())
private var addExtraKeys = false
fun parseFromAssets(layoutName: String): ArrayList<ArrayList<KeyParams>> {
val layoutFile = when (layoutName) {
"nordic" -> { addExtraKeys = true; "qwerty" }
"spanish" -> {
if (params.mId.locale.language == "eo") "eo" // this behaves a bit different than before, but probably still fine
else { addExtraKeys = true; "qwerty" }
}
"german", "swiss", "serbian_qwertz" -> { addExtraKeys = true; "qwertz" }
else -> layoutName
}
return parse(context.assets.open("layouts/$layoutFile.txt").reader().readText())
}
fun parse(layoutContent: String): ArrayList<ArrayList<KeyParams>> {
params.readAttributes(context, null)
val keysInRows = ArrayList<ArrayList<KeyParams>>()
val baseKeys: MutableList<List<BaseKey>> = parseAdjustablePartOfLayout(layoutContent)
val baseKeys: MutableList<List<BaseKey>> = parseCoreLayout(layoutContent)
if (!params.mId.mNumberRowEnabled) {
// todo (later): not all layouts have numbers on first row, so maybe have some layout flag to switch it off (or an option)
// but for latin it's fine, so don't care now
@ -51,7 +65,14 @@ class SimpleKeyboardParser(private val params: KeyboardParams, private val conte
// are always added to the rows near the bottom
keysInRows.add(getBottomRowAndAdjustBaseKeys(baseKeys))
baseKeys.reversed().forEachIndexed { i, row ->
baseKeys.reversed().forEachIndexed { i, it ->
val row: List<BaseKey> = if (i == 0) {
// add bottom row extra keys
it + context.getString(R.string.key_def_extra_bottom_right)
.split(",").mapNotNull { if (it.isBlank()) null else BaseKey(it.trim()) }
} else {
it
}
// parse functional keys for this row (if any)
val functionalKeysDefs = if (i < functionalKeysReversed.size) functionalKeysReversed[i]
else emptyList<String>() to emptyList()
@ -61,38 +82,50 @@ class SimpleKeyboardParser(private val params: KeyboardParams, private val conte
// determine key width, maybe scale factor for keys, and spacers to add
val usedKeyWidth = params.mDefaultRelativeKeyWidth * row.size
val availableWidth = 1f - (functionalKeysLeft.sumOf { it.mRelativeWidth }) - (functionalKeysRight.sumOf { it.mRelativeWidth })
val width: Float
val functionalKeyWidth = (functionalKeysLeft.sumOf { it.mRelativeWidth }) + (functionalKeysRight.sumOf { it.mRelativeWidth })
val availableWidth = 1f - functionalKeyWidth
var keyWidth: Float
val spacerWidth: Float
if (availableWidth - usedKeyWidth > 0.0001f) { // don't add spacers if only a tiny bit is empty
// width available, add spacer
width = params.mDefaultRelativeKeyWidth
keyWidth = params.mDefaultRelativeKeyWidth
spacerWidth = (availableWidth - usedKeyWidth) / 2
} else {
// need more width, re-scale
spacerWidth = 0f
width = availableWidth / row.size
keyWidth = availableWidth / row.size
}
if (spacerWidth != 0f) {
paramsRow.add(KeyParams.newSpacer(params).apply { mRelativeWidth = spacerWidth })
paramsRow.add(KeyParams.newSpacer(params, spacerWidth))
}
if (keyWidth < params.mDefaultRelativeKeyWidth * 0.82 && spacerWidth == 0f) {
// keys are very narrow, also rescale the functional keys to make keys a little wider
// 0.82 is just some guess for "too narrow"
// todo (maybe): works reasonably well, but actually functional keys could give some more of their width,
// as long as they end up above mDefaultRelativeKeyWidth
val allKeyScale = 1f / (functionalKeyWidth + row.size * params.mDefaultRelativeKeyWidth)
keyWidth = params.mDefaultRelativeKeyWidth * allKeyScale
functionalKeysLeft.forEach { it.mRelativeWidth *= allKeyScale }
functionalKeysRight.forEach { it.mRelativeWidth *= allKeyScale }
}
for (key in row) {
paramsRow.add(KeyParams(
key.label,
params,
width, // any reasonable way to scale width if there is a long text? might be allowed in user-defined layout
keyWidth, // any reasonable way to scale width if there is a long text? might be allowed in user-defined layout
0, // todo: maybe autoScale / autoXScale if label has more than 2 characters (exception for emojis?)
Key.BACKGROUND_TYPE_NORMAL,
key.moreKeys
))
}
if (spacerWidth != 0f) {
paramsRow.add(KeyParams.newSpacer(params).apply { mRelativeWidth = spacerWidth })
paramsRow.add(KeyParams.newSpacer(params, spacerWidth))
}
functionalKeysRight.forEach { paramsRow.add(it) }
keysInRows.add(0, paramsRow) // we're doing it backwards, so add on top
}
resizeLastNormalRowIfNecessaryForAlignment(keysInRows)
// rescale height if we have more than 4 rows
val heightRescale = if (keysInRows.size > 4) 4f / keysInRows.size else 1f
if (params.mId.mNumberRowEnabled)
@ -108,13 +141,53 @@ class SimpleKeyboardParser(private val params: KeyboardParams, private val conte
return keysInRows
}
private fun parseAdjustablePartOfLayout(layoutContent: String) =
layoutContent.split("\n\n").mapTo(mutableListOf()) { row -> row.split("\n").mapNotNull {
if (it.isBlank()) return@mapNotNull null
val split = it.split(" ")
val moreKeys = if (split.size == 1) null else Array(split.size - 1) { split[it + 1] }
BaseKey(split.first(), moreKeys)
} }
// resize keys in last row if they are wider than keys in the row above
// this is done so the keys align with the keys above
// done e.g. for nordic and swiss layouts
private fun resizeLastNormalRowIfNecessaryForAlignment(keysInRows: ArrayList<ArrayList<KeyParams>>) {
if (keysInRows.size < 3)
return
val lastNormalRow = keysInRows[keysInRows.lastIndex - 1]
val rowAboveLastNormalRow = keysInRows[keysInRows.lastIndex - 2]
if (lastNormalRow.any { it.isSpacer } || rowAboveLastNormalRow.any { it.isSpacer })
return // annoying to deal with, and probably no resize needed anyway
val lastNormalRowKeyWidth = lastNormalRow.first { it.mBackgroundType == Key.BACKGROUND_TYPE_NORMAL }.mRelativeWidth
val rowAboveLastNormalRowKeyWidth = rowAboveLastNormalRow.first { it.mBackgroundType == Key.BACKGROUND_TYPE_NORMAL }.mRelativeWidth
if (lastNormalRowKeyWidth <= rowAboveLastNormalRowKeyWidth + 0.0001f)
return // no need
if (lastNormalRow.any { it.mBackgroundType == Key.BACKGROUND_TYPE_NORMAL && it.mRelativeWidth != lastNormalRowKeyWidth })
return // normal keys have different width, don't deal with this
val numberOfNormalKeys = lastNormalRow.count { it.mBackgroundType == Key.BACKGROUND_TYPE_NORMAL }
val widthBefore = numberOfNormalKeys * lastNormalRowKeyWidth
val widthAfter = numberOfNormalKeys * rowAboveLastNormalRowKeyWidth
val spacerWidth = (widthBefore - widthAfter) / 2
// resize keys and add spacers
lastNormalRow.forEach { if (it.mBackgroundType == Key.BACKGROUND_TYPE_NORMAL) it.mRelativeWidth = rowAboveLastNormalRowKeyWidth }
lastNormalRow.add(lastNormalRow.indexOfFirst { it.mBackgroundType == Key.BACKGROUND_TYPE_NORMAL }, KeyParams.newSpacer(params, spacerWidth))
lastNormalRow.add(lastNormalRow.indexOfLast { it.mBackgroundType == Key.BACKGROUND_TYPE_NORMAL } + 1, KeyParams.newSpacer(params, spacerWidth))
}
private fun parseCoreLayout(layoutContent: String) =
layoutContent.replace("\r\n", "\n").split("\n\n").mapIndexedTo(mutableListOf()) { i, row ->
row.split("\n").mapNotNull {
if (it.isBlank()) return@mapNotNull null
val split = it.splitOnWhitespace()
val moreKeys = if (split.size == 1) {
null
} else if (split.size == 2 && split.last() == "$$$") { // todo: no good reason to ignore it if size > 2
// todo (later): could improve handling and show more currency moreKeys, depending on the moreMoreKeys setting
if (params.mId.passwordInput())
arrayOf("$")
else
arrayOf(getCurrencyKey(params.mId.locale).first)
} else {
Array(split.size - 1) { split[it + 1] }
}
BaseKey(split.first(), moreKeys)
} + if (addExtraKeys)
(params.mLocaleKeyTexts.getExtraKeys(i + 1)?.let { it.map { BaseKey(it.first, it.second) } } ?: emptyList())
else emptyList()
}
private fun parseFunctionalKeys(): List<Pair<List<String>, List<String>>> =
context.getString(R.string.key_def_functional).split("\n").mapNotNull { line ->
@ -136,7 +209,7 @@ class SimpleKeyboardParser(private val params: KeyboardParams, private val conte
baseKeys.removeLast()
val bottomRow = ArrayList<KeyParams>()
context.getString(R.string.key_def_bottom_row).split(",").forEach {
val key = it.trim().split(" ").first()
val key = it.trim().splitOnWhitespace().first()
val adjustKey = when (key) {
KEY_COMMA -> adjustedKeys?.first()
KEY_PERIOD -> adjustedKeys?.last()
@ -203,7 +276,7 @@ class SimpleKeyboardParser(private val params: KeyboardParams, private val conte
n,
params,
params.mDefaultRelativeKeyWidth,
Key.LABEL_FLAGS_DISABLE_HINT_LABEL, // todo (later): maybe optional or enable (but then all numbers should have hints)
Key.LABEL_FLAGS_DISABLE_HINT_LABEL, // todo (later): maybe optional or enable (but then all numbers should have moreKeys)
Key.BACKGROUND_TYPE_NORMAL,
numbersMoreKeys[i] // todo (later, non-latin): language may add some (either alt numbers, or latin numbers if they are replaced above, see number todo)
))
@ -213,7 +286,7 @@ class SimpleKeyboardParser(private val params: KeyboardParams, private val conte
// for comma and period: label will override default, moreKeys will be appended
private fun getFunctionalKeyParams(def: String, label: String? = null, moreKeys: Array<String>? = null): KeyParams {
val split = def.trim().split(" ")
val split = def.trim().splitOnWhitespace()
val key = split[0]
val width = if (split.size == 2) split[1].substringBefore("%").toFloat() / 100f
else params.mDefaultRelativeKeyWidth
@ -248,7 +321,7 @@ class SimpleKeyboardParser(private val params: KeyboardParams, private val conte
width,
Key.LABEL_FLAGS_HAS_POPUP_HINT or Key.LABEL_FLAGS_HAS_SHIFTED_LETTER_HINT, // todo (later): check what LABEL_FLAGS_HAS_SHIFTED_LETTER_HINT does, maybe remove the flag here
if (label?.first()?.isLetter() == true) Key.BACKGROUND_TYPE_NORMAL else Key.BACKGROUND_TYPE_FUNCTIONAL,
moreKeys?.let { getPeriodMoreKeys() + it } ?: getPeriodMoreKeys()
moreKeys?.let { getPunctuationMoreKeys() + it } ?: getPunctuationMoreKeys()
)
KEY_ACTION -> KeyParams(
"${getActionKeyLabel()}|${getActionKeyCode()}",
@ -293,13 +366,13 @@ class SimpleKeyboardParser(private val params: KeyboardParams, private val conte
KEY_EMOJI_COM -> if (params.mId.mMode == KeyboardId.MODE_URL || params.mId.mMode == KeyboardId.MODE_EMAIL)
getFunctionalKeyParams(KEY_COM)
else getFunctionalKeyParams(KEY_EMOJI)
KEY_COM -> KeyParams(
".com", // todo: should depend on language
KEY_COM -> KeyParams( // todo: label and moreKeys could be in localeKeyTexts
".com",
params,
width,
Key.LABEL_FLAGS_AUTO_X_SCALE or Key.LABEL_FLAGS_FONT_NORMAL or Key.LABEL_FLAGS_HAS_POPUP_HINT or Key.LABEL_FLAGS_PRESERVE_CASE,
Key.BACKGROUND_TYPE_FUNCTIONAL,
arrayOf("!hasLabels!", ".net", ".org", ".gov", ".edu") // todo: maybe should be in languageMoreKeys
arrayOf("!hasLabels!", ".net", ".org", ".gov", ".edu")
)
KEY_LANGUAGE_SWITCH -> KeyParams(
"!icon/language_switch_key|!code/key_language_switch",
@ -325,22 +398,6 @@ class SimpleKeyboardParser(private val params: KeyboardParams, private val conte
Key.BACKGROUND_TYPE_FUNCTIONAL,
null
)
KEY_EXCLAMATION -> KeyParams(
"!",
params,
width,
Key.LABEL_FLAGS_FONT_DEFAULT,
Key.BACKGROUND_TYPE_NORMAL,
arrayOf("¡") // todo (later) may depend on language
)
KEY_QUESTION -> KeyParams(
"\\?",
params,
width,
Key.LABEL_FLAGS_FONT_DEFAULT,
Key.BACKGROUND_TYPE_NORMAL,
arrayOf("¿") // todo (later) may depend on language
)
else -> throw IllegalArgumentException("unknown key definition \"$key\"")
}
}
@ -370,7 +427,6 @@ class SimpleKeyboardParser(private val params: KeyboardParams, private val conte
"!code/key_shift_enter"
else "!code/key_enter"
private fun getActionKeyMoreKeys(): Array<String>? {
val action = params.mId.imeAction()
val navigatePrev = params.mId.navigatePrevious()
@ -431,23 +487,20 @@ class SimpleKeyboardParser(private val params: KeyboardParams, private val conte
private fun String.replaceIconWithLabelIfNoDrawable(): String {
if (params.mIconsSet.getIconDrawable(KeyboardIconsSet.getIconId(this)) != null) return this
val id = context.resources.getIdentifier("label_$this", "string", context.packageName)
return context.getString(id)
val ril = object : RunInLocale<String>() { // todo (later): simpler way of doing this in a single line?
override fun job(res: Resources) = res.getString(id)
}
return ril.runInLocale(context.resources, params.mId.locale)
}
// todo: may depend on language
private fun getAlphabetLabel(): String {
return "ABC"
}
private fun getAlphabetLabel() = params.mLocaleKeyTexts.labelAlphabet
// todo: may depend on language
private fun getSymbolLabel(): String {
return "\\?123"
}
private fun getSymbolLabel() = params.mLocaleKeyTexts.labelSymbols
private fun getShiftLabel(): String {
val elementId = params.mId.mElementId
if (elementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED)
return "=\\<" // todo: may depend on language
return params.mLocaleKeyTexts.labelShiftSymbols
if (elementId == KeyboardId.ELEMENT_SYMBOLS)
return getSymbolLabel()
if (elementId == KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED || elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED
@ -476,16 +529,29 @@ class SimpleKeyboardParser(private val params: KeyboardParams, private val conte
return keys.toTypedArray()
}
private fun getPeriodMoreKeys(): Array<String> {
private fun getPunctuationMoreKeys(): Array<String> {
if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS || params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED)
return arrayOf("")
// todo: language-dependent, also influences the number after autoColumnOrder
// there is a weird messup with morekeys_punctuation and morekeys_period
// by default, morekeys_period is taken from morekeys_punctuation, but some languages override this
// morekeys_period is also changed by some languages
// period key always uses morekeys_period, except for dvorak layout which is the only user of morekeys_punctuation
// -> clean it up when implementing the language-dependent moreKeys
return arrayOf("!autoColumnOrder!8", "\\,", "?", "!", "#", ")", "(", "/", ";", "'", "@", ":", "-", "\"", "+", "\\%", "&")
val moreKeys = params.mLocaleKeyTexts.getMoreKeys("punctuation") ?:
// todo: some (non-latin) languages have different parenthesis keys
arrayOf("${Key.MORE_KEYS_AUTO_COLUMN_ORDER}8", "\\,", "?", "!", "#", ")", "(", "/", ";", "'", "@", ":", "-", "\"", "+", "\\%", "&")
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
// this makes use of removal of empty moreKeys in MoreKeySpec.insertAdditionalMoreKeys
// todo: maybe do this as part of removing unnecessary moreKeys instead of here?
moreKeys[moreKeys.indexOf("!")] = ""
moreKeys[moreKeys.indexOf("?")] = ""
val columns = moreKeys[0].substringAfter(Key.MORE_KEYS_AUTO_COLUMN_ORDER).toIntOrNull()
if (columns != null)
moreKeys[0] = "${Key.MORE_KEYS_AUTO_COLUMN_ORDER}${columns - 1}"
}
return moreKeys
}
companion object {
fun hasLayoutFile(layoutName: String) = layoutName in supportedLayouts
// todo: adjust when changing layout names, and of course when anything changes...
private val supportedLayouts = hashSetOf("qwerty", "qwertz", "halmak", "workman", "bepo", "swiss", "german", "nordic", "spanish", "serbian_qwertz")
}
}
@ -536,5 +602,3 @@ private const val KEY_SHIFT = "shift"
private const val KEY_NUMPAD = "numpad"
private const val KEY_SYMBOL = "symbol"
private const val KEY_ALPHA = "alphabet"
private const val KEY_QUESTION = "question"
private const val KEY_EXCLAMATION = "exclamation"

View file

@ -609,6 +609,8 @@ public final class StringUtils {
if (label == null || !ScriptUtils.scriptSupportsUppercase(locale)) {
return label;
}
if (label.equals("ß"))
return ""; // upcasing of standalone ß, SS is not useful as s is on the keyboard anyway
return label.toUpperCase(getLocaleUsedForToTitleCase(locale));
}

File diff suppressed because one or more lines are too long

View file

@ -15,8 +15,11 @@ import android.util.Log
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.preference.Preference
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.dslul.openboard.inputmethod.dictionarypack.DictionaryPackConstants
import org.dslul.openboard.inputmethod.keyboard.KeyboardLayoutSet
import org.dslul.openboard.inputmethod.keyboard.KeyboardSwitcher
import org.dslul.openboard.inputmethod.latin.AudioAndHapticFeedbackManager
import org.dslul.openboard.inputmethod.latin.BuildConfig
import org.dslul.openboard.inputmethod.latin.R
@ -24,9 +27,6 @@ import org.dslul.openboard.inputmethod.latin.SystemBroadcastReceiver
import org.dslul.openboard.inputmethod.latin.common.FileUtils
import org.dslul.openboard.inputmethod.latin.define.JniLibName
import org.dslul.openboard.inputmethod.latin.settings.SeekBarDialogPreference.ValueProxy
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.dslul.openboard.inputmethod.keyboard.KeyboardSwitcher
import java.io.File
import java.io.FileInputStream
import java.io.IOException
@ -56,8 +56,6 @@ class AdvancedSettingsFragment : SubScreenFragment() {
"userunigram.*/userunigram.*\\.(body|header)".toRegex(),
"UserHistoryDictionary.*/UserHistoryDictionary.*\\.(body|header)".toRegex(),
"spellcheck_userunigram.*/spellcheck_userunigram.*\\.(body|header)".toRegex(),
// todo: found "b.<locale>.dict" folder, where does it come from?
// possibly some obfuscation thing that occurred after upgrading to gradle 8?
) }
private val libraryFilePicker = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
@ -290,10 +288,9 @@ class AdvancedSettingsFragment : SubScreenFragment() {
}
override fun onSharedPreferenceChanged(prefs: SharedPreferences, key: String?) {
if (Settings.PREF_SHOW_SETUP_WIZARD_ICON == key) {
SystemBroadcastReceiver.toggleAppIcon(requireContext())
} else if (Settings.PREF_SHOW_ALL_MORE_KEYS == key) {
KeyboardLayoutSet.onKeyboardThemeChanged()
when (key) {
Settings.PREF_SHOW_SETUP_WIZARD_ICON -> SystemBroadcastReceiver.toggleAppIcon(requireContext())
Settings.PREF_MORE_MORE_KEYS, Settings.PREF_USE_NEW_KEYBOARD_PARSING -> KeyboardLayoutSet.onSystemLocaleChanged()
}
}
}

View file

@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-3.0-only
package org.dslul.openboard.inputmethod.latin.settings
import android.content.Context
@ -18,6 +17,7 @@ import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.core.view.size
import org.dslul.openboard.inputmethod.dictionarypack.DictionaryPackConstants
import org.dslul.openboard.inputmethod.keyboard.KeyboardLayoutSet
import org.dslul.openboard.inputmethod.latin.BinaryDictionaryGetter
import org.dslul.openboard.inputmethod.latin.R
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils
@ -182,9 +182,11 @@ class LanguageSettingsDialog(
Settings.setSecondaryLocales(prefs, mainLocaleString, localeStrings - locale.toString())
binding.secondaryLocales.removeView(rowBinding.root)
reloadSetting()
KeyboardLayoutSet.onSystemLocaleChanged()
}
}
binding.secondaryLocales.addView(rowBinding.root)
KeyboardLayoutSet.onSystemLocaleChanged()
}
private fun fillDictionariesView() {

View file

@ -24,6 +24,7 @@ import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import org.dslul.openboard.inputmethod.keyboard.KeyboardTheme;
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.LocaleKeyTextsKt;
import org.dslul.openboard.inputmethod.latin.AudioAndHapticFeedbackManager;
import org.dslul.openboard.inputmethod.latin.InputAttributes;
import org.dslul.openboard.inputmethod.latin.R;
@ -102,6 +103,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static final String PREF_GESTURE_FLOATING_PREVIEW_TEXT = "pref_gesture_floating_preview_text";
public static final String PREF_GESTURE_SPACE_AWARE = "pref_gesture_space_aware";
public static final String PREF_SHOW_SETUP_WIZARD_ICON = "pref_show_setup_wizard_icon";
public static final String PREF_USE_NEW_KEYBOARD_PARSING = "pref_use_new_keyboard_parsing"; // todo: remove later
public static final String PREF_ONE_HANDED_MODE = "pref_one_handed_mode_enabled";
public static final String PREF_ONE_HANDED_GRAVITY = "pref_one_handed_mode_gravity";
@ -124,7 +126,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static final String PREF_ENABLED_INPUT_STYLES = "pref_enabled_input_styles";
public static final String PREF_SELECTED_INPUT_STYLE = "pref_selected_input_style";
public static final String PREF_USE_SYSTEM_LOCALES = "pref_use_system_locales";
public static final String PREF_SHOW_ALL_MORE_KEYS = "pref_show_all_more_keys";
public static final String PREF_MORE_MORE_KEYS = "pref_more_more_keys";
public static final String PREF_URL_DETECTION = "pref_url_detection";
public static final String PREF_DONT_SHOW_MISSING_DICTIONARY_DIALOG = "pref_dont_show_missing_dict_dialog";
@ -469,6 +471,14 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
prefs.edit().putString(Settings.PREF_PINNED_KEYS, String.join(";", keys)).apply();
}
public static int readMoreMoreKeysPref(final SharedPreferences prefs) {
return switch (prefs.getString(Settings.PREF_MORE_MORE_KEYS, "normal")) {
case "all" -> LocaleKeyTextsKt.MORE_KEYS_ALL;
case "more" -> LocaleKeyTextsKt.MORE_KEYS_MORE;
default -> LocaleKeyTextsKt.MORE_KEYS_NORMAL;
};
}
public static List<Locale> getSecondaryLocales(final SharedPreferences prefs, final String mainLocaleString) {
final String localesString = prefs.getString(PREF_SECONDARY_LOCALES_PREFIX + mainLocaleString.toLowerCase(Locale.ROOT), "");

View file

@ -19,6 +19,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.dslul.openboard.inputmethod.compat.AppWorkaroundsUtils;
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.LocaleKeyTextsKt;
import org.dslul.openboard.inputmethod.latin.InputAttributes;
import org.dslul.openboard.inputmethod.latin.R;
import org.dslul.openboard.inputmethod.latin.RichInputMethodManager;
@ -79,7 +80,7 @@ public class SettingsValues {
public final boolean mOneHandedModeEnabled;
public final int mOneHandedModeGravity;
public final boolean mNarrowKeyGaps;
public final boolean mShowAllMoreKeys;
public final int mShowMoreKeys;
public final List<Locale> mSecondaryLocales;
// Use bigrams to predict the next word when there is no input for it yet
public final boolean mBigramPredictionEnabled;
@ -104,6 +105,7 @@ public class SettingsValues {
public final boolean mUrlDetectionEnabled;
public final List<String> mPinnedKeys;
public final float mBottomPaddingScale;
public final boolean mUseNewKeyboardParsing;
// From the input box
@NonNull
@ -215,7 +217,9 @@ public class SettingsValues {
mOneHandedModeGravity = Settings.readOneHandedModeGravity(prefs);
final InputMethodSubtype selectedSubtype = SubtypeSettingsKt.getSelectedSubtype(prefs);
mSecondaryLocales = Settings.getSecondaryLocales(prefs, selectedSubtype.getLocale());
mShowAllMoreKeys = selectedSubtype.isAsciiCapable() && prefs.getBoolean(Settings.PREF_SHOW_ALL_MORE_KEYS, false);
mShowMoreKeys = selectedSubtype.isAsciiCapable()
? Settings.readMoreMoreKeysPref(prefs)
: LocaleKeyTextsKt.MORE_KEYS_NORMAL;
mColors = Settings.getColorsForCurrentTheme(context, prefs);
mAddToPersonalDictionary = prefs.getBoolean(Settings.PREF_ADD_TO_PERSONAL_DICTIONARY, false);
@ -230,6 +234,7 @@ public class SettingsValues {
mPinnedKeys = Settings.readPinnedKeys(prefs);
mSpacingAndPunctuations = new SpacingAndPunctuations(res, mUrlDetectionEnabled);
mBottomPaddingScale = prefs.getFloat(Settings.PREF_BOTTOM_PADDING_SCALE, DEFAULT_SIZE_SCALE);
mUseNewKeyboardParsing = prefs.getBoolean(Settings.PREF_USE_NEW_KEYBOARD_PARSING, true);
}
public boolean isApplicationSpecifiedCompletionsOn() {

View file

@ -89,8 +89,6 @@
<string name="space_trackpad_summary">কার্সর সরানোর জন্য স্পেসবারে সোয়াইপ করুন</string>
<string name="autospace_after_punctuation">যতিচিহ্নের পরে স্বয়ংক্রিয় স্পেস</string>
<string name="autospace_after_punctuation_summary">নতুন শব্দ লেখার সময় যতিচিহ্নের পরে স্বয়ংক্রিয়ভাবে স্পেস বসবে</string>
<string name="show_all_more_keys_title">পপআপে সব অক্ষর প্রদর্শন</string>
<string name="show_all_more_keys_summary">ল্যাটিন কিবোর্ড ব্যবহারের সময় বোতামে দীর্ঘ চাপে বেশি অক্ষর দেখাবে</string>
<string name="url_detection_title">ইউআরএল শনাক্তকরণ</string>
<string name="url_detection_summary">ইউআরএল এবং অনুরূপ একটি একক শব্দ হিসেবে শনাক্ত করার চেষ্টা করবে</string>
<string name="prefs_force_incognito_mode">ছদ্মবেশী মোড আরোপ</string>

View file

@ -257,8 +257,6 @@ Nouveau dictionnaire:
<string name="subtype_with_layout_bn_BD"><xliff:g id="LANGUAGE_NAME" example="Bangla">%s</xliff:g> (Akkhor)</string>
<string name="autospace_after_punctuation">Espace automatique après la ponctuation</string>
<string name="autospace_after_punctuation_summary">Insertion automatique d\'un espace après la ponctuation lors de la saisie d\'un nouveau mot</string>
<string name="show_all_more_keys_title">Afficher tous les caractères dans les popups</string>
<string name="show_all_more_keys_summary">Lorsque l\'on utilise un clavier latin, un appui long sur une touche permet d\'obtenir davantage de caractères</string>
<string name="url_detection_title">Détection d\'URL</string>
<string name="url_detection_summary">Essaye de détecter les URL et autres éléments similaires comme un seul mot</string>
<string name="use_system_language_to_select_input_method_subtypes">"Utiliser les langues du système"</string>

View file

@ -2,6 +2,7 @@
<resources>
<string name="key_def_functional">";delete 10%
;action 10%
shift 10%; exclamation, question, shift"</string>
shift 10%; shift"</string>
<string name="key_def_extra_bottom_right">"!, ?"</string>
<string name="key_def_bottom_row">"symbol, comma, space, period, emoji_com"</string>
</resources>

View file

@ -145,4 +145,14 @@
<item>@string/language_switch_key_switch_input_method</item>
<item>@string/language_switch_key_switch_both</item>
</string-array>
<string-array name="show_more_keys_values">
<item>normal</item>
<item>more</item>
<item>all</item>
</string-array>
<string-array name="show_more_keys_entries">
<item>@string/show_more_keys_normal</item>
<item>@string/show_more_keys_more</item>
<item>@string/show_more_keys_all</item>
</string-array>
</resources>

View file

@ -7,6 +7,10 @@
Width (in percent of keyboard width) can be appended to the key name, defaults to default key width
-->
<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)
-->
<string name="key_def_extra_bottom_right" 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,

View file

@ -160,6 +160,10 @@
<string name="space_language_slide">Space bar language slide</string>
<!-- Description for "space_language_slide" option. -->
<string name="space_language_slide_summary">Swipe upwards on the spacebar to change the language</string>
<!-- Preferences item for enabling the simplified keyboard parser and future other parsers (will be removed after some testing) -->
<string name="use_new_keyboard_parsing">Use new keyboard parser</string>
<!-- Description for "use_new_keyboard_parsing" option (will be removed after some testing) -->
<string name="use_new_keyboard_parsing_summary">Use this setting if you encounter issues with changed keyboard layouts (please report the problem). This setting will be removed at some point.</string>
<!-- Preferences item and dialog title for backup and restore -->
<string name="backup_restore_title">Backup and restore</string>
<!-- Message for backup and restore dialog -->
@ -189,10 +193,14 @@
<string name="autospace_after_punctuation">Autospace after punctuation</string>
<!-- Description for "insert_more_spaces" option. -->
<string name="autospace_after_punctuation_summary">Automatically insert space after punctuation when typing a new word</string>
<!-- Preferences item for showing all available keys in long-press popup -->
<string name="show_all_more_keys_title">Show all keys in popup</string>
<!-- Description for "show_all_more_keys" option. -->
<string name="show_all_more_keys_summary">When using a latin keyboard, more characters are available on long-pressing a key</string>
<!-- Preferences item for showing more keys in long-press popup -->
<string name="show_more_keys_title">Show more letters with diacritics in popup</string>
<!-- Option for showing only letters defined in the current language file in long-press popup -->
<string name="show_more_keys_normal">Show variants defined in keyboard languages (default)</string>
<!-- Option for showing letters defined in at least two locales -->
<string name="show_more_keys_more">Add common variants</string>
<!-- Option for showing letters defined in any locale -->
<string name="show_more_keys_all">Add all available variants</string>
<!-- Preferences item for enabling URL detection -->
<string name="url_detection_title">URL detection</string>
<!-- Description for "url_detection_title" option. -->

View file

@ -56,6 +56,15 @@
android:defaultValue="true"
android:persistent="true" />
<ListPreference
android:key="pref_more_more_keys"
android:title="@string/show_more_keys_title"
android:entries="@array/show_more_keys_entries"
android:entryValues="@array/show_more_keys_values"
android:defaultValue="normal"
android:summary="%s"
android:persistent="true" />
<Preference
android:key="pref_backup_restore"
android:title="@string/backup_restore_title" />
@ -70,18 +79,18 @@
<PreferenceCategory
android:title="@string/settings_category_experimental">
<SwitchPreferenceCompat
android:key="pref_use_new_keyboard_parsing"
android:title="@string/use_new_keyboard_parsing"
android:summary="@string/use_new_keyboard_parsing_summary"
android:defaultValue="true" />
<SwitchPreferenceCompat
android:key="pref_space_language_slide"
android:title="@string/space_language_slide"
android:summary="@string/space_language_slide_summary"
android:defaultValue="false" />
<SwitchPreferenceCompat
android:key="pref_show_all_more_keys"
android:title="@string/show_all_more_keys_title"
android:summary="@string/show_all_more_keys_summary"
android:defaultValue="false" />
<SwitchPreferenceCompat
android:key="pref_url_detection"
android:title="@string/url_detection_title"

9
layouts.md Normal file
View file

@ -0,0 +1,9 @@
# Layouts
(WIP) information about the layout format
## simple
One key per line, two consecutive newlines mark a row end.
Key format: [label] [moreKeys], all separated by space, e.g. `a 0 + *` will create a key with text a, and the keys `0`, `+`, and `*` on long press. Some characters currently require escape using `\` (todo: add the list, or better add them in code instead of requiring it in the layouts).
Special symbols: `%` (only for language-dependent moreKeys, not user defined, also better use sth like `%%%`) acts as placeholder for normal moreKeys. `$$$` will be replaced by currency (or default to `$`).
Language-dependent moreKeys should never contain "special" moreKeys, i.e. those starting with `!` (exception for `punctuation`)

245
tools/morekeys_reader.py Normal file
View file

@ -0,0 +1,245 @@
#!/bin/python
import pathlib
import xml.etree.ElementTree as ET
import langcodes
import re
# (WIP) script for reading moreKeys from tools used to create KeyTextsTable while resolving "!text/" references
# plan is to create a straightforward moreKeys file per language
xml_folder = "make-keyboard-text/src/main/resources/"
default_file = pathlib.Path(__file__).parent / f"{xml_folder}/values/donottranslate-more-keys.xml"
out_folder = pathlib.Path(__file__).parent / "../app/src/main/assets/language_key_texts/"
def append_to_morekeys(morekeys, text, label):
if label in morekeys:
morekeys[label] = morekeys[label] + " " + text
else:
morekeys[label] = text
def prepend_to_morekeys(morekeys, text, label):
if label in morekeys:
morekeys[label] = text + " " + morekeys[label]
else:
morekeys[label] = text
def read_keys(file):
root = ET.parse(file).getroot()
morekeys = dict()
extra_keys = dict()
for key in root.iter("resources"):
for string in key.iter("string"):
for tag, value in string.items():
if tag != "name":
print("ignoring tag " + tag)
continue
if string.text is None:
text = ""
else:
text = resolve_text(string.text, file)
if "!text/" in text:
raise ValueError(f"can't have !text/ in {text} (from {string.text})")
if " " in text:
raise ValueError(f"can't have consecutive spaces in {text} (from {string.text})")
if value.startswith("keyspec_") and "_row" in value:
# put additional key labels (for nordic, spanish, swiss)
key = value.split("_row")[1]
d = extra_keys.get(key, dict())
d["label"] = text
extra_keys[key] = d
elif value.startswith("morekeys_") and "_row" in value:
# put additional key morekeys (for nordic, spanish, swiss)
key = value.split("_row")[1]
d = extra_keys.get(key, dict())
d["morekeys"] = text
extra_keys[key] = d
elif value.startswith("morekeys_"):
key_label = value.split("morekeys_")[1]
if len(key_label) > 1 and key_label != "punctuation":
print(f"ignoring long more key: {key_label}: {text}")
continue
morekeys[key_label] = text
elif value == "single_quotes":
prepend_to_morekeys(morekeys, text, '\'')
elif value == "single_angle_quotes":
append_to_morekeys(morekeys, text, '\'')
elif value == "double_quotes":
prepend_to_morekeys(morekeys, text, '\"')
elif value == "double_angle_quotes":
append_to_morekeys(morekeys, text, '\"')
# todo: labels should be in [labels] and use sth like symbols: ?123
else:
print(f"ignored tag: {tag}={value}, {text}")
keys = dict()
keys["morekeys"] = morekeys
keys["extra_keys"] = extra_keys
return keys
def resolve_text(text, file):
if text.startswith("\"") and text.endswith("\"") and len(text) > 1:
text = text[1:-1]
sp = re.split("(?<!\\\\),", text)
if len(sp) > 1: # resolve each entry separately
result = []
for t in sp:
resolved = resolve_text(t, file)
if text.startswith("\\"): # remove backslash at start, this seems to be happening somewhere in android parsing too
result.append(resolved[1:])
else:
result.append(resolved)
return " ".join(result) # join with space, because that doesn't cause issues with comma in moreKeys
if "!text/" not in text:
if text.startswith("\\"): # see above
return text[1:]
return text
root = ET.parse(file).getroot()
sp = text.split("!text/")
required = sp[1]
for key in root.findall(".//string"):
for tag, value in key.items():
if tag == "name" and value == required:
return resolve_text(key.text, file)
# fall back to searching in no-language values
root = ET.parse(default_file).getroot()
for key in root.findall(".//string"):
for tag, value in key.items():
if tag == "name" and value == required:
return resolve_text(key.text, file)
raise LookupError(text + " not found in " + str(file))
def read_locale_from_folder(folder):
if folder.startswith("values-"):
return folder.split("values-")[1]
return None
def write_keys(outfile, keys, locc=""):
with open(outfile, "w") as f:
# write section [more_keys], then [extra_keys], skip if empty
if len(keys["morekeys"]) > 0:
f.write("[morekeys]\n")
for k, v in keys["morekeys"].items():
f.write(f"{k} {v}\n")
if len(keys["extra_keys"]) > 0:
f.write("\n")
if len(keys["extra_keys"]) > 0 and locc != "eo": # eo has the extra key moved into the layout
f.write("[extra_keys]\n")
# clarify somewhere that extra keys only apply to default layout (where to get?)
for k, v in sorted(keys["extra_keys"].items()):
row = k.split("_")[0]
morekeys = v.get("morekeys", "")
label = v["label"]
if len(morekeys) == 0:
f.write(f"{row}: {label}\n")
else:
f.write(f"{row}: {label} {morekeys}\n")
def get_morekeys_texts(write=False):
val = []
for file in (pathlib.Path(__file__).parent / xml_folder).iterdir():
locc = read_locale_from_folder(file.name)
if locc is None:
continue
loc = langcodes.Language.get(locc)
script = loc.assume_script().script
# some scripts are not detected, fill in the current state of OpenBoard
if locc == "sr" or locc == "ky" or locc == "mn":
script = "Cyrl"
if locc == "sr-rZZ" or locc == "uz" or locc == "zz" or locc == "az" or locc == "tl":
script = "Latn"
if script is None:
raise ValueError("undefined script")
if script != "Latn":
continue # skip non-latin scripts for now
print(file)
keys = read_keys(f"{file}/donottranslate-more-keys.xml")
val.append(keys)
if not write:
continue
outfile_name = locc.replace("-r", "_").lower() + ".txt"
outfile = pathlib.Path(out_folder + outfile_name)
outfile.parent.mkdir(exist_ok=True, parents=True)
write_keys(outfile, keys, locc)
return val
def write_combined_lists(keys):
infos_by_letters = dict()
for key in keys:
for k, v in key["morekeys"].items():
infos = infos_by_letters.get(k, dict())
for l in v.split(" "):
if l == "%":
continue
infos[l] = infos.get(l, 0) + 1
infos_by_letters[k] = infos
with open(out_folder + "all_more_keys.txt", 'w') as f:
f.write("[morekeys]\n")
for letter, info in infos_by_letters.items():
f.write(letter + " " + " ".join(info.keys()) + "\n")
with open(out_folder + "more_more_keys.txt", 'w') as f:
f.write("[morekeys]\n")
for letter, info in infos_by_letters.items():
morekeys = []
for morekey, count in info.items():
if count > 1:
morekeys.append(morekey)
if len(morekeys) > 0:
f.write(letter + " " + " ".join(morekeys) + "\n")
def main():
# k = read_keys(default_file)
# write_keys(pathlib.Path(__file__).parent / f"defaultkeys.txt", k)
keys = get_morekeys_texts()
write_combined_lists(keys)
# need to check strings:
# latin, but only in symbol layout
# single_quotes, double_quotes (both used in morekeys of single/double quotes in symbol keyboard)
# single_angle_quotes, double_angle_quotes (same place as above -> merge into the same base ' or ")
# -> just treat them like morekeys_' and morekeys_"
# ... resolving those is really horrible, check different things and maybe include all if not too much?
# latin, but for layout and not for moreKeys
# keyspec_nordic_row (+swiss and spanish) -> normal keys, what do? really specify a layout? or allow modifying?
# keyspec_q + w, y, x (eo only -> hmm, have a separate layout?)
# not latin, but cyrillic (and maybe other non-latin)
# keyspec_east_slavic_row
# keylabel_to_alpha
# label_go_key and other keys (hi-rZZ and sr-rZZ -> why here? they should be in app strings, right?)
# not in latin (so far)
# keyspec_symbols
# additional_morekeys_symbols
# keyspec_currency
# keylabel_to_symbol
# keyspec_comma
# keyhintlabel_period -> that's with the shifted key hint maybe
# keyhintlabel_tablet_period
# keyspec_period
# keyspec_tablet_period
# keyspec_symbols_question
# keyspec_symbols_semicolon
# keyspec_symbols_percent
# keyspec_tablet_comma
# keyhintlabel_tablet_comma
# keyspec_left_parenthesis + right
# keyspec_left_square_bracket + right
# keyspec_left_curly_bracket + right
# keyspec_less_than + greater
# keyspec_less_than_equal + greater
# keyspec_left_double_angle_quote + right
# keyspec_left_single_angle_quote + right
if __name__ == "__main__":
main()