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_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 ) )
else :
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 ) )