#include "selfdrive/ui/qt/home.h" #include #include #include #include #include "common/swaglog.h" #include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/widgets/scrollview.h" // HomeWindow: the container for the offroad and onroad UIs HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) { // CLEARPILOT Sidebar set to invisible in drive view. params.putBool("Sidebar", false); QHBoxLayout *main_layout = new QHBoxLayout(this); main_layout->setMargin(0); main_layout->setSpacing(0); sidebar = new Sidebar(this); main_layout->addWidget(sidebar); QObject::connect(sidebar, &Sidebar::openSettings, this, &HomeWindow::openSettings); QObject::connect(sidebar, &Sidebar::openOnroad, this, &HomeWindow::showOnroad); slayout = new QStackedLayout(); main_layout->addLayout(slayout); home = new ClearPilotPanel(this); QObject::connect(home, &ClearPilotPanel::openSettings, this, &HomeWindow::openSettings); QObject::connect(home, &ClearPilotPanel::openStatus, this, &HomeWindow::openStatus); QObject::connect(home, &ClearPilotPanel::closePanel, [=]() { // Return to splash or onroad depending on state if (uiState()->scene.started) { slayout->setCurrentWidget(onroad); } else { slayout->setCurrentWidget(ready); } }); slayout->addWidget(home); onroad = new OnroadWindow(this); slayout->addWidget(onroad); // CLEARPILOT ready = new ReadyWindow(this); slayout->addWidget(ready); driver_view = new DriverViewWindow(this); connect(driver_view, &DriverViewWindow::done, [=] { showDriverView(false); }); slayout->addWidget(driver_view); setAttribute(Qt::WA_NoSystemBackground); QObject::connect(uiState(), &UIState::uiUpdate, this, &HomeWindow::updateState); QObject::connect(uiState(), &UIState::offroadTransition, this, &HomeWindow::offroadTransition); QObject::connect(uiState(), &UIState::offroadTransition, sidebar, &Sidebar::offroadTransition); } // Debug function to activate onroad UI void HomeWindow::showOnroad() { sidebar->setVisible(false); slayout->setCurrentWidget(onroad); // sidebar->setVisible(params.getBool("Sidebar")); } void HomeWindow::showSidebar(bool show) { sidebar->setVisible(show); } void HomeWindow::updateState(const UIState &s) { if (s.scene.started) { showDriverView(s.scene.driver_camera_timer >= 10, true); // CLEARPILOT: show splash screen when onroad but in park bool parked = s.scene.parked; if (parked && !was_parked_onroad) { LOGW("CLP UI: park transition -> showing splash"); slayout->setCurrentWidget(ready); } else if (!parked && was_parked_onroad) { LOGW("CLP UI: drive transition -> showing onroad"); slayout->setCurrentWidget(onroad); } was_parked_onroad = parked; // CLEARPILOT: honor display on/off while showing splash in park if (parked && ready->isVisible()) { int screenMode = paramsMemory.getInt("ScreenDisaplayMode"); if (screenMode == 1) { Hardware::set_display_power(false); } else { Hardware::set_display_power(true); } } } } void HomeWindow::offroadTransition(bool offroad) { sidebar->setVisible(false); if (offroad) { LOGW("CLP UI: offroad transition -> showing splash"); was_parked_onroad = false; slayout->setCurrentWidget(ready); } else { // CLEARPILOT: start onroad in splash — updateState will switch to // camera view once the car shifts out of park LOGW("CLP UI: onroad transition -> showing splash (parked)"); was_parked_onroad = true; slayout->setCurrentWidget(ready); } } void HomeWindow::showDriverView(bool show, bool started) { if (show) { LOGW("CLP UI: showDriverView(true) -> driver_view"); emit closeSettings(); slayout->setCurrentWidget(driver_view); sidebar->setVisible(false); } else if (!started) { // Offroad, not started — show home menu slayout->setCurrentWidget(home); sidebar->setVisible(false); } // CLEARPILOT: when started, don't touch slayout here — // updateState handles park->splash and drive->onroad transitions } void HomeWindow::mousePressEvent(QMouseEvent* e) { // CLEARPILOT: tap from any view goes to ClearPilotPanel if (ready->isVisible() || onroad->isVisible()) { LOGW("CLP UI: tap -> showing ClearPilotPanel"); sidebar->setVisible(false); slayout->setCurrentWidget(home); } } void HomeWindow::mouseDoubleClickEvent(QMouseEvent* e) { HomeWindow::mousePressEvent(e); // const SubMaster &sm = *(uiState()->sm); } // CLEARPILOT: ClearPilotPanel — settings-style sidebar menu static const char *clpSidebarBtnStyle = R"( QPushButton { color: grey; border: none; background: none; font-size: 65px; font-weight: 500; } QPushButton:checked { color: white; } QPushButton:pressed { color: #ADADAD; } )"; ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) { // Sidebar QWidget *sidebar_widget = new QWidget; QVBoxLayout *sidebar_layout = new QVBoxLayout(sidebar_widget); sidebar_layout->setContentsMargins(50, 50, 100, 50); // Close button QPushButton *close_btn = new QPushButton("← Back"); close_btn->setStyleSheet(R"( QPushButton { color: white; border-radius: 25px; background: #292929; font-size: 50px; font-weight: 500; } QPushButton:pressed { color: #ADADAD; } )"); close_btn->setFixedSize(300, 125); sidebar_layout->addSpacing(10); sidebar_layout->addWidget(close_btn, 0, Qt::AlignRight); QObject::connect(close_btn, &QPushButton::clicked, [=]() { emit closePanel(); }); // Panel content area panel_widget = new QStackedWidget(); // Define panels: sidebar label -> content widget // Home panel: buttons for Status and System Settings QWidget *home_panel = new QWidget(this); QVBoxLayout *home_layout = new QVBoxLayout(home_panel); home_layout->setContentsMargins(50, 25, 50, 25); home_layout->setSpacing(20); QPushButton *status_btn = new QPushButton("Status"); status_btn->setFixedHeight(120); status_btn->setStyleSheet(R"( QPushButton { background-color: #393939; color: white; border-radius: 15px; font-size: 50px; font-weight: 500; text-align: left; padding-left: 30px; } QPushButton:pressed { background-color: #4a4a4a; } )"); QObject::connect(status_btn, &QPushButton::clicked, [=]() { emit openStatus(); }); home_layout->addWidget(status_btn); QPushButton *sysset_btn = new QPushButton("System Settings"); sysset_btn->setFixedHeight(120); sysset_btn->setStyleSheet(status_btn->styleSheet()); QObject::connect(sysset_btn, &QPushButton::clicked, [=]() { emit openSettings(); }); home_layout->addWidget(sysset_btn); home_layout->addStretch(); // Dashcam panel: placeholder QWidget *dashcam_panel = new QWidget(this); QVBoxLayout *dash_layout = new QVBoxLayout(dashcam_panel); dash_layout->setContentsMargins(50, 25, 50, 25); QLabel *dash_label = new QLabel("Dashcam viewer coming soon"); dash_label->setStyleSheet("color: grey; font-size: 40px;"); dash_label->setAlignment(Qt::AlignCenter); dash_layout->addWidget(dash_label); dash_layout->addStretch(); // Debug panel ListWidget *debug_panel = new ListWidget(this); debug_panel->setContentsMargins(50, 25, 50, 25); auto *telemetry_toggle = new ParamControl("TelemetryEnabled", "Telemetry Logging", "Record telemetry data to CSV in the session log directory. " "Captures only changed values for efficiency.", "", this); debug_panel->addItem(telemetry_toggle); // Register panels with sidebar buttons QList> panels = { {"Home", home_panel}, {"Dashcam", dashcam_panel}, {"Debug", debug_panel}, }; for (auto &[name, panel] : panels) { QPushButton *btn = new QPushButton(name); btn->setCheckable(true); btn->setStyleSheet(clpSidebarBtnStyle); btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); sidebar_layout->addWidget(btn, 0, Qt::AlignRight); ScrollView *panel_frame = new ScrollView(panel, this); panel_widget->addWidget(panel_frame); QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() { btn->setChecked(true); panel_widget->setCurrentWidget(w); }); } // Select Home by default if (auto *first_btn = sidebar_widget->findChild(QString(), Qt::FindDirectChildrenOnly)) { // Skip close_btn, find first sidebar btn } panel_widget->setCurrentIndex(0); // Main layout: sidebar + panels QHBoxLayout *main_layout = new QHBoxLayout(this); sidebar_widget->setFixedWidth(500); main_layout->addWidget(sidebar_widget); main_layout->addWidget(panel_widget); setStyleSheet(R"( * { color: white; font-size: 50px; } ClearPilotPanel { background-color: black; } QStackedWidget, ScrollView { background-color: #292929; border-radius: 30px; } )"); }