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.
Add dependency into project Cargo.toml
file:
toml
[dependencies]
raui = "0.1"
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.
```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 call
Server::process()` on invalid tree. Tree is invalid when one of it's nodes has changed internal state.
```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).
```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.
```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 with
Server::set_focused()` function.
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);
} ```
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);
});
} ```
```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).
TODO
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 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).