288 * GENDERS = std::array { Gender::MALE, Gender::FEMALE, GENDER::OTHER };
293 * It
's not hard to guess that we'll use indices of
this array to uniquely identify `Gender` associated with
this
303 * To perform opposite operation (i.e., `Gender` to
int), we can use `
aui::indexOf`:
312 * To bring these conversions together,
let's use overloaded lambda:
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]; },
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.
324 * The function-like
object above detects the direction of transformation and performs as follows:
326 * GENDER_INDEX_PROJECTION(0);
327 * GENDER_INDEX_PROJECTION(Gender::MALE);
331 * It is all what we need to set up bidirectional transformations. Inside
AUI_ENTRY:
335 *
class MyWindow:
public AWindow {
337 * MyWindow(
const _<User>& user) {
342 * | ranges::to_vector);
350 * _new<ADropdownList>(gendersStr)
let {
358 * user->gender.biProjected(GENDER_INDEX_PROJECTION),
359 * it->selectionId());
364 * _new<MyWindow>(user)->show();
367 * 
369 * - If we
try to change `user->gender` programmatically,
ADropdownList will respond:
371 * user->gender = Gender::FEMALE;
372 * EXPECT_EQ(dropdownList->getSelectedId(), 1);
375 * 
377 * - If the user changes the value of
ADropdownList, it reflects on the model as well:
379 * EXPECT_EQ(user->gender, Gender::OTHER);
382 * 
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.
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).
391 * Declarative syntax uses `&` and `&&` operators to set up connections. These were chosen intentionally: `&&` resembles
392 * chain, so we "chaining view and property up".
394 * - `&` sets up one-directional connection (`AObject::connect`).
395 * - `&&` sets up bidirectional connection (`AObject::biConnect`).
397 * Also, `>` operator (resembles arrow) is used to specify the destination slot.
399 * The example below is essentially the same as @ref "UIDataBindingTest_Label_via_let" but uses declarative connection set up syntax.
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"
406 * using namespace declarative;
408 * AProperty<AString> name;
411 * auto user = aui::ptr::manage(new User { .name = "Roza" });
413 * class MyWindow: public AWindow {
415 * MyWindow(const _<User>& user) {
416 * setContents(Centered {
417 * _new<ALabel>() & user->name > &ALabel::text
421 * auto window = _new<MyWindow>(user);
425 * 
426 * Note that the label already displays the value stored in User.
428 * Let's change the
name:
430 * user->name =
"Vasil";
433 * 
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.
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.
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:
448 * /* PREDEFINED! You don't need to define it! This listing is an example */
452 *
static auto property(
const _<ALabel>& view) {
return view->text(); }
456 * We can use
this predefined specialization to omit the destination property:
458 * _new<ALabel>() & user->name
462 * Behaviour of such connection is equal to @ref
"UIDataBindingTest_Label_via_declarative":
464 * 
465 * Note that the label already displays the value stored in User.
467 * Let
's change the name:
469 * user->name = "Vasil";
472 * 
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".
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.
481 * In AUI, there's
aui::ranged_number template which stores valid value range right inside the type:
489 * These strong types can be used to propagate their traits on views, i.e.,
ANumberPicker. When
using declarative
491 * the view. Here
's an abstract on how `ANumberPicker` defines specialization of `ADataBingingDefault` with
492 * `aui::ranged_number`:
494 * /* PREDEFINED! You don't need to define it! This listing is an example */
495 *
template <aui::arithmetic UnderlyingType, auto min, auto max>
498 *
static auto property(
const _<ANumberPicker>& view) {
499 *
return view->
value();
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);
509 * As you can see,
this specialization pulls the min and max values from `
aui::ranged_number` type and sets them
511 * that has constraints encoded inside its type.
513 * _new<ANumberPicker>() && user->age,
517 * We
're using `operator&&` here to set up bidirectional connection. For more info, go to
518 * @ref "UIDataBindingTest_Declarative_bidirectional_connection".
521 * By creating this connection, we've done a little bit more. We
've set ANumberPicker::setMin and
522 * ANumberPicker::setMax as well:
524 * EXPECT_EQ(numberPicker->getMin(), 1);
525 * EXPECT_EQ(numberPicker->getMax(), 99);
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.
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`.
536 * using namespace declarative;
538 * AProperty<AString> name;
541 * auto user = aui::ptr::manage(new User { .name = "Roza" });
543 * class MyWindow: public AWindow {
545 * MyWindow(const _<User>& user) {
547 * setContents(Centered {
548 * _new<ALabel>() & user->name.readProjected(&AString::uppercase)
552 * auto window = _new<MyWindow>(user);
556 * 
558 * Note that the label already displays the **projected** value stored in User.
560 * Projection applies to value changes as well. Let's change the
name:
562 * user->name =
"Vasil";
564 * EXPECT_EQ(user->name,
"Vasil");
565 * EXPECT_EQ(label->text(),
"VASIL");
568 * 
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.
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.
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.
581 * _new<ATextField>() && user->name
585 * This gives the following result:
586 * 
588 * Let
's change the name programmatically:
590 * user->name = "Vasil";
594 * ATextField will respond:
595 * 
597 * If the user changes the value from UI, these changes will reflect on `user->model` as well:
598 * 
600 * EXPECT_EQ(user->name, "Changed from UI");
604 * This way we've set up bidirectional projection via `&&` which makes `user->name` aware of UI
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`.
611 * Let
's repeat the @ref "UIDataBindingTest_Bidirectional_projection" sample in declarative way:
613 * _new<ADropdownList>(gendersStr) && user->gender.biProjected(GENDER_INDEX_PROJECTION) > &ADropdownList::selectionId
616 * 
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`.
622 * - If we try to change `user->gender` programmatically, ADropdownList will respond:
624 * user->gender = Gender::FEMALE;
625 * EXPECT_EQ(dropdownList->getSelectedId(), 1); // second option
628 * 
630 * - If the user changes the value of ADropdownList, it reflects on the model as well:
632 * EXPECT_EQ(user->gender, Gender::OTHER);
635 * 
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