From 0e264e1b057b4b0bd02f05c57ac082fbb7588a73 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Fri, 3 Jan 2025 11:02:19 -0500 Subject: [PATCH 1/2] Revert "ui: sunnypilot offroad UI" (#511) Revert "ui: sunnypilot offroad UI (#500)" This reverts commit 3717a111afed884375f70ba1f288b9aadd6268ec. --- .codespellignore | 1 - SConstruct | 10 - scripts/lint/lint.sh | 2 +- selfdrive/ui/SConscript | 10 +- selfdrive/ui/main.cc | 8 +- selfdrive/ui/qt/body.h | 6 - selfdrive/ui/qt/home.h | 22 +- selfdrive/ui/qt/network/networking.cc | 6 - selfdrive/ui/qt/network/networking.h | 11 - selfdrive/ui/qt/offroad/developer_panel.cc | 7 +- selfdrive/ui/qt/offroad/developer_panel.h | 4 - selfdrive/ui/qt/offroad/onboarding.h | 7 +- selfdrive/ui/qt/offroad/settings.cc | 3 - selfdrive/ui/qt/offroad/settings.h | 30 +- selfdrive/ui/qt/onroad/buttons.h | 4 - selfdrive/ui/qt/onroad/model.h | 4 - selfdrive/ui/qt/onroad/onroad_home.cc | 4 - selfdrive/ui/qt/onroad/onroad_home.h | 12 +- selfdrive/ui/qt/request_repeater.h | 5 - selfdrive/ui/qt/sidebar.cc | 4 - selfdrive/ui/qt/sidebar.h | 8 +- selfdrive/ui/qt/util.h | 6 - selfdrive/ui/qt/widgets/cameraview.h | 5 - selfdrive/ui/qt/widgets/controls.cc | 3 - selfdrive/ui/qt/widgets/controls.h | 6 +- selfdrive/ui/qt/widgets/ssh_keys.h | 7 - selfdrive/ui/qt/widgets/toggle.h | 3 +- selfdrive/ui/qt/widgets/wifi.h | 5 - selfdrive/ui/qt/window.cc | 11 +- selfdrive/ui/qt/window.h | 13 +- selfdrive/ui/tests/test_ui/run.py | 4 +- selfdrive/ui/ui.cc | 12 +- selfdrive/ui/ui.h | 19 +- .../selfdrive/assets/offroad/icon_home.svg | 3 - .../assets/offroad/icon_software.png | 3 - .../selfdrive/assets/offroad/icon_toggle.png | 3 - sunnypilot/selfdrive/ui/.gitignore | 2 - sunnypilot/selfdrive/ui/SConscript | 21 - sunnypilot/selfdrive/ui/qt/home.cc | 19 - sunnypilot/selfdrive/ui/qt/home.h | 38 -- .../ui/qt/offroad/settings/onboarding.cc | 102 --- .../ui/qt/offroad/settings/onboarding.h | 35 -- .../ui/qt/offroad/settings/settings.cc | 146 ----- .../ui/qt/offroad/settings/settings.h | 39 -- .../selfdrive/ui/qt/onroad/onroad_home.cc | 32 - .../selfdrive/ui/qt/onroad/onroad_home.h | 24 - sunnypilot/selfdrive/ui/qt/sidebar.cc | 27 - sunnypilot/selfdrive/ui/qt/sidebar.h | 27 - .../selfdrive/ui/qt/widgets/controls.cc | 254 -------- sunnypilot/selfdrive/ui/qt/widgets/controls.h | 588 ------------------ .../selfdrive/ui/qt/widgets/drive_stats.cc | 103 --- .../selfdrive/ui/qt/widgets/drive_stats.h | 32 - .../selfdrive/ui/qt/widgets/scrollview.cc | 37 -- .../selfdrive/ui/qt/widgets/scrollview.h | 24 - sunnypilot/selfdrive/ui/qt/widgets/toggle.cc | 30 - sunnypilot/selfdrive/ui/qt/widgets/toggle.h | 20 - sunnypilot/selfdrive/ui/qt/window.cc | 20 - sunnypilot/selfdrive/ui/qt/window.h | 26 - sunnypilot/selfdrive/ui/ui.cc | 53 -- sunnypilot/selfdrive/ui/ui.h | 38 -- 60 files changed, 52 insertions(+), 1956 deletions(-) delete mode 100644 .codespellignore delete mode 100644 sunnypilot/selfdrive/assets/offroad/icon_home.svg delete mode 100644 sunnypilot/selfdrive/assets/offroad/icon_software.png delete mode 100644 sunnypilot/selfdrive/assets/offroad/icon_toggle.png delete mode 100644 sunnypilot/selfdrive/ui/.gitignore delete mode 100644 sunnypilot/selfdrive/ui/SConscript delete mode 100644 sunnypilot/selfdrive/ui/qt/home.cc delete mode 100644 sunnypilot/selfdrive/ui/qt/home.h delete mode 100644 sunnypilot/selfdrive/ui/qt/offroad/settings/onboarding.cc delete mode 100644 sunnypilot/selfdrive/ui/qt/offroad/settings/onboarding.h delete mode 100644 sunnypilot/selfdrive/ui/qt/offroad/settings/settings.cc delete mode 100644 sunnypilot/selfdrive/ui/qt/offroad/settings/settings.h delete mode 100644 sunnypilot/selfdrive/ui/qt/onroad/onroad_home.cc delete mode 100644 sunnypilot/selfdrive/ui/qt/onroad/onroad_home.h delete mode 100644 sunnypilot/selfdrive/ui/qt/sidebar.cc delete mode 100644 sunnypilot/selfdrive/ui/qt/sidebar.h delete mode 100644 sunnypilot/selfdrive/ui/qt/widgets/controls.cc delete mode 100644 sunnypilot/selfdrive/ui/qt/widgets/controls.h delete mode 100644 sunnypilot/selfdrive/ui/qt/widgets/drive_stats.cc delete mode 100644 sunnypilot/selfdrive/ui/qt/widgets/drive_stats.h delete mode 100644 sunnypilot/selfdrive/ui/qt/widgets/scrollview.cc delete mode 100644 sunnypilot/selfdrive/ui/qt/widgets/scrollview.h delete mode 100644 sunnypilot/selfdrive/ui/qt/widgets/toggle.cc delete mode 100644 sunnypilot/selfdrive/ui/qt/widgets/toggle.h delete mode 100644 sunnypilot/selfdrive/ui/qt/window.cc delete mode 100644 sunnypilot/selfdrive/ui/qt/window.h delete mode 100644 sunnypilot/selfdrive/ui/ui.cc delete mode 100644 sunnypilot/selfdrive/ui/ui.h diff --git a/.codespellignore b/.codespellignore deleted file mode 100644 index 916169347a..0000000000 --- a/.codespellignore +++ /dev/null @@ -1 +0,0 @@ -Wen diff --git a/SConstruct b/SConstruct index 39f7d14d03..934143fa08 100644 --- a/SConstruct +++ b/SConstruct @@ -74,12 +74,6 @@ AddOption('--minimal', default=os.path.exists(File('#.lfsconfig').abspath), # minimal by default on release branch (where there's no LFS) help='the minimum build to run openpilot. no tests, tools, etc.') -AddOption('--stock-ui', - action='store_true', - dest='stock_ui', - default=False, - help='Build stock openpilot UI instead of sunnypilot UI') - ## Architecture name breakdown (arch) ## - larch64: linux tici aarch64 ## - aarch64: linux pc aarch64 @@ -178,10 +172,6 @@ else: if arch != "Darwin": ldflags += ["-Wl,--as-needed", "-Wl,--no-undefined"] -if not GetOption('stock_ui'): - cflags += ["-DSUNNYPILOT"] - cxxflags += ["-DSUNNYPILOT"] - ccflags_option = GetOption('ccflags') if ccflags_option: ccflags += ccflags_option.split(' ') diff --git a/scripts/lint/lint.sh b/scripts/lint/lint.sh index 94ba59beed..0002a44726 100755 --- a/scripts/lint/lint.sh +++ b/scripts/lint/lint.sh @@ -57,7 +57,7 @@ function run_tests() { if [[ -z "$FAST" ]]; then run "mypy" mypy $PYTHON_FILES - run "codespell" codespell $ALL_FILES --ignore-words=$ROOT/.codespellignore + run "codespell" codespell $ALL_FILES fi return $FAILED diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index f3957c3d03..c851d7a969 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -12,12 +12,6 @@ if arch == "Darwin": del base_libs[base_libs.index('OpenCL')] qt_env['FRAMEWORKS'] += ['OpenCL'] -sp_widgets_src = [] -sp_qt_src = [] -if not GetOption('stock_ui'): - SConscript(['../../sunnypilot/selfdrive/ui/SConscript']) - Import('sp_widgets_src', 'sp_qt_src') - # FIXME: remove this once we're on 5.15 (24.04) qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"] @@ -26,7 +20,7 @@ widgets_src = ["qt/widgets/input.cc", "qt/widgets/wifi.cc", "qt/prime_state.cc", "qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc", "qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc", "qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc", - "qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"] + sp_widgets_src + "qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"] widgets = qt_env.Library("qt_widgets", widgets_src, LIBS=base_libs) Export('widgets') @@ -37,7 +31,7 @@ qt_src = ["main.cc", "ui.cc", "qt/sidebar.cc", "qt/body.cc", "qt/offroad/software_settings.cc", "qt/offroad/developer_panel.cc", "qt/offroad/onboarding.cc", "qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc", "qt/onroad/onroad_home.cc", "qt/onroad/annotated_camera.cc", "qt/onroad/model.cc", - "qt/onroad/buttons.cc", "qt/onroad/alerts.cc", "qt/onroad/driver_monitoring.cc", "qt/onroad/hud.cc"] + sp_qt_src + "qt/onroad/buttons.cc", "qt/onroad/alerts.cc", "qt/onroad/driver_monitoring.cc", "qt/onroad/hud.cc"] # build translation files with open(File("translations/languages.json").abspath) as f: diff --git a/selfdrive/ui/main.cc b/selfdrive/ui/main.cc index 268a3d000b..4903a3db3d 100644 --- a/selfdrive/ui/main.cc +++ b/selfdrive/ui/main.cc @@ -4,16 +4,10 @@ #include #include "system/hardware/hw.h" +#include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/window.h" -#ifdef SUNNYPILOT -#include "../../sunnypilot/selfdrive/ui/qt/window.h" -#define MainWindow MainWindowSP -#else -#include "selfdrive/ui/qt/qt_window.h" -#endif - int main(int argc, char *argv[]) { setpriority(PRIO_PROCESS, 0, -20); diff --git a/selfdrive/ui/qt/body.h b/selfdrive/ui/qt/body.h index b0ea20e552..567a54d49b 100644 --- a/selfdrive/ui/qt/body.h +++ b/selfdrive/ui/qt/body.h @@ -5,13 +5,7 @@ #include #include "common/util.h" - -#ifdef SUNNYPILOT -#include "../../sunnypilot/selfdrive/ui/ui.h" -#define UIState UIStateSP -#else #include "selfdrive/ui/ui.h" -#endif class RecordButton : public QPushButton { Q_OBJECT diff --git a/selfdrive/ui/qt/home.h b/selfdrive/ui/qt/home.h index 24ae756584..55bc706c0d 100644 --- a/selfdrive/ui/qt/home.h +++ b/selfdrive/ui/qt/home.h @@ -9,22 +9,11 @@ #include "common/params.h" #include "selfdrive/ui/qt/body.h" -#include "selfdrive/ui/qt/widgets/offroad_alerts.h" -#include "selfdrive/ui/ui.h" - -#ifdef SUNNYPILOT -#include "../../sunnypilot/selfdrive/ui/qt/widgets/controls.h" -#include "../../sunnypilot/selfdrive/ui/qt/onroad/onroad_home.h" -#include "../../sunnypilot/selfdrive/ui/qt/sidebar.h" -#define OnroadWindow OnroadWindowSP -#define LayoutWidget LayoutWidgetSP -#define Sidebar SidebarSP -#define ElidedLabel ElidedLabelSP -#else -#include "selfdrive/ui/qt/widgets/controls.h" #include "selfdrive/ui/qt/onroad/onroad_home.h" #include "selfdrive/ui/qt/sidebar.h" -#endif +#include "selfdrive/ui/qt/widgets/controls.h" +#include "selfdrive/ui/qt/widgets/offroad_alerts.h" +#include "selfdrive/ui/ui.h" class OffroadHome : public QFrame { Q_OBJECT @@ -69,12 +58,13 @@ protected: void mousePressEvent(QMouseEvent* e) override; void mouseDoubleClickEvent(QMouseEvent* e) override; +private: Sidebar *sidebar; OffroadHome *home; OnroadWindow *onroad; BodyWindow *body; QStackedLayout *slayout; -protected slots: - virtual void updateState(const UIState &s); +private slots: + void updateState(const UIState &s); }; diff --git a/selfdrive/ui/qt/network/networking.cc b/selfdrive/ui/qt/network/networking.cc index 0f101a35c3..066dc3ca7e 100644 --- a/selfdrive/ui/qt/network/networking.cc +++ b/selfdrive/ui/qt/network/networking.cc @@ -8,14 +8,8 @@ #include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/util.h" - -#ifdef SUNNYPILOT -#include "../../sunnypilot/selfdrive/ui/qt/widgets/controls.h" -#include "../../sunnypilot/selfdrive/ui/qt/widgets/scrollview.h" -#else #include "selfdrive/ui/qt/widgets/controls.h" #include "selfdrive/ui/qt/widgets/scrollview.h" -#endif static const int ICON_WIDTH = 49; diff --git a/selfdrive/ui/qt/network/networking.h b/selfdrive/ui/qt/network/networking.h index 93d7229e94..4fd604039b 100644 --- a/selfdrive/ui/qt/network/networking.h +++ b/selfdrive/ui/qt/network/networking.h @@ -8,17 +8,6 @@ #include "selfdrive/ui/qt/widgets/ssh_keys.h" #include "selfdrive/ui/qt/widgets/toggle.h" -#ifdef SUNNYPILOT -#include "../../sunnypilot/selfdrive/ui/qt/widgets/controls.h" -#define ButtonControl ButtonControlSP -#define ElidedLabel ElidedLabelSP -#define LabelControl LabelControlSP -#define ListWidget ListWidgetSP -#define ToggleControl ToggleControlSP -#else -#include "selfdrive/ui/qt/widgets/controls.h" -#endif - class WifiItem : public QWidget { Q_OBJECT public: diff --git a/selfdrive/ui/qt/offroad/developer_panel.cc b/selfdrive/ui/qt/offroad/developer_panel.cc index 190a18d46d..bbd69f72ce 100644 --- a/selfdrive/ui/qt/offroad/developer_panel.cc +++ b/selfdrive/ui/qt/offroad/developer_panel.cc @@ -2,13 +2,8 @@ #include "selfdrive/ui/qt/offroad/developer_panel.h" #include "selfdrive/ui/qt/widgets/ssh_keys.h" -#include "common/util.h" - -#ifdef SUNNYPILOT -#include "../../sunnypilot/selfdrive/ui/qt/widgets/controls.h" -#else #include "selfdrive/ui/qt/widgets/controls.h" -#endif +#include "common/util.h" DeveloperPanel::DeveloperPanel(SettingsWindow *parent) : ListWidget(parent) { // SSH keys diff --git a/selfdrive/ui/qt/offroad/developer_panel.h b/selfdrive/ui/qt/offroad/developer_panel.h index 824660458b..fe38612e57 100644 --- a/selfdrive/ui/qt/offroad/developer_panel.h +++ b/selfdrive/ui/qt/offroad/developer_panel.h @@ -1,10 +1,6 @@ #pragma once -#ifdef SUNNYPILOT -#include "../../sunnypilot/selfdrive/ui/qt/offroad/settings/settings.h" -#else #include "selfdrive/ui/qt/offroad/settings.h" -#endif class DeveloperPanel : public ListWidget { Q_OBJECT diff --git a/selfdrive/ui/qt/offroad/onboarding.h b/selfdrive/ui/qt/offroad/onboarding.h index 0f6dbb5063..a1b6895ba0 100644 --- a/selfdrive/ui/qt/offroad/onboarding.h +++ b/selfdrive/ui/qt/offroad/onboarding.h @@ -71,7 +71,6 @@ public slots: private: void showEvent(QShowEvent *event) override; -protected: QPushButton *accept_btn; signals: @@ -98,10 +97,10 @@ class OnboardingWindow : public QStackedWidget { public: explicit OnboardingWindow(QWidget *parent = 0); inline void showTrainingGuide() { setCurrentIndex(1); } - virtual inline bool completed() const { return accepted_terms && training_done; } + inline bool completed() const { return accepted_terms && training_done; } -protected: - virtual void updateActiveScreen(); +private: + void updateActiveScreen(); Params params; bool accepted_terms = false, training_done = false; diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index 93dcd793c9..d4aab53f5a 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -100,9 +100,7 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { } // Toggles with confirmation dialogs -#ifndef SUNNYPILOT toggles["ExperimentalMode"]->setActiveIcon("../assets/img_experimental.svg"); -#endif toggles["ExperimentalMode"]->setConfirmation(true, true); toggles["ExperimentalLongitudinalEnabled"]->setConfirmation(true, false); @@ -356,7 +354,6 @@ void SettingsWindow::setCurrentPanel(int index, const QString ¶m) { } SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { - RETURN_IF_SUNNYPILOT // setup two main layouts sidebar_widget = new QWidget; diff --git a/selfdrive/ui/qt/offroad/settings.h b/selfdrive/ui/qt/offroad/settings.h index 9c4acac582..de0528ee0a 100644 --- a/selfdrive/ui/qt/offroad/settings.h +++ b/selfdrive/ui/qt/offroad/settings.h @@ -10,21 +10,9 @@ #include #include -#include "selfdrive/ui/qt/util.h" - -#ifdef SUNNYPILOT -#include "../../sunnypilot/selfdrive/ui/ui.h" -#include "../../sunnypilot/selfdrive/ui/qt/widgets/controls.h" -#define ListWidget ListWidgetSP -#define ParamControl ParamControlSP -#define ButtonControl ButtonControlSP -#define ButtonParamControl ButtonParamControlSP -#define ToggleControl ToggleControlSP -#define LabelControl LabelControlSP -#else #include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/widgets/controls.h" -#endif // ********** settings window + top-level panels ********** class SettingsWindow : public QFrame { @@ -42,7 +30,7 @@ signals: void reviewTrainingGuide(); void expandToggleDescription(const QString ¶m); -protected: +private: QPushButton *sidebar_alert_widget; QWidget *sidebar_widget; QButtonGroup *nav_btns; @@ -57,12 +45,12 @@ public: signals: void reviewTrainingGuide(); -protected slots: +private slots: void poweroff(); void reboot(); void updateCalibDescription(); -protected: +private: Params params; ButtonControl *pair_device; }; @@ -76,15 +64,15 @@ public: public slots: void expandToggleDescription(const QString ¶m); -protected slots: - virtual void updateState(const UIState &s); +private slots: + void updateState(const UIState &s); -protected: +private: Params params; std::map toggles; ButtonParamControl *long_personality_setting; - virtual void updateToggles(); + void updateToggles(); }; class SoftwarePanel : public ListWidget { @@ -92,7 +80,7 @@ class SoftwarePanel : public ListWidget { public: explicit SoftwarePanel(QWidget* parent = nullptr); -protected: +private: void showEvent(QShowEvent *event) override; void updateLabels(); void checkForUpdates(); diff --git a/selfdrive/ui/qt/onroad/buttons.h b/selfdrive/ui/qt/onroad/buttons.h index 6bf479d136..9c91bc3c7b 100644 --- a/selfdrive/ui/qt/onroad/buttons.h +++ b/selfdrive/ui/qt/onroad/buttons.h @@ -2,11 +2,7 @@ #include -#ifdef SUNNYPILOT -#include "../../sunnypilot/selfdrive/ui/ui.h" -#else #include "selfdrive/ui/ui.h" -#endif const int btn_size = 192; const int img_size = (btn_size / 4) * 3; diff --git a/selfdrive/ui/qt/onroad/model.h b/selfdrive/ui/qt/onroad/model.h index d2303eebd6..79547e4b83 100644 --- a/selfdrive/ui/qt/onroad/model.h +++ b/selfdrive/ui/qt/onroad/model.h @@ -3,11 +3,7 @@ #include #include -#ifdef SUNNYPILOT -#include "../../sunnypilot/selfdrive/ui/ui.h" -#else #include "selfdrive/ui/ui.h" -#endif class ModelRenderer { public: diff --git a/selfdrive/ui/qt/onroad/onroad_home.cc b/selfdrive/ui/qt/onroad/onroad_home.cc index 7db9a05a0c..080f9bd50f 100644 --- a/selfdrive/ui/qt/onroad/onroad_home.cc +++ b/selfdrive/ui/qt/onroad/onroad_home.cc @@ -35,12 +35,8 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) { alerts->raise(); setAttribute(Qt::WA_OpaquePaintEvent); - - // We handle the connection of the signals on the derived class -#ifndef SUNNYPILOT QObject::connect(uiState(), &UIState::uiUpdate, this, &OnroadWindow::updateState); QObject::connect(uiState(), &UIState::offroadTransition, this, &OnroadWindow::offroadTransition); -#endif } void OnroadWindow::updateState(const UIState &s) { diff --git a/selfdrive/ui/qt/onroad/onroad_home.h b/selfdrive/ui/qt/onroad/onroad_home.h index 2224ede32e..c321d2d44f 100644 --- a/selfdrive/ui/qt/onroad/onroad_home.h +++ b/selfdrive/ui/qt/onroad/onroad_home.h @@ -3,24 +3,20 @@ #include "selfdrive/ui/qt/onroad/alerts.h" #include "selfdrive/ui/qt/onroad/annotated_camera.h" -#ifdef SUNNYPILOT -#define UIState UIStateSP -#endif - class OnroadWindow : public QWidget { Q_OBJECT public: OnroadWindow(QWidget* parent = 0); -protected: +private: void paintEvent(QPaintEvent *event); OnroadAlerts *alerts; AnnotatedCameraWidget *nvg; QColor bg = bg_colors[STATUS_DISENGAGED]; QHBoxLayout* split; -protected slots: - virtual void offroadTransition(bool offroad); - virtual void updateState(const UIState &s); +private slots: + void offroadTransition(bool offroad); + void updateState(const UIState &s); }; diff --git a/selfdrive/ui/qt/request_repeater.h b/selfdrive/ui/qt/request_repeater.h index 7c3c740b5e..c0e2758273 100644 --- a/selfdrive/ui/qt/request_repeater.h +++ b/selfdrive/ui/qt/request_repeater.h @@ -2,12 +2,7 @@ #include "common/util.h" #include "selfdrive/ui/qt/api.h" - -#ifdef SUNNYPILOT -#include "../../sunnypilot/selfdrive/ui/ui.h" -#else #include "selfdrive/ui/ui.h" -#endif class RequestRepeater : public HttpRequest { public: diff --git a/selfdrive/ui/qt/sidebar.cc b/selfdrive/ui/qt/sidebar.cc index 866a5b62d0..966396edc2 100644 --- a/selfdrive/ui/qt/sidebar.cc +++ b/selfdrive/ui/qt/sidebar.cc @@ -110,10 +110,6 @@ void Sidebar::updateState(const UIState &s) { void Sidebar::paintEvent(QPaintEvent *event) { QPainter p(this); - paintSidebar(p); -} - -void Sidebar::paintSidebar(QPainter &p) { p.setPen(Qt::NoPen); p.setRenderHint(QPainter::Antialiasing); diff --git a/selfdrive/ui/qt/sidebar.h b/selfdrive/ui/qt/sidebar.h index d23427336e..2091418e52 100644 --- a/selfdrive/ui/qt/sidebar.h +++ b/selfdrive/ui/qt/sidebar.h @@ -5,13 +5,8 @@ #include #include -#include "selfdrive/ui/qt/network/networking.h" - -#ifdef SUNNYPILOT -#include "../../sunnypilot/selfdrive/ui/ui.h" -#else #include "selfdrive/ui/ui.h" -#endif +#include "selfdrive/ui/qt/network/networking.h" typedef QPair, QColor> ItemStatus; Q_DECLARE_METATYPE(ItemStatus); @@ -40,7 +35,6 @@ protected: void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void drawMetric(QPainter &p, const QPair &label, QColor c, int y); - virtual void paintSidebar(QPainter &p); QPixmap home_img, flag_img, settings_img; bool onroad, flag_pressed, settings_pressed; diff --git a/selfdrive/ui/qt/util.h b/selfdrive/ui/qt/util.h index 6ea53d354a..2bf1a70a62 100644 --- a/selfdrive/ui/qt/util.h +++ b/selfdrive/ui/qt/util.h @@ -13,12 +13,6 @@ #include "cereal/gen/cpp/car.capnp.h" #include "common/params.h" -#ifdef SUNNYPILOT -#define RETURN_IF_SUNNYPILOT return; -#else -#define RETURN_IF_SUNNYPILOT // Do nothing -#endif - QString getVersion(); QString getBrand(); QString getUserAgent(); diff --git a/selfdrive/ui/qt/widgets/cameraview.h b/selfdrive/ui/qt/widgets/cameraview.h index 63b2bf8628..29aa8493c7 100644 --- a/selfdrive/ui/qt/widgets/cameraview.h +++ b/selfdrive/ui/qt/widgets/cameraview.h @@ -23,12 +23,7 @@ #endif #include "msgq/visionipc/visionipc_client.h" - -#ifdef SUNNYPILOT -#include "../../sunnypilot/selfdrive/ui/ui.h" -#else #include "selfdrive/ui/ui.h" -#endif const int FRAME_BUFFER_SIZE = 5; diff --git a/selfdrive/ui/qt/widgets/controls.cc b/selfdrive/ui/qt/widgets/controls.cc index 7dd3178200..40dda971f5 100644 --- a/selfdrive/ui/qt/widgets/controls.cc +++ b/selfdrive/ui/qt/widgets/controls.cc @@ -3,10 +3,7 @@ #include #include -#include "selfdrive/ui/qt/util.h" - AbstractControl::AbstractControl(const QString &title, const QString &desc, const QString &icon, QWidget *parent) : QFrame(parent) { - RETURN_IF_SUNNYPILOT QVBoxLayout *main_layout = new QVBoxLayout(this); main_layout->setMargin(0); diff --git a/selfdrive/ui/qt/widgets/controls.h b/selfdrive/ui/qt/widgets/controls.h index 9562c4ad22..aebf934f2a 100644 --- a/selfdrive/ui/qt/widgets/controls.h +++ b/selfdrive/ui/qt/widgets/controls.h @@ -40,7 +40,7 @@ class AbstractControl : public QFrame { Q_OBJECT public: - virtual void setDescription(const QString &desc) { + void setDescription(const QString &desc) { if (description) description->setText(desc); } @@ -52,7 +52,7 @@ public: value->setText(val); } - virtual const QString getDescription() { + const QString getDescription() { return description->text(); } @@ -60,7 +60,7 @@ public: QPixmap icon_pixmap; public slots: - virtual void showDescription() { + void showDescription() { description->setVisible(true); } diff --git a/selfdrive/ui/qt/widgets/ssh_keys.h b/selfdrive/ui/qt/widgets/ssh_keys.h index 53f0afac1b..920bd651e2 100644 --- a/selfdrive/ui/qt/widgets/ssh_keys.h +++ b/selfdrive/ui/qt/widgets/ssh_keys.h @@ -3,14 +3,7 @@ #include #include "system/hardware/hw.h" - -#ifdef SUNNYPILOT -#include "../../sunnypilot/selfdrive/ui/qt/widgets/controls.h" -#define ButtonControl ButtonControlSP -#define ToggleControl ToggleControlSP -#else #include "selfdrive/ui/qt/widgets/controls.h" -#endif // SSH enable toggle class SshToggle : public ToggleControl { diff --git a/selfdrive/ui/qt/widgets/toggle.h b/selfdrive/ui/qt/widgets/toggle.h index a0fa434a4c..e7263a008f 100644 --- a/selfdrive/ui/qt/widgets/toggle.h +++ b/selfdrive/ui/qt/widgets/toggle.h @@ -23,13 +23,14 @@ public: update(); } bool getEnabled(); - virtual void setEnabled(bool value); + void setEnabled(bool value); protected: void paintEvent(QPaintEvent*) override; void mouseReleaseEvent(QMouseEvent*) override; void enterEvent(QEvent*) override; +private: QColor circleColor; QColor green; bool enabled = true; diff --git a/selfdrive/ui/qt/widgets/wifi.h b/selfdrive/ui/qt/widgets/wifi.h index 40552a699d..60c865f2b8 100644 --- a/selfdrive/ui/qt/widgets/wifi.h +++ b/selfdrive/ui/qt/widgets/wifi.h @@ -4,12 +4,7 @@ #include #include -#ifdef SUNNYPILOT -#include "../../sunnypilot/selfdrive/ui/ui.h" -#define UIState UIStateSP -#else #include "selfdrive/ui/ui.h" -#endif class WiFiPromptWidget : public QFrame { Q_OBJECT diff --git a/selfdrive/ui/qt/window.cc b/selfdrive/ui/qt/window.cc index c28f3a2422..e1ec916c6f 100644 --- a/selfdrive/ui/qt/window.cc +++ b/selfdrive/ui/qt/window.cc @@ -4,20 +4,16 @@ #include "system/hardware/hw.h" -// We have this constructor so that we can provide custom implementations of the windows. By default (stock_ui) would receive them as nullptr, so they'll be instantiated with stock. Otherwise they'd be SP instances -MainWindow::MainWindow(QWidget *parent, HomeWindow *hw, SettingsWindow *sw, OnboardingWindow *ow) : - QWidget(parent), - homeWindow(hw ? hw : new HomeWindow(this)), - settingsWindow(sw ? sw : new SettingsWindow(this)), - onboardingWindow(ow ? ow : new OnboardingWindow(this)) { - +MainWindow::MainWindow(QWidget *parent) : QWidget(parent) { main_layout = new QStackedLayout(this); main_layout->setMargin(0); + homeWindow = new HomeWindow(this); main_layout->addWidget(homeWindow); QObject::connect(homeWindow, &HomeWindow::openSettings, this, &MainWindow::openSettings); QObject::connect(homeWindow, &HomeWindow::closeSettings, this, &MainWindow::closeSettings); + settingsWindow = new SettingsWindow(this); main_layout->addWidget(settingsWindow); QObject::connect(settingsWindow, &SettingsWindow::closeSettings, this, &MainWindow::closeSettings); QObject::connect(settingsWindow, &SettingsWindow::reviewTrainingGuide, [=]() { @@ -25,6 +21,7 @@ MainWindow::MainWindow(QWidget *parent, HomeWindow *hw, SettingsWindow *sw, Onbo main_layout->setCurrentWidget(onboardingWindow); }); + onboardingWindow = new OnboardingWindow(this); main_layout->addWidget(onboardingWindow); QObject::connect(onboardingWindow, &OnboardingWindow::onboardingDone, [=]() { main_layout->setCurrentWidget(homeWindow); diff --git a/selfdrive/ui/qt/window.h b/selfdrive/ui/qt/window.h index 2ed56e6d58..05b61e1f76 100644 --- a/selfdrive/ui/qt/window.h +++ b/selfdrive/ui/qt/window.h @@ -11,18 +11,15 @@ class MainWindow : public QWidget { Q_OBJECT public: - explicit MainWindow(QWidget *parent = 0) : MainWindow(parent, nullptr, nullptr, nullptr) {} - -protected: - explicit MainWindow(QWidget *parent, HomeWindow *hw = nullptr, SettingsWindow *sw = nullptr, OnboardingWindow *ow = nullptr); - HomeWindow *homeWindow; - SettingsWindow *settingsWindow; - OnboardingWindow *onboardingWindow; - virtual void closeSettings(); + explicit MainWindow(QWidget *parent = 0); private: bool eventFilter(QObject *obj, QEvent *event) override; void openSettings(int index = 0, const QString ¶m = ""); + void closeSettings(); QStackedLayout *main_layout; + HomeWindow *homeWindow; + SettingsWindow *settingsWindow; + OnboardingWindow *onboardingWindow; }; diff --git a/selfdrive/ui/tests/test_ui/run.py b/selfdrive/ui/tests/test_ui/run.py index 7ffd66ed9a..2c4d8d7037 100644 --- a/selfdrive/ui/tests/test_ui/run.py +++ b/selfdrive/ui/tests/test_ui/run.py @@ -160,7 +160,7 @@ def setup_offroad_alert(click, pm: PubMaster): # Toggle between settings and home to refresh the offroad alert widget setup_settings_device(click, pm) - click(100, 100) + click(240, 216) def setup_update_available(click, pm: PubMaster): Params().put_bool("UpdateAvailable", True) @@ -170,7 +170,7 @@ def setup_update_available(click, pm: PubMaster): Params().put("UpdaterNewReleaseNotes", release_notes + "\n") setup_settings_device(click, pm) - click(100, 100) + click(240, 216) def setup_pair_device(click, pm: PubMaster): click(1950, 435) diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 3179a383c2..ec3d40961d 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -9,17 +9,16 @@ #include "common/swaglog.h" #include "common/util.h" #include "common/watchdog.h" -#include "qt/util.h" #include "system/hardware/hw.h" #define BACKLIGHT_DT 0.05 #define BACKLIGHT_TS 10.00 -void update_sockets(UIState *s) { +static void update_sockets(UIState *s) { s->sm->update(0); } -void update_state(UIState *s) { +static void update_state(UIState *s) { SubMaster &sm = *(s->sm); UIScene &scene = s->scene; @@ -99,7 +98,6 @@ UIState::UIState(QObject *parent) : QObject(parent) { prime_state = new PrimeState(this); language = QString::fromStdString(Params().get("LanguageSetting")); - RETURN_IF_SUNNYPILOT // update timer timer = new QTimer(this); QObject::connect(timer, &QTimer::timeout, this, &UIState::update); @@ -107,7 +105,6 @@ UIState::UIState(QObject *parent) : QObject(parent) { } void UIState::update() { - RETURN_IF_SUNNYPILOT update_sockets(this); update_state(this); updateStatus(); @@ -121,9 +118,8 @@ void UIState::update() { Device::Device(QObject *parent) : brightness_filter(BACKLIGHT_OFFROAD, BACKLIGHT_TS, BACKLIGHT_DT), QObject(parent) { setAwake(true); resetInteractiveTimeout(); -#ifndef SUNNYPILOT + QObject::connect(uiState(), &UIState::uiUpdate, this, &Device::update); -#endif } void Device::update(const UIState &s) { @@ -189,7 +185,6 @@ void Device::updateWakefulness(const UIState &s) { setAwake(s.scene.ignition || interactive_timeout > 0); } -#ifndef SUNNYPILOT UIState *uiState() { static UIState ui_state; return &ui_state; @@ -199,4 +194,3 @@ Device *device() { static Device _device; return &_device; } -#endif diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index bf932c2ab5..6ff67cacde 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -67,7 +67,7 @@ class UIState : public QObject { public: UIState(QObject* parent = 0); - virtual void updateStatus(); + void updateStatus(); inline bool engaged() const { return scene.started && (*sm)["selfdriveState"].getSelfdriveState().getEnabled(); } @@ -82,19 +82,15 @@ signals: void uiUpdate(const UIState &s); void offroadTransition(bool offroad); -protected slots: - virtual void update(); - -protected: - QTimer *timer; +private slots: + void update(); private: + QTimer *timer; bool started_prev = false; }; -#ifndef SUNNYPILOT UIState *uiState(); -#endif // device management class class Device : public QObject { @@ -107,7 +103,7 @@ public: offroad_brightness = std::clamp(brightness, 0, 100); } -protected: +private: bool awake = false; int interactive_timeout = 0; bool ignition_on = false; @@ -130,10 +126,5 @@ public slots: void update(const UIState &s); }; -#ifndef SUNNYPILOT Device *device(); -#endif - void ui_update_params(UIState *s); -void update_state(UIState *s); -void update_sockets(UIState *s); diff --git a/sunnypilot/selfdrive/assets/offroad/icon_home.svg b/sunnypilot/selfdrive/assets/offroad/icon_home.svg deleted file mode 100644 index ca90cc7bf6..0000000000 --- a/sunnypilot/selfdrive/assets/offroad/icon_home.svg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1673f8d46251a05787b60346193852991739345506dc7e9b106dfb370d3611ed -size 489 diff --git a/sunnypilot/selfdrive/assets/offroad/icon_software.png b/sunnypilot/selfdrive/assets/offroad/icon_software.png deleted file mode 100644 index 70915e2906..0000000000 --- a/sunnypilot/selfdrive/assets/offroad/icon_software.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:01fd38c0d5b05c3ce4ed07bfe4cd47a3ea61f68f24e6cc3058190d90d8200f9b -size 4785 diff --git a/sunnypilot/selfdrive/assets/offroad/icon_toggle.png b/sunnypilot/selfdrive/assets/offroad/icon_toggle.png deleted file mode 100644 index 51906798b9..0000000000 --- a/sunnypilot/selfdrive/assets/offroad/icon_toggle.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:11336d2f65100bc0dc2a53e09efae41ae85817b27e1a5bf134cc995e631c5b52 -size 4073 diff --git a/sunnypilot/selfdrive/ui/.gitignore b/sunnypilot/selfdrive/ui/.gitignore deleted file mode 100644 index e047b1f5dd..0000000000 --- a/sunnypilot/selfdrive/ui/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -moc_* -*.moc diff --git a/sunnypilot/selfdrive/ui/SConscript b/sunnypilot/selfdrive/ui/SConscript deleted file mode 100644 index 421744f257..0000000000 --- a/sunnypilot/selfdrive/ui/SConscript +++ /dev/null @@ -1,21 +0,0 @@ -widgets_src = [ - "../../sunnypilot/selfdrive/ui/ui.cc", - "../../sunnypilot/selfdrive/ui/qt/window.cc", - "../../sunnypilot/selfdrive/ui/qt/widgets/controls.cc", - "../../sunnypilot/selfdrive/ui/qt/widgets/drive_stats.cc", - "../../sunnypilot/selfdrive/ui/qt/widgets/scrollview.cc", - "../../sunnypilot/selfdrive/ui/qt/widgets/toggle.cc", -] - -qt_src = [ - "../../sunnypilot/selfdrive/ui/qt/home.cc", - "../../sunnypilot/selfdrive/ui/qt/sidebar.cc", - "../../sunnypilot/selfdrive/ui/qt/offroad/settings/onboarding.cc", - "../../sunnypilot/selfdrive/ui/qt/offroad/settings/settings.cc", - "../../sunnypilot/selfdrive/ui/qt/onroad/onroad_home.cc", -] - -sp_widgets_src = widgets_src -sp_qt_src = qt_src - -Export('sp_widgets_src', 'sp_qt_src') diff --git a/sunnypilot/selfdrive/ui/qt/home.cc b/sunnypilot/selfdrive/ui/qt/home.cc deleted file mode 100644 index 8cdd178a46..0000000000 --- a/sunnypilot/selfdrive/ui/qt/home.cc +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#include "../../sunnypilot/selfdrive/ui/qt/home.h" - -HomeWindowSP::HomeWindowSP(QWidget *parent) : HomeWindow(parent) { -} - -void HomeWindowSP::updateState(const UIState &s) { - HomeWindow::updateState(s); -} - -void HomeWindowSP::mousePressEvent(QMouseEvent *e) { - HomeWindow::mousePressEvent(e); -} diff --git a/sunnypilot/selfdrive/ui/qt/home.h b/sunnypilot/selfdrive/ui/qt/home.h deleted file mode 100644 index b4129126d3..0000000000 --- a/sunnypilot/selfdrive/ui/qt/home.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#pragma once - -#include -#include - -#include "common/params.h" -#include "selfdrive/ui/qt/body.h" -#include "selfdrive/ui/qt/widgets/offroad_alerts.h" -#include "../../sunnypilot/selfdrive/ui/ui.h" -#include "selfdrive/ui/qt/home.h" - -#ifdef SUNNYPILOT -#include "../../sunnypilot/selfdrive/ui/qt/sidebar.h" -#define OnroadWindow OnroadWindowSP -#else -#include "selfdrive/ui/qt/sidebar.h" -#include "selfdrive/ui/qt/onroad/onroad_home.h" -#endif - -class HomeWindowSP : public HomeWindow { - Q_OBJECT - -public: - explicit HomeWindowSP(QWidget *parent = 0); - -protected: - void mousePressEvent(QMouseEvent *e) override; - -private slots: - void updateState(const UIState &s) override; -}; diff --git a/sunnypilot/selfdrive/ui/qt/offroad/settings/onboarding.cc b/sunnypilot/selfdrive/ui/qt/offroad/settings/onboarding.cc deleted file mode 100644 index 2615743f27..0000000000 --- a/sunnypilot/selfdrive/ui/qt/offroad/settings/onboarding.cc +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#include "../../sunnypilot/selfdrive/ui/qt/offroad/settings/onboarding.h" - -#include - -#include -#include - -#include "common/util.h" -#include "common/params.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/widgets/input.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" - -void TermsPageSP::showEvent(QShowEvent *event) { - // late init, building QML widget takes 200ms - if (layout()) { - return; - } - - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(45, 35, 45, 45); - main_layout->setSpacing(0); - - QLabel *title = new QLabel(tr("Terms & Conditions")); - title->setStyleSheet("font-size: 90px; font-weight: 600;"); - main_layout->addWidget(title); - - QLabel *text = new QLabel(this); - text->setTextFormat(Qt::RichText); - text->setWordWrap(true); - std::string tc_text = sunnypilot_tc ? "../assets/offroad/sp_tc.html" : "../assets/offroad/tc.html"; - text->setText(QString::fromStdString(util::read_file(tc_text))); - text->setStyleSheet("font-size:50px; font-weight: 200; color: #C9C9C9; background-color:#1B1B1B; padding:50px 50px;"); - ScrollView *scroll = new ScrollView(text, this); - - main_layout->addSpacing(30); - main_layout->addWidget(scroll); - main_layout->addSpacing(50); - - QHBoxLayout *buttons = new QHBoxLayout; - buttons->setMargin(0); - buttons->setSpacing(45); - main_layout->addLayout(buttons); - - QPushButton *decline_btn = new QPushButton(tr("Decline")); - buttons->addWidget(decline_btn); - QObject::connect(decline_btn, &QPushButton::clicked, this, &TermsPage::declinedTerms); - - accept_btn = new QPushButton(tr("Scroll to accept")); - accept_btn->setEnabled(false); - accept_btn->setStyleSheet(R"( - QPushButton { - background-color: #465BEA; - } - QPushButton:pressed { - background-color: #3049F4; - } - QPushButton:disabled { - background-color: #4F4F4F; - } - )"); - buttons->addWidget(accept_btn); - QObject::connect(accept_btn, &QPushButton::clicked, this, &TermsPage::acceptedTerms); - QScrollBar *scroll_bar = scroll->verticalScrollBar(); - connect(scroll_bar, &QScrollBar::valueChanged, this, [this, scroll_bar](int value) { - if (value == scroll_bar->maximum()) { - enableAccept(); - } - }); -} - -void OnboardingWindowSP::updateActiveScreen() { - if (accepted_terms && training_done && !accepted_terms_sp) { - setCurrentIndex(3); - } else { - OnboardingWindow::updateActiveScreen(); - } -} - -OnboardingWindowSP::OnboardingWindowSP(QWidget *parent) : OnboardingWindow(parent) { - std::string current_terms_version_sp = params.get("TermsVersionSunnypilot"); - accepted_terms_sp = params.get("HasAcceptedTermsSP") == current_terms_version_sp; - LOGD("accepted_terms_sp: %s", params.get("HasAcceptedTermsSP").c_str()); - - auto *terms_sp = new TermsPageSP(true, parent); - addWidget(terms_sp); // index = 3 - connect(terms_sp, &TermsPageSP::acceptedTerms, [=]() { - params.put("HasAcceptedTermsSP", current_terms_version_sp); - accepted_terms_sp = true; - updateActiveScreen(); - }); - connect(terms_sp, &TermsPageSP::declinedTerms, [=]() { setCurrentIndex(2); }); - - OnboardingWindowSP::updateActiveScreen(); -} diff --git a/sunnypilot/selfdrive/ui/qt/offroad/settings/onboarding.h b/sunnypilot/selfdrive/ui/qt/offroad/settings/onboarding.h deleted file mode 100644 index 626428478c..0000000000 --- a/sunnypilot/selfdrive/ui/qt/offroad/settings/onboarding.h +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#pragma once - -#include "selfdrive/ui/qt/offroad/onboarding.h" - -#include "selfdrive/ui/qt/qt_window.h" - -class TermsPageSP : public TermsPage { - Q_OBJECT - -public: - explicit TermsPageSP(bool sunnypilot = false, QWidget *parent = 0) : TermsPage(parent), sunnypilot_tc(sunnypilot) {} - -private: - bool sunnypilot_tc = false; - void showEvent(QShowEvent *event) override; -}; - -class OnboardingWindowSP : public OnboardingWindow { - Q_OBJECT - -public: - explicit OnboardingWindowSP(QWidget *parent = 0); - inline bool completed() const override { return accepted_terms && accepted_terms_sp && training_done; } - -private: - bool accepted_terms_sp = false; - void updateActiveScreen() override; -}; diff --git a/sunnypilot/selfdrive/ui/qt/offroad/settings/settings.cc b/sunnypilot/selfdrive/ui/qt/offroad/settings/settings.cc deleted file mode 100644 index cf446c6c3f..0000000000 --- a/sunnypilot/selfdrive/ui/qt/offroad/settings/settings.cc +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#include "../../sunnypilot/selfdrive/ui/qt/offroad/settings/settings.h" - -#include "selfdrive/ui/qt/network/networking.h" -#include "../../sunnypilot/selfdrive/ui/qt/widgets/scrollview.h" -#include "selfdrive/ui/qt/offroad/developer_panel.h" - -TogglesPanelSP::TogglesPanelSP(SettingsWindow *parent) : TogglesPanel(parent) { - QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &TogglesPanelSP::updateState); -} - -void TogglesPanelSP::updateState(const UIStateSP &s) { - TogglesPanel::updateState(s); -} - -SettingsWindowSP::SettingsWindowSP(QWidget *parent) : SettingsWindow(parent) { - // setup two main layouts - sidebar_widget = new QWidget; - QVBoxLayout *sidebar_layout = new QVBoxLayout(sidebar_widget); - panel_widget = new QStackedWidget(); - - // setup layout for close button - QVBoxLayout *close_btn_layout = new QVBoxLayout; - close_btn_layout->setContentsMargins(0, 0, 0, 20); - - // close button - QPushButton *close_btn = new QPushButton(tr("×")); - close_btn->setStyleSheet(R"( - QPushButton { - font-size: 140px; - padding-bottom: 20px; - border-radius: 76px; - background-color: #292929; - font-weight: 400; - } - QPushButton:pressed { - background-color: #3B3B3B; - } - )"); - close_btn->setFixedSize(152, 152); - close_btn_layout->addWidget(close_btn, 0, Qt::AlignLeft); - QObject::connect(close_btn, &QPushButton::clicked, this, &SettingsWindow::closeSettings); - - // setup buttons widget - QWidget *buttons_widget = new QWidget; - QVBoxLayout *buttons_layout = new QVBoxLayout(buttons_widget); - buttons_layout->setMargin(0); - buttons_layout->addSpacing(10); - - // setup panels - DevicePanel *device = new DevicePanel(this); - QObject::connect(device, &DevicePanel::reviewTrainingGuide, this, &SettingsWindow::reviewTrainingGuide); - - TogglesPanelSP *toggles = new TogglesPanelSP(this); - QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription); - - auto networking = new Networking(this); - QObject::connect(uiState()->prime_state, &PrimeState::changed, networking, &Networking::setPrimeType); - - QList panels = { - PanelInfo(" " + tr("Device"), device, "../../sunnypilot/selfdrive/assets/offroad/icon_home.svg"), - PanelInfo(" " + tr("Network"), networking, "../assets/offroad/icon_network.png"), - PanelInfo(" " + tr("Toggles"), toggles, "../../sunnypilot/selfdrive/assets/offroad/icon_toggle.png"), - PanelInfo(" " + tr("Software"), new SoftwarePanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_software.png"), - PanelInfo(" " + tr("Developer"), new DeveloperPanel(this), "../assets/offroad/icon_shell.png"), - }; - - nav_btns = new QButtonGroup(this); - for (auto &[name, panel, icon] : panels) { - QPushButton *btn = new QPushButton(name); - btn->setCheckable(true); - btn->setChecked(nav_btns->buttons().size() == 0); - btn->setIcon(QIcon(QPixmap(icon))); - btn->setIconSize(QSize(70, 70)); - btn->setStyleSheet(R"( - QPushButton { - border-radius: 20px; - width: 400px; - height: 98px; - color: #bdbdbd; - border: none; - background: none; - font-size: 50px; - font-weight: 500; - text-align: left; - padding-left: 22px; - } - QPushButton:checked { - background-color: #696868; - color: white; - } - QPushButton:pressed { - color: #ADADAD; - } - )"); - btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); - nav_btns->addButton(btn); - buttons_layout->addWidget(btn, 0, Qt::AlignLeft | Qt::AlignBottom); - - const int lr_margin = (name != (" " + tr("Network")) || (name != (" " + tr("sunnypilot")))) ? 50 : 0; // Network and sunnypilot panel handles its own margins - panel->setContentsMargins(lr_margin, 25, lr_margin, 25); - - ScrollViewSP *panel_frame = new ScrollViewSP(panel, this); - panel_widget->addWidget(panel_frame); - - QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() { - btn->setChecked(true); - panel_widget->setCurrentWidget(w); - }); - } - sidebar_layout->setContentsMargins(50, 50, 25, 50); - - // main settings layout, sidebar + main panel - QHBoxLayout *main_layout = new QHBoxLayout(this); - - // add layout for close button - sidebar_layout->addLayout(close_btn_layout); - - // add layout for buttons scrolling - ScrollViewSP *buttons_scrollview = new ScrollViewSP(buttons_widget, this); - sidebar_layout->addWidget(buttons_scrollview); - - sidebar_widget->setFixedWidth(500); - main_layout->addWidget(sidebar_widget); - main_layout->addWidget(panel_widget); - - setStyleSheet(R"( - * { - color: white; - font-size: 50px; - } - SettingsWindow { - background-color: black; - } - QStackedWidget, ScrollViewSP { - background-color: black; - border-radius: 30px; - } - )"); -} diff --git a/sunnypilot/selfdrive/ui/qt/offroad/settings/settings.h b/sunnypilot/selfdrive/ui/qt/offroad/settings/settings.h deleted file mode 100644 index 5678a3171b..0000000000 --- a/sunnypilot/selfdrive/ui/qt/offroad/settings/settings.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#pragma once - -#include -#include - -#include "selfdrive/ui/qt/offroad/settings.h" - -class SettingsWindowSP : public SettingsWindow { - Q_OBJECT - -public: - explicit SettingsWindowSP(QWidget *parent = nullptr); - -protected: - struct PanelInfo { - QString name; - QWidget *widget; - QString icon; - - PanelInfo(const QString &name, QWidget *widget, const QString &icon) : name(name), widget(widget), icon(icon) {} - }; -}; - -class TogglesPanelSP : public TogglesPanel { - Q_OBJECT - -public: - explicit TogglesPanelSP(SettingsWindow *parent); - -private slots: - void updateState(const UIStateSP &s); -}; diff --git a/sunnypilot/selfdrive/ui/qt/onroad/onroad_home.cc b/sunnypilot/selfdrive/ui/qt/onroad/onroad_home.cc deleted file mode 100644 index a2743df34c..0000000000 --- a/sunnypilot/selfdrive/ui/qt/onroad/onroad_home.cc +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#include "../../sunnypilot/selfdrive/ui/qt/onroad/onroad_home.h" - -#include "common/swaglog.h" -#include "selfdrive/ui/qt/util.h" - -OnroadWindowSP::OnroadWindowSP(QWidget *parent) : OnroadWindow(parent) { - QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &OnroadWindowSP::updateState); - QObject::connect(uiStateSP(), &UIStateSP::offroadTransition, this, &OnroadWindowSP::offroadTransition); -} - -void OnroadWindowSP::updateState(const UIStateSP &s) { - if (!s.scene.started) { - return; - } - - OnroadWindow::updateState(s); -} - -void OnroadWindowSP::mousePressEvent(QMouseEvent *e) { - OnroadWindow::mousePressEvent(e); -} - -void OnroadWindowSP::offroadTransition(bool offroad) { - OnroadWindow::offroadTransition(offroad); -} diff --git a/sunnypilot/selfdrive/ui/qt/onroad/onroad_home.h b/sunnypilot/selfdrive/ui/qt/onroad/onroad_home.h deleted file mode 100644 index 193fdae0dc..0000000000 --- a/sunnypilot/selfdrive/ui/qt/onroad/onroad_home.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#pragma once - -#include "selfdrive/ui/qt/onroad/onroad_home.h" - -class OnroadWindowSP : public OnroadWindow { - Q_OBJECT - -public: - OnroadWindowSP(QWidget *parent = 0); - -private: - void mousePressEvent(QMouseEvent *e) override; - -protected slots: - void offroadTransition(bool offroad) override; - void updateState(const UIStateSP &s) override; -}; diff --git a/sunnypilot/selfdrive/ui/qt/sidebar.cc b/sunnypilot/selfdrive/ui/qt/sidebar.cc deleted file mode 100644 index fb177856ca..0000000000 --- a/sunnypilot/selfdrive/ui/qt/sidebar.cc +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#include "../../sunnypilot/selfdrive/ui/qt/sidebar.h" - -#include -#include - -#include "selfdrive/ui/qt/util.h" - -SidebarSP::SidebarSP(QWidget *parent) : Sidebar(parent) { - QObject::disconnect(uiState(), &UIState::uiUpdate, this, &Sidebar::updateState); - QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &SidebarSP::updateState); -} - -void SidebarSP::updateState(const UIStateSP &s) { - if (!isVisible()) return; - Sidebar::updateState(s); -} - -void SidebarSP::paintSidebar(QPainter &p) { - Sidebar::paintSidebar(p); -} diff --git a/sunnypilot/selfdrive/ui/qt/sidebar.h b/sunnypilot/selfdrive/ui/qt/sidebar.h deleted file mode 100644 index 5a9a66df50..0000000000 --- a/sunnypilot/selfdrive/ui/qt/sidebar.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#pragma once - -#include - -#include "selfdrive/ui/qt/sidebar.h" - -#include "../../sunnypilot/selfdrive/ui/ui.h" - -class SidebarSP : public Sidebar { - Q_OBJECT - -public slots: - void updateState(const UIStateSP &s); - -public: - explicit SidebarSP(QWidget *parent = 0); - -private: - void paintSidebar(QPainter &p) override; -}; diff --git a/sunnypilot/selfdrive/ui/qt/widgets/controls.cc b/sunnypilot/selfdrive/ui/qt/widgets/controls.cc deleted file mode 100644 index a72fe34758..0000000000 --- a/sunnypilot/selfdrive/ui/qt/widgets/controls.cc +++ /dev/null @@ -1,254 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#include "../../sunnypilot/selfdrive/ui/qt/widgets/controls.h" - -#include -#include - -QFrame *horizontal_line(QWidget *parent) { - QFrame *line = new QFrame(parent); - line->setFrameShape(QFrame::StyledPanel); - line->setStyleSheet(R"( - border-width: 2px; - border-bottom-style: solid; - border-color: gray; - )"); - line->setFixedHeight(10); - return line; -} - -// AbstractControlSP - -AbstractControlSP::AbstractControlSP(const QString &title, const QString &desc, const QString &icon, QWidget *parent) - : AbstractControl(title, desc, icon, parent) { - - main_layout = new QVBoxLayout(this); - main_layout->setMargin(0); - - hlayout = new QHBoxLayout; - hlayout->setMargin(0); - hlayout->setSpacing(20); - - // title - title_label = new QPushButton(title); - title_label->setFixedHeight(120); - title_label->setStyleSheet("font-size: 50px; font-weight: 450; text-align: left; border: none;"); - hlayout->addWidget(title_label, 1); - - // value next to control button - value = new ElidedLabelSP(); - value->setAlignment(Qt::AlignRight | Qt::AlignVCenter); - value->setStyleSheet("color: #aaaaaa"); - hlayout->addWidget(value); - - main_layout->addLayout(hlayout); - - // description - description = new QLabel(desc); - description->setContentsMargins(40, 20, 40, 20); - description->setStyleSheet("font-size: 40px; color: grey"); - description->setWordWrap(true); - description->setVisible(false); - main_layout->addWidget(description); - - connect(title_label, &QPushButton::clicked, [=]() { - if (!description->isVisible()) { - emit showDescriptionEvent(); - } - - if (!description->text().isEmpty()) { - description->setVisible(!description->isVisible()); - } - }); - - main_layout->addStretch(); -} - -void AbstractControlSP::hideEvent(QHideEvent *e) { - if (description != nullptr) { - description->hide(); - } -} - -AbstractControlSP_SELECTOR::AbstractControlSP_SELECTOR(const QString &title, const QString &desc, const QString &icon, QWidget *parent) - : AbstractControlSP(title, desc, icon, parent) { - - if (title_label != nullptr) { - delete title_label; - title_label = nullptr; - } - - if (description != nullptr) { - delete description; - description = nullptr; - } - - if (value != nullptr) { - ReplaceWidget(value, new QWidget()); - value = nullptr; - } - - QLayoutItem *item; - while ((item = main_layout->takeAt(0)) != nullptr) { - if (item->widget()) { - delete item->widget(); - } - delete item; - } - - main_layout->setMargin(0); - - hlayout = new QHBoxLayout; - hlayout->setMargin(0); - hlayout->setSpacing(0); - - // title - if (!title.isEmpty()) { - title_label = new QPushButton(title); - title_label->setFixedHeight(120); - title_label->setStyleSheet("font-size: 50px; font-weight: 450; text-align: left; border: none; padding: 20 0 0 0"); - main_layout->addWidget(title_label, 1); - - connect(title_label, &QPushButton::clicked, [=]() { - if (!description->isVisible()) { - emit showDescriptionEvent(); - } - - if (!description->text().isEmpty()) { - bool isVisible = !description->isVisible(); - description->setVisible(isVisible); - - if (isVisible && spacingItem) { - main_layout->removeItem(spacingItem); - delete spacingItem; - spacingItem = nullptr; - } else if (!isVisible && spacingItem == nullptr) { - spacingItem = new QSpacerItem(44, 44, QSizePolicy::Minimum, QSizePolicy::Fixed); - main_layout->insertItem(main_layout->indexOf(description), spacingItem); - } - } - }); - } else { - main_layout->addSpacing(20); - } - - main_layout->addLayout(hlayout); - if (!desc.isEmpty() && spacingItem == nullptr) { - spacingItem = new QSpacerItem(44, 44, QSizePolicy::Minimum, QSizePolicy::Fixed); - main_layout->insertItem(main_layout->count(), spacingItem); - } - - // description - description = new QLabel(desc); - description->setContentsMargins(0, 20, 40, 20); - description->setStyleSheet("font-size: 40px; color: grey"); - description->setWordWrap(true); - description->setVisible(false); - main_layout->addWidget(description); - - main_layout->addStretch(); -} - -void AbstractControlSP_SELECTOR::hideEvent(QHideEvent *e) { - if (description != nullptr) { - description->hide(); - } - - if (spacingItem == nullptr) { - spacingItem = new QSpacerItem(44, 44, QSizePolicy::Minimum, QSizePolicy::Fixed); - main_layout->insertItem(main_layout->indexOf(description), spacingItem); - } -} - -// controls - -ButtonControlSP::ButtonControlSP(const QString &title, const QString &text, const QString &desc, QWidget *parent) - : AbstractControlSP(title, desc, "", parent) { - - btn.setText(text); - btn.setStyleSheet(R"( - QPushButton { - padding: 0; - border-radius: 50px; - font-size: 35px; - font-weight: 500; - color: #E4E4E4; - background-color: #393939; - } - QPushButton:pressed { - background-color: #4a4a4a; - } - QPushButton:disabled { - color: #33E4E4E4; - } - )"); - btn.setFixedSize(250, 100); - QObject::connect(&btn, &QPushButton::clicked, this, &ButtonControlSP::clicked); - hlayout->addWidget(&btn); -} - -// ElidedLabelSP - -ElidedLabelSP::ElidedLabelSP(QWidget *parent) : ElidedLabelSP({}, parent) { -} - -ElidedLabelSP::ElidedLabelSP(const QString &text, QWidget *parent) : QLabel(text.trimmed(), parent) { - setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - setMinimumWidth(1); -} - -void ElidedLabelSP::resizeEvent(QResizeEvent *event) { - QLabel::resizeEvent(event); - lastText_ = elidedText_ = ""; -} - -void ElidedLabelSP::paintEvent(QPaintEvent *event) { - const QString curText = text(); - if (curText != lastText_) { - elidedText_ = fontMetrics().elidedText(curText, Qt::ElideRight, contentsRect().width()); - lastText_ = curText; - } - - QPainter painter(this); - drawFrame(&painter); - QStyleOption opt; - opt.initFrom(this); - style()->drawItemText(&painter, contentsRect(), alignment(), opt.palette, isEnabled(), elidedText_, foregroundRole()); -} - -// ParamControlSP - -ParamControlSP::ParamControlSP(const QString ¶m, const QString &title, const QString &desc, const QString &icon, QWidget *parent) - : ToggleControlSP(title, desc, icon, false, parent) { - - key = param.toStdString(); - QObject::connect(this, &ParamControlSP::toggleFlipped, this, &ParamControlSP::toggleClicked); - - hlayout->removeWidget(&toggle); - hlayout->insertWidget(0, &toggle); - - hlayout->removeWidget(this->icon_label); - hlayout->insertWidget(1, this->icon_label); -} - -void ParamControlSP::toggleClicked(bool state) { - auto do_confirm = [this]() { - QString content("

" + title_label->text() + "


" - "

" + getDescription() + "

"); - return ConfirmationDialog(content, tr("Enable"), tr("Cancel"), true, this).exec(); - }; - - bool confirmed = store_confirm && params.getBool(key + "Confirmed"); - if (!confirm || confirmed || !state || do_confirm()) { - if (store_confirm && state) params.putBool(key + "Confirmed", true); - params.putBool(key, state); - setIcon(state); - } else { - toggle.togglePosition(); - } -} diff --git a/sunnypilot/selfdrive/ui/qt/widgets/controls.h b/sunnypilot/selfdrive/ui/qt/widgets/controls.h deleted file mode 100644 index ebfc640cb7..0000000000 --- a/sunnypilot/selfdrive/ui/qt/widgets/controls.h +++ /dev/null @@ -1,588 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#pragma once - -#include -#include -#include -#include - -#include "common/params.h" -#include "selfdrive/ui/qt/widgets/controls.h" -#include "selfdrive/ui/qt/widgets/input.h" -#include "../../sunnypilot/selfdrive/ui/qt/widgets/toggle.h" - -QFrame *horizontal_line(QWidget *parent = nullptr); - -inline void ReplaceWidget(QWidget *old_widget, QWidget *new_widget) { - if (old_widget && old_widget->parentWidget() && old_widget->parentWidget()->layout()) { - old_widget->parentWidget()->layout()->replaceWidget(old_widget, new_widget); - old_widget->hide(); - old_widget->deleteLater(); - } -} - -class ElidedLabelSP : public QLabel { - Q_OBJECT - -public: - explicit ElidedLabelSP(QWidget *parent = 0); - explicit ElidedLabelSP(const QString &text, QWidget *parent = 0); - - void setColor(const QString &color) { - setStyleSheet("QLabel { color : " + color + "; }"); - } - -signals: - void clicked(); - -protected: - void paintEvent(QPaintEvent *event) override; - void resizeEvent(QResizeEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override { - if (rect().contains(event->pos())) { - emit clicked(); - } - } - QString lastText_, elidedText_; -}; - -class AbstractControlSP : public AbstractControl { - Q_OBJECT - -public: - void setDescription(const QString &desc) override { - if (description) description->setText(desc); - } - - void setValue(const QString &val, std::optional color = std::nullopt) { - value->setText(val); - if (color.has_value()) { - value->setColor(color.value()); - } - } - - const QString getDescription() override { - return description->text(); - } - - void hideDescription() { - description->setVisible(false); - } - -public slots: - void showDescription() override { - description->setVisible(true); - } - -protected: - AbstractControlSP(const QString &title, const QString &desc = "", const QString &icon = "", QWidget *parent = nullptr); - void hideEvent(QHideEvent *e) override; - - QVBoxLayout *main_layout; - ElidedLabelSP *value; - QLabel *description = nullptr; -}; - -// AbstractControlSP_SELECTOR - -class AbstractControlSP_SELECTOR : public AbstractControlSP { - Q_OBJECT - -protected: - AbstractControlSP_SELECTOR(const QString &title, const QString &desc = "", const QString &icon = "", QWidget *parent = nullptr); - void hideEvent(QHideEvent *e) override; - -private: - QSpacerItem *spacingItem = nullptr; -}; - -// widget to display a value -class LabelControlSP : public AbstractControlSP { - Q_OBJECT - -public: - LabelControlSP(const QString &title, const QString &text = "", const QString &desc = "", QWidget *parent = nullptr) : AbstractControlSP(title, desc, "", parent) { - label.setText(text); - label.setAlignment(Qt::AlignRight | Qt::AlignVCenter); - hlayout->addWidget(&label); - } - void setText(const QString &text) { label.setText(text); } - -private: - ElidedLabelSP label; -}; - -// widget for a button with a label -class ButtonControlSP : public AbstractControlSP { - Q_OBJECT - -public: - ButtonControlSP(const QString &title, const QString &text, const QString &desc = "", QWidget *parent = nullptr); - inline void setText(const QString &text) { btn.setText(text); } - inline QString text() const { return btn.text(); } - inline void click() { btn.click(); } - -signals: - void clicked(); - -public slots: - void setEnabled(bool enabled) { btn.setEnabled(enabled); } - -private: - QPushButton btn; -}; - -class ToggleControlSP : public AbstractControlSP { - Q_OBJECT - -public: - ToggleControlSP(const QString &title, const QString &desc = "", const QString &icon = "", const bool state = false, QWidget *parent = nullptr) : AbstractControlSP(title, desc, icon, parent) { - // space between toggle and title - icon_label = new QLabel(this); - hlayout->addWidget(icon_label); - - toggle.setFixedSize(150, 100); - if (state) { - toggle.togglePosition(); - } - hlayout->insertWidget(0, &toggle); - hlayout->insertWidget(1, this->icon_label); - QObject::connect(&toggle, &ToggleSP::stateChanged, this, &ToggleControlSP::toggleFlipped); - } - - void setEnabled(bool enabled) { - toggle.setEnabled(enabled); - toggle.update(); - } - -signals: - void toggleFlipped(bool state); - -protected: - ToggleSP toggle; -}; - -// widget to toggle params -class ParamControlSP : public ToggleControlSP { - Q_OBJECT - -public: - ParamControlSP(const QString ¶m, const QString &title, const QString &desc, const QString &icon, QWidget *parent = nullptr); - void setConfirmation(bool _confirm, bool _store_confirm) { - confirm = _confirm; - store_confirm = _store_confirm; - } - - void setActiveIcon(const QString &icon) { - active_icon_pixmap = QPixmap(icon).scaledToWidth(80, Qt::SmoothTransformation); - } - - void refresh() { - bool state = params.getBool(key); - if (state != toggle.on) { - toggle.togglePosition(); - setIcon(state); - } - } - - void showEvent(QShowEvent *event) override { - refresh(); - } - - bool isToggled() { return params.getBool(key); } - -private: - void toggleClicked(bool state); - void setIcon(bool state) { - if (state && !active_icon_pixmap.isNull()) { - icon_label->setPixmap(active_icon_pixmap); - } else if (!icon_pixmap.isNull()) { - icon_label->setPixmap(icon_pixmap); - } - } - - std::string key; - Params params; - QPixmap active_icon_pixmap; - bool confirm = false; - bool store_confirm = false; -}; - -class ButtonParamControlSP : public AbstractControlSP_SELECTOR { - Q_OBJECT - -public: - ButtonParamControlSP(const QString ¶m, const QString &title, const QString &desc, const QString &icon, - const std::vector &button_texts, const int minimum_button_width = 300) : AbstractControlSP_SELECTOR(title, desc, icon), button_texts(button_texts) { - const QString style = R"( - QPushButton { - border-radius: 20px; - font-size: 50px; - font-weight: 450; - height:150px; - padding: 0 25 0 25; - color: #FFFFFF; - } - QPushButton:pressed { - background-color: #4a4a4a; - } - QPushButton:checked:enabled { - background-color: #696868; - } - QPushButton:disabled { - color: #33FFFFFF; - } - QPushButton:checked:disabled { - background-color: #121212; - color: #33FFFFFF; - } - )"; - key = param.toStdString(); - int value = atoi(params.get(key).c_str()); - - button_group = new QButtonGroup(this); - button_group->setExclusive(true); - for (int i = 0; i < button_texts.size(); i++) { - QPushButton *button = new QPushButton(button_texts[i], this); - button->setCheckable(true); - button->setChecked(i == value); - button->setStyleSheet(style); - button->setMinimumWidth(minimum_button_width); - if (i == 0) hlayout->addSpacing(2); - hlayout->addWidget(button); - button_group->addButton(button, i); - } - - hlayout->setAlignment(Qt::AlignLeft); - - QObject::connect(button_group, QOverload::of(&QButtonGroup::buttonClicked), [=](int id) { - params.put(key, std::to_string(id)); - emit buttonToggled(id); - }); - } - - void setEnabled(bool enable) { - for (auto btn: button_group->buttons()) { - btn->setEnabled(enable); - } - button_group_enabled = enable; - - update(); - } - - void setCheckedButton(int id) { - button_group->button(id)->setChecked(true); - } - - void refresh() { - int value = atoi(params.get(key).c_str()); - - if (value >= button_texts.size()) { - value = button_texts.size() - 1; - } - if (value < 0) { - value = 0; - } - - button_group->button(value)->setChecked(true); - } - - void showEvent(QShowEvent *event) override { - refresh(); - } - - void setButton(QString param) { - key = param.toStdString(); - int value = atoi(params.get(key).c_str()); - for (int i = 0; i < button_group->buttons().size(); i++) { - button_group->buttons()[i]->setChecked(i == value); - } - } - - void setDisabledSelectedButton(std::string val) { - int value = atoi(val.c_str()); - for (int i = 0; i < button_group->buttons().size(); i++) { - button_group->buttons()[i]->setEnabled(i != value); - } - } - -protected: - void paintEvent(QPaintEvent *event) override { - QPainter p(this); - p.setRenderHint(QPainter::Antialiasing); - - // Calculate the total width and height for the background rectangle - int w = 0; - int h = 150; - - for (int i = 0; i < hlayout->count(); ++i) { - QPushButton *button = qobject_cast(hlayout->itemAt(i)->widget()); - if (button) { - w += button->width(); - } - } - - // Draw the rectangle -#ifdef __APPLE__ - QRect rect(0 + 2, h - 16, w, h); -#else - QRect rect(0 + 2, h - 24, w, h); -#endif - p.setPen(QPen(QColor(button_group_enabled ? "#696868" : "#121212"), 3)); - p.drawRoundedRect(rect, 20, 20); - } - -signals: - void buttonToggled(int btn_id); - -private: - std::string key; - Params params; - QButtonGroup *button_group; - std::vector button_texts; - - bool button_group_enabled = true; -}; - -class ListWidgetSP : public QWidget { - Q_OBJECT - -public: - explicit ListWidgetSP(QWidget *parent = 0, const bool split_line = true) : QWidget(parent), _split_line(split_line), outer_layout(this) { - outer_layout.setMargin(0); - outer_layout.setSpacing(0); - outer_layout.addLayout(&inner_layout); - inner_layout.setMargin(0); - inner_layout.setSpacing(25); // default spacing is 25 - outer_layout.addStretch(); - } - inline void addItem(QWidget *w) { inner_layout.addWidget(w); } - inline void addItem(QLayout *layout) { inner_layout.addLayout(layout); } - inline void setSpacing(int spacing) { inner_layout.setSpacing(spacing); } - - inline void AddWidgetAt(const int index, QWidget *new_widget) { inner_layout.insertWidget(index, new_widget); } - inline void RemoveWidgetAt(const int index) { - if (QLayoutItem *item; (item = inner_layout.takeAt(index)) != nullptr) { - if (item->widget()) delete item->widget(); - delete item; - } - } - - inline void ReplaceOrAddWidget(QWidget *old_widget, QWidget *new_widget) { - if (const int index = inner_layout.indexOf(old_widget); index != -1) { - RemoveWidgetAt(index); - AddWidgetAt(index, new_widget); - } else { - addItem(new_widget); - } - } - -private: - void paintEvent(QPaintEvent *) override { - QPainter p(this); - p.setPen(Qt::gray); - for (int i = 0; i < inner_layout.count() - 1; ++i) { - QWidget *widget = inner_layout.itemAt(i)->widget(); - if ((widget == nullptr || widget->isVisible()) && _split_line) { - QRect r = inner_layout.itemAt(i)->geometry(); - int bottom = r.bottom() + inner_layout.spacing() / 2; - p.drawLine(r.left(), bottom, r.right(), bottom); - } - } - } - QVBoxLayout outer_layout; - QVBoxLayout inner_layout; - - bool _split_line; -}; - -// convenience class for wrapping layouts -class LayoutWidgetSP : public QWidget { - Q_OBJECT - -public: - LayoutWidgetSP(QLayout *l, QWidget *parent = nullptr) : QWidget(parent) { - setLayout(l); - } -}; - -class OptionControlSP : public AbstractControlSP_SELECTOR { - Q_OBJECT - -private: - struct MinMaxValue { - int min_value; - int max_value; - }; - -public: - OptionControlSP(const QString ¶m, const QString &title, const QString &desc, const QString &icon, - const MinMaxValue &range, const int per_value_change = 1) : _title(title), AbstractControlSP_SELECTOR(title, desc, icon) { - const QString style = R"( - QPushButton { - border-radius: 20px; - font-size: 60px; - font-weight: 500; - width: 150px; - height: 150px; - padding: -3 25 3 25; - color: #FFFFFF; - font-weight: bold; - } - QPushButton:pressed { - color: #5C5C5C; - } - QPushButton:disabled { - color: #5C5C5C; - } - )"; - - label.setStyleSheet(label_enabled_style); - label.setFixedWidth(300); - label.setAlignment(Qt::AlignCenter); - - const std::vector button_texts{"-", "+"}; - - key = param.toStdString(); - value = atoi(params.get(key).c_str()); - - button_group = new QButtonGroup(this); - button_group->setExclusive(true); - for (int i = 0; i < button_texts.size(); i++) { - QPushButton *button = new QPushButton(button_texts[i], this); - button->setStyleSheet(style + ((i == 0) ? "QPushButton { text-align: left; }" : - "QPushButton { text-align: right; }")); - hlayout->addWidget(button, 0, ((i == 0) ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignVCenter); - if (i == 0) { - hlayout->addWidget(&label, 0, Qt::AlignCenter); - } - button_group->addButton(button, i); - - QObject::connect(button, &QPushButton::clicked, [=]() { - int change_value = (i == 0) ? -per_value_change : per_value_change; - key = param.toStdString(); - value = atoi(params.get(key).c_str()); - value += change_value; - value = std::clamp(value, range.min_value, range.max_value); - params.put(key, QString::number(value).toStdString()); - - button_group->button(0)->setEnabled(!(value <= range.min_value)); - button_group->button(1)->setEnabled(!(value >= range.max_value)); - - updateLabels(); - - if (request_update) { - emit updateOtherToggles(); - } - }); - } - - hlayout->setAlignment(Qt::AlignLeft); - } - - void setUpdateOtherToggles(bool _update) { - request_update = _update; - } - - inline void setLabel(const QString &text) { label.setText(text); } - - void setEnabled(bool enabled) { - for (auto btn: button_group->buttons()) { - btn->setEnabled(enabled); - } - label.setEnabled(enabled); - label.setStyleSheet(enabled ? label_enabled_style : label_disabled_style); - button_enabled = enabled; - - update(); - } - -protected: - void paintEvent(QPaintEvent *event) override { - QPainter p(this); - p.setRenderHint(QPainter::Antialiasing); - - // Calculate the total width and height for the background rectangle - int w = 0; - int h = 150; - - for (int i = 0; i < hlayout->count(); ++i) { - QWidget *widget = qobject_cast(hlayout->itemAt(i)->widget()); - if (widget) { - w += widget->width(); - } - } - - // Draw the rectangle - QRect rect(0, !_title.isEmpty() ? (h - 24) : 20, w, h); - p.setBrush(QColor(button_enabled ? "#b24a4a4a" : "#121212")); // Background color - p.setPen(QPen(Qt::NoPen)); - p.drawRoundedRect(rect, 20, 20); - } - -signals: - void updateLabels(); - void updateOtherToggles(); - -private: - std::string key; - int value; - QButtonGroup *button_group; - QLabel label; - Params params; - std::map option_label = {}; - bool request_update = false; - QString _title = ""; - - const QString label_enabled_style = "font-size: 50px; font-weight: 450; color: #FFFFFF;"; - const QString label_disabled_style = "font-size: 50px; font-weight: 450; color: #5C5C5C;"; - - bool button_enabled = true; -}; - -class SubPanelButton : public QPushButton { - Q_OBJECT - -public: - SubPanelButton(const QString &text, const int minimum_button_width = 800, QWidget *parent = nullptr) : QPushButton(text, parent) { - const QString buttonStyle = R"( - QPushButton { - border-radius: 20px; - font-size: 50px; - font-weight: 450; - height: 150px; - padding: 0 25px 0 25px; - color: #FFFFFF; - } - QPushButton:enabled { - background-color: #393939; - } - QPushButton:pressed { - background-color: #4A4A4A; - } - QPushButton:disabled { - background-color: #121212; - color: #5C5C5C; - } - )"; - - setStyleSheet(buttonStyle); - setFixedWidth(minimum_button_width); - } -}; - -class PanelBackButton : public QPushButton { - Q_OBJECT - -public: - PanelBackButton(const QString &label = "Back", QWidget *parent = nullptr) : QPushButton(label, parent) { - setObjectName("back_btn"); - setFixedSize(400, 100); - } -}; diff --git a/sunnypilot/selfdrive/ui/qt/widgets/drive_stats.cc b/sunnypilot/selfdrive/ui/qt/widgets/drive_stats.cc deleted file mode 100644 index 0814e1e24e..0000000000 --- a/sunnypilot/selfdrive/ui/qt/widgets/drive_stats.cc +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#include "../../sunnypilot/selfdrive/ui/qt/widgets/drive_stats.h" - -#include -#include -#include - -#include "common/params.h" -#include "selfdrive/ui/qt/request_repeater.h" -#include "selfdrive/ui/qt/util.h" - -static QLabel* newLabel(const QString& text, const QString &type) { - QLabel* label = new QLabel(text); - label->setProperty("type", type); - return label; -} - -DriveStats::DriveStats(QWidget* parent) : QFrame(parent) { - metric_ = Params().getBool("IsMetric"); - - QVBoxLayout* main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(50, 50, 50, 60); - - auto add_stats_layouts = [=](const QString &title, StatsLabels& labels) { - QGridLayout* grid_layout = new QGridLayout; - grid_layout->setVerticalSpacing(10); - grid_layout->setContentsMargins(0, 10, 0, 10); - - int row = 0; - grid_layout->addWidget(newLabel(title, "title"), row++, 0, 1, 3); - grid_layout->addItem(new QSpacerItem(0, 50), row++, 0, 1, 1); - - grid_layout->addWidget(labels.routes = newLabel("0", "number"), row, 0, Qt::AlignLeft); - grid_layout->addWidget(labels.distance = newLabel("0", "number"), row, 1, Qt::AlignLeft); - grid_layout->addWidget(labels.hours = newLabel("0", "number"), row, 2, Qt::AlignLeft); - - grid_layout->addWidget(newLabel((tr("Drives")), "unit"), row + 1, 0, Qt::AlignLeft); - grid_layout->addWidget(labels.distance_unit = newLabel(getDistanceUnit(), "unit"), row + 1, 1, Qt::AlignLeft); - grid_layout->addWidget(newLabel(tr("Hours"), "unit"), row + 1, 2, Qt::AlignLeft); - - main_layout->addLayout(grid_layout); - }; - - add_stats_layouts(tr("ALL TIME"), all_); - main_layout->addStretch(); - add_stats_layouts(tr("PAST WEEK"), week_); - - if (auto dongleId = getDongleId()) { - QString url = CommaApi::BASE_URL + "/v1.1/devices/" + *dongleId + "/stats"; - RequestRepeater* repeater = new RequestRepeater(this, url, "ApiCache_DriveStats", 30); - QObject::connect(repeater, &RequestRepeater::requestDone, this, &DriveStats::parseResponse); - } - - setStyleSheet(R"( - DriveStats { - background-color: #333333; - border-radius: 10px; - } - - QLabel[type="title"] { font-size: 51px; font-weight: 500; } - QLabel[type="number"] { font-size: 78px; font-weight: 500; } - QLabel[type="unit"] { font-size: 51px; font-weight: 300; color: #A0A0A0; } - )"); -} - -void DriveStats::updateStats() { - auto update = [=](const QJsonObject& obj, StatsLabels& labels) { - labels.routes->setText(QString::number((int)obj["routes"].toDouble())); - labels.distance->setText(QString::number(int(obj["distance"].toDouble() * (metric_ ? MILE_TO_KM : 1)))); - labels.distance_unit->setText(getDistanceUnit()); - labels.hours->setText(QString::number((int)(obj["minutes"].toDouble() / 60))); - }; - - QJsonObject json = stats_.object(); - update(json["all"].toObject(), all_); - update(json["week"].toObject(), week_); -} - -void DriveStats::parseResponse(const QString& response, bool success) { - if (!success) return; - - QJsonDocument doc = QJsonDocument::fromJson(response.trimmed().toUtf8()); - if (doc.isNull()) { - qDebug() << "JSON Parse failed on getting past drives statistics"; - return; - } - stats_ = doc; - updateStats(); -} - -void DriveStats::showEvent(QShowEvent* event) { - bool metric = Params().getBool("IsMetric"); - if (metric_ != metric) { - metric_ = metric; - updateStats(); - } -} diff --git a/sunnypilot/selfdrive/ui/qt/widgets/drive_stats.h b/sunnypilot/selfdrive/ui/qt/widgets/drive_stats.h deleted file mode 100644 index 0b2802ed8c..0000000000 --- a/sunnypilot/selfdrive/ui/qt/widgets/drive_stats.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#pragma once - -#include -#include - -class DriveStats : public QFrame { - Q_OBJECT - -public: - explicit DriveStats(QWidget* parent = 0); - -private: - void showEvent(QShowEvent *event) override; - void updateStats(); - inline QString getDistanceUnit() const { return metric_ ? tr("KM") : tr("Miles"); } - - bool metric_; - QJsonDocument stats_; - struct StatsLabels { - QLabel *routes, *distance, *distance_unit, *hours; - } all_, week_; - -private slots: - void parseResponse(const QString &response, bool success); -}; diff --git a/sunnypilot/selfdrive/ui/qt/widgets/scrollview.cc b/sunnypilot/selfdrive/ui/qt/widgets/scrollview.cc deleted file mode 100644 index ab3ae1401c..0000000000 --- a/sunnypilot/selfdrive/ui/qt/widgets/scrollview.cc +++ /dev/null @@ -1,37 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * Last updated: July 29, 2024 -*/ - -#include "../../sunnypilot/selfdrive/ui/qt/widgets/scrollview.h" - -#include - -void ScrollViewSP::setLastScrollPosition() { - lastScrollPosition = verticalScrollBar()->value(); -} - -void ScrollViewSP::restoreScrollPosition() { - verticalScrollBar()->setValue(lastScrollPosition); -} diff --git a/sunnypilot/selfdrive/ui/qt/widgets/scrollview.h b/sunnypilot/selfdrive/ui/qt/widgets/scrollview.h deleted file mode 100644 index 5c20bce2f2..0000000000 --- a/sunnypilot/selfdrive/ui/qt/widgets/scrollview.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#pragma once - -#include "selfdrive/ui/qt/widgets/scrollview.h" - -class ScrollViewSP : public ScrollView { - Q_OBJECT - -public: - explicit ScrollViewSP(QWidget *w = nullptr, QWidget *parent = nullptr) : ScrollView(w, parent) {} - -public slots: - void setLastScrollPosition(); - void restoreScrollPosition(); - -private: - int lastScrollPosition = 0; -}; diff --git a/sunnypilot/selfdrive/ui/qt/widgets/toggle.cc b/sunnypilot/selfdrive/ui/qt/widgets/toggle.cc deleted file mode 100644 index e240e15664..0000000000 --- a/sunnypilot/selfdrive/ui/qt/widgets/toggle.cc +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#include "../../sunnypilot/selfdrive/ui/qt/widgets/toggle.h" - -#include - -ToggleSP::ToggleSP(QWidget *parent) : Toggle(parent) { - _height_rect = 80; -} - -void ToggleSP::paintEvent(QPaintEvent *e) { - this->setFixedHeight(100); - QPainter p(this); - p.setPen(Qt::NoPen); - p.setRenderHint(QPainter::Antialiasing, true); - - // Draw toggle background - enabled ? green.setRgb(0x1e79e8) : green.setRgb(0x125db8); - p.setBrush(on ? green : QColor(0x292929)); - p.drawRoundedRect(QRect(0, 10, width(), _height_rect), _height_rect / 2, _height_rect / 2); - - // Draw toggle circle - p.setBrush(circleColor); - p.drawEllipse(QRectF(_x_circle - _radius + 6, 16, 68, 68)); -} diff --git a/sunnypilot/selfdrive/ui/qt/widgets/toggle.h b/sunnypilot/selfdrive/ui/qt/widgets/toggle.h deleted file mode 100644 index e98dea39af..0000000000 --- a/sunnypilot/selfdrive/ui/qt/widgets/toggle.h +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#pragma once - -#include "selfdrive/ui/qt/widgets/toggle.h" - -class ToggleSP : public Toggle { - Q_OBJECT - -public: - explicit ToggleSP(QWidget *parent = nullptr); - -protected: - void paintEvent(QPaintEvent *) override; -}; diff --git a/sunnypilot/selfdrive/ui/qt/window.cc b/sunnypilot/selfdrive/ui/qt/window.cc deleted file mode 100644 index 168a1af467..0000000000 --- a/sunnypilot/selfdrive/ui/qt/window.cc +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#include "../../sunnypilot/selfdrive/ui/qt/window.h" - -MainWindowSP::MainWindowSP(QWidget *parent) - : MainWindow(parent, new HomeWindowSP(parent), new SettingsWindowSP(parent), new OnboardingWindowSP(parent)) { - - homeWindow = dynamic_cast(MainWindow::homeWindow); - settingsWindow = dynamic_cast(MainWindow::settingsWindow); - onboardingWindow = dynamic_cast(MainWindow::onboardingWindow); -} - -void MainWindowSP::closeSettings() { - MainWindow::closeSettings(); -} diff --git a/sunnypilot/selfdrive/ui/qt/window.h b/sunnypilot/selfdrive/ui/qt/window.h deleted file mode 100644 index 39d20aec8f..0000000000 --- a/sunnypilot/selfdrive/ui/qt/window.h +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#pragma once - -#include "selfdrive/ui/qt/window.h" -#include "../../sunnypilot/selfdrive/ui/qt/home.h" -#include "../../sunnypilot/selfdrive/ui/qt/offroad/settings/settings.h" -#include "offroad/settings/onboarding.h" - -class MainWindowSP : public MainWindow { - Q_OBJECT - -public: - explicit MainWindowSP(QWidget *parent = 0); - -private: - HomeWindowSP *homeWindow; - SettingsWindowSP *settingsWindow; - OnboardingWindowSP *onboardingWindow; - void closeSettings() override; -}; diff --git a/sunnypilot/selfdrive/ui/ui.cc b/sunnypilot/selfdrive/ui/ui.cc deleted file mode 100644 index 3475a5e6e9..0000000000 --- a/sunnypilot/selfdrive/ui/ui.cc +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#include "../../sunnypilot/selfdrive/ui/ui.h" - -#include "common/watchdog.h" - -void UIStateSP::updateStatus() { - UIState::updateStatus(); -} - -UIStateSP::UIStateSP(QObject *parent) : UIState(parent) { - sm = std::make_unique(std::vector{ - "modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", - "pandaStates", "carParams", "driverMonitoringState", "carState", "driverStateV2", - "wideRoadCameraState", "managerState", "selfdriveState", "longitudinalPlan", - }); - - // update timer - timer = new QTimer(this); - QObject::connect(timer, &QTimer::timeout, this, &UIStateSP::update); - timer->start(1000 / UI_FREQ); -} - -// This method overrides completely the update method from the parent class intentionally. -void UIStateSP::update() { - update_sockets(this); - update_state(this); - updateStatus(); - - if (sm->frame % UI_FREQ == 0) { - watchdog_kick(nanos_since_boot()); - } - emit uiUpdate(*this); -} - -DeviceSP::DeviceSP(QObject *parent) : Device(parent) { - QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &DeviceSP::update); -} - -UIStateSP *uiStateSP() { - static UIStateSP ui_state; - return &ui_state; -} - -DeviceSP *deviceSP() { - static DeviceSP _device; - return &_device; -} diff --git a/sunnypilot/selfdrive/ui/ui.h b/sunnypilot/selfdrive/ui/ui.h deleted file mode 100644 index 65c9237969..0000000000 --- a/sunnypilot/selfdrive/ui/ui.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#pragma once - -#include "selfdrive/ui/ui.h" - -class UIStateSP : public UIState { - Q_OBJECT - -public: - UIStateSP(QObject *parent = 0); - void updateStatus() override; - -signals: - void uiUpdate(const UIStateSP &s); - -private slots: - void update() override; -}; - -UIStateSP *uiStateSP(); -inline UIStateSP *uiState() { return uiStateSP(); }; - -// device management class -class DeviceSP : public Device { - Q_OBJECT - -public: - DeviceSP(QObject *parent = 0); -}; - -DeviceSP *deviceSP(); -inline DeviceSP *device() { return deviceSP(); } From 3966599e9d287d2ebac32c3033345a5f9ce913ae Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Fri, 3 Jan 2025 15:04:09 -0500 Subject: [PATCH 2/2] ui: sunnypilot offroad UI (#512) * Revert "Revert "ui: sunnypilot offroad UI" (#511)" This reverts commit 0e264e1b057b4b0bd02f05c57ac082fbb7588a73. * Revert "move files to sp dir" This reverts commit c72d732259d35872fca92b90289785e7b4086140. * remove drive stats for now * update translation * update onboarding * remove sp onboarding for now * rearrange * remove more * shorter license --- .codespellignore | 1 + SConstruct | 10 + scripts/lint/lint.sh | 2 +- selfdrive/ui/SConscript | 10 +- selfdrive/ui/main.cc | 8 +- selfdrive/ui/qt/body.h | 6 + selfdrive/ui/qt/home.h | 22 +- selfdrive/ui/qt/network/networking.cc | 6 + selfdrive/ui/qt/network/networking.h | 11 + selfdrive/ui/qt/offroad/developer_panel.cc | 7 +- selfdrive/ui/qt/offroad/developer_panel.h | 4 + selfdrive/ui/qt/offroad/onboarding.h | 7 +- selfdrive/ui/qt/offroad/settings.cc | 3 + selfdrive/ui/qt/offroad/settings.h | 30 +- selfdrive/ui/qt/onroad/buttons.h | 4 + selfdrive/ui/qt/onroad/model.h | 4 + selfdrive/ui/qt/onroad/onroad_home.cc | 4 + selfdrive/ui/qt/onroad/onroad_home.h | 12 +- selfdrive/ui/qt/request_repeater.h | 5 + selfdrive/ui/qt/sidebar.cc | 4 + selfdrive/ui/qt/sidebar.h | 8 +- selfdrive/ui/qt/util.h | 6 + selfdrive/ui/qt/widgets/cameraview.h | 5 + selfdrive/ui/qt/widgets/controls.cc | 3 + selfdrive/ui/qt/widgets/controls.h | 6 +- selfdrive/ui/qt/widgets/ssh_keys.h | 7 + selfdrive/ui/qt/widgets/toggle.h | 3 +- selfdrive/ui/qt/widgets/wifi.h | 5 + selfdrive/ui/qt/window.cc | 9 +- selfdrive/ui/qt/window.h | 11 +- selfdrive/ui/sunnypilot/SConscript | 19 + selfdrive/ui/sunnypilot/qt/home.cc | 19 + selfdrive/ui/sunnypilot/qt/home.h | 38 ++ .../qt/offroad/settings/settings.cc | 146 +++++ .../sunnypilot/qt/offroad/settings/settings.h | 39 ++ .../ui/sunnypilot/qt/onroad/onroad_home.cc | 32 + .../ui/sunnypilot/qt/onroad/onroad_home.h | 24 + selfdrive/ui/sunnypilot/qt/sidebar.cc | 27 + selfdrive/ui/sunnypilot/qt/sidebar.h | 27 + .../ui/sunnypilot/qt/widgets/controls.cc | 254 ++++++++ selfdrive/ui/sunnypilot/qt/widgets/controls.h | 588 ++++++++++++++++++ .../ui/sunnypilot/qt/widgets/scrollview.cc | 18 + .../ui/sunnypilot/qt/widgets/scrollview.h | 24 + selfdrive/ui/sunnypilot/qt/widgets/toggle.cc | 30 + selfdrive/ui/sunnypilot/qt/widgets/toggle.h | 20 + selfdrive/ui/sunnypilot/qt/window.cc | 19 + selfdrive/ui/sunnypilot/qt/window.h | 24 + selfdrive/ui/sunnypilot/ui.cc | 53 ++ selfdrive/ui/sunnypilot/ui.h | 38 ++ selfdrive/ui/tests/test_ui/run.py | 4 +- selfdrive/ui/translations/main_ar.ts | 42 ++ selfdrive/ui/translations/main_de.ts | 42 ++ selfdrive/ui/translations/main_es.ts | 42 ++ selfdrive/ui/translations/main_fr.ts | 42 ++ selfdrive/ui/translations/main_ja.ts | 42 ++ selfdrive/ui/translations/main_ko.ts | 42 ++ selfdrive/ui/translations/main_pt-BR.ts | 42 ++ selfdrive/ui/translations/main_th.ts | 42 ++ selfdrive/ui/translations/main_tr.ts | 42 ++ selfdrive/ui/translations/main_zh-CHS.ts | 42 ++ selfdrive/ui/translations/main_zh-CHT.ts | 42 ++ selfdrive/ui/ui.cc | 12 +- selfdrive/ui/ui.h | 19 +- .../selfdrive/assets/offroad/icon_home.svg | 3 + .../assets/offroad/icon_software.png | 3 + .../selfdrive/assets/offroad/icon_toggle.png | 3 + 66 files changed, 2118 insertions(+), 50 deletions(-) create mode 100644 .codespellignore create mode 100644 selfdrive/ui/sunnypilot/SConscript create mode 100644 selfdrive/ui/sunnypilot/qt/home.cc create mode 100644 selfdrive/ui/sunnypilot/qt/home.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h create mode 100644 selfdrive/ui/sunnypilot/qt/onroad/onroad_home.cc create mode 100644 selfdrive/ui/sunnypilot/qt/onroad/onroad_home.h create mode 100644 selfdrive/ui/sunnypilot/qt/sidebar.cc create mode 100644 selfdrive/ui/sunnypilot/qt/sidebar.h create mode 100644 selfdrive/ui/sunnypilot/qt/widgets/controls.cc create mode 100644 selfdrive/ui/sunnypilot/qt/widgets/controls.h create mode 100644 selfdrive/ui/sunnypilot/qt/widgets/scrollview.cc create mode 100644 selfdrive/ui/sunnypilot/qt/widgets/scrollview.h create mode 100644 selfdrive/ui/sunnypilot/qt/widgets/toggle.cc create mode 100644 selfdrive/ui/sunnypilot/qt/widgets/toggle.h create mode 100644 selfdrive/ui/sunnypilot/qt/window.cc create mode 100644 selfdrive/ui/sunnypilot/qt/window.h create mode 100644 selfdrive/ui/sunnypilot/ui.cc create mode 100644 selfdrive/ui/sunnypilot/ui.h create mode 100644 sunnypilot/selfdrive/assets/offroad/icon_home.svg create mode 100644 sunnypilot/selfdrive/assets/offroad/icon_software.png create mode 100644 sunnypilot/selfdrive/assets/offroad/icon_toggle.png diff --git a/.codespellignore b/.codespellignore new file mode 100644 index 0000000000..916169347a --- /dev/null +++ b/.codespellignore @@ -0,0 +1 @@ +Wen diff --git a/SConstruct b/SConstruct index 934143fa08..39f7d14d03 100644 --- a/SConstruct +++ b/SConstruct @@ -74,6 +74,12 @@ AddOption('--minimal', default=os.path.exists(File('#.lfsconfig').abspath), # minimal by default on release branch (where there's no LFS) help='the minimum build to run openpilot. no tests, tools, etc.') +AddOption('--stock-ui', + action='store_true', + dest='stock_ui', + default=False, + help='Build stock openpilot UI instead of sunnypilot UI') + ## Architecture name breakdown (arch) ## - larch64: linux tici aarch64 ## - aarch64: linux pc aarch64 @@ -172,6 +178,10 @@ else: if arch != "Darwin": ldflags += ["-Wl,--as-needed", "-Wl,--no-undefined"] +if not GetOption('stock_ui'): + cflags += ["-DSUNNYPILOT"] + cxxflags += ["-DSUNNYPILOT"] + ccflags_option = GetOption('ccflags') if ccflags_option: ccflags += ccflags_option.split(' ') diff --git a/scripts/lint/lint.sh b/scripts/lint/lint.sh index 0002a44726..94ba59beed 100755 --- a/scripts/lint/lint.sh +++ b/scripts/lint/lint.sh @@ -57,7 +57,7 @@ function run_tests() { if [[ -z "$FAST" ]]; then run "mypy" mypy $PYTHON_FILES - run "codespell" codespell $ALL_FILES + run "codespell" codespell $ALL_FILES --ignore-words=$ROOT/.codespellignore fi return $FAILED diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index c851d7a969..47ad5bdefa 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -12,6 +12,12 @@ if arch == "Darwin": del base_libs[base_libs.index('OpenCL')] qt_env['FRAMEWORKS'] += ['OpenCL'] +sp_widgets_src = [] +sp_qt_src = [] +if not GetOption('stock_ui'): + SConscript(['sunnypilot/SConscript']) + Import('sp_widgets_src', 'sp_qt_src') + # FIXME: remove this once we're on 5.15 (24.04) qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"] @@ -20,7 +26,7 @@ widgets_src = ["qt/widgets/input.cc", "qt/widgets/wifi.cc", "qt/prime_state.cc", "qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc", "qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc", "qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc", - "qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"] + "qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"] + sp_widgets_src widgets = qt_env.Library("qt_widgets", widgets_src, LIBS=base_libs) Export('widgets') @@ -31,7 +37,7 @@ qt_src = ["main.cc", "ui.cc", "qt/sidebar.cc", "qt/body.cc", "qt/offroad/software_settings.cc", "qt/offroad/developer_panel.cc", "qt/offroad/onboarding.cc", "qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc", "qt/onroad/onroad_home.cc", "qt/onroad/annotated_camera.cc", "qt/onroad/model.cc", - "qt/onroad/buttons.cc", "qt/onroad/alerts.cc", "qt/onroad/driver_monitoring.cc", "qt/onroad/hud.cc"] + "qt/onroad/buttons.cc", "qt/onroad/alerts.cc", "qt/onroad/driver_monitoring.cc", "qt/onroad/hud.cc"] + sp_qt_src # build translation files with open(File("translations/languages.json").abspath) as f: diff --git a/selfdrive/ui/main.cc b/selfdrive/ui/main.cc index 4903a3db3d..db96817f1a 100644 --- a/selfdrive/ui/main.cc +++ b/selfdrive/ui/main.cc @@ -4,10 +4,16 @@ #include #include "system/hardware/hw.h" -#include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/window.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/window.h" +#define MainWindow MainWindowSP +#else +#include "selfdrive/ui/qt/qt_window.h" +#endif + int main(int argc, char *argv[]) { setpriority(PRIO_PROCESS, 0, -20); diff --git a/selfdrive/ui/qt/body.h b/selfdrive/ui/qt/body.h index 567a54d49b..187e015af7 100644 --- a/selfdrive/ui/qt/body.h +++ b/selfdrive/ui/qt/body.h @@ -5,7 +5,13 @@ #include #include "common/util.h" + +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#define UIState UIStateSP +#else #include "selfdrive/ui/ui.h" +#endif class RecordButton : public QPushButton { Q_OBJECT diff --git a/selfdrive/ui/qt/home.h b/selfdrive/ui/qt/home.h index 55bc706c0d..f3227c6559 100644 --- a/selfdrive/ui/qt/home.h +++ b/selfdrive/ui/qt/home.h @@ -9,12 +9,23 @@ #include "common/params.h" #include "selfdrive/ui/qt/body.h" -#include "selfdrive/ui/qt/onroad/onroad_home.h" -#include "selfdrive/ui/qt/sidebar.h" -#include "selfdrive/ui/qt/widgets/controls.h" #include "selfdrive/ui/qt/widgets/offroad_alerts.h" #include "selfdrive/ui/ui.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#include "selfdrive/ui/sunnypilot/qt/onroad/onroad_home.h" +#include "selfdrive/ui/sunnypilot/qt/sidebar.h" +#define OnroadWindow OnroadWindowSP +#define LayoutWidget LayoutWidgetSP +#define Sidebar SidebarSP +#define ElidedLabel ElidedLabelSP +#else +#include "selfdrive/ui/qt/widgets/controls.h" +#include "selfdrive/ui/qt/onroad/onroad_home.h" +#include "selfdrive/ui/qt/sidebar.h" +#endif + class OffroadHome : public QFrame { Q_OBJECT @@ -58,13 +69,12 @@ protected: void mousePressEvent(QMouseEvent* e) override; void mouseDoubleClickEvent(QMouseEvent* e) override; -private: Sidebar *sidebar; OffroadHome *home; OnroadWindow *onroad; BodyWindow *body; QStackedLayout *slayout; -private slots: - void updateState(const UIState &s); +protected slots: + virtual void updateState(const UIState &s); }; diff --git a/selfdrive/ui/qt/network/networking.cc b/selfdrive/ui/qt/network/networking.cc index 066dc3ca7e..02c137413c 100644 --- a/selfdrive/ui/qt/network/networking.cc +++ b/selfdrive/ui/qt/network/networking.cc @@ -8,8 +8,14 @@ #include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/util.h" + +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" +#else #include "selfdrive/ui/qt/widgets/controls.h" #include "selfdrive/ui/qt/widgets/scrollview.h" +#endif static const int ICON_WIDTH = 49; diff --git a/selfdrive/ui/qt/network/networking.h b/selfdrive/ui/qt/network/networking.h index 4fd604039b..2d924588f0 100644 --- a/selfdrive/ui/qt/network/networking.h +++ b/selfdrive/ui/qt/network/networking.h @@ -8,6 +8,17 @@ #include "selfdrive/ui/qt/widgets/ssh_keys.h" #include "selfdrive/ui/qt/widgets/toggle.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#define ButtonControl ButtonControlSP +#define ElidedLabel ElidedLabelSP +#define LabelControl LabelControlSP +#define ListWidget ListWidgetSP +#define ToggleControl ToggleControlSP +#else +#include "selfdrive/ui/qt/widgets/controls.h" +#endif + class WifiItem : public QWidget { Q_OBJECT public: diff --git a/selfdrive/ui/qt/offroad/developer_panel.cc b/selfdrive/ui/qt/offroad/developer_panel.cc index bbd69f72ce..eee48a6bdb 100644 --- a/selfdrive/ui/qt/offroad/developer_panel.cc +++ b/selfdrive/ui/qt/offroad/developer_panel.cc @@ -2,9 +2,14 @@ #include "selfdrive/ui/qt/offroad/developer_panel.h" #include "selfdrive/ui/qt/widgets/ssh_keys.h" -#include "selfdrive/ui/qt/widgets/controls.h" #include "common/util.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#else +#include "selfdrive/ui/qt/widgets/controls.h" +#endif + DeveloperPanel::DeveloperPanel(SettingsWindow *parent) : ListWidget(parent) { // SSH keys addItem(new SshToggle()); diff --git a/selfdrive/ui/qt/offroad/developer_panel.h b/selfdrive/ui/qt/offroad/developer_panel.h index fe38612e57..0351cd045c 100644 --- a/selfdrive/ui/qt/offroad/developer_panel.h +++ b/selfdrive/ui/qt/offroad/developer_panel.h @@ -1,6 +1,10 @@ #pragma once +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#else #include "selfdrive/ui/qt/offroad/settings.h" +#endif class DeveloperPanel : public ListWidget { Q_OBJECT diff --git a/selfdrive/ui/qt/offroad/onboarding.h b/selfdrive/ui/qt/offroad/onboarding.h index a1b6895ba0..0f6dbb5063 100644 --- a/selfdrive/ui/qt/offroad/onboarding.h +++ b/selfdrive/ui/qt/offroad/onboarding.h @@ -71,6 +71,7 @@ public slots: private: void showEvent(QShowEvent *event) override; +protected: QPushButton *accept_btn; signals: @@ -97,10 +98,10 @@ class OnboardingWindow : public QStackedWidget { public: explicit OnboardingWindow(QWidget *parent = 0); inline void showTrainingGuide() { setCurrentIndex(1); } - inline bool completed() const { return accepted_terms && training_done; } + virtual inline bool completed() const { return accepted_terms && training_done; } -private: - void updateActiveScreen(); +protected: + virtual void updateActiveScreen(); Params params; bool accepted_terms = false, training_done = false; diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index d4aab53f5a..93dcd793c9 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -100,7 +100,9 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { } // Toggles with confirmation dialogs +#ifndef SUNNYPILOT toggles["ExperimentalMode"]->setActiveIcon("../assets/img_experimental.svg"); +#endif toggles["ExperimentalMode"]->setConfirmation(true, true); toggles["ExperimentalLongitudinalEnabled"]->setConfirmation(true, false); @@ -354,6 +356,7 @@ void SettingsWindow::setCurrentPanel(int index, const QString ¶m) { } SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { + RETURN_IF_SUNNYPILOT // setup two main layouts sidebar_widget = new QWidget; diff --git a/selfdrive/ui/qt/offroad/settings.h b/selfdrive/ui/qt/offroad/settings.h index de0528ee0a..41914c0ec0 100644 --- a/selfdrive/ui/qt/offroad/settings.h +++ b/selfdrive/ui/qt/offroad/settings.h @@ -10,9 +10,21 @@ #include #include -#include "selfdrive/ui/ui.h" #include "selfdrive/ui/qt/util.h" + +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#define ListWidget ListWidgetSP +#define ParamControl ParamControlSP +#define ButtonControl ButtonControlSP +#define ButtonParamControl ButtonParamControlSP +#define ToggleControl ToggleControlSP +#define LabelControl LabelControlSP +#else +#include "selfdrive/ui/ui.h" #include "selfdrive/ui/qt/widgets/controls.h" +#endif // ********** settings window + top-level panels ********** class SettingsWindow : public QFrame { @@ -30,7 +42,7 @@ signals: void reviewTrainingGuide(); void expandToggleDescription(const QString ¶m); -private: +protected: QPushButton *sidebar_alert_widget; QWidget *sidebar_widget; QButtonGroup *nav_btns; @@ -45,12 +57,12 @@ public: signals: void reviewTrainingGuide(); -private slots: +protected slots: void poweroff(); void reboot(); void updateCalibDescription(); -private: +protected: Params params; ButtonControl *pair_device; }; @@ -64,15 +76,15 @@ public: public slots: void expandToggleDescription(const QString ¶m); -private slots: - void updateState(const UIState &s); +protected slots: + virtual void updateState(const UIState &s); -private: +protected: Params params; std::map toggles; ButtonParamControl *long_personality_setting; - void updateToggles(); + virtual void updateToggles(); }; class SoftwarePanel : public ListWidget { @@ -80,7 +92,7 @@ class SoftwarePanel : public ListWidget { public: explicit SoftwarePanel(QWidget* parent = nullptr); -private: +protected: void showEvent(QShowEvent *event) override; void updateLabels(); void checkForUpdates(); diff --git a/selfdrive/ui/qt/onroad/buttons.h b/selfdrive/ui/qt/onroad/buttons.h index 9c91bc3c7b..3afaed33f4 100644 --- a/selfdrive/ui/qt/onroad/buttons.h +++ b/selfdrive/ui/qt/onroad/buttons.h @@ -2,7 +2,11 @@ #include +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#else #include "selfdrive/ui/ui.h" +#endif const int btn_size = 192; const int img_size = (btn_size / 4) * 3; diff --git a/selfdrive/ui/qt/onroad/model.h b/selfdrive/ui/qt/onroad/model.h index 79547e4b83..7e1b43acb4 100644 --- a/selfdrive/ui/qt/onroad/model.h +++ b/selfdrive/ui/qt/onroad/model.h @@ -3,7 +3,11 @@ #include #include +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#else #include "selfdrive/ui/ui.h" +#endif class ModelRenderer { public: diff --git a/selfdrive/ui/qt/onroad/onroad_home.cc b/selfdrive/ui/qt/onroad/onroad_home.cc index 080f9bd50f..7db9a05a0c 100644 --- a/selfdrive/ui/qt/onroad/onroad_home.cc +++ b/selfdrive/ui/qt/onroad/onroad_home.cc @@ -35,8 +35,12 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) { alerts->raise(); setAttribute(Qt::WA_OpaquePaintEvent); + + // We handle the connection of the signals on the derived class +#ifndef SUNNYPILOT QObject::connect(uiState(), &UIState::uiUpdate, this, &OnroadWindow::updateState); QObject::connect(uiState(), &UIState::offroadTransition, this, &OnroadWindow::offroadTransition); +#endif } void OnroadWindow::updateState(const UIState &s) { diff --git a/selfdrive/ui/qt/onroad/onroad_home.h b/selfdrive/ui/qt/onroad/onroad_home.h index c321d2d44f..2224ede32e 100644 --- a/selfdrive/ui/qt/onroad/onroad_home.h +++ b/selfdrive/ui/qt/onroad/onroad_home.h @@ -3,20 +3,24 @@ #include "selfdrive/ui/qt/onroad/alerts.h" #include "selfdrive/ui/qt/onroad/annotated_camera.h" +#ifdef SUNNYPILOT +#define UIState UIStateSP +#endif + class OnroadWindow : public QWidget { Q_OBJECT public: OnroadWindow(QWidget* parent = 0); -private: +protected: void paintEvent(QPaintEvent *event); OnroadAlerts *alerts; AnnotatedCameraWidget *nvg; QColor bg = bg_colors[STATUS_DISENGAGED]; QHBoxLayout* split; -private slots: - void offroadTransition(bool offroad); - void updateState(const UIState &s); +protected slots: + virtual void offroadTransition(bool offroad); + virtual void updateState(const UIState &s); }; diff --git a/selfdrive/ui/qt/request_repeater.h b/selfdrive/ui/qt/request_repeater.h index c0e2758273..a0e8bde0eb 100644 --- a/selfdrive/ui/qt/request_repeater.h +++ b/selfdrive/ui/qt/request_repeater.h @@ -2,7 +2,12 @@ #include "common/util.h" #include "selfdrive/ui/qt/api.h" + +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#else #include "selfdrive/ui/ui.h" +#endif class RequestRepeater : public HttpRequest { public: diff --git a/selfdrive/ui/qt/sidebar.cc b/selfdrive/ui/qt/sidebar.cc index 966396edc2..866a5b62d0 100644 --- a/selfdrive/ui/qt/sidebar.cc +++ b/selfdrive/ui/qt/sidebar.cc @@ -110,6 +110,10 @@ void Sidebar::updateState(const UIState &s) { void Sidebar::paintEvent(QPaintEvent *event) { QPainter p(this); + paintSidebar(p); +} + +void Sidebar::paintSidebar(QPainter &p) { p.setPen(Qt::NoPen); p.setRenderHint(QPainter::Antialiasing); diff --git a/selfdrive/ui/qt/sidebar.h b/selfdrive/ui/qt/sidebar.h index 2091418e52..b1ce5887f5 100644 --- a/selfdrive/ui/qt/sidebar.h +++ b/selfdrive/ui/qt/sidebar.h @@ -5,9 +5,14 @@ #include #include -#include "selfdrive/ui/ui.h" #include "selfdrive/ui/qt/network/networking.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#else +#include "selfdrive/ui/ui.h" +#endif + typedef QPair, QColor> ItemStatus; Q_DECLARE_METATYPE(ItemStatus); @@ -35,6 +40,7 @@ protected: void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void drawMetric(QPainter &p, const QPair &label, QColor c, int y); + virtual void paintSidebar(QPainter &p); QPixmap home_img, flag_img, settings_img; bool onroad, flag_pressed, settings_pressed; diff --git a/selfdrive/ui/qt/util.h b/selfdrive/ui/qt/util.h index 2bf1a70a62..6ea53d354a 100644 --- a/selfdrive/ui/qt/util.h +++ b/selfdrive/ui/qt/util.h @@ -13,6 +13,12 @@ #include "cereal/gen/cpp/car.capnp.h" #include "common/params.h" +#ifdef SUNNYPILOT +#define RETURN_IF_SUNNYPILOT return; +#else +#define RETURN_IF_SUNNYPILOT // Do nothing +#endif + QString getVersion(); QString getBrand(); QString getUserAgent(); diff --git a/selfdrive/ui/qt/widgets/cameraview.h b/selfdrive/ui/qt/widgets/cameraview.h index 29aa8493c7..e446ef5987 100644 --- a/selfdrive/ui/qt/widgets/cameraview.h +++ b/selfdrive/ui/qt/widgets/cameraview.h @@ -23,7 +23,12 @@ #endif #include "msgq/visionipc/visionipc_client.h" + +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#else #include "selfdrive/ui/ui.h" +#endif const int FRAME_BUFFER_SIZE = 5; diff --git a/selfdrive/ui/qt/widgets/controls.cc b/selfdrive/ui/qt/widgets/controls.cc index 40dda971f5..7dd3178200 100644 --- a/selfdrive/ui/qt/widgets/controls.cc +++ b/selfdrive/ui/qt/widgets/controls.cc @@ -3,7 +3,10 @@ #include #include +#include "selfdrive/ui/qt/util.h" + AbstractControl::AbstractControl(const QString &title, const QString &desc, const QString &icon, QWidget *parent) : QFrame(parent) { + RETURN_IF_SUNNYPILOT QVBoxLayout *main_layout = new QVBoxLayout(this); main_layout->setMargin(0); diff --git a/selfdrive/ui/qt/widgets/controls.h b/selfdrive/ui/qt/widgets/controls.h index aebf934f2a..9562c4ad22 100644 --- a/selfdrive/ui/qt/widgets/controls.h +++ b/selfdrive/ui/qt/widgets/controls.h @@ -40,7 +40,7 @@ class AbstractControl : public QFrame { Q_OBJECT public: - void setDescription(const QString &desc) { + virtual void setDescription(const QString &desc) { if (description) description->setText(desc); } @@ -52,7 +52,7 @@ public: value->setText(val); } - const QString getDescription() { + virtual const QString getDescription() { return description->text(); } @@ -60,7 +60,7 @@ public: QPixmap icon_pixmap; public slots: - void showDescription() { + virtual void showDescription() { description->setVisible(true); } diff --git a/selfdrive/ui/qt/widgets/ssh_keys.h b/selfdrive/ui/qt/widgets/ssh_keys.h index 920bd651e2..ef40346c83 100644 --- a/selfdrive/ui/qt/widgets/ssh_keys.h +++ b/selfdrive/ui/qt/widgets/ssh_keys.h @@ -3,7 +3,14 @@ #include #include "system/hardware/hw.h" + +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#define ButtonControl ButtonControlSP +#define ToggleControl ToggleControlSP +#else #include "selfdrive/ui/qt/widgets/controls.h" +#endif // SSH enable toggle class SshToggle : public ToggleControl { diff --git a/selfdrive/ui/qt/widgets/toggle.h b/selfdrive/ui/qt/widgets/toggle.h index e7263a008f..a0fa434a4c 100644 --- a/selfdrive/ui/qt/widgets/toggle.h +++ b/selfdrive/ui/qt/widgets/toggle.h @@ -23,14 +23,13 @@ public: update(); } bool getEnabled(); - void setEnabled(bool value); + virtual void setEnabled(bool value); protected: void paintEvent(QPaintEvent*) override; void mouseReleaseEvent(QMouseEvent*) override; void enterEvent(QEvent*) override; -private: QColor circleColor; QColor green; bool enabled = true; diff --git a/selfdrive/ui/qt/widgets/wifi.h b/selfdrive/ui/qt/widgets/wifi.h index 60c865f2b8..3daf25a124 100644 --- a/selfdrive/ui/qt/widgets/wifi.h +++ b/selfdrive/ui/qt/widgets/wifi.h @@ -4,7 +4,12 @@ #include #include +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/ui.h" +#define UIState UIStateSP +#else #include "selfdrive/ui/ui.h" +#endif class WiFiPromptWidget : public QFrame { Q_OBJECT diff --git a/selfdrive/ui/qt/window.cc b/selfdrive/ui/qt/window.cc index e1ec916c6f..4479d4f79d 100644 --- a/selfdrive/ui/qt/window.cc +++ b/selfdrive/ui/qt/window.cc @@ -4,16 +4,19 @@ #include "system/hardware/hw.h" -MainWindow::MainWindow(QWidget *parent) : QWidget(parent) { +// We have this constructor so that we can provide custom implementations of the windows. By default (stock_ui) would receive them as nullptr, so they'll be instantiated with stock. Otherwise they'd be SP instances +MainWindow::MainWindow(QWidget *parent, HomeWindow *hw, SettingsWindow *sw) : + QWidget(parent), + homeWindow(hw ? hw : new HomeWindow(this)), + settingsWindow(sw ? sw : new SettingsWindow(this)) { + main_layout = new QStackedLayout(this); main_layout->setMargin(0); - homeWindow = new HomeWindow(this); main_layout->addWidget(homeWindow); QObject::connect(homeWindow, &HomeWindow::openSettings, this, &MainWindow::openSettings); QObject::connect(homeWindow, &HomeWindow::closeSettings, this, &MainWindow::closeSettings); - settingsWindow = new SettingsWindow(this); main_layout->addWidget(settingsWindow); QObject::connect(settingsWindow, &SettingsWindow::closeSettings, this, &MainWindow::closeSettings); QObject::connect(settingsWindow, &SettingsWindow::reviewTrainingGuide, [=]() { diff --git a/selfdrive/ui/qt/window.h b/selfdrive/ui/qt/window.h index 05b61e1f76..8a118b0bb6 100644 --- a/selfdrive/ui/qt/window.h +++ b/selfdrive/ui/qt/window.h @@ -11,15 +11,18 @@ class MainWindow : public QWidget { Q_OBJECT public: - explicit MainWindow(QWidget *parent = 0); + explicit MainWindow(QWidget *parent = 0) : MainWindow(parent, nullptr, nullptr) {} + +protected: + explicit MainWindow(QWidget *parent, HomeWindow *hw = nullptr, SettingsWindow *sw = nullptr); + HomeWindow *homeWindow; + SettingsWindow *settingsWindow; + virtual void closeSettings(); private: bool eventFilter(QObject *obj, QEvent *event) override; void openSettings(int index = 0, const QString ¶m = ""); - void closeSettings(); QStackedLayout *main_layout; - HomeWindow *homeWindow; - SettingsWindow *settingsWindow; OnboardingWindow *onboardingWindow; }; diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript new file mode 100644 index 0000000000..efbe6a8e4a --- /dev/null +++ b/selfdrive/ui/sunnypilot/SConscript @@ -0,0 +1,19 @@ +widgets_src = [ + "sunnypilot/qt/widgets/toggle.cc", + "sunnypilot/qt/widgets/controls.cc", + "sunnypilot/qt/widgets/scrollview.cc", +] + +qt_src = [ + "sunnypilot/ui.cc", + "sunnypilot/qt/sidebar.cc", + "sunnypilot/qt/window.cc", + "sunnypilot/qt/home.cc", + "sunnypilot/qt/offroad/settings/settings.cc", + "sunnypilot/qt/onroad/onroad_home.cc", +] + +sp_widgets_src = widgets_src +sp_qt_src = qt_src + +Export('sp_widgets_src', 'sp_qt_src') diff --git a/selfdrive/ui/sunnypilot/qt/home.cc b/selfdrive/ui/sunnypilot/qt/home.cc new file mode 100644 index 0000000000..2fd4dcbbee --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/home.cc @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/home.h" + +HomeWindowSP::HomeWindowSP(QWidget *parent) : HomeWindow(parent) { +} + +void HomeWindowSP::updateState(const UIState &s) { + HomeWindow::updateState(s); +} + +void HomeWindowSP::mousePressEvent(QMouseEvent *e) { + HomeWindow::mousePressEvent(e); +} diff --git a/selfdrive/ui/sunnypilot/qt/home.h b/selfdrive/ui/sunnypilot/qt/home.h new file mode 100644 index 0000000000..4cf532d14c --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/home.h @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include +#include + +#include "common/params.h" +#include "selfdrive/ui/qt/body.h" +#include "selfdrive/ui/qt/widgets/offroad_alerts.h" +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/qt/home.h" + +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/sidebar.h" +#define OnroadWindow OnroadWindowSP +#else +#include "selfdrive/ui/qt/sidebar.h" +#include "selfdrive/ui/qt/onroad/onroad_home.h" +#endif + +class HomeWindowSP : public HomeWindow { + Q_OBJECT + +public: + explicit HomeWindowSP(QWidget *parent = 0); + +protected: + void mousePressEvent(QMouseEvent *e) override; + +private slots: + void updateState(const UIState &s) override; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc new file mode 100644 index 0000000000..3a66101a82 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc @@ -0,0 +1,146 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" + +#include "selfdrive/ui/qt/network/networking.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" +#include "selfdrive/ui/qt/offroad/developer_panel.h" + +TogglesPanelSP::TogglesPanelSP(SettingsWindow *parent) : TogglesPanel(parent) { + QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &TogglesPanelSP::updateState); +} + +void TogglesPanelSP::updateState(const UIStateSP &s) { + TogglesPanel::updateState(s); +} + +SettingsWindowSP::SettingsWindowSP(QWidget *parent) : SettingsWindow(parent) { + // setup two main layouts + sidebar_widget = new QWidget; + QVBoxLayout *sidebar_layout = new QVBoxLayout(sidebar_widget); + panel_widget = new QStackedWidget(); + + // setup layout for close button + QVBoxLayout *close_btn_layout = new QVBoxLayout; + close_btn_layout->setContentsMargins(0, 0, 0, 20); + + // close button + QPushButton *close_btn = new QPushButton(tr("×")); + close_btn->setStyleSheet(R"( + QPushButton { + font-size: 140px; + padding-bottom: 20px; + border-radius: 76px; + background-color: #292929; + font-weight: 400; + } + QPushButton:pressed { + background-color: #3B3B3B; + } + )"); + close_btn->setFixedSize(152, 152); + close_btn_layout->addWidget(close_btn, 0, Qt::AlignLeft); + QObject::connect(close_btn, &QPushButton::clicked, this, &SettingsWindow::closeSettings); + + // setup buttons widget + QWidget *buttons_widget = new QWidget; + QVBoxLayout *buttons_layout = new QVBoxLayout(buttons_widget); + buttons_layout->setMargin(0); + buttons_layout->addSpacing(10); + + // setup panels + DevicePanel *device = new DevicePanel(this); + QObject::connect(device, &DevicePanel::reviewTrainingGuide, this, &SettingsWindow::reviewTrainingGuide); + + TogglesPanelSP *toggles = new TogglesPanelSP(this); + QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription); + + auto networking = new Networking(this); + QObject::connect(uiState()->prime_state, &PrimeState::changed, networking, &Networking::setPrimeType); + + QList panels = { + PanelInfo(" " + tr("Device"), device, "../../sunnypilot/selfdrive/assets/offroad/icon_home.svg"), + PanelInfo(" " + tr("Network"), networking, "../assets/offroad/icon_network.png"), + PanelInfo(" " + tr("Toggles"), toggles, "../../sunnypilot/selfdrive/assets/offroad/icon_toggle.png"), + PanelInfo(" " + tr("Software"), new SoftwarePanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_software.png"), + PanelInfo(" " + tr("Developer"), new DeveloperPanel(this), "../assets/offroad/icon_shell.png"), + }; + + nav_btns = new QButtonGroup(this); + for (auto &[name, panel, icon] : panels) { + QPushButton *btn = new QPushButton(name); + btn->setCheckable(true); + btn->setChecked(nav_btns->buttons().size() == 0); + btn->setIcon(QIcon(QPixmap(icon))); + btn->setIconSize(QSize(70, 70)); + btn->setStyleSheet(R"( + QPushButton { + border-radius: 20px; + width: 400px; + height: 98px; + color: #bdbdbd; + border: none; + background: none; + font-size: 50px; + font-weight: 500; + text-align: left; + padding-left: 22px; + } + QPushButton:checked { + background-color: #696868; + color: white; + } + QPushButton:pressed { + color: #ADADAD; + } + )"); + btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + nav_btns->addButton(btn); + buttons_layout->addWidget(btn, 0, Qt::AlignLeft | Qt::AlignBottom); + + const int lr_margin = (name != (" " + tr("Network")) || (name != (" " + tr("sunnypilot")))) ? 50 : 0; // Network and sunnypilot panel handles its own margins + panel->setContentsMargins(lr_margin, 25, lr_margin, 25); + + ScrollViewSP *panel_frame = new ScrollViewSP(panel, this); + panel_widget->addWidget(panel_frame); + + QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() { + btn->setChecked(true); + panel_widget->setCurrentWidget(w); + }); + } + sidebar_layout->setContentsMargins(50, 50, 25, 50); + + // main settings layout, sidebar + main panel + QHBoxLayout *main_layout = new QHBoxLayout(this); + + // add layout for close button + sidebar_layout->addLayout(close_btn_layout); + + // add layout for buttons scrolling + ScrollViewSP *buttons_scrollview = new ScrollViewSP(buttons_widget, this); + sidebar_layout->addWidget(buttons_scrollview); + + sidebar_widget->setFixedWidth(500); + main_layout->addWidget(sidebar_widget); + main_layout->addWidget(panel_widget); + + setStyleSheet(R"( + * { + color: white; + font-size: 50px; + } + SettingsWindow { + background-color: black; + } + QStackedWidget, ScrollViewSP { + background-color: black; + border-radius: 30px; + } + )"); +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h new file mode 100644 index 0000000000..5678a3171b --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include +#include + +#include "selfdrive/ui/qt/offroad/settings.h" + +class SettingsWindowSP : public SettingsWindow { + Q_OBJECT + +public: + explicit SettingsWindowSP(QWidget *parent = nullptr); + +protected: + struct PanelInfo { + QString name; + QWidget *widget; + QString icon; + + PanelInfo(const QString &name, QWidget *widget, const QString &icon) : name(name), widget(widget), icon(icon) {} + }; +}; + +class TogglesPanelSP : public TogglesPanel { + Q_OBJECT + +public: + explicit TogglesPanelSP(SettingsWindow *parent); + +private slots: + void updateState(const UIStateSP &s); +}; diff --git a/selfdrive/ui/sunnypilot/qt/onroad/onroad_home.cc b/selfdrive/ui/sunnypilot/qt/onroad/onroad_home.cc new file mode 100644 index 0000000000..b26d1b828a --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/onroad/onroad_home.cc @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/onroad/onroad_home.h" + +#include "common/swaglog.h" +#include "selfdrive/ui/qt/util.h" + +OnroadWindowSP::OnroadWindowSP(QWidget *parent) : OnroadWindow(parent) { + QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &OnroadWindowSP::updateState); + QObject::connect(uiStateSP(), &UIStateSP::offroadTransition, this, &OnroadWindowSP::offroadTransition); +} + +void OnroadWindowSP::updateState(const UIStateSP &s) { + if (!s.scene.started) { + return; + } + + OnroadWindow::updateState(s); +} + +void OnroadWindowSP::mousePressEvent(QMouseEvent *e) { + OnroadWindow::mousePressEvent(e); +} + +void OnroadWindowSP::offroadTransition(bool offroad) { + OnroadWindow::offroadTransition(offroad); +} diff --git a/selfdrive/ui/sunnypilot/qt/onroad/onroad_home.h b/selfdrive/ui/sunnypilot/qt/onroad/onroad_home.h new file mode 100644 index 0000000000..193fdae0dc --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/onroad/onroad_home.h @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/qt/onroad/onroad_home.h" + +class OnroadWindowSP : public OnroadWindow { + Q_OBJECT + +public: + OnroadWindowSP(QWidget *parent = 0); + +private: + void mousePressEvent(QMouseEvent *e) override; + +protected slots: + void offroadTransition(bool offroad) override; + void updateState(const UIStateSP &s) override; +}; diff --git a/selfdrive/ui/sunnypilot/qt/sidebar.cc b/selfdrive/ui/sunnypilot/qt/sidebar.cc new file mode 100644 index 0000000000..af090e738f --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/sidebar.cc @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/sidebar.h" + +#include +#include + +#include "selfdrive/ui/qt/util.h" + +SidebarSP::SidebarSP(QWidget *parent) : Sidebar(parent) { + QObject::disconnect(uiState(), &UIState::uiUpdate, this, &Sidebar::updateState); + QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &SidebarSP::updateState); +} + +void SidebarSP::updateState(const UIStateSP &s) { + if (!isVisible()) return; + Sidebar::updateState(s); +} + +void SidebarSP::paintSidebar(QPainter &p) { + Sidebar::paintSidebar(p); +} diff --git a/selfdrive/ui/sunnypilot/qt/sidebar.h b/selfdrive/ui/sunnypilot/qt/sidebar.h new file mode 100644 index 0000000000..ff9b78748f --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/sidebar.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include + +#include "selfdrive/ui/qt/sidebar.h" + +#include "selfdrive/ui/sunnypilot/ui.h" + +class SidebarSP : public Sidebar { + Q_OBJECT + +public slots: + void updateState(const UIStateSP &s); + +public: + explicit SidebarSP(QWidget *parent = 0); + +private: + void paintSidebar(QPainter &p) override; +}; diff --git a/selfdrive/ui/sunnypilot/qt/widgets/controls.cc b/selfdrive/ui/sunnypilot/qt/widgets/controls.cc new file mode 100644 index 0000000000..0c0e174001 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/widgets/controls.cc @@ -0,0 +1,254 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +#include +#include + +QFrame *horizontal_line(QWidget *parent) { + QFrame *line = new QFrame(parent); + line->setFrameShape(QFrame::StyledPanel); + line->setStyleSheet(R"( + border-width: 2px; + border-bottom-style: solid; + border-color: gray; + )"); + line->setFixedHeight(10); + return line; +} + +// AbstractControlSP + +AbstractControlSP::AbstractControlSP(const QString &title, const QString &desc, const QString &icon, QWidget *parent) + : AbstractControl(title, desc, icon, parent) { + + main_layout = new QVBoxLayout(this); + main_layout->setMargin(0); + + hlayout = new QHBoxLayout; + hlayout->setMargin(0); + hlayout->setSpacing(20); + + // title + title_label = new QPushButton(title); + title_label->setFixedHeight(120); + title_label->setStyleSheet("font-size: 50px; font-weight: 450; text-align: left; border: none;"); + hlayout->addWidget(title_label, 1); + + // value next to control button + value = new ElidedLabelSP(); + value->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + value->setStyleSheet("color: #aaaaaa"); + hlayout->addWidget(value); + + main_layout->addLayout(hlayout); + + // description + description = new QLabel(desc); + description->setContentsMargins(40, 20, 40, 20); + description->setStyleSheet("font-size: 40px; color: grey"); + description->setWordWrap(true); + description->setVisible(false); + main_layout->addWidget(description); + + connect(title_label, &QPushButton::clicked, [=]() { + if (!description->isVisible()) { + emit showDescriptionEvent(); + } + + if (!description->text().isEmpty()) { + description->setVisible(!description->isVisible()); + } + }); + + main_layout->addStretch(); +} + +void AbstractControlSP::hideEvent(QHideEvent *e) { + if (description != nullptr) { + description->hide(); + } +} + +AbstractControlSP_SELECTOR::AbstractControlSP_SELECTOR(const QString &title, const QString &desc, const QString &icon, QWidget *parent) + : AbstractControlSP(title, desc, icon, parent) { + + if (title_label != nullptr) { + delete title_label; + title_label = nullptr; + } + + if (description != nullptr) { + delete description; + description = nullptr; + } + + if (value != nullptr) { + ReplaceWidget(value, new QWidget()); + value = nullptr; + } + + QLayoutItem *item; + while ((item = main_layout->takeAt(0)) != nullptr) { + if (item->widget()) { + delete item->widget(); + } + delete item; + } + + main_layout->setMargin(0); + + hlayout = new QHBoxLayout; + hlayout->setMargin(0); + hlayout->setSpacing(0); + + // title + if (!title.isEmpty()) { + title_label = new QPushButton(title); + title_label->setFixedHeight(120); + title_label->setStyleSheet("font-size: 50px; font-weight: 450; text-align: left; border: none; padding: 20 0 0 0"); + main_layout->addWidget(title_label, 1); + + connect(title_label, &QPushButton::clicked, [=]() { + if (!description->isVisible()) { + emit showDescriptionEvent(); + } + + if (!description->text().isEmpty()) { + bool isVisible = !description->isVisible(); + description->setVisible(isVisible); + + if (isVisible && spacingItem) { + main_layout->removeItem(spacingItem); + delete spacingItem; + spacingItem = nullptr; + } else if (!isVisible && spacingItem == nullptr) { + spacingItem = new QSpacerItem(44, 44, QSizePolicy::Minimum, QSizePolicy::Fixed); + main_layout->insertItem(main_layout->indexOf(description), spacingItem); + } + } + }); + } else { + main_layout->addSpacing(20); + } + + main_layout->addLayout(hlayout); + if (!desc.isEmpty() && spacingItem == nullptr) { + spacingItem = new QSpacerItem(44, 44, QSizePolicy::Minimum, QSizePolicy::Fixed); + main_layout->insertItem(main_layout->count(), spacingItem); + } + + // description + description = new QLabel(desc); + description->setContentsMargins(0, 20, 40, 20); + description->setStyleSheet("font-size: 40px; color: grey"); + description->setWordWrap(true); + description->setVisible(false); + main_layout->addWidget(description); + + main_layout->addStretch(); +} + +void AbstractControlSP_SELECTOR::hideEvent(QHideEvent *e) { + if (description != nullptr) { + description->hide(); + } + + if (spacingItem == nullptr) { + spacingItem = new QSpacerItem(44, 44, QSizePolicy::Minimum, QSizePolicy::Fixed); + main_layout->insertItem(main_layout->indexOf(description), spacingItem); + } +} + +// controls + +ButtonControlSP::ButtonControlSP(const QString &title, const QString &text, const QString &desc, QWidget *parent) + : AbstractControlSP(title, desc, "", parent) { + + btn.setText(text); + btn.setStyleSheet(R"( + QPushButton { + padding: 0; + border-radius: 50px; + font-size: 35px; + font-weight: 500; + color: #E4E4E4; + background-color: #393939; + } + QPushButton:pressed { + background-color: #4a4a4a; + } + QPushButton:disabled { + color: #33E4E4E4; + } + )"); + btn.setFixedSize(250, 100); + QObject::connect(&btn, &QPushButton::clicked, this, &ButtonControlSP::clicked); + hlayout->addWidget(&btn); +} + +// ElidedLabelSP + +ElidedLabelSP::ElidedLabelSP(QWidget *parent) : ElidedLabelSP({}, parent) { +} + +ElidedLabelSP::ElidedLabelSP(const QString &text, QWidget *parent) : QLabel(text.trimmed(), parent) { + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + setMinimumWidth(1); +} + +void ElidedLabelSP::resizeEvent(QResizeEvent *event) { + QLabel::resizeEvent(event); + lastText_ = elidedText_ = ""; +} + +void ElidedLabelSP::paintEvent(QPaintEvent *event) { + const QString curText = text(); + if (curText != lastText_) { + elidedText_ = fontMetrics().elidedText(curText, Qt::ElideRight, contentsRect().width()); + lastText_ = curText; + } + + QPainter painter(this); + drawFrame(&painter); + QStyleOption opt; + opt.initFrom(this); + style()->drawItemText(&painter, contentsRect(), alignment(), opt.palette, isEnabled(), elidedText_, foregroundRole()); +} + +// ParamControlSP + +ParamControlSP::ParamControlSP(const QString ¶m, const QString &title, const QString &desc, const QString &icon, QWidget *parent) + : ToggleControlSP(title, desc, icon, false, parent) { + + key = param.toStdString(); + QObject::connect(this, &ParamControlSP::toggleFlipped, this, &ParamControlSP::toggleClicked); + + hlayout->removeWidget(&toggle); + hlayout->insertWidget(0, &toggle); + + hlayout->removeWidget(this->icon_label); + hlayout->insertWidget(1, this->icon_label); +} + +void ParamControlSP::toggleClicked(bool state) { + auto do_confirm = [this]() { + QString content("

" + title_label->text() + "


" + "

" + getDescription() + "

"); + return ConfirmationDialog(content, tr("Enable"), tr("Cancel"), true, this).exec(); + }; + + bool confirmed = store_confirm && params.getBool(key + "Confirmed"); + if (!confirm || confirmed || !state || do_confirm()) { + if (store_confirm && state) params.putBool(key + "Confirmed", true); + params.putBool(key, state); + setIcon(state); + } else { + toggle.togglePosition(); + } +} diff --git a/selfdrive/ui/sunnypilot/qt/widgets/controls.h b/selfdrive/ui/sunnypilot/qt/widgets/controls.h new file mode 100644 index 0000000000..a5071d97e7 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/widgets/controls.h @@ -0,0 +1,588 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include +#include +#include +#include + +#include "common/params.h" +#include "selfdrive/ui/qt/widgets/controls.h" +#include "selfdrive/ui/qt/widgets/input.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/toggle.h" + +QFrame *horizontal_line(QWidget *parent = nullptr); + +inline void ReplaceWidget(QWidget *old_widget, QWidget *new_widget) { + if (old_widget && old_widget->parentWidget() && old_widget->parentWidget()->layout()) { + old_widget->parentWidget()->layout()->replaceWidget(old_widget, new_widget); + old_widget->hide(); + old_widget->deleteLater(); + } +} + +class ElidedLabelSP : public QLabel { + Q_OBJECT + +public: + explicit ElidedLabelSP(QWidget *parent = 0); + explicit ElidedLabelSP(const QString &text, QWidget *parent = 0); + + void setColor(const QString &color) { + setStyleSheet("QLabel { color : " + color + "; }"); + } + +signals: + void clicked(); + +protected: + void paintEvent(QPaintEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override { + if (rect().contains(event->pos())) { + emit clicked(); + } + } + QString lastText_, elidedText_; +}; + +class AbstractControlSP : public AbstractControl { + Q_OBJECT + +public: + void setDescription(const QString &desc) override { + if (description) description->setText(desc); + } + + void setValue(const QString &val, std::optional color = std::nullopt) { + value->setText(val); + if (color.has_value()) { + value->setColor(color.value()); + } + } + + const QString getDescription() override { + return description->text(); + } + + void hideDescription() { + description->setVisible(false); + } + +public slots: + void showDescription() override { + description->setVisible(true); + } + +protected: + AbstractControlSP(const QString &title, const QString &desc = "", const QString &icon = "", QWidget *parent = nullptr); + void hideEvent(QHideEvent *e) override; + + QVBoxLayout *main_layout; + ElidedLabelSP *value; + QLabel *description = nullptr; +}; + +// AbstractControlSP_SELECTOR + +class AbstractControlSP_SELECTOR : public AbstractControlSP { + Q_OBJECT + +protected: + AbstractControlSP_SELECTOR(const QString &title, const QString &desc = "", const QString &icon = "", QWidget *parent = nullptr); + void hideEvent(QHideEvent *e) override; + +private: + QSpacerItem *spacingItem = nullptr; +}; + +// widget to display a value +class LabelControlSP : public AbstractControlSP { + Q_OBJECT + +public: + LabelControlSP(const QString &title, const QString &text = "", const QString &desc = "", QWidget *parent = nullptr) : AbstractControlSP(title, desc, "", parent) { + label.setText(text); + label.setAlignment(Qt::AlignRight | Qt::AlignVCenter); + hlayout->addWidget(&label); + } + void setText(const QString &text) { label.setText(text); } + +private: + ElidedLabelSP label; +}; + +// widget for a button with a label +class ButtonControlSP : public AbstractControlSP { + Q_OBJECT + +public: + ButtonControlSP(const QString &title, const QString &text, const QString &desc = "", QWidget *parent = nullptr); + inline void setText(const QString &text) { btn.setText(text); } + inline QString text() const { return btn.text(); } + inline void click() { btn.click(); } + +signals: + void clicked(); + +public slots: + void setEnabled(bool enabled) { btn.setEnabled(enabled); } + +private: + QPushButton btn; +}; + +class ToggleControlSP : public AbstractControlSP { + Q_OBJECT + +public: + ToggleControlSP(const QString &title, const QString &desc = "", const QString &icon = "", const bool state = false, QWidget *parent = nullptr) : AbstractControlSP(title, desc, icon, parent) { + // space between toggle and title + icon_label = new QLabel(this); + hlayout->addWidget(icon_label); + + toggle.setFixedSize(150, 100); + if (state) { + toggle.togglePosition(); + } + hlayout->insertWidget(0, &toggle); + hlayout->insertWidget(1, this->icon_label); + QObject::connect(&toggle, &ToggleSP::stateChanged, this, &ToggleControlSP::toggleFlipped); + } + + void setEnabled(bool enabled) { + toggle.setEnabled(enabled); + toggle.update(); + } + +signals: + void toggleFlipped(bool state); + +protected: + ToggleSP toggle; +}; + +// widget to toggle params +class ParamControlSP : public ToggleControlSP { + Q_OBJECT + +public: + ParamControlSP(const QString ¶m, const QString &title, const QString &desc, const QString &icon, QWidget *parent = nullptr); + void setConfirmation(bool _confirm, bool _store_confirm) { + confirm = _confirm; + store_confirm = _store_confirm; + } + + void setActiveIcon(const QString &icon) { + active_icon_pixmap = QPixmap(icon).scaledToWidth(80, Qt::SmoothTransformation); + } + + void refresh() { + bool state = params.getBool(key); + if (state != toggle.on) { + toggle.togglePosition(); + setIcon(state); + } + } + + void showEvent(QShowEvent *event) override { + refresh(); + } + + bool isToggled() { return params.getBool(key); } + +private: + void toggleClicked(bool state); + void setIcon(bool state) { + if (state && !active_icon_pixmap.isNull()) { + icon_label->setPixmap(active_icon_pixmap); + } else if (!icon_pixmap.isNull()) { + icon_label->setPixmap(icon_pixmap); + } + } + + std::string key; + Params params; + QPixmap active_icon_pixmap; + bool confirm = false; + bool store_confirm = false; +}; + +class ButtonParamControlSP : public AbstractControlSP_SELECTOR { + Q_OBJECT + +public: + ButtonParamControlSP(const QString ¶m, const QString &title, const QString &desc, const QString &icon, + const std::vector &button_texts, const int minimum_button_width = 300) : AbstractControlSP_SELECTOR(title, desc, icon), button_texts(button_texts) { + const QString style = R"( + QPushButton { + border-radius: 20px; + font-size: 50px; + font-weight: 450; + height:150px; + padding: 0 25 0 25; + color: #FFFFFF; + } + QPushButton:pressed { + background-color: #4a4a4a; + } + QPushButton:checked:enabled { + background-color: #696868; + } + QPushButton:disabled { + color: #33FFFFFF; + } + QPushButton:checked:disabled { + background-color: #121212; + color: #33FFFFFF; + } + )"; + key = param.toStdString(); + int value = atoi(params.get(key).c_str()); + + button_group = new QButtonGroup(this); + button_group->setExclusive(true); + for (int i = 0; i < button_texts.size(); i++) { + QPushButton *button = new QPushButton(button_texts[i], this); + button->setCheckable(true); + button->setChecked(i == value); + button->setStyleSheet(style); + button->setMinimumWidth(minimum_button_width); + if (i == 0) hlayout->addSpacing(2); + hlayout->addWidget(button); + button_group->addButton(button, i); + } + + hlayout->setAlignment(Qt::AlignLeft); + + QObject::connect(button_group, QOverload::of(&QButtonGroup::buttonClicked), [=](int id) { + params.put(key, std::to_string(id)); + emit buttonToggled(id); + }); + } + + void setEnabled(bool enable) { + for (auto btn: button_group->buttons()) { + btn->setEnabled(enable); + } + button_group_enabled = enable; + + update(); + } + + void setCheckedButton(int id) { + button_group->button(id)->setChecked(true); + } + + void refresh() { + int value = atoi(params.get(key).c_str()); + + if (value >= button_texts.size()) { + value = button_texts.size() - 1; + } + if (value < 0) { + value = 0; + } + + button_group->button(value)->setChecked(true); + } + + void showEvent(QShowEvent *event) override { + refresh(); + } + + void setButton(QString param) { + key = param.toStdString(); + int value = atoi(params.get(key).c_str()); + for (int i = 0; i < button_group->buttons().size(); i++) { + button_group->buttons()[i]->setChecked(i == value); + } + } + + void setDisabledSelectedButton(std::string val) { + int value = atoi(val.c_str()); + for (int i = 0; i < button_group->buttons().size(); i++) { + button_group->buttons()[i]->setEnabled(i != value); + } + } + +protected: + void paintEvent(QPaintEvent *event) override { + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + + // Calculate the total width and height for the background rectangle + int w = 0; + int h = 150; + + for (int i = 0; i < hlayout->count(); ++i) { + QPushButton *button = qobject_cast(hlayout->itemAt(i)->widget()); + if (button) { + w += button->width(); + } + } + + // Draw the rectangle +#ifdef __APPLE__ + QRect rect(0 + 2, h - 16, w, h); +#else + QRect rect(0 + 2, h - 24, w, h); +#endif + p.setPen(QPen(QColor(button_group_enabled ? "#696868" : "#121212"), 3)); + p.drawRoundedRect(rect, 20, 20); + } + +signals: + void buttonToggled(int btn_id); + +private: + std::string key; + Params params; + QButtonGroup *button_group; + std::vector button_texts; + + bool button_group_enabled = true; +}; + +class ListWidgetSP : public QWidget { + Q_OBJECT + +public: + explicit ListWidgetSP(QWidget *parent = 0, const bool split_line = true) : QWidget(parent), _split_line(split_line), outer_layout(this) { + outer_layout.setMargin(0); + outer_layout.setSpacing(0); + outer_layout.addLayout(&inner_layout); + inner_layout.setMargin(0); + inner_layout.setSpacing(25); // default spacing is 25 + outer_layout.addStretch(); + } + inline void addItem(QWidget *w) { inner_layout.addWidget(w); } + inline void addItem(QLayout *layout) { inner_layout.addLayout(layout); } + inline void setSpacing(int spacing) { inner_layout.setSpacing(spacing); } + + inline void AddWidgetAt(const int index, QWidget *new_widget) { inner_layout.insertWidget(index, new_widget); } + inline void RemoveWidgetAt(const int index) { + if (QLayoutItem *item; (item = inner_layout.takeAt(index)) != nullptr) { + if (item->widget()) delete item->widget(); + delete item; + } + } + + inline void ReplaceOrAddWidget(QWidget *old_widget, QWidget *new_widget) { + if (const int index = inner_layout.indexOf(old_widget); index != -1) { + RemoveWidgetAt(index); + AddWidgetAt(index, new_widget); + } else { + addItem(new_widget); + } + } + +private: + void paintEvent(QPaintEvent *) override { + QPainter p(this); + p.setPen(Qt::gray); + for (int i = 0; i < inner_layout.count() - 1; ++i) { + QWidget *widget = inner_layout.itemAt(i)->widget(); + if ((widget == nullptr || widget->isVisible()) && _split_line) { + QRect r = inner_layout.itemAt(i)->geometry(); + int bottom = r.bottom() + inner_layout.spacing() / 2; + p.drawLine(r.left(), bottom, r.right(), bottom); + } + } + } + QVBoxLayout outer_layout; + QVBoxLayout inner_layout; + + bool _split_line; +}; + +// convenience class for wrapping layouts +class LayoutWidgetSP : public QWidget { + Q_OBJECT + +public: + LayoutWidgetSP(QLayout *l, QWidget *parent = nullptr) : QWidget(parent) { + setLayout(l); + } +}; + +class OptionControlSP : public AbstractControlSP_SELECTOR { + Q_OBJECT + +private: + struct MinMaxValue { + int min_value; + int max_value; + }; + +public: + OptionControlSP(const QString ¶m, const QString &title, const QString &desc, const QString &icon, + const MinMaxValue &range, const int per_value_change = 1) : _title(title), AbstractControlSP_SELECTOR(title, desc, icon) { + const QString style = R"( + QPushButton { + border-radius: 20px; + font-size: 60px; + font-weight: 500; + width: 150px; + height: 150px; + padding: -3 25 3 25; + color: #FFFFFF; + font-weight: bold; + } + QPushButton:pressed { + color: #5C5C5C; + } + QPushButton:disabled { + color: #5C5C5C; + } + )"; + + label.setStyleSheet(label_enabled_style); + label.setFixedWidth(300); + label.setAlignment(Qt::AlignCenter); + + const std::vector button_texts{"-", "+"}; + + key = param.toStdString(); + value = atoi(params.get(key).c_str()); + + button_group = new QButtonGroup(this); + button_group->setExclusive(true); + for (int i = 0; i < button_texts.size(); i++) { + QPushButton *button = new QPushButton(button_texts[i], this); + button->setStyleSheet(style + ((i == 0) ? "QPushButton { text-align: left; }" : + "QPushButton { text-align: right; }")); + hlayout->addWidget(button, 0, ((i == 0) ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignVCenter); + if (i == 0) { + hlayout->addWidget(&label, 0, Qt::AlignCenter); + } + button_group->addButton(button, i); + + QObject::connect(button, &QPushButton::clicked, [=]() { + int change_value = (i == 0) ? -per_value_change : per_value_change; + key = param.toStdString(); + value = atoi(params.get(key).c_str()); + value += change_value; + value = std::clamp(value, range.min_value, range.max_value); + params.put(key, QString::number(value).toStdString()); + + button_group->button(0)->setEnabled(!(value <= range.min_value)); + button_group->button(1)->setEnabled(!(value >= range.max_value)); + + updateLabels(); + + if (request_update) { + emit updateOtherToggles(); + } + }); + } + + hlayout->setAlignment(Qt::AlignLeft); + } + + void setUpdateOtherToggles(bool _update) { + request_update = _update; + } + + inline void setLabel(const QString &text) { label.setText(text); } + + void setEnabled(bool enabled) { + for (auto btn: button_group->buttons()) { + btn->setEnabled(enabled); + } + label.setEnabled(enabled); + label.setStyleSheet(enabled ? label_enabled_style : label_disabled_style); + button_enabled = enabled; + + update(); + } + +protected: + void paintEvent(QPaintEvent *event) override { + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + + // Calculate the total width and height for the background rectangle + int w = 0; + int h = 150; + + for (int i = 0; i < hlayout->count(); ++i) { + QWidget *widget = qobject_cast(hlayout->itemAt(i)->widget()); + if (widget) { + w += widget->width(); + } + } + + // Draw the rectangle + QRect rect(0, !_title.isEmpty() ? (h - 24) : 20, w, h); + p.setBrush(QColor(button_enabled ? "#b24a4a4a" : "#121212")); // Background color + p.setPen(QPen(Qt::NoPen)); + p.drawRoundedRect(rect, 20, 20); + } + +signals: + void updateLabels(); + void updateOtherToggles(); + +private: + std::string key; + int value; + QButtonGroup *button_group; + QLabel label; + Params params; + std::map option_label = {}; + bool request_update = false; + QString _title = ""; + + const QString label_enabled_style = "font-size: 50px; font-weight: 450; color: #FFFFFF;"; + const QString label_disabled_style = "font-size: 50px; font-weight: 450; color: #5C5C5C;"; + + bool button_enabled = true; +}; + +class SubPanelButton : public QPushButton { + Q_OBJECT + +public: + SubPanelButton(const QString &text, const int minimum_button_width = 800, QWidget *parent = nullptr) : QPushButton(text, parent) { + const QString buttonStyle = R"( + QPushButton { + border-radius: 20px; + font-size: 50px; + font-weight: 450; + height: 150px; + padding: 0 25px 0 25px; + color: #FFFFFF; + } + QPushButton:enabled { + background-color: #393939; + } + QPushButton:pressed { + background-color: #4A4A4A; + } + QPushButton:disabled { + background-color: #121212; + color: #5C5C5C; + } + )"; + + setStyleSheet(buttonStyle); + setFixedWidth(minimum_button_width); + } +}; + +class PanelBackButton : public QPushButton { + Q_OBJECT + +public: + PanelBackButton(const QString &label = "Back", QWidget *parent = nullptr) : QPushButton(label, parent) { + setObjectName("back_btn"); + setFixedSize(400, 100); + } +}; diff --git a/selfdrive/ui/sunnypilot/qt/widgets/scrollview.cc b/selfdrive/ui/sunnypilot/qt/widgets/scrollview.cc new file mode 100644 index 0000000000..49f6e93430 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/widgets/scrollview.cc @@ -0,0 +1,18 @@ +/** +* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +#include + +void ScrollViewSP::setLastScrollPosition() { + lastScrollPosition = verticalScrollBar()->value(); +} + +void ScrollViewSP::restoreScrollPosition() { + verticalScrollBar()->setValue(lastScrollPosition); +} diff --git a/selfdrive/ui/sunnypilot/qt/widgets/scrollview.h b/selfdrive/ui/sunnypilot/qt/widgets/scrollview.h new file mode 100644 index 0000000000..5c20bce2f2 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/widgets/scrollview.h @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/qt/widgets/scrollview.h" + +class ScrollViewSP : public ScrollView { + Q_OBJECT + +public: + explicit ScrollViewSP(QWidget *w = nullptr, QWidget *parent = nullptr) : ScrollView(w, parent) {} + +public slots: + void setLastScrollPosition(); + void restoreScrollPosition(); + +private: + int lastScrollPosition = 0; +}; diff --git a/selfdrive/ui/sunnypilot/qt/widgets/toggle.cc b/selfdrive/ui/sunnypilot/qt/widgets/toggle.cc new file mode 100644 index 0000000000..601c8f94c1 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/widgets/toggle.cc @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/widgets/toggle.h" + +#include + +ToggleSP::ToggleSP(QWidget *parent) : Toggle(parent) { + _height_rect = 80; +} + +void ToggleSP::paintEvent(QPaintEvent *e) { + this->setFixedHeight(100); + QPainter p(this); + p.setPen(Qt::NoPen); + p.setRenderHint(QPainter::Antialiasing, true); + + // Draw toggle background + enabled ? green.setRgb(0x1e79e8) : green.setRgb(0x125db8); + p.setBrush(on ? green : QColor(0x292929)); + p.drawRoundedRect(QRect(0, 10, width(), _height_rect), _height_rect / 2, _height_rect / 2); + + // Draw toggle circle + p.setBrush(circleColor); + p.drawEllipse(QRectF(_x_circle - _radius + 6, 16, 68, 68)); +} diff --git a/selfdrive/ui/sunnypilot/qt/widgets/toggle.h b/selfdrive/ui/sunnypilot/qt/widgets/toggle.h new file mode 100644 index 0000000000..e98dea39af --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/widgets/toggle.h @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/qt/widgets/toggle.h" + +class ToggleSP : public Toggle { + Q_OBJECT + +public: + explicit ToggleSP(QWidget *parent = nullptr); + +protected: + void paintEvent(QPaintEvent *) override; +}; diff --git a/selfdrive/ui/sunnypilot/qt/window.cc b/selfdrive/ui/sunnypilot/qt/window.cc new file mode 100644 index 0000000000..f332e1ed8e --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/window.cc @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/window.h" + +MainWindowSP::MainWindowSP(QWidget *parent) + : MainWindow(parent, new HomeWindowSP(parent), new SettingsWindowSP(parent)) { + + homeWindow = dynamic_cast(MainWindow::homeWindow); + settingsWindow = dynamic_cast(MainWindow::settingsWindow); +} + +void MainWindowSP::closeSettings() { + MainWindow::closeSettings(); +} diff --git a/selfdrive/ui/sunnypilot/qt/window.h b/selfdrive/ui/sunnypilot/qt/window.h new file mode 100644 index 0000000000..d4c0900901 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/window.h @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/qt/window.h" +#include "selfdrive/ui/sunnypilot/qt/home.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" + +class MainWindowSP : public MainWindow { + Q_OBJECT + +public: + explicit MainWindowSP(QWidget *parent = 0); + +private: + HomeWindowSP *homeWindow; + SettingsWindowSP *settingsWindow; + void closeSettings() override; +}; diff --git a/selfdrive/ui/sunnypilot/ui.cc b/selfdrive/ui/sunnypilot/ui.cc new file mode 100644 index 0000000000..e869d3b2dd --- /dev/null +++ b/selfdrive/ui/sunnypilot/ui.cc @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/ui.h" + +#include "common/watchdog.h" + +void UIStateSP::updateStatus() { + UIState::updateStatus(); +} + +UIStateSP::UIStateSP(QObject *parent) : UIState(parent) { + sm = std::make_unique(std::vector{ + "modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", + "pandaStates", "carParams", "driverMonitoringState", "carState", "driverStateV2", + "wideRoadCameraState", "managerState", "selfdriveState", "longitudinalPlan", + }); + + // update timer + timer = new QTimer(this); + QObject::connect(timer, &QTimer::timeout, this, &UIStateSP::update); + timer->start(1000 / UI_FREQ); +} + +// This method overrides completely the update method from the parent class intentionally. +void UIStateSP::update() { + update_sockets(this); + update_state(this); + updateStatus(); + + if (sm->frame % UI_FREQ == 0) { + watchdog_kick(nanos_since_boot()); + } + emit uiUpdate(*this); +} + +DeviceSP::DeviceSP(QObject *parent) : Device(parent) { + QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &DeviceSP::update); +} + +UIStateSP *uiStateSP() { + static UIStateSP ui_state; + return &ui_state; +} + +DeviceSP *deviceSP() { + static DeviceSP _device; + return &_device; +} diff --git a/selfdrive/ui/sunnypilot/ui.h b/selfdrive/ui/sunnypilot/ui.h new file mode 100644 index 0000000000..65c9237969 --- /dev/null +++ b/selfdrive/ui/sunnypilot/ui.h @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/ui.h" + +class UIStateSP : public UIState { + Q_OBJECT + +public: + UIStateSP(QObject *parent = 0); + void updateStatus() override; + +signals: + void uiUpdate(const UIStateSP &s); + +private slots: + void update() override; +}; + +UIStateSP *uiStateSP(); +inline UIStateSP *uiState() { return uiStateSP(); }; + +// device management class +class DeviceSP : public Device { + Q_OBJECT + +public: + DeviceSP(QObject *parent = 0); +}; + +DeviceSP *deviceSP(); +inline DeviceSP *device() { return deviceSP(); } diff --git a/selfdrive/ui/tests/test_ui/run.py b/selfdrive/ui/tests/test_ui/run.py index 2c4d8d7037..7ffd66ed9a 100644 --- a/selfdrive/ui/tests/test_ui/run.py +++ b/selfdrive/ui/tests/test_ui/run.py @@ -160,7 +160,7 @@ def setup_offroad_alert(click, pm: PubMaster): # Toggle between settings and home to refresh the offroad alert widget setup_settings_device(click, pm) - click(240, 216) + click(100, 100) def setup_update_available(click, pm: PubMaster): Params().put_bool("UpdateAvailable", True) @@ -170,7 +170,7 @@ def setup_update_available(click, pm: PubMaster): Params().put("UpdaterNewReleaseNotes", release_notes + "\n") setup_settings_device(click, pm) - click(240, 216) + click(100, 100) def setup_pair_device(click, pm: PubMaster): click(1950, 435) diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts index 0b18af4f25..ac5433ff47 100644 --- a/selfdrive/ui/translations/main_ar.ts +++ b/selfdrive/ui/translations/main_ar.ts @@ -480,6 +480,17 @@ إلغاء + + ParamControlSP + + Enable + تمكين + + + Cancel + إلغاء + + PrimeAdWidget @@ -642,6 +653,37 @@ This may take up to a minute. المطور + + SettingsWindowSP + + × + × + + + Device + الجهاز + + + Network + الشبكة + + + Toggles + المثبتتات + + + Software + البرنامج + + + Developer + المطور + + + sunnypilot + sunnypilot + + Setup diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index 1f40c2e40a..8dbc436792 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -475,6 +475,17 @@ Aktivieren + + ParamControlSP + + Cancel + Abbrechen + + + Enable + Aktivieren + + PrimeAdWidget @@ -624,6 +635,37 @@ This may take up to a minute. + + SettingsWindowSP + + × + x + + + Device + Gerät + + + Network + Netzwerk + + + Toggles + Schalter + + + Software + Software + + + Developer + + + + sunnypilot + sunnypilot + + Setup diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts index 6a27d49137..5f7e980aa0 100644 --- a/selfdrive/ui/translations/main_es.ts +++ b/selfdrive/ui/translations/main_es.ts @@ -476,6 +476,17 @@ Cancelar + + ParamControlSP + + Enable + Activar + + + Cancel + Cancelar + + PrimeAdWidget @@ -626,6 +637,37 @@ Esto puede tardar un minuto. Desarrollador + + SettingsWindowSP + + × + × + + + Device + Dispositivo + + + Network + Red + + + Toggles + Ajustes + + + Software + Software + + + Developer + Desarrollador + + + sunnypilot + sunnypilot + + Setup diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts index bcaff48bbe..2348b10199 100644 --- a/selfdrive/ui/translations/main_fr.ts +++ b/selfdrive/ui/translations/main_fr.ts @@ -476,6 +476,17 @@ Annuler + + ParamControlSP + + Enable + Activer + + + Cancel + Annuler + + PrimeAdWidget @@ -626,6 +637,37 @@ Cela peut prendre jusqu'à une minute. + + SettingsWindowSP + + × + × + + + Device + Appareil + + + Network + Réseau + + + Toggles + Options + + + Software + Logiciel + + + Developer + + + + sunnypilot + sunnypilot + + Setup diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index 9e16d612f2..68c629a011 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -474,6 +474,17 @@ を有効化 + + ParamControlSP + + Cancel + キャンセル + + + Enable + を有効化 + + PrimeAdWidget @@ -620,6 +631,37 @@ This may take up to a minute. + + SettingsWindowSP + + × + × + + + Device + デバイス + + + Network + ネットワーク + + + Toggles + 機能設定 + + + Software + ソフトウェア + + + Developer + + + + sunnypilot + sunnypilot + + Setup diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index 4bbcae92a7..f26ad2d624 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -475,6 +475,17 @@ 활성화 + + ParamControlSP + + Cancel + 취소 + + + Enable + 활성화 + + PrimeAdWidget @@ -622,6 +633,37 @@ This may take up to a minute. 개발자 + + SettingsWindowSP + + × + × + + + Device + 장치 + + + Network + 네트워크 + + + Toggles + 토글 + + + Software + 소프트웨어 + + + Developer + 개발자 + + + sunnypilot + sunnypilot + + Setup diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index ec3d186287..be5732275d 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -476,6 +476,17 @@ Ativar + + ParamControlSP + + Cancel + Cancelar + + + Enable + Ativar + + PrimeAdWidget @@ -626,6 +637,37 @@ Isso pode levar até um minuto. Desenvdor + + SettingsWindowSP + + × + × + + + Device + Dispositivo + + + Network + Rede + + + Toggles + Ajustes + + + Software + Software + + + Developer + Desenvdor + + + sunnypilot + sunnypilot + + Setup diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts index 82e135a856..fc8bacd8be 100644 --- a/selfdrive/ui/translations/main_th.ts +++ b/selfdrive/ui/translations/main_th.ts @@ -475,6 +475,17 @@ ยกเลิก + + ParamControlSP + + Enable + เปิดใช้งาน + + + Cancel + ยกเลิก + + PrimeAdWidget @@ -622,6 +633,37 @@ This may take up to a minute. + + SettingsWindowSP + + × + × + + + Device + อุปกรณ์ + + + Network + เครือข่าย + + + Toggles + ตัวเลือก + + + Software + ซอฟต์แวร์ + + + Developer + + + + sunnypilot + sunnypilot + + Setup diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts index 58da05c4da..d89e3c6693 100644 --- a/selfdrive/ui/translations/main_tr.ts +++ b/selfdrive/ui/translations/main_tr.ts @@ -474,6 +474,17 @@ + + ParamControlSP + + Enable + + + + Cancel + + + PrimeAdWidget @@ -620,6 +631,37 @@ This may take up to a minute. + + SettingsWindowSP + + × + x + + + Device + Cihaz + + + Network + + + + Toggles + Değiştirme + + + Software + Yazılım + + + Developer + + + + sunnypilot + sunnypilot + + Setup diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index ab679b7f09..207d1c8b33 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -475,6 +475,17 @@ 启用 + + ParamControlSP + + Cancel + 取消 + + + Enable + 启用 + + PrimeAdWidget @@ -622,6 +633,37 @@ This may take up to a minute. + + SettingsWindowSP + + × + × + + + Device + 设备 + + + Network + 网络 + + + Toggles + 设定 + + + Software + 软件 + + + Developer + + + + sunnypilot + sunnypilot + + Setup diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index f88b12abad..c820d85333 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -475,6 +475,17 @@ 啟用 + + ParamControlSP + + Cancel + 取消 + + + Enable + 啟用 + + PrimeAdWidget @@ -622,6 +633,37 @@ This may take up to a minute. + + SettingsWindowSP + + × + × + + + Device + 裝置 + + + Network + 網路 + + + Toggles + 設定 + + + Software + 軟體 + + + Developer + + + + sunnypilot + sunnypilot + + Setup diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index ec3d40961d..3179a383c2 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -9,16 +9,17 @@ #include "common/swaglog.h" #include "common/util.h" #include "common/watchdog.h" +#include "qt/util.h" #include "system/hardware/hw.h" #define BACKLIGHT_DT 0.05 #define BACKLIGHT_TS 10.00 -static void update_sockets(UIState *s) { +void update_sockets(UIState *s) { s->sm->update(0); } -static void update_state(UIState *s) { +void update_state(UIState *s) { SubMaster &sm = *(s->sm); UIScene &scene = s->scene; @@ -98,6 +99,7 @@ UIState::UIState(QObject *parent) : QObject(parent) { prime_state = new PrimeState(this); language = QString::fromStdString(Params().get("LanguageSetting")); + RETURN_IF_SUNNYPILOT // update timer timer = new QTimer(this); QObject::connect(timer, &QTimer::timeout, this, &UIState::update); @@ -105,6 +107,7 @@ UIState::UIState(QObject *parent) : QObject(parent) { } void UIState::update() { + RETURN_IF_SUNNYPILOT update_sockets(this); update_state(this); updateStatus(); @@ -118,8 +121,9 @@ void UIState::update() { Device::Device(QObject *parent) : brightness_filter(BACKLIGHT_OFFROAD, BACKLIGHT_TS, BACKLIGHT_DT), QObject(parent) { setAwake(true); resetInteractiveTimeout(); - +#ifndef SUNNYPILOT QObject::connect(uiState(), &UIState::uiUpdate, this, &Device::update); +#endif } void Device::update(const UIState &s) { @@ -185,6 +189,7 @@ void Device::updateWakefulness(const UIState &s) { setAwake(s.scene.ignition || interactive_timeout > 0); } +#ifndef SUNNYPILOT UIState *uiState() { static UIState ui_state; return &ui_state; @@ -194,3 +199,4 @@ Device *device() { static Device _device; return &_device; } +#endif diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index 6ff67cacde..bf932c2ab5 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -67,7 +67,7 @@ class UIState : public QObject { public: UIState(QObject* parent = 0); - void updateStatus(); + virtual void updateStatus(); inline bool engaged() const { return scene.started && (*sm)["selfdriveState"].getSelfdriveState().getEnabled(); } @@ -82,15 +82,19 @@ signals: void uiUpdate(const UIState &s); void offroadTransition(bool offroad); -private slots: - void update(); +protected slots: + virtual void update(); + +protected: + QTimer *timer; private: - QTimer *timer; bool started_prev = false; }; +#ifndef SUNNYPILOT UIState *uiState(); +#endif // device management class class Device : public QObject { @@ -103,7 +107,7 @@ public: offroad_brightness = std::clamp(brightness, 0, 100); } -private: +protected: bool awake = false; int interactive_timeout = 0; bool ignition_on = false; @@ -126,5 +130,10 @@ public slots: void update(const UIState &s); }; +#ifndef SUNNYPILOT Device *device(); +#endif + void ui_update_params(UIState *s); +void update_state(UIState *s); +void update_sockets(UIState *s); diff --git a/sunnypilot/selfdrive/assets/offroad/icon_home.svg b/sunnypilot/selfdrive/assets/offroad/icon_home.svg new file mode 100644 index 0000000000..ca90cc7bf6 --- /dev/null +++ b/sunnypilot/selfdrive/assets/offroad/icon_home.svg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1673f8d46251a05787b60346193852991739345506dc7e9b106dfb370d3611ed +size 489 diff --git a/sunnypilot/selfdrive/assets/offroad/icon_software.png b/sunnypilot/selfdrive/assets/offroad/icon_software.png new file mode 100644 index 0000000000..70915e2906 --- /dev/null +++ b/sunnypilot/selfdrive/assets/offroad/icon_software.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:01fd38c0d5b05c3ce4ed07bfe4cd47a3ea61f68f24e6cc3058190d90d8200f9b +size 4785 diff --git a/sunnypilot/selfdrive/assets/offroad/icon_toggle.png b/sunnypilot/selfdrive/assets/offroad/icon_toggle.png new file mode 100644 index 0000000000..51906798b9 --- /dev/null +++ b/sunnypilot/selfdrive/assets/offroad/icon_toggle.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:11336d2f65100bc0dc2a53e09efae41ae85817b27e1a5bf134cc995e631c5b52 +size 4073