AUI Framework  master
Cross-platform module-based framework for developing C++20 desktop applications
ADataBinding.h
1/*
2 * AUI Framework - Declarative UI toolkit for modern C++20
3 * Copyright (C) 2020-2024 Alex2772 and Contributors
4 *
5 * SPDX-License-Identifier: MPL-2.0
6 *
7 * This Source Code Form is subject to the terms of the Mozilla Public
8 * License, v. 2.0. If a copy of the MPL was not distributed with this
9 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 */
11
12#pragma once
13
14#include "AUI/Traits/concepts.h"
15#include <functional>
16#include <type_traits>
17#include <AUI/Common/SharedPtr.h>
18#include <AUI/Common/ASignal.h>
19#include <AUI/Traits/members.h>
20#include <AUI/View/AView.h>
21
22
23template<typename View, typename FieldType>
25public:
30 static void setup(const _<View>& view) {}
31
32 static ASignal<FieldType>(View::*getGetter()) {
33 return nullptr;
34 }
35 static void(View::*getSetter())(const FieldType& v) {
36 return nullptr;
37 }
38};
39
40template <typename Model>
41class ADataBinding;
42
43template<typename Model, typename View, typename ModelField, typename Getter, aui::invocable<View*, const ModelField&> Setter>
45private:
46 ADataBinding<Model>* mBinder;
47 Setter setter;
48 std::decay_t<ModelField>(Model::*mField);
49 ASignal<std::decay_t<Getter>>(View::*mGetter);
50
51public:
52 ADataBindingLinker(ADataBinding<Model>* binder, ASignal<std::decay_t<Getter>>(View::*getter), Setter setter,
53 std::decay_t<ModelField>(Model::*field)):
54 mBinder(binder), mGetter(getter), setter(std::move(setter)), mField(field) {}
55
56 ADataBinding<Model>* getBinder() const {
57 return mBinder;
58 }
59
60 auto getSetterFunc() const {
61 return setter;
62 }
63
64 auto getField() const {
65 return mField;
66 }
67
68 auto getGetter() const {
69 return mGetter;
70 }
71};
72template<typename Model, typename Data, typename Projection>
74private:
75 ADataBinding<Model>* mBinder;
76 Data(Model::*mField);
77 Projection mProjection;
78
79public:
80 ADataBindingLinker2(ADataBinding<Model>* binder, Data(Model::*field), Projection projection = nullptr) : mBinder(binder), mField(field), mProjection(std::move(projection)) {}
81
82 ADataBinding<Model>* getBinder() const {
83 return mBinder;
84 }
85
86 auto getField() const {
87 return mField;
88 }
89
90 Projection getProjection() const {
91 return mProjection;
92 }
93};
94
157template <typename Model>
158class ADataBinding: public AObject {
159private:
160 using Observer = std::function<void(const Model& model, unsigned)>;
161 ADeque<Observer> mLinkObservers;
162
163 Model* mModel = nullptr;
164 bool mOwning = false;
165
166 void* mExcept = nullptr;
167
168public:
169
170 ADataBinding() = default;
171 explicit ADataBinding(const Model& m)
172 {
173 setModel(m);
174 }
175 explicit ADataBinding(Model* m)
176 {
177 setModel(m);
178 }
179
180 virtual ~ADataBinding() {
181 if (mOwning) {
182 delete mModel;
183 }
184 }
185
191 template<typename View, typename ModelField, typename SetterArg>
192 auto operator()(ModelField(Model::*field), void(View::*setterFunc)(SetterArg)) {
193 AUI_ASSERT(setterFunc != nullptr);
194 return operator()<View, ModelField>(field, (ASignal<ModelField>(View::*))nullptr, setterFunc);
195 }
196
213 template<typename ModelField, typename SetterLambda>
214 auto operator()(ModelField(Model::*field), SetterLambda setterLambda) {
215 using lambda_args = typename aui::lambda_info<SetterLambda>::args;
216 return aui::tuple_visitor<lambda_args>::for_each_all([&]<typename ViewReference, typename DataArgument>() {
217 static_assert(std::is_reference_v<ViewReference>, "View is expected to be a reference");
218 static_assert(aui::convertible_to<ModelField, DataArgument>, "lambda's argument is expected to be constructible from ModelField");
219 using View = std::decay_t<ViewReference>;
220 return operator()<View, ModelField>(field, (ASignal<ModelField>(View::*))nullptr, [setterLambda = std::move(setterLambda)](View* view, DataArgument dataArgument) {
221 std::invoke(setterLambda, *view, std::forward<DataArgument>(dataArgument));
222 });
223 });
224 }
225
230 template<typename View, typename ModelField, typename GetterRV, aui::invocable<View*, const ModelField&> Setter>
231 auto operator()(ModelField(Model::*field),
232 ASignal<GetterRV>(View::*getter),
233 Setter setter = (void(View::*)(const ModelField&))nullptr) {
234 return ADataBindingLinker<Model, View, std::decay_t<ModelField>, GetterRV, Setter>(this, getter, std::move(setter), field);
235 }
236
243 template<typename Data>
244 auto operator()(Data(Model::*field)) {
245 return ADataBindingLinker2<Model, Data, std::nullptr_t>(this, field, nullptr);
246 }
247
253 template<typename Data, aui::invocable<Data> Projection>
254 ADataBindingLinker2<Model, Data, Projection> operator()(Data(Model::*field), Projection projection) {
255 return ADataBindingLinker2<Model, Data, Projection>(this, field, std::move(projection));
256 }
257 const Model& getModel() const noexcept {
258 return *mModel;
259 }
260
261 Model const * operator->() const noexcept {
262 return &getModel();
263 }
264
265 Model& getEditableModel() {
266 return *mModel;
267 }
268 void setModel(const Model& model) {
269 if (mOwning) {
270 delete mModel;
271 }
272 mOwning = true;
273 mModel = new Model(model);
274 notifyUpdate();
275 }
276
277 void setModel(Model* model) {
278 if (mOwning) {
279 delete mModel;
280 }
281 mOwning = false;
282 mModel = model;
283 notifyUpdate();
284 }
285
286 const void* getExclusion() const {
287 return mExcept;
288 }
289
290 void notifyUpdate(void* except = nullptr, unsigned field = -1) {
291 mExcept = except;
292 for (auto& applier : mLinkObservers) {
293 applier(*mModel, field);
294 }
296 }
297
298 template<typename ModelField>
299 void notifyUpdate(ModelField(Model::*field)) {
300 union converter {
301 unsigned i;
302 decltype(field) p;
303 } c;
304 c.p = field;
305 notifyUpdate(nullptr, c.i);
306 }
307
308 template<typename ModelField, aui::convertible_to<ModelField> U>
309 void setValue(ModelField(Model::*field), U&& value) {
310 mModel->*field = std::move(value);
311 notifyUpdate(field);
312 }
313
314 void addObserver(Observer applier) {
315 mLinkObservers << std::move(applier);
316 if (mModel) {
317 mLinkObservers.last()(*mModel, -1);
318 }
319 }
320
321 template<aui::invocable T>
322 void addObserver(T&& applier) {
323 addObserver([applier = std::forward<T>(applier)](const Model&, unsigned) {
324 applier();
325 });
326 }
327
328 template<typename ModelField, typename FieldObserver>
329 void addObserverNoInitialCall(ModelField(Model::*field), FieldObserver&& observer) {
330 mLinkObservers << [observer = std::forward<FieldObserver>(observer), field](const Model& model, unsigned index) {
331 union converter {
332 unsigned i;
333 decltype(field) p;
334 } c;
335 c.p = field;
336 if (c.i == index || index == -1) {
337 observer(model.*field);
338 }
339 };
340 }
341 template<typename ModelField, typename FieldObserver>
342 void addObserver(ModelField(Model::*field), FieldObserver&& observer) {
343 addObserverNoInitialCall(field, std::forward<FieldObserver>(observer));
344 if (mModel) {
345 mLinkObservers.last()(*mModel, -1);
346 }
347 }
348
349
350signals:
355};
356
357template<typename Klass1, typename View, typename Model, typename ModelField, typename GetterRV, typename SetterArg>
358_<Klass1> operator&&(const _<Klass1>& modelBinding, const ADataBindingLinker<Model, View, ModelField, GetterRV, SetterArg>& linker) {
359 union converter {
360 unsigned i;
361 decltype(linker.getField()) p;
362 };
363 if (linker.getGetter()) {
364 AObject::connect(modelBinding.get()->*(linker.getGetter()), linker.getBinder(), [modelBinding, linker](const GetterRV& data) {
365 AUI_ASSERTX(&linker.getBinder()->getEditableModel(), "please setModel for ADataBinding");
366 modelBinding->setSignalsEnabled(false);
367 linker.getBinder()->getEditableModel().*(linker.getField()) = data;
368 converter c;
369 c.p = linker.getField();
370 linker.getBinder()->notifyUpdate(modelBinding.get(), c.i);
371 modelBinding->setSignalsEnabled(true);
372 });
373 }
374
375 if constexpr (aui::convertible_to<decltype(linker.getSetterFunc()), bool>) {
376 if (!bool(linker.getSetterFunc())) {
377 return modelBinding;
378 }
379 }
380 linker.getBinder()->addObserver([modelBinding, linker](const Model& model, unsigned field) {
381 converter c;
382 c.p = linker.getField();
383 if (c.i == field || field == -1) {
384 if (modelBinding.get() != linker.getBinder()->getExclusion()) {
385 std::invoke(linker.getSetterFunc(), modelBinding.get(), model.*(linker.getField()));
386 }
387 }
388 });
389
390 return modelBinding;
391}
392
393
394namespace aui::detail {
395 template<typename ForcedClazz, typename Type>
397 template<typename... Args>
398 static Type(ForcedClazz::*with_args(std::tuple<Args...>))(Args...) {
399 return nullptr;
400 }
401 };
402}
403
404template<typename View, typename Model, typename Data, typename Projection>
405_<View> operator&&(const _<View>& object, const ADataBindingLinker2<Model, Data, Projection>& linker) {
407
408 constexpr bool is_default_projection = std::is_same_v<Projection, std::nullptr_t>;
409 using projection_deduced = std::conditional_t<is_default_projection, aui::identity, Projection>;
410 static_assert(std::is_invocable_v<projection_deduced, Data>, "projection is expected to accept Data value from model");
411 using data_deduced = std::decay_t<std::invoke_result_t<projection_deduced, Data>>;
412
413 using getter = ASignal<Data>(View::*);
415
416 using setter_ret = typename setter::return_t;
417 using setter_args = typename setter::args;
418
419 using my_pointer_to_member = typename aui::detail::pointer_to_member<View, setter_ret>;
420
421 using pointer_to_setter = decltype(my_pointer_to_member::with_args(std::declval<setter_args>()));
422
423 if constexpr (is_default_projection) {
424 getter g = nullptr;
425 // getter is optional.
426 if constexpr (requires { ADataBindingDefault<View, Data>::getGetter(); }) {
428 }
429 auto s = static_cast<pointer_to_setter>(ADataBindingDefault<View, Data>::getSetter());
430
431 AUI_ASSERTX(g != nullptr || s != nullptr, "ADataBindingDefault is not defined for View, Data");
432
433 object && (*linker.getBinder())(linker.getField(), g, s);
434 } else {
435 object && (*linker.getBinder())(linker.getField(), [projection = linker.getProjection()](View& v, const Data& d) {
436 auto s = static_cast<pointer_to_setter>(ADataBindingDefault<View, data_deduced>::getSetter());
437 std::invoke(s, &v, projection(d));
438 });
439 }
440 return object;
441}
442
443template<typename View>
444struct ADataBindingDefault<View, Visibility> {
445public:
450 static void setup(const _<View>& view) {}
451
452 static auto getSetter() {
453 return &AView::setVisibility;
454 }
455};
Definition: ADataBinding.h:73
Definition: ADataBinding.h:44
Data binding implementation.
Definition: ADataBinding.h:158
auto operator()(ModelField(Model::*field), ASignal< GetterRV >(View::*getter), Setter setter=(void(View::*)(const ModelField &)) nullptr)
Create a connection to specified pointer-to-member-field signal and pointer-to-member-function setter...
Definition: ADataBinding.h:231
auto operator()(ModelField(Model::*field), void(View::*setterFunc)(SetterArg))
Create a connection to setter only.
Definition: ADataBinding.h:192
auto operator()(ModelField(Model::*field), SetterLambda setterLambda)
Create a connection to specified lambda setter only.
Definition: ADataBinding.h:214
emits modelChanged
Data in the model has changed.
Definition: ADataBinding.h:354
auto operator()(Data(Model::*field))
Create a connection via ADataBindingDefault.
Definition: ADataBinding.h:244
ADataBindingLinker2< Model, Data, Projection > operator()(Data(Model::*field), Projection projection)
Create a connection via ADataBindingDefault and projection (setter only).
Definition: ADataBinding.h:254
A std::deque with AUI extensions.
Definition: ADeque.h:27
StoredType & last() noexcept
Definition: ADeque.h:164
A base object class.
Definition: AObject.h:49
Definition: ASignal.h:24
An std::weak_ptr with AUI extensions.
Definition: SharedPtrTypes.h:177
API_AUI_CORE const ACommandLineArgs & args() noexcept
Definition: OSAndroid.cpp:29
#define emit
emits the specified signal in context of this object.
Definition: AObject.h:196
#define AUI_ASSERT(condition)
Asserts that the passed condition evaluates to true.
Definition: Assert.h:55
#define AUI_ASSERTX(condition, what)
Asserts that the passed condition evaluates to true. Adds extra message string.
Definition: Assert.h:74
static void setup(const _< View > &view)
Definition: ADataBinding.h:450
Definition: ADataBinding.h:24
static void setup(const _< View > &view)
Definition: ADataBinding.h:30
Definition: ADataBinding.h:396
Definition: members.h:19
Definition: parameter_pack.h:76