diff --git a/selfdrive/assets/navigation/icon_directions.svg b/selfdrive/assets/navigation/icon_directions.svg
new file mode 100644
index 000000000..66009ac43
--- /dev/null
+++ b/selfdrive/assets/navigation/icon_directions.svg
@@ -0,0 +1 @@
+
diff --git a/selfdrive/assets/navigation/icon_directions_outlined.svg b/selfdrive/assets/navigation/icon_directions_outlined.svg
new file mode 100644
index 000000000..4d31bfd93
--- /dev/null
+++ b/selfdrive/assets/navigation/icon_directions_outlined.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/selfdrive/assets/navigation/icon_favorite.svg b/selfdrive/assets/navigation/icon_favorite.svg
new file mode 100644
index 000000000..ba64df4ab
--- /dev/null
+++ b/selfdrive/assets/navigation/icon_favorite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/selfdrive/assets/navigation/icon_home.svg b/selfdrive/assets/navigation/icon_home.svg
new file mode 100644
index 000000000..cb8701109
--- /dev/null
+++ b/selfdrive/assets/navigation/icon_home.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/selfdrive/assets/navigation/icon_recent.svg b/selfdrive/assets/navigation/icon_recent.svg
new file mode 100644
index 000000000..668aa3820
--- /dev/null
+++ b/selfdrive/assets/navigation/icon_recent.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/selfdrive/assets/navigation/icon_settings.svg b/selfdrive/assets/navigation/icon_settings.svg
new file mode 100644
index 000000000..134cc0f31
--- /dev/null
+++ b/selfdrive/assets/navigation/icon_settings.svg
@@ -0,0 +1 @@
+
diff --git a/selfdrive/assets/navigation/icon_work.svg b/selfdrive/assets/navigation/icon_work.svg
new file mode 100644
index 000000000..c1ea6c5e3
--- /dev/null
+++ b/selfdrive/assets/navigation/icon_work.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/selfdrive/assets/navigation/screenshot.png b/selfdrive/assets/navigation/screenshot.png
deleted file mode 100644
index 3e89c0475..000000000
Binary files a/selfdrive/assets/navigation/screenshot.png and /dev/null differ
diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript
index f46e3d587..97d406076 100644
--- a/selfdrive/ui/SConscript
+++ b/selfdrive/ui/SConscript
@@ -29,7 +29,7 @@ widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/drive_stats.cc", "qt/
qt_env['CPPDEFINES'] = []
if maps:
base_libs += ['qmapboxgl']
- widgets_src += ["qt/maps/map_helpers.cc", "qt/maps/map_settings.cc", "qt/maps/map.cc"]
+ widgets_src += ["qt/maps/map_helpers.cc", "qt/maps/map_settings.cc", "qt/maps/map.cc", "qt/maps/map_panel.cc"]
qt_env['CPPDEFINES'] += ["ENABLE_MAPS"]
widgets = qt_env.Library("qt_widgets", widgets_src, LIBS=base_libs)
diff --git a/selfdrive/ui/qt/home.cc b/selfdrive/ui/qt/home.cc
index 587e2f445..85c6c3426 100644
--- a/selfdrive/ui/qt/home.cc
+++ b/selfdrive/ui/qt/home.cc
@@ -7,9 +7,14 @@
#include "selfdrive/ui/qt/offroad/experimental_mode.h"
#include "selfdrive/ui/qt/util.h"
-#include "selfdrive/ui/qt/widgets/drive_stats.h"
#include "selfdrive/ui/qt/widgets/prime.h"
+#ifdef ENABLE_MAPS
+#include "selfdrive/ui/qt/maps/map_settings.h"
+#else
+#include "selfdrive/ui/qt/widgets/drive_stats.h"
+#endif
+
// HomeWindow: the container for the offroad and onroad UIs
HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
@@ -137,10 +142,15 @@ OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) {
home_layout->setContentsMargins(0, 0, 0, 0);
home_layout->setSpacing(30);
- // left: DriveStats/PrimeAdWidget
+ // left: MapSettings/PrimeAdWidget
QStackedWidget *left_widget = new QStackedWidget(this);
+#ifdef ENABLE_MAPS
+ left_widget->addWidget(new MapSettings);
+#else
left_widget->addWidget(new DriveStats);
+#endif
left_widget->addWidget(new PrimeAdWidget);
+ left_widget->setStyleSheet("border-radius: 10px;");
left_widget->setCurrentIndex(uiState()->primeType() ? 0 : 1);
connect(uiState(), &UIState::primeTypeChanged, [=](int prime_type) {
diff --git a/selfdrive/ui/qt/maps/map.cc b/selfdrive/ui/qt/maps/map.cc
index abbf434d6..bcf6e9d06 100644
--- a/selfdrive/ui/qt/maps/map.cc
+++ b/selfdrive/ui/qt/maps/map.cc
@@ -44,6 +44,29 @@ MapWindow::MapWindow(const QMapboxGLSettings &settings) : m_settings(settings),
map_eta->move(25, 1080 - h - bdr_s*2);
map_eta->setVisible(false);
+ // Settings button
+ QSize icon_size(120, 120);
+ directions_icon = loadPixmap("../assets/navigation/icon_directions_outlined.svg", icon_size);
+ settings_icon = loadPixmap("../assets/navigation/icon_settings.svg", icon_size);
+
+ settings_btn = new QPushButton(directions_icon, "", this);
+ settings_btn->setIconSize(icon_size);
+ settings_btn->setStyleSheet(R"(
+ QPushButton {
+ background-color: #96000000;
+ border-radius: 50px;
+ padding: 24px;
+ }
+ QPushButton:pressed {
+ background-color: #D9000000;
+ }
+ )");
+ settings_btn->show(); // force update
+ settings_btn->move(bdr_s, 1080 - bdr_s*3 - settings_btn->height());
+ QObject::connect(settings_btn, &QPushButton::clicked, [=]() {
+ emit openSettings();
+ });
+
auto last_gps_position = coordinate_from_param("LastGPSPosition");
if (last_gps_position.has_value()) {
last_position = *last_gps_position;
@@ -128,7 +151,7 @@ void MapWindow::updateState(const UIState &s) {
// Only open the map on setting destination the first time
if (allow_open) {
- setVisible(true); // Show map on destination set/change
+ emit requestVisible(true); // Show map on destination set/change
allow_open = false;
}
}
@@ -186,6 +209,19 @@ void MapWindow::updateState(const UIState &s) {
} else {
clearRoute();
}
+
+ // TODO: only move if position should change
+ // don't move while map isn't visible
+ if (isVisible()) {
+ auto pos = 1080 - bdr_s*2 - settings_btn->height() - bdr_s;
+ if (map_eta->isVisible()) {
+ settings_btn->move(bdr_s, pos - map_eta->height());
+ settings_btn->setIcon(settings_icon);
+ } else {
+ settings_btn->move(bdr_s, pos);
+ settings_btn->setIcon(directions_icon);
+ }
+ }
}
if (sm.rcv_frame("navRoute") != route_rcv_frame) {
@@ -321,7 +357,7 @@ void MapWindow::offroadTransition(bool offroad) {
clearRoute();
} else {
auto dest = coordinate_from_param("NavDestination");
- setVisible(dest.has_value());
+ emit requestVisible(dest.has_value());
}
last_bearing = {};
}
diff --git a/selfdrive/ui/qt/maps/map.h b/selfdrive/ui/qt/maps/map.h
index 0d8b93a5f..658d26ee4 100644
--- a/selfdrive/ui/qt/maps/map.h
+++ b/selfdrive/ui/qt/maps/map.h
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -110,6 +111,8 @@ private:
MapInstructions* map_instructions;
MapETA* map_eta;
+ QPushButton *settings_btn;
+ QPixmap directions_icon, settings_icon;
void clearRoute();
void updateDestinationMarker();
@@ -125,5 +128,7 @@ signals:
void distanceChanged(float distance);
void instructionsChanged(cereal::NavInstruction::Reader instruction);
void ETAChanged(float seconds, float seconds_typical, float distance);
-};
+ void requestVisible(bool visible);
+ void openSettings();
+};
diff --git a/selfdrive/ui/qt/maps/map_panel.cc b/selfdrive/ui/qt/maps/map_panel.cc
new file mode 100644
index 000000000..564012482
--- /dev/null
+++ b/selfdrive/ui/qt/maps/map_panel.cc
@@ -0,0 +1,34 @@
+#include "selfdrive/ui/qt/maps/map_panel.h"
+
+#include
+#include
+
+#include "selfdrive/ui/qt/maps/map.h"
+#include "selfdrive/ui/qt/maps/map_settings.h"
+#include "selfdrive/ui/qt/util.h"
+#include "selfdrive/ui/ui.h"
+
+MapPanel::MapPanel(const QMapboxGLSettings &mapboxSettings, QWidget *parent) : QFrame(parent) {
+ content_stack = new QStackedLayout(this);
+ content_stack->setContentsMargins(0, 0, 0, 0);
+
+ auto map = new MapWindow(mapboxSettings);
+ QObject::connect(uiState(), &UIState::offroadTransition, map, &MapWindow::offroadTransition);
+ QObject::connect(map, &MapWindow::requestVisible, [=](bool visible) {
+ setVisible(visible);
+ });
+ QObject::connect(map, &MapWindow::openSettings, [=]() {
+ content_stack->setCurrentIndex(1);
+ });
+ content_stack->addWidget(map);
+
+ auto settings = new MapSettings(true, parent);
+ QObject::connect(settings, &MapSettings::closeSettings, [=]() {
+ content_stack->setCurrentIndex(0);
+ });
+ content_stack->addWidget(settings);
+}
+
+bool MapPanel::isShowingMap() const {
+ return content_stack->currentIndex() == 0;
+}
diff --git a/selfdrive/ui/qt/maps/map_panel.h b/selfdrive/ui/qt/maps/map_panel.h
new file mode 100644
index 000000000..abea77483
--- /dev/null
+++ b/selfdrive/ui/qt/maps/map_panel.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include
+#include
+#include
+
+class MapPanel : public QFrame {
+ Q_OBJECT
+
+public:
+ explicit MapPanel(const QMapboxGLSettings &settings, QWidget *parent = nullptr);
+
+ bool isShowingMap() const;
+
+private:
+ QStackedLayout *content_stack;
+};
diff --git a/selfdrive/ui/qt/maps/map_settings.cc b/selfdrive/ui/qt/maps/map_settings.cc
index f626925ad..e9c8dcfcb 100644
--- a/selfdrive/ui/qt/maps/map_settings.cc
+++ b/selfdrive/ui/qt/maps/map_settings.cc
@@ -1,132 +1,95 @@
#include "map_settings.h"
#include
+#include
#include "common/util.h"
-#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/request_repeater.h"
-#include "selfdrive/ui/qt/widgets/controls.h"
#include "selfdrive/ui/qt/widgets/scrollview.h"
static QString shorten(const QString &str, int max_len) {
return str.size() > max_len ? str.left(max_len).trimmed() + "…" : str;
}
-MapPanel::MapPanel(QWidget* parent) : QWidget(parent) {
- QStackedLayout *stack = new QStackedLayout(this);
+MapSettings::MapSettings(bool closeable, QWidget *parent)
+ : QFrame(parent), current_destination(nullptr) {
+ QSize icon_size(100, 100);
+ close_icon = loadPixmap("../assets/icons/close.svg", icon_size);
- QWidget *main_widget = new QWidget;
- QVBoxLayout *main_layout = new QVBoxLayout(main_widget);
- main_layout->setSpacing(20);
+ setContentsMargins(0, 0, 0, 0);
- // Home & Work layout
- QHBoxLayout *home_work_layout = new QHBoxLayout;
- {
- // Home
- home_button = new QPushButton;
- home_button->setIconSize(QSize(MAP_PANEL_ICON_SIZE, MAP_PANEL_ICON_SIZE));
- home_address = new QLabel;
- home_address->setWordWrap(true);
-
- QHBoxLayout *home_layout = new QHBoxLayout;
- home_layout->addWidget(home_button);
- home_layout->addSpacing(30);
- home_layout->addWidget(home_address);
- home_layout->addStretch();
-
- // Work
- work_button = new QPushButton;
- work_button->setIconSize(QSize(MAP_PANEL_ICON_SIZE, MAP_PANEL_ICON_SIZE));
- work_address = new QLabel;
- work_address->setWordWrap(true);
-
- QHBoxLayout *work_layout = new QHBoxLayout;
- work_layout->addWidget(work_button);
- work_layout->addSpacing(30);
- work_layout->addWidget(work_address);
- work_layout->addStretch();
-
- home_work_layout->addLayout(home_layout, 1);
- home_work_layout->addSpacing(50);
- home_work_layout->addLayout(work_layout, 1);
- }
+ auto *frame = new QVBoxLayout(this);
+ frame->setContentsMargins(40, 40, 40, 0);
+ frame->setSpacing(0);
- main_layout->addLayout(home_work_layout);
- main_layout->addWidget(horizontal_line());
-
- // Current route
+ auto *heading_frame = new QHBoxLayout;
+ heading_frame->setContentsMargins(0, 0, 0, 0);
+ heading_frame->setSpacing(32);
{
- current_widget = new QWidget(this);
- QVBoxLayout *current_layout = new QVBoxLayout(current_widget);
-
- QLabel *title = new QLabel(tr("Current Destination"));
- title->setStyleSheet("font-size: 55px");
- current_layout->addWidget(title);
-
- current_route = new ButtonControl("", tr("CLEAR"));
- current_route->setStyleSheet("padding-left: 40px;");
- current_layout->addWidget(current_route);
- QObject::connect(current_route, &ButtonControl::clicked, [=]() {
- params.remove("NavDestination");
- updateCurrentRoute();
- });
-
- current_layout->addSpacing(10);
- current_layout->addWidget(horizontal_line());
- current_layout->addSpacing(20);
- }
- main_layout->addWidget(current_widget);
-
- // Recents
- QLabel *recents_title = new QLabel(tr("Recent Destinations"));
- recents_title->setStyleSheet("font-size: 55px");
- main_layout->addWidget(recents_title);
+ if (closeable) {
+ auto *close_btn = new QPushButton("←");
+ close_btn->setStyleSheet(R"(
+ QPushButton {
+ color: #FFFFFF;
+ font-size: 100px;
+ padding-bottom: 8px;
+ border 1px grey solid;
+ border-radius: 70px;
+ background-color: #292929;
+ font-weight: 500;
+ }
+ QPushButton:pressed {
+ background-color: #3B3B3B;
+ }
+ )");
+ close_btn->setFixedSize(140, 140);
+ QObject::connect(close_btn, &QPushButton::clicked, [=]() { emit closeSettings(); });
+ // TODO: read map_on_left from ui state
+ heading_frame->addWidget(close_btn);
+ }
- recent_layout = new QVBoxLayout;
- QWidget *recent_widget = new LayoutWidget(recent_layout, this);
- ScrollView *recent_scroller = new ScrollView(recent_widget, this);
- main_layout->addWidget(recent_scroller);
+ auto *heading = new QVBoxLayout;
+ heading->setContentsMargins(0, 0, 0, 0);
+ heading->setSpacing(16);
+ {
+ auto *title = new QLabel(tr("NAVIGATION"), this);
+ title->setStyleSheet("color: #FFFFFF; font-size: 54px; font-weight: 600;");
+ heading->addWidget(title);
- // No prime upsell
- QWidget * no_prime_widget = new QWidget;
- {
- QVBoxLayout *no_prime_layout = new QVBoxLayout(no_prime_widget);
- QLabel *signup_header = new QLabel(tr("Try the Navigation Beta"));
- signup_header->setStyleSheet(R"(font-size: 75px; color: white; font-weight:600;)");
- signup_header->setAlignment(Qt::AlignCenter);
-
- no_prime_layout->addWidget(signup_header);
- no_prime_layout->addSpacing(50);
-
- QLabel *screenshot = new QLabel;
- QPixmap pm = QPixmap("../assets/navigation/screenshot.png");
- screenshot->setPixmap(pm.scaledToWidth(1080, Qt::SmoothTransformation));
- no_prime_layout->addWidget(screenshot, 0, Qt::AlignHCenter);
-
- QLabel *signup = new QLabel(tr("Get turn-by-turn directions displayed and more with a comma\nprime subscription. Sign up now: https://connect.comma.ai"));
- signup->setStyleSheet(R"(font-size: 45px; color: white; font-weight:300;)");
- signup->setAlignment(Qt::AlignCenter);
-
- no_prime_layout->addSpacing(20);
- no_prime_layout->addWidget(signup);
- no_prime_layout->addStretch();
+ auto *subtitle = new QLabel(tr("Manage at connect.comma.ai"), this);
+ subtitle->setStyleSheet("color: #A0A0A0; font-size: 40px; font-weight: 300;");
+ heading->addWidget(subtitle);
+ }
+ heading_frame->addLayout(heading, 1);
}
-
- stack->addWidget(main_widget);
- stack->addWidget(no_prime_widget);
- connect(uiState(), &UIState::primeTypeChanged, [=](int prime_type) {
- stack->setCurrentIndex(prime_type ? 0 : 1);
+ frame->addLayout(heading_frame);
+ frame->addSpacing(32);
+
+ current_widget = new DestinationWidget(this);
+ QObject::connect(current_widget, &DestinationWidget::actionClicked, [=]() {
+ if (!current_destination) return;
+ params.remove("NavDestination");
+ updateCurrentRoute();
});
+ frame->addWidget(current_widget);
+ frame->addSpacing(32);
+ frame->addWidget(horizontal_line());
+ QWidget *destinations_container = new QWidget(this);
+ destinations_layout = new QVBoxLayout(destinations_container);
+ destinations_layout->setContentsMargins(0, 32, 0, 32);
+ destinations_layout->setSpacing(20);
+ ScrollView *destinations_scroller = new ScrollView(destinations_container, this);
+ frame->addWidget(destinations_scroller);
- clear();
+ setStyleSheet("MapSettings { background-color: #333333; }");
if (auto dongle_id = getDongleId()) {
// Fetch favorite and recent locations
{
QString url = CommaApi::BASE_URL + "/v1/navigation/" + *dongle_id + "/locations";
RequestRepeater* repeater = new RequestRepeater(this, url, "ApiCache_NavDestinations", 30, true);
- QObject::connect(repeater, &RequestRepeater::requestDone, this, &MapPanel::parseResponse);
+ QObject::connect(repeater, &RequestRepeater::requestDone, this, &MapSettings::parseResponse);
}
// Destination set while offline
@@ -147,153 +110,236 @@ MapPanel::MapPanel(QWidget* parent) : QWidget(parent) {
// Send DELETE to clear destination server side
deleter->sendRequest(url, HttpRequest::Method::DELETE);
}
+
+ // Update UI (athena can set destination at any time)
+ updateCurrentRoute();
});
}
}
}
-void MapPanel::showEvent(QShowEvent *event) {
+void MapSettings::showEvent(QShowEvent *event) {
updateCurrentRoute();
- refresh();
-}
-
-void MapPanel::clear() {
- home_button->setIcon(QPixmap("../assets/navigation/home_inactive.png"));
- home_address->setStyleSheet(R"(font-size: 50px; color: grey;)");
- home_address->setText(tr("No home\nlocation set"));
- home_button->disconnect();
-
- work_button->setIcon(QPixmap("../assets/navigation/work_inactive.png"));
- work_address->setStyleSheet(R"(font-size: 50px; color: grey;)");
- work_address->setText(tr("No work\nlocation set"));
- work_button->disconnect();
-
- clearLayout(recent_layout);
}
-void MapPanel::updateCurrentRoute() {
+void MapSettings::updateCurrentRoute() {
auto dest = QString::fromStdString(params.get("NavDestination"));
- QJsonDocument doc = QJsonDocument::fromJson(dest.trimmed().toUtf8());
- if (dest.size() && !doc.isNull()) {
- auto name = doc["place_name"].toString();
- auto details = doc["place_details"].toString();
- current_route->setTitle(shorten(name + " " + details, 42));
+ if (dest.size()) {
+ QJsonDocument doc = QJsonDocument::fromJson(dest.trimmed().toUtf8());
+ if (doc.isNull()) {
+ qWarning() << "JSON Parse failed on NavDestination" << dest;
+ return;
+ }
+ auto destination = new NavDestination(doc.object());
+ if (current_destination && *destination == *current_destination) return;
+ current_destination = destination;
+ current_widget->set(current_destination, true);
+ } else {
+ current_destination = nullptr;
+ current_widget->unset("", true);
}
- current_widget->setVisible(dest.size() && !doc.isNull());
+ if (isVisible()) refresh();
}
-void MapPanel::parseResponse(const QString &response, bool success) {
- if (!success) return;
-
+void MapSettings::parseResponse(const QString &response, bool success) {
+ if (!success || response == cur_destinations) return;
cur_destinations = response;
- if (isVisible()) {
- refresh();
- }
+ refresh();
}
-void MapPanel::refresh() {
- if (cur_destinations == prev_destinations) return;
-
- QJsonDocument doc = QJsonDocument::fromJson(cur_destinations.trimmed().toUtf8());
- if (doc.isNull()) {
- qDebug() << "JSON Parse failed on navigation locations";
- return;
- }
+void MapSettings::refresh() {
+ bool has_home = false, has_work = false;
+ auto destinations = std::vector();
- prev_destinations = cur_destinations;
- clear();
-
- // add favorites before recents
- bool has_recents = false;
- for (auto &save_type: {NAV_TYPE_FAVORITE, NAV_TYPE_RECENT}) {
- for (auto location : doc.array()) {
- auto obj = location.toObject();
-
- auto type = obj["save_type"].toString();
- auto label = obj["label"].toString();
- auto name = obj["place_name"].toString();
- auto details = obj["place_details"].toString();
-
- if (type != save_type) continue;
-
- if (type == NAV_TYPE_FAVORITE && label == NAV_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 == NAV_TYPE_FAVORITE && label == NAV_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(15, 14, 40, 14);
-
- QLabel *star = new QLabel("★");
- auto sp = star->sizePolicy();
- sp.setRetainSizeWhenHidden(true);
- star->setSizePolicy(sp);
-
- star->setVisible(type == NAV_TYPE_FAVORITE);
- star->setStyleSheet(R"(font-size: 60px;)");
- layout->addWidget(star);
- layout->addSpacing(10);
-
-
- QLabel *recent_label = new QLabel(shorten(name + " " + details, 45));
- recent_label->setStyleSheet(R"(font-size: 50px;)");
-
- 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;
- color: #9c9c9c;
- }
- )");
+ auto destinations_str = cur_destinations.trimmed();
+ if (!destinations_str.isEmpty()) {
+ QJsonDocument doc = QJsonDocument::fromJson(destinations_str.toUtf8());
+ if (doc.isNull()) {
+ qWarning() << "JSON Parse failed on navigation locations" << cur_destinations;
+ return;
+ }
- QObject::connect(widget, &ClickableWidget::clicked, [=]() {
- navigateTo(obj);
- emit closeSettings();
- });
+ for (auto el : doc.array()) {
+ auto destination = new NavDestination(el.toObject());
- recent_layout->addWidget(widget);
- recent_layout->addSpacing(10);
- has_recents = true;
+ // add home and work later if they are missing
+ if (destination->isFavorite()) {
+ if (destination->label() == NAV_FAVORITE_LABEL_HOME) has_home = true;
+ else if (destination->label() == NAV_FAVORITE_LABEL_WORK) has_work = true;
}
+
+ // skip current destination
+ if (current_destination && *destination == *current_destination) continue;
+ destinations.push_back(destination);
}
}
- if (!has_recents) {
- QLabel *no_recents = new QLabel(tr("no recent destinations"));
- no_recents->setStyleSheet(R"(font-size: 50px; color: #9c9c9c)");
- recent_layout->addWidget(no_recents);
+ // TODO: should we build a new layout and swap it in?
+ clearLayout(destinations_layout);
+
+ // Sort: HOME, WORK, and then descending-alphabetical FAVORITES, RECENTS
+ std::sort(destinations.begin(), destinations.end(), [](const NavDestination *a, const NavDestination *b) {
+ if (a->isFavorite() && b->isFavorite()) {
+ if (a->label() == NAV_FAVORITE_LABEL_HOME) return true;
+ else if (b->label() == NAV_FAVORITE_LABEL_HOME) return false;
+ else if (a->label() == NAV_FAVORITE_LABEL_WORK) return true;
+ else if (b->label() == NAV_FAVORITE_LABEL_WORK) return false;
+ else if (a->label() != b->label()) return a->label() < b->label();
+ }
+ else if (a->isFavorite()) return true;
+ else if (b->isFavorite()) return false;
+ return a->name() < b->name();
+ });
+
+ for (auto destination : destinations) {
+ auto widget = new DestinationWidget(this);
+ widget->set(destination, false);
+
+ QObject::connect(widget, &QPushButton::clicked, [=]() {
+ navigateTo(destination->toJson());
+ emit closeSettings();
+ });
+
+ destinations_layout->addWidget(widget);
}
- recent_layout->addStretch();
+ // add home and work if missing
+ if (!has_home) {
+ auto widget = new DestinationWidget(this);
+ widget->unset(NAV_FAVORITE_LABEL_HOME);
+ destinations_layout->insertWidget(0, widget);
+ }
+ if (!has_work) {
+ auto widget = new DestinationWidget(this);
+ widget->unset(NAV_FAVORITE_LABEL_WORK);
+ // TODO: refactor to remove this hack
+ int index = !has_home || (current_destination && current_destination->isFavorite() && current_destination->label() == NAV_FAVORITE_LABEL_HOME) ? 0 : 1;
+ destinations_layout->insertWidget(index, widget);
+ }
+
+ destinations_layout->addStretch();
repaint();
}
-void MapPanel::navigateTo(const QJsonObject &place) {
+void MapSettings::navigateTo(const QJsonObject &place) {
QJsonDocument doc(place);
params.put("NavDestination", doc.toJson().toStdString());
+ updateCurrentRoute();
+}
+
+DestinationWidget::DestinationWidget(QWidget *parent) : QPushButton(parent) {
+ setContentsMargins(0, 0, 0, 0);
+
+ auto *frame = new QHBoxLayout(this);
+ frame->setContentsMargins(32, 24, 32, 24);
+ frame->setSpacing(32);
+
+ icon = new QLabel(this);
+ icon->setAlignment(Qt::AlignCenter);
+ icon->setFixedSize(96, 96);
+ icon->setObjectName("icon");
+ frame->addWidget(icon);
+
+ auto *inner_frame = new QVBoxLayout;
+ inner_frame->setContentsMargins(0, 0, 0, 0);
+ inner_frame->setSpacing(0);
+ {
+ title = new ElidedLabel(this);
+ title->setAttribute(Qt::WA_TransparentForMouseEvents);
+ inner_frame->addWidget(title);
+
+ subtitle = new ElidedLabel(this);
+ subtitle->setAttribute(Qt::WA_TransparentForMouseEvents);
+ subtitle->setObjectName("subtitle");
+ inner_frame->addWidget(subtitle);
+ }
+ frame->addLayout(inner_frame, 1);
+
+ action = new QPushButton(this);
+ action->setFixedSize(96, 96);
+ action->setObjectName("action");
+ action->setStyleSheet("font-size: 65px; font-weight: 600;");
+ QObject::connect(action, &QPushButton::clicked, [=]() { emit clicked(); });
+ QObject::connect(action, &QPushButton::clicked, [=]() { emit actionClicked(); });
+ frame->addWidget(action);
+
+ setFixedHeight(164);
+ setStyleSheet(R"(
+ DestinationWidget { background-color: #202123; border-radius: 10px; }
+ QLabel { color: #FFFFFF; font-size: 48px; font-weight: 400; }
+ #icon { background-color: #3B4356; border-radius: 48px; }
+ #subtitle { color: #9BA0A5; }
+ #action { border: none; border-radius: 48px; color: #FFFFFF; padding-bottom: 4px; }
+
+ /* current destination */
+ [current="true"] { background-color: #E8E8E8; }
+ [current="true"] QLabel { color: #000000; }
+ [current="true"] #icon { background-color: #42906B; }
+ [current="true"] #subtitle { color: #333333; }
+ [current="true"] #action { color: #202123; }
+
+ /* no saved destination */
+ [set="false"] QLabel { color: #9BA0A5; }
+ [current="true"][set="false"] QLabel { color: #A0000000; }
+
+ /* pressed */
+ [current="false"]:pressed { background-color: #18191B; }
+ [current="true"] #action:pressed { background-color: #D6D6D6; }
+ )");
+}
+
+void DestinationWidget::set(NavDestination *destination, bool current) {
+ setProperty("current", current);
+ setProperty("set", true);
+
+ auto icon_pixmap = current ? icons().directions : icons().recent;
+ auto title_text = destination->name();
+ auto subtitle_text = destination->details();
+
+ if (destination->isFavorite()) {
+ if (destination->label() == NAV_FAVORITE_LABEL_HOME) {
+ icon_pixmap = icons().home;
+ title_text = tr("Home");
+ subtitle_text = destination->name() + ", " + destination->details();
+ } else if (destination->label() == NAV_FAVORITE_LABEL_WORK) {
+ icon_pixmap = icons().work;
+ title_text = tr("Work");
+ subtitle_text = destination->name() + ", " + destination->details();
+ } else {
+ icon_pixmap = icons().favorite;
+ }
+ }
+
+ icon->setPixmap(icon_pixmap);
+
+ // TODO: onroad and offroad have different dimensions
+ title->setText(shorten(title_text, 26));
+ subtitle->setText(shorten(subtitle_text, 26));
+ subtitle->setVisible(true);
+
+ // TODO: use pixmap
+ action->setAttribute(Qt::WA_TransparentForMouseEvents, !current);
+ action->setText(current ? "×" : "→");
+ action->setVisible(true);
+
+ setStyleSheet(styleSheet());
+}
+
+void DestinationWidget::unset(const QString &label, bool current) {
+ setProperty("current", current);
+ setProperty("set", false);
+
+ if (label.isEmpty()) {
+ icon->setPixmap(icons().directions);
+ title->setText(tr("No destination set"));
+ } else {
+ QString title_text = label == NAV_FAVORITE_LABEL_HOME ? tr("home") : tr("work");
+ icon->setPixmap(label == NAV_FAVORITE_LABEL_HOME ? icons().home : icons().work);
+ title->setText(tr("No %1 location set").arg(title_text));
+ }
+
+ subtitle->setVisible(false);
+ action->setVisible(false);
+
+ setStyleSheet(styleSheet());
}
diff --git a/selfdrive/ui/qt/maps/map_settings.h b/selfdrive/ui/qt/maps/map_settings.h
index 8dd044c37..38c021036 100644
--- a/selfdrive/ui/qt/maps/map_settings.h
+++ b/selfdrive/ui/qt/maps/map_settings.h
@@ -1,46 +1,120 @@
#pragma once
+
+#include
#include
#include
#include
#include
#include
#include
-#include
-#include
#include "common/params.h"
+#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/widgets/controls.h"
-const int MAP_PANEL_ICON_SIZE = 200;
-
const QString NAV_TYPE_FAVORITE = "favorite";
const QString NAV_TYPE_RECENT = "recent";
const QString NAV_FAVORITE_LABEL_HOME = "home";
const QString NAV_FAVORITE_LABEL_WORK = "work";
-class MapPanel : public QWidget {
+class NavDestination;
+class DestinationWidget;
+
+class MapSettings : public QFrame {
Q_OBJECT
public:
- explicit MapPanel(QWidget* parent = nullptr);
+ explicit MapSettings(bool closeable = false, QWidget *parent = nullptr);
void navigateTo(const QJsonObject &place);
void parseResponse(const QString &response, bool success);
void updateCurrentRoute();
- void clear();
private:
void showEvent(QShowEvent *event) override;
void refresh();
Params params;
- QString prev_destinations, cur_destinations;
- QPushButton *home_button, *work_button;
- QLabel *home_address, *work_address;
- QVBoxLayout *recent_layout;
- QWidget *current_widget;
- ButtonControl *current_route;
+ QString cur_destinations;
+ QVBoxLayout *destinations_layout;
+ NavDestination *current_destination;
+ DestinationWidget *current_widget;
+
+ QPixmap close_icon;
signals:
void closeSettings();
};
+
+class NavDestination {
+public:
+ explicit NavDestination(const QJsonObject &place)
+ : type_(place["save_type"].toString()), label_(place["label"].toString()),
+ name_(place["place_name"].toString()), details_(place["place_details"].toString()),
+ latitude_(place["latitude"].toDouble()), longitude_(place["longitude"].toDouble()) {
+ // if details starts with `name, ` remove it
+ if (details_.startsWith(name_ + ", ")) {
+ details_ = details_.mid(name_.length() + 2);
+ }
+ }
+
+ QString type() const { return type_; }
+ QString label() const { return label_; }
+ QString name() const { return name_; }
+ QString details() const { return details_; }
+
+ bool isFavorite() const { return type_ == NAV_TYPE_FAVORITE; }
+ bool isRecent() const { return type_ == NAV_TYPE_RECENT; }
+
+ bool operator==(const NavDestination &rhs) {
+ return type_ == rhs.type_ && label_ == rhs.label_ && name_ == rhs.name_ &&
+ details_ == rhs.details_ && latitude_ == rhs.latitude_ && longitude_ == rhs.longitude_;
+ }
+
+ QJsonObject toJson() const {
+ QJsonObject obj;
+ obj["save_type"] = type_;
+ obj["label"] = label_;
+ obj["place_name"] = name_;
+ obj["place_details"] = details_;
+ obj["latitude"] = latitude_;
+ obj["longitude"] = longitude_;
+ return obj;
+ }
+
+private:
+ QString type_, label_, name_, details_;
+ double latitude_, longitude_;
+};
+
+class DestinationWidget : public QPushButton {
+ Q_OBJECT
+public:
+ explicit DestinationWidget(QWidget *parent = nullptr);
+
+ void set(NavDestination *, bool current = false);
+ void unset(const QString &label, bool current = false);
+
+signals:
+ void actionClicked();
+
+private:
+ struct NavIcons {
+ QPixmap home, work, favorite, recent, directions;
+ };
+
+ static NavIcons icons() {
+ static NavIcons nav_icons {
+ loadPixmap("../assets/navigation/icon_home.svg", {48, 48}),
+ loadPixmap("../assets/navigation/icon_work.svg", {48, 48}),
+ loadPixmap("../assets/navigation/icon_favorite.svg", {48, 48}),
+ loadPixmap("../assets/navigation/icon_recent.svg", {48, 48}),
+ loadPixmap("../assets/navigation/icon_directions.svg", {48, 48}),
+ };
+ return nav_icons;
+ }
+
+private:
+ QLabel *icon, *title, *subtitle;
+ QPushButton *action;
+};
diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc
index d9f200865..83ab77211 100644
--- a/selfdrive/ui/qt/offroad/settings.cc
+++ b/selfdrive/ui/qt/offroad/settings.cc
@@ -8,10 +8,6 @@
#include "selfdrive/ui/qt/offroad/networking.h"
-#ifdef ENABLE_MAPS
-#include "selfdrive/ui/qt/maps/map_settings.h"
-#endif
-
#include "common/params.h"
#include "common/watchdog.h"
#include "common/util.h"
@@ -385,12 +381,6 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
{tr("Software"), new SoftwarePanel(this)},
};
-#ifdef ENABLE_MAPS
- auto map_panel = new MapPanel(this);
- panels.push_back({tr("Navigation"), map_panel});
- QObject::connect(map_panel, &MapPanel::closeSettings, this, &SettingsWindow::closeSettings);
-#endif
-
nav_btns = new QButtonGroup(this);
for (auto &[name, panel] : panels) {
QPushButton *btn = new QPushButton(name);
diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc
index c678d07fa..e9009d42b 100644
--- a/selfdrive/ui/qt/onroad.cc
+++ b/selfdrive/ui/qt/onroad.cc
@@ -3,12 +3,13 @@
#include
#include
+#include
#include "common/timing.h"
#include "selfdrive/ui/qt/util.h"
#ifdef ENABLE_MAPS
-#include "selfdrive/ui/qt/maps/map.h"
#include "selfdrive/ui/qt/maps/map_helpers.h"
+#include "selfdrive/ui/qt/maps/map_panel.h"
#endif
OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) {
@@ -78,10 +79,15 @@ void OnroadWindow::updateState(const UIState &s) {
}
void OnroadWindow::mousePressEvent(QMouseEvent* e) {
+#ifdef ENABLE_MAPS
if (map != nullptr) {
bool sidebarVisible = geometry().x() > 0;
+ if (map->isVisible() && !((MapPanel *)map)->isShowingMap() && e->windowPos().x() >= 1080) {
+ return;
+ }
map->setVisible(!sidebarVisible && !map->isVisible());
}
+#endif
// propagation event to parent(HomeWindow)
QWidget::mousePressEvent(e);
}
@@ -90,16 +96,14 @@ void OnroadWindow::offroadTransition(bool offroad) {
#ifdef ENABLE_MAPS
if (!offroad) {
if (map == nullptr && (uiState()->primeType() || !MAPBOX_TOKEN.isEmpty())) {
- MapWindow * m = new MapWindow(get_mapbox_settings());
+ auto m = new MapPanel(get_mapbox_settings());
map = m;
- QObject::connect(uiState(), &UIState::offroadTransition, m, &MapWindow::offroadTransition);
-
m->setFixedWidth(topWidget(this)->width() / 2 - bdr_s);
split->insertWidget(0, m);
- // Make map visible after adding to split
- m->offroadTransition(offroad);
+ // hidden by default, made visible when navRoute is published
+ m->setVisible(false);
}
}
#endif
diff --git a/selfdrive/ui/qt/widgets/controls.cc b/selfdrive/ui/qt/widgets/controls.cc
index 456cf748f..6693c2247 100644
--- a/selfdrive/ui/qt/widgets/controls.cc
+++ b/selfdrive/ui/qt/widgets/controls.cc
@@ -7,8 +7,6 @@ QFrame *horizontal_line(QWidget *parent) {
QFrame *line = new QFrame(parent);
line->setFrameShape(QFrame::StyledPanel);
line->setStyleSheet(R"(
- margin-left: 40px;
- margin-right: 40px;
border-width: 1px;
border-bottom-style: solid;
border-color: gray;
@@ -127,19 +125,3 @@ void ElidedLabel::paintEvent(QPaintEvent *event) {
opt.initFrom(this);
style()->drawItemText(&painter, contentsRect(), alignment(), opt.palette, isEnabled(), elidedText_, foregroundRole());
}
-
-ClickableWidget::ClickableWidget(QWidget *parent) : QWidget(parent) { }
-
-void ClickableWidget::mouseReleaseEvent(QMouseEvent *event) {
- if (rect().contains(event->pos())) {
- 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/widgets/controls.h b/selfdrive/ui/qt/widgets/controls.h
index 807441cc8..9e745ad1a 100644
--- a/selfdrive/ui/qt/widgets/controls.h
+++ b/selfdrive/ui/qt/widgets/controls.h
@@ -298,17 +298,3 @@ public:
setLayout(l);
}
};
-
-class ClickableWidget : public QWidget {
- Q_OBJECT
-
-public:
- ClickableWidget(QWidget *parent = nullptr);
-
-protected:
- void mouseReleaseEvent(QMouseEvent *event) override;
- void paintEvent(QPaintEvent *) override;
-
-signals:
- void clicked();
-};
diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts
index 18ce67ee9..d67def18e 100644
--- a/selfdrive/ui/translations/main_de.ts
+++ b/selfdrive/ui/translations/main_de.ts
@@ -116,6 +116,33 @@
Ablehnen, deinstallieren %1
+
+ DestinationWidget
+
+ Home
+
+
+
+ Work
+
+
+
+ No destination set
+
+
+
+ No %1 location set
+
+
+
+ home
+
+
+
+ work
+
+
+
DevicePanel
@@ -356,42 +383,14 @@
- MapPanel
-
- Current Destination
- Aktuelles Ziel
-
-
- CLEAR
- LÖSCHEN
-
-
- Recent Destinations
- Letzte Ziele
-
-
- Try the Navigation Beta
- Beta Navigation ausprobieren
-
-
- Get turn-by-turn directions displayed and more with a comma
-prime subscription. Sign up now: https://connect.comma.ai
- Erhalte echtzeit Wegführung und mehr mit dem comma prime
-Abonnement. Melde dich jetzt an: https://connect.comma.ai
-
-
- No home
-location set
- Keine Heimadresse gesetzt
-
+ MapSettings
- No work
-location set
- Keine Arbeitsadresse gesetzt
+ NAVIGATION
+
- no recent destinations
- Keine kürzlich gewählten Ziele
+ Manage at connect.comma.ai
+
@@ -676,10 +675,6 @@ This may take up to a minute.
Software
Software
-
- Navigation
- Navigation
-
Setup
diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts
index ad430615d..8cae4dec1 100644
--- a/selfdrive/ui/translations/main_ja.ts
+++ b/selfdrive/ui/translations/main_ja.ts
@@ -116,6 +116,33 @@
拒否して %1 をアンインストール
+
+ DestinationWidget
+
+ Home
+
+
+
+ Work
+
+
+
+ No destination set
+
+
+
+ No %1 location set
+
+
+
+ home
+
+
+
+ work
+
+
+
DevicePanel
@@ -355,44 +382,14 @@
- MapPanel
-
- Current Destination
- 現在の目的地
-
-
- CLEAR
- 削除
-
-
- Recent Destinations
- 最近の目的地
-
-
- Try the Navigation Beta
- β版ナビゲーションを試す
-
-
- Get turn-by-turn directions displayed and more with a comma
-prime subscription. Sign up now: https://connect.comma.ai
- より詳細な案内情報を得ることができます。
-詳しくはこちら:https://connect.comma.ai
-
-
- No home
-location set
- 自宅の住所はまだ
-設定されていません
-
+ MapSettings
- No work
-location set
- 職場の住所はまだ
-設定されていません
+ NAVIGATION
+
- no recent destinations
- 最近の目的地履歴がありません
+ Manage at connect.comma.ai
+
@@ -674,10 +671,6 @@ This may take up to a minute.
Software
ソフトウェア
-
- Navigation
- ナビゲーション
-
Setup
diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts
index 3e427779e..e024a3999 100644
--- a/selfdrive/ui/translations/main_ko.ts
+++ b/selfdrive/ui/translations/main_ko.ts
@@ -116,6 +116,33 @@
거절, %1 제거
+
+ DestinationWidget
+
+ Home
+
+
+
+ Work
+
+
+
+ No destination set
+
+
+
+ No %1 location set
+
+
+
+ home
+
+
+
+ work
+
+
+
DevicePanel
@@ -355,44 +382,14 @@
- MapPanel
-
- Current Destination
- 현재 목적지
-
-
- CLEAR
- 삭제
-
-
- Recent Destinations
- 최근 목적지
-
-
- Try the Navigation Beta
- 네비게이션(베타)를 사용해보세요
-
-
- Get turn-by-turn directions displayed and more with a comma
-prime subscription. Sign up now: https://connect.comma.ai
- 자세한 경로안내를 원하시면 comma prime을 구독하세요.
-등록:https://connect.comma.ai
-
-
- No home
-location set
- 집
-설정되지않음
-
+ MapSettings
- No work
-location set
- 회사
-설정되지않음
+ NAVIGATION
+
- no recent destinations
- 최근 목적지 없음
+ Manage at connect.comma.ai
+
@@ -675,10 +672,6 @@ This may take up to a minute.
Software
소프트웨어
-
- Navigation
- 네비게이션
-
Setup
diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts
index d7ca16c2c..4235571dc 100644
--- a/selfdrive/ui/translations/main_pt-BR.ts
+++ b/selfdrive/ui/translations/main_pt-BR.ts
@@ -116,6 +116,33 @@
Rejeitar, desintalar %1
+
+ DestinationWidget
+
+ Home
+
+
+
+ Work
+
+
+
+ No destination set
+
+
+
+ No %1 location set
+
+
+
+ home
+
+
+
+ work
+
+
+
DevicePanel
@@ -356,44 +383,14 @@
- MapPanel
-
- Current Destination
- Destino Atual
-
-
- CLEAR
- LIMPAR
-
-
- Recent Destinations
- Destinos Recentes
-
-
- Try the Navigation Beta
- Experimente a Navegação Beta
-
-
- Get turn-by-turn directions displayed and more with a comma
-prime subscription. Sign up now: https://connect.comma.ai
- Obtenha instruções passo a passo exibidas e muito mais com
-uma assinatura prime. Inscreva-se agora: https://connect.comma.ai
-
-
- No home
-location set
- Sem local
-residência definido
-
+ MapSettings
- No work
-location set
- Sem local de
-trabalho definido
+ NAVIGATION
+
- no recent destinations
- sem destinos recentes
+ Manage at connect.comma.ai
+
@@ -679,10 +676,6 @@ Isso pode levar até um minuto.
Software
Software
-
- Navigation
- Navegação
-
Setup
diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts
index c7ddca899..7ff99d182 100644
--- a/selfdrive/ui/translations/main_zh-CHS.ts
+++ b/selfdrive/ui/translations/main_zh-CHS.ts
@@ -116,6 +116,33 @@
拒绝并卸载%1
+
+ DestinationWidget
+
+ Home
+
+
+
+ Work
+
+
+
+ No destination set
+
+
+
+ No %1 location set
+
+
+
+ home
+
+
+
+ work
+
+
+
DevicePanel
@@ -355,42 +382,14 @@
- MapPanel
-
- Current Destination
- 当前目的地
-
-
- CLEAR
- 清空
-
-
- Recent Destinations
- 最近目的地
-
-
- Try the Navigation Beta
- 试用导航测试版
-
-
- Get turn-by-turn directions displayed and more with a comma
-prime subscription. Sign up now: https://connect.comma.ai
- 订阅comma prime以获取导航。
-立即注册:https://connect.comma.ai
-
-
- No home
-location set
- 家:未设定
-
+ MapSettings
- No work
-location set
- 工作:未设定
+ NAVIGATION
+
- no recent destinations
- 无最近目的地
+ Manage at connect.comma.ai
+
@@ -672,10 +671,6 @@ This may take up to a minute.
Software
软件
-
- Navigation
- 导航
-
Setup
diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts
index 06eca034f..748a4f594 100644
--- a/selfdrive/ui/translations/main_zh-CHT.ts
+++ b/selfdrive/ui/translations/main_zh-CHT.ts
@@ -116,6 +116,33 @@
拒絕並解除安裝 %1
+
+ DestinationWidget
+
+ Home
+
+
+
+ Work
+
+
+
+ No destination set
+
+
+
+ No %1 location set
+
+
+
+ home
+
+
+
+ work
+
+
+
DevicePanel
@@ -355,44 +382,14 @@
- MapPanel
-
- Current Destination
- 當前目的地
-
-
- CLEAR
- 清除
-
-
- Recent Destinations
- 最近目的地
-
-
- Try the Navigation Beta
- 試用導航功能
-
-
- Get turn-by-turn directions displayed and more with a comma
-prime subscription. Sign up now: https://connect.comma.ai
- 成為 comma 高級會員來使用導航功能
-立即註冊:https://connect.comma.ai
-
-
- No home
-location set
- 未設定
-住家位置
-
+ MapSettings
- No work
-location set
- 未設定
-工作位置
+ NAVIGATION
+
- no recent destinations
- 沒有最近的導航記錄
+ Manage at connect.comma.ai
+
@@ -674,10 +671,6 @@ This may take up to a minute.
Software
軟體
-
- Navigation
- 導航
-
Setup