-
Notifications
You must be signed in to change notification settings - Fork 167
/
binding_evaluator.h
162 lines (142 loc) · 5.3 KB
/
binding_evaluator.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/*
This file is part of KDBindings.
SPDX-FileCopyrightText: 2021-2023 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
Author: Sean Harmer <[email protected]>
SPDX-License-Identifier: MIT
Contact KDAB at <[email protected]> for commercial licensing options.
*/
#pragma once
#include <functional>
#include <map>
#include <memory>
namespace KDBindings {
/**
* @brief A BindingEvaluator provides a mechanism to control the exact time
* when a KDBindings::Binding is reevaluated.
*
* A BindingEvaluator represents a collection of Binding instances that can be
* selectively reevaluated.
*
* If a Binding is created using KDBindings::makeBoundProperty with a BindingEvaluator,
* the Binding will only be evaluated if BindingEvaluator::evaluateAll is called
* on the given evaluator.
*
* Note that instances of BindingEvaluator internally wrap their collection of
* Bindings in such a way that copying a BindingEvaluator does not actually
* copy the collection of Bindings. Therefore adding a Binding to a copy of a
* BindingEvaluator will also add it to the original.
* This is done for ease of use, so evaluators can be passed around easily throughout
* the codebase.
*
* Examples:
* - @ref 06-lazy-property-bindings/main.cpp
*/
class BindingEvaluator
{
// We use pimpl here so that we can pass evaluators around by value (copies)
// yet each copy refers to the same set of data
struct Private {
// TODO: Use std::vector here?
std::map<int, std::function<void()>> m_bindingEvalFunctions;
int m_currentId;
};
public:
/** A BindingEvaluator can be default constructed */
BindingEvaluator() = default;
/**
* A BindingEvaluator can be copy constructed.
*
* Note that copying the evaluator will NOT create a new collection of
* Binding instances, but the new evaluator will refer to the same collection,
* so creating a new Binding with this evaluator will also modify the previous one.
*/
BindingEvaluator(const BindingEvaluator &) noexcept = default;
/**
* A BindingEvaluator can be copy assigned.
*
* Note that copying the evaluator will NOT create a new collection of
* Binding instances, but the new evaluator will refer to the same collection,
* so creating a new Binding with this evaluator will also modify the previous one.
*/
BindingEvaluator &operator=(const BindingEvaluator &) noexcept = default;
/**
* A BindingEvaluator can not be move constructed.
*/
BindingEvaluator(BindingEvaluator &&other) noexcept = delete;
/**
* A BindingEvaluator can not be move assigned.
*/
BindingEvaluator &operator=(BindingEvaluator &&other) noexcept = delete;
/**
* This function evaluates all Binding instances that were constructed with this
* evaluator, in the order they were inserted.
*
* It will therefore update the associated Property instances as well.
*/
void evaluateAll() const
{
// a std::map's ordering is deterministic, so the bindings are evaluated
// in the order they were inserted, ensuring correct transitive dependency
// evaluation.
for (auto &[id, func] : m_d->m_bindingEvalFunctions)
func();
}
private:
template<typename BindingType>
int insert(BindingType *binding)
{
m_d->m_bindingEvalFunctions.insert({ ++(m_d->m_currentId),
[=]() { binding->evaluate(); } });
return m_d->m_currentId;
}
void remove(int id)
{
m_d->m_bindingEvalFunctions.erase(id);
}
std::shared_ptr<Private> m_d{ std::make_shared<Private>() };
template<typename T, typename UpdaterT>
friend class Binding;
};
/**
* This subclass of BindingEvaluator doesn't do anything special on its own.
* It is used together with a template specialization of Binding to provide
* old-school, immediate mode Bindings.
*
* Any Binding that is constructed with an ImmediateBindingEvaluator will not wait
* for the evaluator to call evaluateAll, but rather evaluate the Binding immediately
* when any of its bindables (i.e. Property instances) change.
* This can lead to a Property Binding being evaluated many
* times before the result is ever used in a typical GUI application.
*/
class ImmediateBindingEvaluator final : public BindingEvaluator
{
public:
static inline ImmediateBindingEvaluator instance()
{
static ImmediateBindingEvaluator evaluator;
return evaluator;
}
};
} // namespace KDBindings
/**
* @example 06-lazy-property-bindings/main.cpp
*
* An example of how to use KDBindings::BindingEvaluator together
* with a KDBindings::Property to create a Property binding that is
* only reevaluated on demand.
*
* The output of this example is:
* ```
* The initial size of the image = 1920000 bytes
* The new size of the image = 8294400 bytes
* ```
*
* Note the difference to @ref 05-property-bindings/main.cpp, where the
* new size of the image is calculated twice.
*
* This feature is especially useful to reduce the performance impact of
* bindings and to create bindings that only update in specific intervals.
* <br/><!-- This <br/> is a workaround for a bug in doxybook2 that causes
* the rendering of the example code to break because it is missing a
* newline-->
*/