Skip to content
This repository has been archived by the owner on Oct 29, 2023. It is now read-only.

Basic usage

Simon edited this page Jan 9, 2021 · 11 revisions

The simplest Fungal UI application possible consists of just 2 lines:

// initializes variables and the console environment
ui.init();

// starts drawing and listening for events
ui.exec();

Adding controls

All controls require at minimum a name, which is used internally to identify and retrieve controls and must be unique for each control object. Most standard controls also require a title which is displayed either above or inside the control body.

Controls are added to the ui either by calling ui::add or by utilizing the synonymous << operator with a pointer to a control object. However, having added a control to the ui does not necessitate that the control will be displayed. To display controls, you have to first call the ui::display function, supplying either a pointer or the name of the control.

Let's add a button to the ui. Buttons require a name, a title and optionally you can supply a callback which we'll explicitly set to NULL and get back to later.

// initializes variables and the console environment
ui.init();

// create a button and add it to the ui
ui << new button("button_name", "Press me", NULL);

// display button
ui.display("button_name");

// starts drawing and listening for events
ui.exec();

This lone button is neither useful nor pretty (much like myself) so let's get it some friends.

Pages

A page is a control which is used to display a number of different controls at once. They are equivalent to your traditional UI framework's vertical layout. They are responsible for formatting, layout, and internal scrolling when controls go out of the bounds of the console. Controls can be added to a page with a similar syntax to that of adding controls to the ui - either with page::add or with the << operator.

In our example, let's instead add a button and a textfield to a page.

// initializes variables and the console environment
ui.init();

// create a page with a button and a textfield
page* new_page = new page("new_page", "Example page");
*new_page << new button("button_name", "Press me", NULL);
*new_page << new textfield("textfield_name", "Type something");
ui << new_page;

// display page
ui.display(new_page);

// starts drawing and listening for events
ui.exec();

Since controls can be added at any point, it's good practise to initialize all controls at once to avoid creating the same control multiple times. As such, it might be a good idea to clearly seperate pages from one another to improve readability. This could be done through use of brackets and indentation. This has no bearing on how the code is compiled and is just for aesthetic purposes.

// create a page with a button and a textfield
page* new_page = new page("new_page", "Example page");
{
    *new_page << new textfield("textfield_name", "Type something");
    *new_page << new button("button_name", "Press me", NULL);
    ui << new_page;
}

Retrieving controls

Controls can be retrieved at any point through the ui::get function which accepts the the name of the control as argument and returns a generic control pointer. These provide a fair amount of functionality relating to the control but should you need to interact with the control more specifically, you first have to cast the control to the desired control subtype. The control class provides a helper function for this purpose, control::as, which accepts a template argument of the expected control type and returns the casted pointer. To avoid having to retrieve the pointer and casting it seperately, the ui class provides yet another helper function, ui::get_as which functions the same as ui::get but also accepts the expected control type and casts it to said type.

Example:

// ui::get
control* some_control = ui.get("some_control_name");

// control::as
button* some_button = some_control->as<button>();

// ui::get_as
checkbox* some_checkbox = ui.get_as<checkbox>("some_checkbox_name");

Input controls

An input control is a type of control which gets some kind of user input. Each input control has primarily two extra functions, input_control::value and input_control::set_value. To retrieve the value of an input control from the ui, one could first retrieve the specific input control pointer, then call the input_control::value function but fortunately the ui class defines a helper function, ui::get_value, which accepts as template argument the expected type of the value and the name of the input control.

Example:

// ui::get_as, input_control::value
checkbox* some_checkbox = ui.get_as<checkbox>("some_checkbox_name");
bool checkbox_value = some_checkbox->value();

// ui::get_value
std::string textfield_value = ui.get_value<std::string>("some_textfield_name");

Callbacks

Most of the program's logic is going to be laid out through various callbacks. Most controls have two callbacks: control::on_enter and control::on_update. The first is called when the control is selected and the enter key is pressed (analogous to on_click in a traditional UI framework) and the second is called when the control is updated, most commonly when the user has interacted with it in some way. These callbacks also pass as argument a pointer to the control which raised the event.

Some controls accept a callback in their constructor, such as the button and buttonbox.

void on_enter_callback(control* sender)
{
    // do something
}

control* some_control = ui.get("some_control_name");
some_control->on_enter = on_enter_callback;

To set a callback to a member function, you have to first bind the function.

Some controls may have different callback arguments or return types, as such it's important to set the callback from the correct type and not from a generic control* to make sure the overriding callback is being set, as that is the one which will be called.

Example

With this information in mind, here's the code for a simple login screen. The code for this example can be found in demo/main.cpp.

void on_login(control*)
{
    // retrieve values from textfields

    std::string username = ui.get_value<std::string>("text_username");
    std::string password = ui.get_value<std::string>("text_password");

    // very secure credentials check
    if (username == "admin" && password == "password1")
        ui.display("second_page");
}

int main()
{
    // initializes variables and the console environment
    ui.init();

    // create and add controls in the login page
    page* login_page = new page("login_page", "Login");
    {
        *login_page << new textfield("text_username", "Username");
        *login_page << new textfield("text_password", "Password", "", true);
        *login_page << new button("button_confirm_login", "Login", on_login);

        ui << login_page;
    }

    // create and add second page
    page* second_page = new page("second_page", "This is a second page");
    {
        *second_page << new multiselect("multiselect", "Multiselect", { "Option 1", "Option 2", "Option 3" });
        *second_page << new multiselect("multiselect2", "Multiselect2", { "Option 1", "Option 2", "Option 3" });

        ui << second_page;
    }

    // set displayed control
    ui.display(login_page);

    // starts drawing and listening for events
    ui.exec();
}

Clone this wiki locally