|
|
|
@ -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 |
|
|
|
|
font = gui_app.font() |
|
|
|
|
display_text = "*" * len(self._input_text) if self._password_mode else self._input_text |
|
|
|
|
|
|
|
|
|
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)) |
|
|
|
|
# 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) |
|
|
|
|
|
|
|
|
|