system/ui: Full-Featured InputBox Component (#35178)

custom input box component
This commit is contained in:
Dean Lee
2025-05-12 22:21:12 +08:00
committed by GitHub
parent 35dbdf9124
commit 2b32325be1
3 changed files with 172 additions and 26 deletions
+12 -25
View File
@@ -1,6 +1,7 @@
import pyray as rl
from openpilot.system.ui.lib.application import gui_app
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
# Constants for special keys
@@ -47,28 +48,25 @@ class Keyboard:
def __init__(self, max_text_size: int = 255):
self._layout = keyboard_layouts["lowercase"]
self._max_text_size = max_text_size
self._string_pointer = rl.ffi.new("char[]", max_text_size)
self._input_text = ""
self._clear()
self._input_box = InputBox(max_text_size)
@property
def text(self):
result = rl.ffi.string(self._string_pointer).decode("utf-8")
self._clear()
return result
return self._input_box.text
def clear(self):
self._input_box.clear()
def render(self, title, sub_title):
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"):
self._clear()
self.clear()
return 0
# Text box for input
self._sync_string_pointer()
rl.gui_text_box(rl.Rectangle(rect.x, rect.y + 160, rect.width, 100), self._string_pointer, self._max_text_size, True)
self._input_text = rl.ffi.string(self._string_pointer).decode("utf-8")
self._input_box.render(rl.Rectangle(rect.x, rect.y + 160, rect.width, 100))
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
@@ -104,18 +102,7 @@ class Keyboard:
self._layout = keyboard_layouts["numbers"]
elif key == SYMBOL_KEY:
self._layout = keyboard_layouts["specials"]
elif key == BACKSPACE_KEY and len(self._input_text) > 0:
self._input_text = self._input_text[:-1]
elif key != BACKSPACE_KEY and len(self._input_text) < self._max_text_size:
self._input_text += key
def _clear(self):
self._input_text = ''
self._string_pointer[0] = b'\0'
def _sync_string_pointer(self):
"""Sync the C-string pointer with the internal Python string."""
encoded = self._input_text.encode("utf-8")[:self._max_text_size - 1] # Leave room for the null terminator
buffer = rl.ffi.buffer(self._string_pointer)
buffer[:len(encoded)] = encoded
self._string_pointer[len(encoded)] = b'\0' # Null terminator
elif key == BACKSPACE_KEY:
self._input_box.delete_char_before_cursor()
else:
self._input_box.add_char_at_cursor(key)
+3 -1
View File
@@ -64,7 +64,9 @@ class WifiManagerUI:
case StateNeedsAuth(network):
result = self.keyboard.render("Enter password", f"for {network.ssid}")
if result == 1:
self.connect_to_network(network, self.keyboard.text)
password = self.keyboard.text
self.keyboard.clear()
self.connect_to_network(network, password)
elif result == 0:
self.state = StateIdle()