diff --git a/selfdrive/ui/layouts/home.py b/selfdrive/ui/layouts/home.py index 11d87cd112..1a1f69afe1 100644 --- a/selfdrive/ui/layouts/home.py +++ b/selfdrive/ui/layouts/home.py @@ -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() diff --git a/selfdrive/ui/layouts/main.py b/selfdrive/ui/layouts/main.py index 1f28068010..992fb3a402 100644 --- a/selfdrive/ui/layouts/main.py +++ b/selfdrive/ui/layouts/main.py @@ -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) diff --git a/selfdrive/ui/layouts/network.py b/selfdrive/ui/layouts/network.py index cc28b7cff5..37aa12a68a 100644 --- a/selfdrive/ui/layouts/network.py +++ b/selfdrive/ui/layouts/network.py @@ -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): diff --git a/selfdrive/ui/layouts/settings/developer.py b/selfdrive/ui/layouts/settings/developer.py index ba2c21bc38..101dd82623 100644 --- a/selfdrive/ui/layouts/settings/developer.py +++ b/selfdrive/ui/layouts/settings/developer.py @@ -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 diff --git a/selfdrive/ui/layouts/settings/device.py b/selfdrive/ui/layouts/settings/device.py index 25b275e863..97ae98082a 100644 --- a/selfdrive/ui/layouts/settings/device.py +++ b/selfdrive/ui/layouts/settings/device.py @@ -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 diff --git a/selfdrive/ui/layouts/settings/settings.py b/selfdrive/ui/layouts/settings/settings.py index 0dc10236b7..1c47d49d31 100644 --- a/selfdrive/ui/layouts/settings/settings.py +++ b/selfdrive/ui/layouts/settings/settings.py @@ -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: diff --git a/selfdrive/ui/layouts/settings/software.py b/selfdrive/ui/layouts/settings/software.py index b50526332d..3002ad86ec 100644 --- a/selfdrive/ui/layouts/settings/software.py +++ b/selfdrive/ui/layouts/settings/software.py @@ -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 diff --git a/selfdrive/ui/layouts/settings/toggles.py b/selfdrive/ui/layouts/settings/toggles.py index 04c858c97a..d8ebf9607f 100644 --- a/selfdrive/ui/layouts/settings/toggles.py +++ b/selfdrive/ui/layouts/settings/toggles.py @@ -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) diff --git a/selfdrive/ui/layouts/sidebar.py b/selfdrive/ui/layouts/sidebar.py index 637cd12217..9003fc67f3 100644 --- a/selfdrive/ui/layouts/sidebar.py +++ b/selfdrive/ui/layouts/sidebar.py @@ -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) diff --git a/selfdrive/ui/onroad/alert_renderer.py b/selfdrive/ui/onroad/alert_renderer.py index 7802959e73..b14c85a953 100644 --- a/selfdrive/ui/onroad/alert_renderer.py +++ b/selfdrive/ui/onroad/alert_renderer.py @@ -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 diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index 95e0efc6d6..611320ad36 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -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) diff --git a/selfdrive/ui/onroad/cameraview.py b/selfdrive/ui/onroad/cameraview.py index 46aa829c1a..f46ba71401 100644 --- a/selfdrive/ui/onroad/cameraview.py +++ b/selfdrive/ui/onroad/cameraview.py @@ -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() diff --git a/selfdrive/ui/onroad/driver_camera_view.py b/selfdrive/ui/onroad/driver_camera_view.py index 66ede91232..a05f1545f5 100644 --- a/selfdrive/ui/onroad/driver_camera_view.py +++ b/selfdrive/ui/onroad/driver_camera_view.py @@ -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( diff --git a/selfdrive/ui/onroad/driver_state.py b/selfdrive/ui/onroad/driver_state.py index 60eaf25582..22abd39622 100644 --- a/selfdrive/ui/onroad/driver_state.py +++ b/selfdrive/ui/onroad/driver_state.py @@ -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 diff --git a/selfdrive/ui/onroad/exp_button.py b/selfdrive/ui/onroad/exp_button.py index 56f2fa0089..5c1c4ff281 100644 --- a/selfdrive/ui/onroad/exp_button.py +++ b/selfdrive/ui/onroad/exp_button.py @@ -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 diff --git a/selfdrive/ui/onroad/hud_renderer.py b/selfdrive/ui/onroad/hud_renderer.py index a9cf07d856..71629e259c 100644 --- a/selfdrive/ui/onroad/hud_renderer.py +++ b/selfdrive/ui/onroad/hud_renderer.py @@ -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()) diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index 1c52676a69..027b8342f3 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -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 diff --git a/selfdrive/ui/widgets/offroad_alerts.py b/selfdrive/ui/widgets/offroad_alerts.py index 9948db5b85..7872ba70a0 100644 --- a/selfdrive/ui/widgets/offroad_alerts.py +++ b/selfdrive/ui/widgets/offroad_alerts.py @@ -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 diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index b63e6a9335..f051d9f10a 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -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 diff --git a/system/ui/lib/inputbox.py b/system/ui/lib/inputbox.py index 4269869006..5c3f234151 100644 --- a/system/ui/lib/inputbox.py +++ b/system/ui/lib/inputbox.py @@ -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 diff --git a/system/ui/lib/list_view.py b/system/ui/lib/list_view.py index 3eae33045b..94e0cfeb80 100644 --- a/system/ui/lib/list_view.py +++ b/system/ui/lib/list_view.py @@ -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) diff --git a/system/ui/lib/toggle.py b/system/ui/lib/toggle.py index b1ea36408c..ba8686b19e 100644 --- a/system/ui/lib/toggle.py +++ b/system/ui/lib/toggle.py @@ -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() diff --git a/system/ui/reset.py b/system/ui/reset.py index 1da3db4c64..2e623833d9 100755 --- a/system/ui/reset.py +++ b/system/ui/reset.py @@ -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) diff --git a/system/ui/setup.py b/system/ui/setup.py index 6bdfd311e8..5a281f0e0d 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -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: diff --git a/system/ui/spinner.py b/system/ui/spinner.py index 6998ed01c2..84bf395d4d 100755 --- a/system/ui/spinner.py +++ b/system/ui/spinner.py @@ -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 diff --git a/system/ui/text.py b/system/ui/text.py index f24e8bf2cd..7da7d0cf45 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -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): diff --git a/system/ui/updater.py b/system/ui/updater.py index a4fb80cd96..d01a2801b2 100755 --- a/system/ui/updater.py +++ b/system/ui/updater.py @@ -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: diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 94f3f93734..61bf82fd85 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -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) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 979dbb9a31..2e91545d1c 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -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) diff --git a/system/ui/widgets/option_dialog.py b/system/ui/widgets/option_dialog.py index 8b829d4945..a335ffc547 100644 --- a/system/ui/widgets/option_dialog.py +++ b/system/ui/widgets/option_dialog.py @@ -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