1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
2 | // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial |
3 | |
4 | #pragma once |
5 | #include <string_view> |
6 | #include <memory> |
7 | |
8 | namespace slint::cbindgen_private { |
9 | struct PropertyAnimation; |
10 | } |
11 | |
12 | #include "slint_properties_internal.h" |
13 | #include "slint_builtin_structs_internal.h" |
14 | |
15 | namespace slint::private_api { |
16 | |
17 | using cbindgen_private::StateInfo; |
18 | |
19 | inline void slint_property_set_animated_binding_helper( |
20 | const cbindgen_private::PropertyHandleOpaque *handle, void (*binding)(void *, int *), |
21 | void *user_data, void (*drop_user_data)(void *), |
22 | const cbindgen_private::PropertyAnimation *animation_data, |
23 | cbindgen_private::PropertyAnimation (*transition_data)(void *, uint64_t *)) |
24 | { |
25 | cbindgen_private::slint_property_set_animated_binding_int( |
26 | handle, binding, user_data, drop_user_data, animation_data, transition_data); |
27 | } |
28 | |
29 | inline void slint_property_set_animated_binding_helper( |
30 | const cbindgen_private::PropertyHandleOpaque *handle, void (*binding)(void *, float *), |
31 | void *user_data, void (*drop_user_data)(void *), |
32 | const cbindgen_private::PropertyAnimation *animation_data, |
33 | cbindgen_private::PropertyAnimation (*transition_data)(void *, uint64_t *)) |
34 | { |
35 | cbindgen_private::slint_property_set_animated_binding_float( |
36 | handle, binding, user_data, drop_user_data, animation_data, transition_data); |
37 | } |
38 | |
39 | inline void slint_property_set_animated_binding_helper( |
40 | const cbindgen_private::PropertyHandleOpaque *handle, void (*binding)(void *, Color *), |
41 | void *user_data, void (*drop_user_data)(void *), |
42 | const cbindgen_private::PropertyAnimation *animation_data, |
43 | cbindgen_private::PropertyAnimation (*transition_data)(void *, uint64_t *)) |
44 | { |
45 | cbindgen_private::slint_property_set_animated_binding_color( |
46 | handle, binding, user_data, drop_user_data, animation_data, transition_data); |
47 | } |
48 | |
49 | inline void slint_property_set_animated_binding_helper( |
50 | const cbindgen_private::PropertyHandleOpaque *handle, void (*binding)(void *, Brush *), |
51 | void *user_data, void (*drop_user_data)(void *), |
52 | const cbindgen_private::PropertyAnimation *animation_data, |
53 | cbindgen_private::PropertyAnimation (*transition_data)(void *, uint64_t *)) |
54 | { |
55 | cbindgen_private::slint_property_set_animated_binding_brush( |
56 | handle, binding, user_data, drop_user_data, animation_data, transition_data); |
57 | } |
58 | |
59 | template<typename T> |
60 | struct Property |
61 | { |
62 | Property() { cbindgen_private::slint_property_init(out: &inner); } |
63 | ~Property() { cbindgen_private::slint_property_drop(handle: &inner); } |
64 | Property(const Property &) = delete; |
65 | Property(Property &&) = delete; |
66 | Property &operator=(const Property &) = delete; |
67 | explicit Property(const T &value) : value(value) |
68 | { |
69 | cbindgen_private::slint_property_init(out: &inner); |
70 | } |
71 | |
72 | /* Should it be implicit? |
73 | void operator=(const T &value) { |
74 | set(value); |
75 | }*/ |
76 | |
77 | void set(const T &value) const |
78 | { |
79 | if ((inner._0 & 0b10) == 0b10 || this->value != value) { |
80 | this->value = value; |
81 | cbindgen_private::slint_property_set_changed(handle: &inner, value: &this->value); |
82 | } |
83 | } |
84 | |
85 | const T &get() const |
86 | { |
87 | cbindgen_private::slint_property_update(handle: &inner, val: &value); |
88 | return value; |
89 | } |
90 | |
91 | template<typename F> |
92 | void set_binding(F binding) const |
93 | { |
94 | cbindgen_private::slint_property_set_binding( |
95 | handle: &inner, |
96 | binding: [](void *user_data, void *value) { |
97 | *reinterpret_cast<T *>(value) = (*reinterpret_cast<F *>(user_data))(); |
98 | }, |
99 | user_data: new F(binding), drop_user_data: [](void *user_data) { delete reinterpret_cast<F *>(user_data); }, |
100 | intercept_set: nullptr, intercept_set_binding: nullptr); |
101 | } |
102 | |
103 | inline void set_animated_value(const T &value, |
104 | const cbindgen_private::PropertyAnimation &animation_data) const; |
105 | template<typename F> |
106 | inline void |
107 | set_animated_binding(F binding, const cbindgen_private::PropertyAnimation &animation_data) const |
108 | { |
109 | private_api::slint_property_set_animated_binding_helper( |
110 | &inner, |
111 | [](void *user_data, T *value) { |
112 | *reinterpret_cast<T *>(value) = (*reinterpret_cast<F *>(user_data))(); |
113 | }, |
114 | new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); }, |
115 | &animation_data, nullptr); |
116 | } |
117 | |
118 | template<typename F, typename Trans> |
119 | inline void set_animated_binding_for_transition(F binding, Trans animation) const |
120 | { |
121 | struct UserData |
122 | { |
123 | F binding; |
124 | Trans animation; |
125 | }; |
126 | private_api::slint_property_set_animated_binding_helper( |
127 | &inner, |
128 | [](void *user_data, T *value) { |
129 | *reinterpret_cast<T *>(value) = |
130 | reinterpret_cast<UserData *>(user_data)->binding(); |
131 | }, |
132 | new UserData { binding, animation }, |
133 | [](void *user_data) { delete reinterpret_cast<UserData *>(user_data); }, nullptr, |
134 | [](void *user_data, uint64_t *instant) { |
135 | return reinterpret_cast<UserData *>(user_data)->animation(instant); |
136 | }); |
137 | } |
138 | |
139 | bool is_dirty() const { return cbindgen_private::slint_property_is_dirty(handle: &inner); } |
140 | void mark_dirty() const { cbindgen_private::slint_property_mark_dirty(handle: &inner); } |
141 | |
142 | static void link_two_way(const Property<T> *p1, const Property<T> *p2) |
143 | { |
144 | auto value = p2->get(); |
145 | cbindgen_private::PropertyHandleOpaque handle {}; |
146 | if ((p2->inner._0 & 0b10) == 0b10) { |
147 | std::swap(handle, const_cast<Property<T> *>(p2)->inner); |
148 | } |
149 | auto common_property = std::make_shared<Property<T>>(handle, std::move(value)); |
150 | struct TwoWayBinding |
151 | { |
152 | std::shared_ptr<Property<T>> common_property; |
153 | }; |
154 | auto del_fn = [](void *user_data) { delete reinterpret_cast<TwoWayBinding *>(user_data); }; |
155 | auto call_fn = [](void *user_data, void *value) { |
156 | *reinterpret_cast<T *>(value) = |
157 | reinterpret_cast<TwoWayBinding *>(user_data)->common_property->get(); |
158 | }; |
159 | auto intercept_fn = [](void *user_data, const void *value) { |
160 | reinterpret_cast<TwoWayBinding *>(user_data)->common_property->set( |
161 | *reinterpret_cast<const T *>(value)); |
162 | return true; |
163 | }; |
164 | auto intercept_binding_fn = [](void *user_data, void *value) { |
165 | cbindgen_private::slint_property_set_binding_internal( |
166 | handle: &reinterpret_cast<TwoWayBinding *>(user_data)->common_property->inner, binding: value); |
167 | return true; |
168 | }; |
169 | cbindgen_private::slint_property_set_binding(handle: &p1->inner, binding: call_fn, |
170 | user_data: new TwoWayBinding { common_property }, drop_user_data: del_fn, |
171 | intercept_set: intercept_fn, intercept_set_binding: intercept_binding_fn); |
172 | cbindgen_private::slint_property_set_binding(handle: &p2->inner, binding: call_fn, |
173 | user_data: new TwoWayBinding { common_property }, drop_user_data: del_fn, |
174 | intercept_set: intercept_fn, intercept_set_binding: intercept_binding_fn); |
175 | } |
176 | |
177 | /// Internal (private) constructor used by link_two_way |
178 | explicit Property(cbindgen_private::PropertyHandleOpaque inner, T value) |
179 | : inner(inner), value(std::move(value)) |
180 | { |
181 | } |
182 | |
183 | private: |
184 | cbindgen_private::PropertyHandleOpaque inner; |
185 | mutable T value {}; |
186 | template<typename F> |
187 | friend void set_state_binding(const Property<StateInfo> &property, F binding); |
188 | }; |
189 | |
190 | template<> |
191 | inline void Property<int32_t>::set_animated_value( |
192 | const int32_t &new_value, const cbindgen_private::PropertyAnimation &animation_data) const |
193 | { |
194 | cbindgen_private::slint_property_set_animated_value_int(handle: &inner, from: value, to: new_value, |
195 | animation_data: &animation_data); |
196 | } |
197 | |
198 | template<> |
199 | inline void |
200 | Property<float>::set_animated_value(const float &new_value, |
201 | const cbindgen_private::PropertyAnimation &animation_data) const |
202 | { |
203 | cbindgen_private::slint_property_set_animated_value_float(handle: &inner, from: value, to: new_value, |
204 | animation_data: &animation_data); |
205 | } |
206 | |
207 | template<typename F> |
208 | void set_state_binding(const Property<StateInfo> &property, F binding) |
209 | { |
210 | cbindgen_private::slint_property_set_state_binding( |
211 | handle: &property.inner, |
212 | binding: [](void *user_data) -> int32_t { return (*reinterpret_cast<F *>(user_data))(); }, |
213 | user_data: new F(binding), drop_user_data: [](void *user_data) { delete reinterpret_cast<F *>(user_data); }); |
214 | } |
215 | |
216 | /// PropertyTracker allows keeping track of when properties change and lazily evaluate code |
217 | /// if necessary. |
218 | /// Once constructed, you can call evaluate() with a functor that will be invoked. Any |
219 | /// Property<T> types that have their value read from within the invoked functor or any code that's |
220 | /// reached from there are added to internal book-keeping. When after returning from evaluate(), |
221 | /// any of these accessed properties change their value, the property tracker's is_dirt() function |
222 | /// will return true. |
223 | /// |
224 | /// PropertyTracker instances nest, so if during the evaluation of one tracker, another tracker's |
225 | /// evaluate() function gets called and properties from within that evaluation change their value |
226 | /// later, both tracker instances will report true for is_dirty(). If you would like to disable the |
227 | /// nesting, use the evaluate_as_dependency_root() function instead. |
228 | struct PropertyTracker |
229 | { |
230 | /// Constructs a new property tracker instance. |
231 | PropertyTracker() { cbindgen_private::slint_property_tracker_init(out: &inner); } |
232 | /// Destroys the property tracker. |
233 | ~PropertyTracker() { cbindgen_private::slint_property_tracker_drop(handle: &inner); } |
234 | /// The copy constructor is intentionally deleted, property trackers cannot be copied. |
235 | PropertyTracker(const PropertyTracker &) = delete; |
236 | /// The assignment operator is intentionally deleted, property trackers cannot be copied. |
237 | PropertyTracker &operator=(const PropertyTracker &) = delete; |
238 | |
239 | /// Returns true if any properties accessed during the last evaluate() call have changed their |
240 | /// value since then. |
241 | bool is_dirty() const { return cbindgen_private::slint_property_tracker_is_dirty(handle: &inner); } |
242 | |
243 | /// Invokes the provided functor \a f and tracks accessed to any properties during that |
244 | /// invocation. |
245 | template<typename F> |
246 | auto evaluate(const F &f) const -> std::enable_if_t<std::is_same_v<decltype(f()), void>> |
247 | { |
248 | cbindgen_private::slint_property_tracker_evaluate( |
249 | handle: &inner, callback: [](void *f) { (*reinterpret_cast<const F *>(f))(); }, user_data: const_cast<F *>(&f)); |
250 | } |
251 | |
252 | /// Invokes the provided functor \a f and tracks accessed to any properties during that |
253 | /// invocation. Use this overload if your functor returns a value, as evaluate() will pass it on |
254 | /// and return it. |
255 | template<typename F> |
256 | auto evaluate(const F &f) const |
257 | -> std::enable_if_t<!std::is_same_v<decltype(f()), void>, decltype(f())> |
258 | { |
259 | decltype(f()) result; |
260 | this->evaluate([&] { result = f(); }); |
261 | return result; |
262 | } |
263 | |
264 | /// Invokes the provided functor \a f and tracks accessed to any properties during that |
265 | /// invocation. |
266 | /// |
267 | /// This starts a new dependency chain and if called during the evaluation of another |
268 | /// property tracker, the outer tracker will not be notified if any accessed properties change. |
269 | template<typename F> |
270 | auto evaluate_as_dependency_root(const F &f) const |
271 | -> std::enable_if_t<std::is_same_v<decltype(f()), void>> |
272 | { |
273 | cbindgen_private::slint_property_tracker_evaluate_as_dependency_root( |
274 | handle: &inner, callback: [](void *f) { (*reinterpret_cast<const F *>(f))(); }, user_data: const_cast<F *>(&f)); |
275 | } |
276 | |
277 | /// Invokes the provided functor \a f and tracks accessed to any properties during that |
278 | /// invocation. Use this overload if your functor returns a value, as evaluate() will pass it on |
279 | /// and return it. |
280 | /// |
281 | /// This starts a new dependency chain and if called during the evaluation of another |
282 | /// property tracker, the outer tracker will not be notified if any accessed properties change. |
283 | template<typename F> |
284 | auto evaluate_as_dependency_root(const F &f) const |
285 | -> std::enable_if_t<!std::is_same_v<decltype(f()), void>, decltype(f())> |
286 | { |
287 | decltype(f()) result; |
288 | this->evaluate_as_dependency_root([&] { result = f(); }); |
289 | return result; |
290 | } |
291 | |
292 | private: |
293 | cbindgen_private::PropertyTrackerOpaque inner; |
294 | }; |
295 | |
296 | } // namespace slint::private_api |
297 | |