From 62427dbdb452398005dfc5004909a306080b0542 Mon Sep 17 00:00:00 2001 From: Sander Land Date: Sat, 18 Jul 2020 00:48:05 +0200 Subject: [PATCH] bug fix / ime testing --- katrain/__main__.py | 9 +++--- katrain/core/constants.py | 2 +- katrain/core/game_node.py | 9 +++--- katrain/gui/kivyutils.py | 59 ++++++++++++++++++++++++++++++++++++++- katrain/gui/popups.py | 21 ++------------ 5 files changed, 69 insertions(+), 31 deletions(-) diff --git a/katrain/__main__.py b/katrain/__main__.py index fbbd67c..297633e 100644 --- a/katrain/__main__.py +++ b/katrain/__main__.py @@ -4,7 +4,7 @@ import os os.environ["KCFG_KIVY_LOG_LEVEL"] = os.environ.get("KCFG_KIVY_LOG_LEVEL", "warning") if "KIVY_AUDIO" not in os.environ: - os.environ["KIVY_AUDIO"] = "sdl2" # seems to be most stable / some players hard crash + os.environ["KIVY_AUDIO"] = "sdl2" # some backends hard crash / this seems to be most stable # next, icon from katrain.core.utils import find_package_resource, PATHS @@ -382,19 +382,18 @@ class KaTrainGui(Screen, KaTrainBase): def _do_output_sgf(self): msg = self.game.write_sgf(self.config("general/sgf_save")) self.log(msg, OUTPUT_INFO) - self.controls.set_status(msg, OUTPUT_INFO) + self.controls.set_status(msg, STATUS_INFO) def load_sgf_from_clipboard(self): clipboard = Clipboard.paste() if not clipboard: - self.controls.set_status(f"Ctrl-V pressed but clipboard is empty.", STATUS_ERROR) + self.controls.set_status(f"Ctrl-V pressed but clipboard is empty.", STATUS_INFO) return try: move_tree = KaTrainSGF.parse_sgf(clipboard) except Exception as exc: self.controls.set_status( - i18n._("Failed to import from clipboard").format(error=exc, contents=clipboard[:50]), - STATUS_ERROR + i18n._("Failed to import from clipboard").format(error=exc, contents=clipboard[:50]), STATUS_INFO ) return move_tree.nodes_in_tree[-1].analyze( diff --git a/katrain/core/constants.py b/katrain/core/constants.py index e55a794..f10ee00 100644 --- a/katrain/core/constants.py +++ b/katrain/core/constants.py @@ -34,7 +34,7 @@ AI_INFLUENCE = "ai:p:influence" AI_TERRITORY = "ai:p:territory" AI_RANK = "ai:p:rank" -AI_CONFIG_DEFAULT = AI_SCORELOSS +AI_CONFIG_DEFAULT = AI_RANK AI_STRATEGIES_ENGINE = [AI_DEFAULT, AI_HANDICAP, AI_SCORELOSS, AI_JIGO] AI_STRATEGIES_PICK = [AI_PICK, AI_LOCAL, AI_TENUKI, AI_INFLUENCE, AI_TERRITORY, AI_RANK] diff --git a/katrain/core/game_node.py b/katrain/core/game_node.py index a4f8676..35ec382 100644 --- a/katrain/core/game_node.py +++ b/katrain/core/game_node.py @@ -32,12 +32,11 @@ class GameNode(SGFNode): else: show_class = False if ( - (save_comments_player or {}).get(self.player, False) - and show_class - and self.analysis_ready - and self.parent + self.parent and self.parent.analysis_ready - ) or note: + and self.analysis_ready + and (note or ((save_comments_player or {}).get(self.player, False) and show_class)) + ): candidate_moves = self.parent.candidate_moves top_x = Move.from_gtp(candidate_moves[0]["move"]).sgf(self.board_size) best_sq = [ diff --git a/katrain/gui/kivyutils.py b/katrain/gui/kivyutils.py index 5e3b875..19128f8 100644 --- a/katrain/gui/kivyutils.py +++ b/katrain/gui/kivyutils.py @@ -1,5 +1,6 @@ from kivy.clock import Clock from kivy.core.text import Label as CoreLabel +from kivy.core.window import Window from kivy.graphics import * from kivy.properties import ( BooleanProperty, @@ -9,18 +10,20 @@ from kivy.properties import ( OptionProperty, StringProperty, ) -from kivy.uix.behaviors import ButtonBehavior, ToggleButtonBehavior +from kivy.uix.behaviors import ButtonBehavior, ToggleButtonBehavior, FocusBehavior from kivy.uix.boxlayout import BoxLayout from kivy.uix.button import Button from kivy.uix.label import Label from kivy.uix.scrollview import ScrollView from kivy.uix.spinner import Spinner +from kivy.uix.textinput import TextInput from kivy.uix.widget import Widget from kivymd.app import MDApp from kivymd.uix.behaviors import CircularRippleBehavior, RectangularRippleBehavior from kivymd.uix.boxlayout import MDBoxLayout from kivymd.uix.button import BaseFlatButton, BasePressedButton from kivymd.uix.navigationdrawer import MDNavigationDrawer +from kivymd.uix.textfield import MDTextField from katrain.core.constants import AI_STRATEGIES_RECOMMENDED_ORDER, GAME_TYPES, MODE_PLAY, PLAYER_AI from katrain.core.lang import i18n @@ -162,6 +165,60 @@ class BGBoxLayout(BoxLayout, BackgroundMixin): # -- gui elements +# MDTextField +class IMETextField(TextInput): + imo_char = StringProperty("") # current character being input by the IME system, or '' if nothing + + def _bind_keyboard(self): + super()._bind_keyboard() + Window.bind(on_textedit=self.on_textedit) + + def _unbind_keyboard(self): + super()._unbind_keyboard() + Window.unbind(on_textedit=self.on_textedit) + + def do_backspace(self, from_undo=False, mode="bkspc"): + if self.imo_char == "": # IMO handles sub-character backspaces + return super().do_backspace(from_undo, mode) + + def do_cursor_movement(self, *args, **kwargs): + # handle this before cursor changes, otherwise left arrow duplicates characters + self.on_textedit(None, "") + super().do_cursor_movement(*args, **kwargs) + + def on_textedit(self, window, imo_input): + print("on_textedit", repr(imo_input), "prev", repr(self.imo_char)) + if self.imo_char == "" and imo_input == "": + return + + if self.imo_char == "" and imo_input != "" and self._selection: + self.delete_selection() + + cc, cr = self.cursor + ci = self.cursor_index() + text = self._lines[cr] + if cc != 0 and text[cc - 1] == self.imo_char: + new_text = text[: cc - 1] + imo_input + text[cc:] + print("replace IMO", new_text) + self._set_line_text(cr, new_text) + else: + new_text = text[:cc] + imo_input + text[cc:] + print( + "insert new IMO", new_text, + ) + self._set_line_text(cr, new_text) + self.cursor = self.get_cursor_from_index(ci + len(imo_input)) + + start, finish, lines, lineflags, len_lines = self._get_line_from_cursor(cr, new_text) + self._refresh_text_from_property("insert", start, finish, lines, lineflags, len_lines) + self.imo_char = imo_input + + def insert_text(self, text, from_undo=False): + print("insert text", repr(text), repr(self.imo_char)) + if self.imo_char != "": + self.on_textedit(None, "") # 핫 -> 하세 gives an insert 하 event, so remove 핫 + super().insert_text(text, from_undo) + class I18NSpinner(Spinner): __events__ = ["on_select"] diff --git a/katrain/gui/popups.py b/katrain/gui/popups.py index bd63272..ce94bef 100644 --- a/katrain/gui/popups.py +++ b/katrain/gui/popups.py @@ -104,21 +104,7 @@ class LabelledSpinner(I18NSpinner): class LabelledFloatInput(LabelledTextInput): - signed = BooleanProperty(True) - pat = re.compile("[^0-9-]") - - def insert_text(self, substring, from_undo=False): - pat = self.pat - if "." in self.text: - s = re.sub(pat, "", substring) - else: - s = ".".join([re.sub(pat, "", s) for s in substring.split(".", 1)]) - r = super().insert_text(s, from_undo=from_undo) - if not self.signed and "-" in self.text: - self.text = self.text.replace("-", "") - elif self.text and "-" in self.text[1:]: - self.text = self.text[0] + self.text[1:].replace("-", "") - return r + input_filter = ObjectProperty("float") @property def input_value(self): @@ -126,10 +112,7 @@ class LabelledFloatInput(LabelledTextInput): class LabelledIntInput(LabelledTextInput): - pat = re.compile("[^0-9]") - - def insert_text(self, substring, from_undo=False): - return super().insert_text(re.sub(self.pat, "", substring), from_undo=from_undo) + input_filter = ObjectProperty("int") @property def input_value(self):