// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 #pragma once #include #include namespace slint::cbindgen_private { struct PropertyAnimation; struct ChangeTracker { void *inner; }; } #include "slint_properties_internal.h" #include "slint_builtin_structs_internal.h" namespace slint::private_api { using cbindgen_private::StateInfo; inline void slint_property_set_animated_binding_helper( const cbindgen_private::PropertyHandleOpaque *handle, void (*binding)(void *, int *), void *user_data, void (*drop_user_data)(void *), const cbindgen_private::PropertyAnimation *animation_data, cbindgen_private::PropertyAnimation (*transition_data)(void *, uint64_t *)) { cbindgen_private::slint_property_set_animated_binding_int( handle, binding, user_data, drop_user_data, animation_data, transition_data); } inline void slint_property_set_animated_binding_helper( const cbindgen_private::PropertyHandleOpaque *handle, void (*binding)(void *, float *), void *user_data, void (*drop_user_data)(void *), const cbindgen_private::PropertyAnimation *animation_data, cbindgen_private::PropertyAnimation (*transition_data)(void *, uint64_t *)) { cbindgen_private::slint_property_set_animated_binding_float( handle, binding, user_data, drop_user_data, animation_data, transition_data); } inline void slint_property_set_animated_binding_helper( const cbindgen_private::PropertyHandleOpaque *handle, void (*binding)(void *, Color *), void *user_data, void (*drop_user_data)(void *), const cbindgen_private::PropertyAnimation *animation_data, cbindgen_private::PropertyAnimation (*transition_data)(void *, uint64_t *)) { cbindgen_private::slint_property_set_animated_binding_color( handle, binding, user_data, drop_user_data, animation_data, transition_data); } inline void slint_property_set_animated_binding_helper( const cbindgen_private::PropertyHandleOpaque *handle, void (*binding)(void *, Brush *), void *user_data, void (*drop_user_data)(void *), const cbindgen_private::PropertyAnimation *animation_data, cbindgen_private::PropertyAnimation (*transition_data)(void *, uint64_t *)) { cbindgen_private::slint_property_set_animated_binding_brush( handle, binding, user_data, drop_user_data, animation_data, transition_data); } template struct Property { Property() { cbindgen_private::slint_property_init(&inner); } ~Property() { cbindgen_private::slint_property_drop(&inner); } Property(const Property &) = delete; Property(Property &&) = delete; Property &operator=(const Property &) = delete; explicit Property(const T &value) : value(value) { cbindgen_private::slint_property_init(&inner); } /* Should it be implicit? void operator=(const T &value) { set(value); }*/ void set(const T &value) const { if ((inner._0 & 0b10) == 0b10 || this->value != value) { this->value = value; cbindgen_private::slint_property_set_changed(&inner, &this->value); } } const T &get() const { cbindgen_private::slint_property_update(&inner, &value); return value; } template void set_binding(F binding) const { cbindgen_private::slint_property_set_binding( &inner, [](void *user_data, void *value) { *reinterpret_cast(value) = (*reinterpret_cast(user_data))(); }, new F(binding), [](void *user_data) { delete reinterpret_cast(user_data); }, nullptr, nullptr); } inline void set_animated_value(const T &value, const cbindgen_private::PropertyAnimation &animation_data) const; template inline void set_animated_binding(F binding, const cbindgen_private::PropertyAnimation &animation_data) const { private_api::slint_property_set_animated_binding_helper( &inner, [](void *user_data, T *value) { *reinterpret_cast(value) = (*reinterpret_cast(user_data))(); }, new F(binding), [](void *user_data) { delete reinterpret_cast(user_data); }, &animation_data, nullptr); } template inline void set_animated_binding_for_transition(F binding, Trans animation) const { struct UserData { F binding; Trans animation; }; private_api::slint_property_set_animated_binding_helper( &inner, [](void *user_data, T *value) { *reinterpret_cast(value) = reinterpret_cast(user_data)->binding(); }, new UserData { binding, animation }, [](void *user_data) { delete reinterpret_cast(user_data); }, nullptr, [](void *user_data, uint64_t *instant) { return reinterpret_cast(user_data)->animation(instant); }); } bool is_dirty() const { return cbindgen_private::slint_property_is_dirty(&inner); } void mark_dirty() const { cbindgen_private::slint_property_mark_dirty(&inner); } static void link_two_way(const Property *p1, const Property *p2) { auto value = p2->get(); cbindgen_private::PropertyHandleOpaque handle {}; if ((p2->inner._0 & 0b10) == 0b10) { std::swap(handle, const_cast *>(p2)->inner); } auto common_property = std::make_shared>(handle, std::move(value)); struct TwoWayBinding { std::shared_ptr> common_property; }; auto del_fn = [](void *user_data) { delete reinterpret_cast(user_data); }; auto call_fn = [](void *user_data, void *value) { *reinterpret_cast(value) = reinterpret_cast(user_data)->common_property->get(); }; auto intercept_fn = [](void *user_data, const void *value) { reinterpret_cast(user_data)->common_property->set( *reinterpret_cast(value)); return true; }; auto intercept_binding_fn = [](void *user_data, void *value) { cbindgen_private::slint_property_set_binding_internal( &reinterpret_cast(user_data)->common_property->inner, value); return true; }; cbindgen_private::slint_property_set_binding(&p1->inner, call_fn, new TwoWayBinding { common_property }, del_fn, intercept_fn, intercept_binding_fn); cbindgen_private::slint_property_set_binding(&p2->inner, call_fn, new TwoWayBinding { common_property }, del_fn, intercept_fn, intercept_binding_fn); } /// Internal (private) constructor used by link_two_way explicit Property(cbindgen_private::PropertyHandleOpaque inner, T value) : inner(inner), value(std::move(value)) { } const T &get_internal() const { return value; } void set_constant() const { cbindgen_private::slint_property_set_constant(&inner); } private: cbindgen_private::PropertyHandleOpaque inner; mutable T value {}; template friend void set_state_binding(const Property &property, F binding); }; template<> inline void Property::set_animated_value( const int32_t &new_value, const cbindgen_private::PropertyAnimation &animation_data) const { cbindgen_private::slint_property_set_animated_value_int(&inner, value, new_value, &animation_data); } template<> inline void Property::set_animated_value(const float &new_value, const cbindgen_private::PropertyAnimation &animation_data) const { cbindgen_private::slint_property_set_animated_value_float(&inner, value, new_value, &animation_data); } template void set_state_binding(const Property &property, F binding) { cbindgen_private::slint_property_set_state_binding( &property.inner, [](void *user_data) -> int32_t { return (*reinterpret_cast(user_data))(); }, new F(binding), [](void *user_data) { delete reinterpret_cast(user_data); }); } /// PropertyTracker allows keeping track of when properties change and lazily evaluate code /// if necessary. /// Once constructed, you can call evaluate() with a functor that will be invoked. Any /// Property types that have their value read from within the invoked functor or any code that's /// reached from there are added to internal book-keeping. When after returning from evaluate(), /// any of these accessed properties change their value, the property tracker's is_dirt() function /// will return true. /// /// PropertyTracker instances nest, so if during the evaluation of one tracker, another tracker's /// evaluate() function gets called and properties from within that evaluation change their value /// later, both tracker instances will report true for is_dirty(). If you would like to disable the /// nesting, use the evaluate_as_dependency_root() function instead. struct PropertyTracker { /// Constructs a new property tracker instance. PropertyTracker() { cbindgen_private::slint_property_tracker_init(&inner); } /// Destroys the property tracker. ~PropertyTracker() { cbindgen_private::slint_property_tracker_drop(&inner); } /// The copy constructor is intentionally deleted, property trackers cannot be copied. PropertyTracker(const PropertyTracker &) = delete; /// The assignment operator is intentionally deleted, property trackers cannot be copied. PropertyTracker &operator=(const PropertyTracker &) = delete; /// Returns true if any properties accessed during the last evaluate() call have changed their /// value since then. bool is_dirty() const { return cbindgen_private::slint_property_tracker_is_dirty(&inner); } /// Invokes the provided functor \a f and tracks accessed to any properties during that /// invocation. template auto evaluate(const F &f) const -> std::enable_if_t> { cbindgen_private::slint_property_tracker_evaluate( &inner, [](void *f) { (*reinterpret_cast(f))(); }, const_cast(&f)); } /// Invokes the provided functor \a f and tracks accessed to any properties during that /// invocation. Use this overload if your functor returns a value, as evaluate() will pass it on /// and return it. template auto evaluate(const F &f) const -> std::enable_if_t, decltype(f())> { decltype(f()) result; this->evaluate([&] { result = f(); }); return result; } /// Invokes the provided functor \a f and tracks accessed to any properties during that /// invocation. /// /// This starts a new dependency chain and if called during the evaluation of another /// property tracker, the outer tracker will not be notified if any accessed properties change. template auto evaluate_as_dependency_root(const F &f) const -> std::enable_if_t> { cbindgen_private::slint_property_tracker_evaluate_as_dependency_root( &inner, [](void *f) { (*reinterpret_cast(f))(); }, const_cast(&f)); } /// Invokes the provided functor \a f and tracks accessed to any properties during that /// invocation. Use this overload if your functor returns a value, as evaluate() will pass it on /// and return it. /// /// This starts a new dependency chain and if called during the evaluation of another /// property tracker, the outer tracker will not be notified if any accessed properties change. template auto evaluate_as_dependency_root(const F &f) const -> std::enable_if_t, decltype(f())> { decltype(f()) result; this->evaluate_as_dependency_root([&] { result = f(); }); return result; } private: cbindgen_private::PropertyTrackerOpaque inner; }; struct ChangeTracker { ChangeTracker() { cbindgen_private::slint_change_tracker_construct(&inner); } ~ChangeTracker() { cbindgen_private::slint_change_tracker_drop(&inner); } template void init(Data data, FnEval fn_eval, FnNotify fn_notify) { using Value = std::invoke_result_t; struct Inner { Data data; FnEval fn_eval; FnNotify fn_notify; Value value; }; auto data_ptr = new Inner { std::move(data), std::move(fn_eval), std::move(fn_notify), Value() }; cbindgen_private::slint_change_tracker_init( &inner, data_ptr, [](void *d) { delete reinterpret_cast(d); }, [](void *d) { auto inner = reinterpret_cast(d); auto v = inner->fn_eval(inner->data); bool r = v != inner->value; inner->value = v; return r; }, [](void *d) { auto inner = reinterpret_cast(d); inner->fn_notify(inner->data, inner->value); }); } private: cbindgen_private::ChangeTracker inner; }; } // namespace slint::private_api