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>bb10
parent
9275edbc31
commit
c80f262258
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -1394,6 +1394,7 @@ namespace Qt {
|
|||
ImAnchorRectangle = 0x4000,
|
||||
ImInputItemClipRectangle = 0x8000,
|
||||
|
||||
ImReadOnly = 0x10000,
|
||||
ImPlatformData = 0x80000000,
|
||||
ImQueryInput = ImCursorRectangle | ImCursorPosition | ImSurroundingText |
|
||||
ImCurrentSelection | ImAnchorRectangle | ImAnchorPosition,
|
||||
|
|
|
|||
|
|
@ -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 && (!readOnlyVariant.isValid() || readOnly)) {
|
||||
QtAndroidInput::updateHandles(Hidden);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpos == anchor || im->anchorRectangle().isNull()) {
|
||||
if (!query.value(Qt::ImEnabled).toBool()) {
|
||||
QtAndroidInput::updateHandles(Hidden);
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in New Issue