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 "pathdeform.h" |
52 | |
53 | #include <QGuiApplication> |
54 | #include <QScreen> |
55 | #include <QtDebug> |
56 | #include <QMouseEvent> |
57 | #include <QTimerEvent> |
58 | #include <QLayout> |
59 | #include <QLineEdit> |
60 | #include <QPainter> |
61 | #include <QSlider> |
62 | #include <QLabel> |
63 | #include <QDesktopWidget> |
64 | #include <qmath.h> |
65 | |
66 | PathDeformControls::PathDeformControls(QWidget *parent, |
67 | PathDeformRenderer* renderer, bool smallScreen) |
68 | : QWidget(parent) |
69 | { |
70 | m_renderer = renderer; |
71 | |
72 | if (smallScreen) |
73 | layoutForSmallScreen(); |
74 | else |
75 | layoutForDesktop(); |
76 | } |
77 | |
78 | void PathDeformControls::layoutForDesktop() |
79 | { |
80 | QGroupBox* mainGroup = new QGroupBox(this); |
81 | mainGroup->setTitle(tr(s: "Controls" )); |
82 | |
83 | QGroupBox *radiusGroup = new QGroupBox(mainGroup); |
84 | radiusGroup->setTitle(tr(s: "Lens Radius" )); |
85 | QSlider *radiusSlider = new QSlider(Qt::Horizontal, radiusGroup); |
86 | radiusSlider->setRange(min: 15, max: 150); |
87 | radiusSlider->setSizePolicy(hor: QSizePolicy::Preferred, ver: QSizePolicy::Fixed); |
88 | |
89 | QGroupBox *deformGroup = new QGroupBox(mainGroup); |
90 | deformGroup->setTitle(tr(s: "Deformation" )); |
91 | QSlider *deformSlider = new QSlider(Qt::Horizontal, deformGroup); |
92 | deformSlider->setRange(min: -100, max: 100); |
93 | deformSlider->setSizePolicy(hor: QSizePolicy::Preferred, ver: QSizePolicy::Fixed); |
94 | |
95 | QGroupBox *fontSizeGroup = new QGroupBox(mainGroup); |
96 | fontSizeGroup->setTitle(tr(s: "Font Size" )); |
97 | QSlider *fontSizeSlider = new QSlider(Qt::Horizontal, fontSizeGroup); |
98 | fontSizeSlider->setRange(min: 16, max: 200); |
99 | fontSizeSlider->setSizePolicy(hor: QSizePolicy::Preferred, ver: QSizePolicy::Fixed); |
100 | |
101 | QGroupBox *textGroup = new QGroupBox(mainGroup); |
102 | textGroup->setTitle(tr(s: "Text" )); |
103 | QLineEdit *textInput = new QLineEdit(textGroup); |
104 | |
105 | QPushButton *animateButton = new QPushButton(mainGroup); |
106 | animateButton->setText(tr(s: "Animated" )); |
107 | animateButton->setCheckable(true); |
108 | |
109 | QPushButton *showSourceButton = new QPushButton(mainGroup); |
110 | showSourceButton->setText(tr(s: "Show Source" )); |
111 | |
112 | #if QT_CONFIG(opengl) |
113 | QPushButton *enableOpenGLButton = new QPushButton(mainGroup); |
114 | enableOpenGLButton->setText(tr(s: "Use OpenGL" )); |
115 | enableOpenGLButton->setCheckable(true); |
116 | enableOpenGLButton->setChecked(m_renderer->usesOpenGL()); |
117 | #endif |
118 | |
119 | QPushButton *whatsThisButton = new QPushButton(mainGroup); |
120 | whatsThisButton->setText(tr(s: "What's This?" )); |
121 | whatsThisButton->setCheckable(true); |
122 | |
123 | |
124 | mainGroup->setFixedWidth(180); |
125 | |
126 | QVBoxLayout *mainGroupLayout = new QVBoxLayout(mainGroup); |
127 | mainGroupLayout->addWidget(radiusGroup); |
128 | mainGroupLayout->addWidget(deformGroup); |
129 | mainGroupLayout->addWidget(fontSizeGroup); |
130 | mainGroupLayout->addWidget(textGroup); |
131 | mainGroupLayout->addWidget(animateButton); |
132 | mainGroupLayout->addStretch(stretch: 1); |
133 | #if QT_CONFIG(opengl) |
134 | mainGroupLayout->addWidget(enableOpenGLButton); |
135 | #endif |
136 | mainGroupLayout->addWidget(showSourceButton); |
137 | mainGroupLayout->addWidget(whatsThisButton); |
138 | |
139 | QVBoxLayout *radiusGroupLayout = new QVBoxLayout(radiusGroup); |
140 | radiusGroupLayout->addWidget(radiusSlider); |
141 | |
142 | QVBoxLayout *deformGroupLayout = new QVBoxLayout(deformGroup); |
143 | deformGroupLayout->addWidget(deformSlider); |
144 | |
145 | QVBoxLayout *fontSizeGroupLayout = new QVBoxLayout(fontSizeGroup); |
146 | fontSizeGroupLayout->addWidget(fontSizeSlider); |
147 | |
148 | QVBoxLayout *textGroupLayout = new QVBoxLayout(textGroup); |
149 | textGroupLayout->addWidget(textInput); |
150 | |
151 | QVBoxLayout * mainLayout = new QVBoxLayout(this); |
152 | mainLayout->addWidget(mainGroup); |
153 | mainLayout->setContentsMargins(QMargins()); |
154 | |
155 | connect(sender: radiusSlider, signal: &QAbstractSlider::valueChanged, receiver: m_renderer, slot: &PathDeformRenderer::setRadius); |
156 | connect(sender: deformSlider, signal: &QAbstractSlider::valueChanged, receiver: m_renderer, slot: &PathDeformRenderer::setIntensity); |
157 | connect(sender: fontSizeSlider, signal: &QAbstractSlider::valueChanged, receiver: m_renderer, slot: &PathDeformRenderer::setFontSize); |
158 | connect(sender: animateButton, signal: &QAbstractButton::clicked, receiver: m_renderer, slot: &PathDeformRenderer::setAnimated); |
159 | #if QT_CONFIG(opengl) |
160 | connect(sender: enableOpenGLButton, signal: &QAbstractButton::clicked, receiver: m_renderer, slot: &ArthurFrame::enableOpenGL); |
161 | #endif |
162 | |
163 | connect(sender: textInput, signal: &QLineEdit::textChanged, receiver: m_renderer, slot: &PathDeformRenderer::setText); |
164 | connect(sender: m_renderer, signal: &ArthurFrame::descriptionEnabledChanged, |
165 | receiver: whatsThisButton, slot: &QAbstractButton::setChecked); |
166 | connect(sender: whatsThisButton, signal: &QAbstractButton::clicked, receiver: m_renderer, slot: &ArthurFrame::setDescriptionEnabled); |
167 | connect(sender: showSourceButton, signal: &QAbstractButton::clicked, receiver: m_renderer, slot: &ArthurFrame::showSource); |
168 | |
169 | animateButton->animateClick(); |
170 | deformSlider->setValue(80); |
171 | fontSizeSlider->setValue(120); |
172 | radiusSlider->setValue(100); |
173 | textInput->setText(tr(s: "Qt" )); |
174 | } |
175 | |
176 | void PathDeformControls::layoutForSmallScreen() |
177 | { |
178 | QGroupBox* mainGroup = new QGroupBox(this); |
179 | mainGroup->setTitle(tr(s: "Controls" )); |
180 | |
181 | QLabel *radiusLabel = new QLabel(mainGroup); |
182 | radiusLabel->setText(tr(s: "Lens Radius:" )); |
183 | QSlider *radiusSlider = new QSlider(Qt::Horizontal, mainGroup); |
184 | radiusSlider->setRange(min: 15, max: 150); |
185 | radiusSlider->setSizePolicy(hor: QSizePolicy::Expanding, ver: QSizePolicy::Fixed); |
186 | |
187 | QLabel *deformLabel = new QLabel(mainGroup); |
188 | deformLabel->setText(tr(s: "Deformation:" )); |
189 | QSlider *deformSlider = new QSlider(Qt::Horizontal, mainGroup); |
190 | deformSlider->setRange(min: -100, max: 100); |
191 | deformSlider->setSizePolicy(hor: QSizePolicy::Expanding, ver: QSizePolicy::Fixed); |
192 | |
193 | QLabel *fontSizeLabel = new QLabel(mainGroup); |
194 | fontSizeLabel->setText(tr(s: "Font Size:" )); |
195 | QSlider *fontSizeSlider = new QSlider(Qt::Horizontal, mainGroup); |
196 | fontSizeSlider->setRange(min: 16, max: 200); |
197 | fontSizeSlider->setSizePolicy(hor: QSizePolicy::Expanding, ver: QSizePolicy::Fixed); |
198 | |
199 | QPushButton *animateButton = new QPushButton(tr(s: "Animated" ), mainGroup); |
200 | animateButton->setCheckable(true); |
201 | |
202 | #if QT_CONFIG(opengl) |
203 | QPushButton *enableOpenGLButton = new QPushButton(mainGroup); |
204 | enableOpenGLButton->setText(tr(s: "Use OpenGL" )); |
205 | enableOpenGLButton->setCheckable(mainGroup); |
206 | enableOpenGLButton->setChecked(m_renderer->usesOpenGL()); |
207 | #endif |
208 | |
209 | QPushButton *quitButton = new QPushButton(tr(s: "Quit" ), mainGroup); |
210 | QPushButton *okButton = new QPushButton(tr(s: "OK" ), mainGroup); |
211 | |
212 | |
213 | QGridLayout *mainGroupLayout = new QGridLayout(mainGroup); |
214 | mainGroupLayout->setContentsMargins(QMargins()); |
215 | mainGroupLayout->addWidget(radiusLabel, row: 0, column: 0, Qt::AlignRight); |
216 | mainGroupLayout->addWidget(radiusSlider, row: 0, column: 1); |
217 | mainGroupLayout->addWidget(deformLabel, row: 1, column: 0, Qt::AlignRight); |
218 | mainGroupLayout->addWidget(deformSlider, row: 1, column: 1); |
219 | mainGroupLayout->addWidget(fontSizeLabel, row: 2, column: 0, Qt::AlignRight); |
220 | mainGroupLayout->addWidget(fontSizeSlider, row: 2, column: 1); |
221 | mainGroupLayout->addWidget(animateButton, row: 3,column: 0, rowSpan: 1,columnSpan: 2); |
222 | #if QT_CONFIG(opengl) |
223 | mainGroupLayout->addWidget(enableOpenGLButton, row: 4,column: 0, rowSpan: 1,columnSpan: 2); |
224 | #endif |
225 | |
226 | QVBoxLayout *mainLayout = new QVBoxLayout(this); |
227 | mainLayout->addWidget(mainGroup); |
228 | mainLayout->addStretch(stretch: 1); |
229 | mainLayout->addWidget(okButton); |
230 | mainLayout->addWidget(quitButton); |
231 | |
232 | connect(sender: quitButton, signal: &QAbstractButton::clicked, receiver: this, slot: &PathDeformControls::quitPressed); |
233 | connect(sender: okButton, signal: &QAbstractButton::clicked, receiver: this, slot: &PathDeformControls::okPressed); |
234 | connect(sender: radiusSlider, signal: &QAbstractSlider::valueChanged, receiver: m_renderer, slot: &PathDeformRenderer::setRadius); |
235 | connect(sender: deformSlider, signal: &QAbstractSlider::valueChanged, receiver: m_renderer, slot: &PathDeformRenderer::setIntensity); |
236 | connect(sender: fontSizeSlider, signal: &QAbstractSlider::valueChanged, receiver: m_renderer, slot: &PathDeformRenderer::setFontSize); |
237 | connect(sender: animateButton, signal: &QAbstractButton::clicked, receiver: m_renderer, slot: &PathDeformRenderer::setAnimated); |
238 | #if QT_CONFIG(opengl) |
239 | connect(sender: enableOpenGLButton, signal: &QAbstractButton::clicked, receiver: m_renderer, slot: &ArthurFrame::enableOpenGL); |
240 | #endif |
241 | |
242 | |
243 | animateButton->animateClick(); |
244 | deformSlider->setValue(80); |
245 | fontSizeSlider->setValue(120); |
246 | |
247 | QRect screen_size = QGuiApplication::primaryScreen()->geometry(); |
248 | radiusSlider->setValue(qMin(a: screen_size.width(), b: screen_size.height())/5); |
249 | |
250 | m_renderer->setText(tr(s: "Qt" )); |
251 | } |
252 | |
253 | PathDeformWidget::PathDeformWidget(QWidget *parent, bool smallScreen) |
254 | : QWidget(parent) |
255 | { |
256 | setWindowTitle(tr(s: "Vector Deformation" )); |
257 | |
258 | m_renderer = new PathDeformRenderer(this, smallScreen); |
259 | m_renderer->setSizePolicy(hor: QSizePolicy::Expanding, ver: QSizePolicy::Expanding); |
260 | |
261 | // Layouts |
262 | QHBoxLayout *mainLayout = new QHBoxLayout(this); |
263 | mainLayout->addWidget(m_renderer); |
264 | |
265 | m_controls = new PathDeformControls(nullptr, m_renderer, smallScreen); |
266 | m_controls->setSizePolicy(hor: QSizePolicy::Fixed, ver: QSizePolicy::Minimum); |
267 | |
268 | if (!smallScreen) |
269 | mainLayout->addWidget(m_controls); |
270 | |
271 | m_renderer->loadSourceFile(fileName: ":res/deform/pathdeform.cpp" ); |
272 | m_renderer->loadDescription(filename: ":res/deform/pathdeform.html" ); |
273 | m_renderer->setDescriptionEnabled(false); |
274 | |
275 | connect(sender: m_renderer, signal: &PathDeformRenderer::clicked, |
276 | receiver: this, slot: &PathDeformWidget::showControls); |
277 | connect(sender: m_controls, signal: &PathDeformControls::okPressed, |
278 | receiver: this, slot: &PathDeformWidget::hideControls); |
279 | connect(sender: m_controls, signal: &PathDeformControls::quitPressed, |
280 | qApp, slot: &QCoreApplication::quit); |
281 | } |
282 | |
283 | |
284 | void PathDeformWidget::showControls() |
285 | { |
286 | m_controls->showFullScreen(); |
287 | } |
288 | |
289 | void PathDeformWidget::hideControls() |
290 | { |
291 | m_controls->hide(); |
292 | } |
293 | |
294 | void PathDeformWidget::setStyle(QStyle *style) |
295 | { |
296 | QWidget::setStyle(style); |
297 | if (!m_controls) |
298 | return; |
299 | |
300 | m_controls->setStyle(style); |
301 | |
302 | const QList<QWidget *> widgets = m_controls->findChildren<QWidget *>(); |
303 | for (QWidget *w : widgets) |
304 | w->setStyle(style); |
305 | } |
306 | |
307 | static inline QRect circle_bounds(const QPointF ¢er, qreal radius, qreal compensation) |
308 | { |
309 | return QRect(qRound(d: center.x() - radius - compensation), |
310 | qRound(d: center.y() - radius - compensation), |
311 | qRound(d: (radius + compensation) * 2), |
312 | qRound(d: (radius + compensation) * 2)); |
313 | |
314 | } |
315 | |
316 | const int LENS_EXTENT = 10; |
317 | |
318 | PathDeformRenderer::PathDeformRenderer(QWidget *widget, bool smallScreen) |
319 | : ArthurFrame(widget) |
320 | { |
321 | m_radius = 100; |
322 | m_pos = QPointF(m_radius, m_radius); |
323 | m_direction = QPointF(1, 1); |
324 | m_fontSize = 24; |
325 | m_animated = true; |
326 | m_repaintTimer.start(msec: 25, obj: this); |
327 | m_repaintTracker.start(); |
328 | m_intensity = 100; |
329 | m_smallScreen = smallScreen; |
330 | |
331 | // m_fpsTimer.start(1000, this); |
332 | // m_fpsCounter = 0; |
333 | |
334 | generateLensPixmap(); |
335 | } |
336 | |
337 | void PathDeformRenderer::setText(const QString &text) |
338 | { |
339 | m_text = text; |
340 | |
341 | QFont f("times new roman,utopia" ); |
342 | f.setStyleStrategy(QFont::ForceOutline); |
343 | f.setPointSize(m_fontSize); |
344 | f.setStyleHint(QFont::Times); |
345 | |
346 | QFontMetrics fm(f); |
347 | |
348 | m_paths.clear(); |
349 | m_pathBounds = QRect(); |
350 | |
351 | QPointF advance(0, 0); |
352 | |
353 | bool do_quick = true; |
354 | for (int i=0; i<text.size(); ++i) { |
355 | if (text.at(i).unicode() >= 0x4ff && text.at(i).unicode() <= 0x1e00) { |
356 | do_quick = false; |
357 | break; |
358 | } |
359 | } |
360 | |
361 | if (do_quick) { |
362 | for (int i=0; i<text.size(); ++i) { |
363 | QPainterPath path; |
364 | path.addText(point: advance, f, text: text.mid(position: i, n: 1)); |
365 | m_pathBounds |= path.boundingRect(); |
366 | m_paths << path; |
367 | advance += QPointF(fm.horizontalAdvance(text.mid(position: i, n: 1)), 0); |
368 | } |
369 | } else { |
370 | QPainterPath path; |
371 | path.addText(point: advance, f, text); |
372 | m_pathBounds |= path.boundingRect(); |
373 | m_paths << path; |
374 | } |
375 | |
376 | for (int i=0; i<m_paths.size(); ++i) |
377 | m_paths[i] = m_paths[i] * QTransform(1, 0, 0, 1, -m_pathBounds.x(), -m_pathBounds.y()); |
378 | |
379 | update(); |
380 | } |
381 | |
382 | |
383 | void PathDeformRenderer::generateLensPixmap() |
384 | { |
385 | qreal rad = m_radius + LENS_EXTENT; |
386 | |
387 | QRect bounds = circle_bounds(center: QPointF(), radius: rad, compensation: 0); |
388 | |
389 | QPainter painter; |
390 | |
391 | if (preferImage()) { |
392 | m_lens_image = QImage(bounds.size(), QImage::Format_ARGB32_Premultiplied); |
393 | m_lens_image.fill(pixel: 0); |
394 | painter.begin(&m_lens_image); |
395 | } else { |
396 | m_lens_pixmap = QPixmap(bounds.size()); |
397 | m_lens_pixmap.fill(fillColor: Qt::transparent); |
398 | painter.begin(&m_lens_pixmap); |
399 | } |
400 | |
401 | QRadialGradient gr(rad, rad, rad, 3 * rad / 5, 3 * rad / 5); |
402 | gr.setColorAt(pos: 0.0, color: QColor(255, 255, 255, 191)); |
403 | gr.setColorAt(pos: 0.2, color: QColor(255, 255, 127, 191)); |
404 | gr.setColorAt(pos: 0.9, color: QColor(150, 150, 200, 63)); |
405 | gr.setColorAt(pos: 0.95, color: QColor(0, 0, 0, 127)); |
406 | gr.setColorAt(pos: 1, color: QColor(0, 0, 0, 0)); |
407 | painter.setRenderHint(hint: QPainter::Antialiasing); |
408 | painter.setBrush(gr); |
409 | painter.setPen(Qt::NoPen); |
410 | painter.drawEllipse(x: 0, y: 0, w: bounds.width(), h: bounds.height()); |
411 | } |
412 | |
413 | |
414 | void PathDeformRenderer::setAnimated(bool animated) |
415 | { |
416 | m_animated = animated; |
417 | |
418 | if (m_animated) { |
419 | // m_fpsTimer.start(1000, this); |
420 | // m_fpsCounter = 0; |
421 | m_repaintTimer.start(msec: 25, obj: this); |
422 | m_repaintTracker.start(); |
423 | } else { |
424 | // m_fpsTimer.stop(); |
425 | m_repaintTimer.stop(); |
426 | } |
427 | } |
428 | |
429 | void PathDeformRenderer::timerEvent(QTimerEvent *e) |
430 | { |
431 | |
432 | if (e->timerId() == m_repaintTimer.timerId()) { |
433 | |
434 | if (QLineF(QPointF(0,0), m_direction).length() > 1) |
435 | m_direction *= 0.995; |
436 | qreal time = m_repaintTracker.restart(); |
437 | |
438 | QRect rectBefore = circle_bounds(center: m_pos, radius: m_radius, compensation: m_fontSize); |
439 | |
440 | qreal dx = m_direction.x(); |
441 | qreal dy = m_direction.y(); |
442 | if (time > 0) { |
443 | dx = dx * time * .1; |
444 | dy = dy * time * .1; |
445 | } |
446 | |
447 | m_pos += QPointF(dx, dy); |
448 | |
449 | if (m_pos.x() - m_radius < 0) { |
450 | m_direction.setX(-m_direction.x()); |
451 | m_pos.setX(m_radius); |
452 | } else if (m_pos.x() + m_radius > width()) { |
453 | m_direction.setX(-m_direction.x()); |
454 | m_pos.setX(width() - m_radius); |
455 | } |
456 | |
457 | if (m_pos.y() - m_radius < 0) { |
458 | m_direction.setY(-m_direction.y()); |
459 | m_pos.setY(m_radius); |
460 | } else if (m_pos.y() + m_radius > height()) { |
461 | m_direction.setY(-m_direction.y()); |
462 | m_pos.setY(height() - m_radius); |
463 | } |
464 | |
465 | #if QT_CONFIG(opengl) |
466 | if (usesOpenGL()) { |
467 | update(); |
468 | } else |
469 | #endif |
470 | { |
471 | QRect rectAfter = circle_bounds(center: m_pos, radius: m_radius, compensation: m_fontSize); |
472 | update(rectAfter | rectBefore); |
473 | } |
474 | } |
475 | // else if (e->timerId() == m_fpsTimer.timerId()) { |
476 | // printf("fps: %d\n", m_fpsCounter); |
477 | // emit frameRate(m_fpsCounter); |
478 | // m_fpsCounter = 0; |
479 | |
480 | // } |
481 | } |
482 | |
483 | void PathDeformRenderer::mousePressEvent(QMouseEvent *e) |
484 | { |
485 | if (m_show_doc) { |
486 | setDescriptionEnabled(false); |
487 | return; |
488 | } |
489 | setDescriptionEnabled(false); |
490 | |
491 | m_repaintTimer.stop(); |
492 | m_offset = QPointF(); |
493 | if (QLineF(m_pos, e->pos()).length() <= m_radius) |
494 | m_offset = m_pos - e->pos(); |
495 | |
496 | m_mousePress = e->pos(); |
497 | |
498 | // If we're not running in small screen mode, always assume we're dragging |
499 | m_mouseDrag = !m_smallScreen; |
500 | |
501 | mouseMoveEvent(e); |
502 | } |
503 | |
504 | void PathDeformRenderer::mouseReleaseEvent(QMouseEvent *e) |
505 | { |
506 | if (e->buttons() == Qt::NoButton && m_animated) { |
507 | m_repaintTimer.start(msec: 10, obj: this); |
508 | m_repaintTracker.start(); |
509 | } |
510 | |
511 | if (!m_mouseDrag && m_smallScreen) |
512 | emit clicked(); |
513 | } |
514 | |
515 | void PathDeformRenderer::mouseMoveEvent(QMouseEvent *e) |
516 | { |
517 | if (!m_mouseDrag && (QLineF(m_mousePress, e->pos()).length() > 25.0) ) |
518 | m_mouseDrag = true; |
519 | |
520 | if (m_mouseDrag) { |
521 | QRect rectBefore = circle_bounds(center: m_pos, radius: m_radius, compensation: m_fontSize); |
522 | if (e->type() == QEvent::MouseMove) { |
523 | QLineF line(m_pos, e->pos() + m_offset); |
524 | line.setLength(line.length() * .1); |
525 | QPointF dir(line.dx(), line.dy()); |
526 | m_direction = (m_direction + dir) / 2; |
527 | } |
528 | m_pos = e->pos() + m_offset; |
529 | #if QT_CONFIG(opengl) |
530 | if (usesOpenGL()) { |
531 | update(); |
532 | } else |
533 | #endif |
534 | { |
535 | QRect rectAfter = circle_bounds(center: m_pos, radius: m_radius, compensation: m_fontSize); |
536 | update(rectBefore | rectAfter); |
537 | } |
538 | } |
539 | } |
540 | |
541 | QPainterPath PathDeformRenderer::lensDeform(const QPainterPath &source, const QPointF &offset) |
542 | { |
543 | QPainterPath path; |
544 | path.addPath(path: source); |
545 | |
546 | qreal flip = m_intensity / qreal(100); |
547 | |
548 | for (int i=0; i<path.elementCount(); ++i) { |
549 | const QPainterPath::Element &e = path.elementAt(i); |
550 | |
551 | qreal x = e.x + offset.x(); |
552 | qreal y = e.y + offset.y(); |
553 | |
554 | qreal dx = x - m_pos.x(); |
555 | qreal dy = y - m_pos.y(); |
556 | qreal len = m_radius - qSqrt(v: dx * dx + dy * dy); |
557 | |
558 | if (len > 0) { |
559 | path.setElementPositionAt(i, |
560 | x: x + flip * dx * len / m_radius, |
561 | y: y + flip * dy * len / m_radius); |
562 | } else { |
563 | path.setElementPositionAt(i, x, y); |
564 | } |
565 | |
566 | } |
567 | |
568 | return path; |
569 | } |
570 | |
571 | void PathDeformRenderer::paint(QPainter *painter) |
572 | { |
573 | int pad_x = 5; |
574 | int pad_y = 5; |
575 | |
576 | int skip_x = qRound(d: m_pathBounds.width() + pad_x + m_fontSize/2); |
577 | int skip_y = qRound(d: m_pathBounds.height() + pad_y); |
578 | |
579 | painter->setPen(Qt::NoPen); |
580 | painter->setBrush(Qt::black); |
581 | |
582 | QRectF clip(painter->clipPath().boundingRect()); |
583 | |
584 | int overlap = pad_x / 2; |
585 | |
586 | for (int start_y=0; start_y < height(); start_y += skip_y) { |
587 | |
588 | if (start_y > clip.bottom()) |
589 | break; |
590 | |
591 | int start_x = -overlap; |
592 | for (; start_x < width(); start_x += skip_x) { |
593 | |
594 | if (start_y + skip_y >= clip.top() && |
595 | start_x + skip_x >= clip.left() && |
596 | start_x <= clip.right()) { |
597 | for (int i=0; i<m_paths.size(); ++i) { |
598 | QPainterPath path = lensDeform(source: m_paths[i], offset: QPointF(start_x, start_y)); |
599 | painter->drawPath(path); |
600 | } |
601 | } |
602 | } |
603 | overlap = skip_x - (start_x - width()); |
604 | |
605 | } |
606 | |
607 | if (preferImage()) { |
608 | painter->drawImage(p: m_pos - QPointF(m_radius + LENS_EXTENT, m_radius + LENS_EXTENT), |
609 | image: m_lens_image); |
610 | } else { |
611 | painter->drawPixmap(p: m_pos - QPointF(m_radius + LENS_EXTENT, m_radius + LENS_EXTENT), |
612 | pm: m_lens_pixmap); |
613 | } |
614 | } |
615 | |
616 | void PathDeformRenderer::setRadius(int radius) |
617 | { |
618 | qreal max = qMax(a: m_radius, b: (qreal)radius); |
619 | m_radius = radius; |
620 | generateLensPixmap(); |
621 | if (!m_animated || m_radius < max) { |
622 | #if QT_CONFIG(opengl) |
623 | if (usesOpenGL()){ |
624 | update(); |
625 | return; |
626 | } |
627 | #endif |
628 | update(circle_bounds(center: m_pos, radius: max, compensation: m_fontSize)); |
629 | } |
630 | } |
631 | |
632 | void PathDeformRenderer::setIntensity(int intensity) |
633 | { |
634 | m_intensity = intensity; |
635 | if (!m_animated) { |
636 | #if QT_CONFIG(opengl) |
637 | if (usesOpenGL()) { |
638 | update(); |
639 | return; |
640 | } |
641 | #endif |
642 | update(circle_bounds(center: m_pos, radius: m_radius, compensation: m_fontSize)); |
643 | } |
644 | } |
645 | |