raui 0.1.1

Renderer Agnostic User Interface system
Documentation

Renderer Agnostic User Interface system

Build Status Crates.io

This module gives you a way to make UI logic completely separated from rendering (still you have to implement rendering UI tree got from this module, or use some of already existing renderers - they will be added to list here when some renderer will appear). Also: this project is highly inspired by React made by facebook team.

Contents

  1. Installation
  2. Example of usage
  3. Usage
    1. Server
      1. Render event
      2. Signal event
      3. Client rect
      4. Focused node
      5. Component nodes
      6. Performing actions on components
      7. Processing UI tree
      8. Signaling actions
      9. Triggering events
    2. Components

Installation

Add dependency into project Cargo.toml file:

[dependencies]
raui = "0.1"

Example of usage

Here you can see RAUI plugin example ready to use with host application: https://github.com/Simteract/raui-plugin-example

Usage

Server

NOTE: Event callbacks are extern "C" functions by design because RAUI basically serves as system that can connect application logic with host application renderer, still staying separated library to keep it independent from host.

Render event

extern fn on_render(buffer: *const u8, size: u32) {
    // ...
}

fn main() {
    let mut server = Server::new();

    server.on_render_event = Some(on_render);
}

This callback called when RAUI re-renders UI tree. Tt happens when we call Server::process() on invalid tree. Tree is invalid when one of it's nodes has changed internal state.

Signal event

extern fn on_signal(id: *const c_char) {
    // ...
}

fn main() {
    let mut server = Server::new();

    server.on_signal_event = Some(on_signal);
}

This callback is called when some UI tree node triggers some signal. Signals are easiest way to communicate actions between RAUI server and host application. They can be used to tell host that we clicked some button (for now the do not pass any arguments, but later they will).

Client rect

fn main() {
    let mut server = Server::new();

    server.set_client_rect(
        &Rect::from(
            &Vec2::zero(),
            &Vec2::from(1024.0, 768.0)
        )
    );
}

Client rect sets viewport for UI tree. In most cases you just want to match your application window size with no offset.

Focused node

fn main() {
    let mut server = Server::new();

    let node = server.set_root::<Container>();
    server.set_focused(Some(node));
}

By default node is focused when user press mouse button on node with BehaviourFlags::BF_READING_MOUSE behaviour:

component.set_behaviour(behaviour_flags::BF_READING_MOUSE);

but we can set focused node manually with Server::set_focused() function.

Component nodes

Nodes cannot be created as standalone objects and then connected to server or other node - instead, they have to be created and automatically connected using server.

Create root node easy way:

fn main() {
    let mut server = Server::new();

    server.set_root::<Container>();
}

Create root node with action (usually this is the way to setup node on it's creation phase and create it's childs):

fn main() {
    let mut server = Server::new();

    server.set_root_with_action::<Container, _>(&mut |server, node, component| {
        component.set_area(
            &Rect::from(
                &Vec2::zero(),
                &Vec2::zero()
            )
        );
        component.set_coords(components::container::CS_PARENT);
        component.set_color(&Color::red());
    });
}

Create child node easy way:

fn main() {
    let mut server = Server::new();

    server.set_root_with_action::<Container, _>(&mut |server, node, component| {
        server.add_child::<Image>(node);
    });
}

Create child node with action:

fn main() {
    let mut server = Server::new();

    server.set_root_with_action::<Container, _>(&mut |server, node, component| {
        server.add_child_with_action::<Image, _>(node, &mut |server, node, component| {
            component.set_area(
                &Rect::from(
                    &Vec2::from(100.0, 50.0),
                    &Vec2::from(300.0, 150.0)
                )
            );
            component.set_coords(components::container::CS_LOCAL);
            component.set_image_source("logo".to_string());
            component.set_behaviour(behaviour_flags::BF_READING_MOUSE);
        });
    });
}

If you want to remove node at some point you can do it by:

fn main() {
    let mut server = Server::new();

    let node = server.set_root::<Container>();
    // ...
    server.remove_component(node);
}

Performing actions on components

At some point you may want to modify your component (it's state). You can do it with Server::perform_action functions family.

Performing action on node:

fn main() {
    let mut server = Server::new();

    let node = server.set_root::<Container>();
    // ...
    server.perform_action::<_>(node, &mut |component| {
        component.set_behaviour(behaviour_flags::BF_READING_MOUSE);
    });
}

Performing action on node children only:

fn main() {
    let mut server = Server::new();

    let mut subnode = ComponentNode::default();
    server.set_root_with_action::<Container, _>(&mut |server, node, component| {
        subnode = server.add_child::<Image>(node);
    });
    // ...
    server.perform_action_on_children::<_>(subnode, &mut |component| {
        component.set_behaviour(behaviour_flags::BF_READING_MOUSE);
    });
}

Performing action on node and it's children:

fn main() {
    let mut server = Server::new();

    let mut subnode = ComponentNode::default();
    server.set_root_with_action::<Container, _>(&mut |server, node, component| {
        subnode = server.add_child::<Image>(node);
    });
    // ...
    server.perform_action_on_children_too::<_>(subnode, &mut |component| {
        component.set_behaviour(behaviour_flags::BF_READING_MOUSE);
    });
}

Processing UI tree

fn main() {
    let mut server = Server::new();

    server.set_root_with_action::<Container, _>(&mut |server, node, component| {
        server.add_child::<Image>(node);
    });

    server.process(false);
}

This function processes whole UI tree and triggers render event if tree is invalid (has changed state).

Signaling actions

TODO

Triggering events

With this functions, host application can communicate changes in UI environment (like mouse and keyboard interactions).

Triggering mouse events:

fn main() {
    let mut server = Server::new();

    server.set_root_with_action::<Container, _>(&mut |server, node, component| {
        server.add_child_with_action::<Image, _>(node, &mut |server, node, component| {
            component.set_area(
                &Rect::from(
                    &Vec2::from(100.0, 50.0),
                    &Vec2::from(300.0, 150.0)
                )
            );
            component.set_coords(components::container::CS_LOCAL);
            component.set_image_source("logo".to_string());
            component.set_behaviour(behaviour_flags::BF_READING_MOUSE);
            component.base.on_click_event = Some(|| println!("Image clicked!"));
        });
    });

    server.trigger_mouse_down(&Vec2::from(200.0, 100.0));
    server.trigger_mouse_move(&Vec2::from(300.0, 100.0));
    server.trigger_mouse_up(&Vec2::from(300.0, 100.0));

    server.trigger_mouse_click(&Vec2::from(200.0, 100.0));
}

Triggering keyboard events:

fn main() {
    let mut server = Server::new();

    server.set_root_with_action::<Container, _>(&mut |server, node, component| {
        server.add_child_with_action::<Image, _>(node, &mut |server, node, component| {
            component.set_area(
                &Rect::from(
                    &Vec2::from(100.0, 50.0),
                    &Vec2::from(300.0, 150.0)
                )
            );
            component.set_coords(components::container::CS_LOCAL);
            component.set_image_source("logo".to_string());
            component.set_behaviour(behaviour_flags::BF_READING_MOUSE);
            component.base.on_key_pressed_event = Some(
                |code, _modifiers| println!("Pressed key on text: {}", code)
            );
        });
    });

    server.trigger_key_pressed(32, &key_modifiers::KM_NONE);
    server.trigger_key_released(32, &key_modifiers::KM_NONE);

    server.trigger_key_tap(32, &key_modifiers::KM_NONE);
}

Components

Components are logic of node. Each component should do one and only one thing, like: be a container that layout it's children; draw image; draw text. Construct UI tree using smallest components you can use and try to avoid big "inheritance"/composition chains of your custom components.

There are basic components provided by module:

  • Container that can layout itself and it's children relative to some coordinate system;
  • Image that can display image;
  • Text that can display text.

If you want to make your custom component, you have to create struct that implement Component and all it's functions (yeah, thanks to rust design, more your trait "mass" is, more you have to produce boilerplate code to implement it, but for now it's unavoidable pain in the a**). If you want your custom component to "inherit" Container functionality, just composite it (you can see example of it here).