• Bramastyo Harimukti's avatar
    Implemented proper double click behavior of shift handler · e8b7dcea
    Bramastyo Harimukti authored
    
    Previously, there is nothing to separate whether the button
    command is a single click or a double click. Every time we
    click the shift button, it always look for the second click
    and always activate the caps lock. There is no time limit to
    specify if there will be a second click and always recognized
    the upcoming click to activate the caps lock. The correct be-
    havior of a shift button is that a single click is used to
    enable the upper-case and automatically turned into lower case
    when the next letter is typed. Another case, within a specific
    time, if a second click comes, the caps-lock is activated and
    keep the upper-case mode, otherwise the upcoming click is used
    to abort the upper-case and change the keyboard mode back to
    lower-case.
    
    Unit test is also updated.
    
    [ChangeLog] Changed behavior of shift handler to only activate
    caps lock if the shift key is double-clicked.
    
    Change-Id: Ia04a61ca6df5407f37eb73b9de65c6ccd0128547
    Reviewed-by: default avatarMitch Curtis <mitch.curtis@qt.io>
    e8b7dcea
shifthandler.cpp 11.42 KiB
/****************************************************************************
**
** Copyright (C) 2016 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 "shifthandler.h"
#include "inputcontext.h"
#include "inputengine.h"
#include <QtCore/private/qobject_p.h>
#include <QSet>
#include <QGuiApplication>
#include <QTime>
#include <QStyleHints>
namespace QtVirtualKeyboard {
class ShiftHandlerPrivate : public QObjectPrivate
public:
    ShiftHandlerPrivate() :
        QObjectPrivate(),
        inputContext(0),
        sentenceEndingCharacters(QString(".!?") + QChar(Qt::Key_exclamdown) + QChar(Qt::Key_questiondown)),
        autoCapitalizationEnabled(false),
        toggleShiftEnabled(false),
        shiftChanged(false),
        manualShiftLanguageFilter(QSet<QLocale::Language>() << QLocale::Arabic << QLocale::Persian << QLocale::Hindi << QLocale::Korean),
        manualCapsInputModeFilter(QSet<InputEngine::InputMode>() << InputEngine::Cangjie << InputEngine::Zhuyin),
        noAutoUppercaseInputModeFilter(QSet<InputEngine::InputMode>() << InputEngine::FullwidthLatin << InputEngine::Pinyin << InputEngine::Cangjie << InputEngine::Zhuyin),
        allCapsInputModeFilter(QSet<InputEngine::InputMode>() << InputEngine::Hiragana << InputEngine::Katakana)
        timer.start();
    InputContext *inputContext;
    QString sentenceEndingCharacters;
    bool autoCapitalizationEnabled;
    bool toggleShiftEnabled;
    bool shiftChanged;
    QLocale locale;
    QTime timer;
    const QSet<QLocale::Language> manualShiftLanguageFilter;
    const QSet<InputEngine::InputMode> manualCapsInputModeFilter;
    const QSet<InputEngine::InputMode> noAutoUppercaseInputModeFilter;
    const QSet<InputEngine::InputMode> allCapsInputModeFilter;
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
/*! \qmltype ShiftHandler \inqmlmodule QtQuick.VirtualKeyboard \ingroup qtvirtualkeyboard-qml \instantiates QtVirtualKeyboard::ShiftHandler \brief Manages the shift state. */ /*! \class QtVirtualKeyboard::ShiftHandler \inmodule InputFramework \brief Manages the shift state. */ ShiftHandler::ShiftHandler(InputContext *parent) : QObject(*new ShiftHandlerPrivate(), parent) { Q_D(ShiftHandler); d->inputContext = parent; if (d->inputContext) { connect(d->inputContext, SIGNAL(inputMethodHintsChanged()), SLOT(restart())); connect(d->inputContext, SIGNAL(inputItemChanged()), SLOT(restart())); connect(d->inputContext->inputEngine(), SIGNAL(inputModeChanged()), SLOT(restart())); connect(d->inputContext, SIGNAL(preeditTextChanged()), SLOT(autoCapitalize())); connect(d->inputContext, SIGNAL(surroundingTextChanged()), SLOT(autoCapitalize())); connect(d->inputContext, SIGNAL(cursorPositionChanged()), SLOT(autoCapitalize())); connect(d->inputContext, SIGNAL(shiftChanged()), SLOT(shiftChanged())); connect(d->inputContext, SIGNAL(capsLockChanged()), SLOT(shiftChanged())); connect(d->inputContext, SIGNAL(localeChanged()), SLOT(localeChanged())); d->locale = QLocale(d->inputContext->locale()); } } /*! \internal */ ShiftHandler::~ShiftHandler() { } QString ShiftHandler::sentenceEndingCharacters() const { Q_D(const ShiftHandler); return d->sentenceEndingCharacters; } void ShiftHandler::setSentenceEndingCharacters(const QString &value) { Q_D(ShiftHandler); if (d->sentenceEndingCharacters != value) { d->sentenceEndingCharacters = value; autoCapitalize(); emit sentenceEndingCharactersChanged(); } } bool ShiftHandler::autoCapitalizationEnabled() const { Q_D(const ShiftHandler); return d->autoCapitalizationEnabled; } bool ShiftHandler::toggleShiftEnabled() const { Q_D(const ShiftHandler); return d->toggleShiftEnabled; }
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
/*! \since 1.2 \qmlmethod void ShiftHandler::toggleShift() Toggles the current shift state. This method provides the functionality of the shift key. \sa toggleShiftEnabled */ /*! \since 1.2 \fn void QtVirtualKeyboard::ShiftHandler::toggleShift() Toggles the current shift state. This method provides the functionality of the shift key. \sa toggleShiftEnabled */ void ShiftHandler::toggleShift() { Q_D(ShiftHandler); if (!d->toggleShiftEnabled) return; if (d->manualShiftLanguageFilter.contains(d->locale.language())) { d->inputContext->setCapsLock(false); d->inputContext->setShift(!d->inputContext->shift()); } else if (d->inputContext->inputMethodHints() & Qt::ImhNoAutoUppercase || d->manualCapsInputModeFilter.contains(d->inputContext->inputEngine()->inputMode())) { bool capsLock = d->inputContext->capsLock(); d->inputContext->setCapsLock(!capsLock); d->inputContext->setShift(!capsLock); } else { if (d->inputContext->capsLock()) { d->inputContext->setCapsLock(!d->inputContext->capsLock() && d->inputContext->shift() && !d->shiftChanged); } QStyleHints *style = QGuiApplication::styleHints(); if (d->timer.elapsed() > style->mouseDoubleClickInterval()) { d->timer.restart(); } else if (d->timer.elapsed() < style->mouseDoubleClickInterval() && !d->inputContext->capsLock()) { d->inputContext->setCapsLock(!d->inputContext->capsLock() && d->inputContext->shift() && !d->shiftChanged); } d->inputContext->setShift(d->inputContext->capsLock() || !d->inputContext->shift()); d->shiftChanged = false; } } void ShiftHandler::reset() { Q_D(ShiftHandler); if (d->inputContext->inputItem()) { Qt::InputMethodHints inputMethodHints = d->inputContext->inputMethodHints(); InputEngine::InputMode inputMode = d->inputContext->inputEngine()->inputMode(); bool preferUpperCase = (inputMethodHints & (Qt::ImhPreferUppercase | Qt::ImhUppercaseOnly)); bool autoCapitalizationEnabled = !(d->inputContext->inputMethodHints() & (Qt::ImhNoAutoUppercase | Qt::ImhUppercaseOnly | Qt::ImhLowercaseOnly | Qt::ImhEmailCharactersOnly | Qt::ImhUrlCharactersOnly | Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly)) && !d->noAutoUppercaseInputModeFilter.contains(inputMode); bool toggleShiftEnabled = !(inputMethodHints & (Qt::ImhUppercaseOnly | Qt::ImhLowercaseOnly)); // For filtered languages reset the initial shift status to lower case // and allow manual shift change if (d->manualShiftLanguageFilter.contains(d->locale.language()) || d->manualCapsInputModeFilter.contains(inputMode)) { preferUpperCase = false;
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
autoCapitalizationEnabled = false; toggleShiftEnabled = true; } else if (d->allCapsInputModeFilter.contains(inputMode)) { preferUpperCase = true; autoCapitalizationEnabled = false; toggleShiftEnabled = false; } d->inputContext->setShift(preferUpperCase); d->inputContext->setCapsLock(preferUpperCase); setToggleShiftEnabled(toggleShiftEnabled); setAutoCapitalizationEnabled(autoCapitalizationEnabled); } } void ShiftHandler::autoCapitalize() { Q_D(ShiftHandler); if (d->inputContext->capsLock()) return; if (!d->autoCapitalizationEnabled || !d->inputContext->preeditText().isEmpty()) { d->inputContext->setShift(false); } else { int cursorPosition = d->inputContext->cursorPosition(); bool preferLowerCase = d->inputContext->inputMethodHints() & Qt::ImhPreferLowercase; if (cursorPosition == 0) { d->inputContext->setShift(!preferLowerCase); } else { QString text = d->inputContext->surroundingText(); text.truncate(cursorPosition); text = text.trimmed(); if (text.length() == 0) d->inputContext->setShift(!preferLowerCase); else if (text.length() > 0 && d->sentenceEndingCharacters.indexOf(text[text.length() - 1]) >= 0) d->inputContext->setShift(!preferLowerCase); else d->inputContext->setShift(false); } } } void ShiftHandler::restart() { reset(); autoCapitalize(); } void ShiftHandler::shiftChanged() { Q_D(ShiftHandler); d->shiftChanged = true; } void ShiftHandler::localeChanged() { Q_D(ShiftHandler); d->locale = QLocale(d->inputContext->locale()); restart(); } void ShiftHandler::setAutoCapitalizationEnabled(bool enabled) { Q_D(ShiftHandler); if (d->autoCapitalizationEnabled != enabled) { d->autoCapitalizationEnabled = enabled; emit autoCapitalizationEnabledChanged(); } } void ShiftHandler::setToggleShiftEnabled(bool enabled) {
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
Q_D(ShiftHandler); if (d->toggleShiftEnabled != enabled) { d->toggleShiftEnabled = enabled; emit toggleShiftEnabledChanged(); } } /*! \property QtVirtualKeyboard::ShiftHandler::sentenceEndingCharacters This property specifies the sentence ending characters which will cause shift state change. By default, the property is initialized to sentence ending characters found in the ASCII range (i.e. ".!?"). */ /*! \qmlproperty string ShiftHandler::sentenceEndingCharacters This property specifies the sentence ending characters which will cause shift state change. By default, the property is initialized to sentence ending characters found in the ASCII range (i.e. ".!?"). */ /*! \since 1.2 \property QtVirtualKeyboard::ShiftHandler::autoCapitalizationEnabled This property provides the current state of the automatic capitalization feature. */ /*! \since 1.2 \qmlproperty bool ShiftHandler::autoCapitalizationEnabled This property provides the current state of the automatic capitalization feature. */ /*! \since 1.2 \property QtVirtualKeyboard::ShiftHandler::toggleShiftEnabled This property provides the current state of the toggleShift() method. When true, the current shift state can be changed by calling the toggleShift() method. */ /*! \since 1.2 \qmlproperty bool ShiftHandler::toggleShiftEnabled This property provides the current state of the toggleShift() method. When true, the current shift state can be changed by calling the toggleShift() method. */ } // namespace QtVirtualKeyboard