mirror of
				https://github.com/slint-ui/slint.git
				synced 2025-10-31 12:04:33 +00:00 
			
		
		
		
	 de9eb69f60
			
		
	
	
		de9eb69f60
		
	
	
	
	
		
			
			After adding an Image in a builtin struct, some code and include need to be moved a bit around
		
			
				
	
	
		
			350 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			350 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright © SixtyFPS GmbH <info@slint.dev>
 | |
| // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
 | |
| 
 | |
| #pragma once
 | |
| #include <string_view>
 | |
| #include <memory>
 | |
| 
 | |
| 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<typename T>
 | |
| 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<typename F>
 | |
|     void set_binding(F binding) const
 | |
|     {
 | |
|         cbindgen_private::slint_property_set_binding(
 | |
|                 &inner,
 | |
|                 [](void *user_data, void *value) {
 | |
|                     *reinterpret_cast<T *>(value) = (*reinterpret_cast<F *>(user_data))();
 | |
|                 },
 | |
|                 new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); },
 | |
|                 nullptr, nullptr);
 | |
|     }
 | |
| 
 | |
|     inline void set_animated_value(const T &value,
 | |
|                                    const cbindgen_private::PropertyAnimation &animation_data) const;
 | |
|     template<typename F>
 | |
|     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<T *>(value) = (*reinterpret_cast<F *>(user_data))();
 | |
|                 },
 | |
|                 new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); },
 | |
|                 &animation_data, nullptr);
 | |
|     }
 | |
| 
 | |
|     template<typename F, typename Trans>
 | |
|     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<T *>(value) =
 | |
|                             reinterpret_cast<UserData *>(user_data)->binding();
 | |
|                 },
 | |
|                 new UserData { binding, animation },
 | |
|                 [](void *user_data) { delete reinterpret_cast<UserData *>(user_data); }, nullptr,
 | |
|                 [](void *user_data, uint64_t *instant) {
 | |
|                     return reinterpret_cast<UserData *>(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<T> *p1, const Property<T> *p2)
 | |
|     {
 | |
|         auto value = p2->get();
 | |
|         cbindgen_private::PropertyHandleOpaque handle {};
 | |
|         if ((p2->inner._0 & 0b10) == 0b10) {
 | |
|             std::swap(handle, const_cast<Property<T> *>(p2)->inner);
 | |
|         }
 | |
|         auto common_property = std::make_shared<Property<T>>(handle, std::move(value));
 | |
|         struct TwoWayBinding
 | |
|         {
 | |
|             std::shared_ptr<Property<T>> common_property;
 | |
|         };
 | |
|         auto del_fn = [](void *user_data) { delete reinterpret_cast<TwoWayBinding *>(user_data); };
 | |
|         auto call_fn = [](void *user_data, void *value) {
 | |
|             *reinterpret_cast<T *>(value) =
 | |
|                     reinterpret_cast<TwoWayBinding *>(user_data)->common_property->get();
 | |
|         };
 | |
|         auto intercept_fn = [](void *user_data, const void *value) {
 | |
|             reinterpret_cast<TwoWayBinding *>(user_data)->common_property->set(
 | |
|                     *reinterpret_cast<const T *>(value));
 | |
|             return true;
 | |
|         };
 | |
|         auto intercept_binding_fn = [](void *user_data, void *value) {
 | |
|             cbindgen_private::slint_property_set_binding_internal(
 | |
|                     &reinterpret_cast<TwoWayBinding *>(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<typename F>
 | |
|     friend void set_state_binding(const Property<StateInfo> &property, F binding);
 | |
| };
 | |
| 
 | |
| template<>
 | |
| inline void Property<int32_t>::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<float>::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<>
 | |
| inline void
 | |
| Property<Color>::set_animated_value(const Color &new_value,
 | |
|                                     const cbindgen_private::PropertyAnimation &animation_data) const
 | |
| {
 | |
|     cbindgen_private::slint_property_set_animated_value_color(&inner, value, new_value,
 | |
|                                                               &animation_data);
 | |
| }
 | |
| 
 | |
| template<typename F>
 | |
| void set_state_binding(const Property<StateInfo> &property, F binding)
 | |
| {
 | |
|     cbindgen_private::slint_property_set_state_binding(
 | |
|             &property.inner,
 | |
|             [](void *user_data) -> int32_t { return (*reinterpret_cast<F *>(user_data))(); },
 | |
|             new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(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<T> 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<typename F>
 | |
|     auto evaluate(const F &f) const -> std::enable_if_t<std::is_same_v<decltype(f()), void>>
 | |
|     {
 | |
|         cbindgen_private::slint_property_tracker_evaluate(
 | |
|                 &inner, [](void *f) { (*reinterpret_cast<const F *>(f))(); }, const_cast<F *>(&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<typename F>
 | |
|     auto evaluate(const F &f) const
 | |
|             -> std::enable_if_t<!std::is_same_v<decltype(f()), void>, 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<typename F>
 | |
|     auto evaluate_as_dependency_root(const F &f) const
 | |
|             -> std::enable_if_t<std::is_same_v<decltype(f()), void>>
 | |
|     {
 | |
|         cbindgen_private::slint_property_tracker_evaluate_as_dependency_root(
 | |
|                 &inner, [](void *f) { (*reinterpret_cast<const F *>(f))(); }, const_cast<F *>(&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<typename F>
 | |
|     auto evaluate_as_dependency_root(const F &f) const
 | |
|             -> std::enable_if_t<!std::is_same_v<decltype(f()), void>, 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<typename Data, typename FnEval, typename FnNotify>
 | |
|     void init(Data data, FnEval fn_eval, FnNotify fn_notify)
 | |
|     {
 | |
|         using Value = std::invoke_result_t<FnEval, Data>;
 | |
|         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<Inner *>(d); },
 | |
|                 [](void *d) {
 | |
|                     auto inner = reinterpret_cast<Inner *>(d);
 | |
|                     auto v = inner->fn_eval(inner->data);
 | |
|                     bool r = v != inner->value;
 | |
|                     inner->value = v;
 | |
|                     return r;
 | |
|                 },
 | |
|                 [](void *d) {
 | |
|                     auto inner = reinterpret_cast<Inner *>(d);
 | |
|                     inner->fn_notify(inner->data, inner->value);
 | |
|                 });
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     cbindgen_private::ChangeTracker inner;
 | |
| };
 | |
| 
 | |
| } // namespace slint::private_api
 |