Commit b085b8ce authored by Michael Winkelmann's avatar Michael Winkelmann

First commit

parents
Pipeline #1222 canceled with stages
Photo.jpg

1.63 MB

QT += quick
CONFIG += c++11
# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
viewercontainer.cpp
RESOURCES += qml.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
HEADERS += \
viewercontainer.h
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "viewercontainer.h"
int main(int argc, char *argv[])
{
if (qEnvironmentVariableIsEmpty("QTGLESSTREAM_DISPLAY")) {
qputenv("QT_QPA_EGLFS_PHYSICAL_WIDTH", QByteArray("213"));
qputenv("QT_QPA_EGLFS_PHYSICAL_HEIGHT", QByteArray("120"));
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
}
QGuiApplication app(argc, argv);
qmlRegisterType<ViewerContainer>("ViewerContainer",1,0,"ViewerContainer");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
import QtQuick 2.12
import QtQuick.Window 2.12
import ViewerContainer 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ViewerContainer {
anchors.fill: parent
contentItem: Image {
source: "qrc:///Photo.jpg"
//width: implicitWidth()/5
fillMode: Image.PreserveAspectFit
}
}
}
<RCC>
<qresource prefix="/">
<file>main.qml</file>
<file>Photo.jpg</file>
</qresource>
</RCC>
#include "viewercontainer.h"
#include <QDebug>
ViewerContainer::ViewerContainer(QQuickItem* parent) :
QQuickItem(parent)
{
setFlag(QQuickItem::ItemHasContents);
setAcceptedMouseButtons(Qt::LeftButton);
}
qreal ViewerContainer::zoomLevel() const {
return contentItem_ ? contentItem_->scale() : 0.0;
}
qreal ViewerContainer::maxZoomLevel() const {
return maxZoomLevel_;
}
void ViewerContainer::setMaxZoomLevel(qreal maxZoomLevel) {
if (qFloatDistance(maxZoomLevel,maxZoomLevel_) > 0) {
maxZoomLevel_ = maxZoomLevel;
zoom(zoomLevel());
emit maxZoomLevelChanged();
}
}
qreal ViewerContainer::minZoomLevel() const {
return minZoomLevel_;
}
void ViewerContainer::setMinZoomLevel(qreal minZoomLevel) {
if (qFloatDistance(minZoomLevel,minZoomLevel_) > 0) {
minZoomLevel_ = minZoomLevel;
zoom(zoomLevel());
emit minZoomLevelChanged();
}
}
bool ViewerContainer::keepInBounds() const {
return keepInBounds_;
}
void ViewerContainer::setKeepInBounds(bool keepInBounds) {
if (keepInBounds != keepInBounds_) {
keepInBounds_ = keepInBounds;
emit keepInBoundsChanged();
}
}
QQuickItem* ViewerContainer::contentItem() const {
return contentItem_.data();
}
void ViewerContainer::setContentItem(QQuickItem* contentItem) {
if (contentItem_ != contentItem) {
if (contentItem_)
contentItem_->deleteLater();
contentItem_ = contentItem;
contentItem_->setParentItem(this);
contentItem_->setTransformOrigin(QQuickItem::TopLeft);
center();
update();
emit contentItemChanged();
}
}
QRectF ViewerContainer::contentItemGeometry() const {
if (!contentItem_)
return QRectF();
QPointF topLeft(contentItem_->mapToItem(this,QPointF(0,0)));
QPointF bottomRight(contentItem_->mapToItem(this,QPointF(contentItem_->width(),contentItem_->height())));
return { topLeft, bottomRight };
}
void ViewerContainer::zoom(qreal zoomLevel) {
QPointF c(contentItem_->width()/2,contentItem_->height()/2);
zoomAtPosition(contentItem_->mapToItem(this,c),zoomLevel);
}
void ViewerContainer::zoomAtPosition(QPointF const& pos, qreal zoomLevel) {
if (!contentItem_)
return;
zoomLevel = qBound(minZoomLevel_,zoomLevel,maxZoomLevel_);
if (qFloatDistance(zoomLevel,this->zoomLevel()) > 0) {
auto oldPos = mapToItem(contentItem(), pos);
contentItem_->setScale(zoomLevel);
auto newPos = mapToItem(contentItem(), pos);
auto dx = contentItem_->scale() * (newPos.x() - oldPos.x());
auto dy = contentItem_->scale() * (newPos.y() - oldPos.y());
translate(QPointF(dx,dy));
}
}
void ViewerContainer::translate(QPointF const& delta) {
if (!contentItem_)
return;
auto geom = contentItemGeometry();
auto newX = contentItem_->x() + delta.x();
auto newY = contentItem_->y() + delta.y();
if (keepInBounds()) {
if (geom.width() <= width())
newX = qBound(0.0,newX,width() - geom.width());
else
newX = qBound(width() - geom.width(),newX,0.0);
if (geom.height() <= height())
newY = qBound(0.0,newY,height() - geom.height());
else
newY = qBound(height() - geom.height(),newY,0.0);
}
bool changed = false;
if (qFloatDistance(contentItem_->x(),newX) > 0) {
contentItem_->setX(newX);
changed = true;
}
if (qFloatDistance(contentItem_->y(),newY) > 0) {
contentItem_->setY(newY);
changed = true;
}
if (changed)
emit contentItemGeometryChanged();
}
void ViewerContainer::zoomToFit() {
center();
auto w = std::max(width(),height());
auto h = std::min(width(),height());
if (width() < height())
std::swap(w,h);
zoom(std::min(w/contentItem_->width(),h/contentItem_->height()));
}
void ViewerContainer::center() {
if (!contentItem_)
return;
lastPos_ = QPointF(-1,-1);
contentItem_->setX((width() - zoomLevel()*contentItem_->width())/2);
contentItem_->setY((height() - zoomLevel()*contentItem_->height())/2);
}
void ViewerContainer::mousePressEvent(QMouseEvent* event) {
if (!contentItem_)
return;
if (event->button() == Qt::LeftButton) {
lastPos_ = event->pos();
}
}
void ViewerContainer::wheelEvent(QWheelEvent* event) {
if (!contentItem_)
return;
zoomAtPosition(event->posF(),zoomLevel() + zoomLevel() * event->angleDelta().y() / 1000.0);
}
void ViewerContainer::mouseMoveEvent(QMouseEvent* event) {
if (!contentItem_)
return;
if (lastPos_ != QPointF(-1,-1)) {
translate(event->pos() - lastPos_);
lastPos_ = event->pos();
}
}
void ViewerContainer::mouseReleaseEvent(QMouseEvent*) {
if (!contentItem_)
return;
lastPos_ = QPointF(-1,-1);
}
void ViewerContainer::touchEvent(QTouchEvent *event) {
auto& touchPoints = event->touchPoints();
if (touchPoints.size() == 1) {
auto p = touchPoints[0];
if (p.state() == Qt::TouchPointMoved) {
translate(p.pos() - p.lastPos());
return;
}
}
if (touchPoints.size() == 2) {
auto p1 = touchPoints[0];
auto p2 = touchPoints[1];
auto oldCenter = 0.5 * (p1.lastPos() + p2.lastPos());
auto newCenter = 0.5 * (p1.pos() + p2.pos());
auto l = QVector2D(p1.lastNormalizedPos() - p2.lastNormalizedPos()).length();
auto nL = QVector2D(p1.normalizedPos() - p2.normalizedPos()).length();
zoomAtPosition(newCenter,zoomLevel() * qreal(nL)/qreal(l));
translate(newCenter - oldCenter);
}
}
#ifndef VIEWERCONTAINER_H
#define VIEWERCONTAINER_H
#include <QQuickItem>
#include <QPointer>
class ViewerContainer : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(qreal zoomLevel READ zoomLevel NOTIFY zoomLevelChanged)
Q_PROPERTY(qreal maxZoomLevel READ maxZoomLevel WRITE setMaxZoomLevel NOTIFY maxZoomLevelChanged)
Q_PROPERTY(qreal minZoomLevel READ minZoomLevel WRITE setMinZoomLevel NOTIFY minZoomLevelChanged)
Q_PROPERTY(bool keepInBounds READ keepInBounds WRITE setKeepInBounds NOTIFY keepInBoundsChanged)
Q_PROPERTY(QQuickItem* contentItem READ contentItem WRITE setContentItem NOTIFY contentItemChanged)
Q_PROPERTY(QRectF contentItemGeometry READ contentItemGeometry NOTIFY contentItemGeometryChanged)
public:
ViewerContainer(QQuickItem* parent = nullptr);
qreal zoomLevel() const;
qreal maxZoomLevel() const;
void setMaxZoomLevel(qreal);
qreal minZoomLevel() const;
void setMinZoomLevel(qreal);
bool keepInBounds() const;
void setKeepInBounds(bool);
QQuickItem* contentItem() const;
void setContentItem(QQuickItem* contentItem);
QRectF contentItemGeometry() const;
signals:
void contentItemChanged();
void zoomLevelChanged();
void minZoomLevelChanged();
void maxZoomLevelChanged();
void keepInBoundsChanged();
void contentItemGeometryChanged();
public slots:
void zoom(qreal zoomLevel);
void zoomAtPosition(QPointF const&, qreal zoomLevel);
void translate(QPointF const& delta);
void zoomToFit();
void center();
protected:
void mousePressEvent(QMouseEvent*) override;
void wheelEvent(QWheelEvent*) override;
void mouseMoveEvent(QMouseEvent*) override;
void mouseReleaseEvent(QMouseEvent*) override;
void touchEvent(QTouchEvent *event) override;
private:
qreal minZoomLevel_ = 0.01;
qreal maxZoomLevel_ = 10.0;
bool keepInBounds_ = true;
QPointF lastPos_ = QPointF(-1,-1);
QPointer<QQuickItem> contentItem_;
};
#endif // VIEWERCONTAINER_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment