diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 7a38c66215..358172d765 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -19,7 +19,8 @@ if arch == "Darwin": del base_libs[base_libs.index('OpenCL')] qt_env['FRAMEWORKS'] += ['OpenCL'] -widgets_src = ["qt/widgets/input.cc", "qt/widgets/drive_stats.cc", +widgets_src = ["qt/util.cc", + "qt/widgets/input.cc", "qt/widgets/drive_stats.cc", "qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc", "qt/widgets/offroad_alerts.cc", "qt/widgets/setup.cc", "qt/widgets/keyboard.cc", "qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#phonelibs/qrcode/QrCode.cc", "qt/api.cc", diff --git a/selfdrive/ui/qt/maps/map.cc b/selfdrive/ui/qt/maps/map.cc index b0dbeceb49..ad9846da0a 100644 --- a/selfdrive/ui/qt/maps/map.cc +++ b/selfdrive/ui/qt/maps/map.cc @@ -9,6 +9,7 @@ #include "selfdrive/ui/ui.h" #include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/maps/map_helpers.h" +#include "selfdrive/ui/qt/request_repeater.h" const int PAN_TIMEOUT = 100; @@ -69,7 +70,6 @@ MapWindow::MapWindow(const QMapboxGLSettings &settings) : m_settings(settings) { last_position = *last_gps_position; } - grabGesture(Qt::GestureType::PinchGesture); } diff --git a/selfdrive/ui/qt/maps/map_settings.cc b/selfdrive/ui/qt/maps/map_settings.cc index bf17091bc9..0868b8fef1 100644 --- a/selfdrive/ui/qt/maps/map_settings.cc +++ b/selfdrive/ui/qt/maps/map_settings.cc @@ -4,6 +4,7 @@ #include "selfdrive/ui/qt/request_repeater.h" #include "selfdrive/ui/qt/widgets/controls.h" +#include "selfdrive/ui/qt/util.h" #include "selfdrive/common/util.h" static QString shorten(const QString &str, int max_len) { @@ -47,8 +48,21 @@ MapPanel::MapPanel(QWidget* parent) : QWidget(parent) { main_layout->addLayout(home_work_layout); main_layout->addSpacing(50); main_layout->addWidget(horizontal_line()); + main_layout->addSpacing(50); + + // Recents + QLabel *recent = new QLabel("Recent"); + recent->setStyleSheet(R"(font-size: 55px;)"); + main_layout->addWidget(recent); + + main_layout->addSpacing(20); + + recent_layout = new QVBoxLayout; + main_layout->addLayout(recent_layout); // Settings + main_layout->addSpacing(50); + main_layout->addWidget(horizontal_line()); main_layout->addWidget(new ParamControl("NavSettingTime24h", "Show ETA in 24h format", "Use 24h format instead of am/pm", @@ -60,10 +74,27 @@ MapPanel::MapPanel(QWidget* parent) : QWidget(parent) { std::string dongle_id = Params().get("DongleId"); if (util::is_valid_dongle_id(dongle_id)) { - std::string url = "https://api.commadotai.com/v1/navigation/" + dongle_id + "/locations"; - RequestRepeater* repeater = new RequestRepeater(this, QString::fromStdString(url), "ApiCache_NavDestinations", 30); - QObject::connect(repeater, &RequestRepeater::receivedResponse, this, &MapPanel::parseResponse); + // Fetch favorite and recent locations + { + std::string url = "https://api.commadotai.com/v1/navigation/" + dongle_id + "/locations"; + RequestRepeater* repeater = new RequestRepeater(this, QString::fromStdString(url), "ApiCache_NavDestinations", 30); + QObject::connect(repeater, &RequestRepeater::receivedResponse, this, &MapPanel::parseResponse); + } + + // Destination set while offline + { + std::string url = "https://api.commadotai.com/v1/navigation/" + dongle_id + "/next"; + RequestRepeater* repeater = new RequestRepeater(this, QString::fromStdString(url), "", 10); + + QObject::connect(repeater, &RequestRepeater::receivedResponse, [](QString resp) { + auto params = Params(); + if (resp != "null" && params.get("NavDestination").empty()) { + params.put("NavDestination", resp.toStdString()); + } + }); + } } + } void MapPanel::clear() { @@ -76,6 +107,8 @@ void MapPanel::clear() { work_address->setStyleSheet(R"(font-size: 50px; color: grey;)"); work_address->setText("No work\nlocation set"); work_button->disconnect(); + + clearLayout(recent_layout); } @@ -88,6 +121,7 @@ void MapPanel::parseResponse(const QString &response) { clear(); + bool has_recents = false; for (auto location : doc.array()) { auto obj = location.toObject(); @@ -96,26 +130,65 @@ void MapPanel::parseResponse(const QString &response) { auto name = obj["place_name"].toString(); auto details = shorten(obj["place_details"].toString(), 30); - if (type == "favorite") { - if (label == "home") { - home_address->setText(name); - home_address->setStyleSheet(R"(font-size: 50px; color: white;)"); - home_button->setIcon(QPixmap("../assets/navigation/home.png")); - QObject::connect(home_button, &QPushButton::clicked, [=]() { - navigateTo(obj); - emit closeSettings(); - }); - } else if (label == "work") { - work_address->setText(name); - work_address->setStyleSheet(R"(font-size: 50px; color: white;)"); - work_button->setIcon(QPixmap("../assets/navigation/work.png")); - QObject::connect(work_button, &QPushButton::clicked, [=]() { - navigateTo(obj); - emit closeSettings(); - }); - } + if (type == "favorite" && label == "home") { + home_address->setText(name); + home_address->setStyleSheet(R"(font-size: 50px; color: white;)"); + home_button->setIcon(QPixmap("../assets/navigation/home.png")); + QObject::connect(home_button, &QPushButton::clicked, [=]() { + navigateTo(obj); + emit closeSettings(); + }); + } else if (type == "favorite" && label == "work") { + work_address->setText(name); + work_address->setStyleSheet(R"(font-size: 50px; color: white;)"); + work_button->setIcon(QPixmap("../assets/navigation/work.png")); + QObject::connect(work_button, &QPushButton::clicked, [=]() { + navigateTo(obj); + emit closeSettings(); + }); + } else { + ClickableWidget *widget = new ClickableWidget; + QHBoxLayout *layout = new QHBoxLayout(widget); + layout->setContentsMargins(40, 10, 40, 10); + + QLabel *recent_label = new QLabel(name + " " + details); + recent_label->setStyleSheet(R"(font-size: 50px; color: #9c9c9c)"); + + layout->addWidget(recent_label); + layout->addStretch(); + + QLabel *arrow = new QLabel("→"); + arrow->setStyleSheet(R"(font-size: 60px;)"); + layout->addWidget(arrow); + + widget->setStyleSheet(R"( + .ClickableWidget { + border-radius: 10px; + border-width: 1px; + border-style: solid; + border-color: gray; + } + QWidget { + background-color: #393939; + } + )"); + + QObject::connect(widget, &ClickableWidget::clicked, [=]() { + navigateTo(obj); + emit closeSettings(); + }); + + recent_layout->addWidget(widget); + recent_layout->addSpacing(10); + has_recents = true; } } + + if (!has_recents) { + QLabel *no_recents = new QLabel(" no recent destinations"); + no_recents->setStyleSheet(R"(font-size: 50px; color: #9c9c9c)"); + recent_layout->addWidget(no_recents); + } } void MapPanel::navigateTo(const QJsonObject &place) { diff --git a/selfdrive/ui/qt/maps/map_settings.h b/selfdrive/ui/qt/maps/map_settings.h index 26e4f95f95..4cda9eb6f1 100644 --- a/selfdrive/ui/qt/maps/map_settings.h +++ b/selfdrive/ui/qt/maps/map_settings.h @@ -1,10 +1,11 @@ #pragma once -#include +#include +#include +#include #include #include -#include -#include -#include +#include +#include class MapPanel : public QWidget { Q_OBJECT @@ -18,6 +19,7 @@ public: private: QPushButton *home_button, *work_button; QLabel *home_address, *work_address; + QVBoxLayout *recent_layout; signals: void closeSettings(); diff --git a/selfdrive/ui/qt/util.cc b/selfdrive/ui/qt/util.cc new file mode 100644 index 0000000000..ce2529c02e --- /dev/null +++ b/selfdrive/ui/qt/util.cc @@ -0,0 +1,18 @@ +#include "selfdrive/ui/qt/util.h" +#include +#include + +ClickableWidget::ClickableWidget(QWidget *parent) : QWidget(parent) { } + +void ClickableWidget::mouseReleaseEvent(QMouseEvent *event) { + emit clicked(); +} + + +// Fix stylesheets +void ClickableWidget::paintEvent(QPaintEvent *) { + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} diff --git a/selfdrive/ui/qt/util.h b/selfdrive/ui/qt/util.h index 59ff0cf9d8..9da0397706 100644 --- a/selfdrive/ui/qt/util.h +++ b/selfdrive/ui/qt/util.h @@ -3,8 +3,10 @@ #include #include #include +#include #include #include +#include #include "selfdrive/common/params.h" @@ -69,3 +71,18 @@ inline void setQtSurfaceFormat() { #endif QSurfaceFormat::setDefaultFormat(fmt); } + +class ClickableWidget : public QWidget +{ + Q_OBJECT + +public: + ClickableWidget(QWidget *parent = nullptr); + +protected: + void mouseReleaseEvent(QMouseEvent *event) override; + void paintEvent(QPaintEvent *) override; + +signals: + void clicked(); +};