AUI Framework  develop
Cross-platform base for C++ UI apps
Loading...
Searching...
No Matches
AProperty.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 <AUI/Common/ASignal.h>
   15#include <AUI/Common/APropertyPrecomputed.h>
   16#include <AUI/Common/PropertyModifier.h>
   17#include <AUI/Common/detail/property.h>
   18
   29template <typename T>
   30struct AProperty: AObjectBase {
   31    static_assert(!std::is_reference_v<T>, "====================> AProperty: attempt to wrap a reference.");
   32
   33    using Underlying = T;
   34
   42    T raw{};
   43
   48
   49    AProperty()
   50        requires aui::default_initializable<T>
   51    = default;
   52
   53    template <aui::convertible_to<T> U>
   54    AProperty(U&& value) noexcept(noexcept(T(std::forward<U>(value)))): raw(std::forward<U>(value)) {}
   55
   56    AObjectBase* boundObject() {
   57        return this;
   58    }
   59
   60    const AObjectBase* boundObject() const {
   61        return this;
   62    }
   63
   64    AProperty(const AProperty& value): raw(value.raw) {
   65    }
   66
   67    AProperty(AProperty&& value) noexcept: raw(std::move(value.raw)) {
   68        value.notify();
   69    }
   70
   71    AProperty& operator=(const AProperty& value) {
   72        if (this == &value) {
   73            return *this;
   74        }
   75        operator=(value.raw);
   76        return *this;
   77    }
   78
   79    AProperty& operator=(AProperty&& value) noexcept {
   80        if (this == &value) {
   81            return *this;
   82        }
   83        operator=(std::move(value.raw));
   84        value.notify();
   85        return *this;
   86    }
   87
   88    template <aui::convertible_to<T> U>
   89    AProperty& operator=(U&& value) noexcept {
   90        static constexpr auto IS_COMPARABLE = requires { this->raw == value; };
   91        if constexpr (IS_COMPARABLE) {
   92            if (this->raw == value) [[unlikely]] {
   93                return *this;
   94            }
   95        }
   96        this->raw = std::forward<U>(value);
   97        notify();
   98        return *this;
   99    }
  100
  101    template <ASignalInvokable SignalInvokable>
  102    void operator^(SignalInvokable&& t) {
  103        t.invokeSignal(nullptr);
  104    }
  105
  116    void notify() {
  117        emit changed(this->raw);
  118    }
  119
  120    [[nodiscard]]
  121    const T& value() const noexcept {
  123        return raw;
  124    }
  125
  126    [[nodiscard]] operator const T&() const noexcept { return value(); }
  127
  128    [[nodiscard]]
  129    const T* operator->() const noexcept {
  130        return &value();
  131    }
  132
  133    [[nodiscard]]
  134    const T& operator*() const noexcept {
  135        return value();
  136    }
  137
  142        return { *this };
  143    }
  144
  148    template<aui::invocable<const T&> Projection>
  149    [[nodiscard]]
  150    auto readProjected(Projection&& projection) const noexcept {
  151        return aui::detail::property::makeReadonlyProjection(*this, std::forward<Projection>(projection));
  152    }
  153
  157    template<aui::invocable<const T&> ProjectionRead,
  158             aui::invocable<const std::invoke_result_t<ProjectionRead, T>&> ProjectionWrite>
  159    [[nodiscard]]
  160    auto biProjected(ProjectionRead&& projectionRead, ProjectionWrite&& projectionWrite) noexcept {
  161        return aui::detail::property::makeBidirectionalProjection(*this,
  162                                                                  std::forward<ProjectionRead>(projectionRead),
  163                                                                  std::forward<ProjectionWrite>(projectionWrite));
  164    }
  165
  169    template<aui::detail::property::ProjectionBidirectional<T> Projection>
  170    [[nodiscard]]
  171    auto biProjected(Projection&& projectionBidirectional) noexcept {
  172        return aui::detail::property::makeBidirectionalProjection(*this, projectionBidirectional);
  173    }
  174
  175    template<typename Rhs>
  176    decltype(auto) operator[](Rhs&& rhs) const {
  177        return raw[std::forward<Rhs>(rhs)];
  178    }
  179
  180private:
  181    friend class AObject;
  185    [[nodiscard]]
  186    auto assignment() noexcept {
  187        return aui::detail::property::makeAssignment(*this);
  188    }
  189};
  190static_assert(AAnyProperty<AProperty<int>>, "AProperty does not conform AAnyProperty concept");
  191static_assert(AAnyProperty<AProperty<int>&>, "AProperty does not conform AAnyProperty concept");
  192static_assert(APropertyReadable<const AProperty<int>>, "const AProperty does not conform AAnyProperty concept");
  193
  194// implementation of property modifier for AProperty is relatively straightforward (in comparison to APropertyDef) since
  195// we have access to raw value. we just need to notify the property upon destruction
  196template<typename T>
  198public:
  199    using Underlying = T;
  200    PropertyModifier(AProperty<T>& owner): mOwner(&owner) {}
  201    ~PropertyModifier() {
  202        if (mOwner == nullptr) {
  203            return;
  204        }
  205        mOwner->notify();
  206    }
  207
  208    [[nodiscard]]
  209    Underlying& value() const noexcept {
  210        return mOwner->raw;
  211    }
  212
  213    [[nodiscard]]
  214    Underlying* operator->() const noexcept {
  215        return &value();
  216    }
  217
  218private:
  219    AProperty<T>* mOwner;
  220};
  221
  222// simple check operators defined in detail/property.h work.
  223static_assert(requires { AProperty<int>() + 1; });
  224
  225#include <AUI/Common/APropertyDef.h> // just for convenience
Definition AObjectBase.h:24
Temporary transparent object that gains write access to underlying property's value,...
Definition PropertyModifier.h:26
Definition concepts.h:226
Definition concepts.h:195
type_of< T > t
Selects views that are of the specified C++ types.
Definition type_of.h:71
ASignal< Args... > emits
A signal declaration.
Definition ASignal.h:577
#define emit
emits the specified signal in context of this object.
Definition AObject.h:344
Basic easy-to-use property implementation containing T.
Definition AProperty.h:30
void notify()
Notify observers that a change was occurred (no preconditions).
Definition AProperty.h:116
auto readProjected(Projection &&projection) const noexcept
Makes a readonly projection of this property.
Definition AProperty.h:150
emits< T > changed
Signal that notifies data changes.
Definition AProperty.h:47
auto biProjected(Projection &&projectionBidirectional) noexcept
Makes a bidirectional projection of this property (by a single aui::lambda_overloaded).
Definition AProperty.h:171
aui::PropertyModifier< AProperty > writeScope() noexcept
Definition AProperty.h:141
auto biProjected(ProjectionRead &&projectionRead, ProjectionWrite &&projectionWrite) noexcept
Makes a bidirectional projection of this property.
Definition AProperty.h:160
T raw
Stored value.
Definition AProperty.h:42
static void addDependency(const AAbstractSignal &signal)
Adds observer to the specified signal, if called inside a reactive expression evaluation.