--- a/src/hbwidgets/editors/hbselectioncontrol_p.cpp Mon Oct 04 17:49:30 2010 +0300
+++ b/src/hbwidgets/editors/hbselectioncontrol_p.cpp Mon Oct 18 18:23:13 2010 +0300
@@ -40,17 +40,23 @@
#include "hbdialog_p.h"
#include "hbabstractedit.h"
#include "hbabstractedit_p.h"
+#include "hblineedit.h"
#include "hbtoucharea.h"
#include "hbpangesture.h"
#include "hbtapgesture.h"
#include "hbevent.h"
#include "hbpopup.h"
+#include "hbmagnifier_p.h"
#include "hbnamespace_p.h"
#include "hbmainwindow.h"
+#include "hbdeviceprofile.h"
+#include "hbiconitem.h"
#include <QTextCursor>
+#include <QTextBlock>
#include <QGraphicsItem>
+#include <QGraphicsWidget>
#include <QAbstractTextDocumentLayout>
#include <QGraphicsSceneMouseEvent>
#include <QBasicTimer>
@@ -61,69 +67,185 @@
#include <hbwidgetfeedback.h>
+
+#define HB_DEBUG_PAINT_INFO 0
+
typedef QHash<HbMainWindow*,HbSelectionControl*> HbSelectionControlHash;
Q_GLOBAL_STATIC(HbSelectionControlHash, globalSelectionControlHash)
namespace {
static const int SNAP_DELAY = 300;
+ static const int MAGNIFIER_OPEN_DELAY = 200;
+ static const qreal MAGNIFIER_SCALE_FACTOR = 1.5;
}
+class MyEditor: public HbAbstractEdit
+{
+public:
+ using HbAbstractEdit::drawContents;
+};
+
+
+class HbMagnifierDelegateEditor : public HbMagnifierDelegate
+{
+public:
+ HbMagnifierDelegateEditor(HbAbstractEdit * editor) {this->editor = editor;}
+ virtual void drawContents(QPainter *painter, const QStyleOptionGraphicsItem *option)
+ {
+ if (option && editor) {
+ static_cast<MyEditor*>(editor)->drawContents(painter,*option);
+ }
+ }
+ void setEditor(HbAbstractEdit * editor){this->editor = editor;}
+private:
+ HbAbstractEdit * editor;
+};
+
class HbSelectionControlPrivate :public HbDialogPrivate
{
Q_DECLARE_PUBLIC(HbSelectionControl)
public:
+ enum InteractionMode {
+ Selection,
+ CursorPositioning
+ };
+
+ enum HandleType {
+ DummyHandle,
+ SelectionStartHandle,
+ SelectionEndHandle
+ };
+
+
HbSelectionControlPrivate();
+ ~HbSelectionControlPrivate();
void init();
void createPrimitives();
- void updateHandle(int newHandlePos,
+ void updateHandle(int cursorPos,
+ int newHandlePos,
Qt::AlignmentFlag handleAlignment,
QGraphicsItem *handle,
- QGraphicsItem *handleTouchArea,
- HbStyle::Primitive handlePrimitive);
+ QGraphicsItem *handleTouchArea);
+ void updateMagnifier();
+ void initMagnifier();
+
QGraphicsItem * reparent(QGraphicsItem *item);
void reparent(QGraphicsItem *item, QGraphicsItem *newParent);
void reparentHandles(QGraphicsItem *newParent);
- void tapGestureFinished (const QPointF& point);
+ QPointF constrainHitTestPoint(const QPointF& point);
+ HbSelectionControlPrivate::HandleType handleForPoint(const QPointF& point);
+ void gestureStarted(const QPointF &point);
+ void tapGestureStarted(HbTapGesture *gesture);
+ void tapGestureFinished();
+ void delayedTapFinished();
void panGestureStarted (HbPanGesture *gesture);
void panGestureUpdated (HbPanGesture *gesture);
void panGestureFinished (HbPanGesture *gesture);
+ void panGestureCanceled();
void show();
void _q_aboutToChangeView();
void detachEditor(bool updateAtthachedEditorState);
+#if HB_DEBUG_PAINT_INFO
+ void updateDebugPaintInfo();
+#endif
+
+
public:
HbAbstractEdit *mEdit;
- QGraphicsItem *mTopLevelAncestor;
+ HbMagnifierDelegateEditor *mMagnifierDelegate; // Owned by mMagnifier
+ HbMagnifier* mMagnifier;
- QGraphicsItem *mSelectionStartHandle;
- QGraphicsItem *mSelectionEndHandle;
+ QGraphicsItem *mTopLevelAncestor;
+ // The offset between the gesture start point and first hit test point
+ QPointF mTouchOffsetFromHitTestPoint;
+
+ HbIconItem *mSelectionStartHandle;
+ HbIconItem *mSelectionEndHandle;
HbTouchArea* mSelectionStartTouchArea;
HbTouchArea* mSelectionEndTouchArea;
- HbSelectionControl::HandleType mPressed;
- bool mPanInProgress;
+ InteractionMode mInteractionMode;
+ HandleType mPressed;
+ bool mScrollInProgress;
+ QPointF mHitTestPoint; // HbSelectionControl's coordinate system
+ QPointF mTouchPoint; // HbSelectionControl's coordinate system
+ QPointF mStartHandleHitTestPoint; // HbSelectionControl's coordinate system
+ QPointF mEndHandleHitTestPoint; // HbSelectionControl's coordinate system
QBasicTimer mWordSnapTimer;
+ QBasicTimer mDelayedTapTimer;
+
+ bool mMagnifierEnabled;
+ qreal mLastCursorHeight;
+ qreal mHandleMarginFromLine;
+ qreal mMagnifierMarginFromLine;
+ qreal mMagnifierLeftRightMarginFromHandle;
+ qreal mMagnifierMaxDescent;
+ qreal mVerticalScreenMargin;
+ qreal mTouchYOffsetFromMagnifierReferenceLine; // HbSelectionControl's coordinate system
+ qreal mStartHandleMagnifierReferenceLine; // HbSelectionControl's coordinate system
+ qreal mEndHandleMagnifierReferenceLine; // HbSelectionControl's coordinate system
+ qreal mMagnifierMaxYOffsetFromTouchPoint;
+ qreal mMagnifierMinYOffsetFromTouchPoint;
+ qreal mMaxHitTestPointOffsetYFromLine;
+ bool mIsPanActive;
+
+#if HB_DEBUG_PAINT_INFO
+ QRectF mDocRectDebug; // HbSelectionControl's coordinate system
+#endif
};
HbSelectionControlPrivate::HbSelectionControlPrivate():
mEdit(0),
+ mMagnifierDelegate(0),
+ mMagnifier(0),
mTopLevelAncestor(0),
mSelectionStartHandle(0),
mSelectionEndHandle(0),
mSelectionStartTouchArea(0),
mSelectionEndTouchArea(0),
- mPressed(HbSelectionControl::HandleType(0)),
- mPanInProgress(false)
+ mInteractionMode(InteractionMode(0)),
+ mPressed(HandleType(0)),
+ mScrollInProgress(false),
+ mMagnifierEnabled(true),
+ mLastCursorHeight(0.0),
+ mHandleMarginFromLine(0.0),
+ mMagnifierMarginFromLine(0.0),
+ mMagnifierLeftRightMarginFromHandle(0.0),
+ mMagnifierMaxDescent(0.0),
+ mVerticalScreenMargin(0.0),
+ mTouchYOffsetFromMagnifierReferenceLine(0.0),
+ mStartHandleMagnifierReferenceLine(0.0),
+ mEndHandleMagnifierReferenceLine(0.0),
+ mMagnifierMaxYOffsetFromTouchPoint(0.0),
+ mMagnifierMinYOffsetFromTouchPoint(0.0),
+ mMaxHitTestPointOffsetYFromLine(0.0),
+ mIsPanActive(false)
{
}
+HbSelectionControlPrivate::~HbSelectionControlPrivate()
+{
+}
+
void HbSelectionControlPrivate::init()
{
Q_Q(HbSelectionControl);
+
+ // Set the size of the control to 0
+ q->resize(0,0);
+
+#if HB_DEBUG_PAINT_INFO
+ // Override 0 size to be able to paint paint
+ q->resize(1,1);
+#endif
+
+ q->style()->parameter(QLatin1String("hb-param-margin-gene-middle-vertical"), mVerticalScreenMargin);
+
createPrimitives();
q->setVisible(false);
@@ -136,23 +258,25 @@
q->setFlags(itemFlags);
q->setFocusPolicy(Qt::NoFocus);
q->setActive(false);
-
- // Control will handle all events going to different handlers.
- q->setHandlesChildEvents(true);
}
void HbSelectionControlPrivate::createPrimitives()
{
Q_Q(HbSelectionControl);
if (!mSelectionStartHandle) {
- mSelectionStartHandle = q->style()->createPrimitive(HbStyle::P_SelectionControl_selectionstart, q);
+
+ mSelectionStartHandle = new HbIconItem(q);
+ mSelectionStartHandle->setIconName(QLatin1String("qtg_graf_editor_handle_begin"));
+ HbStyle::setItemName(mSelectionStartHandle, QLatin1String("handle-icon"));
mSelectionStartHandle->setFlag(QGraphicsItem::ItemIsPanel);
mSelectionStartHandle->setFlag(QGraphicsItem::ItemIsFocusable,false);
mSelectionStartHandle->setActive(false);
}
if (!mSelectionEndHandle) {
- mSelectionEndHandle = q->style()->createPrimitive(HbStyle::P_SelectionControl_selectionend, q);
+ mSelectionEndHandle = new HbIconItem(q);
+ mSelectionEndHandle->setIconName(QLatin1String("qtg_graf_editor_handle_end"));
+ HbStyle::setItemName(mSelectionEndHandle, QLatin1String("handle-icon"));
mSelectionEndHandle->setFlag(QGraphicsItem::ItemIsPanel);
mSelectionEndHandle->setFlag(QGraphicsItem::ItemIsFocusable,false);
mSelectionEndHandle->setActive(false);
@@ -163,9 +287,10 @@
mSelectionStartTouchArea->setFlag(QGraphicsItem::ItemIsPanel);
mSelectionStartTouchArea->setFlag(QGraphicsItem::ItemIsFocusable,false);
mSelectionStartTouchArea->setActive(false);
- HbStyle::setItemName(mSelectionStartTouchArea, "handle-toucharea");
+ HbStyle::setItemName(mSelectionStartTouchArea, QLatin1String("handle-toucharea"));
mSelectionStartTouchArea->grabGesture(Qt::TapGesture);
mSelectionStartTouchArea->grabGesture(Qt::PanGesture);
+ mSelectionStartTouchArea->installEventFilter(q);
}
if (!mSelectionEndTouchArea) {
@@ -173,31 +298,60 @@
mSelectionEndTouchArea->setFlag(QGraphicsItem::ItemIsPanel);
mSelectionEndTouchArea->setFlag(QGraphicsItem::ItemIsFocusable,false);
mSelectionEndTouchArea->setActive(false);
- HbStyle::setItemName(mSelectionEndTouchArea, "handle-toucharea");
+ HbStyle::setItemName(mSelectionEndTouchArea, QLatin1String("handle-toucharea"));
mSelectionEndTouchArea->grabGesture(Qt::TapGesture);
mSelectionEndTouchArea->grabGesture(Qt::PanGesture);
+ mSelectionEndTouchArea->installEventFilter(q);
+ }
+ if(!mMagnifier) {
+ mMagnifier = new HbMagnifier(q);
+ mMagnifier->setFlag(QGraphicsItem::ItemIsPanel);
+ mMagnifier->setFlag(QGraphicsItem::ItemIsFocusable,false);
+ mMagnifier->setActive(false);
+ HbStyle::setItemName(mMagnifier, QLatin1String("magnifier"));
+ mMagnifierDelegate = new HbMagnifierDelegateEditor(mEdit);
+ mMagnifier->setContentDelegate(mMagnifierDelegate);
+ mMagnifier->hide();
+ initMagnifier();
}
}
/*
Updates given handle associated with handleTouchArea to place it
newHandlePos in the selected text.
- handlePrimitive identifies handle graphics.
*/
-void HbSelectionControlPrivate::updateHandle(int newHandlePos,
+void HbSelectionControlPrivate::updateHandle(
+ int cursorPos,
+ int newHandlePos,
Qt::AlignmentFlag handleAlignment,
QGraphicsItem *handle,
- QGraphicsItem *handleTouchArea,
- HbStyle::Primitive handlePrimitive)
+ QGraphicsItem *handleTouchArea)
{
Q_Q(HbSelectionControl);
- HbStyleOption option;
+ QRectF rect = mEdit->rectForPosition(newHandlePos,(Qt::AlignTop||mInteractionMode == CursorPositioning)?
+ QTextLine::Leading:QTextLine::Trailing);
+ qreal lineHeight = rect.height();
+
+ if(cursorPos == newHandlePos) {
+ mLastCursorHeight = lineHeight;
+ }
+
+ // Store the current hit test point for the given handle
+ // Convert rect to HbSelectionControl's coordinate system
+ QRectF rectInSelectionControlCoord = q->mapRectFromItem(mEdit,rect);
- q->initStyleOption(&option);
- mEdit->style()->updatePrimitive(handle, handlePrimitive, &option);
-
- QRectF rect = mEdit->rectForPosition(newHandlePos,Qt::AlignTop?QTextLine::Leading:QTextLine::Trailing);
+ if (handleAlignment == Qt::AlignTop) {
+ mStartHandleHitTestPoint = rectInSelectionControlCoord.center();
+ mStartHandleHitTestPoint.setY(qMin(mStartHandleHitTestPoint.y(),
+ rectInSelectionControlCoord.top()+mMaxHitTestPointOffsetYFromLine));
+ mStartHandleMagnifierReferenceLine = rectInSelectionControlCoord.top();
+ } else {
+ mEndHandleHitTestPoint = rectInSelectionControlCoord.center();
+ mEndHandleHitTestPoint.setY(qMax(mEndHandleHitTestPoint.y(),
+ rectInSelectionControlCoord.bottom()-mMaxHitTestPointOffsetYFromLine));
+ mEndHandleMagnifierReferenceLine = rectInSelectionControlCoord.bottom();
+ }
// Convert rect to handle's parent coordinate system
rect = handle->parentItem()->mapRectFromItem(mEdit,rect);
@@ -205,29 +359,132 @@
// Center handle around center point of rect
QRectF boundingRect = handle->boundingRect();
+ bool positionHandlesTouchEachOther = (boundingRect.height()*2 > lineHeight);
+
boundingRect.moveCenter(rect.center());
+ // Position handle either on top of rect using margin mHandleMarginFromLine or so that
+ // the two handles touch eachother
if (handleAlignment == Qt::AlignTop) {
- boundingRect.moveBottom(rect.top());
+ if (positionHandlesTouchEachOther) {
+ boundingRect.moveBottom(rect.center().y());
+ } else {
+ boundingRect.moveTop(rect.top()-mHandleMarginFromLine);
+ }
} else {
- boundingRect.moveTop(rect.bottom());
+ if (positionHandlesTouchEachOther) {
+ boundingRect.moveTop(rect.center().y());
+ } else {
+ boundingRect.moveBottom(rect.bottom()+mHandleMarginFromLine);
+ }
}
handle->setPos(boundingRect.topLeft());
- // Center handle touch area around center pos of handle
+ // Position handle touch area around center-top of handle
QPointF centerPos = boundingRect.center();
+ rect = boundingRect;
boundingRect = handleTouchArea->boundingRect();
boundingRect.moveCenter(centerPos);
+
+ if (handleAlignment != Qt::AlignTop) {
+ boundingRect.moveTop(rect.top());
+ }
+
handleTouchArea->setPos(boundingRect.topLeft());
- if (!mPanInProgress) {
+ if (!mScrollInProgress) {
QGraphicsItem * newParent = reparent(handle);
reparent(handleTouchArea, newParent);
}
}
+void HbSelectionControlPrivate::updateMagnifier()
+{
+ Q_Q(HbSelectionControl);
+
+ if (mMagnifier->isVisible()) {
+ QPointF canvasHitTestPos = q->mapToItem(HbAbstractEditPrivate::d_ptr(mEdit)->canvas,mHitTestPoint);
+ QRectF canvasCursorRect = HbAbstractEditPrivate::d_ptr(mEdit)->rectForPositionInCanvasCoords(mEdit->textCursor().position(),QTextLine::Leading);
+ QPointF centerOfMagnification = canvasCursorRect.center();
+ centerOfMagnification.setX(canvasHitTestPos.x());
+ canvasCursorRect.moveCenter(centerOfMagnification);
+
+ // Set the visible content to be magnified
+ qreal magnifierHeight = mMagnifier->boundingRect().height();
+
+ // Check if the line fits vertically to the magnifier if not align the bottom of the line with the bottom of the
+ // magnifier constraining a maximum line descent.
+ if (magnifierHeight < canvasCursorRect.height()*mMagnifier->contentScale()) {
+ const QTextCursor cursor = mEdit->textCursor();
+ const QTextBlock block = cursor.block();
+ const int positionInBlock = cursor.position() - block.position();
+ QTextLine line = block.layout()->lineForTextPosition(positionInBlock);
+ qreal lineDescent = line.descent();
+ qreal lineBottom = canvasCursorRect.bottom();
+ if (mMagnifierMaxDescent < lineDescent) {
+ lineBottom-= lineDescent-mMagnifierMaxDescent;
+ }
+ // Compensate for rounding error by adding +1
+ centerOfMagnification.setY(lineBottom-magnifierHeight/(2*mMagnifier->contentScale())+1);
+ }
+
+ mMagnifier->centerOnContent(centerOfMagnification);
+
+ // -- Position magnifier --
+
+ // Convert cursorRect to HbSelectionControl's coordinate system
+ QRectF rect = q->mapRectFromItem(HbAbstractEditPrivate::d_ptr(mEdit)->canvas,canvasCursorRect);
+
+ QRectF boundingRect = mMagnifier->boundingRect();
+ boundingRect.moveCenter(rect.center());
+
+ const qreal KMagnifierBottomUpperBound = rect.top()-mMagnifierMarginFromLine+mTouchYOffsetFromMagnifierReferenceLine;
+ const qreal KMagnifierBottomLowerBound = rect.bottom()-mMagnifierMarginFromLine+mTouchYOffsetFromMagnifierReferenceLine;
+
+ qreal newMagnifierBottom = (mPressed == SelectionStartHandle?KMagnifierBottomUpperBound:KMagnifierBottomLowerBound);
+
+ // -- Constrain vertical position --
+
+ // Constrain maximum distance of bottom of the magnifier from touch point
+ newMagnifierBottom = qMax(newMagnifierBottom,mTouchPoint.y()-mMagnifierMaxYOffsetFromTouchPoint);
+
+ // Constrain minimum distance of bottom of the magnifier from touch point
+ newMagnifierBottom = qMin(newMagnifierBottom,mTouchPoint.y()-mMagnifierMinYOffsetFromTouchPoint);
+
+ // Constrain the bottom of the magnifier to be within the feasible bounds
+ newMagnifierBottom = qMin(qMax(newMagnifierBottom,KMagnifierBottomUpperBound),KMagnifierBottomLowerBound);
+
+ boundingRect.moveBottom(newMagnifierBottom);
+ boundingRect.moveTop(qMax(mVerticalScreenMargin,boundingRect.top()));
+
+ // Readjust magnifier position if there is not enough space at the top of the screen
+ if (mTouchPoint.y()-mMagnifierMinYOffsetFromTouchPoint < boundingRect.bottom()) {
+
+ boundingRect.moveRight(mTouchPoint.x()-mMagnifierLeftRightMarginFromHandle);
+
+ if(boundingRect.left() < 0) {
+ boundingRect.moveLeft(mTouchPoint.x()+mMagnifierLeftRightMarginFromHandle);
+ }
+ }
+ mMagnifier->setPos(boundingRect.topLeft());
+#if HB_DEBUG_PAINT_INFO
+ updateDebugPaintInfo();
+#endif
+ }
+
+}
+
+void HbSelectionControlPrivate::initMagnifier()
+{
+ mMagnifier->setContentScale(MAGNIFIER_SCALE_FACTOR);
+ mMagnifier->setZValue(100);
+ mMagnifier->setMask(QLatin1String("qtg_fr_editor_magnifier_mask"));
+ mMagnifier->setOverlay(QLatin1String("qtg_fr_editor_magnifier_overlay"));
+ updateMagnifier();
+}
+
/*
Reparents item to q if item's bounding rect intersects mEdit's viewPort rectangle or otherwise to
@@ -256,23 +513,23 @@
void HbSelectionControlPrivate::reparent(QGraphicsItem *item, QGraphicsItem *newParent)
{
- Q_Q(HbSelectionControl);
-
if (item && newParent && newParent != item->parentItem()) {
// Reparent handle items to newParent
QPointF pos = newParent->mapFromItem(item->parentItem(),item->pos());
- // If the item is parented to other then q we have to
- // turn off the QGraphicsItem::ItemIsPanel flag because
- // otherwise the new parent loses its activeness.
- bool enablePanel = (newParent == q);
+ // ----------
+ // Workaround for Qt bug: Missing repaint when QGraphicsItem is reparented to another
+ // item with flag QGraphicsItem::ItemClipsChildrenToShape is set.
+ // ----------
+ // Remove when the bug is fixed
+ if(item->isWidget()) {
+ if (qobject_cast<HbIconItem *>(static_cast<QGraphicsWidget *>(item))) {
+ item->scene()->invalidate(item->sceneBoundingRect());
+ }
+ }
+ // ----------------------------------------------------------------------------------
- item->setFlag(QGraphicsItem::ItemIsPanel,enablePanel);
-
- // TODO: This is a workaround for a Qt bug when reparenting from a clipping parent to a
- // non-clipping parent
- item->setParentItem(0);
item->setParentItem(newParent);
item->setPos(pos);
}
@@ -288,36 +545,58 @@
}
-void HbSelectionControlPrivate::tapGestureFinished(const QPointF &pos)
-{
- if (mEdit->contextMenuFlags().testFlag(Hb::ShowTextContextMenuOnSelectionClicked)) {
- mEdit->showContextMenu(pos);
- }
-}
-
-void HbSelectionControlPrivate::panGestureStarted(HbPanGesture *gesture)
+/*
+ Returns the constrained hit test point calculated from point.
+ point has to be in HbSelectionControl's coordinate system.
+ The returned pos is also in HbSelectionControl's coordinate system.
+*/
+QPointF HbSelectionControlPrivate::constrainHitTestPoint(const QPointF& point)
{
Q_Q(HbSelectionControl);
- QPointF point = q->mapFromScene(gesture->sceneStartPos());
- mPressed = HbSelectionControl::DummyHandle;
+ QRectF docRect = QRectF(q->mapFromItem(HbAbstractEditPrivate::d_ptr(mEdit)->canvas->parentItem(),
+ HbAbstractEditPrivate::d_ptr(mEdit)->canvas->pos()),
+ HbAbstractEditPrivate::d_ptr(mEdit)->doc->size());
+
+ // Constrain hitTestPos within docRect with mLastCursorHeight/2 top/bottom margins.
+ QPointF hitTestPos = QPointF(qMin(qMax(point.x(),docRect.left()),docRect.right()),
+ qMin(qMax(point.y(),docRect.top()+mLastCursorHeight/2),docRect.bottom()-mLastCursorHeight/2));
+
+#if HB_DEBUG_PAINT_INFO
+ mDocRectDebug = docRect;
+#endif
+
+ return hitTestPos;
+}
+
+
+/*
+ Returns the handle whose touch area contains point.
+ point has to be in HbSelectionControl's coordinate system.
+*/
+HbSelectionControlPrivate::HandleType HbSelectionControlPrivate::handleForPoint(const QPointF& point) {
+
+ Q_Q(HbSelectionControl);
+
+ HandleType pressed = DummyHandle;
// Find out which handle is being moved
- if (mSelectionStartTouchArea->contains(q->mapToItem(mSelectionStartTouchArea, point))) {
- mPressed = HbSelectionControl::SelectionStartHandle;
+ if (mSelectionStartTouchArea->isVisible() &&
+ mSelectionStartTouchArea->contains(q->mapToItem(mSelectionStartTouchArea, point))) {
+ pressed = SelectionStartHandle;
}
if (mSelectionEndTouchArea->contains(q->mapToItem(mSelectionEndTouchArea, point))) {
bool useArea = true;
- if(mPressed != HbSelectionControl::DummyHandle) {
+ if(pressed != DummyHandle) {
// The press point was inside in both of the touch areas
// choose the touch area whose center is closer to the press point
- QRectF rect = mSelectionStartTouchArea->boundingRect();
- rect.moveTopLeft(mSelectionStartTouchArea->pos());
+ QRectF rect = mSelectionStartHandle->boundingRect();
+ rect.moveTopLeft(mSelectionStartHandle->pos());
QLineF lineEventPosSelStartCenter(point,rect.center());
- rect = mSelectionEndTouchArea->boundingRect();
- rect.moveTopLeft(mSelectionEndTouchArea->pos());
+ rect = mSelectionEndHandle->boundingRect();
+ rect.moveTopLeft(mSelectionEndHandle->pos());
QLineF lineEventPosSelEndCenter(point,rect.center());
if (lineEventPosSelStartCenter.length() < lineEventPosSelEndCenter.length()) {
@@ -325,22 +604,41 @@
}
}
if (useArea) {
- mPressed = HbSelectionControl::SelectionEndHandle;
+ pressed = SelectionEndHandle;
}
}
- if (mPressed == HbSelectionControl::DummyHandle) {
+ return pressed;
+}
+
+void HbSelectionControlPrivate::gestureStarted(const QPointF &point)
+{
+ mPressed = handleForPoint(point);
+ mTouchPoint = point;
+
+ if (mPressed == DummyHandle) {
// Hit is outside touch areas, ignore
return;
}
+ // Calculate touch offsets
+ mHitTestPoint = mStartHandleHitTestPoint;
+ qreal magnifierReferenceLine = mStartHandleMagnifierReferenceLine;
+ if (mPressed == SelectionEndHandle) {
+ mHitTestPoint = mEndHandleHitTestPoint;
+ magnifierReferenceLine = mEndHandleMagnifierReferenceLine;
+ }
+
+ mTouchYOffsetFromMagnifierReferenceLine = point.y() - magnifierReferenceLine;
+ mTouchOffsetFromHitTestPoint = mHitTestPoint - point;
+
// Position cursor at the pressed selection handle
QTextCursor cursor = mEdit->textCursor();
int selStartPos = qMin(mEdit->textCursor().anchor(),mEdit->textCursor().position());
int selEndPos = qMax(mEdit->textCursor().anchor(),mEdit->textCursor().position());
- if (mPressed == HbSelectionControl::SelectionStartHandle) {
+ if (mPressed == SelectionStartHandle) {
cursor.setPosition(selEndPos);
cursor.setPosition(selStartPos, QTextCursor::KeepAnchor);
} else {
@@ -348,7 +646,60 @@
cursor.setPosition(selEndPos, QTextCursor::KeepAnchor);
}
mEdit->setTextCursor(cursor);
+}
+void HbSelectionControlPrivate::tapGestureStarted(HbTapGesture *gesture)
+{
+ Q_Q(HbSelectionControl);
+
+ mMagnifier->hideWithEffect();
+ if (!mDelayedTapTimer.isActive()) {
+ mDelayedTapTimer.start(MAGNIFIER_OPEN_DELAY,q);
+ }
+ QPointF point = q->mapFromScene(gesture->sceneStartPos());
+ gestureStarted(point);
+}
+
+void HbSelectionControlPrivate::tapGestureFinished()
+{
+ mDelayedTapTimer.stop();
+ if (!mIsPanActive) {
+ mSelectionStartTouchArea->show();
+ mSelectionStartHandle->show();
+ mMagnifier->hideWithEffect();
+ }
+}
+
+void HbSelectionControlPrivate::delayedTapFinished()
+{
+ Q_Q(HbSelectionControl);
+
+ // Reset gesture override to have enable more responsive pan
+ q->scene()->setProperty(HbPrivate::OverridingGesture.latin1(),QVariant());
+
+ if (mPressed == HbSelectionControlPrivate::SelectionEndHandle && mInteractionMode == HbSelectionControlPrivate::CursorPositioning) {
+ mSelectionStartTouchArea->hide();
+ mSelectionStartHandle->hide();
+ }
+ if (mMagnifierEnabled) {
+ mMagnifier->showWithEffect();
+ q->updatePrimitives();
+ }
+}
+
+
+
+void HbSelectionControlPrivate::panGestureStarted(HbPanGesture *gesture)
+{
+ Q_Q(HbSelectionControl);
+
+ mIsPanActive = true;
+ QPointF point = q->mapFromScene(gesture->sceneStartPos());
+ gestureStarted(point);
+ if (mPressed == SelectionEndHandle && mInteractionMode == HbSelectionControlPrivate::CursorPositioning) {
+ mSelectionStartTouchArea->hide();
+ mSelectionStartHandle->hide();
+ }
}
@@ -373,8 +724,21 @@
mEdit->setTextCursor(cursor);
}
- mPressed = HbSelectionControl::DummyHandle;
+ mSelectionStartTouchArea->show();
+ mSelectionStartHandle->show();
+ // This has to be set before updatePrimitives call
+ mIsPanActive = false;
q->updatePrimitives();
+ mMagnifier->hideWithEffect();
+
+ // This has to be set at last
+ mPressed = DummyHandle;
+}
+
+void HbSelectionControlPrivate::panGestureCanceled()
+{
+ mMagnifier->hideWithEffect();
+ mIsPanActive = false;
}
@@ -382,67 +746,53 @@
{
Q_Q(HbSelectionControl);
- QRectF docRect = QRectF(mEdit->mapFromItem(HbAbstractEditPrivate::d_ptr(mEdit)->canvas->parentItem(),
- HbAbstractEditPrivate::d_ptr(mEdit)->canvas->pos()),
- HbAbstractEditPrivate::d_ptr(mEdit)->doc->size());
-
- QPointF editPos = mEdit->mapFromScene(gesture->sceneStartPos() + gesture->sceneOffset());
- QPointF origEditPos = editPos;
- bool outsideCanvas = !docRect.contains(origEditPos);
-
- // Constrain editPos within docRect
- editPos = QPointF(qMin(qMax(editPos.x(),docRect.left()),docRect.right()),
- qMin(qMax(editPos.y(),docRect.top()),docRect.bottom()));
-
- QRectF handleRect = mSelectionStartHandle->boundingRect();
-
- handleRect.moveCenter(editPos);
+ // Calculate new hittest point
+ QPointF point = q->mapFromScene(gesture->sceneStartPos() + gesture->sceneOffset());
+ mTouchPoint = point;
+ mHitTestPoint = (point + mTouchOffsetFromHitTestPoint);
+ mHitTestPoint = constrainHitTestPoint(mHitTestPoint);
- // Set hitTestPos based on which handle was grabbed
- QPointF hitTestPos = handleRect.center();
-
- if (mPressed == HbSelectionControl::SelectionStartHandle) {
- hitTestPos.setY(handleRect.bottom()+1);
- } else {
- hitTestPos.setY(handleRect.top()-1);
- }
-
- // Override hitTestPos if origEditPos was outside the canvas
- if (outsideCanvas) {
- if (origEditPos.y() < docRect.top()) {
- hitTestPos.setY(handleRect.bottom()+1);
- } else if (docRect.bottom() < origEditPos.y()) {
- hitTestPos.setY(handleRect.top()-1);
- }
- }
+#if HB_DEBUG_PAINT_INFO
+ updateDebugPaintInfo();
+#endif
QTextCursor cursor;
cursor = mEdit->textCursor();
- // Hit test for the center of current selection touch area
- int hitPos = HbAbstractEditPrivate::d_ptr(mEdit)->hitTest(hitTestPos,Qt::FuzzyHit);
- // if no valid hit pos or empty selection return
- if (hitPos == -1 || hitPos == cursor.anchor()) {
+ // Hit test for the center of current selection touch area
+ int hitPos = HbAbstractEditPrivate::d_ptr(mEdit)->hitTest(q->mapToItem(mEdit,mHitTestPoint),Qt::FuzzyHit);
+
+ // if no valid hit pos or empty selection in read-only mode return
+ if (hitPos == -1 || (mEdit->isReadOnly() && hitPos == cursor.anchor() && mInteractionMode == Selection)) {
return;
}
- bool handlesMoved(false);
+ bool isCursorMoved = false;
if (hitPos != cursor.position()) {
- handlesMoved = true;
+ isCursorMoved = true;
}
- cursor.setPosition(hitPos, QTextCursor::KeepAnchor);
- if (handlesMoved) {
+
+ cursor.setPosition(hitPos, ((mInteractionMode==Selection||mPressed == SelectionStartHandle)
+ ?QTextCursor::KeepAnchor:QTextCursor::MoveAnchor));
+
+ if (isCursorMoved) {
if (mEdit) {
HbWidgetFeedback::triggered(mEdit, Hb::InstantDraggedOver);
}
- // Restart timer every time when a selection handle moved
- mWordSnapTimer.start(SNAP_DELAY, q);
+ if (mInteractionMode==Selection) {
+ // Restart timer every time when a selection handle moved
+ mWordSnapTimer.start(SNAP_DELAY, q);
+ }
mEdit->setTextCursor(cursor);
}
// Ensure that the hitPos is visible
HbAbstractEditPrivate::d_ptr(mEdit)->ensurePositionVisible(hitPos);
+ if (mMagnifierEnabled) {
+ mMagnifier->showWithEffect();
+ }
q->updatePrimitives();
+
}
void HbSelectionControlPrivate::show() {
@@ -456,8 +806,9 @@
}
if (q->scene() != mEdit->scene() && mEdit->scene()) {
- mEdit->scene()->addItem(q);
- }
+ mEdit->scene()->addItem(q);
+ }
+
q->show();
q->updatePrimitives();
}
@@ -469,6 +820,7 @@
if (mEdit && q->isVisible()) {
mEdit->deselect();
+ q->hideHandles();
}
}
@@ -488,6 +840,16 @@
}
}
+#if HB_DEBUG_PAINT_INFO
+void HbSelectionControlPrivate::updateDebugPaintInfo()
+{
+ Q_Q(HbSelectionControl);
+ mEdit->update();
+ q->update();
+}
+#endif
+
+
HbSelectionControl::HbSelectionControl() : HbWidget(*new HbSelectionControlPrivate(),0)
{
@@ -519,18 +881,47 @@
d->mEdit = edit;
QObject::connect(d->mEdit, SIGNAL(cursorPositionChanged(int, int)), control, SLOT(updatePrimitives()));
QObject::connect(d->mEdit, SIGNAL(contentsChanged()), control, SLOT(updatePrimitives()));
+ d->mMagnifierDelegate->setEditor(d->mEdit);
+
+
+ // Set the background of magnifier to the background of HbLineEdit or HbTextEdit
+ QLatin1String magnifierBackground("qtg_fr_lineedit_normal_c");
+ if (!qobject_cast<HbLineEdit*>(d->mEdit)) {
+ magnifierBackground = QLatin1String("qtg_fr_textedit_normal_c");
+
+ }
+ d->mMagnifier->setBackground(magnifierBackground);
// find first top-level ancestor of d->mEdit
for(d->mTopLevelAncestor = d->mEdit;
d->mTopLevelAncestor->parentItem();
- d->mTopLevelAncestor = d->mTopLevelAncestor->parentItem()){};
+ d->mTopLevelAncestor = d->mTopLevelAncestor->parentItem()){
+
+ // Workaround for Qt bug: QTBUG-13473
+ // This line could be removed after Qt fixes this bug.
+ d->mTopLevelAncestor->setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
+
+ };
}
return control;
}
+void HbSelectionControl::detachEditor(HbAbstractEdit *edit)
+{
+ if(!edit || !edit->mainWindow()) {
+ qWarning("HbSelectionControl: attempting to detach to null editor pointer!");
+ }
+
+ HbSelectionControl *control = globalSelectionControlHash()->value(edit->mainWindow());
+ if (control) {
+ control->detachEditor();
+ }
+}
+
void HbSelectionControl::detachEditor()
{
Q_D(HbSelectionControl);
+ d->mMagnifierDelegate->setEditor(0);
d->detachEditor(true);
}
@@ -554,7 +945,10 @@
Q_D(HbSelectionControl);
if (!isVisible() && d->mEdit) {
d->show();
- }
+ }
+ // selection start handles might be hidden
+ d->mSelectionStartTouchArea->show();
+ d->mSelectionStartHandle->show();
}
void HbSelectionControl::scrollStarted()
@@ -562,9 +956,13 @@
Q_D(HbSelectionControl);
if (isVisible() && d->mEdit) {
- d->mPanInProgress = true;
+ d->mScrollInProgress = true;
// Reparent handle items to editor canvas on pan start
d->reparentHandles(HbAbstractEditPrivate::d_ptr(d->mEdit)->canvas);
+ d->mMagnifier->hideWithEffect();
+ // Note: This call is not needed if it is guaranteed that this method
+ // will be called before the scrolling started.
+ updatePrimitives();
}
}
@@ -573,7 +971,7 @@
Q_D(HbSelectionControl);
if (isVisible() && d->mEdit) {
- d->mPanInProgress = false;
+ d->mScrollInProgress = false;
updatePrimitives();
}
}
@@ -585,6 +983,9 @@
if (event->timerId() == d->mWordSnapTimer.timerId()) {
d->mWordSnapTimer.stop();
+ } else if (event->timerId() == d->mDelayedTapTimer.timerId()) {
+ d->mDelayedTapTimer.stop();
+ d->delayedTapFinished();
}
}
@@ -592,11 +993,67 @@
{
Q_D(HbSelectionControl);
- HbWidget::polish(params);
- QSizeF size = d->mSelectionStartTouchArea->preferredSize();
- d->mSelectionStartTouchArea->resize(size);
- d->mSelectionEndTouchArea->resize(size);
- updatePrimitives();
+ if (isVisible()) {
+
+ const QLatin1String KHandleMarginFromLine("handle-margin-from-line");
+ const QLatin1String KMagnifierMarginFromLine("magnifier-margin-from-line");
+ const QLatin1String KMagnifierLeftRightMarginFromHandle("magnifier-left-right-margin-from-handle");
+ const QLatin1String KMagnifierMaxDescent("magnifier-max-descent");
+
+ params.addParameter(KHandleMarginFromLine);
+ params.addParameter(KMagnifierMarginFromLine);
+ params.addParameter(KMagnifierLeftRightMarginFromHandle);
+ params.addParameter(KMagnifierMaxDescent);
+
+ HbWidget::polish(params);
+
+ // Set size of handles
+ QSizeF size = d->mSelectionStartHandle->preferredSize();
+ d->mSelectionStartHandle->setSize(size);
+ d->mSelectionEndHandle->setSize(size);
+
+ // Set max y offset for hit test point
+ // TODO: consider setting this value from css
+ d->mMaxHitTestPointOffsetYFromLine = size.height();
+
+ // Set size of touch areas
+ size = d->mSelectionEndTouchArea->preferredSize();
+ d->mSelectionEndTouchArea->resize(size);
+
+
+ // Increase the height of the touch area of the start selection handle
+ size.setHeight(size.height()*2-d->mSelectionStartHandle->size().height());
+ d->mSelectionStartTouchArea->resize(size);
+
+
+
+ // Set size of magnifier
+ d->mMagnifier->resize(d->mMagnifier->preferredSize());
+
+ if (params.value(KHandleMarginFromLine).isValid()) {
+ d->mHandleMarginFromLine = params.value(KHandleMarginFromLine).toReal();
+ }
+
+ if (params.value(KMagnifierMarginFromLine).isValid()) {
+ d->mMagnifierMarginFromLine = params.value(KMagnifierMarginFromLine).toReal();
+ }
+ if (params.value(KMagnifierLeftRightMarginFromHandle).isValid()) {
+ d->mMagnifierLeftRightMarginFromHandle = params.value(KMagnifierLeftRightMarginFromHandle).toReal();
+ }
+
+ if (params.value(KMagnifierMaxDescent).isValid()) {
+ d->mMagnifierMaxDescent = params.value(KMagnifierMaxDescent).toReal();
+ }
+
+ // Set the min/max magnifier touch offsets
+ // TODO: consider setting this value from css
+ d->mMagnifierMaxYOffsetFromTouchPoint = d->mMagnifierMarginFromLine*2;
+ d->mMagnifierMinYOffsetFromTouchPoint = d->mMagnifierMarginFromLine/2;
+
+ updatePrimitives();
+ } else {
+ HbWidget::polish(params);
+ }
}
QVariant HbSelectionControl::itemChange(GraphicsItemChange change, const QVariant &value)
@@ -610,26 +1067,26 @@
void HbSelectionControl::gestureEvent(QGestureEvent* event) {
Q_D(HbSelectionControl);
+
if(HbTapGesture *tap = qobject_cast<HbTapGesture*>(event->gesture(Qt::TapGesture))) {
- QPointF pos = event->mapToGraphicsScene(tap->position());
+ if (d->mEdit) {
+ // GestureFinshed is only delegated while the mDelayedTapTimer is active
+ if (tap->state() != Qt::GestureFinished || d->mDelayedTapTimer.isActive()) {
+ HbAbstractEditPrivate::d_ptr(d->mEdit)->gestureEvent(event,this);
+ }
+ }
+
switch(tap->state()) {
+
case Qt::GestureStarted:
- if (d->mEdit) {
- HbWidgetFeedback::triggered(this, Hb::InstantPressed);
- }
- break;
- case Qt::GestureUpdated:
+ d->tapGestureStarted(tap);
break;
- case Qt::GestureFinished:
- if (d->mEdit) {
- d->tapGestureFinished(pos);
- HbWidgetFeedback::triggered(this, Hb::InstantReleased);
- }
+ case Qt::GestureCanceled:
+ case Qt::GestureFinished:
+ d->tapGestureFinished();
break;
- case Qt::GestureCanceled:
- break;
- default:
- break;
+ default:
+ break;
}
}
@@ -652,6 +1109,10 @@
}
break;
case Qt::GestureCanceled:
+ if (d->mEdit) {
+ d->panGestureCanceled();
+ HbWidgetFeedback::triggered(d->mEdit, Hb::InstantReleased);
+ }
break;
default:
break;
@@ -659,6 +1120,19 @@
}
}
+bool HbSelectionControl::eventFilter(QObject * watched, QEvent *event)
+{
+ Q_UNUSED(watched)
+
+ // Filter gesture events and delegate to gestureEvent()
+ if (event->type() == QEvent::Gesture || event->type() == QEvent::GestureOverride) {
+ gestureEvent(static_cast<QGestureEvent*>(event));
+ return true;
+ }
+ return false;
+}
+
+
bool HbSelectionControl::event(QEvent *event)
{
Q_D(HbSelectionControl);
@@ -666,28 +1140,103 @@
if (event->type() == HbEvent::DeviceProfileChanged && d->mEdit) {
HbDeviceProfileChangedEvent* dpEvent = static_cast<HbDeviceProfileChangedEvent*>(event);
if ( dpEvent->profile().alternateProfileName() == dpEvent->oldProfile().name() ) {
+ d->initMagnifier();
updatePrimitives();
}
}
return HbWidget::event(event);
}
+
+void HbSelectionControl::setMagnifierEnabled(bool enable)
+{
+ Q_D(HbSelectionControl);
+ d->mMagnifierEnabled = enable;
+}
+
+bool HbSelectionControl::isMagnifierEnabled() const
+{
+ Q_D(const HbSelectionControl);
+ return d->mMagnifierEnabled;
+}
+
+void HbSelectionControl::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(widget)
+ Q_UNUSED(option)
+ Q_UNUSED(painter)
+
+#if HB_DEBUG_PAINT_INFO
+ Q_D(HbSelectionControl);
+ painter->save();
+
+ // draw mHitTestPoint
+ painter->setPen(Qt::yellow);
+ painter->setBrush(Qt::yellow);
+ painter->drawEllipse(d->mHitTestPoint, 3,3);
+
+ // draw mDocRectDebug
+ painter->setBrush(Qt::NoBrush);
+ painter->drawRect(d->mDocRectDebug);
+
+ // draw line representing mMagnifierMaxYOffsetFromTouchPoint
+ painter->setPen(Qt::red);
+ qreal magnifierMaxYOffsetLine = d->mTouchPoint.y()-d->mMagnifierMaxYOffsetFromTouchPoint;
+ painter->drawLine(QPointF(0,magnifierMaxYOffsetLine),QPointF(500,magnifierMaxYOffsetLine));
+
+ // draw line representing mMagnifierMinYOffsetFromTouchPoint
+ painter->setPen(Qt::green);
+ qreal magnifierMinYOffsetLine = d->mTouchPoint.y()-d->mMagnifierMinYOffsetFromTouchPoint;
+ painter->drawLine(QPointF(0,magnifierMinYOffsetLine),QPointF(500,magnifierMinYOffsetLine));
+
+ // draw line representing magnifierStartYOffsetLine
+ painter->setPen(Qt::black);
+ qreal magnifierStartYOffsetLine = d->mStartHandleMagnifierReferenceLine - d->mMagnifierMarginFromLine;
+ painter->drawLine(QPointF(0,magnifierStartYOffsetLine),QPointF(500,magnifierStartYOffsetLine));
+
+ // draw line representing magnifierStartYOffsetLine
+ painter->setPen(Qt::magenta);
+ qreal magnifierEndYOffsetLine = d->mEndHandleMagnifierReferenceLine - d->mMagnifierMarginFromLine;
+ painter->drawLine(QPointF(0,magnifierEndYOffsetLine),QPointF(500,magnifierEndYOffsetLine));
+
+
+ painter->drawRect(boundingRect());
+
+ painter->restore();
+#endif
+
+}
+
+
void HbSelectionControl::updatePrimitives()
{
Q_D(HbSelectionControl);
if (isVisible() && d->polished && d->mEdit) {
- if (d->mEdit->textCursor().hasSelection() ) {
-
- int selStartPos = qMin(d->mEdit->textCursor().anchor(),d->mEdit->textCursor().position());
- int selEndPos = qMax(d->mEdit->textCursor().anchor(),d->mEdit->textCursor().position());
+
+ const bool hasSelection = d->mEdit->textCursor().hasSelection();
+
+ // The interaction mode can be change only when there is no pan in progress
+ if (!d->mIsPanActive) {
+ d->mInteractionMode = (hasSelection?HbSelectionControlPrivate::Selection:
+ HbSelectionControlPrivate::CursorPositioning);
+ }
- d->updateHandle(selStartPos,Qt::AlignTop,d->mSelectionStartHandle,d->mSelectionStartTouchArea,HbStyle::P_SelectionControl_selectionstart);
- d->updateHandle(selEndPos,Qt::AlignBottom,d->mSelectionEndHandle,d->mSelectionEndTouchArea,HbStyle::P_SelectionControl_selectionend);
+ const int cursorPos = d->mEdit->textCursor().position();
+ int selStartPos = cursorPos;
+ int selEndPos = cursorPos;
+ d->mSelectionEndHandle->setIconName(QLatin1String("qtg_graf_editor_handle_finetune"));
+
+ if (hasSelection) {
+ selStartPos = qMin(d->mEdit->textCursor().anchor(),cursorPos);
+ selEndPos = qMax(d->mEdit->textCursor().anchor(),cursorPos);
+ d->mSelectionEndHandle->setIconName(QLatin1String("qtg_graf_editor_handle_end"));
}
- else {
- hide();
- }
+
+ d->updateHandle(cursorPos,selStartPos,Qt::AlignTop,d->mSelectionStartHandle,d->mSelectionStartTouchArea);
+ d->updateHandle(cursorPos,selEndPos,Qt::AlignBottom,d->mSelectionEndHandle,d->mSelectionEndTouchArea);
+ d->updateMagnifier();
}
}
#include "moc_hbselectioncontrol_p.cpp"
+