qt5base-lts/examples/corelib/threads/mandelbrot/mandelbrotwidget.cpp
Nicholas Bennett fc5482cac6 Support pinch zoom gesture in the Mandelbrot example
Used QGesture for this as per the gesture example.
Moved the help and info text to separate lines.
Enabled word wrap to prevent text going off screen.

As a drive-by, the code to center the window at a size that's a fraction
of the screen is replaced with a simple sizeHint() override. The user
should have control over placement, particularly now that it should be
placed manually onto a touchscreen if the user has one.

Done-with: Shawn Rutledge
Fixes: QTBUG-96702
Pick-to: 6.4
Change-Id: I8dba8b09bed474f585341e9a7a8c71fb60293eee
Reviewed-by: Rami Potinkara <rami.potinkara@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2022-07-21 08:38:44 +03:00

249 lines
6.6 KiB
C++

// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "mandelbrotwidget.h"
#include <QGesture>
#include <QKeyEvent>
#include <QPainter>
#include <math.h>
//! [0]
const double DefaultCenterX = -0.637011;
const double DefaultCenterY = -0.0395159;
const double DefaultScale = 0.00403897;
const double ZoomInFactor = 0.8;
const double ZoomOutFactor = 1 / ZoomInFactor;
const int ScrollStep = 20;
//! [0]
//! [1]
MandelbrotWidget::MandelbrotWidget(QWidget *parent) :
QWidget(parent),
centerX(DefaultCenterX),
centerY(DefaultCenterY),
pixmapScale(DefaultScale),
curScale(DefaultScale)
{
help = tr("Zoom with mouse wheel, +/- keys or pinch. Scroll with arrow keys or by dragging.");
connect(&thread, &RenderThread::renderedImage,
this, &MandelbrotWidget::updatePixmap);
setWindowTitle(tr("Mandelbrot"));
#if QT_CONFIG(cursor)
setCursor(Qt::CrossCursor);
#endif
}
//! [1]
//! [2]
void MandelbrotWidget::paintEvent(QPaintEvent * /* event */)
{
QPainter painter(this);
painter.fillRect(rect(), Qt::black);
if (pixmap.isNull()) {
painter.setPen(Qt::white);
painter.drawText(rect(), Qt::AlignCenter|Qt::TextWordWrap, tr("Rendering initial image, please wait..."));
//! [2] //! [3]
return;
//! [3] //! [4]
}
//! [4]
//! [5]
if (qFuzzyCompare(curScale, pixmapScale)) {
//! [5] //! [6]
painter.drawPixmap(pixmapOffset, pixmap);
//! [6] //! [7]
} else {
//! [7] //! [8]
auto previewPixmap = qFuzzyCompare(pixmap.devicePixelRatio(), qreal(1))
? pixmap
: pixmap.scaled(pixmap.deviceIndependentSize().toSize(), Qt::KeepAspectRatio,
Qt::SmoothTransformation);
double scaleFactor = pixmapScale / curScale;
int newWidth = int(previewPixmap.width() * scaleFactor);
int newHeight = int(previewPixmap.height() * scaleFactor);
int newX = pixmapOffset.x() + (previewPixmap.width() - newWidth) / 2;
int newY = pixmapOffset.y() + (previewPixmap.height() - newHeight) / 2;
painter.save();
painter.translate(newX, newY);
painter.scale(scaleFactor, scaleFactor);
QRectF exposed = painter.transform().inverted().mapRect(rect()).adjusted(-1, -1, 1, 1);
painter.drawPixmap(exposed, previewPixmap, exposed);
painter.restore();
}
//! [8] //! [9]
QFontMetrics metrics = painter.fontMetrics();
if (!info.isEmpty()){
int infoWidth = metrics.horizontalAdvance(info);
int infoHeight = metrics.height();
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(0, 0, 0, 127));
infoHeight = (infoWidth/width()+1) * (infoHeight + 5);
painter.drawRect((width() - infoWidth) / 2 - 5, 0, infoWidth + 10, infoHeight);
painter.setPen(Qt::white);
painter.drawText(rect(), Qt::AlignHCenter|Qt::AlignTop|Qt::TextWordWrap, info);
}
int helpWidth = metrics.horizontalAdvance(help);
int helpHeight = metrics.height();
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(0, 0, 0, 127));
helpHeight = (helpWidth/width()+1) * (helpHeight + 5);
painter.drawRect((width() - helpWidth) / 2 - 5, height()-helpHeight, helpWidth + 10, helpHeight);
painter.setPen(Qt::white);
painter.drawText(rect(), Qt::AlignHCenter|Qt::AlignBottom|Qt::TextWordWrap, help);
}
//! [9]
//! [10]
void MandelbrotWidget::resizeEvent(QResizeEvent * /* event */)
{
thread.render(centerX, centerY, curScale, size(), devicePixelRatio());
}
//! [10]
//! [11]
void MandelbrotWidget::keyPressEvent(QKeyEvent *event)
{
switch (event->key()) {
case Qt::Key_Plus:
zoom(ZoomInFactor);
break;
case Qt::Key_Minus:
zoom(ZoomOutFactor);
break;
case Qt::Key_Left:
scroll(-ScrollStep, 0);
break;
case Qt::Key_Right:
scroll(+ScrollStep, 0);
break;
case Qt::Key_Down:
scroll(0, -ScrollStep);
break;
case Qt::Key_Up:
scroll(0, +ScrollStep);
break;
case Qt::Key_Q:
close();
break;
default:
QWidget::keyPressEvent(event);
}
}
//! [11]
#if QT_CONFIG(wheelevent)
//! [12]
void MandelbrotWidget::wheelEvent(QWheelEvent *event)
{
const int numDegrees = event->angleDelta().y() / 8;
const double numSteps = numDegrees / double(15);
zoom(pow(ZoomInFactor, numSteps));
}
//! [12]
#endif
//! [13]
void MandelbrotWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
lastDragPos = event->position().toPoint();
}
//! [13]
//! [14]
void MandelbrotWidget::mouseMoveEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton) {
pixmapOffset += event->position().toPoint() - lastDragPos;
lastDragPos = event->position().toPoint();
update();
}
}
//! [14]
//! [15]
void MandelbrotWidget::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
pixmapOffset += event->position().toPoint() - lastDragPos;
lastDragPos = QPoint();
const auto pixmapSize = pixmap.deviceIndependentSize().toSize();
int deltaX = (width() - pixmapSize.width()) / 2 - pixmapOffset.x();
int deltaY = (height() - pixmapSize.height()) / 2 - pixmapOffset.y();
scroll(deltaX, deltaY);
}
}
//! [15]
//! [16]
void MandelbrotWidget::updatePixmap(const QImage &image, double scaleFactor)
{
if (!lastDragPos.isNull())
return;
info = image.text(RenderThread::infoKey());
pixmap = QPixmap::fromImage(image);
pixmapOffset = QPoint();
lastDragPos = QPoint();
pixmapScale = scaleFactor;
update();
}
//! [16]
//! [17]
void MandelbrotWidget::zoom(double zoomFactor)
{
curScale *= zoomFactor;
update();
thread.render(centerX, centerY, curScale, size(), devicePixelRatio());
}
//! [17]
//! [18]
void MandelbrotWidget::scroll(int deltaX, int deltaY)
{
centerX += deltaX * curScale;
centerY += deltaY * curScale;
update();
thread.render(centerX, centerY, curScale, size(), devicePixelRatio());
}
//! [18]
//! [gesture1]
#ifndef QT_NO_GESTURES
bool MandelbrotWidget::gestureEvent(QGestureEvent *event)
{
if (auto *pinch = static_cast<QPinchGesture *>(event->gesture(Qt::PinchGesture))) {
if (pinch->changeFlags().testFlag(QPinchGesture::ScaleFactorChanged))
zoom(1.0 / pinch->scaleFactor());
return true;
}
return false;
}
bool MandelbrotWidget::event(QEvent *event)
{
if (event->type() == QEvent::Gesture)
return gestureEvent(static_cast<QGestureEvent*>(event));
return QWidget::event(event);
}
#endif
//! [gesture1]