Hint

Refer to QMK’s documentation for details on how to use community modules

ui

Composable design of GUIs over QP, based on a hierarchy of nodes.

This system was designed for flexibility, because computing each element’s position and size by hand was tedious and prone to errors. It also wasn’t dynamic, in the sense that if some element of the screen wasn’t being drawn (eg: feature disabled), sizes wouldn’t adapt and leave a gap.

As a convenience, a handful of builtin integrations are provided, eg: uptime, QMK’s version, current layer, …

Check them out under modules/elpekenin/ui/elpekenin/ui folder. You can #include "elpekenin/ui/<some_file>.h" and use it on your keyboard.

On a similar fashion, some of my modules also implement this kind of integration when the UI module is enabled.

struct ui_time_t
[source]

Time unit.

UI_MILLISECONDS(x)
[source]

Create a ui_time_t of x milliseconds.

UI_SECONDS(x)
[source]

Create a ui_time_t of x seconds.

UI_MINUTES(x)
[source]

Create a ui_time_t of x minutes.

UI_HOURS(x)
[source]

Create a ui_time_t of x hours.

UI_DAYS(x)
[source]

Create a ui_time_t of x days.

UI_STOP
[source]

Sentinel value of ui_time_t that represents that a node stops rendering.

bool ui_time_lte(ui_time_t lhs, ui_time_t rhs)
[source]

Compare two ui_time_t, checking if lhs is less than, or equal, to rhs.

ui_time_t ui_time_add(ui_time_t lhs, ui_time_t rhs)
[source]

Perform the addition of two ui_time_t.

ui_time_t ui_time_now(void)
[source]

Current time, as a ui_time_t


As mentioned, this module builds upon ui_node_t, which must be created.

Use the following macros to create them:

UI_CHILDREN(x)
[source]
A node can have children, this is represented by:
  • .children = UI_CHILDREN(nodes): Where nodes is an array of ui_node_t’s

To compute the size of each child, parents must specify how their size will be shared between all children.
  • .split_direction = UI_SPLIT_DIR_{LEFT_RIGHT,RIGHT_LEFT}: All children are as tall as parent, and size splits horizontally

  • .split_direction = UI_SPLIT_DIR_{TOP_BOTTOM,BOTTOM_TOP}: All children are as wide as parent, and size splits vertically

Every node must define how big they want to be by setting .node_size to the following macros:

UI_ABSOLUTE(x)
[source]

x pixels in size

UI_RELATIVE(x)
[source]

x % of parent’s size

UI_FONT(x)
[source]

x times the font’s height. Can only be used within vertical split node.

Warning

Executes qp_load_font_mem(*(void**)node->args) to compute size. That is, the node’s args must point to a structure whose first element is a font’s array.

UI_IMAGE(x)
[source]

x times a image’s width/height (depending on parent’s split direction).

Warning

Executes``qp_load_image_mem((void*)node->args)`` to compute size. That is, the node’s args must point to a structure whose first element is an image’s array.

UI_REMAINING()
[source]

Claim the parent’s remaining (not used by siblings) size.

bool ui_init(ui_node_t *root, ui_coord_t width, ui_coord_t height)
[source]

Once you’ve declared a node tree, use this function to compute all nodes’ size/position.

If the input can’t be resolved (eg children don’t fit into parent), function will flag the node as invalid, and return false.

Hint

If a node must run some validation (eg: its computed height >= font used), it can provide an .init function. Returning false from it means that requirements weren’t met, causing tree’s resolution to fail.

bool ui_render(ui_node_t *root, painter_device_t display)
[source]

You shall use this function to render all nodes.

Each node describes how it’s rendered by providing a .render function.

If it needs to track some state, it may use the .args field to store a pointer into whichever structure.

Return value is the time before calling it again. Return UI_STOP as a flag for “do not repeat”

You want run this function periodically (ie: from housekeeping_task_user).

Warning

There isn’t any kind of limitation to the area in which each node can draw. This means that nodes are expected to respect the boundaries provided.

For example

if (!ui_text_fits(font, text)) {
    qp_close_font(font);
    return (ui_time_t)UI_SECONDS(1);
}