##// END OF EJS Templates
Manage the scroll of the tab QScrollArea during a drag&drop operation
trabillard -
r843:8d32ce09be20
parent child
Show More
@@ -1,42 +1,67
1 1 #ifndef DRAGDROPHELPER_H
2 2 #define DRAGDROPHELPER_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <QWidget>
6 6
7 7 class QVBoxLayout;
8 8 class QScrollArea;
9 9 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
16 41 {
17 42 public:
18 43 DragDropHelper();
19 44 ~DragDropHelper();
20 45
21 46 static const QString MIME_TYPE_GRAPH;
22 47 static const QString MIME_TYPE_ZONE;
23 48
24 49 void setCurrentDragWidget(VisualizationDragWidget* dragWidget);
25 50 VisualizationDragWidget* getCurrentDragWidget() const;
26 51
27 52 QWidget &placeHolder() const;
28 53 void insertPlaceHolder(QVBoxLayout* layout, int index);
29 54 void removePlaceHolder();
30 55 bool isPlaceHolderSet() const;
31 56
32 57 void addDragDropScrollArea(QScrollArea* scrollArea);
33 58 void removeDragDropScrollArea(QScrollArea* scrollArea);
34 59
35 60 QUrl imageTemporaryUrl(const QImage& image) const;
36 61
37 62 private:
38 63 class DragDropHelperPrivate;
39 64 spimpl::unique_impl_ptr<DragDropHelperPrivate> impl;
40 65 };
41 66
42 67 #endif // DRAGDROPHELPER_H
@@ -1,105 +1,248
1 1 #include "DragDropHelper.h"
2 2 #include "Visualization/VisualizationDragWidget.h"
3 3 #include "SqpApplication.h"
4 4
5 5 #include <QDragMoveEvent>
6 6 #include <QDragEnterEvent>
7 7 #include <QScrollBar>
8 8 #include <QScrollArea>
9 9 #include <QVBoxLayout>
10 10 #include <QTimer>
11 11 #include <QDir>
12 12
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);
note

I think it's better to test if the cast success. For that you need a dynamic_cast

note

In case of a DragMove, Qt ensure the object will always be a QWidget.

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
19 152 VisualizationDragWidget* m_currentDragWidget = nullptr;
20 153 std::unique_ptr<QWidget> m_placeHolder = nullptr;
21 154 std::unique_ptr<DragDropScroller> m_dragDropScroller = nullptr;
22 155 QString m_imageTempUrl; //Temporary file for image url generated by the drag & drop. Not using QTemporaryFile to have a name which is not generated.
23 156
24 157 explicit DragDropHelperPrivate()
25 158 : m_placeHolder{std::make_unique<QWidget>()},
26 159 m_dragDropScroller{std::make_unique<DragDropScroller>()}
27 160 {
28 161 m_placeHolder->setStyleSheet("background-color: #BBD5EE; border:2px solid #2A7FD4");
29 162 sqpApp->installEventFilter(m_dragDropScroller.get());
30 163
31 164
32 165 m_imageTempUrl = QDir::temp().absoluteFilePath("Scqlop_graph.png");
33 166 }
34 167
35 168 void preparePlaceHolder() const
36 169 {
37 170 if (m_currentDragWidget)
38 171 {
39 172 m_placeHolder->setMinimumSize(m_currentDragWidget->size());
40 173 m_placeHolder->setSizePolicy(m_currentDragWidget->sizePolicy());
41 174 }
42 175 else
43 176 {
44 177 m_placeHolder->setMinimumSize(200, 200);
45 178 }
46 179 }
47 180 };
48 181
49 182
50 183 DragDropHelper::DragDropHelper() :
51 184 impl{spimpl::make_unique_impl<DragDropHelperPrivate>()}
52 185 {
53 186 }
54 187
55 188 DragDropHelper::~DragDropHelper()
56 189 {
57 190 QFile::remove(impl->m_imageTempUrl);
58 191 }
59 192
60 193 void DragDropHelper::setCurrentDragWidget(VisualizationDragWidget *dragWidget)
61 194 {
62 195 impl->m_currentDragWidget = dragWidget;
63 196 }
64 197
65 198 VisualizationDragWidget *DragDropHelper::getCurrentDragWidget() const
66 199 {
67 200 return impl->m_currentDragWidget;
68 201 }
69 202
70 203
71 204 QWidget& DragDropHelper::placeHolder() const
72 205 {
73 206 return *impl->m_placeHolder;
74 207 }
75 208
76 209 void DragDropHelper::insertPlaceHolder(QVBoxLayout *layout, int index)
77 210 {
78 211 removePlaceHolder();
79 212 impl->preparePlaceHolder();
80 213 layout->insertWidget(index, impl->m_placeHolder.get());
81 214 impl->m_placeHolder->show();
82 215 }
83 216
84 217 void DragDropHelper::removePlaceHolder()
85 218 {
86 219 auto parentWidget = impl->m_placeHolder->parentWidget();
87 220 if (parentWidget)
88 221 {
89 222 parentWidget->layout()->removeWidget(impl->m_placeHolder.get());
90 223 impl->m_placeHolder->setParent(nullptr);
91 224 impl->m_placeHolder->hide();
92 225 }
93 226 }
94 227
95 228 bool DragDropHelper::isPlaceHolderSet() const
96 229 {
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);
103 246 return QUrl::fromLocalFile(impl->m_imageTempUrl);
104 247 }
105 248
@@ -1,181 +1,183
1 1 #include "Visualization/VisualizationTabWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "ui_VisualizationTabWidget.h"
4 4
5 5 #include "Visualization/VisualizationZoneWidget.h"
6 6 #include "Visualization/VisualizationGraphWidget.h"
7 7
8 8 #include "SqpApplication.h"
9 9 #include "DragDropHelper.h"
10 10
11 11 Q_LOGGING_CATEGORY(LOG_VisualizationTabWidget, "VisualizationTabWidget")
12 12
13 13 namespace {
14 14
15 15 /// Generates a default name for a new zone, according to the number of zones already displayed in
16 16 /// the tab
17 17 QString defaultZoneName(const QLayout &layout)
18 18 {
19 19 auto count = 0;
20 20 for (auto i = 0; i < layout.count(); ++i) {
21 21 if (dynamic_cast<VisualizationZoneWidget *>(layout.itemAt(i)->widget())) {
22 22 count++;
23 23 }
24 24 }
25 25
26 26 return QObject::tr("Zone %1").arg(count + 1);
27 27 }
28 28
29 29 /**
30 30 * Applies a function to all zones of the tab represented by its layout
31 31 * @param layout the layout that contains zones
32 32 * @param fun the function to apply to each zone
33 33 */
34 34 template <typename Fun>
35 35 void processZones(QLayout &layout, Fun fun)
36 36 {
37 37 for (auto i = 0; i < layout.count(); ++i) {
38 38 if (auto item = layout.itemAt(i)) {
39 39 if (auto visualizationZoneWidget
40 40 = dynamic_cast<VisualizationZoneWidget *>(item->widget())) {
41 41 fun(*visualizationZoneWidget);
42 42 }
43 43 }
44 44 }
45 45 }
46 46
47 47 } // namespace
48 48
49 49 struct VisualizationTabWidget::VisualizationTabWidgetPrivate {
50 50 explicit VisualizationTabWidgetPrivate(const QString &name) : m_Name{name} {}
51 51
52 52 QString m_Name;
53 53 };
54 54
55 55 VisualizationTabWidget::VisualizationTabWidget(const QString &name, QWidget *parent)
56 56 : QWidget{parent},
57 57 ui{new Ui::VisualizationTabWidget},
58 58 impl{spimpl::make_unique_impl<VisualizationTabWidgetPrivate>(name)}
59 59 {
60 60 ui->setupUi(this);
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);
67 68 }
68 69
69 70 VisualizationTabWidget::~VisualizationTabWidget()
70 71 {
72 sqpApp->dragDropHelper().removeDragDropScrollArea(ui->scrollArea);
71 73 delete ui;
72 74 }
73 75
74 76 void VisualizationTabWidget::addZone(VisualizationZoneWidget *zoneWidget)
75 77 {
76 78 ui->dragDropContainer->addDragWidget(zoneWidget);
77 79 }
78 80
79 81 void VisualizationTabWidget::insertZone(int index, VisualizationZoneWidget *zoneWidget)
80 82 {
81 83 ui->dragDropContainer->insertDragWidget(index, zoneWidget);
82 84 }
83 85
84 86 VisualizationZoneWidget *VisualizationTabWidget::createZone(std::shared_ptr<Variable> variable)
85 87 {
86 88 return createZone({variable}, -1);
87 89 }
88 90
89 91 VisualizationZoneWidget *VisualizationTabWidget::createZone(const QList<std::shared_ptr<Variable> > &variables, int index)
90 92 {
91 93 auto zoneWidget = new VisualizationZoneWidget{defaultZoneName(*ui->dragDropContainer->layout()), this};
92 94 this->insertZone(index, zoneWidget);
93 95
94 96 // Creates a new graph into the zone
95 97 zoneWidget->createGraph(variables, index);
96 98
97 99 return zoneWidget;
98 100 }
99 101
100 102 void VisualizationTabWidget::accept(IVisualizationWidgetVisitor *visitor)
101 103 {
102 104 if (visitor) {
103 105 visitor->visitEnter(this);
104 106
105 107 // Apply visitor to zone children: widgets different from zones are not visited (no action)
106 108 processZones(tabLayout(), [visitor](VisualizationZoneWidget &zoneWidget) {
107 109 zoneWidget.accept(visitor);
108 110 });
109 111
110 112 visitor->visitLeave(this);
111 113 }
112 114 else {
113 115 qCCritical(LOG_VisualizationTabWidget()) << tr("Can't visit widget : the visitor is null");
114 116 }
115 117 }
116 118
117 119 bool VisualizationTabWidget::canDrop(const Variable &variable) const
118 120 {
119 121 // A tab can always accomodate a variable
120 122 Q_UNUSED(variable);
121 123 return true;
122 124 }
123 125
124 126 bool VisualizationTabWidget::contains(const Variable &variable) const
125 127 {
126 128 Q_UNUSED(variable);
127 129 return false;
128 130 }
129 131
130 132 QString VisualizationTabWidget::name() const
131 133 {
132 134 return impl->m_Name;
133 135 }
134 136
135 137 void VisualizationTabWidget::closeEvent(QCloseEvent *event)
136 138 {
137 139 // Closes zones in the tab
138 140 processZones(tabLayout(), [](VisualizationZoneWidget &zoneWidget) { zoneWidget.close(); });
139 141
140 142 QWidget::closeEvent(event);
141 143 }
142 144
143 145 QLayout &VisualizationTabWidget::tabLayout() const noexcept
144 146 {
145 147 return *ui->dragDropContainer->layout();
146 148 }
147 149
148 150 void VisualizationTabWidget::dropMimeData(int index, const QMimeData *mimeData)
149 151 {
150 152 auto& helper = sqpApp->dragDropHelper();
151 153 if (mimeData->hasFormat(DragDropHelper::MIME_TYPE_GRAPH))
152 154 {
153 155 auto graphWidget = static_cast<VisualizationGraphWidget*>(helper.getCurrentDragWidget());
154 156 auto parentDragDropContainer = qobject_cast<VisualizationDragDropContainer*>(graphWidget->parentWidget());
155 157 Q_ASSERT(parentDragDropContainer);
156 158
157 159 auto nbGraph = parentDragDropContainer->countDragWidget();
158 160 if (nbGraph == 1)
159 161 {
160 162 //This is the only graph in the previous zone, close the zone
161 163 graphWidget->parentZoneWidget()->close();
162 164 }
163 165 else
164 166 {
165 167 //Close the graph
166 168 graphWidget->close();
167 169 }
168 170
169 171 const auto& variables = graphWidget->variables();
170 172 createZone(variables, index);
171 173 }
172 174 else if (mimeData->hasFormat(DragDropHelper::MIME_TYPE_ZONE))
173 175 {
174 176 //Simple move of the zone, no variable operation associated
175 177 auto zoneWidget = static_cast<VisualizationZoneWidget*>(helper.getCurrentDragWidget());
176 178 auto parentDragDropContainer = zoneWidget->parentWidget();
177 179 parentDragDropContainer->layout()->removeWidget(zoneWidget);
178 180
179 181 ui->dragDropContainer->insertDragWidget(index, zoneWidget);
180 182 }
181 183 }
General Comments 6
Approved

Status change > Approved

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