src/hbcore/vkbhosts/private/hbvkbgeometrylogic_p.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 17 Sep 2010 08:32:10 +0300
changeset 28 b7da29130b0e
child 30 80e4d18b72f5
permissions -rw-r--r--
Revision: 201035 Kit: 201037

/****************************************************************************
**
** 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 "hbvkbgeometrylogic_p.h"

#include <QtGlobal>
#include <hbglobal.h>

const qreal HbCursorLineMargin = 15.0;
const qreal HbContainerBorderMargin = 20.0;
const qreal HbEditorExtraMargin = 17.0;

/*!
    \class HbVkbGeometryLogicPrivate
    \brief Calculates screen movement in cases the keyboard would overlap with editor

    This class contains calculations and logic to move screen in cases, when upcoming
    keyboard would overlap the editor so, that text written could not be seen. Idea is
    to minimize the movement as much as possible.

*/

/*!
    \internal
    \brief Construct current state object screen status.

    Constructor to create status object based on given parameters. These parameters
    are used to calculate possible movement vector for container.

    \param screenSize       Size of screen.
    \param keybadSize       Size of keyboard.
    \param sceneArea        Size of view area, to which container belongs.
    \param isVkbOpen        Information about current status of VKB.
    \param hideTitlebar     Should titlebar hiding be calculated?
    \param hideStatusbar    Should statusbar hiding be calculated?
    \param containerArea    Area of editor's top item.
    \param editorArea       Area of editor itself.
    \param cursorArea       Area of cursor inside editor.
*/
HbVkbGeometryLogicPrivate::HbVkbGeometryLogicPrivate(
        const QSizeF& screenSize,
        const QSizeF& keypadSize,
        const QRectF& sceneArea,
        bool isVkbOpen,
        bool hideTitlebar,
        bool hideStatusbar,
        const QRectF& containerArea,
        const QRectF& editorArea,
        const QRectF& cursorArea)
{
    // We need to consider situation, when keyboard is already on the screen, in
    // which case, titlebar and statusbar are already hidden, thus bigger visible area
    // is already in use, so no adjustments needed.
    if ( isVkbOpen ) {
        mVisibleArea = QRectF(0.0, 0.0, screenSize.width(), screenSize.height() - keypadSize.height());
        mAdjust = 0.0;
    } else if ( hideTitlebar || hideStatusbar ) {
        // Without titlebar and statusbar, the visible area is from the top of the screen
        // to the top of the keyboard. Also, container needs to move slightly, when
        // bars are going to be closed.
        mVisibleArea = QRectF(0.0, 0.0, screenSize.width(), screenSize.height() - keypadSize.height());
        mAdjust = mVisibleArea.top() - sceneArea.top();
    } else {
        // When titlebar and statusbar are visible, visible area is going to be from
        // bottom of the titlebar to top of the keyboard. No container movement needed.
        mVisibleArea = QRectF(0.0, 0.0, screenSize.width(), sceneArea.height() - keypadSize.height());
        mAdjust = 0.0;
    }

    // Find out the container area.
    mContainerArea = containerArea;
    mContainerArea.adjust(0.0, -HbContainerBorderMargin, 0.0, HbContainerBorderMargin);
    mContainerArea.translate(QPointF(0, mAdjust));

    // Find out the editor bounding box and add a small margin to height.
    mEditorArea = editorArea;
    mEditorArea.adjust(0.0, -HbCursorLineMargin, 0.0, HbCursorLineMargin);
    mEditorArea.translate(QPointF(0, mAdjust));

    // Finally, get cursor size and adjust it little bit
    mCursorArea = cursorArea;
    mCursorArea.adjust(0.0, -HbEditorExtraMargin, 0.0, HbEditorExtraMargin);
    mCursorArea.translate(QPointF(0, mAdjust));
}

/*!
    \internal
    \brief Check the source area fits inside target area.
*/
bool HbVkbGeometryLogicPrivate::fitsArea(const QRectF& target, const QRectF& source) const
{
    return source.width() <= target.width() && source.height() <= target.height();
}

/*!
    \internal
    \brief Checks, whether the container fits into the visible area.

    When keyboard opens, the screen will contain visible area and the keyboard area.
    This method checks, whether container will fit the visible area.

    \return True, when fits. Otherwise false.
*/
bool HbVkbGeometryLogicPrivate::containerFitsVisibleArea() const
{
     return fitsArea(mVisibleArea, mContainerArea);
}

/*!
    \internal
    \brief Checks, whether the editor fits into the visible area.

    When keyboard opens, the screen will contain visible area and the keyboard area.
    This method check, whether editor itself can be fitted to screen. This is needed
    when the container cannot fit the screen, so the editor needs to be positioned.

    \return
*/
bool HbVkbGeometryLogicPrivate::editorFitsVisibleArea() const
{
    return fitsArea(mVisibleArea, mEditorArea);
}

/*!
    \internal
    \brief Check if container is fully visible.

    \return True, when fully inside visible area, otherwise false.
*/
bool HbVkbGeometryLogicPrivate::isContainerVisible() const
{
    return mVisibleArea.contains(mContainerArea);
}

/*!
    \internal
    \return True, when editor inside visible area
*/
bool HbVkbGeometryLogicPrivate::isEditorVisible() const
{
    return mVisibleArea.contains(mEditorArea);
}

/*!
    \internal
    \return True, when cursor inside visible area
*/
bool HbVkbGeometryLogicPrivate::isCursorVisible() const
{
    // Check wheter cursor inside the visible area.
    return mVisibleArea.contains(mCursorArea);
}

/*!
    \internal
    \brief Calculates movement vector for viewport.

    \return True, when container needs to be moved.
*/
bool HbVkbGeometryLogicPrivate::calculateContainerMovement(QPointF& vector) const
{
    // In case editor or cursor inside visible area, no extra movement needed.
    if ( isCursorVisible() ) {
        vector.rx() = 0.0;
        vector.ry() = mAdjust;
        return false;
    }

    // At this point we know, that cursor is not inside of visible area,
    // after VKB has been shown. To make it bit prettier, let's check, if we can
    // move and fit the whole editor into the screen at once.
    if ( !isEditorVisible() && editorFitsVisibleArea() ) {
        // Editor is not in screen but fits there, so simply move the whole editor
        // to screen. Only thing yet to check is, which direction the editor needs
        // to be moved.
        if ( mEditorArea.top() <= mVisibleArea.top() ) {
            vector = QPointF(0.0, -mEditorArea.top());
        } else {
            // In case editor is not inside visible area, move editor until it is.
            vector = QPointF(0.0, mVisibleArea.bottom() - mEditorArea.bottom());
        }

        vector.ry() += mAdjust;

        // Vector has been calculated, so finish the story and return.
        return true;
    }

    // At this point we know, that cursor is not visible and the editor does not fit
    // into the visible area. Here we need to move editor, so that the cursor can be
    // seen in the visible area. There are two ways to do this.
    // 1) Move container, until bottom of editor is reached OR
    // 2) Move container, until cursor hits top of the screen
    int cursorMove = (int)(mVisibleArea.top() - mCursorArea.top());
    int editorMove = (int)(mVisibleArea.bottom() - mEditorArea.bottom());

    // Choose smaller movement (notice usage of negative values)
    vector = QPointF(0.0, cursorMove >= editorMove ? cursorMove : editorMove);

    vector.ry() += mAdjust;
    return true;
}