some changes

This commit is contained in:
Jason Wen
2026-05-01 00:15:14 -04:00
parent 1086abcf6b
commit d2ab08eaf2
9 changed files with 100 additions and 48 deletions
+2 -8
View File
@@ -69,11 +69,6 @@ class MiciMainLayout(Scroller):
self._onroad_layout.set_click_callback(lambda: self._scroll_to(self._home_layout))
device.add_interactive_timeout_callback(self._on_interactive_timeout)
def _is_on_side_panel(self) -> bool: # TODO: remove sunny not going like it ?
onroad_x = self._onroad_layout.rect.x
current_scroll = self._scroller.scroll_panel.get_offset()
return abs(current_scroll - onroad_x) > self._rect.width / 2
def _scroll_to(self, layout: Widget):
layout_x = int(layout.rect.x)
self._scroller.scroll_to(layout_x, smooth=True)
@@ -105,21 +100,20 @@ class MiciMainLayout(Scroller):
# onroad: after delay, pop nav stack and scroll to onroad
# offroad: immediately scroll to home, but don't pop nav stack (can stay in settings)
if ui_state.started:
self._scroll_to(self._onroad_layout)
self._onroad_time_delay = rl.get_time()
else:
self._scroll_to(self._home_layout)
# FIXME: these two pops can interrupt user interacting in the settings
if self._onroad_time_delay is not None and rl.get_time() - self._onroad_time_delay >= ONROAD_DELAY:
if not self._is_on_side_panel():
if not gui_app.sunnypilot_ui() or self._should_auto_scroll_to_onroad():
gui_app.pop_widgets_to(self, lambda: self._scroll_to(self._onroad_layout))
self._onroad_time_delay = None
# When car leaves standstill, pop nav stack and scroll to onroad
CS = ui_state.sm["carState"]
if not CS.standstill and self._prev_standstill:
if not self._is_on_side_panel():
if not gui_app.sunnypilot_ui() or self._should_auto_scroll_to_onroad():
gui_app.pop_widgets_to(self, lambda: self._scroll_to(self._onroad_layout))
self._prev_standstill = CS.standstill
@@ -0,0 +1,13 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from openpilot.selfdrive.ui.mici.layouts.main import MiciMainLayout
class MiciMainLayoutSP(MiciMainLayout):
def _should_auto_scroll_to_onroad(self) -> bool:
return not self._onroad_layout.is_on_info_panel()
+18 -13
View File
@@ -5,19 +5,20 @@ This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import pyray as rl
from openpilot.system.ui.widgets.scroller import Scroller
from openpilot.system.ui.lib.application import gui_app
from openpilot.selfdrive.ui.mici.onroad.augmented_road_view import AugmentedRoadView
from openpilot.selfdrive.ui.sunnypilot.mici.widgets.scroller_sp import ScrollerSP
from openpilot.selfdrive.ui.sunnypilot.mici.onroad.augmented_road_view import AugmentedRoadViewSP
from openpilot.selfdrive.ui.sunnypilot.mici.layouts.onroad_info_panel import OnroadInfoPanel
if gui_app.sunnypilot_ui():
from openpilot.selfdrive.ui.sunnypilot.mici.onroad.augmented_road_view import AugmentedRoadViewSP as AugmentedRoadView
from openpilot.selfdrive.ui.sunnypilot.mici.layouts.onroad_info_panel import OnroadInfoPanel
CONFIDENCE_BALL_VISIBLE_RATIO = 0.4
HORIZONTAL_SETTLE_PX = 5
HORIZONTAL_RESET_RATIO = 0.5
class OnroadViewContainerSP(Scroller):
class OnroadViewContainerSP(ScrollerSP):
def __init__(self, bookmark_callback=None):
super().__init__(horizontal=False, snap_items=True, spacing=0, pad=0, scroll_indicator=False, edge_shadows=False)
self.road_view = AugmentedRoadView(bookmark_callback=bookmark_callback)
self.road_view = AugmentedRoadViewSP(bookmark_callback=bookmark_callback)
self.onroad_info_panel = OnroadInfoPanel(bookmark_callback=bookmark_callback)
self._scroller.add_widgets([
@@ -25,6 +26,7 @@ class OnroadViewContainerSP(Scroller):
self.onroad_info_panel,
])
self._scroller.set_reset_scroll_at_show(False)
self._scroller.set_scrolling_enabled(lambda: abs(self.rect.x) < HORIZONTAL_SETTLE_PX)
def set_rect(self, rect: rl.Rectangle):
super().set_rect(rect)
@@ -32,21 +34,24 @@ class OnroadViewContainerSP(Scroller):
self.onroad_info_panel.set_rect(rect)
return self
def is_swiping_left(self):
def is_swiping_left(self) -> bool:
return self.road_view.is_swiping_left() or self.onroad_info_panel.is_swiping_left()
def set_click_callback(self, callback):
def set_click_callback(self, callback) -> None:
self.road_view.set_click_callback(callback)
self.onroad_info_panel.set_click_callback(callback)
def is_on_info_panel(self) -> bool:
"""True when scrolled past halfway toward onroad_info_panel (used by main layout
to skip auto-pop-back-to-camera while user is reading the info panel)."""
return abs(self._scroller.scroll_panel.get_offset()) > self._rect.height / 2
def _render(self, rect: rl.Rectangle):
if abs(self.rect.x) > gui_app.width / 2:
if abs(self.rect.x) > gui_app.width * HORIZONTAL_RESET_RATIO:
self._scroller.scroll_panel.set_offset(0)
self._scroller.set_scrolling_enabled(lambda: abs(self.rect.x) < 5)
vertical_offset = self._scroller.scroll_panel.get_offset()
show_ball = abs(vertical_offset) < rect.height * 0.4
show_ball = abs(vertical_offset) < rect.height * CONFIDENCE_BALL_VISIBLE_RATIO
self.road_view.set_show_confidence_ball(show_ball)
super()._render(rect)
@@ -32,6 +32,7 @@ class OnroadInfoPanelColors:
dark_grey: rl.Color = rl.Color(100, 100, 100, 255)
bg_dark: rl.Color = rl.Color(0, 0, 0, 255)
card_bg: rl.Color = rl.Color(50, 50, 50, 200)
badge_bg: rl.Color = rl.Color(60, 60, 60, 255)
COLORS = OnroadInfoPanelColors()
@@ -121,7 +122,7 @@ class OnroadInfoPanel(Widget):
rl.draw_text_ex(self._font_semi_bold, unit, rl.Vector2(left_x, mid_y - 95), 38, 0, COLORS.grey)
rl.draw_text_ex(self._font_bold, speed_val, rl.Vector2(left_x, mid_y - 60), 110, 0, speed_color)
sign_width = 135 if ui_state.is_metric else 135
sign_width = 135
sign_height = 135 if ui_state.is_metric else 175
has_next = self.next_speed_limit > 0 and self.next_speed_limit != self.speed_limit
@@ -172,13 +173,13 @@ class OnroadInfoPanel(Widget):
badge_cx = badge_x + badge_r
badge_cy = badge_y + badge_r
rl.draw_circle(int(badge_cx), int(badge_cy), badge_r + 2, COLORS.dark_grey)
rl.draw_circle(int(badge_cx), int(badge_cy), badge_r, rl.Color(60, 60, 60, 255))
rl.draw_circle(int(badge_cx), int(badge_cy), badge_r, COLORS.badge_bg)
self._draw_text_centered(self._font_bold, offset_val, 24, rl.Vector2(badge_cx, badge_cy), COLORS.white)
else:
mutcd_badge_x = sign_x + sign_width - badge_sz * 0.65
mutcd_badge_y = sign_y - badge_sz * 0.50
badge_rect = rl.Rectangle(mutcd_badge_x, mutcd_badge_y, badge_sz, badge_sz)
rl.draw_rectangle_rounded(badge_rect, 0.25, 10, rl.Color(60, 60, 60, 255))
rl.draw_rectangle_rounded(badge_rect, 0.25, 10, COLORS.badge_bg)
rl.draw_rectangle_rounded_lines_ex(badge_rect, 0.25, 10, 2, COLORS.dark_grey)
self._draw_text_centered(self._font_bold, offset_val, 24, rl.Vector2(mutcd_badge_x + badge_sz / 2, mutcd_badge_y + badge_sz / 2), COLORS.white)
@@ -9,12 +9,17 @@ import pyray as rl
from openpilot.selfdrive.ui.mici.onroad.augmented_road_view import AugmentedRoadView
class _SuppressedConfidenceBall:
def render(self, *_):
pass
class AugmentedRoadViewSP(AugmentedRoadView):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._show_confidence_ball: bool = True
self._real_confidence_ball = self._confidence_ball
self._confidence_ball = _ConfidenceBall()
self._confidence_ball = _SuppressedConfidenceBall()
def set_show_confidence_ball(self, show: bool) -> None:
self._show_confidence_ball = show
@@ -23,8 +28,3 @@ class AugmentedRoadViewSP(AugmentedRoadView):
super()._render(rect)
if self._show_confidence_ball:
self._real_confidence_ball.render(self.rect)
class _ConfidenceBall:
def render(self, *_):
pass
@@ -0,0 +1,34 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import pyray as rl
from openpilot.system.ui.lib.application import MouseEvent
from openpilot.system.ui.lib.scroll_panel2 import GuiScrollPanel2, ScrollState
class GuiScrollPanel2SP(GuiScrollPanel2):
"""Reject orthogonal-dominant drags so nested scrollers (outer horizontal +
inner vertical) don't both engage on a slightly diagonal swipe.
Implemented as a post-super state rollback rather than reimplementing the
PRESSED state machine — keeps stock behaviour authoritative."""
def _handle_mouse_event(self, mouse_event: MouseEvent, bounds: rl.Rectangle, bounds_size: float,
content_size: float) -> None:
pre_state = self._state
super()._handle_mouse_event(mouse_event, bounds, bounds_size, content_size)
if self._state == ScrollState.MANUAL_SCROLL and pre_state == ScrollState.PRESSED and \
self._initial_click_event is not None:
diff_x = abs(mouse_event.pos.x - self._initial_click_event.pos.x)
diff_y = abs(mouse_event.pos.y - self._initial_click_event.pos.y)
along = diff_x if self._horizontal else diff_y
anti = diff_y if self._horizontal else diff_x
if anti > along:
self._state = ScrollState.STEADY
self._velocity = 0.0
self._velocity_buffer.clear()
@@ -0,0 +1,16 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from openpilot.system.ui.widgets.scroller import Scroller
from openpilot.selfdrive.ui.sunnypilot.mici.widgets.scroll_panel_sp import GuiScrollPanel2SP
class ScrollerSP(Scroller):
def __init__(self, **kwargs):
super().__init__(**kwargs)
inner = self._scroller
inner.scroll_panel = GuiScrollPanel2SP(inner._horizontal, handle_out_of_bounds=not inner._snap_items)
+3
View File
@@ -8,6 +8,9 @@ from openpilot.selfdrive.ui.layouts.main import MainLayout
from openpilot.selfdrive.ui.mici.layouts.main import MiciMainLayout
from openpilot.selfdrive.ui.ui_state import ui_state
if gui_app.sunnypilot_ui():
from openpilot.selfdrive.ui.sunnypilot.mici.layouts.main import MiciMainLayoutSP as MiciMainLayout
BIG_UI = gui_app.big_ui()
+4 -18
View File
@@ -147,16 +147,8 @@ class GuiScrollPanel2:
self._initial_click_event = mouse_event
elif self._state == ScrollState.PRESSED:
initial_click_pos = self._initial_click_event.pos
diff_x = abs(mouse_event.pos.x - initial_click_pos.x)
diff_y = abs(mouse_event.pos.y - initial_click_pos.y)
diff = diff_x if self._horizontal else diff_y
anti_diff = diff_y if self._horizontal else diff_x
if diff > MIN_DRAG_PIXELS or anti_diff > MIN_DRAG_PIXELS:
print(f"[{'HORIZ' if self._horizontal else 'VERT '}] SWIPE detected. diff={diff:.1f}, anti={anti_diff:.1f}")
initial_click_pos = self._get_mouse_pos(cast(MouseEvent, self._initial_click_event))
diff = abs(mouse_pos - initial_click_pos)
if mouse_event.left_released:
# Special handling for down and up clicks across two frames
# TODO: not sure what that means or if it's accurate anymore
@@ -165,15 +157,9 @@ class GuiScrollPanel2:
elif diff <= MIN_DRAG_PIXELS:
self._state = ScrollState.STEADY
else:
if diff > anti_diff:
self._state = ScrollState.MANUAL_SCROLL
else:
self._state = ScrollState.STEADY
elif diff > MIN_DRAG_PIXELS and diff > anti_diff:
self._state = ScrollState.MANUAL_SCROLL
elif diff > MIN_DRAG_PIXELS:
self._state = ScrollState.MANUAL_SCROLL
elif anti_diff > MIN_DRAG_PIXELS and anti_diff > diff:
# Orthogonal swipe detected, abort handling
self._state = ScrollState.STEADY
elif self._state == ScrollState.MANUAL_SCROLL:
if mouse_event.left_released: