cabana: drag&drop to change chart order (#27832)

pull/27833/head
Dean Lee 2 years ago committed by GitHub
parent 08b143997b
commit 409bfcacb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 105
      tools/cabana/chartswidget.cc
  2. 25
      tools/cabana/chartswidget.h

@ -19,6 +19,7 @@
#include <QtConcurrent>
const int MAX_COLUMN_COUNT = 4;
const QString mime_type = "application/x-cabanachartview";
static inline bool xLessThan(const QPointF &p, float x) { return p.x() < x; }
// ChartsWidget
@ -75,14 +76,7 @@ ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), auto_scroll_tim
main_layout->addWidget(toolbar);
// charts
charts_layout = new QGridLayout();
charts_layout->setSpacing(10);
charts_container = new QWidget(this);
QVBoxLayout *charts_main_layout = new QVBoxLayout(charts_container);
charts_main_layout->setContentsMargins(0, 0, 0, 0);
charts_main_layout->addLayout(charts_layout);
charts_main_layout->addStretch(0);
charts_container = new ChartsContainer(this);
charts_scroll = new QScrollArea(this);
charts_scroll->setFrameStyle(QFrame::NoFrame);
@ -227,7 +221,7 @@ ChartView *ChartsWidget::createChart() {
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_back(chart);
charts.push_front(chart);
updateLayout();
updateToolBar();
return chart;
@ -236,7 +230,7 @@ ChartView *ChartsWidget::createChart() {
void ChartsWidget::showChart(const MessageId &id, const cabana::Signal *sig, bool show, bool merge) {
ChartView *chart = findChart(id, sig);
if (show && !chart) {
chart = merge && charts.size() > 0 ? charts.back() : createChart();
chart = merge && charts.size() > 0 ? charts.front() : createChart();
chart->addSeries(id, sig);
} else if (!show && chart) {
chart->removeIf([&](auto &s) { return s.msg_id == id && s.sig == sig; });
@ -252,7 +246,8 @@ void ChartsWidget::setColumnCount(int n) {
}
}
void ChartsWidget::updateLayout() {
void ChartsWidget::updateLayout(bool force) {
auto charts_layout = charts_container->charts_layout;
int n = MAX_COLUMN_COUNT;
for (; n > 1; --n) {
if ((n * CHART_MIN_WIDTH + (n - 1) * charts_layout->spacing()) < charts_layout->geometry().width()) break;
@ -262,13 +257,13 @@ void ChartsWidget::updateLayout() {
columns_action->setVisible(show_column_cb);
n = std::min(column_count, n);
if (charts.size() != charts_layout->count() || n != current_column_count) {
if ((charts.size() != charts_layout->count() || n != current_column_count) || force) {
current_column_count = n;
charts_layout->parentWidget()->setUpdatesEnabled(false);
charts_container->setUpdatesEnabled(false);
for (int i = 0; i < charts.size(); ++i) {
charts_layout->addWidget(charts[charts.size() - i - 1], i / n, i % n);
charts_layout->addWidget(charts[i], i / n, i % n);
}
QTimer::singleShot(0, [this]() { charts_layout->parentWidget()->setUpdatesEnabled(true); });
QTimer::singleShot(0, [this]() { charts_container->setUpdatesEnabled(true); });
}
}
@ -299,6 +294,13 @@ void ChartsWidget::doAutoScroll() {
bool vertical_unchanged = value == scroll->value();
if (vertical_unchanged) {
stopAutoScroll();
} else {
// mouseMoveEvent to updates the drag-selection rectangle
const QPoint globalPos = charts_scroll->viewport()->mapToGlobal(pos);
const QPoint windowPos = charts_scroll->window()->mapFromGlobal(globalPos);
QMouseEvent mm(QEvent::MouseMove, pos, windowPos, globalPos,
Qt::NoButton, Qt::LeftButton, Qt::NoModifier, Qt::MouseEventSynthesizedByQt);
QApplication::sendEvent(charts_scroll->viewport(), &mm);
}
}
@ -423,7 +425,7 @@ ChartView::ChartView(const std::pair<double, double> &x_range, ChartsWidget *par
void ChartView::createToolButtons() {
move_icon = new QGraphicsPixmapItem(utils::icon("grip-horizontal"), chart());
move_icon->setToolTip(tr("Drag and drop to combine charts"));
move_icon->setToolTip(tr("Drag and drop to move chart"));
QToolButton *remove_btn = toolButton("x", tr("Remove Chart"));
close_btn_proxy = new QGraphicsProxyWidget(chart());
@ -864,32 +866,23 @@ void ChartView::hideTip() {
}
void ChartView::dragEnterEvent(QDragEnterEvent *event) {
can_drop = event->source() != this;
viewport()->update();
QChartView::dragEnterEvent(event);
if (event->mimeData()->hasFormat(mime_type)) {
drawDropIndicator(event->source() != this);
event->acceptProposedAction();
}
void ChartView::dragLeaveEvent(QDragLeaveEvent *event) {
can_drop = false;
viewport()->update();
QChartView::dragLeaveEvent(event);
}
void ChartView::dragMoveEvent(QDragMoveEvent *event) {
if (event->mimeData()->hasFormat(mime_type)) {
event->setDropAction(event->source() == this ? Qt::MoveAction : Qt::CopyAction);
event->accept();
} else {
event->ignore();
}
charts_widget->startAutoScroll();
}
void ChartView::dropEvent(QDropEvent *event) {
if (event->mimeData()->hasFormat(mime_type)) {
if (event->source() == this) {
event->setDropAction(Qt::MoveAction);
event->accept();
} else {
if (event->source() != this) {
ChartView *source_chart = (ChartView *)event->source();
for (auto &s : source_chart->sigs) {
source_chart->chart()->removeSeries(s.series);
@ -906,8 +899,6 @@ void ChartView::dropEvent(QDropEvent *event) {
event->acceptProposedAction();
}
can_drop = false;
} else {
event->ignore();
}
}
@ -1199,3 +1190,55 @@ void ValueTipLabel::paintEvent(QPaintEvent *ev) {
p.end();
QLabel::paintEvent(ev);
}
// ChartsContainer
ChartsContainer::ChartsContainer(ChartsWidget *parent) : charts_widget(parent), QWidget(parent) {
setAcceptDrops(true);
QVBoxLayout *charts_main_layout = new QVBoxLayout(this);
charts_main_layout->setContentsMargins(0, 0, 0, 0);
charts_layout = new QGridLayout();
charts_layout->setSpacing(10);
charts_main_layout->addLayout(charts_layout);
charts_main_layout->addStretch(0);
}
void ChartsContainer::dragEnterEvent(QDragEnterEvent *event) {
if (event->mimeData()->hasFormat(mime_type)) {
event->acceptProposedAction();
drawDropIndicator(event->pos());
}
}
void ChartsContainer::dropEvent(QDropEvent *event) {
if (event->mimeData()->hasFormat(mime_type)) {
auto w = getDropBefore(event->pos());
auto chart = qobject_cast<ChartView *>(event->source());
if (w != chart) {
charts_widget->charts.removeOne(chart);
int to = w ? charts_widget->charts.indexOf(w) : charts_widget->charts.size();
charts_widget->charts.insert(to, chart);
charts_widget->updateLayout(true);
event->acceptProposedAction();
}
drawDropIndicator({});
}
}
void ChartsContainer::paintEvent(QPaintEvent *ev) {
if (!drop_indictor_pos.isNull() && !childAt(drop_indictor_pos)) {
if (auto insert_after = getDropBefore(drop_indictor_pos)) {
auto area = insert_after->geometry();
QRect r = QRect(area.left(), area.top() - 10, area.width(), 10);
QPainter(this).fillRect(r, qApp->palette().highlight());
}
}
}
ChartView *ChartsContainer::getDropBefore(const QPoint &pos) const {
auto it = std::find_if(charts_widget->charts.cbegin(), charts_widget->charts.cend(), [&pos](auto c) {
auto area = c->geometry();
return pos.x() >= area.left() && pos.x() <= area.right() && pos.y() < area.top();
});
return it == charts_widget->charts.cend() ? nullptr : *it;
}

@ -84,7 +84,7 @@ private:
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *ev) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void dragLeaveEvent(QDragLeaveEvent *event) override;
void dragLeaveEvent(QDragLeaveEvent *event) override { drawDropIndicator(false); }
void dragMoveEvent(QDragMoveEvent *event) override;
void dropEvent(QDropEvent *event) override;
void leaveEvent(QEvent *event) override;
@ -96,6 +96,7 @@ private:
void paintEvent(QPaintEvent *event) override;
void drawForeground(QPainter *painter, const QRectF &rect) override;
void drawBackground(QPainter *painter, const QRectF &rect) override;
void drawDropIndicator(bool draw) { can_drop = draw; viewport()->update(); }
std::tuple<double, double, int> getNiceAxisNumbers(qreal min, qreal max, int tick_count);
qreal niceNumber(qreal x, bool ceiling);
QXYSeries *createSeries(SeriesType type, QColor color);
@ -113,7 +114,6 @@ private:
ValueTipLabel tip_label;
QList<SigItem> sigs;
double cur_sec = 0;
const QString mime_type = "application/x-cabanachartview";
SeriesType series_type = SeriesType::Line;
bool is_scrubbing = false;
bool resume_after_scrub = false;
@ -124,6 +124,21 @@ private:
friend class ChartsWidget;
};
class ChartsContainer : public QWidget {
public:
ChartsContainer(ChartsWidget *parent);
void dragEnterEvent(QDragEnterEvent *event) override;
void dropEvent(QDropEvent *event) override;
void dragLeaveEvent(QDragLeaveEvent *event) override { drawDropIndicator({}); }
void drawDropIndicator(const QPoint &pt) { drop_indictor_pos = pt; update(); }
void paintEvent(QPaintEvent *ev) override;
ChartView *getDropBefore(const QPoint &pos) const;
QGridLayout *charts_layout;
ChartsWidget *charts_widget;
QPoint drop_indictor_pos;
};
class ChartsWidget : public QFrame {
Q_OBJECT
@ -158,7 +173,7 @@ private:
void doAutoScroll();
void updateToolBar();
void setMaxChartRange(int value);
void updateLayout();
void updateLayout(bool force = false);
void settingChanged();
void showValueTip(double sec);
bool eventFilter(QObject *obj, QEvent *event) override;
@ -178,9 +193,8 @@ private:
QUndoStack *zoom_undo_stack;
QAction *remove_all_btn;
QGridLayout *charts_layout;
QList<ChartView *> charts;
QWidget *charts_container;
ChartsContainer *charts_container;
QScrollArea *charts_scroll;
uint32_t max_chart_range = 0;
bool is_zoomed = false;
@ -194,6 +208,7 @@ private:
QTimer align_timer;
friend class ZoomCommand;
friend class ChartView;
friend class ChartsContainer;
};
class ZoomCommand : public QUndoCommand {

Loading…
Cancel
Save