blob: dc49f513f71ef9e6c0481806d66f127e0e556ce7 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtVirtualKeyboard/private/shadowinputcontext_p.h>
#include <QtVirtualKeyboard/qvirtualkeyboardinputcontext.h>
#include <QtVirtualKeyboard/private/virtualkeyboarddebug_p.h>
#include <QtCore/private/qobject_p.h>
#include <QGuiApplication>
#include <QQuickItem>
QT_BEGIN_NAMESPACE
bool operator==(const QInputMethodEvent::Attribute &attribute1, const QInputMethodEvent::Attribute &attribute2);
namespace QtVirtualKeyboard {
class ShadowInputContextPrivate : public QObjectPrivate
{
public:
ShadowInputContextPrivate() :
QObjectPrivate(),
inputContext(nullptr),
anchorRectIntersectsClipRect(false),
cursorRectIntersectsClipRect(false),
selectionControlVisible(false)
{
}
QVirtualKeyboardInputContext *inputContext;
QPointer<QObject> inputItem;
QString preeditText;
QList<QInputMethodEvent::Attribute> preeditTextAttributes;
QRectF anchorRectangle;
QRectF cursorRectangle;
bool anchorRectIntersectsClipRect;
bool cursorRectIntersectsClipRect;
bool selectionControlVisible;
};
ShadowInputContext::ShadowInputContext(QObject *parent) :
QObject(*new ShadowInputContextPrivate(), parent)
{
}
void ShadowInputContext::setInputContext(QVirtualKeyboardInputContext *inputContext)
{
Q_D(ShadowInputContext);
d->inputContext = inputContext;
}
QObject *ShadowInputContext::inputItem() const
{
Q_D(const ShadowInputContext);
return d->inputItem.data();
}
void ShadowInputContext::setInputItem(QObject *inputItem)
{
Q_D(ShadowInputContext);
if (d->inputItem != inputItem) {
d->inputItem = inputItem;
emit inputItemChanged();
update(Qt::ImQueryAll);
}
}
QRectF ShadowInputContext::anchorRectangle() const
{
Q_D(const ShadowInputContext);
return d->anchorRectangle;
}
QRectF ShadowInputContext::cursorRectangle() const
{
Q_D(const ShadowInputContext);
return d->cursorRectangle;
}
bool ShadowInputContext::anchorRectIntersectsClipRect() const
{
Q_D(const ShadowInputContext);
return d->anchorRectIntersectsClipRect;
}
bool ShadowInputContext::cursorRectIntersectsClipRect() const
{
Q_D(const ShadowInputContext);
return d->cursorRectIntersectsClipRect;
}
bool ShadowInputContext::selectionControlVisible() const
{
Q_D(const ShadowInputContext);
return d->selectionControlVisible;
}
void ShadowInputContext::setSelectionOnFocusObject(const QPointF &anchorPos, const QPointF &cursorPos)
{
Q_D(ShadowInputContext);
QObject *focus = d->inputItem;
if (!focus)
return;
QQuickItem *quickItem = qobject_cast<QQuickItem *>(d->inputItem);
bool success;
int anchor = queryFocusObject(Qt::ImCursorPosition, quickItem ? quickItem->mapFromScene(anchorPos) : anchorPos).toInt(&success);
if (success) {
int cursor = queryFocusObject(Qt::ImCursorPosition, quickItem ? quickItem->mapFromScene(cursorPos) : cursorPos).toInt(&success);
if (success) {
QList<QInputMethodEvent::Attribute> imAttributes;
imAttributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, anchor, cursor - anchor, QVariant()));
QInputMethodEvent event(QString(), imAttributes);
QGuiApplication::sendEvent(QGuiApplication::focusObject(), &event);
}
}
}
void ShadowInputContext::updateSelectionProperties()
{
Q_D(ShadowInputContext);
if (!d->inputItem)
return;
QInputMethodQueryEvent imQueryEvent(Qt::ImAnchorRectangle |
Qt::ImCursorRectangle |
Qt::ImInputItemClipRectangle);
QGuiApplication::sendEvent(d->inputItem, &imQueryEvent);
QQuickItem *quickItem = qobject_cast<QQuickItem *>(d->inputItem);
const QRectF anchorRect = imQueryEvent.value(Qt::ImAnchorRectangle).toRectF();
const QRectF cursorRect = imQueryEvent.value(Qt::ImCursorRectangle).toRectF();
const QRectF anchorRectangle = quickItem ? quickItem->mapRectToScene(anchorRect) : anchorRect;
const QRectF cursorRectangle = quickItem ? quickItem->mapRectToScene(cursorRect) : cursorRect;
const QRectF inputItemClipRect = imQueryEvent.value(Qt::ImInputItemClipRectangle).toRectF();
const bool anchorRectIntersectsClipRect = inputItemClipRect.intersects(anchorRect);
const bool cursorRectIntersectsClipRect = inputItemClipRect.intersects(cursorRect);
const bool selectionControlVisible = d->inputContext->isSelectionControlVisible();
const bool newAnchorRectangle = anchorRectangle != d->anchorRectangle;
const bool newCursorRectangle = cursorRectangle != d->cursorRectangle;
const bool newAnchorRectIntersectsClipRect = anchorRectIntersectsClipRect != d->anchorRectIntersectsClipRect;
const bool newCursorRectIntersectsClipRect = cursorRectIntersectsClipRect != d->cursorRectIntersectsClipRect;
const bool newSelectionControlVisible = selectionControlVisible != d->selectionControlVisible;
d->anchorRectangle = anchorRectangle;
d->cursorRectangle = cursorRectangle;
d->anchorRectIntersectsClipRect = anchorRectIntersectsClipRect;
d->cursorRectIntersectsClipRect = cursorRectIntersectsClipRect;
d->selectionControlVisible = selectionControlVisible;
if (newAnchorRectangle)
emit anchorRectangleChanged();
if (newCursorRectangle)
emit cursorRectangleChanged();
if (newAnchorRectIntersectsClipRect)
emit anchorRectIntersectsClipRectChanged();
if (newCursorRectIntersectsClipRect)
emit cursorRectIntersectsClipRectChanged();
if (newSelectionControlVisible)
emit selectionControlVisibleChanged();
}
void ShadowInputContext::update(Qt::InputMethodQueries queries)
{
Q_UNUSED(queries)
Q_D(ShadowInputContext);
if (!d->inputItem)
return;
QInputMethodQueryEvent imQueryEvent(Qt::ImQueryInput);
QGuiApplication::sendEvent(d->inputItem, &imQueryEvent);
const QString surroundingText = imQueryEvent.value(Qt::ImSurroundingText).toString();
const int cursorPosition = imQueryEvent.value(Qt::ImCursorPosition).toInt();
const int anchorPosition = imQueryEvent.value(Qt::ImAnchorPosition).toInt();
const QString newSurroundingText = d->inputContext->surroundingText();
const int newCursorPosition = d->inputContext->cursorPosition();
const int newAnchorPosition = d->inputContext->anchorPosition();
bool updateSurroundingText = newSurroundingText != surroundingText;
bool updateSelection = newCursorPosition != cursorPosition || newAnchorPosition != anchorPosition;
if (updateSurroundingText || updateSelection) {
QList<QInputMethodEvent::Attribute> attributes;
attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection,
newAnchorPosition,
newCursorPosition - newAnchorPosition, QVariant()));
QInputMethodEvent inputEvent(QString(), attributes);
if (updateSurroundingText)
inputEvent.setCommitString(newSurroundingText, -cursorPosition, surroundingText.length());
QGuiApplication::sendEvent(d->inputItem, &inputEvent);
}
const QString newPreeditText = d->inputContext->preeditText();
const QList<QInputMethodEvent::Attribute> newPreeditAttributes = d->inputContext->preeditTextAttributes();
if (d->preeditText != newPreeditText || d->preeditTextAttributes != newPreeditAttributes) {
d->preeditText = newPreeditText;
d->preeditTextAttributes = newPreeditAttributes;
QInputMethodEvent inputEvent(d->preeditText, d->preeditTextAttributes);
QGuiApplication::sendEvent(d->inputItem, &inputEvent);
}
updateSelectionProperties();
}
QVariant ShadowInputContext::queryFocusObject(Qt::InputMethodQuery query, QVariant argument)
{
Q_D(ShadowInputContext);
QVariant retval;
QObject *focusObject = d->inputItem;
if (!focusObject)
return retval;
bool newMethodWorks = QMetaObject::invokeMethod(focusObject, "inputMethodQuery",
Qt::DirectConnection,
Q_RETURN_ARG(QVariant, retval),
Q_ARG(Qt::InputMethodQuery, query),
Q_ARG(QVariant, argument));
if (newMethodWorks)
return retval;
QInputMethodQueryEvent queryEvent(query);
QCoreApplication::sendEvent(focusObject, &queryEvent);
return queryEvent.value(query);
}
} // namespace QtVirtualKeyboard
QT_END_NAMESPACE