Compare commits

...

9 Commits

Author SHA1 Message Date
Jules Aguillon
ab23a73357 Merge branch 'MAIN' into selection-mode
Some checks failed
Make Apk CI / Build-Apk (push) Has been cancelled
Check translations / check-translations (push) Has been cancelled
2025-03-15 16:09:10 +01:00
Jules Aguillon
9cfeb0f0c2 Add a delay after a Keyevent key in a macro (#918)
Some checks failed
Make Apk CI / Build-Apk (push) Has been cancelled
Check translations / check-translations (push) Has been cancelled
Check layouts / check_layout.output (push) Has been cancelled
Check layouts / Generated files (push) Has been cancelled
* Construct a single handler

* Add a delay after a Keyevent key in a macro

Add a delay before sending the next key to avoid race conditions causing
keys to be handled in the wrong order. Notably, KeyEvent keys handling
is scheduled differently than the other edit functions.
2025-03-10 23:41:51 +01:00
Spike
5e77fa84cf doc: Massage section on "Escape codes" (#912)
Some checks failed
Make Apk CI / Build-Apk (push) Has been cancelled
Check translations / check-translations (push) Has been cancelled
Check layouts / check_layout.output (push) Has been cancelled
Check layouts / Generated files (push) Has been cancelled
* Add introductory text to the tables

And mention characters that don't have escapes

* Turn tables around

Tables should be structured by what the user wants, not by what the code does.

* Address Julow review #1

- Merge tables, no matter which rule requires escaping; "in the usual way for XML" applies to both
- 3 escapes not mandatory removed from table to new ¶ below
- Found one more symbol → legend

* doc: Clarify escaping of comma and colon per #915
2025-03-08 12:11:54 +01:00
Jules Aguillon
06fbc83c9c sync_translations.py: Handle '\n' in store descriptions
These are added by Weblate.
2025-03-08 12:08:57 +01:00
Jules Aguillon
a123810247 Change indentation of strings.xml files to match Weblate
This runs sync_translations.py, which also remove uneeded comments.
2025-03-08 12:05:35 +01:00
Jules Aguillon
906755b787 prefs: Add 'delete_word' and 'forward_delete_word' to extra keys
Some checks failed
Make Apk CI / Build-Apk (push) Has been cancelled
Check translations / check-translations (push) Has been cancelled
Check layouts / check_layout.output (push) Has been cancelled
Check layouts / Generated files (push) Has been cancelled
The gesture combination is mentioned. Preferred position are added.
2025-03-01 16:52:46 +01:00
Jules Aguillon
80c52460c7 Add 'delete_word' and 'forward_delete_word' keys
These keys are the equivalent of ctrl+backspace and ctrl+delete,
respectively.

They can be reached with Gesture+backspace and Gesture+delete
respectively.
2025-03-01 16:52:46 +01:00
Jules Aguillon
13988ba2fe Selection mode: Move each ends of selection separately with slider
When the selection mode is activated, the space bar sliders change how
they affect the selection:

- The left side of the slider moves the left position of the selection.
  To shrink the selection from the left side, the slider must be
  activated by sliding to the left, extending the selection
  temporarilly, then by sliding to the right.

- The right side of the slider affects the right position if the
  selection.
2025-03-01 16:34:20 +01:00
Jules Aguillon
68c4ba96b7 Selection mode: Space to cancel the selection
This adds the "selection mode", which is activated when text is selected
in the text box. The selection mode is exited when the selection is
cleared.

While the selection mode is activated, the Space and Esc keys are
modified into the "selection cancel" key, which remove the selection
without changing the text.
The space bar is otherwise easy to type by accident during a selection
and causes the selected text to be deleted.
2025-03-01 15:07:03 +01:00
31 changed files with 2645 additions and 2352 deletions

Binary file not shown.

View File

@@ -25,31 +25,30 @@ Key values can be any of the following:
+ `⏯:keyevent:85` A play/pause key (which has no effect in most apps). + `⏯:keyevent:85` A play/pause key (which has no effect in most apps).
+ `my@:'my.email@domain.com'` A key that sends an arbitrary string + `my@:'my.email@domain.com'` A key that sends an arbitrary string
- A macro, `symbol:key_def1,key_def2,...`. - A macro, `legend:key_def1,key_def2,...`.
This results in a key that behaves as if the sequence of `key_def` had been pressed in order. This results in a key with legend `legend` that behaves as if the sequence of `key_def` had been pressed in order.
Examples: Examples:
+ `CA:ctrl,a,ctrl,c` A key with legend CA that sends the sequence `ctrl+a`, `ctrl+c`. + `CA:ctrl,a,ctrl,c` A key with legend CA that sends the sequence `ctrl+a`, `ctrl+c`.
+ `Cd:ctrl,backspace` A key with legend Cd that sends the shortcut `ctrl+backspace`. + `Cd:ctrl,backspace` A key with legend Cd that sends the shortcut `ctrl+backspace`.
## Escape codes ### Escape codes
Value | Escape code for
When defining a key value, several characters have special effects. If you want a character not to have its usual effect but to be taken literally, you should "escape" it in the usual way for XML:
To get this character... | ...you can type
:---- | :------ :---- | :------
`\?` | `?` A literal newline character, which is different from `enter` and `action` in certain apps. | `\n`
`\#` | `#` A literal tab character, which is different from `tab` in certain apps. | `\t`
`\@` | `@` `\` | `\\`
`\n` | Literal newline character. This is different from `enter` and `action` in certain apps. `&` | `&`
`\t` | Literal tab character. This is different from `tab` in certain apps. `<` | `&lt;`
`\\` | `\` `>` | `&gt;`
`"` | `&quot;`
XML escape codes also work, including: The characters `?`, `#`, and `@` do not need to be escaped when writing custom layouts. Internally, they can be escaped by prepending backslash (by typing `\?`, `\#`, and `\@`).
Value | Escape code for The characters `,` and `:` can be escaped in a key value, using single quotes. For example, this macro defines a key with legend `http` that sends a string containing `:`: `<key c="http:home,'https://'" />` For simplicity, `,` and `:` cannot be escaped in the key legend.
:------- | :------
`&amp;` | `&`
`&lt;` | `<`
`&gt;` | `>`
`&quot;` | `"`
## Modifiers ## Modifiers
System modifiers are sent to the app, which can take app-specific action. System modifiers are sent to the app, which can take app-specific action.
@@ -88,11 +87,13 @@ These keys are sent to apps, which are free to ignore them. The keyboard does no
## Keyboard editing actions ## Keyboard editing actions
In contrast, these keys perform editing on the text without sending anything to the app. In contrast, these keys perform editing on the text without sending anything to the app.
Value | Meaning Value | Meaning
:----------------- | :------ :-------------------- | :------
`cursor_left` | Moves the cursor to the left with the slider gesture. `cursor_left` | Moves the cursor to the left with the slider gesture.
`cursor_right` | Moves the cursor to the right with the slider gesture. `cursor_right` | Moves the cursor to the right with the slider gesture.
`cursor_up` | Moves the cursor up with the slider gesture. Warning: this might make the cursor leave the text box. `cursor_up` | Moves the cursor up with the slider gesture. Warning: this might make the cursor leave the text box.
`cursor_down` | Moves the cursor down with the slider gesture. Warning: this might make the cursor leave the text box. `cursor_down` | Moves the cursor down with the slider gesture. Warning: this might make the cursor leave the text box.
`delete_word` | Delete the word to the left of the cursor.
`forward_delete_word` | Delete the word to the right of the cursor.
## Whitespace ## Whitespace
Value | Meaning Value | Meaning

View File

@@ -127,6 +127,9 @@ Tato aplikace neobsahuje žádné reklamy, nevyužívá připojení k síti a je
<!-- <string name="key_descr_zwnj">Zero width non-joiner</string> --> <!-- <string name="key_descr_zwnj">Zero width non-joiner</string> -->
<!-- <string name="key_descr_nbsp">Non-breaking space</string> --> <!-- <string name="key_descr_nbsp">Non-breaking space</string> -->
<!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> --> <!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> -->
<!-- <string name="key_descr_delete_word">Delete a word</string> -->
<!-- <string name="key_descr_forward_delete_word">Delete a word on the right</string> -->
<!-- <string name="key_descr_gesture">Gesture</string> -->
<string name="clipboard_history_heading">Nedávno kopírovaný text</string> <string name="clipboard_history_heading">Nedávno kopírovaný text</string>
<string name="clipboard_pin_heading">Připnout</string> <string name="clipboard_pin_heading">Připnout</string>
<string name="clipboard_remove_confirm">Odebrat ze schránky?</string> <string name="clipboard_remove_confirm">Odebrat ze schránky?</string>

View File

@@ -127,6 +127,9 @@ Diese App enthält keine Werbung, benötigt keinen Netzwerkzugriff und ist quell
<!-- <string name="key_descr_zwnj">Zero width non-joiner</string> --> <!-- <string name="key_descr_zwnj">Zero width non-joiner</string> -->
<!-- <string name="key_descr_nbsp">Non-breaking space</string> --> <!-- <string name="key_descr_nbsp">Non-breaking space</string> -->
<!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> --> <!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> -->
<!-- <string name="key_descr_delete_word">Delete a word</string> -->
<!-- <string name="key_descr_forward_delete_word">Delete a word on the right</string> -->
<!-- <string name="key_descr_gesture">Gesture</string> -->
<string name="clipboard_history_heading">Zuletzt kopierter Text</string> <string name="clipboard_history_heading">Zuletzt kopierter Text</string>
<string name="clipboard_pin_heading">Angeheftet</string> <string name="clipboard_pin_heading">Angeheftet</string>
<string name="clipboard_remove_confirm">Aus der Zwischenablage entfernen?</string> <string name="clipboard_remove_confirm">Aus der Zwischenablage entfernen?</string>

View File

@@ -127,6 +127,9 @@ La misma no contiene ningún anuncio/publicidad, no realiza peticiones de red y
<!-- <string name="key_descr_zwnj">Zero width non-joiner</string> --> <!-- <string name="key_descr_zwnj">Zero width non-joiner</string> -->
<!-- <string name="key_descr_nbsp">Non-breaking space</string> --> <!-- <string name="key_descr_nbsp">Non-breaking space</string> -->
<!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> --> <!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> -->
<!-- <string name="key_descr_delete_word">Delete a word</string> -->
<!-- <string name="key_descr_forward_delete_word">Delete a word on the right</string> -->
<!-- <string name="key_descr_gesture">Gesture</string> -->
<string name="clipboard_history_heading">Textos recién copiados</string> <string name="clipboard_history_heading">Textos recién copiados</string>
<string name="clipboard_pin_heading">Pegado</string> <string name="clipboard_pin_heading">Pegado</string>
<string name="clipboard_remove_confirm">¿Sacar este portapapeles?</string> <string name="clipboard_remove_confirm">¿Sacar este portapapeles?</string>

View File

@@ -127,6 +127,9 @@ This application contains no ads, doesn't make any network requests and is Open
<!-- <string name="key_descr_zwnj">Zero width non-joiner</string> --> <!-- <string name="key_descr_zwnj">Zero width non-joiner</string> -->
<!-- <string name="key_descr_nbsp">Non-breaking space</string> --> <!-- <string name="key_descr_nbsp">Non-breaking space</string> -->
<!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> --> <!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> -->
<!-- <string name="key_descr_delete_word">Delete a word</string> -->
<!-- <string name="key_descr_forward_delete_word">Delete a word on the right</string> -->
<!-- <string name="key_descr_gesture">Gesture</string> -->
<!-- <string name="clipboard_history_heading">Recently copied text</string> --> <!-- <string name="clipboard_history_heading">Recently copied text</string> -->
<!-- <string name="clipboard_pin_heading">Pinned</string> --> <!-- <string name="clipboard_pin_heading">Pinned</string> -->
<!-- <string name="clipboard_remove_confirm">Remove this clipboard?</string> --> <!-- <string name="clipboard_remove_confirm">Remove this clipboard?</string> -->

View File

@@ -127,6 +127,9 @@ Cette application ne contient pas de publicité, n'accède pas au réseau et est
<!-- <string name="key_descr_zwnj">Zero width non-joiner</string> --> <!-- <string name="key_descr_zwnj">Zero width non-joiner</string> -->
<!-- <string name="key_descr_nbsp">Non-breaking space</string> --> <!-- <string name="key_descr_nbsp">Non-breaking space</string> -->
<!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> --> <!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> -->
<!-- <string name="key_descr_delete_word">Delete a word</string> -->
<!-- <string name="key_descr_forward_delete_word">Delete a word on the right</string> -->
<!-- <string name="key_descr_gesture">Gesture</string> -->
<string name="clipboard_history_heading">Texte récemment copié</string> <string name="clipboard_history_heading">Texte récemment copié</string>
<string name="clipboard_pin_heading">Épinglé</string> <string name="clipboard_pin_heading">Épinglé</string>
<string name="clipboard_remove_confirm">Supprimer ce presse-papiers ?</string> <string name="clipboard_remove_confirm">Supprimer ce presse-papiers ?</string>

View File

@@ -127,6 +127,9 @@ This application contains no ads, doesn't make any network requests and is Open
<!-- <string name="key_descr_zwnj">Zero width non-joiner</string> --> <!-- <string name="key_descr_zwnj">Zero width non-joiner</string> -->
<!-- <string name="key_descr_nbsp">Non-breaking space</string> --> <!-- <string name="key_descr_nbsp">Non-breaking space</string> -->
<!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> --> <!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> -->
<!-- <string name="key_descr_delete_word">Delete a word</string> -->
<!-- <string name="key_descr_forward_delete_word">Delete a word on the right</string> -->
<!-- <string name="key_descr_gesture">Gesture</string> -->
<!-- <string name="clipboard_history_heading">Recently copied text</string> --> <!-- <string name="clipboard_history_heading">Recently copied text</string> -->
<!-- <string name="clipboard_pin_heading">Pinned</string> --> <!-- <string name="clipboard_pin_heading">Pinned</string> -->
<!-- <string name="clipboard_remove_confirm">Remove this clipboard?</string> --> <!-- <string name="clipboard_remove_confirm">Remove this clipboard?</string> -->

View File

@@ -128,6 +128,9 @@ PCキーボードでの半角入力を再現しています。日本語入力、
<!-- <string name="key_descr_zwnj">Zero width non-joiner</string> --> <!-- <string name="key_descr_zwnj">Zero width non-joiner</string> -->
<!-- <string name="key_descr_nbsp">Non-breaking space</string> --> <!-- <string name="key_descr_nbsp">Non-breaking space</string> -->
<!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> --> <!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> -->
<!-- <string name="key_descr_delete_word">Delete a word</string> -->
<!-- <string name="key_descr_forward_delete_word">Delete a word on the right</string> -->
<!-- <string name="key_descr_gesture">Gesture</string> -->
<string name="clipboard_history_heading">最近コピーしたテキスト</string> <string name="clipboard_history_heading">最近コピーしたテキスト</string>
<string name="clipboard_pin_heading">お気に入り</string> <string name="clipboard_pin_heading">お気に入り</string>
<string name="clipboard_remove_confirm">クリップボードから削除しますか?</string> <string name="clipboard_remove_confirm">クリップボードから削除しますか?</string>

View File

@@ -127,6 +127,9 @@
<!-- <string name="key_descr_zwnj">Zero width non-joiner</string> --> <!-- <string name="key_descr_zwnj">Zero width non-joiner</string> -->
<!-- <string name="key_descr_nbsp">Non-breaking space</string> --> <!-- <string name="key_descr_nbsp">Non-breaking space</string> -->
<!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> --> <!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> -->
<!-- <string name="key_descr_delete_word">Delete a word</string> -->
<!-- <string name="key_descr_forward_delete_word">Delete a word on the right</string> -->
<!-- <string name="key_descr_gesture">Gesture</string> -->
<string name="clipboard_history_heading">최근에 복사한 텍스트</string> <string name="clipboard_history_heading">최근에 복사한 텍스트</string>
<string name="clipboard_pin_heading">고정</string> <string name="clipboard_pin_heading">고정</string>
<string name="clipboard_remove_confirm">이 클립보드를 제거하시겠습니까?</string> <string name="clipboard_remove_confirm">이 클립보드를 제거하시겠습니까?</string>

View File

@@ -129,6 +129,9 @@ Tagad lieliski piemērota izmantošanai ikdienā.
<!-- <string name="key_descr_zwnj">Zero width non-joiner</string> --> <!-- <string name="key_descr_zwnj">Zero width non-joiner</string> -->
<!-- <string name="key_descr_nbsp">Non-breaking space</string> --> <!-- <string name="key_descr_nbsp">Non-breaking space</string> -->
<!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> --> <!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> -->
<!-- <string name="key_descr_delete_word">Delete a word</string> -->
<!-- <string name="key_descr_forward_delete_word">Delete a word on the right</string> -->
<!-- <string name="key_descr_gesture">Gesture</string> -->
<string name="clipboard_history_heading">Nesen starpliktuvē ievietots teksts</string> <string name="clipboard_history_heading">Nesen starpliktuvē ievietots teksts</string>
<string name="clipboard_pin_heading">Piesprausts</string> <string name="clipboard_pin_heading">Piesprausts</string>
<string name="clipboard_remove_confirm">Noņemt šo starpliktuves vienumu?</string> <string name="clipboard_remove_confirm">Noņemt šo starpliktuves vienumu?</string>

View File

@@ -127,6 +127,9 @@ Aplikacja nie zawiera reklam, nie żąda dostępu do internetu, a jej kod źród
<!-- <string name="key_descr_zwnj">Zero width non-joiner</string> --> <!-- <string name="key_descr_zwnj">Zero width non-joiner</string> -->
<!-- <string name="key_descr_nbsp">Non-breaking space</string> --> <!-- <string name="key_descr_nbsp">Non-breaking space</string> -->
<!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> --> <!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> -->
<!-- <string name="key_descr_delete_word">Delete a word</string> -->
<!-- <string name="key_descr_forward_delete_word">Delete a word on the right</string> -->
<!-- <string name="key_descr_gesture">Gesture</string> -->
<string name="clipboard_history_heading">Ostatnio skopiowane elementy</string> <string name="clipboard_history_heading">Ostatnio skopiowane elementy</string>
<string name="clipboard_pin_heading">Przypięte</string> <string name="clipboard_pin_heading">Przypięte</string>
<string name="clipboard_remove_confirm">Usunąć ten element ze schowka?</string> <string name="clipboard_remove_confirm">Usunąć ten element ze schowka?</string>

View File

@@ -127,6 +127,9 @@ Este aplicativo não contém anúncios, não faz nenhuma solicitação de rede e
<!-- <string name="key_descr_zwnj">Zero width non-joiner</string> --> <!-- <string name="key_descr_zwnj">Zero width non-joiner</string> -->
<!-- <string name="key_descr_nbsp">Non-breaking space</string> --> <!-- <string name="key_descr_nbsp">Non-breaking space</string> -->
<!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> --> <!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> -->
<!-- <string name="key_descr_delete_word">Delete a word</string> -->
<!-- <string name="key_descr_forward_delete_word">Delete a word on the right</string> -->
<!-- <string name="key_descr_gesture">Gesture</string> -->
<string name="clipboard_history_heading">Textos recém copiados</string> <string name="clipboard_history_heading">Textos recém copiados</string>
<string name="clipboard_pin_heading">Fixados</string> <string name="clipboard_pin_heading">Fixados</string>
<string name="clipboard_remove_confirm">Remover esta cópia?</string> <string name="clipboard_remove_confirm">Remover esta cópia?</string>

View File

@@ -127,6 +127,9 @@ Această aplicație nu conține publicitate, nu folosește rețeaua deloc și e
<!-- <string name="key_descr_zwnj">Zero width non-joiner</string> --> <!-- <string name="key_descr_zwnj">Zero width non-joiner</string> -->
<!-- <string name="key_descr_nbsp">Non-breaking space</string> --> <!-- <string name="key_descr_nbsp">Non-breaking space</string> -->
<!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> --> <!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> -->
<!-- <string name="key_descr_delete_word">Delete a word</string> -->
<!-- <string name="key_descr_forward_delete_word">Delete a word on the right</string> -->
<!-- <string name="key_descr_gesture">Gesture</string> -->
<!-- <string name="clipboard_history_heading">Recently copied text</string> --> <!-- <string name="clipboard_history_heading">Recently copied text</string> -->
<!-- <string name="clipboard_pin_heading">Pinned</string> --> <!-- <string name="clipboard_pin_heading">Pinned</string> -->
<!-- <string name="clipboard_remove_confirm">Remove this clipboard?</string> --> <!-- <string name="clipboard_remove_confirm">Remove this clipboard?</string> -->

View File

@@ -127,6 +127,9 @@
<string name="key_descr_zwnj">Разделитель нулевой ширины</string> <string name="key_descr_zwnj">Разделитель нулевой ширины</string>
<string name="key_descr_nbsp">Неразрывный пробел</string> <string name="key_descr_nbsp">Неразрывный пробел</string>
<string name="key_descr_nnbsp">Узкий неразрывный пробел</string> <string name="key_descr_nnbsp">Узкий неразрывный пробел</string>
<!-- <string name="key_descr_delete_word">Delete a word</string> -->
<!-- <string name="key_descr_forward_delete_word">Delete a word on the right</string> -->
<!-- <string name="key_descr_gesture">Gesture</string> -->
<string name="clipboard_history_heading">Недавно скопированный текст</string> <string name="clipboard_history_heading">Недавно скопированный текст</string>
<string name="clipboard_pin_heading">Закреплено</string> <string name="clipboard_pin_heading">Закреплено</string>
<string name="clipboard_remove_confirm">Удалить этот буфер обмена?</string> <string name="clipboard_remove_confirm">Удалить этот буфер обмена?</string>

View File

@@ -127,6 +127,9 @@ Bu uygulama açık kaynaklıdır. Reklam içermez ve internete bağlanmaz."</str
<!-- <string name="key_descr_zwnj">Zero width non-joiner</string> --> <!-- <string name="key_descr_zwnj">Zero width non-joiner</string> -->
<!-- <string name="key_descr_nbsp">Non-breaking space</string> --> <!-- <string name="key_descr_nbsp">Non-breaking space</string> -->
<!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> --> <!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> -->
<!-- <string name="key_descr_delete_word">Delete a word</string> -->
<!-- <string name="key_descr_forward_delete_word">Delete a word on the right</string> -->
<!-- <string name="key_descr_gesture">Gesture</string> -->
<string name="clipboard_history_heading">Son kopyalanan metin</string> <string name="clipboard_history_heading">Son kopyalanan metin</string>
<string name="clipboard_pin_heading">Sabitlendi</string> <string name="clipboard_pin_heading">Sabitlendi</string>
<string name="clipboard_remove_confirm">Bu sabitlemeyi sil</string> <string name="clipboard_remove_confirm">Bu sabitlemeyi sil</string>

View File

@@ -127,6 +127,9 @@
<string name="key_descr_zwnj">Разділювач нульової ширини</string> <string name="key_descr_zwnj">Разділювач нульової ширини</string>
<string name="key_descr_nbsp">Нерозривний пробіл</string> <string name="key_descr_nbsp">Нерозривний пробіл</string>
<string name="key_descr_nnbsp">Вузький нерозривний пробіл</string> <string name="key_descr_nnbsp">Вузький нерозривний пробіл</string>
<!-- <string name="key_descr_delete_word">Delete a word</string> -->
<!-- <string name="key_descr_forward_delete_word">Delete a word on the right</string> -->
<!-- <string name="key_descr_gesture">Gesture</string> -->
<string name="clipboard_history_heading">Нещодавно скопійований текст</string> <string name="clipboard_history_heading">Нещодавно скопійований текст</string>
<string name="clipboard_pin_heading">Закріплено</string> <string name="clipboard_pin_heading">Закріплено</string>
<string name="clipboard_remove_confirm">Видалити цей буфер обміну?</string> <string name="clipboard_remove_confirm">Видалити цей буфер обміну?</string>

View File

@@ -127,6 +127,9 @@ Bây giờ đã hoàn hảo cho việc sử dụng hàng ngày.
<!-- <string name="key_descr_zwnj">Zero width non-joiner</string> --> <!-- <string name="key_descr_zwnj">Zero width non-joiner</string> -->
<!-- <string name="key_descr_nbsp">Non-breaking space</string> --> <!-- <string name="key_descr_nbsp">Non-breaking space</string> -->
<!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> --> <!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> -->
<!-- <string name="key_descr_delete_word">Delete a word</string> -->
<!-- <string name="key_descr_forward_delete_word">Delete a word on the right</string> -->
<!-- <string name="key_descr_gesture">Gesture</string> -->
<!-- <string name="clipboard_history_heading">Recently copied text</string> --> <!-- <string name="clipboard_history_heading">Recently copied text</string> -->
<!-- <string name="clipboard_pin_heading">Pinned</string> --> <!-- <string name="clipboard_pin_heading">Pinned</string> -->
<!-- <string name="clipboard_remove_confirm">Remove this clipboard?</string> --> <!-- <string name="clipboard_remove_confirm">Remove this clipboard?</string> -->

View File

@@ -127,6 +127,9 @@
<!-- <string name="key_descr_zwnj">Zero width non-joiner</string> --> <!-- <string name="key_descr_zwnj">Zero width non-joiner</string> -->
<!-- <string name="key_descr_nbsp">Non-breaking space</string> --> <!-- <string name="key_descr_nbsp">Non-breaking space</string> -->
<!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> --> <!-- <string name="key_descr_nnbsp">Narrow non-breaking space</string> -->
<!-- <string name="key_descr_delete_word">Delete a word</string> -->
<!-- <string name="key_descr_forward_delete_word">Delete a word on the right</string> -->
<!-- <string name="key_descr_gesture">Gesture</string> -->
<!-- <string name="clipboard_history_heading">Recently copied text</string> --> <!-- <string name="clipboard_history_heading">Recently copied text</string> -->
<!-- <string name="clipboard_pin_heading">Pinned</string> --> <!-- <string name="clipboard_pin_heading">Pinned</string> -->
<!-- <string name="clipboard_remove_confirm">Remove this clipboard?</string> --> <!-- <string name="clipboard_remove_confirm">Remove this clipboard?</string> -->

View File

@@ -127,6 +127,9 @@ This application contains no ads, doesn't make any network requests and is Open
<string name="key_descr_zwnj">Zero width non-joiner</string> <string name="key_descr_zwnj">Zero width non-joiner</string>
<string name="key_descr_nbsp">Non-breaking space</string> <string name="key_descr_nbsp">Non-breaking space</string>
<string name="key_descr_nnbsp">Narrow non-breaking space</string> <string name="key_descr_nnbsp">Narrow non-breaking space</string>
<string name="key_descr_delete_word">Delete a word</string>
<string name="key_descr_forward_delete_word">Delete a word on the right</string>
<string name="key_descr_gesture">Gesture</string>
<string name="clipboard_history_heading">Recently copied text</string> <string name="clipboard_history_heading">Recently copied text</string>
<string name="clipboard_pin_heading">Pinned</string> <string name="clipboard_pin_heading">Pinned</string>
<string name="clipboard_remove_confirm">Remove this clipboard?</string> <string name="clipboard_remove_confirm">Remove this clipboard?</string>

View File

@@ -1,7 +1,6 @@
package juloo.keyboard2; package juloo.keyboard2;
import android.os.Handler; import android.os.Handler;
import android.os.Looper;
import android.text.InputType; import android.text.InputType;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
@@ -27,9 +26,9 @@ public final class Autocapitalisation
InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES |
InputType.TYPE_TEXT_FLAG_CAP_WORDS; InputType.TYPE_TEXT_FLAG_CAP_WORDS;
public Autocapitalisation(Looper looper, Callback cb) public Autocapitalisation(Handler h, Callback cb)
{ {
_handler = new Handler(looper); _handler = h;
_callback = cb; _callback = cb;
} }

View File

@@ -2,6 +2,7 @@ package juloo.keyboard2;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.os.Looper; import android.os.Looper;
import android.os.Handler;
import android.text.InputType; import android.text.InputType;
import android.view.KeyCharacterMap; import android.view.KeyCharacterMap;
import android.view.KeyEvent; import android.view.KeyEvent;
@@ -28,10 +29,10 @@ public final class KeyEventHandler
[setSelection] could be used instead. */ [setSelection] could be used instead. */
boolean _move_cursor_force_fallback = false; boolean _move_cursor_force_fallback = false;
public KeyEventHandler(Looper looper, IReceiver recv) public KeyEventHandler(IReceiver recv)
{ {
_recv = recv; _recv = recv;
_autocap = new Autocapitalisation(looper, _autocap = new Autocapitalisation(recv.getHandler(),
this.new Autocapitalisation_callback()); this.new Autocapitalisation_callback());
_mods = Pointers.Modifiers.EMPTY; _mods = Pointers.Modifiers.EMPTY;
} }
@@ -146,11 +147,11 @@ public final class KeyEventHandler
if (down) if (down)
{ {
_meta_state = _meta_state | meta_flags; _meta_state = _meta_state | meta_flags;
send_keyevent(KeyEvent.ACTION_DOWN, eventCode); send_keyevent(KeyEvent.ACTION_DOWN, eventCode, _meta_state);
} }
else else
{ {
send_keyevent(KeyEvent.ACTION_UP, eventCode); send_keyevent(KeyEvent.ACTION_UP, eventCode, _meta_state);
_meta_state = _meta_state & ~meta_flags; _meta_state = _meta_state & ~meta_flags;
} }
} }
@@ -181,25 +182,28 @@ public final class KeyEventHandler
} }
} }
/*
* Don't set KeyEvent.FLAG_SOFT_KEYBOARD.
*/
void send_key_down_up(int keyCode) void send_key_down_up(int keyCode)
{ {
send_keyevent(KeyEvent.ACTION_DOWN, keyCode); send_key_down_up(keyCode, _meta_state);
send_keyevent(KeyEvent.ACTION_UP, keyCode);
} }
void send_keyevent(int eventAction, int eventCode) /** Ignores currently pressed system modifiers. */
void send_key_down_up(int keyCode, int metaState)
{
send_keyevent(KeyEvent.ACTION_DOWN, keyCode, metaState);
send_keyevent(KeyEvent.ACTION_UP, keyCode, metaState);
}
void send_keyevent(int eventAction, int eventCode, int metaState)
{ {
InputConnection conn = _recv.getCurrentInputConnection(); InputConnection conn = _recv.getCurrentInputConnection();
if (conn == null) if (conn == null)
return; return;
conn.sendKeyEvent(new KeyEvent(1, 1, eventAction, eventCode, 0, conn.sendKeyEvent(new KeyEvent(1, 1, eventAction, eventCode, 0,
_meta_state, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE)); KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
if (eventAction == KeyEvent.ACTION_UP) if (eventAction == KeyEvent.ACTION_UP)
_autocap.event_sent(eventCode, _meta_state); _autocap.event_sent(eventCode, metaState);
} }
void send_text(CharSequence text) void send_text(CharSequence text)
@@ -236,6 +240,9 @@ public final class KeyEventHandler
case REPLACE: send_context_menu_action(android.R.id.replaceText); break; case REPLACE: send_context_menu_action(android.R.id.replaceText); break;
case ASSIST: send_context_menu_action(android.R.id.textAssist); break; case ASSIST: send_context_menu_action(android.R.id.textAssist); break;
case AUTOFILL: send_context_menu_action(android.R.id.autofill); break; case AUTOFILL: send_context_menu_action(android.R.id.autofill); break;
case DELETE_WORD: send_key_down_up(KeyEvent.KEYCODE_DEL, KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON); break;
case FORWARD_DELETE_WORD: send_key_down_up(KeyEvent.KEYCODE_FORWARD_DEL, KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON); break;
case SELECTION_CANCEL: cancel_selection(); break;
} }
} }
@@ -253,15 +260,17 @@ public final class KeyEventHandler
return conn.getExtractedText(_move_cursor_req, 0); return conn.getExtractedText(_move_cursor_req, 0);
} }
/** [repeatition] might be negative, in which case the direction is reversed. */ /** [r] might be negative, in which case the direction is reversed. */
void handle_slider(KeyValue.Slider s, int repeatition) void handle_slider(KeyValue.Slider s, int r)
{ {
switch (s) switch (s)
{ {
case Cursor_left: move_cursor(-repeatition); break; case Cursor_left: move_cursor(-r); break;
case Cursor_right: move_cursor(repeatition); break; case Cursor_right: move_cursor(r); break;
case Cursor_up: move_cursor_vertical(-repeatition); break; case Cursor_up: move_cursor_vertical(-r); break;
case Cursor_down: move_cursor_vertical(repeatition); break; case Cursor_down: move_cursor_vertical(r); break;
case Selection_cursor_left: move_cursor_sel(r, true); break;
case Selection_cursor_right: move_cursor_sel(r, false); break;
} }
} }
@@ -275,12 +284,7 @@ public final class KeyEventHandler
if (conn == null) if (conn == null)
return; return;
ExtractedText et = get_cursor_pos(conn); ExtractedText et = get_cursor_pos(conn);
int system_mods = if (et != null && can_set_selection(conn))
KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON | KeyEvent.META_META_ON;
// Fallback to sending key events if system modifiers are activated or
// ExtractedText is not supported, for example on Termux.
if (!_move_cursor_force_fallback && et != null
&& (_meta_state & system_mods) == 0)
{ {
int sel_start = et.selectionStart; int sel_start = et.selectionStart;
int sel_end = et.selectionEnd; int sel_end = et.selectionEnd;
@@ -299,8 +303,45 @@ public final class KeyEventHandler
sel_start = sel_end; sel_start = sel_end;
} }
if (conn.setSelection(sel_start, sel_end)) if (conn.setSelection(sel_start, sel_end))
return; // [setSelection] succeeded, don't fallback to key events return; // Fallback to sending key events if [setSelection] failed
} }
move_cursor_fallback(d);
}
/** Move one of the two side of a selection. If [sel_left] is true, the left
position is moved, otherwise the right position is moved. */
void move_cursor_sel(int d, boolean sel_left)
{
InputConnection conn = _recv.getCurrentInputConnection();
if (conn == null)
return;
ExtractedText et = get_cursor_pos(conn);
if (et != null && can_set_selection(conn))
{
int sel_start = et.selectionStart;
int sel_end = et.selectionEnd;
if (sel_left == (sel_start <= sel_end))
sel_start += d;
else
sel_end += d;
if (conn.setSelection(sel_start, sel_end))
return; // Fallback to sending key events if [setSelection] failed
}
move_cursor_fallback(d);
}
/** Returns whether the selection can be set using [conn.setSelection()].
This can happen on Termux or when system modifiers are activated for
example. */
boolean can_set_selection(InputConnection conn)
{
final int system_mods =
KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON | KeyEvent.META_META_ON;
return !_move_cursor_force_fallback && (_meta_state & system_mods) == 0;
}
void move_cursor_fallback(int d)
{
if (d < 0) if (d < 0)
send_key_down_up_repeat(KeyEvent.KEYCODE_DPAD_LEFT, -d); send_key_down_up_repeat(KeyEvent.KEYCODE_DPAD_LEFT, -d);
else else
@@ -319,32 +360,73 @@ public final class KeyEventHandler
void evaluate_macro(KeyValue[] keys) void evaluate_macro(KeyValue[] keys)
{ {
final Pointers.Modifiers empty = Pointers.Modifiers.EMPTY; if (keys.length == 0)
return;
// Ignore modifiers that are activated at the time the macro is evaluated // Ignore modifiers that are activated at the time the macro is evaluated
mods_changed(empty); mods_changed(Pointers.Modifiers.EMPTY);
Pointers.Modifiers mods = empty; evaluate_macro_loop(keys, 0, Pointers.Modifiers.EMPTY, _autocap.pause());
final boolean autocap_paused = _autocap.pause(); }
for (KeyValue kv : keys)
/** Evaluate the macro asynchronously to make sure event are processed in the
right order. */
void evaluate_macro_loop(final KeyValue[] keys, int i, Pointers.Modifiers mods, final boolean autocap_paused)
{
boolean should_delay = false;
KeyValue kv = KeyModifier.modify(keys[i], mods);
if (kv != null)
{ {
kv = KeyModifier.modify(kv, mods);
if (kv == null)
continue;
if (kv.hasFlagsAny(KeyValue.FLAG_LATCH)) if (kv.hasFlagsAny(KeyValue.FLAG_LATCH))
{ {
// Non-special latchable keys clear latched modifiers // Non-special latchable keys clear latched modifiers
if (!kv.hasFlagsAny(KeyValue.FLAG_SPECIAL)) if (!kv.hasFlagsAny(KeyValue.FLAG_SPECIAL))
mods = empty; mods = Pointers.Modifiers.EMPTY;
mods = mods.with_extra_mod(kv); mods = mods.with_extra_mod(kv);
} }
else else
{ {
key_down(kv, false); key_down(kv, false);
key_up(kv, mods); key_up(kv, mods);
mods = empty; mods = Pointers.Modifiers.EMPTY;
} }
should_delay = wait_after_macro_key(kv);
} }
i++;
if (i >= keys.length) // Stop looping
{
_autocap.unpause(autocap_paused); _autocap.unpause(autocap_paused);
} }
else if (should_delay)
{
// Add a delay before sending the next key to avoid race conditions
// causing keys to be handled in the wrong order. Notably, KeyEvent keys
// handling is scheduled differently than the other edit functions.
final int i_ = i;
final Pointers.Modifiers mods_ = mods;
_recv.getHandler().postDelayed(new Runnable() {
public void run()
{
evaluate_macro_loop(keys, i_, mods_, autocap_paused);
}
}, 1000/30);
}
else
evaluate_macro_loop(keys, i, mods, autocap_paused);
}
boolean wait_after_macro_key(KeyValue kv)
{
switch (kv.getKind())
{
case Keyevent:
case Editing:
case Event:
return true;
case Slider:
return _move_cursor_force_fallback;
default:
return false;
}
}
/** Repeat calls to [send_key_down_up]. */ /** Repeat calls to [send_key_down_up]. */
void send_key_down_up_repeat(int event_code, int repeat) void send_key_down_up_repeat(int event_code, int repeat)
@@ -353,12 +435,27 @@ public final class KeyEventHandler
send_key_down_up(event_code); send_key_down_up(event_code);
} }
void cancel_selection()
{
InputConnection conn = _recv.getCurrentInputConnection();
if (conn == null)
return;
ExtractedText et = get_cursor_pos(conn);
if (et == null) return;
final int curs = et.selectionStart;
// Notify the receiver as Android's [onUpdateSelection] is not triggered.
if (conn.setSelection(curs, curs));
_recv.selection_state_changed(false);
}
public static interface IReceiver public static interface IReceiver
{ {
public void handle_event_key(KeyValue.Event ev); public void handle_event_key(KeyValue.Event ev);
public void set_shift_state(boolean state, boolean lock); public void set_shift_state(boolean state, boolean lock);
public void set_compose_pending(boolean pending); public void set_compose_pending(boolean pending);
public void selection_state_changed(boolean selection_is_ongoing);
public InputConnection getCurrentInputConnection(); public InputConnection getCurrentInputConnection();
public Handler getHandler();
} }
class Autocapitalisation_callback implements Autocapitalisation.Callback class Autocapitalisation_callback implements Autocapitalisation.Callback

View File

@@ -82,6 +82,7 @@ public final class KeyModifier
case HOOK_ABOVE: return apply_compose(k, ComposeKeyData.accent_hook_above); case HOOK_ABOVE: return apply_compose(k, ComposeKeyData.accent_hook_above);
case DOUBLE_GRAVE: return apply_compose(k, ComposeKeyData.accent_double_grave); case DOUBLE_GRAVE: return apply_compose(k, ComposeKeyData.accent_double_grave);
case ARROW_RIGHT: return apply_combining_char(k, "\u20D7"); case ARROW_RIGHT: return apply_combining_char(k, "\u20D7");
case SELECTION_MODE: return apply_selection_mode(k);
default: return k; default: return k;
} }
} }
@@ -381,6 +382,41 @@ public final class KeyModifier
case SHIFT: name = "capslock"; break; case SHIFT: name = "capslock"; break;
} }
break; break;
case Keyevent:
switch (k.getKeyevent())
{
case KeyEvent.KEYCODE_DEL: name = "delete_word"; break;
case KeyEvent.KEYCODE_FORWARD_DEL: name = "forward_delete_word"; break;
}
break;
}
return (name == null) ? k : KeyValue.getKeyByName(name);
}
private static KeyValue apply_selection_mode(KeyValue k)
{
String name = null;
switch (k.getKind())
{
case Char:
switch (k.getChar())
{
case ' ': name = "selection_cancel"; break;
}
break;
case Slider:
switch (k.getSlider())
{
case Cursor_left: name = "selection_cursor_left"; break;
case Cursor_right: name = "selection_cursor_right"; break;
}
break;
case Keyevent:
switch (k.getKeyevent())
{
case KeyEvent.KEYCODE_ESCAPE: name = "selection_cancel"; break;
}
break;
} }
return (name == null) ? k : KeyValue.getKeyByName(name); return (name == null) ? k : KeyValue.getKeyByName(name);
} }

View File

@@ -59,6 +59,7 @@ public final class KeyValue implements Comparable<KeyValue>
BREVE, BREVE,
BAR, BAR,
FN, FN,
SELECTION_MODE,
} // Last is be applied first } // Last is be applied first
public static enum Editing public static enum Editing
@@ -75,6 +76,9 @@ public final class KeyValue implements Comparable<KeyValue>
SHARE, SHARE,
ASSIST, ASSIST,
AUTOFILL, AUTOFILL,
DELETE_WORD,
FORWARD_DELETE_WORD,
SELECTION_CANCEL,
} }
public static enum Placeholder public static enum Placeholder
@@ -707,10 +711,15 @@ public final class KeyValue implements Comparable<KeyValue>
case "pasteAsPlainText": return editingKey(0xE035, Editing.PASTE_PLAIN); case "pasteAsPlainText": return editingKey(0xE035, Editing.PASTE_PLAIN);
case "undo": return editingKey(0xE036, Editing.UNDO); case "undo": return editingKey(0xE036, Editing.UNDO);
case "redo": return editingKey(0xE037, Editing.REDO); case "redo": return editingKey(0xE037, Editing.REDO);
case "delete_word": return editingKey(0xE01B, Editing.DELETE_WORD);
case "forward_delete_word": return editingKey(0xE01C, Editing.FORWARD_DELETE_WORD);
case "cursor_left": return sliderKey(Slider.Cursor_left, 1); case "cursor_left": return sliderKey(Slider.Cursor_left, 1);
case "cursor_right": return sliderKey(Slider.Cursor_right, 1); case "cursor_right": return sliderKey(Slider.Cursor_right, 1);
case "cursor_up": return sliderKey(Slider.Cursor_up, 1); case "cursor_up": return sliderKey(Slider.Cursor_up, 1);
case "cursor_down": return sliderKey(Slider.Cursor_down, 1); case "cursor_down": return sliderKey(Slider.Cursor_down, 1);
case "selection_cancel": return editingKey("Esc", Editing.SELECTION_CANCEL, FLAG_SMALLER_FONT);
case "selection_cursor_left": return sliderKey(Slider.Selection_cursor_left, -1); // Move the left side of the selection
case "selection_cursor_right": return sliderKey(Slider.Selection_cursor_right, 1);
// These keys are not used // These keys are not used
case "replaceText": return editingKey("repl", Editing.REPLACE); case "replaceText": return editingKey("repl", Editing.REPLACE);
case "textAssist": return editingKey(0xE038, Editing.ASSIST); case "textAssist": return editingKey(0xE038, Editing.ASSIST);
@@ -760,6 +769,9 @@ public final class KeyValue implements Comparable<KeyValue>
case "": case "": case "": case "":
return makeStringKey(name, FLAG_SMALLER_FONT); return makeStringKey(name, FLAG_SMALLER_FONT);
/* Internal keys */
case "selection_mode": return makeInternalModifier(Modifier.SELECTION_MODE);
default: return null; default: return null;
} }
} }
@@ -776,7 +788,9 @@ public final class KeyValue implements Comparable<KeyValue>
Cursor_left(0xE008), Cursor_left(0xE008),
Cursor_right(0xE006), Cursor_right(0xE006),
Cursor_up(0xE005), Cursor_up(0xE005),
Cursor_down(0xE007); Cursor_down(0xE007),
Selection_cursor_left(0xE008),
Selection_cursor_right(0xE006);
final String symbol; final String symbol;

View File

@@ -6,6 +6,7 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.inputmethodservice.InputMethodService; import android.inputmethodservice.InputMethodService;
import android.os.Build.VERSION; import android.os.Build.VERSION;
import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.text.InputType; import android.text.InputType;
import android.util.Log; import android.util.Log;
@@ -38,6 +39,7 @@ public class Keyboard2 extends InputMethodService
private ViewGroup _emojiPane = null; private ViewGroup _emojiPane = null;
private ViewGroup _clipboard_pane = null; private ViewGroup _clipboard_pane = null;
public int actionId; // Action performed by the Action key. public int actionId; // Action performed by the Action key.
private Handler _handler;
private Config _config; private Config _config;
@@ -107,7 +109,8 @@ public class Keyboard2 extends InputMethodService
{ {
super.onCreate(); super.onCreate();
SharedPreferences prefs = DirectBootAwarePreferences.get_shared_preferences(this); SharedPreferences prefs = DirectBootAwarePreferences.get_shared_preferences(this);
_keyeventhandler = new KeyEventHandler(getMainLooper(), this.new Receiver()); _handler = new Handler(getMainLooper());
_keyeventhandler = new KeyEventHandler(this.new Receiver());
Config.initGlobalConfig(prefs, getResources(), _keyeventhandler); Config.initGlobalConfig(prefs, getResources(), _keyeventhandler);
prefs.registerOnSharedPreferenceChangeListener(this); prefs.registerOnSharedPreferenceChangeListener(this);
_config = Config.globalConfig(); _config = Config.globalConfig();
@@ -359,6 +362,8 @@ public class Keyboard2 extends InputMethodService
{ {
super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd); super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd);
_keyeventhandler.selection_updated(oldSelStart, newSelStart); _keyeventhandler.selection_updated(oldSelStart, newSelStart);
if ((oldSelStart == oldSelEnd) != (newSelStart == newSelEnd))
_keyboardView.set_selection_state(newSelStart != newSelEnd);
} }
@Override @Override
@@ -477,10 +482,20 @@ public class Keyboard2 extends InputMethodService
_keyboardView.set_compose_pending(pending); _keyboardView.set_compose_pending(pending);
} }
public void selection_state_changed(boolean selection_is_ongoing)
{
_keyboardView.set_selection_state(selection_is_ongoing);
}
public InputConnection getCurrentInputConnection() public InputConnection getCurrentInputConnection()
{ {
return Keyboard2.this.getCurrentInputConnection(); return Keyboard2.this.getCurrentInputConnection();
} }
public Handler getHandler()
{
return _handler;
}
} }
private IBinder getConnectionToken() private IBinder getConnectionToken()

View File

@@ -139,6 +139,13 @@ public class Keyboard2View extends View
set_fake_ptr_latched(_compose_key, _compose_kv, pending, false); set_fake_ptr_latched(_compose_key, _compose_kv, pending, false);
} }
/** Called from [Keybard2.onUpdateSelection]. */
public void set_selection_state(boolean selection_state)
{
set_fake_ptr_latched(KeyboardData.Key.EMPTY,
KeyValue.getKeyByName("selection_mode"), selection_state, true);
}
public KeyValue modifyKey(KeyValue k, Pointers.Modifiers mods) public KeyValue modifyKey(KeyValue k, Pointers.Modifiers mods)
{ {
return KeyModifier.modify(k, mods); return KeyModifier.modify(k, mods);

View File

@@ -422,6 +422,8 @@ public final class KeyboardData
indication = i; indication = i;
} }
static final Key EMPTY = new Key(new KeyValue[9], null, 0, 1.f, 1.f, null);
/** Read a key value attribute that have a synonym. Having both synonyms /** Read a key value attribute that have a synonym. Having both synonyms
present at the same time is an error. present at the same time is an error.
Returns [null] if the attributes are not present. */ Returns [null] if the attributes are not present. */

View File

@@ -72,6 +72,8 @@ public class ExtraKeysPreference extends PreferenceCategory
"pasteAsPlainText", "pasteAsPlainText",
"undo", "undo",
"redo", "redo",
"delete_word",
"forward_delete_word",
"superscript", "superscript",
"subscript", "subscript",
"f11_placeholder", "f11_placeholder",
@@ -177,6 +179,14 @@ public class ExtraKeysPreference extends PreferenceCategory
id = R.string.key_descr_redo; id = R.string.key_descr_redo;
additional_info = format_key_combination(new String[]{"fn", "undo"}); additional_info = format_key_combination(new String[]{"fn", "undo"});
break; break;
case "delete_word":
id = R.string.key_descr_delete_word;
additional_info = format_key_combination_gesture(res, "backspace");
break;
case "forward_delete_word":
id = R.string.key_descr_forward_delete_word;
additional_info = format_key_combination_gesture(res, "forward_delete");
break;
case "selectAll": id = R.string.key_descr_selectAll; break; case "selectAll": id = R.string.key_descr_selectAll; break;
case "shareText": id = R.string.key_descr_shareText; break; case "shareText": id = R.string.key_descr_shareText; break;
case "subscript": id = R.string.key_descr_subscript; break; case "subscript": id = R.string.key_descr_subscript; break;
@@ -276,6 +286,7 @@ public class ExtraKeysPreference extends PreferenceCategory
return kv.getString(); return kv.getString();
} }
/** Format a key combination */
static String format_key_combination(String[] keys) static String format_key_combination(String[] keys)
{ {
StringBuilder out = new StringBuilder(); StringBuilder out = new StringBuilder();
@@ -287,6 +298,13 @@ public class ExtraKeysPreference extends PreferenceCategory
return out.toString(); return out.toString();
} }
/** Explain a gesture on a key */
static String format_key_combination_gesture(Resources res, String key_name)
{
return res.getString(R.string.key_descr_gesture) + " + "
+ KeyValue.getKeyByName(key_name).getString();
}
static KeyboardData.PreferredPos key_preferred_pos(String key_name) static KeyboardData.PreferredPos key_preferred_pos(String key_name)
{ {
switch (key_name) switch (key_name)
@@ -341,6 +359,16 @@ public class ExtraKeysPreference extends PreferenceCategory
new KeyboardData.KeyPos(0, -1, 3), new KeyboardData.KeyPos(0, -1, 3),
new KeyboardData.KeyPos(0, -1, 4), new KeyboardData.KeyPos(0, -1, 4),
}); });
case "delete_word":
return new KeyboardData.PreferredPos(KeyValue.getKeyByName("backspace"),
new KeyboardData.KeyPos[]{
new KeyboardData.KeyPos(-1, -1, 3),
});
case "forward_delete_word":
return new KeyboardData.PreferredPos(KeyValue.getKeyByName("backspace"),
new KeyboardData.KeyPos[]{
new KeyboardData.KeyPos(-1, -1, 4),
});
} }
return KeyboardData.PreferredPos.DEFAULT; return KeyboardData.PreferredPos.DEFAULT;
} }

18
srcs/special_font/01B.svg Normal file
View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Based on materialdesignicons.com backspace-outline -->
<svg
version="1.1"
width="24"
height="24"
viewBox="0 0 24 24"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<path
id="text3"
style="font-weight:600;font-size:13.3333px;font-family:FreeSans;-inkscape-font-specification:'FreeSans Semi-Bold'"
d="m 17.517249,16.883978 c -0.599999,0 -1.199997,0 -1.799996,0 -0.542221,-2.528883 -1.084441,-5.057765 -1.626662,-7.5866476 -0.528888,2.5288826 -1.057775,5.0577646 -1.586663,7.5866476 -0.599998,0 -1.199997,0 -1.799995,0 -0.9155533,-3.239992 -1.8311066,-6.479984 -2.7466599,-9.7199755 0.706665,0 1.4133299,0 2.1199949,0 0.502221,2.4266605 1.004442,4.8533215 1.506663,7.2799815 0.502221,-2.426661 1.004441,-4.853321 1.506662,-7.2799815 0.657776,0 1.315553,0 1.973329,0 0.524443,2.4311048 1.048886,4.8622095 1.573329,7.2933145 0.484443,-2.431105 0.968887,-4.8622097 1.45333,-7.2933145 0.706665,0 1.413329,0 2.119994,0 C 19.3128,10.403994 18.415024,13.643986 17.517249,16.883978 Z M 22,3 c 1.232984,-0.040548 2.168377,1.1810823 2,2.36 -0.0069,4.6147431 0.01374,9.230179 -0.01033,13.844488 C 23.906211,20.390581 22.701093,21.155835 21.58,21 16.677486,20.9972 11.77479,21.0056 6.8723901,20.9958 5.6427252,20.925088 5.1783178,19.667046 4.5442984,18.812248 3.0295323,16.541498 1.5147661,14.270749 0,12 1.8548324,9.2326362 3.6795639,6.4439141 5.5533789,3.6900195 6.2693382,2.7489141 7.4877473,3.0506966 8.5000001,3 13,3 17.5,3 22,3 Z m 0,2 C 17,5 12,5 7,5 5.4266667,7.3333333 3.8533333,9.6666667 2.28,12 c 1.5733333,2.333333 3.1466667,4.666667 4.72,7 5,0 10,0 15,0 0,-4.666667 0,-9.3333333 0,-14 z" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

18
srcs/special_font/01C.svg Normal file
View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Based on materialdesignicons.com backspace-reverse-outline -->
<svg
version="1.1"
width="24"
height="24"
viewBox="0 0 24 24"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<path
id="text3"
style="font-weight:600;font-size:13.3333px;font-family:FreeSans;-inkscape-font-specification:'FreeSans Semi-Bold'"
d="m 13.559976,16.719975 c -0.599999,0 -1.199997,0 -1.799996,0 -0.542221,-2.528883 -1.084441,-5.057765 -1.626662,-7.586648 -0.5288876,2.528883 -1.0577752,5.057765 -1.5866628,7.586648 -0.5999985,0 -1.1999969,0 -1.7999954,0 -0.9155532,-3.239992 -1.8311065,-6.479984 -2.7466597,-9.7199759 0.7066649,0 1.4133297,0 2.1199946,0 0.502221,2.4266606 1.0044419,4.8533209 1.5066629,7.2799819 0.5022209,-2.426661 1.0044419,-4.8533213 1.5066628,-7.2799819 0.6577762,0 1.3155526,0 1.9733286,0 0.524443,2.431105 1.048886,4.8622099 1.573329,7.2933149 0.484443,-2.431105 0.968887,-4.8622099 1.45333,-7.2933149 0.706665,0 1.413329,0 2.119994,0 -0.897775,3.2399919 -1.795551,6.4799839 -2.693326,9.7199759 z M 2,3 C 0.76701608,2.9594518 -0.16837714,4.1810822 0,5.36 0.00686259,9.9747431 -0.01373935,14.590179 0.01032577,19.204488 0.09378911,20.390581 1.2989069,21.155835 2.42,21 c 4.902514,-0.0028 9.80521,0.0056 14.70761,-0.0042 1.229665,-0.07074 1.694072,-1.328782 2.328092,-2.18358 C 20.970468,16.541498 22.485234,14.270749 24,12 22.145168,9.2326362 20.320436,6.4439141 18.446621,3.6900195 17.730662,2.7489141 16.512253,3.0506966 15.5,3 11,3 6.5,3 2,3 Z m 0,2 c 5,0 10,0 15,0 1.573333,2.3333333 3.146667,4.6666667 4.72,7 C 20.146667,14.333333 18.573333,16.666667 17,19 12,19 7,19 2,19 2,14.333333 2,9.6666667 2,5 Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -74,7 +74,11 @@ def sync_metadata(value_dir, strings):
os.makedirs(meta_dir) os.makedirs(meta_dir)
txt_file = os.path.join(meta_dir, fname) txt_file = os.path.join(meta_dir, fname)
with open(txt_file, "w", encoding="utf-8") as out: with open(txt_file, "w", encoding="utf-8") as out:
out.write(string.text.removeprefix('"').removesuffix('"')) out.write(string.text
.replace("\\n", "\n")
.replace("\\'", "'")
.removeprefix('"')
.removesuffix('"'))
out.write("\n") out.write("\n")
sync_meta_file("title.txt", ("app_name_release", None)) sync_meta_file("title.txt", ("app_name_release", None))
sync_meta_file("short_description.txt", ("short_description", None)) sync_meta_file("short_description.txt", ("short_description", None))