diff --git a/selfdrive/ui/qt/home.cc b/selfdrive/ui/qt/home.cc index f2ac7ff6d2..6299f54e7c 100644 --- a/selfdrive/ui/qt/home.cc +++ b/selfdrive/ui/qt/home.cc @@ -6,9 +6,6 @@ #include #include "selfdrive/common/params.h" -#include "selfdrive/common/swaglog.h" -#include "selfdrive/common/timing.h" -#include "selfdrive/common/util.h" #include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/widgets/drive_stats.h" #include "selfdrive/ui/qt/widgets/setup.h" @@ -94,18 +91,24 @@ OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) { // top header QHBoxLayout* header_layout = new QHBoxLayout(); + header_layout->setSpacing(16); date = new QLabel(); - header_layout->addWidget(date, 0, Qt::AlignHCenter | Qt::AlignLeft); + header_layout->addWidget(date, 1, Qt::AlignHCenter | Qt::AlignLeft); - alert_notification = new QPushButton(); - alert_notification->setObjectName("alert_notification"); - alert_notification->setVisible(false); - QObject::connect(alert_notification, &QPushButton::released, this, &OffroadHome::openAlerts); - header_layout->addWidget(alert_notification, 0, Qt::AlignHCenter | Qt::AlignRight); + update_notif = new QPushButton("UPDATE"); + update_notif->setVisible(false); + update_notif->setStyleSheet("background-color: #364DEF;"); + QObject::connect(update_notif, &QPushButton::released, [=]() { center_layout->setCurrentIndex(1); }); + header_layout->addWidget(update_notif, 0, Qt::AlignHCenter | Qt::AlignRight); - QLabel* version = new QLabel(getBrandVersion()); - header_layout->addWidget(version, 0, Qt::AlignHCenter | Qt::AlignRight); + alert_notif = new QPushButton(); + alert_notif->setVisible(false); + alert_notif->setStyleSheet("background-color: #E22C2C;"); + QObject::connect(alert_notif, &QPushButton::released, [=] { center_layout->setCurrentIndex(2); }); + header_layout->addWidget(alert_notif, 0, Qt::AlignHCenter | Qt::AlignRight); + + header_layout->addWidget(new QLabel(getBrandVersion()), 0, Qt::AlignHCenter | Qt::AlignRight); main_layout->addLayout(header_layout); @@ -113,32 +116,29 @@ OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) { main_layout->addSpacing(25); center_layout = new QStackedLayout(); - QHBoxLayout* statsAndSetup = new QHBoxLayout(); + QWidget* statsAndSetupWidget = new QWidget(this); + QHBoxLayout* statsAndSetup = new QHBoxLayout(statsAndSetupWidget); statsAndSetup->setMargin(0); - - DriveStats* drive = new DriveStats; + DriveStats* drive = new DriveStats(); drive->setFixedSize(800, 800); statsAndSetup->addWidget(drive); - - SetupWidget* setup = new SetupWidget; - statsAndSetup->addWidget(setup); - - QWidget* statsAndSetupWidget = new QWidget(); - statsAndSetupWidget->setLayout(statsAndSetup); + statsAndSetup->addWidget(new SetupWidget); center_layout->addWidget(statsAndSetupWidget); + // add update & alerts widgets + update_widget = new UpdateAlert(); + QObject::connect(update_widget, &UpdateAlert::dismiss, [=]() { center_layout->setCurrentIndex(0); }); + center_layout->addWidget(update_widget); alerts_widget = new OffroadAlert(); - QObject::connect(alerts_widget, &OffroadAlert::closeAlerts, this, &OffroadHome::closeAlerts); + QObject::connect(alerts_widget, &OffroadAlert::dismiss, [=]() { center_layout->setCurrentIndex(0); }); center_layout->addWidget(alerts_widget); - center_layout->setAlignment(alerts_widget, Qt::AlignCenter); main_layout->addLayout(center_layout, 1); // set up refresh timer timer = new QTimer(this); - QObject::connect(timer, &QTimer::timeout, this, &OffroadHome::refresh); - timer->start(10 * 1000); + timer->callOnTimeout(this, &OffroadHome::refresh); setStyleSheet(R"( * { @@ -147,61 +147,47 @@ OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) { OffroadHome { background-color: black; } - #alert_notification { - padding: 15px; - padding-left: 30px; - padding-right: 30px; - border: 1px solid; + OffroadHome > QPushButton { + padding: 15px 30px; border-radius: 5px; font-size: 40px; font-weight: 500; } - OffroadHome>QLabel { + OffroadHome > QLabel { font-size: 55px; } )"); -} - -void OffroadHome::showEvent(QShowEvent *event) { refresh(); } -void OffroadHome::openAlerts() { - center_layout->setCurrentIndex(1); +void OffroadHome::showEvent(QShowEvent *event) { + timer->start(10 * 1000); } -void OffroadHome::closeAlerts() { - center_layout->setCurrentIndex(0); +void OffroadHome::hideEvent(QHideEvent *event) { + timer->stop(); } void OffroadHome::refresh() { - bool first_refresh = !date->text().size(); - if (!isVisible() && !first_refresh) { - return; - } - date->setText(QDateTime::currentDateTime().toString("dddd, MMMM d")); - // update alerts - - alerts_widget->refresh(); - if (!alerts_widget->alertCount && !alerts_widget->updateAvailable) { - closeAlerts(); - alert_notification->setVisible(false); - return; - } - - if (alerts_widget->updateAvailable) { - alert_notification->setText("UPDATE"); - } else { - int alerts = alerts_widget->alertCount; - alert_notification->setText(QString::number(alerts) + " ALERT" + (alerts == 1 ? "" : "S")); + bool updateAvailable = update_widget->refresh(); + int alerts = alerts_widget->refresh(); + + // pop-up new notification + int idx = center_layout->currentIndex(); + if (!updateAvailable && !alerts) { + idx = 0; + } else if (updateAvailable && (!update_notif->isVisible() || (!alerts && idx == 2))) { + idx = 1; + } else if (alerts && (!alert_notif->isVisible() || (!updateAvailable && idx == 1))) { + idx = 2; } + center_layout->setCurrentIndex(idx); - if (!alert_notification->isVisible() && !first_refresh) { - openAlerts(); + update_notif->setVisible(updateAvailable); + alert_notif->setVisible(alerts); + if (alerts) { + alert_notif->setText(QString::number(alerts) + " ALERT" + (alerts > 1 ? "S" : "")); } - alert_notification->setVisible(true); - // Red background for alerts, blue for update available - alert_notification->setStyleSheet(alerts_widget->updateAvailable ? "background-color: #364DEF" : "background-color: #E22C2C"); } diff --git a/selfdrive/ui/qt/home.h b/selfdrive/ui/qt/home.h index ab60af0a21..e133710bcd 100644 --- a/selfdrive/ui/qt/home.h +++ b/selfdrive/ui/qt/home.h @@ -19,21 +19,18 @@ class OffroadHome : public QFrame { public: explicit OffroadHome(QWidget* parent = 0); -protected: +private: void showEvent(QShowEvent *event) override; + void hideEvent(QHideEvent *event) override; + void refresh(); -private: QTimer* timer; - QLabel* date; QStackedLayout* center_layout; + UpdateAlert *update_widget; OffroadAlert* alerts_widget; - QPushButton* alert_notification; - -public slots: - void closeAlerts(); - void openAlerts(); - void refresh(); + QPushButton* alert_notif; + QPushButton* update_notif; }; class HomeWindow : public QWidget { diff --git a/selfdrive/ui/qt/widgets/offroad_alerts.cc b/selfdrive/ui/qt/widgets/offroad_alerts.cc index 1074548758..0fcdb87161 100644 --- a/selfdrive/ui/qt/widgets/offroad_alerts.cc +++ b/selfdrive/ui/qt/widgets/offroad_alerts.cc @@ -3,33 +3,21 @@ #include #include #include -#include +#include #include "selfdrive/common/util.h" #include "selfdrive/hardware/hw.h" +#include "selfdrive/ui/qt/widgets/scrollview.h" -OffroadAlert::OffroadAlert(QWidget* parent) : QFrame(parent) { +AbstractAlert::AbstractAlert(bool hasRebootBtn, QWidget *parent) : QFrame(parent) { QVBoxLayout *main_layout = new QVBoxLayout(this); main_layout->setMargin(50); main_layout->setSpacing(30); - QWidget *alerts_widget = new QWidget(this); - alerts_layout = new QVBoxLayout(alerts_widget); - alerts_layout->setMargin(0); - alerts_layout->setSpacing(30); - alerts_widget->setStyleSheet("background-color: transparent;"); - - // release notes - releaseNotes.setWordWrap(true); - releaseNotes.setVisible(false); - releaseNotes.setStyleSheet("font-size: 48px;"); - releaseNotes.setAlignment(Qt::AlignTop); - - releaseNotesScroll = new ScrollView(&releaseNotes, this); - main_layout->addWidget(releaseNotesScroll); - - alertsScroll = new ScrollView(alerts_widget, this); - main_layout->addWidget(alertsScroll); + QWidget *widget = new QWidget; + scrollable_layout = new QVBoxLayout(widget); + widget->setStyleSheet("background-color: transparent;"); + main_layout->addWidget(new ScrollView(widget)); // bottom footer, dismiss + reboot buttons QHBoxLayout *footer_layout = new QHBoxLayout(); @@ -38,14 +26,14 @@ OffroadAlert::OffroadAlert(QWidget* parent) : QFrame(parent) { QPushButton *dismiss_btn = new QPushButton("Dismiss"); dismiss_btn->setFixedSize(400, 125); footer_layout->addWidget(dismiss_btn, 0, Qt::AlignBottom | Qt::AlignLeft); - QObject::connect(dismiss_btn, &QPushButton::released, this, &OffroadAlert::closeAlerts); - - rebootBtn.setText("Reboot and Update"); - rebootBtn.setFixedSize(600, 125); - rebootBtn.setVisible(false); - footer_layout->addWidget(&rebootBtn, 0, Qt::AlignBottom | Qt::AlignRight); - QObject::connect(&rebootBtn, &QPushButton::released, [=]() { Hardware::reboot(); }); + QObject::connect(dismiss_btn, &QPushButton::released, this, &AbstractAlert::dismiss); + if (hasRebootBtn) { + QPushButton *rebootBtn = new QPushButton("Reboot and Update"); + rebootBtn->setFixedSize(600, 125); + footer_layout->addWidget(rebootBtn, 0, Qt::AlignBottom | Qt::AlignRight); + QObject::connect(rebootBtn, &QPushButton::released, [=]() { Hardware::reboot(); }); + } setStyleSheet(R"( * { font-size: 48px; @@ -64,49 +52,55 @@ OffroadAlert::OffroadAlert(QWidget* parent) : QFrame(parent) { )"); } -void OffroadAlert::refresh() { +int OffroadAlert::refresh() { if (alerts.empty()) { // setup labels for each alert - QString json = QString::fromStdString(util::read_file("../controls/lib/alerts_offroad.json")); + QString json = util::read_file("../controls/lib/alerts_offroad.json").c_str(); QJsonObject obj = QJsonDocument::fromJson(json.toUtf8()).object(); - for (auto &k : obj.keys()) { - QLabel *l = new QLabel(this); - alerts[k.toStdString()] = l; - int severity = obj[k].toObject()["severity"].toInt(); + // descending sort labels by severity + std::vector> sorted; + for (auto it = obj.constBegin(); it != obj.constEnd(); ++it) { + sorted.push_back({it.key().toStdString(), it.value()["severity"].toInt()}); + } + std::sort(sorted.begin(), sorted.end(), [=](auto &l, auto &r) { return l.second > r.second; }); + for (auto &[key, severity] : sorted) { + QLabel *l = new QLabel(this); + alerts[key] = l; l->setMargin(60); l->setWordWrap(true); - l->setStyleSheet("background-color: " + QString(severity ? "#E22C2C" : "#292929")); - l->setVisible(false); - alerts_layout->addWidget(l); + l->setStyleSheet(QString("background-color: %1").arg(severity ? "#E22C2C" : "#292929")); + scrollable_layout->addWidget(l); } - alerts_layout->addStretch(1); + scrollable_layout->addStretch(1); } - updateAlerts(); - - rebootBtn.setVisible(updateAvailable); - releaseNotesScroll->setVisible(updateAvailable); - releaseNotes.setText(QString::fromStdString(params.get("ReleaseNotes"))); - - alertsScroll->setVisible(!updateAvailable); - for (const auto& [k, label] : alerts) { - label->setVisible(!label->text().isEmpty()); + int alertCount = 0; + for (const auto &[key, label] : alerts) { + QString text; + std::string bytes = params.get(key); + if (bytes.size()) { + auto doc_par = QJsonDocument::fromJson(bytes.c_str()); + text = doc_par["text"].toString(); + } + label->setText(text); + label->setVisible(!text.isEmpty()); + alertCount += !text.isEmpty(); } + return alertCount; } -void OffroadAlert::updateAlerts() { - alertCount = 0; - for (const auto& [key, label] : alerts) { - auto bytes = params.get(key.c_str()); - if (bytes.size()) { - QJsonDocument doc_par = QJsonDocument::fromJson(QByteArray(bytes.data(), bytes.size())); - QJsonObject obj = doc_par.object(); - label->setText(obj.value("text").toString()); - alertCount++; - } else { - label->setText(""); - } +UpdateAlert::UpdateAlert(QWidget *parent) : AbstractAlert(true, parent) { + releaseNotes = new QLabel(this); + releaseNotes->setWordWrap(true); + releaseNotes->setAlignment(Qt::AlignTop); + scrollable_layout->addWidget(releaseNotes); +} + +bool UpdateAlert::refresh() { + bool updateAvailable = params.getBool("UpdateAvailable"); + if (updateAvailable) { + releaseNotes->setText(params.get("ReleaseNotes").c_str()); } - updateAvailable = params.getBool("UpdateAvailable") && alertCount < 1; + return updateAvailable; } diff --git a/selfdrive/ui/qt/widgets/offroad_alerts.h b/selfdrive/ui/qt/widgets/offroad_alerts.h index 88a69c8dac..c285457c6f 100644 --- a/selfdrive/ui/qt/widgets/offroad_alerts.h +++ b/selfdrive/ui/qt/widgets/offroad_alerts.h @@ -2,37 +2,41 @@ #include -#include #include -#include #include #include "selfdrive/common/params.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" -class OffroadAlert : public QFrame { +class AbstractAlert : public QFrame { + Q_OBJECT + +protected: + AbstractAlert(bool hasRebootBtn, QWidget *parent = nullptr); + QVBoxLayout *scrollable_layout; + Params params; + +signals: + void dismiss(); +}; + +class UpdateAlert : public AbstractAlert { Q_OBJECT public: - explicit OffroadAlert(QWidget *parent = 0); - int alertCount = 0; - bool updateAvailable; + UpdateAlert(QWidget *parent = 0); + bool refresh(); private: - void updateAlerts(); - - Params params; - std::map alerts; + QLabel *releaseNotes = nullptr; +}; - QLabel releaseNotes; - QPushButton rebootBtn; - ScrollView *alertsScroll; - ScrollView *releaseNotesScroll; - QVBoxLayout *alerts_layout; +class OffroadAlert : public AbstractAlert { + Q_OBJECT -signals: - void closeAlerts(); +public: + explicit OffroadAlert(QWidget *parent = 0) : AbstractAlert(false, parent) {} + int refresh(); -public slots: - void refresh(); +private: + std::map alerts; };