AUI Framework  develop
Cross-platform base for C++ UI apps
Loading...
Searching...
No Matches
AFieldObservable.h
    1/*
    2 * AUI Framework - Declarative UI toolkit for modern C++20
    3 * Copyright (C) 2020-2025 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>
   28class AFieldObservable
   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) {
  132        removeObserver(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>
  162AFieldObservableAdapter<T, std::decay_t<AdapterCallable>> AFieldObservable<T>::operator()(AdapterCallable&& callable) noexcept {
  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:179
static decltype(auto) connect(const Signal &signal, Object *object, Function &&function)
Connects signal to the slot of the specified object.
Definition AObject.h:86
static void(View::*)(const FieldType &v) getSetter()
Returns setter for ADataBinding (deprecated)
Definition ADataBinding.h:63
static ASignal< FieldType >View::* getGetter()
Returns getter for ADataBinding (deprecated)
Definition ADataBinding.h:55
Definition AFieldObservable.h:153