Created
October 22, 2022 09:31
-
-
Save brvier/4b2eda49e6d5e9d50aacaccc65d4d707 to your computer and use it in GitHub Desktop.
Test Android keyboard with swype.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
""" | |
on_textedit event sample. | |
""" | |
from kivy.app import App | |
from kivy.lang import Builder | |
from kivy.properties import StringProperty | |
from kivy.uix.textinput import TextInput | |
from kivy.uix.widget import Widget | |
class TextInputIME(TextInput): | |
testtext = StringProperty() | |
def __init__(self, **kwargs): | |
super(TextInputIME, self).__init__(**kwargs) | |
self._ime_previous_is_delete = False | |
self._is_ime = False | |
def keyboard_on_textinput(self, window, text): | |
print("keyboard_on_textinput_:{}:{}".format(window, text)) | |
if self._selection: | |
self.delete_selection() | |
text = text.removeprefix(self._ime_composition) | |
self._ime_composition = "" | |
self._is_ime = False | |
self.insert_text(text, False) | |
def on_cursor(self, instance, value): | |
print("on_cursor:{}:{}".format(instance, value)) | |
self._ime_composition = "" | |
super(TextInputIME, self).on_cursor(instance, value) | |
def window_on_textedit(self, window, ime_input): | |
print( | |
"window_on_textedit:{}:{}:{}:{}".format( | |
window, ime_input, self._ime_composition, self._ime_previous_is_delete | |
) | |
) | |
text_lines = self._lines or [""] | |
self._is_ime = True | |
if not self._ime_composition: | |
try: | |
cc, cr = self.cursor | |
text = text_lines[cr] | |
print(cc, cr, text) | |
print( | |
"text[cc:cc+len]:{} - {}".format( | |
text[cc - len(ime_input) - 1 : cc], | |
ime_input, | |
) | |
) | |
if text[cc - len(ime_input) - 1 : cc - 1] == ime_input: | |
self._ime_cursor = self.cursor | |
self._ime_composition = text[cc - len(ime_input) - 1 : cc] | |
except IndexError as err: | |
print("Index error:{}".format(err)) | |
if self._ime_composition: | |
pcc, pcr = self._ime_cursor | |
text = text_lines[pcr] | |
len_ime = len(self._ime_composition) | |
if text[pcc - len_ime : pcc] == self._ime_composition: # always? | |
remove_old_ime_text = text[: pcc - len_ime] + text[pcc:] | |
ci = self.cursor_index() | |
self._refresh_text_from_property( | |
"insert", *self._get_line_from_cursor(pcr, remove_old_ime_text) | |
) | |
self.cursor = self.get_cursor_from_index(ci - len_ime) | |
if ime_input: | |
if self._selection: | |
self.delete_selection() | |
cc, cr = self.cursor | |
text = text_lines[cr] | |
new_text = text[:cc] + ime_input + text[cc:] | |
self._refresh_text_from_property( | |
"insert", *self._get_line_from_cursor(cr, new_text) | |
) | |
self.cursor = self.get_cursor_from_index( | |
self.cursor_index() + len(ime_input) | |
) | |
self._ime_composition = ime_input | |
self._ime_cursor = self.cursor | |
def do_backspace(self, from_undo=False, mode="bkspc"): | |
"""Do backspace operation from the current cursor position. | |
This action might do several things: | |
- removing the current selection if available. | |
- removing the previous char and move the cursor back. | |
- do nothing, if we are at the start. | |
""" | |
FL_IS_LINEBREAK = 0x01 | |
# IME system handles its own backspaces | |
if self.readonly: # or self._ime_composition: | |
return | |
print("do_backspace:{}".format(self._ime_composition)) | |
self._ime_previous_is_delete = True | |
col, row = self.cursor | |
_lines = self._lines | |
_lines_flags = self._lines_flags | |
text = _lines[row] | |
cursor_index = self.cursor_index() | |
if col == 0 and row == 0: | |
return | |
start = row | |
if col == 0: | |
if _lines_flags[row] == FL_IS_LINEBREAK: | |
substring = "\n" | |
new_text = _lines[row - 1] + text | |
else: | |
substring = _lines[row - 1][-1] if len(_lines[row - 1]) > 0 else "" | |
new_text = _lines[row - 1][:-1] + text | |
self._set_line_text(row - 1, new_text) | |
self._delete_line(row) | |
start = row - 1 | |
else: | |
# ch = text[col-1] | |
substring = text[col - 1] | |
new_text = text[: col - 1] + text[col:] | |
self._set_line_text(row, new_text) | |
# refresh just the current line instead of the whole text | |
start, finish, lines, lineflags, len_lines = self._get_line_from_cursor( | |
start, new_text | |
) | |
# avoid trigger refresh, leads to issue with | |
# keys/text send rapidly through code. | |
self._refresh_text_from_property( | |
"insert" if col == 0 else "del", start, finish, lines, lineflags, len_lines | |
) | |
self.cursor = self.get_cursor_from_index(cursor_index - 1) | |
# handle undo and redo | |
self._set_unredo_bkspc( | |
cursor_index, cursor_index - 1, substring, from_undo, mode | |
) | |
def _on_textedit(self, window, text, start=0, length=0): | |
print( | |
"DEBUG _on_textedit:{}:{} - {} - {}:{}".format( | |
window, text, self._is_ime, start, length | |
) | |
) | |
def keyboard_on_key_down(self, window, keycode, text, modifiers): | |
print( | |
"DEBUG keyboard_on_key_down:{}:{}:{}:{} - {}".format( | |
window, keycode, text, modifiers, self._is_ime | |
) | |
) | |
if self._is_ime: | |
return False | |
super(TextInputIME, self).keyboard_on_key_down(window, keycode, text, modifiers) | |
class MainWidget(Widget): | |
text = StringProperty() | |
def __init__(self, **kwargs): | |
super(MainWidget, self).__init__(**kwargs) | |
self.text = "" | |
class TextEditTestApp(App): | |
def __init__(self, **kwargs): | |
super(TextEditTestApp, self).__init__(**kwargs) | |
def build(self): | |
return MainWidget() | |
if __name__ == "__main__": | |
Builder.load_string( | |
""" | |
<MainWidget>: | |
BoxLayout: | |
orientation: 'vertical' | |
size: root.size | |
Label: | |
size_hint: 1, 0.05 | |
text: "Text editing by IME : " + text_box._ime_composition | |
Label: | |
size_hint: 1, 0.05 | |
text: "Text property IME : " + text_box.text | |
TextInputIME: | |
size_hint: 1, 1 | |
id: text_box | |
focus: True | |
keyboard_suggestions: True | |
pos_hint: {"top": 0.7} | |
input_type: "text" | |
""" | |
) | |
TextEditTestApp().run() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment