diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 0dbac8b484..0d34d0ddcc 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -37,8 +37,10 @@ ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), auto_scroll_tim int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize); toolbar->setIconSize({icon_size, icon_size}); - QAction *new_plot_btn = toolbar->addAction(utils::icon("file-plus"), tr("New Chart")); - QAction *new_tab_btn = toolbar->addAction(utils::icon("window-stack"), tr("New Tab")); + auto new_plot_btn = new ToolButton("file-plus", tr("New Chart")); + auto new_tab_btn = new ToolButton("window-stack", tr("New Tab")); + toolbar->addWidget(new_plot_btn); + toolbar->addWidget(new_tab_btn); toolbar->addWidget(title_label = new QLabel()); title_label->setContentsMargins(0, 0, style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing), 0); @@ -65,18 +67,15 @@ ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), auto_scroll_tim // zoom controls zoom_undo_stack = new QUndoStack(this); - undo_zoom_action = zoom_undo_stack->createUndoAction(this); + toolbar->addAction(undo_zoom_action = zoom_undo_stack->createUndoAction(this)); undo_zoom_action->setIcon(utils::icon("arrow-counterclockwise")); - toolbar->addAction(undo_zoom_action); - redo_zoom_action = zoom_undo_stack->createRedoAction(this); + toolbar->addAction(redo_zoom_action = zoom_undo_stack->createRedoAction(this)); redo_zoom_action->setIcon(utils::icon("arrow-clockwise")); - toolbar->addAction(redo_zoom_action); - reset_zoom_action = toolbar->addAction(utils::icon("zoom-out"), ""); - reset_zoom_action->setToolTip(tr("Reset zoom")); - qobject_cast(toolbar->widgetForAction(reset_zoom_action))->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + reset_zoom_action = toolbar->addWidget(reset_zoom_btn = new ToolButton("zoom-out", tr("Reset Zoom"))); + reset_zoom_btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - remove_all_btn = toolbar->addAction(utils::icon("x"), tr("Remove all charts")); - dock_btn = toolbar->addAction(""); + toolbar->addWidget(remove_all_btn = new ToolButton("x", tr("Remove all charts"))); + toolbar->addWidget(dock_btn = new ToolButton("")); main_layout->addWidget(toolbar); // tabbar @@ -101,6 +100,7 @@ ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), auto_scroll_tim main_layout->addWidget(charts_scroll); // init settings + current_theme = settings.theme; column_count = std::clamp(settings.chart_column_count, 1, MAX_COLUMN_COUNT); max_chart_range = std::clamp(settings.chart_range, 1, settings.max_cached_minutes * 60); display_range = {0, max_chart_range}; @@ -114,16 +114,16 @@ ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), auto_scroll_tim QObject::connect(can, &AbstractStream::eventsMerged, this, &ChartsWidget::eventsMerged); QObject::connect(can, &AbstractStream::updated, this, &ChartsWidget::updateState); QObject::connect(range_slider, &QSlider::valueChanged, this, &ChartsWidget::setMaxChartRange); - QObject::connect(new_plot_btn, &QAction::triggered, this, &ChartsWidget::newChart); - QObject::connect(remove_all_btn, &QAction::triggered, this, &ChartsWidget::removeAll); - QObject::connect(reset_zoom_action, &QAction::triggered, this, &ChartsWidget::zoomReset); + QObject::connect(new_plot_btn, &QToolButton::clicked, this, &ChartsWidget::newChart); + QObject::connect(remove_all_btn, &QToolButton::clicked, this, &ChartsWidget::removeAll); + QObject::connect(reset_zoom_btn, &QToolButton::clicked, this, &ChartsWidget::zoomReset); QObject::connect(&settings, &Settings::changed, this, &ChartsWidget::settingChanged); - QObject::connect(new_tab_btn, &QAction::triggered, this, &ChartsWidget::newTab); + QObject::connect(new_tab_btn, &QToolButton::clicked, this, &ChartsWidget::newTab); QObject::connect(tabbar, &QTabBar::tabCloseRequested, this, &ChartsWidget::removeTab); QObject::connect(tabbar, &QTabBar::currentChanged, [this](int index) { if (index != -1) updateLayout(true); }); - QObject::connect(dock_btn, &QAction::triggered, [this]() { + QObject::connect(dock_btn, &QToolButton::clicked, [this]() { emit dock(!docking); docking = !docking; updateToolBar(); @@ -170,10 +170,6 @@ void ChartsWidget::setZoom(double min, double max) { emit rangeChanged(min, max, is_zoomed); } -void ChartsWidget::zoomIn(double min, double max) { - zoom_undo_stack->push(new ZoomCommand(this, {min, max})); -} - void ChartsWidget::zoomReset() { setZoom(display_range.first, display_range.second); zoom_undo_stack->clear(); @@ -228,17 +224,26 @@ void ChartsWidget::updateToolBar() { undo_zoom_action->setVisible(is_zoomed); redo_zoom_action->setVisible(is_zoomed); reset_zoom_action->setVisible(is_zoomed); - reset_zoom_action->setText(is_zoomed ? tr("%1-%2").arg(zoomed_range.first, 0, 'f', 1).arg(zoomed_range.second, 0, 'f', 1) : ""); + reset_zoom_btn->setText(is_zoomed ? tr("%1-%2").arg(zoomed_range.first, 0, 'f', 1).arg(zoomed_range.second, 0, 'f', 1) : ""); remove_all_btn->setEnabled(!charts.isEmpty()); - dock_btn->setIcon(utils::icon(docking ? "arrow-up-right-square" : "arrow-down-left-square")); + dock_btn->setIcon(docking ? "arrow-up-right-square" : "arrow-down-left-square"); dock_btn->setToolTip(docking ? tr("Undock charts") : tr("Dock charts")); } void ChartsWidget::settingChanged() { + if (std::exchange(current_theme, settings.theme) != current_theme) { + undo_zoom_action->setIcon(utils::icon("arrow-counterclockwise")); + redo_zoom_action->setIcon(utils::icon("arrow-clockwise")); + auto theme = settings.theme == DARK_THEME ? QChart::QChart::ChartThemeDark : QChart::ChartThemeLight; + for (auto c : charts) { + c->setTheme(theme); + } + } range_slider->setRange(1, settings.max_cached_minutes * 60); for (auto c : charts) { c->setFixedHeight(settings.chart_height); c->setSeriesType((SeriesType)settings.chart_series_type); + c->resetChartCache(); } } @@ -253,14 +258,7 @@ ChartView *ChartsWidget::createChart() { chart->setFixedHeight(settings.chart_height); chart->setMinimumWidth(CHART_MIN_WIDTH); chart->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); - chart->chart()->setTheme(settings.theme == 2 ? QChart::QChart::ChartThemeDark : QChart::ChartThemeLight); - QObject::connect(chart, &ChartView::remove, [=]() { removeChart(chart); }); - QObject::connect(chart, &ChartView::zoomIn, this, &ChartsWidget::zoomIn); - QObject::connect(chart, &ChartView::zoomUndo, undo_zoom_action, &QAction::trigger); - QObject::connect(chart, &ChartView::seriesRemoved, this, &ChartsWidget::seriesChanged); - QObject::connect(chart, &ChartView::seriesAdded, this, &ChartsWidget::seriesChanged); QObject::connect(chart, &ChartView::axisYLabelWidthChanged, &align_timer, qOverload<>(&QTimer::start)); - QObject::connect(chart, &ChartView::hovered, this, &ChartsWidget::showValueTip); charts.push_front(chart); currentCharts().push_front(chart); updateLayout(true); @@ -408,7 +406,7 @@ void ChartsWidget::alignCharts() { bool ChartsWidget::eventFilter(QObject *obj, QEvent *event) { if (obj != this && event->type() == QEvent::Close) { - emit dock_btn->triggered(); + emit dock_btn->clicked(); return true; } return false; @@ -439,7 +437,7 @@ bool ChartsWidget::event(QEvent *event) { } if (back_button) { - emit undo_zoom_action->triggered(); + zoom_undo_stack->undo(); return true; } return QFrame::event(event); @@ -466,6 +464,7 @@ ChartView::ChartView(const std::pair &x_range, ChartsWidget *par // TODO: enable zoomIn/seekTo in live streaming mode. setRubberBand(can->liveStreaming() ? QChartView::NoRubberBand : QChartView::HorizontalRubberBand); setMouseTracking(true); + setTheme(settings.theme == DARK_THEME ? QChart::QChart::ChartThemeDark : QChart::ChartThemeLight); QObject::connect(axis_y, &QValueAxis::rangeChanged, [this]() { resetChartCache(); }); QObject::connect(axis_y, &QAbstractAxis::titleTextChanged, [this]() { resetChartCache(); }); @@ -480,7 +479,7 @@ void ChartView::createToolButtons() { move_icon = new QGraphicsPixmapItem(utils::icon("grip-horizontal"), chart()); move_icon->setToolTip(tr("Drag and drop to move chart")); - QToolButton *remove_btn = toolButton("x", tr("Remove Chart")); + QToolButton *remove_btn = new ToolButton("x", tr("Remove Chart")); close_btn_proxy = new QGraphicsProxyWidget(chart()); close_btn_proxy->setWidget(remove_btn); close_btn_proxy->setZValue(chart()->zValue() + 11); @@ -500,19 +499,34 @@ void ChartView::createToolButtons() { menu->addSeparator(); menu->addAction(tr("Manage series"), this, &ChartView::manageSeries); - QToolButton *manage_btn = toolButton("list", ""); + QToolButton *manage_btn = new ToolButton("list", ""); manage_btn->setMenu(menu); manage_btn->setPopupMode(QToolButton::InstantPopup); + manage_btn->setStyleSheet("QToolButton::menu-indicator { image: none; }"); manage_btn_proxy = new QGraphicsProxyWidget(chart()); manage_btn_proxy->setWidget(manage_btn); manage_btn_proxy->setZValue(chart()->zValue() + 11); - QObject::connect(remove_btn, &QToolButton::clicked, this, &ChartView::remove); + QObject::connect(remove_btn, &QToolButton::clicked, [this]() { charts_widget->removeChart(this); }); QObject::connect(change_series_group, &QActionGroup::triggered, [this](QAction *action) { setSeriesType((SeriesType)action->data().toInt()); }); } +void ChartView::setTheme(QChart::ChartTheme theme) { + chart()->setTheme(theme); + if (theme == QChart::ChartThemeDark) { + axis_x->setTitleBrush(palette().color(QPalette::Text)); + axis_x->setLabelsBrush(palette().color(QPalette::Text)); + axis_y->setTitleBrush(palette().color(QPalette::Text)); + axis_y->setLabelsBrush(palette().color(QPalette::Text)); + chart()->legend()->setLabelColor(palette().color(QPalette::Text)); + } + for (auto &s : sigs) { + s.series->setColor(getColor(s.sig)); + } +} + void ChartView::addSeries(const MessageId &msg_id, const cabana::Signal *sig) { if (hasSeries(msg_id, sig)) return; @@ -521,7 +535,7 @@ void ChartView::addSeries(const MessageId &msg_id, const cabana::Signal *sig) { updateTitle(); updateSeries(sig); updateSeriesPoints(); - emit seriesAdded(msg_id, sig); + emit charts_widget->seriesChanged(); } bool ChartView::hasSeries(const MessageId &msg_id, const cabana::Signal *sig) const { @@ -534,17 +548,15 @@ void ChartView::removeIf(std::function predicate) { if (predicate(*it)) { chart()->removeSeries(it->series); it->series->deleteLater(); - auto msg_id = it->msg_id; - auto sig = it->sig; it = sigs.erase(it); - emit seriesRemoved(msg_id, sig); } else { ++it; } } if (sigs.empty()) { - emit remove(); + charts_widget->removeChart(this); } else if (sigs.size() != prev_size) { + emit charts_widget->seriesChanged(); updateAxisY(); resetChartCache(); } @@ -781,7 +793,7 @@ qreal ChartView::niceNumber(qreal x, bool ceiling) { void ChartView::leaveEvent(QEvent *event) { if (tip_label.isVisible()) { - emit hovered(-1); + charts_widget->showValueTip(-1); } QChartView::leaveEvent(event); } @@ -863,13 +875,13 @@ void ChartView::mouseReleaseEvent(QMouseEvent *event) { // no rubber dragged, seek to mouse position can->seekTo(min); } else if (rubber->width() > 10) { - emit zoomIn(min, max); + charts_widget->zoom_undo_stack->push(new ZoomCommand(charts_widget, {min, max})); } else { viewport()->update(); } event->accept(); } else if (!can->liveStreaming() && event->button() == Qt::RightButton) { - emit zoomUndo(); + charts_widget->zoom_undo_stack->undo(); event->accept(); } else { QGraphicsView::mouseReleaseEvent(event); @@ -898,9 +910,9 @@ void ChartView::mouseMoveEvent(QMouseEvent *ev) { if (!is_zooming && plot_area.contains(ev->pos())) { const double sec = chart()->mapToValue(ev->pos()).x(); - emit hovered(sec); + charts_widget->showValueTip(sec); } else if (tip_label.isVisible()) { - emit hovered(-1); + charts_widget->showValueTip(-1); } QChartView::mouseMoveEvent(ev); @@ -979,7 +991,7 @@ void ChartView::dropEvent(QDropEvent *event) { updateTitle(); source_chart->sigs.clear(); - emit source_chart->remove(); + charts_widget->removeChart(source_chart); event->acceptProposedAction(); } can_drop = false; @@ -1244,7 +1256,12 @@ QList SeriesSelector::seletedItems() { ValueTipLabel::ValueTipLabel(QWidget *parent) : QLabel(parent, Qt::ToolTip | Qt::FramelessWindowHint) { setForegroundRole(QPalette::ToolTipText); setBackgroundRole(QPalette::ToolTipBase); - setPalette(QToolTip::palette()); + auto palette = QToolTip::palette(); + if (settings.theme != DARK_THEME) { + palette.setColor(QPalette::ToolTipBase, QApplication::palette().color(QPalette::Base)); + palette.setColor(QPalette::ToolTipText, QRgb(0x404044)); // same color as chart label brush + } + setPalette(palette); ensurePolished(); setMargin(1 + style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, nullptr, this)); setAttribute(Qt::WA_ShowWithoutActivating); diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 0a693cf69c..8ad440b876 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -63,13 +63,7 @@ public: }; signals: - void seriesRemoved(const MessageId &id, const cabana::Signal *sig); - void seriesAdded(const MessageId &id, const cabana::Signal *sig); - void zoomIn(double min, double max); - void zoomUndo(); - void remove(); void axisYLabelWidthChanged(int w); - void hovered(double sec); private slots: void signalUpdated(const cabana::Signal *sig); @@ -94,6 +88,7 @@ private: void updateAxisY(); void updateTitle(); void resetChartCache(); + void setTheme(QChart::ChartTheme theme); void paintEvent(QPaintEvent *event) override; void drawForeground(QPainter *painter, const QRectF &rect) override; void drawBackground(QPainter *painter, const QRectF &rect) override; @@ -167,7 +162,6 @@ private: void removeChart(ChartView *chart); void eventsMerged(); void updateState(); - void zoomIn(double min, double max); void zoomReset(); void startAutoScroll(); void stopAutoScroll(); @@ -189,14 +183,15 @@ private: QAction *range_lb_action; QAction *range_slider_action; bool docking = true; - QAction *dock_btn; + ToolButton *dock_btn; QAction *undo_zoom_action; QAction *redo_zoom_action; QAction *reset_zoom_action; + ToolButton *reset_zoom_btn; QUndoStack *zoom_undo_stack; - QAction *remove_all_btn; + ToolButton *remove_all_btn; QList charts; std::unordered_map> tab_charts; QTabBar *tabbar; @@ -212,6 +207,7 @@ private: int auto_scroll_count = 0; QTimer auto_scroll_timer; QTimer align_timer; + int current_theme = 0; friend class ZoomCommand; friend class ChartView; friend class ChartsContainer; diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 543946b19a..dd12d4c8a3 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -22,7 +22,7 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart // message title QHBoxLayout *title_layout = new QHBoxLayout(); - title_layout->setContentsMargins(0, 6, 0, 0); + title_layout->setContentsMargins(3, 6, 3, 0); time_label = new QLabel(this); time_label->setToolTip(tr("Current time")); time_label->setStyleSheet("QLabel{font-weight:bold;}"); @@ -32,9 +32,9 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart name_label->setAlignment(Qt::AlignCenter); name_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); title_layout->addWidget(name_label); - auto edit_btn = toolButton("pencil", tr("Edit Message")); + auto edit_btn = new ToolButton("pencil", tr("Edit Message")); title_layout->addWidget(edit_btn); - remove_btn = toolButton("x-lg", tr("Remove Message")); + remove_btn = new ToolButton("x-lg", tr("Remove Message")); title_layout->addWidget(remove_btn); main_layout->addLayout(title_layout); diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index 4ebe8e0473..ec64880124 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -186,6 +186,7 @@ void HeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalI painter->fillRect(rect, bg_role.value()); } QString text = model()->headerData(logicalIndex, Qt::Horizontal, Qt::DisplayRole).toString(); + painter->setPen(palette().color(settings.theme == DARK_THEME ? QPalette::BrightText : QPalette::Text)); painter->drawText(rect.adjusted(5, 3, -5, -3), defaultAlignment(), text.replace(QChar('_'), ' ')); } diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index 892dfdb91f..c979a20f16 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -541,7 +541,6 @@ void MainWindow::updateDownloadProgress(uint64_t cur, uint64_t total, bool succe void MainWindow::updateStatus() { status_label->setText(tr("Cached Minutes:%1 FPS:%2").arg(settings.max_cached_minutes).arg(settings.fps)); - utils::setTheme(settings.theme); } void MainWindow::dockCharts(bool dock) { diff --git a/tools/cabana/settings.cc b/tools/cabana/settings.cc index a18b8a9ad6..3a1fc14751 100644 --- a/tools/cabana/settings.cc +++ b/tools/cabana/settings.cc @@ -1,11 +1,13 @@ #include "tools/cabana/settings.h" +#include #include #include #include #include -// Settings +#include "tools/cabana/util.h" + Settings settings; Settings::Settings() { @@ -85,21 +87,32 @@ SettingsDlg::SettingsDlg(QWidget *parent) : QDialog(parent) { chart_height->setValue(settings.chart_height); form_layout->addRow(tr("Chart Height"), chart_height); - auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Apply); form_layout->addRow(buttonBox); setFixedWidth(360); - connect(buttonBox, &QDialogButtonBox::accepted, this, &SettingsDlg::save); - connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + connect(buttonBox, &QDialogButtonBox::clicked, [=](QAbstractButton *button) { + auto role = buttonBox->buttonRole(button); + if (role == QDialogButtonBox::AcceptRole) { + save(); + accept(); + } else if (role == QDialogButtonBox::ApplyRole) { + save(); + } else if (role == QDialogButtonBox::RejectRole) { + reject(); + } + }); } void SettingsDlg::save() { settings.fps = fps->value(); - settings.theme = theme->currentIndex(); + if (std::exchange(settings.theme, theme->currentIndex()) != settings.theme) { + // set theme before emit changed + utils::setTheme(settings.theme); + } settings.max_cached_minutes = cached_minutes->value(); settings.chart_series_type = chart_series_type->currentIndex(); settings.chart_height = chart_height->value(); settings.save(); - accept(); emit settings.changed(); } diff --git a/tools/cabana/settings.h b/tools/cabana/settings.h index 8076cbf36d..dd01b602e7 100644 --- a/tools/cabana/settings.h +++ b/tools/cabana/settings.h @@ -5,6 +5,9 @@ #include #include +#define LIGHT_THEME 1 +#define DARK_THEME 2 + class Settings : public QObject { Q_OBJECT diff --git a/tools/cabana/signalview.cc b/tools/cabana/signalview.cc index ee1c154483..c40a0f176c 100644 --- a/tools/cabana/signalview.cc +++ b/tools/cabana/signalview.cc @@ -525,7 +525,7 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts), sparkline_range_slider->setValue(settings.sparkline_range); sparkline_range_slider->setToolTip(tr("Sparkline time range")); - auto collapse_btn = toolButton("dash-square", tr("Collapse All")); + auto collapse_btn = new ToolButton("dash-square", tr("Collapse All")); collapse_btn->setIconSize({12, 12}); hl->addWidget(collapse_btn); @@ -582,8 +582,8 @@ void SignalView::rowsChanged() { h->setContentsMargins(0, v_margin, -h_margin, v_margin); h->setSpacing(style()->pixelMetric(QStyle::PM_ToolBarItemSpacing)); - auto remove_btn = toolButton("x", tr("Remove signal")); - auto plot_btn = toolButton("graph-up", ""); + auto remove_btn = new ToolButton("x", tr("Remove signal")); + auto plot_btn = new ToolButton("graph-up", ""); plot_btn->setCheckable(true); h->addWidget(plot_btn); h->addWidget(remove_btn); diff --git a/tools/cabana/util.cc b/tools/cabana/util.cc index 8a25b11cbf..8d22d93f9a 100644 --- a/tools/cabana/util.cc +++ b/tools/cabana/util.cc @@ -8,9 +8,8 @@ #include #include "selfdrive/ui/qt/util.h" -#include "tools/cabana/settings.h" -static QColor blend(QColor a, QColor b) { +static inline QColor blend(const QColor &a, const QColor &b) { return QColor((a.red() + b.red()) / 2, (a.green() + b.green()) / 2, (a.blue() + b.blue()) / 2, (a.alpha() + b.alpha()) / 2); } @@ -22,6 +21,11 @@ void ChangeTracker::compute(const QByteArray &dat, double ts, uint32_t freq) { std::fill(colors.begin(), colors.end(), QColor(0, 0, 0, 0)); std::fill(last_change_t.begin(), last_change_t.end(), ts); } else { + int factor = settings.theme == DARK_THEME ? 135 : 0; + QColor cyan = QColor(0, 187, 255, start_alpha).lighter(factor); + QColor red = QColor(255, 0, 0, start_alpha).lighter(factor); + QColor greyish_blue = QColor(102, 86, 169, start_alpha / 2).lighter(factor); + for (int i = 0; i < dat.size(); ++i) { const uint8_t last = prev_dat[i]; const uint8_t cur = dat[i]; @@ -30,14 +34,10 @@ void ChangeTracker::compute(const QByteArray &dat, double ts, uint32_t freq) { double delta_t = ts - last_change_t[i]; if (delta_t * freq > periodic_threshold) { // Last change was while ago, choose color based on delta up or down - if (cur > last) { - colors[i] = QColor(0, 187, 255, start_alpha); // Cyan - } else { - colors[i] = QColor(255, 0, 0, start_alpha); // Red - } + colors[i] = (cur > last) ? cyan : red; } else { // Periodic changes - colors[i] = blend(colors[i], QColor(102, 86, 169, start_alpha / 2)); // Greyish/Blue + colors[i] = blend(colors[i], greyish_blue); } // Track bit level changes @@ -146,7 +146,7 @@ QValidator::State NameValidator::validate(QString &input, int &pos) const { namespace utils { QPixmap icon(const QString &id) { - bool dark_theme = settings.theme == 2; + bool dark_theme = settings.theme == DARK_THEME; QPixmap pm; QString key = "bootstrap_" % id % (dark_theme ? "1" : "0"); if (!QPixmapCache::find(key, &pm)) { @@ -154,7 +154,7 @@ QPixmap icon(const QString &id) { if (dark_theme) { QPainter p(&pm); p.setCompositionMode(QPainter::CompositionMode_SourceIn); - p.fillRect(pm.rect(), Qt::white); + p.fillRect(pm.rect(), QColor("#bbbbbb")); } QPixmapCache::insert(key, pm); } @@ -168,42 +168,39 @@ void setTheme(int theme) { static int prev_theme = 0; if (theme != prev_theme) { prev_theme = theme; - if (theme == 2) { - // modify palette to dark - QPalette darkPalette; - darkPalette.setColor(QPalette::Window, QColor(53, 53, 53)); - darkPalette.setColor(QPalette::WindowText, Qt::white); - darkPalette.setColor(QPalette::Base, QColor(25, 25, 25)); - darkPalette.setColor(QPalette::AlternateBase, QColor(53, 53, 53)); - darkPalette.setColor(QPalette::ToolTipBase, Qt::white); - darkPalette.setColor(QPalette::ToolTipText, QColor(41, 41, 41)); - darkPalette.setColor(QPalette::Text, Qt::white); - darkPalette.setColor(QPalette::Button, QColor(53, 53, 53)); - darkPalette.setColor(QPalette::ButtonText, Qt::white); - darkPalette.setColor(QPalette::BrightText, Qt::red); - darkPalette.setColor(QPalette::Link, QColor(42, 130, 218)); - darkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); - darkPalette.setColor(QPalette::HighlightedText, Qt::black); - qApp->setPalette(darkPalette); + QPalette new_palette; + if (theme == DARK_THEME) { + // "Darcula" like dark theme + new_palette.setColor(QPalette::Window, QColor("#353535")); + new_palette.setColor(QPalette::WindowText, QColor("#bbbbbb")); + new_palette.setColor(QPalette::Base, QColor("#3c3f41")); + new_palette.setColor(QPalette::AlternateBase, QColor("#3c3f41")); + new_palette.setColor(QPalette::ToolTipBase, QColor("#3c3f41")); + new_palette.setColor(QPalette::ToolTipText, QColor("#bbb")); + new_palette.setColor(QPalette::Text, QColor("#bbbbbb")); + new_palette.setColor(QPalette::Button, QColor("#3c3f41")); + new_palette.setColor(QPalette::ButtonText, QColor("#bbbbbb")); + new_palette.setColor(QPalette::Highlight, QColor("#2f65ca")); + new_palette.setColor(QPalette::HighlightedText, QColor("#bbbbbb")); + new_palette.setColor(QPalette::BrightText, QColor("#f0f0f0")); + new_palette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor("#777777")); + new_palette.setColor(QPalette::Disabled, QPalette::WindowText, QColor("#777777")); + new_palette.setColor(QPalette::Disabled, QPalette::Text, QColor("#777777"));; + new_palette.setColor(QPalette::Light, QColor("#3c3f41")); + new_palette.setColor(QPalette::Dark, QColor("#353535")); } else { - qApp->setPalette(style->standardPalette()); + new_palette = style->standardPalette(); } + qApp->setPalette(new_palette); style->polish(qApp); + for (auto w : QApplication::allWidgets()) { + w->setPalette(new_palette); + } } } } // namespace utils -QToolButton *toolButton(const QString &icon, const QString &tooltip) { - auto btn = new QToolButton(); - btn->setIcon(utils::icon(icon)); - btn->setToolTip(tooltip); - btn->setAutoRaise(true); - const int metric = qApp->style()->pixelMetric(QStyle::PM_SmallIconSize); - btn->setIconSize({metric, metric}); - return btn; -}; - QString toHex(uint8_t byte) { static std::array hex = []() { std::array ret; diff --git a/tools/cabana/util.h b/tools/cabana/util.h index e0fe4ad036..1f0f54d6b5 100644 --- a/tools/cabana/util.h +++ b/tools/cabana/util.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -14,6 +15,7 @@ #include #include "tools/cabana/dbc/dbc.h" +#include "tools/cabana/settings.h" class ChangeTracker { public: @@ -104,5 +106,27 @@ inline QString formatSeconds(int seconds) { } } -QToolButton *toolButton(const QString &icon, const QString &tooltip); +class ToolButton : public QToolButton { + Q_OBJECT +public: + ToolButton(const QString &icon, const QString &tooltip = {}, QWidget *parent = nullptr) : QToolButton(parent) { + setIcon(icon); + setToolTip(tooltip); + setAutoRaise(true); + const int metric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize); + setIconSize({metric, metric}); + theme = settings.theme; + connect(&settings, &Settings::changed, this, &ToolButton::updateIcon); + } + void setIcon(const QString &icon) { + icon_str = icon; + QToolButton::setIcon(utils::icon(icon_str)); + } + +private: + void updateIcon() { if (std::exchange(theme, settings.theme) != theme) setIcon(icon_str); } + QString icon_str; + int theme; +}; + int num_decimals(double num); diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index 42aa7d4f57..54a2dd6f8f 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -63,6 +63,7 @@ VideoWidget::VideoWidget(QWidget *parent) : QFrame(parent) { QObject::connect(play_btn, &QPushButton::clicked, []() { can->pause(!can->isPaused()); }); QObject::connect(can, &AbstractStream::paused, this, &VideoWidget::updatePlayBtnState); QObject::connect(can, &AbstractStream::resume, this, &VideoWidget::updatePlayBtnState); + QObject::connect(&settings, &Settings::changed, this, &VideoWidget::updatePlayBtnState); updatePlayBtnState(); setWhatsThis(tr(R"( @@ -303,7 +304,7 @@ void InfoLabel::showAlert(const AlertInfo &alert) { void InfoLabel::paintEvent(QPaintEvent *event) { QPainter p(this); - p.setPen(QPen(Qt::white, 2)); + p.setPen(QPen(palette().color(QPalette::BrightText), 2)); if (!pixmap.isNull()) { p.drawPixmap(0, 0, pixmap); p.drawRect(rect()); @@ -322,9 +323,11 @@ void InfoLabel::paintEvent(QPaintEvent *event) { text += "\n" + alert_info.text2; } - QFont font; - font.setPixelSize(!pixmap.isNull() ? 11 : QFont().pixelSize()); - p.setFont(font); + if (!pixmap.isNull()) { + QFont font; + font.setPixelSize(11); + p.setFont(font); + } QRect text_rect = rect().adjusted(2, 2, -2, -2); QRect r = p.fontMetrics().boundingRect(text_rect, Qt::AlignTop | Qt::AlignHCenter | Qt::TextWordWrap, text); p.fillRect(text_rect.left(), r.top(), text_rect.width(), r.height(), color);