AUI Framework  develop
Cross-platform base for C++ UI apps
Loading...
Searching...
No Matches
7GUIs CRUD

Note
This page describes an example listed in 7GUIs.
Create/Read/Update/Delete example.

Challenges: separating the domain and presentation logic, managing mutation, building a non-trivial layout.

The task is to build a frame containing the following elements: a textfield Tprefix, a pair of textfields Tname and Tsurname, a listbox L, buttons BC, BU and BD and the three labels as seen in the screenshot. L presents a view of the data in the database that consists of a list of names. At most one entry can be selected in L at a time. By entering a string into Tprefix the user can filter the names whose surname start with the entered prefix—this should happen immediately without having to submit the prefix with enter. Clicking BC will append the resulting name from concatenating the strings in Tname and Tsurname to L. BU and BD are enabled iff an entry in L is selected. In contrast to BC, BU will not append the resulting name but instead replace the selected entry with the new name. BD will remove the selected entry. The layout is to be done like suggested in the screenshot. In particular, L must occupy all the remaining space.

CRUD (Create, Read, Update and Delete) represents a typical graphical business application. The primary challenge is the separation of domain and presentation logic in the source code that is more or less forced on the implementer due to the ability to filter the view by a prefix. Traditionally, some form of MVC pattern is used to achieve the separation of domain and presentation logic. Also, the approach to managing the mutation of the list of names is tested. A good solution will have a good separation between the domain and presentation logic without much overhead (e.g. in the form of toolkit specific concepts or language/paradigm concepts), a mutation management that is fast but not error-prone and a natural representation of the layout (layout builders are allowed, of course, but would increase the overhead).

/*
* AUI Framework - Declarative UI toolkit for modern C++20
* Copyright (C) 2020-2024 Alex2772 and Contributors
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <range/v3/all.hpp>
#include <AUI/Platform/Entry.h>
#include <AUI/Platform/AWindow.h>
#include <AUI/Util/UIBuildingHelpers.h>
#include <AUI/View/AForEachUI.h>
#include <AUI/View/AButton.h>
#include "AUI/View/ATextField.h"
#include "AUI/View/AListView.h"
using namespace declarative;
struct User {
    AProperty<AString> name;
    AProperty<AString> surname;
    APropertyPrecomputed<AString> displayName = [this] { return "{}, {}"_format(name, surname); };
};
class CRUDWindow: public AWindow {
public:
    CRUDWindow(): AWindow("AUI - 7GUIs - CRUD", 300_dp, 200_dp) {
        setExtraStylesheet(AStylesheet {
          {
            c("selected"),
            BackgroundSolid { AColor::BLUE.transparentize(0.5f) },
          }
        });
        auto FILTER_VIEW = ranges::views::filter([this](const _<User>& user) {
            return user->displayName->startsWith(mFilterPrefix);
        });
        setContents(Vertical {
          Horizontal::Expanding {
            Vertical::Expanding {
              Horizontal {
                Label { "Filter prefix:" },
                _new<ATextField>() with_style { Expanding(1, 0) } && mFilterPrefix,
              },
              AScrollArea::Builder().withExpanding().withContents(
                  AUI_DECLARATIVE_FOR(i, *mUsers | FILTER_VIEW, AVerticalLayout) {
                    auto view = _new<ALabel>();
                    view & i->displayName;
                    connect(mSelectedUser, view, [this, &view = *view, i] {
                        view.setAssName("selected", mSelectedUser == i);
                    });
                    connect(view->clicked, [this, i] {
                        mSelectedUser = i;
                        mEditedUser.name = i->name;
                        mEditedUser.surname = i->surname;
                    });
                    return view;
                  }
              ).build() with_style { BackgroundSolid { AColor::WHITE } },
            },
            Centered::Expanding {
              _form({
                { "Name:", _new<ATextField>() && mEditedUser.name },
                { "Surname:", _new<ATextField>() && mEditedUser.surname },
              }),
            },
          },
          Horizontal {
            Button { "Create" }.connect(&AView::clicked, me::createClicked) & mCreateEnabled > &AView::setEnabled,
            Button { "Update" }.connect(&AView::clicked, me::updateClicked) & mUpdateEnabled > &AView::setEnabled,
            Button { "Delete" }.connect(&AView::clicked, me::deleteClicked) & mDeleteEnabled > &AView::setEnabled,
          },
        });
    }
private:
    AProperty<AVector<_<User>>> mUsers;
    User mEditedUser;
    AProperty<_<User>> mSelectedUser;
    AProperty<AString> mFilterPrefix;
    APropertyPrecomputed<bool> mCreateEnabled = [this] { return !(mEditedUser.surname->empty() || mEditedUser.name->empty()); };
    APropertyPrecomputed<bool> mDeleteEnabled = [this] { return mSelectedUser != nullptr; };
    APropertyPrecomputed<bool> mUpdateEnabled = [this] { return mCreateEnabled && mDeleteEnabled; };
    void createClicked() {
        mUsers.writeScope() << aui::ptr::manage(new User {
          .name = std::exchange(mEditedUser.name, {}), .surname = std::exchange(mEditedUser.surname, {}) });
    }
    void updateClicked() {
        (*mSelectedUser)->name = std::exchange(mEditedUser.name, {});
        (*mSelectedUser)->surname = std::exchange(mEditedUser.surname, {});
    }
    void deleteClicked() {
        mUsers.writeScope()->removeFirst(*mSelectedUser);
        mSelectedUser = nullptr;
    }
};
    _new<CRUDWindow>()->show();
    return 0;
}
AColor transparentize(float alpha) const noexcept
Decreases the alpha channel by the given value.
Definition AColor.h:96
void setContents(const _< AViewContainer > &container)
Moves (like via std::move) all children and layout of the specified container to this container.
void setExtraStylesheet(_< AStylesheet > extraStylesheet)
Definition AView.h:238
emits clicked
Left mouse button clicked.
Definition AView.h:933
Represents a window in the underlying windowing system.
Definition AWindow.h:45
class_of c
Selects views that are of the specified classes.
Definition class_of.h:84
static decltype(auto) connect(const Signal &signal, Object *object, Function &&function)
Connects signal to the slot of the specified object.
Definition AObject.h:86
#define AUI_DECLARATIVE_FOR(value, model, layout)
ranged-for-loop style wrapped for AForEachUI.
Definition AForEachUI.h:402
#define with_style
Allows to define a style to the view right in place.
Definition kAUI.h:287
#define AUI_ENTRY
Application entry point.
Definition Entry.h:90
Button
Specifies button(s) to be displayed.
Definition AMessageBox.h:79
Readonly property that holds a value computed by an expression.
Definition APropertyPrecomputed.h:51
Basic easy-to-use property implementation containing T.
Definition AProperty.h:30
aui::PropertyModifier< AProperty > writeScope() noexcept
Definition AProperty.h:130
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

Source Files#