From 3e82b47c9c87129810ffefe9bbd446b38b9b8c6d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 11 Oct 2025 00:02:17 -0700 Subject: [PATCH] firehose fixups --- selfdrive/ui/layouts/settings/firehose.py | 65 +++++++---------------- system/ui/lib/scroll_panel.py | 4 ++ 2 files changed, 24 insertions(+), 45 deletions(-) diff --git a/selfdrive/ui/layouts/settings/firehose.py b/selfdrive/ui/layouts/settings/firehose.py index 353a9d976f..defe9e3d37 100644 --- a/selfdrive/ui/layouts/settings/firehose.py +++ b/selfdrive/ui/layouts/settings/firehose.py @@ -7,7 +7,8 @@ from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.athena.registration import UNREGISTERED_DONGLE_ID -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE +from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget @@ -43,6 +44,7 @@ class FirehoseLayout(Widget): self.params = Params() self.segment_count = self._get_segment_count() self.scroll_panel = GuiScrollPanel() + self._content_height = 0 self.running = True self.update_thread = threading.Thread(target=self._update_loop, daemon=True) @@ -69,55 +71,25 @@ class FirehoseLayout(Widget): def _render(self, rect: rl.Rectangle): # Calculate content dimensions - content_width = rect.width - 80 - content_height = self._calculate_content_height(int(content_width)) - content_rect = rl.Rectangle(rect.x, rect.y, rect.width, content_height) + content_rect = rl.Rectangle(rect.x, rect.y, rect.width, self._content_height) # Handle scrolling and render with clipping scroll_offset = self.scroll_panel.update(rect, content_rect) rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(rect.height)) - self._render_content(rect, scroll_offset) + self._content_height = self._render_content(rect, scroll_offset) rl.end_scissor_mode() - def _calculate_content_height(self, content_width: int) -> int: - height = 80 # Top margin - - # Title - height += 100 + 40 - - # Description - desc_font = gui_app.font(FontWeight.NORMAL) - desc_lines = wrap_text(desc_font, DESCRIPTION, 45, content_width) - height += len(desc_lines) * 45 + 40 - - # Status section - height += 32 # Separator - status_text, _ = self._get_status() - status_lines = wrap_text(gui_app.font(FontWeight.BOLD), status_text, 60, content_width) - height += len(status_lines) * 60 + 20 - - # Contribution count (if available) - if self.segment_count > 0: - contrib_text = f"{self.segment_count} segment(s) of your driving is in the training dataset so far." - contrib_lines = wrap_text(gui_app.font(FontWeight.BOLD), contrib_text, 52, content_width) - height += len(contrib_lines) * 52 + 20 - - # Instructions section - height += 32 # Separator - inst_lines = wrap_text(gui_app.font(FontWeight.NORMAL), INSTRUCTIONS, 40, content_width) - height += len(inst_lines) * 40 + 40 # Bottom margin - - return height - - def _render_content(self, rect: rl.Rectangle, scroll_offset: float): + def _render_content(self, rect: rl.Rectangle, scroll_offset: float) -> int: x = int(rect.x + 40) y = int(rect.y + 40 + scroll_offset) w = int(rect.width - 80) - # Title + # Title (centered) title_font = gui_app.font(FontWeight.MEDIUM) - rl.draw_text_ex(title_font, TITLE, rl.Vector2(x, y), 100, 0, rl.WHITE) - y += 140 + text_width = measure_text_cached(title_font, TITLE, 100).x + title_x = rect.x + (rect.width - text_width) / 2 + rl.draw_text_ex(title_font, TITLE, rl.Vector2(title_x, y), 100, 0, rl.WHITE) + y += 200 # Description y = self._draw_wrapped_text(x, y, w, DESCRIPTION, gui_app.font(FontWeight.NORMAL), 45, rl.WHITE) @@ -143,14 +115,17 @@ class FirehoseLayout(Widget): y += 30 # Instructions - self._draw_wrapped_text(x, y, w, INSTRUCTIONS, gui_app.font(FontWeight.NORMAL), 40, self.LIGHT_GRAY) + y = self._draw_wrapped_text(x, y, w, INSTRUCTIONS, gui_app.font(FontWeight.NORMAL), 40, self.LIGHT_GRAY) + + # bottom margin + remove effect of scroll offset + return y - self.scroll_panel.offset + 40 - def _draw_wrapped_text(self, x, y, width, text, font, size, color): - wrapped = wrap_text(font, text, size, width) + def _draw_wrapped_text(self, x, y, width, text, font, font_size, color): + wrapped = wrap_text(font, text, font_size, width) for line in wrapped: - rl.draw_text_ex(font, line, rl.Vector2(x, y), size, 0, color) - y += size - return y + rl.draw_text_ex(font, line, rl.Vector2(x, y), font_size, 0, color) + y += font_size * FONT_SCALE + return round(y) def _get_status(self) -> tuple[str, rl.Color]: network_type = ui_state.sm["deviceState"].networkType diff --git a/system/ui/lib/scroll_panel.py b/system/ui/lib/scroll_panel.py index f0031138fe..a5b9fc70d3 100644 --- a/system/ui/lib/scroll_panel.py +++ b/system/ui/lib/scroll_panel.py @@ -128,3 +128,7 @@ class GuiScrollPanel: self._offset_filter_y.x = position self._velocity_filter_y.x = 0.0 self._scroll_state = ScrollState.IDLE + + @property + def offset(self) -> float: + return float(self._offset_filter_y.x)