fatuilog 1: introducing fatui
if you speak italian the name is silly
mood: relieveddevlogfatuilast time i mentioned that
[…]the curse has once again stirred in its sleep, and in the wreckage i have acquired…
a new project
nyehehehe
that new project is fatui, a textmode ui crate for huge nerds. usage looks a bit like this:
let mut backend = fatui::open();
let mut system = sysinfo::System::new();
system.refresh_processes(ProcessesToUpdate::All, true);
while let Some(frame) = backend.step() {
let (header, body) = frame.split(rows!(1, *));
let (status, reload) = header.split(cols(*, 8));
status.draw(text!(
"Status: " black on_white,
if system.processes().is_empty() {
"DEAD" white on_red
} else {
"OK" green on_white
},
));
if reload.draw(button!(white on_dark_grey " reload ")).clicked {
// TODO: do this in a background thread
system.refresh_processes(ProcessesToUpdate::All, true);
}
body.draw(ProcessViewer(system.processes()));
}
now, you may notice that there's no fatui project link on the front page. that's on purpose: i've just started this project! i don't wanna write a bunch of docs just to end up changing everything.
for example, there are a lot of important features left out of that tiny example: scrollable areas, overlapping regions, modals, borders, externally triggered redraws… i more or less understand how these will work under the hood, but designing the apis is hard!
so, until things are a little more set in stone, i'm not going to be publishing tons of documentation, or even pushing this to crates.io. i expect that to be up probably soonish – you know, on my timescale, so you can probably expect fatuilog 2 sometime in august. of 2027.
that said, i do have the basic abstraction and its api set in stone. briefly:
-
get a
dyn Backend
however you like, with a reasonable default beingfatui::open()
-
use
backend.step()
to get the next frame, which has one user input event and a character grid to draw the updated screen into -
use
frame.split()
to break the frame up into sub-frames, then those sub-frames into sub-sub-frames, etc. -
use
frame.draw()
to attach a component to a frame (or sub-frame, or sub-sub– you get it, they're all juststruct Frame
) -
use the return value of
frame.draw()
to get output from the component, like whether a button was just clicked.
this should feel somewhat familiar to anyone who's used
ratatui
,
but there are two major differences:
-
rather than constructing regions with
Layout
s andConstraint
s, yousplit
largerFrame
s into smaller ones. - rather than implementing the event loop and processing yourself, including unpicking mouse events, fatui handles that all for you, and uses those split boundaries to pass inputs to the right place.
these design decisions have some consequences, like making it somewhat more complex to externally trigger a redraw than ratatui does. but i think they're a worthwhile enough tradeoff to spend my time building this, and, well, regardless i think i'm going to have fun!
oh! and about the name. it's literally silly in italian, but it's also two more jokes:
- "facilitate attractive text user interfaces", as a very long backronym
- "fa' tui", "make yours" – that is, make your tui! (literally incorrect, but close enough)