diff --git a/selfdrive/assets/fonts/JetBrainsMono-Medium.ttf b/selfdrive/assets/fonts/JetBrainsMono-Medium.ttf
new file mode 100644
index 0000000000..a6ba5529af
Binary files /dev/null and b/selfdrive/assets/fonts/JetBrainsMono-Medium.ttf differ
diff --git a/selfdrive/assets/img_couch.svg b/selfdrive/assets/img_couch.svg
new file mode 100644
index 0000000000..5b3c048318
--- /dev/null
+++ b/selfdrive/assets/img_couch.svg
@@ -0,0 +1,3 @@
+
diff --git a/selfdrive/assets/img_experimental.svg b/selfdrive/assets/img_experimental.svg
new file mode 100644
index 0000000000..0eaec3b3cd
--- /dev/null
+++ b/selfdrive/assets/img_experimental.svg
@@ -0,0 +1,10 @@
+
diff --git a/selfdrive/assets/img_experimental_grey.svg b/selfdrive/assets/img_experimental_grey.svg
new file mode 100644
index 0000000000..dc87105ac5
--- /dev/null
+++ b/selfdrive/assets/img_experimental_grey.svg
@@ -0,0 +1,4 @@
+
diff --git a/selfdrive/assets/img_experimental_white.svg b/selfdrive/assets/img_experimental_white.svg
new file mode 100644
index 0000000000..ae4f18fde2
--- /dev/null
+++ b/selfdrive/assets/img_experimental_white.svg
@@ -0,0 +1,4 @@
+
diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript
index 84e055752a..669c214746 100644
--- a/selfdrive/ui/SConscript
+++ b/selfdrive/ui/SConscript
@@ -58,7 +58,7 @@ qt_env.Program("qt/spinner", ["qt/spinner.cc"], LIBS=qt_libs)
qt_src = ["main.cc", "qt/sidebar.cc", "qt/onroad.cc", "qt/body.cc",
"qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc",
"qt/offroad/software_settings.cc", "qt/offroad/onboarding.cc",
- "qt/offroad/driverview.cc"]
+ "qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc"]
qt_env.Program("_ui", qt_src + [asset_obj], LIBS=qt_libs)
if GetOption('test'):
qt_src.remove("main.cc") # replaced by test_runner
diff --git a/selfdrive/ui/qt/home.cc b/selfdrive/ui/qt/home.cc
index 3fe00e6ed9..3f3c9a5885 100644
--- a/selfdrive/ui/qt/home.cc
+++ b/selfdrive/ui/qt/home.cc
@@ -4,6 +4,7 @@
#include
#include
+#include "selfdrive/ui/qt/offroad/experimental_mode.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/widgets/drive_stats.h"
#include "selfdrive/ui/qt/widgets/prime.h"
@@ -22,7 +23,8 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
slayout = new QStackedLayout();
main_layout->addLayout(slayout);
- home = new OffroadHome();
+ home = new OffroadHome(this);
+ QObject::connect(home, &OffroadHome::openSettings, this, &HomeWindow::openSettings);
slayout->addWidget(home);
onroad = new OnroadWindow(this);
@@ -128,11 +130,24 @@ OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) {
main_layout->addSpacing(25);
center_layout = new QStackedLayout();
+ // Vertical experimental button and drive stats layout
+ QWidget* statsAndExperimentalModeButtonWidget = new QWidget(this);
+ QVBoxLayout* statsAndExperimentalModeButton = new QVBoxLayout(statsAndExperimentalModeButtonWidget);
+ statsAndExperimentalModeButton->setSpacing(30);
+ statsAndExperimentalModeButton->setMargin(0);
+
+ ExperimentalModeButton *experimental_mode = new ExperimentalModeButton(this);
+ QObject::connect(experimental_mode, &ExperimentalModeButton::openSettings, this, &OffroadHome::openSettings);
+
+ statsAndExperimentalModeButton->addWidget(experimental_mode, 1);
+ statsAndExperimentalModeButton->addWidget(new DriveStats, 1);
+
+ // Horizontal experimental + drive stats and setup widget
QWidget* statsAndSetupWidget = new QWidget(this);
QHBoxLayout* statsAndSetup = new QHBoxLayout(statsAndSetupWidget);
statsAndSetup->setMargin(0);
statsAndSetup->setSpacing(30);
- statsAndSetup->addWidget(new DriveStats, 1);
+ statsAndSetup->addWidget(statsAndExperimentalModeButtonWidget, 1);
statsAndSetup->addWidget(new SetupWidget);
center_layout->addWidget(statsAndSetupWidget);
diff --git a/selfdrive/ui/qt/home.h b/selfdrive/ui/qt/home.h
index 6636da56ec..ed1c215467 100644
--- a/selfdrive/ui/qt/home.h
+++ b/selfdrive/ui/qt/home.h
@@ -22,6 +22,9 @@ class OffroadHome : public QFrame {
public:
explicit OffroadHome(QWidget* parent = 0);
+signals:
+ void openSettings(int index = 0, const QString ¶m = "");
+
private:
void showEvent(QShowEvent *event) override;
void hideEvent(QHideEvent *event) override;
@@ -45,7 +48,7 @@ public:
explicit HomeWindow(QWidget* parent = 0);
signals:
- void openSettings();
+ void openSettings(int index = 0, const QString ¶m = "");
void closeSettings();
public slots:
diff --git a/selfdrive/ui/qt/offroad/experimental_mode.cc b/selfdrive/ui/qt/offroad/experimental_mode.cc
new file mode 100644
index 0000000000..f73149cdf2
--- /dev/null
+++ b/selfdrive/ui/qt/offroad/experimental_mode.cc
@@ -0,0 +1,75 @@
+#include "selfdrive/ui/qt/offroad/experimental_mode.h"
+
+#include
+#include
+#include
+#include
+
+#include "selfdrive/ui/ui.h"
+
+ExperimentalModeButton::ExperimentalModeButton(QWidget *parent) : QPushButton(parent) {
+ chill_pixmap = QPixmap("../assets/img_couch.svg").scaledToWidth(img_width, Qt::SmoothTransformation);
+ experimental_pixmap = QPixmap("../assets/img_experimental_grey.svg").scaledToWidth(img_width, Qt::SmoothTransformation);
+
+ // go to toggles and expand experimental mode description
+ connect(this, &QPushButton::clicked, [=]() { emit openSettings(2, "ExperimentalMode"); });
+
+ setFixedHeight(125);
+ QHBoxLayout *main_layout = new QHBoxLayout;
+ main_layout->setContentsMargins(horizontal_padding, 0, horizontal_padding, 0);
+
+ mode_label = new QLabel;
+ mode_icon = new QLabel;
+ mode_icon->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
+
+ main_layout->addWidget(mode_label, 1, Qt::AlignLeft);
+ main_layout->addWidget(mode_icon, 0, Qt::AlignRight);
+
+ setLayout(main_layout);
+
+ setStyleSheet(R"(
+ QPushButton {
+ border: none;
+ }
+
+ QLabel {
+ font-size: 45px;
+ font-weight: 300;
+ text-align: left;
+ font-family: JetBrainsMono;
+ color: #000000;
+ }
+ )");
+}
+
+void ExperimentalModeButton::paintEvent(QPaintEvent *event) {
+ QPainter p(this);
+ p.setPen(Qt::NoPen);
+ p.setRenderHint(QPainter::Antialiasing);
+
+ QPainterPath path;
+ path.addRoundedRect(rect(), 10, 10);
+
+ // gradient
+ bool pressed = isDown();
+ QLinearGradient gradient(rect().left(), 0, rect().right(), 0);
+ if (experimental_mode) {
+ gradient.setColorAt(0, QColor(255, 155, 63, pressed ? 0xcc : 0xff));
+ gradient.setColorAt(1, QColor(219, 56, 34, pressed ? 0xcc : 0xff));
+ } else {
+ gradient.setColorAt(0, QColor(20, 255, 171, pressed ? 0xcc : 0xff));
+ gradient.setColorAt(1, QColor(35, 149, 255, pressed ? 0xcc : 0xff));
+ }
+ p.fillPath(path, gradient);
+
+ // vertical line
+ p.setPen(QPen(QColor(0, 0, 0, 0x4d), 3, Qt::SolidLine));
+ int line_x = rect().right() - img_width - (2 * horizontal_padding);
+ p.drawLine(line_x, rect().bottom(), line_x, rect().top());
+}
+
+void ExperimentalModeButton::showEvent(QShowEvent *event) {
+ experimental_mode = params.getBool("ExperimentalMode");
+ mode_icon->setPixmap(experimental_mode ? experimental_pixmap : chill_pixmap);
+ mode_label->setText(experimental_mode ? tr("EXPERIMENTAL MODE ON") : tr("CHILL MODE ON"));
+}
diff --git a/selfdrive/ui/qt/offroad/experimental_mode.h b/selfdrive/ui/qt/offroad/experimental_mode.h
new file mode 100644
index 0000000000..bfb7638bbe
--- /dev/null
+++ b/selfdrive/ui/qt/offroad/experimental_mode.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include
+#include
+
+#include "common/params.h"
+
+class ExperimentalModeButton : public QPushButton {
+ Q_OBJECT
+
+public:
+ explicit ExperimentalModeButton(QWidget* parent = 0);
+
+signals:
+ void openSettings(int index = 0, const QString &toggle = "");
+
+private:
+ void showEvent(QShowEvent *event) override;
+
+ Params params;
+ bool experimental_mode;
+ int img_width = 100;
+ int horizontal_padding = 30;
+ QPixmap experimental_pixmap;
+ QPixmap chill_pixmap;
+ QLabel *mode_label;
+ QLabel *mode_icon;
+
+protected:
+ void paintEvent(QPaintEvent *event) override;
+};
diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc
index 85b09dc183..01cb0ea720 100644
--- a/selfdrive/ui/qt/offroad/settings.cc
+++ b/selfdrive/ui/qt/offroad/settings.cc
@@ -39,7 +39,7 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
"ExperimentalMode",
tr("Experimental Mode"),
"",
- "../assets/offroad/icon_road.png",
+ "../assets/img_experimental_white.svg",
},
{
"ExperimentalLongitudinalEnabled",
@@ -100,6 +100,7 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
}
// Toggles with confirmation dialogs
+ toggles["ExperimentalMode"]->setActiveIcon("../assets/img_experimental.svg");
toggles["ExperimentalMode"]->setConfirmation(true, true);
toggles["ExperimentalLongitudinalEnabled"]->setConfirmation(true, false);
@@ -108,6 +109,10 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
});
}
+void TogglesPanel::expandToggleDescription(const QString ¶m) {
+ toggles[param.toStdString()]->showDescription();
+}
+
void TogglesPanel::showEvent(QShowEvent *event) {
updateToggles();
}
@@ -299,8 +304,15 @@ void DevicePanel::poweroff() {
}
void SettingsWindow::showEvent(QShowEvent *event) {
- panel_widget->setCurrentIndex(0);
- nav_btns->buttons()[0]->setChecked(true);
+ setCurrentPanel(0);
+}
+
+void SettingsWindow::setCurrentPanel(int index, const QString ¶m) {
+ panel_widget->setCurrentIndex(index);
+ nav_btns->buttons()[index]->setChecked(true);
+ if (!param.isEmpty()) {
+ emit expandToggleDescription(param);
+ }
}
SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
@@ -341,10 +353,13 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
QObject::connect(device, &DevicePanel::reviewTrainingGuide, this, &SettingsWindow::reviewTrainingGuide);
QObject::connect(device, &DevicePanel::showDriverView, this, &SettingsWindow::showDriverView);
+ TogglesPanel *toggles = new TogglesPanel(this);
+ QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription);
+
QList> panels = {
{tr("Device"), device},
{tr("Network"), new Networking(this)},
- {tr("Toggles"), new TogglesPanel(this)},
+ {tr("Toggles"), toggles},
{tr("Software"), new SoftwarePanel(this)},
};
diff --git a/selfdrive/ui/qt/offroad/settings.h b/selfdrive/ui/qt/offroad/settings.h
index 4177f28cf4..c63be4e138 100644
--- a/selfdrive/ui/qt/offroad/settings.h
+++ b/selfdrive/ui/qt/offroad/settings.h
@@ -17,6 +17,7 @@ class SettingsWindow : public QFrame {
public:
explicit SettingsWindow(QWidget *parent = 0);
+ void setCurrentPanel(int index, const QString ¶m = "");
protected:
void showEvent(QShowEvent *event) override;
@@ -25,6 +26,7 @@ signals:
void closeSettings();
void reviewTrainingGuide();
void showDriverView();
+ void expandToggleDescription(const QString ¶m);
private:
QPushButton *sidebar_alert_widget;
@@ -56,6 +58,9 @@ public:
explicit TogglesPanel(SettingsWindow *parent);
void showEvent(QShowEvent *event) override;
+public slots:
+ void expandToggleDescription(const QString ¶m);
+
private:
Params params;
std::map toggles;
diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc
index fcf29181e7..50f891dd56 100644
--- a/selfdrive/ui/qt/onroad.cc
+++ b/selfdrive/ui/qt/onroad.cc
@@ -174,6 +174,7 @@ AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget* par
pm = std::make_unique>({"uiDebug"});
engage_img = loadPixmap("../assets/img_chffr_wheel.png", {img_size, img_size});
+ experimental_img = loadPixmap("../assets/img_experimental.svg", {img_size - 5, img_size - 5});
dm_img = loadPixmap("../assets/img_driver_face.png", {img_size, img_size});
}
@@ -378,8 +379,9 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) {
// engage-ability icon
if (engageable) {
+ SubMaster &sm = *(uiState()->sm);
drawIcon(p, rect().right() - radius / 2 - bdr_s * 2, radius / 2 + int(bdr_s * 1.5),
- engage_img, bg_colors[status], 1.0);
+ sm["controlsState"].getControlsState().getExperimentalMode() ? experimental_img : engage_img, blackColor(166), 1.0);
}
// dm icon
@@ -409,7 +411,7 @@ void AnnotatedCameraWidget::drawIcon(QPainter &p, int x, int y, QPixmap &img, QB
p.setBrush(bg);
p.drawEllipse(x - radius / 2, y - radius / 2, radius, radius);
p.setOpacity(opacity);
- p.drawPixmap(x - img_size / 2, y - img_size / 2, img);
+ p.drawPixmap(x - img.size().width() / 2, y - img.size().height() / 2, img);
}
diff --git a/selfdrive/ui/qt/onroad.h b/selfdrive/ui/qt/onroad.h
index 7edca6b3d5..9e18355970 100644
--- a/selfdrive/ui/qt/onroad.h
+++ b/selfdrive/ui/qt/onroad.h
@@ -51,6 +51,7 @@ private:
void drawText(QPainter &p, int x, int y, const QString &text, int alpha = 255);
QPixmap engage_img;
+ QPixmap experimental_img;
QPixmap dm_img;
const int radius = 192;
const int img_size = (radius / 2) * 1.5;
diff --git a/selfdrive/ui/qt/sidebar.h b/selfdrive/ui/qt/sidebar.h
index 53ad7467ac..fb96e1d540 100644
--- a/selfdrive/ui/qt/sidebar.h
+++ b/selfdrive/ui/qt/sidebar.h
@@ -20,7 +20,7 @@ public:
explicit Sidebar(QWidget* parent = 0);
signals:
- void openSettings();
+ void openSettings(int index = 0, const QString ¶m = "");
void valueChanged();
public slots:
diff --git a/selfdrive/ui/qt/window.cc b/selfdrive/ui/qt/window.cc
index 04ce15ef23..198b1edbf6 100644
--- a/selfdrive/ui/qt/window.cc
+++ b/selfdrive/ui/qt/window.cc
@@ -53,6 +53,7 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
QFontDatabase::addApplicationFont("../assets/fonts/Inter-Regular.ttf");
QFontDatabase::addApplicationFont("../assets/fonts/Inter-SemiBold.ttf");
QFontDatabase::addApplicationFont("../assets/fonts/Inter-Thin.ttf");
+ QFontDatabase::addApplicationFont("../assets/fonts/JetBrainsMono-Medium.ttf");
// no outline to prevent the focus rectangle
setStyleSheet(R"(
@@ -64,8 +65,9 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
setAttribute(Qt::WA_NoSystemBackground);
}
-void MainWindow::openSettings() {
+void MainWindow::openSettings(int index, const QString ¶m) {
main_layout->setCurrentWidget(settingsWindow);
+ settingsWindow->setCurrentPanel(index, param);
}
void MainWindow::closeSettings() {
diff --git a/selfdrive/ui/qt/window.h b/selfdrive/ui/qt/window.h
index 0bd328aa8a..71fc466c20 100644
--- a/selfdrive/ui/qt/window.h
+++ b/selfdrive/ui/qt/window.h
@@ -15,7 +15,7 @@ public:
private:
bool eventFilter(QObject *obj, QEvent *event) override;
- void openSettings();
+ void openSettings(int index = 0, const QString ¶m = "");
void closeSettings();
Device device;
diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts
index 21fdc48fef..963eeadd81 100644
--- a/selfdrive/ui/translations/main_ja.ts
+++ b/selfdrive/ui/translations/main_ja.ts
@@ -281,6 +281,17 @@
カメラを起動しています
+
+ ExperimentalModeButton
+
+ EXPERIMENTAL MODE ON
+
+
+
+ CHILL MODE ON
+
+
+
InputDialog
diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts
index 57c688ffe6..9defc2c36f 100644
--- a/selfdrive/ui/translations/main_ko.ts
+++ b/selfdrive/ui/translations/main_ko.ts
@@ -281,6 +281,17 @@
카메라 시작중
+
+ ExperimentalModeButton
+
+ EXPERIMENTAL MODE ON
+
+
+
+ CHILL MODE ON
+
+
+
InputDialog
diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts
index 2d7195e0d2..a1c966da45 100644
--- a/selfdrive/ui/translations/main_pt-BR.ts
+++ b/selfdrive/ui/translations/main_pt-BR.ts
@@ -281,6 +281,17 @@
câmera iniciando
+
+ ExperimentalModeButton
+
+ EXPERIMENTAL MODE ON
+
+
+
+ CHILL MODE ON
+
+
+
InputDialog
diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts
index fb9b6dd6f5..e77b3ef63d 100644
--- a/selfdrive/ui/translations/main_zh-CHS.ts
+++ b/selfdrive/ui/translations/main_zh-CHS.ts
@@ -281,6 +281,17 @@
正在启动相机
+
+ ExperimentalModeButton
+
+ EXPERIMENTAL MODE ON
+
+
+
+ CHILL MODE ON
+
+
+
InputDialog
diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts
index 86d5482355..f667fdc978 100644
--- a/selfdrive/ui/translations/main_zh-CHT.ts
+++ b/selfdrive/ui/translations/main_zh-CHT.ts
@@ -281,6 +281,17 @@
開啟相機中
+
+ ExperimentalModeButton
+
+ EXPERIMENTAL MODE ON
+
+
+
+ CHILL MODE ON
+
+
+
InputDialog