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 "sortingbox.h" |
52 | |
53 | #include <QMouseEvent> |
54 | #include <QIcon> |
55 | #include <QPainter> |
56 | #include <QRandomGenerator> |
57 | #include <QStyle> |
58 | #include <QToolButton> |
59 | #include <QToolTip> |
60 | |
61 | //! [0] |
62 | SortingBox::SortingBox(QWidget *parent) |
63 | : QWidget(parent) |
64 | { |
65 | //! [0] //! [1] |
66 | setMouseTracking(true); |
67 | //! [1] //! [2] |
68 | setBackgroundRole(QPalette::Base); |
69 | //! [2] |
70 | |
71 | itemInMotion = nullptr; |
72 | |
73 | //! [3] |
74 | newCircleButton = createToolButton(toolTip: tr(s: "New Circle" ), |
75 | icon: QIcon(":/images/circle.png" ), |
76 | SLOT(createNewCircle())); |
77 | |
78 | newSquareButton = createToolButton(toolTip: tr(s: "New Square" ), |
79 | icon: QIcon(":/images/square.png" ), |
80 | SLOT(createNewSquare())); |
81 | |
82 | newTriangleButton = createToolButton(toolTip: tr(s: "New Triangle" ), |
83 | icon: QIcon(":/images/triangle.png" ), |
84 | SLOT(createNewTriangle())); |
85 | |
86 | circlePath.addEllipse(rect: QRect(0, 0, 100, 100)); |
87 | squarePath.addRect(rect: QRect(0, 0, 100, 100)); |
88 | |
89 | qreal x = trianglePath.currentPosition().x(); |
90 | qreal y = trianglePath.currentPosition().y(); |
91 | trianglePath.moveTo(x: x + 120 / 2, y); |
92 | trianglePath.lineTo(x: 0, y: 100); |
93 | trianglePath.lineTo(x: 120, y: 100); |
94 | trianglePath.lineTo(x: x + 120 / 2, y); |
95 | |
96 | //! [3] //! [4] |
97 | setWindowTitle(tr(s: "Tool Tips" )); |
98 | resize(w: 500, h: 300); |
99 | |
100 | createShapeItem(path: circlePath, toolTip: tr(s: "Circle" ), pos: initialItemPosition(path: circlePath), |
101 | color: initialItemColor()); |
102 | createShapeItem(path: squarePath, toolTip: tr(s: "Square" ), pos: initialItemPosition(path: squarePath), |
103 | color: initialItemColor()); |
104 | createShapeItem(path: trianglePath, toolTip: tr(s: "Triangle" ), |
105 | pos: initialItemPosition(path: trianglePath), color: initialItemColor()); |
106 | } |
107 | //! [4] |
108 | |
109 | //! [5] |
110 | bool SortingBox::event(QEvent *event) |
111 | { |
112 | //! [5] //! [6] |
113 | if (event->type() == QEvent::ToolTip) { |
114 | QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event); |
115 | int index = itemAt(pos: helpEvent->pos()); |
116 | if (index != -1) { |
117 | QToolTip::showText(pos: helpEvent->globalPos(), text: shapeItems[index].toolTip()); |
118 | } else { |
119 | QToolTip::hideText(); |
120 | event->ignore(); |
121 | } |
122 | |
123 | return true; |
124 | } |
125 | return QWidget::event(event); |
126 | } |
127 | //! [6] |
128 | |
129 | //! [7] |
130 | void SortingBox::resizeEvent(QResizeEvent * /* event */) |
131 | { |
132 | int margin = style()->pixelMetric(metric: QStyle::PM_DefaultTopLevelMargin); |
133 | int x = width() - margin; |
134 | int y = height() - margin; |
135 | |
136 | y = updateButtonGeometry(button: newCircleButton, x, y); |
137 | y = updateButtonGeometry(button: newSquareButton, x, y); |
138 | updateButtonGeometry(button: newTriangleButton, x, y); |
139 | } |
140 | //! [7] |
141 | |
142 | //! [8] |
143 | void SortingBox::paintEvent(QPaintEvent * /* event */) |
144 | { |
145 | QPainter painter(this); |
146 | painter.setRenderHint(hint: QPainter::Antialiasing); |
147 | for (const ShapeItem &shapeItem : qAsConst(t&: shapeItems)) { |
148 | //! [8] //! [9] |
149 | painter.translate(offset: shapeItem.position()); |
150 | //! [9] //! [10] |
151 | painter.setBrush(shapeItem.color()); |
152 | painter.drawPath(path: shapeItem.path()); |
153 | painter.translate(offset: -shapeItem.position()); |
154 | } |
155 | } |
156 | //! [10] |
157 | |
158 | //! [11] |
159 | void SortingBox::mousePressEvent(QMouseEvent *event) |
160 | { |
161 | if (event->button() == Qt::LeftButton) { |
162 | int index = itemAt(pos: event->pos()); |
163 | if (index != -1) { |
164 | itemInMotion = &shapeItems[index]; |
165 | previousPosition = event->pos(); |
166 | shapeItems.move(from: index, to: shapeItems.size() - 1); |
167 | update(); |
168 | } |
169 | } |
170 | } |
171 | //! [11] |
172 | |
173 | //! [12] |
174 | void SortingBox::mouseMoveEvent(QMouseEvent *event) |
175 | { |
176 | if ((event->buttons() & Qt::LeftButton) && itemInMotion) |
177 | moveItemTo(pos: event->pos()); |
178 | } |
179 | //! [12] |
180 | |
181 | //! [13] |
182 | void SortingBox::mouseReleaseEvent(QMouseEvent *event) |
183 | { |
184 | if (event->button() == Qt::LeftButton && itemInMotion) { |
185 | moveItemTo(pos: event->pos()); |
186 | itemInMotion = nullptr; |
187 | } |
188 | } |
189 | //! [13] |
190 | |
191 | //! [14] |
192 | void SortingBox::createNewCircle() |
193 | { |
194 | static int count = 1; |
195 | createShapeItem(path: circlePath, toolTip: tr(s: "Circle <%1>" ).arg(a: ++count), |
196 | pos: randomItemPosition(), color: randomItemColor()); |
197 | } |
198 | //! [14] |
199 | |
200 | //! [15] |
201 | void SortingBox::createNewSquare() |
202 | { |
203 | static int count = 1; |
204 | createShapeItem(path: squarePath, toolTip: tr(s: "Square <%1>" ).arg(a: ++count), |
205 | pos: randomItemPosition(), color: randomItemColor()); |
206 | } |
207 | //! [15] |
208 | |
209 | //! [16] |
210 | void SortingBox::createNewTriangle() |
211 | { |
212 | static int count = 1; |
213 | createShapeItem(path: trianglePath, toolTip: tr(s: "Triangle <%1>" ).arg(a: ++count), |
214 | pos: randomItemPosition(), color: randomItemColor()); |
215 | } |
216 | //! [16] |
217 | |
218 | //! [17] |
219 | int SortingBox::itemAt(const QPoint &pos) |
220 | { |
221 | for (int i = shapeItems.size() - 1; i >= 0; --i) { |
222 | const ShapeItem &item = shapeItems[i]; |
223 | if (item.path().contains(pt: pos - item.position())) |
224 | return i; |
225 | } |
226 | return -1; |
227 | } |
228 | //! [17] |
229 | |
230 | //! [18] |
231 | void SortingBox::moveItemTo(const QPoint &pos) |
232 | { |
233 | QPoint offset = pos - previousPosition; |
234 | itemInMotion->setPosition(itemInMotion->position() + offset); |
235 | //! [18] //! [19] |
236 | previousPosition = pos; |
237 | update(); |
238 | } |
239 | //! [19] |
240 | |
241 | //! [20] |
242 | int SortingBox::updateButtonGeometry(QToolButton *button, int x, int y) |
243 | { |
244 | QSize size = button->sizeHint(); |
245 | button->setGeometry(ax: x - size.rwidth(), ay: y - size.rheight(), |
246 | aw: size.rwidth(), ah: size.rheight()); |
247 | |
248 | return y - size.rheight() |
249 | - style()->pixelMetric(metric: QStyle::PM_DefaultLayoutSpacing); |
250 | } |
251 | //! [20] |
252 | |
253 | //! [21] |
254 | void SortingBox::createShapeItem(const QPainterPath &path, |
255 | const QString &toolTip, const QPoint &pos, |
256 | const QColor &color) |
257 | { |
258 | ShapeItem shapeItem; |
259 | shapeItem.setPath(path); |
260 | shapeItem.setToolTip(toolTip); |
261 | shapeItem.setPosition(pos); |
262 | shapeItem.setColor(color); |
263 | shapeItems.append(t: shapeItem); |
264 | update(); |
265 | } |
266 | //! [21] |
267 | |
268 | //! [22] |
269 | QToolButton *SortingBox::createToolButton(const QString &toolTip, |
270 | const QIcon &icon, const char *member) |
271 | { |
272 | QToolButton *button = new QToolButton(this); |
273 | button->setToolTip(toolTip); |
274 | button->setIcon(icon); |
275 | button->setIconSize(QSize(32, 32)); |
276 | connect(sender: button, SIGNAL(clicked()), receiver: this, member); |
277 | |
278 | return button; |
279 | } |
280 | //! [22] |
281 | |
282 | //! [23] |
283 | QPoint SortingBox::initialItemPosition(const QPainterPath &path) |
284 | { |
285 | int x; |
286 | int y = (height() - qRound(d: path.controlPointRect().height()) / 2); |
287 | if (shapeItems.size() == 0) |
288 | x = ((3 * width()) / 2 - qRound(d: path.controlPointRect().width())) / 2; |
289 | else |
290 | x = (width() / shapeItems.size() |
291 | - qRound(d: path.controlPointRect().width())) / 2; |
292 | |
293 | return QPoint(x, y); |
294 | } |
295 | //! [23] |
296 | |
297 | //! [24] |
298 | QPoint SortingBox::randomItemPosition() |
299 | { |
300 | return QPoint(QRandomGenerator::global()->bounded(highest: width() - 120), QRandomGenerator::global()->bounded(highest: height() - 120)); |
301 | } |
302 | //! [24] |
303 | |
304 | //! [25] |
305 | QColor SortingBox::initialItemColor() |
306 | { |
307 | return QColor::fromHsv(h: ((shapeItems.size() + 1) * 85) % 256, s: 255, v: 190); |
308 | } |
309 | //! [25] |
310 | |
311 | //! [26] |
312 | QColor SortingBox::randomItemColor() |
313 | { |
314 | return QColor::fromHsv(h: QRandomGenerator::global()->bounded(highest: 256), s: 255, v: 190); |
315 | } |
316 | //! [26] |
317 | |