Changeset View
Standalone View
lib/documentview/documentview.cpp
Show All 32 Lines | |||||
33 | #include <QGraphicsSceneWheelEvent> | 33 | #include <QGraphicsSceneWheelEvent> | ||
34 | #include <QGraphicsOpacityEffect> | 34 | #include <QGraphicsOpacityEffect> | ||
35 | #include <QPainter> | 35 | #include <QPainter> | ||
36 | #include <QPropertyAnimation> | 36 | #include <QPropertyAnimation> | ||
37 | #include <QPointer> | 37 | #include <QPointer> | ||
38 | #include <QDebug> | 38 | #include <QDebug> | ||
39 | #include <QIcon> | 39 | #include <QIcon> | ||
40 | #include <QUrl> | 40 | #include <QUrl> | ||
41 | #include <QDrag> | ||||
41 | #include <QMimeData> | 42 | #include <QMimeData> | ||
43 | #include <QStyleHints> | ||||
42 | 44 | | |||
43 | // KDE | 45 | // KDE | ||
44 | #include <KLocalizedString> | 46 | #include <KLocalizedString> | ||
47 | #include <KFileItem> | ||||
45 | 48 | | |||
46 | // Local | 49 | // Local | ||
47 | #include <lib/document/document.h> | 50 | #include <lib/document/document.h> | ||
48 | #include <lib/document/documentfactory.h> | 51 | #include <lib/document/documentfactory.h> | ||
49 | #include <lib/documentview/abstractrasterimageviewtool.h> | 52 | #include <lib/documentview/abstractrasterimageviewtool.h> | ||
50 | #include <lib/documentview/birdeyeview.h> | 53 | #include <lib/documentview/birdeyeview.h> | ||
51 | #include <lib/documentview/loadingindicator.h> | 54 | #include <lib/documentview/loadingindicator.h> | ||
52 | #include <lib/documentview/messageviewadapter.h> | 55 | #include <lib/documentview/messageviewadapter.h> | ||
53 | #include <lib/documentview/rasterimageview.h> | 56 | #include <lib/documentview/rasterimageview.h> | ||
54 | #include <lib/documentview/rasterimageviewadapter.h> | 57 | #include <lib/documentview/rasterimageviewadapter.h> | ||
55 | #include <lib/documentview/svgviewadapter.h> | 58 | #include <lib/documentview/svgviewadapter.h> | ||
56 | #include <lib/documentview/videoviewadapter.h> | 59 | #include <lib/documentview/videoviewadapter.h> | ||
57 | #include <lib/hud/hudbutton.h> | 60 | #include <lib/hud/hudbutton.h> | ||
58 | #include <lib/hud/hudwidget.h> | 61 | #include <lib/hud/hudwidget.h> | ||
59 | #include <lib/graphicswidgetfloater.h> | 62 | #include <lib/graphicswidgetfloater.h> | ||
60 | #include <lib/gvdebug.h> | 63 | #include <lib/gvdebug.h> | ||
61 | #include <lib/gwenviewconfig.h> | 64 | #include <lib/gwenviewconfig.h> | ||
62 | #include <lib/mimetypeutils.h> | 65 | #include <lib/mimetypeutils.h> | ||
63 | #include <lib/signalblocker.h> | 66 | #include <lib/signalblocker.h> | ||
64 | #include <lib/urlutils.h> | 67 | #include <lib/urlutils.h> | ||
68 | #include <lib/thumbnailview/dragpixmapgenerator.h> | ||||
69 | #include <lib/thumbnailprovider/thumbnailprovider.h> | ||||
65 | 70 | | |||
66 | namespace Gwenview | 71 | namespace Gwenview | ||
67 | { | 72 | { | ||
68 | 73 | | |||
69 | #undef ENABLE_LOG | 74 | #undef ENABLE_LOG | ||
70 | #undef LOG | 75 | #undef LOG | ||
71 | //#define ENABLE_LOG | 76 | //#define ENABLE_LOG | ||
72 | #ifdef ENABLE_LOG | 77 | #ifdef ENABLE_LOG | ||
Show All 27 Lines | 94 | { | |||
100 | QScopedPointer<AbstractDocumentViewAdapter> mAdapter; | 105 | QScopedPointer<AbstractDocumentViewAdapter> mAdapter; | ||
101 | QList<qreal> mZoomSnapValues; | 106 | QList<qreal> mZoomSnapValues; | ||
102 | Document::Ptr mDocument; | 107 | Document::Ptr mDocument; | ||
103 | DocumentView::Setup mSetup; | 108 | DocumentView::Setup mSetup; | ||
104 | bool mCurrent; | 109 | bool mCurrent; | ||
105 | bool mCompareMode; | 110 | bool mCompareMode; | ||
106 | int controlWheelAccumulatedDelta; | 111 | int controlWheelAccumulatedDelta; | ||
107 | 112 | | |||
113 | QPointF mDragStartPosition; | ||||
114 | QPointer<ThumbnailProvider> mDragThumbnailProvider; | ||||
115 | QPointer<QDrag> mDrag; | ||||
116 | | ||||
108 | void setCurrentAdapter(AbstractDocumentViewAdapter* adapter) | 117 | void setCurrentAdapter(AbstractDocumentViewAdapter* adapter) | ||
109 | { | 118 | { | ||
110 | Q_ASSERT(adapter); | 119 | Q_ASSERT(adapter); | ||
111 | mAdapter.reset(adapter); | 120 | mAdapter.reset(adapter); | ||
112 | 121 | | |||
113 | adapter->widget()->setParentItem(q); | 122 | adapter->widget()->setParentItem(q); | ||
114 | resizeAdapterWidget(); | 123 | resizeAdapterWidget(); | ||
115 | 124 | | |||
▲ Show 20 Lines • Show All 217 Lines • ▼ Show 20 Line(s) | 341 | QObject::connect(anim, SIGNAL(finished()), | |||
333 | q, SLOT(slotFadeInFinished())); | 342 | q, SLOT(slotFadeInFinished())); | ||
334 | } | 343 | } | ||
335 | QObject::connect(anim, SIGNAL(finished()), q, SIGNAL(isAnimatedChanged())); | 344 | QObject::connect(anim, SIGNAL(finished()), q, SIGNAL(isAnimatedChanged())); | ||
336 | anim->setDuration(DocumentView::AnimDuration); | 345 | anim->setDuration(DocumentView::AnimDuration); | ||
337 | mFadeAnimation = anim; | 346 | mFadeAnimation = anim; | ||
338 | q->isAnimatedChanged(); | 347 | q->isAnimatedChanged(); | ||
339 | anim->start(QAbstractAnimation::DeleteWhenStopped); | 348 | anim->start(QAbstractAnimation::DeleteWhenStopped); | ||
340 | } | 349 | } | ||
350 | | ||||
351 | bool canPan() const | ||||
352 | { | ||||
353 | if (!q->canZoom()) { | ||||
354 | return false; | ||||
355 | } | ||||
356 | | ||||
357 | const QSize zoomedImageSize = mDocument->size() * q->zoom(); | ||||
358 | const QSize viewPortSize = q->boundingRect().size().toSize(); | ||||
359 | const bool imageWiderThanViewport = zoomedImageSize.width() > viewPortSize.width(); | ||||
360 | const bool imageTallerThanViewport = zoomedImageSize.height() > viewPortSize.height(); | ||||
361 | return (imageWiderThanViewport || imageTallerThanViewport); | ||||
362 | } | ||||
363 | | ||||
364 | void setDragPixmap(const QPixmap& pix) | ||||
365 | { | ||||
366 | if (mDrag) { | ||||
367 | DragPixmapGenerator::DragPixmap dragPixmap = DragPixmapGenerator::generate({pix}, 1); | ||||
368 | mDrag->setPixmap(dragPixmap.pix); | ||||
369 | mDrag->setHotSpot(dragPixmap.hotSpot); | ||||
370 | } | ||||
371 | } | ||||
372 | | ||||
373 | void executeDrag() | ||||
374 | { | ||||
375 | if (mDrag) { | ||||
376 | mDrag->exec(); | ||||
huoni: Not the most elegant solution to the cursor issue, but it's not too bad. | |||||
377 | if (mAdapter->imageView()) { | ||||
378 | mAdapter->imageView()->resetDragCursor(); | ||||
379 | } | ||||
380 | } | ||||
381 | } | ||||
382 | | ||||
383 | void initDragThumbnailProvider() { | ||||
384 | mDragThumbnailProvider = new ThumbnailProvider(); | ||||
385 | QObject::connect(mDragThumbnailProvider, &ThumbnailProvider::thumbnailLoaded, | ||||
386 | q, &DocumentView::dragThumbnailLoaded); | ||||
387 | QObject::connect(mDragThumbnailProvider, &ThumbnailProvider::thumbnailLoadingFailed, | ||||
388 | q, &DocumentView::dragThumbnailLoadingFailed); | ||||
389 | } | ||||
390 | | ||||
Not entirely sure if this is necessary, but from what I've read, the object referenced isn't destroyed when the point changes. huoni: Not entirely sure if this is necessary, but from what I've read, the object referenced isn't… | |||||
Not sure how to trigger this, but probably makes sense. However, why not delete mDrag? I fear that deleteLater() might execute after you already set the new object. rkflx: Not sure how to trigger this, but probably makes sense. However, why not `delete mDrag`? I fear… | |||||
delete is blocking I believe, I didn't want to delay the drag. I don't think deleteLater is a problem with QPointer, the new drag object will replace the old one, and the old one will get deleted, ...later. huoni: `delete` is blocking I believe, I didn't want to delay the drag. I don't think `deleteLater` is… | |||||
Ah, of course, I think I got confused by the (possibly incorrect) assumption that there can only be a single drag object at any point in time. deleteLater is probably fine here, although it would be interesting to measure whether posting to the event loop is really faster than simply calling delete (not that it would make any difference to the user here). rkflx: Ah, of course, I think I got confused by the (possibly incorrect) assumption that there can… | |||||
391 | void startDragIfSensible() | ||||
392 | { | ||||
393 | if (q->document()->loadingState() == Document::LoadingFailed) { | ||||
394 | return; | ||||
395 | } | ||||
Found a much more concise way to express this: const auto itemList = KFileItemList({q->document()->url()}); rkflx: Found a much more concise way to express this:
const auto itemList = KFileItemList({q… | |||||
huoni: Nice one. | |||||
396 | | ||||
397 | if (q->currentTool()) { | ||||
398 | return; | ||||
399 | } | ||||
400 | | ||||
401 | if (mDrag) { | ||||
402 | mDrag->deleteLater(); | ||||
403 | } | ||||
404 | mDrag = new QDrag(q); | ||||
405 | const auto itemList = KFileItemList({q->document()->url()}); | ||||
406 | mDrag->setMimeData(MimeTypeUtils::selectionMimeData(itemList)); | ||||
rkflx: ↑ Nothing to see here, keep reading ;) | |||||
407 | | ||||
408 | if (q->document()->isModified()) { | ||||
409 | setDragPixmap(QPixmap::fromImage(q->document()->image())); | ||||
410 | executeDrag(); | ||||
411 | } else { | ||||
412 | // Drag is triggered on success or failure of thumbnail generation | ||||
413 | if (mDragThumbnailProvider.isNull()) { | ||||
414 | initDragThumbnailProvider(); | ||||
415 | } | ||||
416 | mDragThumbnailProvider->appendItems(itemList); | ||||
417 | } | ||||
418 | } | ||||
341 | }; | 419 | }; | ||
342 | 420 | | |||
343 | DocumentView::DocumentView(QGraphicsScene* scene) | 421 | DocumentView::DocumentView(QGraphicsScene* scene) | ||
344 | : d(new DocumentViewPrivate) | 422 | : d(new DocumentViewPrivate) | ||
345 | { | 423 | { | ||
346 | setFlag(ItemIsFocusable); | 424 | setFlag(ItemIsFocusable); | ||
347 | setFlag(ItemIsSelectable); | 425 | setFlag(ItemIsSelectable); | ||
348 | setFlag(ItemClipsChildrenToShape); | 426 | setFlag(ItemClipsChildrenToShape); | ||
349 | 427 | | |||
350 | d->q = this; | 428 | d->q = this; | ||
351 | d->mLoadingIndicator = 0; | 429 | d->mLoadingIndicator = 0; | ||
352 | d->mBirdEyeView = 0; | 430 | d->mBirdEyeView = 0; | ||
353 | d->mCurrent = false; | 431 | d->mCurrent = false; | ||
354 | d->mCompareMode = false; | 432 | d->mCompareMode = false; | ||
355 | d->controlWheelAccumulatedDelta = 0; | 433 | d->controlWheelAccumulatedDelta = 0; | ||
434 | d->mDragStartPosition = QPointF(0, 0); | ||||
435 | d->mDrag = nullptr; | ||||
356 | 436 | | |||
357 | // We use an opacity effect instead of using the opacity property directly, because the latter operates at | 437 | // We use an opacity effect instead of using the opacity property directly, because the latter operates at | ||
358 | // the painter level, which means if you draw multiple layers in paint(), all layers get the specified | 438 | // the painter level, which means if you draw multiple layers in paint(), all layers get the specified | ||
359 | // opacity, resulting in all layers being visible when 0 < opacity < 1. | 439 | // opacity, resulting in all layers being visible when 0 < opacity < 1. | ||
360 | // QGraphicsEffects on the other hand, operate after all painting is done, therefore 'flattening' all layers. | 440 | // QGraphicsEffects on the other hand, operate after all painting is done, therefore 'flattening' all layers. | ||
361 | // This is important for fade effects, where we don't want any background layers visible during the fade. | 441 | // This is important for fade effects, where we don't want any background layers visible during the fade. | ||
362 | d->mOpacityEffect = new QGraphicsOpacityEffect(this); | 442 | d->mOpacityEffect = new QGraphicsOpacityEffect(this); | ||
363 | d->mOpacityEffect->setOpacity(0); | 443 | d->mOpacityEffect->setOpacity(0); | ||
I felt having a single ThumbnailProvider was better than repeatedly creating and destroying one. However, the downside is we always create one even if the user doesn't drag. huoni: I felt having a single `ThumbnailProvider` was better than repeatedly creating and destroying… | |||||
Why not combine the advantages of both options, i.e. have a single object, and create it only on demand? :D rkflx: Why not combine the advantages of both options, i.e. have a single object, and create it only… | |||||
364 | setGraphicsEffect(d->mOpacityEffect); | 444 | setGraphicsEffect(d->mOpacityEffect); | ||
365 | 445 | | |||
366 | scene->addItem(this); | 446 | scene->addItem(this); | ||
367 | 447 | | |||
368 | d->setupHud(); | 448 | d->setupHud(); | ||
369 | d->setCurrentAdapter(new EmptyAdapter); | 449 | d->setCurrentAdapter(new EmptyAdapter); | ||
370 | 450 | | |||
371 | setAcceptDrops(true); | 451 | setAcceptDrops(true); | ||
372 | } | 452 | } | ||
373 | 453 | | |||
374 | DocumentView::~DocumentView() | 454 | DocumentView::~DocumentView() | ||
375 | { | 455 | { | ||
456 | delete d->mDragThumbnailProvider; | ||||
457 | delete d->mDrag; | ||||
376 | delete d; | 458 | delete d; | ||
377 | } | 459 | } | ||
378 | 460 | | |||
379 | void DocumentView::createAdapterForDocument() | 461 | void DocumentView::createAdapterForDocument() | ||
380 | { | 462 | { | ||
381 | const MimeTypeUtils::Kind documentKind = d->mDocument->kind(); | 463 | const MimeTypeUtils::Kind documentKind = d->mDocument->kind(); | ||
382 | if (d->mAdapter && documentKind == d->mAdapter->kind() && documentKind != MimeTypeUtils::KIND_UNKNOWN) { | 464 | if (d->mAdapter && documentKind == d->mAdapter->kind() && documentKind != MimeTypeUtils::KIND_UNKNOWN) { | ||
383 | // Do not reuse for KIND_UNKNOWN: we may need to change the message | 465 | // Do not reuse for KIND_UNKNOWN: we may need to change the message | ||
▲ Show 20 Lines • Show All 407 Lines • ▼ Show 20 Line(s) | |||||
791 | bool DocumentView::isAnimated() const | 873 | bool DocumentView::isAnimated() const | ||
792 | { | 874 | { | ||
793 | return d->mMoveAnimation || d->mFadeAnimation; | 875 | return d->mMoveAnimation || d->mFadeAnimation; | ||
794 | } | 876 | } | ||
795 | 877 | | |||
796 | bool DocumentView::sceneEventFilter(QGraphicsItem*, QEvent* event) | 878 | bool DocumentView::sceneEventFilter(QGraphicsItem*, QEvent* event) | ||
797 | { | 879 | { | ||
798 | if (event->type() == QEvent::GraphicsSceneMousePress) { | 880 | if (event->type() == QEvent::GraphicsSceneMousePress) { | ||
881 | const QGraphicsSceneMouseEvent* mouseEvent = static_cast<QGraphicsSceneMouseEvent*>(event); | ||||
rkflx: `const` | |||||
882 | if (mouseEvent->button() == Qt::LeftButton) { | ||||
883 | d->mDragStartPosition = mouseEvent->pos(); | ||||
884 | } | ||||
799 | QMetaObject::invokeMethod(this, "emitFocused", Qt::QueuedConnection); | 885 | QMetaObject::invokeMethod(this, "emitFocused", Qt::QueuedConnection); | ||
800 | } else if (event->type() == QEvent::GraphicsSceneHoverMove) { | 886 | } else if (event->type() == QEvent::GraphicsSceneHoverMove) { | ||
801 | if (d->mBirdEyeView) { | 887 | if (d->mBirdEyeView) { | ||
802 | d->mBirdEyeView->onMouseMoved(); | 888 | d->mBirdEyeView->onMouseMoved(); | ||
803 | } | 889 | } | ||
890 | } else if (event->type() == QEvent::GraphicsSceneMouseMove) { | ||||
891 | const QGraphicsSceneMouseEvent* mouseEvent = static_cast<QGraphicsSceneMouseEvent*>(event); | ||||
rkflx: `const` | |||||
892 | const qreal dragDistance = (mouseEvent->pos() - d->mDragStartPosition).manhattanLength(); | ||||
893 | const qreal minDistanceToStartDrag = QGuiApplication::styleHints()->startDragDistance(); | ||||
894 | if (!d->canPan() && dragDistance >= minDistanceToStartDrag) { | ||||
895 | d->startDragIfSensible(); | ||||
896 | } | ||||
804 | } | 897 | } | ||
805 | return false; | 898 | return false; | ||
806 | } | 899 | } | ||
807 | 900 | | |||
808 | AbstractRasterImageViewTool* DocumentView::currentTool() const | 901 | AbstractRasterImageViewTool* DocumentView::currentTool() const | ||
809 | { | 902 | { | ||
810 | return imageView() ? imageView()->currentTool() : 0; | 903 | return imageView() ? imageView()->currentTool() : 0; | ||
811 | } | 904 | } | ||
Show All 32 Lines | 934 | { | |||
844 | const QUrl url = event->mimeData()->urls().first(); | 937 | const QUrl url = event->mimeData()->urls().first(); | ||
845 | if (UrlUtils::urlIsDirectory(url)) { | 938 | if (UrlUtils::urlIsDirectory(url)) { | ||
846 | emit openDirUrlRequested(url); | 939 | emit openDirUrlRequested(url); | ||
847 | } else { | 940 | } else { | ||
848 | emit openUrlRequested(url); | 941 | emit openUrlRequested(url); | ||
849 | } | 942 | } | ||
850 | } | 943 | } | ||
851 | 944 | | |||
945 | void DocumentView::dragThumbnailLoaded(const KFileItem& item, const QPixmap& pix) | ||||
rkflx: Omitting the last two parameters also seems to work for me. | |||||
946 | { | ||||
947 | d->setDragPixmap(pix); | ||||
Wouldn't it be safer to check mDrag where it is accessed, i.e. in executeDrag() and in setDragPixmap? rkflx: Wouldn't it be safer to check `mDrag` where it is accessed, i.e. in `executeDrag()` and in… | |||||
948 | d->executeDrag(); | ||||
949 | d->mDragThumbnailProvider->removeItems(KFileItemList({item})); | ||||
950 | } | ||||
951 | | ||||
rkflx: removeItems(KFileItemList({item})) | |||||
952 | void DocumentView::dragThumbnailLoadingFailed(const KFileItem& item) | ||||
953 | { | ||||
954 | d->executeDrag(); | ||||
955 | d->mDragThumbnailProvider->removeItems(KFileItemList({item})); | ||||
956 | } | ||||
rkflx: (Here too.) | |||||
957 | | ||||
852 | } // namespace | 958 | } // namespace | ||
rkflx: removeItems(KFileItemList({item})) |
Not the most elegant solution to the cursor issue, but it's not too bad.