335 lines
14 KiB
C++
335 lines
14 KiB
C++
/*
|
|
This file is part of KDBindings.
|
|
|
|
SPDX-FileCopyrightText: 2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
|
|
Author: Sean Harmer <sean.harmer@kdab.com>
|
|
|
|
SPDX-License-Identifier: MIT
|
|
|
|
Contact KDAB at <info@kdab.com> for commercial licensing options.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <kdbindings/node.h>
|
|
#include <kdbindings/node_operators.h>
|
|
#include <kdbindings/node_functions.h>
|
|
#include <kdbindings/make_node.h>
|
|
#include <kdbindings/binding_evaluator.h>
|
|
#include <kdbindings/property_updater.h>
|
|
|
|
namespace KDBindings {
|
|
|
|
/**
|
|
* @brief A combination of a root Node with an evaluator.
|
|
*
|
|
* A root Node is formed whenever multiple properties are combined inside
|
|
* a expression and an evaluator is responsible for re-evaluating such
|
|
* an expression whenever any of the constituent properties change.
|
|
*
|
|
* @tparam T The type of the value that the Binding expression evaluates to.
|
|
* @tparam EvaluatorT The type of the evaluator that is used to evaluate the Binding.
|
|
*/
|
|
template<typename T, typename EvaluatorT = BindingEvaluator>
|
|
class Binding : public PropertyUpdater<T>, public Private::Dirtyable
|
|
{
|
|
static_assert(
|
|
std::is_base_of<BindingEvaluator, EvaluatorT>::value,
|
|
"The EvaluatorT type must inherit from BindingEvaluator.");
|
|
|
|
public:
|
|
/**
|
|
* @brief Construct a new Binding with a specific evaluator.
|
|
*
|
|
* @param rootNode Represents that expression contained in the Binding.
|
|
* @param evaluator Used to evaluate the expression contained in the Binding.
|
|
*/
|
|
explicit Binding(Private::Node<T> &&rootNode, EvaluatorT const &evaluator)
|
|
: m_rootNode{ std::move(rootNode) }
|
|
, m_evaluator{ evaluator }
|
|
{
|
|
m_bindingId = m_evaluator.insert(this);
|
|
m_rootNode.setParent(this);
|
|
}
|
|
|
|
/** Destructs the Binding by deregistering it from its evaluator. */
|
|
~Binding() override
|
|
{
|
|
m_evaluator.remove(m_bindingId);
|
|
}
|
|
|
|
/** A Binding is not default constructible. */
|
|
Binding() = delete;
|
|
|
|
/** A Binding cannot be copy constructed. */
|
|
Binding(Binding const &other) = delete;
|
|
/** A Binding cannot be copy assigned. */
|
|
Binding &operator=(Binding const &other) = delete;
|
|
|
|
// Move construction would invalidate the this pointer passed to the evaluator
|
|
// in the constructor
|
|
/** A Binding can not be move constructed. */
|
|
Binding(Binding &&other) = delete;
|
|
/** A Binding can not be move assigned. */
|
|
Binding &operator=(Binding &&other) = delete;
|
|
|
|
/** Set the function that should be used to notify
|
|
* associated properties when the Binding re-evaluates.
|
|
*/
|
|
void setUpdateFunction(std::function<void(T &&)> const &updateFunction) override
|
|
{
|
|
m_propertyUpdateFunction = updateFunction;
|
|
}
|
|
|
|
/** Returns the current value of the Binding. */
|
|
T get() const override { return m_rootNode.evaluate(); }
|
|
|
|
/** Re-evaluates the value of the Binding and notifies all dependants of the change. */
|
|
void evaluate()
|
|
{
|
|
T value = m_rootNode.evaluate();
|
|
|
|
// Use this to update any associated property via the PropertyUpdater's update function
|
|
m_propertyUpdateFunction(std::move(value));
|
|
}
|
|
|
|
protected:
|
|
Private::Dirtyable **parentVariable() override { return nullptr; }
|
|
const bool *dirtyVariable() const override { return nullptr; }
|
|
|
|
/** The root Node of the Binding represents the expression contained by the Binding. */
|
|
Private::Node<T> m_rootNode;
|
|
/** The evaluator responsible for evaluating this Binding. */
|
|
EvaluatorT m_evaluator;
|
|
/** The function used to notify associated properties when the Binding re-evaluates */
|
|
std::function<void(T &&)> m_propertyUpdateFunction = [](T &&) {};
|
|
/** The id of the Binding, used for keeping track of the Binding in its evaluator. */
|
|
int m_bindingId = -1;
|
|
};
|
|
|
|
/**
|
|
* @brief Helper function to create a Binding from a Property.
|
|
*
|
|
* @tparam T The type of the value that the Binding expression evaluates to.
|
|
* @tparam EvaluatorT The type of the evaluator that is used to evaluate the Binding.
|
|
* @param evaluator The evaluator that is used to evaluate the Binding.
|
|
* @param property The Property to create a Binding from.
|
|
* @return std::unique_ptr<Binding<T, EvaluatorT>> A new Binding that is powered by the evaluator.
|
|
*
|
|
* *Note: For the difference between makeBinding and makeBoundProperty, see the
|
|
* ["Reassigning a Binding"](../getting-started/data-binding/#reassigning-a-binding) section in the Getting Started guide.*
|
|
*/
|
|
template<typename T, typename EvaluatorT>
|
|
inline std::unique_ptr<Binding<T, EvaluatorT>> makeBinding(EvaluatorT &evaluator, Property<T> &property)
|
|
{
|
|
return std::make_unique<Binding<T, EvaluatorT>>(Private::makeNode(property), evaluator);
|
|
}
|
|
|
|
/**
|
|
* @brief Creates a binding from a const property using a specified evaluator.
|
|
*
|
|
* @tparam T The type of the value that the Binding expression evaluates to.
|
|
* @tparam EvaluatorT The type of the evaluator that is used to evaluate the Binding.
|
|
* @param evaluator The evaluator that is used to evaluate the Binding.
|
|
* @param property The const Property to create a Binding from.
|
|
* @return std::unique_ptr<Binding<T, EvaluatorT>> A new Binding that is powered by the evaluator.
|
|
*
|
|
* *Note: Using a const Property ensures that the source cannot be modified through this binding,
|
|
* maintaining data integrity and supporting scenarios where data should only be observed, not altered.*
|
|
*/
|
|
template<typename T, typename EvaluatorT>
|
|
inline std::unique_ptr<Binding<T, EvaluatorT>> makeBinding(EvaluatorT &evaluator, const Property<T> &property)
|
|
{
|
|
return std::make_unique<Binding<T, EvaluatorT>>(Private::makeNode(property), evaluator);
|
|
}
|
|
|
|
/**
|
|
* @brief Helper function to create a Binding from a root Node.
|
|
*
|
|
* @tparam T The type of the value that the Binding expression evaluates to.
|
|
* @tparam EvaluatorT The type of the evaluator that is used to evaluate the Binding.
|
|
* @param evaluator The evaluator that is used to evaluate the Binding.
|
|
* @param rootNode Represents the expression that will be evaluated by the Binding.
|
|
* @return std::unique_ptr<Binding<T, EvaluatorT>> A new Binding that combines the rootNode with the evaluator.
|
|
*
|
|
* *Note: For the difference between makeBinding and makeBoundProperty, see the
|
|
* ["Reassigning a Binding"](../getting-started/data-binding/#reassigning-a-binding) section in the Getting Started guide.*
|
|
*/
|
|
template<typename T, typename EvaluatorT>
|
|
inline std::unique_ptr<Binding<T, EvaluatorT>> makeBinding(EvaluatorT &evaluator, Private::Node<T> &&rootNode)
|
|
{
|
|
return std::make_unique<Binding<T, EvaluatorT>>(std::move(rootNode), evaluator);
|
|
}
|
|
|
|
/**
|
|
* @brief Helper function to create a Binding from a function and its arguments.
|
|
*
|
|
* @tparam EvaluatorT The type of the evaluator that is used to evaluate the Binding.
|
|
* @param evaluator The evaluator that is used to evaluate the Binding.
|
|
* @tparam Func The type of the function - may be any type that implements operator().
|
|
* @param func The function object.
|
|
* @tparam Args The function argument types
|
|
* @param args The function arguments - Possible values include: Properties, Constants and Nodes
|
|
* They will be automatically unwrapped, i.e. a Property<T> will pass a value of type T to func.
|
|
* @return std::unique_ptr<Binding<ReturnType, EvaluatorT>> where ReturnType is the type that results from evaluationg func with the given arguments.
|
|
* The Binding will be powered by the new evaluator.
|
|
*
|
|
* *Note: For the difference between makeBinding and makeBoundProperty, see the
|
|
* ["Reassigning a Binding"](../getting-started/data-binding/#reassigning-a-binding) section in the Getting Started guide.*
|
|
*/
|
|
template<typename EvaluatorT, typename Func, typename... Args, typename = std::enable_if_t<sizeof...(Args) != 0>, typename ResultType = Private::operator_node_result_t<Func, Args...>>
|
|
inline std::unique_ptr<Binding<ResultType, EvaluatorT>> makeBinding(EvaluatorT &evaluator, Func &&func, Args &&...args)
|
|
{
|
|
return std::make_unique<Binding<ResultType, EvaluatorT>>(Private::makeNode(std::forward<Func>(func), std::forward<Args>(args)...), evaluator);
|
|
}
|
|
|
|
/**
|
|
* @brief Provides a convenience for old-school, immediate mode Bindings.
|
|
*
|
|
* This works in conjunction with a do-nothing ImmediateBindingEvaluator class to update the
|
|
* result of the Binding immediately upon any of the dependent bindables (i.e. Property instances)
|
|
* notifying that they have changed. This can lead to a Property Binding being evaluated many
|
|
* times before the result is ever used in a typical GUI application.
|
|
*
|
|
* @tparam T The type of the value that the Binding expression evaluates to.
|
|
*/
|
|
template<typename T>
|
|
class Binding<T, ImmediateBindingEvaluator> : public Binding<T, BindingEvaluator>
|
|
{
|
|
public:
|
|
/**
|
|
* @brief Construct a new Binding with an immediate mode evaluator.
|
|
*
|
|
* @param rootNode Represents that expression contained in the Binding.
|
|
*/
|
|
explicit Binding(Private::Node<T> &&rootNode)
|
|
: Binding<T, BindingEvaluator>(std::move(rootNode), ImmediateBindingEvaluator::instance())
|
|
{
|
|
}
|
|
|
|
/** A Binding is not default constructible. */
|
|
Binding() = delete;
|
|
|
|
virtual ~Binding() = default;
|
|
|
|
/** A Binding cannot be copy constructed. */
|
|
Binding(Binding const &other) = delete;
|
|
/** A Binding cannot be copy assigned. */
|
|
Binding &operator=(Binding const &other) = delete;
|
|
|
|
/** A Binding can not be move constructed. */
|
|
Binding(Binding &&other) = delete;
|
|
/** A Binding can not be move assigned. */
|
|
Binding &operator=(Binding &&other) = delete;
|
|
|
|
void markDirty() override
|
|
{
|
|
Binding::evaluate();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief Helper function to create an immediate mode Binding from a Property.
|
|
*
|
|
* @tparam T The type of the value that the Binding expression evaluates to.
|
|
* @param property The Property to create a Binding from.
|
|
* @return std::unique_ptr<Binding<T, ImmediateBindingEvaluator>>
|
|
* An new Binding bound to an existing Property with immediate evaluation.
|
|
*
|
|
* *Note: For the difference between makeBinding and makeBoundProperty, see the
|
|
* ["Reassigning a Binding"](../getting-started/data-binding/#reassigning-a-binding) section in the Getting Started guide.*
|
|
*/
|
|
template<typename T>
|
|
inline std::unique_ptr<Binding<T, ImmediateBindingEvaluator>> makeBinding(Property<T> &property)
|
|
{
|
|
return std::make_unique<Binding<T, ImmediateBindingEvaluator>>(Private::makeNode(property));
|
|
}
|
|
|
|
/**
|
|
* @brief Creates an immediate mode binding from a const property.
|
|
*
|
|
* @tparam T The type of the value that the Binding expression evaluates to.
|
|
* @param property The const Property to create a Binding from.
|
|
* @return std::unique_ptr<Binding<T, ImmediateBindingEvaluator>> A new Binding that is powered by an
|
|
* immediate mode evaluator, ensuring quick updates to changes.
|
|
*
|
|
* *Note: This binding type is especially suited for scenarios where you need to reflect
|
|
* changes in a property to some dependent components without the need to alter the
|
|
* source property itself.*
|
|
*/
|
|
template<typename T>
|
|
inline std::unique_ptr<Binding<T, ImmediateBindingEvaluator>> makeBinding(const Property<T> &property)
|
|
{
|
|
return std::make_unique<Binding<T, ImmediateBindingEvaluator>>(Private::makeNode(property));
|
|
}
|
|
|
|
/**
|
|
* @brief Helper function to create an immediate mode Binding from a root Node.
|
|
*
|
|
* @tparam T The type of the value that the Binding expression evaluates to.
|
|
* @param rootNode Represents the expression that will be evaluated by the Binding.
|
|
* Typically constructed from a unary/binary operator on a Property.
|
|
* @return std::unique_ptr<Binding<<T, ImmediateBindingEvaluator>> An new Binding bound to a root Node with immediate evaluation.
|
|
*
|
|
* *Note: For the difference between makeBinding and makeBoundProperty, see the
|
|
* ["Reassigning a Binding"](../getting-started/data-binding/#reassigning-a-binding) section in the Getting Started guide.*
|
|
*/
|
|
template<typename T>
|
|
inline std::unique_ptr<Binding<T, ImmediateBindingEvaluator>> makeBinding(Private::Node<T> &&rootNode)
|
|
{
|
|
return std::make_unique<Binding<T, ImmediateBindingEvaluator>>(std::move(rootNode));
|
|
}
|
|
|
|
/**
|
|
* @brief Helper function to create an immediate mode Binding from a function and its arguments.
|
|
*
|
|
* @tparam Func The type of the function - may be any type that implements operator().
|
|
* @param func The function object.
|
|
* @tparam Args The function argument types
|
|
* @param args The function arguments - Possible values include: Properties, Constants and Nodes
|
|
* They will be automatically unwrapped, i.e. a Property<T> will pass a value of type T to func.
|
|
* @return std::unique_ptr<Binding<ReturnType, ImmediateBindingEvaluator>> where ReturnType is the type that results from evaluationg func with the given arguments.
|
|
* The Binding will feature immediate evaluation.
|
|
*
|
|
* *Note: For the difference between makeBinding and makeBoundProperty, see the
|
|
* ["Reassigning a Binding"](../getting-started/data-binding/#reassigning-a-binding) section in the Getting Started guide.*
|
|
*/
|
|
template<typename Func, typename... Args, typename = std::enable_if_t<sizeof...(Args) != 0>, typename ResultType = Private::operator_node_result_t<Func, Args...>>
|
|
inline std::unique_ptr<Binding<ResultType, ImmediateBindingEvaluator>> makeBinding(Func &&func, Args &&...args)
|
|
{
|
|
return std::make_unique<Binding<ResultType, ImmediateBindingEvaluator>>(Private::makeNode(std::forward<Func>(func), std::forward<Args>(args)...));
|
|
}
|
|
|
|
/**
|
|
* @brief Helper function to create a Property with a Binding.
|
|
*
|
|
* This function can take:
|
|
* - Another Property.
|
|
* - A Node, typically created by combining Property instances using operators.
|
|
* - A function with arguments (Nodes, Constants or Properties)
|
|
* By default this will construct a Property with an immediate binding evaluation.
|
|
*
|
|
* Alternatively a BindingEvaluator can be passed as the first argument to this function to control
|
|
* when evaluation takes place.
|
|
*
|
|
* See the documentation for the various overloads of the free @ref makeBinding function for a
|
|
* detailed description of which arguments can be used in which order.
|
|
*
|
|
* Examples:
|
|
* - @ref 05-property-bindings/main.cpp
|
|
* - @ref 06-lazy-property-bindings/main.cpp
|
|
*
|
|
* @return Property A new Property that is bound to the inputs
|
|
*
|
|
* *Note: For the difference between makeBinding and makeBoundProperty, see the
|
|
* ["Reassigning a Binding"](../getting-started/data-binding/#reassigning-a-binding) section in the Getting Started guide.*
|
|
*/
|
|
template<typename... T>
|
|
inline auto makeBoundProperty(T &&...args)
|
|
{
|
|
auto binding = makeBinding(std::forward<T>(args)...);
|
|
return Property<decltype(binding->get())>(std::move(binding));
|
|
}
|
|
|
|
} // namespace KDBindings
|