Files
StarPilot/selfdrive/ui/qt/sidebar.cc
T
firestar5683 2d7d7a1246 Simple Mode
2026-04-07 23:52:11 -05:00

337 lines
15 KiB
C++

#include "selfdrive/ui/qt/sidebar.h"
#include <cstdlib>
#include <string>
#include <QMouseEvent>
#include "selfdrive/ui/qt/util.h"
void Sidebar::drawMetric(QPainter &p, const QPair<QString, QString> &label, QColor c, int y) {
const QRect rect = {30, y, 240, 126};
p.setPen(Qt::NoPen);
p.setBrush(QBrush(c));
p.setClipRect(rect.x() + 4, rect.y(), 18, rect.height(), Qt::ClipOperation::ReplaceClip);
p.drawRoundedRect(QRect(rect.x() + 4, rect.y() + 4, 100, 118), 18, 18);
p.setClipping(false);
QPen pen = QPen(QColor(0xff, 0xff, 0xff, 0x55));
pen.setWidth(2);
p.setPen(pen);
p.setBrush(Qt::NoBrush);
p.drawRoundedRect(rect, 20, 20);
p.setPen(QColor(0xff, 0xff, 0xff));
p.setFont(InterFont(35, QFont::DemiBold));
p.drawText(rect.adjusted(22, 0, 0, 0), Qt::AlignCenter, label.first + "\n" + label.second);
}
Sidebar::Sidebar(QWidget *parent) : QFrame(parent), onroad(false), flag_pressed(false), settings_pressed(false), mic_indicator_pressed(false) {
home_img = loadPixmap("../assets/images/button_home.png", home_btn.size());
flag_img = loadPixmap("../assets/images/button_flag.png", home_btn.size());
settings_img = loadPixmap("../assets/images/button_settings.png", settings_btn.size(), Qt::IgnoreAspectRatio);
mic_img = loadPixmap("../assets/icons/microphone.png", QSize(30, 30));
link_img = loadPixmap("../assets/icons/link.png", QSize(60, 60));
connect(this, &Sidebar::valueChanged, [=] { update(); });
setAttribute(Qt::WA_OpaquePaintEvent);
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
setFixedWidth(300);
QObject::connect(uiState(), &UIState::uiUpdate, this, &Sidebar::updateState);
pm = std::make_unique<PubMaster>(std::vector<const char*>{"bookmarkButton"});
QObject::connect(starpilotUIState(), &StarPilotUIState::themeUpdated, this, &Sidebar::updateTheme);
}
void Sidebar::mousePressEvent(QMouseEvent *event) {
QPoint pos = event->pos();
static constexpr QRect cpuRect = {30, 496, 240, 126};
static constexpr QRect memoryRect = {30, 654, 240, 126};
static constexpr QRect tempRect = {30, 338, 240, 126};
static int showChip = 0;
static int showMemory = 0;
static int showTemp = 0;
StarPilotUIState *fs = starpilotUIState();
StarPilotUIScene &starpilot_scene = fs->starpilot_scene;
QJsonObject &starpilot_toggles = starpilot_scene.starpilot_toggles;
const bool simple_mode = starpilot_toggles.value("simple_mode").toBool();
if (!simple_mode && cpuRect.contains(pos) && starpilot_toggles.value("developer_ui").toBool()) {
showChip = (showChip + 1) % 3;
params.putBool("ShowCPU", showChip == 1);
params.putBool("ShowGPU", showChip == 2);
} else if (!simple_mode && memoryRect.contains(pos) && starpilot_toggles.value("developer_ui").toBool()) {
showMemory = (showMemory + 1) % 4;
params.putBool("ShowMemoryUsage", showMemory == 1);
params.putBool("ShowStorageLeft", showMemory == 2);
params.putBool("ShowStorageUsed", showMemory == 3);
} else if (!simple_mode && tempRect.contains(pos) && starpilot_toggles.value("developer_ui").toBool()) {
showTemp = (showTemp + 1) % 3;
params.putBool("Fahrenheit", showTemp == 2);
params.putBool("NumericalTemp", showTemp != 0);
} else if (onroad && home_btn.contains(pos)) {
flag_pressed = true;
} else if (settings_btn.contains(pos)) {
settings_pressed = true;
} else if (recording_audio && mic_indicator_btn.contains(event->pos())) {
mic_indicator_pressed = true;
}
if (!(flag_pressed || mic_indicator_pressed || settings_pressed)) {
update();
updateStarPilotToggles();
}
}
void Sidebar::mouseReleaseEvent(QMouseEvent *event) {
if (flag_pressed || settings_pressed || mic_indicator_pressed) {
flag_pressed = settings_pressed = mic_indicator_pressed = false;
update();
}
if (onroad && home_btn.contains(event->pos())) {
MessageBuilder msg;
msg.initEvent().initBookmarkButton();
pm->send("bookmarkButton", msg);
} else if (settings_btn.contains(event->pos())) {
emit openSettings();
} else if (recording_audio && mic_indicator_btn.contains(event->pos())) {
emit openSettings(2, "RecordAudio");
}
}
void Sidebar::offroadTransition(bool offroad) {
onroad = !offroad;
update();
}
void Sidebar::updateState(const UIState &s, const StarPilotUIState &fs) {
if (!isVisible()) return;
const StarPilotUIScene &starpilot_scene = fs.starpilot_scene;
const QJsonObject &starpilot_toggles = starpilot_scene.starpilot_toggles;
const bool simple_mode = starpilot_toggles.value("simple_mode").toBool();
static bool previous_simple_mode = simple_mode;
if (previous_simple_mode != simple_mode) {
updateTheme();
previous_simple_mode = simple_mode;
}
const SubMaster &fpsm = *(fs.sm);
const cereal::StarPilotDeviceState::Reader &starpilotDeviceState = fpsm["starpilotDeviceState"].getStarpilotDeviceState();
auto &sm = *(s.sm);
networking = networking ? networking : window()->findChild<Networking *>("");
bool tethering_on = networking && networking->wifi->tethering_on;
auto deviceState = sm["deviceState"].getDeviceState();
const char *desktop_fake_wifi_env = std::getenv("SP_ALLOW_DESKTOP_FAKE_WIFI");
const bool desktop_force_online = Hardware::PC() && desktop_fake_wifi_env != nullptr && std::string(desktop_fake_wifi_env) != "0";
auto net_type_value = desktop_force_online ? cereal::DeviceState::NetworkType::WIFI : deviceState.getNetworkType();
setProperty("netType", tethering_on ? "Hotspot" : network_type[net_type_value]);
int strength = tethering_on ? 4 : (desktop_force_online ? 4 : (int)deviceState.getNetworkStrength());
setProperty("netStrength", strength > 0 ? strength + 1 : 0);
ItemStatus connectStatus;
auto last_ping = deviceState.getLastAthenaPingTime();
if (desktop_force_online) {
connectStatus = ItemStatus{{tr("CONNECT"), tr("ONLINE")}, simple_mode ? good_color : QColor(starpilot_toggles.value("sidebar_color3").toString())};
} else if (last_ping == 0) {
connectStatus = ItemStatus{{tr("CONNECT"), tr("OFFLINE")}, warning_color};
} else {
connectStatus = nanos_since_boot() - last_ping < 80e9
? ItemStatus{{tr("CONNECT"), tr("ONLINE")}, simple_mode ? good_color : QColor(starpilot_toggles.value("sidebar_color3").toString())}
: ItemStatus{{tr("CONNECT"), tr("ERROR")}, danger_color};
}
setProperty("connectStatus", QVariant::fromValue(connectStatus));
int maxTempC = deviceState.getMaxTempC();
QString max_temp = starpilot_toggles.value("fahrenheit").toBool() ? QString::number(maxTempC * 9 / 5 + 32) + "°F" : QString::number(maxTempC) + "°C";
ItemStatus tempStatus = {{tr("TEMP"), simple_mode ? tr("HIGH") : (starpilot_toggles.value("numerical_temp").toBool() ? max_temp : tr("HIGH"))}, danger_color};
auto ts = deviceState.getThermalStatus();
if (ts == cereal::DeviceState::ThermalStatus::GREEN) {
tempStatus = {{tr("TEMP"), simple_mode ? tr("GOOD") : (starpilot_toggles.value("numerical_temp").toBool() ? max_temp : tr("GOOD"))},
simple_mode ? good_color : QColor(starpilot_toggles.value("sidebar_color1").toString())};
} else if (ts == cereal::DeviceState::ThermalStatus::YELLOW) {
tempStatus = {{tr("TEMP"), simple_mode ? tr("OK") : (starpilot_toggles.value("numerical_temp").toBool() ? max_temp : tr("OK"))}, warning_color};
}
setProperty("tempStatus", QVariant::fromValue(tempStatus));
ItemStatus pandaStatus = {{tr("VEHICLE"), tr("ONLINE")}, simple_mode ? good_color : QColor(starpilot_toggles.value("sidebar_color2").toString())};
if (s.scene.pandaType == cereal::PandaState::PandaType::UNKNOWN) {
pandaStatus = {{tr("NO"), tr("PANDA")}, danger_color};
}
setProperty("pandaStatus", QVariant::fromValue(pandaStatus));
setProperty("recordingAudio", s.scene.recording_audio);
if (!simple_mode && (starpilot_toggles.value("cpu_metrics").toBool() || starpilot_toggles.value("gpu_metrics").toBool())) {
capnp::List<int8_t>::Reader cpu_loads = deviceState.getCpuUsagePercent();
int cpu_usage = cpu_loads.size() != 0 ? std::accumulate(cpu_loads.begin(), cpu_loads.end(), 0) / cpu_loads.size() : 0;
int gpu_usage = deviceState.getGpuUsagePercent();
int usage = starpilot_toggles.value("cpu_metrics").toBool() ? cpu_usage : gpu_usage;
QString chip_usage = QString::number(usage) + "%";
ItemStatus chipStatus = {{starpilot_toggles.value("gpu_metrics").toBool() ? tr("GPU") : tr("CPU"), chip_usage}, QColor(starpilot_toggles.value("sidebar_color2").toString())};
if (usage >= 85) {
chipStatus = {{starpilot_toggles.value("gpu_metrics").toBool() ? tr("GPU") : tr("CPU"), chip_usage}, danger_color};
} else if (usage >= 70) {
chipStatus = {{starpilot_toggles.value("gpu_metrics").toBool() ? tr("GPU") : tr("CPU"), chip_usage}, warning_color};
}
setProperty("chipStatus", QVariant::fromValue(chipStatus));
}
if (!simple_mode && (starpilot_toggles.value("memory_metrics").toBool() || starpilot_toggles.value("storage_left_metrics").toBool() || starpilot_toggles.value("storage_used_metrics").toBool())) {
int free_space = deviceState.getFreeSpacePercent();
int memory_usage = deviceState.getMemoryUsagePercent();
int storage_left = starpilotDeviceState.getFreeSpace();
int storage_used = starpilotDeviceState.getUsedSpace();
QString memory = QString::number(memory_usage) + "%";
QString storage = QString::number(starpilot_toggles.value("storage_left_metrics").toBool() ? storage_left : storage_used) + tr(" GB");
if (starpilot_toggles.value("memory_metrics").toBool()) {
ItemStatus memoryStatus = {{tr("MEMORY"), memory}, QColor(starpilot_toggles.value("sidebar_color3").toString())};
if (memory_usage >= 85) {
memoryStatus = {{tr("MEMORY"), memory}, danger_color};
} else if (memory_usage >= 70) {
memoryStatus = {{tr("MEMORY"), memory}, warning_color};
}
setProperty("memoryStatus", QVariant::fromValue(memoryStatus));
} else {
ItemStatus storageStatus = {{starpilot_toggles.value("storage_left_metrics").toBool() ? tr("LEFT") : tr("USED"), storage}, QColor(starpilot_toggles.value("sidebar_color3").toString())};
if (free_space < 25 && free_space >= 10) {
storageStatus = {{starpilot_toggles.value("storage_left_metrics").toBool() ? tr("LEFT") : tr("USED"), storage}, warning_color};
} else if (10 > free_space) {
storageStatus = {{starpilot_toggles.value("storage_left_metrics").toBool() ? tr("LEFT") : tr("USED"), storage}, danger_color};
}
setProperty("storageStatus", QVariant::fromValue(storageStatus));
}
}
}
void Sidebar::paintEvent(QPaintEvent *event) {
QPainter p(this);
p.setPen(Qt::NoPen);
p.setRenderHint(QPainter::Antialiasing);
p.fillRect(rect(), QColor(57, 57, 57));
// buttons
p.setOpacity(settings_pressed ? 0.65 : 1.0);
p.drawPixmap(settings_btn.x(), settings_btn.y(), settings_gif ? settings_gif->currentPixmap() : settings_img);
p.setOpacity(onroad && flag_pressed ? 0.65 : 1.0);
p.drawPixmap(home_btn.x(), home_btn.y(), onroad ? flag_gif ? flag_gif->currentPixmap() : flag_img : home_gif ? home_gif->currentPixmap() : home_img);
if (recording_audio) {
p.setBrush(danger_color);
p.setOpacity(mic_indicator_pressed ? 0.65 : 1.0);
p.drawRoundedRect(mic_indicator_btn, mic_indicator_btn.height() / 2, mic_indicator_btn.height() / 2);
int icon_x = mic_indicator_btn.x() + (mic_indicator_btn.width() - mic_img.width()) / 2;
int icon_y = mic_indicator_btn.y() + (mic_indicator_btn.height() - mic_img.height()) / 2;
p.drawPixmap(icon_x, icon_y, mic_img);
}
p.setOpacity(1.0);
StarPilotUIState *fs = starpilotUIState();
StarPilotUIScene &starpilot_scene = fs->starpilot_scene;
QJsonObject &starpilot_toggles = starpilot_scene.starpilot_toggles;
const bool simple_mode = starpilot_toggles.value("simple_mode").toBool();
// network
if (!simple_mode && starpilot_toggles.value("ip_metrics").toBool()) {
p.setPen(QColor(0xff, 0xff, 0xff));
p.save();
p.setFont(InterFont(30));
p.drawText(QRect(50, 196, 225, 27), Qt::AlignLeft | Qt::AlignVCenter, starpilotUIState()->wifi->getIp4Address());
p.restore();
} else {
int x = 58;
const QColor gray(0x54, 0x54, 0x54);
for (int i = 0; i < 5; ++i) {
p.setBrush(i < net_strength ? Qt::white : gray);
p.drawEllipse(x, 196, 27, 27);
x += 37;
}
}
p.setFont(InterFont(35));
p.setPen(QColor(0xff, 0xff, 0xff));
const QRect r = QRect(58, 247, width() - 100, 50);
if (net_type == "Hotspot") {
p.drawPixmap(r.x(), r.y() + (r.height() - link_img.height()) / 2, link_img);
} else {
p.drawText(r, Qt::AlignLeft | Qt::AlignVCenter, net_type);
}
// metrics
drawMetric(p, temp_status.first, temp_status.second, 338);
if (simple_mode) {
drawMetric(p, panda_status.first, panda_status.second, 496);
drawMetric(p, connect_status.first, connect_status.second, 654);
} else if (starpilot_toggles.value("cpu_metrics").toBool() || starpilot_toggles.value("gpu_metrics").toBool()) {
drawMetric(p, chip_status.first, chip_status.second, 496);
} else {
drawMetric(p, panda_status.first, panda_status.second, 496);
}
if (starpilot_toggles.value("memory_metrics").toBool()) {
drawMetric(p, memory_status.first, memory_status.second, 654);
} else if (starpilot_toggles.value("storage_left_metrics").toBool() || starpilot_toggles.value("storage_used_metrics").toBool()) {
drawMetric(p, storage_status.first, storage_status.second, 654);
} else {
drawMetric(p, connect_status.first, connect_status.second, 654);
}
}
void Sidebar::showEvent(QShowEvent *event) {
updateTheme();
}
void Sidebar::updateTheme() {
auto clearMovie = [](QSharedPointer<QMovie> &movie) {
if (!movie.isNull()) {
movie->stop();
movie.reset();
}
};
if (starpilotUIState()->starpilot_scene.starpilot_toggles.value("simple_mode").toBool()) {
clearMovie(home_gif);
clearMovie(flag_gif);
clearMovie(settings_gif);
home_img = loadPixmap("../assets/images/button_home.png", home_btn.size());
flag_img = loadPixmap("../assets/images/button_flag.png", home_btn.size());
settings_img = loadPixmap("../assets/images/button_settings.png", settings_btn.size(), Qt::IgnoreAspectRatio);
return;
}
loadImage("../../starpilot/assets/active_theme/icons/button_home", home_img, home_gif, home_btn.size(), this);
loadImage("../../starpilot/assets/active_theme/icons/button_flag", flag_img, flag_gif, home_btn.size(), this);
loadImage("../../starpilot/assets/active_theme/icons/button_settings", settings_img, settings_gif, settings_btn.size(), this);
// Keep stock icons visible when a theme is partial or missing an icon.
if (!home_gif && home_img.isNull()) {
home_img = loadPixmap("../assets/images/button_home.png", home_btn.size());
}
if (!flag_gif && flag_img.isNull()) {
flag_img = loadPixmap("../assets/images/button_flag.png", home_btn.size());
}
if (!settings_gif && settings_img.isNull()) {
settings_img = loadPixmap("../assets/images/button_settings.png", settings_btn.size(), Qt::IgnoreAspectRatio);
}
}