mirror of
https://github.com/infiniteCable2/openpilot.git
synced 2026-09-15 03:53:41 +08:00
ui: refactor ListView for generic widget support and simplified item architecture (#35536)
refactor list view apply reviews
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from openpilot.system.ui.lib.list_view import ListView, toggle_item
|
||||
from openpilot.system.ui.lib.widget import Widget
|
||||
from openpilot.system.ui.lib.list_view import ListView, ToggleItem
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.selfdrive.ui.widgets.ssh_key import ssh_key_item
|
||||
from openpilot.selfdrive.ui.widgets.ssh_key import SshKeyItem
|
||||
from openpilot.system.ui.lib.widget import Widget
|
||||
|
||||
# Description constants
|
||||
DESCRIPTIONS = {
|
||||
@@ -16,34 +16,33 @@ DESCRIPTIONS = {
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class DeveloperLayout(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._params = Params()
|
||||
items = [
|
||||
toggle_item(
|
||||
ToggleItem(
|
||||
"Enable ADB",
|
||||
description=DESCRIPTIONS["enable_adb"],
|
||||
DESCRIPTIONS["enable_adb"],
|
||||
initial_state=self._params.get_bool("AdbEnabled"),
|
||||
callback=self._on_enable_adb,
|
||||
),
|
||||
ssh_key_item("SSH Key", description=DESCRIPTIONS["ssh_key"]),
|
||||
toggle_item(
|
||||
SshKeyItem("SSH Key", description=DESCRIPTIONS["ssh_key"]),
|
||||
ToggleItem(
|
||||
"Joystick Debug Mode",
|
||||
description=DESCRIPTIONS["joystick_debug_mode"],
|
||||
DESCRIPTIONS["joystick_debug_mode"],
|
||||
initial_state=self._params.get_bool("JoystickDebugMode"),
|
||||
callback=self._on_joystick_debug_mode,
|
||||
),
|
||||
toggle_item(
|
||||
ToggleItem(
|
||||
"Longitudinal Maneuver Mode",
|
||||
description="",
|
||||
"",
|
||||
initial_state=self._params.get_bool("LongitudinalManeuverMode"),
|
||||
callback=self._on_long_maneuver_mode,
|
||||
),
|
||||
toggle_item(
|
||||
ToggleItem(
|
||||
"openpilot Longitudinal Control (Alpha)",
|
||||
description="",
|
||||
"",
|
||||
initial_state=self._params.get_bool("AlphaLongitudinalEnabled"),
|
||||
callback=self._on_alpha_long_enabled,
|
||||
),
|
||||
@@ -54,7 +53,7 @@ class DeveloperLayout(Widget):
|
||||
def _render(self, rect):
|
||||
self._list_widget.render(rect)
|
||||
|
||||
def _on_enable_adb(self): pass
|
||||
def _on_joystick_debug_mode(self): pass
|
||||
def _on_long_maneuver_mode(self): pass
|
||||
def _on_alpha_long_enabled(self): pass
|
||||
def _on_enable_adb(self, state): pass
|
||||
def _on_joystick_debug_mode(self, state): pass
|
||||
def _on_long_maneuver_mode(self, state): pass
|
||||
def _on_alpha_long_enabled(self, state): pass
|
||||
|
||||
@@ -7,7 +7,7 @@ from openpilot.selfdrive.ui.onroad.driver_camera_dialog import DriverCameraDialo
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.system.hardware import TICI
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
from openpilot.system.ui.lib.list_view import ListView, text_item, button_item, dual_button_item
|
||||
from openpilot.system.ui.lib.list_view import ListView, TextItem, ButtonItem, DualButtonItem
|
||||
from openpilot.system.ui.lib.widget import Widget, DialogResult
|
||||
from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog
|
||||
from openpilot.system.ui.widgets.option_dialog import MultiOptionDialog
|
||||
@@ -44,15 +44,15 @@ class DeviceLayout(Widget):
|
||||
serial = self._params.get("HardwareSerial") or "N/A"
|
||||
|
||||
items = [
|
||||
text_item("Dongle ID", dongle_id),
|
||||
text_item("Serial", serial),
|
||||
button_item("Pair Device", "PAIR", DESCRIPTIONS['pair_device'], callback=self._pair_device),
|
||||
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("Regulatory", "VIEW", callback=self._on_regulatory, visible=TICI),
|
||||
button_item("Review Training Guide", "REVIEW", DESCRIPTIONS['review_guide'], self._on_review_training_guide),
|
||||
button_item("Change Language", "CHANGE", callback=self._show_language_selection, enabled=ui_state.is_offroad),
|
||||
dual_button_item("Reboot", "Power Off", left_callback=self._reboot_prompt, right_callback=self._power_off_prompt),
|
||||
TextItem("Dongle ID", dongle_id),
|
||||
TextItem("Serial", serial),
|
||||
ButtonItem("Pair Device", "PAIR", DESCRIPTIONS['pair_device'], callback=self._pair_device),
|
||||
ButtonItem("Driver Camera", "PREVIEW", DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad),
|
||||
ButtonItem("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt),
|
||||
ButtonItem("Regulatory", "VIEW", callback=self._on_regulatory, visible=TICI),
|
||||
ButtonItem("Review Training Guide", "REVIEW", DESCRIPTIONS['review_guide'], self._on_review_training_guide),
|
||||
ButtonItem("Change Language", "CHANGE", callback=self._show_language_selection, enabled=ui_state.is_offroad),
|
||||
DualButtonItem("Reboot", "Power Off", left_callback=self._reboot_prompt, right_callback=self._power_off_prompt),
|
||||
]
|
||||
return items
|
||||
|
||||
|
||||
@@ -115,13 +115,12 @@ class SettingsLayout(Widget):
|
||||
# Draw button text (right-aligned)
|
||||
text_size = measure_text_cached(self._font_medium, panel_info.name, 65)
|
||||
text_pos = rl.Vector2(
|
||||
button_rect.x + button_rect.width - text_size.x, button_rect.y + (button_rect.height - text_size.y) / 2
|
||||
button_rect.x + button_rect.width - text_size.x, y + (button_rect.height - text_size.y) / 2
|
||||
)
|
||||
rl.draw_text_ex(self._font_medium, panel_info.name, text_pos, 65, 0, text_color)
|
||||
|
||||
# Store button rect for click detection
|
||||
panel_info.button_rect = button_rect
|
||||
|
||||
y += NAV_BTN_HEIGHT + button_spacing
|
||||
|
||||
def _draw_current_panel(self, rect: rl.Rectangle):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
from openpilot.system.ui.lib.list_view import ListView, button_item, text_item
|
||||
from openpilot.system.ui.lib.list_view import ListView, ButtonItem, TextItem
|
||||
from openpilot.system.ui.lib.widget import Widget, DialogResult
|
||||
from openpilot.system.ui.widgets.confirm_dialog import confirm_dialog
|
||||
|
||||
@@ -15,11 +15,11 @@ class SoftwareLayout(Widget):
|
||||
|
||||
def _init_items(self):
|
||||
items = [
|
||||
text_item("Current Version", ""),
|
||||
button_item("Download", "CHECK", callback=self._on_download_update),
|
||||
button_item("Install Update", "INSTALL", callback=self._on_install_update),
|
||||
button_item("Target Branch", "SELECT", callback=self._on_select_branch),
|
||||
button_item("Uninstall", "UNINSTALL", callback=self._on_uninstall),
|
||||
TextItem("Current Version", ""),
|
||||
ButtonItem("Download", "CHECK", callback=self._on_download_update),
|
||||
ButtonItem("Install Update", "INSTALL", callback=self._on_install_update),
|
||||
ButtonItem("Target Branch", "SELECT", callback=self._on_select_branch),
|
||||
ButtonItem("Uninstall", "UNINSTALL", callback=self._on_uninstall),
|
||||
]
|
||||
return items
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from openpilot.system.ui.lib.list_view import ListView, multiple_button_item, toggle_item
|
||||
from openpilot.system.ui.lib.list_view import ListView, MultipleButtonItem, ToggleItem
|
||||
from openpilot.system.ui.lib.widget import Widget
|
||||
from openpilot.common.params import Params
|
||||
|
||||
@@ -29,24 +29,24 @@ class TogglesLayout(Widget):
|
||||
super().__init__()
|
||||
self._params = Params()
|
||||
items = [
|
||||
toggle_item(
|
||||
ToggleItem(
|
||||
"Enable openpilot",
|
||||
DESCRIPTIONS["OpenpilotEnabledToggle"],
|
||||
self._params.get_bool("OpenpilotEnabledToggle"),
|
||||
icon="chffr_wheel.png",
|
||||
),
|
||||
toggle_item(
|
||||
ToggleItem(
|
||||
"Experimental Mode",
|
||||
initial_state=self._params.get_bool("ExperimentalMode"),
|
||||
icon="experimental_white.png",
|
||||
),
|
||||
toggle_item(
|
||||
ToggleItem(
|
||||
"Disengage on Accelerator Pedal",
|
||||
DESCRIPTIONS["DisengageOnAccelerator"],
|
||||
self._params.get_bool("DisengageOnAccelerator"),
|
||||
icon="disengage_on_accelerator.png",
|
||||
),
|
||||
multiple_button_item(
|
||||
MultipleButtonItem(
|
||||
"Driving Personality",
|
||||
DESCRIPTIONS["LongitudinalPersonality"],
|
||||
buttons=["Aggressive", "Standard", "Relaxed"],
|
||||
@@ -55,25 +55,25 @@ class TogglesLayout(Widget):
|
||||
selected_index=int(self._params.get("LongitudinalPersonality") or 0),
|
||||
icon="speed_limit.png"
|
||||
),
|
||||
toggle_item(
|
||||
ToggleItem(
|
||||
"Enable Lane Departure Warnings",
|
||||
DESCRIPTIONS["IsLdwEnabled"],
|
||||
self._params.get_bool("IsLdwEnabled"),
|
||||
icon="warning.png",
|
||||
),
|
||||
toggle_item(
|
||||
ToggleItem(
|
||||
"Always-On Driver Monitoring",
|
||||
DESCRIPTIONS["AlwaysOnDM"],
|
||||
self._params.get_bool("AlwaysOnDM"),
|
||||
icon="monitoring.png",
|
||||
),
|
||||
toggle_item(
|
||||
ToggleItem(
|
||||
"Record and Upload Driver Camera",
|
||||
DESCRIPTIONS["RecordFront"],
|
||||
self._params.get_bool("RecordFront"),
|
||||
icon="monitoring.png",
|
||||
),
|
||||
toggle_item(
|
||||
ToggleItem(
|
||||
"Use Metric System", DESCRIPTIONS["IsMetric"], self._params.get_bool("IsMetric"), icon="monitoring.png"
|
||||
),
|
||||
]
|
||||
|
||||
@@ -8,10 +8,8 @@ from openpilot.common.params import Params
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight
|
||||
from openpilot.system.ui.lib.button import gui_button, ButtonStyle
|
||||
from openpilot.system.ui.lib.list_view import (
|
||||
ItemAction,
|
||||
ListItem,
|
||||
BUTTON_HEIGHT,
|
||||
BUTTON_BORDER_RADIUS,
|
||||
BUTTON_FONT_SIZE,
|
||||
BUTTON_WIDTH,
|
||||
)
|
||||
@@ -21,18 +19,18 @@ from openpilot.system.ui.widgets.confirm_dialog import alert_dialog
|
||||
from openpilot.system.ui.widgets.keyboard import Keyboard
|
||||
|
||||
|
||||
class SshKeyActionState(Enum):
|
||||
class SshKeyState(Enum):
|
||||
LOADING = "LOADING"
|
||||
ADD = "ADD"
|
||||
REMOVE = "REMOVE"
|
||||
|
||||
|
||||
class SshKeyAction(ItemAction):
|
||||
class SshKeyItem(ListItem):
|
||||
HTTP_TIMEOUT = 15 # seconds
|
||||
MAX_WIDTH = 500
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(self.MAX_WIDTH, True)
|
||||
def __init__(self, title: str, description: str):
|
||||
super().__init__(title, description=description)
|
||||
|
||||
self._keyboard = Keyboard()
|
||||
self._params = Params()
|
||||
@@ -41,11 +39,14 @@ class SshKeyAction(ItemAction):
|
||||
|
||||
self._refresh_state()
|
||||
|
||||
def get_action_width(self) -> int:
|
||||
return self.MAX_WIDTH
|
||||
|
||||
def _refresh_state(self):
|
||||
self._username = self._params.get("GithubUsername", "")
|
||||
self._state = SshKeyActionState.REMOVE if self._params.get("GithubSshKeys") else SshKeyActionState.ADD
|
||||
self._state = SshKeyState.REMOVE if self._params.get("GithubSshKeys") else SshKeyState.ADD
|
||||
|
||||
def _render(self, rect: rl.Rectangle) -> bool:
|
||||
def render_action(self, rect: rl.Rectangle) -> bool:
|
||||
# Show error dialog if there's an error
|
||||
if self._error_message:
|
||||
message = copy.copy(self._error_message)
|
||||
@@ -71,8 +72,8 @@ class SshKeyAction(ItemAction):
|
||||
rect.x + rect.width - BUTTON_WIDTH, rect.y + (rect.height - BUTTON_HEIGHT) / 2, BUTTON_WIDTH, BUTTON_HEIGHT
|
||||
),
|
||||
self._state.value,
|
||||
is_enabled=self._state != SshKeyActionState.LOADING,
|
||||
border_radius=BUTTON_BORDER_RADIUS,
|
||||
is_enabled=self._state != SshKeyState.LOADING,
|
||||
border_radius=BUTTON_HEIGHT // 2,
|
||||
font_size=BUTTON_FONT_SIZE,
|
||||
button_style=ButtonStyle.LIST_ACTION,
|
||||
):
|
||||
@@ -81,11 +82,11 @@ class SshKeyAction(ItemAction):
|
||||
return False
|
||||
|
||||
def _handle_button_click(self):
|
||||
if self._state == SshKeyActionState.ADD:
|
||||
if self._state == SshKeyState.ADD:
|
||||
self._keyboard.clear()
|
||||
self._keyboard.set_title("Enter your GitHub username")
|
||||
gui_app.set_modal_overlay(self._keyboard, callback=self._on_username_submit)
|
||||
elif self._state == SshKeyActionState.REMOVE:
|
||||
elif self._state == SshKeyState.REMOVE:
|
||||
self._params.remove("GithubUsername")
|
||||
self._params.remove("GithubSshKeys")
|
||||
self._refresh_state()
|
||||
@@ -98,7 +99,7 @@ class SshKeyAction(ItemAction):
|
||||
if not username:
|
||||
return
|
||||
|
||||
self._state = SshKeyActionState.LOADING
|
||||
self._state = SshKeyState.LOADING
|
||||
threading.Thread(target=lambda: self._fetch_ssh_key(username), daemon=True).start()
|
||||
|
||||
def _fetch_ssh_key(self, username: str):
|
||||
@@ -113,16 +114,12 @@ class SshKeyAction(ItemAction):
|
||||
# Success - save keys
|
||||
self._params.put("GithubUsername", username)
|
||||
self._params.put("GithubSshKeys", keys)
|
||||
self._state = SshKeyActionState.REMOVE
|
||||
self._state = SshKeyState.REMOVE
|
||||
self._username = username
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
self._error_message = "Request timed out"
|
||||
self._state = SshKeyActionState.ADD
|
||||
self._state = SshKeyState.ADD
|
||||
except Exception:
|
||||
self._error_message = f"No SSH keys found for user '{username}'"
|
||||
self._state = SshKeyActionState.ADD
|
||||
|
||||
|
||||
def ssh_key_item(title: str, description: str):
|
||||
return ListItem(title=title, description=description, action_item=SshKeyAction())
|
||||
self._state = SshKeyState.ADD
|
||||
|
||||
Reference in New Issue
Block a user