mirror of
https://github.com/MoreTore/openpilot.git
synced 2026-08-05 16:26:14 +08:00
raylib: common mouse press hook (#35489)
* something like this * need these * rest * another pr * what is this merge conflict f * fix mouse down * rm that! * fix that * rearrange * fix bug where mouse held down on widget, dragged off, then let go * temp * fix that * missing init
This commit is contained in:
@@ -26,6 +26,7 @@ class HomeLayoutState(IntEnum):
|
||||
|
||||
class HomeLayout(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.params = Params()
|
||||
|
||||
self.update_alert = UpdateAlert()
|
||||
@@ -58,7 +59,7 @@ class HomeLayout(Widget):
|
||||
def _set_state(self, state: HomeLayoutState):
|
||||
self.current_state = state
|
||||
|
||||
def render(self, rect: rl.Rectangle):
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
self._update_layout_rects(rect)
|
||||
|
||||
current_time = time.time()
|
||||
|
||||
@@ -16,6 +16,7 @@ class MainState(IntEnum):
|
||||
|
||||
class MainLayout(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._sidebar = Sidebar()
|
||||
self._sidebar_visible = True
|
||||
self._current_mode = MainState.HOME
|
||||
@@ -32,7 +33,7 @@ class MainLayout(Widget):
|
||||
# Set callbacks
|
||||
self._setup_callbacks()
|
||||
|
||||
def render(self, rect):
|
||||
def _render(self, rect):
|
||||
self._current_callback = None
|
||||
|
||||
self._update_layout_rects(rect)
|
||||
|
||||
@@ -6,10 +6,11 @@ from openpilot.system.ui.widgets.network import WifiManagerUI
|
||||
|
||||
class NetworkLayout(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.wifi_manager = WifiManagerWrapper()
|
||||
self.wifi_ui = WifiManagerUI(self.wifi_manager)
|
||||
|
||||
def render(self, rect: rl.Rectangle):
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
self.wifi_ui.render(rect)
|
||||
|
||||
def shutdown(self):
|
||||
|
||||
@@ -14,6 +14,7 @@ DESCRIPTIONS = {
|
||||
|
||||
class DeveloperLayout(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._params = Params()
|
||||
items = [
|
||||
toggle_item(
|
||||
@@ -44,7 +45,7 @@ class DeveloperLayout(Widget):
|
||||
|
||||
self._list_widget = ListView(items)
|
||||
|
||||
def render(self, rect):
|
||||
def _render(self, rect):
|
||||
self._list_widget.render(rect)
|
||||
|
||||
def _on_enable_adb(self): pass
|
||||
|
||||
@@ -17,6 +17,7 @@ DESCRIPTIONS = {
|
||||
|
||||
class DeviceLayout(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
params = Params()
|
||||
dongle_id = params.get("DongleId", encoding="utf-8") or "N/A"
|
||||
serial = params.get("HardwareSerial") or "N/A"
|
||||
@@ -37,7 +38,7 @@ class DeviceLayout(Widget):
|
||||
|
||||
self._list_widget = ListView(items)
|
||||
|
||||
def render(self, rect):
|
||||
def _render(self, rect):
|
||||
self._list_widget.render(rect)
|
||||
|
||||
def _on_pair_device(self): pass
|
||||
|
||||
@@ -50,6 +50,7 @@ class PanelInfo:
|
||||
|
||||
class SettingsLayout(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._params = Params()
|
||||
self._current_panel = PanelType.DEVICE
|
||||
self._max_scroll = 0.0
|
||||
@@ -73,7 +74,7 @@ class SettingsLayout(Widget):
|
||||
def set_callbacks(self, on_close: Callable):
|
||||
self._close_callback = on_close
|
||||
|
||||
def render(self, rect: rl.Rectangle):
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
# Calculate layout
|
||||
sidebar_rect = rl.Rectangle(rect.x, rect.y, SIDEBAR_WIDTH, rect.height)
|
||||
panel_rect = rl.Rectangle(rect.x + SIDEBAR_WIDTH, rect.y, rect.width - SIDEBAR_WIDTH, rect.height)
|
||||
@@ -82,9 +83,6 @@ class SettingsLayout(Widget):
|
||||
self._draw_sidebar(sidebar_rect)
|
||||
self._draw_current_panel(panel_rect)
|
||||
|
||||
if rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT):
|
||||
self.handle_mouse_release(rl.get_mouse_position())
|
||||
|
||||
def _draw_sidebar(self, rect: rl.Rectangle):
|
||||
rl.draw_rectangle_rec(rect, SIDEBAR_COLOR)
|
||||
|
||||
@@ -154,7 +152,7 @@ class SettingsLayout(Widget):
|
||||
alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE,
|
||||
)
|
||||
|
||||
def handle_mouse_release(self, mouse_pos: rl.Vector2) -> bool:
|
||||
def _handle_mouse_release(self, mouse_pos: rl.Vector2) -> bool:
|
||||
# Check close button
|
||||
if rl.check_collision_point_rec(mouse_pos, self._close_btn_rect):
|
||||
if self._close_callback:
|
||||
|
||||
@@ -4,6 +4,7 @@ from openpilot.system.ui.lib.list_view import ListView, button_item, text_item
|
||||
|
||||
class SoftwareLayout(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
items = [
|
||||
text_item("Current Version", ""),
|
||||
button_item("Download", "CHECK", callback=self._on_download_update),
|
||||
@@ -14,7 +15,7 @@ class SoftwareLayout(Widget):
|
||||
|
||||
self._list_widget = ListView(items)
|
||||
|
||||
def render(self, rect):
|
||||
def _render(self, rect):
|
||||
self._list_widget.render(rect)
|
||||
|
||||
def _on_download_update(self): pass
|
||||
|
||||
@@ -21,6 +21,7 @@ DESCRIPTIONS = {
|
||||
|
||||
class TogglesLayout(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._params = Params()
|
||||
items = [
|
||||
toggle_item(
|
||||
@@ -65,5 +66,5 @@ class TogglesLayout(Widget):
|
||||
|
||||
self._list_widget = ListView(items)
|
||||
|
||||
def render(self, rect):
|
||||
def _render(self, rect):
|
||||
self._list_widget.render(rect)
|
||||
|
||||
@@ -62,6 +62,7 @@ class MetricData:
|
||||
|
||||
class Sidebar(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._net_type = NETWORK_TYPES.get(NetworkType.none)
|
||||
self._net_strength = 0
|
||||
|
||||
@@ -83,7 +84,7 @@ class Sidebar(Widget):
|
||||
self._on_settings_click = on_settings
|
||||
self._on_flag_click = on_flag
|
||||
|
||||
def render(self, rect: rl.Rectangle):
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
self.update_state()
|
||||
|
||||
# Background
|
||||
@@ -93,8 +94,6 @@ class Sidebar(Widget):
|
||||
self._draw_network_indicator(rect)
|
||||
self._draw_metrics(rect)
|
||||
|
||||
self._handle_mouse_release()
|
||||
|
||||
def update_state(self):
|
||||
sm = ui_state.sm
|
||||
if not sm.updated['deviceState']:
|
||||
@@ -137,11 +136,7 @@ class Sidebar(Widget):
|
||||
else:
|
||||
self._panda_status.update("VEHICLE", "ONLINE", Colors.GOOD)
|
||||
|
||||
def _handle_mouse_release(self):
|
||||
if not rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT):
|
||||
return
|
||||
|
||||
mouse_pos = rl.get_mouse_position()
|
||||
def _handle_mouse_release(self, mouse_pos: rl.Vector2):
|
||||
if rl.check_collision_point_rec(mouse_pos, SETTINGS_BTN):
|
||||
if self._on_settings_click:
|
||||
self._on_settings_click()
|
||||
@@ -151,7 +146,7 @@ class Sidebar(Widget):
|
||||
|
||||
def _draw_buttons(self, rect: rl.Rectangle):
|
||||
mouse_pos = rl.get_mouse_position()
|
||||
mouse_down = rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT)
|
||||
mouse_down = self._is_pressed and rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT)
|
||||
|
||||
# Settings button
|
||||
settings_down = mouse_down and rl.check_collision_point_rec(mouse_pos, SETTINGS_BTN)
|
||||
|
||||
@@ -61,6 +61,7 @@ ALERT_CRITICAL_REBOOT = Alert(
|
||||
|
||||
class AlertRenderer(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.font_regular: rl.Font = gui_app.font(FontWeight.NORMAL)
|
||||
self.font_bold: rl.Font = gui_app.font(FontWeight.BOLD)
|
||||
|
||||
@@ -91,7 +92,7 @@ class AlertRenderer(Widget):
|
||||
# Return current alert
|
||||
return Alert(text1=ss.alertText1, text2=ss.alertText2, size=ss.alertSize, status=ss.alertStatus)
|
||||
|
||||
def render(self, rect: rl.Rectangle) -> None:
|
||||
def _render(self, rect: rl.Rectangle) -> None:
|
||||
alert = self.get_alert(ui_state.sm)
|
||||
if not alert:
|
||||
return
|
||||
|
||||
@@ -52,7 +52,7 @@ class AugmentedRoadView(CameraView):
|
||||
# Callbacks
|
||||
self.on_click: Callable | None = None
|
||||
|
||||
def render(self, rect):
|
||||
def _render(self, rect):
|
||||
# Only render when system is started to avoid invalid data access
|
||||
if not ui_state.started:
|
||||
return
|
||||
@@ -83,7 +83,7 @@ class AugmentedRoadView(CameraView):
|
||||
)
|
||||
|
||||
# Render the base camera view
|
||||
super().render(rect)
|
||||
super()._render(rect)
|
||||
|
||||
# Draw all UI overlays
|
||||
self.model_renderer.render(self._content_rect)
|
||||
|
||||
@@ -57,6 +57,7 @@ else:
|
||||
|
||||
class CameraView(Widget):
|
||||
def __init__(self, name: str, stream_type: VisionStreamType):
|
||||
super().__init__()
|
||||
self._name = name
|
||||
# Primary stream
|
||||
self.client = VisionIpcClient(name, stream_type, conflate=True)
|
||||
@@ -149,7 +150,7 @@ class CameraView(Widget):
|
||||
[0.0, 0.0, 1.0]
|
||||
])
|
||||
|
||||
def render(self, rect: rl.Rectangle):
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
if self._switching:
|
||||
self._handle_switch()
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ class DriverCameraView(CameraView):
|
||||
super().__init__("camerad", stream_type)
|
||||
self.driver_state_renderer = DriverStateRenderer()
|
||||
|
||||
def render(self, rect):
|
||||
super().render(rect)
|
||||
def _render(self, rect):
|
||||
super()._render(rect)
|
||||
|
||||
if not self.frame:
|
||||
gui_label(
|
||||
|
||||
@@ -43,6 +43,7 @@ class ArcData:
|
||||
|
||||
class DriverStateRenderer(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
# Initial state with NumPy arrays
|
||||
self.face_kpts_draw = DEFAULT_FACE_KPTS_3D.copy()
|
||||
self.is_active = False
|
||||
@@ -74,7 +75,7 @@ class DriverStateRenderer(Widget):
|
||||
self.engaged_color = rl.Color(26, 242, 66, 255)
|
||||
self.disengaged_color = rl.Color(139, 139, 139, 255)
|
||||
|
||||
def render(self, rect):
|
||||
def _render(self, rect):
|
||||
if not self._is_visible(ui_state.sm):
|
||||
return
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ from openpilot.common.params import Params
|
||||
|
||||
class ExpButton(Widget):
|
||||
def __init__(self, button_size: int, icon_size: int):
|
||||
super().__init__()
|
||||
self._params = Params()
|
||||
self._experimental_mode: bool = False
|
||||
self._engageable: bool = False
|
||||
@@ -41,13 +42,13 @@ class ExpButton(Widget):
|
||||
return True
|
||||
return False
|
||||
|
||||
def render(self, rect: rl.Rectangle) -> None:
|
||||
def _render(self, rect: rl.Rectangle) -> None:
|
||||
self._rect.x, self._rect.y = rect.x, rect.y
|
||||
center_x = int(self._rect.x + self._rect.width // 2)
|
||||
center_y = int(self._rect.y + self._rect.height // 2)
|
||||
|
||||
mouse_over = rl.check_collision_point_rec(rl.get_mouse_position(), self._rect)
|
||||
mouse_down = rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT)
|
||||
mouse_down = rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT) and self._is_pressed
|
||||
self._white_color.a = 180 if (mouse_down and mouse_over) or not self._engageable else 255
|
||||
|
||||
texture = self._txt_exp if self._held_or_actual_mode() else self._txt_wheel
|
||||
|
||||
@@ -57,6 +57,7 @@ COLORS = Colors()
|
||||
|
||||
class HudRenderer(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
"""Initialize the HUD renderer."""
|
||||
self.is_cruise_set: bool = False
|
||||
self.is_cruise_available: bool = False
|
||||
@@ -99,7 +100,7 @@ class HudRenderer(Widget):
|
||||
|
||||
self._exp_button.update_state(sm)
|
||||
|
||||
def render(self, rect: rl.Rectangle) -> None:
|
||||
def _render(self, rect: rl.Rectangle) -> None:
|
||||
"""Render HUD elements to the screen."""
|
||||
self._update_state(ui_state.sm)
|
||||
|
||||
@@ -120,7 +121,7 @@ class HudRenderer(Widget):
|
||||
|
||||
button_x = rect.x + rect.width - UI_CONFIG.border_size - UI_CONFIG.button_size
|
||||
button_y = rect.y + UI_CONFIG.border_size
|
||||
self._exp_button.render(rl.Rectangle(button_x, button_y, 0, 0))
|
||||
self._exp_button.render(rl.Rectangle(button_x, button_y, UI_CONFIG.button_size, UI_CONFIG.button_size))
|
||||
|
||||
def handle_mouse_event(self) -> bool:
|
||||
return bool(self._exp_button.handle_mouse_event())
|
||||
|
||||
@@ -45,6 +45,7 @@ class LeadVehicle:
|
||||
|
||||
class ModelRenderer(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._longitudinal_control = False
|
||||
self._experimental_mode = False
|
||||
self._blend_factor = 1.0
|
||||
@@ -86,7 +87,7 @@ class ModelRenderer(Widget):
|
||||
self._car_space_transform = transform.astype(np.float32)
|
||||
self._transform_dirty = True
|
||||
|
||||
def render(self, rect: rl.Rectangle):
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
sm = ui_state.sm
|
||||
|
||||
# Check if data is up-to-date
|
||||
|
||||
@@ -43,6 +43,7 @@ class AlertData:
|
||||
|
||||
class AbstractAlert(Widget, ABC):
|
||||
def __init__(self, has_reboot_btn: bool = False):
|
||||
super().__init__()
|
||||
self.params = Params()
|
||||
self.has_reboot_btn = has_reboot_btn
|
||||
self.dismiss_callback: Callable | None = None
|
||||
@@ -89,7 +90,7 @@ class AbstractAlert(Widget, ABC):
|
||||
|
||||
return False
|
||||
|
||||
def render(self, rect: rl.Rectangle):
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
rl.draw_rectangle_rounded(rect, AlertConstants.BORDER_RADIUS / rect.width, 10, AlertColors.BACKGROUND)
|
||||
|
||||
footer_height = AlertConstants.BUTTON_SIZE[1] + AlertConstants.SPACING
|
||||
|
||||
@@ -28,10 +28,33 @@ FONT_DIR = ASSETS_DIR.joinpath("fonts")
|
||||
|
||||
|
||||
class Widget(abc.ABC):
|
||||
def __init__(self):
|
||||
self._is_pressed = False
|
||||
|
||||
def render(self, rect: rl.Rectangle) -> bool | int | None:
|
||||
ret = self._render(rect)
|
||||
|
||||
# Keep track of whether mouse down started within the widget's rectangle
|
||||
mouse_pos = rl.get_mouse_position()
|
||||
if rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT):
|
||||
if rl.check_collision_point_rec(mouse_pos, rect):
|
||||
self._is_pressed = True
|
||||
|
||||
if rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT):
|
||||
if self._is_pressed and rl.check_collision_point_rec(mouse_pos, rect):
|
||||
self._handle_mouse_release(mouse_pos)
|
||||
self._is_pressed = False
|
||||
|
||||
return ret
|
||||
|
||||
@abc.abstractmethod
|
||||
def render(self, rect: rl.Rectangle) -> bool | None:
|
||||
def _render(self, rect: rl.Rectangle) -> bool | int | None:
|
||||
"""Render the widget within the given rectangle."""
|
||||
|
||||
def _handle_mouse_release(self, mouse_pos: rl.Vector2) -> bool:
|
||||
"""Handle mouse release events, if applicable."""
|
||||
return False
|
||||
|
||||
|
||||
class FontWeight(IntEnum):
|
||||
THIN = 0
|
||||
|
||||
@@ -9,6 +9,7 @@ PASSWORD_MASK_DELAY = 1.5 # Seconds to show character before masking
|
||||
|
||||
class InputBox(Widget):
|
||||
def __init__(self, max_text_size=255, password_mode=False):
|
||||
super().__init__()
|
||||
self._max_text_size = max_text_size
|
||||
self._input_text = ""
|
||||
self._cursor_position = 0
|
||||
@@ -100,7 +101,7 @@ class InputBox(Widget):
|
||||
return True
|
||||
return False
|
||||
|
||||
def render(self, rect, color=rl.BLACK, border_color=rl.DARKGRAY, text_color=rl.WHITE, font_size=80):
|
||||
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
|
||||
|
||||
@@ -32,6 +32,7 @@ BUTTON_FONT_WEIGHT = FontWeight.MEDIUM
|
||||
# Abstract base class for right-side items
|
||||
class RightItem(Widget, ABC):
|
||||
def __init__(self, width: int = 100):
|
||||
super().__init__()
|
||||
self.width = width
|
||||
self.enabled = True
|
||||
|
||||
@@ -47,7 +48,7 @@ class ToggleRightItem(RightItem):
|
||||
self.state = initial_state
|
||||
self.enabled = True
|
||||
|
||||
def render(self, rect: rl.Rectangle) -> bool:
|
||||
def _render(self, rect: rl.Rectangle) -> bool:
|
||||
if self.toggle.render(rl.Rectangle(rect.x, rect.y + (rect.height - TOGGLE_HEIGHT) / 2, self.width, TOGGLE_HEIGHT)):
|
||||
self.state = not self.state
|
||||
return True
|
||||
@@ -73,7 +74,7 @@ class ButtonRightItem(RightItem):
|
||||
self.text = text
|
||||
self.enabled = True
|
||||
|
||||
def render(self, rect: rl.Rectangle) -> bool:
|
||||
def _render(self, rect: rl.Rectangle) -> bool:
|
||||
return (
|
||||
gui_button(
|
||||
rl.Rectangle(rect.x, rect.y + (rect.height - BUTTON_HEIGHT) / 2, BUTTON_WIDTH, BUTTON_HEIGHT),
|
||||
@@ -103,7 +104,7 @@ class TextRightItem(RightItem):
|
||||
text_width = measure_text_cached(font, text, font_size).x
|
||||
super().__init__(int(text_width + 20))
|
||||
|
||||
def render(self, rect: rl.Rectangle) -> bool:
|
||||
def _render(self, rect: rl.Rectangle) -> bool:
|
||||
font = gui_app.font(FontWeight.NORMAL)
|
||||
text_size = measure_text_cached(font, self.text, self.font_size)
|
||||
|
||||
@@ -165,8 +166,9 @@ class ListItem:
|
||||
return rl.Rectangle(right_x, right_y, right_width, ITEM_BASE_HEIGHT)
|
||||
|
||||
|
||||
class ListView:
|
||||
class ListView(Widget):
|
||||
def __init__(self, items: list[ListItem]):
|
||||
super().__init__()
|
||||
self._items: list[ListItem] = items
|
||||
self._last_dim: tuple[float, float] = (0, 0)
|
||||
self.scroll_panel = GuiScrollPanel()
|
||||
@@ -183,7 +185,7 @@ class ListView:
|
||||
def invalid_height_cache(self):
|
||||
self._last_dim = (0, 0)
|
||||
|
||||
def render(self, rect: rl.Rectangle):
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
if self._last_dim != (rect.width, rect.height):
|
||||
self._update_item_rects(rect)
|
||||
self._last_dim = (rect.width, rect.height)
|
||||
|
||||
@@ -14,6 +14,7 @@ ANIMATION_SPEED = 8.0
|
||||
|
||||
class Toggle(Widget):
|
||||
def __init__(self, initial_state=False):
|
||||
super().__init__()
|
||||
self._state = initial_state
|
||||
self._enabled = True
|
||||
self._rect = rl.Rectangle(0, 0, WIDTH, HEIGHT)
|
||||
@@ -50,7 +51,7 @@ class Toggle(Widget):
|
||||
self._progress += delta if self._progress < self._target else -delta
|
||||
self._progress = max(0.0, min(1.0, self._progress))
|
||||
|
||||
def render(self, rect: rl.Rectangle):
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
self._rect.x, self._rect.y = rect.x, rect.y
|
||||
self.update()
|
||||
|
||||
|
||||
+2
-1
@@ -29,6 +29,7 @@ class ResetState(IntEnum):
|
||||
|
||||
class Reset(Widget):
|
||||
def __init__(self, mode):
|
||||
super().__init__()
|
||||
self.mode = mode
|
||||
self.reset_state = ResetState.NONE
|
||||
|
||||
@@ -54,7 +55,7 @@ class Reset(Widget):
|
||||
self.reset_state = ResetState.RESETTING
|
||||
threading.Timer(0.1, self._do_erase).start()
|
||||
|
||||
def render(self, rect: rl.Rectangle):
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
label_rect = rl.Rectangle(rect.x + 140, rect.y, rect.width - 280, 100)
|
||||
gui_label(label_rect, "System Reset", 100, font_weight=FontWeight.BOLD)
|
||||
|
||||
|
||||
+2
-1
@@ -41,6 +41,7 @@ class SetupState(IntEnum):
|
||||
|
||||
class Setup(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.state = SetupState.GETTING_STARTED
|
||||
self.network_check_thread = None
|
||||
self.network_connected = threading.Event()
|
||||
@@ -67,7 +68,7 @@ class Setup(Widget):
|
||||
except (FileNotFoundError, ValueError):
|
||||
self.state = SetupState.LOW_VOLTAGE
|
||||
|
||||
def render(self, rect: rl.Rectangle):
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
if self.state == SetupState.LOW_VOLTAGE:
|
||||
self.render_low_voltage(rect)
|
||||
elif self.state == SetupState.GETTING_STARTED:
|
||||
|
||||
@@ -24,6 +24,7 @@ def clamp(value, min_value, max_value):
|
||||
|
||||
class Spinner(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._comma_texture = gui_app.texture("images/spinner_comma.png", TEXTURE_SIZE, TEXTURE_SIZE)
|
||||
self._spinner_texture = gui_app.texture("images/spinner_track.png", TEXTURE_SIZE, TEXTURE_SIZE, alpha_premultiply=True)
|
||||
self._rotation = 0.0
|
||||
@@ -38,7 +39,7 @@ class Spinner(Widget):
|
||||
self._progress = None
|
||||
self._wrapped_lines = wrap_text(text, FONT_SIZE, gui_app.width - MARGIN_H)
|
||||
|
||||
def render(self, rect: rl.Rectangle):
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
if self._wrapped_lines:
|
||||
# Calculate total height required for spinner and text
|
||||
spacing = 50
|
||||
|
||||
+2
-1
@@ -48,13 +48,14 @@ def wrap_text(text, font_size, max_width):
|
||||
|
||||
class TextWindow(Widget):
|
||||
def __init__(self, text: str):
|
||||
super().__init__()
|
||||
self._textarea_rect = rl.Rectangle(MARGIN, MARGIN, gui_app.width - MARGIN * 2, gui_app.height - MARGIN * 2)
|
||||
self._wrapped_lines = wrap_text(text, FONT_SIZE, self._textarea_rect.width - 20)
|
||||
self._content_rect = rl.Rectangle(0, 0, self._textarea_rect.width - 20, len(self._wrapped_lines) * LINE_HEIGHT)
|
||||
self._scroll_panel = GuiScrollPanel(show_vertical_scroll_bar=True)
|
||||
self._scroll_panel._offset.y = -max(self._content_rect.height - self._textarea_rect.height, 0)
|
||||
|
||||
def render(self, rect: rl.Rectangle):
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
scroll = self._scroll_panel.handle_scroll(self._textarea_rect, self._content_rect)
|
||||
rl.begin_scissor_mode(int(self._textarea_rect.x), int(self._textarea_rect.y), int(self._textarea_rect.width), int(self._textarea_rect.height))
|
||||
for i, line in enumerate(self._wrapped_lines):
|
||||
|
||||
@@ -32,6 +32,7 @@ class Screen(IntEnum):
|
||||
|
||||
class Updater(Widget):
|
||||
def __init__(self, updater_path, manifest_path):
|
||||
super().__init__()
|
||||
self.updater = updater_path
|
||||
self.manifest = manifest_path
|
||||
self.current_screen = Screen.PROMPT
|
||||
@@ -137,7 +138,7 @@ class Updater(Widget):
|
||||
HARDWARE.reboot()
|
||||
return
|
||||
|
||||
def render(self, rect: rl.Rectangle):
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
if self.current_screen == Screen.PROMPT:
|
||||
self.render_prompt_screen(rect)
|
||||
elif self.current_screen == Screen.WIFI:
|
||||
|
||||
@@ -54,6 +54,7 @@ KEYBOARD_LAYOUTS = {
|
||||
|
||||
class Keyboard(Widget):
|
||||
def __init__(self, max_text_size: int = 255, min_text_size: int = 0, password_mode: bool = False, show_password_toggle: bool = False):
|
||||
super().__init__()
|
||||
self._layout_name: Literal["lowercase", "uppercase", "numbers", "specials"] = "lowercase"
|
||||
self._caps_lock = False
|
||||
self._last_shift_press_time = 0
|
||||
@@ -95,7 +96,7 @@ class Keyboard(Widget):
|
||||
self._title = title
|
||||
self._sub_title = sub_title
|
||||
|
||||
def render(self, rect: rl.Rectangle):
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
rect = rl.Rectangle(rect.x + CONTENT_MARGIN, rect.y + CONTENT_MARGIN, rect.width - 2 * CONTENT_MARGIN, rect.height - 2 * CONTENT_MARGIN)
|
||||
gui_label(rl.Rectangle(rect.x, rect.y, rect.width, 95), self._title, 90, font_weight=FontWeight.BOLD)
|
||||
gui_label(rl.Rectangle(rect.x, rect.y + 95, rect.width, 60), self._sub_title, 55, font_weight=FontWeight.NORMAL)
|
||||
|
||||
@@ -59,6 +59,7 @@ UIState = StateIdle | StateConnecting | StateNeedsAuth | StateShowForgetConfirm
|
||||
|
||||
class WifiManagerUI(Widget):
|
||||
def __init__(self, wifi_manager: WifiManagerWrapper):
|
||||
super().__init__()
|
||||
self.state: UIState = StateIdle()
|
||||
self.btn_width: int = 200
|
||||
self.scroll_panel = GuiScrollPanel()
|
||||
@@ -80,7 +81,7 @@ class WifiManagerUI(Widget):
|
||||
self.wifi_manager.start()
|
||||
self.wifi_manager.connect()
|
||||
|
||||
def render(self, rect: rl.Rectangle):
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
with self._lock:
|
||||
if not self._networks:
|
||||
gui_label(rect, "Scanning Wi-Fi networks...", 72, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER)
|
||||
|
||||
@@ -8,6 +8,7 @@ from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel
|
||||
|
||||
class MultiOptionDialog(Widget):
|
||||
def __init__(self, title, options, current=""):
|
||||
super().__init__()
|
||||
self._title = title
|
||||
self._options = options
|
||||
self._current = current if current in options else ""
|
||||
@@ -20,7 +21,7 @@ class MultiOptionDialog(Widget):
|
||||
def selection(self):
|
||||
return self._selection
|
||||
|
||||
def render(self, rect):
|
||||
def _render(self, rect):
|
||||
title_rect = rl.Rectangle(rect.x + self._padding, rect.y + self._padding, rect.width - 2 * self._padding, 70)
|
||||
gui_label(title_rect, self._title, 70)
|
||||
|
||||
@@ -77,6 +78,6 @@ if __name__ == "__main__":
|
||||
|
||||
for _ in gui_app.render():
|
||||
result = dialog.render(rl.Rectangle(100, 100, 1024, 800))
|
||||
if result >= 0:
|
||||
if isinstance(result, int) and result >= 0:
|
||||
print(f"Selected: {dialog.selection}" if result > 0 else "Canceled")
|
||||
break
|
||||
|
||||
Reference in New Issue
Block a user