Karm UI

Karm UI is a library for creating graphical user interfaces for skiftOS.

🛈 Note
This article is a work in progress, and is not yet complete. If you would like to contribute to the documentation, please refer to the contributing guide.

Concepts

Nodes

The basic building block of Karm UI is the node. A node is a graphical element that can be displayed on the screen. There are many types of nodes, such as text nodes, image nodes, and layout nodes.

Node are meant to be composed together to create complex user interfaces.

auto app = Ui::vflow(
    Ui::text("Hello, world!"),
    Ui::image("image.png")
);

There is three families of nodes in Karm UI:

  • Leaf nodes, such as text and image nodes, that contain content to be displayed on the screen.
  • Group nodes, such as flow and grid nodes, that contain other nodes.
  • Proxy nodes, such as decorators, that modify the appearance or behavior of other nodes by wrapping them.

Composability

Nodes are immutable, meaning that they cannot be modified after they are created. Instead, new nodes are created by composing existing nodes together.

auto app = Ui::text("Hello, world!") | Ui::center();

In this example, the center decorator is used to center the text in the host. Note that the original text node is not modified; instead, a new node is created by wrapping the original node with the decorator using the | operator.

Tree structure

Nodes are organized in a tree structure, with each node having zero or more child nodes. The root node of the tree is the top-level node that represents the entire user interface.

auto app = Ui::vflow(
    Ui::text("Hello, world!"),
    Ui::image("image.png")
);

In this example, the vflow node is the root node of the tree, and it contains two child nodes: a text node and an image node.

Types of nodes

Views

Views also known as Leaf nodes, are the most basic type of node. They contain content to be displayed on the screen, such as text or images. They are the content of the user interface.

#include <karm-ui/view.h>

auto app = Ui::text("Hello, world!"); // A text view

auto app = Ui::image("image.png"); // An image view

Layouts

Layouts are nodes that contain other nodes. They are used to arrange nodes in a specific way, such as in a grid or a list. There are two types of layout nodes: flow and grid.

#include <karm-ui/layout.h>

auto app = Ui::vflow(
    Ui::text("Hello, world!"),
    Ui::image("image.png")
); // A vertical flow layout

auto app = Ui::hflow(
    Ui::text("Hello, world!"),
    Ui::image("image.png")
); // A horizontal flow layout

Flow Layouts

A flow layout arranges its child nodes in a single row or column.

  • Ui::vflow(isize gaps, Math::Align align, auto... children) : A vertical flow layout
  • Ui::hflow(isize gaps, Math::Align align, auto... children) : A horizontal flow layout
  • <node> | Ui::grow(float factor) : Expand the node to fill the available space, with a given factor.

Grid Layouts

A grid layout arranges its child nodes in a grid with a fixed number of rows and columns.

  • Child grid(GridStyle style, auto... children); : A grid layout
  • <node> | Ui::cell(Math::Vec2i pos) : Place the node in a specific cell of the grid.

Decorators

Decorators are nodes that modify the appearance or behavior of other nodes. They can be used to add borders, padding, or other visual effects to nodes.

#include <karm-ui/layout.h>

auto app = Ui::text("Hello, world!") | Ui::center(); // Center the text

In this example, the center decorator is used to center the text in the host.

Here is a non exaustive list of decorators

Visibility

  • <node> | Ui::cond(bool visible) : Show or hide a node based on a condition.

Bound and Growth

  • <node> | Ui::bound() : Give a visual bound to the node.
  • <node> | Ui::placed(Math::Recti place) : Place the node at a specific location.
  • <node> | Ui::grow(float factor) : Expand the node to fill the available space, with a given factor.

Alignment

  • <node> | Ui::align(Math::Align) : Align the node in the host.
  • <node> | Ui::center() : Center the node in the host.
  • <node> | Ui::start() : Align the node to the start of the host.
  • <node> | Ui::end() : Align the node to the end of the host.
  • <node> | Ui::fit() : Fit the node to the size of the host.
  • <node> | Ui::cover() : Cover the host with the node.
  • <node> | Ui::vcenter() : Vertically center the node in the host.
  • <node> | Ui::hcenter() : Horizontally center the node in the host.
  • <node> | Ui::vcenterFill() : Vertically center the node in the host and fill the available space.
  • <node> | Ui::hcenterFill() : Horizontally center the node in the host and fill the available space.

Size Management

  • <node> | Ui::sizing(Math::Vec2i min, Math::Vec2i max) : Set the minimum and maximum size of the node.
  • <node> | Ui::minSize(Math::Vec2i min) : Set the minimum size of the node.
  • <node> | Ui::maxSize(Math::Vec2i max) : Set the maximum size of the node.
  • <node> | Ui::pinSize(Math::Vec2i size) : Set the size of the node.
  • <node> | Ui::aspectRatio(f64 ratio) : Set the aspect ratio of the node.

Padding and Margin

  • <node> | Ui::insets(int insets) : Add insets between the node and its parent.

State Management

Uis are stateless by default, but you can use the state node to create stateful UIs.

#include <karm-ui/react.h>

auto app = Ui::state(
    0,
    [](auto state, auto bind) {
        return Ui::vflow(
            Ui::text("Counter: {}", state),
            Ui::button(bind(state + 1) "Increment")
        );
    }
);

In this example, the state node is used to create a stateful UI that displays a counter and a button to increment the counter. The state node takes an initial state value and a function that returns a UI based on the current state value. The bind function is used to create a callback that updates the state when the button is clicked.

Behind the scenes, the state will re-render the UI whenever the state changes and reconcile the differences between the old and new UIs to update the screen.

Going further

This document is only meant to provide a high-level overview of Karm UI. To learn more about the available components and how to use them, you can explore the source code at src/libs/karm-ui