use futtl::Futtl;
#[derive(Copy, Clone)]
pub enum Layout {
Vh(i32),
Vw(i32),
Percent(i32),
Cell(i32),
Auto
}
#[derive(Copy, Clone)]
pub struct LayoutVec2 {
pub x: Layout,
pub y: Layout
}
impl LayoutVec2 {
pub fn new(x: Layout, y: Layout) -> Self {
Self {x, y}
}
}
pub struct Style {
pub position: LayoutVec2,
pub size: LayoutVec2,
pub margin: LayoutVec2,
pub color: futtl::ColorSet,
pub attributes: futtl::Attr
}
pub enum WidgetCtx {
Box(usize),
None
}
pub struct Text {
pub text: String,
pub style: Style
}
pub struct Widget {
pub ctx: WidgetCtx,
pub style: Style,
pub children: Vec<Widget>,
pub content: Vec<Text>
}
pub struct Win {
pub ctx: futtl::Win,
pub children: Vec<Widget>,
pub color: futtl::ColorSet
}
impl Style {
pub fn new() -> Self {
Self {
position: LayoutVec2 {x: Layout::Auto, y: Layout::Auto},
size: LayoutVec2 {x: Layout::Auto, y: Layout::Auto},
margin: LayoutVec2 {x: Layout::Auto, y: Layout::Auto},
color: futtl::ColorSet::new_default(),
attributes: futtl::Attr::NONE
}
}
}
fn evaluate_size(term_size: futtl::Vec2, parent_size: futtl::Vec2, size_obj: LayoutVec2) -> futtl::Vec2 {
let x = match size_obj.x {
Layout::Auto => term_size.x,
Layout::Vw(n) => parent_size.x * n / 100,
Layout::Vh(_) => 0,
Layout::Percent(n) => term_size.x * n / 100,
Layout::Cell(n) => n
};
let y = match size_obj.y {
Layout::Auto => term_size.y,
Layout::Vw(_) => 0,
Layout::Vh(n) => parent_size.y * n / 100,
Layout::Percent(n) => term_size.y * n / 100,
Layout::Cell(n) => n
};
futtl::Vec2::new(x, y)
}
fn evaluate_pos(term_size: futtl::Vec2, parent_size: futtl::Vec2, pos_obj: LayoutVec2) -> futtl::Vec2 {
let x = match pos_obj.x {
Layout::Auto => term_size.x,
Layout::Vw(n) => parent_size.x * n / 100,
Layout::Vh(_) => 0,
Layout::Percent(n) => term_size.x * n / 100,
Layout::Cell(n) => n
};
let y = match pos_obj.y {
Layout::Auto => term_size.y,
Layout::Vw(_) => 0,
Layout::Vh(n) => parent_size.y * n / 100,
Layout::Percent(n) => term_size.y * n / 100,
Layout::Cell(n) => n
};
futtl::Vec2::new(x, y)
}
impl Win {
pub fn new() -> Self {
Self {
ctx: futtl::Win::new(256, futtl::Attr::NONE),
children: Vec::new(),
color: futtl::ColorSet::new(
futtl::Color::ANSI(futtl::AnsiColor::DefaultFG),
futtl::Color::ANSI(futtl::AnsiColor::DefaultBG),
),
}
}
pub fn pack(&mut self, widget: Widget) {
self.children.push(widget);
}
fn evaluate_members(&mut self) {
let term_size = self.ctx.get_size();
for child in self.children.iter_mut() {
let child_size = evaluate_size(term_size, term_size, child.style.size);
let x_size = child_size.x.min(term_size.x);
let y_size = child_size.y.min(term_size.y);
let idx = self.ctx.create_container(x_size, y_size);
child.ctx = WidgetCtx::Box(idx);
}
}
pub fn mainloop(&mut self) {
self.evaluate_members();
let term_size = self.ctx.get_size();
for child in self.children.iter_mut() {
match child.ctx {
WidgetCtx::None => {},
WidgetCtx::Box(_) => {
child.show(term_size, &mut self.ctx);
}
}
}
futtl::flush();
let c = self.ctx.get_in();
if !((c == 'q' as i32) || (c == 27)) {
self.mainloop();
}
}
}
pub trait Stylable: Sized {
fn style(&mut self) -> &mut Style;
fn foreground(mut self, col: futtl::Color) -> Self {
self.style().color.fg = col;
self
}
fn background(mut self, col: futtl::Color) -> Self {
self.style().color.bg = col;
self
}
fn position(mut self, x: Layout, y: Layout) -> Self {
self.style().position.x = x;
self.style().position.y = y;
self
}
fn position_x(mut self, x: Layout) -> Self {
self.style().position.x = x;
self
}
fn position_y(mut self, y: Layout) -> Self {
self.style().position.y = y;
self
}
fn size(mut self, x: Layout, y: Layout) -> Self {
self.style().size.x = x;
self.style().size.y = y;
self
}
fn size_x(mut self, x: Layout) -> Self {
self.style().size.x = x;
self
}
fn size_y(mut self, y: Layout) -> Self {
self.style().size.y = y;
self
}
fn margin(mut self, x: Layout, y: Layout) -> Self {
self.style().margin.x = x;
self.style().margin.y = y;
self
}
fn margin_x(mut self, x: Layout) -> Self {
self.style().margin.x = x;
self
}
fn margin_y(mut self, y: Layout) -> Self {
self.style().margin.y = y;
self
}
fn attribute(mut self, attr: futtl::Attr) -> Self {
self.style().attributes |= attr;
self
}
fn remove_attribute(mut self, attr: futtl::Attr) -> Self {
self.style().attributes &= !attr;
self
}
}
impl Stylable for Widget {
fn style(&mut self) -> &mut Style {
&mut self.style
}
}
impl Stylable for Text {
fn style(&mut self) -> &mut Style {
&mut self.style
}
}
impl Widget {
pub fn new() -> Self {
Self {
ctx: WidgetCtx::None,
style: Style::new(),
children: Vec::new(),
content: Vec::new()
}
}
pub fn style(&mut self, style: Style) {
self.style = style;
}
pub fn text(&mut self, text: String) -> &mut Text {
self.content.push(Text {
text,
style: Style::new(),
});
self.content.last_mut().unwrap()
}
pub fn show(&mut self, term_size: futtl::Vec2, f_ctx: &mut futtl::Win) {
match self.ctx {
WidgetCtx::None => {}
WidgetCtx::Box(ctx) => {
let pos = evaluate_pos(
term_size,
term_size,
self.style.position,
);
let box_ctx = &mut f_ctx.children()[ctx];
box_ctx.clear();
box_ctx.set_curs(0, 0);
box_ctx.add_color(self.style.color, 0);
box_ctx.set_color(0);
for item in self.content.iter() {
box_ctx.put_str(&item.text);
}
box_ctx.show(pos.x, pos.y);
}
}
}
}
impl Default for Win {
fn default() -> Self {
Self::new()
}
}
impl Default for Widget {
fn default() -> Self {
Self::new()
}
}
impl Default for Style {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let mut ctx = Win::new();
let mut text_widget = Widget::new()
.position_x(Layout::Cell(0))
.position_y(Layout::Cell(0))
.size_x(Layout::Vw(100))
.size_y(Layout::Vh(100));
text_widget.text("test".to_string());
ctx.pack(text_widget);
ctx.mainloop();
}
}