From 03d2e7b2b07c03e8fa95cbfc157639476e045174 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 11 Jun 2025 05:32:20 +0800 Subject: [PATCH] ui: extract Widget base class to separate lib/widget.py (#35520) * extract Widget base class to separate lib/widget.py * format * format --------- Co-authored-by: Shane Smiskol --- selfdrive/ui/layouts/home.py | 3 +- selfdrive/ui/layouts/main.py | 2 +- selfdrive/ui/layouts/network.py | 2 +- selfdrive/ui/layouts/settings/developer.py | 2 +- selfdrive/ui/layouts/settings/device.py | 3 +- selfdrive/ui/layouts/settings/firehose.py | 3 +- selfdrive/ui/layouts/settings/settings.py | 3 +- selfdrive/ui/layouts/settings/software.py | 3 +- selfdrive/ui/layouts/settings/toggles.py | 2 +- selfdrive/ui/layouts/sidebar.py | 3 +- selfdrive/ui/onroad/alert_renderer.py | 3 +- selfdrive/ui/onroad/cameraview.py | 3 +- selfdrive/ui/onroad/driver_state.py | 3 +- selfdrive/ui/onroad/exp_button.py | 3 +- selfdrive/ui/onroad/hud_renderer.py | 3 +- selfdrive/ui/onroad/model_renderer.py | 23 ++++++------- selfdrive/ui/widgets/exp_mode_button.py | 2 +- selfdrive/ui/widgets/offroad_alerts.py | 3 +- selfdrive/ui/widgets/prime.py | 3 +- selfdrive/ui/widgets/setup.py | 3 +- selfdrive/ui/widgets/ssh_key.py | 3 +- system/ui/lib/application.py | 36 -------------------- system/ui/lib/inputbox.py | 3 +- system/ui/lib/list_view.py | 3 +- system/ui/lib/toggle.py | 2 +- system/ui/lib/widget.py | 38 ++++++++++++++++++++++ system/ui/reset.py | 3 +- system/ui/setup.py | 3 +- system/ui/spinner.py | 3 +- system/ui/text.py | 3 +- system/ui/updater.py | 3 +- system/ui/widgets/confirm_dialog.py | 3 +- system/ui/widgets/keyboard.py | 3 +- system/ui/widgets/network.py | 3 +- system/ui/widgets/option_dialog.py | 3 +- 35 files changed, 108 insertions(+), 79 deletions(-) create mode 100644 system/ui/lib/widget.py diff --git a/selfdrive/ui/layouts/home.py b/selfdrive/ui/layouts/home.py index c16a1ef4da..f255d2403c 100644 --- a/selfdrive/ui/layouts/home.py +++ b/selfdrive/ui/layouts/home.py @@ -8,7 +8,8 @@ from openpilot.selfdrive.ui.widgets.exp_mode_button import ExperimentalModeButto from openpilot.selfdrive.ui.widgets.prime import PrimeWidget from openpilot.selfdrive.ui.widgets.setup import SetupWidget from openpilot.system.ui.lib.text_measure import measure_text_cached -from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_TEXT_COLOR, Widget +from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_TEXT_COLOR +from openpilot.system.ui.lib.widget import Widget HEADER_HEIGHT = 80 HEAD_BUTTON_FONT_SIZE = 40 diff --git a/selfdrive/ui/layouts/main.py b/selfdrive/ui/layouts/main.py index cc93865424..db5cbf29b2 100644 --- a/selfdrive/ui/layouts/main.py +++ b/selfdrive/ui/layouts/main.py @@ -5,7 +5,7 @@ from openpilot.selfdrive.ui.layouts.home import HomeLayout from openpilot.selfdrive.ui.layouts.settings.settings import SettingsLayout from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.selfdrive.ui.onroad.augmented_road_view import AugmentedRoadView -from openpilot.system.ui.lib.application import Widget +from openpilot.system.ui.lib.widget import Widget class MainState(IntEnum): diff --git a/selfdrive/ui/layouts/network.py b/selfdrive/ui/layouts/network.py index 37aa12a68a..c227facd04 100644 --- a/selfdrive/ui/layouts/network.py +++ b/selfdrive/ui/layouts/network.py @@ -1,5 +1,5 @@ import pyray as rl -from openpilot.system.ui.lib.application import Widget +from openpilot.system.ui.lib.widget import Widget from openpilot.system.ui.lib.wifi_manager import WifiManagerWrapper from openpilot.system.ui.widgets.network import WifiManagerUI diff --git a/selfdrive/ui/layouts/settings/developer.py b/selfdrive/ui/layouts/settings/developer.py index 607d96bcf7..de1bcaac4b 100644 --- a/selfdrive/ui/layouts/settings/developer.py +++ b/selfdrive/ui/layouts/settings/developer.py @@ -1,5 +1,5 @@ -from openpilot.system.ui.lib.application import Widget from openpilot.system.ui.lib.list_view import ListView, toggle_item +from openpilot.system.ui.lib.widget import Widget from openpilot.common.params import Params from openpilot.selfdrive.ui.widgets.ssh_key import ssh_key_item diff --git a/selfdrive/ui/layouts/settings/device.py b/selfdrive/ui/layouts/settings/device.py index a67d8d5d9c..35dd9f6f94 100644 --- a/selfdrive/ui/layouts/settings/device.py +++ b/selfdrive/ui/layouts/settings/device.py @@ -6,8 +6,9 @@ from openpilot.common.params import Params from openpilot.selfdrive.ui.onroad.driver_camera_dialog import DriverCameraDialog from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.hardware import TICI -from openpilot.system.ui.lib.application import gui_app, Widget, DialogResult +from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.list_view import ListView, text_item, button_item, dual_button_item +from openpilot.system.ui.lib.widget import Widget, DialogResult from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog from openpilot.system.ui.widgets.option_dialog import MultiOptionDialog from openpilot.system.ui.widgets.confirm_dialog import confirm_dialog, alert_dialog diff --git a/selfdrive/ui/layouts/settings/firehose.py b/selfdrive/ui/layouts/settings/firehose.py index 4f3ed6f3a0..1c5df061ec 100644 --- a/selfdrive/ui/layouts/settings/firehose.py +++ b/selfdrive/ui/layouts/settings/firehose.py @@ -5,9 +5,10 @@ import threading from openpilot.common.api import Api, api_get from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog -from openpilot.system.ui.lib.application import gui_app, Widget, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel +from openpilot.system.ui.lib.widget import Widget from openpilot.selfdrive.ui.ui_state import ui_state diff --git a/selfdrive/ui/layouts/settings/settings.py b/selfdrive/ui/layouts/settings/settings.py index 356c9e70cd..6f9db1ffda 100644 --- a/selfdrive/ui/layouts/settings/settings.py +++ b/selfdrive/ui/layouts/settings/settings.py @@ -8,9 +8,10 @@ from openpilot.selfdrive.ui.layouts.settings.device import DeviceLayout from openpilot.selfdrive.ui.layouts.settings.firehose import FirehoseLayout from openpilot.selfdrive.ui.layouts.settings.software import SoftwareLayout from openpilot.selfdrive.ui.layouts.settings.toggles import TogglesLayout -from openpilot.system.ui.lib.application import gui_app, FontWeight, Widget +from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.selfdrive.ui.layouts.network import NetworkLayout +from openpilot.system.ui.lib.widget import Widget # Import individual panels diff --git a/selfdrive/ui/layouts/settings/software.py b/selfdrive/ui/layouts/settings/software.py index 4e594d29a4..39b883984e 100644 --- a/selfdrive/ui/layouts/settings/software.py +++ b/selfdrive/ui/layouts/settings/software.py @@ -1,6 +1,7 @@ from openpilot.common.params import Params -from openpilot.system.ui.lib.application import gui_app, Widget, DialogResult +from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.list_view import ListView, button_item, text_item +from openpilot.system.ui.lib.widget import Widget, DialogResult from openpilot.system.ui.widgets.confirm_dialog import confirm_dialog diff --git a/selfdrive/ui/layouts/settings/toggles.py b/selfdrive/ui/layouts/settings/toggles.py index d8ebf9607f..233186be23 100644 --- a/selfdrive/ui/layouts/settings/toggles.py +++ b/selfdrive/ui/layouts/settings/toggles.py @@ -1,5 +1,5 @@ -from openpilot.system.ui.lib.application import Widget from openpilot.system.ui.lib.list_view import ListView, toggle_item +from openpilot.system.ui.lib.widget import Widget from openpilot.common.params import Params # Description constants diff --git a/selfdrive/ui/layouts/sidebar.py b/selfdrive/ui/layouts/sidebar.py index 9003fc67f3..c65f6db736 100644 --- a/selfdrive/ui/layouts/sidebar.py +++ b/selfdrive/ui/layouts/sidebar.py @@ -4,8 +4,9 @@ from dataclasses import dataclass from collections.abc import Callable from cereal import log from openpilot.selfdrive.ui.ui_state import ui_state -from openpilot.system.ui.lib.application import gui_app, FontWeight, Widget +from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.text_measure import measure_text_cached +from openpilot.system.ui.lib.widget import Widget SIDEBAR_WIDTH = 300 METRIC_HEIGHT = 126 diff --git a/selfdrive/ui/onroad/alert_renderer.py b/selfdrive/ui/onroad/alert_renderer.py index fcf28749c8..7a17291bbe 100644 --- a/selfdrive/ui/onroad/alert_renderer.py +++ b/selfdrive/ui/onroad/alert_renderer.py @@ -3,9 +3,10 @@ import pyray as rl from dataclasses import dataclass from cereal import messaging, log from openpilot.system.hardware import TICI -from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_FPS, Widget +from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_FPS from openpilot.system.ui.lib.label import gui_text_box from openpilot.system.ui.lib.text_measure import measure_text_cached +from openpilot.system.ui.lib.widget import Widget from openpilot.selfdrive.ui.ui_state import ui_state ALERT_MARGIN = 40 diff --git a/selfdrive/ui/onroad/cameraview.py b/selfdrive/ui/onroad/cameraview.py index b9f3c172bb..33f1961d2b 100644 --- a/selfdrive/ui/onroad/cameraview.py +++ b/selfdrive/ui/onroad/cameraview.py @@ -5,8 +5,9 @@ import pyray as rl from openpilot.system.hardware import TICI from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf from openpilot.common.swaglog import cloudlog -from openpilot.system.ui.lib.application import gui_app, Widget +from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.egl import init_egl, create_egl_image, destroy_egl_image, bind_egl_image_to_texture, EGLImage +from openpilot.system.ui.lib.widget import Widget CONNECTION_RETRY_INTERVAL = 0.2 # seconds between connection attempts diff --git a/selfdrive/ui/onroad/driver_state.py b/selfdrive/ui/onroad/driver_state.py index a7c7208657..0f55fb4532 100644 --- a/selfdrive/ui/onroad/driver_state.py +++ b/selfdrive/ui/onroad/driver_state.py @@ -2,7 +2,8 @@ import numpy as np import pyray as rl from dataclasses import dataclass from openpilot.selfdrive.ui.ui_state import ui_state, UI_BORDER_SIZE -from openpilot.system.ui.lib.application import gui_app, Widget +from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.widget import Widget # Default 3D coordinates for face keypoints as a NumPy array DEFAULT_FACE_KPTS_3D = np.array([ diff --git a/selfdrive/ui/onroad/exp_button.py b/selfdrive/ui/onroad/exp_button.py index 5c1c4ff281..cabfe2a247 100644 --- a/selfdrive/ui/onroad/exp_button.py +++ b/selfdrive/ui/onroad/exp_button.py @@ -2,7 +2,8 @@ import time import pyray as rl from cereal.messaging import SubMaster from openpilot.selfdrive.ui.ui_state import ui_state -from openpilot.system.ui.lib.application import gui_app, Widget +from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.widget import Widget from openpilot.common.params import Params diff --git a/selfdrive/ui/onroad/hud_renderer.py b/selfdrive/ui/onroad/hud_renderer.py index 71629e259c..f67527c8e2 100644 --- a/selfdrive/ui/onroad/hud_renderer.py +++ b/selfdrive/ui/onroad/hud_renderer.py @@ -3,9 +3,10 @@ from dataclasses import dataclass from cereal.messaging import SubMaster from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus from openpilot.selfdrive.ui.onroad.exp_button import ExpButton -from openpilot.system.ui.lib.application import gui_app, FontWeight, Widget +from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.common.conversions import Conversions as CV +from openpilot.system.ui.lib.widget import Widget # Constants SET_SPEED_NA = 255 diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index 1fb10e01cd..853bfa5065 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -5,8 +5,9 @@ from cereal import messaging, car from dataclasses import dataclass, field from openpilot.common.params import Params from openpilot.selfdrive.ui.ui_state import ui_state -from openpilot.system.ui.lib.application import DEFAULT_FPS, Widget +from openpilot.system.ui.lib.application import DEFAULT_FPS from openpilot.system.ui.lib.shader_polygon import draw_polygon +from openpilot.system.ui.lib.widget import Widget from openpilot.selfdrive.locationd.calibrationd import HEIGHT_INIT CLIP_MARGIN = 500 @@ -383,12 +384,12 @@ class ModelRenderer(Widget): # Filter points within clip region left_in_clip = ( - (left_screen[0] >= x_min) & (left_screen[0] <= x_max) & - (left_screen[1] >= y_min) & (left_screen[1] <= y_max) + (left_screen[0] >= x_min) & (left_screen[0] <= x_max) & + (left_screen[1] >= y_min) & (left_screen[1] <= y_max) ) right_in_clip = ( - (right_screen[0] >= x_min) & (right_screen[0] <= x_max) & - (right_screen[1] >= y_min) & (right_screen[1] <= y_max) + (right_screen[0] >= x_min) & (right_screen[0] <= x_max) & + (right_screen[1] >= y_min) & (right_screen[1] <= y_max) ) both_in_clip = left_in_clip & right_in_clip @@ -401,12 +402,12 @@ class ModelRenderer(Widget): # Handle Y-coordinate inversion on hills if not allow_invert and left_screen.shape[1] > 1: - y = left_screen[1, :] # y-coordinates - keep = y == np.minimum.accumulate(y) - if not np.any(keep): - return np.empty((0, 2), dtype=np.float32) - left_screen = left_screen[:, keep] - right_screen = right_screen[:, keep] + y = left_screen[1, :] # y-coordinates + keep = y == np.minimum.accumulate(y) + if not np.any(keep): + return np.empty((0, 2), dtype=np.float32) + left_screen = left_screen[:, keep] + right_screen = right_screen[:, keep] return np.vstack((left_screen.T, right_screen[:, ::-1].T)).astype(np.float32) diff --git a/selfdrive/ui/widgets/exp_mode_button.py b/selfdrive/ui/widgets/exp_mode_button.py index 3c87aa9b28..83caf413d1 100644 --- a/selfdrive/ui/widgets/exp_mode_button.py +++ b/selfdrive/ui/widgets/exp_mode_button.py @@ -1,7 +1,7 @@ import pyray as rl -from openpilot.system.ui.lib.application import Widget from openpilot.common.params import Params from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.widget import Widget class ExperimentalModeButton(Widget): diff --git a/selfdrive/ui/widgets/offroad_alerts.py b/selfdrive/ui/widgets/offroad_alerts.py index 7872ba70a0..1a156f923e 100644 --- a/selfdrive/ui/widgets/offroad_alerts.py +++ b/selfdrive/ui/widgets/offroad_alerts.py @@ -8,7 +8,8 @@ from openpilot.system.hardware import HARDWARE from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.lib.text_measure import measure_text_cached -from openpilot.system.ui.lib.application import gui_app, FontWeight, Widget +from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.widget import Widget class AlertColors: diff --git a/selfdrive/ui/widgets/prime.py b/selfdrive/ui/widgets/prime.py index 04a0d7aff8..afdfa33e7a 100644 --- a/selfdrive/ui/widgets/prime.py +++ b/selfdrive/ui/widgets/prime.py @@ -1,10 +1,11 @@ import pyray as rl from openpilot.selfdrive.ui.ui_state import ui_state -from openpilot.system.ui.lib.application import gui_app, Widget, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.label import gui_label from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.lib.text_measure import measure_text_cached +from openpilot.system.ui.lib.widget import Widget class PrimeWidget(Widget): diff --git a/selfdrive/ui/widgets/setup.py b/selfdrive/ui/widgets/setup.py index a528d3c993..1c797e32c5 100644 --- a/selfdrive/ui/widgets/setup.py +++ b/selfdrive/ui/widgets/setup.py @@ -2,9 +2,10 @@ import pyray as rl from openpilot.selfdrive.ui.lib.prime_state import PrimeType from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog from openpilot.selfdrive.ui.ui_state import ui_state -from openpilot.system.ui.lib.application import gui_app, Widget, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.wrap_text import wrap_text +from openpilot.system.ui.lib.widget import Widget class SetupWidget(Widget): diff --git a/selfdrive/ui/widgets/ssh_key.py b/selfdrive/ui/widgets/ssh_key.py index e45b04e6ba..418867c86f 100644 --- a/selfdrive/ui/widgets/ssh_key.py +++ b/selfdrive/ui/widgets/ssh_key.py @@ -5,7 +5,7 @@ import copy from enum import Enum from openpilot.common.params import Params -from openpilot.system.ui.lib.application import gui_app, DialogResult, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.list_view import ( ItemAction, @@ -16,6 +16,7 @@ from openpilot.system.ui.lib.list_view import ( BUTTON_WIDTH, ) from openpilot.system.ui.lib.text_measure import measure_text_cached +from openpilot.system.ui.lib.widget import DialogResult from openpilot.system.ui.widgets.confirm_dialog import alert_dialog from openpilot.system.ui.widgets.keyboard import Keyboard diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 0d39ce829e..24297286ef 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -1,4 +1,3 @@ -import abc import atexit import os import time @@ -27,41 +26,6 @@ ASSETS_DIR = files("openpilot.selfdrive").joinpath("assets") FONT_DIR = ASSETS_DIR.joinpath("fonts") -class DialogResult(IntEnum): - CANCEL = 0 - CONFIRM = 1 - NO_ACTION = -1 - - -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 | 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 EXTRA_LIGHT = 1 diff --git a/system/ui/lib/inputbox.py b/system/ui/lib/inputbox.py index 876a9a5d7a..99c6fbbe73 100644 --- a/system/ui/lib/inputbox.py +++ b/system/ui/lib/inputbox.py @@ -1,7 +1,8 @@ import pyray as rl import time -from openpilot.system.ui.lib.application import gui_app, Widget +from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.text_measure import measure_text_cached +from openpilot.system.ui.lib.widget import Widget PASSWORD_MASK_CHAR = "•" PASSWORD_MASK_DELAY = 1.5 # Seconds to show character before masking diff --git a/system/ui/lib/list_view.py b/system/ui/lib/list_view.py index 439f0e77bd..2a18805225 100644 --- a/system/ui/lib/list_view.py +++ b/system/ui/lib/list_view.py @@ -4,11 +4,12 @@ from dataclasses import dataclass from collections.abc import Callable from abc import ABC from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel -from openpilot.system.ui.lib.application import gui_app, FontWeight, Widget +from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.toggle import Toggle, WIDTH as TOGGLE_WIDTH, HEIGHT as TOGGLE_HEIGHT +from openpilot.system.ui.lib.widget import Widget ITEM_BASE_HEIGHT = 170 LINE_PADDING = 40 diff --git a/system/ui/lib/toggle.py b/system/ui/lib/toggle.py index 29491dbd70..4c08da50d1 100644 --- a/system/ui/lib/toggle.py +++ b/system/ui/lib/toggle.py @@ -1,5 +1,5 @@ import pyray as rl -from openpilot.system.ui.lib.application import Widget +from openpilot.system.ui.lib.widget import Widget ON_COLOR = rl.Color(51, 171, 76, 255) OFF_COLOR = rl.Color(0x39, 0x39, 0x39, 255) diff --git a/system/ui/lib/widget.py b/system/ui/lib/widget.py new file mode 100644 index 0000000000..437f7069e9 --- /dev/null +++ b/system/ui/lib/widget.py @@ -0,0 +1,38 @@ +import abc +import pyray as rl +from enum import IntEnum + + +class DialogResult(IntEnum): + CANCEL = 0 + CONFIRM = 1 + NO_ACTION = -1 + + +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 | 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 diff --git a/system/ui/reset.py b/system/ui/reset.py index 2e623833d9..c0dbd1d956 100755 --- a/system/ui/reset.py +++ b/system/ui/reset.py @@ -6,9 +6,10 @@ import threading from enum import IntEnum from openpilot.system.hardware import PC -from openpilot.system.ui.lib.application import gui_app, FontWeight, Widget +from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.label import gui_label, gui_text_box +from openpilot.system.ui.lib.widget import Widget NVME = "/dev/nvme0n1" USERDATA = "/dev/disk/by-partlabel/userdata" diff --git a/system/ui/setup.py b/system/ui/setup.py index 5a281f0e0d..4d9d269480 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -9,9 +9,10 @@ import pyray as rl from cereal import log from openpilot.system.hardware import HARDWARE -from openpilot.system.ui.lib.application import gui_app, FontWeight, Widget +from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.label import gui_label, gui_text_box +from openpilot.system.ui.lib.widget import Widget from openpilot.system.ui.widgets.network import WifiManagerUI, WifiManagerWrapper from openpilot.system.ui.widgets.keyboard import Keyboard diff --git a/system/ui/spinner.py b/system/ui/spinner.py index 84bf395d4d..65c5086880 100755 --- a/system/ui/spinner.py +++ b/system/ui/spinner.py @@ -3,8 +3,9 @@ import pyray as rl import select import sys -from openpilot.system.ui.lib.application import gui_app, Widget +from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.text_measure import measure_text_cached +from openpilot.system.ui.lib.widget import Widget from openpilot.system.ui.text import wrap_text # Constants diff --git a/system/ui/text.py b/system/ui/text.py index 7da7d0cf45..01a580a12b 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -6,7 +6,8 @@ from openpilot.system.hardware import HARDWARE, PC from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel -from openpilot.system.ui.lib.application import gui_app, Widget +from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.widget import Widget MARGIN = 50 SPACING = 40 diff --git a/system/ui/updater.py b/system/ui/updater.py index d01a2801b2..3fd60cfb1d 100755 --- a/system/ui/updater.py +++ b/system/ui/updater.py @@ -6,10 +6,11 @@ import pyray as rl from enum import IntEnum from openpilot.system.hardware import HARDWARE -from openpilot.system.ui.lib.application import gui_app, FontWeight, Widget +from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.label import gui_text_box, gui_label from openpilot.system.ui.lib.wifi_manager import WifiManagerWrapper +from openpilot.system.ui.lib.widget import Widget from openpilot.system.ui.widgets.network import WifiManagerUI # Constants diff --git a/system/ui/widgets/confirm_dialog.py b/system/ui/widgets/confirm_dialog.py index 88816e3566..00361905d6 100644 --- a/system/ui/widgets/confirm_dialog.py +++ b/system/ui/widgets/confirm_dialog.py @@ -1,7 +1,8 @@ import pyray as rl -from openpilot.system.ui.lib.application import gui_app, DialogResult, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.label import gui_text_box +from openpilot.system.ui.lib.widget import DialogResult DIALOG_WIDTH = 1520 DIALOG_HEIGHT = 600 diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 61bf82fd85..338706e73d 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -1,10 +1,11 @@ import time from typing import Literal import pyray as rl -from openpilot.system.ui.lib.application import gui_app, FontWeight, Widget +from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.button import ButtonStyle, gui_button from openpilot.system.ui.lib.inputbox import InputBox from openpilot.system.ui.lib.label import gui_label +from openpilot.system.ui.lib.widget import Widget KEY_FONT_SIZE = 96 DOUBLE_CLICK_THRESHOLD = 0.5 # seconds diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 2e91545d1c..6c5c63be00 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -3,13 +3,14 @@ from threading import Lock from typing import Literal import pyray as rl -from openpilot.system.ui.lib.application import gui_app, Widget +from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.button import ButtonStyle, gui_button from openpilot.system.ui.lib.label import gui_label from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wifi_manager import NetworkInfo, WifiManagerCallbacks, WifiManagerWrapper, SecurityType from openpilot.system.ui.widgets.keyboard import Keyboard from openpilot.system.ui.widgets.confirm_dialog import confirm_dialog +from openpilot.system.ui.lib.widget import Widget NM_DEVICE_STATE_NEED_AUTH = 60 MIN_PASSWORD_LENGTH = 8 diff --git a/system/ui/widgets/option_dialog.py b/system/ui/widgets/option_dialog.py index 6dbc24ee37..59719307f8 100644 --- a/system/ui/widgets/option_dialog.py +++ b/system/ui/widgets/option_dialog.py @@ -1,8 +1,9 @@ import pyray as rl -from openpilot.system.ui.lib.application import Widget, FontWeight +from openpilot.system.ui.lib.application import FontWeight from openpilot.system.ui.lib.button import gui_button, ButtonStyle, TextAlignment from openpilot.system.ui.lib.label import gui_label from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel +from openpilot.system.ui.lib.widget import Widget # Constants MARGIN = 50