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 demonstration applications 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 "qtbox.h"
52
53constexpr qreal ROTATE_SPEED_X = 30.0 / 1000.0;
54constexpr qreal ROTATE_SPEED_Y = 20.0 / 1000.0;
55constexpr qreal ROTATE_SPEED_Z = 40.0 / 1000.0;
56constexpr int MAX_ITEM_SIZE = 512;
57constexpr int MIN_ITEM_SIZE = 16;
58
59//============================================================================//
60// ItemBase //
61//============================================================================//
62
63ItemBase::ItemBase(int size, int x, int y) : m_size(size), m_startTime(QTime::currentTime())
64{
65 setFlag(flag: QGraphicsItem::ItemIsMovable, enabled: true);
66 setFlag(flag: QGraphicsItem::ItemIsSelectable, enabled: true);
67 setFlag(flag: QGraphicsItem::ItemIsFocusable, enabled: true);
68 setAcceptHoverEvents(true);
69 setPos(ax: x, ay: y);
70}
71
72QRectF ItemBase::boundingRect() const
73{
74 return QRectF(-m_size / 2, -m_size / 2, m_size, m_size);
75}
76
77void ItemBase::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *)
78{
79 if (option->state & QStyle::State_Selected) {
80 painter->setRenderHint(hint: QPainter::Antialiasing, on: true);
81 if (option->state & QStyle::State_HasFocus)
82 painter->setPen(Qt::yellow);
83 else
84 painter->setPen(Qt::white);
85 painter->drawRect(rect: boundingRect());
86
87 painter->drawLine(x1: m_size / 2 - 9, y1: m_size / 2, x2: m_size / 2, y2: m_size / 2 - 9);
88 painter->drawLine(x1: m_size / 2 - 6, y1: m_size / 2, x2: m_size / 2, y2: m_size / 2 - 6);
89 painter->drawLine(x1: m_size / 2 - 3, y1: m_size / 2, x2: m_size / 2, y2: m_size / 2 - 3);
90
91 painter->setRenderHint(hint: QPainter::Antialiasing, on: false);
92 }
93}
94
95void ItemBase::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
96{
97 if (!isSelected() && scene()) {
98 scene()->clearSelection();
99 setSelected(true);
100 }
101
102 QMenu menu;
103 QAction *delAction = menu.addAction(text: "Delete");
104 QAction *newAction = menu.addAction(text: "New");
105 QAction *growAction = menu.addAction(text: "Grow");
106 QAction *shrinkAction = menu.addAction(text: "Shrink");
107
108 QAction *selectedAction = menu.exec(pos: event->screenPos());
109
110 if (selectedAction == delAction)
111 deleteSelectedItems(scene: scene());
112 else if (selectedAction == newAction)
113 duplicateSelectedItems(scene: scene());
114 else if (selectedAction == growAction)
115 growSelectedItems(scene: scene());
116 else if (selectedAction == shrinkAction)
117 shrinkSelectedItems(scene: scene());
118}
119
120void ItemBase::duplicateSelectedItems(QGraphicsScene *scene)
121{
122 if (!scene)
123 return;
124
125 const QList<QGraphicsItem *> selected = scene->selectedItems();
126 for (QGraphicsItem *item : selected) {
127 ItemBase *itemBase = qgraphicsitem_cast<ItemBase *>(item);
128 if (itemBase)
129 scene->addItem(item: itemBase->createNew(size: itemBase->m_size, x: itemBase->pos().x() + itemBase->m_size, y: itemBase->pos().y()));
130 }
131}
132
133void ItemBase::deleteSelectedItems(QGraphicsScene *scene)
134{
135 if (!scene)
136 return;
137
138 const QList<QGraphicsItem *> selected = scene->selectedItems();
139 for (QGraphicsItem *item : selected) {
140 ItemBase *itemBase = qgraphicsitem_cast<ItemBase *>(item);
141 if (itemBase)
142 delete itemBase;
143 }
144}
145
146void ItemBase::growSelectedItems(QGraphicsScene *scene)
147{
148 if (!scene)
149 return;
150
151 const QList<QGraphicsItem *> selected = scene->selectedItems();
152 for (QGraphicsItem *item : selected) {
153 ItemBase *itemBase = qgraphicsitem_cast<ItemBase *>(item);
154 if (itemBase) {
155 itemBase->prepareGeometryChange();
156 itemBase->m_size *= 2;
157 if (itemBase->m_size > MAX_ITEM_SIZE)
158 itemBase->m_size = MAX_ITEM_SIZE;
159 }
160 }
161}
162
163void ItemBase::shrinkSelectedItems(QGraphicsScene *scene)
164{
165 if (!scene)
166 return;
167
168 const QList<QGraphicsItem *> selected = scene->selectedItems();
169 for (QGraphicsItem *item : selected) {
170 ItemBase *itemBase = qgraphicsitem_cast<ItemBase *>(item);
171 if (itemBase) {
172 itemBase->prepareGeometryChange();
173 itemBase->m_size /= 2;
174 if (itemBase->m_size < MIN_ITEM_SIZE)
175 itemBase->m_size = MIN_ITEM_SIZE;
176 }
177 }
178}
179
180void ItemBase::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
181{
182 if (m_isResizing) {
183 int dx = int(2.0 * event->pos().x());
184 int dy = int(2.0 * event->pos().y());
185 prepareGeometryChange();
186 m_size = (dx > dy ? dx : dy);
187 if (m_size < MIN_ITEM_SIZE)
188 m_size = MIN_ITEM_SIZE;
189 else if (m_size > MAX_ITEM_SIZE)
190 m_size = MAX_ITEM_SIZE;
191 } else {
192 QGraphicsItem::mouseMoveEvent(event);
193 }
194}
195
196void ItemBase::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
197{
198 if (m_isResizing || (isInResizeArea(pos: event->pos()) && isSelected()))
199 setCursor(Qt::SizeFDiagCursor);
200 else
201 setCursor(Qt::ArrowCursor);
202 QGraphicsItem::hoverMoveEvent(event);
203}
204
205void ItemBase::mousePressEvent(QGraphicsSceneMouseEvent *event)
206{
207 static qreal z = 0.0;
208 setZValue(z += 1.0);
209 if (event->button() == Qt::LeftButton && isInResizeArea(pos: event->pos())) {
210 m_isResizing = true;
211 } else {
212 QGraphicsItem::mousePressEvent(event);
213 }
214}
215
216void ItemBase::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
217{
218 if (event->button() == Qt::LeftButton && m_isResizing) {
219 m_isResizing = false;
220 } else {
221 QGraphicsItem::mouseReleaseEvent(event);
222 }
223}
224
225void ItemBase::keyPressEvent(QKeyEvent *event)
226{
227 switch (event->key()) {
228 case Qt::Key_Delete:
229 deleteSelectedItems(scene: scene());
230 break;
231 case Qt::Key_Insert:
232 duplicateSelectedItems(scene: scene());
233 break;
234 case Qt::Key_Plus:
235 growSelectedItems(scene: scene());
236 break;
237 case Qt::Key_Minus:
238 shrinkSelectedItems(scene: scene());
239 break;
240 default:
241 QGraphicsItem::keyPressEvent(event);
242 break;
243 }
244}
245
246void ItemBase::wheelEvent(QGraphicsSceneWheelEvent *event)
247{
248 prepareGeometryChange();
249 m_size = int(m_size * qExp(v: -event->delta() / 600.0));
250 m_size = qBound(min: MIN_ITEM_SIZE, val: m_size, max: MAX_ITEM_SIZE);
251}
252
253int ItemBase::type() const
254{
255 return Type;
256}
257
258
259bool ItemBase::isInResizeArea(const QPointF &pos)
260{
261 return (-pos.y() < pos.x() - m_size + 9);
262}
263
264//============================================================================//
265// QtBox //
266//============================================================================//
267
268QtBox::QtBox(int size, int x, int y) : ItemBase(size, x, y)
269{
270 for (int i = 0; i < 8; ++i) {
271 m_vertices[i].setX(i & 1 ? 0.5f : -0.5f);
272 m_vertices[i].setY(i & 2 ? 0.5f : -0.5f);
273 m_vertices[i].setZ(i & 4 ? 0.5f : -0.5f);
274 }
275 for (int i = 0; i < 4; ++i) {
276 m_texCoords[i].setX(i & 1 ? 1.0f : 0.0f);
277 m_texCoords[i].setY(i & 2 ? 1.0f : 0.0f);
278 }
279 m_normals[0] = QVector3D(-1.0f, 0.0f, 0.0f);
280 m_normals[1] = QVector3D(1.0f, 0.0f, 0.0f);
281 m_normals[2] = QVector3D(0.0f, -1.0f, 0.0f);
282 m_normals[3] = QVector3D(0.0f, 1.0f, 0.0f);
283 m_normals[4] = QVector3D(0.0f, 0.0f, -1.0f);
284 m_normals[5] = QVector3D(0.0f, 0.0f, 1.0f);
285}
286
287QtBox::~QtBox()
288{
289 delete m_texture;
290}
291
292ItemBase *QtBox::createNew(int size, int x, int y)
293{
294 return new QtBox(size, x, y);
295}
296
297void QtBox::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
298{
299 QRectF rect = boundingRect().translated(p: pos());
300 float width = float(painter->device()->width());
301 float height = float(painter->device()->height());
302
303 float left = 2.0f * float(rect.left()) / width - 1.0f;
304 float right = 2.0f * float(rect.right()) / width - 1.0f;
305 float top = 1.0f - 2.0f * float(rect.top()) / height;
306 float bottom = 1.0f - 2.0f * float(rect.bottom()) / height;
307 float moveToRectMatrix[] = {
308 0.5f * (right - left), 0.0f, 0.0f, 0.0f,
309 0.0f, 0.5f * (bottom - top), 0.0f, 0.0f,
310 0.0f, 0.0f, 1.0f, 0.0f,
311 0.5f * (right + left), 0.5f * (bottom + top), 0.0f, 1.0f
312 };
313
314 painter->beginNativePainting();
315
316 glMatrixMode(GL_PROJECTION);
317 glPushMatrix();
318 glLoadMatrixf(m: moveToRectMatrix);
319 qgluPerspective(fovy: 60.0, aspect: 1.0, zNear: 0.01, zFar: 10.0);
320
321 glMatrixMode(GL_MODELVIEW);
322 glPushMatrix();
323 glLoadIdentity();
324
325 //glEnable(GL_DEPTH_TEST);
326 glEnable(GL_CULL_FACE);
327 glEnable(GL_LIGHTING);
328 glEnable(GL_COLOR_MATERIAL);
329 glEnable(GL_NORMALIZE);
330
331 if (m_texture == nullptr)
332 m_texture = new GLTexture2D(":/res/boxes/qt-logo.jpg", 64, 64);
333 m_texture->bind();
334 glEnable(GL_TEXTURE_2D);
335
336 glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
337 float lightColour[] = {1.0f, 1.0f, 1.0f, 1.0f};
338 float lightDir[] = {0.0f, 0.0f, 1.0f, 0.0f};
339 glLightfv(GL_LIGHT0, GL_DIFFUSE, params: lightColour);
340 glLightfv(GL_LIGHT0, GL_POSITION, params: lightDir);
341 glEnable(GL_LIGHT0);
342
343 glTranslatef(x: 0.0f, y: 0.0f, z: -1.5f);
344 glRotatef(angle: ROTATE_SPEED_X * m_startTime.msecsTo(QTime::currentTime()), x: 1.0f, y: 0.0f, z: 0.0f);
345 glRotatef(angle: ROTATE_SPEED_Y * m_startTime.msecsTo(QTime::currentTime()), x: 0.0f, y: 1.0f, z: 0.0f);
346 glRotatef(angle: ROTATE_SPEED_Z * m_startTime.msecsTo(QTime::currentTime()), x: 0.0f, y: 0.0f, z: 1.0f);
347 int dt = m_startTime.msecsTo(QTime::currentTime());
348 if (dt < 500)
349 glScalef(x: dt / 500.0f, y: dt / 500.0f, z: dt / 500.0f);
350
351 for (int dir = 0; dir < 3; ++dir) {
352 glColor4f(red: 1.0f, green: 1.0f, blue: 1.0f, alpha: 1.0);
353
354 glBegin(GL_TRIANGLE_STRIP);
355 glNormal3fv(v: reinterpret_cast<float *>(&m_normals[2 * dir + 0]));
356 for (int i = 0; i < 2; ++i) {
357 for (int j = 0; j < 2; ++j) {
358 glTexCoord2fv(v: reinterpret_cast<float *>(&m_texCoords[(j << 1) | i]));
359 glVertex3fv(v: reinterpret_cast<float *>(&m_vertices[(i << ((dir + 2) % 3)) | (j << ((dir + 1) % 3))]));
360 }
361 }
362 glEnd();
363
364 glBegin(GL_TRIANGLE_STRIP);
365 glNormal3fv(v: reinterpret_cast<float *>(&m_normals[2 * dir + 1]));
366 for (int i = 0; i < 2; ++i) {
367 for (int j = 0; j < 2; ++j) {
368 glTexCoord2fv(v: reinterpret_cast<float *>(&m_texCoords[(j << 1) | i]));
369 glVertex3fv(v: reinterpret_cast<float *>(&m_vertices[(1 << dir) | (i << ((dir + 1) % 3)) | (j << ((dir + 2) % 3))]));
370 }
371 }
372 glEnd();
373 }
374 m_texture->unbind();
375
376 //glDisable(GL_DEPTH_TEST);
377 glDisable(GL_CULL_FACE);
378 glDisable(GL_LIGHTING);
379 glDisable(GL_COLOR_MATERIAL);
380 glDisable(GL_TEXTURE_2D);
381 glDisable(GL_LIGHT0);
382 glDisable(GL_NORMALIZE);
383
384 glPopMatrix();
385
386 glMatrixMode(GL_PROJECTION);
387 glPopMatrix();
388
389 painter->endNativePainting();
390
391 ItemBase::paint(painter, option, widget);
392}
393
394//============================================================================//
395// CircleItem //
396//============================================================================//
397
398CircleItem::CircleItem(int size, int x, int y) : ItemBase(size, x, y)
399 , m_color(QColor::fromHsv(h: QRandomGenerator::global()->bounded(highest: 360), s: 255, v: 255))
400{}
401
402void CircleItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
403{
404 int dt = m_startTime.msecsTo(QTime::currentTime());
405
406 qreal r0 = 0.5 * m_size * (1.0 - qExp(v: -0.001 * ((dt + 3800) % 4000)));
407 qreal r1 = 0.5 * m_size * (1.0 - qExp(v: -0.001 * ((dt + 0) % 4000)));
408 qreal r2 = 0.5 * m_size * (1.0 - qExp(v: -0.001 * ((dt + 1800) % 4000)));
409 qreal r3 = 0.5 * m_size * (1.0 - qExp(v: -0.001 * ((dt + 2000) % 4000)));
410
411 if (r0 > r1)
412 r0 = 0.0;
413 if (r2 > r3)
414 r2 = 0.0;
415
416 QPainterPath path;
417 path.moveTo(x: r1, y: 0.0);
418 path.arcTo(x: -r1, y: -r1, w: 2 * r1, h: 2 * r1, startAngle: 0.0, arcLength: 360.0);
419 path.lineTo(x: r0, y: 0.0);
420 path.arcTo(x: -r0, y: -r0, w: 2 * r0, h: 2 * r0, startAngle: 0.0, arcLength: -360.0);
421 path.closeSubpath();
422 path.moveTo(x: r3, y: 0.0);
423 path.arcTo(x: -r3, y: -r3, w: 2 * r3, h: 2 * r3, startAngle: 0.0, arcLength: 360.0);
424 path.lineTo(x: r0, y: 0.0);
425 path.arcTo(x: -r2, y: -r2, w: 2 * r2, h: 2 * r2, startAngle: 0.0, arcLength: -360.0);
426 path.closeSubpath();
427 painter->setRenderHint(hint: QPainter::Antialiasing, on: true);
428 painter->setBrush(QBrush(m_color));
429 painter->setPen(Qt::NoPen);
430 painter->drawPath(path);
431 painter->setBrush(Qt::NoBrush);
432 painter->setPen(Qt::SolidLine);
433 painter->setRenderHint(hint: QPainter::Antialiasing, on: false);
434
435 ItemBase::paint(painter, option, widget);
436}
437
438ItemBase *CircleItem::createNew(int size, int x, int y)
439{
440 return new CircleItem(size, x, y);
441}
442
443//============================================================================//
444// SquareItem //
445//============================================================================//
446
447SquareItem::SquareItem(int size, int x, int y) : ItemBase(size, x, y)
448 , m_image(QPixmap(":/res/boxes/square.jpg"))
449{}
450
451void SquareItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
452{
453 int dt = m_startTime.msecsTo(QTime::currentTime());
454 QTransform oldTransform = painter->worldTransform();
455 int dtMod = dt % 2000;
456 qreal amp = 0.002 * (dtMod < 1000 ? dtMod : 2000 - dtMod) - 1.0;
457
458 qreal scale = 0.6 + 0.2 * amp * amp;
459 painter->setWorldTransform(matrix: QTransform().rotate(a: 15.0 * amp).scale(sx: scale, sy: scale), combine: true);
460
461 painter->drawPixmap(x: -m_size / 2, y: -m_size / 2, w: m_size, h: m_size, pm: m_image);
462
463 painter->setWorldTransform(matrix: oldTransform, combine: false);
464 ItemBase::paint(painter, option, widget);
465}
466
467ItemBase *SquareItem::createNew(int size, int x, int y)
468{
469 return new SquareItem(size, x, y);
470}
471

source code of qtbase/examples/widgets/graphicsview/boxes/qtbox.cpp