DragDropScroller.cpp
125 lines
| 4.4 KiB
| text/x-c
|
CppLexer
r891 | #include "DragAndDrop/DragDropScroller.h" | |||
#include <QDragEnterEvent> | ||||
#include <QDragMoveEvent> | ||||
#include <QScrollBar> | ||||
#include <QTimer> | ||||
const int SCROLL_SPEED = 5; | ||||
const int SCROLL_ZONE_SIZE = 50; | ||||
struct DragDropScroller::DragDropScrollerPrivate { | ||||
QList<QScrollArea *> m_ScrollAreas; | ||||
QScrollArea *m_CurrentScrollArea = nullptr; | ||||
std::unique_ptr<QTimer> m_Timer = nullptr; | ||||
enum class ScrollDirection { up, down, unknown }; | ||||
ScrollDirection m_Direction = ScrollDirection::unknown; | ||||
explicit DragDropScrollerPrivate() : m_Timer{std::make_unique<QTimer>()} | ||||
{ | ||||
m_Timer->setInterval(0); | ||||
} | ||||
}; | ||||
DragDropScroller::DragDropScroller(QObject *parent) | ||||
: QObject{parent}, impl{spimpl::make_unique_impl<DragDropScrollerPrivate>()} | ||||
{ | ||||
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<QWidget *>(obj); | ||||
if (impl->m_CurrentScrollArea && impl->m_CurrentScrollArea->isAncestorOf(w)) { | ||||
auto moveEvent = static_cast<QDragMoveEvent *>(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<QWidget *>(obj); | ||||
for (auto scrollArea : impl->m_ScrollAreas) { | ||||
if (impl->m_CurrentScrollArea != scrollArea && scrollArea->isAncestorOf(w)) { | ||||
auto enterEvent = static_cast<QDragEnterEvent *>(event); | ||||
enterEvent->acceptProposedAction(); | ||||
enterEvent->setDropAction(Qt::IgnoreAction); | ||||
impl->m_CurrentScrollArea = scrollArea; | ||||
break; | ||||
} | ||||
} | ||||
} | ||||
else if (event->type() == QEvent::DragLeave) { | ||||
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) { | ||||
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); | ||||
} | ||||
} | ||||