summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2020-02-20 14:09:27 +0100
committerShawn Rutledge <shawn.rutledge@qt.io>2020-02-20 15:55:18 +0100
commitff13e6532975b5372280c02061cb1b7227cf6699 (patch)
tree52839b494927fee50db7d5d4da94529bd5c4d77f
parentf467edc97e66727be7fa3747913e4e01672d4b71 (diff)
Add PdfScrollablePageView, use it in the pdfviewer example
PdfPageView might be useful in some cases, but we need to get feature parity with PdfMultiPageView as much as possible, including scrollbars. Including them in the view is convenient, but also less flexible. Change-Id: Ibbe6a090a5f5b1d340124986fe49672d682ddedb Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
-rw-r--r--examples/pdf/pdfviewer/viewer.qml75
-rw-r--r--src/pdf/quick/plugin.cpp1
-rw-r--r--src/pdf/quick/qml/PdfScrollablePageView.qml249
-rw-r--r--src/pdf/quick/qquickpdflinkmodel.cpp2
-rw-r--r--src/pdf/quick/quick.pro1
-rw-r--r--src/pdf/quick/resources.qrc1
6 files changed, 283 insertions, 46 deletions
diff --git a/examples/pdf/pdfviewer/viewer.qml b/examples/pdf/pdfviewer/viewer.qml
index a2bccab44..777a9660b 100644
--- a/examples/pdf/pdfviewer/viewer.qml
+++ b/examples/pdf/pdfviewer/viewer.qml
@@ -80,57 +80,57 @@ ApplicationWindow {
ToolButton {
action: Action {
shortcut: StandardKey.ZoomIn
- enabled: pageView.sourceSize.width < 10000
+ enabled: view.sourceSize.width < 10000
icon.source: "resources/zoom-in.svg"
- onTriggered: pageView.renderScale *= root.scaleStep
+ onTriggered: view.renderScale *= root.scaleStep
}
}
ToolButton {
action: Action {
shortcut: StandardKey.ZoomOut
- enabled: pageView.sourceSize.width > 50
+ enabled: view.sourceSize.width > 50
icon.source: "resources/zoom-out.svg"
- onTriggered: pageView.renderScale /= root.scaleStep
+ onTriggered: view.renderScale /= root.scaleStep
}
}
ToolButton {
action: Action {
icon.source: "resources/zoom-fit-width.svg"
- onTriggered: pageView.scaleToWidth(root.contentItem.width, root.contentItem.height)
+ onTriggered: view.scaleToWidth(root.contentItem.width, root.contentItem.height)
}
}
ToolButton {
action: Action {
icon.source: "resources/zoom-fit-best.svg"
- onTriggered: pageView.scaleToPage(root.contentItem.width, root.contentItem.height)
+ onTriggered: view.scaleToPage(root.contentItem.width, root.contentItem.height)
}
}
ToolButton {
action: Action {
shortcut: "Ctrl+0"
icon.source: "resources/zoom-original.svg"
- onTriggered: pageView.resetScale()
+ onTriggered: view.resetScale()
}
}
ToolButton {
action: Action {
shortcut: "Ctrl+L"
icon.source: "resources/rotate-left.svg"
- onTriggered: pageView.rotation -= 90
+ onTriggered: view.pageRotation -= 90
}
}
ToolButton {
action: Action {
shortcut: "Ctrl+R"
icon.source: "resources/rotate-right.svg"
- onTriggered: pageView.rotation += 90
+ onTriggered: view.pageRotation += 90
}
}
ToolButton {
action: Action {
icon.source: "resources/go-previous-view-page.svg"
- enabled: pageView.backEnabled
- onTriggered: pageView.back()
+ enabled: view.backEnabled
+ onTriggered: view.back()
}
ToolTip.visible: enabled && hovered
ToolTip.delay: 2000
@@ -141,22 +141,22 @@ ApplicationWindow {
from: 1
to: document.pageCount
editable: true
- value: pageView.currentPage + 1
- onValueModified: pageView.goToPage(value - 1)
+ value: view.currentPage + 1
+ onValueModified: view.goToPage(value - 1)
Shortcut {
sequence: StandardKey.MoveToPreviousPage
- onActivated: pageView.goToPage(currentPageSB.value - 2)
+ onActivated: view.goToPage(currentPageSB.value - 2)
}
Shortcut {
sequence: StandardKey.MoveToNextPage
- onActivated: pageView.goToPage(currentPageSB.value)
+ onActivated: view.goToPage(currentPageSB.value)
}
}
ToolButton {
action: Action {
icon.source: "resources/go-next-view-page.svg"
- enabled: pageView.forwardEnabled
- onTriggered: pageView.forward()
+ enabled: view.forwardEnabled
+ onTriggered: view.forward()
}
ToolTip.visible: enabled && hovered
ToolTip.delay: 2000
@@ -166,8 +166,8 @@ ApplicationWindow {
action: Action {
shortcut: StandardKey.Copy
icon.source: "resources/edit-copy.svg"
- enabled: pageView.selectedText !== ""
- onTriggered: pageView.copySelectionToClipboard()
+ enabled: view.selectedText !== ""
+ onTriggered: view.copySelectionToClipboard()
}
}
Shortcut {
@@ -199,9 +199,9 @@ ApplicationWindow {
}
}
- PdfPageView {
- id: pageView
- x: searchDrawer.position * searchDrawer.width // TODO binding gets broken during centering
+ PdfScrollablePageView {
+ id: view
+ anchors.fill: parent
document: PdfDocument {
id: document
source: Qt.resolvedUrl(root.source)
@@ -210,21 +210,6 @@ ApplicationWindow {
searchString: searchField.text
}
- WheelHandler {
- rotationScale: 15
- target: pageView
- property: "x"
- orientation: Qt.Horizontal
- acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
- }
- WheelHandler {
- rotationScale: 15
- target: pageView
- property: "y"
- orientation: Qt.Vertical
- acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
- }
-
Drawer {
id: searchDrawer
edge: Qt.LeftEdge
@@ -249,7 +234,7 @@ ApplicationWindow {
action: Action {
icon.source: "resources/go-up-search.svg"
shortcut: StandardKey.FindPrevious
- onTriggered: pageView.searchBack()
+ onTriggered: view.searchBack()
}
ToolTip.visible: enabled && hovered
ToolTip.delay: 2000
@@ -279,7 +264,7 @@ ApplicationWindow {
action: Action {
icon.source: "resources/go-down-search.svg"
shortcut: StandardKey.FindNext
- onTriggered: pageView.searchForward()
+ onTriggered: view.searchForward()
}
ToolTip.visible: enabled && hovered
ToolTip.delay: 2000
@@ -291,7 +276,7 @@ ApplicationWindow {
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
- model: pageView.searchModel
+ model: view.searchModel
ScrollBar.vertical: ScrollBar { }
delegate: ItemDelegate {
width: parent ? parent.width : 0
@@ -299,8 +284,8 @@ ApplicationWindow {
highlighted: ListView.isCurrentItem
onClicked: {
searchResultsList.currentIndex = index
- pageView.goToLocation(page, location, 0)
- pageView.searchModel.currentResult = indexOnPage
+ view.goToLocation(page, location, 0)
+ view.searchModel.currentResult = indexOnPage
}
}
}
@@ -308,9 +293,9 @@ ApplicationWindow {
}
footer: Label {
- property size implicitPointSize: document.pagePointSize(pageView.currentPage)
- text: "page " + (pageView.currentPage + 1) + " of " + document.pageCount +
- " scale " + pageView.renderScale.toFixed(2) +
+ property size implicitPointSize: document.pagePointSize(view.currentPage)
+ text: "page " + (view.currentPage + 1) + " of " + document.pageCount +
+ " scale " + view.renderScale.toFixed(2) +
" original " + implicitPointSize.width.toFixed(1) + "x" + implicitPointSize.height.toFixed(1) + "pts"
visible: document.status === PdfDocument.Ready
}
diff --git a/src/pdf/quick/plugin.cpp b/src/pdf/quick/plugin.cpp
index a831a09b6..bb68a817e 100644
--- a/src/pdf/quick/plugin.cpp
+++ b/src/pdf/quick/plugin.cpp
@@ -90,6 +90,7 @@ public:
qmlRegisterType(QUrl("qrc:/qt-project.org/qtpdf/qml/PdfPageView.qml"), uri, 5, 15, "PdfPageView");
qmlRegisterType(QUrl("qrc:/qt-project.org/qtpdf/qml/PdfMultiPageView.qml"), uri, 5, 15, "PdfMultiPageView");
+ qmlRegisterType(QUrl("qrc:/qt-project.org/qtpdf/qml/PdfScrollablePageView.qml"), uri, 5, 15, "PdfScrollablePageView");
}
};
diff --git a/src/pdf/quick/qml/PdfScrollablePageView.qml b/src/pdf/quick/qml/PdfScrollablePageView.qml
new file mode 100644
index 000000000..59bec04a2
--- /dev/null
+++ b/src/pdf/quick/qml/PdfScrollablePageView.qml
@@ -0,0 +1,249 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Pdf 5.15
+import QtQuick.Shapes 1.14
+import Qt.labs.animation 1.0
+
+Flickable {
+ // public API
+ // TODO 5.15: required property
+ property var document: undefined
+ property bool debug: false
+ property alias status: image.status
+
+ property alias selectedText: selection.text
+ function copySelectionToClipboard() {
+ selection.copyToClipboard()
+ }
+
+ // page navigation
+ property alias currentPage: navigationStack.currentPage
+ property alias backEnabled: navigationStack.backAvailable
+ property alias forwardEnabled: navigationStack.forwardAvailable
+ function back() { navigationStack.back() }
+ function forward() { navigationStack.forward() }
+ function goToPage(page) { goToLocation(page, Qt.point(0, 0), 0) }
+ function goToLocation(page, location, zoom) {
+ if (zoom > 0)
+ root.renderScale = zoom
+ navigationStack.push(page, location, zoom)
+ }
+
+ // page scaling
+ property real renderScale: 1
+ property real pageRotation: 0
+ property alias sourceSize: image.sourceSize
+ function resetScale() {
+ paper.scale = 1
+ root.renderScale = 1
+ }
+ function scaleToWidth(width, height) {
+ var pagePointSize = document.pagePointSize(navigationStack.currentPage)
+ root.renderScale = root.width / (paper.rot90 ? pagePointSize.height : pagePointSize.width)
+ if (debug)
+ console.log("scaling", pagePointSize, "to fit", root.width, "rotated?", paper.rot90, "scale", root.renderScale)
+ root.contentX = 0
+ root.contentY = 0
+ }
+ function scaleToPage(width, height) {
+
+ var pagePointSize = document.pagePointSize(navigationStack.currentPage)
+ root.renderScale = Math.min(
+ root.width / (paper.rot90 ? pagePointSize.height : pagePointSize.width),
+ root.height / (paper.rot90 ? pagePointSize.width : pagePointSize.height) )
+ root.contentX = 0
+ root.contentY = 0
+ }
+
+ // text search
+ property alias searchModel: searchModel
+ property alias searchString: searchModel.searchString
+ function searchBack() { --searchModel.currentResult }
+ function searchForward() { ++searchModel.currentResult }
+
+ // implementation
+ id: root
+ contentWidth: paper.width
+ contentHeight: paper.height
+ ScrollBar.vertical: ScrollBar { }
+ ScrollBar.horizontal: ScrollBar { }
+
+ onRenderScaleChanged: {
+ image.sourceSize.width = document.pagePointSize(navigationStack.currentPage).width * renderScale
+ image.sourceSize.height = 0
+ paper.scale = 1
+ }
+
+ PdfSelection {
+ id: selection
+ document: root.document
+ page: navigationStack.currentPage
+ fromPoint: Qt.point(textSelectionDrag.centroid.pressPosition.x / image.pageScale,
+ textSelectionDrag.centroid.pressPosition.y / image.pageScale)
+ toPoint: Qt.point(textSelectionDrag.centroid.position.x / image.pageScale,
+ textSelectionDrag.centroid.position.y / image.pageScale)
+ hold: !textSelectionDrag.active && !tapHandler.pressed
+ }
+
+ PdfSearchModel {
+ id: searchModel
+ document: root.document === undefined ? null : root.document
+ onCurrentPageChanged: root.goToPage(currentPage)
+ }
+
+ PdfNavigationStack {
+ id: navigationStack
+ onCurrentPageChanged: searchModel.currentPage = currentPage
+ // TODO onCurrentLocationChanged: position currentLocation.x and .y in middle // currentPageChanged() MUST occur first!
+ onCurrentZoomChanged: root.renderScale = currentZoom
+ // TODO deal with horizontal location (need WheelHandler or Flickable probably)
+ }
+
+ Rectangle {
+ id: paper
+ width: rot90 ? image.height : image.width
+ height: rot90 ? image.width : image.height
+ property real rotationModulus: Math.abs(root.pageRotation % 180)
+ property bool rot90: rotationModulus > 45 && rotationModulus < 135
+
+ Image {
+ id: image
+ currentFrame: navigationStack.currentPage
+ source: document.status === PdfDocument.Ready ? document.source : ""
+ asynchronous: true
+ fillMode: Image.PreserveAspectFit
+ rotation: root.pageRotation
+ anchors.centerIn: parent
+ property real pageScale: image.paintedWidth / document.pagePointSize(navigationStack.currentPage).width
+ }
+
+ Shape {
+ anchors.fill: parent
+ opacity: 0.25
+ visible: image.status === Image.Ready
+ ShapePath {
+ strokeWidth: 1
+ strokeColor: "cyan"
+ fillColor: "steelblue"
+ scale: Qt.size(image.pageScale, image.pageScale)
+ PathMultiline {
+ paths: searchModel.currentPageBoundingPolygons
+ }
+ }
+ ShapePath {
+ strokeWidth: 1
+ strokeColor: "orange"
+ fillColor: "cyan"
+ scale: Qt.size(image.pageScale, image.pageScale)
+ PathMultiline {
+ paths: searchModel.currentResultBoundingPolygons
+ }
+ }
+ ShapePath {
+ fillColor: "orange"
+ scale: Qt.size(image.pageScale, image.pageScale)
+ PathMultiline {
+ paths: selection.geometry
+ }
+ }
+ }
+
+ Repeater {
+ model: PdfLinkModel {
+ id: linkModel
+ document: root.document
+ page: navigationStack.currentPage
+ }
+ delegate: Rectangle {
+ color: "transparent"
+ border.color: "lightgrey"
+ x: rect.x * image.pageScale
+ y: rect.y * image.pageScale
+ width: rect.width * image.pageScale
+ height: rect.height * image.pageScale
+ MouseArea { // TODO switch to TapHandler / HoverHandler in 5.15
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ onClicked: {
+ if (page >= 0)
+ navigationStack.push(page, Qt.point(0, 0), root.renderScale)
+ else
+ Qt.openUrlExternally(url)
+ }
+ }
+ }
+ }
+
+ PinchHandler {
+ id: pinch
+ minimumScale: 0.1
+ maximumScale: root.renderScale < 4 ? 2 : 1
+ minimumRotation: 0
+ maximumRotation: 0
+ enabled: image.sourceSize.width < 5000
+ onActiveChanged:
+ if (!active) {
+ var newSourceWidth = image.sourceSize.width * paper.scale
+ var ratio = newSourceWidth / image.sourceSize.width
+ if (ratio > 1.1 || ratio < 0.9) {
+ paper.scale = 1
+ root.renderScale *= ratio
+ }
+ // TODO adjust contentX/Y to position the page so the same region is visible
+ paper.x = 0
+ paper.y = 0
+ }
+ grabPermissions: PointerHandler.CanTakeOverFromAnything
+ }
+ DragHandler {
+ id: pageMovingMiddleMouseDrag
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ acceptedButtons: Qt.MiddleButton
+ snapMode: DragHandler.NoSnap
+ }
+ DragHandler {
+ id: textSelectionDrag
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ target: null
+ }
+ TapHandler {
+ id: tapHandler
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ }
+ }
+}
diff --git a/src/pdf/quick/qquickpdflinkmodel.cpp b/src/pdf/quick/qquickpdflinkmodel.cpp
index a3f552d17..f2ff3fd22 100644
--- a/src/pdf/quick/qquickpdflinkmodel.cpp
+++ b/src/pdf/quick/qquickpdflinkmodel.cpp
@@ -96,7 +96,7 @@ QT_BEGIN_NAMESPACE
\endqml
\note General-purpose PDF viewing capabilities are provided by
- \l PdfPageView and \l PdfMultiPageView. PdfLinkModel is only needed
+ \l PdfScrollablePageView and \l PdfMultiPageView. PdfLinkModel is only needed
when building PDF view components from scratch.
*/
diff --git a/src/pdf/quick/quick.pro b/src/pdf/quick/quick.pro
index a0a39d414..b62b80346 100644
--- a/src/pdf/quick/quick.pro
+++ b/src/pdf/quick/quick.pro
@@ -8,6 +8,7 @@ IMPORT_VERSION = 1.0
PDF_QML_FILES = \
qml/PdfMultiPageView.qml \
qml/PdfPageView.qml \
+ qml/PdfScrollablePageView.qml \
QML_FILES += $$PDF_QML_FILES qmldir
diff --git a/src/pdf/quick/resources.qrc b/src/pdf/quick/resources.qrc
index 282610d4c..20cac4827 100644
--- a/src/pdf/quick/resources.qrc
+++ b/src/pdf/quick/resources.qrc
@@ -2,5 +2,6 @@
<qresource prefix="/qt-project.org/qtpdf">
<file>qml/PdfMultiPageView.qml</file>
<file>qml/PdfPageView.qml</file>
+ <file>qml/PdfScrollablePageView.qml</file>
</qresource>
</RCC>