summaryrefslogtreecommitdiffstats
path: root/src/pdf
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-05-14 10:49:00 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-05-15 17:26:19 +0200
commitbc6df3888128e3a0e0d4e2f8a69970ac36d8abe7 (patch)
tree2cb49ad5fffa0f2011b3d98faa363f105135901e /src/pdf
parent10e66c6dd0b8a8dd17252d6408c13b689fac6995 (diff)
parent585da6f74012bd09e8a873080e368cff99c97cbf (diff)
Merge remote-tracking branch 'origin/5.15' into dev
Conflicts: src/pdf/quick/qquickpdfselection_p.h Change-Id: I6eec37a01347c2d47cbfc1114326dfc6b58719ff
Diffstat (limited to 'src/pdf')
-rw-r--r--src/pdf/api/qpdfdocument.h2
-rw-r--r--src/pdf/api/qpdfdocument_p.h11
-rw-r--r--src/pdf/api/qpdflinkmodel_p_p.h2
-rw-r--r--src/pdf/api/qpdfselection.h8
-rw-r--r--src/pdf/api/qpdfselection_p.h10
-rw-r--r--src/pdf/api/qtpdfglobal.h2
-rw-r--r--src/pdf/qpdfdocument.cpp133
-rw-r--r--src/pdf/qpdfselection.cpp34
-rw-r--r--src/pdf/quick/plugin.cpp2
-rw-r--r--src/pdf/quick/qml/PdfMultiPageView.qml171
-rw-r--r--src/pdf/quick/qml/PdfScrollablePageView.qml49
-rw-r--r--src/pdf/quick/qquickpdfdocument.cpp1
-rw-r--r--src/pdf/quick/qquickpdflinkmodel.cpp1
-rw-r--r--src/pdf/quick/qquickpdfnavigationstack.cpp12
-rw-r--r--src/pdf/quick/qquickpdfnavigationstack_p.h2
-rw-r--r--src/pdf/quick/qquickpdfsearchmodel.cpp24
-rw-r--r--src/pdf/quick/qquickpdfsearchmodel_p.h3
-rw-r--r--src/pdf/quick/qquickpdfselection.cpp289
-rw-r--r--src/pdf/quick/qquickpdfselection_p.h39
-rw-r--r--src/pdf/quick/qquicktableviewextra.cpp193
-rw-r--r--src/pdf/quick/qquicktableviewextra_p.h92
-rw-r--r--src/pdf/quick/quick.pro8
22 files changed, 958 insertions, 130 deletions
diff --git a/src/pdf/api/qpdfdocument.h b/src/pdf/api/qpdfdocument.h
index f80a7832b..54ca687fa 100644
--- a/src/pdf/api/qpdfdocument.h
+++ b/src/pdf/api/qpdfdocument.h
@@ -114,6 +114,7 @@ public:
QImage render(int page, QSize imageSize, QPdfDocumentRenderOptions options = QPdfDocumentRenderOptions());
Q_INVOKABLE QPdfSelection getSelection(int page, QPointF start, QPointF end);
+ Q_INVOKABLE QPdfSelection getSelectionAtIndex(int page, int startIndex, int maxLength);
Q_INVOKABLE QPdfSelection getAllText(int page);
Q_SIGNALS:
@@ -127,6 +128,7 @@ private:
friend class QPdfLinkModelPrivate;
friend class QPdfSearchModel;
friend class QPdfSearchModelPrivate;
+ friend class QQuickPdfSelection;
Q_PRIVATE_SLOT(d, void _q_tryLoadingWithSizeFromContentHeader())
Q_PRIVATE_SLOT(d, void _q_copyFromSequentialSourceDevice())
diff --git a/src/pdf/api/qpdfdocument_p.h b/src/pdf/api/qpdfdocument_p.h
index b69b6f19e..9a737766b 100644
--- a/src/pdf/api/qpdfdocument_p.h
+++ b/src/pdf/api/qpdfdocument_p.h
@@ -66,7 +66,7 @@ public:
QPdfMutexLocker();
};
-class QPdfDocumentPrivate: public FPDF_FILEACCESS, public FX_FILEAVAIL, public FX_DOWNLOADHINTS
+class Q_PDF_PRIVATE_EXPORT QPdfDocumentPrivate: public FPDF_FILEACCESS, public FX_FILEAVAIL, public FX_DOWNLOADHINTS
{
public:
QPdfDocumentPrivate();
@@ -106,6 +106,15 @@ public:
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);
+ QPointF getCharPosition(FPDF_TEXTPAGE textPage, double pageHeight, int charIndex);
+ QRectF getCharBox(FPDF_TEXTPAGE textPage, double pageHeight, int charIndex);
+
+ struct TextPosition {
+ QPointF position;
+ qreal height = 0;
+ int charIndex = -1;
+ };
+ TextPosition hitTest(int page, QPointF position);
};
QT_END_NAMESPACE
diff --git a/src/pdf/api/qpdflinkmodel_p_p.h b/src/pdf/api/qpdflinkmodel_p_p.h
index 3e44f1651..0454d6755 100644
--- a/src/pdf/api/qpdflinkmodel_p_p.h
+++ b/src/pdf/api/qpdflinkmodel_p_p.h
@@ -74,7 +74,7 @@ public:
// destination inside PDF
int page = -1; // -1 means look at the url instead
QPointF location;
- qreal zoom = 1;
+ qreal zoom = 0; // 0 means no specified zoom: don't change when clicking
// web destination
QUrl url;
diff --git a/src/pdf/api/qpdfselection.h b/src/pdf/api/qpdfselection.h
index 5a6a1cddc..9d91d46c7 100644
--- a/src/pdf/api/qpdfselection.h
+++ b/src/pdf/api/qpdfselection.h
@@ -53,7 +53,10 @@ class Q_PDF_EXPORT QPdfSelection
Q_GADGET
Q_PROPERTY(bool valid READ isValid)
Q_PROPERTY(QVector<QPolygonF> bounds READ bounds)
+ Q_PROPERTY(QRectF boundingRectangle READ boundingRectangle)
Q_PROPERTY(QString text READ text)
+ Q_PROPERTY(int startIndex READ startIndex)
+ Q_PROPERTY(int endIndex READ endIndex)
public:
~QPdfSelection();
@@ -65,13 +68,16 @@ public:
bool isValid() const;
QVector<QPolygonF> bounds() const;
QString text() const;
+ QRectF boundingRectangle() const;
+ int startIndex() const;
+ int endIndex() const;
#if QT_CONFIG(clipboard)
void copyToClipboard(QClipboard::Mode mode = QClipboard::Clipboard) const;
#endif
private:
QPdfSelection();
- QPdfSelection(const QString &text, QVector<QPolygonF> bounds);
+ QPdfSelection(const QString &text, QVector<QPolygonF> bounds, QRectF boundingRect, int startIndex, int endIndex);
QPdfSelection(QPdfSelectionPrivate *d);
friend class QPdfDocument;
friend class QQuickPdfSelection;
diff --git a/src/pdf/api/qpdfselection_p.h b/src/pdf/api/qpdfselection_p.h
index 37145f7f9..0577e5a31 100644
--- a/src/pdf/api/qpdfselection_p.h
+++ b/src/pdf/api/qpdfselection_p.h
@@ -46,12 +46,18 @@ class QPdfSelectionPrivate : public QSharedData
{
public:
QPdfSelectionPrivate() = default;
- QPdfSelectionPrivate(const QString &text, QVector<QPolygonF> bounds)
+ QPdfSelectionPrivate(const QString &text, QVector<QPolygonF> bounds, QRectF boundingRect, int startIndex, int endIndex)
: text(text),
- bounds(bounds) { }
+ bounds(bounds),
+ boundingRect(boundingRect),
+ startIndex(startIndex),
+ endIndex(endIndex) { }
QString text;
QVector<QPolygonF> bounds;
+ QRectF boundingRect;
+ int startIndex;
+ int endIndex;
};
QT_END_NAMESPACE
diff --git a/src/pdf/api/qtpdfglobal.h b/src/pdf/api/qtpdfglobal.h
index 223ec4bcb..8b4b0c206 100644
--- a/src/pdf/api/qtpdfglobal.h
+++ b/src/pdf/api/qtpdfglobal.h
@@ -53,6 +53,8 @@ QT_BEGIN_NAMESPACE
# endif
#endif
+#define Q_PDF_PRIVATE_EXPORT Q_PDF_EXPORT
+
QT_END_NAMESPACE
#endif // QTPDFGLOBAL_H
diff --git a/src/pdf/qpdfdocument.cpp b/src/pdf/qpdfdocument.cpp
index 89b27da8b..e4ec363ce 100644
--- a/src/pdf/qpdfdocument.cpp
+++ b/src/pdf/qpdfdocument.cpp
@@ -54,7 +54,7 @@ QT_BEGIN_NAMESPACE
// The library is not thread-safe at all, it has a lot of global variables.
Q_GLOBAL_STATIC_WITH_ARGS(QMutex, pdfMutex, (QMutex::Recursive));
static int libraryRefCount;
-static const double CharacterHitTolerance = 6.0;
+static const double CharacterHitTolerance = 16.0;
Q_LOGGING_CATEGORY(qLcDoc, "qt.pdf.document")
QPdfMutexLocker::QPdfMutexLocker()
@@ -402,6 +402,50 @@ QString QPdfDocumentPrivate::getText(FPDF_TEXTPAGE textPage, int startIndex, int
return QString::fromUtf16(buf.constData(), len - 1);
}
+QPointF QPdfDocumentPrivate::getCharPosition(FPDF_TEXTPAGE textPage, double pageHeight, int charIndex)
+{
+ double x, y;
+ int count = FPDFText_CountChars(textPage);
+ bool ok = FPDFText_GetCharOrigin(textPage, qMin(count - 1, charIndex), &x, &y);
+ if (!ok)
+ return QPointF();
+ return QPointF(x, pageHeight - y);
+}
+
+QRectF QPdfDocumentPrivate::getCharBox(FPDF_TEXTPAGE textPage, double pageHeight, int charIndex)
+{
+ double l, t, r, b;
+ bool ok = FPDFText_GetCharBox(textPage, charIndex, &l, &r, &b, &t);
+ if (!ok)
+ return QRectF();
+ return QRectF(l, pageHeight - t, r - l, t - b);
+}
+
+QPdfDocumentPrivate::TextPosition QPdfDocumentPrivate::hitTest(int page, QPointF position)
+{
+ const QPdfMutexLocker lock;
+ FPDF_PAGE pdfPage = FPDF_LoadPage(doc, page);
+ double pageHeight = FPDF_GetPageHeight(pdfPage);
+ FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage);
+ int hitIndex = FPDFText_GetCharIndexAtPos(textPage, position.x(), pageHeight - position.y(),
+ CharacterHitTolerance, CharacterHitTolerance);
+ if (hitIndex >= 0) {
+ QPointF charPos = getCharPosition(textPage, pageHeight, hitIndex);
+ if (!charPos.isNull()) {
+ QRectF charBox = getCharBox(textPage, pageHeight, hitIndex);
+ // If the given position is past the end of the line, i.e. if the right edge of the found character's
+ // bounding box is closer to it than the left edge is, we say that we "hit" the next character index after
+ if (qAbs(charBox.right() - position.x()) < qAbs(charPos.x() - position.x())) {
+ charPos.setX(charBox.right());
+ ++hitIndex;
+ }
+ qCDebug(qLcDoc) << "on page" << page << "@" << position << "got char position" << charPos << "index" << hitIndex;
+ return { charPos, charBox.height(), hitIndex };
+ }
+ }
+ return {};
+}
+
/*!
\class QPdfDocument
\since 5.10
@@ -748,29 +792,80 @@ QPdfSelection QPdfDocument::getSelection(int page, QPointF start, QPointF end)
if (startIndex >= 0 && endIndex != startIndex) {
if (startIndex > endIndex)
qSwap(startIndex, endIndex);
- int count = endIndex - startIndex + 1;
+
+ // If the given end position is past the end of the line, i.e. if the right edge of the last character's
+ // bounding box is closer to it than the left edge is, then extend the char range by one
+ QRectF endCharBox = d->getCharBox(textPage, pageHeight, endIndex);
+ if (qAbs(endCharBox.right() - end.x()) < qAbs(endCharBox.x() - end.x()))
+ ++endIndex;
+
+ int count = endIndex - startIndex;
QString text = d->getText(textPage, startIndex, count);
QVector<QPolygonF> bounds;
+ QRectF hull;
int rectCount = FPDFText_CountRects(textPage, startIndex, endIndex - startIndex);
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;
+ QRectF rect(l, pageHeight - t, r - l, t - b);
+ if (hull.isNull())
+ hull = rect;
+ else
+ hull = hull.united(rect);
+ bounds << QPolygonF(rect);
}
qCDebug(qLcDoc) << page << start << "->" << end << "found" << startIndex << "->" << endIndex << text;
- return QPdfSelection(text, bounds);
+ return QPdfSelection(text, bounds, hull, startIndex, endIndex);
}
qCDebug(qLcDoc) << page << start << "->" << end << "nothing found";
return QPdfSelection();
}
+/*!
+ Returns information about the text on the given \a page that can be found
+ beginning at the given \a startIndex with at most \l maxLength characters.
+*/
+QPdfSelection QPdfDocument::getSelectionAtIndex(int page, int startIndex, int maxLength)
+{
+
+ if (page < 0 || startIndex < 0 || maxLength < 0)
+ return {};
+ const QPdfMutexLocker lock;
+ FPDF_PAGE pdfPage = FPDF_LoadPage(d->doc, page);
+ double pageHeight = FPDF_GetPageHeight(pdfPage);
+ FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage);
+ int pageCount = FPDFText_CountChars(textPage);
+ if (startIndex >= pageCount)
+ return QPdfSelection();
+ QVector<QPolygonF> bounds;
+ QRectF hull;
+ int rectCount = 0;
+ QString text;
+ if (maxLength > 0) {
+ text = d->getText(textPage, startIndex, maxLength);
+ rectCount = FPDFText_CountRects(textPage, startIndex, text.length());
+ for (int i = 0; i < rectCount; ++i) {
+ double l, r, b, t;
+ FPDFText_GetRect(textPage, i, &l, &t, &r, &b);
+ QRectF rect(l, pageHeight - t, r - l, t - b);
+ if (hull.isNull())
+ hull = rect;
+ else
+ hull = hull.united(rect);
+ bounds << QPolygonF(rect);
+ }
+ }
+ if (bounds.isEmpty())
+ hull = QRectF(d->getCharPosition(textPage, pageHeight, startIndex), QSizeF());
+ qCDebug(qLcDoc) << "on page" << page << "at index" << startIndex << "maxLength" << maxLength
+ << "got" << text.length() << "chars," << rectCount << "rects within" << hull;
+ return QPdfSelection(text, bounds, hull, startIndex, startIndex + text.length());
+}
+
+/*!
+ Returns all the text and its bounds on the given \a page.
+*/
QPdfSelection QPdfDocument::getAllText(int page)
{
const QPdfMutexLocker lock;
@@ -782,20 +877,20 @@ QPdfSelection QPdfDocument::getAllText(int page)
return QPdfSelection();
QString text = d->getText(textPage, 0, count);
QVector<QPolygonF> bounds;
+ QRectF hull;
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;
+ QRectF rect(l, pageHeight - t, r - l, t - b);
+ if (hull.isNull())
+ hull = rect;
+ else
+ hull = hull.united(rect);
+ bounds << QPolygonF(rect);
}
- qCDebug(qLcDoc) << "on page" << page << "got" << count << "chars" << rectCount << "rects";
- return QPdfSelection(text, bounds);
+ qCDebug(qLcDoc) << "on page" << page << "got" << count << "chars," << rectCount << "rects within" << hull;
+ return QPdfSelection(text, bounds, hull, 0, count);
}
QT_END_NAMESPACE
diff --git a/src/pdf/qpdfselection.cpp b/src/pdf/qpdfselection.cpp
index e334f0fb6..5f0ee3b20 100644
--- a/src/pdf/qpdfselection.cpp
+++ b/src/pdf/qpdfselection.cpp
@@ -67,8 +67,8 @@ QPdfSelection::QPdfSelection()
\a text string, and which take up space on the page within the polygon
regions given in \a bounds.
*/
-QPdfSelection::QPdfSelection(const QString &text, QVector<QPolygonF> bounds)
- : d(new QPdfSelectionPrivate(text, bounds))
+QPdfSelection::QPdfSelection(const QString &text, QVector<QPolygonF> bounds, QRectF boundingRect, int startIndex, int endIndex)
+ : d(new QPdfSelectionPrivate(text, bounds, boundingRect, startIndex, endIndex))
{
}
@@ -134,6 +134,36 @@ QString QPdfSelection::text() const
return d->text;
}
+/*!
+ \property rect QPdfSelection::boundingRectangle
+
+ This property holds the overall bounding rectangle (convex hull) around \l bounds.
+*/
+QRectF QPdfSelection::boundingRectangle() const
+{
+ return d->boundingRect;
+}
+
+/*!
+ \property int QPdfSelection::startIndex
+
+ This property holds the index at the beginning of \l text within the full text on the page.
+*/
+int QPdfSelection::startIndex() const
+{
+ return d->startIndex;
+}
+
+/*!
+ \property int QPdfSelection::endIndex
+
+ This property holds the index at the end of \l text within the full text on the page.
+*/
+int QPdfSelection::endIndex() const
+{
+ return d->endIndex;
+}
+
#if QT_CONFIG(clipboard)
/*!
Copies \l text to the \l {QGuiApplication::clipboard()}{system clipboard}.
diff --git a/src/pdf/quick/plugin.cpp b/src/pdf/quick/plugin.cpp
index 670fe0bf9..b082fcb4a 100644
--- a/src/pdf/quick/plugin.cpp
+++ b/src/pdf/quick/plugin.cpp
@@ -43,6 +43,7 @@
#include "qquickpdfnavigationstack_p.h"
#include "qquickpdfsearchmodel_p.h"
#include "qquickpdfselection_p.h"
+#include "qquicktableviewextra_p.h"
QT_BEGIN_NAMESPACE
@@ -89,6 +90,7 @@ public:
qmlRegisterType<QQuickPdfNavigationStack>(uri, 5, 15, "PdfNavigationStack");
qmlRegisterType<QQuickPdfSearchModel>(uri, 5, 15, "PdfSearchModel");
qmlRegisterType<QQuickPdfSelection>(uri, 5, 15, "PdfSelection");
+ qmlRegisterType<QQuickTableViewExtra>(uri, 5, 15, "TableViewExtra");
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");
diff --git a/src/pdf/quick/qml/PdfMultiPageView.qml b/src/pdf/quick/qml/PdfMultiPageView.qml
index 70bb5454f..71485c214 100644
--- a/src/pdf/quick/qml/PdfMultiPageView.qml
+++ b/src/pdf/quick/qml/PdfMultiPageView.qml
@@ -48,15 +48,15 @@ Item {
property string selectedText
function selectAll() {
- var currentItem = tableView.itemAtPos(0, tableView.contentY + root.height / 2)
- if (currentItem !== null)
+ var currentItem = tableHelper.itemAtCell(tableHelper.cellAtPos(root.width / 2, root.height / 2))
+ if (currentItem)
currentItem.selection.selectAll()
}
function copySelectionToClipboard() {
- var currentItem = tableView.itemAtPos(0, tableView.contentY + root.height / 2)
+ var currentItem = tableHelper.itemAtCell(tableHelper.cellAtPos(root.width / 2, root.height / 2))
if (debug)
console.log("currentItem", currentItem, "sel", currentItem.selection.text)
- if (currentItem !== null)
+ if (currentItem)
currentItem.selection.copyToClipboard()
}
@@ -69,14 +69,19 @@ Item {
function goToPage(page) {
if (page === navigationStack.currentPage)
return
- goToLocation(page, Qt.point(0, 0), 0)
+ goToLocation(page, Qt.point(-1, -1), 0)
}
function goToLocation(page, location, zoom) {
- if (zoom > 0)
+ if (zoom > 0) {
+ navigationStack.jumping = true // don't call navigationStack.update() because we will push() instead
root.renderScale = zoom
- navigationStack.push(page, location, zoom)
- searchModel.currentPage = page
+ tableView.forceLayout() // but do ensure that the table layout is correct before we try to jump
+ navigationStack.jumping = false
+ }
+ navigationStack.push(page, location, zoom) // actually jump
}
+ property vector2d jumpLocationMargin: Qt.vector2d(10, 10) // px from top-left corner
+ property int currentPageRenderingStatus: Image.Null
// page scaling
property real renderScale: 1
@@ -115,29 +120,27 @@ Item {
id: tableView
anchors.fill: parent
anchors.leftMargin: 2
- model: root.document === undefined ? 0 : root.document.pageCount
+ model: modelInUse && root.document !== undefined ? 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
rowSpacing: 6
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 : (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
- var ret = null
- for (var i = 0; i < contentItem.children.length; ++i) {
- var child = contentItem.children[i];
- if (root.debug)
- console.log(child, "@y", child.y)
- if (child.y < y && (!ret || child.y > ret.y))
- ret = child
- }
- 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
- }
+ property real pageHolderWidth: Math.max(root.width, document === undefined ? 0 :
+ (rot90 ? document.maxPageHeight : document.maxPageWidth) * root.renderScale)
+ contentWidth: document === undefined ? 0 : pageHolderWidth + vscroll.width + 2
rowHeightProvider: function(row) { return (rot90 ? document.pagePointSize(row).width : document.pagePointSize(row).height) * root.renderScale }
+ TableViewExtra {
+ id: tableHelper
+ tableView: tableView
+ }
delegate: Rectangle {
id: pageHolder
color: root.debug ? "beige" : "transparent"
@@ -147,11 +150,8 @@ Item {
rotation: -90; text: pageHolder.width.toFixed(1) + "x" + pageHolder.height.toFixed(1) + "\n" +
image.width.toFixed(1) + "x" + image.height.toFixed(1)
}
- implicitWidth: Math.max(root.width, (tableView.rot90 ? document.maxPageHeight : document.maxPageWidth) * root.renderScale)
+ implicitWidth: tableView.pageHolderWidth
implicitHeight: tableView.rot90 ? image.width : image.height
- onImplicitWidthChanged: tableView.forceLayout()
- objectName: "page " + index
- property int delegateIndex: row // expose the context property for JS outside of the delegate
property alias selection: selection
Rectangle {
id: paper
@@ -177,6 +177,10 @@ Item {
paper.scale = 1
searchHighlights.update()
}
+ onStatusChanged: {
+ if (index === navigationStack.currentPage)
+ root.currentPageRenderingStatus = status
+ }
}
Shape {
anchors.fill: parent
@@ -276,9 +280,17 @@ Item {
target: null
}
TapHandler {
- id: tapHandler
+ id: mouseClickHandler
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
}
+ TapHandler {
+ id: touchTapHandler
+ acceptedDevices: PointerDevice.TouchScreen
+ onTapped: {
+ selection.clear()
+ selection.forceActiveFocus()
+ }
+ }
Repeater {
model: PdfLinkModel {
id: linkModel
@@ -290,6 +302,7 @@ Item {
y: rect.y * paper.pageScale
width: rect.width * paper.pageScale
height: rect.height * paper.pageScale
+ visible: image.status === Image.Ready
ShapePath {
strokeWidth: style.linkUnderscoreStrokeWidth
strokeColor: style.linkUnderscoreColor
@@ -320,17 +333,18 @@ Item {
}
}
}
- }
- PdfSelection {
- id: selection
- document: root.document
- page: image.currentFrame
- fromPoint: Qt.point(textSelectionDrag.centroid.pressPosition.x / paper.pageScale,
- textSelectionDrag.centroid.pressPosition.y / paper.pageScale)
- toPoint: Qt.point(textSelectionDrag.centroid.position.x / paper.pageScale,
- textSelectionDrag.centroid.position.y / paper.pageScale)
- hold: !textSelectionDrag.active && !tapHandler.pressed
- onTextChanged: root.selectedText = text
+ PdfSelection {
+ id: selection
+ anchors.fill: parent
+ document: root.document
+ page: image.currentFrame
+ renderScale: image.renderScale
+ fromPoint: textSelectionDrag.centroid.pressPosition
+ toPoint: textSelectionDrag.centroid.position
+ hold: !textSelectionDrag.active && !mouseClickHandler.pressed
+ onTextChanged: root.selectedText = text
+ focus: true
+ }
}
}
ScrollBar.vertical: ScrollBar {
@@ -338,42 +352,83 @@ Item {
property bool moved: false
onPositionChanged: moved = true
onActiveChanged: {
- var currentItem = tableView.itemAtPos(0, tableView.contentY + root.height / 2)
- var currentPage = currentItem.delegateIndex
- var currentLocation = Qt.point((tableView.contentX - currentItem.x + root.width / 2) / root.renderScale,
- (tableView.contentY - currentItem.y + root.height / 2) / root.renderScale)
+ var cell = tableHelper.cellAtPos(root.width / 2, root.height / 2)
+ var currentItem = tableHelper.itemAtCell(cell)
+ var currentLocation = Qt.point(0, 0)
+ if (currentItem) { // maybe the delegate wasn't loaded yet
+ currentLocation = Qt.point((tableView.contentX - currentItem.x + jumpLocationMargin.x) / root.renderScale,
+ (tableView.contentY - currentItem.y + jumpLocationMargin.y) / root.renderScale)
+ }
if (active) {
moved = false
- navigationStack.push(currentPage, currentLocation, root.renderScale)
+ // emitJumped false to avoid interrupting a pinch if TableView thinks it should scroll at the same time
+ navigationStack.push(cell.y, currentLocation, root.renderScale, false)
} else if (moved) {
- navigationStack.update(currentPage, currentLocation, root.renderScale)
+ navigationStack.update(cell.y, currentLocation, root.renderScale)
}
}
}
ScrollBar.horizontal: ScrollBar { }
}
onRenderScaleChanged: {
- tableView.forceLayout()
- var currentItem = tableView.itemAtPos(tableView.contentX + root.width / 2, tableView.contentY + root.height / 2)
- if (currentItem !== undefined)
- navigationStack.update(currentItem.delegateIndex, Qt.point(currentItem.x / renderScale, currentItem.y / renderScale), renderScale)
+ // if navigationStack.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)
+ return
+ // make TableView rebuild from scratch, because otherwise it doesn't know the delegates are changing size
+ tableView.rebuild()
+ var cell = tableHelper.cellAtPos(root.width / 2, root.height / 2)
+ var currentItem = tableHelper.itemAtCell(cell)
+ if (currentItem) {
+ var currentLocation = Qt.point((tableView.contentX - currentItem.x + jumpLocationMargin.x) / root.renderScale,
+ (tableView.contentY - currentItem.y + jumpLocationMargin.y) / root.renderScale)
+ navigationStack.update(cell.y, currentLocation, renderScale)
+ }
}
PdfNavigationStack {
id: navigationStack
+ property bool jumping: false
+ property int previousPage: 0
onJumped: {
+ jumping = true
root.renderScale = zoom
- tableView.contentX = Math.max(0, location.x - root.width / 2) * root.renderScale
- tableView.contentY = tableView.originY + root.document.heightSumBeforePage(page, tableView.rowSpacing / root.renderScale) * root.renderScale
- if (root.debug) {
- console.log("going to page", page,
- "@y", root.document.heightSumBeforePage(page, tableView.rowSpacing / root.renderScale) * root.renderScale,
- "ended up @", tableView.contentY, "originY is", tableView.originY)
+ if (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
+ var currentYOffset = 0
+ var previousPageDelegate = tableHelper.itemAtCell(0, previousPage)
+ if (previousPageDelegate)
+ currentYOffset = tableView.contentY - previousPageDelegate.y
+ tableHelper.positionViewAtRow(page, Qt.AlignTop, currentYOffset)
+ if (root.debug) {
+ console.log("going from page", previousPage, "to", page, "offset", currentYOffset,
+ "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
+ var xOffsetLimit = Math.max(0, pageSize.width - root.width) / 2
+ var offset = Qt.point(Math.max(-xOffsetLimit, Math.min(xOffsetLimit,
+ location.x * root.renderScale - jumpLocationMargin.x)),
+ Math.max(0, location.y * root.renderScale - jumpLocationMargin.y))
+ tableHelper.positionViewAtCell(0, page, Qt.AlignLeft | Qt.AlignTop, offset)
+ if (root.debug) {
+ console.log("going to zoom", zoom, "loc", location, "on page", page,
+ "ended up @", tableView.contentX.toFixed(1) + ", " + tableView.contentY.toFixed(1))
+ }
}
+ jumping = false
+ previousPage = page
}
+ onCurrentPageChanged: searchModel.currentPage = currentPage
}
PdfSearchModel {
id: searchModel
document: root.document === undefined ? null : root.document
- onCurrentPageChanged: if (currentPage != navigationStack.currentPage) root.goToPage(currentPage)
+ // 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)
}
}
diff --git a/src/pdf/quick/qml/PdfScrollablePageView.qml b/src/pdf/quick/qml/PdfScrollablePageView.qml
index 6076e57df..51d9e530d 100644
--- a/src/pdf/quick/qml/PdfScrollablePageView.qml
+++ b/src/pdf/quick/qml/PdfScrollablePageView.qml
@@ -133,32 +133,29 @@ Flickable {
navigationStack.update(navigationStack.currentPage, currentLocation, root.renderScale)
}
- 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)
+ // 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)
}
PdfNavigationStack {
id: navigationStack
onJumped: {
root.renderScale = zoom
- root.contentX = Math.max(0, location.x * root.renderScale - root.width / 2)
- root.contentY = Math.max(0, location.y * root.renderScale - root.height / 2)
- if (root.debug)
+ var dx = Math.max(0, location.x * root.renderScale - root.width / 2) - root.contentX
+ var dy = Math.max(0, location.y * root.renderScale - root.height / 2) - root.contentY
+ // don't jump if location is in the viewport already, i.e. if the "error" between desired and actual contentX/Y is small
+ if (Math.abs(dx) > root.width / 3)
+ root.contentX += dx
+ if (Math.abs(dy) > root.height / 3)
+ root.contentY += dy
+ if (root.debug) {
console.log("going to zoom", zoom, "loc", location,
"on page", page, "ended up @", root.contentX + ", " + root.contentY)
+ }
}
onCurrentPageChanged: searchModel.currentPage = currentPage
}
@@ -246,9 +243,29 @@ Flickable {
target: null
}
TapHandler {
- id: tapHandler
+ id: mouseClickHandler
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
}
+ TapHandler {
+ id: touchTapHandler
+ acceptedDevices: PointerDevice.TouchScreen
+ onTapped: {
+ selection.clear()
+ selection.focus = true
+ }
+ }
+ }
+
+ PdfSelection {
+ id: selection
+ anchors.fill: parent
+ document: root.document
+ page: navigationStack.currentPage
+ renderScale: image.pageScale
+ fromPoint: textSelectionDrag.centroid.pressPosition
+ toPoint: textSelectionDrag.centroid.position
+ hold: !textSelectionDrag.active && !mouseClickHandler.pressed
+ focus: true
}
PinchHandler {
diff --git a/src/pdf/quick/qquickpdfdocument.cpp b/src/pdf/quick/qquickpdfdocument.cpp
index 3d5f0fa10..ab5910523 100644
--- a/src/pdf/quick/qquickpdfdocument.cpp
+++ b/src/pdf/quick/qquickpdfdocument.cpp
@@ -38,7 +38,6 @@
#include <QQuickItem>
#include <QQmlEngine>
#include <QStandardPaths>
-#include <private/qguiapplication_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/pdf/quick/qquickpdflinkmodel.cpp b/src/pdf/quick/qquickpdflinkmodel.cpp
index f2ff3fd22..4f3958337 100644
--- a/src/pdf/quick/qquickpdflinkmodel.cpp
+++ b/src/pdf/quick/qquickpdflinkmodel.cpp
@@ -38,7 +38,6 @@
#include <QQuickItem>
#include <QQmlEngine>
#include <QStandardPaths>
-#include <private/qguiapplication_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/pdf/quick/qquickpdfnavigationstack.cpp b/src/pdf/quick/qquickpdfnavigationstack.cpp
index 7ba317557..044023ef6 100644
--- a/src/pdf/quick/qquickpdfnavigationstack.cpp
+++ b/src/pdf/quick/qquickpdfnavigationstack.cpp
@@ -90,6 +90,8 @@ void QQuickPdfNavigationStack::forward()
if (forwardAvailableWas != forwardAvailable())
emit forwardAvailableChanged();
m_changing = false;
+ qCDebug(qLcNav) << "forward: index" << m_currentHistoryIndex << "page" << currentPage()
+ << "@" << currentLocation() << "zoom" << currentZoom();
}
/*!
@@ -120,6 +122,8 @@ void QQuickPdfNavigationStack::back()
if (!forwardAvailableWas)
emit forwardAvailableChanged();
m_changing = false;
+ qCDebug(qLcNav) << "back: index" << m_currentHistoryIndex << "page" << currentPage()
+ << "@" << currentLocation() << "zoom" << currentZoom();
}
/*!
@@ -163,13 +167,14 @@ qreal QQuickPdfNavigationStack::currentZoom() const
\qmlmethod void PdfNavigationStack::push(int page, point location, qreal zoom)
Adds the given destination, consisting of \a page, \a location and \a zoom,
- to the history of visited locations.
+ to the history of visited locations. If \a emitJumped is \c false, the
+ \l jumped() signal will not be emitted.
If forwardAvailable is \c true, calling this function represents a branch
in the timeline which causes the "future" to be lost, and therefore
forwardAvailable will change to \c false.
*/
-void QQuickPdfNavigationStack::push(int page, QPointF location, qreal zoom)
+void QQuickPdfNavigationStack::push(int page, QPointF location, qreal zoom, bool emitJumped)
{
if (page == currentPage() && location == currentLocation() && zoom == currentZoom())
return;
@@ -192,7 +197,8 @@ void QQuickPdfNavigationStack::push(int page, QPointF location, qreal zoom)
emit backAvailableChanged();
if (forwardAvailableWas)
emit forwardAvailableChanged();
- emit jumped(page, location, zoom);
+ if (emitJumped)
+ emit jumped(page, location, zoom);
qCDebug(qLcNav) << "push: index" << m_currentHistoryIndex << "page" << page
<< "@" << location << "zoom" << zoom << "-> history" <<
[this]() {
diff --git a/src/pdf/quick/qquickpdfnavigationstack_p.h b/src/pdf/quick/qquickpdfnavigationstack_p.h
index 8d7102fb1..0d88d62fd 100644
--- a/src/pdf/quick/qquickpdfnavigationstack_p.h
+++ b/src/pdf/quick/qquickpdfnavigationstack_p.h
@@ -67,7 +67,7 @@ class QQuickPdfNavigationStack : public QObject
public:
explicit QQuickPdfNavigationStack(QObject *parent = nullptr);
- Q_INVOKABLE void push(int page, QPointF location, qreal zoom);
+ Q_INVOKABLE void push(int page, QPointF location, qreal zoom, bool emitJumped = true);
Q_INVOKABLE void update(int page, QPointF location, qreal zoom);
Q_INVOKABLE void forward();
Q_INVOKABLE void back();
diff --git a/src/pdf/quick/qquickpdfsearchmodel.cpp b/src/pdf/quick/qquickpdfsearchmodel.cpp
index a4b457841..1f62fbad0 100644
--- a/src/pdf/quick/qquickpdfsearchmodel.cpp
+++ b/src/pdf/quick/qquickpdfsearchmodel.cpp
@@ -116,6 +116,29 @@ QVector<QPolygonF> QQuickPdfSearchModel::currentResultBoundingPolygons() const
return ret;
}
+/*!
+ \qmlproperty point PdfSearchModel::currentResultBoundingRect
+
+ The bounding box containing all \l currentResultBoundingPolygons.
+
+ When this property changes, a scrollable view should automatically scroll
+ itself in such a way as to ensure that this region is visible; for example,
+ it could try to position the upper-left corner near the upper-left of its
+ own viewport, subject to the constraints of the scrollable area.
+*/
+QRectF QQuickPdfSearchModel::currentResultBoundingRect() const
+{
+ QRectF ret;
+ const auto &results = const_cast<QQuickPdfSearchModel *>(this)->resultsOnPage(m_currentPage);
+ if (m_currentResult < 0 || m_currentResult >= results.count())
+ return ret;
+ auto rects = results[m_currentResult].rectangles();
+ ret = rects.takeFirst();
+ for (auto rect : rects)
+ ret = ret.united(rect);
+ return ret;
+}
+
void QQuickPdfSearchModel::onResultsChanged()
{
emit currentPageBoundingPolygonsChanged();
@@ -266,6 +289,7 @@ void QQuickPdfSearchModel::setCurrentResult(int currentResult)
m_currentResult = currentResult;
emit currentResultChanged();
emit currentResultBoundingPolygonsChanged();
+ emit currentResultBoundingRectChanged();
}
/*!
diff --git a/src/pdf/quick/qquickpdfsearchmodel_p.h b/src/pdf/quick/qquickpdfsearchmodel_p.h
index 3e05f80e3..66fc583d9 100644
--- a/src/pdf/quick/qquickpdfsearchmodel_p.h
+++ b/src/pdf/quick/qquickpdfsearchmodel_p.h
@@ -64,6 +64,7 @@ class QQuickPdfSearchModel : public QPdfSearchModel
Q_PROPERTY(int currentResult READ currentResult WRITE setCurrentResult NOTIFY currentResultChanged)
Q_PROPERTY(QVector<QPolygonF> currentPageBoundingPolygons READ currentPageBoundingPolygons NOTIFY currentPageBoundingPolygonsChanged)
Q_PROPERTY(QVector<QPolygonF> currentResultBoundingPolygons READ currentResultBoundingPolygons NOTIFY currentResultBoundingPolygonsChanged)
+ Q_PROPERTY(QRectF currentResultBoundingRect READ currentResultBoundingRect NOTIFY currentResultBoundingRectChanged)
public:
explicit QQuickPdfSearchModel(QObject *parent = nullptr);
@@ -81,6 +82,7 @@ public:
QVector<QPolygonF> currentPageBoundingPolygons() const;
QVector<QPolygonF> currentResultBoundingPolygons() const;
+ QRectF currentResultBoundingRect() const;
signals:
void documentChanged();
@@ -88,6 +90,7 @@ signals:
void currentResultChanged();
void currentPageBoundingPolygonsChanged();
void currentResultBoundingPolygonsChanged();
+ void currentResultBoundingRectChanged();
private:
void updateResults();
diff --git a/src/pdf/quick/qquickpdfselection.cpp b/src/pdf/quick/qquickpdfselection.cpp
index 5371e85e5..23fbb80b9 100644
--- a/src/pdf/quick/qquickpdfselection.cpp
+++ b/src/pdf/quick/qquickpdfselection.cpp
@@ -37,13 +37,20 @@
#include "qquickpdfselection_p.h"
#include "qquickpdfdocument_p.h"
#include <QClipboard>
+#include <QGuiApplication>
+#include <QLoggingCategory>
#include <QQuickItem>
#include <QQmlEngine>
+#include <QRegularExpression>
#include <QStandardPaths>
-#include <private/qguiapplication_p.h>
+#include <QtPdf/private/qpdfdocument_p.h>
+
+Q_LOGGING_CATEGORY(qLcIm, "qt.pdf.im")
QT_BEGIN_NAMESPACE
+static const QRegularExpression WordDelimiter("\\s");
+
/*!
\qmltype PdfSelection
\instantiates QQuickPdfSelection
@@ -54,14 +61,29 @@ QT_BEGIN_NAMESPACE
PdfSelection provides the text string and its geometry within a bounding box
from one point to another.
+
+ To modify the selection using the mouse, bind \l fromPoint and \l toPoint
+ to the suitable properties of an input handler so that they will be set to
+ the positions where the drag gesture begins and ends, respectively; and
+ bind the \l hold property so that it will be set to \c true during the drag
+ gesture and \c false when the gesture ends.
+
+ PdfSelection also directly handles Input Method queries so that text
+ selection handles can be used on platforms such as iOS. For this purpose,
+ it must have keyboard focus.
*/
/*!
Constructs a SearchModel.
*/
-QQuickPdfSelection::QQuickPdfSelection(QObject *parent)
- : QObject(parent)
+QQuickPdfSelection::QQuickPdfSelection(QQuickItem *parent)
+ : QQuickItem(parent)
{
+#if QT_CONFIG(im)
+ setFlags(ItemIsFocusScope | ItemAcceptsInputMethod);
+ // workaround to get Copy instead of Paste on the popover menu (QTBUG-83811)
+ setProperty("qt_im_readonly", QVariant(true));
+#endif
}
QQuickPdfDocument *QQuickPdfSelection::document() const
@@ -124,6 +146,24 @@ QVector<QPolygonF> QQuickPdfSelection::geometry() const
return m_geometry;
}
+void QQuickPdfSelection::clear()
+{
+ m_hitPoint = QPointF();
+ m_fromPoint = QPointF();
+ m_toPoint = QPointF();
+ m_heightAtAnchor = 0;
+ m_heightAtCursor = 0;
+ m_fromCharIndex = -1;
+ m_toCharIndex = -1;
+ m_text.clear();
+ m_geometry.clear();
+ emit fromPointChanged();
+ emit toPointChanged();
+ emit textChanged();
+ emit selectedAreaChanged();
+ QGuiApplication::inputMethod()->update(Qt::ImQueryInput);
+}
+
void QQuickPdfSelection::selectAll()
{
QPdfSelection sel = m_document->m_doc.getAllText(m_page);
@@ -136,10 +176,172 @@ void QQuickPdfSelection::selectAll()
if (sel.bounds() != m_geometry) {
m_geometry = sel.bounds();
- emit geometryChanged();
+ emit selectedAreaChanged();
+ }
+#if QT_CONFIG(im)
+ m_fromCharIndex = sel.startIndex();
+ m_toCharIndex = sel.endIndex();
+ if (sel.bounds().isEmpty()) {
+ m_fromPoint = QPointF();
+ m_toPoint = QPointF();
+ } else {
+ m_fromPoint = sel.bounds().first().boundingRect().topLeft() * m_renderScale;
+ m_toPoint = sel.bounds().last().boundingRect().bottomRight() * m_renderScale - QPointF(0, m_heightAtCursor);
+ }
+
+ QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle | Qt::ImAnchorRectangle);
+#endif
+}
+
+#if QT_CONFIG(im)
+void QQuickPdfSelection::keyReleaseEvent(QKeyEvent *ev)
+{
+ qCDebug(qLcIm) << "release" << ev;
+ const auto &allText = pageText();
+ if (ev == QKeySequence::MoveToPreviousWord) {
+ // iOS sends MoveToPreviousWord first to get to the beginning of the word,
+ // and then SelectNextWord to select the whole word.
+ int i = allText.lastIndexOf(WordDelimiter, m_fromCharIndex - allText.length());
+ if (i < 0)
+ i = 0;
+ else
+ i += 1; // don't select the space before the word
+ auto sel = m_document->m_doc.getSelectionAtIndex(m_page, i, m_text.length() + m_fromCharIndex - i);
+ update(sel);
+ QGuiApplication::inputMethod()->update(Qt::ImAnchorRectangle);
+ } else if (ev == QKeySequence::SelectNextWord) {
+ int i = allText.indexOf(WordDelimiter, m_toCharIndex);
+ if (i < 0)
+ i = allText.length(); // go to the end of m_textAfter
+ auto sel = m_document->m_doc.getSelectionAtIndex(m_page, m_fromCharIndex, m_text.length() + i - m_toCharIndex);
+ update(sel);
+ QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle);
+ } else if (ev == QKeySequence::Copy) {
+ copyToClipboard();
+ }
+}
+
+void QQuickPdfSelection::inputMethodEvent(QInputMethodEvent *event)
+{
+ for (auto attr : event->attributes()) {
+ switch (attr.type) {
+ case QInputMethodEvent::Cursor:
+ qCDebug(qLcIm) << "QInputMethodEvent::Cursor: moved to" << attr.start << "len" << attr.length;
+ break;
+ case QInputMethodEvent::Selection: {
+ auto sel = m_document->m_doc.getSelectionAtIndex(m_page, attr.start, attr.length);
+ update(sel);
+ qCDebug(qLcIm) << "QInputMethodEvent::Selection: from" << attr.start << "len" << attr.length
+ << "result:" << m_fromCharIndex << "->" << m_toCharIndex << sel.boundingRectangle();
+ // the iOS plugin decided that it wanted to change the selection, but still has to be told to move the handles (!?)
+ QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle | Qt::ImAnchorRectangle);
+ break;
+ }
+ case QInputMethodEvent::Language:
+ case QInputMethodEvent::Ruby:
+ case QInputMethodEvent::TextFormat:
+ break;
+ }
}
}
+QVariant QQuickPdfSelection::inputMethodQuery(Qt::InputMethodQuery query, const QVariant &argument) const
+{
+ if (!argument.isNull()) {
+ qCDebug(qLcIm) << "IM query" << query << "with arg" << argument;
+ if (query == Qt::ImCursorPosition) {
+ // If it didn't move since last time, return the same result.
+ if (m_hitPoint == argument.toPointF())
+ return inputMethodQuery(query);
+ m_hitPoint = argument.toPointF();
+ auto tp = m_document->m_doc.d->hitTest(m_page, m_hitPoint / m_renderScale);
+ qCDebug(qLcIm) << "ImCursorPosition hit testing in px" << m_hitPoint << "pt" << (m_hitPoint / m_renderScale)
+ << "got char index" << tp.charIndex << "@" << tp.position << "pt," << tp.position * m_renderScale << "px";
+ if (tp.charIndex >= 0) {
+ m_toCharIndex = tp.charIndex;
+ m_toPoint = tp.position * m_renderScale - QPointF(0, m_heightAtCursor);
+ m_heightAtCursor = tp.height * m_renderScale;
+ if (qFuzzyIsNull(m_heightAtAnchor))
+ m_heightAtAnchor = m_heightAtCursor;
+ }
+ }
+ }
+ return inputMethodQuery(query);
+}
+
+QVariant QQuickPdfSelection::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ QVariant ret;
+ switch (query) {
+ case Qt::ImEnabled:
+ ret = true;
+ break;
+ case Qt::ImHints:
+ ret = QVariant(Qt::ImhMultiLine | Qt::ImhNoPredictiveText);
+ break;
+ case Qt::ImInputItemClipRectangle:
+ ret = boundingRect();
+ break;
+ case Qt::ImAnchorPosition:
+ ret = m_fromCharIndex;
+ break;
+ case Qt::ImAbsolutePosition:
+ ret = m_toCharIndex;
+ break;
+ case Qt::ImCursorPosition:
+ ret = m_toCharIndex;
+ break;
+ case Qt::ImAnchorRectangle:
+ ret = QRectF(m_fromPoint, QSizeF(1, m_heightAtAnchor));
+ break;
+ case Qt::ImCursorRectangle:
+ ret = QRectF(m_toPoint, QSizeF(1, m_heightAtCursor));
+ break;
+ case Qt::ImSurroundingText:
+ ret = QVariant(pageText());
+ break;
+ case Qt::ImTextBeforeCursor:
+ ret = QVariant(pageText().mid(0, m_toCharIndex));
+ break;
+ case Qt::ImTextAfterCursor:
+ ret = QVariant(pageText().mid(m_toCharIndex));
+ break;
+ case Qt::ImCurrentSelection:
+ ret = QVariant(m_text);
+ break;
+ case Qt::ImEnterKeyType:
+ break;
+ case Qt::ImFont: {
+ QFont font = QGuiApplication::font();
+ font.setPointSizeF(m_heightAtCursor);
+ ret = font;
+ break;
+ }
+ case Qt::ImMaximumTextLength:
+ break;
+ case Qt::ImPreferredLanguage:
+ break;
+ case Qt::ImPlatformData:
+ break;
+ case Qt::ImQueryInput:
+ case Qt::ImQueryAll:
+ qWarning() << "unexpected composite query";
+ break;
+ }
+ qCDebug(qLcIm) << "IM query" << query << "returns" << ret;
+ return ret;
+}
+#endif // QT_CONFIG(im)
+
+const QString &QQuickPdfSelection::pageText() const
+{
+ if (m_pageTextDirty) {
+ m_pageText = m_document->m_doc.getAllText(m_page).text();
+ m_pageTextDirty = false;
+ }
+ return m_pageText;
+}
+
void QQuickPdfSelection::resetPoints()
{
bool wasHolding = m_hold;
@@ -167,18 +369,42 @@ void QQuickPdfSelection::setPage(int page)
return;
m_page = page;
+ m_pageTextDirty = true;
emit pageChanged();
resetPoints();
}
/*!
+ \qmlproperty real PdfSelection::renderScale
+ \brief The ratio from points to pixels at which the page is rendered.
+
+ This is used to scale \l fromPoint and \l toPoint to find ranges of
+ selected characters in the document, because positions within the document
+ are always given in points.
+*/
+qreal QQuickPdfSelection::renderScale() const
+{
+ return m_renderScale;
+}
+
+void QQuickPdfSelection::setRenderScale(qreal scale)
+{
+ if (qFuzzyCompare(scale, m_renderScale))
+ return;
+
+ m_renderScale = scale;
+ emit renderScaleChanged();
+ updateResults();
+}
+
+/*!
\qmlproperty point PdfSelection::fromPoint
- The beginning location, in \l {https://en.wikipedia.org/wiki/Point_(typography)}{points}
- from the upper-left corner of the page, from which to find selected text.
- This can be bound to a scaled version of the \c centroid.pressPosition
- of a \l DragHandler to begin selecting text from the position where the user
- presses the mouse button and begins dragging, for example.
+ The beginning location, in pixels from the upper-left corner of the page,
+ from which to find selected text. This can be bound to the
+ \c centroid.pressPosition of a \l DragHandler to begin selecting text from
+ the position where the user presses the mouse button and begins dragging,
+ for example.
*/
QPointF QQuickPdfSelection::fromPoint() const
{
@@ -198,11 +424,10 @@ void QQuickPdfSelection::setFromPoint(QPointF fromPoint)
/*!
\qmlproperty point PdfSelection::toPoint
- The ending location, in \l {https://en.wikipedia.org/wiki/Point_(typography)}{points}
- from the upper-left corner of the page, from which to find selected text.
- This can be bound to a scaled version of the \c centroid.position
- of a \l DragHandler to end selection of text at the position where the user
- is currently dragging the mouse, for example.
+ The ending location, in pixels from the upper-left corner of the page,
+ from which to find selected text. This can be bound to the
+ \c centroid.position of a \l DragHandler to end selection of text at the
+ position where the user is currently dragging the mouse, for example.
*/
QPointF QQuickPdfSelection::toPoint() const
{
@@ -267,7 +492,13 @@ void QQuickPdfSelection::updateResults()
{
if (!m_document)
return;
- QPdfSelection sel = m_document->document().getSelection(m_page, m_fromPoint, m_toPoint);
+ QPdfSelection sel = m_document->document().getSelection(m_page,
+ m_fromPoint / m_renderScale, m_toPoint / m_renderScale);
+ update(sel, true);
+}
+
+void QQuickPdfSelection::update(const QPdfSelection &sel, bool textAndGeometryOnly)
+{
if (sel.text() != m_text) {
m_text = sel.text();
if (QGuiApplication::clipboard()->supportsSelection())
@@ -277,7 +508,33 @@ void QQuickPdfSelection::updateResults()
if (sel.bounds() != m_geometry) {
m_geometry = sel.bounds();
- emit geometryChanged();
+ emit selectedAreaChanged();
+ }
+
+ if (textAndGeometryOnly)
+ return;
+
+ m_fromCharIndex = sel.startIndex();
+ m_toCharIndex = sel.endIndex();
+ if (sel.bounds().isEmpty()) {
+ m_fromPoint = sel.boundingRectangle().topLeft() * m_renderScale;
+ m_toPoint = m_fromPoint;
+ } else {
+ Qt::InputMethodQueries toUpdate = {};
+ QRectF firstLineBounds = sel.bounds().first().boundingRect();
+ m_fromPoint = firstLineBounds.topLeft() * m_renderScale;
+ if (!qFuzzyCompare(m_heightAtAnchor, firstLineBounds.height())) {
+ m_heightAtAnchor = firstLineBounds.height() * m_renderScale;
+ toUpdate.setFlag(Qt::ImAnchorRectangle);
+ }
+ QRectF lastLineBounds = sel.bounds().last().boundingRect();
+ if (!qFuzzyCompare(m_heightAtCursor, lastLineBounds.height())) {
+ m_heightAtCursor = lastLineBounds.height() * m_renderScale;
+ toUpdate.setFlag(Qt::ImCursorRectangle);
+ }
+ m_toPoint = lastLineBounds.topRight() * m_renderScale;
+ if (toUpdate)
+ QGuiApplication::inputMethod()->update(toUpdate);
}
}
diff --git a/src/pdf/quick/qquickpdfselection_p.h b/src/pdf/quick/qquickpdfselection_p.h
index d231c0d11..ee7e1f85f 100644
--- a/src/pdf/quick/qquickpdfselection_p.h
+++ b/src/pdf/quick/qquickpdfselection_p.h
@@ -52,30 +52,35 @@
#include <QPolygonF>
#include <QVariant>
#include <QtQml/qqml.h>
+#include <QtQuick/qquickitem.h>
#include "qquickpdfdocument_p.h"
QT_BEGIN_NAMESPACE
+class QPdfSelection;
-class QQuickPdfSelection : public QObject
+class QQuickPdfSelection : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QQuickPdfDocument *document READ document WRITE setDocument NOTIFY documentChanged)
Q_PROPERTY(int page READ page WRITE setPage NOTIFY pageChanged)
+ Q_PROPERTY(qreal renderScale READ renderScale WRITE setRenderScale NOTIFY renderScaleChanged)
Q_PROPERTY(QPointF fromPoint READ fromPoint WRITE setFromPoint NOTIFY fromPointChanged)
Q_PROPERTY(QPointF toPoint READ toPoint WRITE setToPoint NOTIFY toPointChanged)
Q_PROPERTY(bool hold READ hold WRITE setHold NOTIFY holdChanged)
Q_PROPERTY(QString text READ text NOTIFY textChanged)
- Q_PROPERTY(QVector<QPolygonF> geometry READ geometry NOTIFY geometryChanged)
+ Q_PROPERTY(QVector<QPolygonF> geometry READ geometry NOTIFY selectedAreaChanged)
public:
- explicit QQuickPdfSelection(QObject *parent = nullptr);
+ explicit QQuickPdfSelection(QQuickItem *parent = nullptr);
QQuickPdfDocument *document() const;
void setDocument(QQuickPdfDocument * document);
int page() const;
void setPage(int page);
+ qreal renderScale() const;
+ void setRenderScale(qreal scale);
QPointF fromPoint() const;
void setFromPoint(QPointF fromPoint);
QPointF toPoint() const;
@@ -86,6 +91,7 @@ public:
QString text() const;
QVector<QPolygonF> geometry() const;
+ Q_INVOKABLE void clear();
Q_INVOKABLE void selectAll();
#if QT_CONFIG(clipboard)
Q_INVOKABLE void copyToClipboard() const;
@@ -94,24 +100,43 @@ public:
signals:
void documentChanged();
void pageChanged();
+ void renderScaleChanged();
void fromPointChanged();
void toPointChanged();
void holdChanged();
void textChanged();
- void geometryChanged();
+ void selectedAreaChanged();
+
+protected:
+#if QT_CONFIG(im)
+ void keyReleaseEvent(QKeyEvent *ev) override;
+ void inputMethodEvent(QInputMethodEvent *event) override;
+ Q_INVOKABLE QVariant inputMethodQuery(Qt::InputMethodQuery query, const QVariant &argument) const;
+ QVariant inputMethodQuery(Qt::InputMethodQuery query) const override;
+#endif
private:
void resetPoints();
void updateResults();
+ void update(const QPdfSelection &sel, bool textAndGeometryOnly = false);
+ const QString &pageText() const;
private:
QQuickPdfDocument *m_document = nullptr;
+ mutable QPointF m_hitPoint;
QPointF m_fromPoint;
- QPointF m_toPoint;
- QString m_text;
+ mutable QPointF m_toPoint;
+ qreal m_renderScale = 1;
+ mutable qreal m_heightAtAnchor = 0;
+ mutable qreal m_heightAtCursor = 0;
+ QString m_text; // selected text
+ mutable QString m_pageText; // all text on the page
QVector<QPolygonF> m_geometry;
int m_page = 0;
+ int m_fromCharIndex = -1; // same as anchor position
+ mutable int m_toCharIndex = -1; // same as cursor position
bool m_hold = false;
+ mutable bool m_pageTextDirty = true;
Q_DISABLE_COPY(QQuickPdfSelection)
};
@@ -119,5 +144,5 @@ private:
QT_END_NAMESPACE
QML_DECLARE_TYPE(QQuickPdfSelection)
-\
+
#endif // QQUICKPDFSELECTION_P_H
diff --git a/src/pdf/quick/qquicktableviewextra.cpp b/src/pdf/quick/qquicktableviewextra.cpp
new file mode 100644
index 000000000..2b59d6c6e
--- /dev/null
+++ b/src/pdf/quick/qquicktableviewextra.cpp
@@ -0,0 +1,193 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "qquicktableviewextra_p.h"
+#include <QtQml>
+#include <QQmlContext>
+
+Q_LOGGING_CATEGORY(qLcTVE, "qt.pdf.tableextra")
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+ \qmltype TableViewExtra
+ \instantiates QQuickTableViewExtra
+ \inqmlmodule QtQuick.Pdf
+ \ingroup pdf
+ \brief A helper class with missing TableView functions
+ \since 5.15
+
+ TableViewExtra provides equivalents for some functions that will be added
+ to TableView in Qt 6.
+*/
+
+QQuickTableViewExtra::QQuickTableViewExtra(QObject *parent) : QObject(parent)
+{
+}
+
+QPoint QQuickTableViewExtra::cellAtPos(qreal x, qreal y) const
+{
+ QPointF position(x, y);
+#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
+ return m_tableView->cellAtPos(position);
+#else
+ if (!m_tableView->boundingRect().contains(position))
+ return QPoint(-1, -1);
+
+ const QQuickItem *contentItem = m_tableView->contentItem();
+
+ for (const QQuickItem *child : contentItem->childItems()) {
+ const QPointF posInChild = m_tableView->mapToItem(child, position);
+ if (child->boundingRect().contains(posInChild)) {
+ const auto context = qmlContext(child);
+ const int column = context->contextProperty("column").toInt();
+ const int row = context->contextProperty("row").toInt();
+ return QPoint(column, row);
+ }
+ }
+
+ return QPoint(-1, -1);
+#endif
+}
+
+QQuickItem *QQuickTableViewExtra::itemAtCell(const QPoint &cell) const
+{
+#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
+ return m_tableView->itemAtCell(cell);
+#else
+ const QQuickItem *contentItem = m_tableView->contentItem();
+
+ for (QQuickItem *child : contentItem->childItems()) {
+ const auto context = qmlContext(child);
+ const int column = context->contextProperty("column").toInt();
+ const int row = context->contextProperty("row").toInt();
+ if (QPoint(column, row) == cell)
+ return child;
+ }
+
+ return nullptr;
+#endif
+}
+
+void QQuickTableViewExtra::positionViewAtCell(const QPoint &cell, Qt::Alignment alignment, const QPointF &offset)
+{
+#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
+ m_tableView->positionViewAtCell(cell, alignment, offset);
+#else
+ // Note: this fallback implementation assumes all cells to be of the same size!
+
+ if (cell.x() < 0 || cell.x() > m_tableView->columns() - 1)
+ return;
+ if (cell.y() < 0 || cell.y() > m_tableView->rows() - 1)
+ return;
+
+ Qt::Alignment verticalAlignment = alignment & (Qt::AlignTop | Qt::AlignVCenter | Qt::AlignBottom);
+ Qt::Alignment horizontalAlignment = alignment & (Qt::AlignLeft | Qt::AlignHCenter | Qt::AlignRight);
+
+ const QQuickItem *contentItem = m_tableView->contentItem();
+ const QQuickItem *randomChild = contentItem->childItems().first();
+ const qreal cellWidth = randomChild->width();
+ const qreal cellHeight = randomChild->height();
+
+ if (!verticalAlignment && !horizontalAlignment) {
+ qmlWarning(this) << "No valid alignment specified";
+ return;
+ }
+
+ if (horizontalAlignment) {
+ qreal newPosX = 0;
+ const qreal columnPosLeft = int(cell.x() * (cellWidth + m_tableView->columnSpacing()));
+ m_tableView->setContentX(0);
+ m_tableView->forceLayout();
+ m_tableView->setContentX(columnPosLeft);
+ m_tableView->forceLayout();
+
+ switch (horizontalAlignment) {
+ case Qt::AlignLeft:
+ newPosX = m_tableView->contentX() + offset.x();
+ break;
+ case Qt::AlignHCenter:
+ newPosX = m_tableView->contentX()
+ - m_tableView->width() / 2
+ + (cellWidth / 2)
+ + offset.x();
+ break;
+ case Qt::AlignRight:
+ newPosX = m_tableView->contentX()
+ - m_tableView->width()
+ + cellWidth
+ + offset.x();
+ break;
+ }
+
+ m_tableView->setContentX(newPosX);
+ m_tableView->forceLayout();
+ }
+
+ if (verticalAlignment) {
+ qreal newPosY = 0;
+ const qreal rowPosTop = int(cell.y() * (cellHeight + m_tableView->rowSpacing()));
+ m_tableView->setContentY(0);
+ m_tableView->forceLayout();
+ m_tableView->setContentY(rowPosTop);
+ m_tableView->forceLayout();
+
+ switch (verticalAlignment) {
+ case Qt::AlignTop:
+ newPosY = m_tableView->contentY() + offset.y();
+ break;
+ case Qt::AlignVCenter:
+ newPosY = m_tableView->contentY()
+ - m_tableView->height() / 2
+ + (cellHeight / 2)
+ + offset.y();
+ break;
+ case Qt::AlignBottom:
+ newPosY = m_tableView->contentY()
+ - m_tableView->height()
+ + cellHeight
+ + offset.y();
+ break;
+ }
+
+ m_tableView->setContentY(newPosY);
+ m_tableView->forceLayout();
+ }
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/pdf/quick/qquicktableviewextra_p.h b/src/pdf/quick/qquicktableviewextra_p.h
new file mode 100644
index 000000000..11b4955a1
--- /dev/null
+++ b/src/pdf/quick/qquicktableviewextra_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef QQUICKTABLEVIEWEXTRA_P_H
+#define QQUICKTABLEVIEWEXTRA_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QPointF>
+#include <QPolygonF>
+#include <QVariant>
+#include <QtQml/qqml.h>
+#include <QtQuick/qquickitem.h>
+#include <QtQuick/private/qquicktableview_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickTableViewExtra : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickTableView *tableView READ tableView WRITE setTableView)
+
+public:
+ QQuickTableViewExtra(QObject *parent = nullptr);
+
+ QQuickTableView * tableView() const { return m_tableView; }
+ void setTableView(QQuickTableView * tableView) { m_tableView = tableView; }
+
+ Q_INVOKABLE QPoint cellAtPos(qreal x, qreal y) const;
+ Q_INVOKABLE QQuickItem *itemAtCell(int column, int row) const {
+ return itemAtCell(QPoint(column, row));
+ }
+ Q_INVOKABLE QQuickItem *itemAtCell(const QPoint &cell) const;
+ Q_INVOKABLE void positionViewAtCell(int column, int row, Qt::Alignment alignment, const QPointF &offset = QPointF()) {
+ positionViewAtCell(QPoint(column, row), alignment, offset);
+ }
+ Q_INVOKABLE void positionViewAtCell(const QPoint &cell, Qt::Alignment alignment, const QPointF &offset);
+ Q_INVOKABLE void positionViewAtRow(int row, Qt::Alignment alignment, qreal offset = 0) {
+ positionViewAtCell(QPoint(0, row), alignment & Qt::AlignVertical_Mask, QPointF(0, offset));
+ }
+
+private:
+ QQuickTableView *m_tableView = nullptr;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickTableViewExtra)
+
+#endif // QQUICKTABLEVIEWEXTRA_P_H
diff --git a/src/pdf/quick/quick.pro b/src/pdf/quick/quick.pro
index b62b80346..bd6bc8827 100644
--- a/src/pdf/quick/quick.pro
+++ b/src/pdf/quick/quick.pro
@@ -3,6 +3,10 @@ TARGET = pdfplugin
TARGETPATH = QtQuick/Pdf
IMPORT_VERSION = 1.0
+# qpdfdocument_p.h includes pdfium headers which we must find in order to use private API
+CHROMIUM_SRC_DIR = $$QTWEBENGINE_ROOT/$$getChromiumSrcDir()
+INCLUDEPATH += $$CHROMIUM_SRC_DIR
+
#QMAKE_DOCS = $$PWD/doc/qtquickpdf.qdocconf
PDF_QML_FILES = \
@@ -21,6 +25,7 @@ SOURCES += \
qquickpdfnavigationstack.cpp \
qquickpdfsearchmodel.cpp \
qquickpdfselection.cpp \
+ qquicktableviewextra.cpp \
HEADERS += \
qquickpdfdocument_p.h \
@@ -28,7 +33,8 @@ HEADERS += \
qquickpdfnavigationstack_p.h \
qquickpdfsearchmodel_p.h \
qquickpdfselection_p.h \
+ qquicktableviewextra_p.h \
-QT += pdf quick-private gui gui-private core core-private qml qml-private
+QT += pdf pdf-private gui core qml quick quick-private
load(qml_plugin)