From 3927d441f83e0aaf68acc473242fb6e9e35d235d Mon Sep 17 00:00:00 2001 From: zccrs Date: Fri, 9 Mar 2018 13:26:37 +0800 Subject: [PATCH] feat: insert virtual tab on DTabBar when drag enter Change-Id: I59b568756448778ebd793c3e3f500d842ee39dc5 --- src/widgets/dtabbar.cpp | 155 ++++++++++++++++++++++++++++++++++++---- src/widgets/dtabbar.h | 3 + 2 files changed, 143 insertions(+), 15 deletions(-) diff --git a/src/widgets/dtabbar.cpp b/src/widgets/dtabbar.cpp index 83c7ca994..47c0891c2 100644 --- a/src/widgets/dtabbar.cpp +++ b/src/widgets/dtabbar.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -129,6 +130,13 @@ class DTabBarPrivate : public QTabBar, public DObjectPrivate connect(this, &QTabBar::tabMoved, this, [this] (int from, int to) { tabMinimumSize.move(from, to); tabMaximumSize.move(from, to); + + if (dd()->validIndex(ghostTabIndex)) { + if (from == ghostTabIndex) + ghostTabIndex = to; + else if (to == ghostTabIndex) + ghostTabIndex = from; + } }); setAcceptDrops(true); @@ -254,7 +262,10 @@ class DTabBarPrivate : public QTabBar, public DObjectPrivate void startTabFlash(); void setDragingFromOther(bool v); - int tabInsertIndexFromMouse(const QPoint &pos); + int tabInsertIndexFromMouse(QPoint pos); + + void startMove(int index); + void stopMove(); QList tabMinimumSize; QList tabMaximumSize; @@ -267,6 +278,8 @@ class DTabBarPrivate : public QTabBar, public DObjectPrivate bool dragingFromOther = false; // 记录当前drag过来的对象是否可以当做新标签页插入 bool canInsertFromDrag = false; + // 为true忽略drag move事件 + bool ignoreDragEvent = false; QColor maskColor; QColor flashColor; @@ -295,6 +308,8 @@ class DTabBarPrivate : public QTabBar, public DObjectPrivate // 备份启动tab move时的QTabBarPrivate中的这两个值 int scrollOffset; QPoint dragStartPosition; + + int ghostTabIndex = -1; }; void DTabBarPrivate::startDrag(int tabIndex) @@ -802,8 +817,14 @@ void DTabBarPrivate::setDragingFromOther(bool v) topFullWidget->raise(); } -int DTabBarPrivate::tabInsertIndexFromMouse(const QPoint &pos) +int DTabBarPrivate::tabInsertIndexFromMouse(QPoint pos) { + if (pos.y() == height()) + pos.setY(pos.y() - 1); + + if (pos.x() == width()) + pos.setX(pos.x() - 1); + int current = tabAt(pos); QTabBarPrivate *d = reinterpret_cast(qGetPtrHelper(d_ptr)); @@ -811,9 +832,9 @@ int DTabBarPrivate::tabInsertIndexFromMouse(const QPoint &pos) if (!d->validIndex(current)){ if (vertical) - current = pos.y() >= height() ? -1 : 0; + current = pos.y() >= height() ? count() - 1 : 0; else - current = pos.x() >= width() ? -1 : 0; + current = pos.x() >= width() ? count() - 1 : 0; } const QRect ¤t_rect = tabRect(current); @@ -826,6 +847,50 @@ int DTabBarPrivate::tabInsertIndexFromMouse(const QPoint &pos) } } +void DTabBarPrivate::startMove(int index) +{ + if (dd()->dragInProgress) + return; + + dd()->pressedIndex = index; + makeVisible(index); + + const QRect &index_rect = tabRect(index); + + dd()->dragStartPosition = index_rect.center(); + + QVariantAnimation *mouse_animation = new QVariantAnimation(this); + + mouse_animation->setDuration(100); + mouse_animation->setEasingCurve(QEasingCurve::OutSine); + mouse_animation->setStartValue(QCursor::pos()); + mouse_animation->setEndValue(mapToGlobal(index_rect.center())); + + connect(mouse_animation, &QVariantAnimation::valueChanged, this, [] (const QVariant &value) { + const QPoint pos = value.toPoint(); + + QCursor::setPos(pos.x(), pos.y()); + }); + + connect(mouse_animation, &QVariantAnimation::finished, this, [this, mouse_animation] { + mouse_animation->deleteLater(); + ignoreDragEvent = false; + }); + + ignoreDragEvent = true; + mouse_animation->start(); +} + +void DTabBarPrivate::stopMove() +{ + QMouseEvent event(QEvent::MouseButtonRelease, mapFromGlobal(QCursor::pos()), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + bool movable = isMovable(); + setMovable(true); + qApp->sendEvent(this, &event); + setMovable(movable); +} + bool DTabBarPrivate::eventFilter(QObject *watched, QEvent *event) { QTabBarPrivate *d = reinterpret_cast(qGetPtrHelper(d_ptr)); @@ -987,7 +1052,7 @@ void DTabBarPrivate::mouseMoveEvent(QMouseEvent *event) { QTabBarPrivate *d = reinterpret_cast(qGetPtrHelper(d_ptr)); - if (!d->movable) + if (!d->movable && !d->validIndex(ghostTabIndex)) return; // Be safe! @@ -1009,7 +1074,7 @@ void DTabBarPrivate::mouseMoveEvent(QMouseEvent *event) } if (!drag && valid_pressed_index) { - if (offset_y > startDragDistance) { + if (offset_y > startDragDistance && !d->validIndex(ghostTabIndex)) { setupDragableTab(); } } @@ -1034,7 +1099,7 @@ void DTabBarPrivate::mouseMoveEvent(QMouseEvent *event) scrollOffset = d->scrollOffset; dragStartPosition = d->dragStartPosition; - // Auto scroll tags + // Auto scroll tabs autoScrollTabs(event->pos()); } } @@ -1620,6 +1685,16 @@ void DTabBar::dragEnterEvent(QDragEnterEvent *e) if (canInsertFromMimeData(index, e->mimeData())) { d->setDragingFromOther(true); e->acceptProposedAction(); + + // 插入一个虚拟的标签 + if (e->source() != d) { + d->ghostTabIndex = index; + insertFromMimeDataOnDragEnter(index, e->mimeData()); + // 延时启动startMove, 此时tabbar的大小还没有更新 + QTimer::singleShot(10, [d, index] { + d->startMove(index); + }); + } } } @@ -1630,23 +1705,51 @@ void DTabBar::dragLeaveEvent(QDragLeaveEvent *e) d->setDragingFromOther(false); d->stopAutoScrollTabs(); + + if (d->dd()->validIndex(d->ghostTabIndex)) { + d->stopMove(); + d->removeTab(d->ghostTabIndex); + d->ghostTabIndex = -1; + } } void DTabBar::dragMoveEvent(QDragMoveEvent *e) { D_D(DTabBar); + if (d->ignoreDragEvent) + return; + if (e->source() == d) return QWidget::dragMoveEvent(e); - if (e->source() != d) - d->autoScrollTabs(d->mapFromParent(e->pos())); - - int index = d->tabInsertIndexFromMouse(d->mapFromParent(e->pos())); + int index = d->dd()->validIndex(d->ghostTabIndex) ? d->ghostTabIndex : d->tabInsertIndexFromMouse(d->mapFromParent(e->pos())); + bool canInsert = false; if (canInsertFromMimeData(index, e->mimeData())) { d->setDragingFromOther(true); e->acceptProposedAction(); + canInsert = true; + } else if (d->dd()->validIndex(d->ghostTabIndex)) { + d->stopMove(); + d->removeTab(d->ghostTabIndex); + d->ghostTabIndex = -1; + } + + if (e->source() != d) { + if (canInsert) { + if (d->dd()->validIndex(d->ghostTabIndex)) { + QMouseEvent event(QEvent::MouseMove, d->mapFromParent(e->pos()), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + d->mouseMoveEvent(&event); + } else { + d->ghostTabIndex = index; + insertFromMimeDataOnDragEnter(index, e->mimeData()); + d->startMove(index); + } + } else { + d->autoScrollTabs(d->mapFromParent(e->pos())); + } } } @@ -1665,7 +1768,19 @@ void DTabBar::dropEvent(QDropEvent *e) if (canInsertFromMimeData(index, e->mimeData())) { e->acceptProposedAction(); e->setDropAction(Qt::MoveAction); - insertFromMimeData(index, e->mimeData()); + + if (d->dd()->validIndex(d->ghostTabIndex)) { + d->stopMove(); + { + QSignalBlocker blocker(this); + Q_UNUSED(blocker) + d->removeTab(d->ghostTabIndex); + } + insertFromMimeData(d->ghostTabIndex, e->mimeData()); + d->ghostTabIndex = -1; + } else { + insertFromMimeData(index, e->mimeData()); + } } } @@ -1681,6 +1796,13 @@ void DTabBar::resizeEvent(QResizeEvent *e) return QWidget::resizeEvent(e); } +void DTabBar::startTabFlash(int index) +{ + d_func()->flashTabIndex = index; + d_func()->makeVisible(d_func()->flashTabIndex); + d_func()->startTabFlash(); +} + void DTabBar::paintTab(QPainter *painter, int index, const QStyleOptionTab &option) const { Q_UNUSED(index) @@ -1731,9 +1853,12 @@ bool DTabBar::canInsertFromMimeData(int index, const QMimeData *source) const void DTabBar::insertFromMimeData(int index, const QMimeData *source) { - d_func()->flashTabIndex = insertTab(index, source->text()); - d_func()->makeVisible(d_func()->flashTabIndex); - d_func()->startTabFlash(); + startTabFlash(insertTab(index, source->text())); +} + +void DTabBar::insertFromMimeDataOnDragEnter(int index, const QMimeData *source) +{ + startTabFlash(insertTab(index, source->text())); } DTabBarPrivate *DTabBar::d_func() diff --git a/src/widgets/dtabbar.h b/src/widgets/dtabbar.h index 52862a656..16b71f1f4 100644 --- a/src/widgets/dtabbar.h +++ b/src/widgets/dtabbar.h @@ -185,12 +185,15 @@ public Q_SLOTS: void dropEvent(QDropEvent *e) override; void resizeEvent(QResizeEvent *e) override; + void startTabFlash(int index); + virtual void paintTab(QPainter *painter, int index, const QStyleOptionTab &option) const; virtual QPixmap createDragPixmapFromTab(int index, const QStyleOptionTab &option, QPoint *hotspot) const; virtual QMimeData *createMimeDataFromTab(int index, const QStyleOptionTab &option) const; virtual bool canInsertFromMimeData(int index, const QMimeData *source) const; virtual void insertFromMimeData(int index, const QMimeData *source); + virtual void insertFromMimeDataOnDragEnter(int index, const QMimeData *source); private: DTabBarPrivate* d_func();