1 unstable release

new 0.1.0 Jan 19, 2025

#442 in GUI

GPL-3.0-only

250KB
4.5K SLoC

Keru is a Graphical User Interface library.

It is in active development and it's not ready to be used. Many features are missing or half-baked.

It offers a declarative API similar to immediate mode GUI libraries, but it is not immediate mode.

See the docs.rs page for more information.

Screenshot of paint example

Code Example

// Define an unique identity for this button
#[node_key] const INCREASE: NodeKey;

// Add nodes to the UI and set their parameters
ui.add(INCREASE)
    .params(BUTTON)
    .color(Color::RED)
    .text("Increase");

// Place the nodes into the tree and define the layout
ui.v_stack().nest(|| {
    if self.show {
        ui.place(INCREASE);
        ui.label(self.count); // This one doesn't need a key.
    }
});

// Run code in response to events
if ui.is_clicked(INCREASE) {
    self.count += 1;
}

lib.rs:

Keru is an experimental Graphical User Interface library.

It is in active development and it's not ready to be used. Many features are missing or half-baked.

Keru offers a declarative API similar to immediate mode GUI libraries, but it is not immediate mode.

Example

// Define an unique identity for a node. You can also create keys dynamically.
#[node_key] const INCREASE: NodeKey;

// Add the node to the UI and set its parameters
self.ui.add(INCREASE)
    .params(BUTTON)
    .color(Color::RED)
    .text("Increase");

// Place nodes into the tree and define the layout
self.ui.v_stack().nest(|| {
    if self.show {
        self.ui.place(INCREASE);
        self.ui.label(self.count);
    }
});

// Run code in response to events
if self.ui.is_clicked(INCREASE) {
    self.count += 1;
}

Using NodeKeys gives more flexibility when organizing the code, but they are not required. See the "no_keys" example to see a similar counter written without NodeKeys.

Window Loop

If you just want to try out some GUI building code, you can use the one-line loop in example_window_loop. The Counter example uses this method.

However, Keru is intended to be used as part of a regular winit/wgpu window loop managed by the library user. This makes it very simple to combine it with any kind of custom rendering (as long as it uses wgpu).

Once you have a window loop, you can create a [Ui] struct and store it in your main program state. To integrate it with the window loop, you only need to do two things:

  • When you receive a winit WindowEvent, pass it to [Ui::window_event()].
  • When you receive a WindowEvent::RedrawRequested, redeclare your GUI, then call [Ui::render()].

You can use the [Ui::needs_rerender()] to decide whether to render the GUI or skip it.

For a full integration example, see the Painter example. Another simpler integration example will be added in the future.

Declaring the GUI

To redeclare your GUI, you have to start a new GUI "tree", rerun all your GUI declaration code, then finish the tree.

#
#
self.ui.begin_tree();
self.ui.text("Hello World");
self.ui.finish_tree();
#

Note that even if you do this every frame, it doesn't mean that the GUI is re-rendering every frame, doing a full relayout on every frame, or anything of that sort. See the "About" page for more information on this point.

To see how the GUI declaration code works, you can check the basic example above, the Counter example, or the paint_ui.rs file in the painter example.

To summarize, for each element in the GUI, you have to perform some of these conceptual steps:

You can do these things by either calling methods directly on the main [Ui] struct, or by calling chained methods on the result of a previous method.

Methods on the [Ui] struct usually take a NodeKey argument to refer to a specific node.

Creating complex GUIs

  • In dynamic GUIs, you can't identify every node with a static NodeKey in the way the examples do it.

    Instead, you can use the [NodeKey::sibling()] function to create keys dynamically at runtime.

  • To create reusable "widgets", you can just wrap the GUI code in a function. However, it's very likely that you'll need to create a subtree for it to make it work correctly.

These building blocks should be enough to create complex GUIs. But only time will tell.

More information

See the "About" page for more information about how Keru works internally, how it compares to other libraries, and more.

Dependencies

~33–67MB
~1M SLoC