class LUIInputField(LUIObject): """ Simple input field, accepting text input. This input field supports entering text and navigating. Selecting text is (currently) not supported. The input field also supports various keyboard shortcuts: [pos1] Move to the beginning of the text [end] Move to the end of the text [arrow_left] Move one character to the left [arrow_right] Move one character to the right [ctrl] + [arrow_left] Move to the left, skipping over words [ctrl] + [arrow_right] Move to the right, skipping over words [escape] Un-focus input element """ re_skip = re.compile("\W*\w+\W") def __init__(self, parent=None, width=200, placeholder=u"Enter some text ..", value=u"", **kwargs): """ Constructs a new input field. An input field always needs a width specified """ LUIObject.__init__(self, x=0, y=0, solid=True) self.set_width(width) self._layout = LUIHorizontalStretchedLayout(parent=self, prefix="InputField", width="100%") # Container for the text self._text_content = LUIObject(self) self._text_content.margin = (5, 7, 5, 7) self._text_content.clip_bounds = (0, 0, 0, 0) self._text_content.set_size("100%", "100%") # Scroller for the text, so we can move right and left self._text_scroller = LUIObject(parent=self._text_content) self._text_scroller.center_vertical = True self._text = LUILabel(parent=self._text_scroller, text=u"") # Cursor for the current position self._cursor = LUISprite(self._text_scroller, "blank", "skin", x=0, y=0, w=2, h=15) self._cursor.color = (0.5, 0.5, 0.5) self._cursor.margin.top = 2 self._cursor.z_offset = 20 self._cursor_index = 0 self._cursor.hide() self._value = value # Placeholder text, shown when out of focus and no value exists self._placeholder = LUILabel(parent=self._text_content, text=placeholder, shadow=False, center_vertical=True, alpha=0.2) # Various states self._tickrate = 1.0 self._tickstart = 0.0 self._render_text() if parent is not None: self.parent = parent LUIInitialState.init(self, kwargs) @property def value(self): """ Returns the value of the input field """ return self._value @value.setter def value(self, new_value): """ Sets the value of the input field """ if sys.version_info[0] < 3: self._value = unicode(new_value) else: self._value = str(new_value) self._render_text() self.trigger_event("changed", self._value) def clear(self): """ Clears the input value """ self.value = u"" @property def cursor_pos(self): """ Set the cursor position """ return self._cursor_index @cursor_pos.setter def cursor_pos(self, pos): """ Set the cursor position """ if pos >= 0: self._cursor_index = max(0, min(len(self._value), pos)) else: self._cursor_index = max(len(self._value) + pos + 1, 0) self._reset_cursor_tick() self._render_text() def on_tick(self, event): """ Tick handler, gets executed every frame """ frame_time = globalClock.get_frame_time() - self._tickstart show_cursor = frame_time % self._tickrate < 0.5 * self._tickrate if show_cursor: self._cursor.color = (0.5, 0.5, 0.5, 1) else: self._cursor.color = (1, 1, 1, 0) def on_click(self, event): """ Internal on click handler """ self.request_focus() def on_mousedown(self, event): """ Internal mousedown handler """ local_x_offset = self._text.text_handle.get_relative_pos( event.coordinates).x self.cursor_pos = self._text.text_handle.get_char_index(local_x_offset) def _reset_cursor_tick(self): """ Internal method to reset the cursor tick """ self._tickstart = globalClock.get_frame_time() def on_focus(self, event): """ Internal focus handler """ self._cursor.show() self._placeholder.hide() self._reset_cursor_tick() self._layout.color = (0.9, 0.9, 0.9, 1) def on_keydown(self, event): """ Internal keydown handler. Processes the special keys, and if none are present, redirects the event """ key_name = event.message if key_name == "backspace": self._value = self._value[:max(0, self._cursor_index - 1)] + self._value[self. _cursor_index:] self.cursor_pos -= 1 self.trigger_event("changed", self._value) elif key_name == "delete": post_value = self._value[min(len(self._value), self._cursor_index + 1):] self._value = self._value[:self._cursor_index] + post_value self.cursor_pos = self._cursor_index self.trigger_event("changed", self._value) elif key_name == "arrow_left": if event.get_modifier_state("alt") or event.get_modifier_state( "ctrl"): self.cursor_skip_left() else: self.cursor_pos -= 1 elif key_name == "arrow_right": if event.get_modifier_state("alt") or event.get_modifier_state( "ctrl"): self.cursor_skip_right() else: self.cursor_pos += 1 elif key_name == "escape": self.blur() elif key_name == "home": self.cursor_pos = 0 elif key_name == "end": self.cursor_pos = len(self.value) self.trigger_event(key_name, self._value) def on_keyrepeat(self, event): """ Internal keyrepeat handler """ self.on_keydown(event) def on_textinput(self, event): """ Internal textinput handler """ self._value = self._value[:self._cursor_index] + event.message + \ self._value[self._cursor_index:] self.cursor_pos = self._cursor_index + len(event.message) self.trigger_event("changed", self._value) def on_blur(self, event): """ Internal blur handler """ self._cursor.hide() if len(self._value) < 1: self._placeholder.show() self._layout.color = (1, 1, 1, 1) def _render_text(self): """ Internal method to render the text """ self._text.set_text(self._value) self._cursor.left = self._text.left + \ self._text.text_handle.get_char_pos(self._cursor_index) + 1 max_left = self.width - 15 if self._value: self._placeholder.hide() else: if not self.focused: self._placeholder.show() # Scroll if the cursor is outside of the clip bounds rel_pos = self.get_relative_pos(self._cursor.get_abs_pos()).x if rel_pos >= max_left: self._text_scroller.left = min(0, max_left - self._cursor.left) if rel_pos <= 0: self._text_scroller.left = min(0, -self._cursor.left - rel_pos) def cursor_skip_left(self): """ Moves the cursor to the left, skipping the previous word """ left_hand_str = ''.join(reversed(self.value[0:self.cursor_pos])) match = self.re_skip.match(left_hand_str) if match is not None: self.cursor_pos -= match.end() - 1 else: self.cursor_pos = 0 def cursor_skip_right(self): """ Moves the cursor to the right, skipping the next word """ right_hand_str = self.value[self.cursor_pos:] match = self.re_skip.match(right_hand_str) if match is not None: self.cursor_pos += match.end() - 1 else: self.cursor_pos = len(self.value)
class LUIInputField(LUIObject, LUICallback): def __init__(self, parent=None, width=200, placeholder=u"Enter some text ..", value=u""): LUIObject.__init__(self, x=0, y=0, w=width, h=0) LUICallback.__init__(self) self.bgLeft = LUISprite(self, "InputField_Left", "skin") self.bgMid = LUISprite(self, "InputField", "skin") self.bgRight = LUISprite(self, "InputField_Right", "skin") self.textContent = LUIObject(self) self.textContent.margin = (5, 8, 5, 8) self.textContent.clip_bounds = (0, 0, 0, 0) self.textContent.height = self.bgMid.height - 10 self.textContent.width = self.width - 16 self.textScroller = LUIObject(parent=self.textContent, x=0, y=0) self.text = LUILabel(parent=self.textScroller, text=u"", shadow=True) self.cursor = LUISprite(self.textScroller, "blank", "skin", x=0, y=0, w=2, h=15.0) self.cursor.color = (0.5, 0.5, 0.5) self.cursor.margin_top = 3 self.cursor.z_offset = 20 self.cursor_index = 0 self.cursor.hide() self.value = value self.placeholder = LUILabel(parent=self.textContent, text=placeholder, shadow=False) self.placeholder.color = (1, 1, 1, 0.5) self.bgMid.width = self.width - self.bgLeft.width - self.bgRight.width self.bgMid.left = self.bgLeft.width self.bgRight.left = self.bgMid.width + self.bgMid.left if len(self.value) > 0: self.placeholder.hide() self.tickrate = 1.0 self.tickstart = 0.0 self.fit_to_children() self._render_text() if parent is not None: self.parent = parent def _set_cursor_pos(self, pos): self.cursor_index = max(0, min(len(self.value), pos)) self._reset_cursor_tick() def on_tick(self, event): frametime = globalClock.getFrameTime() - self.tickstart show_cursor = frametime % self.tickrate < 0.5 * self.tickrate if show_cursor: self.cursor.color = (0.5, 0.5, 0.5, 1) else: self.cursor.color = (1, 1, 1, 0) def _add_text(self, text): self.value = self.value[: self.cursor_index] + text + self.value[self.cursor_index :] self._set_cursor_pos(self.cursor_index + len(text)) self._render_text() def on_click(self, event): self.request_focus() def on_mousedown(self, event): local_x_offset = self.text.text.get_relative_pos(event.coordinates).x self._set_cursor_pos(self.text.text.get_char_index(local_x_offset)) self._render_text() def _reset_cursor_tick(self): self.tickstart = globalClock.getFrameTime() def on_focus(self, event): self.cursor.show() self.placeholder.hide() self._reset_cursor_tick() self.bgLeft.color = (0.9, 0.9, 0.9, 1) self.bgMid.color = (0.9, 0.9, 0.9, 1) self.bgRight.color = (0.9, 0.9, 0.9, 1) def on_keydown(self, event): key_name = event.get_message() if key_name == "backspace": self.value = self.value[: max(0, self.cursor_index - 1)] + self.value[self.cursor_index :] self._set_cursor_pos(self.cursor_index - 1) self._trigger_callback(self.value) self._render_text() elif key_name == "delete": self.value = self.value[: self.cursor_index] + self.value[min(len(self.value), self.cursor_index + 1) :] self._set_cursor_pos(self.cursor_index) self._trigger_callback(self.value) self._render_text() elif key_name == "arrow_left": self._set_cursor_pos(self.cursor_index - 1) self._render_text() elif key_name == "arrow_right": self._set_cursor_pos(self.cursor_index + 1) self._render_text() def on_keyrepeat(self, event): self.on_keydown(event) def on_textinput(self, event): self._add_text(event.get_message()) self._trigger_callback(self.value) def on_blur(self, event): self.cursor.hide() if len(self.value) < 1: self.placeholder.show() self.bgLeft.color = (1, 1, 1, 1) self.bgMid.color = (1, 1, 1, 1) self.bgRight.color = (1, 1, 1, 1) def get_value(self): return self.value def set_value(self, value): self.value = value # QUESTION: Should we trigger a callback when the user changes the value hisself? self._trigger_callback(self.value) self._render_text() def _render_text(self): self.text.set_text(self.value) self.cursor.left = self.text.left + self.text.text.get_char_pos(self.cursor_index) + 1 max_left = self.width - 20 # Scroll if the cursor is outside of the clip bounds relX = self.get_relative_pos(self.cursor.get_abs_pos()).x if relX >= max_left: self.textScroller.left = min(0, max_left - self.cursor.left) if relX <= 0: self.textScroller.left = min(0, -self.cursor.left - relX)
class LUIInputField(LUIObject): """ Simple input field """ def __init__(self, parent=None, width=200, placeholder=u"Enter some text ..", value=u"", **kwargs): """ Constructs a new input field. An input field always needs a width specified """ LUIObject.__init__(self, x=0, y=0, solid=True) self.set_width(width) self._layout = LUIHorizontalStretchedLayout(parent=self, prefix="InputField", width="100%") # Container for the text self._text_content = LUIObject(self) self._text_content.margin = (5, 7, 5, 7) self._text_content.clip_bounds = (0,0,0,0) self._text_content.set_size("100%", "100%") # Scroller for the text, so we can move right and left self._text_scroller = LUIObject(parent=self._text_content) self._text_scroller.center_vertical = True self._text = LUILabel(parent=self._text_scroller, text=u"") # Cursor for the current position self._cursor = LUISprite(self._text_scroller, "blank", "skin", x=0, y=0, w=2, h=15) self._cursor.color = (0.5, 0.5, 0.5) self._cursor.margin.top = 2 self._cursor.z_offset = 20 self._cursor_index = 0 self._cursor.hide() self._value = value # Placeholder text, shown when out of focus and no value exists self._placeholder = LUILabel(parent=self._text_content, text=placeholder, shadow=False, center_vertical=True, alpha=0.2) # Various states self._tickrate = 1.0 self._tickstart = 0.0 self._render_text() if parent is not None: self.parent = parent LUIInitialState.init(self, kwargs) def get_value(self): """ Returns the value of the input field """ return self._value def set_value(self, value): """ Sets the value of the input field """ self._value = unicode(value) self.trigger_event("changed", self._value) self._render_text() value = property(get_value, set_value) def clear(self): """ Clears the input value """ self.value = u"" def _set_cursor_pos(self, pos): """ Internal method to set the cursor position """ self._cursor_index = max(0, min(len(self._value), pos)) self._reset_cursor_tick() def on_tick(self, event): """ Tick handler, gets executed every frame """ frametime = globalClock.get_frame_time() - self._tickstart show_cursor = frametime % self._tickrate < 0.5 * self._tickrate if show_cursor: self._cursor.color = (0.5,0.5,0.5,1) else: self._cursor.color = (1,1,1,0) def _add_text(self, text): """ Internal method to append text """ self._value = self._value[:self._cursor_index] + text + self._value[self._cursor_index:] self._set_cursor_pos(self._cursor_index + len(text)) self._render_text() def on_click(self, event): """ Internal on click handler """ self.request_focus() def on_mousedown(self, event): """ Internal mousedown handler """ local_x_offset = self._text.text_handle.get_relative_pos(event.coordinates).x self._set_cursor_pos(self._text.text_handle.get_char_index(local_x_offset)) self._render_text() def _reset_cursor_tick(self): """ Internal method to reset the cursor tick """ self._tickstart = globalClock.getFrameTime() def on_focus(self, event): """ Internal focus handler """ self._cursor.show() self._placeholder.hide() self._reset_cursor_tick() self._layout.color = (0.9,0.9,0.9,1) def on_keydown(self, event): """ Internal keydown handler """ key_name = event.message if key_name == "backspace": self._value = self._value[:max(0, self._cursor_index - 1)] + self._value[self._cursor_index:] self._set_cursor_pos(self._cursor_index - 1) self.trigger_event("changed", self._value) self._render_text() elif key_name == "delete": self._value = self._value[:self._cursor_index] + self._value[min(len(self._value), self._cursor_index + 1):] self._set_cursor_pos(self._cursor_index) self.trigger_event("changed", self._value) self._render_text() elif key_name == "arrow_left": self._set_cursor_pos(self._cursor_index - 1) self._render_text() elif key_name == "arrow_right": self._set_cursor_pos(self._cursor_index + 1) self._render_text() self.trigger_event(key_name, self._value) def on_keyrepeat(self, event): """ Internal keyrepeat handler """ self.on_keydown(event) def on_textinput(self, event): """ Internal textinput handler """ self._add_text(event.message) self.trigger_event("changed", self._value) def on_blur(self, event): """ Internal blur handler """ self._cursor.hide() if len(self._value) < 1: self._placeholder.show() self._layout.color = (1,1,1,1) def _render_text(self): """ Internal method to render the text """ self._text.set_text(self._value) self._cursor.left = self._text.left + self._text.text_handle.get_char_pos(self._cursor_index) + 1 max_left = self.width - 15 if self._value: self._placeholder.hide() else: if not self.focused: self._placeholder.show() # Scroll if the cursor is outside of the clip bounds rel_pos = self.get_relative_pos(self._cursor.get_abs_pos()).x if rel_pos >= max_left: self._text_scroller.left = min(0, max_left - self._cursor.left) if rel_pos <= 0: self._text_scroller.left = min(0, - self._cursor.left - rel_pos)
class LUIInputField(LUIObject): """ Simple input field, accepting text input. This input field supports entering text and navigating. Selecting text is (currently) not supported. The input field also supports various keyboard shortcuts: [pos1] Move to the beginning of the text [end] Move to the end of the text [arrow_left] Move one character to the left [arrow_right] Move one character to the right [ctrl] + [arrow_left] Move to the left, skipping over words [ctrl] + [arrow_right] Move to the right, skipping over words [escape] Un-focus input element """ re_skip = re.compile("\W*\w+\W") def __init__(self, parent=None, width=200, placeholder=u"Enter some text ..", value=u"", **kwargs): """ Constructs a new input field. An input field always needs a width specified """ LUIObject.__init__(self, x=0, y=0, solid=True) self.set_width(width) self._layout = LUIHorizontalStretchedLayout(parent=self, prefix="InputField", width="100%") # Container for the text self._text_content = LUIObject(self) self._text_content.margin = (5, 7, 5, 7) self._text_content.clip_bounds = (0,0,0,0) self._text_content.set_size("100%", "100%") # Scroller for the text, so we can move right and left self._text_scroller = LUIObject(parent=self._text_content) self._text_scroller.center_vertical = True self._text = LUILabel(parent=self._text_scroller, text=u"") # Cursor for the current position self._cursor = LUISprite(self._text_scroller, "blank", "skin", x=0, y=0, w=2, h=15) self._cursor.color = (0.5, 0.5, 0.5) self._cursor.margin.top = 2 self._cursor.z_offset = 20 self._cursor_index = 0 self._cursor.hide() self._value = value # Placeholder text, shown when out of focus and no value exists self._placeholder = LUILabel(parent=self._text_content, text=placeholder, shadow=False, center_vertical=True, alpha=0.2) # Various states self._tickrate = 1.0 self._tickstart = 0.0 self._render_text() if parent is not None: self.parent = parent LUIInitialState.init(self, kwargs) @property def value(self): """ Returns the value of the input field """ return self._value @value.setter def value(self, new_value): """ Sets the value of the input field """ self._value = unicode(new_value) self._render_text() self.trigger_event("changed", self._value) def clear(self): """ Clears the input value """ self.value = u"" @property def cursor_pos(self): """ Set the cursor position """ return self._cursor_index @cursor_pos.setter def cursor_pos(self, pos): """ Set the cursor position """ if pos >= 0: self._cursor_index = max(0, min(len(self._value), pos)) else: self._cursor_index = max(len(self._value) + pos + 1, 0) self._reset_cursor_tick() self._render_text() def on_tick(self, event): """ Tick handler, gets executed every frame """ frame_time = globalClock.get_frame_time() - self._tickstart show_cursor = frame_time % self._tickrate < 0.5 * self._tickrate if show_cursor: self._cursor.color = (0.5, 0.5, 0.5, 1) else: self._cursor.color = (1, 1, 1, 0) def on_click(self, event): """ Internal on click handler """ self.request_focus() def on_mousedown(self, event): """ Internal mousedown handler """ local_x_offset = self._text.text_handle.get_relative_pos(event.coordinates).x self.cursor_pos = self._text.text_handle.get_char_index(local_x_offset) def _reset_cursor_tick(self): """ Internal method to reset the cursor tick """ self._tickstart = globalClock.get_frame_time() def on_focus(self, event): """ Internal focus handler """ self._cursor.show() self._placeholder.hide() self._reset_cursor_tick() self._layout.color = (0.9, 0.9, 0.9, 1) def on_keydown(self, event): """ Internal keydown handler. Processes the special keys, and if none are present, redirects the event """ key_name = event.message if key_name == "backspace": self._value = self._value[:max(0, self._cursor_index - 1)] + self._value[self._cursor_index:] self.cursor_pos -= 1 self.trigger_event("changed", self._value) elif key_name == "delete": post_value = self._value[min(len(self._value), self._cursor_index + 1):] self._value = self._value[:self._cursor_index] + post_value self.cursor_pos = self._cursor_index self.trigger_event("changed", self._value) elif key_name == "arrow_left": if event.get_modifier_state("alt") or event.get_modifier_state("ctrl"): self.cursor_skip_left() else: self.cursor_pos -= 1 elif key_name == "arrow_right": if event.get_modifier_state("alt") or event.get_modifier_state("ctrl"): self.cursor_skip_right() else: self.cursor_pos += 1 elif key_name == "escape": self.blur() elif key_name == "home": self.cursor_pos = 0 elif key_name == "end": self.cursor_pos = len(self.value) self.trigger_event(key_name, self._value) def on_keyrepeat(self, event): """ Internal keyrepeat handler """ self.on_keydown(event) def on_textinput(self, event): """ Internal textinput handler """ self._value = self._value[:self._cursor_index] + event.message + \ self._value[self._cursor_index:] self.cursor_pos = self._cursor_index + len(event.message) self.trigger_event("changed", self._value) def on_blur(self, event): """ Internal blur handler """ self._cursor.hide() if len(self._value) < 1: self._placeholder.show() self._layout.color = (1, 1, 1, 1) def _render_text(self): """ Internal method to render the text """ self._text.set_text(self._value) self._cursor.left = self._text.left + \ self._text.text_handle.get_char_pos(self._cursor_index) + 1 max_left = self.width - 15 if self._value: self._placeholder.hide() else: if not self.focused: self._placeholder.show() # Scroll if the cursor is outside of the clip bounds rel_pos = self.get_relative_pos(self._cursor.get_abs_pos()).x if rel_pos >= max_left: self._text_scroller.left = min(0, max_left - self._cursor.left) if rel_pos <= 0: self._text_scroller.left = min(0, - self._cursor.left - rel_pos) def cursor_skip_left(self): """ Moves the cursor to the left, skipping the previous word """ left_hand_str = ''.join(reversed(self.value[0:self.cursor_pos])) match = self.re_skip.match(left_hand_str) if match is not None: self.cursor_pos -= match.end() - 1 else: self.cursor_pos = 0 def cursor_skip_right(self): """ Moves the cursor to the right, skipping the next word """ right_hand_str = self.value[self.cursor_pos:] match = self.re_skip.match(right_hand_str) if match is not None: self.cursor_pos += match.end() - 1 else: self.cursor_pos = len(self.value)
class LUIInputField(LUIObject): """ Simple input field """ def __init__(self, parent=None, width=200, placeholder=u"Enter some text ..", value=u"", **kwargs): """ Constructs a new input field. An input field always needs a width specified """ LUIObject.__init__(self, x=0, y=0, solid=True) self.set_width(width) self._layout = LUIHorizontalStretchedLayout(parent=self, prefix="InputField", width="100%") # Container for the text self._text_content = LUIObject(self) self._text_content.margin = (5, 7, 5, 7) self._text_content.clip_bounds = (0, 0, 0, 0) self._text_content.set_size("100%", "100%") # Scroller for the text, so we can move right and left self._text_scroller = LUIObject(parent=self._text_content) self._text_scroller.center_vertical = True self._text = LUILabel(parent=self._text_scroller, text=u"") # Cursor for the current position self._cursor = LUISprite(self._text_scroller, "blank", "skin", x=0, y=0, w=2, h=15) self._cursor.color = (0.5, 0.5, 0.5) self._cursor.margin.top = 2 self._cursor.z_offset = 20 self._cursor_index = 0 self._cursor.hide() self._value = value # Placeholder text, shown when out of focus and no value exists self._placeholder = LUILabel(parent=self._text_content, text=placeholder, shadow=False, center_vertical=True, alpha=0.2) # Various states self._tickrate = 1.0 self._tickstart = 0.0 self._render_text() if parent is not None: self.parent = parent LUIInitialState.init(self, kwargs) def get_value(self): """ Returns the value of the input field """ return self._value def set_value(self, value): """ Sets the value of the input field """ self._value = unicode(value) self.trigger_event("changed", self._value) self._render_text() value = property(get_value, set_value) def clear(self): """ Clears the input value """ self.value = u"" def _set_cursor_pos(self, pos): """ Internal method to set the cursor position """ self._cursor_index = max(0, min(len(self._value), pos)) self._reset_cursor_tick() def on_tick(self, event): """ Tick handler, gets executed every frame """ frametime = globalClock.get_frame_time() - self._tickstart show_cursor = frametime % self._tickrate < 0.5 * self._tickrate if show_cursor: self._cursor.color = (0.5, 0.5, 0.5, 1) else: self._cursor.color = (1, 1, 1, 0) def _add_text(self, text): """ Internal method to append text """ self._value = self._value[:self._cursor_index] + text + self._value[ self._cursor_index:] self._set_cursor_pos(self._cursor_index + len(text)) self._render_text() def on_click(self, event): """ Internal on click handler """ self.request_focus() def on_mousedown(self, event): """ Internal mousedown handler """ local_x_offset = self._text.text_handle.get_relative_pos( event.coordinates).x self._set_cursor_pos( self._text.text_handle.get_char_index(local_x_offset)) self._render_text() def _reset_cursor_tick(self): """ Internal method to reset the cursor tick """ self._tickstart = globalClock.getFrameTime() def on_focus(self, event): """ Internal focus handler """ self._cursor.show() self._placeholder.hide() self._reset_cursor_tick() self._layout.color = (0.9, 0.9, 0.9, 1) def on_keydown(self, event): """ Internal keydown handler """ key_name = event.message if key_name == "backspace": self._value = self._value[:max(0, self._cursor_index - 1)] + self._value[self. _cursor_index:] self._set_cursor_pos(self._cursor_index - 1) self.trigger_event("changed", self._value) self._render_text() elif key_name == "delete": self._value = self._value[:self._cursor_index] + self._value[ min(len(self._value), self._cursor_index + 1):] self._set_cursor_pos(self._cursor_index) self.trigger_event("changed", self._value) self._render_text() elif key_name == "arrow_left": self._set_cursor_pos(self._cursor_index - 1) self._render_text() elif key_name == "arrow_right": self._set_cursor_pos(self._cursor_index + 1) self._render_text() self.trigger_event(key_name, self._value) def on_keyrepeat(self, event): """ Internal keyrepeat handler """ self.on_keydown(event) def on_textinput(self, event): """ Internal textinput handler """ self._add_text(event.message) self.trigger_event("changed", self._value) def on_blur(self, event): """ Internal blur handler """ self._cursor.hide() if len(self._value) < 1: self._placeholder.show() self._layout.color = (1, 1, 1, 1) def _render_text(self): """ Internal method to render the text """ self._text.set_text(self._value) self._cursor.left = self._text.left + self._text.text_handle.get_char_pos( self._cursor_index) + 1 max_left = self.width - 15 if self._value: self._placeholder.hide() else: if not self.focused: self._placeholder.show() # Scroll if the cursor is outside of the clip bounds rel_pos = self.get_relative_pos(self._cursor.get_abs_pos()).x if rel_pos >= max_left: self._text_scroller.left = min(0, max_left - self._cursor.left) if rel_pos <= 0: self._text_scroller.left = min(0, -self._cursor.left - rel_pos)