diff --git a/lib/documentview/abstractdocumentviewadapter.h b/lib/documentview/abstractdocumentviewadapter.h --- a/lib/documentview/abstractdocumentviewadapter.h +++ b/lib/documentview/abstractdocumentviewadapter.h @@ -39,7 +39,7 @@ namespace Gwenview { -class ImageView; +class AbstractImageView; class RasterImageView; /** @@ -60,7 +60,7 @@ virtual MimeTypeUtils::Kind kind() const = 0; - virtual ImageView* imageView() const + virtual AbstractImageView* imageView() const { return 0; } diff --git a/lib/documentview/abstractimageview.h b/lib/documentview/abstractimageview.h --- a/lib/documentview/abstractimageview.h +++ b/lib/documentview/abstractimageview.h @@ -105,6 +105,8 @@ void applyPendingScrollPos(); + void resetDragCursor(); + public Q_SLOTS: void updateCursor(); diff --git a/lib/documentview/abstractimageview.cpp b/lib/documentview/abstractimageview.cpp --- a/lib/documentview/abstractimageview.cpp +++ b/lib/documentview/abstractimageview.cpp @@ -596,4 +596,10 @@ d->adjustScrollPos(); } +void AbstractImageView::resetDragCursor() +{ + d->mLastDragPos = QPointF(); + updateCursor(); +} + } // namespace diff --git a/lib/documentview/documentview.h b/lib/documentview/documentview.h --- a/lib/documentview/documentview.h +++ b/lib/documentview/documentview.h @@ -228,6 +228,9 @@ void slotFadeInFinished(); + void dragThumbnailLoaded(const KFileItem&, const QPixmap&); + void dragThumbnailLoadingFailed(const KFileItem&); + private: friend struct DocumentViewPrivate; DocumentViewPrivate* const d; diff --git a/lib/documentview/documentview.cpp b/lib/documentview/documentview.cpp --- a/lib/documentview/documentview.cpp +++ b/lib/documentview/documentview.cpp @@ -38,10 +38,13 @@ #include #include #include +#include #include +#include // KDE #include +#include // Local #include @@ -62,6 +65,8 @@ #include #include #include +#include +#include namespace Gwenview { @@ -105,6 +110,10 @@ bool mCompareMode; int controlWheelAccumulatedDelta; + QPointF mDragStartPosition; + QPointer mDragThumbnailProvider; + QPointer mDrag; + void setCurrentAdapter(AbstractDocumentViewAdapter* adapter) { Q_ASSERT(adapter); @@ -338,6 +347,75 @@ q->isAnimatedChanged(); anim->start(QAbstractAnimation::DeleteWhenStopped); } + + bool canPan() const + { + if (!q->canZoom()) { + return false; + } + + const QSize zoomedImageSize = mDocument->size() * q->zoom(); + const QSize viewPortSize = q->boundingRect().size().toSize(); + const bool imageWiderThanViewport = zoomedImageSize.width() > viewPortSize.width(); + const bool imageTallerThanViewport = zoomedImageSize.height() > viewPortSize.height(); + return (imageWiderThanViewport || imageTallerThanViewport); + } + + void setDragPixmap(const QPixmap& pix) + { + if (mDrag) { + DragPixmapGenerator::DragPixmap dragPixmap = DragPixmapGenerator::generate({pix}, 1); + mDrag->setPixmap(dragPixmap.pix); + mDrag->setHotSpot(dragPixmap.hotSpot); + } + } + + void executeDrag() + { + if (mDrag) { + mDrag->exec(); + if (mAdapter->imageView()) { + mAdapter->imageView()->resetDragCursor(); + } + } + } + + void initDragThumbnailProvider() { + mDragThumbnailProvider = new ThumbnailProvider(); + QObject::connect(mDragThumbnailProvider, &ThumbnailProvider::thumbnailLoaded, + q, &DocumentView::dragThumbnailLoaded); + QObject::connect(mDragThumbnailProvider, &ThumbnailProvider::thumbnailLoadingFailed, + q, &DocumentView::dragThumbnailLoadingFailed); + } + + void startDragIfSensible() + { + if (q->document()->loadingState() == Document::LoadingFailed) { + return; + } + + if (q->currentTool()) { + return; + } + + if (mDrag) { + mDrag->deleteLater(); + } + mDrag = new QDrag(q); + const auto itemList = KFileItemList({q->document()->url()}); + mDrag->setMimeData(MimeTypeUtils::selectionMimeData(itemList)); + + if (q->document()->isModified()) { + setDragPixmap(QPixmap::fromImage(q->document()->image())); + executeDrag(); + } else { + // Drag is triggered on success or failure of thumbnail generation + if (mDragThumbnailProvider.isNull()) { + initDragThumbnailProvider(); + } + mDragThumbnailProvider->appendItems(itemList); + } + } }; DocumentView::DocumentView(QGraphicsScene* scene) @@ -353,6 +431,8 @@ d->mCurrent = false; d->mCompareMode = false; d->controlWheelAccumulatedDelta = 0; + d->mDragStartPosition = QPointF(0, 0); + d->mDrag = nullptr; // We use an opacity effect instead of using the opacity property directly, because the latter operates at // the painter level, which means if you draw multiple layers in paint(), all layers get the specified @@ -373,6 +453,8 @@ DocumentView::~DocumentView() { + delete d->mDragThumbnailProvider; + delete d->mDrag; delete d; } @@ -796,11 +878,22 @@ bool DocumentView::sceneEventFilter(QGraphicsItem*, QEvent* event) { if (event->type() == QEvent::GraphicsSceneMousePress) { + const QGraphicsSceneMouseEvent* mouseEvent = static_cast(event); + if (mouseEvent->button() == Qt::LeftButton) { + d->mDragStartPosition = mouseEvent->pos(); + } QMetaObject::invokeMethod(this, "emitFocused", Qt::QueuedConnection); } else if (event->type() == QEvent::GraphicsSceneHoverMove) { if (d->mBirdEyeView) { d->mBirdEyeView->onMouseMoved(); } + } else if (event->type() == QEvent::GraphicsSceneMouseMove) { + const QGraphicsSceneMouseEvent* mouseEvent = static_cast(event); + const qreal dragDistance = (mouseEvent->pos() - d->mDragStartPosition).manhattanLength(); + const qreal minDistanceToStartDrag = QGuiApplication::styleHints()->startDragDistance(); + if (!d->canPan() && dragDistance >= minDistanceToStartDrag) { + d->startDragIfSensible(); + } } return false; } @@ -849,4 +942,17 @@ } } +void DocumentView::dragThumbnailLoaded(const KFileItem& item, const QPixmap& pix) +{ + d->setDragPixmap(pix); + d->executeDrag(); + d->mDragThumbnailProvider->removeItems(KFileItemList({item})); +} + +void DocumentView::dragThumbnailLoadingFailed(const KFileItem& item) +{ + d->executeDrag(); + d->mDragThumbnailProvider->removeItems(KFileItemList({item})); +} + } // namespace diff --git a/lib/documentview/rasterimageviewadapter.h b/lib/documentview/rasterimageviewadapter.h --- a/lib/documentview/rasterimageviewadapter.h +++ b/lib/documentview/rasterimageviewadapter.h @@ -78,6 +78,7 @@ virtual void loadConfig() Q_DECL_OVERRIDE; virtual RasterImageView* rasterImageView() const Q_DECL_OVERRIDE; + virtual AbstractImageView* imageView() const override; virtual QPointF scrollPos() const Q_DECL_OVERRIDE; virtual void setScrollPos(const QPointF& pos) Q_DECL_OVERRIDE; diff --git a/lib/documentview/rasterimageviewadapter.cpp b/lib/documentview/rasterimageviewadapter.cpp --- a/lib/documentview/rasterimageviewadapter.cpp +++ b/lib/documentview/rasterimageviewadapter.cpp @@ -147,6 +147,11 @@ return d->mView; } +AbstractImageView* RasterImageViewAdapter::imageView() const +{ + return d->mView; +} + QPointF RasterImageViewAdapter::scrollPos() const { return d->mView->scrollPos(); diff --git a/lib/documentview/svgviewadapter.h b/lib/documentview/svgviewadapter.h --- a/lib/documentview/svgviewadapter.h +++ b/lib/documentview/svgviewadapter.h @@ -114,6 +114,8 @@ virtual QRectF visibleDocumentRect() const override; + virtual AbstractImageView* imageView() const override; + private: SvgViewAdapterPrivate* const d; }; diff --git a/lib/documentview/svgviewadapter.cpp b/lib/documentview/svgviewadapter.cpp --- a/lib/documentview/svgviewadapter.cpp +++ b/lib/documentview/svgviewadapter.cpp @@ -261,4 +261,9 @@ return QRectF(d->mView->imageOffset(), d->mView->visibleImageSize()); } +AbstractImageView* SvgViewAdapter::imageView() const +{ + return d->mView; +} + } // namespace