Skip to content

A declarative and element-based library for creating GUIs on homebrew'd consoles

License

Notifications You must be signed in to change notification settings

fortheusers/chesto

Repository files navigation

Chesto GPLv3 License

logo

Chesto is a declarative and element-based library for creating user interfaces using SDL2. It borrows some design, syntax, and lifecycle philosophies from React and libgui.

Powering the UI for hb-appstore and vgedit, it supports touch screen and gamepad controls, and currently targets Wii U, Switch, and PC.

It is named after the Chesto Berry, as it seems to prevent sleep while working on it!

Eating it makes you sleepless. It prevents the sleep status condition and other sleep-related status conditions.

Elements

All UI objects in Chesto extend the base Element class. This class provides some lifecycle functions, as well as providing the ability to process input, and add relative children elements.

Lifecycle

The RootDisplay class (or a subclass of it) itself extends the base Element. After calling the constructor, you should build and add the desired subclass'd Elements of yours for the first view.

The order of operations is as follows:

  • RootDisplay's constructor will initialize SDL2 and other required libraries (networking, etc)
  • You set up the initial Elements + Pages as children of RootDisplay (via subclassing)
  • The app will run its main loop until exited (forever):
    • InputEvents will be collected (generalizes touch+mouse+keyboard+gamepad into this class)
    • For all children elements of RootDisplay:
      • bool process(InputEvents* event) method is invoked, giving that child an opportunity to respond to any new input.
        • The children, if they have their own children, will recursively have process invoked on them as well.
        • Should return true if the Element is taking action based on some of the InputEvents.
      • void render (Element* parent) method is invoked, only if at least one Element returned true during process.
        • Recursively render is called on these children's children
        • Can optionally be given a parent Element to use to relatively position the child.
    • The app waits (SDL_Delay) for the next frame (up to 16ms, to approach 60fps), then returns back to the start of the main loop.

Due to this method of processing, and then rendering only if at least one Element in the hierarchy responded to InputEvents, the app should use very little CPU if nothing on the screen is being actively changed/animated.

On top of being able to subclass Element to create groups of other custom Elements laid out how you want them, Chesto also includes some stock elements that have convenient behavior, to be detailed below.

Base Functionality

The base Element class provides super process and render methods that go through the children Elements and take care of propogating their invocations to the children's children. If touchable is set on the Element, a few touch events (onTouchDown, onTouchDrag, and onTouchUp) will automatically be handled.

If a touch event is successfully received, the Element will be highlighted and the bound action will be invoked, which must be set ahead of time by the subclassing Element. For an example of how this looks, see the Button section.

removeAll() can also be called on an Element to completely remove its children, for instance to replace lists of elements, or clean up old elements between page changes.

Drawing Images

The ImageElement class can be used to display images either from disk or the network. For example, a romfs image can be instantiated and positioned like this at coordinates (5, 10) relative to the current Element (this):

ImageElement* icon = new ImageElement(RAMFS "res/icon.png");
icon->position(this->x + 5, this->y + 10);
icon->resize(30, 30);
super::append(icon);

TODO: Network image example (with fallback disk path)

Drawing Text

The TextElement class is used to display sentences or paragraphs of text, with or without wrappinig. Text can be instantiated at (40, 20) relative to the current Element:

CST_Color gray = { 80, 80, 80, 0xff };
int fontSize = 12;
TextElement* status = new TextElement("All good here!", fontSize, &gray);
status->position(this->x + 40, this->y + 20);
super::append(status);

Scrollable Views

The ListElement class should be subclassed and used to contain other groups that automatically need to be presented in a format so that they can trail off the page and be scrolled through either via touch events or gamepad buttons.

You can provide your own cursor logic here as well by overriding process in the LisitElement subclass, if you want the gamepad controls to do more than just scroll the page. It should play nicely with sub-elements that are marked as touchable.

For examples of how this class can be used, see hb-appstore's gui/AppDetails.cpp and gui/AppList.cpp, who both take different approaches to how the user interactes with the view.

Binding Buttons

The Button class automatically subclasses Element and bundles together a TextElement, as well as some touch/gamepad input handling. Like other Elements, the action callback can be set to a member function of another class or function (see here for more info), which will be invoked either when the InputEvent gamepad is triggered or the button is touched.

To create a button, give it the text, the button which corresponds to it, whether it's light or dark themed, and a font size. It can optionally also take a width as the last element, otherwise it will automatically fit the width to the inner text.

Button* start = new Button("Begin!", START_BUTTON, true, 20);
start->position(70, 50);
start->action = std::bind(&TitlePage::launch, this);
super::append(start);

Displaying Progress

The ProgressBar element takes a percent float between 0.0 and 1.0, as well as an overall width for how long the progress bar should be at 100%.

Can be created as follows. If dimBg is set, then the entire screen under this progress bar will be covered with a transparent gray sheet.

ProgressBar pbar = new ProgressBar();
pbar->width = 740;
pbar->position(1280 / 2 - pbar->width / 2, 720 / 2);
pbar->color = 0xff0000ff;
pbar->dimBg = true;
super::append(pbar);

TODO: Show how to provide a callback to networking utilities to update the progress bar rather than always reacting to an input.

Networking Helpers

TODO: Provide some helper functions or examples talking to the network via CURL and interacting with the UI.

License

This software is licensed under the GPLv3.

Example

For an example of what an app that integrates Chesto looks like, see ChestoTesto by CompuCat.

Dependencies

Chesto makes use of resinfs to display images and other assets from memory rather than files. Any files in the top-level resin folder will be bundled using this dependency.

About

A declarative and element-based library for creating GUIs on homebrew'd consoles

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published