diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2020-03-06 17:59:32 +0100 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2020-04-30 11:06:17 +0200 |
commit | 8d13facbd88d821cc89b21f43708cc1a81ac79f3 (patch) | |
tree | 4732dd861938f974fe77366bdeab63db76e842cb /src/pdf/quick/qquickpdfselection_p.h | |
parent | 685430b66ab2830d5e0e5ebafc17294ff1ce1f48 (diff) |
Support text selection handles in PDF views
Testing only on iOS so far; QtPdf doesn't work on Android because of
QTBUG-83459, so we can only hope that this might perhaps be cross-platform,
if only text selection handles actually existed on all platforms.
As usual, text selection begins with a long-press; the iOS platform
plugin intercepts that, and we get QInputMethodEvent::Cursor.
There is no cursor, but we use the opportunity to do hit-testing,
because the Cursor event is the only way that we receive the pixel
location where the user is interacting. Then a popover menu appears,
which contains Select and Select All, and either Copy or Paste depending
on the qt_im_readonly property workaround. You don't get handles until
you choose Select, which will select a word. That makes the popover
menu disappear. You can use the toolbar button to copy to the clipboard.
After that, you can drag either handle. inputMethodQuery(query, argument)
is only ever called with ImCursorPosition regardless which handle is
being dragged, so it doesn't make sense to change the selection there,
even though that would be easy if we were given that information.
Instead, the iOS platform figures out the character range for itself and
sends a QInputMethodEvent::Selection event to tell us which text to
select. And yet it still doesn't move the handles without being told:
QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle | Qt::ImAnchorRectangle)
makes that happen. Then the popover menu will appear again, and now you
can use the Copy function on it as an alternative way to copy text to
the clipboard.
By default, when the user does the initial long-press to start selecting
text, the popover menu has Select, Select All, and Paste. In editable
controls there is a second possible menu that normally has Cut, Copy,
Paste and Delete. We are not able to enter that mode. So as a
workaround, to substitute Copy instead of Paste, we set the
qt_im_readonly property so that QIOSTextResponder::canPerformAction()
can detect it and make the substition. Of course that won't work
without the patch to 5.15; so you still get a useless Paste action on
earlier Qt versions.
Selecting a word via the Select popover menu item happens because iOS sends
QKeySequence::MoveToPreviousWord and then QKeySequence::SelectNextWord.
We spend time calling getSelectionAtIndex() twice because of that.
With an actual keyboard, it should be possible to use keystrokes to
extend the selection, but it doesn't seem to work yet with shift-arrows
on a physical bluetooth keyboard on iOS.
Select All on the popover menu works via inputMethodEvent() with attr
QInputMethodEvent::Selection. Copy sends the standard copy key sequence,
so keyReleaseEvent() handles it.
We must rename the geometryChanged signal to selectedAreaChanged now
that we're inheriting from QQuickItem, to avoid shadowing
QQuickItem::geometryChanged.
For this kind of text selection to work even when the rendering is scaled,
it became necessary to inform PdfSelection of the rendering scale; so
a renderScale property is added. Thus it is sensible (and a nice
simplification in QML code) to use it for the fromPoint and toPoint
properties, such that those are now expressed in pixels rather than points.
Fixes: QTBUG-82441
Task-number: QTBUG-83811
Change-Id: I16ecd2db55c6a834be6139ce4f3aae23446fed54
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
Diffstat (limited to 'src/pdf/quick/qquickpdfselection_p.h')
-rw-r--r-- | src/pdf/quick/qquickpdfselection_p.h | 37 |
1 files changed, 31 insertions, 6 deletions
diff --git a/src/pdf/quick/qquickpdfselection_p.h b/src/pdf/quick/qquickpdfselection_p.h index bb4a50fed..80b363848 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> QT_BEGIN_NAMESPACE +class QPdfSelection; class QQuickPdfDocument; -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) }; |