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