pineapple-pictures/graphicsview.cpp

296 lines
8.4 KiB
C++
Raw Normal View History

2019-09-28 01:18:08 +08:00
#include "graphicsview.h"
2019-09-29 15:52:35 +08:00
#include "graphicsscene.h"
2019-09-28 01:18:08 +08:00
#include <QDebug>
#include <QMouseEvent>
#include <QScrollBar>
2019-09-29 15:52:35 +08:00
#include <QMimeData>
#include <QImageReader>
2019-09-28 01:18:08 +08:00
GraphicsView::GraphicsView(QWidget *parent)
: QGraphicsView (parent)
{
setDragMode(QGraphicsView::ScrollHandDrag);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
2019-09-29 21:22:20 +08:00
setResizeAnchor(QGraphicsView::AnchorUnderMouse);
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
2019-09-29 22:18:38 +08:00
setStyleSheet("background-color: rgba(0, 0, 0, 220);"
2019-09-28 01:18:08 +08:00
"border-radius: 3px;");
2019-09-29 15:52:35 +08:00
setAcceptDrops(true);
2019-09-30 23:02:44 +08:00
setCheckerboardEnabled(false);
connect(horizontalScrollBar(), &QScrollBar::valueChanged, this, &GraphicsView::viewportRectChanged);
connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &GraphicsView::viewportRectChanged);
2019-09-29 15:52:35 +08:00
}
void GraphicsView::showFromUrlList(const QList<QUrl> &urlList)
{
2019-10-03 17:57:14 +08:00
emit navigatorViewRequired(false, 0);
if (urlList.isEmpty()) {
// yeah, it's possible. dragging QQ's original sticker will trigger this, for example.
showText(tr("File url list is empty"));
return;
}
QUrl url(urlList.first());
QString filePath(url.toLocalFile());
if (filePath.endsWith(".svg")) {
showSvg(filePath);
} else if (filePath.endsWith(".gif")) {
showGif(filePath);
} else {
QImageReader imageReader(filePath);
2019-10-13 23:29:41 +08:00
imageReader.setAutoTransform(true);
2019-10-13 22:12:58 +08:00
imageReader.setDecideFormatFromContent(true);
QImage::Format imageFormat = imageReader.imageFormat();
if (imageFormat == QImage::Format_Invalid) {
showText(tr("File is not a valid image"));
} else {
showImage(QPixmap::fromImageReader(&imageReader));
}
}
}
2019-09-29 15:52:35 +08:00
void GraphicsView::showImage(const QPixmap &pixmap)
{
2019-09-29 21:22:20 +08:00
resetTransform();
2019-09-29 15:52:35 +08:00
scene()->showImage(pixmap);
2019-09-30 23:02:44 +08:00
checkAndDoFitInView();
2019-09-29 15:52:35 +08:00
}
void GraphicsView::showText(const QString &text)
{
2019-09-30 23:02:44 +08:00
resetTransform();
2019-09-29 15:52:35 +08:00
scene()->showText(text);
2019-09-30 23:02:44 +08:00
checkAndDoFitInView();
2019-09-29 15:52:35 +08:00
}
2019-09-29 23:53:29 +08:00
void GraphicsView::showSvg(const QString &filepath)
{
2019-09-30 23:02:44 +08:00
resetTransform();
2019-09-29 23:53:29 +08:00
scene()->showSvg(filepath);
2019-09-30 23:02:44 +08:00
checkAndDoFitInView();
2019-09-29 23:53:29 +08:00
}
2019-09-30 13:11:43 +08:00
void GraphicsView::showGif(const QString &filepath)
{
2019-09-30 23:02:44 +08:00
resetTransform();
2019-09-30 13:11:43 +08:00
scene()->showGif(filepath);
2019-09-30 23:02:44 +08:00
checkAndDoFitInView();
2019-09-30 13:11:43 +08:00
}
2019-09-29 15:52:35 +08:00
GraphicsScene *GraphicsView::scene() const
{
return qobject_cast<GraphicsScene*>(QGraphicsView::scene());
}
void GraphicsView::setScene(GraphicsScene *scene)
{
return QGraphicsView::setScene(scene);
2019-09-28 01:18:08 +08:00
}
2019-10-02 16:04:50 +08:00
qreal GraphicsView::scaleFactor() const
{
int angle = static_cast<int>(m_rotateAngle);
if (angle == 0 || angle == 180) {
return qAbs(transform().m11());
} else {
return qAbs(transform().m12());
}
}
void GraphicsView::resetTransform()
{
m_rotateAngle = 0;
QGraphicsView::resetTransform();
}
void GraphicsView::zoomView(qreal scaleFactor)
{
2019-10-04 09:54:13 +08:00
m_enableFitInView = false;
2019-11-02 13:32:13 +08:00
scale(scaleFactor, scaleFactor);
2019-10-03 17:57:14 +08:00
emit navigatorViewRequired(!isThingSmallerThanWindowWith(transform()), m_rotateAngle);
2019-10-02 16:04:50 +08:00
}
void GraphicsView::resetScale()
{
2019-11-02 13:32:13 +08:00
resetWithScaleAndRotate(1, m_rotateAngle);
2019-10-03 17:57:14 +08:00
emit navigatorViewRequired(!isThingSmallerThanWindowWith(transform()), m_rotateAngle);
2019-10-02 16:04:50 +08:00
}
void GraphicsView::rotateView(qreal rotateAngel)
{
m_rotateAngle += rotateAngel;
m_rotateAngle = static_cast<int>(m_rotateAngle) % 360;
2019-11-02 13:32:13 +08:00
resetWithScaleAndRotate(1, m_rotateAngle);
2019-10-02 16:04:50 +08:00
}
void GraphicsView::fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRadioMode)
{
QGraphicsView::fitInView(rect, aspectRadioMode);
}
2019-10-02 14:31:24 +08:00
void GraphicsView::checkAndDoFitInView()
{
if (!isThingSmallerThanWindowWith(transform())) {
m_enableFitInView = true;
fitInView(sceneRect(), Qt::KeepAspectRatio);
}
}
2019-09-30 23:02:44 +08:00
void GraphicsView::toggleCheckerboard()
{
setCheckerboardEnabled(!m_checkerboardEnabled);
}
2019-09-28 01:18:08 +08:00
void GraphicsView::mousePressEvent(QMouseEvent *event)
{
2019-09-29 21:22:20 +08:00
if (shouldIgnoreMousePressMoveEvent(event)) {
2019-09-28 01:18:08 +08:00
event->ignore();
// blumia: return here, or the QMouseEvent event transparency won't
// work if we set a QGraphicsView::ScrollHandDrag drag mode.
return;
}
return QGraphicsView::mousePressEvent(event);
}
void GraphicsView::mouseMoveEvent(QMouseEvent *event)
{
2019-09-29 21:22:20 +08:00
if (shouldIgnoreMousePressMoveEvent(event)) {
2019-09-28 01:18:08 +08:00
event->ignore();
}
return QGraphicsView::mouseMoveEvent(event);
}
void GraphicsView::mouseReleaseEvent(QMouseEvent *event)
{
QGraphicsItem *item = itemAt(event->pos());
if (!item) {
event->ignore();
}
return QGraphicsView::mouseReleaseEvent(event);
}
void GraphicsView::wheelEvent(QWheelEvent *event)
{
2019-10-04 09:54:13 +08:00
event->ignore();
// blumia: no need for calling parent method.
2019-09-28 01:18:08 +08:00
}
2019-09-29 15:52:35 +08:00
2019-09-29 21:22:20 +08:00
void GraphicsView::resizeEvent(QResizeEvent *event)
{
if (m_enableFitInView) {
2019-10-02 16:04:50 +08:00
QTransform tf;
tf.rotate(m_rotateAngle);
if (isThingSmallerThanWindowWith(tf) && scaleFactor() >= 1) {
2019-09-29 21:22:20 +08:00
// no longer need to do fitInView()
// but we leave the m_enableFitInView value unchanged in case
// user resize down the window again.
} else {
fitInView(sceneRect(), Qt::KeepAspectRatio);
}
2019-10-04 21:34:20 +08:00
} else {
emit navigatorViewRequired(!isThingSmallerThanWindowWith(transform()), m_rotateAngle);
2019-09-29 21:22:20 +08:00
}
return QGraphicsView::resizeEvent(event);
}
2019-09-29 15:52:35 +08:00
void GraphicsView::dragEnterEvent(QDragEnterEvent *event)
{
if (event->mimeData()->hasUrls() || event->mimeData()->hasImage() || event->mimeData()->hasText()) {
event->acceptProposedAction();
} else {
event->ignore();
}
qDebug() << event->mimeData() << "Drag Enter Event"
<< event->mimeData()->hasUrls() << event->mimeData()->hasImage()
<< event->mimeData()->formats() << event->mimeData()->hasFormat("text/uri-list");
return QGraphicsView::dragEnterEvent(event);
}
void GraphicsView::dragMoveEvent(QDragMoveEvent *event)
{
Q_UNUSED(event)
2019-09-29 15:52:35 +08:00
// by default, QGraphicsView/Scene will ignore the action if there are no QGraphicsItem under cursor.
// We actually doesn't care and would like to keep the drag event as-is, so just do nothing here.
}
void GraphicsView::dropEvent(QDropEvent *event)
{
event->acceptProposedAction();
const QMimeData * mimeData = event->mimeData();
if (mimeData->hasUrls()) {
showFromUrlList(mimeData->urls());
2019-09-29 15:52:35 +08:00
} else if (mimeData->hasImage()) {
QImage img = qvariant_cast<QImage>(mimeData->imageData());
QPixmap pixmap = QPixmap::fromImage(img);
if (pixmap.isNull()) {
showText(tr("Image data is invalid"));
2019-09-29 15:52:35 +08:00
} else {
showImage(pixmap);
}
} else if (mimeData->hasText()) {
showText(mimeData->text());
2019-09-29 21:22:20 +08:00
} else {
showText(tr("Not supported mimedata: %1").arg(mimeData->formats().first()));
2019-09-29 15:52:35 +08:00
}
}
2019-09-29 21:22:20 +08:00
bool GraphicsView::isThingSmallerThanWindowWith(const QTransform &transform) const
2019-10-02 16:04:50 +08:00
{
2019-09-29 21:22:20 +08:00
return rect().size().expandedTo(transform.mapRect(sceneRect()).size().toSize())
== rect().size();
}
bool GraphicsView::shouldIgnoreMousePressMoveEvent(const QMouseEvent *event) const
{
2019-10-02 14:31:24 +08:00
if (event->buttons() == Qt::NoButton) {
2019-09-29 21:22:20 +08:00
return true;
}
QGraphicsItem *item = itemAt(event->pos());
if (!item) {
return true;
}
2019-10-02 14:31:24 +08:00
if (isThingSmallerThanWindowWith(transform())) {
return true;
2019-09-30 23:02:44 +08:00
}
2019-10-02 14:31:24 +08:00
return false;
2019-09-30 23:02:44 +08:00
}
void GraphicsView::setCheckerboardEnabled(bool enabled)
{
m_checkerboardEnabled = enabled;
if (m_checkerboardEnabled) {
// Prepare background check-board pattern
QPixmap tilePixmap(0x20, 0x20);
2019-10-02 14:31:24 +08:00
tilePixmap.fill(QColor(35, 35, 35, 110));
2019-09-30 23:02:44 +08:00
QPainter tilePainter(&tilePixmap);
2019-10-02 14:31:24 +08:00
QColor color(40, 40, 40, 110);
2019-09-30 23:02:44 +08:00
tilePainter.fillRect(0, 0, 0x10, 0x10, color);
tilePainter.fillRect(0x10, 0x10, 0x10, 0x10, color);
tilePainter.end();
setBackgroundBrush(tilePixmap);
} else {
setBackgroundBrush(Qt::transparent);
}
}
2019-10-02 16:04:50 +08:00
2019-11-02 13:32:13 +08:00
void GraphicsView::resetWithScaleAndRotate(qreal scaleFactor, qreal rotateAngle)
2019-10-02 16:04:50 +08:00
{
QGraphicsView::resetTransform();
2019-11-02 13:32:13 +08:00
scale(scaleFactor, scaleFactor);
rotate(rotateAngle);
2019-10-02 16:04:50 +08:00
}