From 0b0fb3957a693e21c5649115ebf853c235d69db2 Mon Sep 17 00:00:00 2001 From: firestarsdog <229254897+firestarsdog@users.noreply.github.com> Date: Tue, 7 Apr 2026 01:13:27 -0400 Subject: [PATCH] BigUI WIP: More Tab Stuff (cherry picked from commit 9ba41d1a4a46cb95c5ef07901593e72d3d00ad7a) --- .../layouts/settings/starpilot/aethergrid.py | 17 ++- .../ui/layouts/settings/starpilot/data.py | 37 ++--- .../ui/layouts/settings/starpilot/lateral.py | 55 ++++--- .../settings/starpilot/longitudinal.py | 107 +++++++------- .../layouts/settings/starpilot/main_panel.py | 9 +- .../ui/layouts/settings/starpilot/panel.py | 17 +++ .../ui/layouts/settings/starpilot/sounds.py | 8 +- .../settings/starpilot/system_settings.py | 8 +- .../settings/starpilot/tabbed_panel.py | 119 +++++++++++++++ .../ui/layouts/settings/starpilot/themes.py | 8 +- .../ui/layouts/settings/starpilot/visuals.py | 137 ++++-------------- 11 files changed, 310 insertions(+), 212 deletions(-) create mode 100644 selfdrive/ui/layouts/settings/starpilot/tabbed_panel.py diff --git a/selfdrive/ui/layouts/settings/starpilot/aethergrid.py b/selfdrive/ui/layouts/settings/starpilot/aethergrid.py index 77342bd7c..47fc86488 100644 --- a/selfdrive/ui/layouts/settings/starpilot/aethergrid.py +++ b/selfdrive/ui/layouts/settings/starpilot/aethergrid.py @@ -568,8 +568,8 @@ class RadioTileGroup(Widget): self._option_targets.append(0.0) for i in range(len(self._option_offsets)): self._option_offsets[i] += (self._option_targets[i] - self._option_offsets[i]) * (1 - math.exp(-dt / PLATE_TAU)) - padding = 20 - option_w = 240 if len(self.options) <= 3 else 200 + padding = 16 + option_w = 240 if len(self.options) <= 3 else 188 total_width = len(self.options) * option_w + max(0, len(self.options) - 1) * padding if self.title: title_size = measure_text_cached(self._font_title, self.title, 40) @@ -591,10 +591,17 @@ class RadioTileGroup(Widget): rl.draw_rectangle_rounded(face_rect, TILE_RADIUS, 10, color) rl.draw_rectangle_rounded(rl.Rectangle(face_rect.x + 1, face_rect.y + 1, face_rect.width - 2, face_rect.height - 2), TILE_RADIUS, 10, rl.Color(0, 0, 0, 80)) rl.draw_rectangle_rounded(rl.Rectangle(face_rect.x, face_rect.y, face_rect.width - 1.5, face_rect.height - 1.5), TILE_RADIUS, 10, rl.Color(255, 255, 255, 110)) - ts = measure_text_cached(self._font, opt.upper(), 35) + font_size = 30 + spacing = round(font_size * 0.08) + max_width = r.width - 28 + ts = measure_text_cached(self._font, opt, font_size, spacing=spacing) + while font_size > 22 and ts.x > max_width: + font_size -= 1 + spacing = round(font_size * 0.08) + ts = measure_text_cached(self._font, opt, font_size, spacing=spacing) text_pos = rl.Vector2(face_x + (r.width - ts.x) / 2, face_y + (r.height - ts.y) / 2) - rl.draw_text_ex(self._font, opt.upper(), rl.Vector2(round(text_pos.x + 1), round(text_pos.y + 2)), 35, 0, rl.Color(0, 0, 0, 90)) - rl.draw_text_ex(self._font, opt.upper(), rl.Vector2(round(text_pos.x), round(text_pos.y)), 35, 0, rl.WHITE) + rl.draw_text_ex(self._font, opt, rl.Vector2(round(text_pos.x + 1), round(text_pos.y + 2)), font_size, spacing, rl.Color(0, 0, 0, 90)) + rl.draw_text_ex(self._font, opt, rl.Vector2(round(text_pos.x), round(text_pos.y)), font_size, spacing, rl.WHITE) class TileGrid(Widget): diff --git a/selfdrive/ui/layouts/settings/starpilot/data.py b/selfdrive/ui/layouts/settings/starpilot/data.py index 29f21528b..529151a12 100644 --- a/selfdrive/ui/layouts/settings/starpilot/data.py +++ b/selfdrive/ui/layouts/settings/starpilot/data.py @@ -30,7 +30,7 @@ class StarPilotDataLayout(StarPilotPanel): self.CATEGORIES = [ {"title": tr_noop("Manage Backups"), "panel": "backups", "icon": "toggle_icons/icon_system.png", "color": "#D43D8A"}, {"title": tr_noop("Toggle Backups"), "panel": "toggle_backups", "icon": "toggle_icons/icon_system.png", "color": "#D43D8A"}, - {"title": tr_noop("Manage Storage"), "panel": "storage", "icon": "toggle_icons/icon_system.png", "color": "#D43D8A"}, + {"title": tr_noop("Driving Data Storage"), "type": "value", "get_value": self._get_storage, "on_click": lambda: None, "icon": "toggle_icons/icon_system.png", "color": "#D43D8A", "is_enabled": lambda: False}, { "title": tr_noop("Delete Driving Data"), "type": "hub", @@ -50,7 +50,6 @@ class StarPilotDataLayout(StarPilotPanel): self._sub_panels = { "backups": StarPilotBackupsLayout(), "toggle_backups": StarPilotToggleBackupsLayout(), - "storage": StarPilotStorageLayout(), } self._tile_grid = TileGrid(columns=2, padding=20, uniform_width=True) @@ -89,6 +88,18 @@ class StarPilotDataLayout(StarPilotPanel): gui_app.set_modal_overlay(ConfirmDialog(tr("Delete all error logs?"), tr("Delete"), on_close=_do_delete)) + def _get_storage(self): + paths = ["/data/media/0/osm/offline", "/data/media/0/realdata", "/data/backups"] + total = 0 + for p in paths: + pp = Path(p) + if pp.exists(): + total += sum(f.stat().st_size for f in pp.rglob('*') if f.is_file()) + mb = total / (1024 * 1024) + if mb > 1024: + return f"{(mb / 1024):.2f} GB" + return f"{mb:.2f} MB" + class StarPilotBackupsLayout(StarPilotPanel): def __init__(self): @@ -231,25 +242,3 @@ class StarPilotToggleBackupsLayout(StarPilotPanel): self._rebuild_grid() gui_app.set_modal_overlay(SelectionDialog(tr("Delete Toggle Backup"), backups, on_close=_on_select)) - - -class StarPilotStorageLayout(StarPilotPanel): - def __init__(self): - super().__init__() - self._tile_grid = TileGrid(columns=2, padding=20, uniform_width=True) - self.CATEGORIES = [ - {"title": tr_noop("Driving Data"), "type": "value", "get_value": self._get_storage, "on_click": lambda: None, "color": "#D43D8A", "is_enabled": lambda: False}, - ] - self._rebuild_grid() - - def _get_storage(self): - paths = ["/data/media/0/osm/offline", "/data/media/0/realdata", "/data/backups"] - total = 0 - for p in paths: - pp = Path(p) - if pp.exists(): - total += sum(f.stat().st_size for f in pp.rglob('*') if f.is_file()) - mb = total / (1024 * 1024) - if mb > 1024: - return f"{(mb / 1024):.2f} GB" - return f"{mb:.2f} MB" diff --git a/selfdrive/ui/layouts/settings/starpilot/lateral.py b/selfdrive/ui/layouts/settings/starpilot/lateral.py index 7ca1b2ab8..abe79f229 100644 --- a/selfdrive/ui/layouts/settings/starpilot/lateral.py +++ b/selfdrive/ui/layouts/settings/starpilot/lateral.py @@ -5,7 +5,8 @@ from openpilot.system.ui.lib.multilang import tr, tr_noop from openpilot.system.ui.widgets import DialogResult from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog from openpilot.system.ui.widgets.selection_dialog import SelectionDialog -from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel +from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel, create_tile_panel +from openpilot.selfdrive.ui.layouts.settings.starpilot.tabbed_panel import TabSectionSpec, TabbedSectionHost from openpilot.selfdrive.ui.layouts.settings.starpilot.aethergrid import AetherSliderDialog class StarPilotAdvancedLateralLayout(StarPilotPanel): @@ -124,21 +125,41 @@ class StarPilotLateralQOLLayout(StarPilotPanel): class StarPilotLateralLayout(StarPilotPanel): def __init__(self): super().__init__() - self._sub_panels = { - "advanced_lateral": StarPilotAdvancedLateralLayout(), - "always_on_lateral": StarPilotAlwaysOnLateralLayout(), - "lane_changes": StarPilotLaneChangesLayout(), - "lateral_tune": StarPilotLateralTuneLayout(), - "qol": StarPilotLateralQOLLayout(), - } - self.CATEGORIES = [ - {"title": tr_noop("Advanced Lateral Tuning"), "panel": "advanced_lateral", "icon": "toggle_icons/icon_advanced_lateral_tune.png", "color": "#597497"}, + steering_panel = create_tile_panel([ {"title": tr_noop("Always On Lateral"), "panel": "always_on_lateral", "icon": "toggle_icons/icon_always_on_lateral.png", "color": "#597497"}, - {"title": tr_noop("Lane Changes"), "panel": "lane_changes", "icon": "toggle_icons/icon_lane.png", "color": "#597497"}, - {"title": tr_noop("Lateral Tuning"), "panel": "lateral_tune", "icon": "toggle_icons/icon_lateral_tune.png", "color": "#597497"}, {"title": tr_noop("Quality of Life"), "panel": "qol", "icon": "toggle_icons/icon_quality_of_life.png", "color": "#597497"}, - ] - for name, panel in self._sub_panels.items(): - if hasattr(panel, 'set_navigate_callback'): panel.set_navigate_callback(self._navigate_to) - if hasattr(panel, 'set_back_callback'): panel.set_back_callback(self._go_back) - self._rebuild_grid() + ], { + "always_on_lateral": StarPilotAlwaysOnLateralLayout(), + "qol": StarPilotLateralQOLLayout(), + }) + tune_panel = create_tile_panel([ + {"title": tr_noop("Advanced Tuning"), "panel": "advanced_lateral", "icon": "toggle_icons/icon_advanced_lateral_tune.png", "color": "#597497"}, + {"title": tr_noop("Lateral Tuning"), "panel": "lateral_tune", "icon": "toggle_icons/icon_lateral_tune.png", "color": "#597497"}, + ], { + "advanced_lateral": StarPilotAdvancedLateralLayout(), + "lateral_tune": StarPilotLateralTuneLayout(), + }) + + self._section_tabs = TabbedSectionHost([ + TabSectionSpec("steering", "Steering", steering_panel), + TabSectionSpec("lane", "Lane", StarPilotLaneChangesLayout()), + TabSectionSpec("tune", "Tune", tune_panel), + ]) + + def set_navigate_callback(self, callback): + self._section_tabs.set_navigate_callback(callback) + + def set_back_callback(self, callback): + self._section_tabs.set_back_callback(callback) + + def _render(self, rect): + self._section_tabs.render(rect) + + def set_current_sub_panel(self, sub_panel: str): + self._section_tabs.set_current_sub_panel(sub_panel) + + def show_event(self): + self._section_tabs.show_event() + + def hide_event(self): + self._section_tabs.hide_event() diff --git a/selfdrive/ui/layouts/settings/starpilot/longitudinal.py b/selfdrive/ui/layouts/settings/starpilot/longitudinal.py index d2e492904..416455e89 100644 --- a/selfdrive/ui/layouts/settings/starpilot/longitudinal.py +++ b/selfdrive/ui/layouts/settings/starpilot/longitudinal.py @@ -6,56 +6,68 @@ from openpilot.system.ui.widgets import DialogResult from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog from openpilot.system.ui.widgets.selection_dialog import SelectionDialog from openpilot.system.ui.widgets.input_dialog import InputDialog -from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel +from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel, create_tile_panel +from openpilot.selfdrive.ui.layouts.settings.starpilot.tabbed_panel import TabSectionSpec, TabbedSectionHost from openpilot.selfdrive.ui.layouts.settings.starpilot.aethergrid import AetherSliderDialog class StarPilotLongitudinalLayout(StarPilotPanel): def __init__(self): super().__init__() - self._sub_panels = { - "advanced": StarPilotAdvancedLongitudinalLayout(), - "conditional": StarPilotConditionalExperimentalLayout(), - "curve": StarPilotCurveSpeedLayout(), - "personalities": StarPilotPersonalitiesLayout(), + tune_panel = create_tile_panel([ + {"title": tr_noop("Longitudinal Tuning"), "panel": "tuning", "icon": "toggle_icons/icon_longitudinal_tune.png", "color": "#597497"}, + {"title": tr_noop("Advanced Tuning"), "panel": "advanced", "icon": "toggle_icons/icon_advanced_longitudinal_tune.png", "color": "#597497"}, + {"title": tr_noop("Driving Personalities"), "panel": "personalities", "icon": "toggle_icons/icon_personality.png", "color": "#597497"}, + ], { "tuning": StarPilotLongitudinalTuneLayout(), - "qol": StarPilotLongitudinalQOLLayout(), - "slc": StarPilotSpeedLimitControllerLayout(), - "weather": StarPilotWeatherLayout(), - # Personality Sub-panels + "advanced": StarPilotAdvancedLongitudinalLayout(), + "personalities": StarPilotPersonalitiesLayout(), "traffic_personality": StarPilotPersonalityProfileLayout("Traffic"), "aggressive_personality": StarPilotPersonalityProfileLayout("Aggressive"), "standard_personality": StarPilotPersonalityProfileLayout("Standard"), "relaxed_personality": StarPilotPersonalityProfileLayout("Relaxed"), - # SLC Sub-panels - "slc_offsets": StarPilotSLCOffsetsLayout(), - "slc_qol": StarPilotSLCQOLLayout(), - "slc_visuals": StarPilotSLCVisualsLayout(), - # Weather Sub-panels + }) + + adaptive_panel = create_tile_panel([ + {"title": tr_noop("Conditional Experimental"), "panel": "conditional", "icon": "toggle_icons/icon_conditional.png", "color": "#597497"}, + {"title": tr_noop("Curve Speed"), "panel": "curve", "icon": "toggle_icons/icon_speed_map.png", "color": "#597497"}, + {"title": tr_noop("Weather"), "panel": "weather", "icon": "toggle_icons/icon_rainbow.png", "color": "#597497"}, + ], { + "conditional": StarPilotConditionalExperimentalLayout(), + "curve": StarPilotCurveSpeedLayout(), + "weather": StarPilotWeatherLayout(), "low_visibility": StarPilotWeatherBase("LowVisibility"), "rain": StarPilotWeatherBase("Rain"), "rainstorm": StarPilotWeatherBase("RainStorm"), "snow": StarPilotWeatherBase("Snow"), - } + }) - self.CATEGORIES = [ - {"title": tr_noop("Advanced Longitudinal Tuning"), "panel": "advanced", "icon": "toggle_icons/icon_advanced_longitudinal_tune.png", "color": "#597497"}, - {"title": tr_noop("Conditional Experimental Mode"), "panel": "conditional", "icon": "toggle_icons/icon_conditional.png", "color": "#597497"}, - {"title": tr_noop("Curve Speed Controller"), "panel": "curve", "icon": "toggle_icons/icon_speed_map.png", "color": "#597497"}, - {"title": tr_noop("Driving Personalities"), "panel": "personalities", "icon": "toggle_icons/icon_personality.png", "color": "#597497"}, - {"title": tr_noop("Longitudinal Tuning"), "panel": "tuning", "icon": "toggle_icons/icon_longitudinal_tune.png", "color": "#597497"}, - {"title": tr_noop("Quality of Life"), "panel": "qol", "icon": "toggle_icons/icon_quality_of_life.png", "color": "#597497"}, - {"title": tr_noop("Speed Limit Controller"), "panel": "slc", "icon": "toggle_icons/icon_speed_limit.png", "color": "#597497"}, - {"title": tr_noop("Weather"), "panel": "weather", "icon": "toggle_icons/icon_rainbow.png", "color": "#597497"}, - ] - - for name, panel in self._sub_panels.items(): - if hasattr(panel, 'set_navigate_callback'): - panel.set_navigate_callback(self._navigate_to) - if hasattr(panel, 'set_back_callback'): - panel.set_back_callback(self._go_back) + self._section_tabs = TabbedSectionHost([ + TabSectionSpec("tune", "Tune", tune_panel), + TabSectionSpec("adaptive", "Adaptive", adaptive_panel), + TabSectionSpec("limits", "Limits", StarPilotSpeedLimitControllerLayout()), + TabSectionSpec("daily", "Daily", StarPilotLongitudinalQOLLayout()), + ]) self._rebuild_grid() + def set_navigate_callback(self, callback): + self._section_tabs.set_navigate_callback(callback) + + def set_back_callback(self, callback): + self._section_tabs.set_back_callback(callback) + + def _render(self, rect): + self._section_tabs.render(rect) + + def set_current_sub_panel(self, sub_panel: str): + self._section_tabs.set_current_sub_panel(sub_panel) + + def show_event(self): + self._section_tabs.show_event() + + def hide_event(self): + self._section_tabs.hide_event() + class StarPilotAdvancedLongitudinalLayout(StarPilotPanel): def __init__(self): @@ -165,15 +177,6 @@ class StarPilotConditionalExperimentalLayout(StarPilotPanel): "icon": "toggle_icons/icon_conditional.png", "color": "#597497", }, - { - "title": tr_noop("Conditional Experimental"), - "type": "toggle", - "get_state": lambda: self._params.get_bool("ConditionalExperimental"), - "set_state": lambda s: self._params.put_bool("ConditionalExperimental", s), - "icon": "toggle_icons/icon_conditional.png", - "color": "#597497", - "visible": lambda: self._params.get_bool("ConditionalExperimental"), - }, { "title": tr_noop("Below Speed"), "type": "value", @@ -302,16 +305,6 @@ class StarPilotCurveSpeedLayout(StarPilotPanel): "icon": "toggle_icons/icon_speed_map.png", "color": "#597497", }, - { - "title": tr_noop("Curve Speed Controller"), - "desc": tr_noop("Automatically slow down for upcoming curves using data learned from your driving style."), - "type": "toggle", - "get_state": lambda: self._params.get_bool("CurveSpeedController"), - "set_state": lambda s: self._params.put_bool("CurveSpeedController", s), - "icon": "toggle_icons/icon_speed_map.png", - "color": "#597497", - "visible": lambda: self._params.get_bool("CurveSpeedController"), - }, { "title": tr_noop("Status Widget"), "desc": tr_noop("Show the Curve Speed Controller ambient effect on the driving screen."), @@ -677,6 +670,11 @@ class StarPilotLongitudinalQOLLayout(StarPilotPanel): class StarPilotSpeedLimitControllerLayout(StarPilotPanel): def __init__(self): super().__init__() + self._sub_panels = { + "slc_offsets": StarPilotSLCOffsetsLayout(), + "slc_qol": StarPilotSLCQOLLayout(), + "slc_visuals": StarPilotSLCVisualsLayout(), + } self.CATEGORIES = [ { "title": tr_noop("Speed Limit Controller"), @@ -712,6 +710,13 @@ class StarPilotSpeedLimitControllerLayout(StarPilotPanel): "color": "#597497", }, ] + + for panel in self._sub_panels.values(): + if hasattr(panel, 'set_navigate_callback'): + panel.set_navigate_callback(self._navigate_to) + if hasattr(panel, 'set_back_callback'): + panel.set_back_callback(self._go_back) + self._rebuild_grid() def _get_priority_value(self): diff --git a/selfdrive/ui/layouts/settings/starpilot/main_panel.py b/selfdrive/ui/layouts/settings/starpilot/main_panel.py index 62d99b638..ffbeabc99 100644 --- a/selfdrive/ui/layouts/settings/starpilot/main_panel.py +++ b/selfdrive/ui/layouts/settings/starpilot/main_panel.py @@ -110,6 +110,9 @@ class StarPilotLayout(Widget): StarPilotPanelType.LATERAL, StarPilotPanelType.NAVIGATION, StarPilotPanelType.MAPS, + StarPilotPanelType.VISUALS, + StarPilotPanelType.THEMES, + StarPilotPanelType.VEHICLE, ) self._main_grid = TileGrid(columns=None, padding=20) @@ -162,7 +165,11 @@ class StarPilotLayout(Widget): self._depth_callback(depth) def _push_sub_panel(self, sub_panel_name: str): - self._panel_stack.append((self._current_panel, sub_panel_name)) + if sub_panel_name: + self._panel_stack.append((self._current_panel, sub_panel_name)) + else: + while self._panel_stack and self._panel_stack[-1][0] == self._current_panel: + self._panel_stack.pop() self._update_sub_panel_visibility() self._update_depth() diff --git a/selfdrive/ui/layouts/settings/starpilot/panel.py b/selfdrive/ui/layouts/settings/starpilot/panel.py index 622d78be3..b3311da37 100644 --- a/selfdrive/ui/layouts/settings/starpilot/panel.py +++ b/selfdrive/ui/layouts/settings/starpilot/panel.py @@ -8,6 +8,7 @@ import pyray as rl from openpilot.common.params import Params from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.widgets import Widget +from openpilot.selfdrive.ui.layouts.settings.starpilot.aethergrid import TileGrid class StarPilotPanelType(IntEnum): @@ -121,3 +122,19 @@ class StarPilotPanel(Widget): self._sub_panels[self._current_sub_panel].show_event() elif self._scroller: self._scroller.show_event() + + +def create_tile_panel(categories: list[dict], sub_panels: dict[str, Widget] | None = None) -> StarPilotPanel: + panel = StarPilotPanel() + panel.CATEGORIES = categories + panel._sub_panels = sub_panels or {} + panel._tile_grid = TileGrid(columns=2, padding=20, uniform_width=True) + + for name, child in panel._sub_panels.items(): + if hasattr(child, 'set_navigate_callback'): + child.set_navigate_callback(panel._navigate_to) + if hasattr(child, 'set_back_callback'): + child.set_back_callback(panel._go_back) + + panel._rebuild_grid() + return panel diff --git a/selfdrive/ui/layouts/settings/starpilot/sounds.py b/selfdrive/ui/layouts/settings/starpilot/sounds.py index 9e1d085ac..e5e9f2a52 100644 --- a/selfdrive/ui/layouts/settings/starpilot/sounds.py +++ b/selfdrive/ui/layouts/settings/starpilot/sounds.py @@ -58,7 +58,13 @@ class StarPilotSoundsLayout(StarPilotPanel): def _on_section_change(self, index: int): if 0 <= index < len(self._section_names): - self._set_active_section(self._section_names[index]) + previous_panel = self._sub_panels[self._active_section] + if hasattr(previous_panel, 'set_current_sub_panel'): + previous_panel.set_current_sub_panel("") + self._current_sub_panel = "" + self._set_active_section(self._section_names[index], "") + if self._navigate_callback: + self._navigate_callback("") def _set_active_section(self, section_name: str, child_panel: str = ""): if section_name not in self._sub_panels: diff --git a/selfdrive/ui/layouts/settings/starpilot/system_settings.py b/selfdrive/ui/layouts/settings/starpilot/system_settings.py index cfbd5a994..c38f5eccc 100644 --- a/selfdrive/ui/layouts/settings/starpilot/system_settings.py +++ b/selfdrive/ui/layouts/settings/starpilot/system_settings.py @@ -38,7 +38,13 @@ class StarPilotSystemLayout(StarPilotPanel): def _on_section_change(self, index: int): if 0 <= index < len(self._section_names): - self._set_active_section(self._section_names[index]) + previous_panel = self._sub_panels[self._active_section] + if hasattr(previous_panel, 'set_current_sub_panel'): + previous_panel.set_current_sub_panel("") + self._current_sub_panel = "" + self._set_active_section(self._section_names[index], "") + if self._navigate_callback: + self._navigate_callback("") def _set_active_section(self, section_name: str, child_panel: str = ""): if section_name not in self._sub_panels: diff --git a/selfdrive/ui/layouts/settings/starpilot/tabbed_panel.py b/selfdrive/ui/layouts/settings/starpilot/tabbed_panel.py new file mode 100644 index 000000000..871cb397e --- /dev/null +++ b/selfdrive/ui/layouts/settings/starpilot/tabbed_panel.py @@ -0,0 +1,119 @@ +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass + +import pyray as rl + +from openpilot.system.ui.lib.multilang import tr +from openpilot.system.ui.widgets import Widget + +from openpilot.selfdrive.ui.layouts.settings.starpilot.aethergrid import RadioTileGroup + + +@dataclass(frozen=True) +class TabSectionSpec: + key: str + label: str + panel: Widget + + +class TabbedSectionHost(Widget): + def __init__(self, sections: list[TabSectionSpec]): + super().__init__() + if not sections: + raise ValueError("TabbedSectionHost requires at least one section") + + self._sections = {spec.key: spec.panel for spec in sections} + self._section_order = [spec.key for spec in sections] + self._active_section = self._section_order[0] + self._navigate_callback: Callable | None = None + self._back_callback: Callable | None = None + self._current_sub_panel = "" + self._tab_height = 110 + self._panel_top = 140 + self._section_tabs = RadioTileGroup("", [tr(spec.label) for spec in sections], 0, self._on_tab_change) + + for key, panel in self._sections.items(): + if hasattr(panel, "set_navigate_callback"): + panel.set_navigate_callback(lambda sub_panel, section_key=key: self._on_child_navigate(section_key, sub_panel)) + if hasattr(panel, "set_back_callback"): + panel.set_back_callback(self._go_back) + + def set_navigate_callback(self, callback: Callable): + self._navigate_callback = callback + + def set_back_callback(self, callback: Callable): + self._back_callback = callback + + def set_current_sub_panel(self, sub_panel: str): + self._current_sub_panel = sub_panel + if not sub_panel: + panel = self._sections[self._active_section] + if hasattr(panel, "set_current_sub_panel"): + panel.set_current_sub_panel("") + return + + if ":" in sub_panel: + section_key, child_panel = sub_panel.split(":", 1) + self._activate_section(section_key, child_panel) + elif sub_panel in self._sections: + self._activate_section(sub_panel) + else: + panel = self._sections[self._active_section] + if hasattr(panel, "set_current_sub_panel"): + panel.set_current_sub_panel(sub_panel) + + def _on_tab_change(self, index: int): + if 0 <= index < len(self._section_order): + self._current_sub_panel = "" + self._activate_section(self._section_order[index], "") + if self._navigate_callback: + self._navigate_callback("") + + def _activate_section(self, section_key: str, child_panel: str = ""): + if section_key not in self._sections: + return + + previous = self._active_section + if section_key != previous: + previous_panel = self._sections[previous] + if hasattr(previous_panel, "set_current_sub_panel"): + previous_panel.set_current_sub_panel("") + self._sections[previous].hide_event() + self._active_section = section_key + self._sections[section_key].show_event() + + self._section_tabs.set_index(self._section_order.index(section_key)) + panel = self._sections[section_key] + if hasattr(panel, "set_current_sub_panel"): + panel.set_current_sub_panel(child_panel) + + def _on_child_navigate(self, section_key: str, sub_panel: str): + self._current_sub_panel = f"{section_key}:{sub_panel}" if sub_panel else section_key + if self._navigate_callback: + self._navigate_callback(self._current_sub_panel) + + def _go_back(self): + self._current_sub_panel = "" + panel = self._sections[self._active_section] + if hasattr(panel, "set_current_sub_panel"): + panel.set_current_sub_panel("") + if self._back_callback: + self._back_callback() + + def _render(self, rect: rl.Rectangle): + tab_rect = rl.Rectangle(rect.x, rect.y, rect.width, self._tab_height) + panel_rect = rl.Rectangle(rect.x, rect.y + self._panel_top, rect.width, rect.height - self._panel_top) + self._section_tabs.render(tab_rect) + self._sections[self._active_section].render(panel_rect) + + def show_event(self): + super().show_event() + self._section_tabs.show_event() + self._sections[self._active_section].show_event() + + def hide_event(self): + super().hide_event() + self._section_tabs.hide_event() + self._sections[self._active_section].hide_event() diff --git a/selfdrive/ui/layouts/settings/starpilot/themes.py b/selfdrive/ui/layouts/settings/starpilot/themes.py index 7aa090326..38132adbe 100644 --- a/selfdrive/ui/layouts/settings/starpilot/themes.py +++ b/selfdrive/ui/layouts/settings/starpilot/themes.py @@ -105,6 +105,7 @@ class StarPilotThemesLayout(StarPilotPanel): "icon": "toggle_icons/icon_frog.png", "color": "#542A71", "desc": tr_noop("Customize the overall look and feel."), + "visible": lambda: self._params.get_bool("CustomThemes"), }, { "title": tr_noop("Holiday Themes"), @@ -179,13 +180,6 @@ class StarPilotPersonalizeLayout(StarPilotPanel): def __init__(self): super().__init__() self.CATEGORIES = [ - { - "title": tr_noop("Custom Themes"), - "type": "toggle", - "get_state": lambda: self._params.get_bool("CustomThemes"), - "set_state": lambda s: self._params.put_bool("CustomThemes", s), - "color": "#542A71", - }, { "title": tr_noop("Boot Logo"), "type": "value", diff --git a/selfdrive/ui/layouts/settings/starpilot/visuals.py b/selfdrive/ui/layouts/settings/starpilot/visuals.py index eaf39e9a6..f714c882f 100644 --- a/selfdrive/ui/layouts/settings/starpilot/visuals.py +++ b/selfdrive/ui/layouts/settings/starpilot/visuals.py @@ -4,108 +4,44 @@ from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.multilang import tr, tr_noop from openpilot.system.ui.widgets import DialogResult from openpilot.system.ui.widgets.selection_dialog import SelectionDialog -from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel +from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel, create_tile_panel +from openpilot.selfdrive.ui.layouts.settings.starpilot.tabbed_panel import TabSectionSpec, TabbedSectionHost from openpilot.selfdrive.ui.layouts.settings.starpilot.aethergrid import TileGrid, ToggleTile, AetherSliderDialog - - -class StarPilotThemesLayout(StarPilotPanel): - def __init__(self): - super().__init__() - self._sub_panels = { - "personalize": StarPilotPersonalizeLayout(), - } - self.CATEGORIES = [ - {"title": tr_noop("Personalize openpilot"), "panel": "personalize", "icon": "toggle_icons/icon_frog.png", "color": "#8B5CF6"}, - { - "title": tr_noop("Holiday Themes"), - "type": "toggle", - "get_state": lambda: self._params.get_bool("HolidayThemes"), - "set_state": lambda s: self._params.put_bool("HolidayThemes", s), - "icon": "toggle_icons/icon_calendar.png", - "color": "#8B5CF6", - }, - { - "title": tr_noop("Rainbow Path"), - "type": "toggle", - "get_state": lambda: self._params.get_bool("RainbowPath"), - "set_state": lambda s: self._params.put_bool("RainbowPath", s), - "icon": "toggle_icons/icon_rainbow.png", - "color": "#8B5CF6", - }, - { - "title": tr_noop("Random Events"), - "type": "toggle", - "get_state": lambda: self._params.get_bool("RandomEvents"), - "set_state": lambda s: self._params.put_bool("RandomEvents", s), - "icon": "toggle_icons/icon_random.png", - "color": "#8B5CF6", - }, - { - "title": tr_noop("Random Themes"), - "type": "toggle", - "get_state": lambda: self._params.get_bool("RandomThemes"), - "set_state": lambda s: self._params.put_bool("RandomThemes", s), - "icon": "toggle_icons/icon_random_themes.png", - "color": "#8B5CF6", - }, - ] - for name, panel in self._sub_panels.items(): - if hasattr(panel, 'set_navigate_callback'): - panel.set_navigate_callback(self._navigate_to) - if hasattr(panel, 'set_back_callback'): - panel.set_back_callback(self._go_back) - self._rebuild_grid() - - -class StarPilotPersonalizeLayout(StarPilotPanel): - def __init__(self): - super().__init__() - self.CATEGORIES = [ - {"title": tr_noop("Boot Logo"), "type": "hub", "on_click": lambda: self._show_theme_selector("BootLogo"), "color": "#8B5CF6"}, - {"title": tr_noop("Color Scheme"), "type": "hub", "on_click": lambda: self._show_theme_selector("ColorScheme"), "color": "#8B5CF6"}, - {"title": tr_noop("Distance Icons"), "type": "hub", "on_click": lambda: self._show_theme_selector("DistanceIconPack"), "color": "#8B5CF6"}, - {"title": tr_noop("Icon Pack"), "type": "hub", "on_click": lambda: self._show_theme_selector("IconPack"), "color": "#8B5CF6"}, - {"title": tr_noop("Turn Signals"), "type": "hub", "on_click": lambda: self._show_theme_selector("SignalAnimation"), "color": "#8B5CF6"}, - {"title": tr_noop("Sound Pack"), "type": "hub", "on_click": lambda: self._show_theme_selector("SoundPack"), "color": "#8B5CF6"}, - {"title": tr_noop("Steering Wheel"), "type": "hub", "on_click": lambda: self._show_theme_selector("WheelIcon"), "color": "#8B5CF6"}, - ] - self._rebuild_grid() - - def _show_theme_selector(self, key): - themes = ["Stock", "Frog", "Cyberpunk", "Minimal"] - current = self._params.get(key, encoding='utf-8') or "Stock" - - def on_select(res, val): - if res == DialogResult.CONFIRM: - self._params.put(key, val) - self._rebuild_grid() - - gui_app.set_modal_overlay(SelectionDialog(tr(key), themes, current, on_close=on_select)) - - class StarPilotVisualsLayout(StarPilotPanel): def __init__(self): super().__init__() - self._sub_panels = { - "advanced": StarPilotAdvancedVisualsLayout(), - "widgets": StarPilotVisualWidgetsLayout(), - "model": StarPilotModelUILayout(), - "navigation": StarPilotNavigationVisualsLayout(), - "qol": StarPilotVisualQOLLayout(), - } - self.CATEGORIES = [ + display_panel = create_tile_panel([ {"title": tr_noop("Advanced UI Controls"), "panel": "advanced", "icon": "toggle_icons/icon_advanced_device.png", "color": "#8B5CF6"}, - {"title": tr_noop("Driving Screen Widgets"), "panel": "widgets", "icon": "toggle_icons/icon_display.png", "color": "#8B5CF6"}, - {"title": tr_noop("Model UI"), "panel": "model", "icon": "toggle_icons/icon_road.png", "color": "#8B5CF6"}, - {"title": tr_noop("Navigation Widgets"), "panel": "navigation", "icon": "toggle_icons/icon_map.png", "color": "#8B5CF6"}, {"title": tr_noop("Quality of Life"), "panel": "qol", "icon": "toggle_icons/icon_quality_of_life.png", "color": "#8B5CF6"}, - ] - for name, panel in self._sub_panels.items(): - if hasattr(panel, 'set_navigate_callback'): - panel.set_navigate_callback(self._navigate_to) - if hasattr(panel, 'set_back_callback'): - panel.set_back_callback(self._go_back) - self._rebuild_grid() + ], { + "advanced": StarPilotAdvancedVisualsLayout(), + "qol": StarPilotVisualQOLLayout(), + }) + + self._section_tabs = TabbedSectionHost([ + TabSectionSpec("display", "Display", display_panel), + TabSectionSpec("widgets", "Widgets", StarPilotVisualWidgetsLayout()), + TabSectionSpec("model", "Model", StarPilotModelUILayout()), + TabSectionSpec("navigation", "Nav", StarPilotNavigationVisualsLayout()), + ]) + + def set_navigate_callback(self, callback): + self._section_tabs.set_navigate_callback(callback) + + def set_back_callback(self, callback): + self._section_tabs.set_back_callback(callback) + + def _render(self, rect): + self._section_tabs.render(rect) + + def set_current_sub_panel(self, sub_panel: str): + self._section_tabs.set_current_sub_panel(sub_panel) + + def show_event(self): + self._section_tabs.show_event() + + def hide_event(self): + self._section_tabs.hide_event() class StarPilotAdvancedVisualsLayout(StarPilotPanel): @@ -558,15 +494,6 @@ class StarPilotNavigationVisualsLayout(StarPilotPanel): "color": "#8B5CF6", "visible": lambda: self._params.get_bool("NavigationUI"), }, - { - "title": tr_noop("Mapbox Limits"), - "type": "toggle", - "get_state": lambda: self._params.get_bool("SLCMapboxFiller"), - "set_state": lambda s: self._params.put_bool("SLCMapboxFiller", s), - "icon": "toggle_icons/icon_speed_limit.png", - "color": "#8B5CF6", - "visible": lambda: self._params.get_bool("NavigationUI") and self._params.get_bool("ShowSpeedLimits"), - }, { "title": tr_noop("Vienna Signs"), "type": "toggle",