2
* This file is part of Maliit Plugins
4
* Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.
6
* Contact: Mohammad Anwari <Mohammad.Anwari@nokia.com>
8
* Redistribution and use in source and binary forms, with or without modification,
9
* are permitted provided that the following conditions are met:
11
* Redistributions of source code must retain the above copyright notice, this list
12
* of conditions and the following disclaimer.
13
* Redistributions in binary form must reproduce the above copyright notice, this list
14
* of conditions and the following disclaimer in the documentation and/or other materials
15
* provided with the distribution.
16
* Neither the name of Nokia Corporation nor the names of its contributors may be
17
* used to endorse or promote products derived from this software without specific
18
* prior written permission.
20
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23
* THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
#include "logic/hitlogic.h"
34
#include "models/keyarea.h"
35
#include "models/wordribbon.h"
37
#include <QGraphicsView>
40
namespace MaliitKeyboard {
43
void removeActiveKey(QVector<Key> *active_keys,
46
if (not active_keys) {
50
for (int index = 0; index < active_keys->count(); ++index) {
51
if (active_keys->at(index) == key) {
52
active_keys->remove(index);
64
QWidget *extendedWindow;
65
QSharedPointer<Maliit::Plugins::AbstractGraphicsViewSurface> surface;
66
QSharedPointer<Maliit::Plugins::AbstractGraphicsViewSurface> extendedSurface;
67
QVector<Logic::LayoutHelper *> layouts;
68
QVector<Key> active_keys;
69
WordCandidate active_candidate;
72
QElapsedTimer gesture_timer;
73
bool gesture_triggered;
74
QTimer long_press_timer;
75
Logic::LayoutHelper *long_press_layout;
77
explicit GlassPrivate()
88
, gesture_triggered(false)
92
long_press_timer.setInterval(300);
93
long_press_timer.setSingleShot(true);
97
Glass::Glass(QObject *parent)
99
, d_ptr(new GlassPrivate)
101
connect(&d_ptr->long_press_timer, SIGNAL(timeout()),
102
this, SLOT(onLongPressTriggered()),
103
Qt::UniqueConnection);
109
void Glass::setSurface(const QSharedPointer<Maliit::Plugins::AbstractGraphicsViewSurface> &surface)
113
QWidget *window = surface ? surface->view()->viewport() : 0;
116
qCritical() << __PRETTY_FUNCTION__
117
<< "No window given";
121
d->surface = surface;
125
d->window->installEventFilter(this);
128
void Glass::setExtendedSurface(const QSharedPointer<Maliit::Plugins::AbstractGraphicsViewSurface> &surface)
132
QWidget *window = surface ? surface->view()->viewport() : 0;
135
qCritical() << __PRETTY_FUNCTION__
136
<< "No window given";
140
d->extendedSurface = surface;
141
d->extendedWindow = window;
143
window->installEventFilter(this);
146
void Glass::addLayout(Logic::LayoutHelper *layout)
149
d->layouts.append(layout);
152
void Glass::clearLayouts()
158
bool Glass::eventFilter(QObject *obj,
162
static bool measure_fps(QCoreApplication::arguments().contains("-measure-fps"));
164
if (not obj || not ev) {
168
const QSharedPointer<Maliit::Plugins::AbstractGraphicsViewSurface> &eventSurface(obj == d->extendedWindow ? d->extendedSurface : d->surface);
171
case QEvent::Paint: {
173
static int count = 0;
174
static QElapsedTimer fps_timer;
176
if (0 == count % 120) {
177
qDebug() << "FPS:" << count / ((0.01 + fps_timer.elapsed()) / 1000) << count;
187
case QKeyEvent::MouseButtonPress:
188
d->gesture_timer.restart();
189
d->gesture_triggered = false;
191
return handlePressReleaseEvent(ev, eventSurface);
193
case QKeyEvent::MouseButtonRelease:
194
d->long_press_timer.stop();
196
if (d->gesture_triggered) {
200
return handlePressReleaseEvent(ev, eventSurface);
202
case QKeyEvent::MouseMove: {
203
if (d->gesture_triggered) {
207
QMouseEvent *qme = static_cast<QMouseEvent *>(ev);
210
Q_FOREACH (Logic::LayoutHelper *layout, d->layouts) {
211
const QSharedPointer<Maliit::Plugins::AbstractGraphicsViewSurface> targetSurface(layout->activePanel() == Logic::LayoutHelper::ExtendedPanel ? d->extendedSurface : d->surface);
213
const QPoint &pos(targetSurface->translateEventPosition(qme->pos(), eventSurface));
214
const QPoint &last_pos(targetSurface->translateEventPosition(d->last_pos, eventSurface));
215
d->last_pos = qme->pos();
217
const QPoint &press_pos(targetSurface->translateEventPosition(d->press_pos, eventSurface));
219
const QRect &rect(layout->activeKeyAreaGeometry());
221
if (d->gesture_timer.elapsed() < 250) {
222
if (pos.y() > (press_pos.y() - rect.height() * 0.33)
223
&& pos.y() < (press_pos.y() + rect.height() * 0.33)) {
224
if (pos.x() < (press_pos.x() - rect.width() * 0.33)) {
225
d->gesture_triggered = true;
226
Q_EMIT switchRight(layout);
227
} else if (pos.x() > (press_pos.x() + rect.width() * 0.33)) {
228
d->gesture_triggered = true;
229
Q_EMIT switchLeft(layout);
231
} else if (pos.x() > (press_pos.x() - rect.width() * 0.33)
232
&& pos.x() < (press_pos.x() + rect.width() * 0.33)) {
233
if (pos.y() > (press_pos.y() + rect.height() * 0.50)) {
234
d->gesture_triggered = true;
235
Q_EMIT keyboardClosed();
240
if (d->gesture_triggered) {
241
Q_FOREACH (const Key &k, d->active_keys) {
242
Q_EMIT keyExited(k, layout);
245
d->active_keys.clear();
250
const Key &last_key(Logic::keyHit(d->active_keys, rect, last_pos));
253
const Key &key(Logic::keyHit(layout->activeKeyArea().keys(),
254
layout->activeKeyAreaGeometry(),
257
if (last_key != key) {
258
if (last_key.valid()) {
259
removeActiveKey(&d->active_keys, last_key);
260
d->long_press_timer.stop();
261
Q_EMIT keyExited(last_key, layout);
265
d->active_keys.append(key);
267
if (key.hasExtendedKeys() || key.action() == Key::ActionSpace) {
268
d->long_press_timer.start();
269
d->long_press_layout = layout;
272
Q_EMIT keyEntered(key, layout);
287
void Glass::onLongPressTriggered()
291
if (d->gesture_triggered
292
|| d->active_keys.isEmpty()
293
|| not d->long_press_layout
294
|| d->long_press_layout->activePanel() == Logic::LayoutHelper::ExtendedPanel) {
298
Q_FOREACH (const Key &k, d->active_keys) {
299
Q_EMIT keyExited(k, d->long_press_layout); // Not necessarily correct layout for the key ...
302
Q_EMIT keyLongPressed(d->active_keys.last(), d->long_press_layout);
303
d->active_keys.clear();
306
bool Glass::handlePressReleaseEvent(QEvent *ev,
307
const QSharedPointer<Maliit::Plugins::AbstractGraphicsViewSurface> &eventSurface)
315
bool consumed = false;
316
QMouseEvent *qme = static_cast<QMouseEvent *>(ev);
317
d->last_pos = qme->pos();
318
d->press_pos = qme->pos(); // FIXME: dont update on mouse release, clear instead.
321
Q_FOREACH (Logic::LayoutHelper *layout, d->layouts) {
322
const QSharedPointer<Maliit::Plugins::AbstractGraphicsViewSurface> targetSurface(layout->activePanel() == Logic::LayoutHelper::ExtendedPanel ? d->extendedSurface : d->surface);
324
const QPoint &pos(targetSurface->translateEventPosition(qme->pos(), eventSurface));
326
switch (qme->type()) {
327
case QKeyEvent::MouseButtonPress: {
328
const Key &key(Logic::keyHit(layout->activeKeyArea().keys(),
329
layout->activeKeyAreaGeometry(),
330
pos, d->active_keys));
333
d->active_keys.append(key);
334
Q_EMIT keyPressed(key, layout);
335
Q_EMIT keyAreaPressed(layout->activePanel(), layout);
337
if (key.hasExtendedKeys() || key.action() == Key::ActionSpace) {
338
d->long_press_timer.start();
339
d->long_press_layout = layout;
344
const WordCandidate &candidate(Logic::wordCandidateHit(layout->wordRibbon()->candidates(),
345
layout->wordRibbon()->rect(),
348
if (candidate.valid()) {
349
d->active_candidate = candidate;
350
Q_EMIT wordCandidatePressed(candidate, layout);
353
// Hit empty area, we use keyAreaReleased to cancel any user action.
354
// TODO: Better introduce cancelled signal?
355
Q_EMIT keyAreaReleased(Logic::LayoutHelper::NumPanels, layout);
360
case QKeyEvent::MouseButtonRelease: {
361
const Key &key(Logic::keyHit(layout->activeKeyArea().keys(),
362
layout->activeKeyAreaGeometry(),
363
pos, d->active_keys, Logic::AcceptIfInFilter));
366
removeActiveKey(&d->active_keys, key);
367
Q_EMIT keyReleased(key, layout);
368
Q_EMIT keyAreaReleased(layout->activePanel(), layout);
371
const WordCandidate &candidate(Logic::wordCandidateHit(layout->wordRibbon()->candidates(),
372
layout->wordRibbon()->rect(),
375
if (candidate.valid() && candidate == d->active_candidate) {
376
d->active_candidate = WordCandidate();
377
Q_EMIT wordCandidateReleased(candidate, layout);
388
Logic::LayoutHelper::Panel panel = Logic::LayoutHelper::NumPanels;
390
if (layout->centerPanel().rect().contains(pos))
391
panel = Logic::LayoutHelper::CenterPanel;
392
else if (QRect(d->extendedSurface->relativePosition(), d->extendedSurface->size()).contains(pos))
393
panel = Logic::LayoutHelper::ExtendedPanel;
394
else if (layout->leftPanel().rect().contains(pos))
395
panel = Logic::LayoutHelper::LeftPanel;
396
else if (layout->rightPanel().rect().contains(pos))
397
panel = Logic::LayoutHelper::RightPanel;
399
if (panel != Logic::LayoutHelper::NumPanels) {
400
if (qme->type() == QKeyEvent::MouseButtonPress) {
401
Q_EMIT keyAreaPressed(panel, layout);
402
} else if (qme->type() == QKeyEvent::MouseButtonRelease) {
403
Q_EMIT keyAreaReleased(panel, layout);
413
} // namespace MaliitKeyboard