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