diff --git a/system/ui/lib/inputbox.py b/system/ui/lib/inputbox.py index d933d42476..8e53446b43 100644 --- a/system/ui/lib/inputbox.py +++ b/system/ui/lib/inputbox.py @@ -62,13 +62,12 @@ class InputBox: return True return False - def render(self, rect, color=rl.LIGHTGRAY, border_color=rl.DARKGRAY, text_color=rl.BLACK, font_size=80): + def render(self, rect, color=rl.BLACK, border_color=rl.DARKGRAY, text_color=rl.WHITE, 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() @@ -81,7 +80,7 @@ class InputBox: # Display text font = gui_app.font() - display_text = "•" * len(self._input_text) if self._password_mode else self._input_text + display_text = "*" * len(self._input_text) if self._password_mode else self._input_text padding = 10 rl.draw_text_ex( font, @@ -100,7 +99,7 @@ class InputBox: 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) + rl.draw_line(int(cursor_x), int(cursor_y), int(cursor_x), int(cursor_y + cursor_height), rl.LIGHTGRAY) def _handle_mouse_input(self, rect, font_size): """Handle mouse clicks to position cursor.""" diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index b626a26b67..37580f80e5 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -1,5 +1,5 @@ import pyray as rl -from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.button import gui_button from openpilot.system.ui.lib.inputbox import InputBox from openpilot.system.ui.lib.label import gui_label @@ -45,11 +45,16 @@ keyboard_layouts = { class Keyboard: - def __init__(self, max_text_size: int = 255, min_text_size: int = 0): + def __init__(self, max_text_size: int = 255, min_text_size: int = 0, password_mode: bool = False, show_password_toggle: bool = False): self._layout = keyboard_layouts["lowercase"] self._max_text_size = max_text_size self._min_text_size = min_text_size self._input_box = InputBox(max_text_size) + self._password_mode = password_mode + self._show_password_toggle = show_password_toggle + + self._eye_open_texture = gui_app.texture("icons/eye_open.png", 81, 54) + self._eye_closed_texture = gui_app.texture("icons/eye_closed.png", 81, 54) @property def text(self): @@ -60,14 +65,17 @@ class Keyboard: def render(self, title: str, sub_title: str): rect = rl.Rectangle(CONTENT_MARGIN, CONTENT_MARGIN, gui_app.width - 2 * CONTENT_MARGIN, gui_app.height - 2 * CONTENT_MARGIN) - gui_label(rl.Rectangle(rect.x, rect.y, rect.width, 95), title, 90) - gui_label(rl.Rectangle(rect.x, rect.y + 95, rect.width, 60), sub_title, 55, rl.GRAY) - if gui_button(rl.Rectangle(rect.x + rect.width - 300, rect.y, 300, 100), "Cancel"): + gui_label(rl.Rectangle(rect.x, rect.y, rect.width, 95), title, 90, font_weight=FontWeight.BOLD) + gui_label(rl.Rectangle(rect.x, rect.y + 95, rect.width, 60), sub_title, 55, font_weight=FontWeight.NORMAL) + if gui_button(rl.Rectangle(rect.x + rect.width - 386, rect.y, 386, 125), "Cancel"): self.clear() return 0 - # Text box for input - self._input_box.render(rl.Rectangle(rect.x, rect.y + 160, rect.width, 100)) + # Draw input box and password toggle + input_margin = 25 + input_box_rect = rl.Rectangle(rect.x + input_margin, rect.y + 160, rect.width - input_margin, 100) + self._render_input_area(input_box_rect) + h_space, v_space = 15, 15 row_y_start = rect.y + 300 # Starting Y position for the first row key_height = (rect.height - 300 - 3 * v_space) / 4 @@ -95,6 +103,35 @@ class Keyboard: return -1 + def _render_input_area(self, input_rect: rl.Rectangle): + if self._show_password_toggle: + self._input_box.set_password_mode(self._password_mode) + self._input_box.render(rl.Rectangle(input_rect.x, input_rect.y, input_rect.width - 100, input_rect.height)) + + # render eye icon + eye_texture = self._eye_closed_texture if self._password_mode else self._eye_open_texture + + eye_rect = rl.Rectangle(input_rect.x + input_rect.width - 90, input_rect.y, 80, input_rect.height) + eye_x = eye_rect.x + (eye_rect.width - eye_texture.width) / 2 + eye_y = eye_rect.y + (eye_rect.height - eye_texture.height) / 2 + + rl.draw_texture_v(eye_texture, rl.Vector2(eye_x, eye_y), rl.WHITE) + + # Handle click on eye icon + if rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT) and rl.check_collision_point_rec( + rl.get_mouse_position(), eye_rect + ): + self._password_mode = not self._password_mode + else: + self._input_box.render(input_rect) + + rl.draw_line_ex( + rl.Vector2(input_rect.x, input_rect.y + input_rect.height - 2), + rl.Vector2(input_rect.x + input_rect.width, input_rect.y + input_rect.height - 2), + 3.0, # 3 pixel thickness + rl.Color(189, 189, 189, 255), + ) + def handle_key_press(self, key): if key in (SHIFT_DOWN_KEY, ABC_KEY): self._layout = keyboard_layouts["lowercase"] diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index d34ad44f55..5b9d05c96e 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -48,7 +48,7 @@ class WifiManagerUI: self.state: UIState = StateIdle() self.btn_width = 200 self.scroll_panel = GuiScrollPanel() - self.keyboard = Keyboard(max_text_size=MAX_PASSWORD_LENGTH, min_text_size=MIN_PASSWORD_LENGTH) + self.keyboard = Keyboard(max_text_size=MAX_PASSWORD_LENGTH, min_text_size=MIN_PASSWORD_LENGTH, show_password_toggle=True) self._networks: list[NetworkInfo] = []