From 1ce49be89b776e57d77c97eb5caadbb220641be8 Mon Sep 17 00:00:00 2001 From: firestarsdog <229254897+firestarsdog@users.noreply.github.com> Date: Thu, 25 Jun 2026 18:36:17 -0400 Subject: [PATCH] BigUI WIP: Standardize single pane toggle tiles --- .../layouts/settings/starpilot/aethergrid.py | 83 ++++++++++++++----- .../ui/layouts/settings/starpilot/lateral.py | 2 +- .../ui/layouts/settings/starpilot/sounds.py | 10 +-- .../settings/starpilot/system_settings.py | 8 +- .../ui/layouts/settings/starpilot/vehicle.py | 2 +- selfdrive/ui/tests/test_aethergrid.py | 17 +++- 6 files changed, 83 insertions(+), 39 deletions(-) diff --git a/selfdrive/ui/layouts/settings/starpilot/aethergrid.py b/selfdrive/ui/layouts/settings/starpilot/aethergrid.py index dba73b892..1eaf41550 100644 --- a/selfdrive/ui/layouts/settings/starpilot/aethergrid.py +++ b/selfdrive/ui/layouts/settings/starpilot/aethergrid.py @@ -3736,32 +3736,59 @@ class ToggleTile(AetherTile): title_size = max(20, int(round(24 * text_scale))) if not enabled: - self._draw_text_fit(self._font, self.title, - rl.Vector2(rx + content_pad, ry + int(rh * 0.40)), - max_w, title_size, align_center=True, color=_HUD_TEXT_DIM) - disabled_text = tr(self._disabled_label) if self._disabled_label else tr("LOCKED") + title_lines = self._wrap_text(self._font, self.title, max_w, title_size, max_lines=2) desc_size = max(14, int(round(16 * text_scale))) - self._draw_text_fit(self._font_desc, disabled_text, - rl.Vector2(rx + content_pad, ry + int(rh * 0.68)), - max_w, desc_size, align_center=True, color=_HUD_TEXT_DIM) + 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) + + total_text_h = len(title_lines) * (title_size + 4) + len(desc_lines) * (desc_size + 2) + 6 + start_y = ry + (rh - total_text_h) / 2 + + curr_y = start_y + for line in title_lines: + self._draw_text_fit(self._font, line, rl.Vector2(rx + content_pad, curr_y), max_w, title_size, align_center=True, color=_HUD_TEXT_DIM) + curr_y += title_size + 4 + curr_y += 6 + for line in desc_lines: + self._draw_text_fit(self._font_desc, line, rl.Vector2(rx + content_pad, curr_y), max_w, desc_size, align_center=True, color=_HUD_TEXT_DIM) + curr_y += desc_size + 2 else: title_color = rl.WHITE if active else _HUD_TEXT_DIM if self.desc: - desc_size = max(16, int(round(18 * text_scale))) - self._draw_text_fit(self._font, self.title, - rl.Vector2(rx + content_pad, ry + int(rh * 0.28)), - max_w, title_size, align_center=True, color=title_color) - self._draw_text_fit(self._font_desc, self.desc, - rl.Vector2(rx + content_pad, ry + int(rh * 0.50)), - max_w, desc_size, align_center=True, color=rl.Color(255, 255, 255, 140)) + title_lines = self._wrap_text(self._font, self.title, max_w, title_size, max_lines=2) + desc_size = max(14, int(round(16 * text_scale))) + desc_lines = self._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) + else: + title_y = ry + int(rh * 0.16) + + curr_y = title_y + for line in title_lines: + self._draw_text_fit(self._font, line, rl.Vector2(rx + content_pad, curr_y), max_w, title_size, align_center=True, color=title_color) + curr_y += title_size + 4 + + desc_y = title_y + len(title_lines) * (title_size + 4) + 6 + curr_y = desc_y + for line in desc_lines: + self._draw_text_fit(self._font_desc, line, rl.Vector2(rx + content_pad, curr_y), max_w, desc_size, align_center=True, color=rl.Color(255, 255, 255, 140)) + curr_y += desc_size + 2 + led_cx = rx + rw // 2 - led_cy = ry + int(rh * 0.78) + led_cy = ry + rh - 22 else: - self._draw_text_fit(self._font, self.title, - rl.Vector2(rx + content_pad, ry + int(rh * 0.50)), - max_w, title_size, align_center=True, color=title_color) + title_lines = self._wrap_text(self._font, self.title, max_w, title_size, max_lines=2) + led_cy = ry + rh - 24 + total_text_h = len(title_lines) * (title_size + 4) + title_y = ry + (rh - 24 - total_text_h) / 2 + + curr_y = title_y + for line in title_lines: + self._draw_text_fit(self._font, line, rl.Vector2(rx + content_pad, curr_y), max_w, title_size, align_center=True, color=title_color) + curr_y += title_size + 4 + led_cx = rx + rw // 2 - led_cy = ry + int(rh * 0.75) if active: rl.draw_circle(int(led_cx), int(led_cy), 11, rl.Color(accent.r, accent.g, accent.b, 24)) @@ -5129,7 +5156,7 @@ class AetherSegmentedControl(Widget): class TileGrid(Widget): - def __init__(self, columns: int | None = None, padding: int | None = None, uniform_width: bool = False, min_tile_width: int | None = None, tile_height: float | None = None, force_square: bool = False, carousel_rows: int | None = None, carousel_tile_width: float | None = None): + def __init__(self, columns: int | None = None, padding: int | None = None, uniform_width: bool = False, min_tile_width: int | None = None, tile_height: float | None = None, force_square: bool = False, carousel_rows: int | None = None, carousel_tile_width: float | None = None, min_tile_height: float | None = None, max_tile_height: float | None = None): super().__init__() self._columns = columns self._gap = padding if padding is not None else SPACING.tile_gap @@ -5140,6 +5167,8 @@ class TileGrid(Widget): self.force_square = force_square self.carousel_rows = carousel_rows self.carousel_tile_width = carousel_tile_width + self.min_tile_height = min_tile_height + self.max_tile_height = max_tile_height @property @@ -5215,7 +5244,14 @@ class TileGrid(Widget): col_w = (width - (self._gap * (cols - 1))) / cols h = col_w else: - h = self._tile_height if self._tile_height is not None else 130.0 + if self._tile_height is not None: + h = self._tile_height + else: + h = 140.0 if rows == 1 else (160.0 if rows == 2 else 180.0) + if self.min_tile_height is not None: + h = max(h, self.min_tile_height) + if self.max_tile_height is not None: + h = min(h, self.max_tile_height) return rows * h + self.get_internal_gap_height(count, available_width=width) def measure_width(self) -> float: @@ -5255,6 +5291,7 @@ class TileGrid(Widget): cols = self.get_effective_column_count(rect.width, count) rows = self.get_row_count(count, available_width=rect.width) + if self.force_square: uniform_tile_w = (rect.width - (self._gap * (cols - 1))) / cols tile_h = uniform_tile_w @@ -5263,6 +5300,10 @@ class TileGrid(Widget): tile_h = self._tile_height else: tile_h = (rect.height - (self._gap * (rows - 1))) / rows + if self.min_tile_height is not None: + tile_h = max(self.min_tile_height, tile_h) + if self.max_tile_height is not None: + 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 tile_idx = 0 for r in range(rows): diff --git a/selfdrive/ui/layouts/settings/starpilot/lateral.py b/selfdrive/ui/layouts/settings/starpilot/lateral.py index 34cc8237d..d91df8f2f 100644 --- a/selfdrive/ui/layouts/settings/starpilot/lateral.py +++ b/selfdrive/ui/layouts/settings/starpilot/lateral.py @@ -83,7 +83,7 @@ class SteeringManagerView(PanelManagerView): self._controller = controller self._shell_rect = rl.Rectangle(0, 0, 0, 0) - self._toggle_grid = TileGrid(columns=2, padding=12, min_tile_width=100) + self._toggle_grid = TileGrid(columns=2, padding=12, min_tile_width=100, min_tile_height=130.0, max_tile_height=180.0) self._toggle_grid.set_touch_valid_callback(lambda: self._scroll_panel.is_touch_valid()) self._child(self._toggle_grid) diff --git a/selfdrive/ui/layouts/settings/starpilot/sounds.py b/selfdrive/ui/layouts/settings/starpilot/sounds.py index 71a196e15..680215997 100644 --- a/selfdrive/ui/layouts/settings/starpilot/sounds.py +++ b/selfdrive/ui/layouts/settings/starpilot/sounds.py @@ -76,7 +76,7 @@ class SoundsManagerView(PanelManagerView): ) def _init_toggles(self): - self._toggle_grid = TileGrid(columns=2, padding=12) + self._toggle_grid = TileGrid(columns=2, padding=12, min_tile_height=130.0, max_tile_height=180.0) self._child(self._toggle_grid) self._page_grid = self._toggle_grid @@ -278,14 +278,6 @@ class SoundsManagerView(PanelManagerView): self._left_container_h = max_container_h self._tiles_container_h = max_container_h - # Right column tiles: self._tiles_container_h = tile_grid_container - # Available height for grid: self._tiles_container_h - 24 (padding) - right_available_for_grid = self._tiles_container_h - 24 - # The grid has 3 rows of tiles, with 2 gaps of 12px = 24px - right_available_for_tile_rows = right_available_for_grid - 24 - tile_h = max(80.0, min(130.0, right_available_for_tile_rows / 3)) - self._toggle_grid._tile_height = tile_h - return self._compute_two_column_height(section_overhead + max_container_h) def _draw_header(self, rect: rl.Rectangle): diff --git a/selfdrive/ui/layouts/settings/starpilot/system_settings.py b/selfdrive/ui/layouts/settings/starpilot/system_settings.py index cfeda9a3d..430d1a504 100644 --- a/selfdrive/ui/layouts/settings/starpilot/system_settings.py +++ b/selfdrive/ui/layouts/settings/starpilot/system_settings.py @@ -305,7 +305,7 @@ class SystemSettingsManagerView(PanelManagerView): self._basics_tile_grid_h = 0.0 - self._connectivity_tile_grid = TileGrid(columns=2, padding=12) + self._connectivity_tile_grid = TileGrid(columns=2, padding=12, min_tile_height=130.0, max_tile_height=180.0) for toggle_def in self._toggle_defs: tile = self._make_toggle_tile(toggle_def) self._connectivity_tile_grid.add_tile(tile) @@ -512,11 +512,7 @@ class SystemSettingsManagerView(PanelManagerView): self._system_max_container_h = max_container_h - # Scale connectivity tiles - right_available_for_grid = self._system_max_container_h - 24 - right_available_for_tile_rows = right_available_for_grid - 12 - tile_h = max(80.0, min(130.0, right_available_for_tile_rows / 2)) - self._connectivity_tile_grid._tile_height = tile_h + pass return self._compute_two_column_height(max_container_h) else: diff --git a/selfdrive/ui/layouts/settings/starpilot/vehicle.py b/selfdrive/ui/layouts/settings/starpilot/vehicle.py index 983632e1d..3590caec7 100644 --- a/selfdrive/ui/layouts/settings/starpilot/vehicle.py +++ b/selfdrive/ui/layouts/settings/starpilot/vehicle.py @@ -105,7 +105,7 @@ class VehicleSettingsManagerView(PanelManagerView): self._controller = controller self._shell_rect = rl.Rectangle(0, 0, 0, 0) - self._toggle_grid = TileGrid(columns=2, padding=12, min_tile_width=100) + self._toggle_grid = TileGrid(columns=2, padding=12, min_tile_width=100, min_tile_height=130.0, max_tile_height=180.0) self.register_page_grid(self._toggle_grid) self._last_make = "" diff --git a/selfdrive/ui/tests/test_aethergrid.py b/selfdrive/ui/tests/test_aethergrid.py index b1b0f499a..6c5aacfc3 100644 --- a/selfdrive/ui/tests/test_aethergrid.py +++ b/selfdrive/ui/tests/test_aethergrid.py @@ -414,6 +414,21 @@ class TestAethergridContracts(unittest.TestCase): h = grid.measure_height(500) self.assertEqual(h, 740) + def test_tile_grid_measure_height_with_min_max_clamping(self): + mod = _import_aethergrid() + grid = mod.TileGrid(columns=2, padding=10, tile_height=None, min_tile_height=100.0, max_tile_height=150.0) + for _ in range(5): + grid.add_tile(RenderSpy()) + + h = grid.measure_height(500) + self.assertEqual(h, 790) + + spy = RenderSpy() + grid.add_tile(spy) + grid.render(mod.rl.Rectangle(0, 0, 500, 1000)) + self.assertTrue(spy.rects) + self.assertEqual(spy.rects[0].height, 150.0) + def test_tile_grid_measure_height_default_fallback(self): mod = _import_aethergrid() grid = mod.TileGrid(columns=2, padding=10, tile_height=None) @@ -421,7 +436,7 @@ class TestAethergridContracts(unittest.TestCase): grid.add_tile(RenderSpy()) h = grid.measure_height(500) - self.assertEqual(h, 690) + self.assertEqual(h, 940) def test_tile_grid_render_top_left_aligned_with_tile_height(self): mod = _import_aethergrid()