summaryrefslogtreecommitdiffstats
path: root/src/pdfquick/PdfMultiPageView.qml
diff options
context:
space:
mode:
Diffstat (limited to 'src/pdfquick/PdfMultiPageView.qml')
-rw-r--r--src/pdfquick/PdfMultiPageView.qml275
1 files changed, 120 insertions, 155 deletions
diff --git a/src/pdfquick/PdfMultiPageView.qml b/src/pdfquick/PdfMultiPageView.qml
index 2457c5010..194d7866e 100644
--- a/src/pdfquick/PdfMultiPageView.qml
+++ b/src/pdfquick/PdfMultiPageView.qml
@@ -1,41 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtPDF module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** 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 https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Controls
import QtQuick.Pdf
@@ -92,8 +59,8 @@ Item {
*/
function selectAll() {
const currentItem = tableView.itemAtCell(tableView.cellAtPos(root.width / 2, root.height / 2))
- if (currentItem)
- currentItem.selection.selectAll()
+ const pdfSelection = currentItem?.selection as PdfSelection
+ pdfSelection?.selectAll()
}
/*!
@@ -106,9 +73,9 @@ Item {
*/
function copySelectionToClipboard() {
const currentItem = tableView.itemAtCell(tableView.cellAtPos(root.width / 2, root.height / 2))
- console.log(lcMPV, "currentItem", currentItem, "sel", currentItem.selection.text)
- if (currentItem)
- currentItem.selection.copyToClipboard()
+ const pdfSelection = currentItem?.selection as PdfSelection
+ console.log(lcMPV, "currentItem", currentItem, "sel", pdfSelection?.text)
+ pdfSelection?.copyToClipboard()
}
// --------------------------------
@@ -125,9 +92,9 @@ Item {
\c onCurrentPageChanged script) to update the part of the user interface
that shows the current page number, such as a \l SpinBox.
- \sa PdfNavigationStack::currentPage
+ \sa PdfPageNavigator::currentPage
*/
- property alias currentPage: navigationStack.currentPage
+ property alias currentPage: pageNavigator.currentPage
/*!
\qmlproperty bool PdfMultiPageView::backEnabled
@@ -136,9 +103,9 @@ Item {
This property indicates if it is possible to go back in the navigation
history to a previous-viewed page.
- \sa PdfNavigationStack::backAvailable, back()
+ \sa PdfPageNavigator::backAvailable, back()
*/
- property alias backEnabled: navigationStack.backAvailable
+ property alias backEnabled: pageNavigator.backAvailable
/*!
\qmlproperty bool PdfMultiPageView::forwardEnabled
@@ -147,9 +114,9 @@ Item {
This property indicates if it is possible to go to next location in the
navigation history.
- \sa PdfNavigationStack::forwardAvailable, forward()
+ \sa PdfPageNavigator::forwardAvailable, forward()
*/
- property alias forwardEnabled: navigationStack.forwardAvailable
+ property alias forwardEnabled: pageNavigator.forwardAvailable
/*!
\qmlmethod void PdfMultiPageView::back()
@@ -158,9 +125,9 @@ Item {
recently; or does nothing if there is no previous location on the
navigation stack.
- \sa PdfNavigationStack::back(), currentPage, backEnabled
+ \sa PdfPageNavigator::back(), currentPage, backEnabled
*/
- function back() { navigationStack.back() }
+ function back() { pageNavigator.back() }
/*!
\qmlmethod void PdfMultiPageView::forward()
@@ -169,19 +136,19 @@ Item {
method was called; or does nothing if there is no "next" location on the
navigation stack.
- \sa PdfNavigationStack::forward(), currentPage
+ \sa PdfPageNavigator::forward(), currentPage
*/
- function forward() { navigationStack.forward() }
+ function forward() { pageNavigator.forward() }
/*!
\qmlmethod void PdfMultiPageView::goToPage(int page)
Scrolls the view to the given \a page number, if possible.
- \sa PdfNavigationStack::jump(), currentPage
+ \sa PdfPageNavigator::jump(), currentPage
*/
function goToPage(page) {
- if (page === navigationStack.currentPage)
+ if (page === pageNavigator.currentPage)
return
goToLocation(page, Qt.point(-1, -1), 0)
}
@@ -192,15 +159,22 @@ Item {
Scrolls the view to the \a location on the \a page, if possible,
and sets the \a zoom level.
- \sa PdfNavigationStack::jump(), currentPage
+ \sa PdfPageNavigator::jump(), currentPage
*/
function goToLocation(page, location, zoom) {
+ if (tableView.rows === 0) {
+ // save this request for later
+ tableView.pendingRow = page
+ tableView.pendingLocation = location
+ tableView.pendingZoom = zoom
+ return
+ }
if (zoom > 0) {
- navigationStack.jumping = true // don't call navigationStack.update() because we will jump() instead
+ pageNavigator.jumping = true // don't call pageNavigator.update() because we will jump() instead
root.renderScale = zoom
- navigationStack.jumping = false
+ pageNavigator.jumping = false
}
- navigationStack.jump(page, location, zoom) // actually jump
+ pageNavigator.jump(page, location, zoom) // actually jump
}
/*!
@@ -208,8 +182,6 @@ Item {
This property holds the \l {QtQuick::Image::status}{rendering status} of
the \l {currentPage}{current page}.
-
- \sa PdfPageImage::status
*/
property int currentPageRenderingStatus: Image.Null
@@ -221,8 +193,6 @@ Item {
This property holds the ratio of pixels to points. The default is \c 1,
meaning one point (1/72 of an inch) equals 1 logical pixel.
-
- \sa PdfPageImage::status
*/
property real renderScale: 1
@@ -233,8 +203,6 @@ Item {
The default value is \c 0 degrees (that is, no rotation relative to the
orientation of the pages as stored in the PDF file).
-
- \sa PdfPageImage::rotation
*/
property real pageRotation: 0
@@ -300,8 +268,8 @@ Item {
\qmlproperty string PdfMultiPageView::searchString
This property holds the search string that the user may choose to search
- for. It is typically used in a binding to the
- \l {QtQuick.Controls::TextField::text}{text} property of a TextField.
+ for. It is typically used in a binding to the \c text property of a
+ TextField.
\sa searchModel
*/
@@ -335,29 +303,40 @@ Item {
TableView {
id: tableView
property bool debug: false
- property vector2d jumpLocationMargin: Qt.vector2d(10, 10) // px from top-left corner
+ property real minScale: 0.1
+ property real maxScale: 10
+ property point jumpLocationMargin: Qt.point(10, 10) // px away from viewport edges
anchors.fill: parent
anchors.leftMargin: 2
- model: modelInUse && root.document ? root.document.pageCount : 0
- // workaround to make TableView do scheduleRebuildTable(RebuildOption::All) in cases when forceLayout() doesn't
- property bool modelInUse: true
- function rebuild() {
- modelInUse = false
- modelInUse = true
- }
- // end workaround
+ model: root.document ? root.document.pageCount : 0
rowSpacing: 6
property real rotationNorm: Math.round((360 + (root.pageRotation % 360)) % 360)
property bool rot90: rotationNorm == 90 || rotationNorm == 270
onRot90Changed: forceLayout()
onHeightChanged: forceLayout()
onWidthChanged: forceLayout()
- property size firstPagePointSize: document?.status === PdfDocument.Ready ? document.pagePointSize(0) : Qt.size(1, 1)
- property real pageHolderWidth: Math.max(root.width, ((rot90 ? document?.maxPageHeight : document?.maxPageWidth) ?? 0) * root.renderScale)
- columnWidthProvider: function(col) { return document ? pageHolderWidth + vscroll.width + 2 : 0 }
- rowHeightProvider: function(row) { return (rot90 ? document.pagePointSize(row).width : document.pagePointSize(row).height) * root.renderScale }
+ property size firstPagePointSize: root.document?.status === PdfDocument.Ready ? root.document.pagePointSize(0) : Qt.size(1, 1)
+ property real pageHolderWidth: Math.max(root.width, ((rot90 ? root.document?.maxPageHeight : root.document?.maxPageWidth) ?? 0) * root.renderScale)
+ columnWidthProvider: function(col) { return root.document ? pageHolderWidth + vscroll.width + 2 : 0 }
+ rowHeightProvider: function(row) { return (rot90 ? root.document.pagePointSize(row).width : root.document.pagePointSize(row).height) * root.renderScale }
+
+ // delayed-jump feature in case the user called goToPage() or goToLocation() too early
+ property int pendingRow: -1
+ property point pendingLocation
+ property real pendingZoom: -1
+ onRowsChanged: {
+ if (rows > 0 && tableView.pendingRow >= 0) {
+ console.log(lcMPV, "initiating delayed jump to page", tableView.pendingRow, "loc", tableView.pendingLocation, "zoom", tableView.pendingZoom)
+ root.goToLocation(tableView.pendingRow, tableView.pendingLocation, tableView.pendingZoom)
+ tableView.pendingRow = -1
+ tableView.pendingLocation = Qt.point(-1, -1)
+ tableView.pendingZoom = -1
+ }
+ }
+
delegate: Rectangle {
id: pageHolder
+ required property int index
color: tableView.debug ? "beige" : "transparent"
Text {
visible: tableView.debug
@@ -372,12 +351,12 @@ Item {
height: image.height
rotation: root.pageRotation
anchors.centerIn: pinch.active ? undefined : parent
- property size pagePointSize: document.pagePointSize(index)
+ property size pagePointSize: root.document.pagePointSize(pageHolder.index)
property real pageScale: image.paintedWidth / pagePointSize.width
PdfPageImage {
id: image
document: root.document
- currentPage: index
+ currentFrame: pageHolder.index
asynchronous: true
fillMode: Image.PreserveAspectFit
width: paper.pagePointSize.width * root.renderScale
@@ -391,7 +370,7 @@ Item {
searchHighlights.update()
}
onStatusChanged: {
- if (index === navigationStack.currentPage)
+ if (pageHolder.index === pageNavigator.currentPage)
root.currentPageRenderingStatus = status
}
}
@@ -407,7 +386,7 @@ Item {
id: searchHighlights
function update() {
// paths could be a binding, but we need to be able to "kick" it sometimes
- paths = searchModel.boundingPolygonsOnPage(index)
+ paths = searchModel.boundingPolygonsOnPage(pageHolder.index)
}
}
}
@@ -428,7 +407,7 @@ Item {
}
Shape {
anchors.fill: parent
- visible: image.status === Image.Ready && searchModel.currentPage === index
+ visible: image.status === Image.Ready && searchModel.currentPage === pageHolder.index
ShapePath {
strokeWidth: style.currentSearchResultStrokeWidth
strokeColor: style.currentSearchResultStrokeColor
@@ -441,11 +420,10 @@ Item {
}
PinchHandler {
id: pinch
- minimumScale: 0.1
- maximumScale: root.renderScale < 4 ? 2 : 1
+ minimumScale: tableView.minScale / root.renderScale
+ maximumScale: Math.max(1, tableView.maxScale / root.renderScale)
minimumRotation: root.pageRotation
maximumRotation: root.pageRotation
- enabled: image.sourceSize.width < 5000
onActiveChanged:
if (active) {
paper.z = 10
@@ -456,12 +434,16 @@ Item {
const centroidInFlickable = tableView.mapFromItem(paper, pinch.centroid.position.x, pinch.centroid.position.y)
const newSourceWidth = image.sourceSize.width * paper.scale
const ratio = newSourceWidth / image.sourceSize.width
- console.log(lcMPV, "pinch ended on page", index, "with centroid", pinch.centroid.position, centroidInPoints, "wrt flickable", centroidInFlickable,
+ console.log(lcMPV, "pinch ended on page", pageHolder.index,
+ "with scale", paper.scale.toFixed(3), "ratio", ratio.toFixed(3),
+ "centroid", pinch.centroid.position, centroidInPoints,
+ "wrt flickable", centroidInFlickable,
"page at", pageHolder.x.toFixed(2), pageHolder.y.toFixed(2),
"contentX/Y were", tableView.contentX.toFixed(2), tableView.contentY.toFixed(2))
if (ratio > 1.1 || ratio < 0.9) {
const centroidOnPage = Qt.point(centroidInPoints.x * root.renderScale * ratio, centroidInPoints.y * root.renderScale * ratio)
paper.scale = 1
+ pinch.persistentScale = 1
paper.x = 0
paper.y = 0
root.renderScale *= ratio
@@ -506,57 +488,31 @@ Item {
model: PdfLinkModel {
id: linkModel
document: root.document
- page: image.currentPage
+ page: image.currentFrame
}
- delegate: Shape {
- required property rect rect
- required property url url
- required property int page
- required property point location
- required property real zoom
- x: rect.x * paper.pageScale
- y: rect.y * paper.pageScale
- width: rect.width * paper.pageScale
- height: rect.height * paper.pageScale
+ delegate: PdfLinkDelegate {
+ x: rectangle.x * paper.pageScale
+ y: rectangle.y * paper.pageScale
+ width: rectangle.width * paper.pageScale
+ height: rectangle.height * paper.pageScale
visible: image.status === Image.Ready
- ShapePath {
- strokeWidth: style.linkUnderscoreStrokeWidth
- strokeColor: style.linkUnderscoreColor
- strokeStyle: style.linkUnderscoreStrokeStyle
- dashPattern: style.linkUnderscoreDashPattern
- startX: 0; startY: height
- PathLine { x: width; y: height }
- }
- HoverHandler {
- id: linkHH
- cursorShape: Qt.PointingHandCursor
- }
- TapHandler {
- onTapped: {
- if (page >= 0)
- root.goToLocation(page, location, zoom)
+ onTapped:
+ (link) => {
+ if (link.page >= 0)
+ root.goToLocation(link.page, link.location, link.zoom)
else
Qt.openUrlExternally(url)
}
- }
- ToolTip {
- visible: linkHH.hovered
- delay: 1000
- text: page >= 0 ?
- ("page " + (page + 1) +
- " location " + location.x.toFixed(1) + ", " + location.y.toFixed(1) +
- " zoom " + zoom) : url
- }
}
}
PdfSelection {
id: selection
anchors.fill: parent
document: root.document
- page: image.currentPage
+ page: image.currentFrame
renderScale: image.renderScale
- fromPoint: textSelectionDrag.centroid.pressPosition
- toPoint: textSelectionDrag.centroid.position
+ from: textSelectionDrag.centroid.pressPosition
+ to: textSelectionDrag.centroid.position
hold: !textSelectionDrag.active && !mouseClickHandler.pressed
onTextChanged: root.selectedText = text
focus: true
@@ -575,7 +531,7 @@ Item {
? Qt.point((tableView.contentX - currentItem.x + tableView.jumpLocationMargin.x) / root.renderScale,
(tableView.contentY - currentItem.y + tableView.jumpLocationMargin.y) / root.renderScale)
: Qt.point(0, 0) // maybe the delegate wasn't loaded yet
- navigationStack.jump(cell.y, currentLocation, root.renderScale)
+ pageNavigator.jump(cell.y, currentLocation, root.renderScale)
}
onActiveChanged: if (!active ) {
// When the scrollbar stops moving, tell navstack where we are, so as to update currentPage etc.
@@ -585,64 +541,75 @@ Item {
? Qt.point((tableView.contentX - currentItem.x + tableView.jumpLocationMargin.x) / root.renderScale,
(tableView.contentY - currentItem.y + tableView.jumpLocationMargin.y) / root.renderScale)
: Qt.point(0, 0) // maybe the delegate wasn't loaded yet
- navigationStack.update(cell.y, currentLocation, root.renderScale)
+ pageNavigator.update(cell.y, currentLocation, root.renderScale)
}
}
ScrollBar.horizontal: ScrollBar { }
}
onRenderScaleChanged: {
- // if navigationStack.jumped changes the scale, don't turn around and update the stack again;
+ // if pageNavigator.jumped changes the scale, don't turn around and update the stack again;
// and don't force layout either, because positionViewAtCell() will do that
- if (navigationStack.jumping)
+ if (pageNavigator.jumping)
return
- // make TableView rebuild from scratch, because otherwise it doesn't know the delegates are changing size
- tableView.rebuild()
+ // page size changed: TableView needs to redo layout to avoid overlapping delegates or gaps between them
+ tableView.forceLayout()
const cell = tableView.cellAtPos(root.width / 2, root.height / 2)
const currentItem = tableView.itemAtCell(cell)
if (currentItem) {
const currentLocation = Qt.point((tableView.contentX - currentItem.x + tableView.jumpLocationMargin.x) / root.renderScale,
(tableView.contentY - currentItem.y + tableView.jumpLocationMargin.y) / root.renderScale)
- navigationStack.update(cell.y, currentLocation, renderScale)
+ pageNavigator.update(cell.y, currentLocation, renderScale)
}
}
- PdfNavigationStack {
- id: navigationStack
+ PdfPageNavigator {
+ id: pageNavigator
property bool jumping: false
property int previousPage: 0
- onJumped: function(page, location, zoom) {
+ onJumped: function(current) {
jumping = true
- root.renderScale = zoom
- if (location.y < 0) {
+ if (current.zoom > 0)
+ root.renderScale = current.zoom
+ const pageSize = root.document.pagePointSize(current.page)
+ if (current.location.y < 0) {
// invalid to indicate that a specific location was not needed,
// so attempt to position the new page just as the current page is
const previousPageDelegate = tableView.itemAtCell(0, previousPage)
const currentYOffset = previousPageDelegate
? tableView.contentY - previousPageDelegate.y
: 0
- tableView.positionViewAtRow(page, Qt.AlignTop, currentYOffset)
- console.log(lcMPV, "going from page", previousPage, "to", page, "offset", currentYOffset,
+ tableView.positionViewAtRow(current.page, Qt.AlignTop, currentYOffset)
+ console.log(lcMPV, "going from page", previousPage, "to", current.page, "offset", currentYOffset,
+ "ended up @", tableView.contentX.toFixed(1) + ", " + tableView.contentY.toFixed(1))
+ } else if (current.rectangles.length > 0) {
+ // jump to a search result and position the covered area within the viewport
+ pageSize.width *= root.renderScale
+ pageSize.height *= root.renderScale
+ const rectPts = current.rectangles[0]
+ const rectPx = Qt.rect(rectPts.x * root.renderScale - tableView.jumpLocationMargin.x,
+ rectPts.y * root.renderScale - tableView.jumpLocationMargin.y,
+ rectPts.width * root.renderScale + tableView.jumpLocationMargin.x * 2,
+ rectPts.height * root.renderScale + tableView.jumpLocationMargin.y * 2)
+ tableView.positionViewAtCell(0, current.page, TableView.Contain, Qt.point(0, 0), rectPx)
+ console.log(lcMPV, "going to zoom", root.renderScale, "rect", rectPx, "on page", current.page,
"ended up @", tableView.contentX.toFixed(1) + ", " + tableView.contentY.toFixed(1))
} else {
// jump to a page and position the given location relative to the top-left corner of the viewport
- var pageSize = root.document.pagePointSize(page)
pageSize.width *= root.renderScale
pageSize.height *= root.renderScale
- const xOffsetLimit = Math.max(0, pageSize.width - root.width)
- const offset = Qt.point(Math.max(-xOffsetLimit, Math.min(xOffsetLimit,
- location.x * root.renderScale - tableView.jumpLocationMargin.x)),
- Math.max(0, location.y * root.renderScale - tableView.jumpLocationMargin.y))
- tableView.positionViewAtCell(0, page, Qt.AlignLeft | Qt.AlignTop, offset)
- console.log(lcMPV, "going to zoom", zoom, "loc", location, "on page", page,
+ const rectPx = Qt.rect(current.location.x * root.renderScale - tableView.jumpLocationMargin.x,
+ current.location.y * root.renderScale - tableView.jumpLocationMargin.y,
+ tableView.jumpLocationMargin.x * 2, tableView.jumpLocationMargin.y * 2)
+ tableView.positionViewAtCell(0, current.page, TableView.AlignLeft | TableView.AlignTop, Qt.point(0, 0), rectPx)
+ console.log(lcMPV, "going to zoom", root.renderScale, "loc", current.location, "on page", current.page,
"ended up @", tableView.contentX.toFixed(1) + ", " + tableView.contentY.toFixed(1))
}
jumping = false
- previousPage = page
+ previousPage = current.page
}
- onCurrentPageChanged: searchModel.currentPage = currentPage
property url documentSource: root.document.source
onDocumentSourceChanged: {
- navigationStack.clear()
+ pageNavigator.clear()
root.resetScale()
tableView.contentX = 0
tableView.contentY = 0
@@ -651,8 +618,6 @@ Item {
PdfSearchModel {
id: searchModel
document: root.document === undefined ? null : root.document
- // TODO maybe avoid jumping if the result is already fully visible in the viewport
- onCurrentResultBoundingRectChanged: root.goToLocation(currentPage,
- Qt.point(currentResultBoundingRect.x, currentResultBoundingRect.y), 0)
+ onCurrentResultChanged: pageNavigator.jump(currentResultLink)
}
}