system/ui: add text scrolling support to InputBox for long text (#35310)

* add text scrolling support to InputBox for long text

* add 2 pixels buffer to the scissor region
pull/35313/head
Dean Lee 4 days ago committed by GitHub
parent 7ea8277e39
commit 4c9655b5e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 68
      system/ui/lib/inputbox.py

@ -14,6 +14,8 @@ class InputBox:
self._key_press_time = 0
self._repeat_delay = 30
self._repeat_rate = 4
self._text_offset = 0
self._visible_width = 0
@property
def text(self):
@ -23,6 +25,7 @@ class InputBox:
def text(self, value):
self._input_text = value[: self._max_text_size]
self._cursor_position = len(self._input_text)
self._update_text_offset()
def set_password_mode(self, password_mode):
self._password_mode = password_mode
@ -30,6 +33,7 @@ class InputBox:
def clear(self):
self._input_text = ''
self._cursor_position = 0
self._text_offset = 0
def set_cursor_position(self, position):
"""Set the cursor position and reset the blink counter."""
@ -37,6 +41,29 @@ class InputBox:
self._cursor_position = position
self._blink_counter = 0
self._show_cursor = True
self._update_text_offset()
def _update_text_offset(self):
"""Ensure the cursor is visible by adjusting text offset."""
if self._visible_width == 0:
return
font = gui_app.font()
display_text = "*" * len(self._input_text) if self._password_mode else self._input_text
padding = 10
if self._cursor_position > 0:
cursor_x = rl.measure_text_ex(font, display_text[: self._cursor_position], self._font_size, 0).x
else:
cursor_x = 0
visible_width = self._visible_width - (padding * 2)
# Adjust offset if cursor would be outside visible area
if cursor_x < self._text_offset:
self._text_offset = max(0, cursor_x - padding)
elif cursor_x > self._text_offset + visible_width:
self._text_offset = cursor_x - visible_width + padding
def add_char_at_cursor(self, char):
"""Add a character at the current cursor position."""
@ -63,6 +90,10 @@ class InputBox:
return False
def render(self, rect, color=rl.BLACK, border_color=rl.DARKGRAY, text_color=rl.WHITE, font_size=80):
# Store dimensions for text offset calculations
self._visible_width = rect.width
self._font_size = font_size
# Handle mouse input
self._handle_mouse_input(rect, font_size)
@ -82,10 +113,14 @@ class InputBox:
font = gui_app.font()
display_text = "*" * len(self._input_text) if self._password_mode else self._input_text
padding = 10
# Clip text within input box bounds
buffer = 2
rl.begin_scissor_mode(int(rect.x + padding - buffer), int(rect.y), int(rect.width - padding * 2 + buffer * 2), int(rect.height))
rl.draw_text_ex(
font,
display_text,
rl.Vector2(int(rect.x + padding), int(rect.y + rect.height / 2 - font_size / 2)),
rl.Vector2(int(rect.x + padding - self._text_offset), int(rect.y + rect.height / 2 - font_size / 2)),
font_size,
0,
text_color,
@ -97,9 +132,14 @@ class InputBox:
if len(display_text) > 0 and self._cursor_position > 0:
cursor_x += rl.measure_text_ex(font, display_text[: self._cursor_position], font_size, 0).x
# Apply text offset to cursor position
cursor_x -= self._text_offset
cursor_height = font_size + 4
cursor_y = rect.y + rect.height / 2 - cursor_height / 2
rl.draw_line(int(cursor_x), int(cursor_y), int(cursor_x), int(cursor_y + cursor_height), rl.LIGHTGRAY)
rl.draw_line(int(cursor_x), int(cursor_y), int(cursor_x), int(cursor_y + cursor_height), rl.WHITE)
rl.end_scissor_mode()
def _handle_mouse_input(self, rect, font_size):
"""Handle mouse clicks to position cursor."""
@ -107,14 +147,22 @@ class InputBox:
if rl.is_mouse_button_pressed(rl.MOUSE_LEFT_BUTTON) and rl.check_collision_point_rec(mouse_pos, rect):
# Calculate cursor position from click
if len(self._input_text) > 0:
text_width = rl.measure_text_ex(gui_app.font(), self._input_text, font_size, 0).x
text_pos_x = rect.x + 10
if mouse_pos.x - text_pos_x > text_width:
self.set_cursor_position(len(self._input_text))
else:
click_ratio = (mouse_pos.x - text_pos_x) / text_width
self.set_cursor_position(int(len(self._input_text) * click_ratio))
font = gui_app.font()
display_text = "*" * len(self._input_text) if self._password_mode else self._input_text
# Find the closest character position to the click
relative_x = mouse_pos.x - (rect.x + 10) + self._text_offset
best_pos = 0
min_distance = float('inf')
for i in range(len(self._input_text) + 1):
char_width = rl.measure_text_ex(font, display_text[:i], font_size, 0).x
distance = abs(relative_x - char_width)
if distance < min_distance:
min_distance = distance
best_pos = i
self.set_cursor_position(best_pos)
else:
self.set_cursor_position(0)

Loading…
Cancel
Save