aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquickitem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items/qquickitem.cpp')
-rw-r--r--src/quick/items/qquickitem.cpp273
1 files changed, 240 insertions, 33 deletions
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index d72a7b1a1d..d110a01290 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -78,6 +78,10 @@
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_DEBUG
+static bool qsg_leak_check = !qgetenv("QML_LEAK_CHECK").isEmpty();
+#endif
+
#ifdef FOCUS_DEBUG
void printFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1);
void printFocusTree(QQuickItem *item, QQuickItem *scope, int depth)
@@ -739,7 +743,12 @@ void QQuickKeyNavigationAttached::setFocusNavigation(QQuickItem *currentItem, co
while (currentItem != initialItem && isNextItem);
}
-const QQuickKeysAttached::SigMap QQuickKeysAttached::sigMap[] = {
+struct SigMap {
+ int key;
+ const char *sig;
+};
+
+const SigMap sigMap[] = {
{ Qt::Key_Left, "leftPressed" },
{ Qt::Key_Right, "rightPressed" },
{ Qt::Key_Up, "upPressed" },
@@ -771,6 +780,21 @@ const QQuickKeysAttached::SigMap QQuickKeysAttached::sigMap[] = {
{ 0, 0 }
};
+const QByteArray QQuickKeysAttached::keyToSignal(int key)
+{
+ QByteArray keySignal;
+ if (key >= Qt::Key_0 && key <= Qt::Key_9) {
+ keySignal = "digit0Pressed";
+ keySignal[5] = '0' + (key - Qt::Key_0);
+ } else {
+ int i = 0;
+ while (sigMap[i].key && sigMap[i].key != key)
+ ++i;
+ keySignal = sigMap[i].sig;
+ }
+ return keySignal;
+}
+
bool QQuickKeysAttached::isConnected(const char *signalName)
{
Q_D(QQuickKeysAttached);
@@ -1510,6 +1534,11 @@ void QQuickItemPrivate::setAccessibleFlagAndListener()
}
}
+/*!
+Clears all sub focus items from \a scope.
+If \a focus is true, sets the scope's subFocusItem
+to be this item.
+*/
void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
{
Q_Q(QQuickItem);
@@ -1839,6 +1868,11 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
*/
/*!
+ \fn void QQuickItem::activeFocusOnTabChanged(bool)
+ \internal
+*/
+
+/*!
\fn void QQuickItem::childrenChanged()
\internal
*/
@@ -1945,9 +1979,11 @@ static void qt_print_item_count()
QQuickItem::~QQuickItem()
{
#ifndef QT_NO_DEBUG
- --qt_item_count;
- if (qt_item_count < 0)
- qDebug("Item destroyed after qt_print_item_count() was called.");
+ if (qsg_leak_check) {
+ --qt_item_count;
+ if (qt_item_count < 0)
+ qDebug("Item destroyed after qt_print_item_count() was called.");
+ }
#endif
Q_D(QQuickItem);
@@ -1997,6 +2033,93 @@ QQuickItem::~QQuickItem()
}
/*!
+ \internal
+ \brief QQuickItemPrivate::focusNextPrev focuses the next/prev item in the tab-focus-chain
+ \param item The item that currently has the focus
+ \param forward The direction
+ \return Whether the next item in the focus chain is found or not
+
+ If \a next is true, the next item visited will be in depth-first order relative to \a item.
+ If \a next is false, the next item visited will be in reverse depth-first order relative to \a item.
+*/
+bool QQuickItemPrivate::focusNextPrev(QQuickItem *item, bool forward)
+{
+ Q_ASSERT(item);
+ Q_ASSERT(item->activeFocusOnTab());
+
+ QQuickItem *from = 0;
+ if (forward) {
+ from = item->parentItem();
+ } else {
+ if (!item->childItems().isEmpty())
+ from = item->childItems().first();
+ else
+ from = item->parentItem();
+ }
+ bool skip = false;
+ QQuickItem *current = item;
+ do {
+ skip = false;
+ QQuickItem *last = current;
+
+ bool hasChildren = !current->childItems().isEmpty() && current->isEnabled() && current->isVisible();
+
+ // coming from parent: check children
+ if (hasChildren && from == current->parentItem()) {
+ if (forward) {
+ current = current->childItems().first();
+ } else {
+ current = current->childItems().last();
+ if (!current->childItems().isEmpty())
+ skip = true;
+ }
+ } else if (hasChildren && forward && from != current->childItems().last()) {
+ // not last child going forwards
+ int nextChild = current->childItems().indexOf(from) + 1;
+ current = current->childItems().at(nextChild);
+ } else if (hasChildren && !forward && from != current->childItems().first()) {
+ // not first child going backwards
+ int prevChild = current->childItems().indexOf(from) - 1;
+ current = current->childItems().at(prevChild);
+ if (!current->childItems().isEmpty())
+ skip = true;
+ // back to the parent
+ } else if (current->parentItem()) {
+ current = current->parentItem();
+ // we would evaluate the parent twice, thus we skip
+ if (forward) {
+ skip = true;
+ } else if (!forward && !current->childItems().isEmpty()) {
+ if (last != current->childItems().first()) {
+ skip = true;
+ } else if (last == current->childItems().first()) {
+ if (current->isFocusScope() && current->activeFocusOnTab() && current->hasActiveFocus())
+ skip = true;
+ }
+ }
+ } else if (hasChildren) {
+ // Wrap around after checking all items forward
+ if (forward) {
+ current = current->childItems().first();
+ } else {
+ current = current->childItems().last();
+ if (!current->childItems().isEmpty())
+ skip = true;
+ }
+ }
+
+ from = last;
+ } while (skip || !current->activeFocusOnTab() || !current->isEnabled() || !current->isVisible());
+
+ if (current == item)
+ return false;
+
+ current->forceActiveFocus(forward ? Qt::TabFocusReason : Qt::BacktabFocusReason);
+
+ return true;
+}
+
+/*!
\qmlproperty Item QtQuick2::Item::parent
This property holds the visual parent of the item.
@@ -2057,7 +2180,7 @@ void QQuickItem::setParentItem(QQuickItem *parentItem)
while (!scopeItem->isFocusScope() && scopeItem->parentItem())
scopeItem = scopeItem->parentItem();
if (d->window) {
- QQuickWindowPrivate::get(d->window)->clearFocusInScope(scopeItem, scopeFocusedItem,
+ QQuickWindowPrivate::get(d->window)->clearFocusInScope(scopeItem, scopeFocusedItem, Qt::OtherFocusReason,
QQuickWindowPrivate::DontChangeFocusProperty);
if (scopeFocusedItem != this)
QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(this, true);
@@ -2120,7 +2243,7 @@ void QQuickItem::setParentItem(QQuickItem *parentItem)
emit scopeFocusedItem->focusChanged(false);
} else {
if (d->window) {
- QQuickWindowPrivate::get(d->window)->setFocusInScope(scopeItem, scopeFocusedItem,
+ QQuickWindowPrivate::get(d->window)->setFocusInScope(scopeItem, scopeFocusedItem, Qt::OtherFocusReason,
QQuickWindowPrivate::DontChangeFocusProperty);
} else {
QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(scopeItem, true);
@@ -2496,6 +2619,7 @@ QQuickItemPrivate::QQuickItemPrivate()
, isAccessible(false)
, culled(false)
, hasCursor(false)
+ , activeFocusOnTab(false)
, dirtyAttributes(0)
, nextDirtyItem(0)
, prevDirtyItem(0)
@@ -2526,11 +2650,13 @@ QQuickItemPrivate::~QQuickItemPrivate()
void QQuickItemPrivate::init(QQuickItem *parent)
{
#ifndef QT_NO_DEBUG
- ++qt_item_count;
- static bool atexit_registered = false;
- if (!atexit_registered) {
- atexit(qt_print_item_count);
- atexit_registered = true;
+ if (qsg_leak_check) {
+ ++qt_item_count;
+ static bool atexit_registered = false;
+ if (!atexit_registered) {
+ atexit(qt_print_item_count);
+ atexit_registered = true;
+ }
}
#endif
@@ -2979,8 +3105,6 @@ QList<QQuickItem *> QQuickItem::childItems() const
If clipping is enabled, an item will clip its own painting, as well
as the painting of its children, to its bounding rectangle.
-
- Non-rectangular clipping regions are not supported for performance reasons.
*/
/*!
\property QQuickItem::clip
@@ -2990,8 +3114,6 @@ QList<QQuickItem *> QQuickItem::childItems() const
as the painting of its children, to its bounding rectangle. If you set
clipping during an item's paint operation, remember to re-set it to
prevent clipping the rest of your scene.
-
- Non-rectangular clipping regions are not supported for performance reasons.
*/
bool QQuickItem::clip() const
{
@@ -3744,29 +3866,44 @@ void QQuickItem::mapToItem(QQmlV8Function *args) const
/*!
\qmlmethod QtQuick2::Item::forceActiveFocus()
+ \overload
Forces active focus on the item.
This method sets focus on the item and ensures that all ancestor
FocusScope objects in the object hierarchy are also given \l focus.
+ The reason for the focus change will be \a Qt::OtherFocusReason. Use
+ the overloaded method to specify the focus reason to enable better
+ handling of the focus change.
+
\sa activeFocus
*/
+void QQuickItem::forceActiveFocus()
+{
+ forceActiveFocus(Qt::OtherFocusReason);
+}
+
/*!
- Forces active focus on the item.
+ \qmlmethod QtQuick2::Item::forceActiveFocus(Qt::FocusReason reason)
+
+ Forces active focus on the item with the given \a reason.
This method sets focus on the item and ensures that all ancestor
FocusScope objects in the object hierarchy are also given \l focus.
- \sa activeFocus
+ \since QtQuick 2.1
+
+ \sa activeFocus, Qt::FocusReason
*/
-void QQuickItem::forceActiveFocus()
+
+void QQuickItem::forceActiveFocus(Qt::FocusReason reason)
{
- setFocus(true);
+ setFocus(true, reason);
QQuickItem *parent = parentItem();
while (parent) {
if (parent->flags() & QQuickItem::ItemIsFocusScope) {
- parent->setFocus(true);
+ parent->setFocus(true, reason);
}
parent = parent->parentItem();
}
@@ -3788,14 +3925,15 @@ void QQuickItem::forceActiveFocus()
*/
QQuickItem *QQuickItem::childAt(qreal x, qreal y) const
{
- // XXX todo - should this include transform etc.?
const QList<QQuickItem *> children = childItems();
for (int i = children.count()-1; i >= 0; --i) {
QQuickItem *child = children.at(i);
- if (child->isVisible() && child->x() <= x
- && child->x() + child->width() >= x
- && child->y() <= y
- && child->y() + child->height() >= y)
+ // Map coordinates to the child element's coordinate space
+ QPointF point = mapToItem(child, QPointF(x, y));
+ if (child->isVisible() && point.x() >= 0
+ && child->width() >= point.x()
+ && point.y() >= 0
+ && child->height() >= point.y())
return child;
}
return 0;
@@ -3954,8 +4092,8 @@ void QQuickItemPrivate::setState(const QString &state)
This property holds the name of the current state of the item.
- If the item is in its default state — that is, no explicit state has been
- set — then this property holds an empty string. Likewise, you can return
+ If the item is in its default state, that is, no explicit state has been
+ set, then this property holds an empty string. Likewise, you can return
an item to its default state by setting this property to an empty string.
\sa {Qt Quick States}
@@ -3965,8 +4103,8 @@ void QQuickItemPrivate::setState(const QString &state)
This property holds the name of the current state of the item.
- If the item is in its default state — that is, no explicit state has been
- set — then this property holds an empty string. Likewise, you can return
+ If the item is in its default state, that is, no explicit state has been
+ set, then this property holds an empty string. Likewise, you can return
an item to its default state by setting this property to an empty string.
\sa {Qt Quick States}
@@ -4130,6 +4268,23 @@ void QQuickItemPrivate::deliverKeyEvent(QKeyEvent *e)
else
extra->keyHandler->keyReleased(e, true);
}
+
+ if (e->isAccepted())
+ return;
+
+ //only care about KeyPress now
+ if (q->activeFocusOnTab() && e->type() == QEvent::KeyPress) {
+ bool res = false;
+ if (!(e->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier?
+ if (e->key() == Qt::Key_Backtab
+ || (e->key() == Qt::Key_Tab && (e->modifiers() & Qt::ShiftModifier)))
+ res = QQuickItemPrivate::focusNextPrev(q, false);
+ else if (e->key() == Qt::Key_Tab)
+ res = QQuickItemPrivate::focusNextPrev(q, true);
+ if (res)
+ e->setAccepted(true);
+ }
+ }
}
#ifndef QT_NO_IM
@@ -5052,7 +5207,7 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec
q->ungrabMouse();
if (scope && !effectiveEnable && activeFocus) {
windowPriv->clearFocusInScope(
- scope, q, QQuickWindowPrivate::DontChangeFocusProperty | QQuickWindowPrivate::DontChangeSubFocusItem);
+ scope, q, Qt::OtherFocusReason, QQuickWindowPrivate::DontChangeFocusProperty | QQuickWindowPrivate::DontChangeSubFocusItem);
}
}
@@ -5063,7 +5218,7 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec
if (window && scope && effectiveEnable && focus) {
QQuickWindowPrivate::get(window)->setFocusInScope(
- scope, q, QQuickWindowPrivate::DontChangeFocusProperty | QQuickWindowPrivate::DontChangeSubFocusItem);
+ scope, q, Qt::OtherFocusReason, QQuickWindowPrivate::DontChangeFocusProperty | QQuickWindowPrivate::DontChangeSubFocusItem);
}
emit q->enabledChanged();
@@ -5292,6 +5447,53 @@ void QQuickItem::setSmooth(bool smooth)
}
/*!
+ \qmlproperty bool QtQuick2::Item::activeFocusOnTab
+
+ This property holds whether the item wants to be in tab focus
+ chain. By default this is set to false.
+
+ The tab focus chain traverses elements by visiting first the
+ parent, and then its children in the order they occur in the
+ children property. Pressing the tab key on an item in the tab
+ focus chain will move keyboard focus to the next item in the
+ chain. Pressing BackTab (normally Shift+Tab) will move focus
+ to the previous item.
+
+ To set up a manual tab focus chain, see \l KeyNavigation. Tab
+ key events used by Keys or KeyNavigation have precedence over
+ focus chain behavior, ignore the events in other key handlers
+ to allow it to propagate.
+*/
+/*!
+ \property QQuickItem::activeFocusOnTab
+
+ This property holds whether the item wants to be in tab focus
+ chain. By default this is set to false.
+*/
+bool QQuickItem::activeFocusOnTab() const
+{
+ Q_D(const QQuickItem);
+ return d->activeFocusOnTab;
+}
+void QQuickItem::setActiveFocusOnTab(bool activeFocusOnTab)
+{
+ Q_D(QQuickItem);
+ if (d->activeFocusOnTab == activeFocusOnTab)
+ return;
+
+ if (window()) {
+ if ((this == window()->activeFocusItem()) && !activeFocusOnTab) {
+ qWarning("QQuickItem: Cannot set activeFocusOnTab to false once item is the active focus item.");
+ return;
+ }
+ }
+
+ d->activeFocusOnTab = activeFocusOnTab;
+
+ emit activeFocusOnTabChanged(activeFocusOnTab);
+}
+
+/*!
\qmlproperty bool QtQuick2::Item::antialiasing
Primarily used in Rectangle and image based elements to decide if the item should
@@ -5977,6 +6179,11 @@ bool QQuickItem::hasFocus() const
void QQuickItem::setFocus(bool focus)
{
+ setFocus(focus, Qt::OtherFocusReason);
+}
+
+void QQuickItem::setFocus(bool focus, Qt::FocusReason reason)
+{
Q_D(QQuickItem);
if (d->focus == focus)
return;
@@ -5988,9 +6195,9 @@ void QQuickItem::setFocus(bool focus)
scope = scope->parentItem();
if (d->window) {
if (focus)
- QQuickWindowPrivate::get(d->window)->setFocusInScope(scope, this);
+ QQuickWindowPrivate::get(d->window)->setFocusInScope(scope, this, reason);
else
- QQuickWindowPrivate::get(d->window)->clearFocusInScope(scope, this);
+ QQuickWindowPrivate::get(d->window)->clearFocusInScope(scope, this, reason);
} else {
// do the focus changes from setFocusInScope/clearFocusInScope that are
// unrelated to a window