AUI Framework  master
Cross-platform module-based framework for developing C++20 desktop applications
AFieldObservable.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 <list>
15#include <utility>
16#include "AUI/Common/ASignal.h"
17#include "AUI/Traits/concepts.h"
18#include "AUI/Util/ADataBinding.h"
19
20template<typename T, aui::invocable<const T&> AdapterCallable>
22
27template <typename T>
29{
30public:
31 using Observer = std::function<void()>;
32 using ObserverHandle = Observer*;
33
34 AFieldObservable(T initial = T()):
35 mValue(std::move(initial))
36 {
37 }
38
39 void setValue(T t, ObserverHandle exceptObserver = nullptr) {
40 if (mValue != t) {
41 mValue = std::move(t);
42 notifyObservers(exceptObserver);
43 }
44 }
45
46 void notifyObservers(ObserverHandle exceptObserver = nullptr) {
47 for (const auto& v : mObservers) {
48 if (&v != exceptObserver) {
49 v();
50 }
51 }
52 }
53
54 AFieldObservable& operator=(T t)
55 {
56 setValue(t);
57 return *this;
58 }
59
60 T& operator+=(T t) {
61 mValue += t;
62 notifyObservers();
63 return mValue;
64 }
65 T& operator-=(T t) {
66 mValue -= t;
67 notifyObservers();
68 return mValue;
69 }
70 T& operator*=(T t) {
71 mValue *= t;
72 notifyObservers();
73 return mValue;
74 }
75 T& operator/=(T t) {
76 mValue /= t;
77 notifyObservers();
78 return mValue;
79 }
80
81 operator const T&() const noexcept
82 {
83 return mValue;
84 }
85
86 [[nodiscard]]
87 const T& value() const noexcept {
88 return mValue;
89 }
90
91 T* operator->() noexcept {
92 return &mValue;
93 }
94
95 T const * operator->() const noexcept {
96 return &mValue;
97 }
98
103 template<typename Observer_t>
104 ObserverHandle addObserver(Observer_t&& observer) {
105 constexpr bool expectsValue = !std::is_invocable_v<Observer_t>;
106 if constexpr (expectsValue) {
107 observer(mValue);
108 mObservers.push_back([this, observer = std::forward<Observer_t>(observer)] {
109 observer(mValue);
110 });
111 } else {
112 observer();
113 mObservers.push_back(std::forward<Observer_t>(observer));
114 }
115
116 return &mObservers.back();
117 }
118
123 template<typename Observer_t>
124 ObserverHandle operator<<(Observer_t observer) {
125 return addObserver(std::forward<Observer_t>(observer));
126 }
127
131 void operator>>(ObserverHandle h) {
133 }
134
138 void removeObserver(ObserverHandle h) {
139 mObservers.erase(std::remove_if(mObservers.begin(), mObservers.end(), [&](const Observer& o) {
140 return &o == h;
141 }), mObservers.end());
142 }
143
144 template<aui::invocable<const T&> AdapterCallable>
145 AFieldObservableAdapter<T, std::decay_t<AdapterCallable>> operator()(AdapterCallable&& callable) noexcept;
146
147private:
148 T mValue;
149 std::list<Observer> mObservers;
150};
151
152template<typename T, aui::invocable<const T&> AdapterCallable>
154 AFieldObservable<T>& field;
155 AdapterCallable callable;
156
157 using return_t = decltype(callable(std::declval<T>()));
158};
159
160template<typename T>
161template<aui::invocable<const T&> AdapterCallable>
163 return { .field = *this, .callable = std::forward<AdapterCallable>(callable) };
164}
165
166
167template<typename View, typename Data>
168_<View> operator&&(const _<View>& object, AFieldObservable<Data>& observable) {
169 using ObserverHandle = typename std::decay_t<decltype(observable)>::ObserverHandle;
170 auto observerHandle = _new<ObserverHandle>(nullptr);
172 *observerHandle = observable << [weak = object.weak(), observerHandle, observable = &observable](const Data& newValue) {
173 auto object = weak.lock();
174 if (object == nullptr) {
175 observable->removeObserver(*observerHandle);
176 }
177 (object.get()->*ADataBindingDefault<View, Data>::getSetter())(newValue);
178 };
179 }
180 if (auto getter = ADataBindingDefault<View, Data>::getGetter()) {
181 AObject::connect(object.get()->*getter, object, [&observable, observerHandle = *observerHandle](Data newValue) {
182 observable.setValue(std::move(newValue), observerHandle);
183 });
184 }
185
186 return object;
187}
188
189template<typename View, typename ModelData, typename AdapterCallback>
190_<View> operator&&(const _<View>& object, AFieldObservableAdapter<ModelData, AdapterCallback> observableAdapter) {
191 using Data = typename AFieldObservableAdapter<ModelData, AdapterCallback>::return_t;
192 auto& observable = observableAdapter.field;
193 using ObserverHandle = typename std::decay_t<decltype(observable)>::ObserverHandle;
194 auto observerHandle = _new<ObserverHandle>(nullptr);
196 *observerHandle = observable << [weak = object.weak(), observable = &observable, observerHandle, transformer = std::move(observableAdapter.callable)](const ModelData& newValue) {
197 auto object = weak.lock();
198 if (object == nullptr) {
199 observable->removeObserver(*observerHandle);
200 return;
201 }
202 (object.get()->*ADataBindingDefault<View, Data>::getSetter())(transformer(newValue));
203 };
204 }
205
206 return object;
207}
208
Stores a value and observes it's changes, notifying observers.
Definition: AFieldObservable.h:29
ObserverHandle operator<<(Observer_t observer)
Adds an observer, immediately feeding the observer with the current value.
Definition: AFieldObservable.h:124
void operator>>(ObserverHandle h)
Removes an observer.
Definition: AFieldObservable.h:131
ObserverHandle addObserver(Observer_t &&observer)
Adds an observer, immediately feeding the observer with the current value.
Definition: AFieldObservable.h:104
void removeObserver(ObserverHandle h)
Removes an observer.
Definition: AFieldObservable.h:138
An std::weak_ptr with AUI extensions.
Definition: SharedPtrTypes.h:177
Definition: ADataBinding.h:24
Definition: AFieldObservable.h:153