use abstracttui::prelude::*;
use abstracttui::widgets::{ColWidth, Column, Scroll, Table};
const ROWS: usize = 200;
fn rows_view(cx: Scope) -> View {
let mut col = Element::new().style(LayoutStyle::column());
for i in 0..ROWS {
col = col.child(text(format!("row {i:>3} — drag the bar, watch me move")));
}
let _ = cx;
col.build()
}
fn main() -> abstracttui::base::Result<()> {
if !abstracttui::term::have_tty() {
println!("scrollbar: needs an interactive terminal — skipping cleanly");
return Ok(());
}
if let Ok(id) = std::env::var("ABSTRACTTUI_THEME") {
set_theme_by_id(&id);
}
let mut app = App::new(Size::new(100, 30));
let quitter = app.quitter();
app.mount(move |cx| {
let t = use_theme(cx).get().tokens;
let left = cx.signal(0i32);
let right = cx.signal(0i32);
let list_off = cx.signal(0i32);
let gutter = cx.signal(1i32);
let items: Vec<String> = (0..ROWS).map(|i| format!("item {i:>3}")).collect();
let table_rows: Vec<Vec<String>> = (0..ROWS)
.map(|i| vec![format!("file-{i:>3}.rs"), format!("{} kB", i * 3 % 97)])
.collect();
let selected = cx.signal(0usize);
let pane_style = || LayoutStyle::column().grow(1.0).basis(Dimension::Cells(0));
let one = Block::new()
.title("Scroll · gutter 1")
.layout(pane_style())
.child(
Scroll::new(rows_view(cx))
.offset_y(left)
.element(cx, &t)
.build(),
)
.element(&t)
.build();
let two = dyn_view(pane_style(), move || {
let t = use_theme(cx).get().tokens;
let w = gutter.get();
Block::new()
.title(format!("Scroll · gutter {w} (press w)"))
.layout(pane_style())
.child(
Scroll::new(rows_view(cx))
.offset_y(right)
.scrollbar_width(w)
.element(cx, &t)
.build(),
)
.element(&t)
.build()
});
let list = Block::new()
.title("List · same strip")
.layout(pane_style())
.child(List::of(items).offset_y(list_off).element(cx, &t).build())
.element(&t)
.build();
let table = Block::new()
.title("Table · strip never selects")
.layout(pane_style())
.child(
Table::new(vec![
Column::new("name", ColWidth::Flex(1.0)),
Column::new("size", ColWidth::Cells(7)),
])
.rows(table_rows)
.selection(selected)
.element(cx, &t)
.build(),
)
.element(&t)
.build();
let row = |a: View, b: View| {
Element::new()
.style(
LayoutStyle::row()
.gap(1)
.grow(1.0)
.basis(Dimension::Cells(0)),
)
.child(a)
.child(b)
.build()
};
Element::new()
.style(LayoutStyle::column().gap(1).padding(Edges::all(1)))
.shortcut(KeyChord::plain(Key::Char('q')), move |_| quitter.quit())
.shortcut(KeyChord::plain(Key::Char('w')), move |_| {
gutter.update(|w| *w = if *w >= 4 { 1 } else { *w + 1 });
})
.child(row(one, two))
.child(row(list, table))
.child(dyn_view(LayoutStyle::line(1).shrink(0.0), move || {
text(format!(
"offsets — scroll {} · scroll {} · list {} · table row {} \
· press the thumb: nothing moves · w gutter · q quit",
left.get(),
right.get(),
list_off.get(),
selected.get(),
))
}))
.build()
})?;
app.run_with(RunConfig {
hover_ink: true,
..RunConfig::default()
})
}