Update settings categories. Add option for customizing long-press key layout. Draw icon key hints

The texts for more keys have been updated:
* morekeys_a should now include only letters relevant to the active language
* morekeys_misc_a should include misc letters (accents, etc) not specifically needed for typing in the language

The above still needs to be applied to most other languages. Only QWERTY has been updated with the new morekeys system. Other layouts still need to be updated.
This commit is contained in:
Aleksandras Kostarevas 2024-07-07 21:25:36 +03:00
parent 4cba419737
commit dd0ba32804
34 changed files with 2098 additions and 1158 deletions

View File

@ -0,0 +1,6 @@
<vector xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="25" android:viewportWidth="24" android:width="23.04dp">
<path android:fillColor="#00000000" android:pathData="M5.212,14.909V7.636C5.212,6.736 5.569,5.873 6.206,5.236C6.842,4.6 7.706,4.242 8.606,4.242C9.506,4.242 10.369,4.6 11.006,5.236C11.642,5.873 12,6.736 12,7.636V14.909M5.212,10.06H12M10.06,17.818L12.969,20.727L19.757,13.939" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="1.52381" android:strokeColor="#ffffff">
</path>
</vector>

View File

@ -0,0 +1,25 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="13.935484dp" android:viewportHeight="36" android:viewportWidth="62" android:width="24dp">
<path android:fillColor="#FFFFFF" android:pathData="M2,0L7.538,0A2,2 0,0 1,9.538 2L9.538,7.6A2,2 0,0 1,7.538 9.6L2,9.6A2,2 0,0 1,0 7.6L0,2A2,2 0,0 1,2 0z"/>
<path android:fillColor="#FFFFFF" android:pathData="M2,13.2L7.538,13.2A2,2 0,0 1,9.538 15.2L9.538,20.8A2,2 0,0 1,7.538 22.8L2,22.8A2,2 0,0 1,0 20.8L0,15.2A2,2 0,0 1,2 13.2z"/>
<path android:fillColor="#FFFFFF" android:pathData="M15.115,0L20.653,0A2,2 0,0 1,22.653 2L22.653,7.6A2,2 0,0 1,20.653 9.6L15.115,9.6A2,2 0,0 1,13.115 7.6L13.115,2A2,2 0,0 1,15.115 0z"/>
<path android:fillColor="#FFFFFF" android:pathData="M15.115,13.2L20.653,13.2A2,2 0,0 1,22.653 15.2L22.653,20.8A2,2 0,0 1,20.653 22.8L15.115,22.8A2,2 0,0 1,13.115 20.8L13.115,15.2A2,2 0,0 1,15.115 13.2z"/>
<path android:fillColor="#FFFFFF" android:pathData="M28.231,0L33.769,0A2,2 0,0 1,35.769 2L35.769,7.6A2,2 0,0 1,33.769 9.6L28.231,9.6A2,2 0,0 1,26.231 7.6L26.231,2A2,2 0,0 1,28.231 0z"/>
<path android:fillColor="#FFFFFF" android:pathData="M28.231,13.2L33.769,13.2A2,2 0,0 1,35.769 15.2L35.769,20.8A2,2 0,0 1,33.769 22.8L28.231,22.8A2,2 0,0 1,26.231 20.8L26.231,15.2A2,2 0,0 1,28.231 13.2z"/>
<path android:fillColor="#FFFFFF" android:pathData="M12.731,26.4L49.269,26.4A2,2 0,0 1,51.269 28.4L51.269,34A2,2 0,0 1,49.269 36L12.731,36A2,2 0,0 1,10.731 34L10.731,28.4A2,2 0,0 1,12.731 26.4z"/>
<path android:fillColor="#FFFFFF" android:pathData="M41.346,0L46.884,0A2,2 0,0 1,48.884 2L48.884,7.6A2,2 0,0 1,46.884 9.6L41.346,9.6A2,2 0,0 1,39.346 7.6L39.346,2A2,2 0,0 1,41.346 0z"/>
<path android:fillColor="#FFFFFF" android:pathData="M41.346,13.2L46.884,13.2A2,2 0,0 1,48.884 15.2L48.884,20.8A2,2 0,0 1,46.884 22.8L41.346,22.8A2,2 0,0 1,39.346 20.8L39.346,15.2A2,2 0,0 1,41.346 13.2z"/>
<path android:fillColor="#FFFFFF" android:pathData="M54.462,0L60,0A2,2 0,0 1,62 2L62,7.6A2,2 0,0 1,60 9.6L54.462,9.6A2,2 0,0 1,52.462 7.6L52.462,2A2,2 0,0 1,54.462 0z"/>
<path android:fillColor="#FFFFFF" android:pathData="M54.462,13.2L60,13.2A2,2 0,0 1,62 15.2L62,20.8A2,2 0,0 1,60 22.8L54.462,22.8A2,2 0,0 1,52.462 20.8L52.462,15.2A2,2 0,0 1,54.462 13.2z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="34" android:viewportWidth="32" android:width="22.588236dp">
<path android:fillColor="#00000000" android:pathData="M15.789,16.001V6.91a1.82,1.82 0,0 0,-3.104 -1.286,1.82 1.82,0 0,0 -0.533,1.286v10.303l-1.782,-1.782a2.262,2.262 0,0 0,-2.763 -0.339,1.814 1.814,0 0,0 -0.65,2.451c2.277,4.048 3.605,6.362 3.983,6.943l0.238,0.363a7.268,7.268 0,0 0,6.075 3.273h-0.252,2.424a7.269,7.269 0,0 0,7.272 -7.273v-5.454a1.819,1.819 0,1 0,-3.636 0m-7.272,0v-2.424a1.817,1.817 0,1 1,3.636 0v3.03m0,-1.818a1.817,1.817 0,1 1,3.636 0v1.818M5.535,8.236c0.93,-0.68 1.93,-1.26 2.982,-1.73m10.908,-0.859c1.6,0.429 3.1,1.093 4.466,1.954" android:strokeColor="#FFFFFF" android:strokeWidth="2"/>
</vector>

View File

@ -0,0 +1,7 @@
<vector xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="30" android:viewportWidth="30" android:width="24dp">
<path android:fillColor="#00000000" android:pathData="M5.304,24.699H24.698M10.152,19.85V10.153C10.152,8.867 10.663,7.634 11.572,6.725C12.482,5.816 13.715,5.305 15.001,5.305C16.287,5.305 17.52,5.816 18.429,6.725C19.338,7.634 19.849,8.867 19.849,10.153V19.85M10.152,12.577H19.849" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="1.90476" android:strokeColor="#ffffff">
</path>
</vector>

View File

@ -0,0 +1,39 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:pathData="M16.001,26.91C13.108,26.91 10.333,25.761 8.287,23.715C6.241,21.669 5.092,18.894 5.092,16.001C5.092,13.108 6.241,10.333 8.287,8.287C10.333,6.241 13.108,5.092 16.001,5.092C22.025,5.092 26.91,9.434 26.91,14.789C26.91,16.074 26.335,17.308 25.312,18.217C24.289,19.126 22.902,19.637 21.455,19.637H18.425C17.884,19.628 17.356,19.801 16.925,20.127C16.493,20.453 16.183,20.914 16.044,21.437C15.904,21.959 15.944,22.513 16.156,23.011C16.368,23.508 16.74,23.921 17.213,24.183C17.455,24.406 17.621,24.7 17.688,25.022C17.754,25.345 17.718,25.68 17.584,25.981C17.451,26.282 17.226,26.533 16.942,26.7C16.658,26.867 16.329,26.94 16.001,26.91Z"
android:strokeLineJoin="round"
android:strokeWidth="1.90476"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round">
</path>
<path
android:pathData="M10.546,14.183C10.546,14.504 10.674,14.813 10.901,15.04C11.129,15.267 11.437,15.395 11.759,15.395C12.08,15.395 12.388,15.267 12.616,15.04C12.843,14.813 12.971,14.504 12.971,14.183C12.971,13.861 12.843,13.553 12.616,13.326C12.388,13.098 12.08,12.971 11.759,12.971C11.437,12.971 11.129,13.098 10.901,13.326C10.674,13.553 10.546,13.861 10.546,14.183Z"
android:strokeLineJoin="round"
android:strokeWidth="1.90476"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round">
</path>
<path
android:pathData="M15.395,10.546C15.395,10.868 15.523,11.176 15.75,11.403C15.977,11.631 16.285,11.759 16.607,11.759C16.928,11.759 17.237,11.631 17.464,11.403C17.691,11.176 17.819,10.868 17.819,10.546C17.819,10.225 17.691,9.917 17.464,9.689C17.237,9.462 16.928,9.334 16.607,9.334C16.285,9.334 15.977,9.462 15.75,9.689C15.523,9.917 15.395,10.225 15.395,10.546Z"
android:strokeLineJoin="round"
android:strokeWidth="1.90476"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round">
</path>
<path
android:pathData="M20.243,14.183C20.243,14.504 20.371,14.813 20.598,15.04C20.826,15.267 21.134,15.395 21.455,15.395C21.777,15.395 22.085,15.267 22.313,15.04C22.54,14.813 22.668,14.504 22.668,14.183C22.668,13.861 22.54,13.553 22.313,13.326C22.085,13.098 21.777,12.971 21.455,12.971C21.134,12.971 20.826,13.098 20.598,13.326C20.371,13.553 20.243,13.861 20.243,14.183Z"
android:strokeLineJoin="round"
android:strokeWidth="1.90476"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round">
</path>
</vector>

View File

@ -42,7 +42,7 @@
<key-style
latin:styleName="baseForShiftKeyStyle"
latin:keyActionFlags="noKeyPreview"
latin:keyLabelFlags="preserveCase"
latin:keyLabelFlags="preserveCase|disableKeyHintLabel"
latin:moreKeys="!noPanelAutoMoreKey!, |!code/key_capslock" />
<switch>
<case
@ -75,6 +75,7 @@
latin:styleName="deleteKeyStyle"
latin:keySpec="!icon/delete_key|!code/key_delete"
latin:keyActionFlags="isRepeatable|noKeyPreview"
latin:keyLabelFlags="disableKeyHintLabel"
latin:backgroundType="functional" />
<!-- emojiKeyStyle must be defined before including @xml/key_syles_enter. -->
<key-style
@ -89,6 +90,7 @@
latin:styleName="spaceKeyStyle"
latin:keySpec="!icon/space_key|!code/key_space"
latin:backgroundType="spacebar"
latin:keyLabelFlags="disableKeyHintLabel"
latin:keyActionFlags="noKeyPreview|enableLongPress" />
<!-- U+200C: ZERO WIDTH NON-JOINER
U+200D: ZERO WIDTH JOINER -->

View File

@ -217,7 +217,7 @@
<key-style
latin:styleName="defaultEnterKeyStyle"
latin:keySpec="!icon/enter_key|!code/key_enter"
latin:keyLabelFlags="preserveCase|autoXScale|followKeyLabelRatio|followFunctionalTextColor"
latin:keyLabelFlags="preserveCase|autoXScale|followKeyLabelRatio|followFunctionalTextColor|disableKeyHintLabel"
latin:keyActionFlags="noKeyPreview"
latin:backgroundType="action"
latin:parentStyle="navigateMoreKeysStyle" />
@ -227,7 +227,7 @@
<key-style
latin:styleName="defaultEnterKeyStyle"
latin:keySpec="!icon/enter_key|!code/key_enter"
latin:keyLabelFlags="preserveCase|autoXScale|followKeyLabelRatio|followFunctionalTextColor"
latin:keyLabelFlags="preserveCase|autoXScale|followKeyLabelRatio|followFunctionalTextColor|disableKeyHintLabel"
latin:keyActionFlags="noKeyPreview"
latin:backgroundType="action"
latin:parentStyle="navigateMoreKeysStyle" />

View File

@ -23,27 +23,17 @@
>
<Key
latin:keySpec="!text/keyspec_q"
latin:keyHintLabel="1"
latin:additionalMoreKeys="1,\\%"
latin:moreKeys="!text/morekeys_q" />
latin:moreKeys="!text/morekeys_q,!text/qwertysyms_q,!text/number_1,!text/actions_q" />
<Key
latin:keySpec="!text/keyspec_w"
latin:keyHintLabel="2"
latin:additionalMoreKeys="2,\\\\"
latin:moreKeys="!text/morekeys_w" />
latin:moreKeys="!text/morekeys_w,!text/qwertysyms_w,!text/number_2,!text/actions_w" />
<Key
latin:keySpec="e"
latin:keyHintLabel="3"
latin:additionalMoreKeys="3,|"
latin:moreKeys="!text/morekeys_e" />
latin:moreKeys="!text/morekeys_e,!text/morekeys_misc_e,!text/qwertysyms_e,!text/number_3,!text/actions_e" />
<Key
latin:keySpec="r"
latin:keyHintLabel="4"
latin:additionalMoreKeys="4,="
latin:moreKeys="!text/morekeys_r" />
latin:moreKeys="!text/morekeys_r,!text/qwertysyms_r,!text/number_4,!text/actions_r" />
<Key
latin:keySpec="t"
latin:keyHintLabel="5"
latin:additionalMoreKeys="5,!text/keyspec_left_square_bracket"
latin:moreKeys="!text/morekeys_t" />
latin:moreKeys="!text/morekeys_t,!text/qwertysyms_t,!text/number_5,!text/actions_t" />
</merge>

View File

@ -23,26 +23,17 @@
>
<Key
latin:keySpec="!text/keyspec_y"
latin:keyHintLabel="6"
latin:additionalMoreKeys="6,!text/keyspec_right_square_bracket"
latin:moreKeys="!text/morekeys_y" />
latin:moreKeys="!text/morekeys_y,!text/qwertysyms_y,!text/number_6,!text/actions_y" />
<Key
latin:keySpec="u"
latin:keyHintLabel="7"
latin:additionalMoreKeys="7,!text/keyspec_less_than"
latin:moreKeys="!text/morekeys_u" />
latin:moreKeys="!text/morekeys_u,!text/morekeys_misc_u,!text/qwertysyms_u,!text/number_7,!text/actions_u" />
<Key
latin:keySpec="i"
latin:keyHintLabel="8"
latin:additionalMoreKeys="8,!text/keyspec_greater_than"
latin:moreKeys="!text/morekeys_i" />
latin:moreKeys="!text/morekeys_i,!text/morekeys_misc_i,!text/qwertysyms_i,!text/number_8,!text/actions_i" />
<Key
latin:keySpec="o"
latin:keyHintLabel="9"
latin:additionalMoreKeys="9,!text/keyspec_left_curly_bracket"
latin:moreKeys="!text/morekeys_o" />
latin:moreKeys="!text/morekeys_o,!text/morekeys_misc_o,!text/qwertysyms_o,!text/number_9,!text/actions_o" />
<Key
latin:keySpec="p"
latin:keyHintLabel="0"
latin:additionalMoreKeys="0,!text/keyspec_right_curly_bracket" />
latin:moreKeys="!text/morekeys_p,!text/qwertysyms_p,!text/number_0,!text/actions_p" />
</merge>

View File

@ -23,26 +23,17 @@
>
<Key
latin:keySpec="a"
latin:keyHintLabel="\@"
latin:additionalMoreKeys="\\@,!icon/action_select_all|!code/action_select_all"
latin:moreKeys="!text/morekeys_a" />
latin:moreKeys="!text/morekeys_a,!text/morekeys_misc_a,!text/qwertysyms_a,!text/actions_a" />
<Key
latin:keySpec="s"
latin:keyHintLabel="#"
latin:additionalMoreKeys="#"
latin:moreKeys="!text/morekeys_s" />
latin:moreKeys="!text/morekeys_s,!text/morekeys_misc_s,!text/qwertysyms_s,!text/actions_s" />
<Key
latin:keySpec="d"
latin:keyHintLabel="$"
latin:additionalMoreKeys="&#x24;,&#x20AC;,&#x00A3;,&#x00A5;,&#x00A2;"
latin:moreKeys="!text/morekeys_d" />
latin:moreKeys="!text/morekeys_d,!text/qwertysyms_d,!text/actions_d" />
<Key
latin:keySpec="f"
latin:keyHintLabel="%"
latin:additionalMoreKeys="%" />
latin:moreKeys="!text/morekeys_f,!text/qwertysyms_f,!text/actions_f" />
<Key
latin:keySpec="g"
latin:keyHintLabel="&amp;"
latin:additionalMoreKeys="&amp;,|"
latin:moreKeys="!text/morekeys_g" />
latin:moreKeys="!text/morekeys_g,!text/qwertysyms_g,!text/actions_g" />
</merge>

View File

@ -23,22 +23,14 @@
>
<Key
latin:keySpec="h"
latin:keyHintLabel="-"
latin:additionalMoreKeys="-,&#x2013;,&#x2014;,_"
latin:moreKeys="!text/morekeys_h" />
latin:moreKeys="!text/morekeys_h,!text/qwertysyms_h,!text/actions_h" />
<Key
latin:keySpec="j"
latin:keyHintLabel="+"
latin:additionalMoreKeys="+,="
latin:moreKeys="!text/morekeys_j" />
latin:moreKeys="!text/morekeys_j,!text/qwertysyms_j,!text/actions_j" />
<Key
latin:keySpec="k"
latin:keyHintLabel="("
latin:additionalMoreKeys="(,[,{,&#x3c;"
latin:moreKeys="!text/morekeys_k" />
latin:moreKeys="!text/morekeys_k,!text/qwertysyms_k,!text/actions_k" />
<Key
latin:keySpec="l"
latin:keyHintLabel=")"
latin:additionalMoreKeys="),],},&#x3e;"
latin:moreKeys="!text/morekeys_l" />
latin:moreKeys="!text/morekeys_l,!text/qwertysyms_l,!text/actions_l" />
</merge>

View File

@ -23,22 +23,14 @@
>
<Key
latin:keySpec="z"
latin:keyHintLabel="*"
latin:additionalMoreKeys="*,!icon/action_undo|!code/action_undo"
latin:moreKeys="!text/morekeys_z" />
latin:moreKeys="!text/morekeys_z,!text/qwertysyms_z,!text/actions_z" />
<Key
latin:keySpec="!text/keyspec_x"
latin:keyHintLabel="&quot;"
latin:additionalMoreKeys="&quot;,!icon/action_cut|!code/action_cut"
latin:moreKeys="!text/morekeys_x" />
latin:moreKeys="!text/morekeys_x,!text/qwertysyms_x,!text/actions_x" />
<Key
latin:keySpec="c"
latin:keyHintLabel="'"
latin:additionalMoreKeys="',!icon/action_copy|!code/action_copy"
latin:moreKeys="!text/morekeys_c" />
latin:moreKeys="!text/morekeys_c,!text/morekeys_misc_c,!text/qwertysyms_c,!text/actions_c" />
<Key
latin:keySpec="v"
latin:keyHintLabel=":"
latin:additionalMoreKeys=":,!icon/action_paste|!code/action_paste"
latin:moreKeys="!text/morekeys_v" />
latin:moreKeys="!text/morekeys_v,!text/qwertysyms_v,!text/actions_v" />
</merge>

View File

@ -23,15 +23,11 @@
>
<Key
latin:keySpec="b"
latin:keyHintLabel=";"
latin:additionalMoreKeys=";" />
latin:moreKeys="!text/morekeys_b,!text/qwertysyms_b,!text/actions_b" />
<Key
latin:keySpec="n"
latin:keyHintLabel="!"
latin:additionalMoreKeys="!"
latin:moreKeys="!text/morekeys_n" />
latin:moreKeys="!text/morekeys_n,!text/morekeys_misc_n,!text/qwertysyms_n,!text/actions_n" />
<Key
latin:keySpec="m"
latin:keyHintLabel="\?"
latin:additionalMoreKeys="\?,/" />
latin:moreKeys="!text/morekeys_m,!text/qwertysyms_m,!text/actions_m" />
</merge>

View File

@ -60,6 +60,7 @@ public class Key implements Comparable<Key> {
private final String mLabel;
/** Hint label to display on the key in conjunction with the label */
private final String mHintLabel;
private final String mHintIconId;
/** Flags of the label */
private final int mLabelFlags;
private static final int LABEL_FLAGS_ALIGN_HINT_LABEL_TO_BOTTOM = 0x02;
@ -215,6 +216,7 @@ public class Key implements Comparable<Key> {
mHorizontalGap = horizontalGap;
mVerticalGap = verticalGap;
mHintLabel = hintLabel;
mHintIconId = ICON_UNDEFINED;
mLabelFlags = labelFlags;
mBackgroundType = backgroundType;
// TODO: Pass keyActionFlags as an argument.
@ -287,7 +289,8 @@ public class Key implements Comparable<Key> {
final boolean needsToUpcase = needsToUpcase(mLabelFlags, params.mId.mElementId);
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);
String[] moreKeys = style.getStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys, s -> params.mId.mLongPressKeySettings.reorderMoreKeys(s));
// Get maximum column order number and set a relevant mode value.
int moreKeysColumnAndFlags = MORE_KEYS_MODE_MAX_COLUMN_WITH_AUTO_ORDER
@ -320,7 +323,7 @@ public class Key implements Comparable<Key> {
additionalMoreKeys = null;
} else {
additionalMoreKeys = style.getStringArray(keyAttr,
R.styleable.Keyboard_Key_additionalMoreKeys);
R.styleable.Keyboard_Key_additionalMoreKeys, null);
}
moreKeys = MoreKeySpec.insertAdditionalMoreKeys(moreKeys, additionalMoreKeys);
@ -357,28 +360,31 @@ public class Key implements Comparable<Key> {
? StringUtils.toTitleCaseOfKeyLabel(label, localeForUpcasing)
: label;
}
if ((mLabelFlags & LABEL_FLAGS_DISABLE_HINT_LABEL) != 0) {
mHintLabel = null;
mHintIconId = null;
} else {
String hintLabel = style.getString(
keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
if(params.mId.mNumberRow && hintLabel != null && hintLabel.matches("\\d+")) {
hintLabel = "";
}
String hintLabel = null;
String hintIcon = null;
if(moreKeys != null && moreKeys.length > 0) {
String hintLabelCandidate = moreKeys[0];
if(hintLabelCandidate.startsWith("\\")) hintLabelCandidate = hintLabelCandidate.substring(1);
if(hintLabelCandidate.length() == 1) {
hintLabel = hintLabelCandidate;
hintLabel = needsToUpcase
? StringUtils.toTitleCaseOfKeyLabel(hintLabelCandidate, localeForUpcasing)
: hintLabelCandidate;
} else if(hintLabelCandidate.contains("!icon/")) {
hintIcon = KeySpecParser.getIconId(hintLabelCandidate);
}
}
mHintLabel = needsToUpcase
? StringUtils.toTitleCaseOfKeyLabel(hintLabel, localeForUpcasing)
: hintLabel;
mHintLabel = hintLabel;
mHintIconId = hintIcon;
}
String outputText = KeySpecParser.getOutputText(keySpec);
if (needsToUpcase) {
outputText = StringUtils.toTitleCaseOfKeyLabel(outputText, localeForUpcasing);
@ -412,7 +418,7 @@ public class Key implements Comparable<Key> {
: code;
}
final int altCodeInAttr = KeySpecParser.parseCode(
style.getString(keyAttr, R.styleable.Keyboard_Key_altCode), CODE_UNSPECIFIED);
style.getString(keyAttr, R.styleable.Keyboard_Key_altCode, null), CODE_UNSPECIFIED);
final int altCode = needsToUpcase
? StringUtils.toTitleCaseOfKeyCode(altCodeInAttr, localeForUpcasing)
: altCodeInAttr;
@ -435,6 +441,7 @@ public class Key implements Comparable<Key> {
mCode = key.mCode;
mLabel = key.mLabel;
mHintLabel = key.mHintLabel;
mHintIconId = key.mHintIconId;
mLabelFlags = key.mLabelFlags;
mIconId = key.mIconId;
mWidth = key.mWidth;
@ -487,6 +494,7 @@ public class Key implements Comparable<Key> {
key.mCode,
key.mLabel,
key.mHintLabel,
key.mHintIconId,
key.mIconId,
key.mBackgroundType,
Arrays.hashCode(key.mMoreKeys),
@ -514,6 +522,7 @@ public class Key implements Comparable<Key> {
&& o.mCode == mCode
&& TextUtils.equals(o.mLabel, mLabel)
&& TextUtils.equals(o.mHintLabel, mHintLabel)
&& TextUtils.equals(o.mHintIconId, mHintIconId)
&& o.mIconId == mIconId
&& o.mBackgroundType == mBackgroundType
&& Arrays.equals(o.mMoreKeys, mMoreKeys)
@ -588,6 +597,11 @@ public class Key implements Comparable<Key> {
return mHintLabel;
}
@Nullable
public String getHintIconId() {
return mHintIconId;
}
@Nullable
public MoreKeySpec[] getMoreKeys() {
return mMoreKeys;
@ -840,6 +854,19 @@ public class Key implements Comparable<Key> {
return icon;
}
@Nullable
public Drawable getHintIcon(final KeyboardIconsSet iconSet, final int alpha) {
final OptionalAttributes attrs = mOptionalAttributes;
final String iconId = getHintIconId();
if(iconId == null) return null;
final Drawable icon = iconSet.getIconDrawable(iconId);
if (icon != null) {
icon.setAlpha(alpha);
}
return icon;
}
@Nullable
public Drawable getPreviewIcon(final KeyboardIconsSet iconSet) {
return iconSet.getIconDrawable(getIconId());

View File

@ -24,6 +24,7 @@ import android.view.inputmethod.EditorInfo;
import org.futo.inputmethod.compat.EditorInfoCompatUtils;
import org.futo.inputmethod.latin.RichInputMethodSubtype;
import org.futo.inputmethod.latin.settings.LongPressKeySettings;
import org.futo.inputmethod.latin.utils.InputTypeUtils;
import java.util.Arrays;
@ -84,6 +85,7 @@ public final class KeyboardId {
public final boolean mHasShortcutKey;
public final boolean mIsSplitLayout;
public final boolean mNumberRow;
public final LongPressKeySettings mLongPressKeySettings;
private final int mHashCode;
@ -102,6 +104,7 @@ public final class KeyboardId {
mHasShortcutKey = params.mVoiceInputKeyEnabled;
mIsSplitLayout = params.mIsSplitLayoutEnabled;
mNumberRow = params.mNumberRow || params.mIsPasswordField;
mLongPressKeySettings = params.mLongPressKeySettings;
mHashCode = computeHashCode(this);
}
@ -124,7 +127,8 @@ public final class KeyboardId {
id.navigatePrevious(),
id.mSubtype,
id.mIsSplitLayout,
id.mNumberRow
id.mNumberRow,
id.mLongPressKeySettings.hashCode()
});
}
@ -147,7 +151,8 @@ public final class KeyboardId {
&& other.navigatePrevious() == navigatePrevious()
&& other.mSubtype.equals(mSubtype)
&& other.mIsSplitLayout == mIsSplitLayout
&& other.mNumberRow == mNumberRow;
&& other.mNumberRow == mNumberRow
&& other.mLongPressKeySettings.equals(mLongPressKeySettings);
}
private static boolean isAlphabetKeyboard(final int elementId) {

View File

@ -39,7 +39,7 @@ import org.futo.inputmethod.keyboard.internal.UniqueKeysCache;
import org.futo.inputmethod.latin.InputAttributes;
import org.futo.inputmethod.latin.R;
import org.futo.inputmethod.latin.RichInputMethodSubtype;
import org.futo.inputmethod.latin.define.DebugFlags;
import org.futo.inputmethod.latin.settings.LongPressKeySettings;
import org.futo.inputmethod.latin.utils.InputTypeUtils;
import org.futo.inputmethod.latin.utils.ScriptUtils;
import org.futo.inputmethod.latin.utils.SubtypeLocaleUtils;
@ -136,6 +136,8 @@ public final class KeyboardLayoutSet {
new SparseArray<>();
boolean mNumberRow;
LongPressKeySettings mLongPressKeySettings;
}
public static void onSystemLocaleChanged() {
@ -272,6 +274,8 @@ public final class KeyboardLayoutSet {
mResources = context.getResources();
final Params params = mParams;
params.mLongPressKeySettings = new LongPressKeySettings(context);
final EditorInfo editorInfo = (ei != null) ? ei : EMPTY_EDITOR_INFO;
params.mMode = getKeyboardMode(editorInfo);
// TODO: Consolidate those with {@link InputAttributes}.

View File

@ -142,9 +142,15 @@ public class KeyboardView extends View {
R.styleable.Keyboard_Key, defStyle, R.style.KeyboardView);
assert(context instanceof ContextThemeWrapper);
assert(((ContextThemeWrapper) context).getBaseContext() instanceof DynamicThemeProviderOwner);
mDrawableProvider = ((DynamicThemeProviderOwner) ((ContextThemeWrapper) context).getBaseContext()).getDrawableProvider();
if(((ContextThemeWrapper) context).getBaseContext() instanceof DynamicThemeProviderOwner) {
mDrawableProvider = ((DynamicThemeProviderOwner) ((ContextThemeWrapper) context).getBaseContext()).getDrawableProvider();
} else if(context instanceof DynamicThemeProviderOwner) {
mDrawableProvider = ((DynamicThemeProviderOwner) context).getDrawableProvider();
} else {
throw new IllegalStateException("Failed to obtain DynamicThemeProvider");
}
boolean isMoreKeys = keyAttr.getBoolean(R.styleable.Keyboard_Key_isMoreKey, false);
boolean isMoreKeysAction = keyAttr.getBoolean(R.styleable.Keyboard_Key_isAction, false);
@ -419,6 +425,8 @@ public class KeyboardView extends View {
final Keyboard keyboard = getKeyboard();
final Drawable icon = (keyboard == null) ? null
: key.getIcon(keyboard.mIconsSet, params.mAnimAlpha);
final Drawable hintIcon = (keyboard == null) ? null
: key.getHintIcon(keyboard.mIconsSet, params.mAnimAlpha);
float labelX = centerX;
float labelBaseline = centerY;
final String label = key.getLabel();
@ -510,6 +518,17 @@ public class KeyboardView extends View {
final float adjustmentY = params.mHintLabelVerticalAdjustment * labelCharHeight;
canvas.drawText(
hintLabel, 0, hintLabel.length(), hintX, hintBaseline + adjustmentY, paint);
} else if(hintIcon != null) {
final float size = key.selectHintTextSize(params);
int iconWidth = (int)size;
int iconHeight = (int)size;
int hintX = keyWidth - iconWidth - (int)mKeyHintLetterPadding;
int hintY = (int)mKeyHintLetterPadding;
hintIcon.setTint(key.selectHintTextColor(params));
drawIcon(canvas, hintIcon, hintX, hintY, iconWidth, iconHeight);
}
// Draw key icon.

View File

@ -18,14 +18,16 @@ package org.futo.inputmethod.keyboard.internal;
import android.content.res.TypedArray;
import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public abstract class KeyStyle {
private final KeyboardTextsSet mTextsSet;
public abstract @Nullable String[] getStringArray(TypedArray a, int index);
public abstract @Nullable String getString(TypedArray a, int index);
public abstract @Nullable String[] getStringArray(TypedArray a, int index, Function<String, String> stringMutator);
public abstract @Nullable String getString(TypedArray a, int index, Function<String, String> stringMutator);
public abstract int getInt(TypedArray a, int index, int defaultValue);
public abstract int getFlags(TypedArray a, int index);
@ -34,17 +36,23 @@ public abstract class KeyStyle {
}
@Nullable
protected String parseString(final TypedArray a, final int index) {
protected String parseString(final TypedArray a, final int index, Function<String, String> stringMutator) {
if (a.hasValue(index)) {
return mTextsSet.resolveTextReference(a.getString(index));
String s = a.getString(index);
if(stringMutator != null) s = stringMutator.apply(s);
return mTextsSet.resolveTextReference(s);
}
return null;
}
@Nullable
protected String[] parseStringArray(final TypedArray a, final int index) {
protected String[] parseStringArray(final TypedArray a, final int index, Function<String, String> stringMutator) {
if (a.hasValue(index)) {
final String text = mTextsSet.resolveTextReference(a.getString(index));
String s = a.getString(index);
if(stringMutator != null) s = stringMutator.apply(s);
final String text = mTextsSet.resolveTextReference(s);
return MoreKeySpec.splitKeySpecs(text);
}
return null;

View File

@ -28,6 +28,7 @@ import org.xmlpull.v1.XmlPullParserException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -59,14 +60,14 @@ public final class KeyStylesSet {
@Override
@Nullable
public String[] getStringArray(final TypedArray a, final int index) {
return parseStringArray(a, index);
public String[] getStringArray(final TypedArray a, final int index, Function<String, String> stringMutator) {
return parseStringArray(a, index, stringMutator);
}
@Override
@Nullable
public String getString(final TypedArray a, final int index) {
return parseString(a, index);
public String getString(final TypedArray a, final int index, Function<String, String> stringMutator) {
return parseString(a, index, stringMutator);
}
@Override
@ -95,9 +96,9 @@ public final class KeyStylesSet {
@Override
@Nullable
public String[] getStringArray(final TypedArray a, final int index) {
public String[] getStringArray(final TypedArray a, final int index, Function<String, String> stringMutator) {
if (a.hasValue(index)) {
return parseStringArray(a, index);
return parseStringArray(a, index, stringMutator);
}
final Object value = mStyleAttributes.get(index);
if (value != null) {
@ -105,21 +106,21 @@ public final class KeyStylesSet {
return Arrays.copyOf(array, array.length);
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
return parentStyle.getStringArray(a, index);
return parentStyle.getStringArray(a, index, stringMutator);
}
@Override
@Nullable
public String getString(final TypedArray a, final int index) {
public String getString(final TypedArray a, final int index, Function<String, String> stringMutator) {
if (a.hasValue(index)) {
return parseString(a, index);
return parseString(a, index, stringMutator);
}
final Object value = mStyleAttributes.get(index);
if (value != null) {
return (String)value;
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
return parentStyle.getString(a, index);
return parentStyle.getString(a, index, stringMutator);
}
@Override
@ -159,7 +160,7 @@ public final class KeyStylesSet {
private void readString(final TypedArray a, final int index) {
if (a.hasValue(index)) {
mStyleAttributes.put(index, parseString(a, index));
mStyleAttributes.put(index, parseString(a, index, null));
}
}
@ -179,7 +180,7 @@ public final class KeyStylesSet {
private void readStringArray(final TypedArray a, final int index) {
if (a.hasValue(index)) {
mStyleAttributes.put(index, parseStringArray(a, index));
mStyleAttributes.put(index, parseStringArray(a, index, null));
}
}
}

View File

@ -512,7 +512,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
final TypedArray keyAttr = mResources.obtainAttributes(
Xml.asAttributeSet(parser), R.styleable.Keyboard_Key);
final KeyStyle keyStyle = mParams.mKeyStyles.getKeyStyle(keyAttr, parser);
final String keySpec = keyStyle.getString(keyAttr, R.styleable.Keyboard_Key_keySpec);
final String keySpec = keyStyle.getString(keyAttr, R.styleable.Keyboard_Key_keySpec, null);
if (TextUtils.isEmpty(keySpec)) {
throw new ParseException("Empty keySpec", parser);
}

View File

@ -0,0 +1,104 @@
package org.futo.inputmethod.latin.settings
import android.content.Context
import androidx.datastore.preferences.core.stringPreferencesKey
import org.futo.inputmethod.keyboard.internal.MoreKeySpec
import org.futo.inputmethod.latin.uix.SettingsKey
import org.futo.inputmethod.latin.uix.getSettingBlocking
enum class LongPressKey {
Numbers,
LanguageKeys,
Symbols,
QuickActions,
MiscLetters
}
fun LongPressKey.name(context: Context): String {
return when(this) {
LongPressKey.Numbers -> "Numbers"
LongPressKey.LanguageKeys -> "Language keys"
LongPressKey.Symbols -> "Symbols"
LongPressKey.QuickActions -> "Quick actions"
LongPressKey.MiscLetters -> "Misc. letters from common languages"
}
}
fun LongPressKey.description(context: Context): String {
return when(this) {
LongPressKey.Numbers -> "e.g. [1] on [q]"
LongPressKey.LanguageKeys -> "e.g. [á] on [a] in Spanish"
LongPressKey.Symbols -> "e.g. [@] on [a]"
LongPressKey.QuickActions -> "e.g. [Copy] on [c]"
LongPressKey.MiscLetters -> "e.g. [ß] on [s] in all Latin script languages"
}
}
private fun getKind(moreKey: String): LongPressKey? {
val moreKeyStripped = moreKey.replace("!text/", "")
return if(moreKeyStripped.startsWith("morekeys_misc_")) {
LongPressKey.MiscLetters
} else if(moreKeyStripped.startsWith("actions_")) {
LongPressKey.QuickActions
} else if(moreKeyStripped.startsWith("qwertysyms_")) {
LongPressKey.Symbols
} else if(moreKeyStripped.startsWith("number_")) {
LongPressKey.Numbers
} else if(moreKeyStripped.startsWith("morekeys_")) {
LongPressKey.LanguageKeys
} else {
null
}
}
val LongPressKeyLayoutSetting = SettingsKey(
stringPreferencesKey("longPressKeyOrdering"),
"${LongPressKey.Numbers.ordinal},${LongPressKey.LanguageKeys.ordinal},${LongPressKey.Symbols.ordinal},${LongPressKey.QuickActions.ordinal},${LongPressKey.MiscLetters.ordinal}"
)
fun String.toLongPressKeyLayoutItems(): List<LongPressKey> {
return this.split(",").mapNotNull {
val id = it.toIntOrNull() ?: return@mapNotNull null
LongPressKey.entries[id]
}
}
fun List<LongPressKey>.toEncodedString(): String {
return this.joinToString(separator = ",") {
"${it.ordinal}"
}
}
class LongPressKeySettings(val context: Context) {
private val currentSetting = context.getSettingBlocking(LongPressKeyLayoutSetting).toLongPressKeyLayoutItems()
fun reorderMoreKeys(moreKeys: String): String {
val keys = MoreKeySpec.splitKeySpecs(moreKeys)?.toList() ?: listOf(moreKeys)
val finalKeys = mutableListOf<String>()
// Add non configurable keys first
keys.forEach { key ->
if(getKind(key) == null) {
finalKeys.add(key)
}
}
// Add the necessary configurable keys in the correct order.
// Key kinds not enabled are not added
currentSetting.forEach { kind ->
keys.forEach { key ->
if(getKind(key) == kind) {
finalKeys.add(key)
}
}
}
return finalKeys.joinToString(separator = ",")
}
override operator fun equals(other: Any?): Boolean {
return other is LongPressKeySettings && (other.currentSetting.joinToString(",") == currentSetting.joinToString(","))
}
}

View File

@ -22,7 +22,7 @@ import androidx.compose.ui.viewinterop.AndroidView
import org.futo.inputmethod.latin.R
@Composable
fun AndroidTextInput() {
fun AndroidTextInput(allowPredictions: Boolean = true) {
val context = LocalContext.current
val bgColor = MaterialTheme.colorScheme.background
val fgColor = MaterialTheme.colorScheme.onBackground
@ -30,9 +30,14 @@ fun AndroidTextInput() {
if(!LocalInspectionMode.current) {
val editText = remember {
EditText(context).apply {
inputType = EditorInfo.TYPE_CLASS_TEXT
isSingleLine = false
this.
if(allowPredictions) {
inputType = EditorInfo.TYPE_CLASS_TEXT
isSingleLine = false
} else {
inputType = EditorInfo.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE
isSingleLine = true
}
setHint(R.string.try_typing)
setBackgroundColor(bgColor.toArgb())

View File

@ -327,7 +327,7 @@ class UixManager(private val latinIME: LatinIME) {
persistentStates[action] = action.persistentState?.let { it(keyboardManagerForAction) }
}
currWindowActionWindow = action.windowImpl?.let { it(keyboardManagerForAction, persistentStates[action]) }
currWindowActionWindow = (action.windowImpl!!)(keyboardManagerForAction, persistentStates[action])
if(action.keepScreenAwake) {
latinIME.window.window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)

View File

@ -465,11 +465,11 @@ fun SettingSliderSharedPrefsInt(
}
@Composable
fun ScrollableList(content: @Composable () -> Unit) {
fun ScrollableList(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
val scrollState = rememberScrollState()
Column(
modifier = Modifier
modifier = modifier
.fillMaxSize()
.verticalScroll(scrollState)
) {

View File

@ -30,11 +30,15 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.futo.inputmethod.latin.uix.BasicThemeProvider
import org.futo.inputmethod.latin.uix.DynamicThemeProvider
import org.futo.inputmethod.latin.uix.DynamicThemeProviderOwner
import org.futo.inputmethod.latin.uix.ImportResourceActivity
import org.futo.inputmethod.latin.uix.THEME_KEY
import org.futo.inputmethod.latin.uix.USE_SYSTEM_VOICE_INPUT
import org.futo.inputmethod.latin.uix.deferGetSetting
import org.futo.inputmethod.latin.uix.getSetting
import org.futo.inputmethod.latin.uix.getSettingFlow
import org.futo.inputmethod.latin.uix.theme.StatusBarColorSetter
import org.futo.inputmethod.latin.uix.theme.ThemeOption
import org.futo.inputmethod.latin.uix.theme.ThemeOptions
@ -76,7 +80,7 @@ public const val IMPORT_GGUF_MODEL_REQUEST = 71067309
public const val EXPORT_GGUF_MODEL_REQUEST = 80595439
class SettingsActivity : ComponentActivity() {
class SettingsActivity : ComponentActivity(), DynamicThemeProviderOwner {
private val themeOption: MutableState<ThemeOption?> = mutableStateOf(null)
private val inputMethodEnabled = mutableStateOf(false)
@ -181,6 +185,23 @@ class SettingsActivity : ComponentActivity() {
checkForUpdateAndSaveToPreferences(applicationContext)
}
lifecycleScope.launch {
getSettingFlow(THEME_KEY).collect {
val themeOptionFromSettings = ThemeOptions[it]
val themeOption = when {
themeOptionFromSettings == null -> VoiceInputTheme
!themeOptionFromSettings.available(this@SettingsActivity) -> VoiceInputTheme
else -> themeOptionFromSettings
}
this@SettingsActivity.themeOption.value = themeOption
this@SettingsActivity.themeProvider = BasicThemeProvider(
context = this@SettingsActivity,
overrideColorScheme = themeOption.obtainColors(this@SettingsActivity)
)
}
}
deferGetSetting(THEME_KEY) {
val themeOptionFromSettings = ThemeOptions[it]
val themeOption = when {
@ -252,4 +273,10 @@ class SettingsActivity : ComponentActivity() {
}
}
}
// Provides theme for keyboard preview
private var themeProvider: BasicThemeProvider? = null
override fun getDrawableProvider(): DynamicThemeProvider {
return themeProvider!!
}
}

View File

@ -24,9 +24,9 @@ import org.futo.inputmethod.latin.uix.settings.pages.PaymentScreen
import org.futo.inputmethod.latin.uix.settings.pages.PaymentThankYouScreen
import org.futo.inputmethod.latin.uix.settings.pages.PredictiveTextScreen
import org.futo.inputmethod.latin.uix.settings.pages.ThemeScreen
import org.futo.inputmethod.latin.uix.settings.pages.TypingScreen
import org.futo.inputmethod.latin.uix.settings.pages.VoiceInputScreen
import org.futo.inputmethod.latin.uix.settings.pages.addModelManagerNavigation
import org.futo.inputmethod.latin.uix.settings.pages.addTypingNavigation
import org.futo.inputmethod.latin.uix.urlDecode
import org.futo.inputmethod.latin.uix.urlEncode
@ -54,7 +54,7 @@ fun SettingsNavigator(
composable("addLanguage") { AddLanguageScreen(navController) }
composable("predictiveText") { PredictiveTextScreen(navController) }
composable("advancedparams") { AdvancedParametersScreen(navController) }
composable("typing") { TypingScreen(navController) }
addTypingNavigation(navController)
composable("voiceInput") { VoiceInputScreen(navController) }
composable("themes") { ThemeScreen(navController) }
composable("help") { HelpScreen(navController) }

View File

@ -64,32 +64,43 @@ fun HomeScreen(navController: NavHostController = rememberNavController()) {
)
NavigationItem(
title = "Predictive Text",
title = "Keyboard & Typing",
style = NavigationItemStyle.HomeSecondary,
navigate = { navController.navigate("predictiveText") },
icon = painterResource(id = R.drawable.shift)
navigate = { navController.navigate("typing") },
icon = painterResource(id = R.drawable.keyboard)
)
NavigationItem(
title = "Typing Preferences",
style = NavigationItemStyle.HomeSecondary,
navigate = { navController.navigate("typing") },
icon = painterResource(id = R.drawable.delete)
title = "Text Prediction",
style = NavigationItemStyle.HomeTertiary,
navigate = { navController.navigate("predictiveText") },
icon = painterResource(id = R.drawable.text_prediction)
)
NavigationItem(
title = "Voice Input",
style = NavigationItemStyle.HomeSecondary,
style = NavigationItemStyle.HomePrimary,
subtitle = if(useDataStoreValueBlocking(USE_SYSTEM_VOICE_INPUT)) { "Built-in voice input is disabled!" } else { null },
navigate = { navController.navigate("voiceInput") },
icon = painterResource(id = R.drawable.mic_fill)
)
NavigationItem(
title = stringResource(R.string.edit_personal_dictionary),
style = NavigationItemStyle.HomeSecondary,
icon = painterResource(id = R.drawable.book),
navigate = {
val intent = Intent("android.settings.USER_DICTIONARY_SETTINGS")
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(intent)
}
)
NavigationItem(
title = "Theme",
style = NavigationItemStyle.HomeTertiary,
navigate = { navController.navigate("themes") },
icon = painterResource(id = R.drawable.eye)
icon = painterResource(id = R.drawable.themes)
)
if(!isPaid) {
@ -103,7 +114,7 @@ fun HomeScreen(navController: NavHostController = rememberNavController()) {
NavigationItem(
title = "Help & Feedback",
style = NavigationItemStyle.HomePrimary,
style = NavigationItemStyle.HomeSecondary,
navigate = { navController.navigate("help") },
icon = painterResource(id = R.drawable.help_circle)
)
@ -111,7 +122,7 @@ fun HomeScreen(navController: NavHostController = rememberNavController()) {
if(isDeveloper || LocalInspectionMode.current) {
NavigationItem(
title = "Developer Settings",
style = NavigationItemStyle.HomeSecondary,
style = NavigationItemStyle.HomeTertiary,
navigate = { navController.navigate("developer") },
icon = painterResource(id = R.drawable.code)
)

View File

@ -1,46 +1,436 @@
package org.futo.inputmethod.latin.uix.settings.pages
import android.content.Context
import android.preference.PreferenceManager
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.booleanResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.core.content.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.futo.inputmethod.latin.R
import org.futo.inputmethod.latin.settings.LongPressKey
import org.futo.inputmethod.latin.settings.LongPressKeyLayoutSetting
import org.futo.inputmethod.latin.settings.Settings
import org.futo.inputmethod.latin.settings.Settings.PREF_VIBRATION_DURATION_SETTINGS
import org.futo.inputmethod.latin.settings.description
import org.futo.inputmethod.latin.settings.name
import org.futo.inputmethod.latin.settings.toEncodedString
import org.futo.inputmethod.latin.settings.toLongPressKeyLayoutItems
import org.futo.inputmethod.latin.uix.AndroidTextInput
import org.futo.inputmethod.latin.uix.KeyHintsSetting
import org.futo.inputmethod.latin.uix.KeyboardBottomOffsetSetting
import org.futo.inputmethod.latin.uix.KeyboardHeightMultiplierSetting
import org.futo.inputmethod.latin.uix.SHOW_EMOJI_SUGGESTIONS
import org.futo.inputmethod.latin.uix.SettingsKey
import org.futo.inputmethod.latin.uix.actions.AllActions
import org.futo.inputmethod.latin.uix.actions.ClipboardHistoryAction
import org.futo.inputmethod.latin.uix.actions.ClipboardHistoryEnabled
import org.futo.inputmethod.latin.uix.actions.CopyAction
import org.futo.inputmethod.latin.uix.actions.CutAction
import org.futo.inputmethod.latin.uix.actions.EmojiAction
import org.futo.inputmethod.latin.uix.actions.PasteAction
import org.futo.inputmethod.latin.uix.actions.RedoAction
import org.futo.inputmethod.latin.uix.actions.SelectAllAction
import org.futo.inputmethod.latin.uix.actions.SwitchLanguageAction
import org.futo.inputmethod.latin.uix.actions.TextEditAction
import org.futo.inputmethod.latin.uix.actions.UndoAction
import org.futo.inputmethod.latin.uix.actions.VoiceInputAction
import org.futo.inputmethod.latin.uix.getSettingBlocking
import org.futo.inputmethod.latin.uix.settings.DataStoreItem
import org.futo.inputmethod.latin.uix.settings.DropDownPicker
import org.futo.inputmethod.latin.uix.settings.NavigationItem
import org.futo.inputmethod.latin.uix.settings.NavigationItemStyle
import org.futo.inputmethod.latin.uix.settings.ScreenTitle
import org.futo.inputmethod.latin.uix.settings.ScrollableList
import org.futo.inputmethod.latin.uix.settings.SettingItem
import org.futo.inputmethod.latin.uix.settings.SettingListLazy
import org.futo.inputmethod.latin.uix.settings.SettingSlider
import org.futo.inputmethod.latin.uix.settings.SettingSliderSharedPrefsInt
import org.futo.inputmethod.latin.uix.settings.SettingToggleDataStore
import org.futo.inputmethod.latin.uix.settings.SettingToggleSharedPrefs
import org.futo.inputmethod.latin.uix.settings.useDataStore
import org.futo.inputmethod.latin.uix.settings.useSharedPrefsBool
import org.futo.inputmethod.latin.uix.settings.useSharedPrefsInt
import kotlin.math.absoluteValue
import kotlin.math.roundToInt
import kotlin.math.sign
val vibrationDurationSetting = SettingsKey(
intPreferencesKey("vibration_duration"),
-1
)
@Preview
fun NavGraphBuilder.addTypingNavigation(
navController: NavHostController
) {
composable("typing") { TypingScreen(navController) }
composable("resize") { ResizeScreen(navController) }
composable("longPress") { LongPressScreen(navController) }
composable("actionKey") { ActionKeyScreen(navController) }
}
@Preview(showBackground = true)
@Composable
fun ResizeScreen(navController: NavHostController = rememberNavController()) {
val context = LocalContext.current
Box {
ScrollableList {
ScreenTitle("Resize Keyboard", showBack = true, navController)
SettingSlider(
title = "Keyboard Height",
setting = KeyboardHeightMultiplierSetting,
range = 0.33f..1.75f, transform = { it },
indicator = { "${(it * 100.0f).roundToInt()}%" },
steps = 16
)
SettingSlider(
title = "Keyboard Offset",
setting = KeyboardBottomOffsetSetting,
range = 0.0f..50.0f,
hardRange = 0.0f..250.0f,
transform = { it },
indicator = { "${String.format("%.1f", it)} dp" },
steps = 9
)
AndroidTextInput(allowPredictions = false)
}
/*
AndroidView(factory = {
val ctx = ContextThemeWrapper(context, R.style.KeyboardTheme_LXX_Light)
val inputView = LayoutInflater.from(ctx).inflate(
R.layout.input_view, null, false
) as InputView
val settingsValues = Settings.getInstance().current
val builder = KeyboardLayoutSet.Builder(ctx, null)
val res: Resources = ctx.resources
val currentSubtype = RichInputMethodSubtype(Subtypes.getActiveSubtype(ctx))
val keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res)
val keyboardHeight = ResourceUtils.getKeyboardHeight(res, settingsValues)
builder.setKeyboardGeometry(keyboardWidth, keyboardHeight)
builder.setSubtype(currentSubtype)
builder.setVoiceInputKeyEnabled(settingsValues.mShowsVoiceInputKey)
builder.setBottomActionKey(settingsValues.mShowsActionKey, settingsValues.mActionKeyId)
builder.setSplitLayoutEnabledByUser(
ProductionFlags.IS_SPLIT_KEYBOARD_SUPPORTED
&& settingsValues.mIsSplitKeyboardEnabled
)
builder.setNumberRow(settingsValues.mIsNumberRowEnabled)
val keyboardLayoutSet = builder.build()
val keyboardView = inputView.findViewById<View>(R.id.keyboard_view) as MainKeyboardView
keyboardView.setKeyboard(keyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET))
/*
mMainKeyboardFrame = mCurrentInputView!!.findViewById<View>(R.id.main_keyboard_frame)
mKeyboardView =
mCurrentInputView!!.findViewById<View>(R.id.keyboard_view) as MainKeyboardView
*/
inputView
}, modifier = Modifier.align(Alignment.BottomCenter))
*/
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun LazyItemScope.DraggableSettingItem(idx: Int, item: LongPressKey, moveItem: (LongPressKey, Int) -> Unit, disable: (LongPressKey) -> Unit, dragIcon: @Composable () -> Unit) {
val context = LocalContext.current
val dragging = remember { mutableStateOf(false) }
val offset = remember { mutableFloatStateOf(0.0f) }
val height = remember { mutableIntStateOf(1) }
SettingItem(
title = "${idx+1}. " + item.name(context),
subtitle = item.description(context),
icon = dragIcon,
modifier = Modifier
.onSizeChanged { size -> height.intValue = size.height }
.pointerInput(Unit) {
detectDragGestures(
onDragStart = {
dragging.value = true
offset.floatValue = 0.0f
},
onDragEnd = {
dragging.value = false
offset.floatValue = 0.0f
},
onDragCancel = {
dragging.value = false
offset.floatValue = 0.0f
},
onDrag = { change, dragAmount ->
offset.floatValue += dragAmount.y
if (offset.floatValue.absoluteValue > height.intValue) {
val direction = offset.floatValue.sign.toInt()
moveItem(
item,
direction
)
offset.floatValue -= height.intValue * direction
}
}
)
}
.let { modifier ->
if (!dragging.value) {
modifier.animateItemPlacement()
} else {
modifier
.zIndex(10.0f)
.graphicsLayer {
clip = false
translationX = 0.0f
translationY = offset.floatValue
}
}
}
) {
IconButton(onClick = { disable(item) }) {
Icon(Icons.Default.Clear, contentDescription = "Remove")
}
}
}
@OptIn(ExperimentalFoundationApi::class)
fun LazyListScope.longPressKeyLayoutEditor(context: Context, setting: DataStoreItem<String>) {
item {
ScreenTitle(title = "Layout of long-press keys")
}
item {
Button(onClick = {
setting.setValue(LongPressKeyLayoutSetting.default)
}) {
Text("Reset to default")
}
}
val dragIcon: @Composable () -> Unit = {
Icon(Icons.Default.Menu, contentDescription = null, tint = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.75f))
}
val items = setting.value.toLongPressKeyLayoutItems()
val moveItem: (item: LongPressKey, direction: Int) -> Unit = { item, direction ->
val oldItems = context.getSettingBlocking(LongPressKeyLayoutSetting).toLongPressKeyLayoutItems()
val oldIdx = oldItems.indexOf(item)
val insertIdx = (oldIdx + direction).coerceAtLeast(0).coerceAtMost(oldItems.size - 1)
val newItems = oldItems.filter { it != item }.toMutableList().apply {
add(insertIdx, item)
}.toEncodedString()
setting.setValue(newItems)
}
val disable: (item : LongPressKey) -> Unit = { item ->
val oldItems = context.getSettingBlocking(LongPressKeyLayoutSetting).toLongPressKeyLayoutItems()
val newItems = oldItems.filter { it != item }.toEncodedString()
setting.setValue(newItems)
}
val enable: (item : LongPressKey) -> Unit = { item ->
val oldItems = context.getSettingBlocking(LongPressKeyLayoutSetting).toLongPressKeyLayoutItems()
val newItems = oldItems.filter { it != item }.toMutableList().apply {
add(item)
}.toEncodedString()
setting.setValue(newItems)
}
item {
Text("Active", modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center)
}
itemsIndexed(items, key = { i, v -> v.ordinal }) { i, v ->
DraggableSettingItem(idx = i, item = v, moveItem = moveItem, disable = disable, dragIcon = dragIcon)
}
item {
Text("Inactive", modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center)
}
items(LongPressKey.entries.filter { !items.contains(it) }, key = { it.ordinal }) {
SettingItem(
title = it.name(context),
subtitle = it.description(context),
modifier = Modifier.animateItemPlacement()
) {
IconButton(onClick = { enable(it) }) {
Icon(Icons.Default.Add, contentDescription = "Add")
}
}
}
}
@Preview(showBackground = true)
@Composable
fun LongPressScreen(navController: NavHostController = rememberNavController()) {
val context = LocalContext.current
val setting = useDataStore(LongPressKeyLayoutSetting, blocking = true)
SettingListLazy {
item {
ScreenTitle("Long-Press Keys", showBack = true, navController)
}
item {
SettingToggleDataStore(
title = "Show hints",
subtitle = "Display a small hint on each key, showing the primary long-press key",
setting = KeyHintsSetting
)
}
/*
SettingToggleDataStore(
title = "Spacebar language switcher",
subtitle = "Show the language switching menu when long-pressing the space",
setting = KeyHintsSetting
)*/
longPressKeyLayoutEditor(
context = context,
setting = setting,
)
item {
SettingSliderSharedPrefsInt(
title = "Long Press Duration",
subtitle = "How long a key needs to be pressed to be considered a long-press",
key = Settings.PREF_KEY_LONGPRESS_TIMEOUT,
default = 300,
range = 100.0f..700.0f,
hardRange = 25.0f..1200.0f,
transform = { it.roundToInt() },
indicator = { "$it ms" },
steps = 23
)
}
/*
SettingRadio(
title = "Spacebar behavior",
options = listOf(0, 1, 2),
optionNames = listOf(
"Swiping moves cursor, long-pressing switches language",
"Swiping changes language, long-pressing moves cursor",
"Swiping and long-pressing only moves cursor"
),
setting =
)*/
}
}
@Preview(showBackground = true)
@Composable
fun ActionKeyScreen(navController: NavHostController = rememberNavController()) {
val context = LocalContext.current
val isActionKeyActive = useSharedPrefsBool(key = Settings.PREF_SHOW_ACTION_KEY, default = true)
val emojiKey = useSharedPrefsInt(key = Settings.PREF_ACTION_KEY_ID, default = 0)
val permittedOptions = listOf(
EmojiAction,
SwitchLanguageAction,
VoiceInputAction,
ClipboardHistoryAction,
TextEditAction,
UndoAction,
RedoAction,
CutAction,
CopyAction,
PasteAction,
SelectAllAction,
)
ScrollableList {
ScreenTitle("Action Key", showBack = true, navController)
SettingToggleSharedPrefs(
title = "Action key enabled",
subtitle = "Show the action key on the bottom row",
key = Settings.PREF_SHOW_ACTION_KEY,
default = true
)
if(isActionKeyActive.value) {
SettingItem(title = "Action key") {
DropDownPicker(
label = "",
options = permittedOptions,
selection = AllActions[emojiKey.value],
onSet = {
emojiKey.setValue(AllActions.indexOf(it))
},
getDisplayName = {
context.getString(it.name)
},
modifier = Modifier.width(180.dp)
)
}
}
}
}
@Preview(showBackground = true)
@Composable
fun TypingScreen(navController: NavHostController = rememberNavController()) {
val context = LocalContext.current
@ -56,71 +446,103 @@ fun TypingScreen(navController: NavHostController = rememberNavController()) {
}
ScrollableList {
ScreenTitle("Typing Preferences", showBack = true, navController)
ScreenTitle("Keyboard", showBack = true, navController)
NavigationItem(
title = "Resize Keyboard",
subtitle = "Change the height and offset of the keyboard",
style = NavigationItemStyle.Misc,
navigate = { navController.navigate("resize") },
icon = painterResource(id = R.drawable.maximize)
)
SettingToggleSharedPrefs(
title = "Show Number Row",
subtitle = "When active, the number row is shown on top of the keyboard at all times",
key = Settings.PREF_ENABLE_NUMBER_ROW,
default = false,
icon = { Text("123", style = TextBodyRegularMlStyle, color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.75f)) }
)
NavigationItem(
title = "Long-Press Letters & Symbols",
subtitle = "Configure long-press duration and how to order letters/symbols.",
style = NavigationItemStyle.Misc,
navigate = { navController.navigate("longPress") },
icon = painterResource(id = R.drawable.arrow_up)
)
NavigationItem(
title = "Action Key",
subtitle = "Change the action key into an emoji key, language switch key, or disable it altogether.",
style = NavigationItemStyle.Misc,
navigate = { navController.navigate("actionKey") },
icon = painterResource(id = R.drawable.smile)
)
NavigationItem(
title = "Additional Layouts",
subtitle = "Configure additional layouts in the languages screen",
style = NavigationItemStyle.Misc,
navigate = { navController.navigate("languages") },
icon = painterResource(id = R.drawable.keyboard)
)
ScreenTitle(title = "Typing preferences")
SettingToggleSharedPrefs(
title = "Swipe Typing (alpha)",
subtitle = "Allow swiping from key to key to write words.",
key = Settings.PREF_GESTURE_INPUT,
default = true,
icon = {
Icon(painterResource(id = R.drawable.swipe_icon), contentDescription = null,
tint = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.75f))
}
)
SettingToggleDataStore(
title = "Emoji Suggestions",
subtitle = "Suggest emojis while you're typing",
setting = SHOW_EMOJI_SUGGESTIONS
setting = SHOW_EMOJI_SUGGESTIONS,
icon = {
Icon(painterResource(id = R.drawable.smile), contentDescription = null,
tint = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.75f))
}
)
SettingToggleSharedPrefs(
title = "Swipe typing",
key = Settings.PREF_GESTURE_INPUT,
default = true
title = stringResource(R.string.auto_correction),
subtitle = stringResource(R.string.auto_correction_summary),
key = Settings.PREF_AUTO_CORRECTION,
default = true,
icon = {
Icon(painterResource(id = R.drawable.icon_spellcheck), contentDescription = null,
tint = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.75f))
}
)
SettingToggleSharedPrefs(
title = "Number row",
key = Settings.PREF_ENABLE_NUMBER_ROW,
default = false
)
SettingToggleDataStore(
title = "Clipboard History",
setting = ClipboardHistoryEnabled
)
SettingToggleSharedPrefs(
title = "Action key enabled",
subtitle = "Show the action key on the bottom row",
key = Settings.PREF_SHOW_ACTION_KEY,
default = true
)
val emojiKey = useSharedPrefsInt(key = Settings.PREF_ACTION_KEY_ID, default = 0)
SettingItem(title = "Action key") {
DropDownPicker(
label = "",
options = AllActions,
selection = AllActions[emojiKey.value],
onSet = {
emojiKey.setValue(AllActions.indexOf(it))
},
getDisplayName = {
context.getString(it.name)
},
modifier = Modifier.width(180.dp)
)
}
SettingToggleSharedPrefs(
title = stringResource(R.string.auto_cap),
subtitle = stringResource(R.string.auto_cap_summary),
key = Settings.PREF_AUTO_CAP,
default = true
default = true,
icon = {
Text("Aa", style = TextBodyRegularMlStyle, color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.75f))
}
)
SettingToggleSharedPrefs(
title = stringResource(R.string.use_double_space_period),
subtitle = stringResource(R.string.use_double_space_period_summary),
key = Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD,
default = true
)
SettingToggleSharedPrefs(
title = stringResource(R.string.vibrate_on_keypress),
key = Settings.PREF_VIBRATE_ON,
default = booleanResource(R.bool.config_default_vibration_enabled)
default = true,
icon = {
Text(".", style = TextBodyRegularMlStyle, color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.75f))
}
)
SettingToggleSharedPrefs(
title = stringResource(R.string.sound_on_keypress),
key = Settings.PREF_SOUND_ON,
@ -132,6 +554,12 @@ fun TypingScreen(navController: NavHostController = rememberNavController()) {
default = booleanResource(R.bool.config_default_key_preview_popup)
)
SettingToggleSharedPrefs(
title = stringResource(R.string.vibrate_on_keypress),
key = Settings.PREF_VIBRATE_ON,
default = booleanResource(R.bool.config_default_vibration_enabled)
)
SettingSlider(
title = "Vibration",
setting = vibrationDurationSetting,
@ -147,15 +575,10 @@ fun TypingScreen(navController: NavHostController = rememberNavController()) {
}
)
SettingSliderSharedPrefsInt(
title = "Long Press Duration",
key = Settings.PREF_KEY_LONGPRESS_TIMEOUT,
default = 300,
range = 100.0f .. 700.0f,
hardRange = 25.0f .. 1200.0f,
transform = { it.roundToInt() },
indicator = { "$it ms" },
steps = 23
SettingToggleDataStore(
title = "Clipboard History",
setting = ClipboardHistoryEnabled
)
}
}
}

View File

@ -282,39 +282,6 @@ fun ThemePicker(onSelected: (ThemeOption) -> Unit) {
)
}
}
item(span = { GridItemSpan(maxCurrentLineSpan) }) {
CompositionLocalProvider(LocalLayoutDirection provides originalDirection) {
SettingToggleDataStore(
title = "Show symbol hints",
setting = KeyHintsSetting
)
}
}
item(span = { GridItemSpan(maxCurrentLineSpan) }) {
CompositionLocalProvider(LocalLayoutDirection provides originalDirection) {
SettingSlider(
title = "Keyboard Height",
setting = KeyboardHeightMultiplierSetting,
range = 0.33f..1.75f, transform = { it },
indicator = { "${(it * 100.0f).roundToInt()}%" },
steps = 16
)
}
}
item(span = { GridItemSpan(maxCurrentLineSpan) }) {
CompositionLocalProvider(LocalLayoutDirection provides originalDirection) {
SettingSlider(
title = "Keyboard Offset",
setting = KeyboardBottomOffsetSetting,
range = 0.0f..50.0f,
hardRange = 0.0f..250.0f,
transform = { it },
indicator = { "${String.format("%.1f", it)} dp" },
steps = 9
)
}
}
}
}
}

View File

@ -136,6 +136,7 @@ val dismissedMigrateUpdateNotice = SettingsKey(
@Composable
@Preview
fun ConditionalMigrateUpdateNotice() {
if(LocalInspectionMode.current) { return }
val context = LocalContext.current
val value = useDataStore(dismissedMigrateUpdateNotice, blocking = true)
if(!value.value) {

View File

@ -1,28 +1,84 @@
{
"locale": "DEFAULT",
"morekeys": {
"a": [],
"q": [],
"w": [],
"e": [],
"i": [],
"o": [],
"u": [],
"s": [],
"n": [],
"c": [],
"y": [],
"d": [],
"r": [],
"t": [],
"z": [],
"k": [],
"l": [],
"y": [],
"u": [],
"i": [],
"o": [],
"p": [],
"a": [],
"s": [],
"d": [],
"f": [],
"g": [],
"v": [],
"h": [],
"j": [],
"w": [],
"q": [],
"k": [],
"l": [],
"z": [],
"x": [],
"c": [],
"v": [],
"b": [],
"n": [],
"m": [],
"misc_a": [
"à",
"á",
"â",
"ä",
"æ",
"ã",
"å",
"ā"
],
"misc_e": [
"é",
"è",
"ê",
"ë",
"ē"
],
"misc_i": [
"í",
"î",
"ï",
"ī",
"ì"
],
"misc_o": [
"ó",
"ô",
"ö",
"ò",
"œ",
"ø",
"ō",
"õ"
],
"misc_u": [
"ú",
"û",
"ü",
"ù",
"ū"
],
"misc_s": [
"ß"
],
"misc_n": [
"ñ"
],
"misc_c": [
"ç"
],
"nordic_row2_10": [],
"nordic_row2_11": [],
"east_slavic_row2_2": [],
@ -55,6 +111,7 @@
"₱"
],
"punctuation": [
"/",
"?",
"!",
"\u203d",
@ -65,6 +122,7 @@
"="
],
"tablet_punctuation": [
"/",
"?",
"!",
"\u203d",
@ -314,6 +372,119 @@
"double_lqm_rqm": "„,“,”",
"double_9qm_lqm": "”,„,“",
"double_9qm_rqm": "“,„,”",
"double_rqm_9qm": "“,”,„"
"double_rqm_9qm": "“,”,„",
"number_1": "1",
"number_2": "2",
"number_3": "3",
"number_4": "4",
"number_5": "5",
"number_6": "6",
"number_7": "7",
"number_8": "8",
"number_9": "9",
"number_0": "0"
},
"actions": {
"1": [],
"2": [],
"3": [],
"4": [],
"5": [],
"6": [],
"7": [],
"8": [],
"9": [],
"0": [],
"q": [],
"w": [],
"e": [],
"r": [],
"t": [],
"y": [
"!icon/action_redo|!code/action_redo"
],
"u": [],
"i": [],
"o": [],
"p": [],
"a": [
"!icon/action_select_all|!code/action_select_all"
],
"s": [],
"d": [],
"f": [],
"g": [],
"h": [],
"j": [],
"k": [],
"l": [],
"z": [
"!icon/action_undo|!code/action_undo"
],
"x": [
"!icon/action_cut|!code/action_cut"
],
"c": [
"!icon/action_copy|!code/action_copy"
],
"v": [
"!icon/action_paste|!code/action_paste"
],
"b": [],
"n": [],
"m": []
},
"qwertysyms": {
"1": "!",
"2": "@",
"3": "#",
"4": "$",
"5": "%",
"6": "^",
"7": "&",
"8": "*",
"9": "(",
"0": ")",
"q": "\\%",
"w": "\\\\",
"e": "|",
"r": "=",
"t": "[",
"y": "]",
"u": "<",
"i": ">",
"o": "{",
"p": "}",
"a": "@",
"s": "#",
"d": [
"$",
"€",
"£",
"¥",
"¢"
],
"f": "\\%",
"g": ["&", "|"],
"h": ["-", "", "—", "_"],
"j": ["+", "="],
"k": ["(", "[", "{", "<"],
"l": [")", "]", "}", ">" ],
"z": "*",
"x": "\"",
"c": "'",
"v": ":",
"b": ";",
"n": "!",
"m": ["?", "/"]
}
}

View File

@ -1,57 +1,6 @@
{
"locale": "en",
"morekeys": {
"a": [
"à",
"á",
"â",
"ä",
"æ",
"ã",
"å",
"ā"
],
"e": [
"é",
"è",
"ê",
"ë",
"ē"
],
"i": [
"í",
"î",
"ï",
"ī",
"ì"
],
"o": [
"ó",
"ô",
"ö",
"ò",
"œ",
"ø",
"ō",
"õ"
],
"u": [
"ú",
"û",
"ü",
"ù",
"ū"
],
"s": [
"ß"
],
"n": [
"ñ"
],
"c": [
"ç"
]
},
"morekeys": {},
"keyspec": {},
"label": {},
"keylabel": {},

View File

@ -16,10 +16,13 @@ def transform_to_texts(locale_data):
texts["locale"] = locale_data["locale"]
prefixes = ['morekeys_', 'keyspec_', 'label_', 'keylabel_', 'keyhintlabel_', 'additional_morekeys_']
prefixes = ['morekeys_', 'keyspec_', 'label_', 'keylabel_', 'keyhintlabel_', 'additional_morekeys_', 'qwertysyms_', 'actions_']
for prefix in prefixes:
for k, v in locale_data[prefix.rstrip("_")].items():
key = prefix.rstrip("_")
if key not in locale_data: continue
for k, v in locale_data[key].items():
if isinstance(v, list):
texts[prefix + k] = ",".join(v)
else: