diff --git a/selfdrive/ui/layouts/settings/starpilot/aethergrid.py b/selfdrive/ui/layouts/settings/starpilot/aethergrid.py index 6d3d778da..1852df3b5 100644 --- a/selfdrive/ui/layouts/settings/starpilot/aethergrid.py +++ b/selfdrive/ui/layouts/settings/starpilot/aethergrid.py @@ -3658,115 +3658,6 @@ class AetherCategoryDrawer(AetherSettingsView): AetherCategoryTileView = AetherCategoryDrawer -# ── AetherTransitionManager — Spatial Parallax Page Transitions ── - -class AetherTransitionManager: - def __init__(self, duration: float = 0.24): - self.duration = duration - self._time = 0.0 - self._progress = 1.0 - self._direction = 1 # 1 = forward (right to left), -1 = backward (left to right) - self._active = False - self._outgoing_render_fn = None - self._incoming_render_fn = None - - def start(self, outgoing_render_fn, incoming_render_fn, direction: int): - self._outgoing_render_fn = None - self._incoming_render_fn = None - self._direction = direction - self._time = 0.0 - self._progress = 1.0 - self._active = False - - def is_animating(self) -> bool: - return self._active - - def update(self, dt: float): - if not self._active: - return - # Cap dt to avoid large visual jumps on frame spikes - dt = min(0.016, dt) - self._time += dt - - t = self._time / self.duration - if t >= 1.0: - t = 1.0 - self._progress = 1.0 - self._active = False - self._outgoing_render_fn = None - self._incoming_render_fn = None - else: - # Ease-in-out cubic curve (zero initial velocity, smooth acceleration & deceleration) - if t < 0.5: - self._progress = 4.0 * t * t * t - else: - self._progress = 1.0 - (-2.0 * t + 2.0) ** 3 / 2.0 - - def render(self, rect: rl.Rectangle): - if not self._active: - return - - global _GLOBAL_SCISSOR_LIMIT - _GLOBAL_SCISSOR_LIMIT = rect - - # 1. Enforce strict content boundary clipping to block drawing over the sidebar on the left - rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(rect.height)) - - # Clear the transition area with solid background to prevent ghosting/bleeding of transparent areas - rl.draw_rectangle_rec(rect, rl.Color(12, 10, 18, 255)) - - # Use progress directly since the exponential decay is already eased - eased = self._progress - - # Outgoing and incoming rect calculations - if self._direction == 1: - # Forward: incoming slides right to left, outgoing slides left slightly (parallax) - out_x = rect.x - 0.25 * rect.width * eased - in_x = rect.x + rect.width * (1.0 - eased) - else: - # Backward: incoming slides left to right, outgoing slides right slightly (parallax) - out_x = rect.x + 0.25 * rect.width * eased - in_x = rect.x - rect.width * (1.0 - eased) - - out_rect = rl.Rectangle(out_x, rect.y, rect.width, rect.height) - in_rect = rl.Rectangle(in_x, rect.y, rect.width, rect.height) - - # 2. Render outgoing content - if self._outgoing_render_fn: - self._outgoing_render_fn(out_rect) - - # Re-assert content scissor clip after child finishes rendering to override any nested disable calls - rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(rect.height)) - - # 3. Draw dimming overlay on outgoing content - dim_alpha = int(120 * (1.0 - eased)) - if dim_alpha > 0: - rl.draw_rectangle_rec(out_rect, rl.Color(0, 0, 0, dim_alpha)) - - # 4. Render incoming content - if self._incoming_render_fn: - self._incoming_render_fn(in_rect) - - # Re-assert content scissor clip after child finishes rendering - rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(rect.height)) - - # 5. Draw edge shadow/glow on incoming edge to separate layers - shadow_w = 40 - if self._direction == 1: - rl.draw_rectangle_gradient_h( - int(in_rect.x - shadow_w), int(in_rect.y), shadow_w, int(in_rect.height), - rl.Color(0, 0, 0, 0), rl.Color(0, 0, 0, 100) - ) - else: - rl.draw_rectangle_gradient_h( - int(in_rect.x + in_rect.width), int(in_rect.y), shadow_w, int(in_rect.height), - rl.Color(0, 0, 0, 100), rl.Color(0, 0, 0, 0) - ) - - # 6. Disable scissor globally when exiting transition rendering and clear global limit - rl.end_scissor_mode() - _GLOBAL_SCISSOR_LIMIT = None - class AetherTile(Widget): def __init__(self, surface_color: rl.Color | str | None = None, on_click: Callable | None = None): @@ -4080,10 +3971,10 @@ class ToggleTile(AetherTile): title_size = max(32, int(round(41 * text_scale))) if not enabled: - title_lines = self._wrap_text(self._font, self.title, max_w, title_size, max_lines=2) + title_lines = wrap_text(self._font, self.title, max_w, title_size, max_lines=2) desc_size = max(25, int(round(26 * text_scale))) disabled_text = tr(self._disabled_label) if self._disabled_label else tr("LOCKED") - desc_lines = self._wrap_text(self._font_desc, disabled_text, max_w, desc_size, max_lines=2) + desc_lines = wrap_text(self._font_desc, disabled_text, max_w, desc_size, max_lines=2) total_text_h = len(title_lines) * (title_size + 4) + len(desc_lines) * (desc_size + 2) + 6 start_y = ry + (rh - total_text_h) / 2 @@ -4099,9 +3990,9 @@ class ToggleTile(AetherTile): else: title_color = rl.WHITE if active else _HUD_TEXT_DIM if self.desc: - title_lines = self._wrap_text(self._font, self.title, max_w, title_size, max_lines=2) + title_lines = wrap_text(self._font, self.title, max_w, title_size, max_lines=2) desc_size = max(25, int(round(26 * text_scale))) - desc_lines = self._wrap_text(self._font_desc, self.desc, max_w, desc_size, max_lines=2) + desc_lines = wrap_text(self._font_desc, self.desc, max_w, desc_size, max_lines=2) if len(title_lines) == 1: title_y = ry + int(rh * 0.22) @@ -4122,7 +4013,7 @@ class ToggleTile(AetherTile): led_cx = rx + rw // 2 led_cy = ry + rh - 32 else: - title_lines = self._wrap_text(self._font, self.title, max_w, title_size, max_lines=2) + title_lines = wrap_text(self._font, self.title, max_w, title_size, max_lines=2) led_cy = ry + rh - 35 total_text_h = len(title_lines) * (title_size + 4) title_y = ry + (rh - 24 - total_text_h) / 2 @@ -5557,7 +5448,7 @@ class TileGrid(Widget): tile_h = min(self.max_tile_height, tile_h) uniform_tile_w = (rect.width - (self._gap * (cols - 1))) / cols if self._uniform_width else 0 content_height = rows * tile_h + max(0, rows - 1) * self._gap - y_offset = 0 + y_offset = (rect.height - content_height) / 2 if content_height < rect.height else 0 tile_idx = 0 for r in range(rows): remaining = count - tile_idx diff --git a/selfdrive/ui/layouts/settings/starpilot/main_panel.py b/selfdrive/ui/layouts/settings/starpilot/main_panel.py index d74d59854..b234871cb 100644 --- a/selfdrive/ui/layouts/settings/starpilot/main_panel.py +++ b/selfdrive/ui/layouts/settings/starpilot/main_panel.py @@ -17,7 +17,7 @@ from openpilot.selfdrive.ui.layouts.settings.starpilot.system_settings import St from openpilot.selfdrive.ui.layouts.settings.starpilot.appearance import StarPilotAppearanceLayout from openpilot.selfdrive.ui.layouts.settings.starpilot.vehicle import StarPilotVehicleSettingsLayout -from openpilot.selfdrive.ui.layouts.settings.starpilot.aethergrid import TileGrid, HubTile, SPACING, BreadcrumbController, AETHER_LIST_METRICS, AetherListColors, draw_rounded_fill, draw_rounded_stroke, AetherTransitionManager +from openpilot.selfdrive.ui.layouts.settings.starpilot.aethergrid import TileGrid, HubTile, SPACING, BreadcrumbController, AETHER_LIST_METRICS, AetherListColors, draw_rounded_fill, draw_rounded_stroke class StarPilotLayout(Widget): CATEGORIES = [ @@ -92,23 +92,6 @@ class StarPilotLayout(Widget): self._breadcrumbs = BreadcrumbController() self._main_grid = TileGrid(columns=None, padding=SPACING.tile_gap) self._rebuild_grid() - self._transition_manager = AetherTransitionManager() - - def _make_render_fn(self, panel_type: StarPilotPanelType) -> Callable[[rl.Rectangle], None]: - if panel_type == StarPilotPanelType.MAIN: - def render_main(rect: rl.Rectangle): - metrics = AETHER_LIST_METRICS - shell_w = min(rect.width - metrics.outer_margin_x * 2, metrics.max_content_width) - shell_x = rect.x + (rect.width - shell_w) / 2 - grid_rect = rl.Rectangle( - shell_x, rect.y + metrics.outer_margin_y, - shell_w, rect.height - metrics.outer_margin_y * 2 - ) - self._main_grid.render(grid_rect) - return render_main - else: - panel = self._panels[panel_type] - return lambda r: panel.instance.render(r) if panel.instance else None def set_depth_callback(self, callback: Callable): self._depth_callback = callback @@ -247,13 +230,6 @@ class StarPilotLayout(Widget): def _set_current_panel(self, panel_type: StarPilotPanelType): if panel_type != self._current_panel: - old_panel = self._current_panel - direction = -1 if panel_type == StarPilotPanelType.MAIN else 1 - self._transition_manager.start( - self._make_render_fn(old_panel), - self._make_render_fn(panel_type), - direction - ) if self._current_panel != StarPilotPanelType.MAIN: old = self._panels[self._current_panel].instance @@ -304,29 +280,19 @@ class StarPilotLayout(Widget): crumb_rect = rl.Rectangle(glass_rect.x, glass_rect.y, glass_rect.width, glass_rect.height) self._breadcrumbs.draw(crumb_rect) - # Update transitions - self._transition_manager.update(rl.get_frame_time()) - # 4. Render active content panel - if self._transition_manager.is_animating(): - self._transition_manager.render(content_rect) + if self._current_panel == StarPilotPanelType.MAIN: + grid_rect = rl.Rectangle(shell_x, content_rect.y + AETHER_LIST_METRICS.outer_margin_y, shell_w, content_rect.height - AETHER_LIST_METRICS.outer_margin_y * 2) + self._main_grid.render(grid_rect) else: - if self._current_panel == StarPilotPanelType.MAIN: - grid_rect = rl.Rectangle(shell_x, content_rect.y + AETHER_LIST_METRICS.outer_margin_y, shell_w, content_rect.height - AETHER_LIST_METRICS.outer_margin_y * 2) - self._main_grid.render(grid_rect) - else: - panel = self._panels[self._current_panel] - if panel.instance: - panel.instance.render(content_rect) + panel = self._panels[self._current_panel] + if panel.instance: + panel.instance.render(content_rect) def _handle_mouse_press(self, mouse_pos: MousePos): - if self._transition_manager.is_animating(): - return self._breadcrumbs.init_interaction(mouse_pos) def _handle_mouse_release(self, mouse_pos: MousePos): - if self._transition_manager.is_animating(): - return action = self._breadcrumbs.finish_interaction(mouse_pos) if action: self._breadcrumbs.handle_click(action) diff --git a/selfdrive/ui/layouts/settings/starpilot/panel.py b/selfdrive/ui/layouts/settings/starpilot/panel.py index 0b09f8f32..e377dc64b 100644 --- a/selfdrive/ui/layouts/settings/starpilot/panel.py +++ b/selfdrive/ui/layouts/settings/starpilot/panel.py @@ -11,7 +11,7 @@ from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.widgets import DialogResult, Widget from openpilot.system.ui.widgets.option_dialog import MultiOptionDialog -from openpilot.selfdrive.ui.layouts.settings.starpilot.aethergrid import TileGrid, HubTile, ToggleTile, ValueTile, SliderTile, SPACING, AetherSliderDialog, AetherTransitionManager, AetherListColors +from openpilot.selfdrive.ui.layouts.settings.starpilot.aethergrid import TileGrid, HubTile, ToggleTile, ValueTile, SliderTile, SPACING, AetherSliderDialog, AetherListColors from openpilot.selfdrive.ui.layouts.settings.starpilot.sectioned_panel import SectionedTileLayout, TileSection import time @@ -118,7 +118,6 @@ class StarPilotPanel(Widget): self._sectioned_grid = None self.CATEGORIES = [] self.SECTIONS = [] - self._transition_manager = AetherTransitionManager() def set_navigate_callback(self, callback: Callable): self._navigate_callback = callback @@ -227,50 +226,19 @@ class StarPilotPanel(Widget): if tile is not None: self._tile_grid.add_tile(tile) - def _make_render_fn(self, sub_panel: str) -> Callable[[rl.Rectangle], None]: - if sub_panel and sub_panel in self._sub_panels: - panel = self._sub_panels[sub_panel] - return lambda r: panel.render(r) - else: - def render_base(rect: rl.Rectangle): - if self.SECTIONS and self._sectioned_grid: - self._sectioned_grid.render(rect) - elif self.CATEGORIES and self._tile_grid: - self._tile_grid.render(rect) - elif self._scroller: - self._scroller.render(rect) - return render_base - def _navigate_to(self, sub_panel: str): if sub_panel != self._current_sub_panel: - old_sub = self._current_sub_panel - self._transition_manager.start( - self._make_render_fn(old_sub), - self._make_render_fn(sub_panel), - 1 - ) self._current_sub_panel = sub_panel if self._navigate_callback: self._navigate_callback(sub_panel) def _go_back(self): if self._current_sub_panel: - old_sub = self._current_sub_panel - self._transition_manager.start( - self._make_render_fn(old_sub), - self._make_render_fn(""), - -1 - ) self._current_sub_panel = "" if self._back_callback: self._back_callback() def _render(self, rect: rl.Rectangle): - self._transition_manager.update(rl.get_frame_time()) - if self._transition_manager.is_animating(): - self._transition_manager.render(rect) - return - if self._current_sub_panel and self._current_sub_panel in self._sub_panels: self._sub_panels[self._current_sub_panel].render(rect) elif self.SECTIONS and self._sectioned_grid: @@ -281,18 +249,12 @@ class StarPilotPanel(Widget): self._scroller.render(rect) def _handle_mouse_press(self, mouse_pos): - if self._transition_manager.is_animating(): - return super()._handle_mouse_press(mouse_pos) def _handle_mouse_release(self, mouse_pos): - if self._transition_manager.is_animating(): - return super()._handle_mouse_release(mouse_pos) def _handle_mouse_event(self, mouse_event): - if self._transition_manager.is_animating(): - return super()._handle_mouse_event(mouse_event) def show_event(self):