@@ -0,0 +1,45 | |||||
|
1 | #ifndef VISUALIZATIONDRAGDROPCONTAINER_H | |||
|
2 | #define VISUALIZATIONDRAGDROPCONTAINER_H | |||
|
3 | ||||
|
4 | #include <QWidget> | |||
|
5 | #include <QVBoxLayout> | |||
|
6 | #include <QMimeData> | |||
|
7 | #include <Common/spimpl.h> | |||
|
8 | ||||
|
9 | class VisualizationDragWidget; | |||
|
10 | ||||
|
11 | class VisualizationDragDropContainer : public QWidget | |||
|
12 | { | |||
|
13 | Q_OBJECT | |||
|
14 | ||||
|
15 | signals: | |||
|
16 | void dropOccured(int dropIndex, const QMimeData* mimeData); | |||
|
17 | ||||
|
18 | public: | |||
|
19 | VisualizationDragDropContainer(QWidget* parent = nullptr); | |||
|
20 | ||||
|
21 | void addDragWidget(VisualizationDragWidget* dragWidget); | |||
|
22 | void insertDragWidget(int index, VisualizationDragWidget* dragWidget); | |||
|
23 | ||||
|
24 | void setAcceptedMimeTypes(const QStringList& mimeTypes); | |||
|
25 | void setMergeAllowedMimeTypes(const QStringList& mimeTypes); | |||
|
26 | ||||
|
27 | int countDragWidget() const; | |||
|
28 | ||||
|
29 | protected: | |||
|
30 | void dragEnterEvent(QDragEnterEvent *event); | |||
|
31 | void dragLeaveEvent(QDragLeaveEvent *event); | |||
|
32 | void dragMoveEvent(QDragMoveEvent *event); | |||
|
33 | void dropEvent(QDropEvent *event); | |||
|
34 | ||||
|
35 | private: | |||
|
36 | ||||
|
37 | ||||
|
38 | class VisualizationDragDropContainerPrivate; | |||
|
39 | spimpl::unique_impl_ptr<VisualizationDragDropContainerPrivate> impl; | |||
|
40 | ||||
|
41 | private slots: | |||
|
42 | void startDrag(VisualizationDragWidget* dragWidget, const QPoint& dragPosition); | |||
|
43 | }; | |||
|
44 | ||||
|
45 | #endif // VISUALIZATIONDRAGDROPCONTAINER_H |
@@ -0,0 +1,30 | |||||
|
1 | #ifndef VISUALIZATIONDRAGWIDGET_H | |||
|
2 | #define VISUALIZATIONDRAGWIDGET_H | |||
|
3 | ||||
|
4 | #include <QWidget> | |||
|
5 | #include <QMimeData> | |||
|
6 | #include <Common/spimpl.h> | |||
|
7 | ||||
|
8 | class VisualizationDragWidget : public QWidget | |||
|
9 | { | |||
|
10 | Q_OBJECT | |||
|
11 | ||||
|
12 | public: | |||
|
13 | VisualizationDragWidget(QWidget* parent = nullptr); | |||
|
14 | ||||
|
15 | virtual QMimeData* mimeData() const = 0; | |||
|
16 | virtual bool isDragAllowed() const = 0; | |||
|
17 | ||||
|
18 | protected: | |||
|
19 | virtual void mousePressEvent(QMouseEvent *event) override; | |||
|
20 | virtual void mouseMoveEvent(QMouseEvent *event) override; | |||
|
21 | ||||
|
22 | private: | |||
|
23 | class VisualizationDragWidgetPrivate; | |||
|
24 | spimpl::unique_impl_ptr<VisualizationDragWidgetPrivate> impl; | |||
|
25 | ||||
|
26 | signals: | |||
|
27 | void dragDetected(VisualizationDragWidget* dragWidget, const QPoint& dragPosition); | |||
|
28 | }; | |||
|
29 | ||||
|
30 | #endif // VISUALIZATIONDRAGWIDGET_H |
@@ -0,0 +1,312 | |||||
|
1 | #include "Visualization/VisualizationDragDropContainer.h" | |||
|
2 | #include "Visualization/VisualizationDragWidget.h" | |||
|
3 | #include "SqpApplication.h" | |||
|
4 | #include "DragDropHelper.h" | |||
|
5 | ||||
|
6 | #include <QDrag> | |||
|
7 | #include <QVBoxLayout> | |||
|
8 | #include <QDragEnterEvent> | |||
|
9 | ||||
|
10 | #include <memory> | |||
|
11 | ||||
|
12 | struct VisualizationDragDropContainer::VisualizationDragDropContainerPrivate { | |||
|
13 | ||||
|
14 | QVBoxLayout* m_layout; | |||
|
15 | QStringList m_acceptedMimeTypes; | |||
|
16 | QStringList m_mergeAllowedMimeTypes; | |||
|
17 | ||||
|
18 | explicit VisualizationDragDropContainerPrivate(QWidget* widget) | |||
|
19 | { | |||
|
20 | m_layout = new QVBoxLayout(widget); | |||
|
21 | m_layout->setContentsMargins(0,0,0,0); | |||
|
22 | } | |||
|
23 | ||||
|
24 | bool acceptMimeData(const QMimeData* data) const | |||
|
25 | { | |||
|
26 | for (const auto& type : m_acceptedMimeTypes) | |||
|
27 | { | |||
|
28 | if (data->hasFormat(type)) | |||
|
29 | return true; | |||
|
30 | } | |||
|
31 | ||||
|
32 | return false; | |||
|
33 | } | |||
|
34 | ||||
|
35 | bool allowMergeMimeData(const QMimeData* data) const | |||
|
36 | { | |||
|
37 | for (const auto& type : m_mergeAllowedMimeTypes) | |||
|
38 | { | |||
|
39 | if (data->hasFormat(type)) | |||
|
40 | return true; | |||
|
41 | } | |||
|
42 | ||||
|
43 | return false; | |||
|
44 | } | |||
|
45 | ||||
|
46 | bool hasPlaceHolder() const | |||
|
47 | { | |||
|
48 | return sqpApp->dragDropHelper().placeHolder().parentWidget() == m_layout->parentWidget(); | |||
|
49 | } | |||
|
50 | ||||
|
51 | VisualizationDragWidget* getChildDragWidgetAt(QWidget* parent, const QPoint &pos) const | |||
|
52 | { | |||
|
53 | VisualizationDragWidget* dragWidget = nullptr; | |||
|
54 | ||||
|
55 | for (auto child : parent->children()) | |||
|
56 | { | |||
|
57 | auto widget = qobject_cast<VisualizationDragWidget*>(child); | |||
|
58 | if (widget && widget->isVisible()) | |||
|
59 | { | |||
|
60 | if (widget->frameGeometry().contains(pos)) | |||
|
61 | { | |||
|
62 | dragWidget = widget; | |||
|
63 | break; | |||
|
64 | } | |||
|
65 | } | |||
|
66 | } | |||
|
67 | ||||
|
68 | return dragWidget; | |||
|
69 | } | |||
|
70 | ||||
|
71 | bool cursorIsInContainer(QWidget* container) const | |||
|
72 | { | |||
|
73 | auto adustNum = 18; //to be safe, in case of scrollbar on the side | |||
|
74 | auto containerRect = QRect(QPoint(), container->contentsRect().size()).adjusted(adustNum, adustNum, -adustNum, -adustNum); | |||
|
75 | qDebug() << containerRect << container->mapFromGlobal(QCursor::pos()); | |||
|
76 | return containerRect.contains(container->mapFromGlobal(QCursor::pos())); | |||
|
77 | } | |||
|
78 | ||||
|
79 | }; | |||
|
80 | ||||
|
81 | VisualizationDragDropContainer::VisualizationDragDropContainer(QWidget *parent) | |||
|
82 | : QWidget{parent}, impl{spimpl::make_unique_impl<VisualizationDragDropContainerPrivate>(this)} | |||
|
83 | { | |||
|
84 | setAcceptDrops(true); | |||
|
85 | } | |||
|
86 | ||||
|
87 | void VisualizationDragDropContainer::addDragWidget(VisualizationDragWidget *dragWidget) | |||
|
88 | { | |||
|
89 | impl->m_layout->addWidget(dragWidget); | |||
|
90 | disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr); | |||
|
91 | connect(dragWidget, &VisualizationDragWidget::dragDetected, this, &VisualizationDragDropContainer::startDrag); | |||
|
92 | } | |||
|
93 | ||||
|
94 | void VisualizationDragDropContainer::insertDragWidget(int index, VisualizationDragWidget *dragWidget) | |||
|
95 | { | |||
|
96 | impl->m_layout->insertWidget(index, dragWidget); | |||
|
97 | disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr); | |||
|
98 | connect(dragWidget, &VisualizationDragWidget::dragDetected, this, &VisualizationDragDropContainer::startDrag); | |||
|
99 | } | |||
|
100 | ||||
|
101 | void VisualizationDragDropContainer::setAcceptedMimeTypes(const QStringList &mimeTypes) | |||
|
102 | { | |||
|
103 | impl->m_acceptedMimeTypes = mimeTypes; | |||
|
104 | } | |||
|
105 | ||||
|
106 | void VisualizationDragDropContainer::setMergeAllowedMimeTypes(const QStringList &mimeTypes) | |||
|
107 | { | |||
|
108 | impl->m_mergeAllowedMimeTypes = mimeTypes; | |||
|
109 | } | |||
|
110 | ||||
|
111 | int VisualizationDragDropContainer::countDragWidget() const | |||
|
112 | { | |||
|
113 | auto nbGraph = 0; | |||
|
114 | for (auto child : children()) | |||
|
115 | { | |||
|
116 | auto widget = qobject_cast<VisualizationDragWidget*>(child); | |||
|
117 | if (widget) | |||
|
118 | { | |||
|
119 | nbGraph += 1; | |||
|
120 | } | |||
|
121 | } | |||
|
122 | ||||
|
123 | return nbGraph; | |||
|
124 | } | |||
|
125 | ||||
|
126 | void VisualizationDragDropContainer::startDrag(VisualizationDragWidget *dragWidget, const QPoint &dragPosition) | |||
|
127 | { | |||
|
128 | auto& helper = sqpApp->dragDropHelper(); | |||
|
129 | ||||
|
130 | //Note: The management of the drag object is done by Qt | |||
|
131 | auto *drag = new QDrag{dragWidget}; | |||
|
132 | drag->setHotSpot(dragPosition); | |||
|
133 | ||||
|
134 | auto mimeData = dragWidget->mimeData(); | |||
|
135 | drag->setMimeData(mimeData); | |||
|
136 | ||||
|
137 | auto pixmap = QPixmap(dragWidget->size()); | |||
|
138 | dragWidget->render(&pixmap); | |||
|
139 | drag->setPixmap(pixmap); | |||
|
140 | ||||
|
141 | auto image = pixmap.toImage(); | |||
|
142 | mimeData->setImageData(image); | |||
|
143 | mimeData->setUrls({helper.imageTemporaryUrl(image)}); | |||
|
144 | ||||
|
145 | if (impl->m_layout->indexOf(dragWidget) >= 0) | |||
|
146 | { | |||
|
147 | helper.setCurrentDragWidget(dragWidget); | |||
|
148 | ||||
|
149 | if (impl->cursorIsInContainer(this)) | |||
|
150 | { | |||
|
151 | auto dragWidgetIndex = impl->m_layout->indexOf(dragWidget); | |||
|
152 | helper.insertPlaceHolder(impl->m_layout, dragWidgetIndex); | |||
|
153 | dragWidget->setVisible(false); | |||
|
154 | } | |||
|
155 | } | |||
|
156 | ||||
|
157 | //Note: The exec() is blocking on windows but not on linux and macOS | |||
|
158 | drag->exec(Qt::MoveAction | Qt::CopyAction); | |||
|
159 | } | |||
|
160 | ||||
|
161 | void VisualizationDragDropContainer::dragEnterEvent(QDragEnterEvent *event) | |||
|
162 | { | |||
|
163 | if (impl->acceptMimeData(event->mimeData())) | |||
|
164 | { | |||
|
165 | event->acceptProposedAction(); | |||
|
166 | ||||
|
167 | auto& helper = sqpApp->dragDropHelper(); | |||
|
168 | ||||
|
169 | if (!impl->hasPlaceHolder()) | |||
|
170 | { | |||
|
171 | auto dragWidget = helper.getCurrentDragWidget(); | |||
|
172 | auto parentWidget = qobject_cast<VisualizationDragDropContainer*>(dragWidget->parentWidget()); | |||
|
173 | if (parentWidget) | |||
|
174 | { | |||
|
175 | dragWidget->setVisible(false); | |||
|
176 | } | |||
|
177 | ||||
|
178 | auto dragWidgetHovered = impl->getChildDragWidgetAt(this, event->pos()); | |||
|
179 | ||||
|
180 | if (dragWidgetHovered) | |||
|
181 | { | |||
|
182 | auto hoveredWidgetIndex = impl->m_layout->indexOf(dragWidgetHovered); | |||
|
183 | auto dragWidgetIndex = impl->m_layout->indexOf(helper.getCurrentDragWidget()); | |||
|
184 | if (dragWidgetIndex >= 0 && dragWidgetIndex <= hoveredWidgetIndex) | |||
|
185 | hoveredWidgetIndex += 1; //Correction of the index if the drop occurs in the same container | |||
|
186 | ||||
|
187 | helper.insertPlaceHolder(impl->m_layout, hoveredWidgetIndex); | |||
|
188 | } | |||
|
189 | else | |||
|
190 | { | |||
|
191 | helper.insertPlaceHolder(impl->m_layout, 0); | |||
|
192 | } | |||
|
193 | } | |||
|
194 | } | |||
|
195 | else | |||
|
196 | event->ignore(); | |||
|
197 | ||||
|
198 | QWidget::dragEnterEvent(event); | |||
|
199 | } | |||
|
200 | ||||
|
201 | void VisualizationDragDropContainer::dragLeaveEvent(QDragLeaveEvent *event) | |||
|
202 | { | |||
|
203 | Q_UNUSED(event); | |||
|
204 | ||||
|
205 | auto& helper = sqpApp->dragDropHelper(); | |||
|
206 | ||||
|
207 | if (!impl->cursorIsInContainer(this)) | |||
|
208 | { | |||
|
209 | helper.removePlaceHolder(); | |||
|
210 | ||||
|
211 | bool isInternal = true; | |||
|
212 | if (isInternal) | |||
|
213 | { | |||
|
214 | //Only if the drag is strated from the visualization | |||
|
215 | //Show the drag widget at its original place | |||
|
216 | //So the drag widget doesn't stay hidden if the drop occurs outside the visualization drop zone | |||
|
217 | //(It is not possible to catch a drop event outside of the application) | |||
|
218 | ||||
|
219 | auto dragWidget = sqpApp->dragDropHelper().getCurrentDragWidget(); | |||
|
220 | if (dragWidget) | |||
|
221 | { | |||
|
222 | dragWidget->setVisible(true); | |||
|
223 | } | |||
|
224 | } | |||
|
225 | } | |||
|
226 | ||||
|
227 | QWidget::dragLeaveEvent(event); | |||
|
228 | } | |||
|
229 | ||||
|
230 | void VisualizationDragDropContainer::dragMoveEvent(QDragMoveEvent *event) | |||
|
231 | { | |||
|
232 | if (impl->acceptMimeData(event->mimeData())) | |||
|
233 | { | |||
|
234 | auto dragWidgetHovered = impl->getChildDragWidgetAt(this, event->pos()); | |||
|
235 | if (dragWidgetHovered) | |||
|
236 | { | |||
|
237 | auto canMerge = impl->allowMergeMimeData(event->mimeData()); | |||
|
238 | ||||
|
239 | auto nbDragWidget = countDragWidget(); | |||
|
240 | if (nbDragWidget > 0) | |||
|
241 | { | |||
|
242 | auto graphHeight = size().height() / nbDragWidget; | |||
|
243 | auto dropIndex = floor(event->pos().y() / graphHeight); | |||
|
244 | auto zoneSize = qMin(graphHeight / 3.0, 150.0); | |||
|
245 | ||||
|
246 | auto isOnTop = event->pos().y() < dropIndex * graphHeight + zoneSize; | |||
|
247 | auto isOnBottom = event->pos().y() > (dropIndex + 1) * graphHeight - zoneSize; | |||
|
248 | ||||
|
249 | auto& helper = sqpApp->dragDropHelper(); | |||
|
250 | auto placeHolderIndex = impl->m_layout->indexOf(&(helper.placeHolder())); | |||
|
251 | ||||
|
252 | if (isOnTop || isOnBottom) | |||
|
253 | { | |||
|
254 | if (isOnBottom) | |||
|
255 | dropIndex += 1; | |||
|
256 | ||||
|
257 | auto dragWidgetIndex = impl->m_layout->indexOf(helper.getCurrentDragWidget()); | |||
|
258 | if (dragWidgetIndex >= 0 && dragWidgetIndex <= dropIndex) | |||
|
259 | dropIndex += 1; //Correction of the index if the drop occurs in the same container | |||
|
260 | ||||
|
261 | if (dropIndex != placeHolderIndex) | |||
|
262 | { | |||
|
263 | helper.insertPlaceHolder(impl->m_layout, dropIndex); | |||
|
264 | } | |||
|
265 | } | |||
|
266 | else if (canMerge) | |||
|
267 | { | |||
|
268 | //drop on the middle -> merge | |||
|
269 | if (impl->hasPlaceHolder()) | |||
|
270 | { | |||
|
271 | helper.removePlaceHolder(); | |||
|
272 | } | |||
|
273 | } | |||
|
274 | } | |||
|
275 | } | |||
|
276 | } | |||
|
277 | else | |||
|
278 | event->ignore(); | |||
|
279 | ||||
|
280 | QWidget::dragMoveEvent(event); | |||
|
281 | } | |||
|
282 | ||||
|
283 | void VisualizationDragDropContainer::dropEvent(QDropEvent *event) | |||
|
284 | { | |||
|
285 | if (impl->acceptMimeData(event->mimeData())) | |||
|
286 | { | |||
|
287 | auto dragWidget = sqpApp->dragDropHelper().getCurrentDragWidget(); | |||
|
288 | if (impl->hasPlaceHolder() && dragWidget) | |||
|
289 | { | |||
|
290 | auto& helper = sqpApp->dragDropHelper(); | |||
|
291 | ||||
|
292 | auto droppedIndex = impl->m_layout->indexOf(&helper.placeHolder()); | |||
|
293 | ||||
|
294 | auto dragWidgetIndex = impl->m_layout->indexOf(dragWidget); | |||
|
295 | if (dragWidgetIndex >= 0 && dragWidgetIndex < droppedIndex) | |||
|
296 | droppedIndex -= 1; //Correction of the index if the drop occurs in the same container | |||
|
297 | ||||
|
298 | dragWidget->setVisible(true); | |||
|
299 | dragWidget->setStyleSheet(""); | |||
|
300 | ||||
|
301 | event->acceptProposedAction(); | |||
|
302 | ||||
|
303 | helper.removePlaceHolder(); | |||
|
304 | ||||
|
305 | emit dropOccured(droppedIndex, event->mimeData()); | |||
|
306 | } | |||
|
307 | } | |||
|
308 | else | |||
|
309 | event->ignore(); | |||
|
310 | ||||
|
311 | QWidget::dropEvent(event); | |||
|
312 | } |
@@ -0,0 +1,50 | |||||
|
1 | #include "Visualization/VisualizationDragWidget.h" | |||
|
2 | #include "Visualization/VisualizationDragDropContainer.h" | |||
|
3 | ||||
|
4 | #include <QMouseEvent> | |||
|
5 | #include <QApplication> | |||
|
6 | ||||
|
7 | struct VisualizationDragWidget::VisualizationDragWidgetPrivate { | |||
|
8 | ||||
|
9 | QPoint m_dragStartPosition; | |||
|
10 | bool m_dragStartPositionValid = false; | |||
|
11 | ||||
|
12 | explicit VisualizationDragWidgetPrivate() | |||
|
13 | { | |||
|
14 | } | |||
|
15 | }; | |||
|
16 | ||||
|
17 | VisualizationDragWidget::VisualizationDragWidget(QWidget* parent) | |||
|
18 | : QWidget{parent}, impl{spimpl::make_unique_impl<VisualizationDragWidgetPrivate>()} | |||
|
19 | { | |||
|
20 | ||||
|
21 | } | |||
|
22 | ||||
|
23 | void VisualizationDragWidget::mousePressEvent(QMouseEvent *event) | |||
|
24 | { | |||
|
25 | if (event->button() == Qt::LeftButton) | |||
|
26 | impl->m_dragStartPosition = event->pos(); | |||
|
27 | ||||
|
28 | impl->m_dragStartPositionValid = isDragAllowed(); | |||
|
29 | ||||
|
30 | QWidget::mousePressEvent(event); | |||
|
31 | } | |||
|
32 | ||||
|
33 | void VisualizationDragWidget::mouseMoveEvent(QMouseEvent *event) | |||
|
34 | { | |||
|
35 | if (!impl->m_dragStartPositionValid || !isDragAllowed()) | |||
|
36 | return; | |||
|
37 | ||||
|
38 | if (!(event->buttons() & Qt::LeftButton)) | |||
|
39 | return; | |||
|
40 | ||||
|
41 | if (!event->modifiers().testFlag(Qt::AltModifier)) | |||
|
42 | return; | |||
|
43 | ||||
|
44 | if ((event->pos() - impl->m_dragStartPosition).manhattanLength() < QApplication::startDragDistance()) | |||
|
45 | return; | |||
|
46 | ||||
|
47 | emit dragDetected(this, impl->m_dragStartPosition); | |||
|
48 | ||||
|
49 | QWidget::mouseMoveEvent(event); | |||
|
50 | } |
General Comments 0
You need to be logged in to leave comments.
Login now