Component overview
A component in Arkham is a piece of rendering logic that could be reusable, or could be used for organization.
They are simple functions that accept a ViewContext
reference, alongside any injected objects. See Dependency Injection
A simple component might look like this:
fn seperator(ctx: &mut ViewContext) {
let width = ctx.size().width;
let sep_chars = "-".repeat(ctx.size().width);
ctx.insert(sep_chars);
}
Components with parameters
Reusable components will need the ability to pass parameters into them. This is done by returning the component function from within another function.
fn name_field(name: &str) -> impl Fn(&mut ViewContext) {
move |ctx:&mut ViewContext| {
ctx.insert((0,0), "Name:");
ctx.insert((10,0), name);
}
}
Understanding component sizing and positioning
When a component is used it is allocated a specific Rect
that it can render to.
The total dimensions for the component are available in ViewContext::size()
.
Components are also rendered at a specific position (the upper left corner).
Inside a component its coordinates are relative to itself, its upper left corner is (0,0).
Using components
A component can render other components inside them.
In this example we can also see the component positioning and sizing.
The first parameter to ViewContext::component
is a Rect for the component.
This is the position in the parent component it will render its top left corner
to and the size the component is allowed to render to.
The first parameter to Rect::new
is the Pos
for the rect, the coordinates
of its top left corner. From the perspective of the field
component,
the content is inserted at y=0. However, when the component is placed in
the container
component, its upper left coordinate is specified and the
coordinates of the field
component become relative to the specified position
in the container
component.
The second parameter to Rect::new
is the Size
of the Rect
. Its width and height.
fn field(key: &str, value: &str) -> impl Fn(&mut ViewContext) {
move |ctx: &mut ViewContext| {
ctx.insert((0,0), format!("{}:", key));
ctx.insert((10,0), value);
}
}
fn container(ctx: &mut ViewContext) {
ctx.component(Rect::new((0,0), (10,1)), field("Name", "Alice")
ctx.component(Rect::new((0,1), (10,1)), field("Age", "22")
}
The container
component will render to the following:
Name: Alice
Age: 22
Components as closures
When you need something more complex than ViewContext::insert
, but don't want to
build a whole separate component. Components can be defined inline using a closure.
fn container(ctx: &mut ViewContext) {
let size = ctx.size();
ctx.component(
Rect::new((0,0), (size.width, size.height /2))
|ctx: &mut ViewContext| {
ctx.fill_all(Color::Blue);
}
);
}