##// END OF EJS Templates
Displays the miniature of the graph of the zone during a drag
trabillard -
r975:bf3f08af7bf9
parent child
Show More
@@ -1,453 +1,455
1 1 #include "Visualization/VisualizationDragDropContainer.h"
2 2 #include "DragAndDrop/DragDropHelper.h"
3 3 #include "SqpApplication.h"
4 4 #include "Visualization/VisualizationDragWidget.h"
5 5
6 6 #include "Common/VisualizationDef.h"
7 7
8 8 #include <QDrag>
9 9 #include <QDragEnterEvent>
10 10 #include <QVBoxLayout>
11 11
12 12 #include <cmath>
13 13 #include <memory>
14 14
15 15 Q_LOGGING_CATEGORY(LOG_VisualizationDragDropContainer, "VisualizationDragDropContainer")
16 16
17 auto DRAGGED_MINIATURE_WIDTH = 200; // in pixels
18
17 19 struct VisualizationDragDropContainer::VisualizationDragDropContainerPrivate {
18 20
19 21 QVBoxLayout *m_Layout;
20 22 QHash<QString, VisualizationDragDropContainer::DropBehavior> m_AcceptedMimeTypes;
21 23 QString m_PlaceHolderText;
22 24 DragDropHelper::PlaceHolderType m_PlaceHolderType = DragDropHelper::PlaceHolderType::Graph;
23 25
24 26 VisualizationDragDropContainer::AcceptMimeDataFunction m_AcceptMimeDataFun
25 27 = [](auto mimeData) { return true; };
26 28
27 29 int m_MinContainerHeight = 0;
28 30
29 31 explicit VisualizationDragDropContainerPrivate(QWidget *widget)
30 32 {
31 33 m_Layout = new QVBoxLayout(widget);
32 34 m_Layout->setContentsMargins(0, 0, 0, 0);
33 35 }
34 36
35 37 bool acceptMimeData(const QMimeData *data) const
36 38 {
37 39 for (const auto &type : m_AcceptedMimeTypes.keys()) {
38 40 if (data->hasFormat(type) && m_AcceptMimeDataFun(data)) {
39 41 return true;
40 42 }
41 43 }
42 44
43 45 return false;
44 46 }
45 47
46 48 bool allowMergeForMimeData(const QMimeData *data) const
47 49 {
48 50 bool result = false;
49 51 for (auto it = m_AcceptedMimeTypes.constBegin(); it != m_AcceptedMimeTypes.constEnd();
50 52 ++it) {
51 53
52 54 if (data->hasFormat(it.key())
53 55 && (it.value() == VisualizationDragDropContainer::DropBehavior::Merged
54 56 || it.value()
55 57 == VisualizationDragDropContainer::DropBehavior::InsertedAndMerged)) {
56 58 result = true;
57 59 }
58 60 else if (data->hasFormat(it.key())
59 61 && it.value() == VisualizationDragDropContainer::DropBehavior::Inserted) {
60 62 // Merge is forbidden if the mime data contain an acceptable type which cannot be
61 63 // merged
62 64 result = false;
63 65 break;
64 66 }
65 67 }
66 68
67 69 return result;
68 70 }
69 71
70 72 bool allowInsertForMimeData(const QMimeData *data) const
71 73 {
72 74 for (auto it = m_AcceptedMimeTypes.constBegin(); it != m_AcceptedMimeTypes.constEnd();
73 75 ++it) {
74 76 if (data->hasFormat(it.key())
75 77 && (it.value() == VisualizationDragDropContainer::DropBehavior::Inserted
76 78 || it.value()
77 79 == VisualizationDragDropContainer::DropBehavior::InsertedAndMerged)) {
78 80 return true;
79 81 }
80 82 }
81 83
82 84 return false;
83 85 }
84 86
85 87 bool hasPlaceHolder() const
86 88 {
87 89 return sqpApp->dragDropHelper().placeHolder().parentWidget() == m_Layout->parentWidget();
88 90 }
89 91
90 92 VisualizationDragWidget *getChildDragWidgetAt(const QWidget *parent, const QPoint &pos) const
91 93 {
92 94 VisualizationDragWidget *dragWidget = nullptr;
93 95
94 96 for (auto child : parent->children()) {
95 97 auto widget = qobject_cast<VisualizationDragWidget *>(child);
96 98 if (widget && widget->isVisible()) {
97 99 if (widget->frameGeometry().contains(pos)) {
98 100 dragWidget = widget;
99 101 break;
100 102 }
101 103 }
102 104 }
103 105
104 106 return dragWidget;
105 107 }
106 108
107 109 bool cursorIsInContainer(QWidget *container) const
108 110 {
109 111 return container->isAncestorOf(sqpApp->widgetAt(QCursor::pos()));
110 112 }
111 113
112 114 int countDragWidget(const QWidget *parent, bool onlyVisible = false) const
113 115 {
114 116 auto nbGraph = 0;
115 117 for (auto child : parent->children()) {
116 118 if (qobject_cast<VisualizationDragWidget *>(child)) {
117 119 if (!onlyVisible || qobject_cast<VisualizationDragWidget *>(child)->isVisible()) {
118 120 nbGraph += 1;
119 121 }
120 122 }
121 123 }
122 124
123 125 return nbGraph;
124 126 }
125 127
126 128 void findPlaceHolderPosition(const QPoint &pos, bool canInsert, bool canMerge,
127 129 const VisualizationDragDropContainer *container);
128 130 };
129 131
130 132 VisualizationDragDropContainer::VisualizationDragDropContainer(QWidget *parent)
131 133 : QFrame{parent},
132 134 impl{spimpl::make_unique_impl<VisualizationDragDropContainerPrivate>(this)}
133 135 {
134 136 setAcceptDrops(true);
135 137 }
136 138
137 139 void VisualizationDragDropContainer::addDragWidget(VisualizationDragWidget *dragWidget)
138 140 {
139 141 impl->m_Layout->addWidget(dragWidget);
140 142 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
141 143 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
142 144 &VisualizationDragDropContainer::startDrag);
143 145 }
144 146
145 147 void VisualizationDragDropContainer::insertDragWidget(int index,
146 148 VisualizationDragWidget *dragWidget)
147 149 {
148 150 impl->m_Layout->insertWidget(index, dragWidget);
149 151 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
150 152 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
151 153 &VisualizationDragDropContainer::startDrag);
152 154 }
153 155
154 156 void VisualizationDragDropContainer::addAcceptedMimeType(
155 157 const QString &mimeType, VisualizationDragDropContainer::DropBehavior behavior)
156 158 {
157 159 impl->m_AcceptedMimeTypes[mimeType] = behavior;
158 160 }
159 161
160 162 int VisualizationDragDropContainer::countDragWidget() const
161 163 {
162 164 return impl->countDragWidget(this);
163 165 }
164 166
165 167 void VisualizationDragDropContainer::setAcceptMimeDataFunction(
166 168 VisualizationDragDropContainer::AcceptMimeDataFunction fun)
167 169 {
168 170 impl->m_AcceptMimeDataFun = fun;
169 171 }
170 172
171 173 void VisualizationDragDropContainer::setPlaceHolderType(DragDropHelper::PlaceHolderType type,
172 174 const QString &placeHolderText)
173 175 {
174 176 impl->m_PlaceHolderType = type;
175 177 impl->m_PlaceHolderText = placeHolderText;
176 178 }
177 179
178 180 void VisualizationDragDropContainer::startDrag(VisualizationDragWidget *dragWidget,
179 181 const QPoint &dragPosition)
180 182 {
181 183 auto &helper = sqpApp->dragDropHelper();
182 184 helper.resetDragAndDrop();
183 185
184 186 // Note: The management of the drag object is done by Qt
185 187 auto drag = new QDrag{dragWidget};
186 drag->setHotSpot(dragPosition);
187 188
188 189 auto mimeData = dragWidget->mimeData();
189 190 drag->setMimeData(mimeData);
190 191
191 192 auto pixmap = QPixmap(dragWidget->size());
192 193 dragWidget->render(&pixmap);
193 drag->setPixmap(pixmap);
194 drag->setPixmap(pixmap.scaled(DRAGGED_MINIATURE_WIDTH, DRAGGED_MINIATURE_WIDTH,
195 Qt::KeepAspectRatio, Qt::SmoothTransformation));
194 196
195 197 auto image = pixmap.toImage();
196 198 mimeData->setImageData(image);
197 199 mimeData->setUrls({helper.imageTemporaryUrl(image)});
198 200
199 201 if (impl->m_Layout->indexOf(dragWidget) >= 0) {
200 202 helper.setCurrentDragWidget(dragWidget);
201 203
202 204 if (impl->cursorIsInContainer(this)) {
203 205 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
204 206 helper.insertPlaceHolder(impl->m_Layout, dragWidgetIndex, impl->m_PlaceHolderType,
205 207 impl->m_PlaceHolderText);
206 208 dragWidget->setVisible(false);
207 209 }
208 210 else {
209 211 // The drag starts directly outside the drop zone
210 212 // do not add the placeHolder
211 213 }
212 214
213 215 drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::MoveAction);
214 216
215 217 helper.doCloseWidgets();
216 218 }
217 219 else {
218 220 qCWarning(LOG_VisualizationDragDropContainer())
219 221 << tr("VisualizationDragDropContainer::startDrag, drag aborted, the specified "
220 222 "VisualizationDragWidget is not found in this container.");
221 223 }
222 224 }
223 225
224 226 void VisualizationDragDropContainer::dragEnterEvent(QDragEnterEvent *event)
225 227 {
226 228 if (impl->acceptMimeData(event->mimeData())) {
227 229 event->acceptProposedAction();
228 230
229 231 auto &helper = sqpApp->dragDropHelper();
230 232
231 233 if (!impl->hasPlaceHolder()) {
232 234 auto dragWidget = helper.getCurrentDragWidget();
233 235
234 236 if (dragWidget) {
235 237 // If the drag&drop is internal to the visualization, entering the container hide
236 238 // the dragWidget which was made visible by the dragLeaveEvent
237 239 auto parentWidget
238 240 = qobject_cast<VisualizationDragDropContainer *>(dragWidget->parentWidget());
239 241 if (parentWidget) {
240 242 dragWidget->setVisible(false);
241 243 }
242 244 }
243 245
244 246 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
245 247 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
246 248 impl->findPlaceHolderPosition(event->pos(), canInsert, canMerge, this);
247 249 }
248 250 else {
249 251 // do nothing
250 252 }
251 253 }
252 254 else {
253 255 event->ignore();
254 256 }
255 257
256 258 QWidget::dragEnterEvent(event);
257 259 }
258 260
259 261 void VisualizationDragDropContainer::dragLeaveEvent(QDragLeaveEvent *event)
260 262 {
261 263 Q_UNUSED(event);
262 264
263 265 auto &helper = sqpApp->dragDropHelper();
264 266
265 267 if (!impl->cursorIsInContainer(this)) {
266 268 helper.removePlaceHolder();
267 269 helper.setHightlightedDragWidget(nullptr);
268 270 impl->m_MinContainerHeight = 0;
269 271
270 272 auto dragWidget = helper.getCurrentDragWidget();
271 273 if (dragWidget) {
272 274 // dragWidget has a value only if the drag is started from the visualization
273 275 // In that case, shows the drag widget at its original place
274 276 // So the drag widget doesn't stay hidden if the drop occurs outside the visualization
275 277 // drop zone (It is not possible to catch a drop event outside of the application)
276 278
277 279 if (dragWidget) {
278 280 dragWidget->setVisible(true);
279 281 }
280 282 }
281 283 }
282 284 else {
283 285 // Leave event probably received for a child widget.
284 286 // Do nothing.
285 287 // Note: The DragLeave event, doesn't have any mean to determine who sent it.
286 288 }
287 289
288 290 QWidget::dragLeaveEvent(event);
289 291 }
290 292
291 293 void VisualizationDragDropContainer::dragMoveEvent(QDragMoveEvent *event)
292 294 {
293 295 if (impl->acceptMimeData(event->mimeData())) {
294 296 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
295 297 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
296 298 impl->findPlaceHolderPosition(event->pos(), canInsert, canMerge, this);
297 299 }
298 300 else {
299 301 event->ignore();
300 302 }
301 303
302 304 QWidget::dragMoveEvent(event);
303 305 }
304 306
305 307 void VisualizationDragDropContainer::dropEvent(QDropEvent *event)
306 308 {
307 309 auto &helper = sqpApp->dragDropHelper();
308 310
309 311 if (impl->acceptMimeData(event->mimeData())) {
310 312 auto dragWidget = helper.getCurrentDragWidget();
311 313 if (impl->hasPlaceHolder()) {
312 314 // drop where the placeHolder is located
313 315
314 316 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
315 317 if (canInsert) {
316 318 auto droppedIndex = impl->m_Layout->indexOf(&helper.placeHolder());
317 319
318 320 if (dragWidget) {
319 321 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
320 322 if (dragWidgetIndex >= 0 && dragWidgetIndex < droppedIndex) {
321 323 // Correction of the index if the drop occurs in the same container
322 324 // and if the drag is started from the visualization (in that case, the
323 325 // dragWidget is hidden)
324 326 droppedIndex -= 1;
325 327 }
326 328
327 329 dragWidget->setVisible(true);
328 330 }
329 331
330 332 event->acceptProposedAction();
331 333
332 334 helper.removePlaceHolder();
333 335
334 336 emit dropOccuredInContainer(droppedIndex, event->mimeData());
335 337 }
336 338 else {
337 339 qCWarning(LOG_VisualizationDragDropContainer()) << tr(
338 340 "VisualizationDragDropContainer::dropEvent, dropping on the placeHolder, but "
339 341 "the insertion is forbidden.");
340 342 Q_ASSERT(false);
341 343 }
342 344 }
343 345 else if (helper.getHightlightedDragWidget()) {
344 346 // drop on the highlighted widget
345 347
346 348 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
347 349 if (canMerge) {
348 350 event->acceptProposedAction();
349 351 emit dropOccuredOnWidget(helper.getHightlightedDragWidget(), event->mimeData());
350 352 }
351 353 else {
352 354 qCWarning(LOG_VisualizationDragDropContainer())
353 355 << tr("VisualizationDragDropContainer::dropEvent, dropping on a widget, but "
354 356 "the merge is forbidden.");
355 357 Q_ASSERT(false);
356 358 }
357 359 }
358 360 }
359 361 else {
360 362 event->ignore();
361 363 }
362 364
363 365 sqpApp->dragDropHelper().setHightlightedDragWidget(nullptr);
364 366 impl->m_MinContainerHeight = 0;
365 367
366 368 QWidget::dropEvent(event);
367 369 }
368 370
369 371
370 372 void VisualizationDragDropContainer::VisualizationDragDropContainerPrivate::findPlaceHolderPosition(
371 373 const QPoint &pos, bool canInsert, bool canMerge,
372 374 const VisualizationDragDropContainer *container)
373 375 {
374 376 auto &helper = sqpApp->dragDropHelper();
375 377
376 378 auto absPos = container->mapToGlobal(pos);
377 379 auto isOnPlaceHolder = sqpApp->widgetAt(absPos) == &(helper.placeHolder());
378 380
379 381 if (countDragWidget(container, true) == 0) {
380 382 // Drop on an empty container, just add the placeHolder at the top
381 383 helper.insertPlaceHolder(m_Layout, 0, m_PlaceHolderType, m_PlaceHolderText);
382 384 }
383 385 else if (!isOnPlaceHolder) {
384 386 auto nbDragWidget = countDragWidget(container);
385 387 if (nbDragWidget > 0) {
386 388
387 389 if (m_MinContainerHeight == 0) {
388 390 m_MinContainerHeight = container->size().height();
389 391 }
390 392
391 393 m_MinContainerHeight = qMin(m_MinContainerHeight, container->size().height());
392 394 auto graphHeight = qMax(m_MinContainerHeight / nbDragWidget, GRAPH_MINIMUM_HEIGHT);
393 395
394 396 auto posY = pos.y();
395 397 auto dropIndex = floor(posY / graphHeight);
396 398 auto zoneSize = qMin(graphHeight / 4.0, 75.0);
397 399
398 400
399 401 auto isOnTop = posY < dropIndex * graphHeight + zoneSize;
400 402 auto isOnBottom = posY > (dropIndex + 1) * graphHeight - zoneSize;
401 403
402 404 auto placeHolderIndex = m_Layout->indexOf(&(helper.placeHolder()));
403 405
404 406 auto dragWidgetHovered = getChildDragWidgetAt(container, pos);
405 407
406 408 if (canInsert && (isOnTop || isOnBottom || !canMerge)) {
407 409 if (isOnBottom) {
408 410 dropIndex += 1;
409 411 }
410 412
411 413 if (helper.getCurrentDragWidget()) {
412 414 auto dragWidgetIndex = m_Layout->indexOf(helper.getCurrentDragWidget());
413 415 if (dragWidgetIndex >= 0 && dragWidgetIndex <= dropIndex) {
414 416 // Correction of the index if the drop occurs in the same container
415 417 // and if the drag is started from the visualization (in that case, the
416 418 // dragWidget is hidden)
417 419 dropIndex += 1;
418 420 }
419 421 }
420 422
421 423 if (dropIndex != placeHolderIndex) {
422 424 helper.insertPlaceHolder(m_Layout, dropIndex, m_PlaceHolderType,
423 425 m_PlaceHolderText);
424 426 }
425 427
426 428 helper.setHightlightedDragWidget(nullptr);
427 429 }
428 430 else if (canMerge && dragWidgetHovered) {
429 431 // drop on the middle -> merge
430 432 if (hasPlaceHolder()) {
431 433 helper.removePlaceHolder();
432 434 }
433 435
434 436 helper.setHightlightedDragWidget(dragWidgetHovered);
435 437 }
436 438 else {
437 439 qCWarning(LOG_VisualizationDragDropContainer())
438 440 << tr("VisualizationDragDropContainer::findPlaceHolderPosition, no valid drop "
439 441 "action.");
440 442 }
441 443 }
442 444 else {
443 445 qCWarning(LOG_VisualizationDragDropContainer())
444 446 << tr("VisualizationDragDropContainer::findPlaceHolderPosition, no widget "
445 447 "found in the "
446 448 "container");
447 449 }
448 450 }
449 451 else {
450 452 // the mouse is hover the placeHolder
451 453 // Do nothing
452 454 }
453 455 }
General Comments 1
Under Review
author

Auto status change to "Under Review"

You need to be logged in to leave comments. Login now