@@ -10,6 +10,31 class VisualizationDragWidget; | |||
|
10 | 10 | class QMimeData; |
|
11 | 11 | |
|
12 | 12 | /** |
|
13 | * @brief Event filter class which manage the scroll of QScrollArea during a drag&drop operation. | |
|
14 | * @note A QScrollArea inside an other QScrollArea is not fully supported. | |
|
15 | */ | |
|
16 | class DragDropScroller : public QObject | |
|
17 | { | |
|
18 | Q_OBJECT | |
|
19 | ||
|
20 | public: | |
|
21 | DragDropScroller(QObject* parent = nullptr); | |
|
22 | ||
|
23 | void addScrollArea(QScrollArea* scrollArea); | |
|
24 | void removeScrollArea(QScrollArea* scrollArea); | |
|
25 | ||
|
26 | protected: | |
|
27 | bool eventFilter(QObject *obj, QEvent *event); | |
|
28 | ||
|
29 | private: | |
|
30 | class DragDropScrollerPrivate; | |
|
31 | spimpl::unique_impl_ptr<DragDropScrollerPrivate> impl; | |
|
32 | ||
|
33 | private slots: | |
|
34 | void onTimer(); | |
|
35 | }; | |
|
36 | ||
|
37 | /** | |
|
13 | 38 | * @brief Helper class for drag&drop operations. |
|
14 | 39 | */ |
|
15 | 40 | class DragDropHelper |
@@ -13,6 +13,139 | |||
|
13 | 13 | const QString DragDropHelper::MIME_TYPE_GRAPH = "scqlop/graph"; |
|
14 | 14 | const QString DragDropHelper::MIME_TYPE_ZONE = "scqlop/zone"; |
|
15 | 15 | |
|
16 | const int SCROLL_SPEED = 5; | |
|
17 | const int SCROLL_ZONE_SIZE = 50; | |
|
18 | ||
|
19 | struct DragDropScroller::DragDropScrollerPrivate { | |
|
20 | ||
|
21 | QList<QScrollArea*> m_scrollAreas; | |
|
22 | QScrollArea* m_currentScrollArea = nullptr; | |
|
23 | std::unique_ptr<QTimer> m_timer = nullptr; | |
|
24 | ||
|
25 | ||
|
26 | enum class ScrollDirection {up, down, unknown}; | |
|
27 | ScrollDirection m_direction = ScrollDirection::unknown; | |
|
28 | ||
|
29 | explicit DragDropScrollerPrivate() | |
|
30 | : m_timer{std::make_unique<QTimer>()} | |
|
31 | { | |
|
32 | m_timer->setInterval(0); | |
|
33 | } | |
|
34 | }; | |
|
35 | ||
|
36 | DragDropScroller::DragDropScroller(QObject* parent) | |
|
37 | : QObject{parent}, impl{spimpl::make_unique_impl<DragDropScrollerPrivate>()} | |
|
38 | { | |
|
39 | connect(impl->m_timer.get(), &QTimer::timeout, this, &DragDropScroller::onTimer); | |
|
40 | } | |
|
41 | ||
|
42 | void DragDropScroller::addScrollArea(QScrollArea* scrollArea) | |
|
43 | { | |
|
44 | impl->m_scrollAreas << scrollArea; | |
|
45 | scrollArea->viewport()->setAcceptDrops(true); | |
|
46 | } | |
|
47 | ||
|
48 | void DragDropScroller::removeScrollArea(QScrollArea* scrollArea) | |
|
49 | { | |
|
50 | impl->m_scrollAreas.removeAll(scrollArea); | |
|
51 | scrollArea->viewport()->setAcceptDrops(false); | |
|
52 | } | |
|
53 | ||
|
54 | bool DragDropScroller::eventFilter(QObject *obj, QEvent *event) | |
|
55 | { | |
|
56 | if (event->type() == QEvent::DragMove) | |
|
57 | { | |
|
58 | auto w = static_cast<QWidget*>(obj); | |
|
59 | ||
|
60 | if (impl->m_currentScrollArea && impl->m_currentScrollArea->isAncestorOf(w)) | |
|
61 | { | |
|
62 | auto moveEvent = static_cast<QDragMoveEvent*>(event); | |
|
63 | ||
|
64 | auto pos = moveEvent->pos(); | |
|
65 | if (impl->m_currentScrollArea->viewport() != w) | |
|
66 | { | |
|
67 | auto globalPos = w->mapToGlobal(moveEvent->pos()); | |
|
68 | pos = impl->m_currentScrollArea->viewport()->mapFromGlobal(globalPos); | |
|
69 | } | |
|
70 | ||
|
71 | auto isInTopZone = pos.y() > impl->m_currentScrollArea->viewport()->size().height() - SCROLL_ZONE_SIZE; | |
|
72 | auto isInBottomZone = pos.y() < SCROLL_ZONE_SIZE; | |
|
73 | ||
|
74 | if (!isInTopZone && !isInBottomZone) | |
|
75 | { | |
|
76 | impl->m_direction = DragDropScrollerPrivate::ScrollDirection::unknown; | |
|
77 | impl->m_timer->stop(); | |
|
78 | } | |
|
79 | else if (!impl->m_timer->isActive()) | |
|
80 | { | |
|
81 | impl->m_direction = isInTopZone ? DragDropScrollerPrivate::ScrollDirection::up : DragDropScrollerPrivate::ScrollDirection::down; | |
|
82 | impl->m_timer->start(); | |
|
83 | } | |
|
84 | } | |
|
85 | } | |
|
86 | else if (event->type() == QEvent::DragEnter) | |
|
87 | { | |
|
88 | auto w = static_cast<QWidget*>(obj); | |
|
89 | ||
|
90 | for (auto scrollArea : impl-> m_scrollAreas) | |
|
91 | { | |
|
92 | if (impl->m_currentScrollArea != scrollArea && scrollArea->isAncestorOf(w)) | |
|
93 | { | |
|
94 | auto enterEvent = static_cast<QDragEnterEvent*>(event); | |
|
95 | enterEvent->acceptProposedAction(); | |
|
96 | enterEvent->setDropAction(Qt::IgnoreAction); | |
|
97 | impl->m_currentScrollArea = scrollArea; | |
|
98 | break; | |
|
99 | } | |
|
100 | } | |
|
101 | } | |
|
102 | else if (event->type() == QEvent::DragLeave) | |
|
103 | { | |
|
104 | auto w = static_cast<QWidget*>(obj); | |
|
105 | if (impl->m_currentScrollArea) | |
|
106 | { | |
|
107 | if (!QRect(QPoint(), impl->m_currentScrollArea->size()).contains(impl->m_currentScrollArea->mapFromGlobal(QCursor::pos()))) | |
|
108 | { | |
|
109 | impl->m_currentScrollArea = nullptr; | |
|
110 | impl->m_direction = DragDropScrollerPrivate::ScrollDirection::unknown; | |
|
111 | impl->m_timer->stop(); | |
|
112 | } | |
|
113 | } | |
|
114 | } | |
|
115 | else if (event->type() == QEvent::Drop) | |
|
116 | { | |
|
117 | auto w = static_cast<QWidget*>(obj); | |
|
118 | if (impl->m_currentScrollArea) | |
|
119 | { | |
|
120 | impl->m_currentScrollArea = nullptr; | |
|
121 | impl->m_direction = DragDropScrollerPrivate::ScrollDirection::unknown; | |
|
122 | impl->m_timer->stop(); | |
|
123 | } | |
|
124 | } | |
|
125 | ||
|
126 | return false; | |
|
127 | } | |
|
128 | ||
|
129 | void DragDropScroller::onTimer() | |
|
130 | { | |
|
131 | if (impl->m_currentScrollArea) | |
|
132 | { | |
|
133 | auto mvt = 0; | |
|
134 | switch (impl->m_direction) | |
|
135 | { | |
|
136 | case DragDropScrollerPrivate::ScrollDirection::up: | |
|
137 | mvt = SCROLL_SPEED; | |
|
138 | break; | |
|
139 | case DragDropScrollerPrivate::ScrollDirection::down: | |
|
140 | mvt = -SCROLL_SPEED; | |
|
141 | break; | |
|
142 | default: | |
|
143 | break; | |
|
144 | } | |
|
145 | ||
|
146 | impl->m_currentScrollArea->verticalScrollBar()->setValue(impl->m_currentScrollArea->verticalScrollBar()->value() + mvt); | |
|
147 | } | |
|
148 | } | |
|
16 | 149 | |
|
17 | 150 | struct DragDropHelper::DragDropHelperPrivate { |
|
18 | 151 | |
@@ -97,6 +230,16 bool DragDropHelper::isPlaceHolderSet() const | |||
|
97 | 230 | return impl->m_placeHolder->parentWidget(); |
|
98 | 231 | } |
|
99 | 232 | |
|
233 | void DragDropHelper::addDragDropScrollArea(QScrollArea *scrollArea) | |
|
234 | { | |
|
235 | impl->m_dragDropScroller->addScrollArea(scrollArea); | |
|
236 | } | |
|
237 | ||
|
238 | void DragDropHelper::removeDragDropScrollArea(QScrollArea *scrollArea) | |
|
239 | { | |
|
240 | impl->m_dragDropScroller->removeScrollArea(scrollArea); | |
|
241 | } | |
|
242 | ||
|
100 | 243 | QUrl DragDropHelper::imageTemporaryUrl(const QImage& image) const |
|
101 | 244 | { |
|
102 | 245 | image.save(impl->m_imageTempUrl); |
@@ -61,6 +61,7 VisualizationTabWidget::VisualizationTabWidget(const QString &name, QWidget *par | |||
|
61 | 61 | |
|
62 | 62 | ui->dragDropContainer->setAcceptedMimeTypes({DragDropHelper::MIME_TYPE_GRAPH, DragDropHelper::MIME_TYPE_ZONE}); |
|
63 | 63 | connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccured, this, &VisualizationTabWidget::dropMimeData); |
|
64 | sqpApp->dragDropHelper().addDragDropScrollArea(ui->scrollArea); | |
|
64 | 65 | |
|
65 | 66 | // Widget is deleted when closed |
|
66 | 67 | setAttribute(Qt::WA_DeleteOnClose); |
@@ -68,6 +69,7 VisualizationTabWidget::VisualizationTabWidget(const QString &name, QWidget *par | |||
|
68 | 69 | |
|
69 | 70 | VisualizationTabWidget::~VisualizationTabWidget() |
|
70 | 71 | { |
|
72 | sqpApp->dragDropHelper().removeDragDropScrollArea(ui->scrollArea); | |
|
71 | 73 | delete ui; |
|
72 | 74 | } |
|
73 | 75 |
General Comments 0
You need to be logged in to leave comments.
Login now