summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPiotr Mikolajczyk <piotr.mikolajczyk@qt.io>2021-02-25 15:36:29 +0100
committerPiotr Mikolajczyk <piotr.mikolajczyk@qt.io>2021-03-30 10:35:59 +0200
commitc80f262258b7846bf199887bcfdbb6dcfda6ad6f (patch)
tree509ec5a3dde42db3af4fa29f6a1fe7f154f85e98
parent9275edbc31a0ca83740df5b4450b7776c59012dc (diff)
Android: Add select and copy capability to read-only text widgets
In case of a read-only text editing widget it was imposibble to copy text from it. In QtWidgets you could not even see the selection handless. Text selection in QtWidgets module was filtered depending on readOnly property of the widget. Additionally, in InputMethod the read-only state was translated into disabled. Patch also makes the edit menu to be aware of the read-only status of the control - the menu items are different for rw and ro controls. Task-number: QTBUG-91417 Change-Id: Ic7b27f78678eeaa87a38607af787f254db1383b8 Reviewed-by: Rami Potinkara <rami.potinkara@qt.io> Reviewed-by: Ville Voutilainen <ville.voutilainen@qt.io>
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java4
-rw-r--r--src/corelib/global/qnamespace.h1
-rw-r--r--src/plugins/platforms/android/qandroidinputcontext.cpp31
-rw-r--r--src/widgets/widgets/qlineedit.cpp16
-rw-r--r--src/widgets/widgets/qlineedit_p.cpp12
-rw-r--r--src/widgets/widgets/qlineedit_p.h6
-rw-r--r--src/widgets/widgets/qplaintextedit.cpp11
-rw-r--r--src/widgets/widgets/qtextbrowser.cpp11
-rw-r--r--src/widgets/widgets/qtextedit.cpp6
-rw-r--r--src/widgets/widgets/qwidgetlinecontrol_p.h3
-rw-r--r--src/widgets/widgets/qwidgettextcontrol.cpp2
-rw-r--r--tests/auto/widgets/widgets/qtextbrowser/tst_qtextbrowser.cpp18
12 files changed, 99 insertions, 22 deletions
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java
index e9efd06497..0a60587aa8 100644
--- a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java
@@ -566,9 +566,7 @@ public class QtActivityDelegate
break;
}
- if (QtNative.hasClipboardText())
- editButtons |= EditContextView.PASTE_BUTTON;
- else
+ if (!QtNative.hasClipboardText())
editButtons &= ~EditContextView.PASTE_BUTTON;
if ((mode & CursorHandleShowEdit) == CursorHandleShowEdit && editButtons != 0) {
diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h
index 4a051e8ff0..0f5003e593 100644
--- a/src/corelib/global/qnamespace.h
+++ b/src/corelib/global/qnamespace.h
@@ -1394,6 +1394,7 @@ namespace Qt {
ImAnchorRectangle = 0x4000,
ImInputItemClipRectangle = 0x8000,
+ ImReadOnly = 0x10000,
ImPlatformData = 0x80000000,
ImQueryInput = ImCursorRectangle | ImCursorPosition | ImSurroundingText |
ImCurrentSelection | ImAnchorRectangle | ImAnchorPosition,
diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp
index 9ed6dd396d..01b8ae8dee 100644
--- a/src/plugins/platforms/android/qandroidinputcontext.cpp
+++ b/src/plugins/platforms/android/qandroidinputcontext.cpp
@@ -604,34 +604,34 @@ void QAndroidInputContext::updateCursorPosition()
void QAndroidInputContext::updateSelectionHandles()
{
static bool noHandles = qEnvironmentVariableIntValue("QT_QPA_NO_TEXT_HANDLES");
- if (noHandles)
+ if (noHandles || !m_focusObject)
return;
auto im = qGuiApp->inputMethod();
- if (!m_focusObject || ((m_handleMode & 0xff) == Hidden)) {
- // Hide the handles
- QtAndroidInput::updateHandles(Hidden);
- return;
- }
- QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImAnchorPosition | Qt::ImEnabled | Qt::ImCurrentSelection | Qt::ImHints | Qt::ImSurroundingText);
+
+ QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImAnchorPosition | Qt::ImEnabled
+ | Qt::ImCurrentSelection | Qt::ImHints | Qt::ImSurroundingText
+ | Qt::ImReadOnly);
QCoreApplication::sendEvent(m_focusObject, &query);
int cpos = query.value(Qt::ImCursorPosition).toInt();
int anchor = query.value(Qt::ImAnchorPosition).toInt();
+ const QVariant readOnlyVariant = query.value(Qt::ImReadOnly);
+ bool readOnly = readOnlyVariant.toBool();
- if (cpos == anchor || im->anchorRectangle().isNull()) {
- if (!query.value(Qt::ImEnabled).toBool()) {
- QtAndroidInput::updateHandles(Hidden);
- return;
- }
+ if ( cpos == anchor && (!readOnlyVariant.isValid() || readOnly)) {
+ QtAndroidInput::updateHandles(Hidden);
+ return;
+ }
+ if (cpos == anchor || im->anchorRectangle().isNull()) {
auto curRect = cursorRectangle();
QPoint cursorPoint = qGuiApp->focusWindow()->handle()->mapToGlobal(QPoint(curRect.x() + (curRect.width() / 2), curRect.y() + curRect.height()));
QPoint editMenuPoint(cursorPoint.x(), cursorPoint.y());
m_handleMode &= ShowEditPopup;
m_handleMode |= ShowCursor;
- uint32_t buttons = EditContext::PasteButton;
+ uint32_t buttons = readOnly ? 0 : EditContext::PasteButton;
if (!query.value(Qt::ImSurroundingText).toString().isEmpty())
buttons |= EditContext::SelectAllButton;
QtAndroidInput::updateHandles(m_handleMode, editMenuPoint, buttons, cursorPoint);
@@ -650,7 +650,10 @@ void QAndroidInputContext::updateSelectionHandles()
QPoint leftPoint(leftRect.bottomLeft().toPoint());
QPoint righPoint(rightRect.bottomRight().toPoint());
QPoint editPoint(leftRect.united(rightRect).topLeft().toPoint());
- QtAndroidInput::updateHandles(m_handleMode, editPoint, EditContext::AllButtons, leftPoint, righPoint,
+ uint32_t buttons = readOnly ? EditContext::CopyButton | EditContext::SelectAllButton
+ : EditContext::AllButtons;
+
+ QtAndroidInput::updateHandles(m_handleMode, editPoint, buttons, leftPoint, righPoint,
query.value(Qt::ImCurrentSelection).toString().isRightToLeft());
m_hideCursorHandleTimer.stop();
}
diff --git a/src/widgets/widgets/qlineedit.cpp b/src/widgets/widgets/qlineedit.cpp
index 9643c4db43..a308f92f9d 100644
--- a/src/widgets/widgets/qlineedit.cpp
+++ b/src/widgets/widgets/qlineedit.cpp
@@ -1806,7 +1806,7 @@ QRect QLineEdit::cursorRect() const
void QLineEdit::inputMethodEvent(QInputMethodEvent *e)
{
Q_D(QLineEdit);
- if (d->control->isReadOnly()) {
+ if (!d->shouldEnableInputMethod()) {
e->ignore();
return;
}
@@ -1874,6 +1874,20 @@ QVariant QLineEdit::inputMethodQuery(Qt::InputMethodQuery property, QVariant arg
return QVariant(d->control->selectionEnd());
else
return QVariant(d->control->selectionStart());
+ case Qt::ImReadOnly:
+ return isReadOnly();
+ case Qt::ImTextBeforeCursor: {
+ const QPointF pt = argument.toPointF();
+ if (!pt.isNull())
+ return d->textBeforeCursor(d->xToPos(pt.x(), QTextLine::CursorBetweenCharacters));
+ else
+ return d->textBeforeCursor(d->control->cursor()); }
+ case Qt::ImTextAfterCursor: {
+ const QPointF pt = argument.toPointF();
+ if (!pt.isNull())
+ return d->textAfterCursor(d->xToPos(pt.x(), QTextLine::CursorBetweenCharacters));
+ else
+ return d->textAfterCursor(d->control->cursor()); }
default:
return QWidget::inputMethodQuery(property);
}
diff --git a/src/widgets/widgets/qlineedit_p.cpp b/src/widgets/widgets/qlineedit_p.cpp
index 7526379f3c..a5ac6a2211 100644
--- a/src/widgets/widgets/qlineedit_p.cpp
+++ b/src/widgets/widgets/qlineedit_p.cpp
@@ -86,6 +86,18 @@ int QLineEditPrivate::xToPos(int x, QTextLine::CursorPosition betweenOrOn) const
return control->xToPos(x, betweenOrOn);
}
+QString QLineEditPrivate::textBeforeCursor(int curPos) const
+{
+ const QString &text = control->text();
+ return text.mid(0, curPos);
+}
+
+QString QLineEditPrivate::textAfterCursor(int curPos) const
+{
+ const QString &text = control->text();
+ return text.mid(curPos);
+}
+
bool QLineEditPrivate::inSelection(int x) const
{
x -= adjustedContentsRect().x() - hscroll + horizontalMargin;
diff --git a/src/widgets/widgets/qlineedit_p.h b/src/widgets/widgets/qlineedit_p.h
index 936cf2d088..de9b9c1636 100644
--- a/src/widgets/widgets/qlineedit_p.h
+++ b/src/widgets/widgets/qlineedit_p.h
@@ -179,13 +179,19 @@ public:
void setCursorVisible(bool visible);
void setText(const QString& text);
+ QString textBeforeCursor(int curPos) const;
+ QString textAfterCursor(int curPos) const;
void updatePasswordEchoEditing(bool);
void resetInputMethod();
inline bool shouldEnableInputMethod() const
{
+#if defined (Q_OS_ANDROID)
+ return !control->isReadOnly() || control->isSelectableByMouse();
+#else
return !control->isReadOnly();
+#endif
}
inline bool shouldShowPlaceholderText() const
{
diff --git a/src/widgets/widgets/qplaintextedit.cpp b/src/widgets/widgets/qplaintextedit.cpp
index d2006d0562..20fdbcd6a8 100644
--- a/src/widgets/widgets/qplaintextedit.cpp
+++ b/src/widgets/widgets/qplaintextedit.cpp
@@ -71,9 +71,14 @@
QT_BEGIN_NAMESPACE
-static inline bool shouldEnableInputMethod(QPlainTextEdit *plaintextedit)
+static inline bool shouldEnableInputMethod(QPlainTextEdit *control)
{
- return !plaintextedit->isReadOnly();
+#if defined(Q_OS_ANDROID)
+ Q_UNUSED(control);
+ return !control->isReadOnly() || (control->textInteractionFlags() & Qt::TextSelectableByMouse);
+#else
+ return !control->isReadOnly();
+#endif
}
class QPlainTextDocumentLayoutPrivate : public QAbstractTextDocumentLayoutPrivate
@@ -2238,6 +2243,8 @@ QVariant QPlainTextEdit::inputMethodQuery(Qt::InputMethodQuery query, QVariant a
case Qt::ImHints:
case Qt::ImInputItemClipRectangle:
return QWidget::inputMethodQuery(query);
+ case Qt::ImReadOnly:
+ return isReadOnly();
default:
break;
}
diff --git a/src/widgets/widgets/qtextbrowser.cpp b/src/widgets/widgets/qtextbrowser.cpp
index 99ebaa93bf..6b05156ea3 100644
--- a/src/widgets/widgets/qtextbrowser.cpp
+++ b/src/widgets/widgets/qtextbrowser.cpp
@@ -57,6 +57,15 @@
QT_BEGIN_NAMESPACE
+static inline bool shouldEnableInputMethod(QTextBrowser *texbrowser)
+{
+#if defined (Q_OS_ANDROID)
+ return !texbrowser->isReadOnly() || (texbrowser->textInteractionFlags() & Qt::TextSelectableByMouse);
+#else
+ return !texbrowser->isReadOnly();
+#endif
+}
+
Q_LOGGING_CATEGORY(lcBrowser, "qt.text.browser")
class QTextBrowserPrivate : public QTextEditPrivate
@@ -692,7 +701,7 @@ void QTextBrowserPrivate::init()
#ifndef QT_NO_CURSOR
viewport->setCursor(oldCursor);
#endif
- q->setAttribute(Qt::WA_InputMethodEnabled, !q->isReadOnly());
+ q->setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(q));
q->setUndoRedoEnabled(false);
viewport->setMouseTracking(true);
QObject::connect(q->document(), SIGNAL(contentsChanged()), q, SLOT(_q_documentModified()));
diff --git a/src/widgets/widgets/qtextedit.cpp b/src/widgets/widgets/qtextedit.cpp
index 9cdee2964f..2d3f98b4b9 100644
--- a/src/widgets/widgets/qtextedit.cpp
+++ b/src/widgets/widgets/qtextedit.cpp
@@ -79,7 +79,11 @@ QT_BEGIN_NAMESPACE
static inline bool shouldEnableInputMethod(QTextEdit *textedit)
{
+#if defined (Q_OS_ANDROID)
+ return !textedit->isReadOnly() || (textedit->textInteractionFlags() & Qt::TextSelectableByMouse);
+#else
return !textedit->isReadOnly();
+#endif
}
class QTextEditControl : public QWidgetTextControl
@@ -1832,6 +1836,8 @@ QVariant QTextEdit::inputMethodQuery(Qt::InputMethodQuery query, QVariant argume
case Qt::ImHints:
case Qt::ImInputItemClipRectangle:
return QWidget::inputMethodQuery(query);
+ case Qt::ImReadOnly:
+ return isReadOnly();
default:
break;
}
diff --git a/src/widgets/widgets/qwidgetlinecontrol_p.h b/src/widgets/widgets/qwidgetlinecontrol_p.h
index f906165c20..bb24fe4d5b 100644
--- a/src/widgets/widgets/qwidgetlinecontrol_p.h
+++ b/src/widgets/widgets/qwidgetlinecontrol_p.h
@@ -164,6 +164,9 @@ public:
int selectionStart() const { return hasSelectedText() ? m_selstart : -1; }
int selectionEnd() const { return hasSelectedText() ? m_selend : -1; }
+#if defined (Q_OS_ANDROID)
+ bool isSelectableByMouse() const { return true; }
+#endif
bool inSelection(int x) const
{
if (m_selstart >= m_selend)
diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp
index 1972ff366b..c9c4c848fc 100644
--- a/src/widgets/widgets/qwidgettextcontrol.cpp
+++ b/src/widgets/widgets/qwidgettextcontrol.cpp
@@ -2039,7 +2039,7 @@ bool QWidgetTextControlPrivate::dropEvent(const QMimeData *mimeData, const QPoin
void QWidgetTextControlPrivate::inputMethodEvent(QInputMethodEvent *e)
{
Q_Q(QWidgetTextControl);
- if (!(interactionFlags & Qt::TextEditable) || cursor.isNull()) {
+ if (!(interactionFlags & (Qt::TextEditable | Qt::TextSelectableByMouse)) || cursor.isNull()) {
e->ignore();
return;
}
diff --git a/tests/auto/widgets/widgets/qtextbrowser/tst_qtextbrowser.cpp b/tests/auto/widgets/widgets/qtextbrowser/tst_qtextbrowser.cpp
index 73266d77f3..ec187a55b0 100644
--- a/tests/auto/widgets/widgets/qtextbrowser/tst_qtextbrowser.cpp
+++ b/tests/auto/widgets/widgets/qtextbrowser/tst_qtextbrowser.cpp
@@ -466,11 +466,29 @@ void tst_QTextBrowser::textInteractionFlags_vs_readOnly()
void tst_QTextBrowser::inputMethodAttribute_vs_readOnly()
{
QVERIFY(browser->isReadOnly());
+#if defined(Q_OS_ANDROID)
+ QInputMethodQueryEvent query(Qt::ImReadOnly);
+ QCoreApplication::sendEvent(browser, &query);
+ QVERIFY(query.value(Qt::ImReadOnly).toBool());
+#else
QVERIFY(!browser->testAttribute(Qt::WA_InputMethodEnabled));
+#endif
+
browser->setReadOnly(false);
+#if defined(Q_OS_ANDROID)
+ QCoreApplication::sendEvent(browser, &query);
+ QVERIFY(!query.value(Qt::ImReadOnly).toBool());
+#else
QVERIFY(browser->testAttribute(Qt::WA_InputMethodEnabled));
+#endif
+
browser->setReadOnly(true);
+#if defined(Q_OS_ANDROID)
+ QCoreApplication::sendEvent(browser, &query);
+ QVERIFY(query.value(Qt::ImReadOnly).toBool());
+#else
QVERIFY(!browser->testAttribute(Qt::WA_InputMethodEnabled));
+#endif
}
void tst_QTextBrowser::anchorsWithSelfBuiltHtml()