1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the examples of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:BSD$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** BSD License Usage |
18 | ** Alternatively, you may use this file under the terms of the BSD license |
19 | ** as follows: |
20 | ** |
21 | ** "Redistribution and use in source and binary forms, with or without |
22 | ** modification, are permitted provided that the following conditions are |
23 | ** met: |
24 | ** * Redistributions of source code must retain the above copyright |
25 | ** notice, this list of conditions and the following disclaimer. |
26 | ** * Redistributions in binary form must reproduce the above copyright |
27 | ** notice, this list of conditions and the following disclaimer in |
28 | ** the documentation and/or other materials provided with the |
29 | ** distribution. |
30 | ** * Neither the name of The Qt Company Ltd nor the names of its |
31 | ** contributors may be used to endorse or promote products derived |
32 | ** from this software without specific prior written permission. |
33 | ** |
34 | ** |
35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
46 | ** |
47 | ** $QT_END_LICENSE$ |
48 | ** |
49 | ****************************************************************************/ |
50 | |
51 | #include <QtCore> |
52 | #include <QtWidgets> |
53 | |
54 | #define NUM_ITEMS 100 |
55 | #define NUM_LISTS 10 |
56 | |
57 | /*! |
58 | \class RectObject |
59 | Note that it needs to be a QGraphicsObject or else the gestures will not work correctly. |
60 | */ |
61 | class RectObject : public QGraphicsObject |
62 | { |
63 | Q_OBJECT |
64 | |
65 | public: |
66 | |
67 | RectObject(const QString &text, qreal x, qreal y, qreal width, qreal height, QBrush brush, QGraphicsItem *parent = nullptr) |
68 | : QGraphicsObject(parent) |
69 | , m_text(text) |
70 | , m_rect(x, y, width, height) |
71 | , m_pen(brush.color().lighter(), 3.0) |
72 | , m_brush(brush) |
73 | { |
74 | setFlag(flag: QGraphicsItem::ItemClipsToShape, enabled: true); |
75 | } |
76 | |
77 | QRectF boundingRect() const override |
78 | { |
79 | // here we only want the size of the children and not the size of the children of the children... |
80 | qreal halfpw = m_pen.widthF() / 2; |
81 | QRectF rect = m_rect; |
82 | if (halfpw > 0.0) |
83 | rect.adjust(xp1: -halfpw, yp1: -halfpw, xp2: halfpw, yp2: halfpw); |
84 | |
85 | return rect; |
86 | } |
87 | |
88 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override |
89 | { |
90 | Q_UNUSED(option); |
91 | Q_UNUSED(widget); |
92 | painter->setPen(m_pen); |
93 | painter->setBrush(m_brush); |
94 | painter->drawRect(rect: m_rect); |
95 | |
96 | painter->setPen(Qt::black); |
97 | QFont f; |
98 | f.setPixelSize(m_rect.height()); |
99 | painter->setFont(f); |
100 | painter->drawText(r: m_rect, flags: Qt::AlignCenter, text: m_text); |
101 | } |
102 | |
103 | QString m_text; |
104 | QRectF m_rect; |
105 | QPen m_pen; |
106 | QBrush m_brush; |
107 | }; |
108 | |
109 | class ViewObject : public QGraphicsObject |
110 | { |
111 | Q_OBJECT |
112 | public: |
113 | ViewObject(QGraphicsObject *parent) |
114 | : QGraphicsObject(parent) |
115 | { } |
116 | |
117 | QRectF boundingRect() const override |
118 | { |
119 | QRectF rect; |
120 | const auto items = childItems(); |
121 | for (const QGraphicsItem *item : items) |
122 | rect |= item->boundingRect().translated(p: item->pos()); |
123 | return rect; |
124 | } |
125 | |
126 | void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override |
127 | { } |
128 | }; |
129 | |
130 | class ListObject : public QGraphicsObject |
131 | { |
132 | Q_OBJECT |
133 | |
134 | public: |
135 | ListObject(const QSizeF &size, bool useTouch) |
136 | { |
137 | m_size = size; |
138 | setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape, enabled: true); |
139 | // grab gesture via Touch or Mouse events |
140 | QScroller::grabGesture(target: this, gestureType: useTouch ? QScroller::TouchGesture : QScroller::LeftMouseButtonGesture); |
141 | |
142 | // this needs to be QGraphicsOBJECT - otherwise gesture recognition |
143 | // will not work for the parent of the viewport (in this case the |
144 | // list) |
145 | m_viewport = new ViewObject(this); |
146 | |
147 | } |
148 | |
149 | QGraphicsObject *viewport() const |
150 | { |
151 | return m_viewport; |
152 | } |
153 | |
154 | bool event(QEvent *e) override |
155 | { |
156 | switch (e->type()) { |
157 | // ![2] |
158 | case QEvent::ScrollPrepare: { |
159 | QScrollPrepareEvent *se = static_cast<QScrollPrepareEvent *>(e); |
160 | se->setViewportSize(m_size); |
161 | QRectF br = m_viewport->boundingRect(); |
162 | se->setContentPosRange(QRectF(0, 0, |
163 | qMax(a: qreal(0), b: br.width() - m_size.width()), |
164 | qMax(a: qreal(0), b: br.height() - m_size.height()))); |
165 | se->setContentPos(-m_viewport->pos()); |
166 | se->accept(); |
167 | return true; |
168 | } |
169 | // ![1] |
170 | // ![2] |
171 | case QEvent::Scroll: { |
172 | QScrollEvent *se = static_cast<QScrollEvent *>(e); |
173 | m_viewport->setPos(-se->contentPos() - se->overshootDistance()); |
174 | return true; |
175 | } |
176 | // ![2] |
177 | default: |
178 | break; |
179 | } |
180 | return QGraphicsObject::event(ev: e); |
181 | } |
182 | |
183 | bool sceneEvent(QEvent *e) override |
184 | { |
185 | switch (e->type()) { |
186 | case QEvent::TouchBegin: { |
187 | // We need to return true for the TouchBegin here in the |
188 | // top-most graphics object - otherwise gestures in our parent |
189 | // objects will NOT work at all (the accept() flag is already |
190 | // set due to our setAcceptTouchEvents(true) call in the c'tor |
191 | return true; |
192 | |
193 | } |
194 | case QEvent::GraphicsSceneMousePress: { |
195 | // We need to return true for the MousePress here in the |
196 | // top-most graphics object - otherwise gestures in our parent |
197 | // objects will NOT work at all (the accept() flag is already |
198 | // set to true by Qt) |
199 | return true; |
200 | |
201 | } |
202 | default: |
203 | break; |
204 | } |
205 | return QGraphicsObject::sceneEvent(event: e); |
206 | } |
207 | |
208 | QRectF boundingRect() const override |
209 | { |
210 | return QRectF(0, 0, m_size.width() + 3, m_size.height()); |
211 | } |
212 | |
213 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override |
214 | { |
215 | Q_UNUSED(option); |
216 | Q_UNUSED(widget); |
217 | painter->setPen(QPen(QColor(100, 100, 100), 3.0)); |
218 | painter->drawRect(rect: QRectF(1.5, 1.5, m_size.width() - 3, m_size.height() - 3)); |
219 | } |
220 | |
221 | QSizeF m_size; |
222 | ViewObject *m_viewport; |
223 | }; |
224 | |
225 | class MainWindow : public QMainWindow |
226 | { |
227 | Q_OBJECT |
228 | |
229 | public: |
230 | MainWindow(bool useTouch) |
231 | { |
232 | m_scene = new QGraphicsScene(); |
233 | |
234 | // -- make the main list |
235 | ListObject *mainList = new ListObject(QSizeF(780, 400), useTouch); |
236 | mainList->setObjectName(QLatin1String("MainList" )); |
237 | m_scene->addItem(item: mainList); |
238 | // ![3] |
239 | for (int i=0; i<NUM_LISTS; i++) { |
240 | ListObject *childList = new ListObject(QSizeF(mainList->m_size.width()/3, mainList->m_size.height()), useTouch); |
241 | childList->setObjectName(QString("ChildList %1" ).arg(a: i)); |
242 | fillList(list: childList); |
243 | childList->setParentItem(mainList->viewport()); |
244 | childList->setPos(ax: i*mainList->m_size.width()/3, ay: 0); |
245 | } |
246 | mainList->viewport()->setPos(ax: 0, ay: 0); |
247 | |
248 | |
249 | /* |
250 | list1->setTransformOriginPoint(200, 200); |
251 | list1->setRotation(135); |
252 | list1->setPos(20 + 200 * .41, 20 + 200 * .41); |
253 | */ |
254 | // ![3] |
255 | |
256 | m_view = new QGraphicsView(m_scene); |
257 | setCentralWidget(m_view); |
258 | setWindowTitle(tr(s: "Gesture example" )); |
259 | m_scene->setSceneRect(x: 0, y: 0, w: m_view->viewport()->width(), h: m_view->viewport()->height()); |
260 | } |
261 | |
262 | /** |
263 | * Fills the list object \a list with RectObjects. |
264 | */ |
265 | void fillList(ListObject *list) |
266 | { |
267 | qreal h = list->m_size.height() / 10; |
268 | for (int i=0; i<NUM_ITEMS; i++) { |
269 | QColor color = QColor(255*i/NUM_ITEMS, 255*(NUM_ITEMS-i)/NUM_ITEMS, 127*(i%2)+64*(i/2%2)); |
270 | QString text = QLatin1String("Item #" ) + QString::number(i); |
271 | QGraphicsItem *rect = new RectObject(text, 0, 0, list->m_size.width() - 6, h - 3, QBrush(color), list->viewport()); |
272 | rect->setPos(ax: 3, ay: h*i+3); |
273 | } |
274 | list->viewport()->setPos(ax: 0, ay: 0); |
275 | } |
276 | |
277 | |
278 | protected: |
279 | void resizeEvent(QResizeEvent *e) override |
280 | { |
281 | // resize the scene according to our own size to prevent scrolling |
282 | m_scene->setSceneRect(x: 0, y: 0, w: m_view->viewport()->width(), h: m_view->viewport()->height()); |
283 | QMainWindow::resizeEvent(event: e); |
284 | } |
285 | |
286 | QGraphicsScene *m_scene; |
287 | QGraphicsView *m_view; |
288 | }; |
289 | |
290 | int main(int argc, char *argv[]) |
291 | { |
292 | QApplication a(argc, argv); |
293 | bool touch = (a.arguments().contains(str: QLatin1String("--touch" ))); |
294 | MainWindow mw(touch); |
295 | mw.show(); |
296 | #ifdef Q_OS_MAC |
297 | mw.raise(); |
298 | #endif |
299 | return a.exec(); |
300 | } |
301 | |
302 | #include "main.moc" |
303 | |