diff -r 923ff622b8b9 -r 4633027730f5 src/hbinput/inputwidgets/hbinputbuttongroup.cpp --- a/src/hbinput/inputwidgets/hbinputbuttongroup.cpp Tue Jul 06 14:36:53 2010 +0300 +++ b/src/hbinput/inputwidgets/hbinputbuttongroup.cpp Wed Aug 18 10:05:37 2010 +0300 @@ -349,7 +349,7 @@ // Calculate text size QFont font = HbFontSpec(HbFontSpec::Primary).font(); - font.setPixelSize(int(fontSize(HbInputButtonGroup::ButtonTextTypeLabel))); + font.setPixelSize(int(group->fontSize(HbInputButtonGroup::ButtonTextTypeLabel))); QFontMetricsF fontMetrics(font); qreal textWidth = fontMetrics.width(item->text(HbInputButton::ButtonTextIndexPrimary)); @@ -361,14 +361,14 @@ width = HbPreviewWidthInUnits * mUnitValue; } qreal height = HbPreviewHeightInUnits * mUnitValue; - qreal x = q->scenePos().x() + (item->position().x() + 0.5 * item->size().width()) * cellWidth - 0.5 * width; + qreal x = (item->position().x() + 0.5 * item->size().width()) * cellWidth - 0.5 * width; if (x < 0) { x = 0; } else if (x + width > q->boundingRect().width()) { x = q->boundingRect().width() - width; } - qreal y = q->scenePos().y() + item->position().y() * cellHeight - height; - group->setGeometry(QRectF(x, y, width, height)); + qreal y = item->position().y() * cellHeight - height; + group->setGeometry(QRectF(q->mapToScene(x, y), QSizeF(width, height))); if (q->parentItem()) { group->setZValue(q->parentItem()->zValue() + 1); } @@ -400,8 +400,7 @@ if (item->type() == HbInputButton::ButtonTypeFunction) { HbWidgetFeedback::triggered(q, Hb::InstantLongPressed, Hb::ModifierInputFunctionButton); - } - else { + } else { HbWidgetFeedback::triggered(q, Hb::InstantLongPressed); } @@ -485,6 +484,7 @@ if (index >= 0 && index < mButtonData.count()) { HbInputButton *item = mButtonData.at(index); + // Check whether we are actually supposed to handle the event. if ((item->state() != HbInputButton::ButtonStateReleased && item->state() != HbInputButton::ButtonStateLatched) || (mCharacterSelectionPreview && mCharacterSelectionPreview->isVisible())) { @@ -493,11 +493,13 @@ } return; } + + mActiveButtons.append(index); + item->setLastTriggeredPosition(position); if (item->type() == HbInputButton::ButtonTypeFunction) { HbWidgetFeedback::triggered(q, Hb::InstantPressed, Hb::ModifierInputFunctionButton); - } - else { + } else { HbWidgetFeedback::triggered(q, Hb::InstantPressed); } @@ -541,6 +543,7 @@ if (index >= 0 && index < mButtonData.count()) { HbInputButton *item = mButtonData.at(index); + // Check whether we are actually supposed to handle the event. if ((item->state() != HbInputButton::ButtonStateReleased && item->state() != HbInputButton::ButtonStateLatched) || (mCharacterSelectionPreview && mCharacterSelectionPreview->isVisible())) { @@ -550,10 +553,12 @@ return; } + mActiveButtons.append(index); + item->setLastTriggeredPosition(position); + if (item->type() == HbInputButton::ButtonTypeFunction) { HbWidgetFeedback::triggered(q, Hb::InstantPressed, Hb::ModifierInputFunctionButton); - } - else { + } else { HbWidgetFeedback::triggered(q, Hb::InstantPressed); } @@ -583,30 +588,41 @@ { Q_Q(HbInputButtonGroup); - int oldColumn = static_cast(oldPosition.x() / (q->boundingRect().width() / mGridSize.width())); - int oldRow = static_cast(oldPosition.y() / (q->boundingRect().height() / mGridSize.height())); int newColumn = static_cast(newPosition.x() / (q->boundingRect().width() / mGridSize.width())); int newRow = static_cast(newPosition.y() / (q->boundingRect().height() / mGridSize.height())); - int oldIndex = mButtonGridPositions.value(QPair(oldColumn, oldRow), -1); int newIndex = mButtonGridPositions.value(QPair(newColumn, newRow), -1); // Check if movement happens inside button group if (newPosition.x() >= 0 && newPosition.x() < q->boundingRect().width() && newPosition.y() >= 0 && newPosition.y() < q->boundingRect().height() && oldPosition.x() >= 0 && oldPosition.x() < q->boundingRect().width() && - oldPosition.y() >= 0 && oldPosition.y() < q->boundingRect().height()) { + oldPosition.y() >= 0 && oldPosition.y() < q->boundingRect().height()) { + + // When user moves a finger on the keyboard, currently active button(s) should consume events + // as long as the finger position is within the touch area of the button. Button should be removed + // from the mActiveButtons, when finger moves outside the button's touch area. - if (oldIndex != newIndex) { + // The button this event belongs to can be determined by checking which button handled the + // 'oldPosition' coordinate in previous run of either pressEvent or moveEvent. + // If button is no longer active just return. + int activeItemIndex = activeButtonIndex(oldPosition); + if (activeItemIndex < 0) { + return; + } + HbInputButton* activeItem = mButtonData.at(activeItemIndex); + + // In case user has moved finger far enough away from the original button, old active + // item needs to be removed and the new one should be added to active list. + if (!mActiveButtons.contains(newIndex) && !activeItem->activeTouchArea().contains(newPosition)) { releaseEvent(oldPosition, false); pressEvent(newPosition, false); QString text; - HbInputButton *oldItem = mButtonData.at(oldIndex); - if (oldItem->type() == HbInputButton::ButtonTypeLabel) { - text = oldItem->text(HbInputButton::ButtonTextIndexPrimary); + if (activeItem->type() == HbInputButton::ButtonTypeLabel) { + text = activeItem->text(HbInputButton::ButtonTextIndexPrimary); } - QKeyEvent releaseEvent(QEvent::KeyRelease, oldItem->keyCode(), Qt::NoModifier, text); + QKeyEvent releaseEvent(QEvent::KeyRelease, activeItem->keyCode(), Qt::NoModifier, text); HbInputButton *newItem = mButtonData.at(newIndex); if (newItem->type() == HbInputButton::ButtonTypeLabel) { @@ -615,11 +631,16 @@ QKeyEvent pressEvent(QEvent::KeyPress, newItem->keyCode(), Qt::NoModifier, text); q->emitPressedButtonChanged(releaseEvent, pressEvent); + } else { + // Even though we do nothing with this button this time, update the status + // of the item, so next time this function is invoked, this button is correctly + // detected as the owner of the new event. + activeItem->setLastTriggeredPosition(newPosition); } } else { // Move event came from outside button group so create new release and press events // for old and new position. If one of the positions is inside button group - // a new event will get generated. + // a new key event will get emitted automatically. releaseEvent(oldPosition, false); pressEvent(newPosition); } @@ -629,30 +650,24 @@ { Q_Q(HbInputButtonGroup); - // Ignore release events outside button group - if (!(position.x() >= 0 && position.x() < q->boundingRect().width() && - position.y() >= 0 && position.y() < q->boundingRect().height())) { - return; - } + int activeIndex = activeButtonIndex(position); + if (activeIndex >= 0) { + HbInputButton *item = mButtonData.at(activeIndex); - int column = static_cast(position.x() / (q->boundingRect().width() / mGridSize.width())); - int row = static_cast(position.y() / (q->boundingRect().height() / mGridSize.height())); - - int index = mButtonGridPositions.value(QPair(column, row), -1); + cancelLongPress(activeIndex); - if (index >= 0 && index < mButtonData.count()) { - HbInputButton *item = mButtonData.at(index); - - cancelLongPress(index); - + // Check if we need to handle this event if (item->state() != HbInputButton::ButtonStatePressed) { return; } + // This item is pressed, so release can happen for this. + mActiveButtons.removeAll(activeIndex); + item->clearLastTriggeredPosition(); + if (item->type() == HbInputButton::ButtonTypeFunction) { HbWidgetFeedback::triggered(q, Hb::InstantReleased, Hb::ModifierInputFunctionButton); - } - else { + } else { HbWidgetFeedback::triggered(q, Hb::InstantReleased); } @@ -670,8 +685,7 @@ if (emitSignal) { if (item->type() == HbInputButton::ButtonTypeFunction) { HbWidgetFeedback::triggered(q, Hb::InstantClicked, Hb::ModifierInputFunctionButton); - } - else { + } else { HbWidgetFeedback::triggered(q, Hb::InstantClicked); } int actionIndex = item->keyCode() - HbInputButton::ButtonKeyCodeCustom; @@ -699,7 +713,7 @@ int index = mLongPressButtons.at(0); mLongPressButtons.removeAt(0); QTimer *timer = mLongPressTimers.at(0); - mLongPressTimers.removeAt(0); + mLongPressTimers.removeAt(0); if (index >= 0 && index < mButtonData.count()) { HbInputButton *item = mButtonData.at(index); @@ -714,8 +728,7 @@ if (item->type() == HbInputButton::ButtonTypeFunction) { HbWidgetFeedback::triggered(q, Hb::InstantKeyRepeated, Hb::ModifierInputFunctionButton); - } - else { + } else { HbWidgetFeedback::triggered(q, Hb::InstantKeyRepeated); } @@ -724,8 +737,8 @@ text = item->text(HbInputButton::ButtonTextIndexPrimary); } int keycode = item->keyCode(); - QKeyEvent releaeEvent(QEvent::KeyRelease, keycode, Qt::NoModifier, text, true); - q->emitButtonReleased(releaeEvent); + QKeyEvent releaseEvent(QEvent::KeyRelease, keycode, Qt::NoModifier, text, true); + q->emitButtonReleased(releaseEvent); QKeyEvent pressEvent(QEvent::KeyPress, keycode, Qt::NoModifier, text, true); q->emitButtonPressed(pressEvent); } else { @@ -737,8 +750,7 @@ } else { if (item->type() == HbInputButton::ButtonTypeFunction) { HbWidgetFeedback::triggered(q, Hb::InstantLongPressed, Hb::ModifierInputFunctionButton); - } - else { + } else { HbWidgetFeedback::triggered(q, Hb::InstantLongPressed); } @@ -788,6 +800,32 @@ } } +/*! +\internal +\brief Search active button based on position. + +Locates active button based on its last interaction point. + +\param position Location of triggered user action. +\return NULL, when no button has reacted to given coordinates. +\return Pointer to active button. +*/ +int HbInputButtonGroupPrivate::activeButtonIndex(const QPointF &position) +{ + // Here we get the position, where last interaction occurred. + // Start searching from the end of the list, because it is likely, + // that the triggered position happens on the button that was added last. + for (int i = mActiveButtons.count() - 1; i >= 0; --i) { + int activeIndex = mActiveButtons[i]; + if (mButtonData[activeIndex]->wasTriggeredAt(position)) { + return activeIndex; + } + } + + // Position is not inside any single active button. + return -1; +} + void HbInputButtonGroupPrivate::createPrimarySingleTextLayout(int index, const QHash &textContent, const QSizeF &size) { qreal cellWidth = size.width() / mGridSize.width(); @@ -949,32 +987,32 @@ qreal textPositionY = 0.0; switch(textType) { - case HbInputButtonGroup::ButtonTextTypeSingle: - case HbInputButtonGroup::ButtonTextTypeLabel: - textPositionX = (button->position().x() + 0.5 * button->size().width()) * cellSize.width() - 0.5 * textSize.width(); - textPositionY = (button->position().y() + 0.5 * button->size().height()) * cellSize.height() - 0.5 * textSize.height(); - break; + case HbInputButtonGroup::ButtonTextTypeSingle: + case HbInputButtonGroup::ButtonTextTypeLabel: + textPositionX = (button->position().x() + 0.5 * button->size().width()) * cellSize.width() - 0.5 * textSize.width(); + textPositionY = (button->position().y() + 0.5 * button->size().height()) * cellSize.height() - 0.5 * textSize.height(); + break; - case HbInputButtonGroup::ButtonTextTypePrimary: - textPositionX = button->position().x() * cellSize.width() + HbHorizontalMarginInUnits * mUnitValue + mButtonBorderSize; - textPositionY = (button->position().y() + 0.5 * button->size().height()) * cellSize.height() - 0.5 * textSize.height(); - break; + case HbInputButtonGroup::ButtonTextTypePrimary: + textPositionX = button->position().x() * cellSize.width() + HbHorizontalMarginInUnits * mUnitValue + mButtonBorderSize; + textPositionY = (button->position().y() + 0.5 * button->size().height()) * cellSize.height() - 0.5 * textSize.height(); + break; - case HbInputButtonGroup::ButtonTextTypeSecondaryFirstRow: - textPositionX = (button->position().x() + button->size().width()) * cellSize.width() - - textSize.width() - HbHorizontalMarginInUnits * mUnitValue - mButtonBorderSize; - textPositionY = (button->position().y() + button->size().height()) * cellSize.height() - - textSize.height() - HbVerticalMarginInUnits * mUnitValue - mButtonBorderSize; - break; + case HbInputButtonGroup::ButtonTextTypeSecondaryFirstRow: + textPositionX = (button->position().x() + button->size().width()) * cellSize.width() - + textSize.width() - HbHorizontalMarginInUnits * mUnitValue - mButtonBorderSize; + textPositionY = (button->position().y() + button->size().height()) * cellSize.height() - + textSize.height() - HbVerticalMarginInUnits * mUnitValue - mButtonBorderSize; + break; - case HbInputButtonGroup::ButtonTextTypeSecondarySecondRow: - textPositionX = (button->position().x() + button->size().width()) * cellSize.width() - - textSize.width() - HbHorizontalMarginInUnits * mUnitValue - mButtonBorderSize; - textPositionY = button->position().y() * cellSize.height() + HbVerticalMarginInUnits * mUnitValue + mButtonBorderSize; - break; + case HbInputButtonGroup::ButtonTextTypeSecondarySecondRow: + textPositionX = (button->position().x() + button->size().width()) * cellSize.width() - + textSize.width() - HbHorizontalMarginInUnits * mUnitValue - mButtonBorderSize; + textPositionY = button->position().y() * cellSize.height() + HbVerticalMarginInUnits * mUnitValue + mButtonBorderSize; + break; - default: - break; + default: + break; } textLine.setPosition(QPointF(textPositionX, textPositionY)); } @@ -1039,23 +1077,21 @@ qreal HbInputButtonGroupPrivate::fontSize(HbInputButtonGroup::HbInputButtonTextType textType) { - switch(textType) { - case HbInputButtonGroup::ButtonTextTypeSingle: - return HbTextSizeInUnits * mUnitValue; + return mFontSize[textType]; +} - case HbInputButtonGroup::ButtonTextTypePrimary: - return HbPrimaryTextSizeInUnits * mUnitValue; +void HbInputButtonGroupPrivate::setFontSize(HbInputButtonGroup::HbInputButtonTextType textType,qreal size) +{ + mFontSize[textType] = size; +} - case HbInputButtonGroup::ButtonTextTypeSecondaryFirstRow: - case HbInputButtonGroup::ButtonTextTypeSecondarySecondRow: - return HbSecondaryTextSizeInUnits * mUnitValue; - - case HbInputButtonGroup::ButtonTextTypeLabel: - return HbLabelTextSizeInUnits * mUnitValue; - - default: - return 0; - } +void HbInputButtonGroupPrivate::resetFontSizes() +{ + mFontSize[HbInputButtonGroup::ButtonTextTypeSingle] = HbTextSizeInUnits * mUnitValue; + mFontSize[HbInputButtonGroup::ButtonTextTypePrimary] = HbPrimaryTextSizeInUnits * mUnitValue; + mFontSize[HbInputButtonGroup::ButtonTextTypeSecondaryFirstRow] = HbSecondaryTextSizeInUnits * mUnitValue; + mFontSize[HbInputButtonGroup::ButtonTextTypeSecondarySecondRow] = HbSecondaryTextSizeInUnits * mUnitValue; + mFontSize[HbInputButtonGroup::ButtonTextTypeLabel] = HbLabelTextSizeInUnits * mUnitValue; } void HbInputButtonGroupPrivate::startLongPress(int index) @@ -1101,7 +1137,12 @@ d->mUnitValue = HbDeviceProfile::profile(mainWindow()).unitValue(); + resetFontSizes(); + setAcceptedMouseButtons(Qt::LeftButton); + + setFlag(QGraphicsItem::ItemIsPanel, true); + setActive(false); } /*! @@ -1114,6 +1155,8 @@ d->mUnitValue = HbDeviceProfile::profile(mainWindow()).unitValue(); + resetFontSizes(); + setAcceptedMouseButtons(Qt::LeftButton); } @@ -1127,6 +1170,8 @@ d->mUnitValue = HbDeviceProfile::profile(mainWindow()).unitValue(); + resetFontSizes(); + setAcceptedMouseButtons(Qt::LeftButton); setGridSize(size); @@ -1142,6 +1187,8 @@ d->mUnitValue = HbDeviceProfile::profile(mainWindow()).unitValue(); + resetFontSizes(); + setAcceptedMouseButtons(Qt::LeftButton); setGridSize(size); @@ -1202,9 +1249,10 @@ { Q_D(HbInputButtonGroup); - foreach(HbInputButton *button, d->mButtonData) { - if (!data.contains(button)) { - delete button; + for (int i = 0; i < d->mButtonData.count(); ++i) { + if (!data.contains(d->mButtonData.at(i))) { + delete d->mButtonData.at(i); + d->cancelLongPress(i); } } d->mButtonData = data; @@ -1233,6 +1281,7 @@ if (index >= 0 && index < d->mButtonData.count()) { if (data != d->mButtonData.at(index)) { delete d->mButtonData.at(index); + d->cancelLongPress(index); } if (data) { d->mButtonData.replace(index, data); @@ -1489,7 +1538,8 @@ /*! -Sets multi touch enabled or disabled. +If given parameter is true more that one touch points are accepted +simultaneously. \sa isMultiTouchEnabled */ @@ -1502,7 +1552,7 @@ } /*! -Returns multi touch state. +Returns true if more than one touch points are accepted simultaneously. \sa setMultiTouchEnabled */ @@ -1536,6 +1586,26 @@ } /*! +Set font size for given text type +*/ +void HbInputButtonGroup::setFontSize(HbInputButtonTextType textType,qreal size) +{ + Q_D(HbInputButtonGroup); + + return d->setFontSize(textType,size); +} + +/*! +Reset font size for all text type +*/ +void HbInputButtonGroup::resetFontSizes() +{ + Q_D(HbInputButtonGroup); + + return d->resetFontSizes(); +} + +/*! Returns all possible buttons the user could have intended to press for the last registered touch along with their corresponding probabilities. */ @@ -1674,75 +1744,78 @@ { Q_D(HbInputButtonGroup); - if (!d->mEnabled) { + // In case disabled or there is a preview popup open for any button, ignore events in this group. + if (!d->mEnabled || (d->mCharacterSelectionPreview && d->mCharacterSelectionPreview->isVisible())) { event->ignore(); return false; } switch(event->type()) { - case QEvent::TouchBegin: { - QTouchEvent *touchEvent = static_cast(event); - foreach(const QTouchEvent::TouchPoint &point, touchEvent->touchPoints()) { - if (!point.isPrimary() && d->mMultiTouchEnabled) { - d->pressEvent(point.pos()); - } + case QEvent::TouchBegin: { + QTouchEvent *touchEvent = static_cast(event); + foreach(const QTouchEvent::TouchPoint &point, touchEvent->touchPoints()) { + if (!point.isPrimary() && d->mMultiTouchEnabled) { + d->pressEvent(point.pos()); } - break; } + break; + } - case QEvent::TouchUpdate: { - QTouchEvent *touchEvent = static_cast(event); - foreach(const QTouchEvent::TouchPoint &point, touchEvent->touchPoints()) { - if (!point.isPrimary() && d->mMultiTouchEnabled) { - if (point.state() & Qt::TouchPointPressed) { - d->pressEvent(point.pos()); - } else if (point.state() & Qt::TouchPointMoved) { - d->moveEvent(point.lastPos(), point.pos()); - } else if (point.state() & Qt::TouchPointReleased) { - d->releaseEvent(point.pos()); - } - } - } - break; - } - - case QEvent::TouchEnd: { - QTouchEvent *touchEvent = static_cast(event); - foreach(const QTouchEvent::TouchPoint &point, touchEvent->touchPoints()) { - if (!point.isPrimary() && d->mMultiTouchEnabled) { + case QEvent::TouchUpdate: { + QTouchEvent *touchEvent = static_cast(event); + foreach(const QTouchEvent::TouchPoint &point, touchEvent->touchPoints()) { + if (!point.isPrimary() && d->mMultiTouchEnabled) { + if (point.state() & Qt::TouchPointPressed) { + d->pressEvent(point.pos()); + } else if (point.state() & Qt::TouchPointMoved) { + d->moveEvent(point.lastPos(), point.pos()); + } else if (point.state() & Qt::TouchPointReleased) { + d->moveEvent(point.lastPos(), point.pos()); d->releaseEvent(point.pos()); } } - break; } + break; + } - case QEvent::GraphicsSceneMousePress: { - QGraphicsSceneMouseEvent *mouseEvent = static_cast(event); - d->pressEvent(mouseEvent->pos()); - break; + case QEvent::TouchEnd: { + QTouchEvent *touchEvent = static_cast(event); + foreach(const QTouchEvent::TouchPoint &point, touchEvent->touchPoints()) { + if (!point.isPrimary() && d->mMultiTouchEnabled) { + d->moveEvent(point.lastPos(), point.pos()); + d->releaseEvent(point.pos()); + } } + break; + } - case QEvent::GraphicsSceneMouseDoubleClick: { - QGraphicsSceneMouseEvent *mouseEvent = static_cast(event); - d->doublePressEvent(mouseEvent->pos()); - break; - } + case QEvent::GraphicsSceneMousePress: { + QGraphicsSceneMouseEvent *mouseEvent = static_cast(event); + d->pressEvent(mouseEvent->pos()); + break; + } - case QEvent::GraphicsSceneMouseMove: { - QGraphicsSceneMouseEvent *mouseEvent = static_cast(event); - d->moveEvent(mouseEvent->lastPos(), mouseEvent->pos()); - break; - } + case QEvent::GraphicsSceneMouseDoubleClick: { + QGraphicsSceneMouseEvent *mouseEvent = static_cast(event); + d->doublePressEvent(mouseEvent->pos()); + break; + } - case QEvent::GraphicsSceneMouseRelease: { - QGraphicsSceneMouseEvent *mouseEvent = static_cast(event); - d->releaseEvent(mouseEvent->pos()); - cancelButtonPress(); - break; - } + case QEvent::GraphicsSceneMouseMove: { + QGraphicsSceneMouseEvent *mouseEvent = static_cast(event); + d->moveEvent(mouseEvent->lastPos(), mouseEvent->pos()); + break; + } - default: - return HbWidget::event(event); + case QEvent::GraphicsSceneMouseRelease: { + QGraphicsSceneMouseEvent *mouseEvent = static_cast(event); + d->moveEvent(mouseEvent->lastPos(), mouseEvent->pos()); + d->releaseEvent(mouseEvent->pos()); + break; + } + + default: + return HbWidget::event(event); } return true; } @@ -1806,8 +1879,6 @@ /*! \reimp - -Enables touch events if multi touch is enabled. */ void HbInputButtonGroup::showEvent(QShowEvent *event) { @@ -1885,6 +1956,8 @@ ungrabMouse(); + d->mActiveButtons.clear(); + // Cancel long press timers d->mLongPressButtons.clear(); foreach (QTimer *timer, d->mLongPressTimers) { @@ -1896,13 +1969,14 @@ foreach (HbInputButton *button, d->mButtonData) { if (button->state() == HbInputButton::ButtonStatePressed) { button->setState(HbInputButton::ButtonStateReleased); + button->clearLastTriggeredPosition(); } d->hideButtonPreview(button); } - if (d->mCharacterSelectionPreview) { + if (d->mCharacterSelectionPreview) { d->mCharacterSelectionPreview->hide(); } - + d->updateGraphics(QSizeF(boundingRect().width(), boundingRect().height())); d->updateTextLayouts(QSizeF(boundingRect().width(), boundingRect().height())); update();