1 /* 2 * DQt - D bindings for the Qt Toolkit 3 * 4 * GNU Lesser General Public License Usage 5 * This file may be used under the terms of the GNU Lesser 6 * General Public License version 3 as published by the Free Software 7 * Foundation and appearing in the file LICENSE.LGPL3 included in the 8 * packaging of this file. Please review the following information to 9 * ensure the GNU Lesser General Public License version 3 requirements 10 * will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 11 */ 12 module qt.core.propertyprivate; 13 extern(C++): 14 15 import qt.config; 16 import qt.core.bindingstorage; 17 import qt.core.global; 18 import qt.core.metatype; 19 import qt.core.property; 20 import qt.core.taggedpointer; 21 import qt.helpers; 22 23 // 24 // W A R N I N G 25 // ------------- 26 // 27 // This file is not part of the Qt API. It exists purely as an 28 // implementation detail. This header file may change from version to 29 // version without notice, or even be removed. 30 // 31 // We mean it. 32 // 33 34 35 36 /+ template<typename Class, typename T, auto Offset, auto Setter, auto Signal> 37 class QObjectCompatProperty; +/ 38 39 extern(C++, "QtPrivate") { 40 // QPropertyBindingPrivatePtr operates on a RefCountingMixin solely so that we can inline 41 // the constructor and copy constructor 42 struct RefCounted { 43 int ref_ = 0; 44 void addRef() {++ref_;} 45 bool deref() {--ref_; return (ref_) != 0;} 46 } 47 } 48 49 extern(C++, class) struct QQmlPropertyBinding; 50 extern(C++, class) struct QPropertyBindingPrivate; 51 extern(C++, class) struct QPropertyBindingPrivatePtr 52 { 53 public: 54 alias T = /+ QtPrivate:: +/RefCounted; 55 ref T opUnary(string op)() const if(op == "*") { return *d; } 56 /+T* operator ->()/+ noexcept+/ { return d; }+/ 57 /+T* operator ->() const/+ noexcept+/ { return d; }+/ 58 /+/+ explicit +/ auto opCast(T : T)() { return d; }+/ 59 /+/+ explicit +/ auto opCast(T : const(T))() const/+ noexcept+/ { return d; }+/ 60 T* data() /*const*/ /+ noexcept+/ { return d; } 61 T* get() /*const*/ /+ noexcept+/ { return d; } 62 const(T)* constData() const/+ noexcept+/ { return d; } 63 T* take()/+ noexcept+/ { T* x = d; d = null; return x; } 64 65 @disable this(); 66 /+this()/+ noexcept+/ 67 { 68 this.d = null; 69 }+/ 70 ~this() 71 { 72 if (d && (--d.ref_ == 0)) 73 destroyAndFreeMemory(); 74 } 75 /+ Q_CORE_EXPORT +/ void destroyAndFreeMemory(); 76 77 /+ explicit +/this(T* data)/+ noexcept+/ 78 { 79 this.d = data; 80 if (d) d.addRef(); 81 } 82 this(this) 83 { 84 if (d) d.addRef(); 85 } 86 87 //void reset(T* ptr = null)/+ noexcept+/; 88 89 /+ref QPropertyBindingPrivatePtr operator =(ref const(QPropertyBindingPrivatePtr) o)/+ noexcept+/ 90 { 91 reset(o.d); 92 return this; 93 }+/ 94 /+ref QPropertyBindingPrivatePtr operator =(T* o)/+ noexcept+/ 95 { 96 reset(o); 97 return this; 98 }+/ 99 /+ QPropertyBindingPrivatePtr(QPropertyBindingPrivatePtr &&o) noexcept : d(qExchange(o.d, nullptr)) {} +/ 100 /+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QPropertyBindingPrivatePtr) +/ 101 102 /+auto opCast(T : bool) () const/+ noexcept+/ { return d !is null; }+/ 103 /+bool operator !() const/+ noexcept+/ { return d is null; }+/ 104 105 /+ void swap(QPropertyBindingPrivatePtr &other) noexcept 106 { qSwap(d, other.d); } +/ 107 108 /+ friend bool operator==(const QPropertyBindingPrivatePtr &p1, const QPropertyBindingPrivatePtr &p2) noexcept 109 { return p1.d == p2.d; } +/ 110 /+ friend bool operator!=(const QPropertyBindingPrivatePtr &p1, const QPropertyBindingPrivatePtr &p2) noexcept 111 { return p1.d != p2.d; } +/ 112 /+ friend bool operator==(const QPropertyBindingPrivatePtr &p1, const T *ptr) noexcept 113 { return p1.d == ptr; } +/ 114 /+ friend bool operator!=(const QPropertyBindingPrivatePtr &p1, const T *ptr) noexcept 115 { return p1.d != ptr; } +/ 116 /+ friend bool operator==(const T *ptr, const QPropertyBindingPrivatePtr &p2) noexcept 117 { return ptr == p2.d; } +/ 118 /+ friend bool operator!=(const T *ptr, const QPropertyBindingPrivatePtr &p2) noexcept 119 { return ptr != p2.d; } +/ 120 /+ friend bool operator==(const QPropertyBindingPrivatePtr &p1, std::nullptr_t) noexcept 121 { return !p1; } +/ 122 /+ friend bool operator!=(const QPropertyBindingPrivatePtr &p1, std::nullptr_t) noexcept 123 { return p1; } +/ 124 /+ friend bool operator==(std::nullptr_t, const QPropertyBindingPrivatePtr &p2) noexcept 125 { return !p2; } +/ 126 /+ friend bool operator!=(std::nullptr_t, const QPropertyBindingPrivatePtr &p2) noexcept 127 { return p2; } +/ 128 129 private: 130 /+ QtPrivate:: +/RefCounted* d = null; 131 mixin(CREATE_CONVENIENCE_WRAPPERS); 132 } 133 134 struct QPropertyBindingDataPointer; 135 136 extern(C++, class) struct QUntypedPropertyData 137 { 138 public: 139 // sentinel to check whether a class inherits QUntypedPropertyData 140 struct InheritsQUntypedPropertyData {} 141 } 142 143 144 // Used for grouped property evaluations 145 /+ namespace QtPrivate { 146 class QPropertyBindingData} +/ 147 struct QPropertyProxyBindingData 148 { 149 // acts as QPropertyBindingData::d_ptr 150 quintptr d_ptr; 151 /* 152 The two members below store the original binding data and property 153 data pointer of the property which gets proxied. 154 They are set in QPropertyDelayedNotifications::addProperty 155 */ 156 const(/+ QtPrivate:: +/QPropertyBindingData)* originalBindingData; 157 QUntypedPropertyData* propertyData; 158 } 159 160 extern(C++, "QtPrivate") { 161 162 struct BindingFunctionVTable 163 { 164 alias CallFn = ExternCPPFunc!(bool function(QMetaType, QUntypedPropertyData* , void* )); 165 alias DtorFn = ExternCPPFunc!(void function(void* )); 166 alias MoveCtrFn = ExternCPPFunc!(void function(void* , void* )); 167 const(CallFn) call; 168 const(DtorFn) destroy; 169 const(MoveCtrFn) moveConstruct; 170 const(qsizetype) size; 171 172 /+ template<typename Callable, typename PropertyType=void> +/ 173 /+ static constexpr BindingFunctionVTable createFor() 174 { 175 static_assert (alignof(Callable) <= alignof(std::max_align_t), "Bindings do not support overaligned functors!"); 176 return { 177 /*call=*/[](QMetaType metaType, QUntypedPropertyData *dataPtr, void *f){ 178 if constexpr (!std::is_invocable_v<Callable>) { 179 // we got an untyped callable 180 static_assert (std::is_invocable_r_v<bool, Callable, QMetaType, QUntypedPropertyData *> ); 181 auto untypedEvaluationFunction = static_cast<Callable *>(f); 182 return std::invoke(*untypedEvaluationFunction, metaType, dataPtr); 183 } else if constexpr (!std::is_same_v<PropertyType, void>) { // check for void to workaround MSVC issue 184 Q_UNUSED(metaType); 185 QPropertyData<PropertyType> *propertyPtr = static_cast<QPropertyData<PropertyType> *>(dataPtr); 186 // That is allowed by POSIX even if Callable is a function pointer 187 auto evaluationFunction = static_cast<Callable *>(f); 188 PropertyType newValue = std::invoke(*evaluationFunction); 189 if constexpr (QTypeTraits::has_operator_equal_v<PropertyType>) { 190 if (newValue == propertyPtr->valueBypassingBindings()) 191 return false; 192 } 193 propertyPtr->setValueBypassingBindings(std::move(newValue)); 194 return true; 195 } else { 196 // Our code will never instantiate this 197 Q_UNREACHABLE(); 198 return false; 199 } 200 }, 201 /*destroy*/[](void *f){ static_cast<Callable *>(f)->~Callable(); }, 202 /*moveConstruct*/[](void *addr, void *other){ 203 new (addr) Callable(std::move(*static_cast<Callable *>(other))); 204 }, 205 /*size*/sizeof(Callable) 206 }; 207 } +/ 208 } 209 210 /+ template<typename Callable, typename PropertyType=void> 211 inline constexpr BindingFunctionVTable bindingFunctionVTable = BindingFunctionVTable::createFor<Callable, PropertyType>(); +/ 212 213 214 // writes binding result into dataPtr 215 struct QPropertyBindingFunction { 216 const(/+ QtPrivate:: +/BindingFunctionVTable)* vtable; 217 void* functor; 218 } 219 220 alias QPropertyObserverCallback = ExternCPPFunc!(void function(QUntypedPropertyData* )); 221 alias QPropertyBindingWrapper = ExternCPPFunc!(bool function(QMetaType, QUntypedPropertyData* dataPtr, QPropertyBindingFunction)); 222 223 /*! 224 \internal 225 A property normally consists of the actual property value and metadata for the binding system. 226 QPropertyBindingData is the latter part. It stores a pointer to either 227 - a (potentially empty) linked list of notifiers, in case there is no binding set, 228 - an actual QUntypedPropertyBinding when the property has a binding, 229 - or a pointer to QPropertyProxyBindingData when notifications occur inside a grouped update. 230 231 \sa QPropertyDelayedNotifications, beginPropertyUpdateGroup 232 */ 233 extern(C++, class) struct /+ Q_CORE_EXPORT +/ QPropertyBindingData 234 { 235 private: 236 // Mutable because the address of the observer of the currently evaluating binding is stored here, for 237 // notification later when the value changes. 238 /+ mutable +/ quintptr d_ptr = 0; 239 /+ friend struct QT_PREPEND_NAMESPACE(QPropertyBindingDataPointer); +/ 240 /+ friend class QT_PREPEND_NAMESPACE(QQmlPropertyBinding); +/ 241 /+ friend struct QT_PREPEND_NAMESPACE(QPropertyDelayedNotifications); +/ 242 243 /+ template<typename Class, typename T, auto Offset, auto Setter, auto Signal> +/ 244 /+ friend class QT_PREPEND_NAMESPACE(QObjectCompatProperty); +/ 245 246 /+ Q_DISABLE_COPY(QPropertyBindingData) +/ 247 @disable this(this); 248 /+this(ref const(QPropertyBindingData));+//+ref QPropertyBindingData operator =(ref const(QPropertyBindingData));+/public: 249 /+ QPropertyBindingData() = default; +/ 250 /+ QPropertyBindingData(QPropertyBindingData &&other); +/ 251 /+ QPropertyBindingData &operator=(QPropertyBindingData &&other) = delete; +/ 252 ~this(); 253 254 // Is d_ptr pointing to a binding (1) or list of notifiers (0)? 255 extern(D) static immutable quintptr BindingBit = 0x1; 256 // Is d_ptr pointing to QPropertyProxyBindingData (1) or to an actual binding/list of notifiers? 257 extern(D) static immutable quintptr DelayedNotificationBit = 0x2; 258 259 bool hasBinding() const { return (d_ptr & BindingBit) != 0; } 260 bool isNotificationDelayed() const { return (d_ptr & DelayedNotificationBit) != 0; } 261 262 QUntypedPropertyBinding setBinding(ref const(QUntypedPropertyBinding) newBinding, 263 QUntypedPropertyData* propertyDataPtr, 264 QPropertyObserverCallback staticObserverCallback = null, 265 QPropertyBindingWrapper bindingWrapper = null); 266 267 QPropertyBindingPrivate* binding() /*const*/ 268 { 269 quintptr dd = d(); 270 if (dd & BindingBit) 271 return reinterpret_cast!(QPropertyBindingPrivate*)(dd - BindingBit); 272 return null; 273 274 } 275 276 void evaluateIfDirty(const(QUntypedPropertyData)* ) const; // ### Kept for BC reasons, unused 277 278 void removeBinding() 279 { 280 if (hasBinding()) 281 removeBinding_helper(); 282 } 283 284 void registerWithCurrentlyEvaluatingBinding(/+ QtPrivate:: +/qt.core.bindingstorage.BindingEvaluationState* currentBinding) const 285 { 286 if (!currentBinding) 287 return; 288 registerWithCurrentlyEvaluatingBinding_helper(currentBinding); 289 } 290 void registerWithCurrentlyEvaluatingBinding() const; 291 void notifyObservers(QUntypedPropertyData* propertyDataPtr) const; 292 void notifyObservers(QUntypedPropertyData* propertyDataPtr, QBindingStorage* storage) const; 293 private: 294 /*! 295 \internal 296 Returns a reference to d_ptr, except when d_ptr points to a proxy. 297 In that case, a reference to proxy->d_ptr is returned instead. 298 299 To properly support proxying, direct access to d_ptr only occurs when 300 - a function actually deals with proxying (e.g. 301 QPropertyDelayedNotifications::addProperty), 302 - only the tag value is accessed (e.g. hasBinding) or 303 - inside a constructor. 304 */ 305 ref quintptr d_ref() /*const*/ return 306 { 307 quintptr *d = &d_ptr; 308 if (isNotificationDelayed()) 309 return reinterpret_cast!(QPropertyProxyBindingData*)(d_ptr & ~(BindingBit|DelayedNotificationBit)).d_ptr; 310 return *d; 311 } 312 quintptr d() /*const*/ { return d_ref(); } 313 void registerWithCurrentlyEvaluatingBinding_helper(qt.core.bindingstorage.BindingEvaluationState* currentBinding) const; 314 void removeBinding_helper(); 315 } 316 317 extern(C++, class) struct QTagPreservingPointerToPointer(T, Tag) 318 { 319 public: 320 /+ constexpr QTagPreservingPointerToPointer() = default; +/ 321 322 this(T** ptr) 323 { 324 this.d = reinterpret_cast!(quintptr*)(ptr); 325 } 326 327 /+ref QTagPreservingPointerToPointer!(T, Tag) operator =(T** ptr) 328 { 329 d = reinterpret_cast!(quintptr*)(ptr); 330 return this; 331 }+/ 332 333 /+ref QTagPreservingPointerToPointer!(T, Tag) operator =(QTaggedPointer!(T, Tag)* ptr) 334 { 335 d = reinterpret_cast!(quintptr*)(ptr); 336 return this; 337 }+/ 338 339 void clear() 340 { 341 d = null; 342 } 343 344 void setPointer(T* ptr) 345 { 346 *d = reinterpret_cast!(quintptr)(ptr) | (*d & QTaggedPointer!(T, Tag).tagMask()); 347 } 348 349 T* get() const 350 { 351 return reinterpret_cast!(T*)(*d & QTaggedPointer!(T, Tag).pointerMask()); 352 } 353 354 /+/+ explicit +/ auto opCast(T : bool)() const 355 { 356 return d !is null; 357 }+/ 358 359 private: 360 quintptr* d = null; 361 } 362 363 extern(C++, "detail") { 364 /+ template <typename F> 365 struct ExtractClassFromFunctionPointer; 366 367 template<typename T, typename C> 368 struct ExtractClassFromFunctionPointer<T C::*> { using Class = C; }; +/ 369 370 size_t getOffset(size_t o) 371 { 372 return o; 373 } 374 size_t getOffset(ExternCPPFunc!(size_t function()) offsetFn) 375 { 376 return offsetFn(); 377 } 378 } 379 380 } // namespace QtPrivate 381