From 29cb44ee471908ac7c4cac70e3defb8bd72a80cd Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 21 Feb 2020 23:17:43 +0100 Subject: PdfScrollablePageView: retain position after pinch zoom If you do the pinch on a trackpad, the mouse cursor doesn't move, and the same visible part of the page will remain under the cursor after the page is re-rendered. Likewise when doing the pinch on a touchscreen, the centroid (midpoint between the two fingers) is held in place. This is achieved by moving the scrollbars afterwards, subject to their constraints. Change-Id: I34caca4ebbb5c11007dd2462416a42c1a2d8e104 Reviewed-by: Shawn Rutledge --- src/pdf/quick/qml/PdfScrollablePageView.qml | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/pdf/quick/qml/PdfScrollablePageView.qml b/src/pdf/quick/qml/PdfScrollablePageView.qml index 55aa44bbf..ae7bdb6f4 100644 --- a/src/pdf/quick/qml/PdfScrollablePageView.qml +++ b/src/pdf/quick/qml/PdfScrollablePageView.qml @@ -243,15 +243,29 @@ Flickable { enabled: image.sourceSize.width < 5000 onActiveChanged: if (!active) { + var centroidInPoints = Qt.point(pinch.centroid.position.x / root.renderScale, + pinch.centroid.position.y / root.renderScale) + var centroidInFlickable = root.mapFromItem(paper, pinch.centroid.position.x, pinch.centroid.position.y) var newSourceWidth = image.sourceSize.width * paper.scale var ratio = newSourceWidth / image.sourceSize.width + if (root.debug) + console.log("pinch ended with centroid", pinch.centroid.position, centroidInPoints, "wrt flickable", centroidInFlickable, + "page at", paper.x.toFixed(2), paper.y.toFixed(2), + "contentX/Y were", root.contentX.toFixed(2), root.contentY.toFixed(2)) if (ratio > 1.1 || ratio < 0.9) { + var centroidOnPage = Qt.point(centroidInPoints.x * root.renderScale * ratio, centroidInPoints.y * root.renderScale * ratio) paper.scale = 1 - root.renderScale *= ratio + paper.x = 0 + paper.y = 0 + root.contentX = centroidOnPage.x - centroidInFlickable.x + root.contentY = centroidOnPage.y - centroidInFlickable.y + root.renderScale *= ratio // onRenderScaleChanged calls navigationStack.update() so we don't need to here + if (root.debug) + console.log("contentX/Y adjusted to", root.contentX.toFixed(2), root.contentY.toFixed(2)) + } else { + paper.x = 0 + paper.y = 0 } - // TODO adjust contentX/Y to position the page so the same region is visible - paper.x = 0 - paper.y = 0 } grabPermissions: PointerHandler.CanTakeOverFromAnything } -- cgit v1.2.3 From 0d7c2b4a84feec5f65f440dd4a537c8946179314 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 21 Feb 2020 23:51:11 +0100 Subject: PdfScrollablePageView: move selections and links inside the image Now that the Image is being rotated and the "paper" rectangle is not, we need to work in the right coordinate system for rendering of Shapes on top, and for handling drags to select text. Change-Id: I6bc1e2fe7997ffc4673720abdcd3b7e4d9bc9012 Reviewed-by: Shawn Rutledge --- src/pdf/quick/qml/PdfScrollablePageView.qml | 120 +++++++++++++--------------- 1 file changed, 57 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/src/pdf/quick/qml/PdfScrollablePageView.qml b/src/pdf/quick/qml/PdfScrollablePageView.qml index ae7bdb6f4..e27b21d14 100644 --- a/src/pdf/quick/qml/PdfScrollablePageView.qml +++ b/src/pdf/quick/qml/PdfScrollablePageView.qml @@ -175,63 +175,72 @@ Flickable { 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 + 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 { + 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 + 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) + 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) + } } } } + DragHandler { + id: textSelectionDrag + acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus + target: null + } + TapHandler { + id: tapHandler + acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus + } } PinchHandler { @@ -269,20 +278,5 @@ Flickable { } 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 - } } } -- cgit v1.2.3 From dbf1be9c98337701b18dae66892f645c51b8bd22 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 24 Feb 2020 21:19:14 +0100 Subject: PdfMultiPageView: retain position after pinch zoom If you do the pinch on a trackpad, the mouse cursor doesn't move, and the same visible part of the page will remain under the cursor after the page is re-rendered. Likewise when doing the pinch on a touchscreen, the centroid (midpoint between the two fingers) is held in place, subject to the constraints that Flickable.returnToBounds() imposes. Similar to 29cb44ee471908ac7c4cac70e3defb8bd72a80cd. Also corrected the horizontal scrolling range according to rotation; added left and right margins so that the page edges are more visible when the page is wider than the view, and thus the horizontal scrolling range feels more accurate while the scrollbars are still shown. Change-Id: Ia2a15eee5bcd5c9e7f025634f02a5a546e6aab68 Reviewed-by: Shawn Rutledge --- src/pdf/quick/qml/PdfMultiPageView.qml | 43 ++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/pdf/quick/qml/PdfMultiPageView.qml b/src/pdf/quick/qml/PdfMultiPageView.qml index e8eccaf3b..cb4419b4d 100644 --- a/src/pdf/quick/qml/PdfMultiPageView.qml +++ b/src/pdf/quick/qml/PdfMultiPageView.qml @@ -121,13 +121,14 @@ Item { TableView { id: tableView anchors.fill: parent + anchors.leftMargin: 2 model: root.document === undefined ? 0 : root.document.pageCount rowSpacing: 6 - property real rotationModulus: Math.abs(root.pageRotation % 180) - property bool rot90: rotationModulus > 45 && rotationModulus < 135 + property real rotationNorm: Math.round((360 + (root.pageRotation % 360)) % 360) + property bool rot90: rotationNorm == 90 || rotationNorm == 270 onRot90Changed: forceLayout() property size firstPagePointSize: document === undefined ? Qt.size(0, 0) : document.pagePointSize(0) - contentWidth: document === undefined ? 0 : document.maxPageWidth * root.renderScale + contentWidth: document === undefined ? 0 : (rot90 ? document.maxPageHeight : document.maxPageWidth) * root.renderScale + vscroll.width + 2 // workaround for missing function (see https://codereview.qt-project.org/c/qt/qtdeclarative/+/248464) function itemAtPos(x, y, includeSpacing) { // we don't care about x (assume col 0), and assume includeSpacing is true @@ -139,7 +140,7 @@ Item { if (child.y < y && (!ret || child.y > ret.y)) ret = child } - if (root.debug) + if (root.debug && ret !== null) console.log("given y", y, "found", ret, "@", ret.y) return ret // the delegate with the largest y that is less than the given y } @@ -164,7 +165,7 @@ Item { width: image.width height: image.height rotation: root.pageRotation - anchors.centerIn: parent + anchors.centerIn: pinch.active ? undefined : parent property size pagePointSize: document.pagePointSize(index) property real pageScale: image.paintedWidth / pagePointSize.width Image { @@ -223,19 +224,46 @@ Item { id: pinch minimumScale: 0.1 maximumScale: root.renderScale < 4 ? 2 : 1 - minimumRotation: 0 - maximumRotation: 0 + minimumRotation: root.pageRotation + maximumRotation: root.pageRotation enabled: image.sourceSize.width < 5000 onActiveChanged: if (active) { paper.z = 10 } else { paper.z = 0 + var centroidInPoints = Qt.point(pinch.centroid.position.x / root.renderScale, + pinch.centroid.position.y / root.renderScale) + var centroidInFlickable = tableView.mapFromItem(paper, pinch.centroid.position.x, pinch.centroid.position.y) var newSourceWidth = image.sourceSize.width * paper.scale var ratio = newSourceWidth / image.sourceSize.width + if (root.debug) + console.log("pinch ended on page", index, "with 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) { + var centroidOnPage = Qt.point(centroidInPoints.x * root.renderScale * ratio, centroidInPoints.y * root.renderScale * ratio) paper.scale = 1 + paper.x = 0 + paper.y = 0 root.renderScale *= ratio + tableView.forceLayout() + if (tableView.rotationNorm == 0) { + tableView.contentX = pageHolder.x + tableView.originX + centroidOnPage.x - centroidInFlickable.x + tableView.contentY = pageHolder.y + tableView.originY + centroidOnPage.y - centroidInFlickable.y + } else if (tableView.rotationNorm == 90) { + tableView.contentX = pageHolder.x + tableView.originX + image.height - centroidOnPage.y - centroidInFlickable.x + tableView.contentY = pageHolder.y + tableView.originY + centroidOnPage.x - centroidInFlickable.y + } else if (tableView.rotationNorm == 180) { + tableView.contentX = pageHolder.x + tableView.originX + image.width - centroidOnPage.x - centroidInFlickable.x + tableView.contentY = pageHolder.y + tableView.originY + image.height - centroidOnPage.y - centroidInFlickable.y + } else if (tableView.rotationNorm == 270) { + tableView.contentX = pageHolder.x + tableView.originX + centroidOnPage.y - centroidInFlickable.x + tableView.contentY = pageHolder.y + tableView.originY + image.width - centroidOnPage.x - centroidInFlickable.y + } + if (root.debug) + console.log("contentX/Y adjusted to", tableView.contentX.toFixed(2), tableView.contentY.toFixed(2), "y @top", pageHolder.y) + tableView.returnToBounds() } } grabPermissions: PointerHandler.CanTakeOverFromAnything @@ -298,6 +326,7 @@ Item { } } ScrollBar.vertical: ScrollBar { + id: vscroll property bool moved: false onPositionChanged: moved = true onActiveChanged: { -- cgit v1.2.3 From 8091944ae8ccfb4e0b3e7518912802c9c75704d0 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 25 Feb 2020 22:37:09 +0100 Subject: PdfMultiPageView: highlight current search result when tapped in list Change-Id: Ib0a1aeac28350c8705899ab799ce776e6764b306 Reviewed-by: Shawn Rutledge --- src/pdf/quick/qml/PdfMultiPageView.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/pdf/quick/qml/PdfMultiPageView.qml b/src/pdf/quick/qml/PdfMultiPageView.qml index cb4419b4d..d50763a64 100644 --- a/src/pdf/quick/qml/PdfMultiPageView.qml +++ b/src/pdf/quick/qml/PdfMultiPageView.qml @@ -84,6 +84,7 @@ Item { if (zoom > 0) root.renderScale = zoom navigationStack.push(page, location, zoom) + searchModel.currentPage = page } // page scaling @@ -366,6 +367,6 @@ Item { PdfSearchModel { id: searchModel document: root.document === undefined ? null : root.document - onCurrentPageChanged: root.goToPage(currentPage) + onCurrentPageChanged: if (currentPage != navigationStack.currentPage) root.goToPage(currentPage) } } -- cgit v1.2.3 From 926afcaa6f3ce02b51cd535d82454f4b25e0b7f6 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 25 Feb 2020 17:49:56 +0100 Subject: PdfSearchModel: provide ContextBefore and ContextAfter ...as separate roles, to make alignment easier, and to avoid hard-coding HTML tags in the Context role as it was before. But the strings in these context roles are not always adjacent to the search results in geometric coordinates sometimes, in some PDF files, despite having adjacent character indices. I.e. the "next" character after the search string, or the "previous" character before it, could be anywhere on the page. Change-Id: Ief0a490b64fdb3c3ca98506926650648b609ece1 Reviewed-by: Shawn Rutledge --- src/pdf/api/qpdfsearchmodel.h | 3 ++- src/pdf/api/qpdfsearchresult.h | 8 +++++--- src/pdf/api/qpdfsearchresult_p.h | 8 +++++--- src/pdf/qpdfsearchmodel.cpp | 42 ++++++++++++++++++++++++++-------------- src/pdf/qpdfsearchresult.cpp | 16 ++++++++++----- 5 files changed, 51 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/pdf/api/qpdfsearchmodel.h b/src/pdf/api/qpdfsearchmodel.h index cc91e214a..f1593b64b 100644 --- a/src/pdf/api/qpdfsearchmodel.h +++ b/src/pdf/api/qpdfsearchmodel.h @@ -58,7 +58,8 @@ public: Page = Qt::UserRole, IndexOnPage, Location, - Context, + ContextBefore, + ContextAfter, _Count }; Q_ENUM(Role) diff --git a/src/pdf/api/qpdfsearchresult.h b/src/pdf/api/qpdfsearchresult.h index db7af3dd9..84a2f2343 100644 --- a/src/pdf/api/qpdfsearchresult.h +++ b/src/pdf/api/qpdfsearchresult.h @@ -50,18 +50,20 @@ class QPdfSearchResultPrivate; class Q_PDF_EXPORT QPdfSearchResult : public QPdfDestination { Q_GADGET - Q_PROPERTY(QString context READ context) + Q_PROPERTY(QString contextBefore READ contextBefore) + Q_PROPERTY(QString contextAfter READ contextAfter) Q_PROPERTY(QVector rectangles READ rectangles) public: QPdfSearchResult(); ~QPdfSearchResult() {} - QString context() const; + QString contextBefore() const; + QString contextAfter() const; QVector rectangles() const; private: - QPdfSearchResult(int page, QVector rects, QString context); + QPdfSearchResult(int page, QVector rects, QString contextBefore, QString contextAfter); QPdfSearchResult(QPdfSearchResultPrivate *d); friend class QPdfDocument; friend class QPdfSearchModelPrivate; diff --git a/src/pdf/api/qpdfsearchresult_p.h b/src/pdf/api/qpdfsearchresult_p.h index a0f8e4457..615dce4e0 100644 --- a/src/pdf/api/qpdfsearchresult_p.h +++ b/src/pdf/api/qpdfsearchresult_p.h @@ -56,12 +56,14 @@ class QPdfSearchResultPrivate : public QPdfDestinationPrivate { public: QPdfSearchResultPrivate() = default; - QPdfSearchResultPrivate(int page, QVector rects, QString context) : + QPdfSearchResultPrivate(int page, QVector rects, QString contextBefore, QString contextAfter) : QPdfDestinationPrivate(page, rects.first().topLeft(), 0), - context(context), + contextBefore(contextBefore), + contextAfter(contextAfter), rects(rects) {} - QString context; + QString contextBefore; + QString contextAfter; QVector rects; }; diff --git a/src/pdf/qpdfsearchmodel.cpp b/src/pdf/qpdfsearchmodel.cpp index 4129c7cb7..27b7833fc 100644 --- a/src/pdf/qpdfsearchmodel.cpp +++ b/src/pdf/qpdfsearchmodel.cpp @@ -52,7 +52,7 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(qLcS, "qt.pdf.search") static const int UpdateTimerInterval = 100; -static const int ContextChars = 20; +static const int ContextChars = 64; static const double CharacterHitTolerance = 6.0; QPdfSearchModel::QPdfSearchModel(QObject *parent) @@ -95,13 +95,19 @@ QVariant QPdfSearchModel::data(const QModelIndex &index, int role) const return pi.index; case Role::Location: return d->searchResults[pi.page][pi.index].location(); - case Role::Context: - return d->searchResults[pi.page][pi.index].context(); + case Role::ContextBefore: + return d->searchResults[pi.page][pi.index].contextBefore(); + case Role::ContextAfter: + return d->searchResults[pi.page][pi.index].contextAfter(); case Role::_Count: break; } - if (role == Qt::DisplayRole) - return d->searchResults[pi.page][pi.index].context(); + if (role == Qt::DisplayRole) { + const QString ret = d->searchResults[pi.page][pi.index].contextBefore() + + QLatin1String("") + d->searchString + QLatin1String("") + + d->searchResults[pi.page][pi.index].contextAfter(); + return ret; + } return QVariant(); } @@ -124,9 +130,9 @@ void QPdfSearchModel::setSearchString(QString searchString) return; d->searchString = searchString; - emit searchStringChanged(); beginResetModel(); d->clearResults(); + emit searchStringChanged(); endResetModel(); } @@ -161,8 +167,8 @@ void QPdfSearchModel::setDocument(QPdfDocument *document) return; d->document = document; - emit documentChanged(); d->clearResults(); + emit documentChanged(); } void QPdfSearchModel::timerEvent(QTimerEvent *event) @@ -179,7 +185,7 @@ void QPdfSearchModel::timerEvent(QTimerEvent *event) d->doSearch(d->nextPageToUpdate++); } -QPdfSearchModelPrivate::QPdfSearchModelPrivate() +QPdfSearchModelPrivate::QPdfSearchModelPrivate() : QAbstractItemModelPrivate() { } @@ -246,7 +252,7 @@ bool QPdfSearchModelPrivate::doSearch(int page) } qCDebug(qLcS) << rects.last() << "char idx" << startIndex << "->" << endIndex; } - QString context; + QString contextBefore, contextAfter; if (startIndex >= 0 || endIndex >= 0) { startIndex = qMax(0, startIndex - ContextChars); endIndex += ContextChars; @@ -255,13 +261,21 @@ bool QPdfSearchModelPrivate::doSearch(int page) QVector buf(count + 1); int len = FPDFText_GetText(textPage, startIndex, count, buf.data()); Q_ASSERT(len - 1 <= count); // len is number of characters written, including the terminator - context = QString::fromUtf16(buf.constData(), len - 1); - context = context.replace(QLatin1Char('\n'), QLatin1Char(' ')); - context = context.replace(searchString, - QLatin1String("") + searchString + QLatin1String("")); + QString context = QString::fromUtf16(buf.constData(), len - 1); + context = context.replace(QLatin1Char('\n'), QStringLiteral("\u23CE")); + context = context.remove(QLatin1Char('\r')); + // try to find the search string near the middle of the context if possible + int si = context.indexOf(searchString, ContextChars - 5, Qt::CaseInsensitive); + if (si < 0) + si = context.indexOf(searchString, Qt::CaseInsensitive); + if (si < 0) + qWarning() << "search string" << searchString << "not found in context" << context; + contextBefore = context.mid(0, si); + contextAfter = context.mid(si + searchString.length()); } } - newSearchResults << QPdfSearchResult(page, rects, context); + if (!rects.isEmpty()) + newSearchResults << QPdfSearchResult(page, rects, contextBefore, contextAfter); } FPDFText_FindClose(sh); FPDFText_ClosePage(textPage); diff --git a/src/pdf/qpdfsearchresult.cpp b/src/pdf/qpdfsearchresult.cpp index 1164a1d43..53da1c165 100644 --- a/src/pdf/qpdfsearchresult.cpp +++ b/src/pdf/qpdfsearchresult.cpp @@ -42,15 +42,20 @@ QT_BEGIN_NAMESPACE QPdfSearchResult::QPdfSearchResult() : QPdfSearchResult(new QPdfSearchResultPrivate()) { } -QPdfSearchResult::QPdfSearchResult(int page, QVector rects, QString context) : - QPdfSearchResult(new QPdfSearchResultPrivate(page, rects, context)) { } +QPdfSearchResult::QPdfSearchResult(int page, QVector rects, QString contextBefore, QString contextAfter) : + QPdfSearchResult(new QPdfSearchResultPrivate(page, rects, contextBefore, contextAfter)) { } QPdfSearchResult::QPdfSearchResult(QPdfSearchResultPrivate *d) : QPdfDestination(static_cast(d)) { } -QString QPdfSearchResult::context() const +QString QPdfSearchResult::contextBefore() const { - return static_cast(d.data())->context; + return static_cast(d.data())->contextBefore; +} + +QString QPdfSearchResult::contextAfter() const +{ + return static_cast(d.data())->contextAfter; } QVector QPdfSearchResult::rectangles() const @@ -63,7 +68,8 @@ QDebug operator<<(QDebug dbg, const QPdfSearchResult &searchResult) QDebugStateSaver saver(dbg); dbg.nospace(); dbg << "QPdfSearchResult(page=" << searchResult.page() - << " context=" << searchResult.context() + << " contextBefore=" << searchResult.contextBefore() + << " contextAfter=" << searchResult.contextAfter() << " rects=" << searchResult.rectangles(); dbg << ')'; return dbg; -- cgit v1.2.3 From 7e75e9b80249f553fcfbdb2716c93707b40f8c48 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 26 Feb 2020 11:09:09 +0100 Subject: PdfMultiPageView: update search highlights when results change In the single-page view, we bind to the PdfSearchModel.currentPageBoundingPolygons property, which is convenient because it has a changed signal, so it stays updated in all cases: when the user changes the search string, when a different document is loaded, or when changing the current page. In the multi-page view, we need to invoke a function to get the search results on each page, because results are different on each page, and we often need to show multiple pages at the same time. The challenge then is how to ensure that it gets re-evaluated often enough. It requires connecting to more than one signal; but there isn't any way to "kick" a binding when some signal occurs, so the solution isn't very declarative, unfortunately. Change-Id: I72e7dad01b8cb6c43abcccf4fa46e443409b39e0 Reviewed-by: Shawn Rutledge --- src/pdf/quick/qml/PdfMultiPageView.qml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/pdf/quick/qml/PdfMultiPageView.qml b/src/pdf/quick/qml/PdfMultiPageView.qml index d50763a64..5efad39f0 100644 --- a/src/pdf/quick/qml/PdfMultiPageView.qml +++ b/src/pdf/quick/qml/PdfMultiPageView.qml @@ -183,21 +183,33 @@ Item { image.sourceSize.width = paper.pagePointSize.width * renderScale image.sourceSize.height = 0 paper.scale = 1 + searchHighlights.update() } } Shape { anchors.fill: parent opacity: 0.25 visible: image.status === Image.Ready + onVisibleChanged: searchHighlights.update() ShapePath { strokeWidth: 1 strokeColor: "cyan" fillColor: "steelblue" scale: Qt.size(paper.pageScale, paper.pageScale) PathMultiline { - paths: searchModel.boundingPolygonsOnPage(index) + id: searchHighlights + function update() { + // paths could be a binding, but we need to be able to "kick" it sometimes + paths = searchModel.boundingPolygonsOnPage(index) + } } } + Connections { + target: searchModel + // whenever the highlights on the _current_ page change, they actually need to change on _all_ pages + // (usually because the search string has changed) + function onCurrentPageBoundingPolygonsChanged() { searchHighlights.update() } + } ShapePath { fillColor: "orange" scale: Qt.size(paper.pageScale, paper.pageScale) -- cgit v1.2.3 From 25e76f19fb3e92aca05e108f823e0e8e822ee2dc Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 4 Mar 2020 17:45:25 +0100 Subject: PdfMultiPageView: correct the license header Standard header for module code rather than for an example. Change-Id: I39fad99f9c018530ccc12036eb502a78a521a0c0 Reviewed-by: Allan Sandfeld Jensen --- src/pdf/quick/qml/PdfMultiPageView.qml | 52 +++++++++++++--------------------- 1 file changed, 19 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/pdf/quick/qml/PdfMultiPageView.qml b/src/pdf/quick/qml/PdfMultiPageView.qml index 5efad39f0..de61f1782 100644 --- a/src/pdf/quick/qml/PdfMultiPageView.qml +++ b/src/pdf/quick/qml/PdfMultiPageView.qml @@ -1,48 +1,34 @@ /**************************************************************************** ** ** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ +** Contact: http://www.qt.io/licensing/ ** -** This file is part of the examples of the Qt Toolkit. +** This file is part of the QtPDF module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:BSD$ +** $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 https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. ** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: +** 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. ** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** 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$ ** -- cgit v1.2.3 From eb4a66480356356b19969f13553e4179b24c4041 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Sat, 29 Feb 2020 23:29:43 +0100 Subject: Render PDF link decorations as dashed underlines rather than boxes So they look more like (old-school) web links. The underlines are always horizontal and underneath the area of occurrence though; rotated link text has not been tested. Change-Id: I4fc01d88367b0cc9bbc23e9f85a3b42efb271fb8 Reviewed-by: Shawn Rutledge --- src/pdf/quick/qml/PdfMultiPageView.qml | 12 +++++++++--- src/pdf/quick/qml/PdfScrollablePageView.qml | 12 +++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/pdf/quick/qml/PdfMultiPageView.qml b/src/pdf/quick/qml/PdfMultiPageView.qml index de61f1782..9d9e2800a 100644 --- a/src/pdf/quick/qml/PdfMultiPageView.qml +++ b/src/pdf/quick/qml/PdfMultiPageView.qml @@ -282,13 +282,19 @@ Item { document: root.document page: image.currentFrame } - delegate: Rectangle { - color: "transparent" - border.color: "lightgrey" + delegate: Shape { x: rect.x * paper.pageScale y: rect.y * paper.pageScale width: rect.width * paper.pageScale height: rect.height * paper.pageScale + ShapePath { + strokeWidth: 1 + strokeColor: "steelblue" + strokeStyle: ShapePath.DashLine + dashPattern: [ 1, 4 ] + startX: 0; startY: height + PathLine { x: width; y: height } + } MouseArea { // TODO switch to TapHandler / HoverHandler in 5.15 id: linkMA anchors.fill: parent diff --git a/src/pdf/quick/qml/PdfScrollablePageView.qml b/src/pdf/quick/qml/PdfScrollablePageView.qml index e27b21d14..2d335849d 100644 --- a/src/pdf/quick/qml/PdfScrollablePageView.qml +++ b/src/pdf/quick/qml/PdfScrollablePageView.qml @@ -213,13 +213,19 @@ Flickable { document: root.document page: navigationStack.currentPage } - delegate: Rectangle { - color: "transparent" - border.color: "lightgrey" + delegate: Shape { x: rect.x * image.pageScale y: rect.y * image.pageScale width: rect.width * image.pageScale height: rect.height * image.pageScale + ShapePath { + strokeWidth: 1 + strokeColor: "steelblue" + strokeStyle: ShapePath.DashLine + dashPattern: [ 1, 4 ] + startX: 0; startY: height + PathLine { x: width; y: height } + } MouseArea { // TODO switch to TapHandler / HoverHandler in 5.15 anchors.fill: parent cursorShape: Qt.PointingHandCursor -- cgit v1.2.3 From 60663c79a1f7c0642c71f64c2415deca18d8207e Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 26 Feb 2020 22:35:35 +0100 Subject: Add PdfStyle; use Control palette colors PdfStyle.qml can be overridden via file selectors, and contains extra styling beyond what Control.palette provides. Search results are highlighted with a lightened, translucent version of the Controls style's accent color, and the current search result is highlighted with a solid border with the accent color. The text selection color comes from the Controls theme highlight color, which tends to be the same as QPalette::Highlight. The link underscore is also configurable in PdfStyle, but its color comes from the Controls theme link color, which tends to be the same as QPalette::link. Task-number: QTBUG-82540 Change-Id: I7c7e80323cf5d7c94dde775a38cdec239a6351a9 Reviewed-by: Mitch Curtis --- src/pdf/quick/qml/+material/PdfStyle.qml | 54 ++++++++++++++++++++++++++++ src/pdf/quick/qml/+universal/PdfStyle.qml | 55 +++++++++++++++++++++++++++++ src/pdf/quick/qml/PdfMultiPageView.qml | 26 +++++++------- src/pdf/quick/qml/PdfScrollablePageView.qml | 23 ++++++------ src/pdf/quick/qml/PdfStyle.qml | 54 ++++++++++++++++++++++++++++ src/pdf/quick/resources.qrc | 3 ++ 6 files changed, 189 insertions(+), 26 deletions(-) create mode 100644 src/pdf/quick/qml/+material/PdfStyle.qml create mode 100644 src/pdf/quick/qml/+universal/PdfStyle.qml create mode 100644 src/pdf/quick/qml/PdfStyle.qml (limited to 'src') diff --git a/src/pdf/quick/qml/+material/PdfStyle.qml b/src/pdf/quick/qml/+material/PdfStyle.qml new file mode 100644 index 000000000..12df30466 --- /dev/null +++ b/src/pdf/quick/qml/+material/PdfStyle.qml @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** 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 QtQml 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Controls.Material 2.14 +import QtQuick.Shapes 1.14 + +QtObject { + property Control prototypeControl: Control { } + function withAlpha(color, alpha) { + return Qt.hsla(color.hslHue, color.hslSaturation, color.hslLightness, alpha) + } + property color selectionColor: withAlpha(prototypeControl.palette.highlight, 0.5) + property color pageSearchResultsColor: withAlpha(Qt.lighter(Material.accentColor, 1.5), 0.5) + property color currentSearchResultStrokeColor: Material.accentColor + property real currentSearchResultStrokeWidth: 2 + property color linkUnderscoreColor: prototypeControl.palette.link + property real linkUnderscoreStrokeWidth: 1 + property var linkUnderscoreStrokeStyle: ShapePath.DashLine + property var linkUnderscoreDashPattern: [ 1, 4 ] +} diff --git a/src/pdf/quick/qml/+universal/PdfStyle.qml b/src/pdf/quick/qml/+universal/PdfStyle.qml new file mode 100644 index 000000000..e92f2a080 --- /dev/null +++ b/src/pdf/quick/qml/+universal/PdfStyle.qml @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** 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 QtQml 2.14 +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Controls.Universal 2.14 +import QtQuick.Shapes 1.14 + +QtObject { + property Control prototypeControl: Control { } + function withAlpha(color, alpha) { + return Qt.hsla(color.hslHue, color.hslSaturation, color.hslLightness, alpha) + } + property color selectionColor: withAlpha(prototypeControl.palette.highlight, 0.5) + property color pageSearchResultsColor: withAlpha(Qt.lighter(Universal.accent, 1.5), 0.5) + property color currentSearchResultStrokeColor: Universal.accent + property real currentSearchResultStrokeWidth: 2 + property color linkUnderscoreColor: prototypeControl.palette.link + property real linkUnderscoreStrokeWidth: 1 + property var linkUnderscoreStrokeStyle: ShapePath.DashLine + property var linkUnderscoreDashPattern: [ 1, 4 ] +} diff --git a/src/pdf/quick/qml/PdfMultiPageView.qml b/src/pdf/quick/qml/PdfMultiPageView.qml index 9d9e2800a..579c9a1ce 100644 --- a/src/pdf/quick/qml/PdfMultiPageView.qml +++ b/src/pdf/quick/qml/PdfMultiPageView.qml @@ -105,6 +105,7 @@ Item { function searchForward() { ++searchModel.currentResult } id: root + PdfStyle { id: style } TableView { id: tableView anchors.fill: parent @@ -174,13 +175,11 @@ Item { } Shape { anchors.fill: parent - opacity: 0.25 visible: image.status === Image.Ready onVisibleChanged: searchHighlights.update() ShapePath { - strokeWidth: 1 - strokeColor: "cyan" - fillColor: "steelblue" + strokeWidth: -1 + fillColor: style.pageSearchResultsColor scale: Qt.size(paper.pageScale, paper.pageScale) PathMultiline { id: searchHighlights @@ -197,22 +196,21 @@ Item { function onCurrentPageBoundingPolygonsChanged() { searchHighlights.update() } } ShapePath { - fillColor: "orange" + strokeWidth: -1 + fillColor: style.selectionColor scale: Qt.size(paper.pageScale, paper.pageScale) PathMultiline { - id: selectionBoundaries paths: selection.geometry } } } Shape { anchors.fill: parent - opacity: 0.5 visible: image.status === Image.Ready && searchModel.currentPage === index ShapePath { - strokeWidth: 1 - strokeColor: "blue" - fillColor: "cyan" + strokeWidth: style.currentSearchResultStrokeWidth + strokeColor: style.currentSearchResultStrokeColor + fillColor: "transparent" scale: Qt.size(paper.pageScale, paper.pageScale) PathMultiline { paths: searchModel.currentResultBoundingPolygons @@ -288,10 +286,10 @@ Item { width: rect.width * paper.pageScale height: rect.height * paper.pageScale ShapePath { - strokeWidth: 1 - strokeColor: "steelblue" - strokeStyle: ShapePath.DashLine - dashPattern: [ 1, 4 ] + strokeWidth: style.linkUnderscoreStrokeWidth + strokeColor: style.linkUnderscoreColor + strokeStyle: style.linkUnderscoreStrokeStyle + dashPattern: style.linkUnderscoreDashPattern startX: 0; startY: height PathLine { x: width; y: height } } diff --git a/src/pdf/quick/qml/PdfScrollablePageView.qml b/src/pdf/quick/qml/PdfScrollablePageView.qml index 2d335849d..4c43972c9 100644 --- a/src/pdf/quick/qml/PdfScrollablePageView.qml +++ b/src/pdf/quick/qml/PdfScrollablePageView.qml @@ -101,6 +101,7 @@ Flickable { // implementation id: root + PdfStyle { id: style } contentWidth: paper.width contentHeight: paper.height ScrollBar.vertical: ScrollBar { @@ -178,28 +179,26 @@ Flickable { Shape { anchors.fill: parent - opacity: 0.25 visible: image.status === Image.Ready ShapePath { - strokeWidth: 1 - strokeColor: "cyan" - fillColor: "steelblue" + strokeWidth: -1 + fillColor: style.pageSearchResultsColor scale: Qt.size(image.pageScale, image.pageScale) PathMultiline { paths: searchModel.currentPageBoundingPolygons } } ShapePath { - strokeWidth: 1 - strokeColor: "orange" - fillColor: "cyan" + strokeWidth: style.currentSearchResultStrokeWidth + strokeColor: style.currentSearchResultStrokeColor + fillColor: "transparent" scale: Qt.size(image.pageScale, image.pageScale) PathMultiline { paths: searchModel.currentResultBoundingPolygons } } ShapePath { - fillColor: "orange" + fillColor: style.selectionColor scale: Qt.size(image.pageScale, image.pageScale) PathMultiline { paths: selection.geometry @@ -219,10 +218,10 @@ Flickable { width: rect.width * image.pageScale height: rect.height * image.pageScale ShapePath { - strokeWidth: 1 - strokeColor: "steelblue" - strokeStyle: ShapePath.DashLine - dashPattern: [ 1, 4 ] + strokeWidth: style.linkUnderscoreStrokeWidth + strokeColor: style.linkUnderscoreColor + strokeStyle: style.linkUnderscoreStrokeStyle + dashPattern: style.linkUnderscoreDashPattern startX: 0; startY: height PathLine { x: width; y: height } } diff --git a/src/pdf/quick/qml/PdfStyle.qml b/src/pdf/quick/qml/PdfStyle.qml new file mode 100644 index 000000000..090465ce6 --- /dev/null +++ b/src/pdf/quick/qml/PdfStyle.qml @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** 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 QtQml 2.14 +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Shapes 1.14 + +QtObject { + property Control prototypeControl: Control { } + function withAlpha(color, alpha) { + return Qt.hsla(color.hslHue, color.hslSaturation, color.hslLightness, alpha) + } + property color selectionColor: withAlpha(prototypeControl.palette.highlight, 0.5) + property color pageSearchResultsColor: "#80B0C4DE" + property color currentSearchResultStrokeColor: "cyan" + property real currentSearchResultStrokeWidth: 2 + property color linkUnderscoreColor: prototypeControl.palette.link + property real linkUnderscoreStrokeWidth: 1 + property var linkUnderscoreStrokeStyle: ShapePath.DashLine + property var linkUnderscoreDashPattern: [ 1, 4 ] +} diff --git a/src/pdf/quick/resources.qrc b/src/pdf/quick/resources.qrc index 20cac4827..8270a2028 100644 --- a/src/pdf/quick/resources.qrc +++ b/src/pdf/quick/resources.qrc @@ -1,5 +1,8 @@ + qml/+material/PdfStyle.qml + qml/+universal/PdfStyle.qml + qml/PdfStyle.qml qml/PdfMultiPageView.qml qml/PdfPageView.qml qml/PdfScrollablePageView.qml -- cgit v1.2.3 From f253884934cbdbc16fd9b783a2b115960d11af10 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 10 Mar 2020 12:59:13 +0100 Subject: PDF quick plugin: fix warning about unused engine arg on static builds The build for iOS is static. Change-Id: I3e6c13c920861a2a6bf0079e53e76322bb93935f Reviewed-by: Shawn Rutledge --- src/pdf/quick/plugin.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/pdf/quick/plugin.cpp b/src/pdf/quick/plugin.cpp index bb68a817e..670fe0bf9 100644 --- a/src/pdf/quick/plugin.cpp +++ b/src/pdf/quick/plugin.cpp @@ -71,7 +71,9 @@ public: void initializeEngine(QQmlEngine *engine, const char *uri) override { Q_UNUSED(uri); -#ifndef QT_STATIC +#ifdef QT_STATIC + Q_UNUSED(engine); +#else engine->addImportPath(QStringLiteral("qrc:/")); #endif } -- cgit v1.2.3 From 5dc78ed4e2891205a7162b696b3439a87253140f Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 10 Mar 2020 12:57:32 +0100 Subject: PdfSelection: add selectAll() function; use in examples The usual shortcut (control-A) now selects all text on the current page, it is highlighted, and it can be copied to the clipboard. Change-Id: I5e6d9cae675862808f8b9027cb47024ca65cf2fd Reviewed-by: Shawn Rutledge --- src/pdf/api/qpdfdocument.h | 1 + src/pdf/api/qpdfdocument_p.h | 1 + src/pdf/qpdfdocument.cpp | 43 +++++++++++++++++++++++++---- src/pdf/quick/qml/PdfMultiPageView.qml | 5 ++++ src/pdf/quick/qml/PdfPageView.qml | 3 ++ src/pdf/quick/qml/PdfScrollablePageView.qml | 3 ++ src/pdf/quick/qquickpdfselection.cpp | 16 +++++++++++ src/pdf/quick/qquickpdfselection_p.h | 1 + 8 files changed, 67 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/pdf/api/qpdfdocument.h b/src/pdf/api/qpdfdocument.h index 40df181f4..027cdeb47 100644 --- a/src/pdf/api/qpdfdocument.h +++ b/src/pdf/api/qpdfdocument.h @@ -113,6 +113,7 @@ public: QImage render(int page, QSize imageSize, QPdfDocumentRenderOptions options = QPdfDocumentRenderOptions()); Q_INVOKABLE QPdfSelection getSelection(int page, QPointF start, QPointF end); + Q_INVOKABLE QPdfSelection getAllText(int page); Q_SIGNALS: void passwordChanged(); diff --git a/src/pdf/api/qpdfdocument_p.h b/src/pdf/api/qpdfdocument_p.h index 15d8b8259..2dcb70407 100644 --- a/src/pdf/api/qpdfdocument_p.h +++ b/src/pdf/api/qpdfdocument_p.h @@ -105,6 +105,7 @@ public: static int fpdf_GetBlock(void* param, unsigned long position, unsigned char* pBuf, unsigned long size); static void fpdf_AddSegment(struct _FX_DOWNLOADHINTS* pThis, size_t offset, size_t size); void updateLastError(); + QString getText(FPDF_TEXTPAGE textPage, int startIndex, int count); }; QT_END_NAMESPACE diff --git a/src/pdf/qpdfdocument.cpp b/src/pdf/qpdfdocument.cpp index 1e8a0f527..d56edf4e9 100644 --- a/src/pdf/qpdfdocument.cpp +++ b/src/pdf/qpdfdocument.cpp @@ -393,6 +393,15 @@ void QPdfDocumentPrivate::fpdf_AddSegment(_FX_DOWNLOADHINTS *pThis, size_t offse Q_UNUSED(size); } +QString QPdfDocumentPrivate::getText(FPDF_TEXTPAGE textPage, int startIndex, int count) +{ + QVector buf(count + 1); + // TODO is that enough space in case one unicode character is more than one in utf-16? + int len = FPDFText_GetText(textPage, startIndex, count, buf.data()); + Q_ASSERT(len - 1 <= count); // len is number of characters written, including the terminator + return QString::fromUtf16(buf.constData(), len - 1); +} + /*! \class QPdfDocument \since 5.10 @@ -737,15 +746,10 @@ QPdfSelection QPdfDocument::getSelection(int page, QPointF start, QPointF end) int endIndex = FPDFText_GetCharIndexAtPos(textPage, end.x(), pageHeight - end.y(), CharacterHitTolerance, CharacterHitTolerance); if (startIndex >= 0 && endIndex != startIndex) { - QString text; if (startIndex > endIndex) qSwap(startIndex, endIndex); int count = endIndex - startIndex + 1; - QVector buf(count + 1); - // TODO is that enough space in case one unicode character is more than one in utf-16? - int len = FPDFText_GetText(textPage, startIndex, count, buf.data()); - Q_ASSERT(len - 1 <= count); // len is number of characters written, including the terminator - text = QString::fromUtf16(buf.constData(), len - 1); + QString text = d->getText(textPage, startIndex, count); QVector bounds; int rectCount = FPDFText_CountRects(textPage, startIndex, endIndex - startIndex); for (int i = 0; i < rectCount; ++i) { @@ -767,6 +771,33 @@ QPdfSelection QPdfDocument::getSelection(int page, QPointF start, QPointF end) return QPdfSelection(); } +QPdfSelection QPdfDocument::getAllText(int page) +{ + const QPdfMutexLocker lock; + FPDF_PAGE pdfPage = FPDF_LoadPage(d->doc, page); + double pageHeight = FPDF_GetPageHeight(pdfPage); + FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage); + int count = FPDFText_CountChars(textPage); + if (count < 1) + return QPdfSelection(); + QString text = d->getText(textPage, 0, count); + QVector bounds; + int rectCount = FPDFText_CountRects(textPage, 0, count); + for (int i = 0; i < rectCount; ++i) { + double l, r, b, t; + FPDFText_GetRect(textPage, i, &l, &t, &r, &b); + QPolygonF poly; + poly << QPointF(l, pageHeight - t); + poly << QPointF(r, pageHeight - t); + poly << QPointF(r, pageHeight - b); + poly << QPointF(l, pageHeight - b); + poly << QPointF(l, pageHeight - t); + bounds << poly; + } + qCDebug(qLcDoc) << "on page" << page << "got" << count << "chars" << rectCount << "rects"; + return QPdfSelection(text, bounds); +} + QT_END_NAMESPACE #include "moc_qpdfdocument.cpp" diff --git a/src/pdf/quick/qml/PdfMultiPageView.qml b/src/pdf/quick/qml/PdfMultiPageView.qml index 579c9a1ce..70bb5454f 100644 --- a/src/pdf/quick/qml/PdfMultiPageView.qml +++ b/src/pdf/quick/qml/PdfMultiPageView.qml @@ -47,6 +47,11 @@ Item { property bool debug: false property string selectedText + function selectAll() { + var currentItem = tableView.itemAtPos(0, tableView.contentY + root.height / 2) + if (currentItem !== null) + currentItem.selection.selectAll() + } function copySelectionToClipboard() { var currentItem = tableView.itemAtPos(0, tableView.contentY + root.height / 2) if (debug) diff --git a/src/pdf/quick/qml/PdfPageView.qml b/src/pdf/quick/qml/PdfPageView.qml index dfd00a1a8..b90ad2d7f 100644 --- a/src/pdf/quick/qml/PdfPageView.qml +++ b/src/pdf/quick/qml/PdfPageView.qml @@ -46,6 +46,9 @@ Rectangle { property alias status: image.status property alias selectedText: selection.text + function selectAll() { + selection.selectAll() + } function copySelectionToClipboard() { selection.copyToClipboard() } diff --git a/src/pdf/quick/qml/PdfScrollablePageView.qml b/src/pdf/quick/qml/PdfScrollablePageView.qml index 4c43972c9..6076e57df 100644 --- a/src/pdf/quick/qml/PdfScrollablePageView.qml +++ b/src/pdf/quick/qml/PdfScrollablePageView.qml @@ -47,6 +47,9 @@ Flickable { property alias status: image.status property alias selectedText: selection.text + function selectAll() { + selection.selectAll() + } function copySelectionToClipboard() { selection.copyToClipboard() } diff --git a/src/pdf/quick/qquickpdfselection.cpp b/src/pdf/quick/qquickpdfselection.cpp index d313820ba..5371e85e5 100644 --- a/src/pdf/quick/qquickpdfselection.cpp +++ b/src/pdf/quick/qquickpdfselection.cpp @@ -124,6 +124,22 @@ QVector QQuickPdfSelection::geometry() const return m_geometry; } +void QQuickPdfSelection::selectAll() +{ + QPdfSelection sel = m_document->m_doc.getAllText(m_page); + if (sel.text() != m_text) { + m_text = sel.text(); + if (QGuiApplication::clipboard()->supportsSelection()) + sel.copyToClipboard(QClipboard::Selection); + emit textChanged(); + } + + if (sel.bounds() != m_geometry) { + m_geometry = sel.bounds(); + emit geometryChanged(); + } +} + void QQuickPdfSelection::resetPoints() { bool wasHolding = m_hold; diff --git a/src/pdf/quick/qquickpdfselection_p.h b/src/pdf/quick/qquickpdfselection_p.h index a0e6d1a8d..bb4a50fed 100644 --- a/src/pdf/quick/qquickpdfselection_p.h +++ b/src/pdf/quick/qquickpdfselection_p.h @@ -86,6 +86,7 @@ public: QString text() const; QVector geometry() const; + Q_INVOKABLE void selectAll(); #if QT_CONFIG(clipboard) Q_INVOKABLE void copyToClipboard() const; #endif -- cgit v1.2.3