diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2020-02-07 14:54:31 +0100 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2020-02-11 08:30:06 +0100 |
commit | e5a33355798d3277c631b0024f389cdca2f2c683 (patch) | |
tree | bf4ee5e91241a9cafd8dfe185a1174fb27f3114f | |
parent | 7afe95f14d7d048a73baa12b3bd5f6a9bcea2ccb (diff) |
PDF multipage viewer: iterate search results
This version still has separate PdfSearchModel instances on each page,
but now there are buttons to iterate and highlight the search results
in order. When you come to the last result on one page, hitting the
"Find Next" button will jump to the next page, and keep jumping forward
from there until another result is found. Unfortunately this jumping
takes time if it skips over a lot of pages because of empty search
results. That seems to be another reason to make PdfSearchModel into a
whole-document search model and use one instance.
Also reorganize PdfMultiPageView.qml's public API into sections
according to functionality rather than by type.
Change-Id: I677a764fcbf231b2656aff8abe7240a27582a696
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
-rw-r--r-- | examples/pdf/multipage/resources/go-down-search.svg | 13 | ||||
-rw-r--r-- | examples/pdf/multipage/resources/go-up-search.svg | 8 | ||||
-rw-r--r-- | examples/pdf/multipage/viewer.qml | 81 | ||||
-rw-r--r-- | examples/pdf/multipage/viewer.qrc | 2 | ||||
-rw-r--r-- | src/pdf/quick/qml/PdfMultiPageView.qml | 100 |
5 files changed, 148 insertions, 56 deletions
diff --git a/examples/pdf/multipage/resources/go-down-search.svg b/examples/pdf/multipage/resources/go-down-search.svg new file mode 100644 index 000000000..ae17ab93b --- /dev/null +++ b/examples/pdf/multipage/resources/go-down-search.svg @@ -0,0 +1,13 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + <defs id="defs3051"> + <style type="text/css" id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + </defs> + <path style="fill:currentColor;fill-opacity:1;stroke:none" + d="M 4.7070312 8 L 4 8.7070312 L 10.125 14.832031 L 12 16.707031 L 13.875 14.832031 L 20 8.7070312 L 19.292969 8 L 13.167969 14.125 L 12 15.292969 L 10.832031 14.125 L 4.7070312 8 z " + class="ColorScheme-Text" + /> +</svg> diff --git a/examples/pdf/multipage/resources/go-up-search.svg b/examples/pdf/multipage/resources/go-up-search.svg new file mode 100644 index 000000000..5cc155873 --- /dev/null +++ b/examples/pdf/multipage/resources/go-up-search.svg @@ -0,0 +1,8 @@ +<svg height="24" width="24" xmlns="http://www.w3.org/2000/svg"> + <style type="text/css" id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + <path d="M4.707 16L4 15.293l8-8 8 8-.707.707L12 8.707" class="ColorScheme-Text" fill="currentColor"/> +</svg> diff --git a/examples/pdf/multipage/viewer.qml b/examples/pdf/multipage/viewer.qml index bbc28cd8d..3d9cd371a 100644 --- a/examples/pdf/multipage/viewer.qml +++ b/examples/pdf/multipage/viewer.qml @@ -167,30 +167,6 @@ ApplicationWindow { onTriggered: view.copySelectionToClipboard() } } - TextField { - id: searchField - placeholderText: "search" - Layout.minimumWidth: 200 - Layout.fillWidth: true - Image { - visible: searchField.text !== "" - source: "resources/edit-clear.svg" - anchors { - right: parent.right - top: parent.top - bottom: parent.bottom - margins: 3 - rightMargin: 5 - } - TapHandler { - onTapped: searchField.clear() - } - } - } - Shortcut { - sequence: StandardKey.Find - onActivated: searchField.forceActiveFocus() - } Shortcut { sequence: StandardKey.Quit onActivated: Qt.quit() @@ -259,6 +235,63 @@ ApplicationWindow { onCurrentPageChanged: currentPageSB.value = view.currentPage + 1 } + Drawer { + id: searchDrawer + edge: Qt.BottomEdge + x: 20 + width: searchLayout.implicitWidth + height: searchLayout.implicitHeight + dim: false + Shortcut { + sequence: StandardKey.Find + onActivated: { + searchDrawer.open() + searchField.forceActiveFocus() + } + } + RowLayout { + id: searchLayout + ToolButton { + action: Action { + icon.source: "resources/go-up-search.svg" + onTriggered: view.searchBack() + } + ToolTip.visible: enabled && hovered + ToolTip.delay: 2000 + ToolTip.text: "find previous" + } + TextField { + id: searchField + placeholderText: "search" + Layout.minimumWidth: 200 + Layout.fillWidth: true + Image { + visible: searchField.text !== "" + source: "resources/edit-clear.svg" + anchors { + right: parent.right + top: parent.top + bottom: parent.bottom + margins: 3 + rightMargin: 5 + } + TapHandler { + onTapped: searchField.clear() + } + } + } + ToolButton { + action: Action { + icon.source: "resources/go-down-search.svg" + onTriggered: view.searchForward() + } + ToolTip.visible: enabled && hovered + ToolTip.delay: 2000 + ToolTip.text: "find next" + } + } + } + footer: ToolBar { height: statusLabel.implicitHeight * 1.5 Label { diff --git a/examples/pdf/multipage/viewer.qrc b/examples/pdf/multipage/viewer.qrc index fa3561caf..9698a2689 100644 --- a/examples/pdf/multipage/viewer.qrc +++ b/examples/pdf/multipage/viewer.qrc @@ -3,8 +3,10 @@ <file>viewer.qml</file> <file>resources/edit-clear.svg</file> <file>resources/edit-copy.svg</file> + <file>resources/go-down-search.svg</file> <file>resources/go-next-view-page.svg</file> <file>resources/go-previous-view-page.svg</file> + <file>resources/go-up-search.svg</file> <file>resources/rotate-left.svg</file> <file>resources/rotate-right.svg</file> <file>resources/zoom-in.svg</file> diff --git a/src/pdf/quick/qml/PdfMultiPageView.qml b/src/pdf/quick/qml/PdfMultiPageView.qml index acef9fbea..bc5134267 100644 --- a/src/pdf/quick/qml/PdfMultiPageView.qml +++ b/src/pdf/quick/qml/PdfMultiPageView.qml @@ -58,32 +58,33 @@ Item { // public API // TODO 5.15: required property property var document: undefined - property real renderScale: 1 - property real pageRotation: 0 - property string searchString + property string selectedText - property alias currentPage: navigationStack.currentPage function copySelectionToClipboard() { if (listView.currentItem !== null) listView.currentItem.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 resetScale() { - root.renderScale = 1 + 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 + function resetScale() { root.renderScale = 1 } function scaleToWidth(width, height) { root.renderScale = width / (listView.rot90 ? listView.firstPagePointSize.height : listView.firstPagePointSize.width) } - function scaleToPage(width, height) { var windowAspect = width / height var pageAspect = listView.firstPagePointSize.width / listView.firstPagePointSize.height @@ -102,14 +103,39 @@ Item { } } - function goToPage(page) { - goToLocation(page, Qt.point(0, 0), 0) + // text search + property alias searchString: searchModel.searchString + property bool searchBackEnabled: searchModel.currentResult > 0 + property bool searchForwardEnabled: searchModel.currentResult < searchModel.matchGeometry.length - 1 + function searchBack() { + if (searchModel.currentResult > 0) { + --searchModel.currentResult + } else { + searchModel.deferRendering = true // save time while we are searching + while (searchModel.currentResult <= 0) { + if (navigationStack.currentPage > 0) + goToPage(navigationStack.currentPage - 1) + else + goToPage(document.pageCount - 1) + searchModel.currentResult = searchModel.matchGeometry.length - 1 + } + searchModel.deferRendering = false + } } - - function goToLocation(page, location, zoom) { - if (zoom > 0) - root.renderScale = zoom - navigationStack.push(page, location, zoom) + function searchForward() { + if (searchModel.currentResult < searchModel.matchGeometry.length - 1) { + ++searchModel.currentResult + } else { + searchModel.deferRendering = true // save time while we are searching + while (searchModel.currentResult >= searchModel.matchGeometry.length - 1) { + searchModel.currentResult = 0 + if (navigationStack.currentPage < document.pageCount - 1) + goToPage(navigationStack.currentPage + 1) + else + goToPage(0) + } + searchModel.deferRendering = false + } } id: root @@ -133,7 +159,7 @@ Item { property real pageScale: image.paintedWidth / pagePointSize.width Image { id: image - source: document.source + source: searchModel.deferRendering ? "" : document.source currentFrame: index asynchronous: true fillMode: Image.PreserveAspectFit @@ -152,18 +178,26 @@ Item { Shape { anchors.fill: parent opacity: 0.25 - visible: image.status === Image.Ready + visible: image.status === Image.Ready && searchModel.page == index ShapePath { strokeWidth: 1 - strokeColor: "blue" - fillColor: "cyan" + strokeColor: "steelblue" + fillColor: "lightsteelblue" scale: Qt.size(paper.pageScale, paper.pageScale) PathMultiline { - id: searchResultBoundaries paths: searchModel.matchGeometry } } ShapePath { + strokeWidth: 1 + strokeColor: "blue" + fillColor: "cyan" + scale: Qt.size(paper.pageScale, paper.pageScale) + PathPolyline { + path: searchModel.matchGeometry[searchModel.currentResult] + } + } + ShapePath { fillColor: "orange" scale: Qt.size(paper.pageScale, paper.pageScale) PathMultiline { @@ -172,12 +206,6 @@ Item { } } } - PdfSearchModel { - id: searchModel - document: root.document - page: image.currentFrame - searchString: root.searchString - } PdfSelection { id: selection document: root.document @@ -274,4 +302,12 @@ Item { onCurrentZoomChanged: root.renderScale = currentZoom // TODO deal with horizontal location (need another Flickable probably) } + PdfSearchModel { + id: searchModel + document: root.document === undefined ? null : root.document + page: navigationStack.currentPage + searchString: root.searchString + property int currentResult: 0 + property bool deferRendering: false + } } |