Gaugen is a tool for composing simple, vector-based UIs with rust and nanovg-rs. This current version is very preliminary.
X-Plane 11 integration example.
On startup, gaugen has to be provided with packages that contain components out of which our UI will be built:
gaugen::SessionBuilder::new()
.register_components(gaugen::basic_components::components())
.register_components(gaugen::geometry_components::components())
.init(|session: &mut Session| {
//here goes the drawing
}
A component contains all the logic required for drawing particular types of UI elements. For example, on the X-Plane 11 demo, all the half-wheel-style indicators are drawn using RotationalIndicator component from the basic_components package.
At the moment the only way to compose UI is trough JSON file, for example:
{
"type": "GroupingBox",
"data": {
"spacing": 0.95,
"title": "Demo group",
"title_size": {
"RelativeToHeight": 0.1
}
},
"children": [
{
"type": "Split",
"data": {
"direction": "Vertical"
},
"children": [
{
"type": "TextField",
"name": "my_textfield",
"data": {
"text": "Hello world!"
}
},
{
"type": "TextField"
},
{
"type": "RotationalIndicator",
"name": "my_indicator"
}
]
}
]
}
will produce:
The components are organised into tree hierarchy. Each is configured either by component's default data (i.e. this rotational indicator being set 50), by data supplied in the json (i.e. GroupingBox) or mix of both, json having of course priority over default data (i.e. "Hello World" TextField).
Layout can be then loaded and drawn:
let mut view = session.new_view("screen.json").unwrap();
loop {
[...]
if !session.draw(&mut view, &frontend::DarkPalette {}, &HashMap::new()) {
break; // handle window being closed, etc.
}
}
Gaugen provides hooks to enable overriding both the default data and the static-layout-data-from-json to allow for dynamic updating of the components.
let mut view = session.new_view("screen.json").unwrap();
let mut counter = 0;
loop {
let mut hooks = gaugen::Hooks::new();
gaugen::add_hook(&mut hooks, "my_textfield", "text", "Bye world!".to_string());
gaugen::add_hook(&mut hooks, "my_textfield", "front_color", "ffff0000".to_string());
gaugen::add_hook(&mut hooks, "my_indicator", "value", 99);
if !session.draw(&mut view, &gaugen::frontend::DarkPalette {}, &hooks) {
break; // handle window being closed, etc.
}
}
Now this will produce:
Each component type must provide three data models:
impl gaugen::Component<GroupingBoxData, GroupingBoxInternalData> for GroupingBox {
[...]
}
In this case, GroupingBox contains all the data shared between all instances of grouping boxes. Those can be, for example, a background texture common to all grouping boxes.
GroupingBoxData on the other hand provides data specific to a particual instance of some grouping box on the screen. This is to provide all of the high-level configuration of the component, such as padding (i.e. 5%), or tittle (i.e. "SYSTEMS"). Because it will be supplied in a dynamic manner, it is required to be serializable. This is refered as public data.
GroupingBoxInternalData takes care of storing mutable, internal, non-serializable state for each instance. For grouping boxes this is used to cache intermediate geometry calculations instead of repeating them on each frame.
Once data model is provided, necessary component's logic must be defined, in particular initialization of a new instance, so gaugen can turn any public data into new component's instance:
impl gaugen::Component<GroupingBoxData, GroupingBoxInternalData> for GroupingBox {
fn init_instance(
&self,
__ctx: &mut frontend::PresentationContext,
data: &GroupingBoxData,
children_sizes: &[gaugen::ControlGeometry],
) -> gaugen::AfterInit<GroupingBoxInternalData> {
[...]
}
}
And of course a drawing action, so gaugen can actually order drawing of particular instances:
impl gaugen::Component<GroupingBoxData, GroupingBoxInternalData> for GroupingBox {
fn draw(
&self,
ctx: &mut frontend::PresentationContext,
zone: gaugen::DrawZone,
children: &mut [Box<dyn FnMut(gaugen::DrawZone) + '_>],
internal_data: &mut GroupingBoxInternalData,
public_data: &GroupingBoxData,
) {
[...]
}
}
basic_components and geometry_components are provided with gaugen and contain all the components used in examples and can be used as examples themselves when creating new components.
- example allowing for exploration of data models of the avalible components
- input handling
- abstract and document geometrical coupling between parents & children
- elimite all hard-coded colors from basic components
- resource management (i.e. fonts)