From b237c17156a1626fada7fad50dc0892f790d6295 Mon Sep 17 00:00:00 2001 From: axis Date: Wed, 25 Aug 2010 15:37:56 +0200 Subject: Fixed sporadically failing autotest on Symbian. It failed because there sometimes is a spike in resource usage just after starting a process. This was fixed by adding a longer wait delay. RevBy: Trust me --- tests/auto/qtimer/tst_qtimer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/auto/qtimer/tst_qtimer.cpp b/tests/auto/qtimer/tst_qtimer.cpp index 8d213ed..b651187 100644 --- a/tests/auto/qtimer/tst_qtimer.cpp +++ b/tests/auto/qtimer/tst_qtimer.cpp @@ -161,8 +161,9 @@ void tst_QTimer::singleShotTimeout() QCOMPARE(helper.count, 1); } -#if defined(Q_OS_SYMBIAN) && defined(Q_CC_NOKIAX86) -// Increase wait as emulator startup can cause unexpected delays +#if defined(Q_OS_SYMBIAN) +// Increase wait as emulator startup can cause unexpected delays, and +// on hardware there are sometimes spikes right after process startup. #define TIMEOUT_TIMEOUT 2000 #else #define TIMEOUT_TIMEOUT 200 -- cgit v0.12 From 51f8b65b585a198a62daa4403374441b667966ce Mon Sep 17 00:00:00 2001 From: axis Date: Wed, 25 Aug 2010 15:41:32 +0200 Subject: Added catching of errors in timers. According to the bug report this may happen in SMP systems and systems with certain schedulers. This patch does not actually fix the bug which causes the timer error, but avoids the timer firing if it happens, and gives a chance to handle the error. Task: QT-3591 RevBy: mread --- src/corelib/kernel/qeventdispatcher_symbian.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/corelib/kernel/qeventdispatcher_symbian.cpp b/src/corelib/kernel/qeventdispatcher_symbian.cpp index 1bad8ed..5cc6ae3 100644 --- a/src/corelib/kernel/qeventdispatcher_symbian.cpp +++ b/src/corelib/kernel/qeventdispatcher_symbian.cpp @@ -232,8 +232,12 @@ void QTimerActiveObject::DoCancel() void QTimerActiveObject::RunL() { - int error; - QT_TRYCATCH_ERROR(error, Run()); + int error = KErrNone; + if (iStatus == KErrNone) { + QT_TRYCATCH_ERROR(error, Run()); + } else { + error = iStatus.Int(); + } // All Symbian error codes are negative. if (error < 0) { CActiveScheduler::Current()->Error(error); // stop and report here, as this timer will be deleted on scope exit -- cgit v0.12 From c5901037f0d3ccd45b0c79b38ef5b04552dad0aa Mon Sep 17 00:00:00 2001 From: axis Date: Fri, 27 Aug 2010 10:31:39 +0200 Subject: Revert "Long-press shortcuts for symbols on QWERTY keyboard don't work" This reverts commit 05eacd9ad40f8adb5aaa12a8b90113a73b43f642. Conflicts: src/gui/inputmethod/qcoefepinputcontext_p.h src/gui/inputmethod/qcoefepinputcontext_s60.cpp --- src/gui/inputmethod/qcoefepinputcontext_p.h | 1 - src/gui/inputmethod/qcoefepinputcontext_s60.cpp | 15 +-------------- src/gui/widgets/qlinecontrol.cpp | 2 +- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/gui/inputmethod/qcoefepinputcontext_p.h b/src/gui/inputmethod/qcoefepinputcontext_p.h index d5243c3..cc14e89 100644 --- a/src/gui/inputmethod/qcoefepinputcontext_p.h +++ b/src/gui/inputmethod/qcoefepinputcontext_p.h @@ -151,7 +151,6 @@ private: int m_inlinePosition; MFepInlineTextFormatRetriever *m_formatRetriever; MFepPointerEventHandlerDuringInlineEdit *m_pointerHandler; - int m_cursorPos; QBasicTimer m_tempPreeditStringTimeout; bool m_hasTempPreeditString; }; diff --git a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp index c4d60a5..73396b6 100644 --- a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp +++ b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp @@ -79,7 +79,6 @@ QCoeFepInputContext::QCoeFepInputContext(QObject *parent) m_inlinePosition(0), m_formatRetriever(0), m_pointerHandler(0), - m_cursorPos(0), m_hasTempPreeditString(false) { m_fepState->SetObjectProvider(this); @@ -596,8 +595,6 @@ void QCoeFepInputContext::StartFepInlineEditL(const TDesC& aInitialInlineText, commitTemporaryPreeditString(); - m_cursorPos = w->inputMethodQuery(Qt::ImCursorPosition).toInt(); - QList attributes; m_cursorVisibility = aCursorVisibility ? 1 : 0; @@ -820,23 +817,13 @@ void QCoeFepInputContext::commitCurrentString(bool cancelFepTransaction) { int longPress = 0; - if (m_preeditString.size() == 0) { - QWidget *w = focusWidget(); - if (!cancelFepTransaction && w) { - // We must replace the last character only if the input box has already accepted one - if (w->inputMethodQuery(Qt::ImCursorPosition).toInt() != m_cursorPos) - longPress = 1; - } - } - QList attributes; QInputMethodEvent event(QLatin1String(""), attributes); - event.setCommitString(m_preeditString, 0-longPress, longPress); + event.setCommitString(m_preeditString, 0, 0); m_preeditString.clear(); sendEvent(event); m_hasTempPreeditString = false; - longPress = 0; if (cancelFepTransaction) { CCoeFep* fep = CCoeEnv::Static()->Fep(); diff --git a/src/gui/widgets/qlinecontrol.cpp b/src/gui/widgets/qlinecontrol.cpp index d027b91..b6dfd13 100644 --- a/src/gui/widgets/qlinecontrol.cpp +++ b/src/gui/widgets/qlinecontrol.cpp @@ -419,7 +419,7 @@ void QLineControl::processInputMethodEvent(QInputMethodEvent *event) int c = m_cursor; // cursor position after insertion of commit string - if (event->replacementStart() == 0) + if (event->replacementStart() <= 0) c += event->commitString().length() + qMin(-event->replacementStart(), event->replacementLength()); m_cursor += event->replacementStart(); -- cgit v0.12 From 52cf47565e402dc682038ccaf8d725401802b603 Mon Sep 17 00:00:00 2001 From: axis Date: Tue, 31 Aug 2010 13:15:28 +0200 Subject: Cleaned up position tracking in the Symbian input methods. This fixed a case where using password mode would lead to Qt believing that the cursor was one step to the left of where it really was. This would have the effect of replacing the last character instead of appending to it, and even crashing if the cursor was all the way to the left. The code is also much cleaner this way, but it meant that QTBUG-9867 had to be solved differently this time. We do this by assuming that and empty FEP update means "erase last character", which seems to work well in practice. Also added a long overdue autotest for the FEP input methods. Most tests pass, but some don't, which I will try to fix later. Task: QTBUG-9867 Task: QTBUG-12949 RevBy: Miikka Heikkinen AutoTest: Included --- src/gui/inputmethod/qcoefepinputcontext_p.h | 10 +- src/gui/inputmethod/qcoefepinputcontext_s60.cpp | 19 +- src/gui/kernel/qt_s60_p.h | 4 +- tests/auto/qinputcontext/qinputcontext.pro | 4 + tests/auto/qinputcontext/tst_qinputcontext.cpp | 659 +++++++++++++++++++++++- 5 files changed, 679 insertions(+), 17 deletions(-) diff --git a/src/gui/inputmethod/qcoefepinputcontext_p.h b/src/gui/inputmethod/qcoefepinputcontext_p.h index cc14e89..2fd6d16 100644 --- a/src/gui/inputmethod/qcoefepinputcontext_p.h +++ b/src/gui/inputmethod/qcoefepinputcontext_p.h @@ -66,10 +66,10 @@ QT_BEGIN_NAMESPACE -class QCoeFepInputContext : public QInputContext, - public MCoeFepAwareTextEditor, - public MCoeFepAwareTextEditor_Extension1, - public MObjectProvider +class Q_AUTOTEST_EXPORT QCoeFepInputContext : public QInputContext, + public MCoeFepAwareTextEditor, + public MCoeFepAwareTextEditor_Extension1, + public MObjectProvider { Q_OBJECT @@ -153,6 +153,8 @@ private: MFepPointerEventHandlerDuringInlineEdit *m_pointerHandler; QBasicTimer m_tempPreeditStringTimeout; bool m_hasTempPreeditString; + + friend class tst_QInputContext; }; QT_END_NAMESPACE diff --git a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp index 73396b6..135b894 100644 --- a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp +++ b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp @@ -240,7 +240,6 @@ bool QCoeFepInputContext::filterEvent(const QEvent *event) && focusWidget()->inputMethodHints() & Qt::ImhHiddenText && !keyEvent->text().isEmpty()) { // Send some temporary preedit text in order to make text visible for a moment. - m_cursorPos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt(); m_preeditString = keyEvent->text(); QList attributes; QInputMethodEvent imEvent(m_preeditString, attributes); @@ -296,10 +295,6 @@ void QCoeFepInputContext::commitTemporaryPreeditString() return; commitCurrentString(false); - - //update cursor position, now this pre-edit text has been committed. - //this prevents next keypress overwriting it (QTBUG-11673) - m_cursorPos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt(); } void QCoeFepInputContext::mouseHandler( int x, QMouseEvent *event) @@ -609,9 +604,10 @@ void QCoeFepInputContext::StartFepInlineEditL(const TDesC& aInitialInlineText, // Let's remove the selected text if aInitialInlineText is empty and there is selected text if (m_preeditString.isEmpty()) { int anchor = w->inputMethodQuery(Qt::ImAnchorPosition).toInt(); - int replacementLength = qAbs(m_cursorPos-anchor); + int cursorPos = w->inputMethodQuery(Qt::ImCursorPosition).toInt(); + int replacementLength = qAbs(cursorPos-anchor); if (replacementLength > 0) { - int replacementStart = m_cursorPos < anchor ? 0 : -replacementLength; + int replacementStart = cursorPos < anchor ? 0 : -replacementLength; QList clearSelectionAttributes; QInputMethodEvent clearSelectionEvent(QLatin1String(""), clearSelectionAttributes); clearSelectionEvent.setCommitString(QLatin1String(""), replacementStart, replacementLength); @@ -644,8 +640,13 @@ void QCoeFepInputContext::UpdateFepInlineTextL(const TDesC& aNewInlineText, m_inlinePosition, m_cursorVisibility, QVariant())); - m_preeditString = qt_TDesC2QString(aNewInlineText); - QInputMethodEvent event(m_preeditString, attributes); + QString newPreeditString = qt_TDesC2QString(aNewInlineText); + QInputMethodEvent event(newPreeditString, attributes); + if (newPreeditString.isEmpty() && m_preeditString.isEmpty()) { + // In Symbian world this means "erase last character". + event.setCommitString("", -1, 1); + } + m_preeditString = newPreeditString; sendEvent(event); } diff --git a/src/gui/kernel/qt_s60_p.h b/src/gui/kernel/qt_s60_p.h index ad6a99a..4b60842 100644 --- a/src/gui/kernel/qt_s60_p.h +++ b/src/gui/kernel/qt_s60_p.h @@ -87,7 +87,7 @@ const TInt KInternalStatusPaneChange = 0x50000000; //this macro exists because EColor16MAP enum value doesn't exist in Symbian OS 9.2 #define Q_SYMBIAN_ECOLOR16MAP TDisplayMode(13) -class QS60ThreadLocalData +class Q_AUTOTEST_EXPORT QS60ThreadLocalData { public: QS60ThreadLocalData(); @@ -171,7 +171,7 @@ public: #endif }; -QS60Data* qGlobalS60Data(); +Q_AUTOTEST_EXPORT QS60Data* qGlobalS60Data(); #define S60 qGlobalS60Data() class QAbstractLongTapObserver diff --git a/tests/auto/qinputcontext/qinputcontext.pro b/tests/auto/qinputcontext/qinputcontext.pro index b3ea8c2..ec6831e 100644 --- a/tests/auto/qinputcontext/qinputcontext.pro +++ b/tests/auto/qinputcontext/qinputcontext.pro @@ -1,2 +1,6 @@ load(qttest_p4) SOURCES += tst_qinputcontext.cpp + +symbian { + LIBS += -lws32 -lcone +} diff --git a/tests/auto/qinputcontext/tst_qinputcontext.cpp b/tests/auto/qinputcontext/tst_qinputcontext.cpp index 644b463..23cfd9e 100644 --- a/tests/auto/qinputcontext/tst_qinputcontext.cpp +++ b/tests/auto/qinputcontext/tst_qinputcontext.cpp @@ -48,17 +48,26 @@ #include #include #include +#include + +#ifdef Q_OS_SYMBIAN +#include +#include + +#include +#include +#endif class tst_QInputContext : public QObject { Q_OBJECT public: - tst_QInputContext() {} + tst_QInputContext() : m_phoneIsQwerty(false) {} virtual ~tst_QInputContext() {} public slots: - void initTestCase() {} + void initTestCase(); void cleanupTestCase() {} void init() {} void cleanup() {} @@ -69,8 +78,176 @@ private slots: void closeSoftwareInputPanel(); void selections(); void focusProxy(); + void symbianTestCoeFepInputContext_data(); + void symbianTestCoeFepInputContext(); + +private: + bool m_phoneIsQwerty; }; +#ifdef Q_OS_SYMBIAN +class KeyEvent : public TWsEvent +{ +public: + KeyEvent(QWidget *w, TInt type, TInt scanCode, TUint code, TUint modifiers, TInt repeats) { + iHandle = w->effectiveWinId()->DrawableWindow()->WindowGroupId(); + iType = type; + SetTimeNow(); + TKeyEvent *keyEvent = reinterpret_cast(iEventData); + keyEvent->iScanCode = scanCode; + keyEvent->iCode = code; + keyEvent->iModifiers = modifiers; + keyEvent->iRepeats = repeats; + } +}; + +class FepReplayEvent +{ +public: + enum Type { + Pause, + Key, + CompleteKey + }; + + FepReplayEvent(int msecsToPause) + : m_type(Pause) + , m_msecsToPause(msecsToPause) + { + } + + FepReplayEvent(TInt keyType, TInt scanCode, TUint code, TUint modifiers, TInt repeats) + : m_type(Key) + , m_keyType(keyType) + , m_scanCode(scanCode) + , m_code(code) + , m_modifiers(modifiers) + , m_repeats(repeats) + { + } + + FepReplayEvent(TInt scanCode, TUint code, TUint modifiers, TInt repeats) + : m_type(CompleteKey) + , m_scanCode(scanCode) + , m_code(code) + , m_modifiers(modifiers) + , m_repeats(repeats) + { + } + + void sendEvent(QWidget *w, TInt type, TInt scanCode, TUint code, TUint modifiers, TInt repeats) + { + KeyEvent event(w, type, scanCode, code, modifiers, repeats); + S60->wsSession().SendEventToWindowGroup(w->effectiveWinId()->DrawableWindow()->WindowGroupId(), event); + } + + void pause(int msecs) + { + // Don't use qWait here. The polling nature of that function screws up the test. + QTimer timer; + QEventLoop loop; + QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + timer.setSingleShot(true); + timer.start(msecs); + loop.exec(); + } + + // For some reason, the test fails if using processEvents instead of an event loop + // with a zero timer to quit it, so use the timer. +#define KEY_WAIT 0 + + void replay(QWidget *w) + { + if (m_type == Pause) { + pause(m_msecsToPause); + } else if (m_type == Key) { + sendEvent(w, m_keyType, m_scanCode, m_code, m_modifiers, m_repeats); + if (m_keyType != EEventKeyDown) + // EEventKeyDown events should have no pause before the EEventKey event. + pause(KEY_WAIT); + } else if (m_type == CompleteKey) { + sendEvent(w, EEventKeyDown, m_scanCode, 0, m_modifiers, m_repeats); + // EEventKeyDown events should have no pause before the EEventKey event. + sendEvent(w, EEventKey, m_scanCode, m_code, m_modifiers, m_repeats); + pause(KEY_WAIT); + sendEvent(w, EEventKeyUp, m_scanCode, 0, m_modifiers, m_repeats); + pause(KEY_WAIT); + } + } + +private: + Type m_type; + int m_msecsToPause; + TInt m_keyType; + TInt m_scanCode; + TUint m_code; + TUint m_modifiers; + TInt m_repeats; +}; + +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(Qt::InputMethodHints) +Q_DECLARE_METATYPE(QLineEdit::EchoMode); + +#endif // Q_OS_SYMBIAN + +void tst_QInputContext::initTestCase() +{ +#ifdef Q_OS_SYMBIAN + // Sanity test. Checks FEP for: + // - T9 mode is default (it will attempt to fix this) + // - Language is English (it cannot fix this; bail out if not correct) + QWidget w; + QLayout *layout = new QVBoxLayout; + w.setLayout(layout); + QLineEdit *lineedit = new QLineEdit; + layout->addWidget(lineedit); + lineedit->setFocus(); +#ifdef QT_KEYPAD_NAVIGATION + lineedit->setEditFocus(true); +#endif + w.show(); + + QDesktopWidget desktop; + QRect screenSize = desktop.screenGeometry(&w); + if (screenSize.width() > screenSize.height()) { + // Crude way of finding out we are running on a qwerty phone. + m_phoneIsQwerty = true; + return; + } + + for (int iterations = 0; iterations < 16; iterations++) { + QTest::qWait(500); + + QList keyEvents; + + keyEvents << FepReplayEvent('9', '9', 0, 0); + keyEvents << FepReplayEvent('6', '6', 0, 0); + keyEvents << FepReplayEvent('8', '8', 0, 0); + keyEvents << FepReplayEvent(EStdKeyRightArrow, EKeyRightArrow, 0, 0); + + foreach(FepReplayEvent event, keyEvents) { + event.replay(lineedit); + } + + QApplication::processEvents(); + + if (lineedit->text().endsWith("you", Qt::CaseInsensitive)) { + // Success! + return; + } + + // Try changing modes. + // After 8 iterations, try to press the mode switch twice before typing. + for (int c = 0; c <= iterations / 8; c++) { + FepReplayEvent(EStdKeyHash, '#', 0, 0).replay(lineedit); + } + } + + QFAIL("FEP sanity test failed. Either the phone is not set to English, or the test was unable to enable T9"); +#endif +} + void tst_QInputContext::maximumTextLength() { QLineEdit le; @@ -285,5 +462,483 @@ void tst_QInputContext::focusProxy() QCOMPARE(gic->focusWidget(), &proxy); } +void tst_QInputContext::symbianTestCoeFepInputContext_data() +{ +#ifdef Q_OS_SYMBIAN + QTest::addColumn ("inputMethodEnabled"); + QTest::addColumn ("inputMethodHints"); + QTest::addColumn ("maxLength"); // Zero for no limit + QTest::addColumn ("echoMode"); + QTest::addColumn > ("keyEvents"); + QTest::addColumn ("finalString"); + QTest::addColumn ("preeditString"); + QList events; + + events << FepReplayEvent(EStdKeyBackspace, EKeyBackspace, 0, 0); + events << FepReplayEvent(EStdKeyBackspace, EKeyBackspace, 0, 0); + events << FepReplayEvent('5', '5', 0, 0); + events << FepReplayEvent('4', '4', 0, 0); + events << FepReplayEvent('6', '6', 0, 0); + events << FepReplayEvent(EStdKeyBackspace, EKeyBackspace, 0, 0); + events << FepReplayEvent(EStdKeyBackspace, EKeyBackspace, 0, 0); + events << FepReplayEvent('1', '1', 0, 0); + events << FepReplayEvent(EStdKeyBackspace, EKeyBackspace, 0, 0); + events << FepReplayEvent('2', '2', 0, 0); + events << FepReplayEvent('1', '1', 0, 0); + QTest::newRow("Numbers (no FEP)") + << false + << Qt::InputMethodHints(Qt::ImhNone) + << 0 + << QLineEdit::Normal + << events + << QString("521") + << QString(""); + QTest::newRow("Numbers and password mode (no FEP)") + << false + << Qt::InputMethodHints(Qt::ImhNone) + << 0 + << QLineEdit::Password + << events + << QString("521") + << QString(""); + QTest::newRow("Numbers") + << true + << Qt::InputMethodHints(Qt::ImhDigitsOnly) + << 0 + << QLineEdit::Normal + << events + << QString("521") + << QString(""); + QTest::newRow("Numbers max length (no FEP)") + << false + << Qt::InputMethodHints(Qt::ImhNone) + << 2 + << QLineEdit::Normal + << events + << QString("21") + << QString(""); + QTest::newRow("Numbers max length") + << true + << Qt::InputMethodHints(Qt::ImhDigitsOnly) + << 2 + << QLineEdit::Normal + << events + << QString("21") + << QString(""); + events.clear(); + + events << FepReplayEvent(EEventKeyDown, '5', 0, 0, 0); + events << FepReplayEvent(EEventKey, '5', '5', 0, 0); + events << FepReplayEvent(EEventKey, '5', '5', 0, 1); + events << FepReplayEvent(EEventKey, '5', '5', 0, 1); + events << FepReplayEvent(EEventKeyUp, '5', 0, 0, 0); + QTest::newRow("Numbers and autorepeat (no FEP)") + << false + << Qt::InputMethodHints(Qt::ImhNone) + << 0 + << QLineEdit::Normal + << events + << QString("555") + << QString(""); + events.clear(); + + events << FepReplayEvent(EStdKeyBackspace, EKeyBackspace, 0, 0); + events << FepReplayEvent('2', '2', 0, 0); + events << FepReplayEvent('3', '3', 0, 0); + events << FepReplayEvent('4', '4', 0, 0); + events << FepReplayEvent('4', '4', 0, 0); + events << FepReplayEvent('5', '5', 0, 0); + events << FepReplayEvent('5', '5', 0, 0); + events << FepReplayEvent(EStdKeyBackspace, EKeyBackspace, 0, 0); + QTest::newRow("Multitap") + << true + << Qt::InputMethodHints(Qt::ImhNoPredictiveText) + << 0 + << QLineEdit::Normal + << events + << QString("Adh") + << QString(""); + QTest::newRow("Multitap with no auto uppercase") + << true + << Qt::InputMethodHints(Qt::ImhNoPredictiveText | Qt::ImhNoAutoUppercase) + << 0 + << QLineEdit::Normal + << events + << QString("adh") + << QString(""); + QTest::newRow("Multitap with uppercase") + << true + << Qt::InputMethodHints(Qt::ImhNoPredictiveText | Qt::ImhPreferUppercase) + << 0 + << QLineEdit::Normal + << events + << QString("ADH") + << QString(""); + QTest::newRow("Multitap with lowercase") + << true + << Qt::InputMethodHints(Qt::ImhNoPredictiveText | Qt::ImhPreferLowercase) + << 0 + << QLineEdit::Normal + << events + << QString("adh") + << QString(""); + QTest::newRow("Multitap with forced uppercase") + << true + << Qt::InputMethodHints(Qt::ImhNoPredictiveText | Qt::ImhUppercaseOnly) + << 0 + << QLineEdit::Normal + << events + << QString("ADH") + << QString(""); + QTest::newRow("Multitap with forced lowercase") + << true + << Qt::InputMethodHints(Qt::ImhNoPredictiveText | Qt::ImhLowercaseOnly) + << 0 + << QLineEdit::Normal + << events + << QString("adh") + << QString(""); + events.clear(); + + events << FepReplayEvent(EStdKeyHash, '#', 0, 0); + events << FepReplayEvent('2', '2', 0, 0); + events << FepReplayEvent('2', '2', 0, 0); + events << FepReplayEvent('3', '3', 0, 0); + events << FepReplayEvent('4', '4', 0, 0); + events << FepReplayEvent('4', '4', 0, 0); + events << FepReplayEvent('5', '5', 0, 0); + events << FepReplayEvent('5', '5', 0, 0); + events << FepReplayEvent(EStdKeyBackspace, EKeyBackspace, 0, 0); + QTest::newRow("Multitap with mode switch") + << true + << Qt::InputMethodHints(Qt::ImhNoPredictiveText) + << 0 + << QLineEdit::Normal + << events + << QString("bdh") + << QString(""); + events.clear(); + + events << FepReplayEvent('7', '7', 0, 0); + events << FepReplayEvent('7', '7', 0, 0); + events << FepReplayEvent('8', '8', 0, 0); + events << FepReplayEvent('9', '9', 0, 0); + events << FepReplayEvent('9', '9', 0, 0); + QTest::newRow("Multitap with unfinished text") + << true + << Qt::InputMethodHints(Qt::ImhNoPredictiveText) + << 0 + << QLineEdit::Normal + << events + << QString("Qt") + << QString("x"); + events << FepReplayEvent(2000); + QTest::newRow("Multitap with committed text") + << true + << Qt::InputMethodHints(Qt::ImhNoPredictiveText) + << 0 + << QLineEdit::Normal + << events + << QString("Qtx") + << QString(""); + events.clear(); + + events << FepReplayEvent('4', '4', 0, 0); + events << FepReplayEvent('4', '4', 0, 0); + // Simulate holding down hash key. + events << FepReplayEvent(EEventKeyDown, EStdKeyHash, 0, 0, 0); + events << FepReplayEvent(EEventKey, EStdKeyHash, '#', 0, 0); + events << FepReplayEvent(500); + events << FepReplayEvent(EEventKey, EStdKeyHash, '#', 0, 1); + events << FepReplayEvent(EEventKey, EStdKeyHash, '#', 0, 1); + events << FepReplayEvent(EEventKey, EStdKeyHash, '#', 0, 1); + events << FepReplayEvent(EEventKeyUp, EStdKeyHash, 0, 0, 0); + events << FepReplayEvent('7', '7', 0, 0); + events << FepReplayEvent('7', '7', 0, 0); + events << FepReplayEvent('8', '8', 0, 0); + // QTBUG-9867: Switch back as well to make sure we don't get extra symbols + events << FepReplayEvent(EEventKeyDown, EStdKeyHash, 0, 0, 0); + events << FepReplayEvent(EEventKey, EStdKeyHash, '#', 0, 0); + events << FepReplayEvent(500); + events << FepReplayEvent(EEventKey, EStdKeyHash, '#', 0, 1); + events << FepReplayEvent(EEventKey, EStdKeyHash, '#', 0, 1); + events << FepReplayEvent(EEventKey, EStdKeyHash, '#', 0, 1); + events << FepReplayEvent(EEventKeyUp, EStdKeyHash, 0, 0, 0); + events << FepReplayEvent('9', '9', 0, 0); + events << FepReplayEvent('6', '6', 0, 0); + events << FepReplayEvent('8', '8', 0, 0); + events << FepReplayEvent(2000); + events << FepReplayEvent(EStdKeyDevice3, EKeyDevice3, 0, 0); // Select key + QTest::newRow("Multitap and numbers") + << true + << Qt::InputMethodHints(Qt::ImhNoPredictiveText) + << 0 + << QLineEdit::Normal + << events + << QString("H778wmt") + << QString(""); + QTest::newRow("T9 and numbers") + << true + << Qt::InputMethodHints(Qt::ImhPreferLowercase) + << 0 + << QLineEdit::Normal + << events + << QString("hi778you") + << QString(""); + events.clear(); + + events << FepReplayEvent('4', '4', 0, 0); + events << FepReplayEvent('4', '4', 0, 0); + events << FepReplayEvent(EStdKeyDevice3, EKeyDevice3, 0, 0); // Select key + QTest::newRow("T9") + << true + << Qt::InputMethodHints(Qt::ImhPreferLowercase) + << 0 + << QLineEdit::Normal + << events + << QString("hi") + << QString(""); + QTest::newRow("T9 with uppercase") + << true + << Qt::InputMethodHints(Qt::ImhPreferUppercase) + << 0 + << QLineEdit::Normal + << events + << QString("HI") + << QString(""); + QTest::newRow("T9 with forced lowercase") + << true + << Qt::InputMethodHints(Qt::ImhLowercaseOnly) + << 0 + << QLineEdit::Normal + << events + << QString("hi") + << QString(""); + QTest::newRow("T9 with forced uppercase") + << true + << Qt::InputMethodHints(Qt::ImhUppercaseOnly) + << 0 + << QLineEdit::Normal + << events + << QString("HI") + << QString(""); + QTest::newRow("T9 with maxlength") + << true + << Qt::InputMethodHints(Qt::ImhLowercaseOnly) + << 1 + << QLineEdit::Normal + << events + << QString("i") + << QString(""); + events.clear(); + + events << FepReplayEvent('4', '4', 0, 0); + events << FepReplayEvent('4', '4', 0, 0); + events << FepReplayEvent(EStdKeyLeftArrow, EKeyLeftArrow, 0, 0); + events << FepReplayEvent(EStdKeyLeftArrow, EKeyLeftArrow, 0, 0); + events << FepReplayEvent('9', '9', 0, 0); + events << FepReplayEvent('6', '6', 0, 0); + events << FepReplayEvent('8', '8', 0, 0); + events << FepReplayEvent('0', '0', 0, 0); + events << FepReplayEvent(EStdKeyRightArrow, EKeyRightArrow, 0, 0); + events << FepReplayEvent(EStdKeyRightArrow, EKeyRightArrow, 0, 0); + events << FepReplayEvent('8', '8', 0, 0); + events << FepReplayEvent('8', '8', 0, 0); + QTest::newRow("T9 with movement and unfinished text") + << true + << Qt::InputMethodHints(Qt::ImhPreferLowercase) + << 0 + << QLineEdit::Normal + << events + << QString("you hi") + << QString("tv"); + QTest::newRow("T9 with movement, password and unfinished text") + << true + << Qt::InputMethodHints(Qt::ImhPreferLowercase) + << 0 + << QLineEdit::Password + << events + << QString("wmt h") + << QString("u"); + QTest::newRow("T9 with movement, maxlength, password and unfinished text") + << true + << Qt::InputMethodHints(Qt::ImhPreferLowercase) + << 2 + << QLineEdit::Password + << events + << QString("wh") + << QString(""); + QTest::newRow("T9 with movement, maxlength and unfinished text") + << true + << Qt::InputMethodHints(Qt::ImhPreferLowercase) + << 2 + << QLineEdit::Normal + << events + << QString("hi") + << QString(""); + QTest::newRow("Multitap with movement and unfinished text") + << true + << Qt::InputMethodHints(Qt::ImhNoPredictiveText | Qt::ImhPreferLowercase) + << 0 + << QLineEdit::Normal + << events + << QString("wmt h") + << QString("u"); + QTest::newRow("Multitap with movement, maxlength and unfinished text") + << true + << Qt::InputMethodHints(Qt::ImhNoPredictiveText | Qt::ImhPreferLowercase) + << 2 + << QLineEdit::Normal + << events + << QString("wh") + << QString(""); + QTest::newRow("Numbers with movement") + << true + << Qt::InputMethodHints(Qt::ImhDigitsOnly) + << 0 + << QLineEdit::Normal + << events + << QString("96804488") + << QString(""); + QTest::newRow("Numbers with movement and maxlength") + << true + << Qt::InputMethodHints(Qt::ImhDigitsOnly) + << 2 + << QLineEdit::Normal + << events + << QString("44") + << QString(""); + QTest::newRow("Numbers with movement, password and unfinished text") + << true + << Qt::InputMethodHints(Qt::ImhDigitsOnly) + << 0 + << QLineEdit::Password + << events + << QString("9680448") + << QString("8"); + QTest::newRow("Numbers with movement, maxlength, password and unfinished text") + << true + << Qt::InputMethodHints(Qt::ImhDigitsOnly) + << 2 + << QLineEdit::Password + << events + << QString("44") + << QString(""); + events << FepReplayEvent(EStdKeyRightArrow, EKeyRightArrow, 0, 0); + QTest::newRow("T9 with movement") + << true + << Qt::InputMethodHints(Qt::ImhPreferLowercase) + << 0 + << QLineEdit::Normal + << events + << QString("you htvi") + << QString(""); + QTest::newRow("T9 with movement and password") + << true + << Qt::InputMethodHints(Qt::ImhPreferLowercase) + << 0 + << QLineEdit::Password + << events + << QString("wmt hu") + << QString(""); + QTest::newRow("T9 with movement, maxlength and password") + << true + << Qt::InputMethodHints(Qt::ImhPreferLowercase) + << 2 + << QLineEdit::Password + << events + << QString("wh") + << QString(""); + QTest::newRow("Multitap with movement") + << true + << Qt::InputMethodHints(Qt::ImhNoPredictiveText | Qt::ImhPreferLowercase) + << 0 + << QLineEdit::Normal + << events + << QString("wmt hu") + << QString(""); + QTest::newRow("Multitap with movement and maxlength") + << true + << Qt::InputMethodHints(Qt::ImhNoPredictiveText | Qt::ImhPreferLowercase) + << 2 + << QLineEdit::Normal + << events + << QString("wh") + << QString(""); + QTest::newRow("Numbers with movement and password") + << true + << Qt::InputMethodHints(Qt::ImhDigitsOnly) + << 0 + << QLineEdit::Password + << events + << QString("96804488") + << QString(""); + QTest::newRow("Numbers with movement, maxlength and password") + << true + << Qt::InputMethodHints(Qt::ImhDigitsOnly) + << 2 + << QLineEdit::Password + << events + << QString("44") + << QString(""); + events.clear(); +#endif +} + +void tst_QInputContext::symbianTestCoeFepInputContext() +{ +#ifndef Q_OS_SYMBIAN + QSKIP("This is a Symbian-only test", SkipAll); +#else + QCoeFepInputContext *ic = qobject_cast(qApp->inputContext()); + if (!ic) { + QSKIP("coefep is not the active input context; skipping test", SkipAll); + } + + QFETCH(bool, inputMethodEnabled); + QFETCH(Qt::InputMethodHints, inputMethodHints); + QFETCH(int, maxLength); + QFETCH(QLineEdit::EchoMode, echoMode); + QFETCH(QList, keyEvents); + QFETCH(QString, finalString); + QFETCH(QString, preeditString); + + if (inputMethodEnabled && m_phoneIsQwerty) { + QSKIP("Skipping advanced input method tests on QWERTY phones", SkipSingle); + } + + QWidget w; + QLayout *layout = new QVBoxLayout; + w.setLayout(layout); + QLineEdit *lineedit = new QLineEdit; + layout->addWidget(lineedit); + lineedit->setFocus(); +#ifdef QT_KEYPAD_NAVIGATION + lineedit->setEditFocus(true); +#endif + w.show(); + + lineedit->setAttribute(Qt::WA_InputMethodEnabled, inputMethodEnabled); + lineedit->setInputMethodHints(inputMethodHints); + if (maxLength > 0) + lineedit->setMaxLength(maxLength); + lineedit->setEchoMode(echoMode); + + QTest::qWait(200); + + foreach(FepReplayEvent event, keyEvents) { + event.replay(lineedit); + } + + QApplication::processEvents(); + + QCOMPARE(lineedit->text(), finalString); + QEXPECT_FAIL("Numbers with movement, maxlength, password and unfinished text" + , "Fails due to QTBUG-12949" + , Continue); + QCOMPARE(ic->m_preeditString, preeditString); +#endif +} + QTEST_MAIN(tst_QInputContext) #include "tst_qinputcontext.moc" -- cgit v0.12 From f0ec1d2ec229f5a2c47dfa9392a2625da25fc69f Mon Sep 17 00:00:00 2001 From: axis Date: Wed, 1 Sep 2010 11:47:09 +0200 Subject: Fixed a case where a newly created native widget would lose focus. If a native control was created for a child widget, it would receive the FocusChanged signal in its QSymbianControl with a value of not- focused. This would lead the code to deactivate the whole toplevel window. This was fixed by having Qt traverse the widget chain up to the toplevel and check if parent widgets are also not focused. AutoTest: Included and passed RevBy: Denis Dzyubenko RevBy: mread --- src/gui/kernel/qapplication_s60.cpp | 29 ++++++++++++++++++-------- tests/auto/qinputcontext/tst_qinputcontext.cpp | 1 - tests/auto/qwidget/tst_qwidget.cpp | 25 ++++++++++++++++++++++ 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/gui/kernel/qapplication_s60.cpp b/src/gui/kernel/qapplication_s60.cpp index 7c5e790..7408360 100644 --- a/src/gui/kernel/qapplication_s60.cpp +++ b/src/gui/kernel/qapplication_s60.cpp @@ -1242,17 +1242,28 @@ void QSymbianControl::FocusChanged(TDrawNow /* aDrawNow */) S60->setStatusPaneAndButtonGroupVisibility(statusPaneVisibility, buttonGroupVisibility); #endif } else if (QApplication::activeWindow() == qwidget->window()) { - if (CCoeEnv::Static()->AppUi()->IsDisplayingMenuOrDialog() || S60->menuBeingConstructed) { - QWidget *fw = QApplication::focusWidget(); - if (fw) { - QFocusEvent event(QEvent::FocusOut, Qt::PopupFocusReason); - QCoreApplication::sendEvent(fw, &event); - } - m_symbianPopupIsOpen = true; - return; + bool focusedControlFound = false; + WId winId = 0; + for (QWidget *w = qwidget->parentWidget(); w && (winId = w->internalWinId()); w = w->parentWidget()) { + if (winId->IsFocused() && winId->IsVisible()) { + focusedControlFound = true; + break; + } else if (w->isWindow()) + break; } + if (!focusedControlFound) { + if (CCoeEnv::Static()->AppUi()->IsDisplayingMenuOrDialog() || S60->menuBeingConstructed) { + QWidget *fw = QApplication::focusWidget(); + if (fw) { + QFocusEvent event(QEvent::FocusOut, Qt::PopupFocusReason); + QCoreApplication::sendEvent(fw, &event); + } + m_symbianPopupIsOpen = true; + return; + } - QApplication::setActiveWindow(0); + QApplication::setActiveWindow(0); + } } // else { We don't touch the active window unless we were explicitly activated or deactivated } } diff --git a/tests/auto/qinputcontext/tst_qinputcontext.cpp b/tests/auto/qinputcontext/tst_qinputcontext.cpp index 23cfd9e..c90cc5b 100644 --- a/tests/auto/qinputcontext/tst_qinputcontext.cpp +++ b/tests/auto/qinputcontext/tst_qinputcontext.cpp @@ -448,7 +448,6 @@ void tst_QInputContext::focusProxy() QInputContext *gic = qApp->inputContext(); QVERIFY(gic); - qDebug() << gic->focusWidget() << &proxy; QCOMPARE(gic->focusWidget(), &proxy); // then change the focus proxy and check that input context is valid diff --git a/tests/auto/qwidget/tst_qwidget.cpp b/tests/auto/qwidget/tst_qwidget.cpp index ef05b91..098ce3c 100644 --- a/tests/auto/qwidget/tst_qwidget.cpp +++ b/tests/auto/qwidget/tst_qwidget.cpp @@ -402,6 +402,8 @@ private slots: #endif // QT_MAC_USE_COCOA #endif + void nativeChildFocus(); + private: bool ensureScreenSize(int width, int height); QWidget *testWidget; @@ -10531,5 +10533,28 @@ void tst_QWidget::taskQTBUG_11373() #endif // QT_MAC_USE_COCOA #endif +void tst_QWidget::nativeChildFocus() +{ + QWidget w; + QLayout *layout = new QVBoxLayout; + w.setLayout(layout); + QLineEdit *p1 = new QLineEdit; + QLineEdit *p2 = new QLineEdit; + layout->addWidget(p1); + layout->addWidget(p2); + p1->setObjectName("p1"); + p2->setObjectName("p2"); + w.show(); + w.activateWindow(); + p1->setFocus(); + p1->setAttribute(Qt::WA_NativeWindow); + p2->setAttribute(Qt::WA_NativeWindow); + QApplication::processEvents(); + QTest::qWaitForWindowShown(&w); + + QCOMPARE(QApplication::activeWindow(), &w); + QCOMPARE(QApplication::focusWidget(), p1); +} + QTEST_MAIN(tst_QWidget) #include "tst_qwidget.moc" -- cgit v0.12 From 5ca6264933af60b3cd376b7f08bea008fa69b515 Mon Sep 17 00:00:00 2001 From: axis Date: Wed, 1 Sep 2010 13:53:32 +0200 Subject: Fixed input context trying to squeeze content into a full widget. Problem was reproduced on N97. If the FEP detects that the widget is full while still editing text, it will try to send those events as key events instead. Since this screws up the content in the widget, we stop those events from reaching the widget in the input context. AutoTest: Passed Task: QTBUG-12949 RevBy: Miikka Heikkinen --- src/gui/inputmethod/qcoefepinputcontext_s60.cpp | 7 +++++++ tests/auto/qinputcontext/tst_qinputcontext.cpp | 3 --- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp index 135b894..add3d17 100644 --- a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp +++ b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp @@ -236,6 +236,13 @@ bool QCoeFepInputContext::filterEvent(const QEvent *event) break; } + QString widgetText = focusWidget()->inputMethodQuery(Qt::ImSurroundingText).toString(); + int maxLength = focusWidget()->inputMethodQuery(Qt::ImMaximumTextLength).toInt(); + if (!keyEvent->text().isEmpty() && widgetText.size() + m_preeditString.size() >= maxLength) { + // Don't send key events with string content if the widget is "full". + return true; + } + if (keyEvent->type() == QEvent::KeyPress && focusWidget()->inputMethodHints() & Qt::ImhHiddenText && !keyEvent->text().isEmpty()) { diff --git a/tests/auto/qinputcontext/tst_qinputcontext.cpp b/tests/auto/qinputcontext/tst_qinputcontext.cpp index c90cc5b..93813f9 100644 --- a/tests/auto/qinputcontext/tst_qinputcontext.cpp +++ b/tests/auto/qinputcontext/tst_qinputcontext.cpp @@ -932,9 +932,6 @@ void tst_QInputContext::symbianTestCoeFepInputContext() QApplication::processEvents(); QCOMPARE(lineedit->text(), finalString); - QEXPECT_FAIL("Numbers with movement, maxlength, password and unfinished text" - , "Fails due to QTBUG-12949" - , Continue); QCOMPARE(ic->m_preeditString, preeditString); #endif } -- cgit v0.12