Skip to content

Commit

Permalink
Added new convenience readonly visibleChildren property to Item
Browse files Browse the repository at this point in the history
Change-Id: I5ec541226fabd72c05ce8ccb8bb7e56f6ec7717a
Task-number: 22724
Reviewed-by: Aaron Kennedy <aaron.kennedy@nokia.com>
  • Loading branch information
Mathias Malmqvist authored and Qt by Nokia committed Feb 9, 2012
1 parent e299f0d commit 9c17939
Show file tree
Hide file tree
Showing 5 changed files with 278 additions and 6 deletions.
70 changes: 65 additions & 5 deletions src/quick/items/qquickitem.cpp
Expand Up @@ -1904,7 +1904,11 @@ void QQuickItem::setParentItem(QQuickItem *parentItem)
QQuickCanvasPrivate::get(d->canvas)->clearFocusInScope(scopeItem, scopeFocusedItem,
QQuickCanvasPrivate::DontChangeFocusProperty);

const bool wasVisible = isVisible();
op->removeChild(this);
if (wasVisible) {
emit oldParentItem->visibleChildrenChanged();
}
} else if (d->canvas) {
QQuickCanvasPrivate::get(d->canvas)->parentlessItems.remove(this);
}
Expand Down Expand Up @@ -1951,6 +1955,8 @@ void QQuickItem::setParentItem(QQuickItem *parentItem)
}

emit parentChanged(d->parentItem);
if (isVisible() && d->parentItem)
emit d->parentItem->visibleChildrenChanged();
}

void QQuickItem::stackBefore(const QQuickItem *sibling)
Expand Down Expand Up @@ -2488,6 +2494,39 @@ void QQuickItemPrivate::children_clear(QDeclarativeListProperty<QQuickItem> *pro
p->childItems.at(0)->setParentItem(0);
}

void QQuickItemPrivate::visibleChildren_append(QDeclarativeListProperty<QQuickItem>*, QQuickItem *self)
{
// do nothing
qmlInfo(self) << "QQuickItem: visibleChildren property is readonly and cannot be assigned to.";
}

int QQuickItemPrivate::visibleChildren_count(QDeclarativeListProperty<QQuickItem> *prop)
{
QQuickItemPrivate *p = QQuickItemPrivate::get(static_cast<QQuickItem *>(prop->object));
int visibleCount = 0;
int c = p->childItems.count();
while (c--) {
if (p->childItems.at(c)->isVisible()) visibleCount++;
}

return visibleCount;
}

QQuickItem *QQuickItemPrivate::visibleChildren_at(QDeclarativeListProperty<QQuickItem> *prop, int index)
{
QQuickItemPrivate *p = QQuickItemPrivate::get(static_cast<QQuickItem *>(prop->object));
const int childCount = p->childItems.count();
if (index >= childCount || index < 0)
return 0;

int visibleCount = -1;
for (int i = 0; i < childCount; i++) {
if (p->childItems.at(i)->isVisible()) visibleCount++;
if (visibleCount == index) return p->childItems.at(i);
}
return 0;
}

int QQuickItemPrivate::transform_count(QDeclarativeListProperty<QQuickTransform> *prop)
{
QQuickItem *that = static_cast<QQuickItem *>(prop->object);
Expand Down Expand Up @@ -3321,6 +3360,20 @@ QDeclarativeListProperty<QQuickItem> QQuickItemPrivate::children()

}

/*!
\qmlproperty real QtQuick2::Item::visibleChildren
This read-only property lists all of the item's children that are currently visible.
Note that a child's visibility may have changed explicitly, or because the visibility
of this (it's parent) item or another grandparent changed.
*/
QDeclarativeListProperty<QQuickItem> QQuickItemPrivate::visibleChildren()
{
return QDeclarativeListProperty<QQuickItem>(q_func(), 0, QQuickItemPrivate::visibleChildren_append,
QQuickItemPrivate::visibleChildren_count,
QQuickItemPrivate::visibleChildren_at);

}

QDeclarativeListProperty<QDeclarativeState> QQuickItemPrivate::states()
{
return _states()->statesProperty();
Expand Down Expand Up @@ -3894,7 +3947,9 @@ void QQuickItem::setVisible(bool v)

d->explicitVisible = v;

d->setEffectiveVisibleRecur(d->calcEffectiveVisible());
const bool childVisibilityChanged = d->setEffectiveVisibleRecur(d->calcEffectiveVisible());
if (childVisibilityChanged && d->parentItem)
emit d->parentItem->visibleChildrenChanged(); // signal the parent, not this!
}

bool QQuickItem::isEnabled() const
Expand Down Expand Up @@ -3926,18 +3981,18 @@ bool QQuickItemPrivate::calcEffectiveVisible() const
return explicitVisible && (!parentItem || QQuickItemPrivate::get(parentItem)->effectiveVisible);
}

void QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible)
bool QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible)
{
Q_Q(QQuickItem);

if (newEffectiveVisible && !explicitVisible) {
// This item locally overrides visibility
return;
return false; // effective visibility didn't change
}

if (newEffectiveVisible == effectiveVisible) {
// No change necessary
return;
return false; // effective visibility didn't change
}

effectiveVisible = newEffectiveVisible;
Expand All @@ -3950,8 +4005,9 @@ void QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible)
q->ungrabMouse();
}

bool childVisibilityChanged = false;
for (int ii = 0; ii < childItems.count(); ++ii)
QQuickItemPrivate::get(childItems.at(ii))->setEffectiveVisibleRecur(newEffectiveVisible);
childVisibilityChanged |= QQuickItemPrivate::get(childItems.at(ii))->setEffectiveVisibleRecur(newEffectiveVisible);

for (int ii = 0; ii < changeListeners.count(); ++ii) {
const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii);
Expand All @@ -3963,6 +4019,10 @@ void QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible)
QAccessible::updateAccessibility(QAccessibleEvent(effectiveVisible ? QAccessible::ObjectShow : QAccessible::ObjectHide, q, 0));

emit q->visibleChanged();
if (childVisibilityChanged)
emit q->visibleChildrenChanged();

return true; // effective visibility DID change
}

bool QQuickItemPrivate::calcEffectiveEnable() const
Expand Down
2 changes: 2 additions & 0 deletions src/quick/items/qquickitem.h
Expand Up @@ -116,6 +116,7 @@ class Q_QUICK_EXPORT QQuickItem : public QObject, public QDeclarativeParserStatu
Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged FINAL)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged FINAL)
Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QDeclarativeListProperty<QQuickItem> visibleChildren READ visibleChildren NOTIFY visibleChildrenChanged DESIGNABLE false)

Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QDeclarativeListProperty<QDeclarativeState> states READ states DESIGNABLE false)
Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QDeclarativeListProperty<QDeclarativeTransition> transitions READ transitions DESIGNABLE false)
Expand Down Expand Up @@ -341,6 +342,7 @@ public Q_SLOTS:
void opacityChanged();
void enabledChanged();
void visibleChanged();
void visibleChildrenChanged();
void rotationChanged();
void scaleChanged();

Expand Down
8 changes: 7 additions & 1 deletion src/quick/items/qquickitem_p.h
Expand Up @@ -246,6 +246,7 @@ class Q_QUICK_EXPORT QQuickItemPrivate : public QObjectPrivate
QDeclarativeListProperty<QObject> data();
QDeclarativeListProperty<QObject> resources();
QDeclarativeListProperty<QQuickItem> children();
QDeclarativeListProperty<QQuickItem> visibleChildren();

QDeclarativeListProperty<QDeclarativeState> states();
QDeclarativeListProperty<QDeclarativeTransition> transitions();
Expand Down Expand Up @@ -281,6 +282,11 @@ class Q_QUICK_EXPORT QQuickItemPrivate : public QObjectPrivate
static QQuickItem *children_at(QDeclarativeListProperty<QQuickItem> *, int);
static void children_clear(QDeclarativeListProperty<QQuickItem> *);

// visibleChildren property
static void visibleChildren_append(QDeclarativeListProperty<QQuickItem> *prop, QQuickItem *o);
static int visibleChildren_count(QDeclarativeListProperty<QQuickItem> *prop);
static QQuickItem *visibleChildren_at(QDeclarativeListProperty<QQuickItem> *prop, int index);

// transform property
static int transform_count(QDeclarativeListProperty<QQuickTransform> *list);
static void transform_append(QDeclarativeListProperty<QQuickTransform> *list, QQuickTransform *);
Expand Down Expand Up @@ -471,7 +477,7 @@ class Q_QUICK_EXPORT QQuickItemPrivate : public QObjectPrivate
void deliverDragEvent(QEvent *);

bool calcEffectiveVisible() const;
void setEffectiveVisibleRecur(bool);
bool setEffectiveVisibleRecur(bool);
bool calcEffectiveEnable() const;
void setEffectiveEnableRecur(QQuickItem *scope, bool);

Expand Down
143 changes: 143 additions & 0 deletions tests/auto/qtquick2/qquickitem2/data/visiblechildren.qml
@@ -0,0 +1,143 @@
import QtQuick 2.0

Item {
id: root
width: 400
height: 300

Row {
id: row
Item { id: item1
Item { id: item1_1; visible: true }
Item { id: item1_2; visible: true }
}
Item { id: item2 }
Item { id: item3
Item { id: item3_1; visible: false }
Item { id: item3_2; visible: false }
}
Item { id: item4; visible: false
Item { id: item4_1 // implicitly invisible
Item { id: item4_1_1 } // implicitly invisible
Item { id: item4_1_2 } // implicitly invisible
}
}
}

property int row_changeEventCalls: 0
property int item1_changeEventCalls: 0
property int item2_changeEventCalls: 0
property int item3_changeEventCalls: 0
property int item4_1_changeEventCalls: 0
property int item4_1_1_changeEventCalls: 0
Connections { target: row; onVisibleChildrenChanged: row_changeEventCalls++ }
Connections { target: item1; onVisibleChildrenChanged: item1_changeEventCalls++ }
Connections { target: item2; onVisibleChildrenChanged: item2_changeEventCalls++ }
Connections { target: item3; onVisibleChildrenChanged: item3_changeEventCalls++ }
Connections { target: item4_1; onVisibleChildrenChanged: item4_1_changeEventCalls++ }
Connections { target: item4_1_1; onVisibleChildrenChanged: item4_1_1_changeEventCalls++ }

// Make sure there are three visible children and no signals fired yet
property bool test1_1: row.visibleChildren.length == 3
property bool test1_2: row.visibleChildren[0] == item1 && row.visibleChildren[1] == item2 && row.visibleChildren[2] == item3
property bool test1_3: row_changeEventCalls == 0
property bool test1_4: item1_changeEventCalls == 0 && item2_changeEventCalls == 0 && item3_changeEventCalls == 0

// Next test
function hideFirstAndLastRowChild() {
item1.visible = false;
item3.visible = false;
}

// Make sure row is signaled twice and item1 only once, and item3 not at all, and that item2 is the visible child
property bool test2_1: row.visibleChildren.length == 1
property bool test2_2: row.visibleChildren[0] == item2
property bool test2_3: row_changeEventCalls == 2
property bool test2_4: item1_changeEventCalls == 1 && item2_changeEventCalls == 0 && item3_changeEventCalls == 0

// Next test
function showLastRowChildsLastChild() {
item3_2.visible = true;
}

// Make sure item3_changeEventCalls is not signaled
property bool test3_1: row.visibleChildren.length == 1
property bool test3_2: row.visibleChildren[0] == item2
property bool test3_3: row_changeEventCalls == 2
property bool test3_4: item1_changeEventCalls == 1 && item2_changeEventCalls == 0 && item3_changeEventCalls == 0

// Next test
function showLastRowChild() {
item3.visible = true;
}

// Make sure row and item3 are signaled
property bool test4_1: row.visibleChildren.length == 2
property bool test4_2: row.visibleChildren[0] == item2 && row.visibleChildren[1] == item3
property bool test4_3: row_changeEventCalls == 3
property bool test4_4: item1_changeEventCalls == 1 && item2_changeEventCalls == 0 && item3_changeEventCalls == 1

// Next test
function tryWriteToReadonlyVisibleChildren() {
var foo = fooComponent.createObject(root);
if (Qt.isQtObject(foo) && foo.children.length == 3 && foo.visibleChildren.length == 3) {
test5_1 = true;
}

foo.visibleChildren.length = 10; // make sure this has no effect
test5_1 = (foo.visibleChildren.length == 3);
delete foo;
}

Component {
id: fooComponent
Item {
children: [ Item {},Item {},Item {} ]
visibleChildren: [ Item {} ]
}
}

// Make sure visibleChildren.length is 3 and stays that way
property bool test5_1: false

// Next test
function reparentVisibleItem3() {
item3.parent = hiddenItem; // item3 has one visible children
}

Item { id: hiddenItem; visible: false }

property bool test6_1: row.visibleChildren.length == 1 && row_changeEventCalls == 4
property bool test6_2: item3_changeEventCalls == 2
property bool test6_3: item3.visible == false

// Next test

property bool test6_4: item4_1.visible == false && item4_1_changeEventCalls == 0

function reparentImlicitlyInvisibleItem4_1() {
item4_1.parent = visibleItem;
}

Item { id: visibleItem; visible: true }
property int visibleItem_changeEventCalls: 0
Connections { target: visibleItem; onVisibleChildrenChanged: visibleItem_changeEventCalls++ }


// Make sure that an item with implictly invisible children will be signaled when reparented to a visible parent
property bool test7_1: row.visibleChildren.length == 1 && row_changeEventCalls == 4
property bool test7_2: item4_1.visible == true
property bool test7_3: item4_1_changeEventCalls == 1
property bool test7_4: visibleItem_changeEventCalls == 1



// FINALLY make sure nothing has changes while we weren't paying attention

property bool test8_1: row.visibleChildren.length == 1 && row.visibleChildren[0] == item2 && row_changeEventCalls == 4
property bool test8_2: item1_changeEventCalls == 1 && item1.visible == false
property bool test8_3: item2_changeEventCalls == 0 && item2.visible == true
property bool test8_4: item3_changeEventCalls == 2 && item3.visible == false
property bool test8_5: item4_1_1_changeEventCalls == 0 && item4_1_1.visible == true

}

0 comments on commit 9c17939

Please sign in to comment.