Merge lp:~zsombi/ubuntu-ui-toolkit/33-listitem-attached into lp:~zsombi/ubuntu-ui-toolkit/listitem-master
- 33-listitem-attached
- Merge into listitem-master
Status: | Merged |
---|---|
Approved by: | Zsombor Egri |
Approved revision: | 1329 |
Merged at revision: | 1270 |
Proposed branch: | lp:~zsombi/ubuntu-ui-toolkit/33-listitem-attached |
Merge into: | lp:~zsombi/ubuntu-ui-toolkit/listitem-master |
Prerequisite: | lp:~zsombi/ubuntu-ui-toolkit/32-listitemactions-attached |
Diff against target: |
582 lines (+406/-20) 8 files modified
components.api (+1/-0) modules/Ubuntu/Components/plugin/plugin.pro (+5/-2) modules/Ubuntu/Components/plugin/propertychange_p.cpp (+76/-0) modules/Ubuntu/Components/plugin/propertychange_p.h (+39/-0) modules/Ubuntu/Components/plugin/uclistitem.cpp (+30/-18) modules/Ubuntu/Components/plugin/uclistitem.h (+25/-0) modules/Ubuntu/Components/plugin/uclistitem_p.h (+23/-0) modules/Ubuntu/Components/plugin/uclistitemattached.cpp (+207/-0) |
To merge this branch: | bzr merge lp:~zsombi/ubuntu-ui-toolkit/33-listitem-attached |
Related bugs: | |
Related blueprints: |
SDK: Design a new ListItem and layouts
(Undefined)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tim Peeters | Approve | ||
Zsombor Egri | Pending | ||
Review via email: mp+240836@code.launchpad.net |
Commit message
Description of the change
Introducing ListItem attached to ListItem's parent item, or to ListView. Yet handles ascending Flickables, further functionality will be introduced in later MRs.
- 1319. By Zsombor Egri
-
small fixes
- 1320. By Zsombor Egri
-
prereq sync
- 1321. By Zsombor Egri
-
minor adjustments on global flickable interactive handling
- 1322. By Zsombor Egri
-
fix orphan disconnect warnings
- 1323. By Zsombor Egri
-
prereq sync
- 1324. By Zsombor Egri
-
prereq sync
- 1325. By Zsombor Egri
-
prereq sync
- 1326. By Zsombor Egri
-
prereq sync
- 1327. By Zsombor Egri
-
redo binding and property change as flickables may be added to the parent change while initialiozation completes
- 1328. By Zsombor Egri
-
typo fixed
Tim Peeters (tpeeters) wrote : | # |
Tim Peeters (tpeeters) wrote : | # |
537 +void UCListItemAttac
maybe it is a matter of taste, but here you have to set the bool to TRUE in order to DISABLE interactive. Wouldn't be the other way around (enableInteract
Tim Peeters (tpeeters) wrote : | # |
540 + if ((d->globalDisabled && disable) || (!d->globalDisabled && disable)) {
this is the same as: if (disable) {
Tim Peeters (tpeeters) wrote : | # |
540 + if ((d->globalDisabled && disable) || (!d->globalDisabled && disable)) {
541 + // disabling or re-disabling
542 + d->disablerItem = item;
543 + if (d->globalDisabled == disable) {
544 + // was already disabled, leave
545 + return;
546 + }
547 + d->globalDisabled = disable;
548 + } else if (d->globalDisabled && !disable && d->disablerItem == item) {
549 + // the one disabled it will enable
550 + d->globalDisabled = disable;
551 + d->disablerItem
552 + } else {
553 + // none of the above, leave
554 + return;
555 + }
same as code below, which is simpler:
if (disable) {
d->disablerItem = item; // what is this for?
if (d->globalDisabled) {
return;
}
} else if (d->globalDisabled && item == d->disablerItem) {
d->globalDisabled = false;
d->disablerIt
} else {
// !disabled && (!globalDisabled || item != d->disablerItem)
return;
}
Tim Peeters (tpeeters) wrote : | # |
178 +void UCListItemPriva
Maybe immediateRebound() is more clear?
Or add a "// rebound without animation"
Tim Peeters (tpeeters) wrote : | # |
427 +}
428 +// connect all flickables
add empty line inbetween these two
Tim Peeters (tpeeters) wrote : | # |
354 + void clearFlickableL
355 + void buildFlickables
rename the first function to clearFlickables
Zsombor Egri (zsombi) wrote : | # |
> 537 +void UCListItemAttac
> disable)
>
> maybe it is a matter of taste, but here you have to set the bool to TRUE in
> order to DISABLE interactive. Wouldn't be the other way around
> (enableInteract
Well, as you said, it's a matter of taste :) The main purpose of the whole logic is to disable, not to enable. But it all depends on what naming convention do we keep in mind: positive naming, or negative naming. And in that sense you are right, a positive approach would fit better.
Tim Peeters (tpeeters) wrote : | # |
490 +// register item to be rebount
rebounD, I think..
Tim Peeters (tpeeters) wrote : | # |
493 + // we cannot bind the item until we have an other one bount
everywhere (also variable names): bount --> bound
Zsombor Egri (zsombi) wrote : | # |
> > 537 +void UCListItemAttac
> > disable)
> >
> > maybe it is a matter of taste, but here you have to set the bool to TRUE in
> > order to DISABLE interactive. Wouldn't be the other way around
> > (enableInteract
>
> Well, as you said, it's a matter of taste :) The main purpose of the whole
> logic is to disable, not to enable. But it all depends on what naming
> convention do we keep in mind: positive naming, or negative naming. And in
> that sense you are right, a positive approach would fit better.
Ah, the comment already went out. I was to say that I won't change it here :)
Zsombor Egri (zsombi) wrote : | # |
> 540 + if ((d->globalDisabled && disable) || (!d->globalDisabled &&
> disable)) {
> 541 + // disabling or re-disabling
> 542 + d->disablerItem = item;
> 543 + if (d->globalDisabled == disable) {
> 544 + // was already disabled, leave
> 545 + return;
> 546 + }
> 547 + d->globalDisabled = disable;
> 548 + } else if (d->globalDisabled && !disable && d->disablerItem ==
> item) {
> 549 + // the one disabled it will enable
> 550 + d->globalDisabled = disable;
> 551 + d->disablerItem
> 552 + } else {
> 553 + // none of the above, leave
> 554 + return;
> 555 + }
>
> same as code below, which is simpler:
>
> if (disable) {
> d->disablerItem = item; // what is this for?
> if (d->globalDisabled) {
> return;
> }
> } else if (d->globalDisabled && item == d->disablerItem) {
> d->globalDisabled = false;
> d->disablerItem
> } else {
> // !disabled && (!globalDisabled || item != d->disablerItem)
> return;
> }
Right, changed it.
The disablerItem is there to know that we should re-enable the interactive flag only if the same item which disabled requests it. Example, you have a Flickable with ListItems, all having their own ListItemAction declared (not a shared one!!), one ListItem swiped in. You start to swipe an other one, in that moment the previous one will initiate rebounding, and the one you swiped will lock the Flickable and start swiping. At this point this will be the one who requested the lock. The other one finishes the rebound animation, and as part of the animation end, the lock is released. But that should not happen as you may still be dragging the other ListItem, right? So the member makes sure that we only unlock the Flickable when the one locking it requests it.
Zsombor Egri (zsombi) wrote : | # |
> 178 +void UCListItemPriva
>
> Maybe immediateRebound() is more clear?
> Or add a "// rebound without animation"
comment added
- 1329. By Zsombor Egri
-
review comments applied
Zsombor Egri (zsombi) wrote : | # |
All comments addressed
- 1330. By Zsombor Egri
-
prereq sync
Preview Diff
1 | === modified file 'components.api' |
2 | --- components.api 2014-11-19 15:10:32 +0000 |
3 | +++ components.api 2014-11-19 15:10:34 +0000 |
4 | @@ -902,6 +902,7 @@ |
5 | Method { |
6 | name: "snapToPosition" |
7 | Parameter { name: "position"; type: "double" } |
8 | + Component { name: "UCListItemAttached"; prototype: "QObject" } |
9 | name: "UCListItemDivider" |
10 | prototype: "QObject" |
11 | Property { name: "visible"; type: "bool" } |
12 | |
13 | === modified file 'modules/Ubuntu/Components/plugin/plugin.pro' |
14 | --- modules/Ubuntu/Components/plugin/plugin.pro 2014-11-19 15:10:32 +0000 |
15 | +++ modules/Ubuntu/Components/plugin/plugin.pro 2014-11-19 15:10:34 +0000 |
16 | @@ -72,7 +72,8 @@ |
17 | uclistitem.h \ |
18 | uclistitem_p.h \ |
19 | uclistitemactions.h \ |
20 | - uclistitemactions_p.h |
21 | + uclistitemactions_p.h \ |
22 | + propertychange_p.h |
23 | |
24 | SOURCES += plugin.cpp \ |
25 | uctheme.cpp \ |
26 | @@ -112,7 +113,9 @@ |
27 | adapters/actionsproxy_p.cpp \ |
28 | uclistitem.cpp \ |
29 | uclistitemactions.cpp \ |
30 | - uclistitemactionsattached.cpp |
31 | + uclistitemactionsattached.cpp \ |
32 | + uclistitemattached.cpp \ |
33 | + propertychange_p.cpp |
34 | |
35 | # adapters |
36 | SOURCES += adapters/alarmsadapter_organizer.cpp |
37 | |
38 | === added file 'modules/Ubuntu/Components/plugin/propertychange_p.cpp' |
39 | --- modules/Ubuntu/Components/plugin/propertychange_p.cpp 1970-01-01 00:00:00 +0000 |
40 | +++ modules/Ubuntu/Components/plugin/propertychange_p.cpp 2014-11-19 15:10:34 +0000 |
41 | @@ -0,0 +1,76 @@ |
42 | +/* |
43 | + * Copyright 2014 Canonical Ltd. |
44 | + * |
45 | + * This program is free software; you can redistribute it and/or modify |
46 | + * it under the terms of the GNU Lesser General Public License as published by |
47 | + * the Free Software Foundation; version 3. |
48 | + * |
49 | + * This program is distributed in the hope that it will be useful, |
50 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
51 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
52 | + * GNU Lesser General Public License for more details. |
53 | + * |
54 | + * You should have received a copy of the GNU Lesser General Public License |
55 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
56 | + */ |
57 | + |
58 | +#include "propertychange_p.h" |
59 | + |
60 | +#include <QtQml/private/qqmlabstractbinding_p.h> |
61 | +#define foreach Q_FOREACH //workaround to fix private includes |
62 | +#include <QtQml/private/qqmlbinding_p.h> // for QmlBinding |
63 | +#undef foreach |
64 | + |
65 | +/* |
66 | + * The class is used to save properties and their bindings while the property is |
67 | + * altered temporarily. |
68 | + */ |
69 | +PropertyChange::PropertyChange(QObject *item, const char *property) |
70 | + : m_backedUp(false) |
71 | + , qmlProperty(item, property, qmlContext(item)) |
72 | +{ |
73 | +} |
74 | +PropertyChange::~PropertyChange() |
75 | +{ |
76 | + restore(this); |
77 | +} |
78 | + |
79 | +/* |
80 | + * Sets a value to the property. Will back up the original values if it wasn't yet. |
81 | + * This function can be called many times, it will not destroy the backed up value/binding. |
82 | + */ |
83 | +void PropertyChange::setValue(PropertyChange *change, const QVariant &value) |
84 | +{ |
85 | + if (!change) { |
86 | + return; |
87 | + } |
88 | + if (!change->m_backedUp) { |
89 | + change->backup.first = QQmlPropertyPrivate::setBinding(change->qmlProperty, 0); |
90 | + change->backup.second = change->qmlProperty.read(); |
91 | + change->m_backedUp = true; |
92 | + } |
93 | + change->qmlProperty.write(value); |
94 | +} |
95 | + |
96 | +/* |
97 | + * Restore backed up value or binding. |
98 | + */ |
99 | +void PropertyChange::restore(PropertyChange *change) |
100 | +{ |
101 | + if (!change) { |
102 | + return; |
103 | + } |
104 | + if (change->m_backedUp) { |
105 | + // if there was a binding, restore it |
106 | + if (change->backup.first) { |
107 | + QQmlAbstractBinding *prevBinding = QQmlPropertyPrivate::setBinding(change->qmlProperty, change->backup.first); |
108 | + if (prevBinding && prevBinding != change->backup.first) { |
109 | + prevBinding->destroy(); |
110 | + } |
111 | + } else { |
112 | + // there was no binding, restore previous value |
113 | + change->qmlProperty.write(change->backup.second); |
114 | + } |
115 | + change->m_backedUp = false; |
116 | + } |
117 | +} |
118 | |
119 | === added file 'modules/Ubuntu/Components/plugin/propertychange_p.h' |
120 | --- modules/Ubuntu/Components/plugin/propertychange_p.h 1970-01-01 00:00:00 +0000 |
121 | +++ modules/Ubuntu/Components/plugin/propertychange_p.h 2014-11-19 15:10:34 +0000 |
122 | @@ -0,0 +1,39 @@ |
123 | +/* |
124 | + * Copyright 2014 Canonical Ltd. |
125 | + * |
126 | + * This program is free software; you can redistribute it and/or modify |
127 | + * it under the terms of the GNU Lesser General Public License as published by |
128 | + * the Free Software Foundation; version 3. |
129 | + * |
130 | + * This program is distributed in the hope that it will be useful, |
131 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
132 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
133 | + * GNU Lesser General Public License for more details. |
134 | + * |
135 | + * You should have received a copy of the GNU Lesser General Public License |
136 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
137 | + */ |
138 | + |
139 | +#ifndef PROPERTYCHANGE_P_H |
140 | +#define PROPERTYCHANGE_P_H |
141 | + |
142 | +#include <QtCore/QVariant> |
143 | +#include <QtCore/QObject> |
144 | +#include <QtQml/QQmlProperty> |
145 | + |
146 | +class QQmlAbstractBinding; |
147 | +class PropertyChange |
148 | +{ |
149 | +public: |
150 | + PropertyChange(QObject *item, const char *property); |
151 | + ~PropertyChange(); |
152 | + |
153 | + static void setValue(PropertyChange* change, const QVariant &value); |
154 | + static void restore(PropertyChange* change); |
155 | +private: |
156 | + bool m_backedUp; |
157 | + QQmlProperty qmlProperty; |
158 | + QPair<QQmlAbstractBinding*, QVariant> backup; |
159 | +}; |
160 | + |
161 | +#endif // PROPERTYCHANGE_P_H |
162 | |
163 | === modified file 'modules/Ubuntu/Components/plugin/uclistitem.cpp' |
164 | --- modules/Ubuntu/Components/plugin/uclistitem.cpp 2014-11-19 15:10:32 +0000 |
165 | +++ modules/Ubuntu/Components/plugin/uclistitem.cpp 2014-11-19 15:10:34 +0000 |
166 | @@ -197,6 +197,7 @@ |
167 | , overshoot(UCUnits::instance().gu(2)) |
168 | , color(Qt::transparent) |
169 | , highlightColor(Qt::transparent) |
170 | + , attachedProperties(0) |
171 | , contentItem(new QQuickItem) |
172 | , divider(new UCListItemDivider) |
173 | , leadingActions(0) |
174 | @@ -245,6 +246,12 @@ |
175 | listenToRebind(false); |
176 | } |
177 | |
178 | +// rebound without animation |
179 | +void UCListItemPrivate::promptRebound() |
180 | +{ |
181 | + setPressed(false); |
182 | +} |
183 | + |
184 | // called when units size changes |
185 | void UCListItemPrivate::_q_updateSize() |
186 | { |
187 | @@ -283,14 +290,9 @@ |
188 | // connects/disconnects from the Flickable anchestor to get notified when to do rebound |
189 | void UCListItemPrivate::listenToRebind(bool listen) |
190 | { |
191 | - if (flickable.isNull()) { |
192 | - return; |
193 | - } |
194 | - Q_Q(UCListItem); |
195 | - if (listen) { |
196 | - QObject::connect(flickable.data(), SIGNAL(movementStarted()), q, SLOT(_q_rebound())); |
197 | - } else { |
198 | - QObject::disconnect(flickable.data(), SIGNAL(movementStarted()), q, SLOT(_q_rebound())); |
199 | + if (attachedProperties) { |
200 | + Q_Q(UCListItem); |
201 | + attachedProperties->listenToRebind(q, listen); |
202 | } |
203 | } |
204 | |
205 | @@ -359,6 +361,11 @@ |
206 | { |
207 | } |
208 | |
209 | +UCListItemAttached *UCListItem::qmlAttachedProperties(QObject *owner) |
210 | +{ |
211 | + return new UCListItemAttached(owner); |
212 | +} |
213 | + |
214 | void UCListItem::componentComplete() |
215 | { |
216 | UCStyledItemBase::componentComplete(); |
217 | @@ -381,11 +388,18 @@ |
218 | d->flickable = qobject_cast<QQuickFlickable*>(data.item->parentItem()); |
219 | } |
220 | |
221 | + // attach ListItem properties to the flickable or to the parent item |
222 | if (d->flickable) { |
223 | - // connect to flickable to get width changes |
224 | - QObject::connect(d->flickable, SIGNAL(widthChanged()), this, SLOT(_q_updateSize())); |
225 | + d->attachedProperties = static_cast<UCListItemAttached*>(qmlAttachedPropertiesObject<UCListItem>(d->flickable)); |
226 | } else if (data.item) { |
227 | - QObject::connect(data.item, SIGNAL(widthChanged()), this, SLOT(_q_updateSize())); |
228 | + d->attachedProperties = static_cast<UCListItemAttached*>(qmlAttachedPropertiesObject<UCListItem>(data.item)); |
229 | + } else { |
230 | + // about to be deleted or reparented, disable attached |
231 | + d->attachedProperties = 0; |
232 | + } |
233 | + |
234 | + if (data.item) { |
235 | + QObject::connect(d->flickable ? d->flickable : data.item, SIGNAL(widthChanged()), this, SLOT(_q_updateSize())); |
236 | } |
237 | |
238 | // update size |
239 | @@ -437,7 +451,7 @@ |
240 | { |
241 | UCStyledItemBase::mousePressEvent(event); |
242 | Q_D(UCListItem); |
243 | - if (!d->flickable.isNull() && d->flickable->isMoving()) { |
244 | + if (d->attachedProperties && d->attachedProperties->isMoving()) { |
245 | // while moving, we cannot select any items |
246 | return; |
247 | } |
248 | @@ -445,22 +459,20 @@ |
249 | d->setPressed(true); |
250 | // connect the Flickable to know when to rebound |
251 | d->listenToRebind(true); |
252 | - // accept the event so we get the rest of the events as well |
253 | - event->setAccepted(true); |
254 | } |
255 | + // accept the event so we get the rest of the events as well |
256 | + event->setAccepted(true); |
257 | } |
258 | |
259 | void UCListItem::mouseReleaseEvent(QMouseEvent *event) |
260 | { |
261 | + UCStyledItemBase::mouseReleaseEvent(event); |
262 | Q_D(UCListItem); |
263 | // set released |
264 | if (d->pressed) { |
265 | + d->listenToRebind(false); |
266 | Q_EMIT clicked(); |
267 | } |
268 | - // save pressed state as UCFocusScope resets it seemlessly |
269 | - bool wasPressed = d->pressed; |
270 | - UCStyledItemBase::mouseReleaseEvent(event); |
271 | - d->pressed = wasPressed; |
272 | d->setPressed(false); |
273 | } |
274 | |
275 | |
276 | === modified file 'modules/Ubuntu/Components/plugin/uclistitem.h' |
277 | --- modules/Ubuntu/Components/plugin/uclistitem.h 2014-11-19 15:10:32 +0000 |
278 | +++ modules/Ubuntu/Components/plugin/uclistitem.h 2014-11-19 15:10:34 +0000 |
279 | @@ -23,6 +23,7 @@ |
280 | class UCListItemContent; |
281 | class UCListItemDivider; |
282 | class UCListItemActions; |
283 | +class UCListItemAttached; |
284 | class UCListItemPrivate; |
285 | class UCListItem : public UCStyledItemBase |
286 | { |
287 | @@ -42,6 +43,8 @@ |
288 | explicit UCListItem(QQuickItem *parent = 0); |
289 | ~UCListItem(); |
290 | |
291 | + static UCListItemAttached *qmlAttachedProperties(QObject *owner); |
292 | + |
293 | QQuickItem *contentItem() const; |
294 | UCListItemDivider *divider() const; |
295 | UCListItemActions *leadingActions() const; |
296 | @@ -87,4 +90,26 @@ |
297 | Q_PRIVATE_SLOT(d_func(), void _q_updateSize()) |
298 | }; |
299 | |
300 | +QML_DECLARE_TYPEINFO(UCListItem, QML_HAS_ATTACHED_PROPERTIES) |
301 | + |
302 | +class UCListItemAttachedPrivate; |
303 | +class UCListItemAttached : public QObject |
304 | +{ |
305 | + Q_OBJECT |
306 | +public: |
307 | + explicit UCListItemAttached(QObject *owner); |
308 | + ~UCListItemAttached(); |
309 | + |
310 | + bool listenToRebind(UCListItem *item, bool listen); |
311 | + void disableInteractive(UCListItem *item, bool disable); |
312 | + bool isMoving(); |
313 | + bool isBoundTo(UCListItem *item); |
314 | + |
315 | +private Q_SLOTS: |
316 | + void unbindItem(); |
317 | +private: |
318 | + Q_DECLARE_PRIVATE(UCListItemAttached) |
319 | + QScopedPointer<UCListItemAttachedPrivate> d_ptr; |
320 | +}; |
321 | + |
322 | #endif // UCLISTITEM_H |
323 | |
324 | === modified file 'modules/Ubuntu/Components/plugin/uclistitem_p.h' |
325 | --- modules/Ubuntu/Components/plugin/uclistitem_p.h 2014-11-19 15:10:32 +0000 |
326 | +++ modules/Ubuntu/Components/plugin/uclistitem_p.h 2014-11-19 15:10:34 +0000 |
327 | @@ -42,6 +42,7 @@ |
328 | |
329 | void _q_updateColors(); |
330 | void _q_rebound(); |
331 | + void promptRebound(); |
332 | void _q_updateSize(); |
333 | int index(); |
334 | void setPressed(bool pressed); |
335 | @@ -57,6 +58,7 @@ |
336 | QColor color; |
337 | QColor highlightColor; |
338 | QPointer<QQuickFlickable> flickable; |
339 | + UCListItemAttached *attachedProperties; |
340 | QQuickItem *contentItem; |
341 | UCListItemDivider *divider; |
342 | UCListItemActions *leadingActions; |
343 | @@ -67,6 +69,27 @@ |
344 | void setContentMoving(bool moved); |
345 | }; |
346 | |
347 | +class PropertyChange; |
348 | +class UCListItemAttachedPrivate |
349 | +{ |
350 | + Q_DECLARE_PUBLIC(UCListItemAttached) |
351 | +public: |
352 | + UCListItemAttachedPrivate(UCListItemAttached *qq); |
353 | + ~UCListItemAttachedPrivate(); |
354 | + |
355 | + void clearFlickablesList(); |
356 | + void buildFlickablesList(); |
357 | + void clearChangesList(); |
358 | + void buildChangesList(const QVariant &newValue); |
359 | + |
360 | + UCListItemAttached *q_ptr; |
361 | + bool globalDisabled; |
362 | + QList< QPointer<QQuickFlickable> > flickables; |
363 | + QList< PropertyChange* > changes; |
364 | + QPointer<UCListItem> boundItem; |
365 | + QPointer<UCListItem> disablerItem; |
366 | +}; |
367 | + |
368 | class UCListItemDivider : public QObject |
369 | { |
370 | Q_OBJECT |
371 | |
372 | === added file 'modules/Ubuntu/Components/plugin/uclistitemattached.cpp' |
373 | --- modules/Ubuntu/Components/plugin/uclistitemattached.cpp 1970-01-01 00:00:00 +0000 |
374 | +++ modules/Ubuntu/Components/plugin/uclistitemattached.cpp 2014-11-19 15:10:34 +0000 |
375 | @@ -0,0 +1,207 @@ |
376 | +/* |
377 | + * Copyright 2014 Canonical Ltd. |
378 | + * |
379 | + * This program is free software; you can redistribute it and/or modify |
380 | + * it under the terms of the GNU Lesser General Public License as published by |
381 | + * the Free Software Foundation; version 3. |
382 | + * |
383 | + * This program is distributed in the hope that it will be useful, |
384 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
385 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
386 | + * GNU Lesser General Public License for more details. |
387 | + * |
388 | + * You should have received a copy of the GNU Lesser General Public License |
389 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
390 | + */ |
391 | + |
392 | +#include "ucunits.h" |
393 | +#include "uctheme.h" |
394 | +#include "uclistitem.h" |
395 | +#include "uclistitem_p.h" |
396 | +#include "propertychange_p.h" |
397 | +#include <QtQuick/private/qquickflickable_p.h> |
398 | + |
399 | +/* |
400 | + * The properties are attached to the ListItem's parent item or to its closest |
401 | + * Flickable parent, when embedded in ListView or Flickable. There will be only |
402 | + * one attached property per Flickable for all embedded child ListItems, enabling |
403 | + * in this way the controlling of the interactive flag of the Flickable and all |
404 | + * its ascendant Flickables. |
405 | + */ |
406 | +UCListItemAttachedPrivate::UCListItemAttachedPrivate(UCListItemAttached *qq) |
407 | + : q_ptr(qq) |
408 | + , globalDisabled(false) |
409 | +{ |
410 | +} |
411 | + |
412 | +UCListItemAttachedPrivate::~UCListItemAttachedPrivate() |
413 | +{ |
414 | + clearChangesList(); |
415 | + clearFlickablesList(); |
416 | +} |
417 | + |
418 | +// disconnect all flickables |
419 | +void UCListItemAttachedPrivate::clearFlickablesList() |
420 | +{ |
421 | + Q_Q(UCListItemAttached); |
422 | + Q_FOREACH(const QPointer<QQuickFlickable> &flickable, flickables) { |
423 | + if (flickable.data()) |
424 | + QObject::disconnect(flickable.data(), &QQuickFlickable::movementStarted, |
425 | + q, &UCListItemAttached::unbindItem); |
426 | + } |
427 | + flickables.clear(); |
428 | +} |
429 | + |
430 | +// connect all flickables |
431 | +void UCListItemAttachedPrivate::buildFlickablesList() |
432 | +{ |
433 | + Q_Q(UCListItemAttached); |
434 | + QQuickItem *item = qobject_cast<QQuickItem*>(q->parent()); |
435 | + if (!item) { |
436 | + return; |
437 | + } |
438 | + clearFlickablesList(); |
439 | + while (item) { |
440 | + QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(item); |
441 | + if (flickable) { |
442 | + QObject::connect(flickable, &QQuickFlickable::movementStarted, |
443 | + q, &UCListItemAttached::unbindItem); |
444 | + flickables << flickable; |
445 | + } |
446 | + item = item->parentItem(); |
447 | + } |
448 | +} |
449 | + |
450 | +void UCListItemAttachedPrivate::clearChangesList() |
451 | +{ |
452 | + // clear property change objects |
453 | + Q_Q(UCListItemAttached); |
454 | + Q_FOREACH(PropertyChange *change, changes) { |
455 | + // deleting PropertyChange will restore the saved property |
456 | + // to its original binding/value |
457 | + delete change; |
458 | + } |
459 | + changes.clear(); |
460 | +} |
461 | + |
462 | +void UCListItemAttachedPrivate::buildChangesList(const QVariant &newValue) |
463 | +{ |
464 | + // collect all ascendant flickables |
465 | + Q_Q(UCListItemAttached); |
466 | + QQuickItem *item = qobject_cast<QQuickItem*>(q->parent()); |
467 | + if (!item) { |
468 | + return; |
469 | + } |
470 | + clearChangesList(); |
471 | + while (item) { |
472 | + QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(item); |
473 | + if (flickable) { |
474 | + PropertyChange *change = new PropertyChange(item, "interactive"); |
475 | + PropertyChange::setValue(change, newValue); |
476 | + changes << change; |
477 | + } |
478 | + item = item->parentItem(); |
479 | + } |
480 | +} |
481 | + |
482 | +UCListItemAttached::UCListItemAttached(QObject *owner) |
483 | + : QObject(owner) |
484 | + , d_ptr(new UCListItemAttachedPrivate(this)) |
485 | +{ |
486 | +} |
487 | + |
488 | +UCListItemAttached::~UCListItemAttached() |
489 | +{ |
490 | +} |
491 | + |
492 | +// register item to be rebound |
493 | +bool UCListItemAttached::listenToRebind(UCListItem *item, bool listen) |
494 | +{ |
495 | + // we cannot bind the item until we have an other one bound |
496 | + bool result = false; |
497 | + Q_D(UCListItemAttached); |
498 | + if (listen) { |
499 | + if (d->boundItem.isNull() || (d->boundItem == item)) { |
500 | + d->boundItem = item; |
501 | + // rebuild flickable list |
502 | + d->buildFlickablesList(); |
503 | + result = true; |
504 | + } |
505 | + } else if (d->boundItem == item) { |
506 | + d->boundItem.clear(); |
507 | + result = true; |
508 | + } |
509 | + return result; |
510 | +} |
511 | + |
512 | +// reports true if any of the ascendant flickables is moving |
513 | +bool UCListItemAttached::isMoving() |
514 | +{ |
515 | + Q_D(UCListItemAttached); |
516 | + Q_FOREACH(const QPointer<QQuickFlickable> &flickable, d->flickables) { |
517 | + if (flickable && flickable->isMoving()) { |
518 | + return true; |
519 | + } |
520 | + } |
521 | + return false; |
522 | +} |
523 | + |
524 | +// returns true if the given ListItem is bound to listen on moving changes |
525 | +bool UCListItemAttached::isBoundTo(UCListItem *item) |
526 | +{ |
527 | + Q_D(UCListItemAttached); |
528 | + return d->boundItem == item; |
529 | +} |
530 | + |
531 | +/* |
532 | + * Disable/enable interactive flag for the ascendant flickables. The item is used |
533 | + * to detect whether the same item is trying to enable the flickables which disabled |
534 | + * it before. The enabled/disabled states are not equivalent to the enabled/disabled |
535 | + * state of the interactive flag. |
536 | + * When disabled, always the last item disabling will be kept as active disabler, |
537 | + * and only the active disabler can enable (restore) the interactive flag state. |
538 | + */ |
539 | +void UCListItemAttached::disableInteractive(UCListItem *item, bool disable) |
540 | +{ |
541 | + Q_D(UCListItemAttached); |
542 | + if (disable) { |
543 | + // disabling or re-disabling |
544 | + d->disablerItem = item; |
545 | + if (d->globalDisabled == disable) { |
546 | + // was already disabled, leave |
547 | + return; |
548 | + } |
549 | + d->globalDisabled = true; |
550 | + } else if (d->globalDisabled && d->disablerItem == item) { |
551 | + // the one disabled it will enable |
552 | + d->globalDisabled = false; |
553 | + d->disablerItem.clear(); |
554 | + } else { |
555 | + // !disabled && (!globalDisabled || item != d->disablerItem) |
556 | + return; |
557 | + } |
558 | + if (disable) { |
559 | + // (re)build changes list with disabling the interactive value |
560 | + d->buildChangesList(false); |
561 | + } else { |
562 | + d->clearChangesList(); |
563 | + } |
564 | +} |
565 | + |
566 | +void UCListItemAttached::unbindItem() |
567 | +{ |
568 | + Q_D(UCListItemAttached); |
569 | + if (d->boundItem) { |
570 | + // depending on content item's X coordinate, we either do animated or prompt rebind |
571 | + if (d->boundItem->contentItem()->x() != 0.0) { |
572 | + // content is not in origin, rebind |
573 | + UCListItemPrivate::get(d->boundItem.data())->_q_rebound(); |
574 | + } else { |
575 | + // do some cleanup |
576 | + UCListItemPrivate::get(d->boundItem.data())->promptRebound(); |
577 | + } |
578 | + d->boundItem.clear(); |
579 | + } |
580 | + // clear binding list |
581 | + d->clearFlickablesList(); |
582 | +} |
229 + // about to be deleted or reparrented, disable attached
*reparented