Skip to content

AUI_REACT#

Explicitly denotes a reactive expression.

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

Definition#

#define AUI_REACT(...)        \
    ::aui::react::makeExpression(       \
        [=]() -> decltype(auto) { return (__VA_ARGS__); } \
    )

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_OVERRIDE_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_OVERRIDE_STYLE { LayoutSpacing { 4_dp } },
            Label { AUI_REACT("{:.1f}s"_format(duration_cast<milliseconds>(*mElapsedTime).count() / 1000.f)) },
            Horizontal {
              Label { "Duration:" },
              /// [timer_slider]
              Slider {
                .value = AUI_REACT(float(mDuration->count()) / float(MAX_DURATION.count())),
                .onValueChanged =
                    [this](aui::float_within_0_1 newValue) {
                        mDuration =
                            high_resolution_clock::duration(long(float(MAX_DURATION.count()) * float(newValue)));
                    },
              } AUI_OVERRIDE_STYLE { Expanding { 1, 0 } },
              /// [timer_slider]
            } AUI_OVERRIDE_STYLE { LayoutSpacing { 4_dp } },
            _new<AButton>("Reset Timer") AUI_OVERRIDE_STYLE {
                  Expanding { 1, 0 },
                } AUI_LET { connect(it->clicked, me::reset); },
          } AUI_OVERRIDE_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. The lambda is wrapped with aui::react::Expression to be strongly typed.

The decltype(auto) return type is used to avoid property copy when referenced.

Examples#

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

7GUIs Flight Booker — Flight Booker.

            std::unique_lock lock(state.userChangesText);
            state.parsed = parseDate(s);
        });
        AObject::connect(
            AUI_REACT(ass::PropertyList { BackgroundSolid(!state->parsed->hasValue() ? AColor::RED : AColor::WHITE) }),
            AUI_SLOT(it)::setCustomStyle);
    };
}
/// [date_field]
examples/7guis/circle_drawer/src/main.cpp

7GUIs Circle Drawer — Undo, redo, dialog control.

                          "", 200_dp, 50_dp, dynamic_cast<AWindow*>(ASurface::current()), WindowStyle::MODAL);
                      radiusPopup->setContents(Vertical {
                        Label { "Adjust diameter of circle at {}."_format(circle->position) },
                        Slider {
                          .value = AUI_REACT(circle->radius / MAX_RADIUS),
                          .onValueChanged =
                              [this, circle](aui::float_within_0_1 s) {
                                  circle->radius = s * MAX_RADIUS;
                                  mState->circles.notify();
                              },
examples/7guis/timer/src/main.cpp

7GUIs Timer — Timer example.

                        it->setCustomStyle({ Expanding { 1, 0 } });
                    },
              },
            } AUI_OVERRIDE_STYLE { LayoutSpacing { 4_dp } },
            Label { AUI_REACT("{:.1f}s"_format(duration_cast<milliseconds>(*mElapsedTime).count() / 1000.f)) },
            Horizontal {
              Label { "Duration:" },
              /// [timer_slider]
              Slider {
                .value = AUI_REACT(float(mDuration->count()) / float(MAX_DURATION.count())),
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_OVERRIDE_STYLE { LayoutSpacing { 4_dp } },
        });
    }
examples/app/notes/src/main.cpp

Notes App — Note taking app that demonstrates usage of AListModel, AProperty, user data saving and loading.

    };

    return Vertical {
        Label {
          .text = AUI_REACT(stringOneLinePreview(note->title)),
        } AUI_OVERRIDE_STYLE { FontSize { 10_pt }, ATextOverflow::ELLIPSIS },
        Label {
          .text = AUI_REACT(stringOneLinePreview(note->content)),
        } AUI_OVERRIDE_STYLE {
              ATextOverflow::ELLIPSIS,
examples/ui/contacts/src/main.cpp

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

                            SpacerFixed(8_dp),
                            AText::fromString(predefined::DISCLAIMER) AUI_OVERRIDE_STYLE { ATextAlign::CENTER },
                            SpacerFixed(8_dp),
                            experimental::Dynamic {
                                .content = AUI_REACT(mSearchQuery->empty() ? indexedList() : searchQueryList()),
                            },
                            Label { AUI_REACT("{} contact(s)"_format(mContactCount)) } AUI_LET {
                                AObject::connect(AUI_REACT(ass::PropertyList{
                                    FontSize { 10_pt },
                                    ATextAlign::CENTER,
examples/ui/opengl_simple/src/main.cpp

OpenGL Example — Demonstrates how to integrate custom OpenGL rendering with AUI Framework.

          _new<MyRenderer>(state) AUI_OVERRIDE_STYLE { Expanding() },
          Horizontal::Expanding {
            Vertical {
              Vertical {
                Label { AUI_REACT(fmt::format("FPS: {:.1f}", *state->fps)) },
              } AUI_OVERRIDE_STYLE {
                    Padding(16_dp),
                    BackgroundSolid { AColor::WHITE.transparentize(0.5f) },
                    MinSize { 150_dp, {} },
                  },
examples/ui/views/src/ExampleWindow.cpp

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

    };
    auto state = _new<State>();
    return Vertical {
        CheckBox {
          AUI_REACT(state->checked1),
          [state](bool checked) { state->checked1 = checked; },
          Label { "Unchecked checkbox" },
        },
        CheckBox {
          AUI_REACT(state->checked2),

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_OVERRIDE_STYLE { Opacity(0.5f), FontSize { 32_dp } },
    } AUI_OVERRIDE_STYLE {
        FixedSize { 64_dp },
        BorderRadius { 32_dp },
        BackgroundGradient { AColor::GRAY.lighter(0.5f), AColor::GRAY, 163_deg },