From 8d32ce09be2074dfa5dfb52bd9d64bc2f93d5fa2 2017-10-25 07:32:56 From: Thibaud Rabillard Date: 2017-10-25 07:32:56 Subject: [PATCH] Manage the scroll of the tab QScrollArea during a drag&drop operation --- diff --git a/gui/include/DragDropHelper.h b/gui/include/DragDropHelper.h index d103039..d6b6284 100644 --- a/gui/include/DragDropHelper.h +++ b/gui/include/DragDropHelper.h @@ -10,6 +10,31 @@ class VisualizationDragWidget; class QMimeData; /** + * @brief Event filter class which manage the scroll of QScrollArea during a drag&drop operation. + * @note A QScrollArea inside an other QScrollArea is not fully supported. + */ +class DragDropScroller : public QObject +{ + Q_OBJECT + +public: + DragDropScroller(QObject* parent = nullptr); + + void addScrollArea(QScrollArea* scrollArea); + void removeScrollArea(QScrollArea* scrollArea); + +protected: + bool eventFilter(QObject *obj, QEvent *event); + +private: + class DragDropScrollerPrivate; + spimpl::unique_impl_ptr impl; + +private slots: + void onTimer(); +}; + +/** * @brief Helper class for drag&drop operations. */ class DragDropHelper diff --git a/gui/src/DragDropHelper.cpp b/gui/src/DragDropHelper.cpp index 9f7cc58..b327352 100644 --- a/gui/src/DragDropHelper.cpp +++ b/gui/src/DragDropHelper.cpp @@ -13,6 +13,139 @@ const QString DragDropHelper::MIME_TYPE_GRAPH = "scqlop/graph"; const QString DragDropHelper::MIME_TYPE_ZONE = "scqlop/zone"; +const int SCROLL_SPEED = 5; +const int SCROLL_ZONE_SIZE = 50; + +struct DragDropScroller::DragDropScrollerPrivate { + + QList m_scrollAreas; + QScrollArea* m_currentScrollArea = nullptr; + std::unique_ptr m_timer = nullptr; + + + enum class ScrollDirection {up, down, unknown}; + ScrollDirection m_direction = ScrollDirection::unknown; + + explicit DragDropScrollerPrivate() + : m_timer{std::make_unique()} + { + m_timer->setInterval(0); + } +}; + +DragDropScroller::DragDropScroller(QObject* parent) + : QObject{parent}, impl{spimpl::make_unique_impl()} +{ + connect(impl->m_timer.get(), &QTimer::timeout, this, &DragDropScroller::onTimer); +} + +void DragDropScroller::addScrollArea(QScrollArea* scrollArea) +{ + impl->m_scrollAreas << scrollArea; + scrollArea->viewport()->setAcceptDrops(true); +} + +void DragDropScroller::removeScrollArea(QScrollArea* scrollArea) +{ + impl->m_scrollAreas.removeAll(scrollArea); + scrollArea->viewport()->setAcceptDrops(false); +} + +bool DragDropScroller::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::DragMove) + { + auto w = static_cast(obj); + + if (impl->m_currentScrollArea && impl->m_currentScrollArea->isAncestorOf(w)) + { + auto moveEvent = static_cast(event); + + auto pos = moveEvent->pos(); + if (impl->m_currentScrollArea->viewport() != w) + { + auto globalPos = w->mapToGlobal(moveEvent->pos()); + pos = impl->m_currentScrollArea->viewport()->mapFromGlobal(globalPos); + } + + auto isInTopZone = pos.y() > impl->m_currentScrollArea->viewport()->size().height() - SCROLL_ZONE_SIZE; + auto isInBottomZone = pos.y() < SCROLL_ZONE_SIZE; + + if (!isInTopZone && !isInBottomZone) + { + impl->m_direction = DragDropScrollerPrivate::ScrollDirection::unknown; + impl->m_timer->stop(); + } + else if (!impl->m_timer->isActive()) + { + impl->m_direction = isInTopZone ? DragDropScrollerPrivate::ScrollDirection::up : DragDropScrollerPrivate::ScrollDirection::down; + impl->m_timer->start(); + } + } + } + else if (event->type() == QEvent::DragEnter) + { + auto w = static_cast(obj); + + for (auto scrollArea : impl-> m_scrollAreas) + { + if (impl->m_currentScrollArea != scrollArea && scrollArea->isAncestorOf(w)) + { + auto enterEvent = static_cast(event); + enterEvent->acceptProposedAction(); + enterEvent->setDropAction(Qt::IgnoreAction); + impl->m_currentScrollArea = scrollArea; + break; + } + } + } + else if (event->type() == QEvent::DragLeave) + { + auto w = static_cast(obj); + if (impl->m_currentScrollArea) + { + if (!QRect(QPoint(), impl->m_currentScrollArea->size()).contains(impl->m_currentScrollArea->mapFromGlobal(QCursor::pos()))) + { + impl->m_currentScrollArea = nullptr; + impl->m_direction = DragDropScrollerPrivate::ScrollDirection::unknown; + impl->m_timer->stop(); + } + } + } + else if (event->type() == QEvent::Drop) + { + auto w = static_cast(obj); + if (impl->m_currentScrollArea) + { + impl->m_currentScrollArea = nullptr; + impl->m_direction = DragDropScrollerPrivate::ScrollDirection::unknown; + impl->m_timer->stop(); + } + } + + return false; +} + +void DragDropScroller::onTimer() +{ + if (impl->m_currentScrollArea) + { + auto mvt = 0; + switch (impl->m_direction) + { + case DragDropScrollerPrivate::ScrollDirection::up: + mvt = SCROLL_SPEED; + break; + case DragDropScrollerPrivate::ScrollDirection::down: + mvt = -SCROLL_SPEED; + break; + default: + break; + } + + impl->m_currentScrollArea->verticalScrollBar()->setValue(impl->m_currentScrollArea->verticalScrollBar()->value() + mvt); + } +} struct DragDropHelper::DragDropHelperPrivate { @@ -97,6 +230,16 @@ bool DragDropHelper::isPlaceHolderSet() const return impl->m_placeHolder->parentWidget(); } +void DragDropHelper::addDragDropScrollArea(QScrollArea *scrollArea) +{ + impl->m_dragDropScroller->addScrollArea(scrollArea); +} + +void DragDropHelper::removeDragDropScrollArea(QScrollArea *scrollArea) +{ + impl->m_dragDropScroller->removeScrollArea(scrollArea); +} + QUrl DragDropHelper::imageTemporaryUrl(const QImage& image) const { image.save(impl->m_imageTempUrl); diff --git a/gui/src/Visualization/VisualizationTabWidget.cpp b/gui/src/Visualization/VisualizationTabWidget.cpp index bb4c90d..31df3ea 100644 --- a/gui/src/Visualization/VisualizationTabWidget.cpp +++ b/gui/src/Visualization/VisualizationTabWidget.cpp @@ -61,6 +61,7 @@ VisualizationTabWidget::VisualizationTabWidget(const QString &name, QWidget *par ui->dragDropContainer->setAcceptedMimeTypes({DragDropHelper::MIME_TYPE_GRAPH, DragDropHelper::MIME_TYPE_ZONE}); connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccured, this, &VisualizationTabWidget::dropMimeData); + sqpApp->dragDropHelper().addDragDropScrollArea(ui->scrollArea); // Widget is deleted when closed setAttribute(Qt::WA_DeleteOnClose); @@ -68,6 +69,7 @@ VisualizationTabWidget::VisualizationTabWidget(const QString &name, QWidget *par VisualizationTabWidget::~VisualizationTabWidget() { + sqpApp->dragDropHelper().removeDragDropScrollArea(ui->scrollArea); delete ui; }