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: toml [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

```rust 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 callServer::process()` on invalid tree. Tree is invalid when one of it's nodes has changed internal state.

Signal event

```rust extern fn onsignal(id: *const cchar) { // ... }

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

```rust 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

```rust 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: rust component.setbehaviour(behaviourflags::BFREADINGMOUSE); `` but we can set focused node manually withServer::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: ```rust 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): ```rust 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: ```rust 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: ```rust 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: ```rust 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: ```rust 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: ```rust 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: ```rust 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

```rust 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: ```rust 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: ```rust 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).