AUI Framework  master
Cross-platform base for C++ UI apps
Loading...
Searching...
No Matches
properties.h
    1
    2
    3
    4/* !!!!!!!!!!!!!!!! THIS FILE IS AUTOGENERATED !!!!!!!!!!!!!!!! */
    5
    6
  288 * GENDERS = std::array { Gender::MALE, Gender::FEMALE, GENDER::OTHER };
  289 * @endcode
  290 *
  291 * We just using `aui::enumerate::ALL_VALUES` because it was provided conveniently by `AUI_ENUM_VALUES` for us.
  292 *
  293 * It's not hard to guess that we'll use indices of this array to uniquely identify `Gender` associated with this
  294 * index:
  295 * @code{cpp}
  296 * /* pseudocode */
  297 * GENDERS[0]; // -> MALE
  298 * GENDERS[1]; // -> FEMALE
  299 * GENDERS[2]; // -> OTHER
  300 * @endcode
  301 *
  302 *
  303 * To perform opposite operation (i.e., `Gender` to int), we can use `aui::indexOf`:
  304 * @code{cpp}
  305 * /* pseudocode */
  306 * aui::indexOf(GENDERS, Gender::MALE);   // -> 0
  307 * aui::indexOf(GENDERS, Gender::FEMALE); // -> 1
  308 * aui::indexOf(GENDERS, Gender::OTHER);  // -> 2
  309 * @endcode
  310 *
  311 *
  312 * To bring these conversions together, let's use overloaded lambda:
  313 * @code{cpp}
  314 * static constexpr auto GENDER_INDEX_PROJECTION = aui::lambda_overloaded {
  315 *     [](Gender g) -> int { return aui::indexOf(GENDERS, g).valueOr(0); },
  316 *     [](int i) -> Gender { return GENDERS[i]; },
  317 * };
  318 * @endcode
  319 *
  320 * @note
  321 * It's convenient to use lambda trailing return type syntax (i.e., `... -> int`, `... -> Gender`)
  322 * to make it obvious what do transformations do and how one type is transformed to another.
  323 *
  324 * The function-like object above detects the direction of transformation and performs as follows:
  325 * @code{cpp}
  326 * GENDER_INDEX_PROJECTION(0); // -> MALE
  327 * GENDER_INDEX_PROJECTION(Gender::MALE); // -> 0
  328 * @endcode
  329 *
  330 *
  331 * It is all what we need to set up bidirectional transformations. Inside AUI_ENTRY:
  332 * @code{cpp}
  333 *    auto user = aui::ptr::manage(new User { .gender = Gender::MALE });
  334 *
  335 *    class MyWindow: public AWindow {
  336 *    public:
  337 *        MyWindow(const _<User>& user) {
  338 *            // generate a string list model for genders from GENDERS array defined earlier
  339 *            auto gendersStr = AListModel<AString>::fromVector(
  340 *                GENDERS
  341 *                | ranges::views::transform(AEnumerate<Gender>::toName)
  342 *                | ranges::to_vector);
  343 *
  344 *            // equivalent:
  345 *            // gendersStr = { "MALE", "FEMALE", "OTHER" }
  346 *            // you can customize the displayed strings by playing with
  347 *            // ranges::views::transform argument.
  348 *
  349 *            setContents(Centered {
  350 *              _new<ADropdownList>(gendersStr) let {
  351 *                  // AObject::connect(user->gender, it->selectionId());
  352 *                  //
  353 *                  // The code above would break, because Gender and int
  354 *                  // (selectionId() type) are incompatible.
  355 *                  //
  356 *                  // Instead, define bidirectional projection:
  357 *                   AObject::biConnect(
  358 *                       user->gender.biProjected(GENDER_INDEX_PROJECTION),
  359 *                       it->selectionId());
  360 *                  },
  361 *            });
  362 *        }
  363 *    };
  364 *    _new<MyWindow>(user)->show();
  365 * @endcode
  366 *
  367 * ![](imgs/UIDataBindingTest.Declarative_bidirectional_projection_1.png)
  368 *
  369 * - If we try to change `user->gender` programmatically, ADropdownList will respond:
  370 * @code{cpp}
  371 * user->gender = Gender::FEMALE;
  372 * EXPECT_EQ(dropdownList->getSelectedId(), 1); // second option
  373 * @endcode
  374 *
  375 * ![](imgs/UIDataBindingTest.Declarative_bidirectional_projection_2.png)
  376 *
  377 * - If the user changes the value of ADropdownList, it reflects on the model as well:
  378 * @code{cpp}
  379 * EXPECT_EQ(user->gender, Gender::OTHER);
  380 * @endcode
  381 *
  382 * ![](imgs/UIDataBindingTest.Declarative_bidirectional_projection_3.png)
  383 *
  384 * # UI declarative data binding {#UI_declarative_data_binding}
  385 * <b aui-src="aui.uitests/tests/UIDataBindingTest.cpp#L479"></b>
  386 * As said earlier, \c let syntax is a little bit clunky and requires extra boilerplate code to set up.
  387 *
  388 * Here's where declarative syntax comes into play. The logic behind the syntax is the same as in
  389 * `AObject::connect`/`AObject::biConnect` (for ease of replacement/understanding).
  390 *
  391 * Declarative syntax uses `&` and `&&` operators to set up connections. These were chosen intentionally: `&&` resembles
  392 * chain, so we "chaining view and property up".
  393 *
  394 * - `&` sets up one-directional connection (`AObject::connect`).
  395 * - `&&` sets up bidirectional connection (`AObject::biConnect`).
  396 *
  397 * Also, `>` operator (resembles arrow) is used to specify the destination slot.
  398 *
  399 * The example below is essentially the same as @ref "UIDataBindingTest_Label_via_let" but uses declarative connection set up syntax.
  400 *
  401 * ## Label via declarative {#UIDataBindingTest_Label_via_declarative}
  402 * <b aui-src="aui.uitests/tests/UIDataBindingTest.cpp#L494"></b>
  403 * Use `&` and `>` expression to connect the model's username property to the label's @ref ALabel::text "text"
  404 * property.
  405 * @code{cpp}
  406 *    using namespace declarative;
  407 *    struct User {
  408 *        AProperty<AString> name;
  409 *    };
  410 *
  411 *    auto user = aui::ptr::manage(new User { .name = "Roza" });
  412 *
  413 *    class MyWindow: public AWindow {
  414 *    public:
  415 *        MyWindow(const _<User>& user) {
  416 *            setContents(Centered {
  417 *              _new<ALabel>() & user->name > &ALabel::text
  418 *            });
  419 *        }
  420 *    };
  421 *    auto window = _new<MyWindow>(user);
  422 *    window->show();
  423 * @endcode
  424 *
  425 * ![](imgs/UIDataBindingTest.Label_via_declarative_1.png)
  426 * Note that the label already displays the value stored in User.
  427 *
  428 * Let's change the name:
  429 * @code{cpp}
  430 * user->name = "Vasil";
  431 * @endcode
  432 *
  433 * ![](imgs/UIDataBindingTest.Label_via_declarative_2.png)
  434 * In this example, we've achieved the same intuitive behaviour of data binding of `user->name` (like in
  435 * @ref "UIDataBindingTest_Label_via_let" example) but using declarative syntax. The logic behind `&` is almost the same as with `let`
  436 * and `AObject::connect` so projection use cases can be adapted in a similar manner.
  437 *
  438 * ## ADataBindingDefault for omitting view property {#UIDataBindingTest_ADataBindingDefault_for_omitting_view_property}
  439 * <b aui-src="aui.uitests/tests/UIDataBindingTest.cpp#L551"></b>
  440 * In previous example we have explicitly specified ALabel's property to connect with.
  441 *
  442 * One of notable features of declarative way (in comparison to procedural `let` way) is that we can omit the view's
  443 * property to connect with if such `ADataBindingDefault` specialization exist for the target view and the property
  444 * type. Some views have already predefined such specialization for their underlying types. For instance, ALabel has
  445 * such specialization:
  446 *
  447 * @code{cpp}
  448 * /* PREDEFINED! You don't need to define it! This listing is an example */
  449 * template<>
  450 * struct ADataBindingDefault<ALabel, AString> {
  451 * public:
  452 *     static auto property(const _<ALabel>& view) { return view->text(); }
  453 * };
  454 * @endcode
  455 *
  456 * We can use this predefined specialization to omit the destination property:
  457 * @code{cpp}
  458 * _new<ALabel>() & user->name
  459 * @endcode
  460 *
  461 *
  462 * Behaviour of such connection is equal to @ref "UIDataBindingTest_Label_via_declarative":
  463 *
  464 * ![](imgs/UIDataBindingTest.Label_via_declarative_1.png)
  465 * Note that the label already displays the value stored in User.
  466 *
  467 * Let's change the name:
  468 * @code{cpp}
  469 * user->name = "Vasil";
  470 * @endcode
  471 *
  472 * ![](imgs/UIDataBindingTest.Label_via_declarative_2.png)
  473 * In this example, we've omitted the destination property of the connection while maintaining the same behaviour
  474 * as in @ref "UIDataBindingTest_Label_via_declarative".
  475 *
  476 * ## ADataBindingDefault strong type propagation {#UIDataBindingTest_ADataBindingDefault_strong_type_propagation}
  477 * <b aui-src="aui.uitests/tests/UIDataBindingTest.cpp#L622"></b>
  478 * Think of `ADataBindingDefault` as we're not only connecting properties to properties, but also creating a
  479 * "property to view" relationship. This philosophy covers the following scenario.
  480 *
  481 * In AUI, there's aui::ranged_number template which stores valid value range right inside the type:
  482 * @code{cpp}
  483 * struct User {
  485 * };
  486 * @endcode
  487 *
  488 *
  489 * These strong types can be used to propagate their traits on views, i.e., ANumberPicker. When using declarative
  490 * syntax, the property system calls `ADataBindingDefault::setup` to apply some extra traits of the bound value on
  491 * the view. Here's an abstract on how `ANumberPicker` defines specialization of `ADataBingingDefault` with
  492 * `aui::ranged_number`:
  493 * @code{cpp}
  494 * /* PREDEFINED! You don't need to define it! This listing is an example */
  495 * template <aui::arithmetic UnderlyingType, auto min, auto max>
  496 * struct ADataBindingDefault<ANumberPicker, aui::ranged_number<UnderlyingType, min, max>> {
  497 * public:
  498 *     static auto property(const _<ANumberPicker>& view) {
  499 *         return view->value();
  500 *     }
  501 *     static void setup(const _<ANumberPicker>& view) {
  502 *         view->setMin(aui::ranged_number<UnderlyingType, min, max>::MIN);
  503 *         view->setMax(aui::ranged_number<UnderlyingType, min, max>::MAX);
  504 *     }
  505 *     // ...
  506 * };
  507 * @endcode
  508 *
  509 * As you can see, this specialization pulls the min and max values from `aui::ranged_number` type and sets them
  510 * to `ANumberPicker`. This way `ANumberPicker` finds out the valid range of values by simply being bound to value
  511 * that has constraints encoded inside its type.
  512 * @code{cpp}
  513 * _new<ANumberPicker>() && user->age,
  514 * @endcode
  515 *
  516 * @note
  517 * We're using `operator&&` here to set up bidirectional connection. For more info, go to
  518 * @ref "UIDataBindingTest_Declarative_bidirectional_connection".
  519 *
  520 *
  521 * By creating this connection, we've done a little bit more. We've set ANumberPicker::setMin and
  522 * ANumberPicker::setMax as well:
  523 * @code{cpp}
  524 * EXPECT_EQ(numberPicker->getMin(), 1);
  525 * EXPECT_EQ(numberPicker->getMax(), 99);
  526 * @endcode
  527 *
  528 *
  529 * This example demonstrates how to use declarative binding to propagate strong types. `aui::ranged_number`
  530 * propagates its constraints on `ANumberPicker` thanks to `ADataBindingDefault` specialization.
  531 *
  532 * ## Label via declarative projection {#UIDataBindingTest_Label_via_declarative_projection}
  533 * <b aui-src="aui.uitests/tests/UIDataBindingTest.cpp#L696"></b>
  534 * We can use projections in the same way as with `let`.
  535 * @code{cpp}
  536 *    using namespace declarative;
  537 *    struct User {
  538 *        AProperty<AString> name;
  539 *    };
  540 *
  541 *    auto user = aui::ptr::manage(new User { .name = "Roza" });
  542 *
  543 *    class MyWindow: public AWindow {
  544 *    public:
  545 *        MyWindow(const _<User>& user) {
  546 *            _<ALabel> label;
  547 *            setContents(Centered {
  548 *                _new<ALabel>() & user->name.readProjected(&AString::uppercase)
  549 *            });
  550 *        }
  551 *    };
  552 *    auto window = _new<MyWindow>(user);
  553 *    window->show();
  554 * @endcode
  555 *
  556 * ![](imgs/UIDataBindingTest.Label_via_declarative_projection_1.png)
  557 *
  558 * Note that the label already displays the **projected** value stored in User.
  559 *
  560 * Projection applies to value changes as well. Let's change the name:
  561 * @code{cpp}
  562 *    user->name = "Vasil";
  563 *
  564 *    EXPECT_EQ(user->name, "Vasil");
  565 *    EXPECT_EQ(label->text(), "VASIL"); // projected
  566 * @endcode
  567 *
  568 * ![](imgs/UIDataBindingTest.Label_via_declarative_projection_2.png)
  569 *
  570 * ## Declarative bidirectional connection {#UIDataBindingTest_Declarative_bidirectional_connection}
  571 * <b aui-src="aui.uitests/tests/UIDataBindingTest.cpp#L825"></b>
  572 * In previous examples, we've used `&` to make one directional (one sided) connection. This is
  573 * perfectly enough for ALabel because it cannot be changed by user.
  574 *
  575 * In some cases, you might want to use property-to-property as it's bidirectional. It's used for populating view
  576 * from model and obtaining data from view back to the model.
  577 *
  578 * For this example, let's use ATextField instead of ALabel as it's an editable view. In this case, we'd want to use
  579 * `&&` because we do want `user->name` to be aware of changes of the view.
  580 * @code{cpp}
  581 * _new<ATextField>() && user->name
  582 * @endcode
  583 *
  584 *
  585 * This gives the following result:
  586 * ![](imgs/UIDataBindingTest.Declarative_bidirectional_connection_1.png)
  587 *
  588 * Let's change the name programmatically:
  589 * @code{cpp}
  590 * user->name = "Vasil";
  591 * @endcode
  592 *
  593 *
  594 * ATextField will respond:
  595 * ![](imgs/UIDataBindingTest.Declarative_bidirectional_connection_2.png)
  596 *
  597 * If the user changes the value from UI, these changes will reflect on `user->model` as well:
  598 * ![](imgs/UIDataBindingTest.Declarative_bidirectional_connection_3.png)
  599 * @code{cpp}
  600 * EXPECT_EQ(user->name, "Changed from UI");
  601 * @endcode
  602 *
  603 *
  604 * This way we've set up bidirectional projection via `&&` which makes `user->name` aware of UI
  605 * changes.
  606 *
  607 * ## Declarative bidirectional projection {#UIDataBindingTest_Declarative_bidirectional_projection}
  608 * <b aui-src="aui.uitests/tests/UIDataBindingTest.cpp#L890"></b>
  609 * We can use projections in the same way as with `let`.
  610 *
  611 * Let's repeat the @ref "UIDataBindingTest_Bidirectional_projection" sample in declarative way:
  612 * @code{cpp}
  613 * _new<ADropdownList>(gendersStr) && user->gender.biProjected(GENDER_INDEX_PROJECTION) > &ADropdownList::selectionId
  614 * @endcode
  615 *
  616 * ![](imgs/UIDataBindingTest.Declarative_bidirectional_projection_1.png)
  617 * @note
  618 * We used the `&&` operator here instead of `&` because we want the connection work in both
  619 * directions: `user.gender -> ADropdownList` and `ADropdownList -> user.gender`.
  620 *
  621 *
  622 * - If we try to change `user->gender` programmatically, ADropdownList will respond:
  623 * @code{cpp}
  624 * user->gender = Gender::FEMALE;
  625 * EXPECT_EQ(dropdownList->getSelectedId(), 1); // second option
  626 * @endcode
  627 *
  628 * ![](imgs/UIDataBindingTest.Declarative_bidirectional_projection_2.png)
  629 *
  630 * - If the user changes the value of ADropdownList, it reflects on the model as well:
  631 * @code{cpp}
  632 * EXPECT_EQ(user->gender, Gender::OTHER);
  633 * @endcode
  634 *
  635 * ![](imgs/UIDataBindingTest.Declarative_bidirectional_projection_3.png)
  636 *
  637 */
A button with dropdown list.
Definition ADropdownList.h:27
static const AString & toName(enum_t value)
Map runtime enum value to name. Throws an exception if no such value.
Definition AEnumerate.h:134
Represents a simple single-line text display view.
Definition ALabel.h:23
static _< AListModel< StoredType > > fromVector(AVector< V > t)
Definition AListModel.h:268
A text field for numbers with increase/decrease buttons.
Definition ANumberPicker.h:24
Represents a Unicode character string.
Definition AString.h:38
Editable field with text to receive a text input from the user.
Definition ATextField.h:24
void setContents(const _< AViewContainer > &container)
Moves (like via std::move) all children and layout of the specified container to this container.
Represents a window in the underlying windowing system.
Definition AWindow.h:45
std::add_lvalue_reference_t< T > value() const noexcept
Dereferences the stored pointer.
Definition SharedPtrTypes.h:294
class_of c
Selects views that are of the specified classes.
Definition class_of.h:84
type_of< T > t
Selects views that are of the specified C++ types.
Definition type_of.h:71
constexpr auto ALL_VALUES
constexpr std::array of all possible enum values is the order they've been passed to AUI_ENUM_VALUES.
Definition AEnumerate.h:176
AOptional< size_t > indexOf(const Container &c, const typename Container::const_reference value) noexcept
Finds the index of the first occurrence of the value.
Definition containers.h:227
static void biConnect(PropertySource &&propertySource, PropertyDestination &&propertyDestination)
Connects source property to the destination property and opposite (bidirectionally).
Definition AObject.h:156
AString name(T *v)
Runtime reflection based on typeid.
Definition AReflect.h:29
#define let
Performs multiple operations on a single object without repeating its name (in place) This function c...
Definition kAUI.h:262
#define AUI_ENUM_VALUES(enum_t,...)
Defines all enum values for AEnumerate.
Definition AEnumerate.h:208
#define AUI_ENTRY
Application entry point.
Definition Entry.h:90
Defines how View handles properties of FieldType type.
Definition ADataBinding.h:37
static void setup(const _< View > &view)
Called then view linked with field.
Definition ADataBinding.h:43
static auto property(const _< View > &view)
Returns property definition for FieldType.
Definition ADataBinding.h:49
Basic easy-to-use property implementation containing T.
Definition AProperty.h:30
static _< T > manage(T *raw)
Delegates memory management of the raw pointer T* raw to the shared pointer, which is returned.
Definition SharedPtrTypes.h:424
Clamps the possible values for a number to the specified range: [min;max].
Definition values.h:423