Skip to content

Instantly share code, notes, and snippets.

@brvier
Created October 22, 2022 09:31
Show Gist options
  • Save brvier/4b2eda49e6d5e9d50aacaccc65d4d707 to your computer and use it in GitHub Desktop.
Save brvier/4b2eda49e6d5e9d50aacaccc65d4d707 to your computer and use it in GitHub Desktop.
Test Android keyboard with swype.
# -*- 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