1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2020 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtCore module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qproperty.h" |
41 | #include "qproperty_p.h" |
42 | |
43 | #include <qscopedvaluerollback.h> |
44 | #include <QScopeGuard> |
45 | |
46 | QT_BEGIN_NAMESPACE |
47 | |
48 | using namespace QtPrivate; |
49 | |
50 | void QPropertyBindingDataPointer::addObserver(QPropertyObserver *observer) |
51 | { |
52 | if (auto *binding = bindingPtr()) { |
53 | observer->prev = &binding->firstObserver.ptr; |
54 | observer->next = binding->firstObserver.ptr; |
55 | if (observer->next) |
56 | observer->next->prev = &observer->next; |
57 | binding->firstObserver.ptr = observer; |
58 | } else { |
59 | auto firstObserver = reinterpret_cast<QPropertyObserver*>(ptr->d_ptr & ~QPropertyBindingData::FlagMask); |
60 | observer->prev = reinterpret_cast<QPropertyObserver**>(&ptr->d_ptr); |
61 | observer->next = firstObserver; |
62 | if (observer->next) |
63 | observer->next->prev = &observer->next; |
64 | } |
65 | setFirstObserver(observer); |
66 | } |
67 | |
68 | QPropertyBindingPrivate::~QPropertyBindingPrivate() |
69 | { |
70 | if (firstObserver) |
71 | firstObserver.unlink(); |
72 | } |
73 | |
74 | void QPropertyBindingPrivate::unlinkAndDeref() |
75 | { |
76 | propertyDataPtr = nullptr; |
77 | if (!ref.deref()) |
78 | delete this; |
79 | } |
80 | |
81 | void QPropertyBindingPrivate::markDirtyAndNotifyObservers() |
82 | { |
83 | if (dirty) |
84 | return; |
85 | dirty = true; |
86 | if (eagerlyUpdating) { |
87 | error = QPropertyBindingError(QPropertyBindingError::BindingLoop); |
88 | return; |
89 | } |
90 | |
91 | eagerlyUpdating = true; |
92 | QScopeGuard guard([&](){eagerlyUpdating = false;}); |
93 | if (requiresEagerEvaluation()) { |
94 | // these are compat properties that we will need to evaluate eagerly |
95 | evaluateIfDirtyAndReturnTrueIfValueChanged(propertyDataPtr); |
96 | } |
97 | if (firstObserver) |
98 | firstObserver.notify(this, propertyDataPtr); |
99 | if (hasStaticObserver) |
100 | staticObserverCallback(propertyDataPtr); |
101 | } |
102 | |
103 | bool QPropertyBindingPrivate::evaluateIfDirtyAndReturnTrueIfValueChanged(const QUntypedPropertyData *data) |
104 | { |
105 | if (!dirty) |
106 | return false; |
107 | |
108 | if (updating) { |
109 | error = QPropertyBindingError(QPropertyBindingError::BindingLoop); |
110 | return false; |
111 | } |
112 | |
113 | /* |
114 | * Evaluating the binding might lead to the binding being broken. This can |
115 | * cause ref to reach zero at the end of the function. However, the |
116 | * updateGuard's destructor will then still trigger, trying to set the |
117 | * updating bool to its old value |
118 | * To prevent this, we create a QPropertyBindingPrivatePtr which ensures |
119 | * that the object is still alive when updateGuard's dtor runs. |
120 | */ |
121 | QPropertyBindingPrivatePtr keepAlive {this}; |
122 | QScopedValueRollback<bool> updateGuard(updating, true); |
123 | |
124 | BindingEvaluationState evaluationFrame(this); |
125 | |
126 | bool changed = false; |
127 | |
128 | Q_ASSERT(propertyDataPtr == data); |
129 | QUntypedPropertyData *mutable_data = const_cast<QUntypedPropertyData *>(data); |
130 | |
131 | if (hasBindingWrapper) { |
132 | changed = staticBindingWrapper(metaType, mutable_data, evaluationFunction); |
133 | } else { |
134 | changed = evaluationFunction(metaType, mutable_data); |
135 | } |
136 | |
137 | dirty = false; |
138 | return changed; |
139 | } |
140 | |
141 | QUntypedPropertyBinding::QUntypedPropertyBinding() = default; |
142 | |
143 | QUntypedPropertyBinding::QUntypedPropertyBinding(QMetaType metaType, QUntypedPropertyBinding::BindingEvaluationFunction function, |
144 | const QPropertyBindingSourceLocation &location) |
145 | : d(new QPropertyBindingPrivate(metaType, std::move(function), std::move(location))) |
146 | { |
147 | } |
148 | |
149 | QUntypedPropertyBinding::QUntypedPropertyBinding(QUntypedPropertyBinding &&other) |
150 | : d(std::move(other.d)) |
151 | { |
152 | } |
153 | |
154 | QUntypedPropertyBinding::QUntypedPropertyBinding(const QUntypedPropertyBinding &other) |
155 | : d(other.d) |
156 | { |
157 | } |
158 | |
159 | QUntypedPropertyBinding &QUntypedPropertyBinding::operator=(const QUntypedPropertyBinding &other) |
160 | { |
161 | d = other.d; |
162 | return *this; |
163 | } |
164 | |
165 | QUntypedPropertyBinding &QUntypedPropertyBinding::operator=(QUntypedPropertyBinding &&other) |
166 | { |
167 | d = std::move(other.d); |
168 | return *this; |
169 | } |
170 | |
171 | QUntypedPropertyBinding::QUntypedPropertyBinding(QPropertyBindingPrivate *priv) |
172 | : d(priv) |
173 | { |
174 | } |
175 | |
176 | QUntypedPropertyBinding::~QUntypedPropertyBinding() |
177 | { |
178 | } |
179 | |
180 | bool QUntypedPropertyBinding::isNull() const |
181 | { |
182 | return !d; |
183 | } |
184 | |
185 | QPropertyBindingError QUntypedPropertyBinding::error() const |
186 | { |
187 | if (!d) |
188 | return QPropertyBindingError(); |
189 | return d->bindingError(); |
190 | } |
191 | |
192 | QMetaType QUntypedPropertyBinding::valueMetaType() const |
193 | { |
194 | if (!d) |
195 | return QMetaType(); |
196 | return d->valueMetaType(); |
197 | } |
198 | |
199 | QPropertyBindingData::~QPropertyBindingData() |
200 | { |
201 | QPropertyBindingDataPointer d{this}; |
202 | for (auto observer = d.firstObserver(); observer;) { |
203 | auto next = observer.nextObserver(); |
204 | observer.unlink(); |
205 | observer = next; |
206 | } |
207 | if (auto binding = d.bindingPtr()) |
208 | binding->unlinkAndDeref(); |
209 | } |
210 | |
211 | QUntypedPropertyBinding QPropertyBindingData::setBinding(const QUntypedPropertyBinding &binding, |
212 | QUntypedPropertyData *propertyDataPtr, |
213 | QPropertyObserverCallback staticObserverCallback, |
214 | QtPrivate::QPropertyBindingWrapper guardCallback) |
215 | { |
216 | QPropertyBindingPrivatePtr oldBinding; |
217 | QPropertyBindingPrivatePtr newBinding = binding.d; |
218 | |
219 | QPropertyBindingDataPointer d{this}; |
220 | QPropertyObserverPointer observer; |
221 | |
222 | if (auto *existingBinding = d.bindingPtr()) { |
223 | if (existingBinding == newBinding.data()) |
224 | return QUntypedPropertyBinding(oldBinding.data()); |
225 | oldBinding = QPropertyBindingPrivatePtr(existingBinding); |
226 | observer = oldBinding->takeObservers(); |
227 | oldBinding->unlinkAndDeref(); |
228 | d_ptr &= FlagMask; |
229 | } else { |
230 | observer = d.firstObserver(); |
231 | } |
232 | |
233 | if (newBinding) { |
234 | newBinding.data()->ref.ref(); |
235 | d_ptr = (d_ptr & FlagMask) | reinterpret_cast<quintptr>(newBinding.data()); |
236 | d_ptr |= BindingBit; |
237 | newBinding->setDirty(true); |
238 | newBinding->setProperty(propertyDataPtr); |
239 | if (observer) |
240 | newBinding->prependObserver(observer); |
241 | newBinding->setStaticObserver(staticObserverCallback, guardCallback); |
242 | if (newBinding->requiresEagerEvaluation()) { |
243 | auto changed = newBinding->evaluateIfDirtyAndReturnTrueIfValueChanged(propertyDataPtr); |
244 | if (changed) |
245 | observer.notify(newBinding.data(), propertyDataPtr, /*alreadyKnownToHaveChanged=*/true); |
246 | } |
247 | } else if (observer) { |
248 | d.setObservers(observer.ptr); |
249 | } else { |
250 | d_ptr &= ~QPropertyBindingData::BindingBit; |
251 | } |
252 | |
253 | if (oldBinding) |
254 | oldBinding->detachFromProperty(); |
255 | |
256 | return QUntypedPropertyBinding(oldBinding.data()); |
257 | } |
258 | |
259 | QPropertyBindingData::QPropertyBindingData(QPropertyBindingData &&other) : d_ptr(std::exchange(other.d_ptr, 0)) |
260 | { |
261 | QPropertyBindingDataPointer d{this}; |
262 | d.fixupFirstObserverAfterMove(); |
263 | } |
264 | |
265 | QPropertyBindingPrivate *QPropertyBindingData::binding() const |
266 | { |
267 | QPropertyBindingDataPointer d{this}; |
268 | if (auto binding = d.bindingPtr()) |
269 | return binding; |
270 | return nullptr; |
271 | } |
272 | |
273 | static thread_local QBindingStatus bindingStatus; |
274 | |
275 | BindingEvaluationState::BindingEvaluationState(QPropertyBindingPrivate *binding) |
276 | : binding(binding) |
277 | { |
278 | // store a pointer to the currentBindingEvaluationState to avoid a TLS lookup in |
279 | // the destructor (as these come with a non zero cost) |
280 | currentState = &bindingStatus.currentlyEvaluatingBinding; |
281 | previousState = *currentState; |
282 | *currentState = this; |
283 | binding->clearDependencyObservers(); |
284 | } |
285 | |
286 | CurrentCompatProperty::CurrentCompatProperty(QBindingStatus *status, QUntypedPropertyData *property) |
287 | : property(property) |
288 | { |
289 | // store a pointer to the currentBindingEvaluationState to avoid a TLS lookup in |
290 | // the destructor (as these come with a non zero cost) |
291 | currentState = &status->currentCompatProperty; |
292 | previousState = *currentState; |
293 | *currentState = this; |
294 | } |
295 | |
296 | QPropertyBindingPrivate *QPropertyBindingPrivate::currentlyEvaluatingBinding() |
297 | { |
298 | auto currentState = bindingStatus.currentlyEvaluatingBinding ; |
299 | return currentState ? currentState->binding : nullptr; |
300 | } |
301 | |
302 | void QPropertyBindingData::evaluateIfDirty(const QUntypedPropertyData *property) const |
303 | { |
304 | QPropertyBindingDataPointer d{this}; |
305 | QPropertyBindingPrivate *binding = d.bindingPtr(); |
306 | if (!binding) |
307 | return; |
308 | binding->evaluateIfDirtyAndReturnTrueIfValueChanged(property); |
309 | } |
310 | |
311 | void QPropertyBindingData::removeBinding() |
312 | { |
313 | QPropertyBindingDataPointer d{this}; |
314 | |
315 | if (auto *existingBinding = d.bindingPtr()) { |
316 | auto observer = existingBinding->takeObservers(); |
317 | d_ptr &= ExtraBit; |
318 | if (observer) |
319 | d.setObservers(observer.ptr); |
320 | existingBinding->unlinkAndDeref(); |
321 | } |
322 | } |
323 | |
324 | void QPropertyBindingData::registerWithCurrentlyEvaluatingBinding() const |
325 | { |
326 | auto currentState = bindingStatus.currentlyEvaluatingBinding; |
327 | if (!currentState) |
328 | return; |
329 | |
330 | QPropertyBindingDataPointer d{this}; |
331 | |
332 | QPropertyObserverPointer dependencyObserver = currentState->binding->allocateDependencyObserver(); |
333 | dependencyObserver.setBindingToMarkDirty(currentState->binding); |
334 | dependencyObserver.observeProperty(d); |
335 | } |
336 | |
337 | void QPropertyBindingData::notifyObservers(QUntypedPropertyData *propertyDataPtr) const |
338 | { |
339 | QPropertyBindingDataPointer d{this}; |
340 | if (QPropertyObserverPointer observer = d.firstObserver()) |
341 | observer.notify(d.bindingPtr(), propertyDataPtr); |
342 | } |
343 | |
344 | int QPropertyBindingDataPointer::observerCount() const |
345 | { |
346 | int count = 0; |
347 | for (auto observer = firstObserver(); observer; observer = observer.nextObserver()) |
348 | ++count; |
349 | return count; |
350 | } |
351 | |
352 | QPropertyObserver::QPropertyObserver(ChangeHandler changeHandler) |
353 | { |
354 | QPropertyObserverPointer d{this}; |
355 | d.setChangeHandler(changeHandler); |
356 | } |
357 | |
358 | QPropertyObserver::QPropertyObserver(QUntypedPropertyData *aliasedPropertyPtr) |
359 | { |
360 | QPropertyObserverPointer d{this}; |
361 | d.setAliasedProperty(aliasedPropertyPtr); |
362 | } |
363 | |
364 | /*! \internal |
365 | */ |
366 | void QPropertyObserver::setSource(const QPropertyBindingData &property) |
367 | { |
368 | QPropertyObserverPointer d{this}; |
369 | QPropertyBindingDataPointer propPrivate{&property}; |
370 | d.observeProperty(propPrivate); |
371 | } |
372 | |
373 | QPropertyObserver::~QPropertyObserver() |
374 | { |
375 | if (next.tag() == ActivelyExecuting) { |
376 | if (nodeState) |
377 | *nodeState = nullptr; |
378 | } |
379 | QPropertyObserverPointer d{this}; |
380 | d.unlink(); |
381 | } |
382 | |
383 | QPropertyObserver::QPropertyObserver(QPropertyObserver &&other) noexcept |
384 | { |
385 | bindingToMarkDirty = std::exchange(other.bindingToMarkDirty, {}); |
386 | next = std::exchange(other.next, {}); |
387 | prev = std::exchange(other.prev, {}); |
388 | if (next) |
389 | next->prev = &next; |
390 | if (prev) |
391 | prev.setPointer(this); |
392 | if (next.tag() == ActivelyExecuting) |
393 | *nodeState = this; |
394 | } |
395 | |
396 | QPropertyObserver &QPropertyObserver::operator=(QPropertyObserver &&other) noexcept |
397 | { |
398 | if (this == &other) |
399 | return *this; |
400 | |
401 | QPropertyObserverPointer d{this}; |
402 | d.unlink(); |
403 | bindingToMarkDirty = nullptr; |
404 | |
405 | bindingToMarkDirty = std::exchange(other.bindingToMarkDirty, {}); |
406 | next = std::exchange(other.next, {}); |
407 | prev = std::exchange(other.prev, {}); |
408 | if (next) |
409 | next->prev = &next; |
410 | if (prev) |
411 | prev.setPointer(this); |
412 | if (next.tag() == ActivelyExecuting) |
413 | *nodeState = this; |
414 | |
415 | return *this; |
416 | } |
417 | |
418 | void QPropertyObserverPointer::unlink() |
419 | { |
420 | if (ptr->next.tag() == QPropertyObserver::ObserverNotifiesAlias) |
421 | ptr->aliasedPropertyData = nullptr; |
422 | if (ptr->next) |
423 | ptr->next->prev = ptr->prev; |
424 | if (ptr->prev) |
425 | ptr->prev.setPointer(ptr->next.data()); |
426 | ptr->next = nullptr; |
427 | ptr->prev.clear(); |
428 | } |
429 | |
430 | void QPropertyObserverPointer::setChangeHandler(QPropertyObserver::ChangeHandler changeHandler) |
431 | { |
432 | Q_ASSERT(ptr->next.tag() != QPropertyObserver::ActivelyExecuting); |
433 | ptr->changeHandler = changeHandler; |
434 | ptr->next.setTag(QPropertyObserver::ObserverNotifiesChangeHandler); |
435 | } |
436 | |
437 | void QPropertyObserverPointer::setAliasedProperty(QUntypedPropertyData *property) |
438 | { |
439 | Q_ASSERT(ptr->next.tag() != QPropertyObserver::ActivelyExecuting); |
440 | ptr->aliasedPropertyData = property; |
441 | ptr->next.setTag(QPropertyObserver::ObserverNotifiesAlias); |
442 | } |
443 | |
444 | void QPropertyObserverPointer::setBindingToMarkDirty(QPropertyBindingPrivate *binding) |
445 | { |
446 | Q_ASSERT(ptr->next.tag() != QPropertyObserver::ActivelyExecuting); |
447 | ptr->bindingToMarkDirty = binding; |
448 | ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding); |
449 | } |
450 | |
451 | /*! |
452 | \internal |
453 | QPropertyObserverNodeProtector is a RAII wrapper which takes care of the internal switching logic |
454 | for QPropertyObserverPointer::notify (described ibidem) |
455 | */ |
456 | template <QPropertyObserver::ObserverTag tag> |
457 | struct [[nodiscard]] QPropertyObserverNodeProtector { |
458 | QPropertyObserver m_placeHolder; |
459 | QPropertyObserver *&m_observer; |
460 | union { |
461 | QPropertyBindingPrivate *m_binding; |
462 | QPropertyObserver::ChangeHandler m_changeHandler; |
463 | }; |
464 | QPropertyObserverNodeProtector(QPropertyObserver *&observer) |
465 | : m_observer(observer) |
466 | { |
467 | static_assert(tag == QPropertyObserver::ObserverNotifiesBinding || |
468 | tag == QPropertyObserver::ObserverNotifiesChangeHandler); |
469 | if constexpr (tag == QPropertyObserver::ObserverNotifiesBinding) |
470 | m_binding = m_observer->bindingToMarkDirty; |
471 | else |
472 | m_changeHandler = m_observer->changeHandler; |
473 | switchNodes(m_placeHolder, m_observer); |
474 | m_observer->nodeState = &m_observer; |
475 | m_observer->next.setTag(QPropertyObserver::ActivelyExecuting); |
476 | m_placeHolder.next.setTag(QPropertyObserver::ActivelyExecuting); |
477 | } |
478 | |
479 | ~QPropertyObserverNodeProtector() { |
480 | if (m_observer) { |
481 | if constexpr (tag == QPropertyObserver::ObserverNotifiesBinding) |
482 | m_observer->bindingToMarkDirty = m_binding; |
483 | else |
484 | m_observer->changeHandler = m_changeHandler; |
485 | switchNodes(*m_observer, &m_placeHolder); |
486 | m_observer->next.setTag(tag); |
487 | } |
488 | // set tag to a safer value where we don't execute anything in the dtor |
489 | m_placeHolder.next.setTag(QPropertyObserver::ObserverNotifiesChangeHandler); |
490 | } |
491 | |
492 | /*! |
493 | \internal |
494 | replaces a node \a observer in the list with another node \a placeholder which must not be in the list |
495 | */ |
496 | static void switchNodes(QPropertyObserver &placeHolder, QPropertyObserver *observer) { |
497 | placeHolder.next = std::exchange(observer->next, {}); |
498 | placeHolder.prev = std::exchange(observer->prev, {}); |
499 | if (placeHolder.next) { |
500 | placeHolder.next->prev = &placeHolder.next; |
501 | } |
502 | if (placeHolder.prev) |
503 | placeHolder.prev.setPointer(&placeHolder); |
504 | }; |
505 | }; |
506 | |
507 | /*! \internal |
508 | \a propertyDataPtr is a pointer to the observed property's property data |
509 | In case that property has a binding, \a triggeringBinding points to the binding's QPropertyBindingPrivate |
510 | \a alreadyKnownToHaveChanged is an optional parameter, which is needed in the case |
511 | of eager evaluation: |
512 | There, we have already evaluated the binding, and thus the change detection for the |
513 | ObserverNotifiesChangeHandler case would not work. Thus we instead pass the knowledge of |
514 | whether the value has changed we obtained when evaluating the binding eagerly along |
515 | */ |
516 | void QPropertyObserverPointer::notify(QPropertyBindingPrivate *triggeringBinding, QUntypedPropertyData *propertyDataPtr,bool alreadyKnownToHaveChanged) |
517 | { |
518 | bool knownIfPropertyChanged = alreadyKnownToHaveChanged; |
519 | bool propertyChanged = true; |
520 | |
521 | auto observer = const_cast<QPropertyObserver*>(ptr); |
522 | /* |
523 | * The basic idea of the loop is as follows: We iterate over all observers in the linked list, |
524 | * and execute the functionality corresponding to their tag. |
525 | * However, complication arise due to the fact that the triggered operations might modify the list, |
526 | * which includes deletion and move of the current and next nodes. |
527 | * Therefore, we take a few safety precautions: |
528 | * 1. Before executing any action which might modify the list, we replace the actual node in the list with |
529 | * a placeholder node. As that one is stack allocated and owned by us, we can rest assured that it is |
530 | * still there after the action has executed, and placeHolder->next points to the actual next node in the list. |
531 | * Note that taking next at the beginning of the loop does not work, as the execuated action might either move |
532 | * or delete that node. |
533 | * 2. To properly handle deletion or moves of the real current node, we store a pointer to a pointer to itself in |
534 | * its nodeState. Whenever the node is reallocated and moved, we update that pointer to point to its new |
535 | * location. If the node is actually deleted, we set it to nullptr. |
536 | * 3. After the triggered action has finished, we can use that information to restore the list to contain the actual |
537 | * node again. We either switch the nodes with the real nodes current location, or, if the real node has been |
538 | * deleted, we simply unlink the temporary node. |
539 | */ |
540 | while (observer) { |
541 | QPropertyObserver *next = nullptr; |
542 | char preventBug[1] = {'\0'}; // QTBUG-87245 |
543 | Q_UNUSED(preventBug); |
544 | switch (observer->next.tag()) { |
545 | case QPropertyObserver::ObserverNotifiesChangeHandler: |
546 | if (auto handlerToCall = observer->changeHandler) { |
547 | // both evaluateIfDirtyAndReturnTrueIfValueChanged and handlerToCall might modify the list |
548 | QPropertyObserverNodeProtector<QPropertyObserver::ObserverNotifiesChangeHandler> protector(observer); |
549 | if (!knownIfPropertyChanged && triggeringBinding) { |
550 | knownIfPropertyChanged = true; |
551 | propertyChanged = triggeringBinding->evaluateIfDirtyAndReturnTrueIfValueChanged(propertyDataPtr); |
552 | } |
553 | if (!propertyChanged) |
554 | return; |
555 | handlerToCall(observer, propertyDataPtr); |
556 | next = protector.m_placeHolder.next.data(); |
557 | } else { |
558 | next = observer->next.data(); |
559 | } |
560 | break; |
561 | case QPropertyObserver::ObserverNotifiesBinding: |
562 | if (auto bindingToMarkDirty = observer->bindingToMarkDirty) { |
563 | QPropertyObserverNodeProtector<QPropertyObserver::ObserverNotifiesBinding> protector(observer); |
564 | bindingToMarkDirty->markDirtyAndNotifyObservers(); |
565 | next = protector.m_placeHolder.next.data(); |
566 | } else { |
567 | next = observer->next.data(); |
568 | } |
569 | break; |
570 | case QPropertyObserver::ObserverNotifiesAlias: |
571 | next = observer->next.data(); |
572 | break; |
573 | case QPropertyObserver::ActivelyExecuting: |
574 | // recursion is already properly handled somewhere else |
575 | return; |
576 | default: |
577 | Q_UNREACHABLE(); |
578 | } |
579 | observer = next; |
580 | } |
581 | } |
582 | |
583 | void QPropertyObserverPointer::observeProperty(QPropertyBindingDataPointer property) |
584 | { |
585 | if (ptr->prev) |
586 | unlink(); |
587 | property.addObserver(ptr); |
588 | } |
589 | |
590 | QPropertyBindingError::QPropertyBindingError() |
591 | { |
592 | } |
593 | |
594 | QPropertyBindingError::QPropertyBindingError(Type type, const QString &description) |
595 | { |
596 | if (type != NoError) { |
597 | d = new QPropertyBindingErrorPrivate; |
598 | d->type = type; |
599 | d->description = description; |
600 | } |
601 | } |
602 | |
603 | QPropertyBindingError::QPropertyBindingError(const QPropertyBindingError &other) |
604 | : d(other.d) |
605 | { |
606 | } |
607 | |
608 | QPropertyBindingError &QPropertyBindingError::operator=(const QPropertyBindingError &other) |
609 | { |
610 | d = other.d; |
611 | return *this; |
612 | } |
613 | |
614 | QPropertyBindingError::QPropertyBindingError(QPropertyBindingError &&other) |
615 | : d(std::move(other.d)) |
616 | { |
617 | } |
618 | |
619 | QPropertyBindingError &QPropertyBindingError::operator=(QPropertyBindingError &&other) |
620 | { |
621 | d = std::move(other.d); |
622 | return *this; |
623 | } |
624 | |
625 | QPropertyBindingError::~QPropertyBindingError() |
626 | { |
627 | } |
628 | |
629 | QPropertyBindingError::Type QPropertyBindingError::type() const |
630 | { |
631 | if (!d) |
632 | return QPropertyBindingError::NoError; |
633 | return d->type; |
634 | } |
635 | |
636 | QString QPropertyBindingError::description() const |
637 | { |
638 | if (!d) |
639 | return QString(); |
640 | return d->description; |
641 | } |
642 | |
643 | /*! |
644 | \class QPropertyData |
645 | \inmodule QtCore |
646 | \brief The QPropertyData class is a helper class for properties with automatic property bindings. |
647 | \since 6.0 |
648 | |
649 | \ingroup tools |
650 | |
651 | QPropertyData\<T\> is a common base class for classes that can hold properties with automatic |
652 | data bindings. It mainly wraps the stored data, and offers low level access to that data. |
653 | |
654 | The low level access to the data provided by this class bypasses the binding mechanism, and should be |
655 | used with care, as updates to the values will not get propagated to any bindings that depend on this |
656 | property. |
657 | |
658 | You should usually call value() and setValue() on QProperty<T> or QObjectBindableProperty<T>, not use |
659 | the low level mechanisms provided in this class. |
660 | */ |
661 | |
662 | /*! \fn template <typename T> QPropertyData<T>::parameter_type QPropertyData<T>::valueBypassingBindings() const |
663 | |
664 | Returns the data stored in this property. |
665 | |
666 | \note As this will bypass any binding evaluation it might return an outdated value if a |
667 | binding is set on this property. Using this method will also not register the property |
668 | access with any currently executing binding. |
669 | */ |
670 | |
671 | /*! \fn template <typename T> void QPropertyData<T>::setValueBypassingBindings(parameter_type v) |
672 | |
673 | Sets the data value stored in this property to \a v. |
674 | |
675 | \note Using this method will bypass any potential binding registered for this property. |
676 | */ |
677 | |
678 | /*! \fn template <typename T> void QPropertyData<T>::setValueBypassingBindings(rvalue_ref v) |
679 | \overload |
680 | |
681 | Sets the data value stored in this property to \a v. |
682 | |
683 | \note Using this method will bypass any potential binding registered for this property. |
684 | */ |
685 | |
686 | |
687 | /*! |
688 | \class QProperty |
689 | \inmodule QtCore |
690 | \brief The QProperty class is a template class that enables automatic property bindings. |
691 | \since 6.0 |
692 | |
693 | \ingroup tools |
694 | |
695 | QProperty\<T\> is a generic container that holds an instance of T. You can assign |
696 | a value to it and you can read it via the value() function or the T conversion |
697 | operator. You can also tie the property to an expression that computes the value |
698 | dynamically, the binding expression. It is represented as a C++ lambda and |
699 | can be used to express relationships between different properties in your |
700 | application. |
701 | |
702 | The binding expression computes the value by reading other QProperty values. |
703 | Behind the scenes this dependency is tracked. Whenever a change in any property's |
704 | dependency is detected, the binding expression is re-evaluated and the new |
705 | result is applied to the property. This happens lazily, by marking the binding |
706 | as dirty and evaluating it only when the property's value is requested. For example: |
707 | |
708 | \code |
709 | QProperty<QString> firstname("John"); |
710 | QProperty<QString> lastname("Smith"); |
711 | QProperty<int> age(41); |
712 | |
713 | QProperty<QString> fullname; |
714 | fullname.setBinding([&]() { return firstname.value() + " " + lastname.value() + " age:" + QString::number(age.value()); }); |
715 | |
716 | qDebug() << fullname.value(); // Prints "John Smith age: 41" |
717 | |
718 | firstname = "Emma"; // Marks binding expression as dirty |
719 | |
720 | qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma Smith age: 41" |
721 | |
722 | // Birthday is coming up |
723 | age.setValue(age.value() + 1); |
724 | |
725 | qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma Smith age: 42" |
726 | \endcode |
727 | |
728 | When a new value is assigned to the \c firstname property, the binding |
729 | expression for \c fullname is marked as dirty. So when the last \c qDebug() statement |
730 | tries to read the name value of the \c fullname property, the expression is |
731 | evaluated again, \c firstname() will be called again and return the new value. |
732 | |
733 | Since bindings are C++ lambda expressions, they may do anything that's possible |
734 | in C++. This includes calling other functions. If those functions access values |
735 | held by QProperty, they automatically become dependencies to the binding. |
736 | |
737 | Binding expressions may use properties of any type, so in the above example the age |
738 | is an integer and folded into the string value using conversion to integer, but |
739 | the dependency is fully tracked. |
740 | |
741 | \section1 Tracking properties |
742 | |
743 | Sometimes the relationships between properties cannot be expressed using |
744 | bindings. Instead you may need to run custom code whenever the value of a property |
745 | changes and instead of assigning the value to another property, pass it to |
746 | other parts of your application. For example writing data into a network socket |
747 | or printing debug output. QProperty provides two mechanisms for tracking. |
748 | |
749 | You can register for a callback function to be called whenever the value of |
750 | a property changes, by using onValueChanged(). If you want the callback to also |
751 | be called for the current value of the property, register your callback using |
752 | subscribe() instead. |
753 | */ |
754 | |
755 | /*! |
756 | \fn template <typename T> QProperty<T>::QProperty() |
757 | |
758 | Constructs a property with a default constructed instance of T. |
759 | */ |
760 | |
761 | /*! |
762 | \fn template <typename T> explicit QProperty<T>::QProperty(const T &initialValue) |
763 | |
764 | Constructs a property with the provided \a initialValue. |
765 | */ |
766 | |
767 | /*! |
768 | \fn template <typename T> explicit QProperty<T>::QProperty(T &&initialValue) |
769 | |
770 | Move-Constructs a property with the provided \a initialValue. |
771 | */ |
772 | |
773 | /*! |
774 | \fn template <typename T> QProperty<T>::QProperty(QProperty<T> &&other) |
775 | |
776 | Move-constructs a QProperty instance, making it point at the same object that |
777 | \a other was pointing to. |
778 | */ |
779 | |
780 | /*! |
781 | \fn template <typename T> QProperty<T>::QProperty(const QPropertyBinding<T> &binding) |
782 | |
783 | Constructs a property that is tied to the provided \a binding expression. The |
784 | first time the property value is read, the binding is evaluated. Whenever a |
785 | dependency of the binding changes, the binding will be re-evaluated the next |
786 | time the value of this property is read. |
787 | */ |
788 | |
789 | /*! |
790 | \fn template <typename T> template <typename Functor> QProperty<T>::QProperty(Functor &&f) |
791 | |
792 | Constructs a property that is tied to the provided binding expression \a f. The |
793 | first time the property value is read, the binding is evaluated. Whenever a |
794 | dependency of the binding changes, the binding will be re-evaluated the next |
795 | time the value of this property is read. |
796 | */ |
797 | |
798 | /*! |
799 | \fn template <typename T> QProperty<T>::~QProperty() |
800 | |
801 | Destroys the property. |
802 | */ |
803 | |
804 | /*! |
805 | \fn template <typename T> T QProperty<T>::value() const |
806 | |
807 | Returns the value of the property. This may evaluate a binding expression that |
808 | is tied to this property, before returning the value. |
809 | */ |
810 | |
811 | /*! |
812 | \fn template <typename T> void QProperty<T>::setValue(rvalue_ref newValue) |
813 | \fn template <typename T> void QProperty<T>::setValue(parameter_type newValue) |
814 | |
815 | Assigns \a newValue to this property and removes the property's associated |
816 | binding, if present. |
817 | */ |
818 | |
819 | /*! |
820 | \fn template <typename T> QProperty<T> &QProperty<T>::operator=(rvalue_ref newValue) |
821 | \fn template <typename T> QProperty<T> &QProperty<T>::operator=(parameter_type newValue) |
822 | |
823 | Assigns \a newValue to this property and returns a reference to this QProperty. |
824 | */ |
825 | |
826 | /*! |
827 | \fn template <typename T> QProperty<T> &QProperty<T>::operator=(const QPropertyBinding<T> &newBinding) |
828 | |
829 | Associates the value of this property with the provided \a newBinding |
830 | expression and returns a reference to this property. The first time the |
831 | property value is read, the binding is evaluated. Whenever a dependency of the |
832 | binding changes, the binding will be re-evaluated the next time the value of |
833 | this property is read. |
834 | */ |
835 | |
836 | /*! |
837 | \fn template <typename T> QPropertyBinding<T> QProperty<T>::setBinding(const QPropertyBinding<T> &newBinding) |
838 | |
839 | Associates the value of this property with the provided \a newBinding |
840 | expression and returns the previously associated binding. The first time the |
841 | property value is read, the binding is evaluated. Whenever a dependency of the |
842 | binding changes, the binding will be re-evaluated the next time the value of |
843 | this property is read. |
844 | */ |
845 | |
846 | /*! |
847 | \fn template <typename T> template <typename Functor> QPropertyBinding<T> QProperty<T>::setBinding(Functor f) |
848 | \overload |
849 | |
850 | Associates the value of this property with the provided functor \a f and |
851 | returns the previously associated binding. The first time the property value |
852 | is read, the binding is evaluated by invoking the call operator () of \a f. |
853 | Whenever a dependency of the binding changes, the binding will be re-evaluated |
854 | the next time the value of this property is read. |
855 | */ |
856 | |
857 | /*! |
858 | \fn template <typename T> QPropertyBinding<T> bool QProperty<T>::setBinding(const QUntypedPropertyBinding &newBinding) |
859 | \overload |
860 | |
861 | Associates the value of this property with the provided \a newBinding |
862 | expression. The first time the property value is read, the binding is evaluated. |
863 | Whenever a dependency of the binding changes, the binding will be re-evaluated |
864 | the next time the value of this property is read. |
865 | |
866 | Returns true if the type of this property is the same as the type the binding |
867 | function returns; false otherwise. |
868 | */ |
869 | |
870 | /*! |
871 | \fn template <typename T> QPropertyBinding<T> QProperty<T>::binding() const |
872 | |
873 | Returns the binding expression that is associated with this property. A |
874 | default constructed QPropertyBinding<T> will be returned if no such |
875 | association exists. |
876 | */ |
877 | |
878 | /*! |
879 | \fn template <typename T> QPropertyBinding<T> QProperty<T>::takeBinding() |
880 | |
881 | Disassociates the binding expression from this property and returns it. After |
882 | calling this function, the value of the property will only change if you |
883 | assign a new value to it, or when a new binding is set. |
884 | */ |
885 | |
886 | /*! |
887 | \fn template <typename T> template <typename Functor> QPropertyChangeHandler<T, Functor> QProperty<T>::onValueChanged(Functor f) |
888 | |
889 | Registers the given functor \a f as a callback that shall be called whenever |
890 | the value of the property changes. |
891 | |
892 | The callback \a f is expected to be a type that has a plain call operator () without any |
893 | parameters. This means that you can provide a C++ lambda expression, an std::function |
894 | or even a custom struct with a call operator. |
895 | |
896 | The returned property change handler object keeps track of the registration. When it |
897 | goes out of scope, the callback is de-registered. |
898 | */ |
899 | |
900 | /*! |
901 | \fn template <typename T> template <typename Functor> QPropertyChangeHandler<T, Functor> QProperty<T>::subscribe(Functor f) |
902 | |
903 | Subscribes the given functor \a f as a callback that is called immediately and whenever |
904 | the value of the property changes in the future. |
905 | |
906 | The callback \a f is expected to be a type that has a plain call operator () without any |
907 | parameters. This means that you can provide a C++ lambda expression, an std::function |
908 | or even a custom struct with a call operator. |
909 | |
910 | The returned property change handler object keeps track of the subscription. When it |
911 | goes out of scope, the callback is unsubscribed. |
912 | */ |
913 | |
914 | /*! |
915 | \fn template <typename T> QtPrivate::QPropertyBindingData &QProperty<T>::bindingData() const |
916 | \internal |
917 | */ |
918 | |
919 | /*! |
920 | \class QObjectBindableProperty |
921 | \inmodule QtCore |
922 | \brief The QObjectBindableProperty class is a template class that enables automatic property bindings |
923 | for property data stored in QObject derived classes. |
924 | \since 6.0 |
925 | |
926 | \ingroup tools |
927 | |
928 | QObjectBindableProperty is a generic container that holds an |
929 | instance of T and behaves mostly like \l QProperty. The extra template |
930 | parameters are used to identify the surrounding class and a member function of |
931 | that class. The member function will be called whenever the value held by the |
932 | property changes. |
933 | |
934 | You can use QObjectBindableProperty to add binding support to code that uses Q_PROPERTY. |
935 | The getter and setter methods are easy to adapt for accessing a \l QObjectBindableProperty |
936 | rather than the plain value. In order to invoke the change signal on property changes, use |
937 | QObjectBindableProperty and pass the change signal as a callback. |
938 | |
939 | QObjectBindableProperty is usually not used directly, instead an instance of it is created by |
940 | using the Q_BINDABLE_PROPERTY_DATA macro. |
941 | |
942 | Use the Q_BINDABLE_PROPERTY macro in the class declaration to declare the property as bindable. |
943 | |
944 | \code |
945 | class MyClass : public QObject |
946 | { |
947 | \Q_OBJECT |
948 | Q_PROPERTY(int x READ x WRITE setX NOTIFY xChanged BINDABLE bindableX) |
949 | public: |
950 | int x() const { return xProp; } |
951 | void setX(int x) { xProp = x; } |
952 | Bindable<int> bindableX() { return QBindable<int>(&xProp); } |
953 | |
954 | signals: |
955 | void xChanged(); |
956 | |
957 | private: |
958 | // Declare the instance of the bindable property data. |
959 | Q_OBJECT_BINDABLE_PROPERTY(MyClass, int, xProp, &MyClass::xChanged) |
960 | }; |
961 | \endcode |
962 | */ |
963 | |
964 | /*! |
965 | \fn template <typename Class, typename T, auto offset, auto Callback> QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty() |
966 | |
967 | Constructs a property with a default constructed instance of T. |
968 | */ |
969 | |
970 | /*! |
971 | \fn template <typename Class, typename T, auto offset, auto Callback> explicit QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty(const T &initialValue) |
972 | |
973 | Constructs a property with the provided \a initialValue. |
974 | */ |
975 | |
976 | /*! |
977 | \fn template <typename Class, typename T, auto offset, auto Callback> explicit QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty(T &&initialValue) |
978 | |
979 | Move-Constructs a property with the provided \a initialValue. |
980 | */ |
981 | |
982 | /*! |
983 | \fn template <typename Class, typename T, auto offset, auto Callback> QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty(Class *owner, const QPropertyBinding<T> &binding) |
984 | |
985 | Constructs a property that is tied to the provided \a binding expression. The |
986 | first time the property value is read, the binding is evaluated. Whenever a |
987 | dependency of the binding changes, the binding will be re-evaluated the next |
988 | time the value of this property is read. When the property value changes \a |
989 | owner is notified via the Callback function. |
990 | */ |
991 | |
992 | /*! |
993 | \fn template <typename Class, typename T, auto offset, auto Callback> QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty(Class *owner, QPropertyBinding<T> &&binding) |
994 | |
995 | Constructs a property that is tied to the provided \a binding expression. The |
996 | first time the property value is read, the binding is evaluated. Whenever a |
997 | dependency of the binding changes, the binding will be re-evaluated the next |
998 | time the value of this property is read. When the property value changes \a |
999 | owner is notified via the Callback function. |
1000 | */ |
1001 | |
1002 | |
1003 | /*! |
1004 | \fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty(Functor &&f) |
1005 | |
1006 | Constructs a property that is tied to the provided binding expression \a f. The |
1007 | first time the property value is read, the binding is evaluated. Whenever a |
1008 | dependency of the binding changes, the binding will be re-evaluated the next |
1009 | time the value of this property is read. |
1010 | */ |
1011 | |
1012 | /*! |
1013 | \fn template <typename Class, typename T, auto offset, auto Callback> QObjectBindableProperty<Class, T, offset, Callback>::~QObjectBindableProperty() |
1014 | |
1015 | Destroys the property. |
1016 | */ |
1017 | |
1018 | /*! |
1019 | \fn template <typename Class, typename T, auto offset, auto Callback> T QObjectBindableProperty<Class, T, offset, Callback>::value() const |
1020 | |
1021 | Returns the value of the property. This may evaluate a binding expression that |
1022 | is tied to this property, before returning the value. |
1023 | */ |
1024 | |
1025 | /*! |
1026 | \fn template <typename Class, typename T, auto offset, auto Callback> void QObjectBindableProperty<Class, T, offset, Callback>::setValue(parameter_type newValue) |
1027 | \fn template <typename Class, typename T, auto offset, auto Callback> void QObjectBindableProperty<Class, T, offset, Callback>::setValue(rvalue_ref newValue) |
1028 | |
1029 | Assigns \a newValue to this property and removes the property's associated |
1030 | binding, if present. If the property value changes as a result, calls the |
1031 | Callback function on \a owner. |
1032 | */ |
1033 | |
1034 | /*! |
1035 | \fn template <typename Class, typename T, auto offset, auto Callback> QPropertyBinding<T> QObjectBindableProperty<Class, T, offset, Callback>::setBinding(const QPropertyBinding<T> &newBinding) |
1036 | |
1037 | Associates the value of this property with the provided \a newBinding |
1038 | expression and returns the previously associated binding. The first time the |
1039 | property value is read, the binding is evaluated. Whenever a dependency of the |
1040 | binding changes, the binding will be re-evaluated the next time the value of |
1041 | this property is read. When the property value changes \a owner is notified |
1042 | via the Callback function. |
1043 | */ |
1044 | |
1045 | /*! |
1046 | \fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QPropertyBinding<T> QObjectBindableProperty<Class, T, offset, Callback>::setBinding(Functor f) |
1047 | \overload |
1048 | |
1049 | Associates the value of this property with the provided functor \a f and |
1050 | returns the previously associated binding. The first time the property value |
1051 | is read, the binding is evaluated by invoking the call operator () of \a f. |
1052 | Whenever a dependency of the binding changes, the binding will be re-evaluated |
1053 | the next time the value of this property is read. When the property value |
1054 | changes \a owner is notified via the Callback function. |
1055 | */ |
1056 | |
1057 | /*! |
1058 | \fn template <typename Class, typename T, auto offset, auto Callback> QPropertyBinding<T> bool QObjectBindableProperty<Class, T, offset, Callback>::setBinding(const QUntypedPropertyBinding &newBinding) |
1059 | \overload |
1060 | |
1061 | Associates the value of this property with the provided \a newBinding |
1062 | expression. The first time the property value is read, the binding is evaluated. |
1063 | Whenever a dependency of the binding changes, the binding will be re-evaluated |
1064 | the next time the value of this property is read. |
1065 | |
1066 | Returns \c true if the type of this property is the same as the type the binding |
1067 | function returns; \c false otherwise. |
1068 | */ |
1069 | |
1070 | /*! |
1071 | \fn template <typename Class, typename T, auto offset, auto Callback> bool QObjectBindableProperty<Class, T, offset, Callback>::hasBinding() const |
1072 | |
1073 | Returns true if the property is associated with a binding; false otherwise. |
1074 | */ |
1075 | |
1076 | |
1077 | /*! |
1078 | \fn template <typename Class, typename T, auto offset, auto Callback> QPropertyBinding<T> QObjectBindableProperty<Class, T, offset, Callback>::binding() const |
1079 | |
1080 | Returns the binding expression that is associated with this property. A |
1081 | default constructed QPropertyBinding<T> will be returned if no such |
1082 | association exists. |
1083 | */ |
1084 | |
1085 | /*! |
1086 | \fn template <typename Class, typename T, auto offset, auto Callback> QPropertyBinding<T> QObjectBindableProperty<Class, T, offset, Callback>::takeBinding() |
1087 | |
1088 | Disassociates the binding expression from this property and returns it. After |
1089 | calling this function, the value of the property will only change if you |
1090 | assign a new value to it, or when a new binding is set. |
1091 | */ |
1092 | |
1093 | /*! |
1094 | \fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QPropertyChangeHandler<T, Functor> QObjectBindableProperty<Class, T, offset, Callback>::onValueChanged(Functor f) |
1095 | |
1096 | Registers the given functor \a f as a callback that shall be called whenever |
1097 | the value of the property changes. |
1098 | |
1099 | The callback \a f is expected to be a type that has a plain call operator () without any |
1100 | parameters. This means that you can provide a C++ lambda expression, an std::function |
1101 | or even a custom struct with a call operator. |
1102 | |
1103 | The returned property change handler object keeps track of the registration. When it |
1104 | goes out of scope, the callback is de-registered. |
1105 | */ |
1106 | |
1107 | /*! |
1108 | \fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QPropertyChangeHandler<T, Functor> QObjectBindableProperty<Class, T, offset, Callback>::subscribe(Functor f) |
1109 | |
1110 | Subscribes the given functor \a f as a callback that is called immediately and whenever |
1111 | the value of the property changes in the future. |
1112 | |
1113 | The callback \a f is expected to be a type that has a plain call operator () without any |
1114 | parameters. This means that you can provide a C++ lambda expression, an std::function |
1115 | or even a custom struct with a call operator. |
1116 | |
1117 | The returned property change handler object keeps track of the subscription. When it |
1118 | goes out of scope, the callback is unsubscribed. |
1119 | */ |
1120 | |
1121 | /*! |
1122 | \fn template <typename T> QtPrivate::QPropertyBase &QObjectBindableProperty<Class, T, offset, Callback>::propertyBase() const |
1123 | \internal |
1124 | */ |
1125 | |
1126 | /*! |
1127 | \class QPropertyChangeHandler |
1128 | \inmodule QtCore |
1129 | \brief The QPropertyChangeHandler class controls the lifecycle of change callback installed on a QProperty. |
1130 | |
1131 | \ingroup tools |
1132 | |
1133 | QPropertyChangeHandler\<PropertyType, Functor\> is created when registering a |
1134 | callback on a QProperty to listen to changes to the property's value, using QProperty::onValueChanged |
1135 | and QProperty::subscribe. As long as the change handler is alive, the callback remains installed. |
1136 | |
1137 | A handler instance can be transferred between C++ scopes using move semantics. |
1138 | */ |
1139 | |
1140 | /*! |
1141 | \class QPropertyAlias |
1142 | \inmodule QtCore |
1143 | \brief The QPropertyAlias class is a safe alias for a QProperty with same template parameter. |
1144 | |
1145 | \ingroup tools |
1146 | |
1147 | QPropertyAlias\<T\> wraps a pointer to a QProperty\<T\> and automatically |
1148 | invalidates itself when the QProperty\<T\> is destroyed. It forwards all |
1149 | method invocations to the wrapped property. For example: |
1150 | |
1151 | \code |
1152 | QProperty<QString> *name = new QProperty<QString>("John"); |
1153 | QProperty<int> age(41); |
1154 | |
1155 | QPropertyAlias<QString> nameAlias(name); |
1156 | QPropertyAlias<int> ageAlias(&age); |
1157 | |
1158 | QPropertyAlias<QString> fullname; |
1159 | fullname.setBinding([&]() { return nameAlias.value() + " age:" + QString::number(ageAlias.value()); }); |
1160 | |
1161 | qDebug() << fullname.value(); // Prints "Smith age: 41" |
1162 | |
1163 | *name = "Emma"; // Marks binding expression as dirty |
1164 | |
1165 | qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma age: 41" |
1166 | |
1167 | // Birthday is coming up |
1168 | ageAlias.setValue(age.value() + 1); // Writes the age property through the alias |
1169 | |
1170 | qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma age: 42" |
1171 | |
1172 | delete name; // Leaves the alias in an invalid, but accessible state |
1173 | nameAlias.setValue("Eve"); // Ignored: nameAlias carries a default-constructed QString now |
1174 | |
1175 | ageAlias.setValue(92); |
1176 | qDebug() << fullname.value(); // Re-evaluates the binding expression and prints " age: 92" |
1177 | \endcode |
1178 | */ |
1179 | |
1180 | /*! |
1181 | \fn template <typename T> QPropertyAlias<T>::QPropertyAlias(QProperty<T> *property) |
1182 | |
1183 | Constructs a property alias for the given \a property. |
1184 | */ |
1185 | |
1186 | /*! |
1187 | \fn template <typename T> explicit QPropertyAlias<T>::QPropertyAlias(QPropertyAlias<T> *alias) |
1188 | |
1189 | Constructs a property alias for the property aliased by \a alias. |
1190 | */ |
1191 | |
1192 | /*! |
1193 | \fn template <typename T> T QPropertyAlias<T>::value() const |
1194 | |
1195 | Returns the value of the aliased property. This may evaluate a binding |
1196 | expression that is tied to the property, before returning the value. |
1197 | */ |
1198 | |
1199 | /*! |
1200 | \fn template <typename T> QPropertyAlias<T>::operator T() const |
1201 | |
1202 | Returns the value of the aliased property. This may evaluate a binding |
1203 | expression that is tied to the property, before returning the value. |
1204 | */ |
1205 | |
1206 | /*! |
1207 | \fn template <typename T> void QPropertyAlias<T>::setValue(const T &newValue) |
1208 | |
1209 | Assigns \a newValue to the aliased property and removes the property's |
1210 | associated binding, if present. |
1211 | */ |
1212 | |
1213 | /*! |
1214 | \fn template <typename T> QPropertyAlias<T> &QPropertyAlias<T>::operator=(const T &newValue) |
1215 | |
1216 | Assigns \a newValue to the aliased property and returns a reference to this |
1217 | QPropertyAlias. |
1218 | */ |
1219 | |
1220 | /*! |
1221 | \fn template <typename T> QPropertyAlias<T> &QPropertyAlias<T>::operator=(T &&newValue) |
1222 | \overload |
1223 | |
1224 | Assigns \a newValue to the aliased property and returns a reference to this |
1225 | QPropertyAlias. |
1226 | */ |
1227 | |
1228 | /*! |
1229 | \fn template <typename T> QPropertyAlias<T> &QPropertyAlias<T>::operator=(const QPropertyBinding<T> &newBinding) |
1230 | \overload |
1231 | |
1232 | Associates the value of the aliased property with the provided \a newBinding |
1233 | expression and returns a reference to this alias. The first time the |
1234 | property value is read, either from the property itself or from any alias, the |
1235 | binding is evaluated. Whenever a dependency of the binding changes, the |
1236 | binding will be re-evaluated the next time the value of this property is read. |
1237 | */ |
1238 | |
1239 | /*! |
1240 | \fn template <typename T> QPropertyBinding<T> QPropertyAlias<T>::setBinding(const QPropertyBinding<T> &newBinding) |
1241 | |
1242 | Associates the value of the aliased property with the provided \a newBinding |
1243 | expression and returns any previous binding the associated with the aliased |
1244 | property. The first time the property value is read, either from the property |
1245 | itself or from any alias, the binding is evaluated. Whenever a dependency of |
1246 | the binding changes, the binding will be re-evaluated the next time the value |
1247 | of this property is read. |
1248 | |
1249 | Returns any previous binding associated with the property, or a |
1250 | default-constructed QPropertyBinding<T>. |
1251 | */ |
1252 | |
1253 | /*! |
1254 | \fn template <typename T> QPropertyBinding<T> bool QPropertyAlias<T>::setBinding(const QUntypedPropertyBinding &newBinding) |
1255 | \overload |
1256 | |
1257 | Associates the value of the aliased property with the provided \a newBinding |
1258 | expression. The first time the property value is read, either from the |
1259 | property itself or from any alias, the binding is evaluated. Whenever a |
1260 | dependency of the binding changes, the binding will be re-evaluated the next |
1261 | time the value of this property is read. |
1262 | |
1263 | Returns true if the type of this property is the same as the type the binding |
1264 | function returns; false otherwise. |
1265 | */ |
1266 | |
1267 | /*! |
1268 | \fn template <typename T> template <typename Functor> QPropertyBinding<T> QPropertyAlias<T>::setBinding(Functor f) |
1269 | \overload |
1270 | |
1271 | Associates the value of the aliased property with the provided functor \a f |
1272 | expression. The first time the property value is read, either from the |
1273 | property itself or from any alias, the binding is evaluated. Whenever a |
1274 | dependency of the binding changes, the binding will be re-evaluated the next |
1275 | time the value of this property is read. |
1276 | |
1277 | Returns any previous binding associated with the property, or a |
1278 | default-constructed QPropertyBinding<T>. |
1279 | */ |
1280 | |
1281 | /*! |
1282 | \fn template <typename T> bool QPropertyAlias<T>::hasBinding() const |
1283 | |
1284 | Returns true if the aliased property is associated with a binding; false |
1285 | otherwise. |
1286 | */ |
1287 | |
1288 | /*! |
1289 | \fn template <typename T> QPropertyBinding<T> QPropertyAlias<T>::binding() const |
1290 | |
1291 | Returns the binding expression that is associated with the aliased property. A |
1292 | default constructed QPropertyBinding<T> will be returned if no such |
1293 | association exists. |
1294 | */ |
1295 | |
1296 | /*! |
1297 | \fn template <typename T> QPropertyBinding<T> QPropertyAlias<T>::takeBinding() |
1298 | |
1299 | Disassociates the binding expression from the aliased property and returns it. |
1300 | After calling this function, the value of the property will only change if |
1301 | you assign a new value to it, or when a new binding is set. |
1302 | */ |
1303 | |
1304 | /*! |
1305 | \fn template <typename T> template <typename Functor> QPropertyChangeHandler<T, Functor> QPropertyAlias<T>::onValueChanged(Functor f) |
1306 | |
1307 | Registers the given functor \a f as a callback that shall be called whenever |
1308 | the value of the aliased property changes. |
1309 | |
1310 | The callback \a f is expected to be a type that has a plain call operator () without any |
1311 | parameters. This means that you can provide a C++ lambda expression, an std::function |
1312 | or even a custom struct with a call operator. |
1313 | |
1314 | The returned property change handler object keeps track of the registration. When it |
1315 | goes out of scope, the callback is de-registered. |
1316 | */ |
1317 | |
1318 | /*! |
1319 | \fn template <typename T> template <typename Functor> QPropertyChangeHandler<T, Functor> QPropertyAlias<T>::subscribe(Functor f) |
1320 | |
1321 | Subscribes the given functor \a f as a callback that is called immediately and whenever |
1322 | the value of the aliased property changes in the future. |
1323 | |
1324 | The callback \a f is expected to be a type that has a plain call operator () without any |
1325 | parameters. This means that you can provide a C++ lambda expression, an std::function |
1326 | or even a custom struct with a call operator. |
1327 | |
1328 | The returned property change handler object keeps track of the subscription. When it |
1329 | goes out of scope, the callback is unsubscribed. |
1330 | */ |
1331 | |
1332 | /*! |
1333 | \fn template <typename T> bool QPropertyAlias<T>::isValid() const |
1334 | |
1335 | Returns true if the aliased property still exists; false otherwise. |
1336 | |
1337 | If the aliased property doesn't exist, all other method calls are ignored. |
1338 | */ |
1339 | |
1340 | struct QBindingStorageData |
1341 | { |
1342 | size_t size = 0; |
1343 | size_t used = 0; |
1344 | // Pair[] pairs; |
1345 | }; |
1346 | |
1347 | struct QBindingStoragePrivate |
1348 | { |
1349 | // This class basically implements a simple and fast hash map to store bindings for a QObject |
1350 | // The reason that we're not using QHash is that QPropertyBindingData can not be copied, only |
1351 | // moved. That doesn't work well together with an implicitly shared class. |
1352 | struct Pair |
1353 | { |
1354 | QUntypedPropertyData *data; |
1355 | QPropertyBindingData bindingData; |
1356 | }; |
1357 | static_assert(alignof(Pair) == alignof(void *)); |
1358 | static_assert(alignof(size_t) == alignof(void *)); |
1359 | |
1360 | QBindingStorageData *&d; |
1361 | |
1362 | static inline Pair *pairs(QBindingStorageData *dd) |
1363 | { |
1364 | Q_ASSERT(dd); |
1365 | return reinterpret_cast<Pair *>(dd + 1); |
1366 | } |
1367 | void reallocate(size_t newSize) |
1368 | { |
1369 | Q_ASSERT(!d || newSize > d->size); |
1370 | size_t allocSize = sizeof(QBindingStorageData) + newSize*sizeof(Pair); |
1371 | void *nd = malloc(allocSize); |
1372 | memset(nd, 0, allocSize); |
1373 | QBindingStorageData *newData = new (nd) QBindingStorageData; |
1374 | newData->size = newSize; |
1375 | if (!d) { |
1376 | d = newData; |
1377 | return; |
1378 | } |
1379 | newData->used = d->used; |
1380 | Pair *p = pairs(d); |
1381 | for (size_t i = 0; i < d->size; ++i, ++p) { |
1382 | if (p->data) { |
1383 | Pair *pp = pairs(newData); |
1384 | Q_ASSERT(newData->size && (newData->size & (newData->size - 1)) == 0); // size is a power of two |
1385 | size_t index = qHash(p->data) & (newData->size - 1); |
1386 | while (pp[index].data) { |
1387 | ++index; |
1388 | if (index == newData->size) |
1389 | index = 0; |
1390 | } |
1391 | new (pp + index) Pair{p->data, QPropertyBindingData(std::move(p->bindingData))}; |
1392 | } |
1393 | } |
1394 | // data has been moved, no need to call destructors on old Pairs |
1395 | free(d); |
1396 | d = newData; |
1397 | } |
1398 | |
1399 | QBindingStoragePrivate(QBindingStorageData *&_d) : d(_d) {} |
1400 | |
1401 | QPropertyBindingData *get(const QUntypedPropertyData *data) |
1402 | { |
1403 | if (!d) |
1404 | return nullptr; |
1405 | Q_ASSERT(d->size && (d->size & (d->size - 1)) == 0); // size is a power of two |
1406 | size_t index = qHash(data) & (d->size - 1); |
1407 | Pair *p = pairs(d); |
1408 | while (p[index].data) { |
1409 | if (p[index].data == data) |
1410 | return &p[index].bindingData; |
1411 | ++index; |
1412 | if (index == d->size) |
1413 | index = 0; |
1414 | } |
1415 | return nullptr; |
1416 | } |
1417 | QPropertyBindingData *getAndCreate(QUntypedPropertyData *data) |
1418 | { |
1419 | if (!d) |
1420 | reallocate(8); |
1421 | else if (d->used*2 >= d->size) |
1422 | reallocate(d->size*2); |
1423 | Q_ASSERT(d->size && (d->size & (d->size - 1)) == 0); // size is a power of two |
1424 | size_t index = qHash(data) & (d->size - 1); |
1425 | Pair *p = pairs(d); |
1426 | while (p[index].data) { |
1427 | if (p[index].data == data) |
1428 | return &p[index].bindingData; |
1429 | ++index; |
1430 | if (index == d->size) |
1431 | index = 0; |
1432 | } |
1433 | ++d->used; |
1434 | new (p + index) Pair{data, QPropertyBindingData()}; |
1435 | return &p[index].bindingData; |
1436 | } |
1437 | |
1438 | void destroy() |
1439 | { |
1440 | if (!d) |
1441 | return; |
1442 | Pair *p = pairs(d); |
1443 | for (size_t i = 0; i < d->size; ++i) { |
1444 | if (p->data) |
1445 | p->~Pair(); |
1446 | ++p; |
1447 | } |
1448 | free(d); |
1449 | } |
1450 | }; |
1451 | |
1452 | /*! |
1453 | \class QBindingStorage |
1454 | \internal |
1455 | |
1456 | QBindingStorage acts as a storage for property binding related data in QObject. |
1457 | Any property in a QObject can be made bindable, by using the Q_BINDABLE_PROPERTY_DATA |
1458 | macro to declare the data storage. Then implement a setter and getter for the property |
1459 | and declare it as a Q_PROPERTY as usual. Finally make it bindable, but using |
1460 | the Q_BINDABLE_PROPERTY macro after the declaration of the setter and getter. |
1461 | */ |
1462 | |
1463 | QBindingStorage::QBindingStorage() |
1464 | { |
1465 | bindingStatus = &QT_PREPEND_NAMESPACE(bindingStatus); |
1466 | Q_ASSERT(bindingStatus); |
1467 | } |
1468 | |
1469 | QBindingStorage::~QBindingStorage() |
1470 | { |
1471 | QBindingStoragePrivate(d).destroy(); |
1472 | } |
1473 | |
1474 | void QBindingStorage::maybeUpdateBindingAndRegister(const QUntypedPropertyData *data) const |
1475 | { |
1476 | Q_ASSERT(bindingStatus); |
1477 | QUntypedPropertyData *dd = const_cast<QUntypedPropertyData *>(data); |
1478 | auto storage = bindingStatus->currentlyEvaluatingBinding ? |
1479 | QBindingStoragePrivate(d).getAndCreate(dd) : |
1480 | QBindingStoragePrivate(d).get(dd); |
1481 | if (!storage) |
1482 | return; |
1483 | if (auto *binding = storage->binding()) |
1484 | binding->evaluateIfDirtyAndReturnTrueIfValueChanged(const_cast<QUntypedPropertyData *>(data)); |
1485 | storage->registerWithCurrentlyEvaluatingBinding(); |
1486 | } |
1487 | |
1488 | QPropertyBindingData *QBindingStorage::bindingData(const QUntypedPropertyData *data) const |
1489 | { |
1490 | return QBindingStoragePrivate(d).get(data); |
1491 | } |
1492 | |
1493 | QPropertyBindingData *QBindingStorage::bindingData(QUntypedPropertyData *data, bool create) |
1494 | { |
1495 | auto storage = create ? |
1496 | QBindingStoragePrivate(d).getAndCreate(data) : |
1497 | QBindingStoragePrivate(d).get(data); |
1498 | return storage; |
1499 | } |
1500 | |
1501 | QT_END_NAMESPACE |
1502 | |