mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-07-17 23:32:07 +08:00
Qt Sidebar (#20775)
* revive #19877 * looks like a sidebar * more like a sidebar * cleanup * fix qcom2 * style * that's the sidebar * more space Co-authored-by: Comma Device <device@comma.ai> old-commit-hash: db9319405b5a650627a45de78a81afaed4ae2128
This commit is contained in:
@@ -27,7 +27,7 @@ qt_env.Program("qt/text", ["qt/text.cc"], LIBS=qt_libs)
|
||||
qt_env.Program("qt/spinner", ["qt/spinner.cc"], LIBS=base_libs)
|
||||
|
||||
# build main UI
|
||||
qt_src = ["main.cc", "ui.cc", "paint.cc", "sidebar.cc", "qt/onroad.cc",
|
||||
qt_src = ["main.cc", "ui.cc", "paint.cc", "qt/sidebar.cc", "qt/onroad.cc",
|
||||
"qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc",
|
||||
"qt/offroad/onboarding.cc", "#phonelibs/nanovg/nanovg.c"]
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "nanovg_gl.h"
|
||||
#include "nanovg_gl_utils.h"
|
||||
#include "paint.hpp"
|
||||
#include "sidebar.hpp"
|
||||
|
||||
// TODO: this is also hardcoded in common/transformations/camera.py
|
||||
// TODO: choose based on frame input size
|
||||
@@ -240,7 +239,6 @@ static void ui_draw_vision_face(UIState *s) {
|
||||
}
|
||||
|
||||
static void ui_draw_driver_view(UIState *s) {
|
||||
s->sidebar_collapsed = true;
|
||||
const bool is_rhd = s->scene.is_rhd;
|
||||
const int width = 4 * s->viz_rect.h / 3;
|
||||
const Rect rect = {s->viz_rect.centerX() - width / 2, s->viz_rect.y, width, s->viz_rect.h}; // x, y, w, h
|
||||
@@ -377,12 +375,8 @@ static void ui_draw_background(UIState *s) {
|
||||
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void ui_draw(UIState *s) {
|
||||
s->viz_rect = Rect{bdr_s, bdr_s, s->fb_w - 2 * bdr_s, s->fb_h - 2 * bdr_s};
|
||||
if (!s->sidebar_collapsed) {
|
||||
s->viz_rect.x += sbr_w;
|
||||
s->viz_rect.w -= sbr_w;
|
||||
}
|
||||
void ui_draw(UIState *s, int w, int h) {
|
||||
s->viz_rect = Rect{bdr_s, bdr_s, w - 2 * bdr_s, h - 2 * bdr_s};
|
||||
|
||||
const bool draw_alerts = s->scene.started;
|
||||
const bool draw_vision = draw_alerts && s->vipc_client->connected;
|
||||
@@ -398,7 +392,7 @@ void ui_draw(UIState *s) {
|
||||
|
||||
// NVG drawing functions - should be no GL inside NVG frame
|
||||
nvgBeginFrame(s->vg, s->fb_w, s->fb_h, 1.0f);
|
||||
ui_draw_sidebar(s);
|
||||
|
||||
if (draw_vision) {
|
||||
ui_draw_vision(s);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "ui.hpp"
|
||||
|
||||
void ui_draw(UIState *s);
|
||||
void ui_draw(UIState *s, int w, int h);
|
||||
void ui_draw_image(const UIState *s, const Rect &r, const char *name, float alpha);
|
||||
void ui_draw_rect(NVGcontext *vg, const Rect &r, NVGcolor color, int width, float radius = 0);
|
||||
void ui_fill_rect(NVGcontext *vg, const Rect &r, const NVGpaint &paint, float radius = 0);
|
||||
|
||||
+30
-16
@@ -15,22 +15,38 @@
|
||||
// HomeWindow: the container for the offroad and onroad UIs
|
||||
|
||||
HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
|
||||
layout = new QStackedLayout();
|
||||
layout->setStackingMode(QStackedLayout::StackAll);
|
||||
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
|
||||
sidebar = new Sidebar(this);
|
||||
layout->addWidget(sidebar);
|
||||
QObject::connect(this, &HomeWindow::update, sidebar, &Sidebar::update);
|
||||
QObject::connect(sidebar, &Sidebar::openSettings, this, &HomeWindow::openSettings);
|
||||
|
||||
slayout = new QStackedLayout();
|
||||
layout->addLayout(slayout);
|
||||
|
||||
onroad = new OnroadWindow(this);
|
||||
layout->addWidget(onroad);
|
||||
slayout->addWidget(onroad);
|
||||
QObject::connect(this, &HomeWindow::update, onroad, &OnroadWindow::update);
|
||||
QObject::connect(this, &HomeWindow::displayPowerChanged, onroad, &OnroadWindow::setEnabled);
|
||||
|
||||
|
||||
home = new OffroadHome();
|
||||
layout->addWidget(home);
|
||||
slayout->addWidget(home);
|
||||
QObject::connect(this, &HomeWindow::openSettings, home, &OffroadHome::refresh);
|
||||
QObject::connect(this, &HomeWindow::offroadTransition, home, &OffroadHome::setVisible);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void HomeWindow::offroadTransition(bool offroad) {
|
||||
if (offroad) {
|
||||
slayout->setCurrentWidget(home);
|
||||
} else {
|
||||
slayout->setCurrentWidget(onroad);
|
||||
}
|
||||
sidebar->setVisible(offroad);
|
||||
}
|
||||
|
||||
void HomeWindow::mousePressEvent(QMouseEvent* e) {
|
||||
// TODO: make a nice driver view widget
|
||||
if (QUIState::ui_state.scene.driver_view) {
|
||||
@@ -39,23 +55,18 @@ void HomeWindow::mousePressEvent(QMouseEvent* e) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Settings button click
|
||||
if (!QUIState::ui_state.sidebar_collapsed && settings_btn.ptInRect(e->x(), e->y())) {
|
||||
emit openSettings();
|
||||
}
|
||||
|
||||
// Handle sidebar collapsing
|
||||
if (QUIState::ui_state.scene.started && (e->x() >= QUIState::ui_state.viz_rect.x - bdr_s)) {
|
||||
QUIState::ui_state.sidebar_collapsed = !QUIState::ui_state.sidebar_collapsed;
|
||||
if (childAt(e->pos()) == onroad) {
|
||||
sidebar->setVisible(!sidebar->isVisible());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// OffroadHome: the offroad home page
|
||||
|
||||
OffroadHome::OffroadHome(QWidget* parent) : QWidget(parent) {
|
||||
OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) {
|
||||
QVBoxLayout* main_layout = new QVBoxLayout();
|
||||
main_layout->setContentsMargins(sbr_w + 50, 50, 50, 50);
|
||||
main_layout->setMargin(50);
|
||||
|
||||
// top header
|
||||
QHBoxLayout* header_layout = new QHBoxLayout();
|
||||
@@ -111,6 +122,9 @@ OffroadHome::OffroadHome(QWidget* parent) : QWidget(parent) {
|
||||
|
||||
setLayout(main_layout);
|
||||
setStyleSheet(R"(
|
||||
OffroadHome {
|
||||
background-color: black;
|
||||
}
|
||||
* {
|
||||
color: white;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <QFrame>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QStackedLayout>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#include "sidebar.hpp"
|
||||
#include "onroad.hpp"
|
||||
#include "ui/ui.hpp"
|
||||
#include "widgets/offroad_alerts.hpp"
|
||||
|
||||
class OffroadHome : public QWidget {
|
||||
class OffroadHome : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@@ -42,14 +44,17 @@ signals:
|
||||
|
||||
// forwarded signals
|
||||
void displayPowerChanged(bool on);
|
||||
void offroadTransition(bool offroad);
|
||||
void update(const UIState &s);
|
||||
|
||||
public slots:
|
||||
void offroadTransition(bool offroad);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent* e) override;
|
||||
|
||||
private:
|
||||
Sidebar *sidebar;
|
||||
OffroadHome *home;
|
||||
OnroadWindow *onroad;
|
||||
QStackedLayout *layout;
|
||||
QStackedLayout *slayout;
|
||||
};
|
||||
|
||||
@@ -39,12 +39,8 @@ void OnroadWindow::update(const UIState &s) {
|
||||
}
|
||||
}
|
||||
|
||||
void OnroadWindow::resizeGL(int w, int h) {
|
||||
std::cout << "resize " << w << "x" << h << std::endl;
|
||||
}
|
||||
|
||||
void OnroadWindow::paintGL() {
|
||||
ui_draw(&QUIState::ui_state);
|
||||
ui_draw(&QUIState::ui_state, width(), height());
|
||||
|
||||
double cur_draw_t = millis_since_boot();
|
||||
double dt = cur_draw_t - prev_draw_t;
|
||||
|
||||
@@ -18,7 +18,6 @@ public:
|
||||
protected:
|
||||
void paintGL() override;
|
||||
void initializeGL() override;
|
||||
void resizeGL(int w, int h) override;
|
||||
|
||||
private:
|
||||
bool enabled;
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
#include "common/util.h"
|
||||
#include "sidebar.hpp"
|
||||
#include "qt_window.hpp"
|
||||
|
||||
StatusWidget::StatusWidget(QString label, QString msg, QColor c, QWidget* parent) : QFrame(parent) {
|
||||
layout.setSpacing(0);
|
||||
|
||||
if(msg.length() > 0){
|
||||
layout.setContentsMargins(50, 24, 16, 24);
|
||||
status.setAlignment(Qt::AlignLeft | Qt::AlignHCenter);
|
||||
status.setStyleSheet(R"(font-size: 65px; font-weight: 500;)");
|
||||
|
||||
substatus.setAlignment(Qt::AlignLeft | Qt::AlignHCenter);
|
||||
substatus.setStyleSheet(R"(font-size: 30px; font-weight: 400;)");
|
||||
|
||||
layout.addWidget(&status, 0, Qt::AlignLeft);
|
||||
layout.addWidget(&substatus, 0, Qt::AlignLeft);
|
||||
} else {
|
||||
layout.setContentsMargins(40, 24, 16, 24);
|
||||
|
||||
status.setAlignment(Qt::AlignCenter);
|
||||
status.setStyleSheet(R"(font-size: 38px; font-weight: 500;)");
|
||||
layout.addWidget(&status, 0, Qt::AlignCenter);
|
||||
}
|
||||
|
||||
update(label, msg, c);
|
||||
|
||||
setMinimumHeight(124);
|
||||
setStyleSheet("background-color: transparent;");
|
||||
setLayout(&layout);
|
||||
}
|
||||
|
||||
void StatusWidget::paintEvent(QPaintEvent *e){
|
||||
QPainter p(this);
|
||||
p.setRenderHint(QPainter::Antialiasing, true);
|
||||
p.setPen(QPen(QColor(0xb2b2b2), 3, Qt::SolidLine, Qt::FlatCap));
|
||||
// origin at 1.5,1.5 because qt issues with pixel perfect borders
|
||||
p.drawRoundedRect(QRectF(1.5, 1.5, size().width()-3, size().height()-3), 30, 30);
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(color);
|
||||
p.setClipRect(0,0,25+6,size().height()-6,Qt::ClipOperation::ReplaceClip);
|
||||
p.drawRoundedRect(QRectF(6, 6, size().width()-12, size().height()-12), 25, 25);
|
||||
}
|
||||
|
||||
void StatusWidget::update(QString label, QString msg, QColor c) {
|
||||
status.setText(label);
|
||||
substatus.setText(msg);
|
||||
color = c;
|
||||
return;
|
||||
}
|
||||
|
||||
SignalWidget::SignalWidget(QString text, int strength, QWidget* parent) : QFrame(parent), _strength(strength) {
|
||||
layout.setMargin(0);
|
||||
layout.setSpacing(0);
|
||||
layout.insertSpacing(0, 45);
|
||||
|
||||
label.setText(text);
|
||||
layout.addWidget(&label, 0, Qt::AlignLeft);
|
||||
label.setStyleSheet(R"(font-size: 35px; font-weight: 400;)");
|
||||
|
||||
setFixedWidth(177);
|
||||
setLayout(&layout);
|
||||
}
|
||||
|
||||
void SignalWidget::paintEvent(QPaintEvent *e){
|
||||
QPainter p(this);
|
||||
p.setRenderHint(QPainter::Antialiasing, true);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(Qt::white);
|
||||
for (int i = 0; i < 5 ; i++){
|
||||
if(i == _strength){
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(Qt::darkGray);
|
||||
}
|
||||
p.drawEllipse(QRectF(_dotspace * i, _top, _dia, _dia));
|
||||
}
|
||||
}
|
||||
|
||||
void SignalWidget::update(QString text, int strength){
|
||||
label.setText(text);
|
||||
_strength = strength;
|
||||
}
|
||||
|
||||
Sidebar::Sidebar(QWidget* parent) : QFrame(parent) {
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
layout->setContentsMargins(25, 50, 25, 50);
|
||||
layout->setSpacing(35);
|
||||
setFixedSize(300, vwp_h);
|
||||
|
||||
QPushButton *s_btn = new QPushButton;
|
||||
s_btn->setStyleSheet(R"(
|
||||
border-image: url(../assets/images/button_settings.png);
|
||||
)");
|
||||
s_btn->setFixedSize(200, 117);
|
||||
layout->addWidget(s_btn, 0, Qt::AlignHCenter);
|
||||
QObject::connect(s_btn, &QPushButton::pressed, this, &Sidebar::openSettings);
|
||||
|
||||
signal = new SignalWidget("--", 0, this);
|
||||
layout->addWidget(signal, 0, Qt::AlignTop | Qt::AlignHCenter);
|
||||
|
||||
temp = new StatusWidget("0°C", "TEMP", QColor(255, 255, 255), this);
|
||||
layout->addWidget(temp, 0, Qt::AlignTop);
|
||||
|
||||
panda = new StatusWidget("NO\nPANDA", "", QColor(201, 34, 49), this);
|
||||
layout->addWidget(panda, 0, Qt::AlignTop);
|
||||
|
||||
connect = new StatusWidget("CONNECT\nOFFLINE", "", QColor(218, 202, 37), this);
|
||||
layout->addWidget(connect, 0, Qt::AlignTop);
|
||||
|
||||
QImage image = QImageReader("../assets/images/button_home.png").read();
|
||||
QLabel *comma = new QLabel(this);
|
||||
comma->setPixmap(QPixmap::fromImage(image));
|
||||
comma->setAlignment(Qt::AlignCenter);
|
||||
layout->addWidget(comma, 1, Qt::AlignHCenter | Qt::AlignVCenter);
|
||||
|
||||
layout->addStretch(1);
|
||||
|
||||
setStyleSheet(R"(
|
||||
Sidebar {
|
||||
background-color: #393939;
|
||||
}
|
||||
* {
|
||||
color: white;
|
||||
}
|
||||
)");
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void Sidebar::update(const UIState &s){
|
||||
static std::map<NetStatus, std::pair<QString, QColor>> connectivity_map = {
|
||||
{NET_ERROR, {"CONNECT\nERROR", COLOR_DANGER}},
|
||||
{NET_CONNECTED, {"CONNECT\nONLINE", COLOR_GOOD}},
|
||||
{NET_DISCONNECTED, {"CONNECT\nOFFLINE", COLOR_WARNING}},
|
||||
};
|
||||
auto net_params = connectivity_map[s.scene.athenaStatus];
|
||||
connect->update(net_params.first, "", net_params.second);
|
||||
|
||||
static std::map<cereal::DeviceState::ThermalStatus, QColor> temp_severity_map = {
|
||||
{cereal::DeviceState::ThermalStatus::GREEN, COLOR_GOOD},
|
||||
{cereal::DeviceState::ThermalStatus::YELLOW, COLOR_WARNING},
|
||||
{cereal::DeviceState::ThermalStatus::RED, COLOR_DANGER},
|
||||
{cereal::DeviceState::ThermalStatus::DANGER, COLOR_DANGER}};
|
||||
QString temp_val = QString("%1 °C").arg((int)s.scene.deviceState.getAmbientTempC());
|
||||
temp->update(temp_val, "TEMP", temp_severity_map[s.scene.deviceState.getThermalStatus()]);
|
||||
|
||||
static std::map<cereal::DeviceState::NetworkType, const char *> network_type_map = {
|
||||
{cereal::DeviceState::NetworkType::NONE, "--"},
|
||||
{cereal::DeviceState::NetworkType::WIFI, "WiFi"},
|
||||
{cereal::DeviceState::NetworkType::CELL2_G, "2G"},
|
||||
{cereal::DeviceState::NetworkType::CELL3_G, "3G"},
|
||||
{cereal::DeviceState::NetworkType::CELL4_G, "4G"},
|
||||
{cereal::DeviceState::NetworkType::CELL5_G, "5G"}};
|
||||
const char *network_type = network_type_map[s.scene.deviceState.getNetworkType()];
|
||||
static std::map<cereal::DeviceState::NetworkStrength, int> network_strength_map = {
|
||||
{cereal::DeviceState::NetworkStrength::UNKNOWN, 1},
|
||||
{cereal::DeviceState::NetworkStrength::POOR, 2},
|
||||
{cereal::DeviceState::NetworkStrength::MODERATE, 3},
|
||||
{cereal::DeviceState::NetworkStrength::GOOD, 4},
|
||||
{cereal::DeviceState::NetworkStrength::GREAT, 5}};
|
||||
const int img_idx = s.scene.deviceState.getNetworkType() == cereal::DeviceState::NetworkType::NONE ? 0 : network_strength_map[s.scene.deviceState.getNetworkStrength()];
|
||||
signal->update(network_type, img_idx);
|
||||
|
||||
QColor panda_color = COLOR_GOOD;
|
||||
QString panda_message = "VEHICLE\nONLINE";
|
||||
if (s.scene.pandaType == cereal::PandaState::PandaType::UNKNOWN) {
|
||||
panda_color = COLOR_DANGER;
|
||||
panda_message = "NO\nPANDA";
|
||||
}
|
||||
#ifdef QCOM2
|
||||
else if (s.scene.started) {
|
||||
panda_color = s.scene.gpsOK ? COLOR_GOOD : COLOR_WARNING;
|
||||
panda_message = QString("SAT CNT\n%1").arg(s.scene.satelliteCount);
|
||||
}
|
||||
#endif
|
||||
panda->update(panda_message, "", panda_color);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
#include <ui.hpp>
|
||||
|
||||
#define COLOR_GOOD QColor(255, 255, 255)
|
||||
#define COLOR_WARNING QColor(218, 202, 37)
|
||||
#define COLOR_DANGER QColor(201, 34, 49)
|
||||
|
||||
class SignalWidget : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SignalWidget(QString text, int strength, QWidget* parent = 0);
|
||||
void update(QString text, int strength);
|
||||
QLabel label;
|
||||
int _strength = 0;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent*) override;
|
||||
|
||||
private:
|
||||
QVBoxLayout layout;
|
||||
|
||||
const float _dotspace = 37; // spacing between dots
|
||||
const float _top = 10;
|
||||
const float _dia = 28; // dot diameter
|
||||
};
|
||||
|
||||
class StatusWidget : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
StatusWidget(QString label, QString msg, QColor c, QWidget* parent = 0);
|
||||
void update(QString label, QString msg, QColor c);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent*) override;
|
||||
|
||||
private:
|
||||
QColor color = COLOR_WARNING;
|
||||
QLabel status;
|
||||
QLabel substatus;
|
||||
QVBoxLayout layout;
|
||||
};
|
||||
|
||||
class Sidebar : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Sidebar(QWidget* parent = 0);
|
||||
|
||||
signals:
|
||||
void openSettings();
|
||||
|
||||
public slots:
|
||||
void update(const UIState &s);
|
||||
|
||||
private:
|
||||
SignalWidget *signal;
|
||||
StatusWidget *temp;
|
||||
StatusWidget *panda;
|
||||
StatusWidget *connect;
|
||||
};
|
||||
@@ -1,143 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <map>
|
||||
#include "common/util.h"
|
||||
#include "paint.hpp"
|
||||
#include "sidebar.hpp"
|
||||
|
||||
static void draw_background(UIState *s) {
|
||||
const NVGcolor color = nvgRGBA(0x39, 0x39, 0x39, 0xff);
|
||||
ui_fill_rect(s->vg, {0, 0, sbr_w, s->fb_h}, color);
|
||||
}
|
||||
|
||||
static void draw_settings_button(UIState *s) {
|
||||
ui_draw_image(s, settings_btn, "button_settings", 0.65f);
|
||||
}
|
||||
|
||||
static void draw_home_button(UIState *s) {
|
||||
ui_draw_image(s, home_btn, "button_home", 1.0f);
|
||||
}
|
||||
|
||||
static void draw_network_strength(UIState *s) {
|
||||
static std::map<cereal::DeviceState::NetworkStrength, int> network_strength_map = {
|
||||
{cereal::DeviceState::NetworkStrength::UNKNOWN, 1},
|
||||
{cereal::DeviceState::NetworkStrength::POOR, 2},
|
||||
{cereal::DeviceState::NetworkStrength::MODERATE, 3},
|
||||
{cereal::DeviceState::NetworkStrength::GOOD, 4},
|
||||
{cereal::DeviceState::NetworkStrength::GREAT, 5}};
|
||||
const int img_idx = s->scene.deviceState.getNetworkType() == cereal::DeviceState::NetworkType::NONE ? 0 : network_strength_map[s->scene.deviceState.getNetworkStrength()];
|
||||
ui_draw_image(s, {58, 196, 176, 27}, util::string_format("network_%d", img_idx).c_str(), 1.0f);
|
||||
}
|
||||
|
||||
static void draw_network_type(UIState *s) {
|
||||
static std::map<cereal::DeviceState::NetworkType, const char *> network_type_map = {
|
||||
{cereal::DeviceState::NetworkType::NONE, "--"},
|
||||
{cereal::DeviceState::NetworkType::WIFI, "WiFi"},
|
||||
{cereal::DeviceState::NetworkType::CELL2_G, "2G"},
|
||||
{cereal::DeviceState::NetworkType::CELL3_G, "3G"},
|
||||
{cereal::DeviceState::NetworkType::CELL4_G, "4G"},
|
||||
{cereal::DeviceState::NetworkType::CELL5_G, "5G"}};
|
||||
const int network_x = 50;
|
||||
const int network_y = 273;
|
||||
const int network_w = 100;
|
||||
const char *network_type = network_type_map[s->scene.deviceState.getNetworkType()];
|
||||
nvgFillColor(s->vg, COLOR_WHITE);
|
||||
nvgFontSize(s->vg, 48);
|
||||
nvgFontFace(s->vg, "sans-regular");
|
||||
nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
|
||||
nvgTextBox(s->vg, network_x, network_y, network_w, network_type ? network_type : "--", NULL);
|
||||
}
|
||||
|
||||
static void draw_metric(UIState *s, const char *label_str, const char *value_str, const int severity, const int y_offset, const char *message_str) {
|
||||
NVGcolor status_color;
|
||||
|
||||
if (severity == 0) {
|
||||
status_color = COLOR_WHITE;
|
||||
} else if (severity == 1) {
|
||||
status_color = COLOR_YELLOW;
|
||||
} else if (severity > 1) {
|
||||
status_color = COLOR_RED;
|
||||
}
|
||||
|
||||
const Rect rect = {30, 338 + y_offset, 240, message_str ? strchr(message_str, '\n') ? 124 : 100 : 148};
|
||||
ui_draw_rect(s->vg, rect, severity > 0 ? COLOR_WHITE : COLOR_WHITE_ALPHA(85), 2, 20.);
|
||||
|
||||
nvgBeginPath(s->vg);
|
||||
nvgRoundedRectVarying(s->vg, rect.x + 6, rect.y + 6, 18, rect.h - 12, 25, 0, 0, 25);
|
||||
nvgFillColor(s->vg, status_color);
|
||||
nvgFill(s->vg);
|
||||
|
||||
if (!message_str) {
|
||||
nvgFillColor(s->vg, COLOR_WHITE);
|
||||
nvgFontSize(s->vg, 78);
|
||||
nvgFontFace(s->vg, "sans-bold");
|
||||
nvgTextAlign(s->vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
|
||||
nvgTextBox(s->vg, rect.x + 50, rect.y + 50, rect.w - 60, value_str, NULL);
|
||||
|
||||
nvgFillColor(s->vg, COLOR_WHITE);
|
||||
nvgFontSize(s->vg, 48);
|
||||
nvgFontFace(s->vg, "sans-regular");
|
||||
nvgTextAlign(s->vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
|
||||
nvgTextBox(s->vg, rect.x + 50, rect.y + 50 + 66, rect.w - 60, label_str, NULL);
|
||||
} else {
|
||||
nvgFillColor(s->vg, COLOR_WHITE);
|
||||
nvgFontSize(s->vg, 48);
|
||||
nvgFontFace(s->vg, "sans-bold");
|
||||
nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
|
||||
nvgTextBox(s->vg, rect.x + 35, rect.y + (strchr(message_str, '\n') ? 40 : 50), rect.w - 50, message_str, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_temp_metric(UIState *s) {
|
||||
static std::map<cereal::DeviceState::ThermalStatus, const int> temp_severity_map = {
|
||||
{cereal::DeviceState::ThermalStatus::GREEN, 0},
|
||||
{cereal::DeviceState::ThermalStatus::YELLOW, 1},
|
||||
{cereal::DeviceState::ThermalStatus::RED, 2},
|
||||
{cereal::DeviceState::ThermalStatus::DANGER, 3}};
|
||||
std::string temp_val = std::to_string((int)s->scene.deviceState.getAmbientTempC()) + "°C";
|
||||
draw_metric(s, "TEMP", temp_val.c_str(), temp_severity_map[s->scene.deviceState.getThermalStatus()], 0, NULL);
|
||||
}
|
||||
|
||||
static void draw_panda_metric(UIState *s) {
|
||||
const int panda_y_offset = 32 + 148;
|
||||
|
||||
int panda_severity = 0;
|
||||
std::string panda_message = "VEHICLE\nONLINE";
|
||||
if (s->scene.pandaType == cereal::PandaState::PandaType::UNKNOWN) {
|
||||
panda_severity = 2;
|
||||
panda_message = "NO\nPANDA";
|
||||
}
|
||||
#ifdef QCOM2
|
||||
else if (s->scene.started) {
|
||||
panda_severity = s->scene.gpsOK ? 0 : 1;
|
||||
panda_message = util::string_format("SAT CNT\n%d", s->scene.satelliteCount);
|
||||
}
|
||||
#endif
|
||||
|
||||
draw_metric(s, NULL, NULL, panda_severity, panda_y_offset, panda_message.c_str());
|
||||
}
|
||||
|
||||
static void draw_connectivity(UIState *s) {
|
||||
static std::map<NetStatus, std::pair<const char *, int>> connectivity_map = {
|
||||
{NET_ERROR, {"CONNECT\nERROR", 2}},
|
||||
{NET_CONNECTED, {"CONNECT\nONLINE", 0}},
|
||||
{NET_DISCONNECTED, {"CONNECT\nOFFLINE", 1}},
|
||||
};
|
||||
auto net_params = connectivity_map[s->scene.athenaStatus];
|
||||
draw_metric(s, NULL, NULL, net_params.second, 180 + 158, net_params.first);
|
||||
}
|
||||
|
||||
void ui_draw_sidebar(UIState *s) {
|
||||
if (s->sidebar_collapsed) {
|
||||
return;
|
||||
}
|
||||
draw_background(s);
|
||||
draw_settings_button(s);
|
||||
draw_home_button(s);
|
||||
draw_network_strength(s);
|
||||
draw_network_type(s);
|
||||
draw_temp_metric(s);
|
||||
draw_panda_metric(s);
|
||||
draw_connectivity(s);
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
#pragma once
|
||||
#include "ui.hpp"
|
||||
|
||||
void ui_draw_sidebar(UIState *s);
|
||||
@@ -314,12 +314,10 @@ static void update_status(UIState *s) {
|
||||
|
||||
s->scene.is_rhd = Params().getBool("IsRHD");
|
||||
s->scene.end_to_end = Params().getBool("EndToEndToggle");
|
||||
s->sidebar_collapsed = true;
|
||||
s->scene.alert_size = cereal::ControlsState::AlertSize::NONE;
|
||||
s->vipc_client = s->scene.driver_view ? s->vipc_client_front : s->vipc_client_rear;
|
||||
} else {
|
||||
s->status = STATUS_OFFROAD;
|
||||
s->sidebar_collapsed = false;
|
||||
s->sound->stop();
|
||||
s->vipc_client->connected = false;
|
||||
}
|
||||
|
||||
@@ -44,12 +44,9 @@ typedef struct Rect {
|
||||
}
|
||||
} Rect;
|
||||
|
||||
const int sbr_w = 300;
|
||||
const int bdr_s = 30;
|
||||
const int header_h = 420;
|
||||
const int footer_h = 280;
|
||||
const Rect settings_btn = {50, 35, 200, 117};
|
||||
const Rect home_btn = {60, 1080 - 180 - 40, 180, 180};
|
||||
|
||||
const int UI_FREQ = 20; // Hz
|
||||
|
||||
@@ -157,7 +154,6 @@ typedef struct UIState {
|
||||
|
||||
bool awake;
|
||||
|
||||
bool sidebar_collapsed;
|
||||
Rect video_rect, viz_rect;
|
||||
float car_space_transform[6];
|
||||
bool wide_camera;
|
||||
|
||||
Reference in New Issue
Block a user