diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index dbc1919221..f012e8b06b 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -102,10 +102,13 @@ class AugmentedRoadView(CameraView): # Handle click events if no HUD interaction occurred if not self._hud_renderer.handle_mouse_event(): - if self._click_callback and rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): + if self._click_callback is not None and rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): if rl.check_collision_point_rec(rl.get_mouse_position(), self._content_rect): self._click_callback() + def _handle_mouse_release(self, _): + pass + def _draw_border(self, rect: rl.Rectangle): border_color = BORDER_COLORS.get(ui_state.status, BORDER_COLORS[UIStatus.DISENGAGED]) rl.draw_rectangle_lines_ex(rect, UI_BORDER_SIZE, border_color) diff --git a/system/ui/widgets/__init__.py b/system/ui/widgets/__init__.py index cca2cec7ad..6372b1813d 100644 --- a/system/ui/widgets/__init__.py +++ b/system/ui/widgets/__init__.py @@ -15,12 +15,13 @@ class Widget(abc.ABC): def __init__(self): self._rect: rl.Rectangle = rl.Rectangle(0, 0, 0, 0) self._parent_rect: rl.Rectangle = rl.Rectangle(0, 0, 0, 0) - self._is_pressed = [False] * MAX_TOUCH_SLOTS + self.__is_pressed = [False] * MAX_TOUCH_SLOTS # if current mouse/touch down started within the widget's rectangle - self._tracking_is_pressed = [False] * MAX_TOUCH_SLOTS + self.__tracking_is_pressed = [False] * MAX_TOUCH_SLOTS self._enabled: bool | Callable[[], bool] = True self._is_visible: bool | Callable[[], bool] = True self._touch_valid_callback: Callable[[], bool] | None = None + self._click_callback: Callable[[], None] | None = None self._multi_touch = False @property @@ -40,7 +41,7 @@ class Widget(abc.ABC): @property def is_pressed(self) -> bool: - return any(self._is_pressed) + return any(self.__is_pressed) @property def enabled(self) -> bool: @@ -56,6 +57,10 @@ class Widget(abc.ABC): def set_visible(self, visible: bool | Callable[[], bool]) -> None: self._is_visible = visible + def set_click_callback(self, click_callback: Callable[[], None] | None) -> None: + """Set a callback to be called when the widget is clicked.""" + self._click_callback = click_callback + def set_touch_valid_callback(self, touch_callback: Callable[[], bool]) -> None: """Set a callback to determine if the widget can be clicked.""" self._touch_valid_callback = touch_callback @@ -91,28 +96,28 @@ class Widget(abc.ABC): # Allows touch to leave the rect and come back in focus if mouse did not release if mouse_event.left_pressed and self._touch_valid(): if rl.check_collision_point_rec(mouse_event.pos, self._rect): - self._is_pressed[mouse_event.slot] = True - self._tracking_is_pressed[mouse_event.slot] = True + self.__is_pressed[mouse_event.slot] = True + self.__tracking_is_pressed[mouse_event.slot] = True # Callback such as scroll panel signifies user is scrolling elif not self._touch_valid(): - self._is_pressed[mouse_event.slot] = False - self._tracking_is_pressed[mouse_event.slot] = False + self.__is_pressed[mouse_event.slot] = False + self.__tracking_is_pressed[mouse_event.slot] = False elif mouse_event.left_released: - if self._is_pressed[mouse_event.slot] and rl.check_collision_point_rec(mouse_event.pos, self._rect): + if self.__is_pressed[mouse_event.slot] and rl.check_collision_point_rec(mouse_event.pos, self._rect): self._handle_mouse_release(mouse_event.pos) - self._is_pressed[mouse_event.slot] = False - self._tracking_is_pressed[mouse_event.slot] = False + self.__is_pressed[mouse_event.slot] = False + self.__tracking_is_pressed[mouse_event.slot] = False # Mouse/touch is still within our rect elif rl.check_collision_point_rec(mouse_event.pos, self._rect): - if self._tracking_is_pressed[mouse_event.slot]: - self._is_pressed[mouse_event.slot] = True + if self.__tracking_is_pressed[mouse_event.slot]: + self.__is_pressed[mouse_event.slot] = True # Mouse/touch left our rect but may come back into focus later elif not rl.check_collision_point_rec(mouse_event.pos, self._rect): - self._is_pressed[mouse_event.slot] = False + self.__is_pressed[mouse_event.slot] = False return ret @@ -128,6 +133,8 @@ class Widget(abc.ABC): def _handle_mouse_release(self, mouse_pos: MousePos) -> bool: """Optionally handle mouse release events.""" + if self._click_callback: + self._click_callback() return False def show_event(self): diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index a5dab19789..eb38f20597 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -165,7 +165,7 @@ def gui_button( class Button(Widget): def __init__(self, text: str, - click_callback: Callable[[], None] = None, + click_callback: Callable[[], None] | None = None, font_size: int = DEFAULT_BUTTON_FONT_SIZE, font_weight: FontWeight = FontWeight.MEDIUM, button_style: ButtonStyle = ButtonStyle.NORMAL, @@ -190,10 +190,6 @@ class Button(Widget): def set_text(self, text): self._label.set_text(text) - def _handle_mouse_release(self, mouse_pos: MousePos): - if self._click_callback and self.enabled: - self._click_callback() - def _update_state(self): if self.enabled: self._label.set_text_color(BUTTON_TEXT_COLOR[self._button_style]) @@ -215,7 +211,7 @@ class ButtonRadio(Button): def __init__(self, text: str, icon, - click_callback: Callable[[], None] = None, + click_callback: Callable[[], None] | None = None, font_size: int = DEFAULT_BUTTON_FONT_SIZE, text_alignment: TextAlignment = TextAlignment.LEFT, border_radius: int = 10, @@ -230,9 +226,8 @@ class ButtonRadio(Button): self.selected = False def _handle_mouse_release(self, mouse_pos: MousePos): + super()._handle_mouse_release(mouse_pos) self.selected = not self.selected - if self._click_callback: - self._click_callback() def _update_state(self): if self.selected: