| 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 | |