KDDockWidgets API Documentation 2.1
Loading...
Searching...
No Matches
node.h
Go to the documentation of this file.
1/*
2 This file is part of KDBindings.
3
4 SPDX-FileCopyrightText: 2021-2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
5 Author: Sean Harmer <sean.harmer@kdab.com>
6
7 SPDX-License-Identifier: MIT
8
9 Contact KDAB at <info@kdab.com> for commercial licensing options.
10*/
11
12#pragma once
13
14#include <kdbindings/property.h>
15#include <kdbindings/signal.h>
16
17#include <functional>
18#include <memory>
19#include <stdexcept>
20#include <type_traits>
21#include <utility>
22
23namespace KDBindings {
24
29class PropertyDestroyedError : public std::runtime_error
30{
31public:
33
34 using std::runtime_error::runtime_error;
35};
36
37namespace Private {
38
40{
41public:
42 virtual ~Dirtyable() = default;
43
44 Dirtyable() = default;
45
47 {
48 auto **parentVar = parentVariable();
49 if (parentVar) {
51 }
52 }
53
54 // overridden by Binding
55 virtual void markDirty()
56 {
57 auto *dirtyVar = dirtyVariable();
58 if (dirtyVar) {
59 if (*dirtyVar) {
60 return;
61 // We are already dirty, don't bother marking the whole tree again.
62 }
63
64 // we only want to have one override for dirtyVariable,
65 // which is const, so we have to const cast here.
66 *const_cast<bool *>(dirtyVar) = true;
67 }
68
69 auto **parentVar = parentVariable();
70 if (parentVar && *parentVar) {
71 (*parentVar)->markDirty();
72 }
73 }
74
75 bool isDirty() const
76 {
77 auto *dirtyVar = dirtyVariable();
78 return dirtyVar && *dirtyVar;
79 }
80
81protected:
82 virtual Dirtyable **parentVariable() = 0;
83 virtual const bool *dirtyVariable() const = 0;
84};
85
86template<typename ResultType>
88{
89public:
90 // Returns a reference, because we cache each evaluated value.
91 // const, because it shouldn't modify the return value of the AST.
92 // Requires mutable caches
93 virtual const ResultType &evaluate() const = 0;
94
95protected:
96 NodeInterface() = default;
97};
98
99template<typename ResultType>
100class Node
101{
102public:
104 : m_interface(std::move(nodeInterface))
105 {
106 }
107
108 const ResultType &evaluate() const
109 {
110 return m_interface->evaluate();
111 }
112
114 {
115 m_interface->setParent(newParent);
116 }
117
118 bool isDirty() const
119 {
120 return m_interface->isDirty();
121 }
122
123private:
124 std::unique_ptr<NodeInterface<ResultType>> m_interface;
125};
126
127template<typename T>
129{
130public:
131 explicit ConstantNode(const T &value)
132 : m_value{ value }
133 {
134 }
135
136 const T &evaluate() const override
137 {
138 return m_value;
139 }
140
141protected:
142 // A constant can never be dirty, so it doesn't need to
143 // know its parent, as it doesn't have to notify it.
144 Dirtyable **parentVariable() override { return nullptr; }
145 const bool *dirtyVariable() const override { return nullptr; }
146
147private:
148 T m_value;
149};
150
151template<typename PropertyType>
152class PropertyNode : public NodeInterface<PropertyType>
153{
154public:
156 : m_parent(nullptr), m_dirty(false)
157 {
158 setProperty(property);
159 }
160
161 // PropertyNodes cannot be moved
163
166 {
167 setProperty(*other.m_property);
168 }
169
171 {
172 m_valueChangedHandle.disconnect();
173 m_movedHandle.disconnect();
174 m_destroyedHandle.disconnect();
175 }
176
177 const PropertyType &evaluate() const override
178 {
179 if (!m_property) {
180 throw PropertyDestroyedError("The Property this node refers to no longer exists!");
181 }
182
183 m_dirty = false;
184 return m_property->get();
185 }
186
187 // This must currently take a const reference, as the "moved" signal emits a const&
189 {
190 if (&property != m_property) {
191 m_property = &property;
192 } else {
193 // Another property was moved into the property this node refers to.
194 // Therefore it will no longer update this Node.
195 m_property = nullptr;
196 }
197 }
198
200 {
201 m_property = nullptr;
202 }
203
204protected:
205 Dirtyable **parentVariable() override { return &m_parent; }
206 const bool *dirtyVariable() const override { return &m_dirty; }
207
208private:
209 void setProperty(Property<PropertyType> &property)
210 {
211 m_property = &property;
212 m_valueChangedHandle = m_property->valueChanged().connect(&PropertyNode<PropertyType>::markDirty, this);
213 m_movedHandle = m_property->m_moved.connect(&PropertyNode<PropertyType>::propertyMoved, this);
214 m_destroyedHandle = m_property->destroyed().connect(&PropertyNode<PropertyType>::propertyDestroyed, this);
215 }
216
217 Property<PropertyType> *m_property;
218 ConnectionHandle m_movedHandle;
219 ConnectionHandle m_valueChangedHandle;
220 ConnectionHandle m_destroyedHandle;
221
222 Dirtyable *m_parent;
223 mutable bool m_dirty;
224};
225
226template<typename ResultType, typename Operator, typename... Ts>
227class OperatorNode : public NodeInterface<ResultType>
228{
229public:
230 // add another typename template for the Operator type, so
231 // it can be a universal reference.
232 template<typename Op>
233 explicit OperatorNode(Op &&op, Node<Ts> &&...arguments)
234 : m_parent{ nullptr }, m_dirty{ true /*dirty until reevaluated*/ }, m_op{ std::move(op) }, m_values{ std::move(arguments)... }, m_result(reevaluate())
235 {
236 static_assert(
237 std::is_convertible_v<decltype(m_op(std::declval<Ts>()...)), ResultType>,
238 "The result of the Operator must be convertible to the ReturnType of the Node");
239
241 }
242
243 template<std::size_t I>
244 auto setParents() -> std::enable_if_t<I == sizeof...(Ts)>
245 {
246 }
247
248 // The enable_if_t confuses clang-format into thinking the
249 // first "<" is a comparison, and not the second.
250 // clang-format off
251 template<std::size_t I>
252 auto setParents() -> std::enable_if_t<I < sizeof...(Ts)>
253 // clang-format on
254 {
255 std::get<I>(m_values).setParent(this);
257 }
258
259 virtual ~OperatorNode() = default;
260
261 const ResultType &evaluate() const override
262 {
263 if (Dirtyable::isDirty()) {
264 m_result = reevaluate();
265 }
266
267 return m_result;
268 }
269
270protected:
271 Dirtyable **parentVariable() override { return &m_parent; }
272 const bool *dirtyVariable() const override { return &m_dirty; }
273
274private:
275 template<std::size_t... Is>
276 ResultType reevaluate_helper(std::index_sequence<Is...>) const
277 {
278 return m_op(std::get<Is>(m_values).evaluate()...);
279 }
280
281 ResultType reevaluate() const
282 {
283 m_dirty = false;
284
285 return reevaluate_helper(std::make_index_sequence<sizeof...(Ts)>());
286 }
287
288 Dirtyable *m_parent;
289 mutable bool m_dirty;
290
291 Operator m_op;
292 std::tuple<Node<Ts>...> m_values;
293
294 // Note: it is important that m_result is evaluated last!
295 // Otherwise the call to reevaluate in the constructor will fail.
296 mutable ResultType m_result;
297};
298
299template<typename T>
300struct is_node_helper : std::false_type {
301};
302
303template<typename T>
304struct is_node_helper<Node<T>> : std::true_type {
305};
306
307template<typename T>
309};
310
311} // namespace Private
312
313} // namespace KDBindings
A ConnectionHandle represents the connection of a Signal to a slot (i.e. a function that is called wh...
Definition signal.h:68
const T & evaluate() const override
Definition node.h:136
ConstantNode(const T &value)
Definition node.h:131
Dirtyable ** parentVariable() override
Definition node.h:144
const bool * dirtyVariable() const override
Definition node.h:145
void setParent(Dirtyable *newParent)
Definition node.h:46
virtual const bool * dirtyVariable() const =0
virtual ~Dirtyable()=default
virtual void markDirty()
Definition node.h:55
virtual Dirtyable ** parentVariable()=0
virtual const ResultType & evaluate() const =0
Node(std::unique_ptr< NodeInterface< ResultType > > &&nodeInterface)
Definition node.h:103
void setParent(Dirtyable *newParent)
Definition node.h:113
const ResultType & evaluate() const
Definition node.h:108
bool isDirty() const
Definition node.h:118
auto setParents() -> std::enable_if_t< I< sizeof...(Ts)>
Definition node.h:252
const bool * dirtyVariable() const override
Definition node.h:272
const ResultType & evaluate() const override
Definition node.h:261
Dirtyable ** parentVariable() override
Definition node.h:271
auto setParents() -> std::enable_if_t< I==sizeof...(Ts)>
Definition node.h:244
OperatorNode(Op &&op, Node< Ts > &&...arguments)
Definition node.h:233
const PropertyType & evaluate() const override
Definition node.h:177
PropertyNode(PropertyNode< PropertyType > &&)=delete
const bool * dirtyVariable() const override
Definition node.h:206
PropertyNode(const PropertyNode< PropertyType > &other)
Definition node.h:164
Dirtyable ** parentVariable() override
Definition node.h:205
PropertyNode(Property< PropertyType > &property)
Definition node.h:155
void propertyMoved(Property< PropertyType > &property)
Definition node.h:188
A PropertyDestroyedError is thrown whenever a binding is evaluated that references a property that no...
Definition node.h:30
A property represents a value that can be part of or the result of data binding.
Definition property.h:138
Signal< const T & > & valueChanged() const
Definition property.h:296
T const & get() const
Definition property.h:330
Signal & destroyed() const
Definition property.h:301
ConnectionHandle connect(std::function< void(Args...)> const &slot)
Definition signal.h:348
typename operator_node_result< Operator, Ts... >::type operator_node_result_t
Definition make_node.h:57
The main namespace of the KDBindings library.
Definition binding.h:21
Definition utils.h:161

© Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
KDDockWidgets
Advanced Dock Widget Framework for Qt
https://www.kdab.com/development-resources/qt-tools/kddockwidgets/
Generated by doxygen 1.9.8