Merge lp:~zsombi/ubuntu-ui-toolkit/theming-attached into lp:ubuntu-ui-toolkit/staging

Proposed by Zsombor Egri on 2015-07-29
Status: Rejected
Rejected by: Christian Dywan on 2015-10-08
Proposed branch: lp:~zsombi/ubuntu-ui-toolkit/theming-attached
Merge into: lp:ubuntu-ui-toolkit/staging
Diff against target: 777 lines (+379/-209)
9 files modified
components.api (+2/-1)
src/Ubuntu/Components/plugin/plugin.cpp (+2/-1)
src/Ubuntu/Components/plugin/plugin.pri (+4/-2)
src/Ubuntu/Components/plugin/ucstyleditembase.cpp (+21/-184)
src/Ubuntu/Components/plugin/ucstyleditembase.h (+1/-3)
src/Ubuntu/Components/plugin/ucstyleditembase_p.h (+7/-14)
src/Ubuntu/Components/plugin/uctheme.cpp (+4/-4)
src/Ubuntu/Components/plugin/ucthemingattached.cpp (+257/-0)
src/Ubuntu/Components/plugin/ucthemingattached.h (+81/-0)
To merge this branch: bzr merge lp:~zsombi/ubuntu-ui-toolkit/theming-attached
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing on 2015-08-06
Christian Dywan (community) 2015-07-29 Approve on 2015-07-30
Review via email: mp+266317@code.launchpad.net

Commit message

Detaching theming management from StyledItem to be reused in other cpp types.

To post a comment you must log in.
Zsombor Egri (zsombi) wrote :

StyledItem was registered twice with the revision 2, so the 1.3 appeared twice in the API file. I fixed that as well.

1584. By Zsombor Egri on 2015-07-30

segfault fixed

Christian Dywan (kalikiana) wrote :

As discussed, there is no effective change in functionality in StyledItemBase so it's already covered by unit tests. We won't really know if it exposes the theming correctly until we have the first real word user of it, which is going to be the Label in C++ which can't currently inherit from StyledItemBase, so this should be the prerequisite branch.

review: Approve
1585. By Zsombor Egri on 2015-08-05

staging sync

1586. By Zsombor Egri on 2015-08-06

simplifying the use of theming

1587. By Zsombor Egri on 2015-08-06

staging sync

Unmerged revisions

1587. By Zsombor Egri on 2015-08-06

staging sync

1586. By Zsombor Egri on 2015-08-06

simplifying the use of theming

1585. By Zsombor Egri on 2015-08-05

staging sync

1584. By Zsombor Egri on 2015-07-30

segfault fixed

1583. By Zsombor Egri on 2015-07-30

renaming styling to theming

1582. By Zsombor Egri on 2015-07-30

API file fixed

1581. By Zsombor Egri on 2015-07-30

staging merge

1580. By Zsombor Egri on 2015-07-29

separation completed

1579. By Zsombor Egri on 2015-07-28

staging sync

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'components.api'
2--- components.api 2015-08-04 20:07:41 +0000
3+++ components.api 2015-08-06 18:06:52 +0000
4@@ -878,7 +878,7 @@
5 Ubuntu.Components.StateSaver 1.0 0.1: QtObject
6 Ubuntu.Components.StyleHints 1.3: QtObject
7 property bool ignoreUnknownProperties
8-Ubuntu.Components.StyledItem 1.3 1.3 1.1 1.0 0.1: Item
9+Ubuntu.Components.StyledItem 1.3 1.1 1.0 0.1: Item
10 property bool activeFocusOnPress 1.3
11 function bool requestFocus(Qt.FocusReason reason) 1.3
12 function bool requestFocus() 1.3
13@@ -1238,6 +1238,7 @@
14 UCStateSaverAttached: QtObject
15 property bool enabled
16 property string properties
17+UCThemingAttached: QtObject
18 Ubuntu.Components.UCUnits 1.0 0.1: QtObject
19 property float gridUnit
20 function float dp(float value)
21
22=== modified file 'src/Ubuntu/Components/plugin/plugin.cpp'
23--- src/Ubuntu/Components/plugin/plugin.cpp 2015-07-23 11:49:38 +0000
24+++ src/Ubuntu/Components/plugin/plugin.cpp 2015-08-06 18:06:52 +0000
25@@ -22,6 +22,7 @@
26
27 #include "plugin.h"
28 #include "uctheme.h"
29+#include "ucthemingattached.h"
30 #include "ucdeprecatedtheme.h"
31
32 #include <QtQml/QQmlContext>
33@@ -208,11 +209,11 @@
34 qmlRegisterType<UCUbuntuShapeOverlay>(uri, 1, 2, "UbuntuShapeOverlay");
35
36 // register 1.3 API
37+ qmlRegisterType<UCThemingAttached>();
38 qmlRegisterType<UCListItem13>(uri, 1, 3, "ListItem");
39 qmlRegisterType<UCTheme>(uri, 1, 3, "ThemeSettings");
40 qmlRegisterType<UCStyledItemBase, 2>(uri, 1, 3, "StyledItem");
41 qmlRegisterSingletonType<UCNamespaceV13>(uri, 1, 3, "Ubuntu", registerUbuntuNamespace13);
42- qmlRegisterType<UCStyledItemBase, 2>(uri, 1, 3, "StyledItem");
43 qmlRegisterCustomType<UCStyleHints>(uri, 1, 3, "StyleHints", new UCStyleHintsParser);
44 qmlRegisterType<UCAction, 1>(uri, 1, 3, "Action");
45 qmlRegisterType<UCUbuntuShape, 2>(uri, 1, 3, "UbuntuShape");
46
47=== modified file 'src/Ubuntu/Components/plugin/plugin.pri'
48--- src/Ubuntu/Components/plugin/plugin.pri 2015-07-30 09:57:45 +0000
49+++ src/Ubuntu/Components/plugin/plugin.pri 2015-08-06 18:06:52 +0000
50@@ -76,7 +76,8 @@
51 $$PWD/ucnamespace.h \
52 $$PWD/ucdeprecatedtheme.h \
53 $$PWD/ucdefaulttheme.h \
54- $$PWD/ucstylehints.h
55+ $$PWD/ucstylehints.h \
56+ $$PWD/ucthemingattached.h
57
58 SOURCES += $$PWD/plugin.cpp \
59 $$PWD/uctheme.cpp \
60@@ -125,7 +126,8 @@
61 $$PWD/ucnamespace.cpp \
62 $$PWD/ucdeprecatedtheme.cpp \
63 $$PWD/ucdefaulttheme.cpp \
64- $$PWD/ucstylehints.cpp
65+ $$PWD/ucstylehints.cpp \
66+ $$PWD/ucthemingattached.cpp
67
68 # adapters
69 SOURCES += $$PWD/adapters/alarmsadapter_organizer.cpp
70
71=== modified file 'src/Ubuntu/Components/plugin/ucstyleditembase.cpp'
72--- src/Ubuntu/Components/plugin/ucstyleditembase.cpp 2015-07-29 11:41:16 +0000
73+++ src/Ubuntu/Components/plugin/ucstyleditembase.cpp 2015-08-06 18:06:52 +0000
74@@ -18,18 +18,16 @@
75
76 #include "ucstyleditembase.h"
77 #include "ucstyleditembase_p.h"
78+#include "ucthemingattached.h"
79 #include "uctheme.h"
80 #include "ucstylehints.h"
81 #include <QtQml/QQmlEngine>
82 #include <QtQuick/private/qquickanchors_p.h>
83
84 UCStyledItemBasePrivate::UCStyledItemBasePrivate()
85- : activeFocusOnPress(false)
86- , styleComponent(0)
87- , styleItemContext(0)
88+ : styleComponent(0)
89 , styleItem(0)
90- , theme(0)
91- , parentStyledItem(0)
92+ , activeFocusOnPress(false)
93 {
94 }
95
96@@ -41,10 +39,6 @@
97 {
98 Q_Q(UCStyledItemBase);
99 q->setFlag(QQuickItem::ItemIsFocusScope);
100- QObject::connect(&UCTheme::defaultTheme(), SIGNAL(nameChanged()),
101- q, SLOT(_q_reloadStyle()));
102- QObject::connect(&UCTheme::defaultTheme(), SIGNAL(versionChanged()),
103- q, SLOT(_q_reloadStyle()));
104 }
105
106
107@@ -455,75 +449,14 @@
108 */
109 UCTheme *UCStyledItemBasePrivate::getTheme() const
110 {
111- if (theme) {
112- return theme;
113- } else if (!parentStyledItem.isNull()) {
114- return UCStyledItemBasePrivate::get(parentStyledItem)->getTheme();
115- }
116- return &UCTheme::defaultTheme();
117+ return theming ? theming->getTheme() : &UCTheme::defaultTheme();
118 }
119 void UCStyledItemBasePrivate::setTheme(UCTheme *newTheme)
120 {
121- Q_Q(UCStyledItemBase);
122- if (theme == newTheme) {
123+ if (theming && !theming->setTheme(newTheme)) {
124 return;
125 }
126
127- // preform pre-theme change tasks
128- preThemeChanged();
129-
130- // disconnect from the previous set
131- UCTheme *connectedSet = theme ?
132- theme :
133- (!parentStyledItem ? &UCTheme::defaultTheme() : NULL);
134- if (connectedSet) {
135- QObject::disconnect(connectedSet, SIGNAL(nameChanged()),
136- q, SLOT(_q_reloadStyle()));
137- QObject::disconnect(connectedSet, SIGNAL(versionChanged()),
138- q, SLOT(_q_reloadStyle()));
139- }
140-
141- UCTheme *prevSet = theme;
142-
143- // resolve new theme
144- if (theme && newTheme) {
145- // no need to redo the parentStack, simply set the theme and leave
146- theme = newTheme;
147- } else {
148- theme = newTheme;
149- if (!newTheme) {
150- // redo the parent chanin
151- disconnectTillItem(0);
152- connectParents(0);
153- }
154- }
155-
156- // connect to the new set
157- connectedSet = theme ?
158- theme :
159- (!parentStyledItem ? &UCTheme::defaultTheme() : NULL);
160- if (connectedSet) {
161- QObject::connect(connectedSet, SIGNAL(nameChanged()),
162- q, SLOT(_q_reloadStyle()));
163- QObject::connect(connectedSet, SIGNAL(versionChanged()),
164- q, SLOT(_q_reloadStyle()));
165- }
166- // detach previous set and attach the new one
167- if (prevSet) {
168- Q_EMIT prevSet->parentThemeChanged();
169- }
170- if (theme) {
171- // re-parent theme to make sure we have it
172- // for the entire lifetime of the styled item
173- theme->setParent(q);
174- Q_EMIT theme->parentThemeChanged();
175- }
176-
177- // perform post-theme changes, update internal styling
178- postThemeChanged();
179-
180- Q_EMIT q->themeChanged();
181-
182 // perform style reload
183 if (!styleComponent) {
184 preStyleChanged();
185@@ -533,102 +466,22 @@
186 }
187 void UCStyledItemBasePrivate::resetTheme()
188 {
189- setTheme(NULL);
190-}
191-
192-// link/unlink all ascendant items until we reach a StyledItem, returns true if the
193-// theme change signal emission is justified
194-bool UCStyledItemBasePrivate::connectParents(QQuickItem *fromItem)
195-{
196- Q_Q(UCStyledItemBase);
197- QQuickItem *item = fromItem ? fromItem : parentItem;
198- while (item) {
199- // push the item onto the stack
200- parentStack.push(QPointer<QQuickItem>(item));
201- UCStyledItemBase *styledItem = qobject_cast<UCStyledItemBase*>(item);
202- if (styledItem) {
203- // this is the closest StyledItem, connect its themeChanged() signal
204- QObject::connect(styledItem, SIGNAL(themeChanged()),
205- q, SLOT(_q_parentStyleChanged()), Qt::DirectConnection);
206- // set the current style set to the one in the parent's one if differs
207- return setParentStyled(styledItem);
208- } else {
209- // connect to the item's parentChanged() signal so we can detect when the parent changes
210- QObject::connect(item, SIGNAL(parentChanged(QQuickItem*)),
211- q, SLOT(_q_ascendantChanged(QQuickItem*)), Qt::DirectConnection);
212- }
213- item = item->parentItem();
214- }
215- return false;
216-}
217-
218-// set the used parent styled item's style; returns true if the parent styled got changed
219-bool UCStyledItemBasePrivate::setParentStyled(UCStyledItemBase *styledItem)
220-{
221- if (parentStyledItem == styledItem) {
222- return false;
223- }
224- parentStyledItem = styledItem;
225- if (theme) {
226- Q_EMIT theme->parentThemeChanged();
227- }
228- return (theme == NULL);
229-}
230-
231-// disconnect parent stack till item is reached; all the stack if item == 0
232-void UCStyledItemBasePrivate::disconnectTillItem(QQuickItem *item)
233-{
234- Q_Q(UCStyledItemBase);
235- while (!parentStack.isEmpty() && item != parentStack.top()) {
236- QPointer<QQuickItem> stackItem = parentStack.pop();
237- // the topmost item can be the only one which is a StyledItem
238- UCStyledItemBase *styledItem = qobject_cast<UCStyledItemBase*>(stackItem.data());
239- if (styledItem) {
240- QObject::disconnect(styledItem, SIGNAL(themeChanged()),
241- q, SLOT(_q_parentStyleChanged()));
242- } else if (!stackItem.isNull()) {
243- QObject::disconnect(stackItem.data(), SIGNAL(parentChanged(QQuickItem*)),
244- q, SLOT(_q_ascendantChanged(QQuickItem*)));
245- }
246- }
247-}
248-
249-// captures ascendant change signal, the sender is the one which counts!
250-void UCStyledItemBasePrivate::_q_ascendantChanged(QQuickItem *ascendant)
251-{
252- Q_Q(UCStyledItemBase);
253- QQuickItem *sender = static_cast<QQuickItem*>(q->sender());
254- if (!sender) {
255- // cannot detect the sender, leave!
256- return;
257- }
258- if (ascendant) {
259- // disconnect from the previous ones
260- disconnectTillItem(sender);
261- parentStyledItem.clear();
262- // traverse ascendants till we reach a StyledItem or root and push them into the stack
263- if (connectParents(ascendant)) {
264- Q_EMIT q->themeChanged();
265- }
266- }
267-}
268-
269-// syncs the ascendant StyledItem's style
270-void UCStyledItemBasePrivate::_q_parentStyleChanged()
271-{
272- // do not trigger themeChanged() on this item if we have a
273- // custom one, but resolve its eventual parent change!
274- if (theme) {
275- Q_EMIT theme->parentThemeChanged();
276- return;
277- }
278- Q_Q(UCStyledItemBase);
279- UCStyledItemBase *styledItem = static_cast<UCStyledItemBase*>(q->sender());
280- if (!styledItem) {
281- return;
282- }
283- setParentStyled(styledItem);
284- Q_EMIT q->themeChanged();
285+ theming->setTheme(Q_NULLPTR);
286+}
287+
288+
289+void UCStyledItemBase::classBegin()
290+{
291+ QQuickItem::classBegin();
292+
293+ Q_D(UCStyledItemBase);
294+ // attach theming
295+ d->theming = UCThemingAttached::attachTheming(this);
296+ if (d->theming) {
297+ d->theming->setListener(d);
298+ connect(d->theming.data(), SIGNAL(themeChanged()),
299+ this, SLOT(_q_reloadStyle()));
300+ }
301 }
302
303 void UCStyledItemBase::componentComplete()
304@@ -667,20 +520,4 @@
305 return QQuickItem::childMouseEventFilter(child, event);
306 }
307
308-// catch parent change event so we can lookup for the parent chain theme
309-void UCStyledItemBase::itemChange(ItemChange change, const ItemChangeData &data)
310-{
311- QQuickItem::itemChange(change, data);
312- if (change == ItemParentHasChanged) {
313- Q_D(UCStyledItemBase);
314- // clean stack
315- d->disconnectTillItem(0);
316- // make sure we reset parent StyledItem
317- d->parentStyledItem.clear();
318- // build the stack - if possible
319- d->connectParents(0);
320- Q_EMIT themeChanged();
321- }
322-}
323-
324 #include "moc_ucstyleditembase.cpp"
325
326=== modified file 'src/Ubuntu/Components/plugin/ucstyleditembase.h'
327--- src/Ubuntu/Components/plugin/ucstyleditembase.h 2015-06-02 12:37:41 +0000
328+++ src/Ubuntu/Components/plugin/ucstyleditembase.h 2015-08-06 18:06:52 +0000
329@@ -53,16 +53,14 @@
330 protected:
331 UCStyledItemBase(UCStyledItemBasePrivate &, QQuickItem *parent);
332
333+ void classBegin();
334 void componentComplete();
335 void mousePressEvent(QMouseEvent *event);
336 bool childMouseEventFilter(QQuickItem *child, QEvent *event);
337- void itemChange(ItemChange, const ItemChangeData &);
338
339 private:
340 Q_DECLARE_PRIVATE(UCStyledItemBase)
341 Q_PRIVATE_SLOT(d_func(), void _q_styleResized())
342- Q_PRIVATE_SLOT(d_func(), void _q_ascendantChanged(QQuickItem*))
343- Q_PRIVATE_SLOT(d_func(), void _q_parentStyleChanged())
344 Q_PRIVATE_SLOT(d_func(), void _q_reloadStyle())
345 };
346
347
348=== modified file 'src/Ubuntu/Components/plugin/ucstyleditembase_p.h'
349--- src/Ubuntu/Components/plugin/ucstyleditembase_p.h 2015-06-02 12:37:41 +0000
350+++ src/Ubuntu/Components/plugin/ucstyleditembase_p.h 2015-08-06 18:06:52 +0000
351@@ -21,10 +21,11 @@
352
353 #include <QtQuick/private/qquickitem_p.h>
354 #include "ucstyleditembase.h"
355+#include "ucthemingattached.h"
356
357 class QQuickMouseArea;
358 class UCStyledItemBase;
359-class UCStyledItemBasePrivate : public QQuickItemPrivate
360+class UCStyledItemBasePrivate : public QQuickItemPrivate, public UCThemeChangeListener
361 {
362 Q_DECLARE_PUBLIC(UCStyledItemBase)
363 public:
364@@ -35,8 +36,6 @@
365
366 void _q_reloadStyle();
367 void _q_styleResized();
368- void _q_ascendantChanged(QQuickItem *ascendant);
369- void _q_parentStyleChanged();
370
371 UCStyledItemBasePrivate();
372 virtual ~UCStyledItemBasePrivate();
373@@ -61,25 +60,19 @@
374 void setTheme(UCTheme *theme);
375 void resetTheme();
376
377- virtual void preThemeChanged(){}
378- virtual void postThemeChanged(){}
379+ void preThemeChanged(){}
380+ void postThemeChanged(){}
381
382 public:
383- bool activeFocusOnPress:1;
384 QString styleDocument;
385+ QPointer<UCThemingAttached> theming;
386+ QPointer<QQmlContext> styleItemContext;
387 QQmlComponent *styleComponent;
388- QPointer<QQmlContext> styleItemContext;
389 QQuickItem *styleItem;
390- UCTheme *theme;
391- QPointer<UCStyledItemBase> parentStyledItem;
392+ bool activeFocusOnPress:1;
393
394 protected:
395- QStack< QPointer<QQuickItem> > parentStack;
396-
397 void connectStyleSizeChanges(bool attach);
398- bool connectParents(QQuickItem *fromItem);
399- bool setParentStyled(UCStyledItemBase *styledItem);
400- void disconnectTillItem(QQuickItem *item);
401 };
402
403 #endif // UCSTYLEDITEMBASE_P_H
404
405=== modified file 'src/Ubuntu/Components/plugin/uctheme.cpp'
406--- src/Ubuntu/Components/plugin/uctheme.cpp 2015-07-20 12:56:17 +0000
407+++ src/Ubuntu/Components/plugin/uctheme.cpp 2015-08-06 18:06:52 +0000
408@@ -24,6 +24,7 @@
409 #include "i18n.h"
410 #include "ucfontutils.h"
411 #include "ucstyleditembase_p.h"
412+#include "ucthemingattached.h"
413
414 #include <QtQml/qqml.h>
415 #include <QtQml/qqmlinfo.h>
416@@ -422,10 +423,9 @@
417 */
418 UCTheme *UCTheme::parentTheme()
419 {
420- UCStyledItemBase *owner = qobject_cast<UCStyledItemBase*>(parent());
421- UCStyledItemBasePrivate *pOwner = owner ? UCStyledItemBasePrivate::get(owner) : NULL;
422- if (pOwner && pOwner->theme == this && pOwner->parentStyledItem) {
423- return UCStyledItemBasePrivate::get(pOwner->parentStyledItem)->getTheme();
424+ UCThemingAttached *theming = itemTheming(static_cast<QQuickItem*>(parent()));
425+ if (theming && theming->m_theme == this && theming->m_parentTheming) {
426+ return theming->m_parentTheming->getTheme();
427 }
428 return NULL;
429 }
430
431=== added file 'src/Ubuntu/Components/plugin/ucthemingattached.cpp'
432--- src/Ubuntu/Components/plugin/ucthemingattached.cpp 1970-01-01 00:00:00 +0000
433+++ src/Ubuntu/Components/plugin/ucthemingattached.cpp 2015-08-06 18:06:52 +0000
434@@ -0,0 +1,257 @@
435+/*
436+ * Copyright 2015 Canonical Ltd.
437+ *
438+ * This program is free software; you can redistribute it and/or modify
439+ * it under the terms of the GNU Lesser General Public License as published by
440+ * the Free Software Foundation; version 3.
441+ *
442+ * This program is distributed in the hope that it will be useful,
443+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
444+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
445+ * GNU Lesser General Public License for more details.
446+ *
447+ * You should have received a copy of the GNU Lesser General Public License
448+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
449+ *
450+ * Author: Zsombor Egri <zsombor.egri@canonical.com>
451+ */
452+
453+#include "ucthemingattached.h"
454+#include <QtQuick/QQuickItem>
455+#include "uctheme.h"
456+
457+/*
458+ * UCThemingAttached is an attached object to all themable components. It is used
459+ * internally by those components which need theme access thru the theme property.
460+ * Components which want to use this should do the following:
461+ * - attach the object in classBegin() function
462+ * - expose a theme property
463+ * - getter should use the getTheme() attached function
464+ * - setter/reset should use the setTheme() attached function
465+ * - themeChanged() signal must be connected to the property change signal
466+ *
467+ * The theme change is notified through listener callbacks. Registering listener
468+ * is not mandatory.
469+ */
470+
471+UCThemingAttached *itemTheming(QQuickItem *item)
472+{
473+ return qobject_cast<UCThemingAttached*>(
474+ qmlAttachedPropertiesObject<UCThemingAttached>(item, false));
475+}
476+
477+UCThemingAttached::UCThemingAttached(QObject *parent)
478+ : QObject(parent)
479+ , m_theme(Q_NULLPTR)
480+ , m_listener(Q_NULLPTR)
481+ , m_ownerItem(static_cast<QQuickItem*>(parent))
482+{
483+}
484+
485+UCThemingAttached *UCThemingAttached::qmlAttachedProperties(QObject *owner)
486+{
487+ UCThemingAttached *theming = new UCThemingAttached(owner);
488+ // the theme is the default one at this stage, so connect all the necessary signals
489+ // owner must have theme signal
490+ theming->connectThemeSignals(theming->getTheme(), true);
491+ // connect parentChanged of owner to know when its parent is changed
492+ connect(theming->m_ownerItem, &QQuickItem::parentChanged,
493+ theming, &UCThemingAttached::itemParentChanged, Qt::DirectConnection);
494+ return theming;
495+}
496+
497+UCThemingAttached *UCThemingAttached::attachTheming(QObject *owner)
498+{
499+ return qobject_cast<UCThemingAttached*>(
500+ qmlAttachedPropertiesObject<UCThemingAttached>(owner, true));
501+}
502+
503+void UCThemingAttached::connectThemeSignals(UCTheme *theme, bool connect)
504+{
505+ if (connect) {
506+ QObject::connect(theme, SIGNAL(nameChanged()), this, SIGNAL(themeChanged()), Qt::DirectConnection);
507+ QObject::connect(theme, SIGNAL(versionChanged()), this, SIGNAL(themeChanged()), Qt::DirectConnection);
508+ } else {
509+ QObject::disconnect(theme, SIGNAL(nameChanged()), this, SIGNAL(themeChanged()));
510+ QObject::disconnect(theme, SIGNAL(versionChanged()), this, SIGNAL(themeChanged()));
511+ }
512+}
513+
514+// link/unlink all ascendant items until we reach a StyledItem, returns true if the
515+// theme change signal emission is justified
516+bool UCThemingAttached::connectParents(QQuickItem *fromItem)
517+{
518+ QQuickItem *item = fromItem ? fromItem : m_ownerItem->parentItem();
519+ while (item) {
520+ // push the item onto the stack
521+ m_parentStack.push(QPointer<QQuickItem>(item));
522+ UCThemingAttached *styling = itemTheming(item);
523+ if (styling) {
524+ // this is the closest StyledItem, connect its themeChanged() signal
525+ QObject::connect(styling, SIGNAL(themeChanged()),
526+ this, SLOT(parentStyleChanged()), Qt::DirectConnection);
527+ // set the current style set to the one in the parent's one if differs
528+ return setParentStyled(styling);
529+ } else {
530+ // connect to the item's parentChanged() signal so we can detect when the parent changes
531+ QObject::connect(item, SIGNAL(parentChanged(QQuickItem*)),
532+ this, SLOT(ascendantChanged(QQuickItem*)), Qt::DirectConnection);
533+ }
534+ item = item->parentItem();
535+ }
536+ return false;
537+}
538+
539+// disconnect parent stack till item is reached; all the stack if item == 0
540+void UCThemingAttached::disconnectTillItem(QQuickItem *item)
541+{
542+ while (!m_parentStack.isEmpty() && item != m_parentStack.top()) {
543+ QPointer<QQuickItem> stackItem = m_parentStack.pop();
544+ // the topmost item can be the only one which is a StyledItem
545+ UCThemingAttached *styling = itemTheming(stackItem.data());
546+ if (styling) {
547+ QObject::disconnect(styling, SIGNAL(themeChanged()),
548+ this, SLOT(parentStyleChanged()));
549+ // clear parent styling as well
550+ if (styling == m_parentTheming) {
551+ m_parentTheming.clear();
552+ }
553+ } else if (!stackItem.isNull()) {
554+ QObject::disconnect(stackItem.data(), SIGNAL(parentChanged(QQuickItem*)),
555+ this, SLOT(ascendantChanged(QQuickItem*)));
556+ }
557+ }
558+}
559+
560+// set the used parent styled item's style; returns true if the parent styled got changed
561+bool UCThemingAttached::setParentStyled(UCThemingAttached *newStyling)
562+{
563+ if (m_parentTheming == newStyling) {
564+ return false;
565+ }
566+ m_parentTheming = newStyling;
567+ if (m_theme) {
568+ Q_EMIT themeChanged();
569+ }
570+ return (m_theme == NULL);
571+}
572+
573+UCTheme *UCThemingAttached::getTheme()
574+{
575+ if (m_theme) {
576+ return m_theme;
577+ } else if (!m_parentTheming.isNull()) {
578+ return m_parentTheming->getTheme();
579+ }
580+ return &UCTheme::defaultTheme();
581+}
582+
583+bool UCThemingAttached::setTheme(UCTheme *newTheme)
584+{
585+ if (m_theme == newTheme) {
586+ return false;
587+ }
588+
589+ // preform pre-theme change tasks
590+ if (m_listener) {
591+ m_listener->preThemeChanged();
592+ }
593+
594+ // disconnect from the previous set
595+ UCTheme *connectedSet = m_theme ?
596+ m_theme :
597+ (!m_parentTheming ? &UCTheme::defaultTheme() : NULL);
598+ if (connectedSet) {
599+ connectThemeSignals(connectedSet, false);
600+ }
601+
602+ UCTheme *prevSet = m_theme;
603+
604+ // resolve new theme
605+ if (m_theme && newTheme) {
606+ // no need to redo the parentStack, simply set the theme and leave
607+ m_theme = newTheme;
608+ } else {
609+ m_theme = newTheme;
610+ if (!newTheme) {
611+ // redo the parent chanin
612+ disconnectTillItem(0);
613+ connectParents(0);
614+ }
615+ }
616+
617+ // connect to the new set
618+ connectedSet = m_theme ?
619+ m_theme :
620+ (!m_parentTheming ? &UCTheme::defaultTheme() : NULL);
621+ if (connectedSet) {
622+ connectThemeSignals(connectedSet, true);
623+ }
624+ // detach previous set and attach the new one
625+ if (prevSet) {
626+ Q_EMIT prevSet->parentThemeChanged();
627+ }
628+ if (m_theme) {
629+ // re-parent theme to make sure we have it
630+ // for the entire lifetime of the styled item
631+ m_theme->setParent(parent());
632+ Q_EMIT m_theme->parentThemeChanged();
633+ }
634+
635+ // perform post-theme changes, update internal styling
636+ if (m_listener) {
637+ m_listener->postThemeChanged();
638+ }
639+
640+ Q_EMIT themeChanged();
641+ return true;
642+}
643+
644+// lookup for the parent chain theme
645+void UCThemingAttached::itemParentChanged()
646+{
647+ // clean stack
648+ disconnectTillItem(0);
649+ // make sure we reset parent StyledItem
650+ m_parentTheming.clear();
651+ // build the stack - if possible
652+ connectParents(0);
653+ Q_EMIT themeChanged();
654+}
655+
656+// captures ascendant change signal, the sender is the one which counts!
657+void UCThemingAttached::ascendantChanged(QQuickItem *ascendant)
658+{
659+ QQuickItem *sender = static_cast<QQuickItem*>(this->sender());
660+ if (!sender) {
661+ // cannot detect the sender, leave!
662+ return;
663+ }
664+ if (ascendant) {
665+ // disconnect from the previous ones
666+ disconnectTillItem(sender);
667+ m_parentTheming.clear();
668+ // traverse ascendants till we reach a StyledItem or root and push them into the stack
669+ if (connectParents(ascendant)) {
670+ Q_EMIT themeChanged();
671+ }
672+ }
673+}
674+
675+// syncs the ascendant styled item's styles
676+void UCThemingAttached::parentStyleChanged()
677+{
678+ // do not trigger themeChanged() on this item if we have a
679+ // custom one, but resolve its eventual parent change!
680+ if (m_theme) {
681+ Q_EMIT m_theme->parentThemeChanged();
682+ return;
683+ }
684+
685+ UCThemingAttached *styling = static_cast<UCThemingAttached*>(sender());
686+ if (!styling) {
687+ return;
688+ }
689+ setParentStyled(styling);
690+ Q_EMIT themeChanged();
691+}
692
693=== added file 'src/Ubuntu/Components/plugin/ucthemingattached.h'
694--- src/Ubuntu/Components/plugin/ucthemingattached.h 1970-01-01 00:00:00 +0000
695+++ src/Ubuntu/Components/plugin/ucthemingattached.h 2015-08-06 18:06:52 +0000
696@@ -0,0 +1,81 @@
697+/*
698+ * Copyright 2015 Canonical Ltd.
699+ *
700+ * This program is free software; you can redistribute it and/or modify
701+ * it under the terms of the GNU Lesser General Public License as published by
702+ * the Free Software Foundation; version 3.
703+ *
704+ * This program is distributed in the hope that it will be useful,
705+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
706+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
707+ * GNU Lesser General Public License for more details.
708+ *
709+ * You should have received a copy of the GNU Lesser General Public License
710+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
711+ *
712+ * Author: Zsombor Egri <zsombor.egri@canonical.com>
713+ */
714+
715+#ifndef UCTHEMINGATTACHED_H
716+#define UCTHEMINGATTACHED_H
717+
718+#include <QtCore/QObject>
719+#include <QtCore/QPointer>
720+#include <QtQml>
721+
722+class UCTheme;
723+class QQuickItem;
724+
725+class UCThemeChangeListener {
726+public:
727+ virtual void preThemeChanged() = 0;
728+ virtual void postThemeChanged() = 0;
729+};
730+
731+class UCThemingAttached : public QObject
732+{
733+ Q_OBJECT
734+public:
735+ explicit UCThemingAttached(QObject *parent = 0);
736+
737+ static UCThemingAttached *qmlAttachedProperties(QObject *owner);
738+ static UCThemingAttached *attachTheming(QObject *owner);
739+
740+ void setListener(UCThemeChangeListener *listener)
741+ {
742+ m_listener = listener;
743+ }
744+ UCTheme *getTheme();
745+ bool setTheme(UCTheme *theme);
746+
747+Q_SIGNALS:
748+ void themeChanged();
749+
750+public Q_SLOTS:
751+
752+ void ascendantChanged(QQuickItem *ascendant);
753+ void parentStyleChanged();
754+ void itemParentChanged();
755+
756+protected:
757+ friend class UCTheme;
758+
759+ QStack< QPointer<QQuickItem> > m_parentStack;
760+ QPointer<UCThemingAttached> m_parentTheming;
761+ UCTheme *m_theme;
762+ UCThemeChangeListener *m_listener;
763+ QQuickItem *m_ownerItem;
764+
765+ void connectThemeSignals(UCTheme *theme, bool connect);
766+
767+ bool connectParents(QQuickItem *fromItem);
768+ void disconnectTillItem(QQuickItem *item);
769+ bool setParentStyled(UCThemingAttached *newStyling);
770+
771+};
772+QML_DECLARE_TYPEINFO(UCThemingAttached, QML_HAS_ATTACHED_PROPERTIES)
773+
774+UCThemingAttached *itemTheming(QQuickItem *item);
775+
776+
777+#endif // UCTHEMINGATTACHED_H

Subscribers

People subscribed via source and target branches