2
* Copyright 2015 Canonical Ltd.
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU Lesser General Public License as published by
6
* the Free Software Foundation; version 3.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU Lesser General Public License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17
#include "ucpagetreenode.h"
18
#include "ucpagetreenode_p.h"
22
Q_LOGGING_CATEGORY(ucPageTreeNode, "ubuntu.components.PageTreeNode", QtMsgType::QtWarningMsg)
24
#define PT_TRACE(params) qCDebug(ucPageTreeNode).noquote().nospace()<< params
26
UCPageTreeNodePrivate::UCPageTreeNodePrivate()
27
: m_parentNode(nullptr),
28
m_activeLeafNode(nullptr),
30
m_propagated(nullptr),
40
* \brief UCPageTreeNodePrivate::init
41
* Initialize the UCPageTreeNode and setup the bindings
43
void UCPageTreeNodePrivate::init()
46
q->setActiveFocusOnPress(true);
48
auto slotUpdateParentLeafNode = [this] () {
49
updateParentLeafNode();
52
//connect all signals that are required to keep the "activeLeafNode" in the
53
//parents valid. The activeLeadNode property in the parent is set if the current
54
//PTN is a leaf itself (isLeaf = true) or one of its children is a leaf
55
QObject::connect(q, &UCPageTreeNode::activeChanged, slotUpdateParentLeafNode);
56
QObject::connect(q, &UCPageTreeNode::activeLeafNodeChanged, slotUpdateParentLeafNode);
57
QObject::connect(q, &UCPageTreeNode::parentNodeChanged, slotUpdateParentLeafNode);
59
//make sure all bindings are in tact
67
* \brief UCPageTreeNodePrivate::initActive
68
* Initialize active property and setup the propagating binding.
69
* By default the parents active property is propagated to the children.
71
void UCPageTreeNodePrivate::initActive()
74
bool intialActive = false;
76
intialActive = m_parentNode->active();
77
QObject::connect(m_parentNode, SIGNAL(activeChanged(bool)),
78
q, SLOT(_q_activeBinding(bool)));
80
_q_activeBinding(intialActive);
85
* \brief UCPageTreeNodePrivate::initPageStack
86
* Initialize pageStack property and setup the propagating binding.
87
* By default the parents pageStack property is propagated to the children.
89
void UCPageTreeNodePrivate::initPageStack()
92
QQuickItem *initialPageStack = nullptr;
94
initialPageStack = m_parentNode->pageStack();
95
QObject::connect(m_parentNode,SIGNAL(pageStackChanged(QQuickItem*)),
96
q, SLOT(_q_pageStackBinding (QQuickItem *)));
98
_q_pageStackBinding(initialPageStack);
103
* \brief UCPageTreeNodePrivate::initPropagated
104
* Initialize __propagated property and setup the propagating binding.
105
* By default the parents __propagated property is propagated to the children.
107
void UCPageTreeNodePrivate::initPropagated()
110
QObject *initialPropagated = nullptr;
112
initialPropagated = m_parentNode->propagated();
113
QObject::connect(m_parentNode,SIGNAL(propagatedChanged(QObject*)),
114
q, SLOT(_q_propagatedBinding (QObject *)));
116
_q_propagatedBinding(initialPropagated);
121
Find the parent node and update the parentNode property
123
void UCPageTreeNodePrivate::updatePageTree()
126
q->setParentNode(getParentPageTreeNode());
132
\brief UCPageTreeNodePrivate::getParentPageTreeNode
133
Returns the parent node in the page tree, or null if the item is the root node or invalid.
135
UCPageTreeNode *UCPageTreeNodePrivate::getParentPageTreeNode()
138
UCPageTreeNode *node = nullptr;
140
//search the current tree for the next parent item that
141
//is a UCPageTreeNode
142
QQuickItem *currItem = q->parentItem();
144
UCPageTreeNode *currPageTreeNode = qobject_cast<UCPageTreeNode *>(currItem);
145
if (currPageTreeNode) {
146
if (currPageTreeNode->isLeaf()) {
147
// children of a leaf are not part of the tree
150
// current node is part of the tree with currPageTreeNode as its parent.
151
node = currPageTreeNode;
155
currItem = currItem->parentItem();
163
* \brief UCPageTreeNodePrivate::_q_activeBinding
164
* Directly updates the activeBinding property. Is used as
165
* callback to support qml style bindings that can be overriden
167
void UCPageTreeNodePrivate::_q_activeBinding(bool active)
169
if (m_active == active)
174
Q_EMIT q->activeChanged(active);
180
* \brief UCPageTreeNodePrivate::_q_pageStackBinding
181
* Directly updates the pageStack property. Is used as
182
* callback to support qml style bindings that can be overriden
184
void UCPageTreeNodePrivate::_q_pageStackBinding(QQuickItem *pageStack)
186
if (m_pageStack == pageStack)
190
m_pageStack = pageStack;
191
Q_EMIT q->pageStackChanged(pageStack);
197
* \brief UCPageTreeNodePrivate::_q_propagatedBinding
198
* Directly updates the __propagated property. Is used as
199
* callback to support qml style bindings that can be overriden
201
void UCPageTreeNodePrivate::_q_propagatedBinding(QObject *propagated)
203
if (m_propagated == propagated)
206
m_propagated = propagated;
207
Q_EMIT q->propagatedChanged(propagated);
212
* \brief UCPageTreeNodePrivate::updateParentLeafNode
213
* Update the activeLeafNode of the parent. The activeLeadNode property
214
* in the parent is set if the current PTN is a leaf itself (isLeaf = true)
215
* or one of its children is a leaf
217
void UCPageTreeNodePrivate::updateParentLeafNode()
220
if (q->active() && q->parentNode()) {
222
q->parentNode()->setActiveLeafNode(q);
224
q->parentNode()->setActiveLeafNode(q->activeLeafNode());
229
* \brief findPTNChild
230
* Returns only the next level of UCPageTreeNode children of \l rootNode.
231
* Just used for debugging output.
233
static QList<UCPageTreeNode *> findPTNChild (QQuickItem *rootNode)
235
QList<UCPageTreeNode *> nodes;
236
UCPageTreeNode *thisNode = qobject_cast<UCPageTreeNode *>(rootNode);
240
Q_FOREACH(QQuickItem *curr, rootNode->childItems()) {
241
nodes.append(findPTNChild(curr));
248
* \brief collectNodes
249
* Recursively collects all UCPageTreeNodes in a node tree and builds up
250
* a representation of the Tree. Just used for debugging output.
252
static QList<UCPageTreeNodePrivate::Node> collectNodes (UCPageTreeNode *root)
255
return QList<UCPageTreeNodePrivate::Node>();
257
QList<UCPageTreeNodePrivate::Node> nodes;
259
QList<QQuickItem *> items = root->childItems();
260
Q_FOREACH(QQuickItem *item, items) {
261
QList<UCPageTreeNode *>subNodes = findPTNChild(item);
263
if (subNodes.isEmpty()) continue;
265
Q_FOREACH(UCPageTreeNode *currPTN, subNodes) {
266
UCPageTreeNodePrivate::Node n;
268
n.m_children = collectNodes(currPTN);
277
* \brief UCPageTreeNodePrivate::dumpNode
278
* Pretty prints the node \l n using the \l oldDepth of the parentNode and
279
* \l depth as the current Nodes prefix. The \l isRoot parameter
280
* specifies if the current Node \l n is the root Node.
282
void UCPageTreeNodePrivate::dumpNode (const Node &n, const QString &oldDepth,const QString &depth, bool isRoot)
284
UCPageTreeNode *currNode = n.m_node;
286
//print the current node name and info
288
PT_TRACE(oldDepth<<"+--"<<currNode);
292
//print the current nodes properties we are interested in
293
switch(QQmlEngine::objectOwnership(currNode)) {
294
case QQmlEngine::CppOwnership:
295
PT_TRACE(QString("%1| ->ownership: ").arg(depth)<<"C++");
297
case QQmlEngine::JavaScriptOwnership:
298
PT_TRACE(QString("%1| ->ownership: ").arg(depth)<<"JS");
302
PT_TRACE(QString("%1| ->parentNode: ").arg(depth)<<currNode->parentNode());
303
PT_TRACE(QString("%1| ->parent: ").arg(depth)<<currNode->parent());
304
PT_TRACE(QString("%1| ->pageStack: ").arg(depth)<<currNode->pageStack()
305
<<" custom:"<<((currNode->d_func()->m_flags & UCPageTreeNodePrivate::CustomPageStack) ? true : false));
306
PT_TRACE(QString("%1| ->propagated: ").arg(depth)<<currNode->propagated()
307
<<" custom:"<<((currNode->d_func()->m_flags & UCPageTreeNodePrivate::CustomPropagated) ? true : false));
308
PT_TRACE(QString("%1| ->active: ").arg(depth)<<currNode->active()
309
<<" custom:"<<((currNode->d_func()->m_flags & UCPageTreeNodePrivate::CustomActive) ? true : false));
310
PT_TRACE(QString("%1| ->activeLeaf: ").arg(depth)<<currNode->activeLeafNode());
312
if (n.m_children.length())
313
PT_TRACE(QString("%1| ->isLeaf: ").arg(depth)<<currNode->isLeaf());
315
PT_TRACE(QString("%1└ ->isLeaf: ").arg(depth)<<currNode->isLeaf());
317
//print the current nodes children
318
for (int i = 0; i < n.m_children.length(); i++) {
319
QString subDepth = depth;
321
if (i == n.m_children.length() - 1 ) //last
322
subDepth.append(" ");
324
subDepth.append("| ");
326
dumpNode(n.m_children.at(i), depth, subDepth, false);
331
* \brief UCPageTreeNodePrivate::dumpNodeTree
332
* Prints the complete node tree this node is part of.
333
* This recursively searches and prints the whole tree, so
334
* its pretty expensive. Do not leaf the calls to it after
335
* finishing the debugging.
337
void UCPageTreeNodePrivate::dumpNodeTree()
341
UCPageTreeNode *node = q;
342
UCPageTreeNode *rootNode = nullptr;
345
node = node->d_func()->getParentPageTreeNode();
348
PT_TRACE("Begin Node List for"<<q);
349
if (Q_UNLIKELY(!rootNode)) {
350
PT_TRACE("Empty tree");
353
root.m_node = rootNode;
354
root.m_children = collectNodes(rootNode);
357
PT_TRACE("End Node List\n");
362
\qmltype PageTreeNode
363
\inqmlmodule Ubuntu.Components 1.1
365
\brief The common parent of \l Page, \l MainView, \l PageStack and \l Tabs.
367
It is used to propagate properties such as \l header and \l toolbar from a
368
\l MainView (the root node) to each \l Page (leaf node) in the tree.
370
UCPageTreeNode::UCPageTreeNode(QQuickItem *parent)
371
: UCStyledItemBase(*(new UCPageTreeNodePrivate), parent)
376
UCPageTreeNode::UCPageTreeNode(UCPageTreeNodePrivate &dd, QQuickItem *parent)
377
: UCStyledItemBase(dd, parent)
382
void UCPageTreeNode::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
386
//update the parentNode property.
387
//Likely it changes together with the Items parent
388
UCStyledItemBase::itemChange(change, value);
389
if (change == QQuickItem::ItemParentHasChanged) {
395
\qmlproperty Item PageTreeNode::isLeaf
396
Whether or not this node is a leaf, that is if it has no descendant that are nodes.
397
Pages are leafs, and they don't have descendants in the PageTree.
399
bool UCPageTreeNode::isLeaf() const
401
return d_func()->m_isLeaf;
404
void UCPageTreeNode::setIsLeaf(bool isLeaf)
408
if (d->m_isLeaf == isLeaf)
411
d->m_isLeaf = isLeaf;
412
Q_EMIT isLeafChanged(isLeaf);
414
//notify all our parent nodes that we are the leaf
415
d->updateParentLeafNode();
419
\qmlproperty Item PageTreeNode::parentNode
420
The parent node of the current node in the page tree.
422
void UCPageTreeNode::setParentNode(UCPageTreeNode *parentNode)
426
if (d->m_parentNode == parentNode)
429
//disconnect from the old parent, we do not want to get
430
//false property updates
431
if (d->m_parentNode) {
432
if (!(d->m_flags & UCPageTreeNodePrivate::CustomActive)) {
433
disconnect(d->m_parentNode, SIGNAL(activeChanged(bool)),
434
this, SLOT(_q_activeBinding(bool)));
436
if (!(d->m_flags & UCPageTreeNodePrivate::CustomPageStack)) {
437
disconnect(d->m_parentNode,SIGNAL(pageStackChanged(QQuickItem*)),
438
this, SLOT(_q_pageStackBinding (QQuickItem *)));
440
if (!(d->m_flags & UCPageTreeNodePrivate::CustomPropagated)) {
441
disconnect(d->m_parentNode,SIGNAL(propagatedChanged(QObject*)),
442
this, SLOT(_q_propagatedBinding (QObject *)));
445
//the parent has changed, in case we or one of our children
446
//were the active leaf node
447
//make sure we are not anymore, since we are not part of that
449
if (d->m_parentNode->activeLeafNode() == this
450
|| d->m_parentNode->activeLeafNode() == activeLeafNode()) {
451
d->m_parentNode->setActiveLeafNode(nullptr);
455
d->m_parentNode = parentNode;
457
//connect to the property changes of the parent node so they
459
if (d->m_parentNode ) {
460
if (!(d->m_flags & UCPageTreeNodePrivate::CustomActive)) {
461
connect(d->m_parentNode, SIGNAL(activeChanged(bool)),
462
this, SLOT(_q_activeBinding(bool)));
464
if (!(d->m_flags & UCPageTreeNodePrivate::CustomPageStack)) {
465
connect(d->m_parentNode,SIGNAL(pageStackChanged(QQuickItem*)),
466
this, SLOT(_q_pageStackBinding (QQuickItem *)));
468
if (!(d->m_flags & UCPageTreeNodePrivate::CustomPropagated)) {
469
connect(d->m_parentNode,SIGNAL(propagatedChanged(QObject*)),
470
this, SLOT(_q_propagatedBinding (QObject *)));
474
//update properties if they are not set manually
475
if (!(d->m_flags & UCPageTreeNodePrivate::CustomActive))
476
d->_q_activeBinding (parentNode && parentNode->active() );
477
if (!(d->m_flags & UCPageTreeNodePrivate::CustomPageStack))
478
d->_q_pageStackBinding (parentNode ? parentNode->pageStack() : nullptr);
479
if (!(d->m_flags & UCPageTreeNodePrivate::CustomPropagated))
480
d->_q_propagatedBinding (parentNode ? parentNode->propagated() : nullptr);
482
Q_EMIT parentNodeChanged (parentNode);
485
UCPageTreeNode *UCPageTreeNode::parentNode() const
487
return d_func()->m_parentNode;
490
void UCPageTreeNode::componentComplete()
492
UCStyledItemBase::componentComplete();
493
d_func()->updatePageTree();
496
void UCPageTreeNode::dumpNodeTree()
498
d_func()->dumpNodeTree();
503
\qmlproperty Item PageTreeNode::__propagated
504
QtObject containing all the properties that are propagated from the
505
root (MainView) of a page tree to its leafs (Pages).
506
This object contains properties such as the header and toolbar that are
507
instantiated by the MainView.
509
This property is internal because the derived classes (MainView and Page)
510
need to access it, but other components using those classes should not have
513
QObject *UCPageTreeNode::propagated() const
515
return d_func()->m_propagated;
518
void UCPageTreeNode::setPropagated(QObject *propagated)
522
//remove the binding to parent
523
if (d->m_parentNode && !(d->m_flags & UCPageTreeNodePrivate::CustomPropagated))
524
disconnect(d->m_parentNode,SIGNAL(propagatedChanged(QObject*)),
525
this, SLOT(_q_propagatedBinding (QObject *)));
527
d->m_flags |= UCPageTreeNodePrivate::CustomPropagated;
528
d->_q_propagatedBinding(propagated);
532
\qmlproperty Item PageTreeNode::toolbar
534
The toolbar of the node. Propagates down from the root node.
535
This property is DEPRECATED.
537
QQuickItem *UCPageTreeNode::toolbar() const
539
return d_func()->m_toolbar;
542
void UCPageTreeNode::setToolbar(QQuickItem *toolbar)
545
if (d->m_toolbar == toolbar)
548
d->m_toolbar = toolbar;
549
Q_EMIT toolbarChanged(toolbar);
554
\qmlproperty Item PageTreeNode::__isPageTreeNode
555
Used to determine whether an Item is a PageTreeNode
557
bool UCPageTreeNode::isPageTreeNode() const
563
\qmlproperty Item PageTreeNode::activeLeafNode
564
The leaf node that is active.
566
void UCPageTreeNode::setActiveLeafNode(QQuickItem *activeLeafNode)
569
if (d->m_activeLeafNode == activeLeafNode)
572
d->m_activeLeafNode = activeLeafNode;
573
Q_EMIT activeLeafNodeChanged(activeLeafNode);
576
QQuickItem *UCPageTreeNode::activeLeafNode() const
578
return d_func()->m_activeLeafNode;
583
\qmlproperty Item PageTreeNode::active
584
At any time, there is exactly one path from the root node to a Page leaf node
585
where all nodes are active. All other nodes are not active. This is used by
586
\l Tabs and \l PageStack to determine which of multiple nodes in the Tabs or
587
PageStack is the currently active one.
589
void UCPageTreeNode::setActive(bool active)
593
//remove the binding to parent
594
if (d->m_parentNode && !(d->m_flags & UCPageTreeNodePrivate::CustomActive)) {
595
disconnect(d->m_parentNode, SIGNAL(activeChanged(bool)),
596
this, SLOT(_q_activeBinding(bool)));
599
d->m_flags |= UCPageTreeNodePrivate::CustomActive;
600
d->_q_activeBinding(active);
603
bool UCPageTreeNode::active() const
605
return d_func()->m_active;
609
\qmlproperty Item PageTreeNode::pageStack
610
The \l PageStack that this Page has been pushed on, or null if it is not
611
part of a PageStack. This value is automatically set for pages that are pushed
612
on a PageStack, and propagates to its child nodes.
614
// Note: pageStack is not included in the propagated property because there may
615
// be multiple PageStacks in a single page tree.
616
void UCPageTreeNode::setPageStack(QQuickItem *pageStack)
620
//remove the binding to parent
621
if (d->m_parentNode && !(d->m_flags & UCPageTreeNodePrivate::CustomPageStack))
622
disconnect(d->m_parentNode,SIGNAL(pageStackChanged(QQuickItem*)),
623
this, SLOT(_q_pageStackBinding (QQuickItem *)));
625
d->m_flags |= UCPageTreeNodePrivate::CustomPageStack;
626
d->_q_pageStackBinding(pageStack);
629
QQuickItem *UCPageTreeNode::pageStack() const
631
return d_func()->m_pageStack;
634
#include "moc_ucpagetreenode.cpp"