diff --git a/pygame_gui/elements/ui_text_entry_box.py b/pygame_gui/elements/ui_text_entry_box.py index 133c4030..9c60804c 100644 --- a/pygame_gui/elements/ui_text_entry_box.py +++ b/pygame_gui/elements/ui_text_entry_box.py @@ -3,7 +3,7 @@ from pygame_gui.core.utility import clipboard_paste, clipboard_copy -from pygame import Rect, MOUSEBUTTONDOWN, MOUSEBUTTONUP, BUTTON_LEFT, KEYDOWN +from pygame import Rect, MOUSEBUTTONDOWN, MOUSEBUTTONUP, BUTTON_LEFT, KEYDOWN, TEXTINPUT from pygame import KMOD_META, KMOD_CTRL, K_a, K_x, K_c, K_v from pygame import K_LEFT, K_RIGHT, K_UP, K_DOWN, K_HOME, K_END, K_BACKSPACE, K_DELETE, K_RETURN from pygame import key @@ -239,8 +239,13 @@ def process_event(self, event: Event) -> bool: consumed_event = True elif self._process_action_key_event(event): consumed_event = True - elif self._process_text_entry_key(event): - consumed_event = True + + if self.is_enabled and self.is_focused and event.type == TEXTINPUT: + processed_any_char = False + for char in event.text: + if self._process_entered_character(char): + processed_any_char = True + consumed_event = processed_any_char if self.html_text != initial_text_state: # new event @@ -262,8 +267,16 @@ def _process_action_key_event(self, event: Event) -> bool: """ consumed_event = False - - if event.key == K_BACKSPACE: + if event.key == K_RETURN: + start_str = self.html_text[:self.edit_position] + end_str = self.html_text[self.edit_position:] + self.html_text = start_str + '\n' + end_str + self.text_box_layout.insert_line_break(self.edit_position, self.parser) + self.edit_position += 1 + self.redraw_from_text_block() + self.cursor_has_moved_recently = True + consumed_event = True + elif event.key == K_BACKSPACE: if abs(self.select_range[0] - self.select_range[1]) > 0: self.text_box_layout.delete_selected_text() low_end = min(self.select_range[0], self.select_range[1]) @@ -432,44 +445,40 @@ def _process_text_entry_key(self, event: Event) -> bool: :return: True if consumed. """ consumed_event = False - if event.key == K_RETURN: - start_str = self.html_text[:self.edit_position] - end_str = self.html_text[self.edit_position:] - self.html_text = start_str + '\n' + end_str - self.text_box_layout.insert_line_break(self.edit_position, self.parser) - self.edit_position += 1 - self.redraw_from_text_block() - self.cursor_has_moved_recently = True - consumed_event = True - elif hasattr(event, 'unicode'): + if hasattr(event, 'unicode'): character = event.unicode - # here we really want to get the font metrics for the current active style where the edit cursor is - # instead we will make do for now - font = self.ui_theme.get_font(self.combined_element_ids) - char_metrics = font.get_metrics(character) - if len(char_metrics) > 0 and char_metrics[0] is not None: - valid_character = True - if valid_character: - if abs(self.select_range[0] - self.select_range[1]) > 0: - low_end = min(self.select_range[0], self.select_range[1]) - high_end = max(self.select_range[0], self.select_range[1]) - self.html_text = self.html_text[:low_end] + character + self.html_text[high_end:] - self.text_box_layout.delete_selected_text() - self.text_box_layout.insert_text(character, low_end, self.parser) - self.edit_position = low_end + 1 - self.select_range = [0, 0] - else: - start_str = self.html_text[:self.edit_position] - end_str = self.html_text[self.edit_position:] - self.html_text = start_str + character + end_str - self.text_box_layout.insert_text(character, self.edit_position, self.parser) - self.edit_position += 1 - self.redraw_from_text_block() - self.cursor_has_moved_recently = True - consumed_event = True + consumed_event = self._process_entered_character(character) return consumed_event + def _process_entered_character(self, character: str) -> bool: + processed_char = False + # here we really want to get the font metrics for the current active style where the edit cursor is + # instead we will make do for now + font = self.ui_theme.get_font(self.combined_element_ids) + char_metrics = font.get_metrics(character) + if len(char_metrics) > 0 and char_metrics[0] is not None: + valid_character = True + if valid_character: + if abs(self.select_range[0] - self.select_range[1]) > 0: + low_end = min(self.select_range[0], self.select_range[1]) + high_end = max(self.select_range[0], self.select_range[1]) + self.html_text = self.html_text[:low_end] + character + self.html_text[high_end:] + self.text_box_layout.delete_selected_text() + self.text_box_layout.insert_text(character, low_end, self.parser) + self.edit_position = low_end + 1 + self.select_range = [0, 0] + else: + start_str = self.html_text[:self.edit_position] + end_str = self.html_text[self.edit_position:] + self.html_text = start_str + character + end_str + self.text_box_layout.insert_text(character, self.edit_position, self.parser) + self.edit_position += 1 + self.redraw_from_text_block() + self.cursor_has_moved_recently = True + processed_char = True + return processed_char + def _process_keyboard_shortcut_event(self, event: Event) -> bool: """ Check if event is one of the CTRL key keyboard shortcuts. diff --git a/pygame_gui/elements/ui_text_entry_line.py b/pygame_gui/elements/ui_text_entry_line.py index 77668a3b..b67c9fb2 100644 --- a/pygame_gui/elements/ui_text_entry_line.py +++ b/pygame_gui/elements/ui_text_entry_line.py @@ -406,8 +406,13 @@ def process_event(self, event: pygame.event.Event) -> bool: consumed_event = True elif self._process_action_key_event(event): consumed_event = True - elif self._process_text_entry_key(event): - consumed_event = True + + if self.is_enabled and self.is_focused and event.type == pygame.TEXTINPUT: + processed_any_char = False + for char in event.text: + if self._process_entered_character(char): + processed_any_char = True + consumed_event = processed_any_char if self.is_enabled and self.is_focused and event.type == pygame.KEYUP: if event.key == pygame.K_RETURN or event.key == pygame.K_KP_ENTER: @@ -429,6 +434,12 @@ def process_event(self, event: pygame.event.Event) -> bool: return consumed_event def _process_text_entry_key(self, event: pygame.event.Event) -> bool: + consumed_event = False + if hasattr(event, 'unicode'): + consumed_event = self._process_entered_character(event.unicode) + return consumed_event + + def _process_entered_character(self, character: str) -> bool: """ Process key input that can be added to the text entry text. @@ -436,15 +447,14 @@ def _process_text_entry_key(self, event: pygame.event.Event) -> bool: :return: True if consumed. """ - consumed_event = False + processed_character = False within_length_limit = True if (self.length_limit is not None and (len(self.text) - abs(self.select_range[0] - self.select_range[1])) >= self.length_limit): within_length_limit = False - if within_length_limit and hasattr(event, 'unicode') and self.font is not None: - character = event.unicode + if within_length_limit and self.font is not None: char_metrics = self.font.get_metrics(character) if len(char_metrics) > 0 and char_metrics[0] is not None: valid_character = True @@ -476,8 +486,8 @@ def _process_text_entry_key(self, event: pygame.event.Event) -> bool: self.edit_position += 1 self.cursor_has_moved_recently = True - consumed_event = True - return consumed_event + processed_character = True + return processed_character def _process_action_key_event(self, event: pygame.event.Event) -> bool: """ diff --git a/tests/test_elements/test_ui_text_entry_box.py b/tests/test_elements/test_ui_text_entry_box.py index 42fc2421..9b1fced5 100644 --- a/tests/test_elements/test_ui_text_entry_box.py +++ b/tests/test_elements/test_ui_text_entry_box.py @@ -714,17 +714,12 @@ def test_enable(self, _init_pygame: None, default_ui_manager: UIManager, assert text_entry.is_enabled is True text_entry.focus() # process a mouse button down event - processed_key_event = text_entry.process_event(pygame.event.Event(pygame.KEYDOWN, - {'key': pygame.K_d, - 'mod': 0, - 'unicode': 'd'})) + processed_text_input_event = text_entry.process_event(pygame.event.Event(pygame.TEXTINPUT, {'text': 'd'})) - text_entry.process_event(pygame.event.Event(pygame.KEYDOWN, {'key': pygame.K_a, 'mod': 0, - 'unicode': 'a'})) - text_entry.process_event(pygame.event.Event(pygame.KEYDOWN, {'key': pygame.K_n, 'mod': 0, - 'unicode': 'n'})) + text_entry.process_event(pygame.event.Event(pygame.TEXTINPUT, {'text': 'a'})) + text_entry.process_event(pygame.event.Event(pygame.TEXTINPUT, {'text': 'n'})) - assert processed_key_event is True and text_entry.get_text() == 'dan' + assert processed_text_input_event is True and text_entry.get_text() == 'dan' def test_on_locale_changed(self, _init_pygame, default_ui_manager, _display_surface_return_none): text_box = UITextEntryBox(initial_text='la la LA LA LAL LAL ALALA' @@ -862,17 +857,12 @@ def test_process_event_text_entered_success(self, _init_pygame: None, text_entry.focus() - processed_key_event = text_entry.process_event(pygame.event.Event(pygame.KEYDOWN, - {'key': pygame.K_d, - 'mod': 0, - 'unicode': 'd'})) + processed_text_input_event = text_entry.process_event(pygame.event.Event(pygame.TEXTINPUT, {'text': 'd'})) - text_entry.process_event(pygame.event.Event(pygame.KEYDOWN, {'key': pygame.K_a, 'mod': 0, - 'unicode': 'a'})) - text_entry.process_event(pygame.event.Event(pygame.KEYDOWN, {'key': pygame.K_n, 'mod': 0, - 'unicode': 'n'})) + text_entry.process_event(pygame.event.Event(pygame.TEXTINPUT, {'text': 'a'})) + text_entry.process_event(pygame.event.Event(pygame.TEXTINPUT, {'text': 'n'})) - assert processed_key_event and text_entry.get_text() == 'dan' + assert processed_text_input_event and text_entry.get_text() == 'dan' def test_process_event_text_entered_with_select_range(self, _init_pygame: None, default_ui_manager: UIManager, @@ -884,13 +874,10 @@ def test_process_event_text_entered_with_select_range(self, _init_pygame: None, text_entry.focus() text_entry.select_range = [1, 9] - # process a mouse button down event - processed_key_event = text_entry.process_event(pygame.event.Event(pygame.KEYDOWN, - {'key': pygame.K_o, - 'mod': 0, - 'unicode': 'o'})) + # process a text input event + processed_text_input_event = text_entry.process_event(pygame.event.Event(pygame.TEXTINPUT, {'text': 'o'})) - assert (processed_key_event is True and + assert (processed_text_input_event is True and text_entry.get_text() == 'Ho hours of fun writing tests') def test_process_event_text_ctrl_c(self, _init_pygame: None, diff --git a/tests/test_elements/test_ui_text_entry_line.py b/tests/test_elements/test_ui_text_entry_line.py index a5cbfad7..b7fd453e 100644 --- a/tests/test_elements/test_ui_text_entry_line.py +++ b/tests/test_elements/test_ui_text_entry_line.py @@ -262,15 +262,10 @@ def test_process_event_text_entered_success(self, _init_pygame: None, text_entry.focus() - processed_key_event = text_entry.process_event(pygame.event.Event(pygame.KEYDOWN, - {'key': pygame.K_d, - 'mod': 0, - 'unicode': 'd'})) + processed_key_event = text_entry.process_event(pygame.event.Event(pygame.TEXTINPUT, {'text': 'd'})) - text_entry.process_event(pygame.event.Event(pygame.KEYDOWN, {'key': pygame.K_a, 'mod': 0, - 'unicode': 'a'})) - text_entry.process_event(pygame.event.Event(pygame.KEYDOWN, {'key': pygame.K_n, 'mod': 0, - 'unicode': 'n'})) + text_entry.process_event(pygame.event.Event(pygame.TEXTINPUT, {'text': 'a'})) + text_entry.process_event(pygame.event.Event(pygame.TEXTINPUT, {'text': 'n'})) assert processed_key_event and text_entry.get_text() == 'dan' @@ -318,13 +313,10 @@ def test_process_event_text_entered_with_select_range(self, _init_pygame: None, text_entry.focus() text_entry.select_range = [1, 9] - # process a mouse button down event - processed_key_event = text_entry.process_event(pygame.event.Event(pygame.KEYDOWN, - {'key': pygame.K_o, - 'mod': 0, - 'unicode': 'o'})) + # process a text input event + processed_text_input_event = text_entry.process_event(pygame.event.Event(pygame.TEXTINPUT, {'text': 'o'})) - assert (processed_key_event is True and + assert (processed_text_input_event is True and text_entry.get_text() == 'Ho hours of fun writing tests') def test_process_event_text_entered_too_long(self, _init_pygame: None, @@ -336,18 +328,13 @@ def test_process_event_text_entered_too_long(self, _init_pygame: None, text_entry.set_text_length_limit(3) text_entry.focus() - text_entry.process_event(pygame.event.Event(pygame.KEYDOWN, {'key': pygame.K_t, 'mod': 0, - 'unicode': 't'})) - text_entry.process_event(pygame.event.Event(pygame.KEYDOWN, {'key': pygame.K_e, 'mod': 0, - 'unicode': 'e'})) - text_entry.process_event(pygame.event.Event(pygame.KEYDOWN, {'key': pygame.K_s, 'mod': 0, - 'unicode': 's'})) - processed_key_event = text_entry.process_event(pygame.event.Event(pygame.KEYDOWN, - {'key': pygame.K_s, - 'mod': 0, - 'unicode': 't'})) + text_entry.process_event(pygame.event.Event(pygame.TEXTINPUT, {'text': 't'})) + text_entry.process_event(pygame.event.Event(pygame.TEXTINPUT, {'text': 'e'})) + text_entry.process_event(pygame.event.Event(pygame.TEXTINPUT, {'text': 's'})) - assert processed_key_event is False and text_entry.get_text() == 'tes' + processed_text_input_event = text_entry.process_event(pygame.event.Event(pygame.TEXTINPUT, {'text': 't'})) + + assert processed_text_input_event is False and text_entry.get_text() == 'tes' def test_process_event_text_ctrl_c(self, _init_pygame: None, _display_surface_return_none: None): @@ -1105,17 +1092,12 @@ def test_enable(self, _init_pygame: None, default_ui_manager: UIManager, assert text_entry.is_enabled is True text_entry.focus() # process a mouse button down event - processed_key_event = text_entry.process_event(pygame.event.Event(pygame.KEYDOWN, - {'key': pygame.K_d, - 'mod': 0, - 'unicode': 'd'})) + processed_text_input_event = text_entry.process_event(pygame.event.Event(pygame.TEXTINPUT, {'text': 'd'})) - text_entry.process_event(pygame.event.Event(pygame.KEYDOWN, {'key': pygame.K_a, 'mod': 0, - 'unicode': 'a'})) - text_entry.process_event(pygame.event.Event(pygame.KEYDOWN, {'key': pygame.K_n, 'mod': 0, - 'unicode': 'n'})) + text_entry.process_event(pygame.event.Event(pygame.TEXTINPUT, {'text': 'a'})) + text_entry.process_event(pygame.event.Event(pygame.TEXTINPUT, {'text': 'n'})) - assert processed_key_event is True and text_entry.get_text() == 'dan' + assert processed_text_input_event is True and text_entry.get_text() == 'dan' def test_show(self, _init_pygame, default_ui_manager, _display_surface_return_none): text_entry = UITextEntryLine(relative_rect=pygame.Rect(100, 100, 200, 30),