diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 1658ff8b68..0dbac8b484 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,7 @@ #include const int MAX_COLUMN_COUNT = 4; +const int CHART_SPACING = 10; const QString mime_type = "application/x-cabanachartview"; static inline bool xLessThan(const QPointF &p, float x) { return p.x() < x; } @@ -78,15 +80,15 @@ ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), auto_scroll_tim main_layout->addWidget(toolbar); // tabbar - QHBoxLayout *tab_layout = new QHBoxLayout(); - tab_layout->addWidget(tabbar = new QTabBar(this)); + tabbar = new QTabBar(this); tabbar->setAutoHide(true); + tabbar->setExpanding(false); + tabbar->setDrawBase(true); tabbar->setAcceptDrops(true); tabbar->setChangeCurrentOnDrag(true); tabbar->setTabsClosable(true); tabbar->setUsesScrollButtons(true); - tab_layout->addStretch(0); - main_layout->addLayout(tab_layout); + main_layout->addWidget(tabbar); // charts charts_container = new ChartsContainer(this); @@ -307,7 +309,7 @@ void ChartsWidget::updateLayout(bool force) { charts_layout->addWidget(current_charts[i], i / n, i % n); current_charts[i]->setVisible(true); } - QTimer::singleShot(0, [this]() { charts_container->setUpdatesEnabled(true); }); + charts_container->setUpdatesEnabled(true); } } @@ -784,20 +786,51 @@ void ChartView::leaveEvent(QEvent *event) { QChartView::leaveEvent(event); } +QPixmap getBlankShadowPixmap(const QSize &size, int extent) { + QGraphicsDropShadowEffect *e = new QGraphicsDropShadowEffect; + e->setColor(QColor(40, 40, 40, 245)); + e->setOffset(0, 2); + e->setBlurRadius(10); + + QGraphicsScene scene; + QGraphicsPixmapItem item; + QPixmap src(size); + src.fill(Qt::white); + item.setPixmap(src); + item.setGraphicsEffect(e); + scene.addItem(&item); + QImage target(src.size() + QSize(extent * 2, extent * 2), QImage::Format_ARGB32); + target.fill(Qt::transparent); + QPainter p(&target); + scene.render(&p, QRectF(), QRectF(-extent, -extent, src.width() + extent * 2, src.height() + extent * 2)); + return QPixmap::fromImage(target); +} + +static QPixmap getDropPixmap(const QPixmap &src) { + static QPixmap shadow_px; + const int extent = 10; + if (shadow_px.size() != src.size() + QSize(extent * 2, extent * 2)) { + shadow_px = getBlankShadowPixmap(src.size(), extent); + } + QPixmap px = shadow_px; + QPainter p(&px); + int delta_w = px.width() - src.width(); + int delta_h = px.height() - src.height(); + p.drawPixmap(QPoint(delta_w / 2, delta_h / 2), src); + p.setCompositionMode(QPainter::CompositionMode_DestinationIn); + p.fillRect(delta_w / 2, delta_h / 2, src.width(), src.height(), QColor(0, 0, 0, 200)); + return px; +} + void ChartView::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton && move_icon->sceneBoundingRect().contains(event->pos())) { QMimeData *mimeData = new QMimeData; mimeData->setData(mime_type, QByteArray::number((qulonglong)this)); - QPixmap pm = grab(); - QPainter p(&pm); - p.setCompositionMode(QPainter::CompositionMode_DestinationIn); - p.fillRect(pm.rect(), QColor(0, 0, 0, 180)); - p.end(); - + QPixmap px = grab().scaledToWidth(CHART_MIN_WIDTH, Qt::SmoothTransformation); QDrag *drag = new QDrag(this); drag->setMimeData(mimeData); - drag->setPixmap(pm); - drag->setHotSpot(event->pos()); + drag->setPixmap(getDropPixmap(px)); + drag->setHotSpot(-QPoint(5, 5)); drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::MoveAction); charts_widget->stopAutoScroll(); } else if (event->button() == Qt::LeftButton && QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier)) { @@ -1247,9 +1280,9 @@ void ValueTipLabel::paintEvent(QPaintEvent *ev) { 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_main_layout->setContentsMargins(0, 10, 0, 0); charts_layout = new QGridLayout(); - charts_layout->setSpacing(10); + charts_layout->setSpacing(CHART_SPACING); charts_main_layout->addLayout(charts_layout); charts_main_layout->addStretch(0); } @@ -1263,13 +1296,13 @@ void ChartsContainer::dragEnterEvent(QDragEnterEvent *event) { void ChartsContainer::dropEvent(QDropEvent *event) { if (event->mimeData()->hasFormat(mime_type)) { - auto w = getDropBefore(event->pos()); + auto w = getDropAfter(event->pos()); auto chart = qobject_cast(event->source()); if (w != chart) { for (auto &[_, list] : charts_widget->tab_charts) { list.removeOne(chart); } - int to = w ? charts_widget->currentCharts().indexOf(w) : charts_widget->currentCharts().size(); + int to = w ? charts_widget->currentCharts().indexOf(w) + 1 : 0; charts_widget->currentCharts().insert(to, chart); charts_widget->updateLayout(true); event->acceptProposedAction(); @@ -1280,18 +1313,31 @@ void ChartsContainer::dropEvent(QDropEvent *event) { 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()); + QRect r; + if (auto insert_after = getDropAfter(drop_indictor_pos)) { + QRect area = insert_after->geometry(); + r = QRect(area.left(), area.bottom() + 1, area.width(), CHART_SPACING); + } else { + r = geometry(); + r.setHeight(CHART_SPACING); } + + const int margin = (CHART_SPACING - 2) / 2; + QPainterPath path; + path.addPolygon(QPolygonF({r.topLeft(), QPointF(r.left() + CHART_SPACING, r.top() + r.height() / 2), r.bottomLeft()})); + path.addPolygon(QPolygonF({r.topRight(), QPointF(r.right() - CHART_SPACING, r.top() + r.height() / 2), r.bottomRight()})); + + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + p.fillPath(path, palette().highlight()); + p.fillRect(r.adjusted(2, margin, -2, -margin), palette().highlight()); } } -ChartView *ChartsContainer::getDropBefore(const QPoint &pos) const { - auto it = std::find_if(charts_widget->currentCharts().cbegin(), charts_widget->currentCharts().cend(), [&pos](auto c) { +ChartView *ChartsContainer::getDropAfter(const QPoint &pos) const { + auto it = std::find_if(charts_widget->currentCharts().crbegin(), charts_widget->currentCharts().crend(), [&pos](auto c) { auto area = c->geometry(); - return pos.x() >= area.left() && pos.x() <= area.right() && pos.y() < area.top(); + return pos.x() >= area.left() && pos.x() <= area.right() && pos.y() >= area.bottom(); }); - return it == charts_widget->currentCharts().cend() ? nullptr : *it; + return it == charts_widget->currentCharts().crend() ? nullptr : *it; } diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 22af411b0d..0a693cf69c 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -97,7 +97,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(); } + void drawDropIndicator(bool draw) { if (std::exchange(can_drop, draw) != can_drop) viewport()->update(); } std::tuple getNiceAxisNumbers(qreal min, qreal max, int tick_count); qreal niceNumber(qreal x, bool ceiling); QXYSeries *createSeries(SeriesType type, QColor color); @@ -133,7 +133,7 @@ public: 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; + ChartView *getDropAfter(const QPoint &pos) const; QGridLayout *charts_layout; ChartsWidget *charts_widget;