parent
35dbdf9124
commit
2b32325be1
3 changed files with 172 additions and 26 deletions
@ -0,0 +1,157 @@ |
||||
import pyray as rl |
||||
from openpilot.system.ui.lib.application import gui_app |
||||
|
||||
|
||||
class InputBox: |
||||
def __init__(self, max_text_size=255, password_mode=False): |
||||
self._max_text_size = max_text_size |
||||
self._input_text = "" |
||||
self._cursor_position = 0 |
||||
self._password_mode = password_mode |
||||
self._blink_counter = 0 |
||||
self._show_cursor = False |
||||
self._last_key_pressed = 0 |
||||
self._key_press_time = 0 |
||||
self._repeat_delay = 30 |
||||
self._repeat_rate = 5 |
||||
|
||||
@property |
||||
def text(self): |
||||
return self._input_text |
||||
|
||||
@text.setter |
||||
def text(self, value): |
||||
self._input_text = value[: self._max_text_size] |
||||
self._cursor_position = len(self._input_text) |
||||
|
||||
def set_password_mode(self, password_mode): |
||||
self._password_mode = password_mode |
||||
|
||||
def clear(self): |
||||
self._input_text = '' |
||||
self._cursor_position = 0 |
||||
|
||||
def set_cursor_position(self, position): |
||||
"""Set the cursor position and reset the blink counter.""" |
||||
if 0 <= position <= len(self._input_text): |
||||
self._cursor_position = position |
||||
self._blink_counter = 0 |
||||
self._show_cursor = True |
||||
|
||||
def add_char_at_cursor(self, char): |
||||
"""Add a character at the current cursor position.""" |
||||
if len(self._input_text) < self._max_text_size: |
||||
self._input_text = self._input_text[: self._cursor_position] + char + self._input_text[self._cursor_position :] |
||||
self.set_cursor_position(self._cursor_position + 1) |
||||
return True |
||||
return False |
||||
|
||||
def delete_char_before_cursor(self): |
||||
"""Delete the character before the cursor position (backspace).""" |
||||
if self._cursor_position > 0: |
||||
self._input_text = self._input_text[: self._cursor_position - 1] + self._input_text[self._cursor_position :] |
||||
self.set_cursor_position(self._cursor_position - 1) |
||||
return True |
||||
return False |
||||
|
||||
def delete_char_at_cursor(self): |
||||
"""Delete the character at the cursor position (delete).""" |
||||
if self._cursor_position < len(self._input_text): |
||||
self._input_text = self._input_text[: self._cursor_position] + self._input_text[self._cursor_position + 1 :] |
||||
self.set_cursor_position(self._cursor_position) |
||||
return True |
||||
return False |
||||
|
||||
def render(self, rect, color=rl.LIGHTGRAY, border_color=rl.DARKGRAY, text_color=rl.BLACK, font_size=80): |
||||
# Handle mouse input |
||||
self._handle_mouse_input(rect, font_size) |
||||
|
||||
# Draw input box |
||||
rl.draw_rectangle_rec(rect, color) |
||||
rl.draw_rectangle_lines_ex(rect, 1, border_color) |
||||
|
||||
# Process keyboard input |
||||
self._handle_keyboard_input() |
||||
|
||||
# Update cursor blink |
||||
self._blink_counter += 1 |
||||
if self._blink_counter >= 30: |
||||
self._show_cursor = not self._show_cursor |
||||
self._blink_counter = 0 |
||||
|
||||
# Display text |
||||
font = gui_app.font() |
||||
display_text = "•" * len(self._input_text) if self._password_mode else self._input_text |
||||
padding = 10 |
||||
rl.draw_text_ex( |
||||
font, |
||||
display_text, |
||||
rl.Vector2(int(rect.x + padding), int(rect.y + rect.height / 2 - font_size / 2)), |
||||
font_size, |
||||
0, |
||||
text_color, |
||||
) |
||||
|
||||
# Draw cursor |
||||
if self._show_cursor: |
||||
cursor_x = rect.x + padding |
||||
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 |
||||
|
||||
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.BLACK) |
||||
|
||||
def _handle_mouse_input(self, rect, font_size): |
||||
"""Handle mouse clicks to position cursor.""" |
||||
mouse_pos = rl.get_mouse_position() |
||||
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(self._input_text, font_size) |
||||
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)) |
||||
|
||||
self.set_cursor_position(0) |
||||
|
||||
def _handle_keyboard_input(self): |
||||
"""Process keyboard input.""" |
||||
key = rl.get_key_pressed() |
||||
|
||||
# Handle key repeats |
||||
if key == self._last_key_pressed and key != 0: |
||||
self._key_press_time += 1 |
||||
if self._key_press_time > self._repeat_delay and self._key_press_time % self._repeat_rate == 0: |
||||
# Process repeated key |
||||
pass |
||||
else: |
||||
return # Skip processing until repeat triggers |
||||
else: |
||||
self._last_key_pressed = key |
||||
self._key_press_time = 0 |
||||
|
||||
# Handle navigation keys |
||||
if key == rl.KEY_LEFT: |
||||
if self._cursor_position > 0: |
||||
self.set_cursor_position(self._cursor_position - 1) |
||||
elif key == rl.KEY_RIGHT: |
||||
if self._cursor_position < len(self._input_text): |
||||
self.set_cursor_position(self._cursor_position + 1) |
||||
elif key == rl.KEY_BACKSPACE: |
||||
self.delete_char_before_cursor() |
||||
elif key == rl.KEY_DELETE: |
||||
self.delete_char_at_cursor() |
||||
elif key == rl.KEY_HOME: |
||||
self.set_cursor_position(0) |
||||
elif key == rl.KEY_END: |
||||
self.set_cursor_position(len(self._input_text)) |
||||
|
||||
# Handle text input |
||||
char = rl.get_char_pressed() |
||||
if char != 0 and char >= 32: # Filter out control characters |
||||
self.add_char_at_cursor(chr(char)) |
Loading…
Reference in new issue