/****************************************************************************
**
** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (developer.feedback@nokia.com)
**
** This file is part of the HbCore module of the UI Extensions for Mobile.
**
** GNU Lesser General Public License Usage
** This file may be used 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.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at developer.feedback@nokia.com.
**
****************************************************************************/
#include "hbabstractvkbhost.h"
#include "hbabstractvkbhost_p.h"
#include "private/hbvkbgeometrylogic_p.h"
#include "hbinputvirtualkeyboard.h"
#include "hbinputsettingproxy.h"
#include "hbinputvkbhostbridge.h"
#include "hbinputmethod.h"
#include "hbdeviceprofile.h"
#include "hbscrollarea.h"
#include "hbmainwindow.h"
#include "hbpopup.h"
#include "hbview.h"
#include "hbwidgetfeedback.h"
#include "hbinstance.h"
#include <QTextEdit>
const int HbAnimationTime = 200;
const qreal HbHeightVerticalFactor = 0.5;
const qreal HbHeightHorizFactor = 0.7;
const qreal HbEditorExtraMargin = 17.0;
const QString KHandWritingName("Handwriting");
// see hbpopup.cpp for this
extern const char* KPositionManagedByVKB;
/// @cond
HbVkbHostContainerWidget::HbVkbHostContainerWidget(QObject *containerWidget)
{
mGraphicsObject = qobject_cast<QGraphicsObject*>(containerWidget);
if (!mGraphicsObject) {
mWidget = qobject_cast<QWidget*>(containerWidget);
}
}
/*!
\internal
Sets container widgets position to new position.
*/
void HbVkbHostContainerWidget::setPos(QPointF newPosition)
{
if (mGraphicsObject) {
mGraphicsObject->setPos(newPosition);
return;
}
if (mWidget) {
#ifdef Q_WS_WIN
QPoint finalPosition = newPosition.toPoint();
finalPosition -= mWidget->geometry().topLeft() - mWidget->frameGeometry().topLeft();
mWidget->move(finalPosition);
#else
mWidget->move(newPosition.toPoint());
#endif
return;
}
}
/*!
\internal
Returns the global position, if container widget is a QGraphicsObject, it returns
scene position. In case the widget is QWidget it returns global co-ordinates
*/
QPointF HbVkbHostContainerWidget::pos()
{
if (mGraphicsObject) {
return mGraphicsObject->pos();
}
if (mWidget) {
return mWidget->mapToGlobal(QPoint(0, 0));
}
return QPointF(0, 0);
}
/*!
\internal
Returns the bounding rect in global co-ordinate, if container widget is a QGraphicsObject
it returns in scene co-ordinate, incase widget is QWidget it returns in global co-ordinate
*/
QRectF HbVkbHostContainerWidget::sceneBoundingRect()
{
if (mGraphicsObject) {
return mGraphicsObject->sceneBoundingRect();
}
if (mWidget) {
return QRectF(mWidget->mapToGlobal(QPoint(0, 0)), mWidget->size());
}
return QRectF(0, 0, 0, 0);
}
/*!
\internal
Connects ontainer specific signals.
*/
void HbVkbHostContainerWidget::connectSignals(QObject *receiver)
{
if (mGraphicsObject) {
QObject::connect(mGraphicsObject, SIGNAL(yChanged()),
receiver, SLOT(ensureCursorVisibility()));
}
HbPopup *popup = qobject_cast<HbPopup *>(mGraphicsObject);
if (popup) {
QObject::connect(popup, SIGNAL(aboutToHide()), receiver, SLOT(_q_containerAboutToClose()));
}
HbView *view = qobject_cast<HbView *>(mGraphicsObject);
if (view) {
QObject::connect(view->mainWindow(), SIGNAL(currentViewChanged(HbView *)),
receiver, SLOT(_q_containerAboutToClose()));
}
}
/*!
\internal
Disconnects container specific signals.
*/
void HbVkbHostContainerWidget::disconnectSignals(QObject *receiver)
{
if (mGraphicsObject) {
QObject::disconnect(mGraphicsObject, SIGNAL(yChanged()),
receiver, SLOT(ensureCursorVisibility()));
}
HbPopup *popup = qobject_cast<HbPopup *>(mGraphicsObject);
if (popup) {
QObject::disconnect(popup, SIGNAL(aboutToHide()), receiver, SLOT(_q_containerAboutToClose()));
}
HbPopup *view = qobject_cast<HbPopup *>(mGraphicsObject);
if (view) {
QObject::disconnect(view->mainWindow(), SIGNAL(currentViewChanged(HbView *)),
receiver, SLOT(_q_containerAboutToClose()));
}
}
/// @endcond
/*!
\proto
@hbcore
\class HbAbstractVkbHost
\brief Base class for HbCore's virtual keyboard hosts.
This class contains common code for HbCore's virtual keyboard hosts.
*/
/// @cond
HbAbstractVkbHostPrivate::HbAbstractVkbHostPrivate(HbAbstractVkbHost *myHost, QObject *containerWidget)
: q_ptr(myHost),
mCallback(0),
mKeypad(0),
mContainerWidget(new HbVkbHostContainerWidget(containerWidget)),
mTimeLine(HbAnimationTime),
mKeypadStatus(HbVkbHost::HbVkbStatusClosed),
mKeypadOperationOngoing(false),
mOriginalContainerPosition(QPointF(0, 0)),
mContainerMovementStartingPoint(QPointF(0, 0)),
mContainerMovementVector(QPointF(0, 0)),
mKeypadMovementStartingPoint(QPointF(0, 0)),
mKeypadMovementVector(QPointF(0, 0)),
mInputMethod(0),
mKeypadStatusBeforeOrientationChange(HbVkbHost::HbVkbStatusClosed),
mTitleBarHiddenByVkbHost(false),
mStatusBarHiddenByVkbHost(false)
{
mTimeLine.setUpdateInterval(16);
}
HbAbstractVkbHostPrivate::~HbAbstractVkbHostPrivate()
{
delete mContainerWidget;
}
/*!
\internal
Initializes starting values to parameters needed for running the keypad and
container widget animation effects.
*/
void HbAbstractVkbHostPrivate::prepareAnimationsCommon()
{
if (mContainerWidget && mContainerWidget->widgetObject() && mKeypad) {
// If the keyboard is not already open, remember the original position.
// That is where the container will eventually be returned to.
if (mKeypadStatus == HbVkbHost::HbVkbStatusClosed) {
mOriginalContainerPosition = mContainerWidget->pos();
}
// Initialize movement variables to starting values. These will
// be fine tuned later.
mKeypadMovementVector = QPointF(0, 0);
mContainerMovementVector = QPointF(0, 0);
mContainerMovementStartingPoint = mContainerWidget->pos();
mKeypadMovementStartingPoint = mKeypad->pos();
}
mScreenSize = screenSize();
// Make sure that the editor is completely visible inside a scrol area.
ensureVisibilityInsideScrollArea();
}
/*!
\internal
Sets up view and focus object variables.
*/
bool HbAbstractVkbHostPrivate::getViewAndFocusObjects(HbView*& currentView, HbInputFocusObject*& focusObject)
{
if (!mKeypad || !mInputMethod || !mContainerWidget ) {
return false;
}
HbMainWindow* window = mainWindow();
if (!window) {
return false;
}
currentView = window->currentView();
if (!currentView) {
return false;
}
focusObject = mInputMethod->focusObject();
if (!focusObject) {
return false;
}
return true;
}
/*!
\internal
Sets up the container widget animation effect.
*/
bool HbAbstractVkbHostPrivate::prepareContainerAnimation(HbVkbHost::HbVkbStatus status)
{
// Init and check main objects
HbView* currentView = 0;
HbInputFocusObject* focusObject = 0;
if (!getViewAndFocusObjects(currentView, focusObject)) {
return false;
}
// Init parameters before calling...
QSizeF keypadSize = mKeypad->size();
QRectF viewRect = currentView->sceneBoundingRect();
bool isPopupType = qobject_cast<HbPopup *>(mContainerWidget->widgetObject()) != NULL;
bool vkbOpen = mKeypadStatus == HbVkbHost::HbVkbStatusOpened;
bool titlebarVisible = currentView->isItemVisible(Hb::TitleBarItem);
bool statusbarVisible = currentView->isItemVisible(Hb::StatusBarItem);
QRectF containerRect = mContainerWidget->sceneBoundingRect();
QRectF editorRect = focusObject->editorGeometry();
QRectF cursorRect = focusObject->microFocus();
if (status == HbVkbHost::HbVkbStatusOpened) {
// Initialize geometry calculation unit to handle all geometry calculations.
HbVkbGeometryLogicPrivate unit = HbVkbGeometryLogicPrivate(
mScreenSize,
keypadSize,
viewRect,
isPopupType,
vkbOpen,
titlebarVisible,
statusbarVisible,
containerRect,
editorRect,
cursorRect);
return unit.calculateContainerMovement( mContainerMovementVector );
}
return false;
}
/*!
\internal
Sets up the keypad widget animation effect.
*/
bool HbAbstractVkbHostPrivate::prepareKeypadAnimation(HbVkbHost::HbVkbStatus status)
{
if (mKeypad) {
if (status == HbVkbHost::HbVkbStatusOpened) {
if (mKeypadStatus == HbVkbHost::HbVkbStatusClosed) {
// Set up keyboard open animation.
mKeypadMovementStartingPoint.setY(mScreenSize.height());
mKeypadMovementVector.setY(-mKeypad->size().height());
if (!disableCursorShift()) {
// Initialize keypad position
mKeypad->setPos(mKeypadMovementStartingPoint);
}
return true;
} else if (mKeypadStatus == HbVkbHost::HbVkbStatusMinimized && mCallback) {
mKeypadMovementVector.setY(-(mKeypad->size().height() - mCallback->minimizedKeyboardSize().height()));
return true;
}
} else {
// It is going to be closed.
mKeypadMovementVector = QPointF(0, mKeypad->size().height());
return true;
}
}
return false;
}
/*!
\internal
Sets up all the animation effects.
*/
bool HbAbstractVkbHostPrivate::prepareAnimations(HbVkbHost::HbVkbStatus status)
{
prepareAnimationsCommon();
bool containerResult = prepareContainerAnimation(status);
if (containerResult) {
// A sanity check. Container should never be moved below it's original
// position. Limit the movement in case editor's micro focus returned faulty value
// or something else bad happened.
if ((mContainerMovementStartingPoint + mContainerMovementVector).y() > mOriginalContainerPosition.y()) {
mContainerMovementVector.setY(mOriginalContainerPosition.y() - mContainerMovementStartingPoint.y());
qWarning("Abstract VKB host: Invalid container position.");
}
}
return (containerResult | prepareKeypadAnimation(status));
}
/*!
\internal
Connects orientation change, view switching and possible other related signals.
*/
void HbAbstractVkbHostPrivate::connectSignals()
{
mContainerWidget->connectSignals(q_ptr);
// global signal not specific to any containter widget, can be connected now.
HbMainWindow *mainWindow = this->mainWindow();
if (mainWindow) {
q_ptr->connect(mainWindow, SIGNAL(aboutToChangeOrientation()), q_ptr, SLOT(orientationAboutToChange()));
q_ptr->connect(mainWindow, SIGNAL(orientationChanged(Qt::Orientation)), q_ptr, SLOT(orientationChanged(Qt::Orientation)));
q_ptr->connect(mainWindow, SIGNAL(aboutToChangeView(HbView *, HbView *)), q_ptr, SLOT(aboutToChangeView(HbView *, HbView *)));
}
}
/*!
\internal
Disconnects orientation change, view switching and possible other related signals.
*/
void HbAbstractVkbHostPrivate::disconnectSignals()
{
mContainerWidget->disconnectSignals(q_ptr);
// global signal not specific to any containter widget, can be connected now.
HbMainWindow *mainWindow = this->mainWindow();
if (mainWindow) {
q_ptr->disconnect(mainWindow, SIGNAL(aboutToChangeOrientation()), q_ptr, SLOT(orientationAboutToChange()));
q_ptr->disconnect(mainWindow, SIGNAL(orientationChanged(Qt::Orientation)), q_ptr, SLOT(orientationChanged(Qt::Orientation)));
q_ptr->disconnect(mainWindow, SIGNAL(aboutToChangeView(HbView *, HbView *)), q_ptr, SLOT(aboutToChangeView(HbView *, HbView *)));
}
}
/*!
\internal
Opens the keypad widget with animation effect.
*/
void HbAbstractVkbHostPrivate::openKeypad()
{
if (mContainerWidget->widgetObject()) {
HbMainWindow *mainWin = mainWindow();
if (mainWin && mKeypad) {
if (mKeypad->scene() != mainWin->scene()) {
// Add keypad to scene if it is not already in there.
mainWin->scene()->addItem(mKeypad);
}
if (mKeypadStatus != HbVkbHost::HbVkbStatusOpened) {
if (mCallback) {
mCallback->aboutToOpen(q_ptr);
}
q_ptr->resizeKeyboard(); // Make sure that the keyboard doesn't exceed given boundaries.
}
if (prepareAnimations(HbVkbHost::HbVkbStatusOpened)) {
// hide the title bar and status bar when opening the vkb
setTitleAndStatusBarVisible(false);
// Run the animation
mKeypadStatus = HbVkbHost::HbVkbStatusOpened;
mTimeLine.start();
}
}
}
}
/*!
\internal
Closes the keypad with animation effect.
*/
void HbAbstractVkbHostPrivate::closeKeypad()
{
if (mKeypadStatus != HbVkbHost::HbVkbStatusClosed) {
if (mCallback) {
mCallback->aboutToClose(q_ptr);
}
if (prepareAnimations(HbVkbHost::HbVkbStatusClosed)) {
// show the title bar and status bar when closing the vkb
setTitleAndStatusBarVisible(true);
mKeypadStatus = HbVkbHost::HbVkbStatusClosed;
mTimeLine.start();
}
}
}
/*!
\internal
Opens the keypad widget without animation effect.
*/
void HbAbstractVkbHostPrivate::openKeypadWithoutAnimation()
{
HbMainWindow *mainWin = mainWindow();
if (mKeypadStatus != HbVkbHost::HbVkbStatusOpened && mKeypad && mContainerWidget->widgetObject() && mainWin) {
if (mKeypad->scene() != mainWin->scene()) {
// Add item to scene if it is not already in there.
mainWin->scene()->addItem(mKeypad);
}
if (mKeypadStatus != HbVkbHost::HbVkbStatusOpened) {
if (mCallback) {
mCallback->aboutToOpen(q_ptr);
}
q_ptr->resizeKeyboard(); // Make sure that the keyboard doesn't exceed given boundaries.
}
if (prepareAnimations(HbVkbHost::HbVkbStatusOpened)) {
// hide the title bar and status bar when opening the vkb
setTitleAndStatusBarVisible(false);
if (!disableCursorShift()) {
// Move the container widget to keep the focused line visible.
mContainerWidget->setPos(mContainerWidget->pos() + mContainerMovementVector);
// Move the keypad
mKeypad->setPos(mKeypadMovementStartingPoint + mKeypadMovementVector);
}
mKeypadStatus = HbVkbHost::HbVkbStatusOpened;
if (mCallback) {
mCallback->keyboardOpened(q_ptr);
}
}
}
}
/*!
\internal
Closes the keypad widget without animating it.
*/
void HbAbstractVkbHostPrivate::closeKeypadWithoutAnimation()
{
if (mKeypadStatus != HbVkbHost::HbVkbStatusClosed && mKeypad && mCallback) {
mCallback->aboutToClose(q_ptr);
// show the title bar and status bar when closing the vkb
setTitleAndStatusBarVisible(true);
// Set original content widget position
mKeypadStatus = HbVkbHost::HbVkbStatusClosed;
if (!disableCursorShift()) {
// Return the container widget to original position.
mContainerWidget->setPos(mOriginalContainerPosition);
mContainerWidget->widgetObject()->setProperty(KPositionManagedByVKB, false );
}
// Hide the keypad
mKeypad->hide();
mCallback->keyboardClosed(q_ptr);
mCallback = 0;
}
}
/*!
\internal
Cancels the ongoing keypad animation and resets the timeline timer.
*/
void HbAbstractVkbHostPrivate::cancelAnimationAndHideVkbWidget()
{
if (mTimeLine.state() == QTimeLine::Running) {
mTimeLine.stop();
if (!disableCursorShift() && mContainerWidget && mContainerWidget->widgetObject()) {
mContainerWidget->setPos(mOriginalContainerPosition);
mContainerWidget->widgetObject()->setProperty(KPositionManagedByVKB, false );
}
if (mKeypad) {
mKeypad->hide();
}
// Clear possible pending call.
mPendingCall.vkb = 0;
emit q_ptr->keypadClosed();
HbVkbHostBridge::instance()->connectHost(0);
mKeypadStatus = HbVkbHost::HbVkbStatusClosed;
}
}
/*!
\internal
Returns pointer to container's main window (if one exists).
*/
HbMainWindow *HbAbstractVkbHostPrivate::mainWindow() const
{
HbWidget *hbWidget = qobject_cast<HbWidget *>(mContainerWidget->widgetObject());
if (hbWidget) {
return hbWidget->mainWindow();
}
// below is the case when we have a pure vanilla application.
// there should be one hbmainwindow to show all the widgets.
if (hbInstance->allMainWindows().size()) {
return hbInstance->allMainWindows().at(0);
}
// no mainwindow.
return 0;
}
/*!
\internal
Returns screen size.
*/
QSizeF HbAbstractVkbHostPrivate::screenSize() const
{
HbMainWindow *mainWin = mainWindow();
QSizeF result = static_cast<QSizeF>(HbDeviceProfile::profile(mainWin).logicalSize());
// do some sanity checking for the size got from device profile
if (result.isNull() || result.width() < 200 || result.height() < 200) {
qWarning("VkbHost error: size from device profile is faulty, using fallback!");
if (mainWin) {
if (mainWin->orientation() == Qt::Horizontal) {
result.setWidth(640);
result.setHeight(360);
} else {
result.setWidth(360);
result.setHeight(640);
}
}
}
return result;
}
bool HbAbstractVkbHostPrivate::disableCursorShift()
{
if (!mInputMethod
|| mainWindow()) {
return false;
}
QByteArray baModes = HbInputSettingProxy::instance()->preferredInputMethodCustomData(Qt::Horizontal);
QString imName(baModes);
if (mainWindow() && mainWindow()->orientation() == Qt::Horizontal && imName == KHandWritingName) {
return true;
}
return false;
}
/*!
\internal
Sets title and status bar visibility.
*/
void HbAbstractVkbHostPrivate::setTitleAndStatusBarVisible(bool visible)
{
// should not change the title bar and status bar visibility when the keypad is opened from a popup
HbPopup *popup = qobject_cast<HbPopup *>(mContainerWidget->widgetObject());
if (popup) {
return;
}
// Set flags to control view behaviour when visibility is changed
HbMainWindow *mainWin = mainWindow();
HbView *currentView;
if (mainWin && (currentView = mainWin->currentView())) {
// Get view flags to use
HbView::HbViewFlags viewFlags = HbView::ViewFlagNone;
getViewFlagsForVisibilityChange(viewFlags, visible, currentView);
// Set the flags
currentView->setViewFlags(viewFlags);
}
}
/*!
\internal
Specifies view flags to use when Title/Statusbar visibility changes.
Derived classes should override as appropriate
*/
void HbAbstractVkbHostPrivate::getViewFlagsForVisibilityChange(HbView::HbViewFlags &viewFlags, bool visible, HbView *view)
{
if (!view) {
return;
}
HbView::HbViewFlags flags = view->viewFlags();
HbView::HbViewFlags setFlags = HbView::ViewFlagNone;
if (!visible) {
// Should set title and statur bar to not visible.
// Check if they were visible before, and set a variable
// to remember it was us who hid them.
if (!(flags & HbView::ViewTitleBarHidden)) {
setFlags |= HbView::ViewTitleBarHidden;
mTitleBarHiddenByVkbHost = true;
}
if (!(flags & HbView::ViewStatusBarHidden)) {
setFlags |= HbView::ViewStatusBarHidden;
mStatusBarHiddenByVkbHost = true;
}
if (mTitleBarHiddenByVkbHost || mStatusBarHiddenByVkbHost) {
setFlags |= HbView::ViewDisableRelayout;
}
// Return the original flags plus the ones we may have set
viewFlags = (flags | setFlags);
} else {
// Title and status bar should become visible.
// Check if we hid them and clear any flags we might have set before.
if ((flags & HbView::ViewDisableRelayout) &&
(mTitleBarHiddenByVkbHost || mStatusBarHiddenByVkbHost)) {
setFlags |= HbView::ViewDisableRelayout;
}
if ((flags & HbView::ViewTitleBarHidden) && mTitleBarHiddenByVkbHost) {
setFlags |= HbView::ViewTitleBarHidden;
mTitleBarHiddenByVkbHost = false;
}
if ((flags & HbView::ViewStatusBarHidden) && mStatusBarHiddenByVkbHost) {
setFlags |= HbView::ViewStatusBarHidden;
mStatusBarHiddenByVkbHost = false;
}
// Return the original flags, clear the ones we have set before
viewFlags = (flags & ~setFlags);
}
}
/*!
\internal
Closes the keypad. This slot is connected to various signals from
different container classes.
*/
void HbAbstractVkbHostPrivate::_q_containerAboutToClose()
{
Q_Q(HbAbstractVkbHost);
q->closeKeypad();
}
/*!
\internal
Finds out if given editor is inside a scroll area and makes sure the cursor position is visible inside the
scroll area.
*/
void HbAbstractVkbHostPrivate::ensureVisibilityInsideScrollArea() const
{
if (mInputMethod && mInputMethod->focusObject()) {
HbInputFocusObject *fo = mInputMethod->focusObject();
HbScrollArea *scrollArea = 0;
QGraphicsObject *graphicsObject = qobject_cast<QGraphicsObject *>(fo->object());
if (graphicsObject) {
for (QGraphicsWidget *parent = graphicsObject->parentWidget(); parent; parent = parent->parentWidget()) {
scrollArea = qobject_cast<HbScrollArea*>(parent);
if (scrollArea) {
break;
}
}
}
if (scrollArea && scrollArea->contentWidget()) {
QRectF scrollRect = scrollArea->sceneBoundingRect();
QRectF editorRect = fo->editorGeometry();
if (!scrollRect.contains(editorRect) &&
scrollRect.intersects(editorRect)) {
// The editor is half visible inside a scroll area.
// Calculate how much the area needs to be scrolled
// to make the cursor line visible inside the scroll
// area. The call scroll area's ensure visibility and
// return the calculated value (it needs to be factored in
// to container animation calculations).
if (editorRect.height() < scrollRect.height()) {
// Whole editor rect fits into scroll area. Move it there.
if (editorRect.bottom() > scrollRect.bottom()) {
// Scroll upwards.
scrollArea->ensureVisible(scrollArea->contentWidget()->mapFromScene(editorRect.bottomLeft()), 0.0, HbEditorExtraMargin);
} else {
// Scroll downwards.
scrollArea->ensureVisible(scrollArea->contentWidget()->mapFromScene(editorRect.topLeft()), 0.0, HbEditorExtraMargin);
}
} else {
// Whole editor doesn't fit into scroll area. Used micro focus position instead.
QRectF microFocus = fo->microFocus();
if (microFocus.bottom() > scrollRect.bottom()) {
// Scroll upwards.
scrollArea->ensureVisible(scrollArea->contentWidget()->mapFromScene(microFocus.bottomLeft()), 0.0, HbEditorExtraMargin);
} else {
// Scroll downwards.
scrollArea->ensureVisible(scrollArea->contentWidget()->mapFromScene(microFocus.topLeft()), 0.0, HbEditorExtraMargin);
}
}
}
}
}
}
/// @endcond
HbAbstractVkbHost::HbAbstractVkbHost(HbWidget *containerWidget) : d_ptr(new HbAbstractVkbHostPrivate(this, containerWidget))
{
Q_D(HbAbstractVkbHost);
setParent(containerWidget);
HbVkbHost::attachHost(this, containerWidget);
if (containerWidget) {
containerWidget->setFlag(QGraphicsItem::ItemSendsGeometryChanges);
}
connect(&d->mTimeLine, SIGNAL(finished()), this, SLOT(animationFinished()));
connect(&d->mTimeLine, SIGNAL(valueChanged(qreal)), this, SLOT(animValueChanged(qreal)));
}
HbAbstractVkbHost::HbAbstractVkbHost(QWidget *containerWidget) : d_ptr(new HbAbstractVkbHostPrivate(this, containerWidget))
{
Q_D(HbAbstractVkbHost);
setParent(containerWidget);
HbVkbHost::attachHost(this, containerWidget);
connect(&d->mTimeLine, SIGNAL(finished()), this, SLOT(animationFinished()));
connect(&d->mTimeLine, SIGNAL(valueChanged(qreal)), this, SLOT(animValueChanged(qreal)));
}
HbAbstractVkbHost::HbAbstractVkbHost(QGraphicsWidget *containerWidget) : d_ptr(new HbAbstractVkbHostPrivate(this, containerWidget))
{
Q_D(HbAbstractVkbHost);
setParent(containerWidget);
HbVkbHost::attachHost(this, containerWidget);
if (containerWidget) {
containerWidget->setFlag(QGraphicsItem::ItemSendsGeometryChanges);
}
connect(&d->mTimeLine, SIGNAL(finished()), this, SLOT(animationFinished()));
connect(&d->mTimeLine, SIGNAL(valueChanged(qreal)), this, SLOT(animValueChanged(qreal)));
}
HbAbstractVkbHost::HbAbstractVkbHost(QGraphicsObject *containerWidget) : d_ptr(new HbAbstractVkbHostPrivate(this, containerWidget))
{
Q_D(HbAbstractVkbHost);
setParent(containerWidget);
HbVkbHost::attachHost(this, containerWidget);
if (containerWidget) {
containerWidget->setFlag(QGraphicsItem::ItemSendsGeometryChanges);
}
connect(&d->mTimeLine, SIGNAL(finished()), this, SLOT(animationFinished()));
connect(&d->mTimeLine, SIGNAL(valueChanged(qreal)), this, SLOT(animValueChanged(qreal)));
}
HbAbstractVkbHost::HbAbstractVkbHost(HbAbstractVkbHostPrivate *dd) : d_ptr(dd)
{
Q_D(HbAbstractVkbHost);
setParent(d->mContainerWidget->widgetObject());
HbVkbHost::attachHost(this, d->mContainerWidget->widgetObject());
connect(&d->mTimeLine, SIGNAL(finished()), this, SLOT(animationFinished()));
connect(&d->mTimeLine, SIGNAL(valueChanged(qreal)), this, SLOT(animValueChanged(qreal)));
}
HbAbstractVkbHost::~HbAbstractVkbHost()
{
if (d_ptr->mKeypad) {
d_ptr->mKeypad->hide();
if (d_ptr->mCallback) {
d_ptr->mCallback->keyboardClosed(this);
d_ptr->mCallback = 0;
}
}
emit keypadClosed();
delete d_ptr;
}
/*!
\reimp
*/
HbVkbHost::HbVkbStatus HbAbstractVkbHost::keypadStatus() const
{
Q_D(const HbAbstractVkbHost);
return d->mKeypadStatus;
}
/*!
\reimp
*/
void HbAbstractVkbHost::openKeypad(HbVirtualKeyboard *vkb, HbInputMethod *owner, bool animationAllowed)
{
Q_D(HbAbstractVkbHost);
if (owner) {
d->mInputMethod = owner;
}
if ((d->mKeypadStatus != HbVkbStatusMinimized) && (!vkb || !owner)) {
// The caller is opening the keypad for the first time but didn't supply
// all the needed parameters. Null parameters are ok only if minimized
// keypad is reopened.
return;
}
if (!HbVkbHostBridge::instance()->connectHost(this)) {
// Do not set open call pending if orientation change is ongoing
connect(HbVkbHostBridge::instance(), SIGNAL(stateTransitionCompleted()), this, SLOT(stateTransitionCompleted()));
// The previous keyboard is still closing. Set the call pending and return.
d->mPendingCall.vkb = vkb;
d->mPendingCall.animationAllowed = animationAllowed;
return;
}
if (!d->mKeypadOperationOngoing) {
d->mKeypadOperationOngoing = true;
if (vkb && (d->mCallback != vkb || !d->mKeypad)) {
// This keypad is opened for the first time or it was opened before but some other keypad
// was opened in between.
d->mCallback = vkb;
d->mKeypad = vkb->asGraphicsWidget();
}
if (!d->mKeypad) {
// Keyboard widget creation failed for some reason, can't go on.
d->mCallback = 0;
return;
}
emit aboutToOpen();
if (d->mContainerWidget && d->mContainerWidget->widgetObject()) {
d->mContainerWidget->widgetObject()->setProperty(KPositionManagedByVKB, true );
}
if (animationAllowed) {
d->openKeypad();
} else {
d->openKeypadWithoutAnimation();
emit keypadOpened();
}
HbWidgetFeedback::triggered(qobject_cast<const HbWidget *>(d->mKeypad), Hb::InstantPopupOpened);
d->connectSignals();
d->mKeypadOperationOngoing = false;
}
}
/*!
\reimp
*/
void HbAbstractVkbHost::closeKeypad(bool animationAllowed)
{
Q_D(HbAbstractVkbHost);
if (d->mKeypadStatus != HbVkbStatusClosed && !d->mKeypadOperationOngoing) {
d->mKeypadOperationOngoing = true;
emit aboutToClose();
if (animationAllowed) {
d->closeKeypad();
} else {
d->closeKeypadWithoutAnimation();
emit keypadClosed();
HbVkbHostBridge::instance()->connectHost(0);
}
d->disconnectSignals();
d->mKeypadOperationOngoing = false;
}
}
/*!
This slot is called every time an animation frame is drawn.
*/
void HbAbstractVkbHost::animValueChanged(qreal value)
{
Q_D(HbAbstractVkbHost);
if (!d->disableCursorShift()) {
// Move the container.
if (d->mContainerWidget->widgetObject()) {
d->mContainerWidget->setPos(d->mContainerMovementStartingPoint + (d->mContainerMovementVector * value));
}
// Move keypad
if (d->mKeypad) {
d->mKeypad->setPos(d->mKeypadMovementStartingPoint + (d->mKeypadMovementVector * value));
}
}
if (d->mCallback && d->mKeypad) {
d->mCallback->keyboardAnimationFrame(HbVirtualKeyboard::HbVkbAnimOpen, value);
}
}
/*!
This slot is called when an animation sequence is completed.
*/
void HbAbstractVkbHost::animationFinished()
{
Q_D(HbAbstractVkbHost);
if (d->mContainerWidget->widgetObject() && d->mKeypad && d->mCallback && d->mInputMethod) {
if (!d->disableCursorShift()) {
// Make sure the container reached target position.
d->mContainerWidget->setPos(d->mContainerMovementStartingPoint + d->mContainerMovementVector);
// Make sure the keypad reached target position.
d->mKeypad->setPos(d->mKeypadMovementStartingPoint + d->mKeypadMovementVector);
}
// Notify
if (d->mKeypadStatus == HbVkbHost::HbVkbStatusOpened) {
d->mCallback->keyboardOpened(this);
if (d->mInputMethod->focusObject()) {
// This is hopefully temporary...
QTextEdit *textEdit = qobject_cast<QTextEdit *>(d->mInputMethod->focusObject()->object());
if (textEdit) {
textEdit->ensureCursorVisible();
}
}
// Make sure the keypad never steals focus.
d->mKeypad->setFlag(QGraphicsItem::ItemIsPanel, true);
if (d->mKeypad->isActive()) {
d->mKeypad->setActive(false);
}
emit keypadOpened();
} else if (d->mKeypadStatus == HbVkbHost::HbVkbStatusMinimized) {
d->mCallback->keyboardMinimized(this);
emit keypadClosed();
} else {
// It was closed. Hide the keyboard.
d->mKeypad->hide();
// Return the container where it was.
d->mContainerWidget->setPos(d->mOriginalContainerPosition);
d->mContainerWidget->widgetObject()->setProperty(KPositionManagedByVKB, false );
d->mCallback->keyboardClosed(this);
emit keypadClosed();
// Keyboard might be opened again due to pending open call
if (d->mKeypadStatus == HbVkbHost::HbVkbStatusClosed) {
HbVkbHostBridge::instance()->connectHost(0);
}
}
}
}
/*!
\reimp
*/
QSizeF HbAbstractVkbHost::keyboardArea() const
{
Q_D(const HbAbstractVkbHost);
HbMainWindow *mainWindow = d->mainWindow();
if (d->mContainerWidget->widgetObject() && mainWindow) {
QSizeF screenSize = d->screenSize();
if (mainWindow->orientation() == Qt::Horizontal) {
return QSizeF(screenSize.width(), screenSize.height() * HbHeightHorizFactor);
} else {
return QSizeF(screenSize.width(), screenSize.height() * HbHeightVerticalFactor);
}
}
return QSizeF(0.0, 0.0);
}
/*!
This slot is connected to orientation change warning signal from the framework
and notifies setting proxy. Notification will then be delivered through setting proxy to all the
interested parties.
*/
void HbAbstractVkbHost::orientationAboutToChange()
{
Q_D(HbAbstractVkbHost);
d->mKeypadStatusBeforeOrientationChange = d->mKeypadStatus;
}
/*!
This slot is connected to orientation change signal from the framework and notifies
the setting proxy. Notification will then be froearded to other interested parties
by the setting proxy.
*/
void HbAbstractVkbHost::orientationChanged(Qt::Orientation orientation)
{
Q_UNUSED(orientation);
}
/*!
\reimp
*/
HbVirtualKeyboard *HbAbstractVkbHost::activeKeypad() const
{
Q_D(const HbAbstractVkbHost);
return d->mCallback;
}
/*!
\reimp
*/
void HbAbstractVkbHost::ensureCursorVisibility()
{
Q_D(HbAbstractVkbHost);
if ((d->mTimeLine.state() == QTimeLine::Running) ||
(d->mKeypadStatus == HbVkbStatusClosed) ||
(d->mKeypadStatus == HbVkbStatusMinimized) ||
!d->mContainerWidget->widgetObject()) {
return;
}
// This will refresh the situation if needed.
d->openKeypad();
}
/*!
Returns the area inside active main window view that will remain visible when the
virtual keyboard is open.
*/
QRectF HbAbstractVkbHost::activeViewRect() const
{
Q_D(const HbAbstractVkbHost);
HbMainWindow *mainWindow = d->mainWindow();
if (d->mContainerWidget->widgetObject() && mainWindow && d->mCallback) {
QSizeF vpSize = d->screenSize();
QRectF viewport = QRectF(QPointF(0.0, 0.0), QPointF(vpSize.width(), vpSize.height()));
if (d->mKeypadStatus == HbVkbStatusMinimized) {
viewport.setHeight(viewport.height() - d->mCallback->minimizedKeyboardSize().height());
} else {
viewport.setHeight(viewport.height() - confirmedKeyboardSize().height());
}
return viewport;
}
return QRectF();
}
/*!
Returns confirmed keyboard size. The method first queries preferred keyboard
size and then clips it against maximum allowed keyboard size. Resulting size is returned.
*/
QSizeF HbAbstractVkbHost::confirmedKeyboardSize()const
{
Q_D(const HbAbstractVkbHost);
if (d->mCallback && d->mKeypad) {
QSizeF kbArea = keyboardArea();
QSizeF confirmed = d->mCallback->preferredKeyboardSize();
if (confirmed.width() > kbArea.width()) {
confirmed.setWidth(kbArea.width());
}
if (confirmed.height() > kbArea.height()) {
confirmed.setHeight(kbArea.height());
}
return QSizeF(confirmed);
}
return QSizeF();
}
/*!
Resizes keyboard widget to its preferred size and makes sure that
the size does not exceed the size that host is willing to give to it.
*/
void HbAbstractVkbHost::resizeKeyboard()
{
Q_D(HbAbstractVkbHost);
if (d->mKeypad) {
QSizeF currentSize = d->mKeypad->size();
QSizeF newSize = confirmedKeyboardSize();
if (currentSize != newSize) {
d->mKeypad->resize(newSize);
}
}
}
/*!
\reimp
*/
QRectF HbAbstractVkbHost::applicationArea() const
{
Q_D(const HbAbstractVkbHost);
if (d->mKeypadStatus == HbVkbStatusOpened ||
d->mKeypadStatus == HbVkbStatusMinimized) {
return activeViewRect();
}
return QRectF();
}
/*!
\deprecated HbAbstractVkbHost::minimizeKeypad(bool)
is deprecated.
*/
void HbAbstractVkbHost::minimizeKeypad(bool animationAllowed)
{
Q_UNUSED(animationAllowed);
}
/*!
\deprecated HbAbstractVkbHost::openMinimizedKeypad(HbVirtualKeyboard*, HbInputMethod*)
is deprecated.
*/
void HbAbstractVkbHost::openMinimizedKeypad(HbVirtualKeyboard *vkb, HbInputMethod *owner)
{
Q_UNUSED(vkb);
Q_UNUSED(owner);
}
/*!
\reimp
*/
HbVkbHost::HbVkbStatus HbAbstractVkbHost::keypadStatusBeforeOrientationChange() const
{
Q_D(const HbAbstractVkbHost);
return d->mKeypadStatusBeforeOrientationChange;
}
/*!
This slot is called when active HbView changes.
*/
void HbAbstractVkbHost::currentViewChanged(HbView *view)
{
Q_UNUSED(view);
}
/*!
\reimp
*/
void HbAbstractVkbHost::refresh()
{
Q_D(HbAbstractVkbHost);
if (d->mKeypadStatus == HbVkbHost::HbVkbStatusOpened &&
d->mTimeLine.state() != QTimeLine::Running) {
d->prepareAnimationsCommon();
if (d->prepareContainerAnimation(HbVkbHost::HbVkbStatusOpened)) {
// Container status needs to be updated. Run the animation.
d->mTimeLine.start();
}
}
}
/*!
\reimp
*/
bool HbAbstractVkbHost::stateTransitionOngoing() const
{
Q_D(const HbAbstractVkbHost);
return (d->mTimeLine.state() == QTimeLine::Running);
}
/*!
Receives signal from HbVkbHostBridge when previous host completes its state
transition and sens pending call if any.
*/
void HbAbstractVkbHost::stateTransitionCompleted()
{
Q_D(HbAbstractVkbHost);
disconnect(HbVkbHostBridge::instance(), SIGNAL(stateTransitionCompleted()), this, SLOT(stateTransitionCompleted()));
if (d->mPendingCall.vkb) {
// There was an open call pending. Do it now.
HbVirtualKeyboard *vkb = d->mPendingCall.vkb;
d->mPendingCall.vkb = 0;
openKeypad(vkb, d->mInputMethod, d->mPendingCall.animationAllowed);
}
}
/*!
This slot is called when change in active HbView starts.
*/
void HbAbstractVkbHost::aboutToChangeView(HbView *oldView, HbView *newView)
{
Q_D(HbAbstractVkbHost);
if (oldView != newView) {
if (d->mTimeLine.state() == QTimeLine::Running) {
d->cancelAnimationAndHideVkbWidget();
if (d->mCallback) {
d->mCallback->keyboardClosed(this);
}
} else if (d->mKeypadStatus != HbVkbStatusClosed) {
d->closeKeypadWithoutAnimation();
emit keypadClosed();
}
}
}
#include "moc_hbabstractvkbhost.cpp"
// End of file