Skip to content

AUI_REACT#

Explicitly denotes a reactive expression.

Header:#include <AUI/Traits/macros.h>
CMake:aui_link(my_target PUBLIC aui::core)

Definition#

#define AUI_REACT(expression) [=]() -> decltype(auto) { return (expression); }

Detailed Description#

AUI_REACT is a core component of AUI Framework's reactive reactive programming model. It's used to create reactive expressions that automatically update UI elements when their dependent values change.

The expression is a C++ expression that depends on AProperty values:

AUI_REACT(expression)

Basic example#

This creates a label that automatically updates when property mCounter changes:

class CounterWindow : public AWindow {
public:
    CounterWindow() : AWindow("AUI - 7GUIs - Counter", 200_dp, 100_dp) {
        setContents(Centered {
          Horizontal {
            Label { AUI_REACT("Count: {}"_format(mCounter)) },
            Button { Label { "Count" }, [this] { mCounter += 1; } },
          } AUI_WITH_STYLE { LayoutSpacing { 4_dp } },
        });
    }

private:
    AProperty<int> mCounter;
};

Formatted label example#

class TimerWindow : public AWindow {
public:
    TimerWindow() : AWindow("AUI - 7GUIs - Timer", 300_dp, 50_dp) {
        setContents(Centered {
          Vertical::Expanding {
            Horizontal {
              Label { "Elapsed Time:" },
              Centered::Expanding {
                _new<AProgressBar>() AUI_LET {
                        it & mElapsedTimeRatio;
                        it->setCustomStyle({ Expanding { 1, 0 } });
                    },
              },
            } AUI_WITH_STYLE { LayoutSpacing { 4_dp } },
            Label { AUI_REACT("{:.1f}s"_format(duration_cast<milliseconds>(*mElapsedTime).count() / 1000.f)) },
            Horizontal {
              Label { "Duration:" },
              _new<ASlider>() AUI_LET {
                      it&& mDuration.biProjected(aui::lambda_overloaded {
                        [](high_resolution_clock::duration d) -> aui::float_within_0_1 {
                            return float(d.count()) / float(MAX_DURATION.count());
                        },
                        [](aui::float_within_0_1 d) -> high_resolution_clock::duration {
                            return high_resolution_clock::duration(long(float(d) * float(MAX_DURATION.count())));
                        },
                      });
                      it->setCustomStyle({ Expanding {} });
                  },
            } AUI_WITH_STYLE { LayoutSpacing { 4_dp } },
            _new<AButton>("Reset Timer") AUI_WITH_STYLE {
                  Expanding { 1, 0 },
                } AUI_LET { connect(it->clicked, me::reset); },
          } AUI_WITH_STYLE { LayoutSpacing { 4_dp } },
        });

        connect(mTimer->fired, me::update);
        mTimer->start();
    }

private:
    _<ATimer> mTimer = _new<ATimer>(100ms);
    high_resolution_clock::time_point mStartTime = high_resolution_clock::now();
    AProperty<high_resolution_clock::time_point> mCurrentTime;
    AProperty<high_resolution_clock::duration> mDuration = 30s;

    APropertyPrecomputed<high_resolution_clock::duration> mElapsedTime = [&] {
        return std::min(mCurrentTime - mStartTime, *mDuration);
    };

    APropertyPrecomputed<aui::float_within_0_1> mElapsedTimeRatio = [&] {
        return float(mElapsedTime->count()) / float(mDuration->count());
    };

    void update() { mCurrentTime = high_resolution_clock::now(); }

    void reset() { mStartTime = high_resolution_clock::now(); }
};

Implementation details#

When used in declarative UI building, AUI_REACT creates an instance of APropertyPrecomputed<T> behind the scenes, which:

  1. Evaluates the expression initially.
  2. Sets up observers for all dependent properties.
  3. Re-evaluates when dependencies change.

The macros itself consists of a lambda syntax with forced [=] capture and explicit decltype(auto) return type.

Examples#

examples/ui/contacts/src/main.cpp

AUI Contacts - Usage of AUI_DECLARATIVE_FOR to make a contacts-like application.

        mSelectedContact = nullptr;
    }

    _<AView> indexedList() {
        return AUI_DECLARATIVE_FOR(group, *mContacts | ranges::views::chunk_by([](const _<Contact>& lhs, const _<Contact>& rhs) {
                                return groupLetter(lhs->displayName) == groupLetter(rhs->displayName);
                            }), AVerticalLayout) {
            auto firstContact = *ranges::begin(group);
            auto firstLetter = groupLetter(firstContact->displayName);
            ALogger::info("Test") << "Computing view for group " << AString(1, firstLetter);

examples/ui/views/src/ExampleWindow.cpp

Views Example - All-in-one views building example.

                                    }
                                }),
                            _new<ASpacerExpanding>(),
                          } AUI_WITH_STYLE { LayoutSpacing { 4_dp } },
                          AUI_DECLARATIVE_FOR(i, *state->colors, AWordWrappingLayout) {
                              return Horizontal {
                                  _new<ALabel>(i.toString()) AUI_WITH_STYLE {
                                      TextColor { i.readableBlackOrWhite() },
                                  }
                              } AUI_WITH_STYLE {

examples/7guis/counter/src/main.cpp

7GUIs Counter - Simple counter.

public:
    CounterWindow() : AWindow("AUI - 7GUIs - Counter", 200_dp, 100_dp) {
        setContents(Centered {
          Horizontal {
            Label { AUI_REACT("Count: {}"_format(mCounter)) },
            Button { Label { "Count" }, [this] { mCounter += 1; } },
          } AUI_WITH_STYLE { LayoutSpacing { 4_dp } },
        });
    }

examples/7guis/timer/src/main.cpp

7GUIs Timer - Timer example.

                        it->setCustomStyle({ Expanding { 1, 0 } });
                    },
              },
            } AUI_WITH_STYLE { LayoutSpacing { 4_dp } },
            Label { AUI_REACT("{:.1f}s"_format(duration_cast<milliseconds>(*mElapsedTime).count() / 1000.f)) },
            Horizontal {
              Label { "Duration:" },
              _new<ASlider>() AUI_LET {
                      it&& mDuration.biProjected(aui::lambda_overloaded {
                        [](high_resolution_clock::duration d) -> aui::float_within_0_1 {

Examples#

examples/ui/contacts/src/view/ContactDetailsView.cpp

AUI Contacts - Usage of AUI_DECLARATIVE_FOR to make a contacts-like application.

namespace {
_<AView> profilePhoto(const _<Contact>& contact) {
    return Centered {
        Label {
          AUI_REACT(contact->displayName->empty() ? "?" : AString(1, contact->displayName->first()).uppercase())
        } AUI_WITH_STYLE { Opacity(0.5f), FontSize { 32_dp } },
    } AUI_WITH_STYLE {
        FixedSize { 64_dp },
        BorderRadius { 32_dp },
        BackgroundGradient { AColor::GRAY.lighter(0.5f), AColor::GRAY, 163_deg },