From 79622ee9a34930f156a7716cdd9a17e65adf5ec7 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Thu, 24 Sep 2015 17:47:26 +0200 Subject: [PATCH] A touchscreen optimized view for a document It supports changing pages by a swipe gesture, pinch zoom and flicking to scroll around. The mobile application will be ported to this new component --- mobile/components/CMakeLists.txt | 2 + mobile/components/DocumentView.qml | 224 +++++++++++++++++++++++++ mobile/components/private/PageView.qml | 112 +++++++++++++ mobile/components/qmldir | 2 +- mobile/components/testDocumentView.qml | 35 ++++ 5 files changed, 374 insertions(+), 1 deletion(-) create mode 100644 mobile/components/DocumentView.qml create mode 100644 mobile/components/private/PageView.qml create mode 100644 mobile/components/testDocumentView.qml diff --git a/mobile/components/CMakeLists.txt b/mobile/components/CMakeLists.txt index 336fb9dcf..a5db04e8e 100644 --- a/mobile/components/CMakeLists.txt +++ b/mobile/components/CMakeLists.txt @@ -33,5 +33,7 @@ target_link_libraries(okularplugin install(TARGETS okularplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/okular) install(FILES qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/okular) +install(FILES DocumentView.qml DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/okular) +install(FILES private/PageView.qml DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/okular/private) #add_subdirectory(test) diff --git a/mobile/components/DocumentView.qml b/mobile/components/DocumentView.qml new file mode 100644 index 000000000..569c1ef31 --- /dev/null +++ b/mobile/components/DocumentView.qml @@ -0,0 +1,224 @@ +/* + * Copyright 2015 by Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.2 +import org.kde.okular 2.0 +import "./private" + +/** + * A touchscreen optimized view for a document + * + * It supports changing pages by a swipe gesture, pinch zoom + * and flicking to scroll around + */ +Item { + id: root + width: 500 + height: 600 + + property DocumentItem documentItem + + Flickable { + id: flick + anchors.fill: parent + + Component.onCompleted: { + flick.contentWidth = flick.width + flick.contentHeight = flick.width / mouseArea.currPageDelegate.pageRatio + } + onWidthChanged: { + flick.contentWidth = flick.width + flick.contentHeight = flick.width / mouseArea.currPageDelegate.pageRatio + } + + PinchArea { + width: flick.contentWidth + height: flick.contentHeight + + property real initialWidth + property real initialHeight + + onPinchStarted: { + initialWidth = mouseArea.currPageDelegate.implicitWidth * mouseArea.currPageDelegate.scaleFactor + initialHeight = mouseArea.currPageDelegate.implicitHeight * mouseArea.currPageDelegate.scaleFactor + } + + onPinchUpdated: { + // adjust content pos due to drag + flick.contentX += pinch.previousCenter.x - pinch.center.x + flick.contentY += pinch.previousCenter.y - pinch.center.y + + // resize content + flick.resizeContent(Math.max(flick.width+1, initialWidth * pinch.scale), Math.max(flick.height, initialHeight * pinch.scale), pinch.center); + flick.returnToBounds(); + } + MouseArea { + id: mouseArea + width: parent.width + height: parent.height + + property real oldMouseX + property real oldMouseY + property bool incrementing: true + property Item currPageDelegate: page1 + property Item prevPageDelegate: page2 + property Item nextPageDelegate: page3 + + onPressed: { + var pos = mapToItem(flick, mouse.x, mouse.y); + oldMouseX = pos.x; + oldMouseY = pos.y; + } + onPositionChanged: { + var pos = mapToItem(flick, mouse.x, mouse.y); + currPageDelegate.x += pos.x - oldMouseX; + mouseArea.incrementing = currPageDelegate.x <= 0; + + preventStealing = (currPageDelegate.x > 0 && flick.atXBeginning) || (currPageDelegate.x < 0 && flick.atXEnd); + + flick.contentY = Math.max(0, Math.min(flick.contentHeight - flick.height, flick.contentY - (pos.y - oldMouseY))); + + oldMouseX = pos.x; + oldMouseY = pos.y; + } + onReleased: { + if (root.documentItem.currentPage > 0 && + currPageDelegate.x > width/6) { + switchAnimation.running = true; + } else if (root.documentItem.currentPage < documentItem.pageCount-1 && + currPageDelegate.x < -width/6) { + switchAnimation.running = true; + } else { + resetAnim.running = true; + } + preventStealing = false; + } + onCanceled: { + resetAnim.running = true; + preventStealing = false; + } + onDoubleClicked: { + flick.contentWidth = flick.width + flick.contentHeight = flick.width / mouseArea.currPageDelegate.pageRatio + } + + PageView { + id: page1 + document: root.documentItem + z: 2 + } + PageView { + id: page2 + document: root.documentItem + z: 1 + } + PageView { + id: page3 + document: root.documentItem + z: 0 + } + + + Binding { + target: mouseArea.currPageDelegate + property: "pageNumber" + value: root.documentItem.currentPage + } + Binding { + target: mouseArea.currPageDelegate + property: "visible" + value: true + } + + Binding { + target: mouseArea.prevPageDelegate + property: "pageNumber" + value: root.documentItem.currentPage - 1 + } + Binding { + target: mouseArea.prevPageDelegate + property: "visible" + value: !mouseArea.incrementing && root.documentItem.currentPage > 0 + } + + Binding { + target: mouseArea.nextPageDelegate + property: "pageNumber" + value: root.documentItem.currentPage + 1 + } + Binding { + target: mouseArea.nextPageDelegate + property: "visible" + value: mouseArea.incrementing && root.documentItem.currentPage < documentItem.pageCount-1 + } + + SequentialAnimation { + id: switchAnimation + NumberAnimation { + target: mouseArea.currPageDelegate + properties: "x" + to: mouseArea.incrementing ? -mouseArea.currPageDelegate.width : mouseArea.currPageDelegate.width + easing.type: Easing.InQuad + //hardcoded number, we would need units from plasma + //which cannot depend from here + duration: 250 + } + ScriptAction { + script: { + mouseArea.currPageDelegate.z = 0; + mouseArea.prevPageDelegate.z = 1; + mouseArea.nextPageDelegate.z = 2; + } + } + ScriptAction { + script: { + mouseArea.currPageDelegate.x = 0 + var oldCur = mouseArea.currPageDelegate; + var oldPrev = mouseArea.prevPageDelegate; + var oldNext = mouseArea.nextPageDelegate; + + if (mouseArea.incrementing) { + root.documentItem.currentPage++; + mouseArea.currPageDelegate = oldNext; + mouseArea.prevPageDelegate = oldCur; + mouseArea. nextPageDelegate = oldPrev; + } else { + root.documentItem.currentPage--; + mouseArea.currPageDelegate = oldPrev; + mouseArea.prevPageDelegate = oldCur; + mouseArea. nextPageDelegate = oldNext; + } + mouseArea.currPageDelegate.z = 2; + mouseArea.prevPageDelegate.z = 1; + mouseArea.nextPageDelegate.z = 0; + } + } + } + NumberAnimation { + id: resetAnim + target: mouseArea.currPageDelegate + properties: "x" + to: 0 + easing.type: Easing.InQuad + duration: 250 + } + } + } + } +} \ No newline at end of file diff --git a/mobile/components/private/PageView.qml b/mobile/components/private/PageView.qml new file mode 100644 index 000000000..dc4e4d670 --- /dev/null +++ b/mobile/components/private/PageView.qml @@ -0,0 +1,112 @@ +/* + * Copyright 2015 by Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.2 +import QtGraphicalEffects 1.0 +import org.kde.okular 2.0 as Okular + +Item { + width: parent.width + height: parent.height + property alias document: page.document + property alias pageNumber: page.pageNumber + implicitWidth: page.implicitWidth + implicitHeight: page.implicitHeight + property real pageRatio: page.implicitWidth / page.implicitHeight + property real scaleFactor: page.width / page.implicitWidth + + Okular.PageItem { + id: page + property bool sameOrientation: parent.width / parent.height > pageRatio + anchors.centerIn: parent + width: sameOrientation ? parent.height * pageRatio : parent.width + height: !sameOrientation ? parent.width / pageRatio : parent.height + document: null + } + + //FIXME: this should use units, but can't depend from plasma from here + Text { + id: unit + text: "M" + visible: false + } + Rectangle { + id: backgroundRectangle + anchors { + top: parent.top + bottom: parent.bottom + left: page.left + right: page.right + topMargin: -unit.height * 10 + bottomMargin: -unit.height * 10 + } + z: -1 + color: "white" + + LinearGradient { + width: unit.width + anchors { + right: parent.left + top: parent.top + bottom: parent.bottom + } + start: Qt.point(0, 0) + end: Qt.point(unit.width, 0) + gradient: Gradient { + GradientStop { + position: 0.0 + color: "transparent" + } + GradientStop { + position: 0.7 + color: Qt.rgba(0, 0, 0, 0.08) + } + GradientStop { + position: 1.0 + color: Qt.rgba(0, 0, 0, 0.2) + } + } + } + + LinearGradient { + width: unit.width + anchors { + left: parent.right + top: parent.top + bottom: parent.bottom + } + start: Qt.point(0, 0) + end: Qt.point(unit.width, 0) + gradient: Gradient { + GradientStop { + position: 0.0 + color: Qt.rgba(0, 0, 0, 0.2) + } + GradientStop { + position: 0.3 + color: Qt.rgba(0, 0, 0, 0.08) + } + GradientStop { + position: 1.0 + color: "transparent" + } + } + } + } +} diff --git a/mobile/components/qmldir b/mobile/components/qmldir index 4d3cc2782..26164a82f 100644 --- a/mobile/components/qmldir +++ b/mobile/components/qmldir @@ -1,3 +1,3 @@ module org.kde.okular plugin okularplugin - +DocumentView 2.0 DocumentView.qml diff --git a/mobile/components/testDocumentView.qml b/mobile/components/testDocumentView.qml new file mode 100644 index 000000000..c913810b5 --- /dev/null +++ b/mobile/components/testDocumentView.qml @@ -0,0 +1,35 @@ +/* + * Copyright 2012 by Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.2 +import QtQuick.Controls 1.0 +import org.kde.okular 2.0 as Okular + +Item { + width: 500 + height: 600 + Okular.DocumentItem { + id: docItem + path: "pageitem.cpp" + } + Okular.DocumentView { + anchors.fill: parent + documentItem: docItem + } +} \ No newline at end of file