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
8namespace slint::cbindgen_private {
9struct PropertyAnimation;
10}
11
12#include "slint_properties_internal.h"
13#include "slint_builtin_structs_internal.h"
14
15namespace slint::private_api {
16
17using cbindgen_private::StateInfo;
18
19inline 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
29inline 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
39inline 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
49inline 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
59template<typename T>
60struct 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
183private:
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
190template<>
191inline 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
198template<>
199inline void
200Property<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
207template<typename F>
208void 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.
228struct 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
292private:
293 cbindgen_private::PropertyTrackerOpaque inner;
294};
295
296} // namespace slint::private_api
297

source code of slint/api/cpp/include/slint_properties.h