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*/
61class RectObject : public QGraphicsObject
62{
63 Q_OBJECT
64
65public:
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(QGraphicsItem::ItemClipsToShape, 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(-halfpw, -halfpw, halfpw, 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(m_rect);
95
96 painter->setPen(Qt::black);
97 QFont f;
98 f.setPixelSize(m_rect.height());
99 painter->setFont(f);
100 painter->drawText(m_rect, Qt::AlignCenter, m_text);
101 }
102
103 QString m_text;
104 QRectF m_rect;
105 QPen m_pen;
106 QBrush m_brush;
107};
108
109class ViewObject : public QGraphicsObject
110{
111 Q_OBJECT
112public:
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(item->pos());
123 return rect;
124 }
125
126 void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override
127 { }
128};
129
130class ListObject : public QGraphicsObject
131{
132 Q_OBJECT
133
134public:
135 ListObject(const QSizeF &size, bool useTouch)
136 {
137 m_size = size;
138 setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
139 // grab gesture via Touch or Mouse events
140 QScroller::grabGesture(this, 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(qreal(0), br.width() - m_size.width()),
164 qMax(qreal(0), 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(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(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(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
225class MainWindow : public QMainWindow
226{
227 Q_OBJECT
228
229public:
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(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(i));
242 fillList(childList);
243 childList->setParentItem(mainList->viewport());
244 childList->setPos(i*mainList->m_size.width()/3, 0);
245 }
246 mainList->viewport()->setPos(0, 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("Gesture example"));
259 m_scene->setSceneRect(0, 0, m_view->viewport()->width(), 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(3, h*i+3);
273 }
274 list->viewport()->setPos(0, 0);
275 }
276
277
278protected:
279 void resizeEvent(QResizeEvent *e) override
280 {
281 // resize the scene according to our own size to prevent scrolling
282 m_scene->setSceneRect(0, 0, m_view->viewport()->width(), m_view->viewport()->height());
283 QMainWindow::resizeEvent(e);
284 }
285
286 QGraphicsScene *m_scene;
287 QGraphicsView *m_view;
288};
289
290int main(int argc, char *argv[])
291{
292 QApplication a(argc, argv);
293 bool touch = (a.arguments().contains(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