diff --git a/app/folderviewcontextmanageritem.cpp b/app/folderviewcontextmanageritem.cpp --- a/app/folderviewcontextmanageritem.cpp +++ b/app/folderviewcontextmanageritem.cpp @@ -28,6 +28,7 @@ #include #include #include +#include // KDE #include @@ -208,6 +209,12 @@ // widget). mView->header()->setStretchLastSection(false); mView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + QScroller* mScroller = QScroller::scroller(mView->viewport()); + QScrollerProperties scrollerProp = mScroller->scrollerProperties(); + scrollerProp.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy,1); + scrollerProp.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy,1); + mScroller->setScrollerProperties(scrollerProp); + mScroller->grabGesture(mView->viewport(), QScroller::TouchGesture); setWidget(mView); QObject::connect(mView, &QTreeView::activated, this, &FolderViewContextManagerItem::slotActivated); diff --git a/app/startmainpage.h b/app/startmainpage.h --- a/app/startmainpage.h +++ b/app/startmainpage.h @@ -61,7 +61,7 @@ protected: void showEvent(QShowEvent*) Q_DECL_OVERRIDE; - + private Q_SLOTS: void slotListViewActivated(const QModelIndex& index); void slotTagViewClicked(const QModelIndex& index); diff --git a/app/startmainpage.cpp b/app/startmainpage.cpp --- a/app/startmainpage.cpp +++ b/app/startmainpage.cpp @@ -33,6 +33,7 @@ #include #include +#include // KDE #include @@ -168,6 +169,14 @@ d->mBookmarksView->setModel(d->mBookmarksModel); d->mBookmarksView->setAutoResizeItemsEnabled(false); + d->mBookmarksView->setDragEnabled(false); + QScroller* mScroller = QScroller::scroller(d->mBookmarksView->viewport()); + QScrollerProperties scrollerProp = mScroller->scrollerProperties(); + scrollerProp.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy,1); + scrollerProp.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy,1); + mScroller->setScrollerProperties(scrollerProp); + mScroller->grabGesture(d->mBookmarksView->viewport(), QScroller::TouchGesture); + connect(d->mBookmarksView, &KFilePlacesView::urlChanged, this, &StartMainPage::urlSelected); // Tag view diff --git a/lib/documentview/documentview.h b/lib/documentview/documentview.h --- a/lib/documentview/documentview.h +++ b/lib/documentview/documentview.h @@ -33,6 +33,7 @@ class QPropertyAnimation; class QUrl; +class QGestureEvent; namespace Gwenview { @@ -207,6 +208,8 @@ bool sceneEventFilter(QGraphicsItem*, QEvent*) Q_DECL_OVERRIDE; void dragEnterEvent(QGraphicsSceneDragDropEvent* event) override; void dropEvent(QGraphicsSceneDragDropEvent* event) override; + bool event(QEvent*) Q_DECL_OVERRIDE; + bool gestureEvent( QGestureEvent*); private Q_SLOTS: void finishOpenUrl(); diff --git a/lib/documentview/documentview.cpp b/lib/documentview/documentview.cpp --- a/lib/documentview/documentview.cpp +++ b/lib/documentview/documentview.cpp @@ -41,6 +41,8 @@ #include #include #include +#include +#include // KDE #include @@ -67,6 +69,7 @@ #include #include #include +#include namespace Gwenview { @@ -113,7 +116,16 @@ QPointF mDragStartPosition; QPointer mDragThumbnailProvider; QPointer mDrag; - + qint64 timestamp; + qint64 lastTouchUpdateTimestamp; + qint64 lastTabtimestamp; + qreal lastRotation; + bool firstMoving; + QPointF startCenterPoint; + bool dragIsStarted; + bool noPinch; + bool isOnlyTab; + void setCurrentAdapter(AbstractDocumentViewAdapter* adapter) { Q_ASSERT(adapter); @@ -424,16 +436,19 @@ setFlag(ItemIsFocusable); setFlag(ItemIsSelectable); setFlag(ItemClipsChildrenToShape); - + d->q = this; d->mLoadingIndicator = nullptr; d->mBirdEyeView = nullptr; d->mCurrent = false; d->mCompareMode = false; d->controlWheelAccumulatedDelta = 0; d->mDragStartPosition = QPointF(0, 0); d->mDrag = nullptr; - + grabGesture(Qt::PinchGesture); + setAcceptTouchEvents (true); + + d->lastTabtimestamp = 0; // 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 // opacity, resulting in all layers being visible when 0 < opacity < 1. @@ -670,6 +685,209 @@ return d->mAdapter->zoom(); } +bool DocumentView::event(QEvent *event) +{ + if (event->type() == QEvent::TouchBegin) { + QTouchEvent *touchEvent = static_cast(event); + event->accept(); + d->timestamp = touchEvent->timestamp(); + d->firstMoving = true; + d->dragIsStarted = false; + d->isOnlyTab = true; + QGraphicsWidget *receiver = this; + QPoint pos = touchEvent->touchPoints().at(0).pos().toPoint(); + QMouseEvent *evt = new QMouseEvent(QEvent::MouseMove,pos, Qt::NoButton, Qt::NoButton,0); + QCoreApplication::postEvent(receiver,evt); + QMetaObject::invokeMethod(this, "emitFocused", Qt::QueuedConnection); + return true; + } + + if (event->type() == QEvent::TouchUpdate) { + QTouchEvent *touchEvent = static_cast(event); + d->isOnlyTab = false; + d->lastTouchUpdateTimestamp = touchEvent->timestamp(); + if (touchEvent->touchPoints().size() == 2) { + if (touchEvent->touchPoints().at(0).state() == Qt::TouchPointReleased || touchEvent->touchPoints().at(1).state() == Qt::TouchPointReleased ) { + d->noPinch = true; + return true; + } + d->noPinch = true; + if (d->lastTouchUpdateTimestamp - d->timestamp >= 200) { + QPointF startone = touchEvent->touchPoints().at(0).startPos(); + QPointF starttwo = touchEvent->touchPoints().at(1).startPos(); + QPointF endone = touchEvent->touchPoints().at(0).pos(); + QPointF endtwo = touchEvent->touchPoints().at(1).pos(); + QPointF vectorone = startone - endone; + QPointF vectortwo = starttwo - endtwo; + int x = (startone - endone).x() * (starttwo - endtwo).x(); + int y = (startone - endone).y() * (starttwo - endtwo).y(); + if (x == 0 && y>0) x=1; + if (y == 0 && x>0) y=1; + if (x >= -500 && x<0 && y>0) x=1; + if (y >= -500 && y<0 && x>0) y=1; + if (x>0 && y>0) { + d->noPinch = true; + d->lastRotation = 0; + QPointF lastPos = touchEvent->touchPoints().at(0).lastPos(); + QPointF pos = touchEvent->touchPoints().at(0).pos(); + QPointF diff = lastPos - pos; + d->mAdapter->setScrollPos(d->mAdapter->scrollPos() + diff); + } else { + if (vectorone.manhattanLength() > 50 || vectortwo.manhattanLength() > 50){ + d->noPinch = false; + } + } + } + return true; + } + + if (touchEvent->touchPoints().size() == 1) { + qint64 now = touchEvent->timestamp(); + QPointF startPos = touchEvent->touchPoints().at(0).startPos(); + QPointF nowPos = touchEvent->touchPoints().at(0).pos(); + if (d->firstMoving && now - d->timestamp >= 400 && (nowPos - startPos).manhattanLength() < 20) { + if (!d->dragIsStarted) { + d->dragIsStarted = true; + d->startDragIfSensible(); + } + d->firstMoving = false; + return true; + } + if (d->dragIsStarted) { + QGraphicsWidget *receiver = this; + QPoint pos = touchEvent->touchPoints().at(0).pos().toPoint(); + QMouseEvent *evt = new QMouseEvent(QEvent::MouseMove,pos, Qt::NoButton, Qt::LeftButton,0); + QCoreApplication::postEvent(receiver,evt); + d->firstMoving = false; + return true; + } + QPointF lastPos = touchEvent->touchPoints().at(0).lastPos(); + QPointF pos = touchEvent->touchPoints().at(0).pos(); + QPointF diff = lastPos - pos; + d->mAdapter->setScrollPos(d->mAdapter->scrollPos() + diff); + d->firstMoving = false; + return true; + } + } + + if (event->type() == QEvent::TouchEnd) { + d->firstMoving = false; + QTouchEvent *touchEvent = static_cast(event); + if (d->dragIsStarted) { + QGraphicsWidget *receiver = this; + QPoint pos = touchEvent->touchPoints().at(0).pos().toPoint(); + QMouseEvent *evt = new QMouseEvent(QEvent::MouseButtonRelease,pos, Qt::LeftButton, Qt::LeftButton,0); + QCoreApplication::postEvent(receiver,evt); + d->dragIsStarted = false; + d->lastTabtimestamp = d->timestamp; + return true; + } + const QPointF start = touchEvent->touchPoints().at(0).startPos(); + const QPointF end = touchEvent->touchPoints().at(0).pos(); + QPointF diff = start - end; + qint64 now = touchEvent->timestamp(); + qint64 time = now - d->timestamp; + qreal doupleclicktime = QGuiApplication::styleHints()->mouseDoubleClickInterval(); + if (now - d->lastTabtimestamp <= doupleclicktime && d->isOnlyTab){ + d->mAdapter->toggleFullScreenRequested(); + d->lastTabtimestamp = d->timestamp; + return true; + } + if (now - d->lastTabtimestamp > doupleclicktime && d->isOnlyTab ){ + QPoint pos = touchEvent->touchPoints().at(0).startScenePos().toPoint(); + QPoint screenPos = touchEvent->touchPoints().at(0).screenPos().toPoint(); + QObject *receiver = touchEvent->target(); + QMouseEvent *evt = new QMouseEvent(QEvent::MouseButtonPress,pos,screenPos, Qt::LeftButton, Qt::LeftButton,0); + QCoreApplication::postEvent(receiver,evt); + QMouseEvent *evtr = new QMouseEvent(QEvent::MouseButtonRelease,pos,screenPos, Qt::LeftButton, Qt::LeftButton,0); + QCoreApplication::postEvent(receiver,evtr); + } + if (touchEvent->touchPoints().size() <= 2 && diff.manhattanLength() > 200 && time < 300) { + if (diff.x() < 0 && abs(diff.x()) >= abs(diff.y()) * 2) { + QPoint scrollPos = d->mAdapter->scrollPos().toPoint(); + if (scrollPos.x() <= 1) { + d->mAdapter->previousImageRequested(); + } + } + if (diff.x() > 0 && abs(diff.x()) >= abs(diff.y()) * 2) { + QPoint scrollPos = d->mAdapter->scrollPos().toPoint(); + int width = d->mAdapter->document()->width() * d->mAdapter->zoom(); + QRect visibleRect = d->mAdapter->visibleDocumentRect().toRect(); + int x = scrollPos.x() + visibleRect.width(); + if (x >= (width - 1)) { + d->mAdapter->nextImageRequested(); + } + } + } + d->lastTabtimestamp = d->timestamp; + return true; + } + + if ( event->type() == QEvent::Gesture ) { + return gestureEvent(static_cast( event )); + } + return QGraphicsWidget::event( event ); +} + +bool DocumentView::gestureEvent( QGestureEvent *event ) +{ + QPinchGesture *pinch = static_cast(event->gesture(Qt::PinchGesture)); + const qreal rotationsDelta = 35; + + if (pinch) + { + event->accept(); + if (pinch->state() == Qt::GestureStarted) { + d->lastRotation = 0; + d->startCenterPoint = pinch->centerPoint(); + } + + if (pinch->state() == Qt::GestureUpdated && !d->noPinch) { + if (pinch->changeFlags() & QPinchGesture::ScaleFactorChanged) { + if (d->mAdapter->canZoom()) { + if ((d->startCenterPoint - pinch->centerPoint()).manhattanLength() <= 600) { + qreal currentZoom = d->mAdapter->zoom(); + qreal sensitivModifier = 0.4; + qreal zoom; + if (pinch->scaleFactor() >= 1.0){ + zoom = ((pinch->scaleFactor() - 1.0) * sensitivModifier + 1.0) * currentZoom; + } else { + zoom = (1.0 - (1.0 - pinch->scaleFactor()) * sensitivModifier) * currentZoom; + } + qreal diff = currentZoom > zoom ? currentZoom - zoom : zoom - currentZoom; + if (diff > 0.0001) { + QPointF zoomCenter = event->mapToGraphicsScene(pinch->centerPoint()) - d->mAdapter->visibleDocumentRect().topLeft(); + d->setZoom(zoom,zoomCenter); + } + } + } + } + + if (pinch->changeFlags() & QPinchGesture::RotationAngleChanged) { + if ((d->startCenterPoint - pinch->centerPoint()).manhattanLength() <= 150) { + if (pinch->rotationAngle() - d->lastRotation > rotationsDelta) { + TransformImageOperation *op = new TransformImageOperation(ROT_90); + op->applyToDocument(d->mDocument); + d->lastRotation = pinch->rotationAngle(); + } + else if (-(pinch->rotationAngle() - d->lastRotation) > rotationsDelta) { + TransformImageOperation *op = new TransformImageOperation(ROT_270); + op->applyToDocument(d->mDocument); + d->lastRotation = pinch->rotationAngle(); + } + } + } + } + + if (pinch->state() == Qt::GestureFinished) { + d->lastRotation = 0; + d->startCenterPoint = QPointF (0,0); + } + return true; + } + return false; +} + void DocumentView::resizeEvent(QGraphicsSceneResizeEvent *event) { d->resizeAdapterWidget(); @@ -891,7 +1109,7 @@ 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) { + if (!d->canPan() && dragDistance >= minDistanceToStartDrag && mouseEvent->source() == Qt::MouseEventNotSynthesized) { d->startDragIfSensible(); } } diff --git a/lib/thumbnailview/thumbnailview.h b/lib/thumbnailview/thumbnailview.h --- a/lib/thumbnailview/thumbnailview.h +++ b/lib/thumbnailview/thumbnailview.h @@ -27,6 +27,7 @@ // KDE #include +#include class KFileItem; class QDragEnterEvent; @@ -158,6 +159,10 @@ void generateThumbnailsForItems(); protected: + bool gestureEvent( QGestureEvent*); + + bool viewportEvent(QEvent*) Q_DECL_OVERRIDE; + void dragEnterEvent(QDragEnterEvent*) Q_DECL_OVERRIDE; void dragMoveEvent(QDragMoveEvent*) Q_DECL_OVERRIDE; diff --git a/lib/thumbnailview/thumbnailview.cpp b/lib/thumbnailview/thumbnailview.cpp --- a/lib/thumbnailview/thumbnailview.cpp +++ b/lib/thumbnailview/thumbnailview.cpp @@ -36,6 +36,9 @@ #include #include #include +#include +#include +#include // KDE #include @@ -186,7 +189,14 @@ QTimeLine* mBusyAnimationTimeLine; bool mCreateThumbnailsForRemoteUrls; - + bool isOnlyTab; + qint64 timestamp; + bool firstMoving; + bool dragIsStarted; + QScroller* mScroller; + QPointF startCenterPoint; + bool dontZoom; + void setupBusyAnimation() { mBusySequence = KIconLoader::global()->loadPixmapSequence(QStringLiteral("process-working"), 22); @@ -280,8 +290,8 @@ }; ThumbnailView::ThumbnailView(QWidget* parent) -: QListView(parent) -, d(new ThumbnailViewPrivate) + : QListView(parent) + , d(new ThumbnailViewPrivate) { d->q = this; d->mScaleMode = ScaleToFit; @@ -309,6 +319,15 @@ setVerticalScrollMode(ScrollPerPixel); setHorizontalScrollMode(ScrollPerPixel); + d->mScroller = QScroller::scroller(this->viewport()); + QScrollerProperties scrollerProp = d->mScroller->scrollerProperties(); + scrollerProp.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy,1); + scrollerProp.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy,1); + d->mScroller->setScrollerProperties(scrollerProp); + d->mScroller->grabGesture(this->viewport(), QScroller::TouchGesture); + viewport()->setAttribute(Qt::WA_AcceptTouchEvents); + viewport()->grabGesture(Qt::PinchGesture); + d->mScheduledThumbnailGenerationTimer.setSingleShot(true); d->mScheduledThumbnailGenerationTimer.setInterval(500); connect(&d->mScheduledThumbnailGenerationTimer, &QTimer::timeout, this, &ThumbnailView::generateThumbnailsForItems); @@ -353,9 +372,9 @@ GV_RETURN_IF_FAIL(d->mThumbnailProvider != thumbnailProvider); if (thumbnailProvider) { connect(thumbnailProvider, SIGNAL(thumbnailLoaded(KFileItem,QPixmap,QSize,qulonglong)), - SLOT(setThumbnail(KFileItem,QPixmap,QSize,qulonglong))); + SLOT(setThumbnail(KFileItem,QPixmap,QSize,qulonglong))); connect(thumbnailProvider, SIGNAL(thumbnailLoadingFailed(KFileItem)), - SLOT(setBrokenThumbnail(KFileItem))); + SLOT(setBrokenThumbnail(KFileItem))); } else { disconnect(d->mThumbnailProvider, 0 , this, 0); } @@ -696,6 +715,148 @@ drag->exec(supportedActions, Qt::CopyAction); } +bool ThumbnailView::viewportEvent(QEvent* event) +{ + switch (event->type()) { + case QEvent::TouchBegin: { + QTouchEvent *touchEvent = static_cast(event); + d->timestamp = touchEvent->timestamp(); + d->firstMoving = true; + d->dragIsStarted = false; + d->isOnlyTab = true; + QModelIndex index = indexAt(touchEvent->touchPoints().at(0).pos().toPoint()); + setCurrentIndex(index); + return true; + } + + case QEvent::TouchUpdate: { + QTouchEvent *touchEvent = static_cast(event); + qreal now = touchEvent->timestamp(); + QPointF distance = touchEvent->touchPoints().at(0).startPos() - touchEvent->touchPoints().at(0).pos(); + if (d->isOnlyTab && now - d->timestamp < 100 && distance.manhattanLength() < 40 && touchEvent->touchPoints().size() == 1) { + d->isOnlyTab = true; + } else { + d->isOnlyTab = false; + } + if (touchEvent->touchPoints().size() == 2) { + d->dontZoom = true; + if (now - d->timestamp >= 300) { + QPointF startone = touchEvent->touchPoints().at(0).startPos(); + QPointF starttwo = touchEvent->touchPoints().at(1).startPos(); + QPointF endone = touchEvent->touchPoints().at(0).pos(); + QPointF endtwo = touchEvent->touchPoints().at(1).pos(); + QPointF vectorone = startone - endone; + QPointF vectortwo = starttwo - endtwo; + int x = (startone - endone).x() * (starttwo - endtwo).x(); + int y = (startone - endone).y() * (starttwo - endtwo).y(); + if (x == 0 && y>0) x=1; + if (y == 0 && x>0) y=1; + if (x >= -500 && x<0 && y>0) x=1; + if (y >= -500 && y<0 && x>0) y=1; + if (x>0 && y>0) { + d->dontZoom = true; + } else { + if (vectorone.manhattanLength() > 50 || vectortwo.manhattanLength() > 50){ + d->dontZoom = false; + } + } + } + } + if (touchEvent->touchPoints().size() == 1) { + qint64 now = touchEvent->timestamp(); + QPointF startPos = touchEvent->touchPoints().at(0).startPos(); + QPointF nowPos = touchEvent->touchPoints().at(0).pos(); + if (d->firstMoving && now - d->timestamp >= 400 && (nowPos - startPos).manhattanLength() <= 20) { + if (!d->dragIsStarted) { + d->mScroller->stop(); + d->dragIsStarted = true; + d->firstMoving = false; + startDrag(Qt::CopyAction); + } + } + } + if (d->dragIsStarted) { + QWidget *receiver = this->viewport(); + QPoint pos = touchEvent->touchPoints().at(0).pos().toPoint(); + QMouseEvent* evt = new QMouseEvent(QEvent::MouseMove,pos, Qt::NoButton, Qt::LeftButton,0); + QCoreApplication::postEvent(receiver,evt); + } + d->firstMoving = false; + return true; + } + + case QEvent::TouchEnd: { + QTouchEvent *touchEvent = static_cast(event); + if (d->dragIsStarted) { + QWidget *receiver = this->viewport(); + QPoint pos = touchEvent->touchPoints().at(0).pos().toPoint(); + QMouseEvent* evt = new QMouseEvent(QEvent::MouseButtonRelease,pos, Qt::LeftButton, Qt::LeftButton,0); + QCoreApplication::postEvent(receiver,evt); + d->dragIsStarted = false; + return true; + } + QPointF startPos = touchEvent->touchPoints().at(0).startPos(); + QPointF endPos = touchEvent->touchPoints().at(0).pos(); + qreal diff = (endPos-startPos).manhattanLength(); + if (d->isOnlyTab && touchEvent->touchPoints().size() == 1 && diff <= 20) { + QPoint pos = touchEvent->touchPoints().at(0).startPos().toPoint(); + QWidget *receiver = this->viewport(); + QMouseEvent* evt = new QMouseEvent(QEvent::MouseButtonPress,pos, Qt::LeftButton, Qt::LeftButton,0); + QCoreApplication::postEvent(receiver,evt); + QMouseEvent* evtr = new QMouseEvent(QEvent::MouseButtonRelease,pos, Qt::LeftButton, Qt::LeftButton,0); + QCoreApplication::postEvent(receiver,evtr); + if (!QGuiApplication::styleHints()->singleClickActivation()) { + QMouseEvent* evt = new QMouseEvent(QEvent::MouseButtonDblClick,pos, Qt::LeftButton, Qt::LeftButton,0); + QCoreApplication::postEvent(receiver,evt); + } + } + return true; + } + case QEvent::Gesture: { + if (gestureEvent(static_cast( event ))) { + return true; + } + } + default: + break; + } + + return QListView::viewportEvent(event); +} + +bool ThumbnailView::gestureEvent( QGestureEvent* event) +{ + static int zoomCounter; + QPinchGesture *pinch = static_cast(event->gesture(Qt::PinchGesture)); + if (pinch) + { + + event->accept(); + if (pinch->state() == Qt::GestureStarted) { + d->startCenterPoint = pinch->centerPoint(); + zoomCounter = 0; + return true; + } + + if (pinch->state() == Qt::GestureUpdated) { + if (!d->dontZoom) { + zoomCounter++; + if (zoomCounter == 1) { + int width = d->mThumbnailSize.width() + ((pinch->scaleFactor() > 1 ? 1 : -1) * 1); + width = qMax(int(MinThumbnailSize), qMin(width, int(MaxThumbnailSize))); + setThumbnailWidth(width); + zoomCounter = 0; + } + return true; + } + } + if (pinch->state() == Qt::GestureFinished) { + return true; + } + } + return false; +} + void ThumbnailView::dragEnterEvent(QDragEnterEvent* event) { QAbstractItemView::dragEnterEvent(event);