Skip to content

Hello world

Стартовое приложение

В качестве основы для любого проекта использующего WUI предлагается минимальное приложение, которое, впрочем сразу сделано под возможность его расширения до крупного проекта.

Данное приложение находится в examples/hello_world и включает полное наличие необходимых ресурсных файлов. На Windows приложение собирается в монолитный exe, на Linux/Mac хранит ресурсы в папке ”res/” рядом с исполняемым файлом. Для реальных приложений лучше указать пути “~/.app_name/res” или, если приложение ставится из под root, что-то вроде “/opt/app_name/res” .

Показано использование theme, locale и config, в приложении, имеющем две цветовые схемы (темная и светлая), два языка и хранящем свою конфигурацию в реестре на Windows и в ini файле на Linux.

main.cpp

#ifdef _WIN32
int APIENTRY wWinMain(_In_ HINSTANCE,
    _In_opt_ HINSTANCE,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
#elif __linux__
int main(int argc, char *argv[])
#endif
{
    wui::framework::init();

    auto ok = wui::config::create_config("hello_world.ini", "Software\\wui\\hello_world");
    if (!ok)
    {
        std::cerr << wui::config::get_error().str() << std::endl;
        return -1;
    }

    wui::error err;

    wui::set_app_locales({
        { wui::locale_type::eng, "English", "res/en_locale.json", TXT_LOCALE_EN },
        { wui::locale_type::rus, "Русский", "res/ru_locale.json", TXT_LOCALE_RU },
    });

    auto current_locale = static_cast<wui::locale_type>(wui::config::get_int("User", "Locale", 
        static_cast<int32_t>(wui::get_default_system_locale())));
    wui::set_current_app_locale(current_locale);
    wui::set_locale_from_type(current_locale, err);
    if (!err.is_ok())
    {
        std::cerr << err.str() << std::endl;
        return -1;
    }

    wui::set_app_themes({
        { "dark", "res/dark.json", TXT_DARK_THEME },
        { "light", "res/light.json", TXT_LIGHT_THEME }
    });

    auto current_theme = wui::config::get_string("User", "Theme", "dark");
    wui::set_current_app_theme(current_theme);
    wui::set_default_theme_from_name(current_theme, err);
    if (!err.is_ok())
    {
        std::cerr << err.str() << std::endl;
        return -1;
    }

    MainFrame mainFrame;
    mainFrame.Run();

    wui::framework::run();
    return 0;
}

Демонстрационное приложение показывает логотип, выводит надпись и предоставляет поле ввода. При нажатии на кнопку, происходит вывод окна сообщения и приложение закрывается. Также показано отслеживание закрытия окна пользователем с выводом сообщения подтверждения.

WUI демонстрация

На следующем скриншоте, тема изменена на светлую, язык на английский и нажата кнопка “Приятно познакомиться”

WUI демонстрация 1

MainFrame.h

class MainFrame
{
public:
    MainFrame();

    void Run();

private:

    void ReceiveEvents(const wui::event &ev);

    void UpdateControlsPosition();

    void OnOK();

    static const int32_t WND_WIDTH = 400, WND_HEIGHT = 400;

    std::shared_ptr<wui::window> window = std::make_shared<wui::window>();

    std::shared_ptr<wui::image> logoImage = std::make_shared<wui::image>(IMG_LOGO);

    std::shared_ptr<wui::text> whatsYourNameText = std::make_shared<wui::text>(
        wui::locale("main_frame", "whats_your_name_text"),
        wui::text_alignment::center,
        "h1_text");

    std::shared_ptr<wui::input> userNameInput = std::make_shared<wui::input>(wui::config::get_string("User", "Name", ""));

    std::shared_ptr<wui::button> okButton = std::make_shared<wui::button>(
        wui::locale("main_frame", "ok_button"),
        std::bind(&MainFrame::OnOK, this));

    std::shared_ptr<wui::message> messageBox = std::make_shared<wui::message>(window);

    bool user_approve_close = false;
};

MainFrame.cpp

MainFrame::MainFrame()
{
    window->subscribe(std::bind(&MainFrame::ReceiveEvents,
        this,
        std::placeholders::_1),
        static_cast<wui::event_type>(static_cast<int32_t>(wui::event_type::internal) |
            static_cast<int32_t>(wui::event_type::system) |
            static_cast<int32_t>(wui::event_type::keyboard)));

    window->add_control(logoImage,         { 0 });
    window->add_control(whatsYourNameText, { 0 });
    window->add_control(userNameInput,     { 0 });
    window->add_control(okButton,          { 0 });

    window->set_default_push_control(okButton);

    window->set_min_size(WND_WIDTH - 1, WND_HEIGHT - 1);
}

void MainFrame::Run()
{
    UpdateControlsPosition();

    window->set_control_callback([&](wui::window_control control, std::string &tooltip_text, bool &continue_) {
        switch (control)
        {
            case wui::window_control::theme:
            {
                wui::error err;

                auto nextTheme = wui::get_next_app_theme();
                wui::set_default_theme_from_name(nextTheme, err);
                if (!err.is_ok())
                {
                    std::cerr << err.str() << std::endl;
                    return;
                }

                wui::config::set_string("User", "Theme", nextTheme);

                window->update_theme();
            }
            break;
            case wui::window_control::lang:
            {
                auto nextLocale = wui::get_next_app_locale();
                wui::set_locale_from_type(nextLocale, err);
                if (!err.is_ok())
                {
                    std::cerr << err.str() << std::endl;
                    return;
                }

                wui::config::set_int("User", "Locale", static_cast<int32_t>(nextLocale));

                tooltip_text = wui::locale("window", "switch_lang");

                window->set_caption(wui::locale("main_frame", "caption"));
                whatsYourNameText->set_text(wui::locale("main_frame", "whats_your_name_text"));
                okButton->set_caption(wui::locale("main_frame", "ok_button"));
            }
            break;
            case wui::window_control::close:
                if (!user_approve_close)
                {
                    continue_ = false;
                    messageBox->show(wui::locale("main_frame", "confirm_close_text"),
                        wui::locale("main_frame", "cross_message_caption"), wui::message_icon::information, wui::message_button::yes_no,
                        [this, &continue_](wui::message_result r) {
                            if (r == wui::message_result::yes)
                            {
                                user_approve_close = true;
                                wui::framework::stop();
                            }
                        });
                }
            break;
        }
    });

    auto width = wui::config::get_int("MainFrame", "Width", WND_WIDTH);
    auto height = wui::config::get_int("MainFrame", "Height", WND_HEIGHT);

    window->init(wui::locale("main_frame", "caption"), { -1, -1, width, height },
        static_cast<wui::window_style>(static_cast<uint32_t>(wui::window_style::frame) |
        static_cast<uint32_t>(wui::window_style::switch_theme_button) |
        static_cast<uint32_t>(wui::window_style::switch_lang_button) |
        static_cast<uint32_t>(wui::window_style::border_all)), [this]() {
            wui::framework::stop();
    });
}

void MainFrame::ReceiveEvents(const wui::event &ev)
{
    if (ev.type == wui::event_type::internal)
    {
        switch (ev.internal_event_.type)
        {
            case wui::internal_event_type::size_changed:        
                if (window->state() == wui::window_state::normal &&
                    ev.internal_event_.x > 0 && ev.internal_event_.y > 0)
                {
                    wui::config::set_int("MainFrame", "Width", ev.internal_event_.x);
                    wui::config::set_int("MainFrame", "Height", ev.internal_event_.y);
                }
                UpdateControlsPosition();
            break;
            case wui::internal_event_type::window_expanded:
            case wui::internal_event_type::window_normalized:
                UpdateControlsPosition();
            break;
        }
    }
}

void MainFrame::UpdateControlsPosition()
{
    const auto width = window->position().width(), height = window->position().height();

    const int32_t top = 40, element_height = 40, space = 30;

    wui::rect pos = { space, top, width - space, top + element_height };
    whatsYourNameText->set_position(pos);
    wui::line_up_top_bottom(pos, element_height, space);
    userNameInput->set_position(pos);
    wui::line_up_top_bottom(pos, element_height * 2, space);

    int32_t center = width / 2;

    pos.left = center - element_height, pos.right = center + element_height;

    logoImage->set_position(pos);

    okButton->set_position({center - 90,
        height - element_height - space,
        center + 90,
        height - space
    });
}

void MainFrame::OnOK()
{
    wui::config::set_string("User", "Name", userNameInput->text());

    messageBox->show(wui::locale("main_frame", "hello_text") + userNameInput->text(),
        wui::locale("main_frame", "ok_message_caption"), wui::message_icon::information, 
        wui::message_button::ok, [this](wui::message_result) {
            user_approve_close = true; window->destroy();
        });
}

Окно и контролы создаются в конструкторе MainFrame. Там же осуществляется подписка приложения на события и добавляются на окно контролы. Коллбеки контролов для краткости отрабатываются при помощи лямбд.

Метод Run() запускает окно и содержит лямбду, обрабатывающую коллбеки от контролов окна (кнопки смена языка и темы). ReceiveEvents() получает события от окна и используется для реагирования на ресайз окна вызывая UpdateControlsPosition(). который и пересчитывает новые координаты контролов.

Resource.h

#pragma once

#ifdef _WIN32

#define IDI_MAIN_ICON           107
#define IMG_LOGO                109

#define IMG_ACCOUNT             110
#define IMG_MENU                111

#define TXT_DARK_THEME          200
#define TXT_LIGHT_THEME         201

#define TXT_LOCALE_EN           210
#define TXT_LOCALE_RU           211

#else // _WIN32

static int TXT_LOCALE_EN = 0, TXT_LOCALE_RU = 0, TXT_DARK_THEME = 0, TXT_LIGHT_THEME = 0;

static constexpr const char* IMG_LOGO               = "logo.png";
static constexpr const char* IMG_ACCOUNT            = "account.png";
static constexpr const char* IMG_MENU               = "menu.png";

#endif

hw.rc

#include "Resource.h"

#define APSTUDIO_READONLY_SYMBOLS
#define APSTUDIO_HIDDEN_SYMBOLS
#include "windows.h"
#undef APSTUDIO_HIDDEN_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

// Icon
IDI_APPLICATION         ICON       "res\\hw.ico"

// Images
IMG_LOGO IMAGES_DARK               "res\\images\\dark\\logo.png"
IMG_LOGO IMAGES_LIGHT              "res\\images\\light\\logo.png"
IMG_ACCOUNT IMAGES_DARK            "res\\images\\dark\\account.png"
IMG_ACCOUNT IMAGES_LIGHT           "res\\images\\light\\account.png"
IMG_MENU IMAGES_DARK               "res\\images\\dark\\menu.png"
IMG_MENU IMAGES_LIGHT              "res\\images\\light\\menu.png"

// Theme / locale jsons
TXT_DARK_THEME JSONS               "res\\dark.json"
TXT_LIGHT_THEME JSONS              "res\\light.json"

TXT_LOCALE_EN JSONS                "res\\en_locale.json"
TXT_LOCALE_RU JSONS                "res\\ru_locale.json"

/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED