mirror of
				https://github.com/slint-ui/slint.git
				synced 2025-11-03 21:24:17 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			295 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			295 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
 | 
						|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
 | 
						|
 | 
						|
#pragma once
 | 
						|
#include <string_view>
 | 
						|
#include <memory>
 | 
						|
 | 
						|
namespace slint::cbindgen_private {
 | 
						|
struct PropertyAnimation;
 | 
						|
}
 | 
						|
 | 
						|
#include "slint_properties_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 *, int32_t *),
 | 
						|
        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))
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
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<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;
 | 
						|
};
 | 
						|
 | 
						|
} // namespace slint::private_api
 |