raylib: dismiss dialog on pair (#36205)

* show for unknown

* use Button to make clicking work

* close on pair

* close on pair

* make widget!

* dynamic pairing btn

* whyyy

* clean up

* can do this

* this button is also hard to tap
pull/36195/merge
Shane Smiskol 2 days ago committed by GitHub
parent 33f01084d1
commit 0711160b1c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      selfdrive/ui/layouts/settings/device.py
  2. 20
      selfdrive/ui/lib/prime_state.py
  3. 9
      selfdrive/ui/ui.py
  4. 11
      selfdrive/ui/widgets/pairing_dialog.py
  5. 15
      selfdrive/ui/widgets/setup.py
  6. 3
      system/ui/lib/application.py
  7. 11
      system/ui/widgets/scroller.py

@ -44,10 +44,13 @@ class DeviceLayout(Widget):
dongle_id = self._params.get("DongleId") or "N/A" dongle_id = self._params.get("DongleId") or "N/A"
serial = self._params.get("HardwareSerial") or "N/A" serial = self._params.get("HardwareSerial") or "N/A"
self._pair_device_btn = button_item("Pair Device", "PAIR", DESCRIPTIONS['pair_device'], callback=self._pair_device)
self._pair_device_btn.set_visible(lambda: not ui_state.prime_state.is_paired())
items = [ items = [
text_item("Dongle ID", dongle_id), text_item("Dongle ID", dongle_id),
text_item("Serial", serial), text_item("Serial", serial),
button_item("Pair Device", "PAIR", DESCRIPTIONS['pair_device'], callback=self._pair_device), self._pair_device_btn,
button_item("Driver Camera", "PREVIEW", DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad), button_item("Driver Camera", "PREVIEW", DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad),
button_item("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt), button_item("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt),
regulatory_btn := button_item("Regulatory", "VIEW", callback=self._on_regulatory), regulatory_btn := button_item("Regulatory", "VIEW", callback=self._on_regulatory),

@ -11,14 +11,14 @@ from openpilot.selfdrive.ui.lib.api_helpers import get_token
class PrimeType(IntEnum): class PrimeType(IntEnum):
UNKNOWN = -2, UNKNOWN = -2
UNPAIRED = -1, UNPAIRED = -1
NONE = 0, NONE = 0
MAGENTA = 1, MAGENTA = 1
LITE = 2, LITE = 2
BLUE = 3, BLUE = 3
MAGENTA_NEW = 4, MAGENTA_NEW = 4
PURPLE = 5, PURPLE = 5
class PrimeState: class PrimeState:
@ -96,5 +96,9 @@ class PrimeState:
with self._lock: with self._lock:
return bool(self.prime_type > PrimeType.NONE) return bool(self.prime_type > PrimeType.NONE)
def is_paired(self) -> bool:
with self._lock:
return self.prime_type > PrimeType.UNPAIRED
def __del__(self): def __del__(self):
self.stop() self.stop()

@ -10,15 +10,14 @@ def main():
gui_app.init_window("UI") gui_app.init_window("UI")
main_layout = MainLayout() main_layout = MainLayout()
main_layout.set_rect(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) main_layout.set_rect(rl.Rectangle(0, 0, gui_app.width, gui_app.height))
for _ in gui_app.render(): for showing_dialog in gui_app.render():
ui_state.update() ui_state.update()
# TODO handle brigntness and awake state here
main_layout.render()
kick_watchdog() kick_watchdog()
if not showing_dialog:
main_layout.render()
if __name__ == "__main__": if __name__ == "__main__":
main() main()

@ -6,17 +6,20 @@ import time
from openpilot.common.api import Api from openpilot.common.api import Api
from openpilot.common.swaglog import cloudlog from openpilot.common.swaglog import cloudlog
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.system.ui.widgets import Widget
from openpilot.system.ui.lib.application import FontWeight, gui_app from openpilot.system.ui.lib.application import FontWeight, gui_app
from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.lib.wrap_text import wrap_text
from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.text_measure import measure_text_cached
from openpilot.selfdrive.ui.ui_state import ui_state
class PairingDialog: class PairingDialog(Widget):
"""Dialog for device pairing with QR code.""" """Dialog for device pairing with QR code."""
QR_REFRESH_INTERVAL = 300 # 5 minutes in seconds QR_REFRESH_INTERVAL = 300 # 5 minutes in seconds
def __init__(self): def __init__(self):
super().__init__()
self.params = Params() self.params = Params()
self.qr_texture: rl.Texture | None = None self.qr_texture: rl.Texture | None = None
self.last_qr_generation = 0 self.last_qr_generation = 0
@ -60,7 +63,11 @@ class PairingDialog:
self._generate_qr_code() self._generate_qr_code()
self.last_qr_generation = current_time self.last_qr_generation = current_time
def render(self, rect: rl.Rectangle) -> int: def _update_state(self):
if ui_state.prime_state.is_paired():
gui_app.set_modal_overlay(None)
def _render(self, rect: rl.Rectangle) -> int:
rl.clear_background(rl.Color(224, 224, 224, 255)) rl.clear_background(rl.Color(224, 224, 224, 255))
self._check_qr_refresh() self._check_qr_refresh()

@ -1,11 +1,10 @@
import pyray as rl import pyray as rl
from openpilot.selfdrive.ui.lib.prime_state import PrimeType
from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog
from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.application import gui_app, FontWeight
from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.lib.wrap_text import wrap_text
from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets import Widget
from openpilot.system.ui.widgets.button import gui_button, ButtonStyle from openpilot.system.ui.widgets.button import Button, ButtonStyle
class SetupWidget(Widget): class SetupWidget(Widget):
@ -13,12 +12,15 @@ class SetupWidget(Widget):
super().__init__() super().__init__()
self._open_settings_callback = None self._open_settings_callback = None
self._pairing_dialog: PairingDialog | None = None self._pairing_dialog: PairingDialog | None = None
self._pair_device_btn = Button("Pair device", self._show_pairing, button_style=ButtonStyle.PRIMARY)
self._open_settings_btn = Button("Open", lambda: self._open_settings_callback() if self._open_settings_callback else None,
button_style=ButtonStyle.PRIMARY)
def set_open_settings_callback(self, callback): def set_open_settings_callback(self, callback):
self._open_settings_callback = callback self._open_settings_callback = callback
def _render(self, rect: rl.Rectangle): def _render(self, rect: rl.Rectangle):
if ui_state.prime_state.get_type() == PrimeType.UNPAIRED: if not ui_state.prime_state.is_paired():
self._render_registration(rect) self._render_registration(rect)
else: else:
self._render_firehose_prompt(rect) self._render_firehose_prompt(rect)
@ -46,8 +48,7 @@ class SetupWidget(Widget):
y += 50 y += 50
button_rect = rl.Rectangle(x, y + 50, w, 128) button_rect = rl.Rectangle(x, y + 50, w, 128)
if gui_button(button_rect, "Pair device", button_style=ButtonStyle.PRIMARY): self._pair_device_btn.render(button_rect)
self._show_pairing()
def _render_firehose_prompt(self, rect: rl.Rectangle): def _render_firehose_prompt(self, rect: rl.Rectangle):
"""Render firehose prompt widget.""" """Render firehose prompt widget."""
@ -80,9 +81,7 @@ class SetupWidget(Widget):
# Open button # Open button
button_height = 48 + 64 # font size + padding button_height = 48 + 64 # font size + padding
button_rect = rl.Rectangle(x, y, w, button_height) button_rect = rl.Rectangle(x, y, w, button_height)
if gui_button(button_rect, "Open", button_style=ButtonStyle.PRIMARY): self._open_settings_btn.render(button_rect)
if self._open_settings_callback:
self._open_settings_callback()
def _show_pairing(self): def _show_pairing(self):
if not self._pairing_dialog: if not self._pairing_dialog:

@ -274,8 +274,9 @@ class GuiApplication:
self._modal_overlay = ModalOverlay() self._modal_overlay = ModalOverlay()
if original_modal.callback is not None: if original_modal.callback is not None:
original_modal.callback(result) original_modal.callback(result)
yield True
else: else:
yield yield False
if self._render_texture: if self._render_texture:
rl.end_texture_mode() rl.end_texture_mode()

@ -27,7 +27,7 @@ class Scroller(Widget):
super().__init__() super().__init__()
self._items: list[Widget] = [] self._items: list[Widget] = []
self._spacing = spacing self._spacing = spacing
self._line_separator = line_separator self._line_separator = LineSeparator() if line_separator else None
self._pad_end = pad_end self._pad_end = pad_end
self.scroll_panel = GuiScrollPanel() self.scroll_panel = GuiScrollPanel()
@ -36,14 +36,19 @@ class Scroller(Widget):
self.add_widget(item) self.add_widget(item)
def add_widget(self, item: Widget) -> None: def add_widget(self, item: Widget) -> None:
if self._line_separator and len(self._items) > 0:
self._items.append(LineSeparator())
self._items.append(item) self._items.append(item)
item.set_touch_valid_callback(self.scroll_panel.is_touch_valid) item.set_touch_valid_callback(self.scroll_panel.is_touch_valid)
def _render(self, _): def _render(self, _):
# TODO: don't draw items that are not in the viewport # TODO: don't draw items that are not in the viewport
visible_items = [item for item in self._items if item.is_visible] visible_items = [item for item in self._items if item.is_visible]
# Add line separator between items
if self._line_separator is not None:
l = len(visible_items)
for i in range(1, len(visible_items)):
visible_items.insert(l - i, self._line_separator)
content_height = sum(item.rect.height for item in visible_items) + self._spacing * (len(visible_items)) content_height = sum(item.rect.height for item in visible_items) + self._spacing * (len(visible_items))
if not self._pad_end: if not self._pad_end:
content_height -= self._spacing content_height -= self._spacing

Loading…
Cancel
Save