diff options
Diffstat (limited to 'src/shared')
-rw-r--r-- | src/shared/qwaylandinputmethodeventbuilder.cpp | 286 | ||||
-rw-r--r-- | src/shared/qwaylandinputmethodeventbuilder.h | 87 | ||||
-rw-r--r-- | src/shared/qwaylandxkb.cpp | 55 | ||||
-rw-r--r-- | src/shared/qwaylandxkb.h | 7 |
4 files changed, 435 insertions, 0 deletions
diff --git a/src/shared/qwaylandinputmethodeventbuilder.cpp b/src/shared/qwaylandinputmethodeventbuilder.cpp new file mode 100644 index 000000000..eb527c15a --- /dev/null +++ b/src/shared/qwaylandinputmethodeventbuilder.cpp @@ -0,0 +1,286 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylandinputmethodeventbuilder.h" + +#include <QInputMethod> +#include <QTextCharFormat> + +#ifdef QT_BUILD_WAYLANDCOMPOSITOR_LIB +#include <QtWaylandCompositor/private/qwayland-server-text-input-unstable-v2.h> +#else +#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h> +#endif + +QT_BEGIN_NAMESPACE + +QWaylandInputMethodEventBuilder::QWaylandInputMethodEventBuilder() + : m_anchor(0) + , m_cursor(0) + , m_deleteBefore(0) + , m_deleteAfter(0) + , m_preeditCursor(0) + , m_preeditStyles() +{ +} + +QWaylandInputMethodEventBuilder::~QWaylandInputMethodEventBuilder() +{ +} + +void QWaylandInputMethodEventBuilder::reset() +{ + m_anchor = 0; + m_cursor = 0; + m_deleteBefore = 0; + m_deleteAfter = 0; + m_preeditCursor = 0; + m_preeditStyles.clear(); +} + +void QWaylandInputMethodEventBuilder::setCursorPosition(int32_t index, int32_t anchor) +{ + m_cursor = index; + m_anchor = anchor; +} + +void QWaylandInputMethodEventBuilder::setDeleteSurroundingText(uint32_t beforeLength, uint32_t afterLength) +{ + m_deleteBefore = beforeLength; + m_deleteAfter = afterLength; +} + +void QWaylandInputMethodEventBuilder::addPreeditStyling(uint32_t index, uint32_t length, uint32_t style) +{ + QTextCharFormat format; + + switch (style) { + case 0: + case 1: + format.setFontUnderline(true); + format.setUnderlineStyle(QTextCharFormat::SingleUnderline); + m_preeditStyles.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, index, length, format)); + break; + case 2: + case 3: + format.setFontWeight(QFont::Bold); + format.setFontUnderline(true); + format.setUnderlineStyle(QTextCharFormat::SingleUnderline); + m_preeditStyles.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, index, length, format)); + break; + case 4: + format.setFontUnderline(true); + format.setUnderlineStyle(QTextCharFormat::SingleUnderline); + m_preeditStyles.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, index, length, format)); + case 5: + format.setFontUnderline(true); + format.setUnderlineStyle(QTextCharFormat::WaveUnderline); + format.setUnderlineColor(QColor(Qt::red)); + m_preeditStyles.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, index, length, format)); + break; +// case QtWayland::wl_text_input::preedit_style_selection: +// case QtWayland::wl_text_input::preedit_style_none: + default: + break; + } +} + +void QWaylandInputMethodEventBuilder::setPreeditCursor(int32_t index) +{ + m_preeditCursor = index; +} + +QInputMethodEvent QWaylandInputMethodEventBuilder::buildCommit(const QString &text) +{ + QList<QInputMethodEvent::Attribute> attributes; + + const QPair<int, int> replacement = replacementForDeleteSurrounding(); + + if (m_cursor != 0 || m_anchor != 0) { + QString surrounding = QInputMethod::queryFocusObject(Qt::ImSurroundingText, QVariant()).toString(); + const int cursor = QInputMethod::queryFocusObject(Qt::ImCursorPosition, QVariant()).toInt(); + const int anchor = QInputMethod::queryFocusObject(Qt::ImAnchorPosition, QVariant()).toInt(); + const int absoluteCursor = QInputMethod::queryFocusObject(Qt::ImAbsolutePosition, QVariant()).toInt(); + + const int absoluteOffset = absoluteCursor - cursor; + + const int cursorAfterCommit = qMin(anchor, cursor) + replacement.first + text.length(); + surrounding.replace(qMin(anchor, cursor) + replacement.first, + qAbs(anchor - cursor) + replacement.second, text); + + attributes.push_back(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, + indexFromWayland(surrounding, m_cursor, cursorAfterCommit) + absoluteOffset, + indexFromWayland(surrounding, m_anchor, cursorAfterCommit) + absoluteOffset, + QVariant())); + } + + QInputMethodEvent event(QString(), attributes); + event.setCommitString(text, replacement.first, replacement.second); + + return event; +} + +QInputMethodEvent QWaylandInputMethodEventBuilder::buildPreedit(const QString &text) +{ + QList<QInputMethodEvent::Attribute> attributes; + + if (m_preeditCursor < 0) { + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant())); + } else if (m_preeditCursor > 0) { + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, indexFromWayland(text, m_preeditCursor), 1, QVariant())); + } + + Q_FOREACH (const QInputMethodEvent::Attribute &attr, m_preeditStyles) { + int start = indexFromWayland(text, attr.start); + int length = indexFromWayland(text, attr.start + attr.length) - start; + attributes.append(QInputMethodEvent::Attribute(attr.type, start, length, attr.value)); + } + + QInputMethodEvent event(text, attributes); + + const QPair<int, int> replacement = replacementForDeleteSurrounding(); + event.setCommitString(QString(), replacement.first, replacement.second); + + return event; +} + +QPair<int, int> QWaylandInputMethodEventBuilder::replacementForDeleteSurrounding() +{ + if (m_deleteBefore == 0 && m_deleteAfter == 0) + return QPair<int, int>(0, 0); + + const QString &surrounding = QInputMethod::queryFocusObject(Qt::ImSurroundingText, QVariant()).toString(); + const int cursor = QInputMethod::queryFocusObject(Qt::ImCursorPosition, QVariant()).toInt(); + const int anchor = QInputMethod::queryFocusObject(Qt::ImAnchorPosition, QVariant()).toInt(); + + const int selectionStart = qMin(cursor, anchor); + const int selectionEnd = qMax(cursor, anchor); + + const int deleteBefore = selectionStart - indexFromWayland(surrounding, -m_deleteBefore, selectionStart); + const int deleteAfter = indexFromWayland(surrounding, m_deleteAfter, selectionEnd) - selectionEnd; + + return QPair<int, int>(-deleteBefore, deleteBefore + deleteAfter); +} + +QWaylandInputMethodContentType QWaylandInputMethodContentType::convert(Qt::InputMethodHints hints) +{ + uint32_t hint = ZWP_TEXT_INPUT_V2_CONTENT_HINT_NONE; + uint32_t purpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_NORMAL; + + if (hints & Qt::ImhHiddenText) { + hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_HIDDEN_TEXT; + } + if (hints & Qt::ImhSensitiveData) { + hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_SENSITIVE_DATA; + } + if ((hints & Qt::ImhNoAutoUppercase) == 0) { + hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_CAPITALIZATION; + } + if (hints & Qt::ImhPreferNumbers) { + // Nothing yet + } + if (hints & Qt::ImhPreferUppercase) { + hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_UPPERCASE; + } + if (hints & Qt::ImhPreferLowercase) { + hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_LOWERCASE; + } + if ((hints & Qt::ImhNoPredictiveText) == 0) { + hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_COMPLETION | ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_CORRECTION; + } + + if ((hints & Qt::ImhDate) && (hints & Qt::ImhTime) == 0) { + purpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_DATE; + } else if ((hints & Qt::ImhDate) && (hints & Qt::ImhTime)) { + purpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_DATETIME; + } else if ((hints & Qt::ImhDate) == 0 && (hints & Qt::ImhTime)) { + purpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_TIME; + } + + if (hints & Qt::ImhPreferLatin) { + hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_LATIN; + } + + if (hints & Qt::ImhMultiLine) { + hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_MULTILINE; + } + + if (hints & Qt::ImhDigitsOnly) { + purpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_DIGITS; + } + if (hints & Qt::ImhFormattedNumbersOnly) { + purpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_NUMBER; + } + if (hints & Qt::ImhUppercaseOnly) { + hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_UPPERCASE; + } + if (hints & Qt::ImhLowercaseOnly) { + hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_LOWERCASE; + } + if (hints & Qt::ImhDialableCharactersOnly) { + purpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_PHONE; + } + if (hints & Qt::ImhEmailCharactersOnly) { + purpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_EMAIL; + } + if (hints & Qt::ImhUrlCharactersOnly) { + purpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_URL; + } + if (hints & Qt::ImhLatinOnly) { + hint |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_LATIN; + } + return QWaylandInputMethodContentType{hint, purpose}; +} + +int QWaylandInputMethodEventBuilder::indexFromWayland(const QString &str, int utf8Index, int baseIndex) +{ + if (utf8Index == 0) + return baseIndex; + + if (utf8Index < 0) { + const QByteArray &utf8 = str.leftRef(baseIndex).toUtf8(); + return QString::fromUtf8(utf8.left(qMax(utf8.length() + utf8Index, 0))).length(); + } else { + const QByteArray &utf8 = str.midRef(baseIndex).toUtf8(); + return QString::fromUtf8(utf8.left(utf8Index)).length() + baseIndex; + } +} + +QT_END_NAMESPACE + diff --git a/src/shared/qwaylandinputmethodeventbuilder.h b/src/shared/qwaylandinputmethodeventbuilder.h new file mode 100644 index 000000000..188a6a94b --- /dev/null +++ b/src/shared/qwaylandinputmethodeventbuilder.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDINPUTMETHODEVENTBUILDER_H +#define QWAYLANDINPUTMETHODEVENTBUILDER_H + +#include <QInputMethodEvent> + +QT_BEGIN_NAMESPACE + +class QWaylandInputMethodEventBuilder +{ +public: + QWaylandInputMethodEventBuilder(); + ~QWaylandInputMethodEventBuilder(); + + void reset(); + + void setCursorPosition(int32_t index, int32_t anchor); + void setDeleteSurroundingText(uint32_t beforeLength, uint32_t afterLength); + + void addPreeditStyling(uint32_t index, uint32_t length, uint32_t style); + void setPreeditCursor(int32_t index); + + QInputMethodEvent buildCommit(const QString &text); + QInputMethodEvent buildPreedit(const QString &text); + + static int indexFromWayland(const QString &str, int utf8Index, int baseIndex = 0); +private: + QPair<int, int> replacementForDeleteSurrounding(); + + int32_t m_anchor; + int32_t m_cursor; + uint32_t m_deleteBefore; + uint32_t m_deleteAfter; + + int32_t m_preeditCursor; + QList<QInputMethodEvent::Attribute> m_preeditStyles; +}; + +struct QWaylandInputMethodContentType { + uint32_t hint; + uint32_t purpose; + + static QWaylandInputMethodContentType convert(Qt::InputMethodHints hints); +}; + + +QT_END_NAMESPACE + +#endif // QWAYLANDINPUTMETHODEVENTBUILDER_H diff --git a/src/shared/qwaylandxkb.cpp b/src/shared/qwaylandxkb.cpp index f5d0527de..32d24bd62 100644 --- a/src/shared/qwaylandxkb.cpp +++ b/src/shared/qwaylandxkb.cpp @@ -40,6 +40,7 @@ #include "qwaylandxkb.h" +#include <QKeyEvent> #include <QString> #ifndef QT_NO_WAYLAND_XKB @@ -282,6 +283,16 @@ static int lookupKeysym(xkb_keysym_t key) return code; } +static xkb_keysym_t toKeysymFromTable(uint32_t key) +{ + for (int i = 0; KeyTbl[i]; i += 2) { + if (key == KeyTbl[i + 1]) + return KeyTbl[i]; + } + + return 0; +} + int QWaylandXkb::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers &modifiers, const QString &text) { int code = 0; @@ -326,6 +337,50 @@ Qt::KeyboardModifiers QWaylandXkb::modifiers(struct xkb_state *state) return modifiers; } +QEvent::Type QWaylandXkb::toQtEventType(uint32_t state) +{ + return state != 0 ? QEvent::KeyPress : QEvent::KeyRelease; +} + +QString QWaylandXkb::textFromKeysym(uint32_t keysym, Qt::KeyboardModifiers modifiers) +{ + uint utf32 = xkb_keysym_to_utf32(keysym); + + // Map control + letter to proper text + if (utf32 >= 'A' && utf32 <= '~' && (modifiers & Qt::ControlModifier)) { + utf32 &= ~0x60; + return QString::fromUcs4(&utf32, 1); + } + + if (utf32) + return QString::fromUcs4(&utf32, 1); + + return QString(); +} + +QVector<xkb_keysym_t> QWaylandXkb::toKeysym(QKeyEvent *event) +{ + QVector<xkb_keysym_t> keysyms; + if (event->key() >= Qt::Key_F1 && event->key() <= Qt::Key_F35) { + keysyms.append(XKB_KEY_F1 + (event->key() - Qt::Key_F1)); + } else if (event->modifiers() & Qt::KeypadModifier) { + if (event->key() >= Qt::Key_0 && event->key() <= Qt::Key_9) + keysyms.append(XKB_KEY_KP_0 + (event->key() - Qt::Key_0)); + else + keysyms.append(toKeysymFromTable(event->key())); + } else if (!event->text().isEmpty()) { + // From libxkbcommon keysym-utf.c: + // "We allow to represent any UCS character in the range U-00000000 to + // U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff." + foreach (uint utf32, event->text().toUcs4()) { + keysyms.append(utf32 | 0x01000000); + } + } else { + keysyms.append(toKeysymFromTable(event->key())); + } + return keysyms; +} + QT_END_NAMESPACE #endif // QT_NO_WAYLAND_XKB diff --git a/src/shared/qwaylandxkb.h b/src/shared/qwaylandxkb.h index bfb38515f..9b5c935a5 100644 --- a/src/shared/qwaylandxkb.h +++ b/src/shared/qwaylandxkb.h @@ -44,15 +44,22 @@ #ifndef QT_NO_WAYLAND_XKB #include <Qt> +#include <QEvent> #include <xkbcommon/xkbcommon.h> QT_BEGIN_NAMESPACE +class QKeyEvent; + class QWaylandXkb { public: static int keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers &modifiers, const QString &text); static Qt::KeyboardModifiers modifiers(struct xkb_state *state); + + static QEvent::Type toQtEventType(uint32_t state); + static QString textFromKeysym(uint32_t keysym, Qt::KeyboardModifiers modifiers); + static QVector<xkb_keysym_t> toKeysym(QKeyEvent *event); }; QT_END_NAMESPACE |