Files
StarPilot/system/ui/widgets/option_dialog.py
T
Shane Smiskol cefddf4b9b ui: add navigation stack for tici (#37275)
* initial

* start to support nav stack in settings panels + fix some navwidget bugs

* add deprecation warning and move more to new nav stack

* fix overriding NavWidget enabled and do developer panel

* fix interactive timeout and do main

* more device, not done yet

* minor network fixes

* dcam dialog

* start onboarding

* fix onboarding

* do mici setup

* remove now useless CUSTOM_SOFTWARE

* support big ui with old modal overlay

* reset can be old modal overlay, but updater needs new since it uses wifiui

* flip name truthiness to inspire excitement

* all *should* work, but will do pass later

* clean up main

* clean up settiings

* clean up dialog and developer

* cleanup mici setup some

* rm one more

* fix keyboard

* revert

* might as well but clarify

* fix networkinfopage buttons

* lint

* nice clean up from cursor

* animate background fade with position

* fix device overlays

* cursor fix pt1

cursor fix pt2

* rm print

* capital

* temp fix from cursor for onboarding not freeing space after reviewing training guide

* fix home screen scroller snap not resetting

* stash

* nice gradient on top

* 40

* 20

* no gradient

* return unused returns and always show regulatory btn

* nice!

* revert selfdrive/ui

* let's do tici first

* bring back ui

* not sure why __del__, SetupWidget was never deleted?

* device "done"

* network "done!!"

* toggles "done"

* software "done"

* developer "done"

* fix onboarding

* use new modal for debug windows

* and aug

* setup "done"

* clean up

* updater "done"

* reset "done"

* pop first before callbacks in case callbacks push

* fix cmt

* not needed

* remove two commented functions for mici

* clean up application

* typing

* static

* not sure what this means

* fix big

* more static

* actually great catch

* fix cmt
2026-02-20 02:43:11 -08:00

80 lines
3.5 KiB
Python

import pyray as rl
from collections.abc import Callable
from openpilot.system.ui.lib.application import gui_app, FontWeight
from openpilot.system.ui.lib.multilang import tr
from openpilot.system.ui.widgets import Widget, DialogResult
from openpilot.system.ui.widgets.button import Button, ButtonStyle
from openpilot.system.ui.widgets.label import gui_label
from openpilot.system.ui.widgets.scroller_tici import Scroller
# Constants
MARGIN = 50
TITLE_FONT_SIZE = 70
ITEM_HEIGHT = 135
BUTTON_SPACING = 50
BUTTON_HEIGHT = 160
ITEM_SPACING = 50
LIST_ITEM_SPACING = 25
class MultiOptionDialog(Widget):
def __init__(self, title, options, current="", option_font_weight=FontWeight.MEDIUM, callback: Callable[[DialogResult], None] | None = None):
super().__init__()
self.title = title
self.options = options
self.current = current
self.selection = current
self._callback = callback
# Create scroller with option buttons
self.option_buttons = [Button(option, click_callback=lambda opt=option: self._on_option_clicked(opt),
font_weight=option_font_weight,
text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT, button_style=ButtonStyle.NORMAL,
text_padding=50, elide_right=True) for option in options]
self.scroller = Scroller(self.option_buttons, spacing=LIST_ITEM_SPACING)
self.cancel_button = Button(lambda: tr("Cancel"), click_callback=lambda: self._set_result(DialogResult.CANCEL))
self.select_button = Button(lambda: tr("Select"), click_callback=lambda: self._set_result(DialogResult.CONFIRM), button_style=ButtonStyle.PRIMARY)
def _set_result(self, result: DialogResult):
gui_app.pop_widget()
if self._callback:
self._callback(result)
def _on_option_clicked(self, option):
self.selection = option
def _render(self, rect):
dialog_rect = rl.Rectangle(rect.x + MARGIN, rect.y + MARGIN, rect.width - 2 * MARGIN, rect.height - 2 * MARGIN)
rl.draw_rectangle_rounded(dialog_rect, 0.02, 20, rl.Color(30, 30, 30, 255))
content_rect = rl.Rectangle(dialog_rect.x + MARGIN, dialog_rect.y + MARGIN,
dialog_rect.width - 2 * MARGIN, dialog_rect.height - 2 * MARGIN)
gui_label(rl.Rectangle(content_rect.x, content_rect.y, content_rect.width, TITLE_FONT_SIZE), self.title, 70, font_weight=FontWeight.BOLD)
# Options area
options_y = content_rect.y + TITLE_FONT_SIZE + ITEM_SPACING
options_h = content_rect.height - TITLE_FONT_SIZE - BUTTON_HEIGHT - 2 * ITEM_SPACING
options_rect = rl.Rectangle(content_rect.x, options_y, content_rect.width, options_h)
# Update button styles and set width based on selection
for i, option in enumerate(self.options):
selected = option == self.selection
button = self.option_buttons[i]
button.set_button_style(ButtonStyle.PRIMARY if selected else ButtonStyle.NORMAL)
button.set_rect(rl.Rectangle(0, 0, options_rect.width, ITEM_HEIGHT))
self.scroller.render(options_rect)
# Buttons
button_y = content_rect.y + content_rect.height - BUTTON_HEIGHT
button_w = (content_rect.width - BUTTON_SPACING) / 2
cancel_rect = rl.Rectangle(content_rect.x, button_y, button_w, BUTTON_HEIGHT)
self.cancel_button.render(cancel_rect)
select_rect = rl.Rectangle(content_rect.x + button_w + BUTTON_SPACING, button_y, button_w, BUTTON_HEIGHT)
self.select_button.set_enabled(self.selection != self.current)
self.select_button.render(select_rect)