diff --git a/tools/cabana/chart/chart.cc b/tools/cabana/chart/chart.cc index 7a056c767..28d4e068e 100644 --- a/tools/cabana/chart/chart.cc +++ b/tools/cabana/chart/chart.cc @@ -421,13 +421,6 @@ qreal ChartView::niceNumber(qreal x, bool ceiling) { return q * z; } -void ChartView::leaveEvent(QEvent *event) { - if (tip_label->isVisible()) { - charts_widget->showValueTip(-1); - } - QChartView::leaveEvent(event); -} - QPixmap getBlankShadowPixmap(const QPixmap &px, int radius) { QGraphicsDropShadowEffect *e = new QGraphicsDropShadowEffect; e->setColor(QColor(40, 40, 40, 245)); @@ -546,7 +539,7 @@ void ChartView::mouseMoveEvent(QMouseEvent *ev) { bool is_zooming = rubber && rubber->isVisible(); clearTrackPoints(); - if (!is_zooming && plot_area.contains(ev->pos())) { + if (!is_zooming && plot_area.contains(ev->pos()) && isActiveWindow()) { const double sec = chart()->mapToValue(ev->pos()).x(); charts_widget->showValueTip(sec); } else if (tip_label->isVisible()) { diff --git a/tools/cabana/chart/chart.h b/tools/cabana/chart/chart.h index d690a14d1..1bfec6355 100644 --- a/tools/cabana/chart/chart.h +++ b/tools/cabana/chart/chart.h @@ -76,7 +76,6 @@ private: void dragLeaveEvent(QDragLeaveEvent *event) override { drawDropIndicator(false); } void dragMoveEvent(QDragMoveEvent *event) override; void dropEvent(QDropEvent *event) override; - void leaveEvent(QEvent *event) override; void resizeEvent(QResizeEvent *event) override; QSize sizeHint() const override; void updateAxisY(); diff --git a/tools/cabana/chart/chartswidget.cc b/tools/cabana/chart/chartswidget.cc index 59c188afb..9822eaa30 100644 --- a/tools/cabana/chart/chartswidget.cc +++ b/tools/cabana/chart/chartswidget.cc @@ -116,13 +116,12 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QFrame(parent) { QObject::connect(tabbar, &QTabBar::currentChanged, [this](int index) { if (index != -1) updateLayout(true); }); - QObject::connect(dock_btn, &QToolButton::clicked, [this]() { - emit dock(!docking); - docking = !docking; - updateToolBar(); - }); + QObject::connect(dock_btn, &QToolButton::clicked, this, &ChartsWidget::toggleChartsDocking); + setIsDocked(true); newTab(); + qApp->installEventFilter(this); + setWhatsThis(tr(R"( Chart view
@@ -177,8 +176,11 @@ QRect ChartsWidget::chartVisibleRect(ChartView *chart) { } void ChartsWidget::showValueTip(double sec) { + if (sec < 0 && !value_tip_visible_) return; + + value_tip_visible_ = sec >= 0; for (auto c : currentCharts()) { - sec >= 0 ? c->showTip(sec) : c->hideTip(); + value_tip_visible_ ? c->showTip(sec) : c->hideTip(); } } @@ -209,6 +211,12 @@ void ChartsWidget::setMaxChartRange(int value) { updateState(); } +void ChartsWidget::setIsDocked(bool docked) { + is_docked = docked; + dock_btn->setIcon(is_docked ? "arrow-up-right-square" : "arrow-down-left-square"); + dock_btn->setToolTip(is_docked ? tr("Float the charts window") : tr("Dock the charts window")); +} + void ChartsWidget::updateToolBar() { title_label->setText(tr("Charts: %1").arg(charts.size())); columns_action->setText(tr("Column: %1").arg(column_count)); @@ -222,8 +230,6 @@ void ChartsWidget::updateToolBar() { reset_zoom_action->setVisible(is_zoomed); reset_zoom_btn->setText(is_zoomed ? tr("%1-%2").arg(can->timeRange()->first, 0, 'f', 2).arg(can->timeRange()->second, 0, 'f', 2) : ""); remove_all_btn->setEnabled(!charts.isEmpty()); - 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() { @@ -375,11 +381,6 @@ QSize ChartsWidget::minimumSizeHint() const { return QSize(CHART_MIN_WIDTH, QWidget::minimumSizeHint().height()); } -void ChartsWidget::resizeEvent(QResizeEvent *event) { - QWidget::resizeEvent(event); - updateLayout(); -} - void ChartsWidget::newChart() { SignalSelector dlg(tr("New Chart"), this); if (dlg.exec() == QDialog::Accepted) { @@ -432,10 +433,16 @@ void ChartsWidget::alignCharts() { } } -bool ChartsWidget::eventFilter(QObject *obj, QEvent *event) { - if (obj != this && event->type() == QEvent::Close) { - emit dock_btn->clicked(); - return true; +bool ChartsWidget::eventFilter(QObject *o, QEvent *e) { + if (value_tip_visible_ && e->type() == QEvent::MouseMove) { + auto pos = static_cast(e)->globalPos(); + bool outside_plot_area =std::none_of(charts.begin(), charts.end(), [&pos](auto c) { + return c->chart()->plotArea().contains(c->mapFromGlobal(pos)); + }); + + if (outside_plot_area) { + showValueTip(-1); + } } return false; } @@ -443,30 +450,25 @@ bool ChartsWidget::eventFilter(QObject *obj, QEvent *event) { bool ChartsWidget::event(QEvent *event) { bool back_button = false; switch (event->type()) { - case QEvent::MouseButtonPress: { - QMouseEvent *ev = static_cast(event); - back_button = ev->button() == Qt::BackButton; + case QEvent::Resize: + updateLayout(); break; - } - case QEvent::NativeGesture: { - QNativeGestureEvent *ev = static_cast(event); - back_button = (ev->value() == 180); + case QEvent::MouseButtonPress: + back_button = static_cast(event)->button() == Qt::BackButton; + break; + case QEvent::NativeGesture: + back_button = (static_cast(event)->value() == 180); break; - } - case QEvent::WindowActivate: case QEvent::WindowDeactivate: - case QEvent::FocusIn: case QEvent::FocusOut: - case QEvent::Leave: showValueTip(-1); - break; default: break; } if (back_button) { zoom_undo_stack->undo(); - return true; + return true; // Return true since the event has been handled } return QFrame::event(event); } diff --git a/tools/cabana/chart/chartswidget.h b/tools/cabana/chart/chartswidget.h index a9ac05db2..0b948b342 100644 --- a/tools/cabana/chart/chartswidget.h +++ b/tools/cabana/chart/chartswidget.h @@ -47,14 +47,14 @@ public slots: void setColumnCount(int n); void removeAll(); void timeRangeChanged(const std::optional> &time_range); + void setIsDocked(bool dock); signals: - void dock(bool floating); + void toggleChartsDocking(); void seriesChanged(); private: QSize minimumSizeHint() const override; - void resizeEvent(QResizeEvent *event) override; bool event(QEvent *event) override; void alignCharts(); void newChart(); @@ -85,7 +85,7 @@ private: LogSlider *range_slider; QAction *range_lb_action; QAction *range_slider_action; - bool docking = true; + bool is_docked = true; ToolButton *dock_btn; QAction *undo_zoom_action; @@ -109,6 +109,7 @@ private: QTimer *auto_scroll_timer; QTimer *align_timer; int current_theme = 0; + bool value_tip_visible_ = false; friend class ZoomCommand; friend class ChartView; friend class ChartsContainer; diff --git a/tools/cabana/chart/tiplabel.cc b/tools/cabana/chart/tiplabel.cc index f5c9cc9cb..be7160283 100644 --- a/tools/cabana/chart/tiplabel.cc +++ b/tools/cabana/chart/tiplabel.cc @@ -9,8 +9,12 @@ #include "tools/cabana/settings.h" TipLabel::TipLabel(QWidget *parent) : QLabel(parent, Qt::ToolTip | Qt::FramelessWindowHint) { + setAttribute(Qt::WA_ShowWithoutActivating); + setAttribute(Qt::WA_TransparentForMouseEvents); + setForegroundRole(QPalette::ToolTipText); setBackgroundRole(QPalette::ToolTipBase); + QFont font; font.setPointSizeF(8.34563465); setFont(font); @@ -22,9 +26,7 @@ TipLabel::TipLabel(QWidget *parent) : QLabel(parent, Qt::ToolTip | Qt::Frameless setPalette(palette); ensurePolished(); setMargin(1 + style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, nullptr, this)); - setAttribute(Qt::WA_ShowWithoutActivating); setTextFormat(Qt::RichText); - setVisible(false); } void TipLabel::showText(const QPoint &pt, const QString &text, QWidget *w, const QRect &rect) { diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index d099fcec9..bf1db63ee 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -198,7 +198,7 @@ void MainWindow::createDockWidgets() { video_splitter->restoreState(settings.video_splitter_state); video_splitter->handle(1)->setEnabled(!can->liveStreaming()); video_dock->setWidget(video_splitter); - QObject::connect(charts_widget, &ChartsWidget::dock, this, &MainWindow::dockCharts); + QObject::connect(charts_widget, &ChartsWidget::toggleChartsDocking, this, &MainWindow::toggleChartsDocking); } void MainWindow::createStatusBar() { @@ -577,20 +577,31 @@ void MainWindow::updateStatus() { status_label->setText(tr("Cached Minutes:%1 FPS:%2").arg(settings.max_cached_minutes).arg(settings.fps)); } -void MainWindow::dockCharts(bool dock) { - if (dock && floating_window) { - floating_window->removeEventFilter(charts_widget); +bool MainWindow::eventFilter(QObject *obj, QEvent *event) { + if (obj == floating_window && event->type() == QEvent::Close) { + toggleChartsDocking(); + return true; + } + return QMainWindow::eventFilter(obj, event); +} + +void MainWindow::toggleChartsDocking() { + if (floating_window) { + // Dock the charts widget back to the main window + floating_window->removeEventFilter(this); charts_layout->insertWidget(0, charts_widget, 1); floating_window->deleteLater(); floating_window = nullptr; - } else if (!dock && !floating_window) { - floating_window = new QWidget(this); - floating_window->setWindowFlags(Qt::Window); + charts_widget->setIsDocked(true); + } else { + // Float the charts widget in a separate window + floating_window = new QWidget(this, Qt::Window); floating_window->setWindowTitle("Charts"); floating_window->setLayout(new QVBoxLayout()); floating_window->layout()->addWidget(charts_widget); - floating_window->installEventFilter(charts_widget); + floating_window->installEventFilter(this); floating_window->showMaximized(); + charts_widget->setIsDocked(false); } } diff --git a/tools/cabana/mainwin.h b/tools/cabana/mainwin.h index 0b869f153..77add4d78 100644 --- a/tools/cabana/mainwin.h +++ b/tools/cabana/mainwin.h @@ -21,7 +21,7 @@ class MainWindow : public QMainWindow { public: MainWindow(); - void dockCharts(bool dock); + void toggleChartsDocking(); void showStatusMessage(const QString &msg, int timeout = 0) { statusBar()->showMessage(msg, timeout); } void loadFile(const QString &fn, SourceSet s = SOURCE_ALL); ChartsWidget *charts_widget = nullptr; @@ -46,6 +46,7 @@ signals: void updateProgressBar(uint64_t cur, uint64_t total, bool success); protected: + bool eventFilter(QObject *obj, QEvent *event) override; void remindSaveChanges(); void closeFile(SourceSet s = SOURCE_ALL); void closeFile(DBCFile *dbc_file);