Module intuitive::components
source · [−]Expand description
A collection of basic components.
This module contains two main things:
- A collection of commonly used components
- The
Component
trait
Components
Components are the main building blocks of Intuitive TUIs. Most components
can be built using the component
attribute macro. For more complex components,
such as those that require special handling when drawing, consider implementing
the Component
trait directly.
Recipes
The examples below are a few recipes for commonly constructed components. Also be
sure to refer to the examples directory in the repository. These recipes exclude
the use
statements in order to shorten the code samples.
- Input Box – An input box
- Input Box With Cursor – An input box with a cursor
- Focus – How to focus on different sections
Input Box
An input box with state can easily be created with a functional component:
#[component(Input)]
fn render(title: String) {
let text = use_state(|| String::new());
let on_key = on_key! { [text]
KeyEvent { code: Char(c), .. } => text.mutate(|text| text.push(c)),
KeyEvent { code: Backspace, .. } => text.mutate(|text| text.pop()),
};
render! {
Section(title) {
Text(text: text.get(), on_key)
}
}
}
Input Box With Cursor
Drawing a cursor requires us to implement a custom Element
,
specifically so we can control the drawing of the cursor. Notice that
we use a functional component to return a custom element::Any
, instead
of returning a render!
invocation.
#[component(Input)]
fn render(title: String) {
let text = use_state(|| String::new());
let on_key = on_key! { [text]
KeyEvent { code: Char(c), .. } => text.mutate(|text| text.push(c)),
KeyEvent { code: Backspace, .. } => text.mutate(|text| text.pop()),
};
AnyElement::new(Frozen {
cursor: text.get().len() as u16,
content: render! {
Section(title: title.clone(), on_key) {
Text(text: text.get())
}
},
})
}
struct Frozen {
cursor: u16,
content: AnyElement,
}
impl Element for Frozen {
fn on_key(&self, event: KeyEvent) {
self.content.on_key(event);
}
fn draw(&self, rect: Rect, frame: &mut Frame) {
self.content.draw(rect, frame);
frame.set_cursor(rect.x + self.cursor + 1, rect.y + 1);
}
}
Focus
In order to implement focusing on specific sections, we need to construct the components
to be focused on, specifically the three Input
s manually, when rendering our Root
component.
Notice that we also call Component::render
on those Input
s, because we want to be
able to delegate key events to them, depending on which is focused. Lastly, we use Embed
in order to make use of a rendered component inside of the render!
macro.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Focus {
A,
B,
C,
}
#[component(Input)]
fn render(title: String, focused: bool) {
let text = use_state(|| String::new());
let color = if *focused { Color::Blue } else { Color::Gray };
let on_key = on_key! { [text]
KeyEvent { code: Char(c), .. } => text.mutate(|text| text.push(c)),
KeyEvent { code: Backspace, .. } => text.mutate(|text| text.pop()),
};
render! {
Section(title, border: color) {
Text(text: text.get(), on_key)
}
}
}
#[component(Root)]
fn render() {
let focus = use_state(|| Focus::A);
let input_a = Input::new("A".to_string(), focus.get() == Focus::A).render();
let input_b = Input::new("B".to_string(), focus.get() == Focus::B).render();
let input_c = Input::new("C".to_string(), focus.get() == Focus::C).render();
let on_key = on_key! { [focus, input_a, input_b, input_c]
KeyEvent { code: Tab, .. } => focus.update(|focus| match focus {
Focus::A => Focus::B,
Focus::B => Focus::C,
Focus::C => Focus::A,
}),
event if focus.get() == Focus::A => input_a.on_key(event),
event if focus.get() == Focus::B => input_b.on_key(event),
event if focus.get() == Focus::C => input_c.on_key(event),
KeyEvent { code: Esc, .. } => event::quit(),
};
render! {
VStack(on_key) {
Embed(content: input_a)
Embed(content: input_b)
Embed(content: input_c)
}
}
}
Modules
experimental
HStack
and VStack
components.Structs
element::Any
or a component::Any
.