[meego-commits] 9764: Changes to devel:qt-mtf/meegotouch-inputmethodkeyboard
araujo
no_reply at build.meego.com
Thu Nov 25 17:54:13 UTC 2010
Hi,
I have made the following changes to meegotouch-inputmethodkeyboard in project devel:qt-mtf. Please review and accept ASAP.
Thank You,
araujo
[This message was auto-generated]
---
Request #9764:
submit: home:araujo:branches:devel:qt-mtf/meegotouch-inputmethodkeyboard(r6)(cleanup) -> devel:qt-mtf/meegotouch-inputmethodkeyboard
Message:
Update to version 0.5.12 (BMC#10501)
State: new 2010-11-25T09:54:11 araujo
Comment: None
changes files:
--------------
--- meegotouch-inputmethodkeyboard.changes
+++ meegotouch-inputmethodkeyboard.changes
@@ -0,0 +1,21 @@
+* Thu Nov 25 2010 Luis Araujo <luis.araujo at collabora.co.uk> - 0.5.12
+- Update to release tag 0.5.12-1
+- Changes: Update the VKB settings according to the new layout
+- Fixes: wrong round corner value for word tracker background.
+- Fixes: Key spacing in landscape mode
+- Changes: use queued connection for key signals
+- Fixes: crash in ut_mimtoolbar.
+- Fixes: Caps lock notification is not shown
+- Changes: support disable toolbar buttons according toolbar xml definition.
+- Fixes: HW key navigation fails in phone dialer while entering numbers
+- Fixes: Ctrl related keys are not working in the number field and the phone application
+- New: Maintain a list of active keys within MImAbstractKeys
+- Changes: Remove active*Key members from MImAbstractKeyArea
+- New: Cache background of keys in common background pixmap
+- Changes: Use a visitor instead of MImAbstractKey::filterActiveKeys
+- Changes: Paint icons and backgrounds of keys separately
+- Fixes: Background caching during language change animation
+- Fixes: Don't touch style in WidgetBar constructor
+- Changes: use \r as vkb return key event text
+- Changes: vkb: send return as a key event, not as input method event
+
old:
----
meegotouch-inputmethodkeyboard-0.5.9.tar.bz2
new:
----
meegotouch-inputmethodkeyboard-0.5.12.tar.bz2
spec files:
-----------
--- meegotouch-inputmethodkeyboard.spec
+++ meegotouch-inputmethodkeyboard.spec
@@ -7,7 +7,7 @@
Name: meegotouch-inputmethodkeyboard
Summary: MeeGo Virtual Keyboard
-Version: 0.5.9
+Version: 0.5.12
Release: 1
Group: System/GUI/Other
License: LGPL v2.1
@@ -23,12 +23,12 @@
BuildRequires: pkgconfig(QtNetwork)
BuildRequires: pkgconfig(QtGui)
BuildRequires: pkgconfig(meegotouch)
-BuildRequires: pkgconfig(MeegoImFramework)
-BuildRequires: pkgconfig(MeegoImEngine)
BuildRequires: pkgconfig(meegotouch-feedbackreactionmaps)
BuildRequires: pkgconfig(x11)
BuildRequires: pkgconfig(xproto)
BuildRequires: pkgconfig(xkbfile)
+BuildRequires: meegotouch-inputmethodframework-devel
+BuildRequires: libmeegoimenginewords-devel
Provides: duikeyboard >= 0.4.0
Provides: meegokeyboard > 0.4.2
Obsoletes: duikeyboard < 0.4.0
other changes:
--------------
++++++ meegotouch-inputmethodkeyboard-0.5.9.tar.bz2 -> meegotouch-inputmethodkeyboard-0.5.12.tar.bz2
--- NEWS
+++ NEWS
@@ -1,3 +1,29 @@
+0.5.11
+========
+
+* Changes in meego-keyboard:
+ - changed signature of following signals in MImAbstractKeyArea:
+ - keyPressed
+ - keyReleased
+ - keyClicked
+ - longKeyPressed
+
+0.5.10-1
+========
+
+* Internal changes in meego-keyboard:
+ - MImAbstractKey:
+ - renamed key() => model()
+ - added resetActiveKeys(): will release all stuck keys
+ - added visitActiveKeys(.): takes a functor, and applies it to all active
+ keys. Aborts iteration if functor returns true.
+ - added MImAbstractKeyVisitor interface
+ - added lastActiveKey(): returns most recently pressed key that wasn't
+ released yet.
+
+ - MImAbstractKeyArea:
+ - renamed updateButtonGeometriesForWith() => updateKeyGeometries()
+
0.5.9-1
=======
--- debian/changelog
+++ debian/changelog
@@ -1,8 +1,34 @@
-meego-keyboard (0.5.10~1) unstable; urgency=low
+meego-keyboard (0.5.13~1) unstable; urgency=low
- *[UNRELEASED]
+ * [UNRELEASED]
- -- Gibran Rodriguez <ext-gibran.rodriguez at nokia.com> Wed, 03 Nov 2010 14:38:51 +0200
+ -- huaming wang <huaming.wang at nokia.com> Tue, 23 Nov 2010 12:03:05 +0200
+
+meego-keyboard (0.5.12-1) unstable; urgency=low
+
+ * Fixes: NB#204452
+ * Fixes: NB#202563 - HW key navigation fails in phone dialer while entering numbers
+ * Fixes: NB#185293 - Ctrl related keys are not working in the number field and the phone application
+ * Implemented: SWP#TINME-1169 Hardware keyboard "text copied to clipboard" notification
+
+ -- huaming wang <huaming.wang at nokia.com> Tue, 23 Nov 2010 11:59:17 +0200
+
+meego-keyboard (0.5.11-1) unstable; urgency=low
+
+ * [UNRELEASED]
+ * Fixes: NB#199711 - COREWEB: /usr/bin/meego-im-uiserver 'MWidgetView::style MWidgetController::style style WidgetBar::styleChanged WidgetBar::WidgetBar'
+ * Fixes: NB#195713 - Pre-edit text disappears when emoticon is inserted in custom toolbar in widgetsgallery
+ * Implemented: SWP#TINME-1135
+
+ -- huaming wang <huaming.wang at nokia.com> Wed, 17 Nov 2010 13:50:01 +0200
+
+meego-keyboard (0.5.10-1) unstable; urgency=low
+
+ * Fixes: NB#194728 - Settings: Latin America Spanish endonym wrong in active input method
+ * Implemented: SWP#TINME-1117
+ * Fixes: NB#194521, Dead key is desactivated when pressing shift
+
+ -- Gibran Rodriguez <ext-gibran.rodriguez at nokia.com> Wed, 10 Nov 2010 13:45:30 +0200
meego-keyboard (0.5.9-1) unstable; urgency=low
--- debian/control
+++ debian/control
@@ -2,7 +2,7 @@
Section: libs
Priority: extra
Maintainer: Mohammad Anwari <Mohammad.Anwari at nokia.com>
-Build-Depends: debhelper (>= 5), libqt4-dev (>= 4.6), doxygen, libmeegoimenginewords-dev (>= 0.3), libmeegoimframework-dev (>= 0.19.27~1), libmeegotouch-dev (>= 0.20), libmeegoreactionmap-dev (>= 0.14.0-1), libxkbfile-dev (>= 1.0.6)
+Build-Depends: debhelper (>= 5), libqt4-dev (>= 4.6), doxygen, libmeegoimenginewords-dev (>= 0.3), libmeegoimframework-dev (>= 0.19.30~1), libmeegotouch-dev (>= 0.20), libmeegoreactionmap-dev (>= 0.14.0-1), libxkbfile-dev (>= 1.0.6)
Standards-Version: 3.7.2
Package: meego-keyboard
--- debian/rules
+++ debian/rules
@@ -15,6 +15,7 @@
CFLAGS += -O0
else
CFLAGS += -O2
+ QMAKE_OPTIONS += DEFINES+=RELEASE_BUILD
endif
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
@@ -32,15 +33,10 @@
QMAKE_OPTIONS += COV_OPTION=on
endif
-ifneq (,$(filter n900,$(TMP_BUILD_OPTS)))
- QMAKE_OPTIONS += DEFINES+=NOCONTROLPANEL
-endif
-
ifneq (,$(filter timestamps,$(TMP_BUILD_OPTS)))
QMAKE_OPTIONS += DEFINES+=M_TIMESTAMP
endif
-QMAKE_OPTIONS += DEFINES+=RELEASE_BUILD
# shared library versions, option 1
version=0.1.0
--- fixture_virtualkeyboard/fixture_virtualkeyboard.cpp
+++ fixture_virtualkeyboard/fixture_virtualkeyboard.cpp
@@ -103,14 +103,14 @@
}
const MImAbstractKey * FixtureVirtualKeyboard::getKey(const MImKeyArea * const widget,
- const QString &label) const
+ const QString &label) const
{
Q_ASSERT(widget);
- foreach (const MImKeyArea::ButtonRow &row, widget->rowList) {
- foreach (const MImKey *button, row.buttons) {
- if (button->label() == label) {
- return button;
+ foreach (const MImKeyArea::KeyRow &row, widget->rowList) {
+ foreach (const MImKey *key, row.keys) {
+ if (key->label() == label) {
+ return key;
}
}
}
@@ -119,14 +119,14 @@
}
const MImAbstractKey * FixtureVirtualKeyboard::getKey(const MImKeyArea * const widget,
- MImKeyBinding::KeyAction action) const
+ MImKeyBinding::KeyAction action) const
{
Q_ASSERT(widget);
- foreach (const MImKeyArea::ButtonRow &row, widget->rowList) {
- foreach (const MImKey *button, row.buttons) {
- if (button->binding().action() == action) {
- return button;
+ foreach (const MImKeyArea::KeyRow &row, widget->rowList) {
+ foreach (const MImKey *key, row.keys) {
+ if (key->binding().action() == action) {
+ return key;
}
}
}
--- fixture_virtualkeyboard/fixture_virtualkeyboard.h
+++ fixture_virtualkeyboard/fixture_virtualkeyboard.h
@@ -47,14 +47,14 @@
* \param label button label of the virtualkeyboard
*/
virtual const MImAbstractKey *getKey(const MImKeyArea * const widget,
- const QString &label) const;
+ const QString &label) const;
/*!
* \brief Get the IkeyButton (button details) by providing the action associated by a keyboard button
* \param widget the button area widget that is queried for the button of a given action.
* \param action Action associated with a keyboard button
*/
virtual const MImAbstractKey *getKey(const MImKeyArea * widget,
- const MImKeyBinding::KeyAction action) const;
+ const MImKeyBinding::KeyAction action) const;
};
#endif
--- m-keyboard/common/keyeventhandler.cpp
+++ m-keyboard/common/keyeventhandler.cpp
@@ -20,6 +20,7 @@
#include "mimabstractkey.h"
#include "mimabstractkeyarea.h"
+#include <MTimestamp>
#include <QDebug>
KeyEventHandler::KeyEventHandler(QObject *parent)
@@ -33,21 +34,29 @@
{
bool ok = false;
- ok = connect(eventSource, SIGNAL(keyPressed(const MImAbstractKey *, const QString &, bool)),
- this, SLOT(handleKeyPress(const MImAbstractKey *, const QString &, bool)));
+ ok = connect(eventSource, SIGNAL(keyPressed(const MImAbstractKey *, QString, bool)),
+ this, SLOT(handleKeyPress(const MImAbstractKey *, const QString &, bool)),
+ Qt::QueuedConnection);
Q_ASSERT(ok);
- ok = connect(eventSource, SIGNAL(keyReleased(const MImAbstractKey *, const QString &, bool)),
- this, SLOT(handleKeyRelease(const MImAbstractKey *, const QString &, bool)));
+ ok = connect(eventSource, SIGNAL(keyReleased(const MImAbstractKey *, QString, bool)),
+ this, SLOT(handleKeyRelease(const MImAbstractKey *, const QString &, bool)),
+ Qt::QueuedConnection);
Q_ASSERT(ok);
- ok = connect(eventSource, SIGNAL(keyClicked(const MImAbstractKey *, const QString &, bool, const QPoint &)),
- this, SLOT(handleKeyClick(const MImAbstractKey *, const QString &, bool, const QPoint &)));
+ ok = connect(eventSource, SIGNAL(keyClicked(const MImAbstractKey *, QString, bool, QPoint)),
+ this, SLOT(handleKeyClick(const MImAbstractKey *, const QString &, bool, const QPoint &)),
+ Qt::QueuedConnection);
+ Q_ASSERT(ok);
+
+ ok = connect(eventSource, SIGNAL(longKeyPressed(const MImAbstractKey *, QString, bool)),
+ this, SLOT(handleLongKeyPress(const MImAbstractKey *, const QString &, bool)));
Q_ASSERT(ok);
}
void KeyEventHandler::handleKeyPress(const MImAbstractKey *key, const QString &accent, bool upperCase)
{
+ mTimestamp("KeyEventHandler", "start");
const KeyEvent event = keyToKeyEvent(*key, QEvent::KeyPress, accent, upperCase);
emit keyPressed(event);
@@ -57,10 +66,12 @@
} else if (shiftHeldDown) {
ignoreShiftClick = true;
}
+ mTimestamp("KeyEventHandler", "end");
}
void KeyEventHandler::handleKeyRelease(const MImAbstractKey *key, const QString &accent, bool upperCase)
{
+ mTimestamp("KeyEventHandler", "start");
const KeyEvent event = keyToKeyEvent(*key, QEvent::KeyRelease, accent, upperCase);
emit keyReleased(event);
@@ -69,11 +80,13 @@
shiftHeldDown = false;
emit shiftPressed(false);
}
+ mTimestamp("KeyEventHandler", "end");
}
void KeyEventHandler::handleKeyClick(const MImAbstractKey *key, const QString &accent, bool upperCase,
const QPoint &point)
{
+ mTimestamp("KeyEventHandler", "start");
const KeyEvent event = keyToKeyEvent(*key, QEvent::KeyRelease, accent, upperCase, point);
if (event.qtKey() == Qt::Key_Shift && ignoreShiftClick) {
@@ -81,6 +94,14 @@
} else {
emit keyClicked(event);
}
+ mTimestamp("KeyEventHandler", "end");
+}
+
+void KeyEventHandler::handleLongKeyPress(const MImAbstractKey *key, const QString &accent, bool upperCase)
+{
+ const KeyEvent event = keyToKeyEvent(*key, QEvent::KeyPress, accent, upperCase);
+
+ emit longKeyPressed(event);
}
KeyEvent KeyEventHandler::keyToKeyEvent(const MImAbstractKey &key, QKeyEvent::Type eventType,
@@ -92,9 +113,9 @@
upperCase |= shiftHeldDown;
if (accent.isEmpty()) {
- event = key.key().toKeyEvent(eventType, upperCase);
+ event = key.model().toKeyEvent(eventType, upperCase);
} else {
- event = key.key().toKeyEvent(eventType, accent.at(0), upperCase);
+ event = key.model().toKeyEvent(eventType, accent.at(0), upperCase);
}
event.setPos(point);
--- m-keyboard/common/keyeventhandler.h
+++ m-keyboard/common/keyeventhandler.h
@@ -71,6 +71,13 @@
void keyClicked(const KeyEvent &event);
/*!
+ * \brief Emitted when key is long pressed.
+ *
+ * \param event key event
+ */
+ void longKeyPressed(const KeyEvent &event);
+
+ /*!
* \brief Emitted when shift key is pressed or released
* \param state Contains true is key is pressed
*/
@@ -92,6 +99,11 @@
*/
void handleKeyClick(const MImAbstractKey *key, const QString &accent, bool upperCase, const QPoint &point);
+ /*!
+ * \brief Generates KeyEvent for given \a key and emits longKeyPress
+ */
+ void handleLongKeyPress(const MImAbstractKey *key, const QString &accent, bool upperCase);
+
private:
//! Turn key button into a KeyEvent, considering current accent and modifier state
KeyEvent keyToKeyEvent(const MImAbstractKey &key, QKeyEvent::Type eventType,
--- m-keyboard/common/mhardwarekeyboard.cpp
+++ m-keyboard/common/mhardwarekeyboard.cpp
@@ -17,8 +17,13 @@
#include <QDebug>
#include <QX11Info>
#include <QTextCodec>
+#include <QApplication>
+#include <QClipboard>
#include <algorithm>
+#include <mplainwindow.h>
+#include <MSceneWindow>
+#include <MBanner>
#include <mabstractinputmethodhost.h>
#include "mhardwarekeyboard.h"
@@ -36,6 +41,7 @@
namespace
{
+ const int CopyValidForNotificationInterval(500); // in ms
const Qt::KeyboardModifier FnLevelModifier = Qt::GroupSwitchModifier;
const Qt::Key FnLevelKey = Qt::Key_AltGr;
const Qt::Key SymKey = Qt::Key_Multi_key;
@@ -209,10 +215,8 @@
{
qDebug() << __PRETTY_FUNCTION__;
- // We don't know how many requirements for direct mode will be yet besides
- // symbol view is required for symbol key. So redirect all the keys to plugin,
- // but now only handle symbol key.
if (imMode != M::InputMethodModeDirect) {
+ connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(handleClipboardDataChange()));
toggleCustomAutoRepeat(true);
shiftShiftCapsLock = false;
@@ -254,6 +258,8 @@
{
qDebug() << __PRETTY_FUNCTION__;
+ disconnect(QApplication::clipboard(), SIGNAL(dataChanged()), this, 0);
+
if (!preedit.isEmpty()) {
inputMethodHost.sendPreeditString("", MInputMethod::PreeditKeyPress);
preedit.clear();
@@ -286,11 +292,28 @@
}
+void MHardwareKeyboard::handleClipboardDataChange()
+{
+ if (!lastCtrlCTime.isValid()
+ || (lastCtrlCTime.addMSecs(CopyValidForNotificationInterval) < QTime::currentTime())) {
+ lastCtrlCTime = QTime();
+ return;
+ }
+ lastCtrlCTime = QTime();
+
+ MBanner &textCopiedBanner(*new MBanner);
+ textCopiedBanner.setStyleName("InformationBanner");
+ textCopiedBanner.setTitle(qtTrId("qtn_comm_text_copied"));
+ textCopiedBanner.appear(MPlainWindow::instance(), MSceneWindow::DestroyWhenDone);
+}
+
+
bool MHardwareKeyboard::actionOnPress(Qt::Key keyCode) const
{
static const Qt::Key pressPassKeys[] = {
Qt::Key_Return, Qt::Key_Backspace, Qt::Key_Delete,
- Qt::Key_Left, Qt::Key_Right, Qt::Key_Up, Qt::Key_Down };
+ Qt::Key_Left, Qt::Key_Right, Qt::Key_Up, Qt::Key_Down,
+ Qt::Key_Home, Qt::Key_End, Qt::Key_PageUp, Qt::Key_PageDown };
static const Qt::Key * const keysEnd = pressPassKeys + ELEMENTS(pressPassKeys);
return keysEnd != std::find(pressPassKeys, keysEnd, keyCode);
@@ -439,6 +462,56 @@
}
}
+bool MHardwareKeyboard::filterArrowKeys(QEvent::Type eventType, Qt::Key keyCode,
+ Qt::KeyboardModifiers modifiers,
+ QString text, bool autoRepeat, int count,
+ quint32 nativeModifiers) const
+{
+ // Home/End/PageUp/PageDown only when user is holding Fn down
+ if (!fnPressed && (nativeModifiers & FnModifierMask)
+ && (keyCode <= Qt::Key_PageDown) && (keyCode >= Qt::Key_Home)) {
+ Q_ASSERT(text.isEmpty());
+ switch (keyCode) {
+ case Qt::Key_Home:
+ keyCode = Qt::Key_Left;
+ break;
+ case Qt::Key_End:
+ keyCode = Qt::Key_Right;
+ break;
+ case Qt::Key_PageUp:
+ keyCode = Qt::Key_Up;
+ break;
+ case Qt::Key_PageDown:
+ keyCode = Qt::Key_Down;
+ break;
+ default:
+ Q_ASSERT_X(false, "MHardwareKeyboard::filterArrowKeys",
+ "Left, Right, Up or Down with Fn modifier");
+ break;
+ }
+ inputMethodHost.sendKeyEvent(
+ QKeyEvent(eventType, keyCode, modifiers, text, autoRepeat, count),
+ MInputMethod::EventRequestEventOnly);
+ return true;
+ }
+
+ return false;
+}
+
+void MHardwareKeyboard::filterMaybeIgnoreFn(Qt::Key &keyCode, QString &text,
+ quint32 nativeScanCode, quint32 nativeModifiers) const
+{
+ // When Fn is pressed while also being locked, fn modifier is ignored for obtaining the text.
+ // We also ignore Fn completely with Control.
+ if ((keyCode != FnLevelKey) && ((fnPressed && (currentLockedMods & FnModifierMask))
+ || ((nativeModifiers & FnModifierMask)
+ && (nativeModifiers & ControlMask)))) {
+ text = keycodeToString(nativeScanCode, (nativeModifiers & ShiftMask) ? 1 : 0);
+ keyCode = text.isEmpty() ? Qt::Key_unknown : static_cast<Qt::Key>(QKeySequence(text)[0]);
+ }
+}
+
+
bool MHardwareKeyboard::filterKeyPress(Qt::Key keyCode, Qt::KeyboardModifiers modifiers,
QString text, bool autoRepeat, int count,
quint32 nativeScanCode, quint32 nativeModifiers)
@@ -457,14 +530,11 @@
return eaten;
}
- // When Fn is pressed while also being locked, fn modifier is ignored for obtaining the text
- if ((keyCode != FnLevelKey) && fnPressed && (currentLockedMods & FnModifierMask)) {
- text = keycodeToString(nativeScanCode, (nativeModifiers & ShiftMask) ? 1 : 0);
- keyCode = text.isEmpty() ? Qt::Key_unknown : static_cast<Qt::Key>(QKeySequence(text)[0]);
- }
+ filterMaybeIgnoreFn(keyCode, text, nativeScanCode, nativeModifiers);
+
// When shift is latched (using lock modifier), shift modifier must not reverse the
// effect of Lock
- else if ((currentLatchedMods & LockMask) && (nativeModifiers & ShiftMask)
+ if ((currentLatchedMods & LockMask) && (nativeModifiers & ShiftMask)
&& (keyCode != Qt::Key_Shift) && (text.length() == 1) && text[0].isLetter()) {
text = keycodeToString(nativeScanCode, (nativeModifiers & FnModifierMask) ? 3 : 1);
keyCode = text.isEmpty() ? Qt::Key_unknown : static_cast<Qt::Key>(QKeySequence(text)[0]);
@@ -493,6 +563,8 @@
if (nativeModifiers & SymModifierMask) {
eatenBySymHandler = handlePressWithSymModifier(text, nativeScanCode, nativeModifiers);
eaten = eaten || eatenBySymHandler;
+ } else if ((nativeModifiers & ControlMask) && (keyCode == Qt::Key_C)) {
+ lastCtrlCTime.start();
}
if (eaten && !eatenBySymHandler) {
@@ -534,6 +606,16 @@
MInputMethod::EventRequestEventOnly);
eaten = true;
}
+ // We ignore Fn completely with Control. keyCode and text have been adjusted already.
+ else if ((nativeModifiers & ControlMask) && (nativeModifiers & FnModifierMask)) {
+ inputMethodHost.sendKeyEvent(
+ QKeyEvent(QEvent::KeyPress, keyCode, modifiers, text, autoRepeat, count),
+ MInputMethod::EventRequestEventOnly);
+ eaten = true;
+ } else if (!eaten){
+ eaten = filterArrowKeys(QEvent::KeyPress, keyCode, modifiers, text, autoRepeat, count,
+ nativeModifiers);
+ }
// Relatch modifiers, X unlatches them on press but we want to unlatch on release
// (and not even always on release, e.g. with Sym+aaab we unlatch only after the
@@ -562,11 +644,8 @@
return eaten;
}
- // When Fn is pressed while also being locked, fn modifier is ignored for obtaining the text
- if ((keyCode != FnLevelKey) && fnPressed && (currentLockedMods & FnModifierMask)) {
- text = keycodeToString(nativeScanCode, (nativeModifiers & ShiftMask) ? 1 : 0);
- keyCode = text.isEmpty() ? Qt::Key_unknown : static_cast<Qt::Key>(QKeySequence(text)[0]);
- }
+ filterMaybeIgnoreFn(keyCode, text, nativeScanCode, nativeModifiers);
+
const bool keyWasPressed(pressedKeys.contains(nativeScanCode));
const quint32 pressNativeModifiers(pressedKeys.value(nativeScanCode));
@@ -615,6 +694,15 @@
modifiers & ~Qt::KeyboardModifiers(Qt::ShiftModifier), text, false, 1),
MInputMethod::EventRequestEventOnly);
eaten = true;
+ // We ignore Fn completely with Control. keyCode and text have been adjusted already.
+ } else if ((nativeModifiers & ControlMask) && (nativeModifiers & FnModifierMask)) {
+ inputMethodHost.sendKeyEvent(
+ QKeyEvent(QEvent::KeyRelease, keyCode, modifiers, text, false, 1),
+ MInputMethod::EventRequestEventOnly);
+ eaten = true;
+ } else if (!eaten) {
+ eaten = filterArrowKeys(QEvent::KeyRelease, keyCode, modifiers, text, false, 1,
+ nativeModifiers);
}
pressedKeys.remove(nativeScanCode);
--- m-keyboard/common/mhardwarekeyboard.h
+++ m-keyboard/common/mhardwarekeyboard.h
@@ -24,6 +24,7 @@
#include <QString>
#include <QEvent>
#include <QTimer>
+#include <QTime>
#include <QRegExp>
#include "mxkb.h"
#include "hwkbcharloopsmanager.h"
@@ -152,6 +153,9 @@
//! Called when long press timer started on key press timeouts.
void handleLongPressTimeout();
+ //! Display notification for Ctrl+C
+ void handleClipboardDataChange();
+
private:
//! \brief If character is not accepted by the client, try to find an acceptable character
//! on another shift level
@@ -267,6 +271,16 @@
QString text,
quint32 nativeScanCode, quint32 nativeModifiers);
+ //! Helper for filterKeyRelease and filterKeyPress, handle arrow keys with Fn
+ bool filterArrowKeys(QEvent::Type eventType, Qt::Key keyCode,
+ Qt::KeyboardModifiers modifiers,
+ QString text, bool autoRepeat, int count,
+ quint32 nativeModifiers) const;
+
+ //! Recognize cases where we need to pretend Fn modifier was not on and change
+ //! \a keyCode and \a text accordingly.
+ void filterMaybeIgnoreFn(Qt::Key &keyCode, QString &text,
+ quint32 nativeScanCode, quint32 nativeModifiers) const;
//! \brief Toggle custom autorepeat if \a enable is true, disable it otherwise
//!
@@ -344,6 +358,8 @@
HwKbDeadKeyMapper deadKeyMapper;
+ QTime lastCtrlCTime;
+
friend class Ut_MHardwareKeyboard;
friend class Ft_MHardwareKeyboard;
};
--- m-keyboard/common/mimkeymodel.cpp
+++ m-keyboard/common/mimkeymodel.cpp
@@ -77,7 +77,7 @@
break;
case ActionReturn:
key = Qt::Key_Return;
- text = "\n";
+ text = "\r";
break;
case ActionTab:
key = Qt::Key_Tab;
--- m-keyboard/common/mimkeymodel.h
+++ m-keyboard/common/mimkeymodel.h
@@ -117,7 +117,7 @@
friend class KeyboardData;
friend class Ut_MImKeyModel;
- friend class Ut_KeyButton;
+ friend class Ut_MImKey;
friend class Ut_MVirtualKeyboard;
};
@@ -243,7 +243,7 @@
friend class KeyboardData;
friend class Ut_MImKeyModel;
- friend class Ut_KeyButton;
+ friend class Ut_MImKey;
};
--- m-keyboard/layouts/es_mx.xml
+++ m-keyboard/layouts/es_mx.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE keyboard SYSTEM 'VirtualKeyboardLayout.dtd'>
-<keyboard title="Español-Latin America" version="1.0" catalog="es_mx" language="es_mx">
+<keyboard title="Español (América Latina)" version="1.0" catalog="" language="es_mx">
<layout type="general" orientation="landscape">
<section id="main">
<row>
--- m-keyboard/layouts/symbols_ar.xml
+++ m-keyboard/layouts/symbols_ar.xml
@@ -360,9 +360,6 @@
<key>
<binding label="!"/>
</key>
- <key>
- <binding label="٫"/>
- </key>
<key style="special" rtl="true">
<binding action="backspace"/>
</key>
--- m-keyboard/m-keyboard.pro
+++ m-keyboard/m-keyboard.pro
@@ -3,6 +3,7 @@
OBJECTS_DIR = .obj
MOC_DIR = .moc
+M_MGEN_OUTDIR = .gen
# we have this line temporarily until new libmeegotouch without rpath is integrated
QT += xml
@@ -10,14 +11,14 @@
contains( DEFINES, RELEASE_BUILD ) {
QMAKE_CFLAGS -= -Werror
QMAKE_CXXFLAGS -= -Werror
+ CONFIG += release
} else {
QMAKE_CFLAGS += -Werror
QMAKE_CXXFLAGS += -Werror
+ CONFIG += debug
}
CONFIG += plugin meegotouch meegoimengine meegoimenginewords meegoimframework meegoreactionmap
-#CONFIG += mcontrolpanel
-DEFINES += NOCONTROLPANEL
CONFIG += link_pkgconfig
PKGCONFIG += gconf-2.0 xkbfile
--- m-keyboard/mkeyboardhost.cpp
+++ m-keyboard/mkeyboardhost.cpp
@@ -20,7 +20,7 @@
#include "mvirtualkeyboardstyle.h"
#include "mvirtualkeyboard.h"
#include "mhardwarekeyboard.h"
-#include "mimcorrectioncandidatewidget.h"
+#include "mimcorrectionhost.h"
#include "keyboarddata.h"
#include "layoutsmanager.h"
#include "symbolview.h"
@@ -63,13 +63,17 @@
bool DefaultInputMethodCorrectionSettingOption = true;
const QString InputMethodCorrectionEngine("/meegotouch/inputmethods/correctionengine");
const QString AutoCapsSentenceDelimiters(".?!¡¿"); // used as regexp character set content!
+ const QString AutoPunctuationTriggers(".,?!");
+ const int MaximumErrorCorrectionCandidate = 5;
const int RotationDuration = 750; //! After vkb hidden, how long to wait until shown again
const int AutoBackspaceDelay = 500; // in ms
+ const int LongPressTime = 600; // in ms
const int BackspaceRepeatInterval = 100; // in ms
const int MultitapTime = 1500; // in ms
const Qt::KeyboardModifier FnLevelModifier = Qt::GroupSwitchModifier;
// This GConf item defines whether multitouch is enabled or disabled
const char * const MultitouchSettings = "/meegotouch/inputmethods/multitouch/enabled";
+ const char * const NotificationObjectName = "ModifierLockNotification";
}
MKeyboardHost::CycleKeyHandler::CycleKeyHandler(MKeyboardHost &parent)
@@ -144,13 +148,12 @@
MKeyboardHost::MKeyboardHost(MAbstractInputMethodHost *imHost, QObject *parent)
: MAbstractInputMethod(imHost, parent),
vkbStyleContainer(0),
- correctionCandidateWidget(0),
+ correctionHost(0),
vkbWidget(0),
symbolView(0),
imCorrectionEngine(0),
inputMethodCorrectionSettings(new MGConfItem(InputMethodCorrectionSetting)),
inputMethodCorrectionEngine(new MGConfItem(InputMethodCorrectionEngine)),
- engineReady(false),
angle(M::Angle0),
rotationInProgress(false),
correctionEnabled(false),
@@ -159,7 +162,7 @@
autoCapsTriggered(false),
cursorPos(-1),
inputMethodMode(M::InputMethodModeNormal),
- backSpaceTimer(),
+ backspaceTimer(),
shiftHeldDown(false),
activeState(MInputMethod::OnScreen),
modifierLockOnBanner(0),
@@ -167,7 +170,9 @@
enableMultiTouch(false),
cycleKeyHandler(new CycleKeyHandler(*this)),
currentIndicatorDeadKey(false),
- engineLayoutDirty(false)
+ engineLayoutDirty(false),
+ backspaceMode(NormalBackspace),
+ wordTrackerSuggestionAcceptedWithSpace(false)
{
displayHeight = MPlainWindow::instance()->visibleSceneSize(M::Landscape).height();
displayWidth = MPlainWindow::instance()->visibleSceneSize(M::Landscape).width();
@@ -182,7 +187,9 @@
// It uses animation to carry out the orientation change transform
// (e.g. rotation and position animation). We do this because transform
// happens in the scene, not in the view (MWindow) anymore.
+ // Enforcing full viewport updates helps to paint correctly in software mode.
MPlainWindow::instance()->sceneManager()->appearSceneWindowNow(sceneWindow);
+ MPlainWindow::instance()->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
// Because we set vkbWidget as a child of sceneWindow the vkb
// will always be in correct orientation. However the animation will be
@@ -210,6 +217,8 @@
this, SLOT(handleKeyPress(const KeyEvent &)));
connect(vkbWidget, SIGNAL(keyReleased(const KeyEvent &)),
this, SLOT(handleKeyRelease(const KeyEvent &)));
+ connect(vkbWidget, SIGNAL(longKeyPressed(const KeyEvent &)),
+ this, SLOT(handleLongKeyPress(const KeyEvent &)));
connect(vkbWidget, SIGNAL(showSymbolViewRequested()),
this, SLOT(showSymbolView()));
@@ -243,7 +252,7 @@
this, SLOT(sendKeyEvent(const QKeyEvent &)));
Q_ASSERT(ok);
ok = connect(imToolbar, SIGNAL(sendStringRequest(const QString &)),
- this, SLOT(sendString(const QString &)));
+ this, SLOT(sendStringFromToolbar(const QString &)));
Q_ASSERT(ok);
ok = connect(imToolbar, SIGNAL(copyPasteClicked(CopyPasteState)),
this, SLOT(sendCopyPaste(CopyPasteState)));
@@ -313,6 +322,8 @@
this, SLOT(handleKeyPress(const KeyEvent &)));
connect(symbolView, SIGNAL(keyReleased(const KeyEvent &)),
this, SLOT(handleKeyRelease(const KeyEvent &)));
+ connect(symbolView, SIGNAL(longKeyPressed(const KeyEvent &)),
+ this, SLOT(handleLongKeyPress(const KeyEvent &)));
symbolView->setSharedHandleArea(sharedHandleArea);
sharedHandleArea->watchOnWidget(symbolView);
@@ -321,24 +332,19 @@
this, SLOT(handleVirtualKeyboardLayoutChanged(const QString &)));
connect(vkbWidget, SIGNAL(shiftLevelChanged()),
+ this, SLOT(handleVirtualKeyboardCapsLock()));
+
+ connect(vkbWidget, SIGNAL(shiftLevelChanged()),
this, SLOT(updateSymbolViewLevel()));
connect(hardwareKeyboard, SIGNAL(shiftStateChanged()),
this, SLOT(updateSymbolViewLevel()));
- connect(vkbWidget, SIGNAL(copyPasteRequest(CopyPasteState)),
- this, SLOT(sendCopyPaste(CopyPasteState)));
- connect(vkbWidget, SIGNAL(sendKeyEventRequest(const QKeyEvent &)),
- this, SLOT(sendKeyEvent(const QKeyEvent &)));
- connect(vkbWidget, SIGNAL(sendStringRequest(const QString &)),
- this, SLOT(sendString(const QString &)));
-
if (!inputMethodCorrectionEngine->value().isNull()) {
imCorrectionEngine = MImEngineWordsInterfaceFactory::instance()->createEngine(
inputMethodCorrectionEngine->value().toString());
if (imCorrectionEngine) {
- engineReady = true;
initializeInputEngine();
connect(inputMethodCorrectionSettings, SIGNAL(valueChanged()),
this, SLOT(synchronizeCorrectionSetting()));
@@ -350,8 +356,8 @@
feedbackPlayer = MComponentData::feedbackPlayer();
- backSpaceTimer.setSingleShot(true);
- connect(&backSpaceTimer, SIGNAL(timeout()), this, SLOT(autoBackspace()));
+ backspaceTimer.setSingleShot(true);
+ connect(&backspaceTimer, SIGNAL(timeout()), this, SLOT(autoBackspace()));
// hide main layout when symbol view is shown to improve performance
connect(symbolView, SIGNAL(opened()), vkbWidget, SLOT(hideMainArea()));
@@ -367,8 +373,8 @@
vkbWidget = 0;
delete symbolView;
symbolView = 0;
- delete correctionCandidateWidget;
- correctionCandidateWidget = 0;
+ delete correctionHost;
+ correctionHost = 0;
delete sceneWindow;
sceneWindow = 0;
delete vkbStyleContainer;
@@ -379,20 +385,21 @@
MImEngineWordsInterfaceFactory::instance()->deleteEngine(imCorrectionEngine);
imCorrectionEngine = 0;
}
- backSpaceTimer.stop();
+ backspaceMode = NormalBackspace;
+ backspaceTimer.stop();
LayoutsManager::destroyInstance();
}
void MKeyboardHost::createCorrectionCandidateWidget()
{
// construct correction candidate widget
- correctionCandidateWidget = new MImCorrectionCandidateWidget(sceneWindow);
- correctionCandidateWidget->hide();
+ correctionHost = new MImCorrectionHost(sceneWindow);
+ correctionHost->hideCorrectionWidget();
- connect(correctionCandidateWidget, SIGNAL(regionUpdated(const QRegion &)),
+ connect(correctionHost, SIGNAL(regionUpdated(const QRegion &)),
this, SLOT(handleRegionUpdate(const QRegion &)));
- connect(correctionCandidateWidget, SIGNAL(candidateClicked(const QString &)),
- this, SLOT(updatePreedit(const QString &)));
+ connect(correctionHost, SIGNAL(candidateClicked(const QString &)),
+ this, SLOT(commitString(const QString &)));
}
@@ -426,14 +433,6 @@
void MKeyboardHost::show()
{
- QWidget *p = MPlainWindow::instance();
-
- if (p->nativeParentWidget()) {
- p = p->nativeParentWidget();
- }
-
- p->raise(); // make sure the window gets displayed
-
if (activeState == MInputMethod::Hardware) {
if (!hardwareKeyboard->symViewAvailable())
symbolView->hideSymbolView();
@@ -458,7 +457,7 @@
void MKeyboardHost::hide()
{
- correctionCandidateWidget->disappear();
+ correctionHost->hideCorrectionWidget();
symbolView->hideSymbolView();
vkbWidget->hideKeyboard();
}
@@ -468,12 +467,13 @@
{
preedit = preeditString;
candidates.clear();
- correctionCandidateWidget->setPreeditString(preeditString);
if (imCorrectionEngine) {
imCorrectionEngine->clearEngineBuffer();
imCorrectionEngine->reselectString(preeditString);
candidates = imCorrectionEngine->candidates();
+ correctionHost->setCandidates(candidates);
}
+ updatePreedit(preedit, candidates.count());
}
@@ -600,12 +600,13 @@
void MKeyboardHost::resetInternalState()
{
- backSpaceTimer.stop();
+ wordTrackerSuggestionAcceptedWithSpace = false;
+ backspaceMode = NormalBackspace;
+ backspaceTimer.stop();
preedit.clear();
candidates.clear();
- correctionCandidateWidget->setPreeditString("");
- correctionCandidateWidget->disappear();
- if (engineReady)
+ correctionHost->reset();
+ if (imCorrectionEngine)
imCorrectionEngine->clearEngineBuffer();
}
@@ -618,7 +619,7 @@
symbolView->prepareToOrientationChange();
vkbWidget->prepareToOrientationChange();
- correctionCandidateWidget->prepareToOrientationChange();
+ correctionHost->prepareToOrientationChange();
}
void MKeyboardHost::finalizeOrientationChange()
@@ -637,10 +638,10 @@
}
// Finalize candidate list after so its region will apply.
- correctionCandidateWidget->finalizeOrientationChange();
+ correctionHost->finalizeOrientationChange();
// If correction candidate widget was open we need to reposition it.
- if (correctionCandidateWidget->isVisible()) {
+ if (correctionHost->isActive()) {
bool success = false;
const QRect rect = inputMethodHost()->preeditRectangle(success);
QRect localRect;
@@ -648,11 +649,10 @@
// the correct coordinates for pre-edit rectangle, so rect here
// is null.
if (success && !rect.isNull() && rotateRect(rect, localRect)) {
- const int bottomLimit = sceneWindow->mapRectFromScene(vkbWidget->mainAreaSceneRect()).top();
-
- correctionCandidateWidget->setPosition(localRect, bottomLimit);
+ correctionHost->setPosition(localRect);
+ correctionHost->showCorrectionWidget(correctionHost->candidateMode());
} else {
- correctionCandidateWidget->disappear();
+ correctionHost->hideCorrectionWidget();
}
}
@@ -735,29 +735,14 @@
void MKeyboardHost::handleMouseClickOnPreedit(const QPoint &mousePos, const QRect &preeditRect)
{
- if (candidates.size() <= 1)
+ Q_UNUSED(mousePos);
+ Q_UNUSED(preeditRect);
+ // Shows suggestion list when there are some candidates.
+ // Even show suggestion list when there is only original input word in candidates.
+ if (candidates.size() <= 0)
return;
- QPoint localMousePos;
- QRect localRect;
-
- // Bottom limit for positioning candidate list widget. Keep above keyboard.
- const int bottomLimit = sceneWindow->mapRectFromScene(vkbWidget->mainAreaSceneRect()).top();
-
- // Use preeditRect if one was passed (not null).
- if (!preeditRect.isNull() && rotateRect(preeditRect, localRect)) {
- correctionCandidateWidget->setPreeditString(preedit);
- correctionCandidateWidget->setCandidates(candidates);
- correctionCandidateWidget->setPosition(localRect, bottomLimit);
- } else if (rotatePoint(mousePos, localMousePos)) {
- correctionCandidateWidget->setPreeditString(preedit);
- correctionCandidateWidget->setCandidates(candidates);
- correctionCandidateWidget->setPosition(localMousePos, bottomLimit);
- } else {
- return;
- }
-
- correctionCandidateWidget->showWidget();
+ correctionHost->showCorrectionWidget(MImCorrectionHost::WordListMode);
}
@@ -771,20 +756,23 @@
void MKeyboardHost::handleAppOrientationChange(int angle)
{
// The application receiving input has changed its orientation. Let's change ours.
- MPlainWindow::instance()->setOrientationAngle((M::OrientationAngle)angle);
+ MPlainWindow::instance()->setOrientationAngle(static_cast<M::OrientationAngle>(angle));
+ this->angle = static_cast<M::OrientationAngle>(angle);
}
-void MKeyboardHost::updatePreedit(const QString &updatedString)
+void MKeyboardHost::commitString(const QString &updatedString)
{
- MInputMethod::PreeditFace face = MInputMethod::PreeditDefault;
-
- if (candidates.count() < 2)
- face = MInputMethod::PreeditNoCandidates;
-
- preedit = updatedString;
- inputMethodHost()->sendPreeditString(updatedString, face);
- correctionCandidateWidget->setPreeditString(updatedString);
+ if (candidates.count() > 1) {
+ int suggestionIndex = candidates.indexOf(updatedString);
+ if (suggestionIndex >= 0) {
+ qDebug() << "save index:" << suggestionIndex;
+ imCorrectionEngine->setSuggestedCandidateIndex(suggestionIndex);
+ }
+ }
+ imCorrectionEngine->saveAndClearEngineBuffer();
+ inputMethodHost()->sendCommitString(updatedString);
+ preedit.clear();
}
@@ -792,12 +780,8 @@
{
// note: backspace shouldn't start accurate mode
if (preedit.length() > 0) {
- if (!backSpaceTimer.isActive()) {
+ if (!backspaceTimer.isActive()) {
setPreedit(preedit.left(preedit.length() - 1));
- const MInputMethod::PreeditFace face
- = candidates.count() < 2
- ? MInputMethod::PreeditNoCandidates : MInputMethod::PreeditDefault;
- inputMethodHost()->sendPreeditString(preedit, face);
} else {
resetInternalState();
inputMethodHost()->sendCommitString("");
@@ -820,7 +804,7 @@
void MKeyboardHost::autoBackspace()
{
- backSpaceTimer.start(BackspaceRepeatInterval); // Must restart before doBackspace
+ backspaceTimer.start(BackspaceRepeatInterval); // Must restart before doBackspace
doBackspace();
}
@@ -844,7 +828,17 @@
requestType = MInputMethod::EventRequestBoth;
} else if (event.qtKey() == Qt::Key_Backspace) {
- backSpaceTimer.start(AutoBackspaceDelay);
+ if (correctionHost->isActive()
+ && correctionHost->candidateMode() == MImCorrectionHost::WordTrackerMode) {
+ // hide word tracker when backspace key press
+ correctionHost->hideCorrectionWidget();
+ // WordTrackerBackspace mode: hide word tracker when backspace key press.
+ // And remove preedit if holding backspace long enough. But does nothing
+ // for backspace key release.
+ startBackspace(WordTrackerBackspace);
+ } else {
+ startBackspace(NormalBackspace);
+ }
}
inputMethodHost()->sendKeyEvent(event.toQKeyEvent(), requestType);
@@ -869,9 +863,16 @@
requestType = MInputMethod::EventRequestBoth;
- } else if ((event.qtKey() == Qt::Key_Backspace) && backSpaceTimer.isActive()) {
- backSpaceTimer.stop();
- doBackspace();
+ } else if ((event.qtKey() == Qt::Key_Backspace)) {
+ if ( backspaceTimer.isActive()) {
+ backspaceTimer.stop();
+ // If the backspace Mode is WordTrackerBackspace, don't need to
+ // do backspace.
+ if (backspaceMode != WordTrackerBackspace) {
+ doBackspace();
+ }
+ backspaceMode = NormalBackspace;
+ }
}
inputMethodHost()->sendKeyEvent(event.toQKeyEvent(), requestType);
@@ -895,11 +896,12 @@
}
// Candidates widget
- if (correctionCandidateWidget && correctionCandidateWidget->isVisible()) {
- correctionCandidateWidget->paintReactionMap(reactionMap, view);
+ if (correctionHost && correctionHost->isActive()) {
+ correctionHost->paintReactionMap(reactionMap, view);
- // Correction candidate widget always occupies whole screen.
- continue;
+ // Correction candidate widget occupies whole screen when it is WordListMode.
+ if (correctionHost->candidateMode() == MImCorrectionHost::WordListMode)
+ continue;
}
// Paint either symview or vkb widget reactive areas.
@@ -1004,8 +1006,41 @@
}
}
+void MKeyboardHost::handleLongKeyPress(const KeyEvent &event)
+{
+ if (event.qtKey() == Qt::Key_Space
+ && correctionEnabled
+ && correctionHost->isActive()
+ && correctionHost->candidateMode() == MImCorrectionHost::WordTrackerMode
+ && candidates.size() > 0) {
+ // long tap space key when word tracker is visible will switch to word list.
+ correctionHost->showCorrectionWidget(MImCorrectionHost::WordListMode);
+ }
+}
+
+void MKeyboardHost::sendCommitStringOrReturnEvent(const KeyEvent &event) const
+{
+ // We send return as a normal key event instead of an input method event so
+ // that it properly triggers returnPressed signal emission in MTextEdit and
+ // QLineEdit (and possibly in other similar targets).
+ if (event.qtKey() == Qt::Key_Return) {
+ inputMethodHost()->sendKeyEvent(KeyEvent(event, QEvent::KeyPress).toQKeyEvent(), MInputMethod::EventRequestEventOnly);
+ inputMethodHost()->sendKeyEvent(event.toQKeyEvent(), MInputMethod::EventRequestEventOnly);
+ } else {
+ inputMethodHost()->sendCommitString(event.text());
+ }
+}
+
void MKeyboardHost::handleTextInputKeyClick(const KeyEvent &event)
{
+ const bool wordTrackerSuggestionAcceptedWithSpacePrev(wordTrackerSuggestionAcceptedWithSpace);
+ if (!((event.specialKey() == KeyEvent::Sym)
+ || (event.specialKey() == KeyEvent::Switch)
+ || (event.specialKey() == KeyEvent::Sym)
+ || (event.qtKey() == Qt::Key_Shift))) {
+ wordTrackerSuggestionAcceptedWithSpace = false;
+ }
+
// Discard KeyPress & Drop type of events.
if (event.type() != QEvent::KeyRelease
@@ -1036,32 +1071,49 @@
if (preedit.length() > 0) {
// we just entered accurate mode. send the previous preedit stuff.
inputMethodHost()->sendCommitString(preedit);
+ if (imCorrectionEngine)
+ imCorrectionEngine->clearEngineBuffer();
preedit.clear();
}
- inputMethodHost()->sendCommitString(text);
+ sendCommitStringOrReturnEvent(event);
} else if ((event.qtKey() == Qt::Key_Space) || (event.qtKey() == Qt::Key_Return) || (event.qtKey() == Qt::Key_Tab)) {
- // commit string
- inputMethodHost()->sendCommitString(preedit);
- if (lastClickEvent.specialKey() != KeyEvent::CycleSet) {
- imCorrectionEngine->setSuggestedCandidateIndex(correctionCandidateWidget->activeIndex());
- imCorrectionEngine->saveAndClearEngineBuffer();
- correctionCandidateWidget->setPreeditString("");
+ // commit suggestion if correction candidate widget is visible and with popupMode
+ // or ignore it if correction widget is visible and with suggestionlist mode
+ // otherwise commit preedit
+ if (event.qtKey() == Qt::Key_Space
+ && correctionHost->isActive()) {
+ if (correctionHost->candidateMode() == MImCorrectionHost::WordTrackerMode) {
+ wordTrackerSuggestionAcceptedWithSpace = true;
+ inputMethodHost()->sendCommitString(correctionHost->suggestion());
+ } else {
+ // ignore space click when word list is visible.
+ return;
+ }
} else {
- imCorrectionEngine->clearEngineBuffer();
+ inputMethodHost()->sendCommitString(preedit);
}
- // send trailing space
- inputMethodHost()->sendCommitString(text);
+ if (lastClickEvent.specialKey() != KeyEvent::CycleSet) {
+ candidates.clear();
+ correctionHost->reset();
+ }
+ // Send trailing space.
+ sendCommitStringOrReturnEvent(event);
+
+ imCorrectionEngine->clearEngineBuffer();
preedit.clear();
+ } else if (wordTrackerSuggestionAcceptedWithSpacePrev && (text.length() == 1)
+ && AutoPunctuationTriggers.contains(text[0])) {
+ doBackspace();
+ inputMethodHost()->sendCommitString(text + " ");
} else {
// common case: just append stuff to current preedit
preedit += text;
candidates.clear();
- qDebug() << "event touch point:" << event.pos();
// send touch point to engine if not in symbol view
// otherwise send character to engine.
if ( !symbolView->isActive())
@@ -1070,11 +1122,27 @@
imCorrectionEngine->appendCharacter(text.at(0));
candidates = imCorrectionEngine->candidates();
+ MImEngine::DictionaryType sourceDictionaryType = imCorrectionEngine->candidateSource(0);
+
+ correctionHost->setCandidates(candidates);
- const MInputMethod::PreeditFace face
- = candidates.count() < 2
- ? MInputMethod::PreeditNoCandidates : MInputMethod::PreeditDefault;
- inputMethodHost()->sendPreeditString(preedit, face);
+ updatePreedit(preedit, candidates.count());
+
+ // because the written word (preedit) should be on the top of candidate list.
+ // So when there are some other candidates and the preedit is not a valid
+ // dictionary word, show word tracker.
+ if (candidates.count() > 1
+ && sourceDictionaryType == MImEngine::DictionaryTypeInvalid) {
+ bool success = false;
+ const QRect rect = inputMethodHost()->preeditRectangle(success);
+ QRect localRect;
+ if (success && !rect.isNull() && rotateRect(rect, localRect)) {
+ correctionHost->setPosition(localRect);
+ correctionHost->showCorrectionWidget(MImCorrectionHost::WordTrackerMode);
+ }
+ } else {
+ correctionHost->hideCorrectionWidget();
+ }
}
}
@@ -1089,7 +1157,7 @@
qDebug() << __PRETTY_FUNCTION__ << "- used language:" << language;
- if (engineReady) {
+ if (imCorrectionEngine) {
// TODO: maybe we should check return values here and in case of failure
// be always in accurate mode, for example
imCorrectionEngine->setLanguage(language, MImEngine::LanguagePriorityPrimary);
@@ -1097,8 +1165,8 @@
updateEngineKeyboardLayout();
synchronizeCorrectionSetting();
imCorrectionEngine->disablePrediction();
- imCorrectionEngine->disableCompletion();
- imCorrectionEngine->setMaximumErrors(6);
+ imCorrectionEngine->enableCompletion();
+ imCorrectionEngine->setMaximumCandidates(MaximumErrorCorrectionCandidate);
imCorrectionEngine->setExactWordPositionInList(MImEngine::ExactInListFirst);
}
}
@@ -1123,7 +1191,7 @@
inputMethodHost()->setGlobalCorrectionEnabled(false);
correctionEnabled = false;
} else {
- if (!engineReady) {
+ if (!imCorrectionEngine) {
inputMethodHost()->setGlobalCorrectionEnabled(false);
correctionEnabled = false;
return;
@@ -1265,6 +1333,15 @@
inputMethodHost()->sendCommitString(text);
}
+void MKeyboardHost::sendStringFromToolbar(const QString &text)
+{
+ if (!preedit.isEmpty()) {
+ sendString(preedit);
+ }
+ reset();
+ sendString(text);
+}
+
void MKeyboardHost::setToolbar(QSharedPointer<const MToolbarData> toolbar)
{
if (toolbar && toolbar->isVisible()) {
@@ -1484,6 +1561,20 @@
}
}
+void MKeyboardHost::handleVirtualKeyboardCapsLock()
+{
+ if (activeState != MInputMethod::OnScreen)
+ return;
+
+ if (vkbWidget->shiftStatus() == ModifierLockedState) {
+ //% "Caps lock on"
+ QString lockOnNotificationLabel = qtTrId("qtn_hwkb_caps_lock");
+ showLockOnInfoBanner(lockOnNotificationLabel);
+ } else if (modifierLockOnBanner) {
+ hideLockOnInfoBanner();
+ }
+}
+
void MKeyboardHost::showLockOnInfoBanner(const QString ¬ification)
{
if (modifierLockOnBanner) {
@@ -1492,6 +1583,7 @@
//TODO: discuss with UI designer whether we need to specify
// the disappear time out.
modifierLockOnBanner = new MBanner;
+ modifierLockOnBanner->setObjectName(NotificationObjectName);
modifierLockOnBanner->setTitle(notification);
modifierLockOnBanner->appear(MSceneWindow::DestroyWhenDone);
}
@@ -1564,7 +1656,7 @@
void MKeyboardHost::updateEngineKeyboardLayout()
{
- if (!engineReady || !engineLayoutDirty)
+ if (!imCorrectionEngine || !engineLayoutDirty)
return;
if (activeState == MInputMethod::OnScreen) {
@@ -1572,3 +1664,23 @@
}
engineLayoutDirty = false;
}
+
+void MKeyboardHost::updatePreedit(const QString &string, int candidateCount)
+{
+ // preedit style type depends on candidateCount.
+ // candidateCount styleType
+ // 0 or 1 PreeditNoCandidates
+ // 1 or >1 PreeditDefault
+ MInputMethod::PreeditFace face = MInputMethod::PreeditNoCandidates;
+ if (candidateCount > 1) {
+ face = MInputMethod::PreeditDefault;
+ }
+
+ inputMethodHost()->sendPreeditString(string, face);
+}
+
+void MKeyboardHost::startBackspace(MKeyboardHost::BackspaceMode mode)
+{
+ backspaceMode = mode;
+ backspaceTimer.start(AutoBackspaceDelay);
+}
--- m-keyboard/mkeyboardhost.h
+++ m-keyboard/mkeyboardhost.h
@@ -29,7 +29,7 @@
class MFeedbackPlayer;
class MGConfItem;
-class MImCorrectionCandidateWidget;
+class MImCorrectionHost;
class MSceneWindow;
class MVirtualKeyboard;
class MVirtualKeyboardStyleContainer;
@@ -97,13 +97,21 @@
*/
void handleKeyRelease(const KeyEvent &event);
+ /*!
+ * Handles user long press a key.
+ * \param event internal key event
+ */
+ void handleLongKeyPress(const KeyEvent &event);
+
//! \brief Draws reaction maps for the topmost widget.
void updateReactionMaps();
/*!
- * Update the pre-edit word
+ * \brief Commits \a string.
+ *
+ * If there exists preedit, the preedit will be replaced.
*/
- void updatePreedit(const QString &string);
+ void commitString(const QString &string);
/*! \brief Prepares vkb for orientation change when application is about to rotate.
*
@@ -161,6 +169,9 @@
//! Sends string
void sendString(const QString &);
+ //! Sends string from toolbar
+ void sendStringFromToolbar(const QString &);
+
//! Handle symbol key click.
void handleSymbolKeyClick();
@@ -177,6 +188,9 @@
*/
void handleHwKeyboardStateChanged();
+ //!Receives modifier state changed and shows Caps Lock infobanner
+ void handleVirtualKeyboardCapsLock();
+
//! show FN/Caps Lock infobanner
void showLockOnInfoBanner(const QString ¬ification);
@@ -281,6 +295,24 @@
//! update input engine keyboard layout according keyboard layout.
void updateEngineKeyboardLayout();
+ //! update preedit according error correction candidates.
+ void updatePreedit(const QString &string, int candidateCount);
+
+ /*!
+ * \brief This enum defines different mode for backspace clicking.
+ */
+ enum BackspaceMode {
+ NormalBackspace, //!< backspace for normal state.
+ WordTrackerBackspace //!< backspace when word tracker is visible.
+ };
+
+ //! start backspaceTimer according \a mode
+ void startBackspace(BackspaceMode mode);
+
+ //! Send return as press&release event pair, send as input method event with
+ //! commit string otherwise.
+ void sendCommitStringOrReturnEvent(const KeyEvent &event) const;
+
private:
class CycleKeyHandler; //! Reacts to cycle key press events.
friend class CycleKeyHandler;
@@ -288,7 +320,7 @@
MVirtualKeyboardStyleContainer *vkbStyleContainer;
- MImCorrectionCandidateWidget *correctionCandidateWidget;
+ MImCorrectionHost *correctionHost;
MVirtualKeyboard *vkbWidget;
MHardwareKeyboard *hardwareKeyboard;
SymbolView *symbolView;
@@ -299,7 +331,6 @@
MGConfItem *inputMethodCorrectionEngine;
QStringList candidates;
- bool engineReady;
M::OrientationAngle angle;
int displayWidth;
@@ -322,7 +353,7 @@
int inputMethodMode;
- QTimer backSpaceTimer;
+ QTimer backspaceTimer;
KeyEvent lastClickEvent;
@@ -362,6 +393,10 @@
//! Indicates whether engine layout need to be updated
bool engineLayoutDirty;
+ BackspaceMode backspaceMode;
+
+ bool wordTrackerSuggestionAcceptedWithSpace;
+
#ifdef UNIT_TEST
friend class Ut_MKeyboardHost;
#endif
--- m-keyboard/mkeyboardsettings.cpp
+++ m-keyboard/mkeyboardsettings.cpp
@@ -25,7 +25,8 @@
#include <QDebug>
namespace {
- const QString SettingsIMCorrectionSetting("/meegotouch/inputmethods/virtualkeyboard/correctionenabled");
+ const QString SettingsImErrorCorrection("/meegotouch/inputmethods/virtualkeyboard/correctionenabled");
+ const QString SettingsImWordCompletion("/meegotouch/inputmethods/virtualkeyboard/completionenabled");
const QString InputMethodLayouts("/meegotouch/inputmethods/virtualkeyboard/layouts");
const QString VKBConfigurationPath("/usr/share/meegotouch/virtual-keyboard/layouts/");
const QString VKBLayoutsFilterRule("*.xml");
@@ -33,12 +34,15 @@
};
MKeyboardSettings::MKeyboardSettings()
- : keyboardErrorCorrectionConf(SettingsIMCorrectionSetting),
+ : keyboardErrorCorrectionConf(SettingsImErrorCorrection),
+ keyboardWordCompletionConf(SettingsImWordCompletion),
selectedKeyboardsConf(InputMethodLayouts)
{
readAvailableKeyboards();
connect(&keyboardErrorCorrectionConf, SIGNAL(valueChanged()),
this, SIGNAL(errorCorrectionChanged()));
+ connect(&keyboardWordCompletionConf, SIGNAL(valueChanged()),
+ this, SIGNAL(wordCompletionChanged()));
connect(&selectedKeyboardsConf, SIGNAL(valueChanged()),
this, SIGNAL(selectedKeyboardsChanged()));
}
@@ -154,6 +158,15 @@
void MKeyboardSettings::setErrorCorrection(bool enabled)
{
- if (keyboardErrorCorrectionConf.value().toBool() != enabled)
- keyboardErrorCorrectionConf.set(enabled);
+ keyboardErrorCorrectionConf.set(enabled);
+}
+
+bool MKeyboardSettings::wordCompletion() const
+{
+ return keyboardWordCompletionConf.value().toBool();
+}
+
+void MKeyboardSettings::setWordCompletion(bool enabled)
+{
+ keyboardWordCompletionConf.set(enabled);
}
--- m-keyboard/mkeyboardsettings.h
+++ m-keyboard/mkeyboardsettings.h
@@ -27,8 +27,8 @@
/*!
* \brief MKeyboardSettings is the implemetation of meego-keyboard setting.
* MKeyboardSettings implement MAbstractInputMethodSettings and create the meego-keyboard
- * setting. It provides below functionalities: get/set error corretion, get/set
- * installed (selected) keyboards.
+ * setting. It provides below functionalities: get/set error corretion, get/set word
+ * completion, get/set installed (selected) keyboards.
*/
class MKeyboardSettings: public QObject, public MAbstractInputMethodSettings
{
@@ -65,6 +65,12 @@
//! Sets error correction option.
void setErrorCorrection(bool);
+ //! Returns the boolean value of word completion option.
+ bool wordCompletion() const;
+
+ //! Sets word completion option.
+ void setWordCompletion(bool);
+
Q_SIGNALS:
//! Emitted when selected keyboards are changed.
void selectedKeyboardsChanged();
@@ -72,6 +78,9 @@
//! Emitted when error correction option is changed.
void errorCorrectionChanged();
+ //! Emitted when word completion option is changed.
+ void wordCompletionChanged();
+
private:
QString keyboardTitle(const QString &layoutFile) const;
QString keyboardLayoutFile(const QString &title) const;
@@ -84,6 +93,7 @@
//! all available keyboards
QList<KeyboardInfo> availableKeyboardInfos;
MGConfItem keyboardErrorCorrectionConf;
+ MGConfItem keyboardWordCompletionConf;
MGConfItem selectedKeyboardsConf;
};
--- m-keyboard/theme/libmeego-keyboard.css
+++ m-keyboard/theme/libmeego-keyboard.css
@@ -1,14 +1,13 @@
- at const HEIGHT_CANDIDATEITEM: 6.4mm;
+ at const HEIGHT_CANDIDATEITEM: 3.2mm;
@const HEIGHT_TOOLBARITEM: 4mm;
MVirtualKeyboardStyle {
font-opacity: 0.9;
- font-color: #00FF00;
- text-color: #0000FF;
deadkey-locked-color: #FFA500;
- notification-font: "Nokia Sans Light" 56;
+ notification-font: $FONT_KEYBOARD;
+ notification-font-size: 56;
notification-font-opacity: 0.9;
notification-border-color: #808080;
notification-background-color: #323232;
@@ -33,13 +32,12 @@
MImAbstractKeyAreaStyle {
background-image: "meegotouch-keyboard-background" 15px 15px 15px 15px;
- font: "Nokia Sans Light" 28;
- font-color: #000000;
+ font: $FONT_KEYBOARD;
+ font-color: $COLOR_FOREGROUND;
secondary-font: $FONT_SMALL;
spacing-vertical: 2mm;
- /* should be 0.4mm or 4px, need to check*/
- spacing-horizontal: 0.2mm;
+ spacing-horizontal: 0.4mm;
padding-top: 2.2mm;
padding-bottom: $MARGIN_DOUBLE;
@@ -101,6 +99,9 @@
key-shift-icon-size: 38 38;
key-shift-icon-id: "icon-m-input-methods-shift";
key-shift-uppercase-icon-id: "icon-m-input-methods-capslock";
+
+ key-tab-icon-size: 0 0;
+ key-tab-icon-id: ; /* non-existant currently */
/* End of single widget keyboard button styling */
/* touch behaviour */
@@ -211,8 +212,8 @@
text-left-offset: 12;
text-right-offset: 12;
- font: "Nokia Sans" 20;
- text-color: #000000;
+ font: $FONT_SMALL_REGULAR;
+ text-color: $COLOR_FOREGROUND;
preferred-size: 414 56;
minimum-size: 414 56;
@@ -281,30 +282,96 @@
padding-right: 11;
}
-MImCorrectionCandidateContainerStyle {
- background-image: "meegotouch-candidatelist-background" 15 15 15 15;
+MImWordTrackerStyle {
+ background-image: "meegotouch-wordtracker-background" 18px 18px 18px 18px;
+
+ wordtracker-pointer-image: "meegotouch-wordtracker-pointer" 0 0 0 0;
+ wordtracker-pointer-size: 13 12;
+ wordtracker-pointer-overlap: 1;
+
+ show-hide-frames: 100;
+ show-hide-time: 400;
+ show-hide-interval: 30;
+
margin-top: 0;
margin-bottom: 0;
margin-left: 0;
margin-right: 0;
- minimum-size: 15mm $HEIGHT_CANDIDATEITEM;
+ padding-top: 0;
+ padding-bottom: 0;
+ padding-right: 0.8mm;
+ padding-left: 0.8mm;
+
+ minimum-size: 6.4mm $HEIGHT_CANDIDATEITEM;
maximum-size: 100% 100%;
}
+MImWordList MContentItemStyle {
+ title-object-name: "MImWordListLabel";
+ background-image : meegotouch-list-fullwidth-background $CORNER_MARGINS;
+}
+
+MImWordList MContentItemStyle:pressed {
+ background-image: meegotouch-wordtracker-item-pressed 15px 15px 15px 15px;
+}
+
+MImWordList MContentItemStyle:selected {
+ title-object-name: "MImWordListLabelSelected";
+ background-image : meegotouch-list-fullwidth-background $CORNER_MARGINS;
+}
+
+MImWordListItem MLabelStyle#MImWordListLabel {
+ font: $FONT_LARGE_BOLD;
+ color: $COLOR_INVERTED_FOREGROUND;
+ margin-top: $INDENT_DEFAULT;
+ margin-bottom: 0;
+ margin-left: $INDENT_DEFAULT;
+ margin-right: $INDENT_DEFAULT;
+}
+
+MImWordListItem MLabelStyle#MImWordListLabelSelected {
+ font: $FONT_LARGE_BOLD;
+ color: #0000ff;
+ margin-top: $INDENT_DEFAULT;
+ margin-bottom: 0;
+ margin-left: $INDENT_DEFAULT;
+ margin-right: $INDENT_DEFAULT;
+}
+
MImCorrectionCandidateItemStyle {
preferred-size: -1 $HEIGHT_CANDIDATEITEM;
- minimum-size: 10mm $HEIGHT_CANDIDATEITEM;
+ minimum-size: 4.8mm $HEIGHT_CANDIDATEITEM;
maximum-size: 100% $HEIGHT_CANDIDATEITEM;
- title-object-name: "CorrectionCandidateItemTitle";
+ background-opacity: 1.0;
+
+ font: $FONT_DEFAULT;
+ font-color: $COLOR_INVERTED_FOREGROUND;
+ press-timeout: 250;
+ release-miss-delta: 30;
+ long-tap-timeout: 600;
+
+ margin-top: 1.1mm;
+ margin-bottom: 1.1mm;
+ margin-left: 0;
+ margin-right: 0;
+
+ padding-top: 0;
+ padding-bottom: 0;
+ padding-right: 0;
+ padding-left: 0;
}
-#CorrectionCandidateItemTitle {
- margin-left: $MARGIN_XLARGE;
- margin-right: $MARGIN_XLARGE;
+MImCorrectionCandidateItemStyle:selected {
+ background-image: meegotouch-wordtracker-selection 15px 15px 15px 15px;
}
+MImCorrectionCandidateItemStyle:pressed {
+ background-image: meegotouch-wordtracker-item-pressed 15px 15px 15px 15px;
+}
+
+
MImToolbarStyle {
background-image: "meegotouch-keyboard-toolbar-background" 15px 15px 15px 15px;
margin-top: 0;
--- m-keyboard/widgets/horizontalswitcher.cpp
+++ m-keyboard/widgets/horizontalswitcher.cpp
@@ -16,6 +16,9 @@
#include "horizontalswitcher.h"
+#include "mimabstractkey.h"
+#include "mimabstractkeyarea.h"
+
#include <QGraphicsSceneResizeEvent>
#include <QGraphicsScene>
#include <QDebug>
@@ -82,6 +85,8 @@
// New item is about to enter
enterAnim.setItem(nextWidget);
nextWidget->setEnabled(false);
+ MImAbstractKey::resetActiveKeys();
+
// Try to fit current size.
nextWidget->resize(size());
@@ -148,9 +153,15 @@
emit switchDone(old, widget);
updateGeometry();
+ MImAbstractKey::resetActiveKeys();
if (old) {
old->hide();
+
+ MImAbstractKeyArea *const keyArea = dynamic_cast<MImAbstractKeyArea *>(old);
+ if (keyArea) {
+ keyArea->modifiersChanged(false);
+ }
}
}
}
--- m-keyboard/widgets/mimabstractkey.cpp
+++ m-keyboard/widgets/mimabstractkey.cpp
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * and appearing in the file LICENSE.LGPL included in the packaging
+ * of this file.
+ */
+
+#include "mimabstractkey.h"
+
+QList<MImAbstractKey *> MImAbstractKey::activeKeys;
+
+MImAbstractKey::~MImAbstractKey()
+{
+ activeKeys.removeAll(this);
+}
+
+MImAbstractKey* MImAbstractKey::lastActiveKey()
+{
+ return (activeKeys.isEmpty() ? 0 : activeKeys.last());
+}
+
+void MImAbstractKey::resetActiveKeys()
+{
+ while (!activeKeys.isEmpty()) {
+ MImAbstractKey *key = activeKeys.takeFirst();
+ key->setSelected(false);
+ key->resetTouchPointCount();
+ }
+}
+
+void MImAbstractKey::visitActiveKeys(MImAbstractKeyVisitor *visitor)
+{
+ foreach(MImAbstractKey *key, activeKeys) {
+ if ((*visitor)(key)) {
+ break;
+ }
+ }
+}
+
--- m-keyboard/widgets/mimabstractkey.h
+++ m-keyboard/widgets/mimabstractkey.h
@@ -20,10 +20,25 @@
#define MIMABSTRACTKEY_H
#include "mimkeymodel.h"
+#include <QList>
+
+class QRect;
+class QPainter;
+class MImAbstractKey;
+
+//! Visitor interface that can be used for MImAbstractKey::visitActiveKeys
+class MImAbstractKeyVisitor
+{
+public:
+ virtual bool operator()(MImAbstractKey *key) = 0;
+};
//! Represents a key model with the key's current binding state, and also contains its visible area.
class MImAbstractKey
{
+protected:
+ static QList<MImAbstractKey *> activeKeys;
+
public:
enum ButtonState {
Normal, //! Normal is the "up" state for not selected buttons.
@@ -31,6 +46,8 @@
Selected //! Selected is the "up" state for selected buttons.
};
+ virtual ~MImAbstractKey();
+
//! \brief Returns current label. It is affected by active modifiers.
virtual const QString label() const = 0;
@@ -44,7 +61,8 @@
virtual const QRectF &buttonBoundingRect() const = 0;
//! \brief Sets shift and accent. Affects label and/or icon.
- virtual void setModifiers(bool shift, QChar accent = QChar()) = 0;
+ virtual void setModifiers(bool shift,
+ QChar accent = QChar()) = 0;
//! \brief Sets the button's state to pressed. Selectable has this too.
virtual void setDownState(bool down) = 0;
@@ -56,7 +74,7 @@
virtual ButtonState state() const = 0;
//! \return the key this button represents
- virtual const MImKeyModel &key() const = 0;
+ virtual const MImKeyModel &model() const = 0;
//! \brief Get current active key binding.
virtual const MImKeyBinding &binding() const = 0;
@@ -71,7 +89,6 @@
//! (e.g., q, w, e, r, t, y).
virtual bool isNormalKey() const = 0;
-
//! \brief Called when a new touchpoint was registered on this button.
//! \returns true if the counter could be increased.
//! Cannot exceed total active touchpoint limit
@@ -87,6 +104,25 @@
//! \brief Get current touchpoint count.
virtual int touchPointCount() const = 0;
+
+ //! \brief Returns most recent key that became active, and wasn't released yet.
+ //! If no key is active, returns 0.
+ static MImAbstractKey* lastActiveKey();
+
+ //! \brief Resets active keys to normal state.
+ //! \warning Be careful when using this. Some key areas may have changed
+ //! their visual appearance according to key modifiers. However,
+ //! if all active keys are reset, those key areas might not be
+ //! aware of this change.
+ //! It is usually better to write a specialized visitor, unless
+ //! you know what you're doing.
+ //! \sa visitActiveKeys, MImAbstractKeyVisitor
+ static void resetActiveKeys();
+
+ //! \brief Visit active keys.
+ //! \param visitor must overload 'bool operator()(MImAbstractKey *key)',
+ //! aborts to visit next key when true is returned.
+ static void visitActiveKeys(MImAbstractKeyVisitor *visitor);
};
#endif
--- m-keyboard/widgets/mimabstractkeyarea.cpp
+++ m-keyboard/widgets/mimabstractkeyarea.cpp
@@ -14,8 +14,6 @@
* of this file.
*/
-
-
#include "flickgesture.h"
#include "flickgesturerecognizer.h"
#include "mvirtualkeyboardstyle.h"
@@ -25,7 +23,7 @@
#include <MApplication>
#include <MComponentData>
-#include <MFeedbackPlayer>
+#include <MFeedback>
#include <MSceneManager>
#include <MGConfItem>
#include <QDebug>
@@ -36,6 +34,7 @@
#include <QKeyEvent>
#include <mtimestamp.h>
+
namespace
{
const qreal ZValueButtons = 0.0;
@@ -70,6 +69,79 @@
&& (target.y() > origin.y() - yDistance)
&& (target.y() < origin.y() + yDistance));
}
+
+ //! \brief Helper class responsible for finding active special keys.
+ //!
+ //! Can be used as visitor.
+ class SpecialKeyFinder
+ : public MImAbstractKeyVisitor
+ {
+ public:
+ enum FindMode {
+ FindShiftKey,
+ FindDeadKey,
+ FindBoth
+ };
+
+ private:
+ MImAbstractKey *mShiftKey;
+ MImAbstractKey *mDeadKey;
+ FindMode mode;
+
+ public:
+ explicit SpecialKeyFinder(FindMode newMode = FindBoth)
+ : MImAbstractKeyVisitor()
+ , mShiftKey(0)
+ , mDeadKey(0)
+ , mode(newMode)
+ {}
+
+ MImAbstractKey *shiftKey() const
+ {
+ return mShiftKey;
+ }
+
+ MImAbstractKey *deadKey() const
+ {
+ return mDeadKey;
+ }
+
+ bool operator()(MImAbstractKey *key)
+ {
+ if (!key) {
+ return false;
+ }
+
+ if (key->isShiftKey()) {
+ mShiftKey = key;
+ } else if (key->isDeadKey()) {
+ mDeadKey = key;
+ }
+
+ switch (mode) {
+ case FindShiftKey:
+ if (mShiftKey) {
+ return true;
+ }
+ break;
+
+ case FindDeadKey:
+ if (mDeadKey) {
+ return true;
+ }
+ break;
+
+ case FindBoth:
+ if (mShiftKey && mDeadKey) {
+ return true;
+ }
+ break;
+ }
+
+
+ return false;
+ }
+ };
}
M::InputMethodMode MImAbstractKeyArea::InputMethodMode;
@@ -78,16 +150,14 @@
bool usePopup,
QGraphicsWidget *parent)
: MStylableWidget(parent),
- mRelativeButtonBaseWidth(0),
+ mRelativeKeyBaseWidth(0),
debugTouchPoints(style()->debugTouchPoints()),
currentLevel(0),
mPopup(usePopup ? PopupFactory::instance()->createPopup(this) : 0),
wasGestureTriggered(false),
enableMultiTouch(MGConfItem(MultitouchSettings).value().toBool()),
- activeKey(0),
- activeDeadkey(0),
- activeShiftKey(0),
- feedbackPlayer(0),
+ feedbackPress(MFeedback::Press),
+ feedbackCancel(MFeedback::Cancel),
section(newSection)
{
// By default multi-touch is disabled
@@ -97,7 +167,6 @@
lastTouchPointPressEvent.restart();
grabGesture(FlickGestureRecognizer::sharedGestureType());
- feedbackPlayer = MComponentData::feedbackPlayer();
longPressTimer.setSingleShot(true);
idleVkbTimer.setSingleShot(true);
@@ -123,9 +192,9 @@
InputMethodMode = inputMethodMode;
}
-qreal MImAbstractKeyArea::relativeButtonBaseWidth() const
+qreal MImAbstractKeyArea::relativeKeyBaseWidth() const
{
- return mRelativeButtonBaseWidth;
+ return mRelativeKeyBaseWidth;
}
const LayoutData::SharedLayoutSection &MImAbstractKeyArea::sectionModel() const
@@ -149,9 +218,12 @@
// screen position, so we can use mapToScene to calculate screen position:
const QPoint pos = mapToScene(buttonRect.topLeft()).toPoint();
+ SpecialKeyFinder finder(SpecialKeyFinder::FindDeadKey);
+ MImAbstractKey::visitActiveKeys(&finder);
+
mPopup->updatePos(buttonRect.topLeft(), pos, buttonRect.toRect().size());
mPopup->handleKeyPressedOnMainArea(key,
- activeDeadkey ? activeDeadkey->label() : QString(),
+ (finder.deadKey() ? finder.deadKey()->label() : QString()),
level() % 2);
}
@@ -169,11 +241,14 @@
MImAbstractKeyArea::handleVisibilityChanged(bool visible)
{
if (mPopup) {
- mPopup->setEnabled(visible);
+ mPopup->setVisible(visible);
}
if (!visible) {
- unlockDeadkeys();
+ SpecialKeyFinder finder(SpecialKeyFinder::FindDeadKey);
+ MImAbstractKey::visitActiveKeys(&finder);
+
+ unlockDeadKeys(finder.deadKey());
}
}
@@ -184,7 +259,10 @@
currentLevel = level;
// Update uppercase / lowercase
- updateButtonModifiers();
+ SpecialKeyFinder finder(SpecialKeyFinder::FindDeadKey);
+ MImAbstractKey::visitActiveKeys(&finder);
+
+ updateKeyModifiers(finder.deadKey() ? finder.deadKey()->label().at(0) : '\0');
update();
}
@@ -205,7 +283,7 @@
const int newWidth = static_cast<int>(event->newSize().width());
if (newWidth != static_cast<int>(event->oldSize().width())) {
- updateButtonGeometriesForWidth(newWidth);
+ updateKeyGeometries(newWidth);
}
}
@@ -253,32 +331,35 @@
gLastMousePos = ev->pos();
}
-void MImAbstractKeyArea::click(MImAbstractKey *key, const QPoint &touchPoint)
+void MImAbstractKeyArea::click(MImAbstractKey *key,
+ const QPoint &pos)
{
if (!key) {
return;
}
+ SpecialKeyFinder finder;
+ MImAbstractKey::visitActiveKeys(&finder);
+ const bool hasActiveShiftKeys = (finder.shiftKey() != 0);
+
if (!key->isDeadKey()) {
- const QString accent = (activeDeadkey ? activeDeadkey->label() : QString());
- emit keyClicked(key, accent, activeShiftKey || level() % 2, touchPoint);
- unlockDeadkeys();
- } else if (key == activeDeadkey) {
- unlockDeadkeys();
+ const QString accent = (finder.deadKey() ? finder.deadKey()->label() : QString());
+ emit keyClicked(key, accent, hasActiveShiftKeys || level() % 2, pos);
+
+ if (!key->isShiftKey()) {
+ unlockDeadKeys(finder.deadKey());
+ }
+ } else if (key == finder.deadKey()) {
+ unlockDeadKeys(finder.deadKey());
} else {
- // Deselect previous dead key, if any
- if (activeDeadkey) {
- activeDeadkey->setSelected(false);
+ // Deselect previous dead key, if any:
+ if (finder.deadKey()) {
+ finder.deadKey()->setSelected(false);
}
- activeDeadkey = key;
- activeDeadkey->setSelected(true);
-
- updateButtonModifiers();
- }
-
- if (key == activeShiftKey) {
- activeShiftKey = 0;
+ // key is the new deadkey:
+ key->setSelected(true);
+ updateKeyModifiers(key->label().at(0));
}
}
@@ -322,12 +403,15 @@
}
longPressTimer.stop();
- activeKey = 0;
}
bool MImAbstractKeyArea::event(QEvent *ev)
{
bool eaten = false;
+ QString start, end;
+ start = QString("%1|start").arg(ev->type());
+ end = QString("%1|end").arg(ev->type());
+ mTimestamp("MImAbstractKeyArea", start);
if (ev->type() == QEvent::Gesture) {
const Qt::GestureType flickGestureType = FlickGestureRecognizer::sharedGestureType();
@@ -364,7 +448,10 @@
eaten = true;
}
- return eaten || MWidget::event(ev);
+ const bool result = eaten || MWidget::event(ev);
+
+ mTimestamp("MImAbstractKeyArea", end);
+ return result;
}
void MImAbstractKeyArea::handleFlickGesture(FlickGesture *gesture)
@@ -380,11 +467,10 @@
mPopup->cancel();
}
- if (activeKey) {
- activeKey->resetTouchPointCount();
+ if (MImAbstractKey *key = MImAbstractKey::lastActiveKey()) {
+ key->resetTouchPointCount();
}
- activeKey = 0;
longPressTimer.stop();
wasGestureTriggered = true;
}
@@ -420,6 +506,7 @@
void MImAbstractKeyArea::touchPointPressed(const QTouchEvent::TouchPoint &tp)
{
+ mTimestamp("MImAbstractKeyArea", "start");
wasGestureTriggered = false;
// Gestures only slow down in speed typing mode:
@@ -438,36 +525,37 @@
if (!key) {
longPressTimer.stop();
+ mTimestamp("MImAbstractKeyArea", "end");
return;
}
// Try to commit currently active key before activating new key:
- if (activeKey
- && activeKey->isNormalKey()
- && activeKey->touchPointCount() > 0) {
+ MImAbstractKey *const lastActiveKey = MImAbstractKey::lastActiveKey();
+ SpecialKeyFinder finder;
+ MImAbstractKey::visitActiveKeys(&finder);
+ const bool hasActiveShiftKeys = (finder.shiftKey() != 0);
+
+ if (lastActiveKey
+ && lastActiveKey->isNormalKey()
+ && lastActiveKey->touchPointCount() > 0) {
// TODO: play release sound? Potentially confusing to user, who
// might still press this key.
- emit keyClicked(activeKey, QString(), activeShiftKey || level() % 2,
+ emit keyClicked(lastActiveKey, QString(),
+ hasActiveShiftKeys || level() % 2,
gAdjustedPositionForCorrection);
- activeKey->resetTouchPointCount();
- activeKey = 0;
+ lastActiveKey->resetTouchPointCount();
}
if (key->increaseTouchPointCount()
&& key->touchPointCount() == 1) {
-
- if (key->isShiftKey()) {
- activeShiftKey = key;
- } else {
- activeKey = key;
- }
-
updatePopup(key);
longPressTimer.start(style()->longPressTimeout());
- emit keyPressed(key, (activeDeadkey ? activeDeadkey->label() : QString()),
- activeShiftKey || level() % 2);
+
+ emit keyPressed(key, (finder.deadKey() ? finder.deadKey()->label() : QString()),
+ hasActiveShiftKeys || level() % 2);
}
+ mTimestamp("MImAbstractKeyArea", "end");
}
void MImAbstractKeyArea::touchPointMoved(const QTouchEvent::TouchPoint &tp)
@@ -477,25 +565,38 @@
return;
}
+ if (tp.scenePos() == tp.lastScenePos()) {
+ return;
+ }
+
+ mTimestamp("MImAbstractKeyArea", "start");
+
const QPoint pos = mapFromScene(tp.scenePos()).toPoint();
const QPoint lastPos = mapFromScene(tp.lastScenePos()).toPoint();
const QPoint startPos = mapFromScene(tp.startScenePos()).toPoint();
const GravitationalLookupResult lookup = gravitationalKeyAt(pos, lastPos, startPos);
+ SpecialKeyFinder finder;
+ MImAbstractKey::visitActiveKeys(&finder);
+ const bool hasActiveShiftKeys = (finder.shiftKey() != 0);
// For a moving touchpoint, we only need to consider enter-key or leave-key events:
if (lookup.key != lookup.lastKey) {
- if (lookup.key
- && lookup.key->increaseTouchPointCount()
- && lookup.key->touchPointCount() == 1) {
- activeKey = lookup.key;
- // Reaction map cannot discover when we move from one key
- // (= reactive area) to another
- feedbackPlayer->play(MFeedbackPlayer::Press);
- longPressTimer.start(style()->longPressTimeout());
- emit keyPressed(lookup.key, (activeDeadkey ? activeDeadkey->label() : QString()),
- activeShiftKey || level() % 2);
+ if (lookup.key) {
+ updatePopup(lookup.key);
+
+ if (lookup.key->increaseTouchPointCount()
+ && lookup.key->touchPointCount() == 1) {
+ // Reaction map cannot discover when we move from one key
+ // (= reactive area) to another
+ // slot is called asynchronously to get screen update as fast as possible
+ QMetaObject::invokeMethod(&feedbackPress, "play", Qt::QueuedConnection);
+ longPressTimer.start(style()->longPressTimeout());
+ emit keyPressed(lookup.key,
+ (finder.deadKey() ? finder.deadKey()->label() : QString()),
+ hasActiveShiftKeys || level() % 2);
+ }
}
if (lookup.lastKey
@@ -503,21 +604,22 @@
&& lookup.lastKey->touchPointCount() == 0) {
// Reaction map cannot discover when we move from one key
// (= reactive area) to another
- feedbackPlayer->play(MFeedbackPlayer::Cancel);
- emit keyReleased(lookup.lastKey, (activeDeadkey ? activeDeadkey->label() : QString()),
- activeShiftKey || level() % 2);
+ // slot is called asynchronously to get screen update as fast as possible
+ QMetaObject::invokeMethod(&feedbackCancel, "play", Qt::QueuedConnection);
+ emit keyReleased(lookup.lastKey,
+ (finder.deadKey() ? finder.deadKey()->label() : QString()),
+ hasActiveShiftKeys || level() % 2);
}
}
if (!lookup.key) {
longPressTimer.stop();
- } else {
- updatePopup(lookup.key);
}
if (debugTouchPoints) {
printTouchPoint(tp, lookup.key, lookup.lastKey);
}
+ mTimestamp("MImAbstractKeyArea", "end");
}
void MImAbstractKeyArea::touchPointReleased(const QTouchEvent::TouchPoint &tp)
@@ -525,6 +627,7 @@
if (wasGestureTriggered) {
return;
}
+ mTimestamp("MImAbstractKeyArea", "start");
idleVkbTimer.start(style()->idleVkbTimeout());
@@ -533,22 +636,17 @@
const QPoint startPos = mapFromScene(tp.startScenePos()).toPoint();
const GravitationalLookupResult lookup = gravitationalKeyAt(pos, lastPos, startPos);
+ SpecialKeyFinder finder;
+ MImAbstractKey::visitActiveKeys(&finder);
+ const bool hasActiveShiftKeys = (finder.shiftKey() != 0);
if (lookup.key
&& lookup.key->decreaseTouchPointCount()
&& lookup.key->touchPointCount() == 0) {
-
- if (lookup.key == activeKey) {
- activeKey = 0;
- }
-
longPressTimer.stop();
- emit keyReleased(lookup.key, (activeDeadkey ? activeDeadkey->label() : QString()),
- activeShiftKey || level() % 2);
-
- if (lookup.key == activeShiftKey) {
- activeShiftKey = 0;
- }
+ emit keyReleased(lookup.key,
+ (finder.deadKey() ? finder.deadKey()->label() : QString()),
+ hasActiveShiftKeys || level() % 2);
click(lookup.key, gAdjustedPositionForCorrection);
}
@@ -557,12 +655,9 @@
&& lookup.lastKey != lookup.key
&& lookup.lastKey->decreaseTouchPointCount()
&& lookup.lastKey->touchPointCount() == 0) {
- emit keyReleased(lookup.lastKey, (activeDeadkey ? activeDeadkey->label() : QString()),
- activeShiftKey || level() % 2);
-
- if (lookup.lastKey == activeShiftKey) {
- activeShiftKey = 0;
- }
+ emit keyReleased(lookup.lastKey,
+ (finder.deadKey() ? finder.deadKey()->label() : QString()),
+ hasActiveShiftKeys || level() % 2);
}
// We're finished with this touch point, inform popup:
@@ -575,12 +670,13 @@
if (debugTouchPoints) {
printTouchPoint(tp, lookup.key, lookup.lastKey);
}
+ mTimestamp("MImAbstractKeyArea", "end");
}
QTouchEvent::TouchPoint MImAbstractKeyArea::createTouchPoint(int id,
- Qt::TouchPointState state,
- const QPointF &pos,
- const QPointF &lastPos)
+ Qt::TouchPointState state,
+ const QPointF &pos,
+ const QPointF &lastPos)
{
QTouchEvent::TouchPoint tp(id);
tp.setState(state);
@@ -625,17 +721,28 @@
return GravitationalLookupResult(key, lastKey);
}
-void
-MImAbstractKeyArea::unlockDeadkeys()
+void MImAbstractKeyArea::unlockDeadKeys(MImAbstractKey *deadKey)
+{
+ if (!deadKey || !deadKey->isDeadKey()) {
+ return;
+ }
+
+ deadKey->setSelected(false);
+ deadKey->resetTouchPointCount();
+ updateKeyModifiers('\0');
+}
+
+void MImAbstractKeyArea::hidePopup()
{
- if (activeDeadkey) {
- activeDeadkey->setSelected(false);
- activeDeadkey = 0;
- updateButtonModifiers();
+ if (!mPopup) {
+ return;
}
+
+ mPopup->setVisible(false);
}
-void MImAbstractKeyArea::drawReactiveAreas(MReactionMap */*reactionMap*/, QGraphicsView */*view*/)
+void MImAbstractKeyArea::drawReactiveAreas(MReactionMap *,
+ QGraphicsView *)
{
// Empty default implementation. Geometries of buttons are known by derived classes.
}
@@ -663,26 +770,25 @@
: QString()) << ")";
}
-void MImAbstractKeyArea::updateButtonModifiers()
+void MImAbstractKeyArea::updateKeyModifiers(const QChar &accent)
{
- bool shift = (currentLevel == 1);
-
// We currently don't allow active dead key level changing. If we did,
- // we should update activeDeadkey level before delivering its accent to
+ // we should update activeDeadKey level before delivering its accent to
// other keys.
- const QChar accent(activeDeadkey ? activeDeadkey->label().at(0) : '\0');
-
+ bool shift = (currentLevel == 1);
modifiersChanged(shift, accent);
}
-void MImAbstractKeyArea::modifiersChanged(bool /*shift*/, const QChar /*accent*/)
+void MImAbstractKeyArea::modifiersChanged(bool,
+ const QChar &)
{
// Empty default implementation
}
void MImAbstractKeyArea::onThemeChangeCompleted()
{
- updateButtonGeometriesForWidth(size().width());
+ // TODO: update all other CSS attributes that are mapped to members.
+ updateKeyGeometries(size().width());
}
const MImAbstractKeyAreaStyleContainer &MImAbstractKeyArea::baseStyle() const
@@ -692,18 +798,23 @@
void MImAbstractKeyArea::handleLongKeyPressed()
{
- if (!activeKey) {
+ MImAbstractKey *const lastActiveKey = MImAbstractKey::lastActiveKey();
+
+ if (!lastActiveKey) {
return;
}
- const QString accent = (activeDeadkey ? activeDeadkey->label()
- : QString());
+ SpecialKeyFinder finder(SpecialKeyFinder::FindDeadKey);
+ MImAbstractKey::visitActiveKeys(&finder);
+
+ const QString accent = (finder.deadKey() ? finder.deadKey()->label()
+ : QString());
if (mPopup) {
- mPopup->handleLongKeyPressedOnMainArea(activeKey, accent, level() % 2);
+ mPopup->handleLongKeyPressedOnMainArea(lastActiveKey, accent, level() % 2);
}
- emit longKeyPressed(activeKey, accent, level() % 2);
+ emit longKeyPressed(lastActiveKey, accent, level() % 2);
}
void MImAbstractKeyArea::handleIdleVkb()
--- m-keyboard/widgets/mimabstractkeyarea.h
+++ m-keyboard/widgets/mimabstractkeyarea.h
@@ -14,8 +14,6 @@
* of this file.
*/
-
-
#ifndef MIMABSTRACTKEYAREA_H
#define MIMABSTRACTKEYAREA_H
@@ -27,6 +25,7 @@
#include "mkeyboardcommon.h"
#include <MStylableWidget>
+#include <MFeedback>
#include <QColor>
#include <QHash>
#include <QList>
@@ -36,7 +35,6 @@
#include <QTime>
class FlickGesture;
-class MFeedbackPlayer;
class MReactionMap;
class MScalableImage;
class MVirtualKeyboardStyleContainer;
@@ -46,10 +44,7 @@
class PopupHost;
class PopupBase;
-/*!
- * \class MImAbstractKeyArea
- * \brief MImAbstractKeyArea is a view for virtual keyboard layout represented by LayoutModel
- */
+//! \brief MImAbstractKeyArea is a view for virtual keyboard layout represented by LayoutModel
class MImAbstractKeyArea
: public MStylableWidget
{
@@ -57,12 +52,10 @@
Q_DISABLE_COPY(MImAbstractKeyArea)
public:
- /*!
- * \brief Constructor
- * \param section A section that this MImAbstractKeyArea visualizes.
- * \param usePopup Sets whether popup should be used when long press occurs.
- * \param parent The widget's parent.
- */
+ //! \brief Constructor
+ //! \param section section that is shown by this key area
+ //! \param usePopup whether popup should be used
+ //! \param parent key area's parent
explicit MImAbstractKeyArea(const LayoutData::SharedLayoutSection §ion,
bool usePopup = false,
QGraphicsWidget *parent = 0);
@@ -70,106 +63,127 @@
//! \brief Destructor
virtual ~MImAbstractKeyArea();
- //! \return layout model
+ //! \brief Returns section shown by this key area.
const LayoutData::SharedLayoutSection §ionModel() const;
- //! Returns current level of this layout.
+ //! \brief Returns current level of this layout.
int level() const;
- //! Expose style used by MImAbstractKeyArea.
+ //! \brief Exposes style used by this key area.
const MImAbstractKeyAreaStyleContainer &baseStyle() const;
- //! Set input method mode for all MImAbstractKeyArea instances
+ //! \brief Sets input method mode for all MImAbstractKeyArea instances.
+ //! \param inputMethodMode the new input method mode
static void setInputMethodMode(M::InputMethodMode inputMethodMode);
- //! Returns relative button base width
- qreal relativeButtonBaseWidth() const;
+ //! \brief Returns relative button base width
+ qreal relativeKeyBaseWidth() const;
+
+ //! \brief Returns all keys from this key area.
+ virtual QList<const MImAbstractKey *> keys() const = 0;
- //! Returns all keys.
- virtual QList<const MImAbstractKey *> keys() = 0;
+ //! \brief Notification for derived classes about button modifier change.
+ //!
+ //! Derived classes should not change the level of selected dead keys. This is to
+ //! ensure all dead keys can be used with all characters in every level.
+ //! \param shift whether shift modifier is enabled
+ //! \param accent which accented version should be used, for a key
+ virtual void modifiersChanged(bool shift,
+ const QChar &accent = QChar());
public slots:
- /*!
- * This slot is used to switch levels
- */
+ //! \brief Tell key area to switch levels for all keys.
+ //! \param level the new level
void switchLevel(int level);
- virtual void setShiftState(ModifierState newShiftState);
-
- virtual void drawReactiveAreas(MReactionMap *reactionMap, QGraphicsView *view);
+ //! \brief Set shift state.
+ //! \param shiftState the new shift state
+ virtual void setShiftState(ModifierState shiftState);
+
+ //! \brief Draw reactive areas for all keys.
+ //! \param reactionMap the reaction map to draw onto
+ //! \param view the view to be used
+ virtual void drawReactiveAreas(MReactionMap *reactionMap,
+ QGraphicsView *view);
+
+ //! \brief Unlock all locked dead keys.
+ //! \param deadKey the corresponding dead key
+ void unlockDeadKeys(MImAbstractKey *deadKey);
- /*!
- * \brief unlock all locked deadkeys
- */
- void unlockDeadkeys();
+ //! \brief Hide popup
+ void hidePopup();
signals:
//! \brief Emitted when the covered region changed
//! \param region The changed region
void regionUpdated(const QRegion ®ion);
- /*!
- * \brief Emitted when key is pressed
- * Note that this happens also when user keeps finger down/mouse
- * button pressed and moves over another key (event is about the new key)
- * \param key describes pressed button
- * \param accent label of pressed dead key if any
- * \param upperCase contains true if key is in uppercase state
- */
- void keyPressed(const MImAbstractKey *key, const QString &accent, bool upperCase);
-
- /*!
- * \brief Emitted when key is released
- * Note that this happens also when user keeps finger down/mouse
- * button pressed and moves over another key (event is about the old key)
- * \param key describes released button
- * \param accent label of pressed dead key if any
- * \param upperCase contains true if key is in uppercase state
- */
- void keyReleased(const MImAbstractKey *key, const QString &accent, bool upperCase);
-
- /*!
- * \brief Emitted when user releases mouse button/lifts finger
- * Except when done on a dead key
- * \param key describes clicked button
- * \param accent label of pressed dead key if any
- * \param upperCase contains true if key is in uppercase state
- * \param touchPoint the touch point for the key
- */
- void keyClicked(const MImAbstractKey *key, const QString &accent, bool upperCase, const QPoint &touchPoint);
-
- /*!
- * \brief Emitted when long press is detected.
- * Long press detection is:
- * - cancelled when latest pressed key is released;
- * - restarted when finger is moved to other key;
- * - restarted when new touch point is recognized by MImAbstractKeyArea.
- *
- * \param key describes pressed button.
- * \param accent Active accent in MImAbstractKeyArea at the time of long press occured.
- * \param upperCase Upper case state in MImAbstractKeyArea at the time of long press occured.
- */
- void longKeyPressed(const MImAbstractKey *key, const QString &accent, bool upperCase);
+ //! \brief Emitted when key is pressed
+ //!
+ //! Note that this happens also when user keeps finger down/mouse
+ //! button pressed and moves over another key (event is about the new key)
+ //! \param key describes pressed button
+ //! \param accent label of pressed dead key if any
+ //! \param upperCase contains true if key is in uppercase state
+ void keyPressed(const MImAbstractKey *key,
+ QString accent,
+ bool upperCase);
+
+ //! \brief Emitted when key is released.
+ //!
+ //! Note that this happens also when user keeps finger down/mouse
+ //! button pressed and moves over another key (event is about the old key)
+ //! \param key describes released button
+ //! \param accent label of pressed dead key if any
+ //! \param upperCase contains true if key is in uppercase state
+ void keyReleased(const MImAbstractKey *key,
+ QString accent,
+ bool upperCase);
+
+ //! \brief Emitted when user releases mouse button/lifts finger.
+ //!
+ //! Except when done on a dead key
+ //! \param key describes clicked button
+ //! \param accent label of pressed dead key if any
+ //! \param upperCase contains true if key is in uppercase state
+ //! \param touchPoint the touch point for the key
+ void keyClicked(const MImAbstractKey *key,
+ QString accent,
+ bool upperCase,
+ QPoint touchPoint);
+
+ //! \brief Emitted when long press is detected.
+ //!
+ //! Long press detection is:
+ //! - cancelled when latest pressed key is released;
+ //! - restarted when finger is moved to other key;
+ //! - restarted when new touch point is recognized by MImAbstractKeyArea.
+ //! \param key describes pressed button
+ //! \param accent active accent in MImAbstractKeyArea at the time of long press occured
+ //! \param upperCase upper case state in MImAbstractKeyArea at the time of long press occured
+ void longKeyPressed(const MImAbstractKey *key,
+ QString accent,
+ bool upperCase);
- //! Emitted when flicked right
+ //! \brief Emitted when key area is flicked right.
void flickRight();
- //! Emitted when flicked left
+ //! \brief Emitted when key area is flicked left.
void flickLeft();
- //! Emitted when flicked down
+ //! \brief Emitted when key area is flicked down.
void flickDown();
- //! \brief Emitted when flicked up
+ //! \brief Emitted when key area is flicked up.
//! \param binding Information about the key where mouse button was pressed
void flickUp(const MImKeyBinding &binding);
//! \brief Emitted if button width has changed
//! \param baseWidth base width used for relative button widths
- void relativeButtonBaseWidthChanged(qreal baseWidth);
+ void relativeKeyBaseWidthChanged(qreal baseWidth);
protected:
- /*! \reimp */
+ //! \reimp
virtual void resizeEvent(QGraphicsSceneResizeEvent *event);
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
@@ -180,61 +194,54 @@
virtual void grabMouseEvent(QEvent *event);
virtual void ungrabMouseEvent(QEvent *event);
virtual bool event(QEvent *event);
- /*! \reimp_end */
+ //! \reimp_end
- //! Called when widget is about to lose visibility.
+ //! \brief Called when key area's visibility changed.
+ //! \param visible the new visbility status
virtual void handleVisibilityChanged(bool visible);
+ //! \brief Invalidates the current background cache.
+ virtual void invalidateBackgroundCache() = 0;
+
//! Shows popup and updates its content and position.
//! \param key current key
void updatePopup(MImAbstractKey *key = 0);
- /*!
- * \brief Get level count of the virtual keyboard.
- * \return int. The level count.
- */
+ //! \brief Get maximum number of columns in this key area.
int maxColumns() const;
- /*!
- * \brief Get row count of the virtual keyboard, in current level.
- * \return int. The row count.
- */
+ //! \brief Get number of rows in this key area.
int rowCount() const;
- //! \brief Updates button labels and/or icons according to current level and deadkey.
- void updateButtonModifiers();
-
- /*! \brief Notification for derived classes about button modifier change.
- *
- * Derived classes should not change the level of selected dead keys. This is to
- * ensure all dead keys can be used with all characters in every level.
- */
- virtual void modifiersChanged(bool shift, QChar accent = QChar());
+ //! \brief Updates button labels and/or icons according to current level
+ //! and deadkey.
+ //! \param accent the accent version of a dead key.
+ void updateKeyModifiers(const QChar &accent = QChar());
//! \brief Returns key at given \a pos.
//!
//! Accepts positions outside widget geometry because
//! of reactive margins.
+ //! \param pos the position (in key area space) to look up key
virtual MImAbstractKey *keyAt(const QPoint &pos) const = 0;
- /*! \brief Calculates button and row geometry based on given \a availableWidth.
- * \post Button rectangle cache and row width cache are up to date.
- */
- virtual void updateButtonGeometriesForWidth(int availableWidth) = 0;
+ //! \brief Updates key (and row) geometry based on given \a availableWidth.
+ //! \param availableWidth with of the key area
+ virtual void updateKeyGeometries(int availableWidth) = 0;
+ //! \brief Returns popup
const PopupBase &popup() const;
+ //! \brief Print touch point information, for debugging purposes.
+ //! \param tp the touch point
+ //! \param key key underneath touch point
+ //! \param lastKey last key that was associated to the touch point
void printTouchPoint(const QTouchEvent::TouchPoint &tp,
const MImAbstractKey *key,
const MImAbstractKey *lastKey = 0) const;
- //! Sets button state and sends release & press events.
- //void setActiveKey(MImAbstractKey *key, TouchPointInfo &tpi);
-
- //! Relative button base width in currently active layout
- qreal mRelativeButtonBaseWidth;
-
- bool debugTouchPoints;
+ qreal mRelativeKeyBaseWidth; //!< Relative key base width in currently active layout
+ bool debugTouchPoints; //!< Whether touch point debugging is enabled
protected slots:
//! Update background images, text layouts, etc. when the theme changed.
@@ -248,6 +255,7 @@
private:
//! \brief Handler for flick gestures from Qt gesture framework.
+ //! \param gesture the flick gesture
void handleFlickGesture(FlickGesture *gesture);
//! \brief Touch point press handler.
@@ -262,10 +270,17 @@
//! \param tp The unprocessed Qt touchpoint.
void touchPointReleased(const QTouchEvent::TouchPoint &tp);
+ //! \brief Helper method to create touch points
+ //! \param id touch point id
+ //! \param state touch point state
+ //! \param pos touch point scene position
+ //! \param lastPos last touch point scene position
static QTouchEvent::TouchPoint createTouchPoint(int id,
Qt::TouchPointState state,
const QPointF &pos,
const QPointF &lastPos);
+
+ //! \brief Helper struct to store results of \a gravitationalKeyAt
struct GravitationalLookupResult
{
GravitationalLookupResult(MImAbstractKey *newKey,
@@ -290,56 +305,29 @@
const QPoint &mappedLastPos,
const QPoint &mappedStartPos) const;
- void click(MImAbstractKey *key, const QPoint &touchPoint = QPoint());
+ //! \brief Trigger a keyClicked signal, and update key area state.
+ //! \param key the clicked key
+ //! \param pos where the key was clicked
+ void click(MImAbstractKey *key,
+ const QPoint &pos = QPoint());
- //! Checks for speed typing mode
+ //! \brief Checks for speed typing mode
//! \warning Not side-effect free when \a restartTimers is actively used.
bool isInSpeedTypingMode(bool restartTimers = false);
- //! Current level
- int currentLevel;
-
- //! Popup to show additional information for a button
- PopupBase *mPopup;
-
- //! List of punctuation labels
- QList<QStringList> punctuationsLabels;
-
- //! List of accent labels
- QList<QStringList> accentLabels;
-
- //! Whether a gesture was already triggered for any active touch point.
- bool wasGestureTriggered;
- bool enableMultiTouch;
-
- //! Active key, there can only be one at a time.
- MImAbstractKey *activeKey;
-
- //! Activated dead key
- MImAbstractKey *activeDeadkey;
-
- //! Activated shift key
- MImAbstractKey *activeShiftKey;
-
- /*!
- * Feedback player instance
- */
- MFeedbackPlayer *feedbackPlayer;
-
- //! layout section viewed by this class
- const LayoutData::SharedLayoutSection section;
-
- static M::InputMethodMode InputMethodMode;
-
- //! This timer is used to recognize long press.
- QTimer longPressTimer;
-
-
- //! Whenever the VKB idles, gestures are activated.
- QTimer idleVkbTimer;
-
- //! Used to measure elapsed time between two touchpoint press events:
- QTime lastTouchPointPressEvent;
+ int currentLevel; //!< current level
+ PopupBase *mPopup; //!< popup to show additional information for a button
+ QList<QStringList> punctuationsLabels; //!< list of punctuation labels
+ QList<QStringList> accentLabels; //!< list of accent labels
+ bool wasGestureTriggered; //!< whether a gesture was already triggered for any active touch point
+ bool enableMultiTouch; //!< whether this key area operates in multitouch mode
+ MFeedback feedbackPress; //!< Press feedback
+ MFeedback feedbackCancel; //!< Cancel feedback
+ const LayoutData::SharedLayoutSection section; //!< layout section shown by this key area
+ static M::InputMethodMode InputMethodMode; //!< used input method mode (same for all key areas)
+ QTimer longPressTimer; //!< used to recognize long press
+ QTimer idleVkbTimer; //!< Whenever this key area of the VKB idles, gestures are activated.
+ QTime lastTouchPointPressEvent; //!< measures elapsed time between two touchpoint press events
M_STYLABLE_WIDGET(MImAbstractKeyAreaStyle)
--- m-keyboard/widgets/mimcorrectioncandidatecontainerstyle.h
+++ m-keyboard/widgets/mimcorrectioncandidatecontainerstyle.h
-/* * This file is part of meego-keyboard *
- *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
- * All rights reserved.
- * Contact: Nokia Corporation (directui at nokia.com)
- *
- * If you have questions regarding the use of this file, please contact
- * Nokia at directui at nokia.com.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * and appearing in the file LICENSE.LGPL included in the packaging
- * of this file.
- */
-
-#ifndef MIMCORRECTIONCANDIDATECONTAINERSTYLE_H
-#define MIMCORRECTIONCANDIDATECONTAINERSTYLE_H
-
-#include <MWidgetStyle>
-
-class MImCorrectionCandidateContainerStyle : public MWidgetStyle
-{
- Q_OBJECT
- M_STYLE(MImCorrectionCandidateContainerStyle)
-};
-
-class M_EXPORT MImCorrectionCandidateContainerStyleContainer : public MWidgetStyleContainer
-{
- M_STYLE_CONTAINER(MImCorrectionCandidateContainerStyle)
-};
-
-#endif
-
--- m-keyboard/widgets/mimcorrectioncandidateitem.cpp
+++ m-keyboard/widgets/mimcorrectioncandidateitem.cpp
@@ -15,15 +15,237 @@
*/
#include "mimcorrectioncandidateitem.h"
-#include "mimcorrectioncandidateitemview.h"
+#include <QTimer>
+#include <QFontMetricsF>
+#include <QGraphicsSceneMouseEvent>
+#include <QTapAndHoldGesture>
+#include <QDebug>
-MImCorrectionCandidateItem::MImCorrectionCandidateItem(MContentItem::ContentItemStyle itemStyle, QGraphicsItem *parent)
- : MContentItem(itemStyle, parent)
+#include <MTheme>
+
+namespace {
+ const int DefaultPressTimeout = 250;
+ const int DefaultReleaseMissDelta = 30;
+ const int DefaultLongTapTimeout = 600;
+}
+
+#include <mwidgetcreator.h>
+M_REGISTER_WIDGET_NO_CREATE(MImCorrectionCandidateItem)
+
+MImCorrectionCandidateItem::MImCorrectionCandidateItem(const QString &title, QGraphicsItem *parent)
+ : MStylableWidget(parent),
+ mSelected(false),
+ mDown(false),
+ mTitle(title),
+ styleModeChangeTimer(),
+ longTapTimer(),
+ queuedStyleModeChange(false)
+
{
- setView(new MImCorrectionCandidateItemView(this));
+ styleModeChangeTimer.setSingleShot(true);
+ connect(&styleModeChangeTimer, SIGNAL(timeout()), SLOT(applyQueuedStyleModeChange()));
+
+ connect(this, SIGNAL(visibleChanged()),
+ this, SLOT(handleVisibilityChanged()));
+
+ setupLongTapTimer();
+ connect(MTheme::instance(), SIGNAL(themeChangeCompleted()),
+ this, SLOT(onThemeChangeCompleted()),
+ Qt::UniqueConnection);
}
MImCorrectionCandidateItem::~MImCorrectionCandidateItem()
{
}
+void MImCorrectionCandidateItem::setTitle(const QString &string)
+{
+ if (mTitle != string) {
+ mTitle = string;
+ update();
+ }
+}
+
+QString MImCorrectionCandidateItem::title() const
+{
+ return mTitle;
+}
+
+void MImCorrectionCandidateItem::setSelected(bool select)
+{
+ mSelected = select;
+ if (mSelected)
+ style().setModeSelected();
+ else
+ style().setModeDefault();
+}
+
+bool MImCorrectionCandidateItem::isSelected() const
+{
+ return mSelected;
+}
+
+void MImCorrectionCandidateItem::click()
+{
+ qDebug() << __PRETTY_FUNCTION__;
+ emit clicked();
+}
+
+void MImCorrectionCandidateItem::longTap()
+{
+ qDebug() << __PRETTY_FUNCTION__;
+ // Clear down state and update style.
+ if (mDown) {
+ mDown = false;
+ updateStyleMode();
+ }
+
+ emit longTapped();
+}
+
+void MImCorrectionCandidateItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ event->accept();
+ if (mDown)
+ return;
+
+ style()->pressFeedback().play();
+ mDown = true;
+ updateStyleMode();
+ longTapTimer.start();
+}
+
+void MImCorrectionCandidateItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ event->accept();
+ if (!mDown) {
+ return;
+ }
+ longTapTimer.stop();
+
+ mDown = false;
+ updateStyleMode();
+
+ QPointF touch = event->scenePos();
+ QRectF rect = sceneBoundingRect();
+ int releaseMissDelta = style()->releaseMissDelta();
+ releaseMissDelta = (releaseMissDelta > 0) ? releaseMissDelta : DefaultReleaseMissDelta;
+ rect.adjust(-releaseMissDelta, -releaseMissDelta,
+ releaseMissDelta, releaseMissDelta);
+ bool pressed = rect.contains(touch);
+
+ if (pressed) {
+ style()->releaseFeedback().play();
+ click();
+ }
+}
+
+void MImCorrectionCandidateItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ event->accept();
+
+ QPointF touch = event->scenePos();
+ QRectF rect = sceneBoundingRect();
+ int releaseMissDelta = style()->releaseMissDelta();
+ releaseMissDelta = (releaseMissDelta > 0) ? releaseMissDelta : DefaultReleaseMissDelta;
+ rect.adjust(-releaseMissDelta, -releaseMissDelta,
+ releaseMissDelta, releaseMissDelta);
+ bool pressed = rect.contains(touch);
+
+ if (pressed != mDown) {
+ longTapTimer.stop();
+ if (pressed) {
+ style()->pressFeedback().play();
+ } else {
+ style()->cancelFeedback().play();
+ }
+ mDown = pressed;
+ updateStyleMode();
+ }
+}
+
+
+void MImCorrectionCandidateItem::updateStyleMode()
+{
+ if (mDown) {
+ int pressTimeout = style()->pressTimeout();
+ pressTimeout = (pressTimeout > 0) ? pressTimeout : DefaultPressTimeout;
+ if (styleModeChangeTimer.isActive()) {
+ styleModeChangeTimer.start(pressTimeout);
+ return;
+ }
+ styleModeChangeTimer.start(pressTimeout);
+ style().setModePressed();
+ } else {
+ if (isSelected()) {
+ style().setModeSelected();
+ } else {
+ if (styleModeChangeTimer.isActive()) {
+ queuedStyleModeChange = true;
+ return;
+ }
+ style().setModeDefault();
+ }
+ }
+
+ applyStyle();
+ update();
+}
+
+void MImCorrectionCandidateItem::applyQueuedStyleModeChange()
+{
+ if (queuedStyleModeChange) {
+ queuedStyleModeChange = false;
+ updateStyleMode();
+ }
+}
+
+void MImCorrectionCandidateItem::drawContents(QPainter *painter, const QStyleOptionGraphicsItem *option) const
+{
+ Q_UNUSED(option);
+ if (!mTitle.isEmpty()) {
+ painter->setFont(style()->font());
+ painter->setPen(style()->fontColor());
+ QSizeF s = size() - QSizeF(style()->marginLeft() + style()->marginRight(),
+ style()->marginTop() + style()->marginBottom());
+ painter->drawText(QRectF(0, 0, s.width(), s.height()), Qt::AlignCenter, mTitle);
+ }
+}
+
+qreal MImCorrectionCandidateItem::idealWidth() const
+{
+ qreal width = 0.0;
+ if (!mTitle.isEmpty()) {
+ QFontMetricsF fm(style()->font());
+ width = fm.width(mTitle);
+ }
+ width += style()->marginLeft() + style()->marginRight()
+ + style()->paddingRight() + style()->paddingLeft();
+ return width;
+}
+
+void MImCorrectionCandidateItem::handleVisibilityChanged()
+{
+ //clear select and down state when hidden
+ if (!isVisible()) {
+ if (mDown || mSelected) {
+ mSelected = false;
+ mDown =false;
+ updateStyleMode();
+ }
+ }
+}
+
+void MImCorrectionCandidateItem::setupLongTapTimer()
+{
+ longTapTimer.setSingleShot(true);
+ int longTapTimeout = style()->longTapTimeout();
+ longTapTimer.setInterval(longTapTimeout);
+ connect(&longTapTimer, SIGNAL(timeout()), SLOT(longTap()), Qt::UniqueConnection);
+}
+
+void MImCorrectionCandidateItem::onThemeChangeCompleted()
+{
+ // reset long tap timer
+ setupLongTapTimer();
+}
--- m-keyboard/widgets/mimcorrectioncandidateitem.h
+++ m-keyboard/widgets/mimcorrectioncandidateitem.h
@@ -16,16 +16,104 @@
#ifndef MIMCORRECTIONCANDIDATEITEM_H
#define MIMCORRECTIONCANDIDATEITEM_H
-#include <MContentItem>
-class MImCorrectionCandidateItem: public MContentItem
+#include <QTimer>
+#include <MStylableWidget>
+#include "mimcorrectioncandidateitemstyle.h"
+
+class MImCorrectionCandidateItem: public MStylableWidget
{
Q_OBJECT
Q_DISABLE_COPY(MImCorrectionCandidateItem)
+
+ Q_PROPERTY(QString title READ title WRITE setTitle)
+
public:
- explicit MImCorrectionCandidateItem(MContentItem::ContentItemStyle itemStyle = MContentItem::SingleTextLabel,
- QGraphicsItem *parent = 0);
+ explicit MImCorrectionCandidateItem(const QString &title = QString(), QGraphicsItem *parent = 0);
+
virtual ~MImCorrectionCandidateItem();
+
+ /*
+ * \brief Sets title label.
+ */
+ void setTitle(const QString &);
+
+ /*
+ * \brief Returns current title label.
+ */
+ QString title() const;
+
+ /*!
+ * \brief Returns the ideal width of the container widget.
+ *
+ * The ideal width is the actually used width of the title together with margins and paddings.
+ */
+ qreal idealWidth() const;
+
+ /*!
+ * \brief Select item.
+ */
+ void setSelected(bool);
+
+ /*
+ * \brief Returns selected state.
+ */
+ bool isSelected() const;
+
+public Q_SLOTS:
+ /*!
+ \brief Makes the list cell to send clicked() signal.
+ */
+ void click();
+
+ /*!
+ *\brief Makes the list cell to send longTapped signal.
+ *\param pos The position of the tap.
+ */
+ void longTap();
+
+protected:
+ /*! \reimp */
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ virtual void drawContents(QPainter *painter, const QStyleOptionGraphicsItem *option) const;
+ /*! \reimp_end */
+
+Q_SIGNALS:
+ /*!
+ * \brief The signal is emitted when the item is clicked.
+ */
+ void clicked();
+
+ /*!
+ * \brief The signal is emitted when the item has been tapped and holded.
+ */
+ void longTapped();
+
+private Q_SLOTS:
+ void applyQueuedStyleModeChange();
+
+ void handleVisibilityChanged();
+
+ /*!
+ * Update stored style stuffs when the theme changed.
+ */
+ void onThemeChangeCompleted();
+
+private:
+ void updateStyleMode();
+
+ void setupLongTapTimer();
+
+ bool mSelected;
+ bool mDown;
+ QString mTitle;
+ QTimer styleModeChangeTimer;
+ QTimer longTapTimer;
+ bool queuedStyleModeChange;
+
+ M_STYLABLE_WIDGET(MImCorrectionCandidateItemStyle)
};
#endif
--- m-keyboard/widgets/mimcorrectioncandidateitemstyle.h
+++ m-keyboard/widgets/mimcorrectioncandidateitemstyle.h
@@ -17,17 +17,27 @@
#ifndef MIMCORRECTIONCANDIDATEITEMSTYLE_H
#define MIMCORRECTIONCANDIDATEITEMSTYLE_H
-#include <mcontentitemstyle.h>
+#include <MWidgetStyle>
-class MImCorrectionCandidateItemStyle : public MContentItemStyle
+class MImCorrectionCandidateItemStyle : public MWidgetStyle
{
Q_OBJECT
M_STYLE(MImCorrectionCandidateItemStyle)
+
+ M_STYLE_ATTRIBUTE(QFont, font, Font)
+ M_STYLE_ATTRIBUTE(QColor, fontColor, FontColor)
+
+ M_STYLE_ATTRIBUTE(int, pressTimeout, PressTimeout)
+ M_STYLE_ATTRIBUTE(int, releaseMissDelta, ReleaseMissDelta)
+ M_STYLE_ATTRIBUTE(int, longTapTimeout, LongTapTimeout)
};
-class M_EXPORT MImCorrectionCandidateItemStyleContainer : public MContentItemStyleContainer
+class M_EXPORT MImCorrectionCandidateItemStyleContainer : public MWidgetStyleContainer
{
M_STYLE_CONTAINER(MImCorrectionCandidateItemStyle)
+
+ M_STYLE_MODE(Landscape)
+ M_STYLE_MODE(Portrait)
};
#endif
--- m-keyboard/widgets/mimcorrectioncandidateitemview.cpp
+++ m-keyboard/widgets/mimcorrectioncandidateitemview.cpp
-/* * This file is part of meego-keyboard *
- *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
- * All rights reserved.
- * Contact: Nokia Corporation (directui at nokia.com)
- *
- * If you have questions regarding the use of this file, please contact
- * Nokia at directui at nokia.com.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * and appearing in the file LICENSE.LGPL included in the packaging
- * of this file.
- */
-
-#include "mimcorrectioncandidateitemview.h"
-#include "mimcorrectioncandidateitem.h"
-
-MImCorrectionCandidateItemView::MImCorrectionCandidateItemView(MImCorrectionCandidateItem *controller)
- : MContentItemView(controller)
-{
-}
-
--- m-keyboard/widgets/mimcorrectioncandidateitemview.h
+++ m-keyboard/widgets/mimcorrectioncandidateitemview.h
-/* * This file is part of meego-keyboard *
- *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
- * All rights reserved.
- * Contact: Nokia Corporation (directui at nokia.com)
- *
- * If you have questions regarding the use of this file, please contact
- * Nokia at directui at nokia.com.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * and appearing in the file LICENSE.LGPL included in the packaging
- * of this file.
- */
-
-#ifndef MIMCORRECTIONCANDIDATEITEMVIEW_H
-#define MIMCORRECTIONCANDIDATEITEMVIEW_H
-
-#include <mcontentitemview.h>
-#include "mimcorrectioncandidateitemstyle.h"
-
-class MImCorrectionCandidateItem;
-
-class MImCorrectionCandidateItemView : public MContentItemView
-{
- Q_OBJECT
- M_VIEW(MContentItemModel, MImCorrectionCandidateItemStyle)
-
-public:
- explicit MImCorrectionCandidateItemView(MImCorrectionCandidateItem *controller);
-};
-
-#endif
-
--- m-keyboard/widgets/mimcorrectioncandidatewidget.cpp
+++ m-keyboard/widgets/mimcorrectioncandidatewidget.cpp
-/* * This file is part of meego-keyboard *
- *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
- * All rights reserved.
- * Contact: Nokia Corporation (directui at nokia.com)
- *
- * If you have questions regarding the use of this file, please contact
- * Nokia at directui at nokia.com.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * and appearing in the file LICENSE.LGPL included in the packaging
- * of this file.
- */
-
-
-
-#include "mimcorrectioncandidatewidget.h"
-#include "mimcorrectioncandidateitem.h"
-
-#include <QGraphicsSceneMouseEvent>
-#include <QDebug>
-#include <QString>
-
-#include <MSceneManager>
-#include <mreactionmap.h>
-#include <mplainwindow.h>
-#include <MWidgetRecycler>
-#include <MList>
-#include <MLabel>
-#include <MGConfItem>
-#include <QGraphicsLinearLayout>
-#include <QStringListModel>
-
-namespace
-{
- const int ZValue = 10;
- const int CandidatesPreeditMargin = 10; // Margin between pre-edit rectangle and candidates
- const int MaximumCandidateLength = 100; // Maximum length of a candidate.
- const QString CandidatesListObjectName("CorrectionCandidateList");
- const QString CandidatesItemObjectName("CorrectionCandidateItem");
- const QString CandidatesItemLabelObjectName("CorrectionCandidateItemTitle");
-
- // This GConf item defines whether multitouch is enabled or disabled
- const QString MultitouchSettings = "/meegotouch/inputmethods/multitouch/enabled";
-};
-
-class MImCorrectionContentItemCreator : public MAbstractCellCreator<MImCorrectionCandidateItem>
-{
-public:
- MImCorrectionContentItemCreator();
- /*! \reimp */
- virtual MWidget *createCell(const QModelIndex &index, MWidgetRecycler &recycler) const;
- virtual void updateCell(const QModelIndex &index, MWidget *cell) const;
- /*! \reimp_end */
-private:
- void updateContentItemMode(const QModelIndex &index, MImCorrectionCandidateItem *contentItem) const;
-};
-
-MImCorrectionContentItemCreator::MImCorrectionContentItemCreator()
-{
-}
-
-MWidget *MImCorrectionContentItemCreator::createCell(const QModelIndex &index, MWidgetRecycler &recycler) const
-{
- MWidget *cell = recycler.take(MContentItem::staticMetaObject.className());
- if (cell == NULL) {
- cell = new MImCorrectionCandidateItem(MContentItem::SingleTextLabel);
- cell->setObjectName(CandidatesItemObjectName);
- }
- updateCell(index, cell);
- return cell;
-}
-
-void MImCorrectionContentItemCreator::updateCell(const QModelIndex &index, MWidget *cell) const
-{
- if (cell == NULL)
- return;
- MImCorrectionCandidateItem *contentItem = qobject_cast<MImCorrectionCandidateItem *>(cell);
- const QVariant data = index.data(Qt::DisplayRole);
- const QStringList rowData = data.value<QStringList>();
- if (rowData.size() > 0) {
- // Restrict the candidate length to MaximumCandidateLength characters.
- contentItem->setTitle(rowData[0].left(MaximumCandidateLength));
- }
- updateContentItemMode(index, contentItem);
-}
-
-void MImCorrectionContentItemCreator::updateContentItemMode(const QModelIndex &index,
- MImCorrectionCandidateItem *contentItem) const
-{
- const int row = index.row();
- bool thereIsNextRow = index.sibling(row + 1, 0).isValid();
- if (row == 0) {
- contentItem->setItemMode(MContentItem::SingleColumnTop);
- } else if (thereIsNextRow) {
- contentItem->setItemMode(MContentItem::SingleColumnCenter);
- } else {
- contentItem->setItemMode(MContentItem::SingleColumnBottom);
- }
-}
-
-MImCorrectionCandidateContainer::MImCorrectionCandidateContainer(QGraphicsItem *parent)
- : MStylableWidget(parent)
-{
-}
-
-MImCorrectionCandidateWidget::MImCorrectionCandidateWidget(QGraphicsWidget *parent)
- : MSceneWindow(parent),
- rotationInProgress(false),
- candidatePosition(0, 0),
- sceneManager(MPlainWindow::instance()->sceneManager()),
- containerWidget(new MImCorrectionCandidateContainer(this)),
- candidatesWidget(new MList(containerWidget)),
- cellCreator(new MImCorrectionContentItemCreator),
- candidatesModel(new QStringListModel(candidatesWidget)),
- candidateWidth(0)
-{
- // By default multi-touch is disabled
- setAcceptTouchEvents(MGConfItem(MultitouchSettings).value().toBool());
-
- setGeometry(QRectF(0, 0, sceneManager->visibleSceneSize().width(),
- sceneManager->visibleSceneSize().height()));
-
- // The z-value should always be more than vkb and text widget's z-value
- setZValue(ZValue);
-
- QGraphicsLinearLayout *layout = new QGraphicsLinearLayout;
- containerWidget->setLayout(layout);
-
- layout->addItem(candidatesWidget);
- candidatesWidget->setObjectName(CandidatesListObjectName);
- candidatesWidget->setCellCreator(cellCreator);
- candidatesWidget->setItemModel(candidatesModel);
- connect(candidatesWidget, SIGNAL(itemClicked(const QModelIndex &)), this, SLOT(select(const QModelIndex &)));
-}
-
-
-MImCorrectionCandidateWidget::~MImCorrectionCandidateWidget()
-{
-}
-
-void MImCorrectionCandidateWidget::setCandidates(const QStringList candidateList)
-{
- // Filter the preedit from the candidate list.
- QStringList filteredCandidateList = candidateList;
- for (int i = 0; i < filteredCandidateList.size(); i++) {
- if (m_preeditString.compare(filteredCandidateList.at(i), Qt::CaseInsensitive) == 0) {
- filteredCandidateList.removeAt(i);
- break;
- }
- }
-
- // Below is the QT way to update model size
- if (candidatesModel->rowCount() > 0)
- candidatesModel->removeRows(0, candidatesModel->rowCount());
- candidatesModel->insertRows(0, filteredCandidateList.size());
- candidatesModel->setStringList(filteredCandidateList);
-
- // Calculate the width for MContentItem dynamically.
- // To ensure the whole words in candidate list could be shown.
- candidateWidth = 0;
- MLabel label;
- label.setObjectName(CandidatesItemLabelObjectName);
- label.setWordWrap(false);
- // Below "label.preferredSize().width()" could be bigger than actual left + right
- // margin of the label. But unfortunately there is no way to get styling parameters
- // outside of styled object. So we assume the preferredSize of an empty label is
- // just its left + right margin.
- const int leftRightMargins = label.preferredSize().width();
- foreach (const QString &candidate, filteredCandidateList) {
- label.setText(candidate);
- candidateWidth = qMax(candidateWidth, label.preferredSize().width());
- }
-
- candidateWidth += leftRightMargins;
- // not less than minimum width
- if (candidateWidth < containerWidget->minimumSize().width())
- candidateWidth = containerWidget->minimumSize().width();
-}
-
-void MImCorrectionCandidateWidget::setPreeditString(const QString &string)
-{
- m_preeditString = string;
-}
-
-QPoint MImCorrectionCandidateWidget::position() const
-{
- return candidatePosition;
-}
-
-QStringList MImCorrectionCandidateWidget::candidates() const
-{
- return candidatesModel->stringList();
-}
-
-QString MImCorrectionCandidateWidget::preeditString() const
-{
- return m_preeditString;
-}
-
-void MImCorrectionCandidateWidget::setPosition(const QPoint &position, int bottomLimit)
-{
- qDebug() << __PRETTY_FUNCTION__;
- QSize sceneSize = sceneManager->visibleSceneSize();
- int popupWidth = candidateWidth;
- int popupHeight = candidatesWidget->preferredSize().height();
- if (bottomLimit < 0) {
- bottomLimit = sceneManager->visibleSceneSize().height();
- }
-
- candidatePosition = position;
-
- // Adjust candidates list so that it doesn't
- // overlap with scene boundary, if possible.
-
- if (candidatePosition.x() + popupWidth > sceneSize.width())
- candidatePosition.setX(sceneSize.width() - popupWidth);
-
- if (candidatePosition.y() + popupHeight > bottomLimit)
- candidatePosition.setY(bottomLimit - popupHeight);
-
- if (candidatePosition.x() < 0)
- candidatePosition.setX(0);
- if (candidatePosition.y() < 0)
- candidatePosition.setY(0);
-
- containerWidget->setPos(candidatePosition.x(), candidatePosition.y());
-}
-
-void MImCorrectionCandidateWidget::setPosition(const QRect &preeditRect, const int bottomLimit)
-{
- qDebug() << "in " << __PRETTY_FUNCTION__;
-
- if (preeditRect.isNull() || !preeditRect.isValid()) {
- candidatePosition = QPoint(0, 0);
- return;
- }
-
- QPoint position;
- QSize sceneSize = sceneManager->visibleSceneSize();
- int popupWidth = candidateWidth;
- int popupHeight = candidatesWidget->preferredSize().height();
-
- // Set horizontal position
-
- if (preeditRect.right() + CandidatesPreeditMargin + popupWidth < sceneSize.width()) {
- // List is positioned to the right of pre-edit rectangle.
- position.setX(preeditRect.x() + preeditRect.width() + CandidatesPreeditMargin);
- } else if (preeditRect.x() - CandidatesPreeditMargin - popupWidth >= 0) {
- // List is positioned to the left of pre-edit rectangle.
- position.setX(preeditRect.x() - CandidatesPreeditMargin - popupWidth);
- } else {
- // No room in neither side. Pick one that has more.
- int roomRight = sceneSize.width() - preeditRect.right();
- int roomLeft = preeditRect.x();
- if (roomRight >= roomLeft) {
- // Align to right side of scene rect
- position.setX(sceneSize.width() - popupWidth);
- } else {
- // Align to left side of scene rect
- position.setX(0);
- }
- }
-
- // Set vertical position
-
- // Vertically the candidatesWidget is centered at the pre-edit rectangle.
- position.setY(preeditRect.y() + preeditRect.height() / 2 - popupHeight / 2);
-
- // Finally handle scene boundaries.
- setPosition(position, bottomLimit);
-}
-
-void MImCorrectionCandidateWidget::showWidget()
-{
- // The height of MList is automatically expanded.
- // But the width of MList is not automatically expanded.
- // So set the container widget's width to candidateWidth,
- // to make MList have the enough width to show whole words.
- containerWidget->setPreferredWidth(candidateWidth);
- appear();
-
- // Extend overlay window to whole screen area.
- emit regionUpdated(mapRectToScene(QRect(QPoint(0, 0), sceneManager->visibleSceneSize())).toRect());
-}
-
-void MImCorrectionCandidateWidget::mousePressEvent(QGraphicsSceneMouseEvent *e)
-{
- Q_UNUSED(e);
-}
-
-void MImCorrectionCandidateWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *e)
-{
- Q_UNUSED(e);
- disappear();
-}
-
-void MImCorrectionCandidateWidget::select(const QModelIndex &index)
-{
- if (!index.isValid())
- return;
- const QVariant selectedVariant = candidatesModel->data(index, Qt::DisplayRole);
- Q_ASSERT(selectedVariant.isValid());
- const QString candidate = selectedVariant.toString();
- if (candidate != m_preeditString) {
- emit candidateClicked(candidate);
- }
- disappear();
-}
-
-void MImCorrectionCandidateWidget::hideEvent(QHideEvent *event)
-{
- MWidget::hideEvent(event);
- emit regionUpdated(QRegion());
-}
-
-int MImCorrectionCandidateWidget::activeIndex() const
-{
- int activeWordIndex = -1;
- QStringList candidateList = candidatesModel->stringList();
- for (int i = 0; i < candidateList.size(); i++) {
- if (m_preeditString.compare(candidateList.at(i), Qt::CaseInsensitive) == 0) {
- activeWordIndex = i;
- break;
- }
- }
- return activeWordIndex;
-}
-
-void MImCorrectionCandidateWidget::prepareToOrientationChange()
-{
- if (sceneWindowState() != MSceneWindow::Disappeared) {
- rotationInProgress = true;
- disappear();
- }
-}
-
-void MImCorrectionCandidateWidget::finalizeOrientationChange()
-{
- setGeometry(QRect(QPoint(0, 0), sceneManager->visibleSceneSize()));
- if (rotationInProgress) {
- showWidget();
- rotationInProgress = false;
- }
-}
-
-void MImCorrectionCandidateWidget::paintReactionMap(MReactionMap *reactionMap, QGraphicsView *view)
-{
- // Clear all with inactive color.
- reactionMap->setInactiveDrawingValue();
- reactionMap->setTransform(QTransform());
- reactionMap->fillRectangle(0, 0, reactionMap->width(), reactionMap->height());
-
- // Draw the actual candidate candidatesWidget area.
- reactionMap->setTransform(this, view);
- reactionMap->setReactiveDrawingValue();
- reactionMap->fillRectangle(candidatesWidget->geometry());
-}
-
-bool MImCorrectionCandidateWidget::sceneEvent(QEvent *e)
-{
- MSceneWindow::sceneEvent(e);
-
- // eat all the touch events to avoid the touch events
- // go to the background virtual keyboard.
- e->setAccepted(e->isAccepted()
- || e->type() == QEvent::TouchBegin
- || e->type() == QEvent::TouchUpdate
- || e->type() == QEvent::TouchEnd);
- return e->isAccepted();
-}
-
--- m-keyboard/widgets/mimcorrectioncandidatewidget.h
+++ m-keyboard/widgets/mimcorrectioncandidatewidget.h
-/* * This file is part of meego-keyboard *
- *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
- * All rights reserved.
- * Contact: Nokia Corporation (directui at nokia.com)
- *
- * If you have questions regarding the use of this file, please contact
- * Nokia at directui at nokia.com.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * and appearing in the file LICENSE.LGPL included in the packaging
- * of this file.
- */
-
-
-
-#ifndef MIMCORRECTIONCANDIDATEWIDGET_H
-#define MIMCORRECTIONCANDIDATEWIDGET_H
-
-#include <QModelIndex>
-#include <MSceneWindow>
-#include <MStylableWidget>
-#include "mimcorrectioncandidatecontainerstyle.h"
-
-class MSceneManager;
-class MList;
-class MImCorrectionContentItemCreator;
-class MReactionMap;
-class QStringListModel;
-
-class MImCorrectionCandidateContainer: public MStylableWidget
-{
- Q_OBJECT
-public:
- explicit MImCorrectionCandidateContainer(QGraphicsItem *parent = 0);
-
-private:
- M_STYLABLE_WIDGET(MImCorrectionCandidateContainerStyle)
-};
-
-/*!
- \class MImCorrectionCandidateWidget
- \brief The MImCorrectionCandidateWidget class is used to show error correction candidate list
-*/
-class MImCorrectionCandidateWidget: public MSceneWindow
-{
- Q_OBJECT
-
- friend class Ut_MImCorrectionCandidateWidget;
-
-public:
- /*! Constructor
- *
- */
- explicit MImCorrectionCandidateWidget(QGraphicsWidget *parent = 0);
-
- /*! Destructor
- *
- */
- ~MImCorrectionCandidateWidget();
-
- /*! Set the candidate list
- *
- */
- void setCandidates(QStringList candidate);
-
- virtual void showWidget();
-
- /*! \brief Sets the position of candidate list. The list cannot be outside screen.
- *
- * If \a bottomLimit is provided, it is respected but not at the expense of
- * being out of screen.
- */
- void setPosition(const QPoint &pos, int bottomLimit = -1);
-
- /*!
- * \brief Sets the position of candidate list based on pre-edit rectangle.
- *
- * Candidate list is put horizontally next to the pre-edit rectangle. Right side
- * of the rectangle is preferred but left side will be used if there is not enough
- * space. Vertically list will be in the middle of the rectangle. If \a bottomLimit
- * is provided, it is respected but not at the expense of being out of screen.
- */
- void setPosition(const QRect &preeditRect, int bottomLimit = -1);
-
- /*! Returns the index of preedit string in the candidate list.
- */
- int activeIndex() const;
-
- /*! Sets the preedit string
- */
- void setPreeditString(const QString &);
-
- /*! Returns the actual position
- * \return QPoint Returns the actual set position
- */
- QPoint position() const;
-
- /*! Returns the actual set candidates
- */
- QStringList candidates() const;
-
- /*! Returns the Preedit String
- */
- QString preeditString() const;
-
- /*!
- * Draw its reactive areas onto the reaction map
- */
- void paintReactionMap(MReactionMap *reactionMap, QGraphicsView *view);
-
- //! Prepare virtual keyboard for orientation change
- void prepareToOrientationChange();
-
- //! Finalize orientation change
- void finalizeOrientationChange();
-
-signals:
- /*! Updates the preedit word
- */
- void candidateClicked(const QString &);
-
- /*! Updates the screen region used by the widget
- */
- void regionUpdated(const QRegion &);
-
-protected:
- /*! \reimp */
- virtual void mousePressEvent(QGraphicsSceneMouseEvent *e);
- virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *e);
- virtual void hideEvent(QHideEvent *event);
- virtual bool sceneEvent(QEvent *event);
- /*! \reimp_end */
-
-protected slots:
- void select(const QModelIndex &);
-
-private:
- bool rotationInProgress;
- QString m_preeditString;
- QPoint candidatePosition;
- MSceneManager *sceneManager;
- MImCorrectionCandidateContainer *containerWidget;
- MList *candidatesWidget;
- MImCorrectionContentItemCreator *cellCreator;
- QStringListModel *candidatesModel;
- qreal candidateWidth;
-
- Q_DISABLE_COPY(MImCorrectionCandidateWidget)
-};
-
-#endif
--- m-keyboard/widgets/mimcorrectionhost.cpp
+++ m-keyboard/widgets/mimcorrectionhost.cpp
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * and appearing in the file LICENSE.LGPL included in the packaging
+ * of this file.
+ */
+
+
+#include "mimcorrectionhost.h"
+#include "mimwordtracker.h"
+#include "mimwordlist.h"
+
+#include <QGraphicsLinearLayout>
+#include <QDebug>
+#include <QString>
+
+#include <MSceneWindow>
+#include <MSceneManager>
+#include <mreactionmap.h>
+#include <mplainwindow.h>
+#include <MGConfItem>
+
+namespace
+{
+ const int CandidatesPreeditGap = 10; // Gap between pre-edit and candidates
+ const int MinimumCandidateWidget = 64; // Minimum length of a candidate.
+};
+
+MImCorrectionHost::MImCorrectionHost(MSceneWindow *parentWindow)
+ : QObject(parentWindow),
+ rotationInProgress(false),
+ wordTrackerPosition(0, 0),
+ currentMode(MImCorrectionHost::WordTrackerMode),
+ wordTracker(new MImWordTracker(parentWindow)),
+ wordList(new MImWordList())
+{
+ connect(wordTracker, SIGNAL(candidateClicked(QString)), this, SLOT(handleCandidateClicked(QString)));
+ connect(wordTracker, SIGNAL(longTapped()), this, SLOT(longTap()));
+ connect(wordTracker, SIGNAL(regionChanged()), this, SLOT(sendRegion()));
+
+ connect(wordList, SIGNAL(candidateClicked(QString)), this, SLOT(handleCandidateClicked(QString)));
+ connect(wordList, SIGNAL(regionChanged()), this, SLOT(sendRegion()));
+}
+
+
+MImCorrectionHost::~MImCorrectionHost()
+{
+ delete wordTracker;
+ delete wordList;
+}
+
+bool MImCorrectionHost::isActive() const
+{
+ return (wordTracker->isVisible() || wordList->isVisible());
+}
+
+void MImCorrectionHost::setCandidates(const QStringList list)
+{
+ candidates = list;
+ suggestionString.clear();
+ if (candidates.isEmpty()) {
+ return;
+ }
+ // The first candidate is always the original input word.
+ // So if there are more than one suggestions, the second one is
+ // the suggestion word.
+ suggestionString = candidates.at(0);
+ if (candidates.count() > 1) {
+ suggestionString = candidates.at(1);
+ }
+
+ wordTracker->setCandidate(suggestionString);
+ wordList->setCandidates(candidates);
+}
+
+QPoint MImCorrectionHost::position() const
+{
+ return wordTrackerPosition;
+}
+
+void MImCorrectionHost::setPosition(const QPoint &position)
+{
+ wordTrackerPosition = position;
+
+ int sceneWidth = MPlainWindow::instance()->sceneManager()->visibleSceneSize().width();
+ wordTrackerPosition.setX(qBound(0, wordTrackerPosition.x(), (int)(sceneWidth - wordTracker->idealWidth())));
+
+ wordTracker->setPosition(wordTrackerPosition);
+}
+
+void MImCorrectionHost::setPosition(const QRect &preeditRect)
+{
+ if (preeditRect.isNull() || !preeditRect.isValid()) {
+ setPosition(QPoint(0, 0));
+ return;
+ }
+
+ QPoint position;
+
+ // Set horizontal position
+ // the right side correction widget is aligned with the right side of
+ // pre-edit rectangle + MinimumCandidateWidget
+ position.setX(preeditRect.right() + MinimumCandidateWidget - wordTracker->idealWidth());
+
+ // Set vertical position
+ // Vertically the candidatesWidget is below the pre-edit + CandidatesPreeditGap.
+ position.setY(preeditRect.bottom() + CandidatesPreeditGap);
+ setPosition(position);
+}
+
+void MImCorrectionHost::showCorrectionWidget(MImCorrectionHost::CandidateMode mode)
+{
+ currentMode = mode;
+
+ if (candidates.isEmpty()) {
+ hideCorrectionWidget();
+ return;
+ }
+
+ if (currentMode == WordTrackerMode) {
+ suggestionString = wordTracker->candidate();
+
+ if (!wordTracker->isVisible()) {
+ wordList->disappear();
+ wordTracker->appear(false);
+ }
+ } else {
+ // Always highlight the first item in the candidate list
+ // which is the origin input word
+ wordList->setHighlightCandidate(candidates.at(0));
+ wordTracker->disappear(false);
+ wordList->appear();
+ }
+}
+
+void MImCorrectionHost::hideCorrectionWidget()
+{
+ wordTracker->disappear(false);
+ wordList->disappear();
+}
+
+MImCorrectionHost::CandidateMode MImCorrectionHost::candidateMode() const
+{
+ return currentMode;
+}
+
+QString MImCorrectionHost::suggestion() const
+{
+ return suggestionString;
+}
+
+void MImCorrectionHost::prepareToOrientationChange()
+{
+ if (isActive()) {
+ rotationInProgress = true;
+ hideCorrectionWidget();
+ }
+}
+
+void MImCorrectionHost::finalizeOrientationChange()
+{
+ if (rotationInProgress) {
+ showCorrectionWidget(currentMode);
+ rotationInProgress = false;
+ }
+}
+
+void MImCorrectionHost::paintReactionMap(MReactionMap *reactionMap, QGraphicsView *view)
+{
+ if (!isActive())
+ return;
+
+ if (wordTracker->isVisible()) {
+ wordTracker->paintReactionMap(reactionMap, view);
+ }
+ if (wordList->isVisible()) {
+ wordList->paintReactionMap(reactionMap, view);
+ }
+}
+
+void MImCorrectionHost::sendRegion()
+{
+ QRegion region;
+ if (isActive()) {
+ if (currentMode == WordListMode) {
+ region = wordList->region();
+ } else {
+ region = wordTracker->region();
+ }
+ }
+ emit regionUpdated(region);
+}
+
+void MImCorrectionHost::handleCandidateClicked(const QString &candidate)
+{
+ if (!candidate.isEmpty() && isActive()) {
+ suggestionString = candidate;
+ emit candidateClicked(candidate);
+ }
+
+ hideCorrectionWidget();
+}
+
+void MImCorrectionHost::reset()
+{
+ setCandidates(QStringList());
+ hideCorrectionWidget();
+}
+
+void MImCorrectionHost::longTap()
+{
+ qDebug() << __PRETTY_FUNCTION__;
+ if (!isActive())
+ return;
+
+ showCorrectionWidget(WordListMode);
+}
--- m-keyboard/widgets/mimcorrectionhost.h
+++ m-keyboard/widgets/mimcorrectionhost.h
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * and appearing in the file LICENSE.LGPL included in the packaging
+ * of this file.
+ */
+
+
+
+#ifndef MIMCORRECTIONHOST_H
+#define MIMCORRECTIONHOST_H
+
+#include <QObject>
+#include <QPoint>
+#include <QString>
+#include <QStringList>
+#include <QRect>
+#include <QRegion>
+
+class MSceneWindow;
+class MReactionMap;
+class MImWordTracker;
+class MImWordList;
+class MReactionMap;
+class QGraphicsView;
+
+/*!
+ \class MImCorrectionHost
+ \brief The MImCorrectionHost class is used to show error correction
+ candidate word tracker or word list.
+*/
+class MImCorrectionHost : public QObject
+{
+ Q_OBJECT
+
+ friend class Ut_MImCorrectionHost;
+
+public:
+ //! CandidateMode is used by showCorrectionWidget and setMode.
+ enum CandidateMode {
+ WordTrackerMode, //!< word tracker
+ WordListMode //!< word suggestion list
+ };
+
+ /*! Constructor
+ *
+ */
+ explicit MImCorrectionHost(MSceneWindow *parentWindow);
+
+ /*! Destructor
+ *
+ */
+ ~MImCorrectionHost();
+
+ /*!
+ * \brief Returns true if tracker or word list is active.
+ */
+ bool isActive() const;
+
+ /*! Set the candidate list
+ *
+ */
+ void setCandidates(QStringList candidate);
+
+ /*!
+ * \brief Shows candidate widget with different \a mode.
+ *
+ * \sa CandidateMode.
+ */
+ virtual void showCorrectionWidget(CandidateMode mode = WordTrackerMode);
+
+ /*!
+ * \brief Hides candidate widget.
+ */
+ virtual void hideCorrectionWidget();
+
+ /*!
+ * \brief Returns current used mode for the candidate widget.
+ *
+ * \sa CandidateMode.
+ */
+ CandidateMode candidateMode() const;
+
+ /*! \brief Sets the position of word tracker.
+ *
+ * The word tracker cannot be outside screen.
+ */
+ void setPosition(const QPoint &pos);
+
+ /*!
+ * \brief Sets the position of word tracker based on pre-edit rectangle.
+ *
+ * TODO: this method will be removed. Will only support to set word tracker position
+ * according the cursor position, not base on pre-edit rectangle anymore.
+ */
+ void setPosition(const QRect &preeditRect);
+
+ /*!
+ * \brief Returns the suggested word.
+ *
+ * The suggestion is the word present on word tracker or the one clicked by user in
+ * the word list.
+ */
+ QString suggestion() const;
+
+ /*! Returns the actual position
+ * \return QPoint Returns the actual set position.
+ */
+ QPoint position() const;
+
+ /*!
+ * \brief Draw its reactive areas onto the reaction map
+ */
+ void paintReactionMap(MReactionMap *reactionMap, QGraphicsView *view);
+
+ //! Prepare virtual keyboard for orientation change
+ void prepareToOrientationChange();
+
+ //! Finalize orientation change
+ void finalizeOrientationChange();
+
+ //! Clear stored suggestion and hide candidate widget.
+ void reset();
+
+signals:
+ //! Updates the preedit word
+ void candidateClicked(const QString &);
+
+ //! Updates the screen region used by the widget
+ void regionUpdated(const QRegion &);
+
+protected slots:
+
+ void handleCandidateClicked(const QString &candidate);
+
+ void sendRegion();
+
+ void longTap();
+
+private:
+ bool rotationInProgress;
+ QPoint wordTrackerPosition;
+ QStringList candidates;
+ CandidateMode currentMode;
+ QString suggestionString;
+
+ MImWordTracker *wordTracker;
+ MImWordList *wordList;
+
+ Q_DISABLE_COPY(MImCorrectionHost)
+};
+
+#endif
--- m-keyboard/widgets/mimkey.cpp
+++ m-keyboard/widgets/mimkey.cpp
@@ -49,19 +49,19 @@
const MImAbstractKeyAreaStyleContainer &style,
QGraphicsItem &parent)
: width(0),
- model(newModel),
+ mModel(newModel),
shift(false),
- currentLabel(model.binding(false)->label()),
+ currentLabel(mModel.binding(false)->label()),
currentState(Normal),
selected(false),
styleContainer(style),
parentItem(parent),
currentTouchPointCount(0)
{
- if (model.binding(false)) {
+ if (mModel.binding(false)) {
loadIcon(false);
}
- if (model.binding(true)) {
+ if (mModel.binding(true)) {
loadIcon(true);
}
}
@@ -114,6 +114,14 @@
if (newState != currentState) {
currentState = newState;
+
+ if ((currentState == Pressed || currentState == Selected)
+ && (not activeKeys.contains(this))) {
+ activeKeys.append(this);
+ } else { // currentState == Normal
+ activeKeys.removeAll(this);
+ }
+
update();
}
}
@@ -133,14 +141,14 @@
return currentState;
}
-const MImKeyModel &MImKey::key() const
+const MImKeyModel &MImKey::model() const
{
- return model;
+ return mModel;
}
const MImKeyBinding &MImKey::binding() const
{
- return *model.binding(shift);
+ return *mModel.binding(shift);
}
bool MImKey::isDeadKey() const
@@ -212,9 +220,11 @@
return iconInfo().id;
}
-void MImKey::drawIcon(const QRect &rectangle, QPainter *painter) const
+void MImKey::drawIcon(QPainter *painter) const
{
- const QPixmap *iconPixmap = icon();
+ const QPixmap *iconPixmap(icon());
+ const QRect rectangle(buttonRect().toRect());
+
if (iconPixmap) {
QPointF iconPos(rectangle.x() + (rectangle.width() - iconPixmap->width()) / 2,
rectangle.y() + (rectangle.height() - iconPixmap->height()) / 2);
@@ -230,7 +240,7 @@
int MImKey::preferredFixedWidth() const
{
- switch(model.width()) {
+ switch(mModel.width()) {
case MImKeyModel::Small:
return styleContainer->keyWidthSmallFixed();
@@ -258,7 +268,7 @@
qreal MImKey::preferredWidth(qreal pixelPerSizeUnit, qreal spacing) const
{
- switch(model.width()) {
+ switch(mModel.width()) {
case MImKeyModel::Small:
return computeWidth(pixelPerSizeUnit,
spacing,
@@ -295,10 +305,14 @@
return -1;
}
+bool MImKey::belongsTo(const QGraphicsItem *item) const
+{
+ return (item && (&parentItem == item));
+}
void MImKey::loadIcon(bool shift)
{
IconInfo &iconInfo(shift ? upperCaseIcon : lowerCaseIcon);
- const MImKeyBinding::KeyAction action(model.binding(shift)->action());
+ const MImKeyBinding::KeyAction action(mModel.binding(shift)->action());
QSize size;
QString iconProperty;
@@ -316,7 +330,7 @@
size = styleContainer->keyShiftIconSize();
break;
case MImKeyBinding::ActionReturn:
- if (model.binding(shift)->label().isEmpty()) {
+ if (mModel.binding(shift)->label().isEmpty()) {
iconProperty = "keyEnterIconId";
size = styleContainer->keyEnterIconSize();
}
@@ -326,7 +340,7 @@
size = styleContainer->keyMenuIconSize();
break;
case MImKeyBinding::ActionTab:
- if (model.binding(shift)->label().isEmpty()) {
+ if (mModel.binding(shift)->label().isEmpty()) {
iconProperty = "keyTabIconId";
size = styleContainer->keyTabIconSize();
}
@@ -335,7 +349,7 @@
break;
}
- iconInfo.id = getCSSProperty<QString>(styleContainer, iconProperty, model.rtl());
+ iconInfo.id = getCSSProperty<QString>(styleContainer, iconProperty, mModel.rtl());
if (!iconInfo.id.isEmpty()) {
iconInfo.pixmap = MTheme::pixmap(iconInfo.id, size);
--- m-keyboard/widgets/mimkey.h
+++ m-keyboard/widgets/mimkey.h
@@ -23,13 +23,14 @@
class MImAbstractKeyAreaStyleContainer;
class QGraphicsItem;
+class MImKeyArea;
//! Represents a key model with the key's current binding state, and also contains its visible area.
class MImKey
: public MImAbstractKey
{
public:
- explicit MImKey(const MImKeyModel &model,
+ explicit MImKey(const MImKeyModel &mModel,
const MImAbstractKeyAreaStyleContainer &style,
QGraphicsItem &parent);
@@ -40,11 +41,12 @@
virtual const QString secondaryLabel() const;
virtual const QRectF &buttonRect() const;
virtual const QRectF &buttonBoundingRect() const;
- virtual void setModifiers(bool shift, QChar accent = QChar());
+ virtual void setModifiers(bool shift,
+ QChar accent = QChar());
virtual void setDownState(bool down);
virtual void setSelected(bool select);
virtual ButtonState state() const;
- virtual const MImKeyModel &key() const;
+ virtual const MImKeyModel &model() const;
virtual const MImKeyBinding &binding() const;
virtual bool isDeadKey() const;
virtual bool isShiftKey() const;
@@ -64,9 +66,8 @@
//! \brief Returns icon identifier, if it was loaded.
QString iconId() const;
- //! \brief Draws the icon of this button, if it has one, to the given rectangle.
- void drawIcon(const QRect &rectangle, QPainter *painter) const;
-
+ //! \brief Draws the icon of this key, if available.
+ virtual void drawIcon(QPainter *painter) const;
//! \brief Calls parent item's QGraphicsItem::update() who actually draws the button.
void update();
@@ -76,6 +77,10 @@
//! Returns preferred dynamic width
qreal preferredWidth(qreal pixelPerSizeUnit, qreal spacing) const;
+ //! \brief Whether a key belongs to a given graphics item.
+ //! \param item the graphics item that logically contains this key
+ virtual bool belongsTo(const QGraphicsItem *item) const;
+
//! Cache for the buttons position and size. They can always
//! be calculated but are faster to access this way.
QRectF cachedBoundingRect;
@@ -101,7 +106,7 @@
void loadIcon(bool shift);
const IconInfo &iconInfo() const;
- const MImKeyModel &model;
+ const MImKeyModel &mModel;
bool shift;
QChar accent;
--- m-keyboard/widgets/mimkeyarea.cpp
+++ m-keyboard/widgets/mimkeyarea.cpp
@@ -14,8 +14,6 @@
* of this file.
*/
-
-
#include "mvirtualkeyboardstyle.h"
#include "mimkeyarea.h"
@@ -26,6 +24,7 @@
#include <QPainter>
#include <QTextCharFormat>
#include <QTextLine>
+#include <QList>
#include <MApplication>
#include <MComponentData>
@@ -33,6 +32,7 @@
#include <mplainwindow.h>
#include <mreactionmap.h>
#include <MTheme>
+#include <MTimestamp>
namespace {
template<class T>
@@ -58,6 +58,150 @@
// not found:
return -1;
}
+
+ //! \brief Helper class responsible for key-painting aspect.
+ //!
+ //! Can be used as visitor.
+ class KeyPainter
+ : public MImAbstractKeyVisitor
+ {
+ public:
+ //! PaintMode can be used to optimize drawing for HW acceleration
+ enum PaintMode {
+ PaintBackground, //!< Only draw background of given keys, fills up iconKeys
+ PaintBackgroundAndIcon //!< Draw both, background and icon, in one go
+ };
+
+ private:
+ const MImKeyArea *const keyArea; //!< owner of the keys
+ QPainter *const painter; //!< used for painting
+ PaintMode mode; //!< current paint mode
+ mutable QList<const MImKey *> iconKeys; //!< collects keys with icons, \sa drawIcons
+
+ public:
+ explicit KeyPainter(const MImKeyArea *newKeyArea,
+ QPainter *newPainter,
+ PaintMode newMode = PaintBackgroundAndIcon)
+ : keyArea(newKeyArea)
+ , painter(newPainter)
+ , mode(newMode)
+ {
+ Q_ASSERT(keyArea != 0);
+ Q_ASSERT(painter != 0);
+ }
+
+ //! \brief Paints background or icon of given key, depending on PaintMode
+ //! \param abstractKey the given key
+ bool operator()(MImAbstractKey *abstractKey)
+ {
+ return operator()(dynamic_cast<const MImKey *>(abstractKey));
+ }
+
+ //! \brief Paints background or icon of given key.
+ //! \param key the given key
+ bool operator()(const MImKey *key) const
+ {
+ if (key && key->belongsTo(keyArea)) {
+ switch (mode) {
+
+ case PaintBackground:
+ // Collect keys first, need to call drawIcons separately:
+ drawBackground(painter, key);
+
+ if (key->icon()) {
+ iconKeys.append(key);
+ }
+
+ break;
+
+ case PaintBackgroundAndIcon:
+ default:
+ drawBackground(painter, key);
+ key->drawIcon(painter);
+ break;
+ }
+ }
+
+ // If used as visitor, then we need to visit all active keys:
+ return false;
+ }
+
+ //! \brief Draw all previously visited keys with icons
+ void drawIcons() const
+ {
+ foreach (const MImKey *key, iconKeys) {
+ key->drawIcon(painter);
+ }
+ }
+
+ //! \brief Draws background for a given key.
+ //! \param painter the painter to be used
+ //! \param key key for which background shall be drawn
+ void drawBackground(QPainter *painter,
+ const MImAbstractKey *key) const
+ {
+ if (!key) {
+ return;
+ }
+
+ const MScalableImage *background = 0;
+
+ switch (key->state()) {
+
+ case MImAbstractKey::Normal:
+ switch (key->model().style()) {
+ case MImKeyModel::SpecialStyle:
+ background = keyArea->baseStyle()->keyBackgroundSpecial();
+ break;
+ case MImKeyModel::DeadkeyStyle:
+ background = keyArea->baseStyle()->keyBackgroundDeadkey();
+ break;
+ case MImKeyModel::NormalStyle:
+ default:
+ background = keyArea->baseStyle()->keyBackground();
+ break;
+ }
+ break;
+
+ case MImAbstractKey::Pressed:
+ switch (key->model().style()) {
+ case MImKeyModel::SpecialStyle:
+ background = keyArea->baseStyle()->keyBackgroundSpecialPressed();
+ break;
+ case MImKeyModel::DeadkeyStyle:
+ background = keyArea->baseStyle()->keyBackgroundDeadkeyPressed();
+ break;
+ case MImKeyModel::NormalStyle:
+ default:
+ background = keyArea->baseStyle()->keyBackgroundPressed();
+ break;
+ }
+ break;
+
+ case MImAbstractKey::Selected:
+ switch (key->model().style()) {
+ case MImKeyModel::SpecialStyle:
+ background = keyArea->baseStyle()->keyBackgroundSpecialSelected();
+ break;
+ case MImKeyModel::DeadkeyStyle:
+ background = keyArea->baseStyle()->keyBackgroundDeadkeySelected();
+ break;
+ case MImKeyModel::NormalStyle:
+ default:
+ background = keyArea->baseStyle()->keyBackgroundSelected();
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (background) {
+ background->draw(key->buttonRect().toRect(), painter);
+ }
+ }
+ };
}
MImKeyArea::MImKeyArea(const LayoutData::SharedLayoutSection &newSection,
@@ -65,11 +209,13 @@
QGraphicsWidget *parent)
: MImAbstractKeyArea(newSection, usePopup, parent),
rowList(newSection->rowCount()),
- widgetHeight(computeWidgetHeight()),
- mMaxNormalizedWidth(maxNormalizedWidth()),
- shiftButton(0),
+ cachedWidgetHeight(computeWidgetHeight()),
+ mMaxNormalizedWidth(computeMaxNormalizedWidth()),
+ shiftKey(0),
textDirty(false),
- equalWidthButtons(true)
+ cachedBackgroundDirty(true),
+ hasCachedBackground(false),
+ equalWidthKeys(true)
{
textLayout.setCacheEnabled(true);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
@@ -80,12 +226,13 @@
MImKeyArea::~MImKeyArea()
{
for (RowIterator rowIter(rowList.begin()); rowIter != rowList.end(); ++rowIter) {
- qDeleteAll(rowIter->buttons);
- rowIter->buttons.clear();
+ qDeleteAll(rowIter->keys);
+ rowIter->keys.clear();
}
}
-QSizeF MImKeyArea::sizeHint(Qt::SizeHint which, const QSizeF &/*constraint*/) const
+QSizeF MImKeyArea::sizeHint(Qt::SizeHint which,
+ const QSizeF &) const
{
int width = 0;
if (which == Qt::MaximumSize) {
@@ -93,17 +240,18 @@
// will apply a constraint for this.
width = QWIDGETSIZE_MAX;
}
- return QSizeF(width, widgetHeight);
+ return QSizeF(width, cachedWidgetHeight);
}
-void MImKeyArea::drawReactiveAreas(MReactionMap *reactionMap, QGraphicsView *view)
+void MImKeyArea::drawReactiveAreas(MReactionMap *reactionMap,
+ QGraphicsView *view)
{
reactionMap->setTransform(this, view);
reactionMap->setReactiveDrawingValue();
- foreach (const ButtonRow &row, rowList) {
- foreach (const MImKey *const button, row.buttons) {
- reactionMap->fillRectangle(button->buttonBoundingRect());
+ foreach (const KeyRow &row, rowList) {
+ foreach (const MImKey *const key, row.keys) {
+ reactionMap->fillRectangle(key->buttonBoundingRect());
}
}
}
@@ -117,25 +265,25 @@
for (int row = 0; row != numRows; ++row, ++rowIter) {
const int numColumns = sectionModel()->columnsAt(row);
- rowIter->stretchButton = 0;
+ rowIter->stretchKey = 0;
- // Add buttons
+ // Add keys
for (int col = 0; col < numColumns; ++col) {
// Parameters to fetch from base class.
MImKeyModel *dataKey = sectionModel()->keyModel(row, col);
- MImKey *button = new MImKey(*dataKey, baseStyle(), *this);
+ MImKey *key = new MImKey(*dataKey, baseStyle(), *this);
- // TODO: Remove restriction to have only one shift button per layout?
+ // TODO: Remove restriction to have only one shift key per layout?
if (dataKey->binding()->action() == MImKeyBinding::ActionShift) {
- shiftButton = button;
+ shiftKey = key;
}
// Only one stretching item per row.
- if (!rowIter->stretchButton) {
- rowIter->stretchButton = (dataKey->width() == MImKeyModel::Stretched ? button : 0);
+ if (!rowIter->stretchKey) {
+ rowIter->stretchKey = (dataKey->width() == MImKeyModel::Stretched ? key : 0);
}
- rowIter->buttons.append(button);
+ rowIter->keys.append(key);
}
}
@@ -157,22 +305,23 @@
// labels. This must be done before QTextLayout::beginLayout().
QString labelContent;
- foreach (const ButtonRow &row, rowList) {
-
- foreach (const MImKey *button, row.buttons) {
+ foreach (const KeyRow &row, rowList) {
+ foreach (const MImKey *key, row.keys) {
// primary label
- QString label = button->label();
+ QString label = key->label();
if (!label.isEmpty()) {
// Add whitespace for QTextLine to be able to cut.
labelContent += label + " ";
// try secondary label
- label = button->secondaryLabel();
+ label = key->secondaryLabel();
if (!label.isEmpty()) {
// Add formatting for this secondary label.
- QTextLayout::FormatRange formatRange = {labelContent.length(), label.length(), secondaryFormat};
+ QTextLayout::FormatRange formatRange = {labelContent.length(),
+ label.length(),
+ secondaryFormat};
formatList.append(formatRange);
labelContent += label + " ";
}
@@ -203,16 +352,16 @@
// rowHasSecondaryLabel is needed for the vertical alignment of
// secondary label purposes.
bool rowHasSecondaryLabel = false;
- foreach (MImKey *button, row->buttons) {
- if (!button->secondaryLabel().isEmpty()) {
+
+ foreach (const MImKey *key, row->keys) {
+ if (!key->secondaryLabel().isEmpty()) {
rowHasSecondaryLabel = true;
}
}
- foreach (MImKey *button, row->buttons) {
-
- const QString &label(button->label());
- const QString &secondary(button->secondaryLabel());
+ foreach (const MImKey *key, row->keys) {
+ const QString &label(key->label());
+ const QString &secondary(key->secondaryLabel());
QPoint labelPos;
QPoint secondaryLabelPos;
@@ -221,14 +370,14 @@
continue;
}
- const QRectF &buttonRect = button->cachedButtonRect;
+ const QRectF &keyRect = key->cachedButtonRect;
if (!rowHasSecondaryLabel) {
// All horizontally centered.
- labelPos = fm.boundingRect(buttonRect.x(),
- buttonRect.y(),
- buttonRect.width(),
- buttonRect.height(),
+ labelPos = fm.boundingRect(keyRect.x(),
+ keyRect.y(),
+ keyRect.width(),
+ keyRect.height(),
Qt::AlignCenter,
label).topLeft();
} else {
@@ -241,22 +390,22 @@
if (landscape) {
// primary label: horizontally centered, top margin defines y
// secondary: horizontally centered, primary bottom + separation margin defines y
- const int primaryY = buttonRect.top() + topMargin;
- labelPos.setX(buttonRect.center().x() - fm.width(label) / 2);
+ const int primaryY = keyRect.top() + topMargin;
+ labelPos.setX(keyRect.center().x() - fm.width(label) / 2);
labelPos.setY(primaryY);
if (!secondary.isEmpty()) {
- secondaryLabelPos.setX(buttonRect.center().x() - secondaryFm.width(secondary) / 2);
+ secondaryLabelPos.setX(keyRect.center().x() - secondaryFm.width(secondary) / 2);
secondaryLabelPos.setY(primaryY + labelHeight + secondarySeparation);
}
} else {
// primary label: horizontally according to left margin, vertically centered
// secondary: horizontally on right of primary + separation margin, vertically centered
- const int primaryX = buttonRect.left() + labelLeftWithSecondary;
+ const int primaryX = keyRect.left() + labelLeftWithSecondary;
labelPos.setX(primaryX);
- labelPos.setY(buttonRect.center().y() - labelHeight / 2);
+ labelPos.setY(keyRect.center().y() - labelHeight / 2);
if (!secondary.isEmpty()) {
secondaryLabelPos.setX(primaryX + fm.width(label) + secondarySeparation);
- secondaryLabelPos.setY(buttonRect.center().y() - secondaryLabelHeight / 2);
+ secondaryLabelPos.setY(keyRect.center().y() - secondaryLabelHeight / 2);
}
}
}
@@ -302,117 +451,95 @@
return qMax<qreal>(0.0, height);
}
-void MImKeyArea::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
+void MImKeyArea::paint(QPainter *onScreenPainter,
+ const QStyleOptionGraphicsItem *,
+ QWidget *)
{
+ mTimestamp("MImKeyArea", "start");
const MImAbstractKeyAreaStyleContainer &style(baseStyle());
- const MScalableImage *background = style->backgroundImage();
- if (background) {
- background->draw(boundingRect().toRect(), painter);
- }
+ // Key areas are disabled during animations. Once we are animated, it
+ // makes no sense to maintain the offscreen cache (especially when
+ // HW-accelerated). Therefore, we draw directly to the screen.
+ // However, we need to remember whether we drew a current version of the
+ // key area to the offscreen cache at all (that's what hasCachedBackground
+ // is for).
+ QPainter *currentPainter = onScreenPainter;
+ if (cachedBackgroundDirty
+ || !isEnabled()
+ || !hasCachedBackground) {
- // Draw images first.
- foreach (const ButtonRow &row, rowList) {
- foreach (const MImKey *button, row.buttons) {
- drawKeyBackground(painter, button);
- button->drawIcon(button->cachedButtonRect.toRect(), painter);
- drawDebugRects(painter, button,
- style->drawButtonBoundingRects(),
- style->drawButtonRects());
- }
- }
-
- if (style->drawReactiveAreas()) {
- drawDebugReactiveAreas(painter);
- }
+ // TODO: find out why size()'s height is incorrect for popup.
+ initCachedBackground(QSize(size().width(), cachedWidgetHeight));
+ QPainter offScreenPainter(cachedBackground.get());
- if (textDirty) {
- buildTextLayout();
- }
+ if (isEnabled()) {
+ currentPainter = &offScreenPainter;
+ hasCachedBackground = true;
+ }
- // Draw text next.
- painter->setPen(style->fontColor());
- textLayout.draw(painter, QPoint());
-}
+ const MScalableImage *background = style->backgroundImage();
-void MImKeyArea::drawKeyBackground(QPainter *painter,
- const MImKey *button)
-{
- if (!button) {
- return;
- }
+ if (background) {
+ background->draw(boundingRect().toRect(), currentPainter);
+ }
- const MScalableImage *background = 0;
+ const bool drawButtonBoundingRects(style->drawButtonBoundingRects());
+ const bool drawButtonRects(style->drawButtonRects());
- switch (button->state()) {
+ // In case of HW acceleration, we want to avoid switching between textures, if possible
+ // Draw backgrounds first, icons later:
+ const KeyPainter kp(this, currentPainter, KeyPainter::PaintBackground);
- case MImAbstractKey::Normal:
- switch (button->key().style()) {
- case MImKeyModel::SpecialStyle:
- background = baseStyle()->keyBackgroundSpecial();
- break;
- case MImKeyModel::DeadkeyStyle:
- background = baseStyle()->keyBackgroundDeadkey();
- break;
- case MImKeyModel::NormalStyle:
- default:
- background = baseStyle()->keyBackground();
- break;
+ foreach (const KeyRow &row, rowList) {
+ foreach (const MImKey *key, row.keys) {
+ kp(key);
+ drawDebugRects(currentPainter, key,
+ drawButtonBoundingRects,
+ drawButtonRects);
+ }
}
- break;
- case MImAbstractKey::Pressed:
- switch (button->key().style()) {
- case MImKeyModel::SpecialStyle:
- background = baseStyle()->keyBackgroundSpecialPressed();
- break;
- case MImKeyModel::DeadkeyStyle:
- background = baseStyle()->keyBackgroundDeadkeyPressed();
- break;
- case MImKeyModel::NormalStyle:
- default:
- background = baseStyle()->keyBackgroundPressed();
- break;
- }
- break;
+ kp.drawIcons();
- case MImAbstractKey::Selected:
- switch (button->key().style()) {
- case MImKeyModel::SpecialStyle:
- background = baseStyle()->keyBackgroundSpecialSelected();
- break;
- case MImKeyModel::DeadkeyStyle:
- background = baseStyle()->keyBackgroundDeadkeySelected();
- break;
- case MImKeyModel::NormalStyle:
- default:
- background = baseStyle()->keyBackgroundSelected();
- break;
+ if (style->drawReactiveAreas()) {
+ drawDebugReactiveAreas(currentPainter);
}
- break;
+ }
- default:
- break;
+ if (hasCachedBackground) {
+ onScreenPainter->drawPixmap(boundingRect().toRect(), *cachedBackground.get());
}
- if (background) {
- background->draw(button->cachedButtonRect.toRect(), painter);
+ KeyPainter kp(this, onScreenPainter, KeyPainter::PaintBackground);
+ MImAbstractKey::visitActiveKeys(&kp);
+ kp.drawIcons();
+
+ if (textDirty) {
+ buildTextLayout();
}
+
+ // Draw text next.
+ onScreenPainter->setPen(style->fontColor());
+ textLayout.draw(onScreenPainter, QPoint());
+
+ cachedBackgroundDirty = false;
+ mTimestamp("MImKeyArea", "end");
}
void MImKeyArea::drawDebugRects(QPainter *painter,
- const MImKey *button,
- bool drawBoundingRects,
- bool drawRects)
+ const MImAbstractKey *key,
+ bool drawBoundingRects,
+ bool drawRects) const
{
if (drawBoundingRects) {
painter->save();
painter->setPen(Qt::red);
painter->setBrush(QBrush(QColor(64, 0, 0, 64)));
- painter->drawRect(button->buttonBoundingRect());
- painter->drawText(button->buttonRect().adjusted(4, 4, -4, -4),
- QString("%1x%2").arg(button->buttonBoundingRect().width())
- .arg(button->buttonBoundingRect().height()));
+ painter->drawRect(key->buttonBoundingRect());
+ painter->drawText(key->buttonRect().adjusted(4, 4, -4, -4),
+ QString("%1x%2").arg(key->buttonBoundingRect().width())
+ .arg(key->buttonBoundingRect().height()));
painter->restore();
}
@@ -420,10 +547,10 @@
painter->save();
painter->setPen(Qt::green);
painter->setBrush(QBrush(QColor(0, 64, 0, 64)));
- painter->drawRect(button->buttonRect());
- painter->drawText(button->buttonRect().adjusted(4, 16, -4, -16),
- QString("%1x%2").arg(button->buttonRect().width())
- .arg(button->buttonRect().height()));
+ painter->drawRect(key->buttonRect());
+ painter->drawText(key->buttonRect().adjusted(4, 16, -4, -16),
+ QString("%1x%2").arg(key->buttonRect().width())
+ .arg(key->buttonRect().height()));
painter->restore();
}
}
@@ -442,10 +569,10 @@
painter->drawLine(QPointF(0, rowPair.second),
QPointF(size().width(), rowPair.second));
- QVector<QPair<qreal, qreal> > buttonOffsets = rowList[rowIdx].buttonOffsets;
+ const QVector<QPair<qreal, qreal> > &keyOffsets = rowList[rowIdx].keyOffsets;
- for(int colIdx = 0; colIdx < buttonOffsets.size(); ++colIdx) {
- QPair<qreal, qreal> colPair = buttonOffsets[colIdx];
+ for(int colIdx = 0; colIdx < keyOffsets.size(); ++colIdx) {
+ QPair<qreal, qreal> colPair = keyOffsets[colIdx];
painter->setPen(Qt::cyan);
painter->drawLine(QPointF(colPair.first, rowPair.first),
QPointF(colPair.first, rowPair.second));
@@ -459,6 +586,17 @@
painter->restore();
}
+void MImKeyArea::initCachedBackground(const QSize &newSize)
+{
+ if (cachedBackground.get() && newSize == cachedBackground->size()) {
+ return; // already initialized
+ }
+
+ cachedBackground.reset(new QPixmap(newSize));
+ cachedBackground->fill(Qt::transparent);
+ hasCachedBackground = false;
+}
+
MImAbstractKey *MImKeyArea::keyAt(const QPoint &pos) const
{
const int numRows = rowList.count();
@@ -473,47 +611,49 @@
return 0;
}
- const ButtonRow ¤tRow = rowList.at(rowIndex);
- const int buttonIndex = binaryRangeFind<qreal>(pos.x(), currentRow.buttonOffsets);
+ const KeyRow ¤tRow = rowList.at(rowIndex);
+ const int keyIndex = binaryRangeFind<qreal>(pos.x(), currentRow.keyOffsets);
- if (buttonIndex == -1) {
+ if (keyIndex == -1) {
return 0;
}
- return currentRow.buttons.at(buttonIndex);
+ return currentRow.keys.at(keyIndex);
}
void MImKeyArea::setShiftState(ModifierState newShiftState)
{
- if (shiftButton) {
- shiftButton->setModifiers(newShiftState != ModifierClearState);
- shiftButton->setSelected(newShiftState == ModifierLockedState);
+ if (shiftKey) {
+ shiftKey->setModifiers(newShiftState != ModifierClearState);
+ shiftKey->setSelected(newShiftState == ModifierLockedState);
}
}
-void MImKeyArea::modifiersChanged(const bool shift, const QChar accent)
+void MImKeyArea::modifiersChanged(const bool shift,
+ const QChar &accent)
{
for (RowIterator row(rowList.begin()); row != rowList.end(); ++row) {
- foreach (MImKey *button, row->buttons) {
- // Shift button and selected keys are detached from the normal level changing.
- if (button != this->shiftButton
- && button->state() != MImAbstractKey::Selected) {
- button->setModifiers(shift, accent);
+ foreach (MImKey *key, row->keys) {
+ // Shift key and selected keys are detached from the normal level changing.
+ if (key != this->shiftKey
+ && key->state() != MImAbstractKey::Selected) {
+ key->setModifiers(shift, accent);
}
}
}
textDirty = true;
-
}
-void MImKeyArea::updateButtonGeometriesForWidth(const int newAvailableWidth)
+void MImKeyArea::updateKeyGeometries(const int newAvailableWidth)
{
if (sectionModel()->maxColumns() == 0) {
return;
}
- widgetHeight = computeWidgetHeight();
+ cachedWidgetHeight = computeWidgetHeight();
+ initCachedBackground(QSize(newAvailableWidth, cachedWidgetHeight));
+
rowOffsets.clear();
const MImAbstractKeyAreaStyleContainer &style(baseStyle());
@@ -525,27 +665,27 @@
- (style->paddingLeft() + style->paddingRight()));
const qreal normalizedWidth = qMax<qreal>(1.0, mMaxNormalizedWidth);
- const qreal availableWidthForButtons = availableWidth - ((normalizedWidth - 1) * HorizontalSpacing);
- mRelativeButtonBaseWidth = availableWidthForButtons / normalizedWidth;
- emit relativeButtonBaseWidthChanged(mRelativeButtonBaseWidth);
+ const qreal availableWidthForKeys = availableWidth - ((normalizedWidth - 1) * HorizontalSpacing);
+ mRelativeKeyBaseWidth = availableWidthForKeys / normalizedWidth;
+ emit relativeKeyBaseWidthChanged(mRelativeKeyBaseWidth);
- // This is used to update the button rectangles
+ // This is used to update the key rectangles
qreal y = style->paddingTop();
- // Button margins
+ // key margins
const qreal leftMargin = HorizontalSpacing / 2;
const qreal rightMargin = HorizontalSpacing - leftMargin;
const qreal topMargin = VerticalSpacing / 2;
const qreal bottomMargin = VerticalSpacing - topMargin;
- QRectF br; // button bounding rectangle
+ QRectF br; // key bounding rectangle
const qreal rowListFactor = (rowList.count() > 1 ? 1 : 0);
for (RowIterator row(rowList.begin()); row != rowList.end(); ++row) {
const qreal rowHeight = preferredRowHeight(row - rowList.begin());
br.setHeight(rowHeight + style->spacingVertical() * rowListFactor);
- row->buttonOffsets.clear();
+ row->keyOffsets.clear();
// Store the row offsets for fast key lookup (the first row's height
// can be adjusted through buttonBoundingRectTopAdjustment,
@@ -567,23 +707,23 @@
// Update row width
qreal rowWidth = 0;
- foreach (MImKey *button, row->buttons) {
- button->width = button->preferredWidth(mRelativeButtonBaseWidth, HorizontalSpacing);
- rowWidth += button->width + HorizontalSpacing;
+ foreach (MImKey *key, row->keys) {
+ key->width = key->preferredWidth(mRelativeKeyBaseWidth, HorizontalSpacing);
+ rowWidth += key->width + HorizontalSpacing;
}
rowWidth -= HorizontalSpacing;
qreal availableWidthForSpacers = 0;
const QList<int> spacerIndices = sectionModel()->spacerIndices(row - rowList.begin());
- int spacerCount = row->stretchButton ? spacerIndices.count() + 1
- : spacerIndices.count();
+ int spacerCount = row->stretchKey ? spacerIndices.count() + 1
+ : spacerIndices.count();
- if (row->stretchButton) {
- rowWidth -= row->stretchButton->width;
+ if (row->stretchKey) {
+ rowWidth -= row->stretchKey->width;
- // Handle the case of one stretch button/no other spacer elments directly:
+ // Handle the case of one stretch key/no other spacer elments directly:
if (spacerCount == 1) {
- row->stretchButton->width = availableWidth - rowWidth;
+ row->stretchKey->width = availableWidth - rowWidth;
rowWidth = availableWidth;
spacerCount = 0;
}
@@ -592,40 +732,40 @@
if ((spacerCount > 0) && (availableWidth > rowWidth)) {
availableWidthForSpacers = (availableWidth - rowWidth) / spacerCount;
- if (row->stretchButton) {
- row->stretchButton->width = availableWidthForSpacers;
+ if (row->stretchKey) {
+ row->stretchKey->width = availableWidthForSpacers;
}
}
- // We can precalculate button rectangles.
+ // We can precalculate key rectangles.
br.moveTop(y - (rowList.count() > 1 ? topMargin : 0));
- // A spacer with an index of -1 means it was put before any button in that row.
+ // A spacer with an index of -1 means it was put before any key in that row.
// Also add layout padding:
qreal x = style->paddingLeft() + spacerIndices.count(-1) * availableWidthForSpacers;
- for (int buttonIndex = 0; buttonIndex < row->buttons.count(); ++buttonIndex) {
- MImKey *button = row->buttons.at(buttonIndex);
+ for (int keyIndex = 0; keyIndex < row->keys.count(); ++keyIndex) {
+ MImKey *const key = row->keys.at(keyIndex);
br.moveLeft(x - leftMargin);
- br.setWidth(button->width + leftMargin + rightMargin);
+ br.setWidth(key->width + leftMargin + rightMargin);
// save it (but cover up for rounding errors, ie, extra spacing pixels):
- button->cachedBoundingRect = br.adjusted(-1, style->buttonBoundingRectTopAdjustment(),
- 1, style->buttonBoundingRectBottomAdjustment());
+ key->cachedBoundingRect = br.adjusted(-1, style->buttonBoundingRectTopAdjustment(),
+ 1, style->buttonBoundingRectBottomAdjustment());
- button->cachedButtonRect = br.adjusted(leftMargin, topMargin, -rightMargin, -bottomMargin);
+ key->cachedButtonRect = br.adjusted(leftMargin, topMargin, -rightMargin, -bottomMargin);
- // Store the button offsets for fast key lookup:
- row->buttonOffsets.append(QPair<qreal, qreal>(button->cachedBoundingRect.left(),
- button->cachedBoundingRect.right()));
+ // Store the key offsets for fast key lookup:
+ row->keyOffsets.append(QPair<qreal, qreal>(key->cachedBoundingRect.left(),
+ key->cachedBoundingRect.right()));
- // Increase x to the next button bounding rect border.
- x += button->width + HorizontalSpacing;
+ // Increase x to the next key bounding rect border.
+ x += key->width + HorizontalSpacing;
// Our spacerIndex is a multi-set, hence we need to add
// availableWidthForSpacers for every ocurrence of spacerIndex:
- x += spacerIndices.count(buttonIndex) * availableWidthForSpacers;
+ x += spacerIndices.count(keyIndex) * availableWidthForSpacers;
}
y += br.height();
@@ -637,7 +777,7 @@
QRectF MImKeyArea::boundingRect() const
{
- return QRectF(0, 0, size().width(), widgetHeight);
+ return QRectF(0, 0, size().width(), cachedWidgetHeight);
}
qreal MImKeyArea::preferredRowHeight(int row) const
@@ -670,24 +810,28 @@
return 0.0;
}
-qreal MImKeyArea::maxNormalizedWidth() const
+qreal MImKeyArea::computeMaxNormalizedWidth() const
{
qreal maxRowWidth = 0.0;
for (int j = 0; j < sectionModel()->rowCount(); ++j) {
qreal rowWidth = 0.0;
+
for (int i = 0; i < sectionModel()->columnsAt(j); ++i) {
const MImKeyModel *key = sectionModel()->keyModel(j, i);
rowWidth += normalizedKeyWidth(key);
}
+
maxRowWidth = qMax(maxRowWidth, rowWidth);
}
+
return maxRowWidth;
}
-qreal MImKeyArea::normalizedKeyWidth(const MImKeyModel *key) const
+qreal MImKeyArea::normalizedKeyWidth(const MImKeyModel *model) const
{
- switch(key->width()) {
+ switch(model->width()) {
+
case MImKeyModel::Small:
return baseStyle()->keyWidthSmall();
@@ -712,18 +856,31 @@
void MImKeyArea::onThemeChangeCompleted()
{
- mMaxNormalizedWidth = maxNormalizedWidth();
+ mMaxNormalizedWidth = computeMaxNormalizedWidth();
MImAbstractKeyArea::onThemeChangeCompleted();
buildTextLayout();
}
-QList<const MImAbstractKey *> MImKeyArea::keys()
+void MImKeyArea::handleVisibilityChanged(bool)
+{
+ invalidateBackgroundCache();
+}
+
+void MImKeyArea::invalidateBackgroundCache()
+{
+ cachedBackgroundDirty = true;
+ hasCachedBackground = false;
+}
+
+QList<const MImAbstractKey *> MImKeyArea::keys() const
{
QList<const MImAbstractKey *> keyList;
- foreach (const ButtonRow &row, rowList) {
- foreach (const MImKey *button, row.buttons) {
- keyList << button;
+
+ foreach (const KeyRow &row, rowList) {
+ foreach (const MImKey *key, row.keys) {
+ keyList.append(key);
}
}
+
return keyList;
}
--- m-keyboard/widgets/mimkeyarea.h
+++ m-keyboard/widgets/mimkeyarea.h
@@ -14,8 +14,6 @@
* of this file.
*/
-
-
#ifndef MIMKEYAREA_H
#define MIMKEYAREA_H
@@ -24,95 +22,111 @@
#include <MScalableImage>
#include <QTextLayout>
+#include <QPixmap>
+#include <QSize>
+#include <memory>
-/*!
- * \brief MImKeyArea is an implementation of MImAbstractKeyArea which
- * does not use separate widgets for buttons, but instead draws them explicitly.
- */
+//! \brief MImKeyArea reimplements MImAbstractKeyArea and is optimized for drawing.
class MImKeyArea
: public MImAbstractKeyArea
{
+ Q_DISABLE_COPY(MImKeyArea)
+
public:
+ //! \brief Contructor, see \a MImAbstractKeyArea
explicit MImKeyArea(const LayoutData::SharedLayoutSection §ion,
bool usePopup = false,
QGraphicsWidget *parent = 0);
+ //! \brief Destructor
virtual ~MImKeyArea();
//! \reimp
- virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *view);
+ virtual void modifiersChanged(bool shift,
+ const QChar &accent = QChar());
+ virtual void paint(QPainter *painter,
+ const QStyleOptionGraphicsItem *option,
+ QWidget *view);
virtual QRectF boundingRect() const;
virtual void setShiftState(ModifierState newShiftState);
- virtual QList<const MImAbstractKey *> keys();
+ virtual QList<const MImAbstractKey *> keys() const;
//! \reimp_end
protected:
- /*! \reimp */
- virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const;
- virtual void drawReactiveAreas(MReactionMap *reactionMap, QGraphicsView *view);
- virtual void updateButtonGeometriesForWidth(int availableWidth);
+ //! \reimp
+ virtual QSizeF sizeHint(Qt::SizeHint which,
+ const QSizeF &constraint) const;
+ virtual void drawReactiveAreas(MReactionMap *reactionMap,
+ QGraphicsView *view);
+ virtual void updateKeyGeometries(int availableWidth);
virtual MImAbstractKey *keyAt(const QPoint &pos) const;
- virtual void modifiersChanged(bool shift, QChar accent = QChar());
virtual void onThemeChangeCompleted();
- /*! \reimp_end */
+ virtual void handleVisibilityChanged(bool visible);
+ virtual void invalidateBackgroundCache();
+ //! \reimp_end
private:
- //! \brief Creates buttons for key data models
+ //! \brief Creates buttons for key data models.
void loadKeys();
//! \brief Builds QTextLayout representation of current button labels for faster drawing.
void buildTextLayout();
+ //! \brief Returns the new height of the key area.
qreal computeWidgetHeight() const;
- //! \brief Return preferred height for a row
+ //! \brief Return preferred height for a row.
+ //! \param row the index of the queried row
qreal preferredRowHeight(int row) const;
- //! \brief Get the maximum width in this widget, in normalized units
- qreal maxNormalizedWidth() const;
-
- //! \brief Normalized width for a particular MImKeyModel.
- qreal normalizedKeyWidth(const MImKeyModel *key) const;
+ //! \brief Compute the maximum width in this widget, in normalized units.
+ qreal computeMaxNormalizedWidth() const;
-
- //! \brief Draws background.
- void drawKeyBackground(QPainter *painter,
- const MImKey *button);
-
- //! \brief Draws button rects/bounding rects, for debugging purposes
+ //! \brief Returns normalized width for a particular MImKeyModel.
+ //! \param model the key model to be queried
+ qreal normalizedKeyWidth(const MImKeyModel *model) const;
+
+ //! \brief Draws button rects/bounding rects, for debugging purposes.
+ //! \param painter the painter to be used
+ //! \param key key for which rects/bounding rects shall be drawn
+ //! \param drawBoundingRects whether to draw bounding rects
+ //! \param drawRects whether to draw rects
void drawDebugRects(QPainter *painter,
- const MImKey *button,
+ const MImAbstractKey *key,
bool drawBoundingRects,
- bool drawRects);
+ bool drawRects) const;
- //! \brief Draws reactive areas of buttons, for debugging purposes
+ //! \brief Draws reactive areas of buttons, for debugging purposes.
+ //! \param painter the painter to be used
void drawDebugReactiveAreas(QPainter *painter);
- struct ButtonRow {
- QList<MImKey*> buttons;
- QVector<QPair<qreal, qreal> > buttonOffsets;
+ //! \brief Initializes the pixmap used for background caching
+ //! \param size the new size
+ void initCachedBackground(const QSize &size);
+
+ //! \brief Helper struct to store a row of keys.
+ struct KeyRow {
+ QList<MImKey*> keys; //!< keys in a row
+ QVector<QPair<qreal, qreal> > keyOffsets; //!< cached offsets for faster key lookups
+ MImKey *stretchKey; //!< each row can have one stretched key
- //! each row can have one stretch button
- MImKey *stretchButton;
};
- typedef QVector<ButtonRow> ButtonRowList;
- typedef ButtonRowList::iterator RowIterator;
- typedef ButtonRowList::const_iterator ConstRowIterator;
-
- ButtonRowList rowList;
- qreal widgetHeight;
- qreal mMaxNormalizedWidth;
-
- QVector<QPair<int, int> > rowOffsets;
-
- //! Shift button is stored here if current layout has a shift button.
- MImKey *shiftButton;
-
- QTextLayout textLayout;
- bool textDirty;
-
- bool equalWidthButtons;
+ typedef QVector<KeyRow> KeyRowList;
+ typedef KeyRowList::iterator RowIterator;
+ typedef KeyRowList::const_iterator ConstRowIterator;
+
+ KeyRowList rowList; //!< stores all rows of this key area
+ qreal cachedWidgetHeight; //!< cached widget height
+ qreal mMaxNormalizedWidth; //!< maximal normalized width, for all rows
+ QVector<QPair<int, int> > rowOffsets; //!< cached offsets for faster key lookups
+ MImKey *shiftKey; //!< stores shift key, if available in this key area
+ QTextLayout textLayout; //!< used to draw key labels onto key area
+ bool textDirty; //!< dirty text cache flag
+ std::auto_ptr<QPixmap> cachedBackground; //!< cached background, containing all keys in inactive state
+ bool cachedBackgroundDirty; //!< dirty background cache flag
+ bool hasCachedBackground; //!< stores whether we already cached the background
+ bool equalWidthKeys; //!< whether to assume equal width for all keys
#ifdef UNIT_TEST
friend class Ut_MImAbstractKeyArea;
--- m-keyboard/widgets/mimwordlist.cpp
+++ m-keyboard/widgets/mimwordlist.cpp
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * and appearing in the file LICENSE.LGPL included in the packaging
+ * of this file.
+ */
+
+
+
+#include "mimwordlist.h"
+#include "mimwordlistitem.h"
+#include <mplainwindow.h>
+
+#include <QGraphicsLinearLayout>
+#include <QDebug>
+#include <QString>
+
+#include <MSceneManager>
+#include <MScene>
+#include <MGConfItem>
+#include <MContentItem>
+#include <MTheme>
+#include <mreactionmap.h>
+
+#include <mwidgetcreator.h>
+M_REGISTER_WIDGET_NO_CREATE(MImWordList)
+
+namespace
+{
+ const char * const WordListObjectName = "CorrectionWordList";
+};
+
+MIMWordListWindow::MIMWordListWindow(MImWordList *widget)
+ : MImOverlay(),
+ listWidget(widget)
+{
+ setVisible(false);
+}
+
+bool MIMWordListWindow::sceneEvent(QEvent *e)
+{
+ // TODO: below hiding list widget could be removed when meegotouch
+ // decide not hiding dialog when tap outside.
+ if (e->type() == QEvent::GraphicsSceneMouseRelease) {
+ listWidget->disappear();
+ }
+ return MImOverlay::sceneEvent(e);
+}
+
+void MIMWordListWindow::handleListAppeared()
+{
+ setVisible(true);
+ listWidget->setParentItem(this);
+}
+
+void MIMWordListWindow::handleListDisappeared()
+{
+ setVisible(false);
+}
+
+MImWordList::MImWordList()
+ : MDialog(),
+ parentWindow(new MIMWordListWindow(this))
+{
+ // for MATTI
+ setObjectName(WordListObjectName);
+
+ MWidget *contentWidget = new MWidget(this);
+ mainLayout = new QGraphicsLinearLayout(Qt::Vertical);
+ mainLayout->setSpacing(0);
+ mainLayout->setContentsMargins(0, 0, 0, 0);
+ contentWidget->setLayout(mainLayout);
+
+ for (int i = 0; i < MaxCandidateCount; i++) {
+ candidateItems[i] = new MImWordListItem(contentWidget);
+ candidateItems[i]->setVisible(false);
+ connect(candidateItems[i], SIGNAL(clicked()), this, SLOT(select()));
+ mainLayout->addItem(candidateItems[i]);
+ }
+ setCentralWidget(contentWidget);
+ hide();
+
+ connect(this, SIGNAL(visibleChanged()),
+ this, SLOT(handleVisibilityChanged()));
+ connect(this, SIGNAL(appeared()),
+ parentWindow, SLOT(handleListAppeared()));
+ connect(this, SIGNAL(disappeared()),
+ parentWindow, SLOT(handleListDisappeared()));
+}
+
+MImWordList::~MImWordList()
+{
+ setParentItem(0);
+ delete parentWindow;
+ parentWindow = 0;
+}
+
+void MImWordList::setCandidates(const QStringList &candidates)
+{
+ mCandidates = candidates.mid(0, MaxCandidateCount);
+
+ for (int i = 0; i < MaxCandidateCount; i++) {
+ if (i < candidates.count()) {
+ candidateItems[i]->setSelected(false);
+ mainLayout->addItem(candidateItems[i]);
+ candidateItems[i]->setTitle(candidates.at(i));
+ candidateItems[i]->setVisible(true);
+ candidateItems[i]->setEnabled(true);
+ candidateItems[i]->setLayoutPosition(M::VerticalCenterPosition);
+ } else {
+ mainLayout->removeItem(candidateItems[i]);
+ candidateItems[i]->setVisible(false);
+ }
+ }
+ mainLayout->invalidate();
+}
+
+QStringList MImWordList::candidates() const
+{
+ return mCandidates;
+}
+
+void MImWordList::setHighlightCandidate(const QString &candidate)
+{
+ int index = mCandidates.indexOf(candidate);
+ if (index >= 0) {
+ candidateItems[index]->setSelected(true);
+ }
+}
+
+void MImWordList::select()
+{
+ // ignore the select actions during animation
+ if (!isVisible() || sceneWindowState() == MSceneWindow::Appearing
+ || sceneWindowState() == MSceneWindow::Disappearing) {
+ return;
+ }
+ MImWordListItem *item = qobject_cast<MImWordListItem *> (sender());
+ if (item) {
+ const QString candidate = item->title();
+ emit candidateClicked(candidate);
+ }
+}
+
+void MImWordList::handleVisibilityChanged()
+{
+ emit regionChanged();
+}
+
+QRegion MImWordList::region() const
+{
+ QRegion ret;
+ if (isVisible()) {
+ const QSize visibleSceneSize = MPlainWindow::instance()->visibleSceneSize(M::Landscape);
+ ret = QRegion(0, 0, visibleSceneSize.width(), visibleSceneSize.height());
+ }
+ return ret;
+}
+
+void MImWordList::paintReactionMap(MReactionMap *reactionMap, QGraphicsView *)
+{
+ if (!isVisible())
+ return;
+ // word list take whole screen. And inner contentitem will play their
+ // default feedback. Don't need reaction map.
+ reactionMap->setInactiveDrawingValue();
+ reactionMap->setTransform(QTransform());
+ reactionMap->fillRectangle(0, 0, reactionMap->width(), reactionMap->height());
+}
--- m-keyboard/widgets/mimwordlist.h
+++ m-keyboard/widgets/mimwordlist.h
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * and appearing in the file LICENSE.LGPL included in the packaging
+ * of this file.
+ */
+
+
+#ifndef MIMWORDLIST_H
+#define MIMWORDLIST_H
+
+#include <MDialog>
+#include "mimoverlay.h"
+
+class QGraphicsLinearLayout;
+class MContentItem;
+class MImWordListItem;
+class MImWordList;
+class MReactionMap;
+
+/*!
+ * \brief MIMWordListWindow is used as the plain translucent parent window for word list dialog.
+ *
+ * MIMWordListWindow prevents mouse and touch events from reaching the virtual keyboard or the application.
+ * \sa MImOverlay.
+ */
+class MIMWordListWindow : public MImOverlay
+{
+ Q_OBJECT
+public:
+ //! Constructor
+ explicit MIMWordListWindow(MImWordList *widget);
+
+public slots:
+ /*
+ * \brief This slot is connected with word list widget's appeared() signal.
+ */
+ void handleListAppeared();
+
+ /*
+ * \brief This slot is connected with word list widget's disappeared() signal.
+ */
+ void handleListDisappeared();
+
+protected:
+ /*! \reimp */
+ virtual bool sceneEvent(QEvent *event);
+ /*! \reimp_end */
+
+private:
+ MImWordList *listWidget;
+};
+
+/*!
+ * \brief MIMWordList is used for word list dialog.
+ *
+ * MImWordList shows a dialog which list the suggested candidates.
+ */
+class MImWordList : public MDialog
+{
+ Q_OBJECT
+ friend class Ut_MImWordList;
+ friend class Ut_MImCorrectionCandidateWidget;
+
+public:
+ enum {
+ MaxCandidateCount = 5
+ };
+
+ //! Constructor
+ explicit MImWordList();
+
+ //! Destructor
+ virtual ~MImWordList();
+
+ /*!
+ * \brief Sets suggestion candidates.
+ */
+ void setCandidates(const QStringList &candidates);
+
+ /*!
+ * \brief Returns the suggestion candidates.
+ */
+ QStringList candidates() const;
+
+ /*!
+ * \brief Sets the highlight suggestion candidate.
+ */
+ void setHighlightCandidate(const QString &);
+
+ /*!
+ * \brief Returns the occupied region of word list widget.
+ */
+ QRegion region() const;
+
+ /*!
+ * \brief Draw its reactive areas onto the reaction map
+ */
+ void paintReactionMap(MReactionMap *reactionMap, QGraphicsView *view);
+
+signals:
+ /*!
+ * \brief This signal is emitted when clicking on a candidate.
+ */
+ void candidateClicked(const QString &);
+
+ //! Emitted when the occupied region of word list is changed.
+ void regionChanged();
+
+private slots:
+
+ void select();
+
+ void handleVisibilityChanged();
+
+private:
+ QStringList mCandidates;
+ QGraphicsLinearLayout *mainLayout;
+ MImWordListItem *candidateItems[MaxCandidateCount];
+ MIMWordListWindow *parentWindow;
+};
+
+#endif
--- m-keyboard/widgets/mimwordlistitem.cpp
+++ m-keyboard/widgets/mimwordlistitem.cpp
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * and appearing in the file LICENSE.LGPL included in the packaging
+ * of this file.
+ */
+
+
+
+#include "mimwordlistitem.h"
+
+#include <mwidgetcreator.h>
+M_REGISTER_WIDGET_NO_CREATE(MImWordListItem)
+
+MImWordListItem::MImWordListItem(QGraphicsItem *parent)
+ : MContentItem(MContentItem::SingleTextLabel, parent)
+{
+}
--- m-keyboard/widgets/mimwordlistitem.h
+++ m-keyboard/widgets/mimwordlistitem.h
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * and appearing in the file LICENSE.LGPL included in the packaging
+ * of this file.
+ */
+
+
+
+#ifndef MIMWORDLISTITEM_H
+#define MIMWORDLISTITEM_H
+
+#include <MContentItem>
+
+class MImWordListItem : public MContentItem
+{
+ Q_OBJECT
+public:
+ explicit MImWordListItem(QGraphicsItem *parent = 0);
+};
+
+#endif
--- m-keyboard/widgets/mimwordtracker.cpp
+++ m-keyboard/widgets/mimwordtracker.cpp
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * and appearing in the file LICENSE.LGPL included in the packaging
+ * of this file.
+ */
+
+
+
+#include "mimwordtracker.h"
+#include "mimcorrectioncandidateitem.h"
+
+#include <QGraphicsLinearLayout>
+#include <QGraphicsSceneMouseEvent>
+#include <QDebug>
+
+#include <mplainwindow.h>
+#include <mreactionmap.h>
+#include <MScalableImage>
+#include <MSceneManager>
+#include <MScene>
+#include <MSceneWindow>
+#include <MGConfItem>
+
+#include <mwidgetcreator.h>
+M_REGISTER_WIDGET_NO_CREATE(MImWordTracker)
+
+namespace
+{
+ // This GConf item defines whether multitouch is enabled or disabled
+ const char * const MultitouchSetting = "/meegotouch/inputmethods/multitouch/enabled";
+
+ const char * const WordTrackerObjectName = "CorrectionWordTracker";
+ const int DefaultShowHideFrames = 100;
+ const int DefaultShowHideTime = 400;
+ const int DefaultShowHideInterval = 30;
+
+ QRect mapToScreenRect(const QRect &widgetRect)
+ {
+ if (!widgetRect.isValid()) {
+ return QRect();
+ }
+
+ M::OrientationAngle angle = MPlainWindow::instance()->orientationAngle();
+ int displayHeight = MPlainWindow::instance()->visibleSceneSize(M::Landscape).height();
+ int displayWidth = MPlainWindow::instance()->visibleSceneSize(M::Landscape).width();
+
+ QRect rect;
+ switch (angle) {
+ case M::Angle90:
+ rect.setRect(displayWidth - widgetRect.y()- widgetRect.height(),
+ widgetRect.x(),
+ widgetRect.height(), widgetRect.width());
+ break;
+ case M::Angle270:
+ rect.setRect(widgetRect.y(),
+ displayHeight - widgetRect.x() - widgetRect.width(),
+ widgetRect.height(), widgetRect.width());
+ break;
+ case M::Angle180:
+ rect.setRect(displayWidth - widgetRect.x() - widgetRect.width(),
+ displayHeight - widgetRect.y() - widgetRect.height(),
+ widgetRect.width(), widgetRect.height());
+ break;
+ case M::Angle0:
+ rect = widgetRect;
+ break;
+ default:
+ qCritical() << __FILE__ << __LINE__ << " Incorrect orientation " << angle;
+ rect = QRect();
+ break;
+ }
+ return rect;
+ }
+};
+
+
+MImWordTracker::MImWordTracker(MSceneWindow *parentWindow)
+ : MStylableWidget(),
+ containerWidget(new QGraphicsWidget()),
+ mIdealWidth(0),
+ candidateItem(new MImCorrectionCandidateItem("", this))
+{
+ containerWidget->setParentItem(parentWindow);
+ this->setParentItem(containerWidget);
+
+ // By default multi-touch is disabled
+ setAcceptTouchEvents(MGConfItem(MultitouchSetting).value().toBool());
+
+ setObjectName(WordTrackerObjectName);
+
+ mainLayout = new QGraphicsLinearLayout(Qt::Vertical);
+ mainLayout->setSpacing(0);
+ mainLayout->setContentsMargins(0, 0, 0, 0);
+ setLayout(mainLayout);
+ mainLayout->addItem(candidateItem);
+ mainLayout->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ connect(candidateItem, SIGNAL(clicked()), this, SLOT(select()));
+ connect(candidateItem, SIGNAL(longTapped()), this, SLOT(longTap()));
+
+ connect(MTheme::instance(), SIGNAL(themeChangeCompleted()),
+ this, SLOT(onThemeChangeCompleted()),
+ Qt::UniqueConnection);
+
+ setupTimeLine();
+ containerWidget->hide();
+}
+
+MImWordTracker::~MImWordTracker()
+{
+ this->setParentItem(0);
+ delete containerWidget;
+}
+
+void MImWordTracker::setCandidate(const QString &string)
+{
+ mCandidate = string;
+ if (isVisible()
+ && showHideTimeline.state() == QTimeLine::Running
+ && showHideTimeline.direction() == QTimeLine::Backward) {
+ // don't update during hiding animation
+ return;
+ }
+ candidateItem->setTitle(string);
+
+ mIdealWidth = candidateItem->idealWidth();;
+ // not less than minimum width
+ if (mIdealWidth < minimumSize().width())
+ mIdealWidth = minimumSize().width();
+
+ mIdealWidth += style()->paddingLeft() + style()->paddingRight()
+ + style()->marginLeft() + style()->marginRight();
+ setPreferredWidth(mIdealWidth);
+}
+
+QString MImWordTracker::candidate() const
+{
+ return mCandidate;
+}
+
+qreal MImWordTracker::idealWidth() const
+{
+ return mIdealWidth;
+}
+
+qreal MImWordTracker::pointerHeight() const
+{
+ return (style()->wordtrackerPointerSize().height()
+ - style()->wordtrackerPointerOverlap());
+}
+
+void MImWordTracker::drawBackground(QPainter *painter, const QStyleOptionGraphicsItem *option) const
+{
+ MStylableWidget::drawBackground(painter, option);
+ if (style()->wordtrackerPointerImage()) {
+ const QSize pointerSize = style()->wordtrackerPointerSize();
+ QRect rect = QRect((idealWidth() - pointerSize.width())/2,
+ style()->wordtrackerPointerOverlap() - pointerSize.height(),
+ pointerSize.width(),
+ pointerSize.height());
+ style()->wordtrackerPointerImage()->draw(rect, painter);
+ }
+}
+
+void MImWordTracker::select()
+{
+ if (showHideTimeline.state() == QTimeLine::Running) {
+ // Ignore select actions during animation.
+ return;
+ }
+ if (!mCandidate.isEmpty()) {
+ emit candidateClicked(mCandidate);
+ }
+}
+
+void MImWordTracker::longTap()
+{
+ if (showHideTimeline.state() == QTimeLine::Running) {
+ // Ignore select actions during animation.
+ return;
+ }
+ if (!mCandidate.isEmpty()) {
+ emit longTapped();
+ }
+}
+
+void MImWordTracker::setupTimeLine()
+{
+ int showHideFrames = style()->showHideFrames();
+ showHideFrames = (showHideFrames > 0) ? showHideFrames : DefaultShowHideFrames;
+ int showHideTime = style()->showHideTime();
+ showHideTime = (showHideTime > 0) ? showHideTime : DefaultShowHideTime;
+ int showHideInterval = style()->showHideInterval();
+ showHideInterval = (showHideInterval > 0) ? showHideInterval : DefaultShowHideInterval;
+
+ showHideTimeline.setCurveShape(QTimeLine::EaseInCurve);
+ showHideTimeline.setFrameRange(0, showHideFrames);
+ showHideTimeline.setDuration(showHideTime);
+ showHideTimeline.setUpdateInterval(showHideInterval);
+ connect(&showHideTimeline, SIGNAL(frameChanged(int)), this, SLOT(fade(int)), Qt::UniqueConnection);
+ connect(&showHideTimeline, SIGNAL(finished()), this, SLOT(showHideFinished()), Qt::UniqueConnection);
+}
+
+void MImWordTracker::fade(int frame)
+{
+ int showHideFrames = showHideTimeline.endFrame();
+ showHideFrames = (showHideFrames > 0) ? showHideFrames : DefaultShowHideFrames;
+ const qreal opacity = qreal(frame) / showHideFrames;
+ parentWidget()->setOpacity(opacity);
+ parentWidget()->update();
+}
+
+
+void MImWordTracker::showHideFinished()
+{
+ const bool hiding = (showHideTimeline.direction() == QTimeLine::Backward);
+
+ if (hiding) {
+ containerWidget->hide();
+ emit regionChanged();
+ }
+}
+
+void MImWordTracker::appear(bool withAnimation)
+{
+ if (!isVisible()) {
+ if (withAnimation) {
+ showHideTimeline.setDirection(QTimeLine::Forward);
+ if (showHideTimeline.state() != QTimeLine::Running) {
+ showHideTimeline.start();
+ }
+ }
+ containerWidget->show();
+ show();
+ } else {
+ containerWidget->update();
+ }
+ emit regionChanged();
+}
+
+void MImWordTracker::disappear(bool withAnimation)
+{
+ if (!isVisible())
+ return;
+
+ if (withAnimation) {
+ showHideTimeline.setDirection(QTimeLine::Backward);
+ if (showHideTimeline.state() != QTimeLine::Running) {
+ showHideTimeline.start();
+ }
+ // will hide and emit regionChanged when timeline is finished
+ } else {
+ containerWidget->hide();
+ emit regionChanged();
+ }
+}
+
+void MImWordTracker::setPosition(const QPoint &pos)
+{
+ QRectF widgetRect, containerRect;
+ QSizeF containerSize = preferredSize();
+ containerSize.setHeight(containerSize.height() + pointerHeight());
+ containerRect = QRectF(pos, containerSize);
+ widgetRect = QRectF(QPointF(0, pointerHeight()), preferredSize());
+
+ containerWidget->setGeometry(containerRect);
+ setGeometry(widgetRect);
+
+ if (isVisible()) {
+ containerWidget->update();
+ emit regionChanged();
+ }
+}
+
+QRegion MImWordTracker::region() const
+{
+ QRegion ret;
+ if (isVisible()) {
+ ret = mapToScreenRect(containerWidget->geometry().toRect());
+ }
+ return ret;
+}
+
+void MImWordTracker::onThemeChangeCompleted()
+{
+ // reset time line
+ setupTimeLine();
+}
+
+void MImWordTracker::paintReactionMap(MReactionMap *reactionMap, QGraphicsView *view)
+{
+ if (!isVisible())
+ return;
+
+ // Clear all with inactive color.
+ reactionMap->setTransform(this, view);
+ reactionMap->setInactiveDrawingValue();
+ reactionMap->fillRectangle(geometry());
+
+ // Draw the actual word tracker area.
+ reactionMap->setReactiveDrawingValue();
+ reactionMap->fillRectangle(geometry());
+}
--- m-keyboard/widgets/mimwordtracker.h
+++ m-keyboard/widgets/mimwordtracker.h
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * and appearing in the file LICENSE.LGPL included in the packaging
+ * of this file.
+ */
+
+
+
+#ifndef MIMWORDTRACKER_H
+#define MIMWORDTRACKER_H
+
+#include <MStylableWidget>
+#include "mimwordtrackerstyle.h"
+
+#include <QTimeLine>
+
+class QGraphicsWidget;
+class QGraphicsLinearLayout;
+class MImCorrectionCandidateItem;
+class MSceneWindow;
+class MReactionMap;
+
+/*!
+ * \brief The MImWordTracker class is used to show error correction word tracker.
+ */
+class MImWordTracker : public MStylableWidget
+{
+ Q_OBJECT
+ friend class Ut_MImWordTracker;
+ friend class Ut_MImCorrectionCandidateWidget;
+
+public:
+ //! Constructor
+ explicit MImWordTracker(MSceneWindow *parentWindow);
+
+ //! Destructor
+ ~MImWordTracker();
+
+ /*!
+ * \brief Set suggested candidate.
+ */
+ void setCandidate(const QString &);
+
+ /*!
+ * \brief Returns the suggested candidate.
+ */
+ QString candidate() const;
+
+ /*!
+ * \brief Returns the ideal width of the word tracker.
+ *
+ * The ideal width is the actually used width of the word tracker together with margins and paddings.
+ */
+ qreal idealWidth() const;
+
+ /*!
+ * \brief Returns the height of pointer for word tracker.
+ */
+ qreal pointerHeight() const;
+
+ /*!
+ * \brief Appears word tracker widget with or without default animation.
+ */
+ void appear(bool withAnimation = false);
+
+ /*!
+ * \brief Disappear word tracker widget with or without default animation.
+ */
+ void disappear(bool withAnimation = false);
+
+ /*!
+ * \brief Sets the position of word tracker.
+ */
+ void setPosition(const QPoint &pos);
+
+ /*!
+ * \brief Returns the visible region of word tracker.
+ */
+ QRegion region() const;
+
+ /*!
+ * \brief Draw its reactive areas onto the reaction map
+ */
+ void paintReactionMap(MReactionMap *reactionMap, QGraphicsView *view);
+
+signals:
+ //! Emitted when word tracker is clicked.
+ void candidateClicked(const QString &);
+
+ /*!
+ * \brief The signal is emitted when the word tracker has been tapped and holded.
+ */
+ void longTapped();
+
+ //! Emitted when the occupied region of word tracker is changed.
+ void regionChanged();
+
+protected slots:
+ void select();
+
+ void longTap();
+
+ /*!
+ * Method to fade the vkb during transition
+ */
+ void fade(int);
+
+ /*!
+ * This function gets called when fading is finished
+ */
+ void showHideFinished();
+
+ /*!
+ * Update stored style stuffs when the theme changed.
+ */
+ void onThemeChangeCompleted();
+
+protected:
+ /*! \reimp */
+ virtual void drawBackground(QPainter *painter, const QStyleOptionGraphicsItem *option) const;
+ /*! \reimp_end */
+
+private:
+ void setupTimeLine();
+
+ QGraphicsWidget *containerWidget;
+ QString mCandidate;
+ int mIdealWidth;
+ QGraphicsLinearLayout *mainLayout;
+ MImCorrectionCandidateItem *candidateItem;
+ QTimeLine showHideTimeline;
+
+ M_STYLABLE_WIDGET(MImWordTrackerStyle)
+};
+
+#endif
--- m-keyboard/widgets/mimwordtrackerstyle.h
+++ m-keyboard/widgets/mimwordtrackerstyle.h
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * and appearing in the file LICENSE.LGPL included in the packaging
+ * of this file.
+ */
+
+#ifndef MIMWORDTRACKERSTYLE_H
+#define MIMWORDTRACKERSTYLE_H
+
+#include <MWidgetStyle>
+
+class MImWordTrackerStyle : public MWidgetStyle
+{
+ Q_OBJECT
+ M_STYLE(MImWordTrackerStyle)
+
+ M_STYLE_PTR_ATTRIBUTE(MScalableImage *, wordtrackerPointerImage, WordtrackPointerImage)
+ M_STYLE_ATTRIBUTE(QSize, wordtrackerPointerSize, WordtrackerPointerSize)
+ M_STYLE_ATTRIBUTE(int, wordtrackerPointerOverlap, WordtrackerPointerOverlap)
+
+ M_STYLE_ATTRIBUTE(int, showHideFrames, ShowHideFrames)
+ M_STYLE_ATTRIBUTE(int, showHideTime, ShowHideTime)
+ M_STYLE_ATTRIBUTE(int, showHideInterval, ShowHideInterval)
+};
+
+class M_EXPORT MImWordTrackerStyleContainer : public MWidgetStyleContainer
+{
+ M_STYLE_CONTAINER(MImWordTrackerStyle)
+};
+
+#endif
+
--- m-keyboard/widgets/mkeyboardsettingswidget.cpp
+++ m-keyboard/widgets/mkeyboardsettingswidget.cpp
@@ -40,7 +40,7 @@
//!object name for settings' widgets
const QString ObjectNameSelectedKeyboardsItem("SelectedKeyboardsItem");
const QString ObjectNameErrorCorrectionButton("KeyboardErrorCorrectionButton");
-
+ const QString ObjectNameWordCompletionButton("KeyboardWordCompletionButton");
const int MKeyboardLayoutRole = Qt::UserRole + 1;
};
@@ -129,6 +129,7 @@
connect(selectedKeyboardsItem, SIGNAL(clicked()), this, SLOT(showKeyboardList()));
addItem(selectedKeyboardsItem);
+ // Error correction settings
errorCorrectionSwitch = new MButton(this);
errorCorrectionSwitch->setObjectName(ObjectNameErrorCorrectionButton);
errorCorrectionSwitch->setViewType(MButton::switchType);
@@ -137,11 +138,34 @@
//% "Error correction"
errorCorrectionLabel->setText(qtTrId("qtn_txts_error_correction"));
errorCorrectionLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
- QGraphicsLinearLayout *l = new QGraphicsLinearLayout(Qt::Horizontal);
- l->addItem(errorCorrectionLabel);
- l->addItem(errorCorrectionSwitch);
- l->setAlignment(errorCorrectionSwitch, Qt::AlignCenter);
- (qobject_cast<MKeyboardSettingsWidget *>(this))->addItem(l);
+ QGraphicsLinearLayout *eCLayout = new QGraphicsLinearLayout(Qt::Horizontal);
+ eCLayout->addItem(errorCorrectionLabel);
+ eCLayout->addItem(errorCorrectionSwitch);
+ eCLayout->setAlignment(errorCorrectionSwitch, Qt::AlignCenter);
+
+ // Word completion settings
+ wordCompletionSwitch = new MButton(this);
+ wordCompletionSwitch->setObjectName(ObjectNameWordCompletionButton);
+ wordCompletionSwitch->setViewType(MButton::switchType);
+ wordCompletionSwitch->setCheckable(true);
+ wordCompletionLabel = new MLabel(this);
+ //% "Word completion"
+ wordCompletionLabel->setText(qtTrId("qtn_txts_word_completion"));
+ wordCompletionLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+ QGraphicsLinearLayout *wCLayout = new QGraphicsLinearLayout(Qt::Horizontal);
+ wCLayout->addItem(wordCompletionLabel);
+ wCLayout->addItem(wordCompletionSwitch);
+ wCLayout->setAlignment(wordCompletionSwitch, Qt::AlignCenter);
+
+ // Add the error correction+word completion widgets to this layout to
+ // have proper alignment of the widgets to the right side.
+ QGraphicsLinearLayout *vertLayout = new QGraphicsLinearLayout(Qt::Vertical);
+
+ // Add the error correction widgets to a vertical layout
+ vertLayout->addItem(eCLayout);
+ // Add the word completion widgets to a vertical layout
+ vertLayout->addItem(wCLayout);
+ addItem(vertLayout);
}
void MKeyboardSettingsWidget::addItem(QGraphicsLayoutItem *item)
@@ -162,10 +186,14 @@
void MKeyboardSettingsWidget::updateTitle()
{
- if (!errorCorrectionLabel || !settingsObject || !selectedKeyboardsItem)
+ if (!errorCorrectionLabel || !wordCompletionLabel
+ || !settingsObject || !selectedKeyboardsItem)
return;
+
//% "Error correction"
errorCorrectionLabel->setText(qtTrId("qtn_txts_error_correction"));
+ //% "Word completion"
+ wordCompletionLabel->setText(qtTrId("qtn_txts_word_completion"));
QStringList keyboards = settingsObject->selectedKeyboards().values();
//% "Installed keyboards (%1)"
QString title = qtTrId("qtn_txts_installed_keyboards")
@@ -194,13 +222,17 @@
connect(this, SIGNAL(visibleChanged()),
this, SLOT(handleVisibilityChanged()));
- if (!settingsObject || !errorCorrectionSwitch)
+ if (!settingsObject || !errorCorrectionSwitch || !wordCompletionSwitch)
return;
connect(errorCorrectionSwitch, SIGNAL(toggled(bool)),
this, SLOT(setErrorCorrectionState(bool)));
connect(settingsObject, SIGNAL(errorCorrectionChanged()),
this, SLOT(syncErrorCorrectionState()));
+ connect(wordCompletionSwitch, SIGNAL(toggled(bool)),
+ this, SLOT(setWordCompletionState(bool)));
+ connect(settingsObject, SIGNAL(wordCompletionChanged()),
+ this, SLOT(syncWordCompletionState()));
connect(settingsObject, SIGNAL(selectedKeyboardsChanged()),
this, SLOT(updateTitle()));
connect(settingsObject, SIGNAL(selectedKeyboardsChanged()),
@@ -237,6 +269,7 @@
{
if (!settingsObject || !keyboardList)
return;
+
//always reload available layouts in case user install/remove some layouts
settingsObject->readAvailableKeyboards();
QStandardItemModel *model = static_cast<QStandardItemModel*> (keyboardList->itemModel());
@@ -258,6 +291,7 @@
{
if (!settingsObject || !keyboardList)
return;
+
QStandardItemModel *model = static_cast<QStandardItemModel*> (keyboardList->itemModel());
foreach (const QString &keyboard, settingsObject->selectedKeyboards().values()) {
QList<QStandardItem *> items = model->findItems(keyboard);
@@ -285,18 +319,20 @@
retranslateUi();
}
-void MKeyboardSettingsWidget::setErrorCorrectionState(bool toggled)
+void MKeyboardSettingsWidget::setErrorCorrectionState(bool enabled)
{
if (!settingsObject)
return;
- if (toggled != settingsObject->errorCorrection())
- settingsObject->setErrorCorrection(toggled) ;
+
+ if (settingsObject->errorCorrection() != enabled)
+ settingsObject->setErrorCorrection(enabled) ;
}
void MKeyboardSettingsWidget::syncErrorCorrectionState()
{
if (!settingsObject)
return;
+
const bool errorCorrectionState = settingsObject->errorCorrection();
if (errorCorrectionSwitch
&& errorCorrectionSwitch->isChecked() != errorCorrectionState) {
@@ -304,6 +340,27 @@
}
}
+void MKeyboardSettingsWidget::setWordCompletionState(bool enabled)
+{
+ if (!settingsObject)
+ return;
+
+ if (settingsObject->wordCompletion() != enabled)
+ settingsObject->setWordCompletion(enabled) ;
+}
+
+void MKeyboardSettingsWidget::syncWordCompletionState()
+{
+ if (!settingsObject)
+ return;
+
+ const bool wordCompletionState = settingsObject->wordCompletion();
+ if (wordCompletionSwitch
+ && wordCompletionSwitch->isChecked() != wordCompletionState) {
+ wordCompletionSwitch->setChecked(wordCompletionState);
+ }
+}
+
void MKeyboardSettingsWidget::notifyNoKeyboards()
{
MBanner *noKeyboardsNotification = new MBanner;
--- m-keyboard/widgets/mkeyboardsettingswidget.h
+++ m-keyboard/widgets/mkeyboardsettingswidget.h
@@ -51,6 +51,8 @@
void updateSelectedKeyboards(const QModelIndex &);
void setErrorCorrectionState(bool toggled);
void syncErrorCorrectionState();
+ void setWordCompletionState(bool toggled);
+ void syncWordCompletionState();
void handleVisibilityChanged();
private:
@@ -65,6 +67,8 @@
MLinearLayoutPolicy *portraitPolicy;
MButton *errorCorrectionSwitch;
MLabel *errorCorrectionLabel;
+ MButton *wordCompletionSwitch;
+ MLabel *wordCompletionLabel;
MDialog *keyboardDialog;
MList *keyboardList;
MContentItem *selectedKeyboardsItem;
--- m-keyboard/widgets/mtoolbarbutton.cpp
+++ m-keyboard/widgets/mtoolbarbutton.cpp
@@ -61,6 +61,7 @@
connect(this, SIGNAL(clicked(bool)),
itemPtr.data(), SLOT(setPressed(bool)));
}
+ setEnabled(itemPtr->enabled());
setVisible(item->isVisible());
connect(this, SIGNAL(clicked(bool)),
@@ -154,6 +155,8 @@
setText(qtTrId(itemPtr->textId().toUtf8().data()));
} else if (attribute == "pressed" && itemPtr->toggle()) {
setChecked(itemPtr->pressed());
+ } else if (attribute == "enabled") {
+ setEnabled(itemPtr->enabled());
} else if (attribute == "visible") {
setVisible(itemPtr->isVisible());
emit availabilityChanged();
--- m-keyboard/widgets/mvirtualkeyboard.cpp
+++ m-keyboard/widgets/mvirtualkeyboard.cpp
@@ -93,6 +93,8 @@
this, SIGNAL(keyReleased(const KeyEvent &)));
connect(&eventHandler, SIGNAL(keyClicked(const KeyEvent &)),
this, SIGNAL(keyClicked(const KeyEvent &)));
+ connect(&eventHandler, SIGNAL(longKeyPressed(const KeyEvent &)),
+ this, SIGNAL(longKeyPressed(const KeyEvent &)));
connect(&eventHandler, SIGNAL(shiftPressed(bool)),
this, SLOT(handleShiftPressed(bool)));
@@ -406,7 +408,7 @@
void
MVirtualKeyboard::fade(int frame)
{
- const float opacity = float(frame) / ShowHideFrames;
+ const qreal opacity = static_cast<qreal>(frame) / ShowHideFrames;
const QSize sceneSize = sceneManager->visibleSceneSize();
if (!hideShowByFadingOnly) {
@@ -537,7 +539,16 @@
activeState = newState;
resetState();
+
static_cast<QGraphicsWidget *>(mainLayout->itemAt(KeyboardIndex))->setVisible(newState == MInputMethod::OnScreen);
+ if (newState != MInputMethod::OnScreen) {
+ MImAbstractKeyArea *keyArea = dynamic_cast<MImAbstractKeyArea *>(mainKeyboardSwitcher->currentWidget());
+
+ if (keyArea) {
+ keyArea->hidePopup();
+ }
+ }
+
showHideTimeline.stop(); // position must be updated by organizeContentAndSendRegion()
organizeContentAndSendRegion();
sendRegionUpdates = savedSendRegionUpdates;
@@ -785,7 +796,7 @@
// Load certain type and orientation from all layouts.
foreach (const QString &layoutFile, layoutsMgr.layoutFileList()) {
MImAbstractKeyArea *mainSection = createMainSectionView(layoutFile, LayoutData::General,
- currentOrientation);
+ currentOrientation);
mainSection->setObjectName("VirtualKeyboardMainRow");
mainSection->setPreferredWidth(MPlainWindow::instance()->visibleSceneSize().width());
mainKeyboardSwitcher->addWidget(mainSection);
@@ -794,31 +805,32 @@
MImAbstractKeyArea *MVirtualKeyboard::createMainSectionView(const QString &layout,
- LayoutData::LayoutType layoutType,
- M::Orientation orientation,
- QGraphicsWidget *parent)
-{
- MImAbstractKeyArea *buttonArea = createSectionView(layout, layoutType, orientation,
- LayoutData::mainSection,
- true, parent);
+ LayoutData::LayoutType layoutType,
+ M::Orientation orientation,
+ QGraphicsWidget *parent)
+{
+ MImAbstractKeyArea *keyArea = createSectionView(layout, layoutType, orientation,
+ LayoutData::mainSection,
+ true, parent);
// horizontal flick handling only on main section of qwerty
- connect(buttonArea, SIGNAL(flickLeft()), this, SLOT(flickLeftHandler()));
- connect(buttonArea, SIGNAL(flickRight()), this, SLOT(flickRightHandler()));
+ connect(keyArea, SIGNAL(flickLeft()), this, SLOT(flickLeftHandler()));
+ connect(keyArea, SIGNAL(flickRight()), this, SLOT(flickRightHandler()));
+ connect(this, SIGNAL(hidden()), keyArea, SLOT(hidePopup()));
- return buttonArea;
+ return keyArea;
}
MImAbstractKeyArea * MVirtualKeyboard::createSectionView(const QString &layout,
- LayoutData::LayoutType layoutType,
- M::Orientation orientation,
- const QString §ion,
- bool usePopup,
- QGraphicsWidget *parent)
+ LayoutData::LayoutType layoutType,
+ M::Orientation orientation,
+ const QString §ion,
+ bool usePopup,
+ QGraphicsWidget *parent)
{
const LayoutData *model = layoutsMgr.layout(layout, layoutType, orientation);
MImAbstractKeyArea *view = new MImKeyArea(model->section(section),
- usePopup, parent);
+ usePopup, parent);
eventHandler.addEventSource(view);
@@ -1039,11 +1051,11 @@
MImAbstractKeyArea *mainKb = keyboardWidget();
foreach (const MImAbstractKey *ikey, mainKb->keys()) {
// only care about the keys which insert characters.
- if (ikey->key().binding()->action() == MImKeyBinding::ActionInsert) {
+ if (ikey->model().binding()->action() == MImKeyBinding::ActionInsert) {
bool isPunctuation = false;
bool isSymbol = false;
QList<QChar> symbols;
- foreach (const QChar &c, ikey->key().binding()->label()) {
+ foreach (const QChar &c, ikey->model().binding()->label()) {
symbols << c;
if (c.isPunct())
isPunctuation = true;
@@ -1054,7 +1066,7 @@
if (isSymbol)
continue;
- foreach (const QChar &c, ikey->key().binding()->accentedLabels()) {
+ foreach (const QChar &c, ikey->model().binding()->accentedLabels()) {
symbols << c;
}
--- m-keyboard/widgets/mvirtualkeyboard.h
+++ m-keyboard/widgets/mvirtualkeyboard.h
@@ -294,6 +294,11 @@
*/
void keyClicked(const KeyEvent &event);
+ /*!
+ * \brief Emitted when key is long pressed
+ */
+ void longKeyPressed(const KeyEvent &event);
+
//! \see MAbstractInputMethod::regionUpdated()
void regionUpdated(const QRegion &);
@@ -313,15 +318,6 @@
*/
void copyPasteClicked(CopyPasteState action);
- //! Emitted when require a copy/paste action
- void copyPasteRequest(CopyPasteState);
-
- //! Emitted when require sending a keyevent
- void sendKeyEventRequest(const QKeyEvent &);
-
- //! Emitted when require sending a string
- void sendStringRequest(const QString &);
-
//! Emitted when shift state is changed
void shiftLevelChanged();
--- m-keyboard/widgets/mvirtualkeyboardstyle.h
+++ m-keyboard/widgets/mvirtualkeyboardstyle.h
@@ -44,6 +44,7 @@
M_STYLE_ATTRIBUTE(QSize, menuSize, MenuSize)
M_STYLE_ATTRIBUTE(QFont, notificationFont, NotificationFont)
+ M_STYLE_ATTRIBUTE(int, notificationFontSize, NotificationFontSize)
M_STYLE_ATTRIBUTE(QColor, notificationBorderColor, NotificationBorderColor)
M_STYLE_ATTRIBUTE(QColor, notificationBackgroundColor, NotificationBackgroundColor)
M_STYLE_ATTRIBUTE(QColor, notificationTextColor, NotificationTextColor)
--- m-keyboard/widgets/notification.cpp
+++ m-keyboard/widgets/notification.cpp
@@ -37,7 +37,8 @@
Notification::Notification(const MVirtualKeyboardStyleContainer *style, QGraphicsWidget *parent)
: MWidget(parent),
- styleContainer(style)
+ styleContainer(style),
+ opacity(0)
{
// Notification sets its own absolute opacity
setFlag(ItemIgnoresParentOpacity, true);
@@ -124,6 +125,7 @@
void Notification::getStyleValues()
{
font = style()->notificationFont();
+ font.setPixelSize(style()->notificationFontSize());
border = style()->notificationBorderColor();
background = style()->notificationBackgroundColor();
textColor = style()->notificationTextColor();
--- m-keyboard/widgets/popupbase.h
+++ m-keyboard/widgets/popupbase.h
@@ -68,8 +68,8 @@
//! Returns whether PopupBase has any visible components
virtual bool isVisible() const = 0;
- //! Enables/disables PopupBase completely (affects visibility)
- virtual void setEnabled(bool ok) = 0;
+ //! Toggles visibility of PopupBase
+ virtual void setVisible(bool visible) = 0;
};
#endif
--- m-keyboard/widgets/popupfactory.cpp
+++ m-keyboard/widgets/popupfactory.cpp
@@ -62,7 +62,7 @@
return false;
}
- virtual void setEnabled(bool)
+ virtual void setVisible(bool)
{}
//! \reimp_end
};
--- m-keyboard/widgets/symbolview.cpp
+++ m-keyboard/widgets/symbolview.cpp
@@ -148,6 +148,8 @@
this, SIGNAL(keyReleased(KeyEvent)));
connect(&eventHandler, SIGNAL(keyClicked(KeyEvent)),
this, SIGNAL(keyClicked(KeyEvent)));
+ connect(&eventHandler, SIGNAL(longKeyPressed(const KeyEvent &)),
+ this, SIGNAL(longKeyPressed(const KeyEvent &)));
connect(&eventHandler, SIGNAL(shiftPressed(bool)),
this, SLOT(handleShiftPressed(bool)));
--- m-keyboard/widgets/symbolview.h
+++ m-keyboard/widgets/symbolview.h
@@ -168,6 +168,11 @@
*/
void keyReleased(const KeyEvent &event);
+ /*!
+ * \brief Emitted when key is long pressed
+ */
+ void longKeyPressed(const KeyEvent &event);
+
//! Emitted when SymbolView has changed its interactive region.
void regionUpdated(const QRegion &);
--- m-keyboard/widgets/widgetbar.cpp
+++ m-keyboard/widgets/widgetbar.cpp
@@ -34,10 +34,6 @@
{
mainLayout.setSpacing(0); // Spacing is handled by dividers.
mainLayout.setContentsMargins(0, 0, 0, 0);
-
- // Update style by calling styleChanged(). This if for widgets that don't have
- // object name set.
- styleChanged();
}
WidgetBar::~WidgetBar()
@@ -105,12 +101,6 @@
// Stop propagating
}
-void WidgetBar::styleChanged()
-{
- setContentsMargins(style()->paddingLeft(), style()->paddingTop(),
- style()->paddingRight(), 0);
-}
-
QSizeF WidgetBar::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
// if there is no visible items in the widgetbar, just return empty size.
--- m-keyboard/widgets/widgetbar.h
+++ m-keyboard/widgets/widgetbar.h
@@ -89,7 +89,6 @@
protected:
//! \reimp
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
- virtual void styleChanged();
//! \reimp_end
private:
--- m-keyboard/widgets/widgets.pri
+++ m-keyboard/widgets/widgets.pri
@@ -19,7 +19,7 @@
$$WIDGETS_DIR/handlestyle.h \
$$WIDGETS_DIR/mtoolbarbuttonstyle.h \
$$WIDGETS_DIR/mimtoolbarstyle.h \
- $$WIDGETS_DIR/mimcorrectioncandidatecontainerstyle.h \
+ $$WIDGETS_DIR/mimwordtrackerstyle.h \
$$WIDGETS_DIR/mimcorrectioncandidateitemstyle.h \
$$WIDGETS_DIR/mtoolbarlabelstyle.h \
@@ -27,9 +27,11 @@
$$PUBLIC_HEADERS \
$$STYLE_HEADERS \
$$WIDGETS_DIR/widgetbar.h \
- $$WIDGETS_DIR/mimcorrectioncandidatewidget.h \
+ $$WIDGETS_DIR/mimcorrectionhost.h \
$$WIDGETS_DIR/mimcorrectioncandidateitem.h \
- $$WIDGETS_DIR/mimcorrectioncandidateitemview.h \
+ $$WIDGETS_DIR/mimwordtracker.h \
+ $$WIDGETS_DIR/mimwordlist.h \
+ $$WIDGETS_DIR/mimwordlistitem.h \
$$WIDGETS_DIR/mimtoolbar.h \
$$WIDGETS_DIR/mvirtualkeyboard.h \
$$WIDGETS_DIR/horizontalswitcher.h \
@@ -52,14 +54,17 @@
SOURCES += \
$$WIDGETS_DIR/widgetbar.cpp \
- $$WIDGETS_DIR/mimcorrectioncandidatewidget.cpp \
+ $$WIDGETS_DIR/mimcorrectionhost.cpp \
$$WIDGETS_DIR/mimcorrectioncandidateitem.cpp \
- $$WIDGETS_DIR/mimcorrectioncandidateitemview.cpp \
+ $$WIDGETS_DIR/mimwordtracker.cpp \
+ $$WIDGETS_DIR/mimwordlist.cpp \
+ $$WIDGETS_DIR/mimwordlistitem.cpp \
$$WIDGETS_DIR/mimtoolbar.cpp \
$$WIDGETS_DIR/mvirtualkeyboard.cpp \
$$WIDGETS_DIR/horizontalswitcher.cpp \
$$WIDGETS_DIR/notification.cpp \
$$WIDGETS_DIR/symbolview.cpp \
+ $$WIDGETS_DIR/mimabstractkey.cpp \
$$WIDGETS_DIR/mimabstractkeyarea.cpp \
$$WIDGETS_DIR/mimkey.cpp \
$$WIDGETS_DIR/mimkeyarea.cpp \
--- tests/bm_mimabstractkeyarea/bm_mimabstractkeyarea.cpp
+++ tests/bm_mimabstractkeyarea/bm_mimabstractkeyarea.cpp
@@ -86,8 +86,8 @@
subject = new MImKeyArea(keyboard->layout(LayoutData::General, M::Landscape)->section(LayoutData::mainSection));
QBENCHMARK {
- subject->updateButtonGeometriesForWidth(864);
- subject->updateButtonModifiers();
+ subject->updateKeyGeometries(864);
+ subject->updateKeyModifiers();
}
}
--- tests/tests.pro
+++ tests/tests.pro
@@ -6,7 +6,9 @@
ut_mvirtualkeyboard \
ut_mkeyboardhost \
ut_mkeyboardplugin \
- ut_mimcorrectioncandidatewidget \
+ ut_mimcorrectionhost \
+ ut_mimwordlist \
+ ut_mimwordtracker \
ut_mimkey \
ut_mimabstractkeyarea \
ut_flickrecognizer \
--- tests/ut_horizontalswitcher/ut_horizontalswitcher.cpp
+++ tests/ut_horizontalswitcher/ut_horizontalswitcher.cpp
@@ -14,8 +14,11 @@
* of this file.
*/
+//#define MIMABSTRACTKEYAREA_H
+
#include "ut_horizontalswitcher.h"
#include "horizontalswitcher.h"
+#include "mimabstractkeyarea.h"
#include "utils.h"
#include <QGraphicsView>
--- tests/ut_horizontalswitcher/ut_horizontalswitcher.pro
+++ tests/ut_horizontalswitcher/ut_horizontalswitcher.pro
@@ -1,10 +1,12 @@
TEMPLATE = app
+CONFIG += meegotouch
+
include(../common_check.pri)
+LIBS += -L/usr/lib -Wl,-rpath=/usr/lib/meego-im-plugins/ -lmeego-keyboard
+
SOURCES += ut_horizontalswitcher.cpp \
- $$WIDGETS_DIR/horizontalswitcher.cpp
HEADERS += ut_horizontalswitcher.h \
- $$WIDGETS_DIR/horizontalswitcher.h
--- tests/ut_mhardwarekeyboard/ut_mhardwarekeyboard.cpp
+++ tests/ut_mhardwarekeyboard/ut_mhardwarekeyboard.cpp
@@ -1134,4 +1134,46 @@
}
+void Ut_MHardwareKeyboard::testArrowKeyFiltering()
+{
+ // Filter when Fn is not being held down
+ QVERIFY(filterKeyPress(Qt::Key_Home, Qt::NoModifier, "", KeycodeCharacter, FnModifierMask));
+
+ QCOMPARE(inputMethodHost->keyEventsSent(), static_cast<unsigned int>(1));
+ QCOMPARE(inputMethodHost->lastKeyEvent().key(), static_cast<int>(Qt::Key_Left));
+ QCOMPARE(inputMethodHost->lastKeyEvent().type(), QEvent::KeyPress);
+
+ QVERIFY(filterKeyRelease(Qt::Key_Home, Qt::NoModifier, "", KeycodeCharacter, FnModifierMask));
+
+ QCOMPARE(inputMethodHost->keyEventsSent(), static_cast<unsigned int>(2));
+ QCOMPARE(inputMethodHost->lastKeyEvent().key(), static_cast<int>(Qt::Key_Left));
+ QCOMPARE(inputMethodHost->lastKeyEvent().type(), QEvent::KeyRelease);
+
+ // Don't filter when Fn is pressed
+ filterKeyPress(FnLevelKey, Qt::NoModifier, "", KeycodeNonCharacter, 0);
+ QVERIFY(!filterKeyPress(Qt::Key_Home, Qt::NoModifier, "", KeycodeCharacter, FnModifierMask));
+ QVERIFY(!filterKeyRelease(Qt::Key_Home, Qt::NoModifier, "", KeycodeCharacter, FnModifierMask));
+ filterKeyRelease(FnLevelKey, Qt::NoModifier, "", KeycodeNonCharacter, FnModifierMask);
+}
+
+
+void Ut_MHardwareKeyboard::testCtrlShortcutsWithFn()
+{
+ // Must work as if Fn was not on.
+ QVERIFY(filterKeyPress(Qt::Key_Percent, Qt::ControlModifier, "", KeycodeCharacter, FnModifierMask | ControlMask));
+
+ QCOMPARE(inputMethodHost->keyEventsSent(), static_cast<unsigned int>(1));
+ QCOMPARE(inputMethodHost->lastKeyEvent().key(), static_cast<int>(Qt::Key_A));
+ QCOMPARE(inputMethodHost->lastKeyEvent().modifiers(), Qt::ControlModifier);
+ QCOMPARE(inputMethodHost->lastKeyEvent().type(), QEvent::KeyPress);
+
+ QVERIFY(filterKeyRelease(Qt::Key_Percent, Qt::ControlModifier, "", KeycodeCharacter, FnModifierMask | ControlMask));
+
+ QCOMPARE(inputMethodHost->keyEventsSent(), static_cast<unsigned int>(2));
+ QCOMPARE(inputMethodHost->lastKeyEvent().key(), static_cast<int>(Qt::Key_A));
+ QCOMPARE(inputMethodHost->lastKeyEvent().modifiers(), Qt::ControlModifier);
+ QCOMPARE(inputMethodHost->lastKeyEvent().type(), QEvent::KeyRelease);
+}
+
+
QTEST_APPLESS_MAIN(Ut_MHardwareKeyboard);
--- tests/ut_mhardwarekeyboard/ut_mhardwarekeyboard.h
+++ tests/ut_mhardwarekeyboard/ut_mhardwarekeyboard.h
@@ -73,6 +73,10 @@
void testDeadKeys();
+ void testArrowKeyFiltering();
+
+ void testCtrlShortcutsWithFn();
+
private:
bool checkLatchedState(unsigned int mask, unsigned int value) const;
bool checkLockedState(unsigned int mask, unsigned int value) const;
--- tests/ut_mimabstractkeyarea/ut_mimabstractkeyarea.cpp
+++ tests/ut_mimabstractkeyarea/ut_mimabstractkeyarea.cpp
@@ -27,6 +27,7 @@
#include <MApplication>
#include <MScene>
+#include <MSceneWindow>
#include <MSceneManager>
#include <MTheme>
@@ -68,11 +69,14 @@
Q_DECLARE_METATYPE(QList<MImKeyBinding::KeyAction>);
Q_DECLARE_METATYPE(Ut_MImAbstractKeyArea::TestOpList);
-MImAbstractKeyArea *createSingleWidgetMImAbstractKeyArea(const LayoutData::SharedLayoutSection §ion,
- bool usePopup = false,
- QGraphicsWidget *parent = 0)
-{
- return new MImKeyArea(section, usePopup, parent);
+namespace {
+
+ MImAbstractKeyArea *createKeyArea(const LayoutData::SharedLayoutSection §ion,
+ bool usePopup = false,
+ QGraphicsWidget *parent = 0)
+ {
+ return new MImKeyArea(section, usePopup, parent);
+ }
}
void Ut_MImAbstractKeyArea::initTestCase()
@@ -91,7 +95,7 @@
qRegisterMetaType<MImAbstractKey::ButtonState>();
qRegisterMetaType<TestOpList>("TestOpList");
- createMScene(new MPlainWindow); // also create singleton
+ sceneWindow = createMSceneWindow(new MPlainWindow); // also create singleton
FlickGestureRecognizer::registerSharedRecognizer();
@@ -103,6 +107,7 @@
void Ut_MImAbstractKeyArea::cleanupTestCase()
{
FlickGestureRecognizer::unregisterSharedRecognizer();
+ delete sceneWindow;
delete MPlainWindow::instance();
delete app;
app = 0;
@@ -124,7 +129,7 @@
void Ut_MImAbstractKeyArea::testLandscapeBoxSize_data()
{
QTest::addColumn<KBACreator>("createKba");
- QTest::newRow("SingleWidgetArea") << &createSingleWidgetMImAbstractKeyArea;
+ QTest::newRow("SingleWidgetArea") << &createKeyArea;
}
void Ut_MImAbstractKeyArea::testLandscapeBoxSize()
@@ -170,7 +175,7 @@
void Ut_MImAbstractKeyArea::testPortraitBoxSize_data()
{
QTest::addColumn<KBACreator>("createKba");
- QTest::newRow("SingleWidgetArea") << &createSingleWidgetMImAbstractKeyArea;
+ QTest::newRow("SingleWidgetArea") << &createKeyArea;
}
void Ut_MImAbstractKeyArea::testPortraitBoxSize()
@@ -213,7 +218,7 @@
void Ut_MImAbstractKeyArea::testLabelPosition_data()
{
QTest::addColumn<KBACreator>("createKba");
- QTest::newRow("SingleWidgetArea") << &createSingleWidgetMImAbstractKeyArea;
+ QTest::newRow("SingleWidgetArea") << &createKeyArea;
}
void Ut_MImAbstractKeyArea::testLabelPosition()
@@ -259,7 +264,7 @@
for (int n = 0; n < positions.count(); ++n) {
qDebug() << "test position" << positions.at(n);
button = subject->keyAt(positions.at(n));
- const MImKeyModel *result = (button ? &button->key() : 0);
+ const MImKeyModel *result = (button ? &button->model() : 0);
QCOMPARE(result, outcome.at(n));
}
}
@@ -267,7 +272,7 @@
void Ut_MImAbstractKeyArea::testSceneEvent_data()
{
QTest::addColumn<KBACreator>("createKba");
- QTest::newRow("SingleWidgetArea") << &createSingleWidgetMImAbstractKeyArea;
+ QTest::newRow("SingleWidgetArea") << &createKeyArea;
}
void Ut_MImAbstractKeyArea::testSceneEvent()
@@ -328,7 +333,7 @@
void Ut_MImAbstractKeyArea::testPaint_data()
{
QTest::addColumn<KBACreator>("createKba");
- QTest::newRow("SingleWidgetArea") << &createSingleWidgetMImAbstractKeyArea;
+ QTest::newRow("SingleWidgetArea") << &createKeyArea;
}
void Ut_MImAbstractKeyArea::testPaint()
@@ -355,7 +360,7 @@
void Ut_MImAbstractKeyArea::testDeadkeys_data()
{
QTest::addColumn<KBACreator>("createKba");
- QTest::newRow("SingleWidgetArea") << &createSingleWidgetMImAbstractKeyArea;
+ QTest::newRow("SingleWidgetArea") << &createKeyArea;
}
void Ut_MImAbstractKeyArea::testDeadkeys()
@@ -416,7 +421,10 @@
}
//after unlock the dead key, test the shift status
- subject->unlockDeadkeys();
+ SpecialKeyFinder finder(SpecialKeyFinder::FindDeadKey);
+ MImAbstractKey::visitActiveKeys(&finder);
+
+ subject->unlockDeadKeys(finder.deadKey());
for (i = 0; i < positions.count(); i++) {
QCOMPARE(keyAt(0, positions[i])->label(), upperUnicodes.at(i));
}
@@ -446,7 +454,7 @@
{
keyboard = new KeyboardData;
QVERIFY(keyboard->loadNokiaKeyboard("fr.xml"));
- subject = createSingleWidgetMImAbstractKeyArea(keyboard->layout(LayoutData::General, M::Landscape)->section(LayoutData::mainSection),
+ subject = createKeyArea(keyboard->layout(LayoutData::General, M::Landscape)->section(LayoutData::mainSection),
false, 0);
MPlainWindow::instance()->scene()->addItem(subject);
@@ -513,15 +521,15 @@
keyboard = new KeyboardData;
QVERIFY(keyboard->loadNokiaKeyboard("test-layout.xml"));
- subject = createSingleWidgetMImAbstractKeyArea(keyboard->layout(LayoutData::General, M::Landscape)->section(LayoutData::mainSection),
+ subject = createKeyArea(keyboard->layout(LayoutData::General, M::Landscape)->section(LayoutData::mainSection),
false, 0);
MImAbstractKey *deadkey = keyAt(2, 8); // accents ´ and ¨
MImAbstractKey *characterKey = keyAt(0, 2); // e, éë, ÉË
QVERIFY(deadkey);
- QVERIFY(deadkey->key().binding(false)->isDead());
- QVERIFY(deadkey->key().binding(true)->isDead());
+ QVERIFY(deadkey->model().binding(false)->isDead());
+ QVERIFY(deadkey->model().binding(true)->isDead());
foreach (TestOperation op, operations) {
switch (op) {
@@ -547,18 +555,18 @@
{
keyboard = new KeyboardData;
QVERIFY(keyboard->loadNokiaKeyboard("test-layout.xml"));
- subject = createSingleWidgetMImAbstractKeyArea(keyboard->layout(LayoutData::General, M::Landscape)->section(LayoutData::mainSection),
+ subject = createKeyArea(keyboard->layout(LayoutData::General, M::Landscape)->section(LayoutData::mainSection),
false, 0);
const MImAbstractKey *eKey(keyAt(0, 2)); // e, ...
- QCOMPARE(eKey->key().binding(false)->extendedLabels(), QString("%1%2").arg(QChar(0xea)).arg(QChar(0xe8)));
- QCOMPARE(eKey->key().binding(true)->extendedLabels(), QString("%1%2").arg(QChar(0xca)).arg(QChar(0xc8)));
+ QCOMPARE(eKey->model().binding(false)->extendedLabels(), QString("%1%2").arg(QChar(0xea)).arg(QChar(0xe8)));
+ QCOMPARE(eKey->model().binding(true)->extendedLabels(), QString("%1%2").arg(QChar(0xca)).arg(QChar(0xc8)));
}
void Ut_MImAbstractKeyArea::testImportedLayouts_data()
{
QTest::addColumn<KBACreator>("createKba");
- QTest::newRow("SingleWidgetArea") << &createSingleWidgetMImAbstractKeyArea;
+ QTest::newRow("SingleWidgetArea") << &createKeyArea;
}
void Ut_MImAbstractKeyArea::testImportedLayouts()
@@ -601,7 +609,7 @@
void Ut_MImAbstractKeyArea::testPopup_data()
{
QTest::addColumn<KBACreator>("createKba");
- QTest::newRow("SingleWidgetArea") << &createSingleWidgetMImAbstractKeyArea;
+ QTest::newRow("SingleWidgetArea") << &createKeyArea;
}
void Ut_MImAbstractKeyArea::testPopup()
@@ -634,7 +642,7 @@
void Ut_MImAbstractKeyArea::testInitialization_data()
{
QTest::addColumn<KBACreator>("createKba");
- QTest::newRow("SingleWidgetArea") << &createSingleWidgetMImAbstractKeyArea;
+ QTest::newRow("SingleWidgetArea") << &createKeyArea;
}
void Ut_MImAbstractKeyArea::testInitialization()
@@ -657,10 +665,10 @@
QVERIFY(layout);
const LayoutData::SharedLayoutSection section = layout->section(LayoutData::mainSection);
- subject = createSingleWidgetMImAbstractKeyArea(section,
+ subject = createKeyArea(section,
false, 0);
- MImKey *shiftButton = static_cast<MImKeyArea *>(subject)->shiftButton;
+ MImKey *shiftButton = static_cast<MImKeyArea *>(subject)->shiftKey;
QVERIFY(shiftButton);
QVERIFY(shiftButton->state() == MImAbstractKey::Normal);
@@ -682,7 +690,7 @@
QVERIFY(layout);
const LayoutData::SharedLayoutSection section = layout->section(LayoutData::mainSection);
- subject = createSingleWidgetMImAbstractKeyArea(section,
+ subject = createKeyArea(section,
false, 0);
MPlainWindow::instance()->scene()->addItem(subject);
subject->resize(defaultLayoutSize());
@@ -786,25 +794,25 @@
rtlKeys << MImKeyBinding::ActionBackspace;
QTest::newRow("SingleWidgetArea Landscape Arabic")
- << &createSingleWidgetMImAbstractKeyArea
+ << &createKeyArea
<< M::Landscape
<< ar
<< rtlKeys;
QTest::newRow("SingleWidgetArea Portrait Arabic" )
- << &createSingleWidgetMImAbstractKeyArea
+ << &createKeyArea
<< M::Portrait
<< ar
<< rtlKeys;
QTest::newRow("SingleWidgetArea Landscape English")
- << &createSingleWidgetMImAbstractKeyArea
+ << &createKeyArea
<< M::Landscape
<< en_gb
<< nothing;
QTest::newRow("SingleWidgetArea Portrait English" )
- << &createSingleWidgetMImAbstractKeyArea
+ << &createKeyArea
<< M::Portrait
<< en_gb
<< nothing;
@@ -822,19 +830,19 @@
subject = createKba(keyboard->layout(LayoutData::General, orientation)->section(LayoutData::mainSection),
false, 0);
- MImKeyArea *buttonArea = dynamic_cast<MImKeyArea *>(subject);
+ MImKeyArea *keyArea = dynamic_cast<MImKeyArea *>(subject);
- QVERIFY2(buttonArea != 0, "Unknown type of button area");
- for (int row = 0; row < buttonArea->rowCount(); ++row) {
- for (int column = 0; column < buttonArea->sectionModel()->columnsAt(row); ++column) {
- MImKey *button = buttonArea->rowList[row].buttons[column];
- QVERIFY(button != 0);
- if (expectedRtlKeys.contains(button->key().binding()->action())) {
- QVERIFY(button->key().rtl());
- QVERIFY2(button->iconId().contains("-rtl-"), "This is not RTL icon");
- } else if (!button->iconId().isEmpty()) {
- QVERIFY(!button->key().rtl());
- QVERIFY2(!button->iconId().contains("-rtl-"), "This is not LTR icon");
+ QVERIFY2(keyArea != 0, "Unknown type of button area");
+ for (int row = 0; row < keyArea->rowCount(); ++row) {
+ for (int column = 0; column < keyArea->sectionModel()->columnsAt(row); ++column) {
+ MImKey *key = keyArea->rowList[row].keys[column];
+ QVERIFY(key != 0);
+ if (expectedRtlKeys.contains(key->model().binding()->action())) {
+ QVERIFY(key->model().rtl());
+ QVERIFY2(key->iconId().contains("-rtl-"), "This is not RTL icon");
+ } else if (!key->iconId().isEmpty()) {
+ QVERIFY(!key->model().rtl());
+ QVERIFY2(!key->iconId().contains("-rtl-"), "This is not LTR icon");
}
}
}
@@ -848,7 +856,7 @@
keyboard = new KeyboardData;
QVERIFY(keyboard->loadNokiaKeyboard("en_us.xml"));
- subject = createSingleWidgetMImAbstractKeyArea(keyboard->layout(LayoutData::General, M::Landscape)->section(LayoutData::mainSection),
+ subject = createKeyArea(keyboard->layout(LayoutData::General, M::Landscape)->section(LayoutData::mainSection),
true, 0);
MPlainWindow::instance()->scene()->addItem(subject);
@@ -1151,7 +1159,7 @@
MImKeyArea *buttonArea = dynamic_cast<MImKeyArea *>(subject);
if (buttonArea) {
- key = buttonArea->rowList[row].buttons[column];
+ key = buttonArea->rowList[row].keys[column];
}
return key;
--- tests/ut_mimabstractkeyarea/ut_mimabstractkeyarea.h
+++ tests/ut_mimabstractkeyarea/ut_mimabstractkeyarea.h
@@ -28,6 +28,7 @@
class MImAbstractKeyArea;
class KeyboardData;
class MImAbstractKey;
+class MSceneWindow;
class Ut_MImAbstractKeyArea : public QObject
{
@@ -36,6 +37,7 @@
MApplication *app;
MImAbstractKeyArea *subject;
KeyboardData *keyboard;
+ MSceneWindow *sceneWindow;
private slots:
void init();
--- tests/ut_mimcorrectioncandidatewidget
+++ tests/ut_mimcorrectioncandidatewidget
-(directory)
--- tests/ut_mimcorrectioncandidatewidget/ut_mimcorrectioncandidatewidget.cpp
+++ tests/ut_mimcorrectioncandidatewidget/ut_mimcorrectioncandidatewidget.cpp
-/* * This file is part of meego-keyboard *
- *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
- * All rights reserved.
- * Contact: Nokia Corporation (directui at nokia.com)
- *
- * If you have questions regarding the use of this file, please contact
- * Nokia at directui at nokia.com.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * and appearing in the file LICENSE.LGPL included in the packaging
- * of this file.
- */
-
-
-
-#include "ut_mimcorrectioncandidatewidget.h"
-#include "mplainwindow.h"
-#include "utils.h"
-#include <MTheme>
-#include <MList>
-#include <QtTest/QTest>
-#include <QObject>
-#include <QDebug>
-#include <QStringList>
-#include <QStringListModel>
-#include <QSignalSpy>
-#include <QGraphicsSceneMouseEvent>
-#include <MSceneManager>
-
-void Ut_MImCorrectionCandidateWidget::initTestCase()
-{
- static int dummyArgc = 2;
- static char *dummyArgv[2] = { (char *) "./ut_mimcorrectioncandidatewidget",
- (char *) "-local-theme" };
- disableQtPlugins();
- app = new MApplication(dummyArgc, dummyArgv);
-
- // MImCorrectionCandidateWidget uses this internally
- new MPlainWindow;
- if (MPlainWindow::instance()->orientationAngle() != M::Angle0) {
- MPlainWindow::instance()->setOrientationAngle(M::Angle0);
- QTest::qWait(1000);
- }
-
- // initialize testCandidateWidgetSize
- m_subject = new MImCorrectionCandidateWidget;
- QStringList candidates;
- candidates << "1" << "2" << "3";
- m_subject->setCandidates(candidates);
- testCandidateWidgetSize = m_subject->candidatesWidget->preferredSize().toSize();
- testCandidateWidgetSize.setWidth(m_subject->candidateWidth);
- delete m_subject;
-}
-
-
-void Ut_MImCorrectionCandidateWidget::init()
-{
- m_subject = new MImCorrectionCandidateWidget;
-
- if (MPlainWindow::instance()->orientationAngle() != M::Angle0) {
- MPlainWindow::instance()->setOrientationAngle(M::Angle0);
- QTest::qWait(1000);
- }
-}
-
-
-void Ut_MImCorrectionCandidateWidget::cleanup()
-{
- delete m_subject;
-}
-
-void Ut_MImCorrectionCandidateWidget::cleanupTestCase()
-{
- delete MPlainWindow::instance();
- delete app;
- app = 0;
-}
-
-void Ut_MImCorrectionCandidateWidget::checkPositionByPoint_data()
-{
- QTest::addColumn<QPoint>("pos");
- QTest::addColumn<int>("bottomLimit");
- QTest::addColumn<QPoint>("expected");
-
- const int sceneHeight = MPlainWindow::instance()->visibleSceneSize().height();
-
- QTest::newRow("null") << QPoint() << -1 << QPoint();
- QTest::newRow("origo") << QPoint(0, 0) << -1 << QPoint(0, 0);
- QTest::newRow("negative") << QPoint(-10, -10) << -1 << QPoint(0, 0);
- QTest::newRow("positive1") << QPoint(10, 10) << -1 << QPoint(10, 10);
- QTest::newRow("positive2") << QPoint(80, 80) << -1 << QPoint(80, 80);
-
- // Goes over sceneHeight limit
- QTest::newRow("positive3") <<QPoint(700, sceneHeight - 50) << -1
- << QPoint(700, ((sceneHeight - testCandidateWidgetSize.height()) > 0)?
- (sceneHeight - testCandidateWidgetSize.height()):0);
-
- // No room above so aligns to y=0.
- QTest::newRow("bottom limit 1") << QPoint(10, 10) << 4 << QPoint(10, 0);
-
- // There is room above so bottom limit holds (y+height=bottomlimit).
- QTest::newRow("bottom limit 2") << QPoint(10, 250) << 220
- << QPoint(10, ((220 - testCandidateWidgetSize.height()) > 0)?
- (220 - testCandidateWidgetSize.height()):0);
-}
-
-void Ut_MImCorrectionCandidateWidget::checkPositionByPoint()
-{
- QStringList candidates;
- candidates << "1" << "2" << "3";
- m_subject->setCandidates(candidates);
- QFETCH(QPoint, pos);
- QFETCH(int, bottomLimit);
- QFETCH(QPoint, expected);
-
- m_subject->setPosition(pos, bottomLimit);
- QCOMPARE(m_subject->position(), expected);
-}
-
-
-void Ut_MImCorrectionCandidateWidget::checkPositionByPreeditRect()
-{
- const int CandidatesPreeditMargin = 10;
- const QSize sceneSize = MPlainWindow::instance()->visibleSceneSize();
-
- QList<QRect> rects;
- QList<QPoint> positionsCheck;
- QPoint pos;
- QSize size;
-
- QStringList candidates;
- candidates << "1" << "2" << "3";
- m_subject->setCandidates(candidates);
-
- // check with null rectangle
- rects.append(QRect());
- positionsCheck.append(QPoint());
- rects.append(QRect(0, 0, 0, 0));
- positionsCheck.append(QPoint(0, 0));
- rects.append(QRect(QPoint(10, 10), QSize(0, 0)));
- positionsCheck.append(QPoint(0, 0));
-
- // invalid rectangle
- rects.append(QRect(QPoint(10, 10), QPoint(5, 5)));
- positionsCheck.append(QPoint());
- rects.append(QRect(QPoint(), QPoint(-100, -100)));
- positionsCheck.append(QPoint());
- rects.append(QRect(QPoint(0, 0), QPoint(5, -5)));
- positionsCheck.append(QPoint());
-
- // valid rectangle inside area
- pos = QPoint(10, 10 + testCandidateWidgetSize.height());
- size = QSize(1, 1);
- rects.append(QRect(pos, size));
- positionsCheck.append(QPoint(pos.x() + size.width() + CandidatesPreeditMargin,
- pos.y() + size.height() / 2 - testCandidateWidgetSize.height() / 2));
-
- pos = QPoint(4, 5 + testCandidateWidgetSize.height());
- size = QSize(100, 5);
- rects.append(QRect(pos, size));
- positionsCheck.append(QPoint(pos.x() + size.width() + CandidatesPreeditMargin,
- pos.y() + size.height() / 2 - testCandidateWidgetSize.height() / 2));
-
- // valid rectangle outside area
- size = QSize(100, 5);
- pos = QPoint(0, testCandidateWidgetSize.height() / 2 - size.height() / 2 - 1);
- rects.append(QRect(pos, size));
- positionsCheck.append(QPoint(pos.x() + size.width() + CandidatesPreeditMargin, 0));
-
- size = QSize(10, 50);
- pos = QPoint(0, testCandidateWidgetSize.height() / 2 - size.height() / 2 - 1);
- rects.append(QRect(pos, size));
- positionsCheck.append(QPoint(pos.x() + size.width() + CandidatesPreeditMargin, 0));
-
- // switch to left side, vertical overlap
- size = QSize(10, 10);
- pos = QPoint(sceneSize.width() - testCandidateWidgetSize.width() + 1,
- sceneSize.height() - testCandidateWidgetSize.height() / 2);
- rects.append(QRect(pos, size));
- positionsCheck.append(QPoint(pos.x() - CandidatesPreeditMargin - testCandidateWidgetSize.width(),
- sceneSize.height() - testCandidateWidgetSize.height()));
-
- for (int i = 0; i < rects.size(); ++i) {
- m_subject->setPosition(rects.at(i));
- QCOMPARE(m_subject->position(), positionsCheck.at(i));
- }
-}
-
-void Ut_MImCorrectionCandidateWidget::checkActiveIndex()
-{
- QStringList candidates;
- candidates << "1" << "2" << "3" << "4" << "5"
- << "ab" << "cd" << "ef" << "gf";
- m_subject->setPreeditString(candidates.last());
- m_subject->setCandidates(candidates);
- //default preedit is not in the list
- QCOMPARE(m_subject->candidatesModel->rowCount(), (candidates.count() - 1));
- QCOMPARE(m_subject->activeIndex(), -1);
-
- QList<int> index;
- index << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7;
-
- for (int i = 0; i < index.size(); ++i) {
- m_subject->setPreeditString(candidates.at(i));
- QCOMPARE(m_subject->activeIndex(), index.at(i));
- }
-}
-
-
-void Ut_MImCorrectionCandidateWidget::checkPreeditString()
-{
- QStringList str;
- str << "autobahnraststaettenbetreiber" << "Nobody_is_there" <<
- "99ab88bc!§%/()=?" << "lalallala";
-
- foreach(const QString & tmp, str) {
- m_subject->setPreeditString(tmp);
- QVERIFY(m_subject->preeditString() == tmp);
- }
-}
-
-void Ut_MImCorrectionCandidateWidget::setCandidatesAndSelect()
-{
- QStringList candidates;
- QStringList expectedWord;
- QList<QPoint> positions;
- QList<int> expectedSignal;
- QGraphicsSceneMouseEvent *press = 0;
- QGraphicsSceneMouseEvent *release = 0;
- int rowHeight = testCandidateWidgetSize.height() / 3;
-
- candidates << "1" << "2" << "3";
- expectedWord << "" << "1" << "2" << "3" << "";
- positions << QPoint(2, -50)
- << QPoint(2, rowHeight / 2)
- << QPoint(2, rowHeight * 3 / 2)
- << QPoint(2, rowHeight * 5 / 2)
- << QPoint(800, rowHeight / 2)
- << QPoint(-20, rowHeight / 2);
- expectedSignal << 0 << 1 << 1 << 0 << 0 << 0;
-
- for (int n = 0; n < positions.count(); ++n) {
- qDebug() << "Test position" << positions.at(n);
- press = new QGraphicsSceneMouseEvent(QEvent::GraphicsSceneMousePress);
- release = new QGraphicsSceneMouseEvent(QEvent::GraphicsSceneMouseRelease);
- press->setPos(positions.at(n));
- release->setPos(positions.at(n));
-
- delete m_subject;
- m_subject = new MImCorrectionCandidateWidget;
- // set initial state to hidden
- m_subject->hide();
- QSignalSpy spyRegion(m_subject, SIGNAL(regionUpdated(const QRegion &)));
- QSignalSpy spyCandidate(m_subject, SIGNAL(candidateClicked(const QString &)));
- QSignalSpy spyDisappeared(m_subject, SIGNAL(disappeared()));
-
- //initialization
- m_subject->setPreeditString(candidates.last());
- m_subject->setCandidates(candidates);
- m_subject->setPosition(QPoint(0, 0));
- m_subject->showWidget();
- // wait until animation is finished
- QTest::qWait(500);
- QStringList filteredCandidates = candidates;
- filteredCandidates.removeOne(candidates.last());
- QVERIFY(m_subject->candidates() == filteredCandidates);
- //actual testing
- QCOMPARE(spyRegion.count(), 1);
-
- QRectF candidatesWidgetRect(m_subject->position(), m_subject->candidatesWidget->preferredSize());
- if (candidatesWidgetRect.contains(positions.at(n))) {
- int index = positions.at(n).y() / rowHeight;
- if (index >= 0 && index < m_subject->candidatesModel->rowCount()) {
- m_subject->candidatesWidget->selectItem(m_subject->candidatesWidget->itemModel()->index(index, 0));
- }
- } else {
- m_subject->mousePressEvent(press);
- m_subject->mouseMoveEvent(press);
- }
- //if we are not crashed then it is ok
-
- m_subject->mouseReleaseEvent(release);
- QCOMPARE(spyCandidate.count(), expectedSignal.at(n));
- if (spyCandidate.count() > 0) {
- QCOMPARE(spyCandidate.first().count(), 1);
- QCOMPARE(spyCandidate.first().first().toString(), expectedWord.at(n));
- }
- // wait until animation is finished
- QTest::qWait(500);
- QCOMPARE(spyDisappeared.count(), 1);
-
- delete press;
- delete release;
- }
-}
-
-void Ut_MImCorrectionCandidateWidget::checkShowWidget()
-{
- QStringList candidates;
- candidates << "1" << "2" << "3" << "4" << "5";
- m_subject->setCandidates(candidates);
- // At least, no crash for show and hide
- m_subject->showWidget();
- m_subject->hide();
-}
-
-QTEST_APPLESS_MAIN(Ut_MImCorrectionCandidateWidget);
-
--- tests/ut_mimcorrectioncandidatewidget/ut_mimcorrectioncandidatewidget.h
+++ tests/ut_mimcorrectioncandidatewidget/ut_mimcorrectioncandidatewidget.h
-/* * This file is part of meego-keyboard *
- *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
- * All rights reserved.
- * Contact: Nokia Corporation (directui at nokia.com)
- *
- * If you have questions regarding the use of this file, please contact
- * Nokia at directui at nokia.com.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * and appearing in the file LICENSE.LGPL included in the packaging
- * of this file.
- */
-
-
-
-#ifndef UT_MIMCORRECTIONCANDIDATEWIDGET_H
-#define UT_MIMCORRECTIONCANDIDATEWIDGET_H
-
-#include "mimcorrectioncandidatewidget.h"
-#include "mapplication.h"
-#include <QtTest/QTest>
-#include <QObject>
-
-class Ut_MImCorrectionCandidateWidget : public QObject
-{
- Q_OBJECT
-
-private:
- MApplication *app;
- MImCorrectionCandidateWidget *m_subject;
-
-private slots:
- //! initialize application and class
- void initTestCase();
- void init();
- void cleanup();
- void cleanupTestCase();
- void checkPositionByPoint_data();
- void checkPositionByPoint();
- void checkPositionByPreeditRect();
- void checkActiveIndex();
- void checkPreeditString();
- void setCandidatesAndSelect();
- void checkShowWidget();
-
-private:
- QSize testCandidateWidgetSize;
-};
-
-#endif
--- tests/ut_mimcorrectioncandidatewidget/ut_mimcorrectioncandidatewidget.pro
+++ tests/ut_mimcorrectioncandidatewidget/ut_mimcorrectioncandidatewidget.pro
-TEMPLATE = app
-CONFIG += meegotouch
-
-DEPENDPATH += .
-INCLUDEPATH += . \
-
-include(../common_check.pri)
-
-LIBS += -Wl,-rpath=/usr/lib/meego-im-plugins/ -lmeego-keyboard
-
-HEADERS += ut_mimcorrectioncandidatewidget.h \
-
-SOURCES += ut_mimcorrectioncandidatewidget.cpp \
-
--- tests/ut_mimcorrectionhost
+++ tests/ut_mimcorrectionhost
+(directory)
--- tests/ut_mimcorrectionhost/ut_mimcorrectionhost.cpp
+++ tests/ut_mimcorrectionhost/ut_mimcorrectionhost.cpp
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * and appearing in the file LICENSE.LGPL included in the packaging
+ * of this file.
+ */
+
+
+
+#include "ut_mimcorrectionhost.h"
+#include "mimwordtracker.h"
+#include "mimwordlist.h"
+#include <mplainwindow.h>
+#include "utils.h"
+#include <QtTest/QTest>
+#include <QObject>
+#include <QDebug>
+#include <QStringList>
+#include <QSignalSpy>
+#include <MSceneManager>
+#include <MSceneWindow>
+
+Q_DECLARE_METATYPE(MImCorrectionHost::CandidateMode)
+
+void Ut_MImCorrectionHost::initTestCase()
+{
+ static int dummyArgc = 2;
+ static char *dummyArgv[2] = { (char *) "./ut_mimcorrectioncandidatewidget",
+ (char *) "-local-theme" };
+ disableQtPlugins();
+ app = new MApplication(dummyArgc, dummyArgv);
+
+ // MImCorrectionHost uses this internally
+ new MPlainWindow;
+ if (MPlainWindow::instance()->orientationAngle() != M::Angle0) {
+ MPlainWindow::instance()->setOrientationAngle(M::Angle0);
+ QTest::qWait(1000);
+ }
+ parentWindow = new MSceneWindow;
+ parentWindow->setManagedManually(true); // we want the scene window to remain in origin
+ // Adds scene window to scene.
+ MPlainWindow::instance()->sceneManager()->appearSceneWindowNow(parentWindow);
+}
+
+
+void Ut_MImCorrectionHost::init()
+{
+ m_subject = new MImCorrectionHost(parentWindow);
+
+ if (MPlainWindow::instance()->orientationAngle() != M::Angle0) {
+ MPlainWindow::instance()->setOrientationAngle(M::Angle0);
+ QTest::qWait(1000);
+ }
+}
+
+
+void Ut_MImCorrectionHost::cleanup()
+{
+ delete m_subject;
+}
+
+void Ut_MImCorrectionHost::cleanupTestCase()
+{
+ delete parentWindow;
+ delete MPlainWindow::instance();
+ delete app;
+ app = 0;
+}
+
+void Ut_MImCorrectionHost::checkShowWidget()
+{
+ QStringList candidates;
+ candidates << "1" << "2" << "3" << "4" << "5";
+ m_subject->setCandidates(candidates);
+ // At least, no crash for show and hide
+ m_subject->showCorrectionWidget(MImCorrectionHost::WordTrackerMode);
+ m_subject->hideCorrectionWidget();
+ m_subject->showCorrectionWidget(MImCorrectionHost::WordListMode);
+ m_subject->hideCorrectionWidget();
+}
+
+void Ut_MImCorrectionHost::checkModes()
+{
+ QStringList candidates;
+ candidates << "1" << "2" << "3" << "4" << "5";
+ m_subject->setCandidates(candidates);
+ m_subject->showCorrectionWidget(MImCorrectionHost::WordTrackerMode);
+ QCOMPARE(m_subject->candidateMode(), MImCorrectionHost::WordTrackerMode);
+ m_subject->showCorrectionWidget(MImCorrectionHost::WordListMode);
+ QCOMPARE(m_subject->candidateMode(), MImCorrectionHost::WordListMode);
+}
+
+void Ut_MImCorrectionHost::checkCandidatesAndPreedit_data()
+{
+ QTest::addColumn<QStringList>("candidates");
+
+ QTest::newRow("test1") << (QStringList() << "1" << "2" << "3" << "4" << "5");
+ QTest::newRow("test2") << (QStringList() << "ab" << "cd" << "ef" << "fg" << "gh");
+}
+
+void Ut_MImCorrectionHost::checkCandidatesAndPreedit()
+{
+ QFETCH(QStringList, candidates);
+
+ m_subject->setCandidates(candidates);
+ QCOMPARE(candidates, m_subject->candidates);
+}
+
+void Ut_MImCorrectionHost::checkSuggestion_data()
+{
+ QTest::addColumn<QStringList>("candidates");
+ QTest::addColumn<MImCorrectionHost::CandidateMode>("candidateMode");
+ QTest::addColumn<QString>("clickedCandidate");
+
+ QTest::newRow("testWordTrackerMode") << (QStringList() << "1" << "2" << "3" << "4" << "5")
+ << MImCorrectionHost::WordTrackerMode
+ << "3";
+ QTest::newRow("test2WordListMode") << (QStringList() << "ab" << "cd" << "ef" << "fg" << "gh")
+ << MImCorrectionHost::WordListMode
+ << "gh";
+}
+
+void Ut_MImCorrectionHost::checkSuggestion()
+{
+ QFETCH(QStringList, candidates);
+ QFETCH(MImCorrectionHost::CandidateMode, candidateMode);
+ QFETCH(QString, clickedCandidate);
+
+ QSignalSpy spy(m_subject, SIGNAL(candidateClicked(const QString &)));
+
+ m_subject->setCandidates(candidates);
+ m_subject->showCorrectionWidget(candidateMode);
+ if (candidateMode == MImCorrectionHost::WordListMode) {
+ QTest::qWait(600);
+ }
+ // default suggestion is the first one in candidate list which is
+ // different with preedit
+ QCOMPARE(m_subject->suggestion(), candidates.at(1));
+
+ m_subject->handleCandidateClicked(clickedCandidate);
+
+ // suggestion is the clciked word
+ QCOMPARE(m_subject->suggestion(), clickedCandidate);
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.first().first().toString(), clickedCandidate);
+}
+
+void Ut_MImCorrectionHost::checkPosition()
+{
+ QStringList candidates;
+ candidates << "fug" << "rug" << "dug" << "tug";
+ m_subject->setCandidates(candidates);
+
+ const QSize sceneSize = MPlainWindow::instance()->visibleSceneSize();
+ int width = m_subject->wordTracker->idealWidth();
+
+ //set a position to check whether the word tracker is still inside screen.
+ QPoint insidePos(sceneSize.width() - width, 100);
+ m_subject->setPosition(insidePos);
+ QCOMPARE(m_subject->position(), insidePos);
+
+ QPoint outsidepPos(sceneSize.width(), 100);
+ m_subject->setPosition(outsidepPos);
+ QVERIFY(m_subject->position() != outsidepPos);
+ QCOMPARE(m_subject->position(), insidePos);
+}
+
+QTEST_APPLESS_MAIN(Ut_MImCorrectionHost);
+
--- tests/ut_mimcorrectionhost/ut_mimcorrectionhost.h
+++ tests/ut_mimcorrectionhost/ut_mimcorrectionhost.h
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * and appearing in the file LICENSE.LGPL included in the packaging
+ * of this file.
+ */
+
+
+
+#ifndef UT_MIMCORRECTIONHOST_H
+#define UT_MIMCORRECTIONHOST_H
+
+#include "mimcorrectionhost.h"
+#include "mapplication.h"
+#include <QtTest/QTest>
+#include <QObject>
+
+class MSceneWindow;
+
+class Ut_MImCorrectionHost : public QObject
+{
+ Q_OBJECT
+
+private:
+ MApplication *app;
+ MImCorrectionHost *m_subject;
+ MSceneWindow *parentWindow;
+
+private slots:
+ //! initialize application and class
+ void initTestCase();
+ void init();
+ void cleanup();
+ void cleanupTestCase();
+ void checkShowWidget();
+ void checkModes();
+ void checkCandidatesAndPreedit_data();
+ void checkCandidatesAndPreedit();
+ void checkSuggestion_data();
+ void checkSuggestion();
+ void checkPosition();
+};
+
+#endif
--- tests/ut_mimcorrectionhost/ut_mimcorrectionhost.pro
+++ tests/ut_mimcorrectionhost/ut_mimcorrectionhost.pro
+TEMPLATE = app
+CONFIG += meegotouch
+
+DEPENDPATH += .
+INCLUDEPATH += . \
+
+include(../common_check.pri)
+
+LIBS += -Wl,-rpath=/usr/lib/meego-im-plugins/ -lmeego-keyboard
+
+HEADERS += ut_mimcorrectionhost.h \
+
+SOURCES += ut_mimcorrectionhost.cpp \
+
--- tests/ut_mimkey/ut_mimkey.cpp
+++ tests/ut_mimkey/ut_mimkey.cpp
@@ -18,7 +18,6 @@
#include "ut_mimkey.h"
-#include "mimabstractkey.h"
#include "mimabstractkeyareastyle.h"
#include "mimkey.h"
#include "mimkeyarea.h"
@@ -31,14 +30,51 @@
#include <QSignalSpy>
#include <QDebug>
-Q_DECLARE_METATYPE(QList<Ut_KeyButton::DirectionPair>)
+Q_DECLARE_METATYPE(QList<Ut_MImKey::DirectionPair>)
+Q_DECLARE_METATYPE(Ut_MImKey::KeyList)
+Q_DECLARE_METATYPE(QList<Ut_MImKey::KeyTriple>)
+Q_DECLARE_METATYPE(QList<int>)
+
+namespace {
+
+ class ActiveKeyFinder
+ : public MImAbstractKeyVisitor
+ {
+ public:
+ bool found;
+ MImAbstractKey *findMe;
+ int visits;
+
+ explicit ActiveKeyFinder(MImAbstractKey *newFindMe = 0)
+ : found(false)
+ , findMe(newFindMe)
+ , visits(0)
+ {}
+
+ bool operator()(MImAbstractKey *key)
+ {
+ ++visits;
-void Ut_KeyButton::initTestCase()
+ found = (found || (key == findMe));
+ return found;
+ }
+ };
+
+ bool isActiveKeyState(MImAbstractKey::ButtonState state)
+ {
+ return ((state == MImAbstractKey::Pressed)
+ || (state == MImAbstractKey::Selected));
+ }
+}
+
+void Ut_MImKey::initTestCase()
{
qRegisterMetaType< QList<DirectionPair> >("QList<DirectionPair>");
+ qRegisterMetaType<KeyList>("KeyList");
+ qRegisterMetaType< QList<KeyTriple> >("QList<KeyTriple>");
static int argc = 2;
- static char *app_name[] = { (char*) "ut_keybutton",
+ static char *app_name[] = { (char*) "ut_mimkey",
(char *) "-local-theme" };
disableQtPlugins();
@@ -48,10 +84,10 @@
style->initialize("", "", 0);
parent = new QGraphicsWidget;
- dataKey = createDataKey();
+ dataKey = createKeyModel();
}
-void Ut_KeyButton::cleanupTestCase()
+void Ut_MImKey::cleanupTestCase()
{
delete style;
delete dataKey;
@@ -60,18 +96,19 @@
delete parent;
}
-void Ut_KeyButton::init()
+void Ut_MImKey::init()
{
subject = new MImKey(*dataKey, *style, *parent);
}
-void Ut_KeyButton::cleanup()
+void Ut_MImKey::cleanup()
{
+ MImAbstractKey::resetActiveKeys();
delete subject;
subject = 0;
}
-void Ut_KeyButton::testSetModifier_data()
+void Ut_MImKey::testSetModifier_data()
{
QTest::addColumn<bool>("shift");
QTest::addColumn<QChar>("accent");
@@ -89,7 +126,7 @@
QTest::newRow("shift, l'accent grave") << true << grave << QString(L'À');
}
-void Ut_KeyButton::testSetModifier()
+void Ut_MImKey::testSetModifier()
{
QFETCH(bool, shift);
QFETCH(QChar, accent);
@@ -99,12 +136,12 @@
QCOMPARE(subject->label(), expectedLabel);
}
-void Ut_KeyButton::testKey()
+void Ut_MImKey::testKey()
{
- QCOMPARE(&subject->key(), dataKey);
+ QCOMPARE(&subject->model(), dataKey);
}
-void Ut_KeyButton::testBinding()
+void Ut_MImKey::testBinding()
{
bool shift = false;
subject->setModifiers(shift);
@@ -115,7 +152,7 @@
QCOMPARE(&subject->binding(), dataKey->binding(shift));
}
-void Ut_KeyButton::testIsDead()
+void Ut_MImKey::testIsDead()
{
MImKeyModel *key = new MImKeyModel;
MImKeyBinding *binding = new MImKeyBinding;
@@ -133,7 +170,7 @@
delete key;
}
-void Ut_KeyButton::testTouchPointCount_data()
+void Ut_MImKey::testTouchPointCount_data()
{
QTest::addColumn<int>("initialCount");
QTest::addColumn< QList<DirectionPair> >("countDirectionList");
@@ -172,7 +209,7 @@
<< MImKey::touchPointLimit() - 2 << MImAbstractKey::Pressed;
}
-void Ut_KeyButton::testTouchPointCount()
+void Ut_MImKey::testTouchPointCount()
{
QFETCH(int, initialCount);
QFETCH(QList<DirectionPair>, countDirectionList);
@@ -201,7 +238,127 @@
QCOMPARE(subject->state(), expectedButtonState);
}
-MImKeyModel *Ut_KeyButton::createDataKey()
+void Ut_MImKey::testResetTouchPointCount()
+{
+ QCOMPARE(subject->touchPointCount(), 0);
+
+ for (int idx = 0; idx < 3; ++idx) {
+ subject->increaseTouchPointCount();
+ }
+
+ QCOMPARE(subject->touchPointCount(), 3);
+
+ subject->resetTouchPointCount();
+ QCOMPARE(subject->touchPointCount(), 0);
+}
+
+void Ut_MImKey::testActiveKeys_data()
+{
+ QTest::addColumn<KeyList>("availableKeys");
+ QTest::addColumn< QList<KeyTriple> >("keyControlSequence");
+ QTest::addColumn< QList<int> >("expectedActiveKeys");
+
+ QTest::newRow("single key")
+ << (KeyList() << createKey())
+ << (QList<KeyTriple>() << KeyTriple(0, MImAbstractKey::Pressed, 0))
+ << (QList<int>() << 0);
+
+ QTest::newRow("two keys")
+ << (KeyList() << createKey() << createKey())
+ << (QList<KeyTriple>() << KeyTriple(0, MImAbstractKey::Pressed, 0)
+ << KeyTriple(1, MImAbstractKey::Selected, 1)
+ << KeyTriple(0, MImAbstractKey::Normal, 1))
+ << (QList<int>() << 1);
+
+ QTest::newRow("last active key")
+ << (KeyList() << createKey() << createKey() << createKey())
+ << (QList<KeyTriple>() << KeyTriple(0, MImAbstractKey::Pressed, 0)
+ << KeyTriple(1, MImAbstractKey::Selected, 1)
+ << KeyTriple(2, MImAbstractKey::Normal, 1)
+ << KeyTriple(1, MImAbstractKey::Normal, 0)
+ << KeyTriple(2, MImAbstractKey::Pressed, 2)
+ << KeyTriple(0, MImAbstractKey::Normal, 2)
+ << KeyTriple(2, MImAbstractKey::Normal, -1))
+ << (QList<int>());
+}
+
+void Ut_MImKey::testActiveKeys()
+{
+ QFETCH(KeyList, availableKeys);
+ QFETCH(QList<KeyTriple>, keyControlSequence);
+ QFETCH(QList<int>, expectedActiveKeys);
+ const MImAbstractKey *const noKey = 0;
+
+ foreach (const KeyTriple &triple, keyControlSequence) {
+ availableKeys.at(triple.index)->setDownState(isActiveKeyState(triple.state));
+ if (triple.lastActiveIndex > -1) {
+ QCOMPARE(MImAbstractKey::lastActiveKey(),
+ availableKeys.at(triple.lastActiveIndex));
+ } else {
+ QCOMPARE(MImAbstractKey::lastActiveKey(), noKey);
+ }
+ }
+
+ foreach (int idx, expectedActiveKeys) {
+ ActiveKeyFinder finder(availableKeys.at(idx));
+ MImAbstractKey::visitActiveKeys(&finder);
+
+ QVERIFY(finder.found);
+ }
+
+ // Verify that remaining keys (those not listed in expectedActiveKeys)
+ // are not in MImAbstractKey::activeKeys:
+ for (int idx = 0; idx < availableKeys.count(); ++idx) {
+ if (!expectedActiveKeys.contains(idx)) {
+ ActiveKeyFinder finder(availableKeys.at(idx));
+ MImAbstractKey::visitActiveKeys(&finder);
+
+ QVERIFY(not finder.found);
+ }
+ }
+}
+
+void Ut_MImKey::testResetActiveKeys()
+{
+ ActiveKeyFinder finder;
+ MImAbstractKey::visitActiveKeys(&finder);
+ QCOMPARE(finder.visits, 0);
+
+ KeyList keys;
+ keys << createKey(true) << createKey(false) << createKey(true);
+ MImAbstractKey::visitActiveKeys(&finder);
+ QCOMPARE(finder.visits, 2);
+
+ MImAbstractKey::resetActiveKeys();
+}
+
+void Ut_MImKey::testVisitActiveKeys()
+{
+ KeyList keys;
+ keys << createKey(true) << createKey(true);
+
+ MImKeyBinding *b = new MImKeyBinding;
+ b->keyAction = MImKeyBinding::ActionShift;
+ MImKeyModel *model = new MImKeyModel;
+ model->bindings[MImKeyModel::NoShift] = b;
+ MImKey *shift = new MImKey(*model, *style, *parent);
+ shift->setDownState(true);
+ keys << shift;
+
+ SpecialKeyFinder finder;
+ MImAbstractKey::visitActiveKeys(&finder);
+ QCOMPARE(finder.shiftKey(), shift);
+ QCOMPARE(finder.visits(), keys.count());
+}
+
+MImKey *Ut_MImKey::createKey(bool state)
+{
+ MImKey *key = new MImKey(*dataKey, *style, *parent);
+ key->setDownState(state);
+ return key;
+}
+
+MImKeyModel *Ut_MImKey::createKeyModel()
{
MImKeyModel *key = new MImKeyModel;
@@ -210,12 +367,14 @@
binding1->dead = false;
binding1->accents = "`´^¨";
binding1->accented_labels = QString(L'à') + L'á' + L'á' + L'â' + L'ä';
+ binding1->keyAction = MImKeyBinding::ActionInsert;
MImKeyBinding *binding2 = new MImKeyBinding;
binding2->keyLabel = "A";
binding2->dead = false;
binding2->accents = "`´^¨";
binding2->accented_labels = QString(L'À') + L'Á' + L'Â' + L'Ä';
+ binding2->keyAction = MImKeyBinding::ActionInsert;
key->bindings[MImKeyModel::NoShift] = binding1;
key->bindings[MImKeyModel::Shift] = binding2;
@@ -223,4 +382,4 @@
return key;
}
-QTEST_APPLESS_MAIN(Ut_KeyButton);
+QTEST_APPLESS_MAIN(Ut_MImKey);
--- tests/ut_mimkey/ut_mimkey.h
+++ tests/ut_mimkey/ut_mimkey.h
@@ -16,8 +16,8 @@
-#ifndef UT_KEYBUTTON_H
-#define UT_KEYBUTTON_H
+#ifndef UT_MIMKEY_H
+#define UT_MIMKEY_H
#include <mimabstractkey.h>
@@ -27,11 +27,12 @@
class MApplication;
class MImKeyModel;
+class MImKey;
class MImAbstractKeyAreaStyleContainer;
class QGraphicsItem;
class KeyboardData;
-class Ut_KeyButton: public QObject
+class Ut_MImKey: public QObject
{
Q_OBJECT
@@ -41,7 +42,30 @@
Up
};
+ struct KeyTriple {
+ int index;
+ MImAbstractKey::ButtonState state;
+ int lastActiveIndex;
+
+ // Needed for metatype registration:
+ KeyTriple()
+ : index(-1)
+ , state(MImAbstractKey::Normal)
+ , lastActiveIndex(-1)
+ {}
+
+ KeyTriple(int newIndex,
+ MImAbstractKey::ButtonState newState,
+ int newLastActiveIndex)
+ : index(newIndex)
+ , state(newState)
+ , lastActiveIndex(newLastActiveIndex)
+ {}
+
+ };
+
typedef QPair<Direction, bool> DirectionPair;
+ typedef QList<MImAbstractKey *> KeyList;
private:
@@ -65,12 +89,20 @@
void testTouchPointCount_data();
void testTouchPointCount();
+ void testResetTouchPointCount();
+
+ void testActiveKeys_data();
+ void testActiveKeys();
+ void testResetActiveKeys();
+ void testVisitActiveKeys();
private:
- MImKeyModel *createDataKey();
+ MImKey *createKey(bool state = false);
+ MImKeyModel *createKeyModel();
};
-Q_DECLARE_METATYPE(Ut_KeyButton::DirectionPair)
+Q_DECLARE_METATYPE(Ut_MImKey::DirectionPair)
+Q_DECLARE_METATYPE(Ut_MImKey::KeyTriple)
Q_DECLARE_METATYPE(MImAbstractKey::ButtonState)
#endif
--- tests/ut_mimkey/ut_mimkey.pro
+++ tests/ut_mimkey/ut_mimkey.pro
@@ -16,6 +16,7 @@
$$STYLE_HEADERS
SOURCES += ut_mimkey.cpp \
+ $$WIDGETS_DIR/mimabstractkey.cpp \
$$WIDGETS_DIR/mimkey.cpp \
$$COMMON_DIR/mimkeymodel.cpp \
$$COMMON_DIR/keyevent.cpp
--- tests/ut_mimtoolbar/ut_mimtoolbar.cpp
+++ tests/ut_mimtoolbar/ut_mimtoolbar.cpp
@@ -82,7 +82,7 @@
qRegisterMetaType<CopyPasteState>("CopyPasteState");
LayoutsManager::createInstance();
- createMScene(new MPlainWindow); // also create singleton
+ sceneWindow = createMSceneWindow(new MPlainWindow); // also create singleton MPlainWindow
ToolbarFileName = QCoreApplication::applicationDirPath() + ToolbarFileName;
QVERIFY(QFile::exists(ToolbarFileName));
@@ -113,6 +113,7 @@
{
toolbarData.clear();
LayoutsManager::destroyInstance();
+ delete sceneWindow;
delete MPlainWindow::instance();
delete app;
app = 0;
--- tests/ut_mimtoolbar/ut_mimtoolbar.h
+++ tests/ut_mimtoolbar/ut_mimtoolbar.h
@@ -28,6 +28,7 @@
class MImToolbar;
class QKeyEvent;
class MWidget;
+class MSceneWindow;
class Ut_MImToolbar : public QObject
{
@@ -36,6 +37,7 @@
private:
MApplication *app;
MImToolbar *m_subject;
+ MSceneWindow *sceneWindow;
int keyEvents;
QSharedPointer<MToolbarData> toolbarData;
--- tests/ut_mimwordlist
+++ tests/ut_mimwordlist
+(directory)
--- tests/ut_mimwordlist/ut_mimwordlist.cpp
+++ tests/ut_mimwordlist/ut_mimwordlist.cpp
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * and appearing in the file LICENSE.LGPL included in the packaging
+ * of this file.
+ */
+
+
+
+#include "ut_mimwordlist.h"
+#include "mimwordlist.h"
+#include "mimwordlistitem.h"
+#include "mplainwindow.h"
+#include "utils.h"
+#include <QtTest/QTest>
+#include <QObject>
+#include <QDebug>
+#include <QStringList>
+#include <QSignalSpy>
+#include <QGraphicsSceneMouseEvent>
+#include <MSceneManager>
+
+void Ut_MImWordList::initTestCase()
+{
+ static int dummyArgc = 2;
+ static char *dummyArgv[2] = { (char *) "./ut_mimcorrectioncandidatewidget",
+ (char *) "-local-theme" };
+ disableQtPlugins();
+ app = new MApplication(dummyArgc, dummyArgv);
+
+ // MImWordList uses this internally
+ new MPlainWindow;
+ if (MPlainWindow::instance()->orientationAngle() != M::Angle0) {
+ MPlainWindow::instance()->setOrientationAngle(M::Angle0);
+ QTest::qWait(1000);
+ }
+}
+
+
+void Ut_MImWordList::init()
+{
+ m_subject = new MImWordList;
+
+ if (MPlainWindow::instance()->orientationAngle() != M::Angle0) {
+ MPlainWindow::instance()->setOrientationAngle(M::Angle0);
+ QTest::qWait(1000);
+ }
+}
+
+
+void Ut_MImWordList::cleanup()
+{
+ delete m_subject;
+}
+
+void Ut_MImWordList::cleanupTestCase()
+{
+ delete MPlainWindow::instance();
+ delete app;
+ app = 0;
+}
+
+void Ut_MImWordList::testCandidates_data()
+{
+ QTest::addColumn<QStringList>("candidates");
+
+ QTest::newRow("test1") << (QStringList() << "1" << "2" << "3" << "4" << "5");
+ QTest::newRow("test2") << (QStringList() << "abc" << "def" << "ghi" << "jfk" << "lmn");
+}
+
+void Ut_MImWordList::testCandidates()
+{
+ QFETCH(QStringList, candidates);
+ m_subject->setCandidates(candidates);
+ QCOMPARE(m_subject->candidates(), candidates);
+}
+
+void Ut_MImWordList::testSelect()
+{
+ QStringList candidates = (QStringList() << "abc" << "def" << "ghi" << "jfk" << "lmn");
+ m_subject->setCandidates(candidates);
+ m_subject->appear();
+ QTest::qWait(600);
+ QVERIFY(m_subject->isVisible());
+ if (m_subject->sceneWindowState() == MSceneWindow::Appearing) {
+ QSKIP("word list is during appearing animation", SkipSingle);
+ }
+ QSignalSpy clickSpy(m_subject, SIGNAL(candidateClicked(const QString &)));
+
+ m_subject->candidateItems[2]->click();
+
+ QCOMPARE(clickSpy.count(), 1);
+ QCOMPARE(clickSpy.first().first().toString(), QString("ghi"));
+ m_subject->disappear();
+}
+
+QTEST_APPLESS_MAIN(Ut_MImWordList);
+
--- tests/ut_mimwordlist/ut_mimwordlist.h
+++ tests/ut_mimwordlist/ut_mimwordlist.h
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * and appearing in the file LICENSE.LGPL included in the packaging
+ * of this file.
+ */
+
+
+
+#ifndef UT_MIMWORDLIST_H
+#define UT_MIMWORDLIST_H
+
+#include "mimwordlist.h"
+#include "mapplication.h"
+#include <QtTest/QTest>
+#include <QObject>
+
+class Ut_MImWordList : public QObject
+{
+ Q_OBJECT
+
+private:
+ MApplication *app;
+ MImWordList *m_subject;
+
+private slots:
+ //! initialize application and class
+ void initTestCase();
+ void init();
+ void cleanup();
+ void cleanupTestCase();
+ void testCandidates_data();
+ void testCandidates();
+ void testSelect();
+};
+
+#endif
--- tests/ut_mimwordlist/ut_mimwordlist.pro
+++ tests/ut_mimwordlist/ut_mimwordlist.pro
+TEMPLATE = app
+CONFIG += meegotouch
+
+DEPENDPATH += .
+INCLUDEPATH += . \
+
+include(../common_check.pri)
+
+LIBS += -Wl,-rpath=/usr/lib/meego-im-plugins/ -lmeego-keyboard
+
+HEADERS += ut_mimwordlist.h \
+
+SOURCES += ut_mimwordlist.cpp \
+
--- tests/ut_mimwordtracker
+++ tests/ut_mimwordtracker
+(directory)
--- tests/ut_mimwordtracker/ut_mimwordtracker.cpp
+++ tests/ut_mimwordtracker/ut_mimwordtracker.cpp
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * and appearing in the file LICENSE.LGPL included in the packaging
+ * of this file.
+ */
+
+
+
+#include "ut_mimwordtracker.h"
+#include "mimwordtracker.h"
+#include "mplainwindow.h"
+#include "utils.h"
+#include <QtTest/QTest>
+#include <QObject>
+#include <QDebug>
+#include <QStringList>
+#include <QSignalSpy>
+#include <QGraphicsSceneMouseEvent>
+#include <MSceneManager>
+
+void Ut_MImWordTracker::initTestCase()
+{
+ static int dummyArgc = 2;
+ static char *dummyArgv[2] = { (char *) "./ut_mimcorrectioncandidatewidget",
+ (char *) "-local-theme" };
+ disableQtPlugins();
+ app = new MApplication(dummyArgc, dummyArgv);
+
+ // MImWordTracker uses this internally
+ new MPlainWindow;
+ if (MPlainWindow::instance()->orientationAngle() != M::Angle0) {
+ MPlainWindow::instance()->setOrientationAngle(M::Angle0);
+ QTest::qWait(1000);
+ }
+ parentWindow = new MSceneWindow;
+ parentWindow->setManagedManually(true); // we want the scene window to remain in origin
+ // Adds scene window to scene.
+ MPlainWindow::instance()->sceneManager()->appearSceneWindowNow(parentWindow);
+}
+
+
+void Ut_MImWordTracker::init()
+{
+ m_subject = new MImWordTracker(parentWindow);
+
+ if (MPlainWindow::instance()->orientationAngle() != M::Angle0) {
+ MPlainWindow::instance()->setOrientationAngle(M::Angle0);
+ QTest::qWait(1000);
+ }
+}
+
+
+void Ut_MImWordTracker::cleanup()
+{
+ delete m_subject;
+}
+
+void Ut_MImWordTracker::cleanupTestCase()
+{
+ delete parentWindow;
+ delete MPlainWindow::instance();
+ delete app;
+ app = 0;
+}
+
+void Ut_MImWordTracker::testCandidate_data()
+{
+ QTest::addColumn<QString>("candidate");
+
+ QTest::newRow("test1") << "1";
+ QTest::newRow("test2") << "abc";
+}
+
+void Ut_MImWordTracker::testCandidate()
+{
+ QFETCH(QString, candidate);
+ m_subject->setCandidate(candidate);
+ QCOMPARE(m_subject->candidate(), candidate);
+}
+
+void Ut_MImWordTracker::testAppearAndDisappear()
+{
+ m_subject->appear(false);
+ QVERIFY(m_subject->isVisible());
+ m_subject->disappear(false);
+ QVERIFY(!m_subject->isVisible());
+}
+
+void Ut_MImWordTracker::testSelect()
+{
+ QString candidate("test");
+ m_subject->appear(false);
+ QVERIFY(m_subject->isVisible());
+ m_subject->setCandidate(candidate);
+
+ QSignalSpy clickSpy(m_subject, SIGNAL(candidateClicked(const QString &)));
+ m_subject->select();
+ QCOMPARE(clickSpy.count(), 1);
+ QCOMPARE(clickSpy.first().first().toString(), candidate);
+}
+
+void Ut_MImWordTracker::testLongTap()
+{
+ QString candidate("test");
+ m_subject->appear(false);
+ QVERIFY(m_subject->isVisible());
+ m_subject->setCandidate(candidate);
+
+ QSignalSpy clickSpy(m_subject, SIGNAL(longTapped()));
+ m_subject->longTap();
+ QCOMPARE(clickSpy.count(), 1);
+}
+
+
+QTEST_APPLESS_MAIN(Ut_MImWordTracker);
+
--- tests/ut_mimwordtracker/ut_mimwordtracker.h
+++ tests/ut_mimwordtracker/ut_mimwordtracker.h
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * and appearing in the file LICENSE.LGPL included in the packaging
+ * of this file.
+ */
+
+
+
+#ifndef UT_MIMWORDTRACKER_H
+#define UT_MIMWORDTRACKER_H
+
+#include "mimwordtracker.h"
+#include "mapplication.h"
+#include <QtTest/QTest>
+#include <QObject>
+
+class MSceneWindow;
+
+class Ut_MImWordTracker : public QObject
+{
+ Q_OBJECT
+
+private:
+ MApplication *app;
+ MImWordTracker *m_subject;
+ MSceneWindow *parentWindow;
+
+private slots:
+ //! initialize application and class
+ void initTestCase();
+ void init();
+ void cleanup();
+ void cleanupTestCase();
+ void testCandidate_data();
+ void testCandidate();
+ void testAppearAndDisappear();
+ void testSelect();
+ void testLongTap();
+};
+
+#endif
--- tests/ut_mimwordtracker/ut_mimwordtracker.pro
+++ tests/ut_mimwordtracker/ut_mimwordtracker.pro
+TEMPLATE = app
+CONFIG += meegotouch
+
+DEPENDPATH += .
+INCLUDEPATH += . \
+
+include(../common_check.pri)
+
+LIBS += -Wl,-rpath=/usr/lib/meego-im-plugins/ -lmeego-keyboard
+
+HEADERS += ut_mimwordtracker.h \
+
+SOURCES += ut_mimwordtracker.cpp \
+
--- tests/ut_mkeyboardhost/minputmethodhoststub.cpp
+++ tests/ut_mkeyboardhost/minputmethodhoststub.cpp
@@ -39,6 +39,7 @@
void MInputMethodHostStub::clear()
{
+ preeditRectangleReturnValue = QRect();
preedit.clear();
commit.clear();
qDeleteAll(keyEvents);
@@ -135,7 +136,7 @@
QRect MInputMethodHostStub::preeditRectangle(bool &valid)
{
valid = true;
- return QRect();
+ return preeditRectangleReturnValue;
}
bool MInputMethodHostStub::autoCapitalizationEnabled(bool &val)
--- tests/ut_mkeyboardhost/minputmethodhoststub.h
+++ tests/ut_mkeyboardhost/minputmethodhoststub.h
@@ -96,6 +96,7 @@
bool textSelected;
bool keyRedirectionEnabled;
MInputMethod::InputModeIndicator indicator;
+ QRect preeditRectangleReturnValue;
};
#endif
--- tests/ut_mkeyboardhost/ut_mkeyboardhost.cpp
+++ tests/ut_mkeyboardhost/ut_mkeyboardhost.cpp
@@ -16,7 +16,7 @@
-#include <mimcorrectioncandidatewidget.h>
+#include <mimcorrectionhost.h>
#include <mvirtualkeyboard.h>
#include <mhardwarekeyboard.h>
#include <mkeyboardhost.h>
@@ -340,9 +340,16 @@
inputMethodHost->clear();
subject->handleKeyClick(KeyEvent("a"));
- subject->handleKeyClick(KeyEvent("\n", QEvent::KeyRelease, Qt::Key_Return));
+ subject->handleKeyClick(KeyEvent("\r", QEvent::KeyRelease, Qt::Key_Return));
QVERIFY(subject->preedit.isEmpty());
- QCOMPARE(inputMethodHost->commit, QString("a\n"));
+ QCOMPARE(inputMethodHost->commit, QString("a"));
+ QCOMPARE(inputMethodHost->keyEvents.count(), 2);
+ QCOMPARE(inputMethodHost->keyEvents[0]->text(), QString("\r"));
+ QCOMPARE(inputMethodHost->keyEvents[0]->key(), static_cast<int>(Qt::Key_Return));
+ QCOMPARE(inputMethodHost->keyEvents[0]->type(), QEvent::KeyPress);
+ QCOMPARE(inputMethodHost->keyEvents[1]->text(), QString("\r"));
+ QCOMPARE(inputMethodHost->keyEvents[1]->key(), static_cast<int>(Qt::Key_Return));
+ QCOMPARE(inputMethodHost->keyEvents[1]->type(), QEvent::KeyRelease);
inputMethodHost->clear();
subject->handleKeyClick(KeyEvent("a"));
@@ -357,6 +364,19 @@
QCOMPARE(subject->imCorrectionEngine->correctionEnabled(), false);
QCOMPARE(subject->correctionEnabled, false);
+ subject->handleKeyClick(KeyEvent("a"));
+ subject->handleKeyClick(KeyEvent("\r", QEvent::KeyRelease, Qt::Key_Return));
+ QVERIFY(subject->preedit.isEmpty());
+ QCOMPARE(inputMethodHost->commit, QString("a"));
+ QCOMPARE(inputMethodHost->keyEvents.count(), 2);
+ QCOMPARE(inputMethodHost->keyEvents[0]->text(), QString("\r"));
+ QCOMPARE(inputMethodHost->keyEvents[0]->key(), static_cast<int>(Qt::Key_Return));
+ QCOMPARE(inputMethodHost->keyEvents[0]->type(), QEvent::KeyPress);
+ QCOMPARE(inputMethodHost->keyEvents[1]->text(), QString("\r"));
+ QCOMPARE(inputMethodHost->keyEvents[1]->key(), static_cast<int>(Qt::Key_Return));
+ QCOMPARE(inputMethodHost->keyEvents[1]->type(), QEvent::KeyRelease);
+ inputMethodHost->clear();
+
subject->handleKeyClick(KeyEvent("m"));
subject->handleKeyClick(KeyEvent("a"));
QCOMPARE(inputMethodHost->commit, QString("ma"));
@@ -382,7 +402,7 @@
QList<Qt::Key> expectedKeys;
testData << KeyEvent("\b", QEvent::KeyRelease, Qt::Key_Backspace)
- << KeyEvent("\n", QEvent::KeyRelease, Qt::Key_Return)
+ << KeyEvent("\r", QEvent::KeyRelease, Qt::Key_Return)
<< KeyEvent(" ", QEvent::KeyRelease, Qt::Key_Space);
expectedKeys << Qt::Key_Backspace << Qt::Key_Return << Qt::Key_Space;
QVERIFY(testData.count() == expectedKeys.count());
@@ -541,7 +561,7 @@
// press and release backspace before timeout will only delete one character,
subject->handleKeyPress(press);
- QVERIFY(subject->backSpaceTimer.isActive());
+ QVERIFY(subject->backspaceTimer.isActive());
subject->handleKeyRelease(release);
subject->handleKeyClick(release);
subject->update();
@@ -550,15 +570,15 @@
// but hold backspace longer than timeout, will delete the whole preedit.
subject->handleKeyPress(press);
- int interval = subject->backSpaceTimer.interval();
+ int interval = subject->backspaceTimer.interval();
QTest::qWait(interval / 2);
- QVERIFY(subject->backSpaceTimer.isActive());
+ QVERIFY(subject->backspaceTimer.isActive());
subject->update();
QVERIFY(subject->vkbWidget->shiftStatus() == ModifierClearState);
QTest::qWait((interval / 2) + 50);
// final state: preedit(""), shift state:on, after holding backspace enough time.
QVERIFY(subject->preedit.isEmpty());
- QVERIFY(!subject->backSpaceTimer.isActive());
+ QVERIFY(!subject->backspaceTimer.isActive());
inputMethodHost->cursorPos = 13;
subject->update();
QVERIFY(subject->vkbWidget->shiftStatus() == ModifierLatchedState);
@@ -656,6 +676,34 @@
QCOMPARE(inputMethodHost->keyEvents.at(1)->type(), QEvent::KeyRelease);
}
+void Ut_MKeyboardHost::testSendString()
+{
+ QString testString("bacon");
+
+ inputMethodHost->clear();
+
+ subject->sendString(testString);
+ QCOMPARE(inputMethodHost->sendCommitStringCalls, 1);
+ QCOMPARE(inputMethodHost->commit, testString);
+}
+
+void Ut_MKeyboardHost::testSendStringFromToolbar()
+{
+ QString preeditString("delicious");
+ QString toolbarString("bacon");
+
+ inputMethodHost->clear();
+ subject->setPreedit(preeditString);
+ subject->sendStringFromToolbar(toolbarString);
+ QCOMPARE(inputMethodHost->sendCommitStringCalls, 2);
+ QCOMPARE(inputMethodHost->commit, preeditString+toolbarString);
+
+ inputMethodHost->clear();
+ subject->sendStringFromToolbar(toolbarString);
+ QCOMPARE(inputMethodHost->sendCommitStringCalls, 1);
+ QCOMPARE(inputMethodHost->commit, toolbarString);
+}
+
QRegion Ut_MKeyboardHost::region(RegionType type, int index)
{
switch(type) {
@@ -738,12 +786,13 @@
// In opaque mode, candidate widget has its own window, so no regions are sent to kbhost.
#ifndef DUI_IM_DISABLE_TRANSLUCENCY
// Ditto for correction candidate widget
- subject->correctionCandidateWidget->showWidget();
+ subject->correctionHost->setCandidates((QStringList() << "abc" << "def"));
+ subject->correctionHost->showCorrectionWidget();
++c1;
QCOMPARE(inputMethodHost->setScreenRegionCalls, c1);
QCOMPARE(inputMethodHost->setInputMethodAreaCalls, c2);
- subject->correctionCandidateWidget->hide();
+ subject->correctionHost->hideCorrectionWidget();
++c1;
QCOMPARE(inputMethodHost->setScreenRegionCalls, c1);
QCOMPARE(inputMethodHost->setInputMethodAreaCalls, c2);
@@ -1592,5 +1641,55 @@
QCOMPARE(gRequestLanguageNotificationCallCount, expectedCallCount);
}
+
+void Ut_MKeyboardHost::testAutoPunctuation_data()
+{
+ QTest::addColumn<QChar>("character");
+ QTest::addColumn<bool>("autopunctuated");
+
+ QTest::newRow(".") << QChar('.') << true;
+ QTest::newRow(",") << QChar(',') << true;
+ QTest::newRow("!") << QChar('!') << true;
+ QTest::newRow("?") << QChar('?') << true;
+ QTest::newRow("a") << QChar('a') << false;
+}
+
+void Ut_MKeyboardHost::testAutoPunctuation()
+{
+ QFETCH(QChar, character);
+ QFETCH(bool, autopunctuated);
+
+ MGConfItem config(InputMethodCorrectionSetting);
+ config.set(QVariant(true));
+
+ if (subject->imCorrectionEngine) {
+ MImEngineWordsInterfaceFactory::instance()->deleteEngine(subject->imCorrectionEngine);
+ }
+
+ DummyDriverMkh *engine(new DummyDriverMkh);
+ subject->imCorrectionEngine = engine;
+ QStringList candidates;
+ candidates << "foo" << "foobar";
+ subject->setPreedit("fo");
+ engine->setCandidates(candidates);
+ engine->setSuggestedCandidateIndexReturnValue(0);
+ inputMethodHost->preeditRectangleReturnValue = QRect(0, 0, 100, 100);
+ subject->handleKeyClick(KeyEvent("o"));
+ subject->handleKeyClick(KeyEvent(" ", QEvent::KeyRelease, Qt::Key_Space));
+ inputMethodHost->commit.clear();
+ inputMethodHost->preedit.clear();
+ subject->handleKeyClick(KeyEvent(character, QEvent::KeyRelease));
+
+ if (autopunctuated) {
+ QCOMPARE(inputMethodHost->commit, QString(character) + " ");
+ QCOMPARE(inputMethodHost->sendKeyEventCalls, 1);
+ QCOMPARE(inputMethodHost->keyEvents[0]->key(), static_cast<int>(Qt::Key_Backspace));
+ } else {
+ QCOMPARE(inputMethodHost->commit, QString());
+ QCOMPARE(inputMethodHost->preedit, QString(character));
+ QCOMPARE(inputMethodHost->sendKeyEventCalls, 0);
+ }
+}
+
QTEST_APPLESS_MAIN(Ut_MKeyboardHost);
--- tests/ut_mkeyboardhost/ut_mkeyboardhost.h
+++ tests/ut_mkeyboardhost/ut_mkeyboardhost.h
@@ -60,6 +60,9 @@
void testCopyPaste();
void testPlusMinus();
+ void testSendString();
+ void testSendStringFromToolbar();
+
void testRegionSignals();
void testSetState_data();
@@ -101,6 +104,9 @@
void testShowLanguageNotification_data();
void testShowLanguageNotification();
+ void testAutoPunctuation_data();
+ void testAutoPunctuation();
+
private:
void rotateToAngle(M::OrientationAngle);
void triggerAutoCaps();
--- tests/ut_mvirtualkeyboard/ut_mvirtualkeyboard.cpp
+++ tests/ut_mvirtualkeyboard/ut_mvirtualkeyboard.cpp
@@ -1014,7 +1014,7 @@
// Those adjustments dont work for reaction maps:
s->setButtonBoundingRectTopAdjustment(0);
s->setButtonBoundingRectBottomAdjustment(0);
- area->updateButtonGeometriesForWidth(area->geometry().width());
+ area->updateKeyGeometries(area->geometry().width());
}
QTEST_APPLESS_MAIN(Ut_MVirtualKeyboard);
--- tests/ut_notification/test.css
+++ tests/ut_notification/test.css
@@ -18,7 +18,8 @@
close-button-margin : 10;
- notification-font: "Nokia Sans Light" 42;
+ notification-font: $FONT_KEYBOARD;
+ notification-font-size: 42;
notification-border-color: #00FF00;
notification-background-color: #FF0000;
notification-text-color: #0000FF;
--- tests/ut_notification/ut_notification.cpp
+++ tests/ut_notification/ut_notification.cpp
@@ -125,15 +125,11 @@
//This test depends on values in test.css
void Ut_Notification::testCSS()
{
- //construct font in the same way as mstylesheet.cpp
- QFont expected("Nokia Sans Light");
- expected.setPixelSize(42);
-
- QVERIFY(subject->background == QColor(Qt::red));
- QVERIFY(subject->border == QColor(Qt::green));
- QVERIFY(subject->textColor == QColor(Qt::blue));
- QVERIFY(subject->opacity == 1.0);
- QCOMPARE(subject->font, expected);
+ QCOMPARE(subject->background, QColor(Qt::red));
+ QCOMPARE(subject->border, QColor(Qt::green));
+ QCOMPARE(subject->textColor, QColor(Qt::blue));
+ QCOMPARE(subject->opacity, 1.0);
+ QCOMPARE(subject->font.pixelSize(), 42);
}
QTEST_APPLESS_MAIN(Ut_Notification);
--- tests/ut_sharedhandlearea/ut_sharedhandlearea.cpp
+++ tests/ut_sharedhandlearea/ut_sharedhandlearea.cpp
@@ -42,7 +42,7 @@
disableQtPlugins();
app = new MApplication(dummyArgc, dummyArgv);
- createMScene(new MPlainWindow); // also create singleton
+ sceneWindow = createMSceneWindow(new MPlainWindow); // also create singleton
parent = new QGraphicsWidget;
MPlainWindow::instance()->scene()->addItem(parent);
@@ -51,6 +51,7 @@
void Ut_SharedHandleArea::cleanupTestCase()
{
delete parent;
+ delete sceneWindow;
delete MPlainWindow::instance();
delete app;
}
--- tests/ut_sharedhandlearea/ut_sharedhandlearea.h
+++ tests/ut_sharedhandlearea/ut_sharedhandlearea.h
@@ -26,6 +26,7 @@
#include <mimtoolbar.h>
class MApplication;
+class MSceneWindow;
class SharedHandleArea;
class Ut_SharedHandleArea : public QObject
@@ -33,6 +34,7 @@
Q_OBJECT
MApplication *app;
+ MSceneWindow *sceneWindow;
QPointer<SharedHandleArea> subject;
QPointer<QGraphicsWidget> parent;
QPointer<MImToolbar> imToolbar;
--- tests/utils/utils.cpp
+++ tests/utils/utils.cpp
@@ -21,6 +21,7 @@
#include <MApplication>
#include <mplainwindow.h>
#include <MSceneWindow>
+#include <MSceneManager>
#endif
#include <QObject>
#include <QTimer>
@@ -55,13 +56,15 @@
}
-// Create graphics scene
+// Create a scene window, set it to manual managed, and appear it.
#ifdef MEEGOTOUCH
-void createMScene(MPlainWindow *w)
+MSceneWindow * createMSceneWindow(MPlainWindow *w)
{
- w->show();
MSceneWindow *sceneWindow = new MSceneWindow;
- sceneWindow->appear(w);
+ sceneWindow->setManagedManually(true); // we want the scene window to remain in origin
+ w->sceneManager()->appearSceneWindowNow(sceneWindow);
+ w->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
+ return sceneWindow;
}
#endif
--- tests/utils/utils.h
+++ tests/utils/utils.h
@@ -17,7 +17,10 @@
#ifndef UTILS_H
+#include "mimabstractkey.h"
+
class MPlainWindow;
+class MSceneWindow;
class QObject;
// Disable loading of MInputContext and QtMaemo6Style
@@ -27,9 +30,88 @@
void waitForSignal(const QObject* object, const char* signal, int timeout = 500);
#ifdef MEEGOTOUCH
-// Create graphics scene
-void createMScene(MPlainWindow *w);
+// Create a scene window, set it to manual managed, and appear it.
+MSceneWindow *createMSceneWindow(MPlainWindow *w);
#endif
+// copy'n'paste from MImAbstractArea impl file, but counts visits, too:
+class SpecialKeyFinder
+ : public MImAbstractKeyVisitor
+{
+public:
+ enum FindMode {
+ FindShiftKey,
+ FindDeadKey,
+ FindBoth
+ };
+
+private:
+ MImAbstractKey *mShiftKey;
+ MImAbstractKey *mDeadKey;
+ FindMode mode;
+ int mVisits;
+
+public:
+ explicit SpecialKeyFinder(FindMode newMode = FindBoth)
+ : mShiftKey(0)
+ , mDeadKey(0)
+ , mode(newMode)
+ , mVisits(0)
+ {}
+
+ MImAbstractKey *shiftKey() const
+ {
+ return mShiftKey;
+ }
+
+ MImAbstractKey *deadKey() const
+ {
+ return mDeadKey;
+ }
+
+ int visits() const
+ {
+ return mVisits;
+ }
+
+ bool operator()(MImAbstractKey *key)
+ {
+ ++mVisits;
+
+ if (!key) {
+ return false;
+ }
+
+ if (key->isShiftKey()) {
+ mShiftKey = key;
+ } else if (key->isDeadKey()) {
+ mDeadKey = key;
+ }
+
+ switch (mode) {
+ case FindShiftKey:
+ if (mShiftKey) {
+ return true;
+ }
+ break;
+
+ case FindDeadKey:
+ if (mDeadKey) {
+ return true;
+ }
+ break;
+
+ case FindBoth:
+ if (mShiftKey && mDeadKey) {
+ return true;
+ }
+ break;
+ }
+
+
+ return false;
+ }
+};
+
#endif
++++++ meegotouch-inputmethodkeyboard.yaml
--- meegotouch-inputmethodkeyboard.yaml
+++ meegotouch-inputmethodkeyboard.yaml
@@ -1,6 +1,6 @@
Name: meegotouch-inputmethodkeyboard
Summary: MeeGo Virtual Keyboard
-Version: 0.5.9
+Version: 0.5.12
Release: 1
Group: System/GUI/Other
License: LGPL v2.1
@@ -9,6 +9,10 @@
- "%{name}-%{version}.tar.bz2"
Description: MeeGo Virtual Keyboard
+PkgBR:
+ - meegotouch-inputmethodframework-devel
+ - libmeegoimenginewords-devel
+
PkgConfigBR:
- QtCore >= 4.6.0
- QtDBus
@@ -16,8 +20,6 @@
- QtNetwork
- QtGui
- meegotouch
- - MeegoImFramework
- - MeegoImEngine
- meegotouch-feedbackreactionmaps
- x11
- xproto
More information about the MeeGo-commits
mailing list