AUI Framework  master
Cross-platform base for C++ UI apps
Loading...
Searching...
No Matches
property.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
   18namespace aui::detail::property {
   19
   20template<typename Projection, typename Source>
   21concept ProjectionBidirectional = requires (Projection&& projectionBidirectional, Source&& source) {
   22    // projection must accept SOURCE type.
   23    { projectionBidirectional } -> aui::invocable<const Source&>;
   24
   25    // projection must be able to accept DESTINATION type to perform the opposite conversion.
   26    { projectionBidirectional } -> aui::invocable<const std::invoke_result_t<Projection, const Source&>&>;
   27
   28    // projection's SOURCE type must be distinguishable from DESTINATION type.
   29    requires not aui::same_as<std::decay_t<decltype(projectionBidirectional(source))>, std::decay_t<Source>>;
   30};
   31
   32template <typename Property>   // can't use AAnyProperty here, as concept would depend on itself
   33auto makeAssignment(Property&& property) { // note the rvalue reference template argument here:
   34    // pass your property as std::move(*this) if your
   35    // property-compliant struct is temporary! otherwise you'll
   36    // spend your weekend on debugging segfaults :)
   37    using Underlying = std::decay_t<decltype(*property)>;
   38    struct Invocable {
   39        Property property;
   40        void operator()(const Underlying& value) const {
   41            // avoid property assignment loop (bidirectional connection)
   42            // PropertyCommonTest.Property2PropertyBoth
   43            if (property.changed.isAtSignalEmissionState()) {
   44                return;
   45            }
   46            const_cast<Property&>(property) = std::move(value);
   47        };
   48    } i = { std::forward<Property>(property) };
   49
   50    return ASlotDef<decltype(property.boundObject()), decltype(i)> {
   51        property.boundObject(),
   52        std::move(i),
   53    };
   54}
   55
   56template<typename Property, typename Projection>
   57auto makeReadonlyProjection(Property&& property, Projection&& projection) {
   58    using Underlying = std::decay_t<decltype(*property)>;
   59    auto signalProjected = property.changed.projected(projection);
   60    using Signal = decltype(signalProjected);
   61    using ProjectionResult = std::invoke_result_t<Projection, Underlying>;
   62    struct PropertyReadProjection {
   63    protected:
   64        Property wrappedProperty;
   65        Projection projection;
   66
   67    public:
   68        Signal changed;
   69        PropertyReadProjection(Property wrappedProperty, Projection&& projection, Signal changed)
   70            : wrappedProperty(wrappedProperty), projection(std::move(projection)), changed(changed) {}
   71        using Underlying = ProjectionResult;
   72
   73        [[nodiscard]]
   74        auto boundObject() const {
   75            return wrappedProperty.boundObject();
   76        }
   77
   78        [[nodiscard]]
   79        Underlying value() const {
   80            return std::invoke(projection, wrappedProperty.value());
   81        }
   82
   83        [[nodiscard]]
   84        Underlying operator*() const noexcept {
   85            return value();
   86        }
   87
   88        [[nodiscard]] operator Underlying() const { return value(); }
   89
   90    };
   91    static_assert(APropertyReadable<PropertyReadProjection>, "PropertyReadProjection must conform with APropertyReadable");
   92    return PropertyReadProjection(std::forward<Property>(property), std::forward<Projection>(projection), std::move(signalProjected));
   93}
   94
   95template<typename PropertyReadProjection, typename ProjectionWrite>
   96struct PropertyReadWriteProjection: PropertyReadProjection {
   97    ProjectionWrite projectionWrite;
   98    using Underlying = typename PropertyReadProjection::Underlying;
   99    explicit PropertyReadWriteProjection(PropertyReadProjection&& read, ProjectionWrite&& projectionWrite)
  100        : PropertyReadProjection(std::move(read)), projectionWrite(std::move(projectionWrite)) {}
  101
  102    template <aui::convertible_to<Underlying> U>
  103    PropertyReadWriteProjection& operator=(U&& value) noexcept {
  104        this->wrappedProperty = std::invoke(projectionWrite, std::forward<U>(value));
  105        return *this;
  106    }
  107
  111    void notify() {
  112        this->wrappedProperty.notify();
  113    }
  114private:
  115    friend class API_AUI_CORE ::AObject;
  116
  120    [[nodiscard]]
  121    auto assignment() noexcept {
  122        return aui::detail::property::makeAssignment(std::move(*this));
  123    }
  124};
  125
  126template<typename Property, aui::not_overloaded_lambda ProjectionRead, aui::not_overloaded_lambda ProjectionWrite>
  127auto makeBidirectionalProjection(Property&& property, ProjectionRead&& projectionRead, ProjectionWrite&& projectionWrite) {
  128    auto readProjected =
  129        makeReadonlyProjection(std::forward<Property>(property), std::forward<ProjectionRead>(projectionRead));
  130    using PropertyReadProjection = decltype(readProjected);
  131    PropertyReadWriteProjection result(std::move(readProjected), std::forward<ProjectionWrite>(projectionWrite));
  132    //    static_assert(APropertyWritable<decltype(result)>, "PropertyReadWriteProjection must conform with APropertyWriteable");
  133    return result;
  134}
  135
  136template<typename Property, ProjectionBidirectional<typename std::decay_t<Property>::Underlying> Projection>
  137auto makeBidirectionalProjection(Property&& property, Projection&& projection) {
  138    // we must define non-overloaded lambdas for makeBidirectionalProjection overload.
  139    using Source = std::decay_t<typename std::decay_t<Property>::Underlying>;
  140    using Destination = std::decay_t<std::invoke_result_t<Projection, const Source&>>;
  141    return makeBidirectionalProjection(
  142        std::forward<Property>(property),
  143        [projection](const Source& s) -> Destination { return std::invoke(projection, s); },
  144        [projection](const Destination& d) -> Source { return std::invoke(projection, d); });
  145}
  146}
  147
void API_AUI_CORE read(aui::no_escape< ISeekableInputStream > stream, const std::function< void(const FileEntry &)> &visitor)
ZIP reader.