mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2025-12-06 03:36:42 +01:00
270 lines
8.9 KiB
Rust
270 lines
8.9 KiB
Rust
use crate::*;
|
|
|
|
pub struct Bordered<S, W>(pub bool, pub S, pub W);
|
|
impl<S: BorderStyle, W: Render<TuiOut>> Content<TuiOut> for Bordered<S, W> {
|
|
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
|
|
Some(Fill::xy(
|
|
lay!(When::new(self.0, Border(self.0, self.1)), Padding::xy(1, 1, &self.2))
|
|
))
|
|
}
|
|
}
|
|
|
|
pub struct Border<S: BorderStyle>(pub bool, pub S);
|
|
impl<S: BorderStyle> Render<TuiOut> for Border<S> {
|
|
fn layout (&self, area: [u16;4]) -> [u16;4] {
|
|
self.1.layout(area)
|
|
}
|
|
fn render (&self, to: &mut TuiOut) {
|
|
if self.0 {
|
|
let area = to.area();
|
|
if area.w() > 0 && area.y() > 0 {
|
|
to.blit(&self.1.nw(), area.x(), area.y(), self.1.style());
|
|
to.blit(&self.1.ne(), area.x() + area.w() - 1, area.y(), self.1.style());
|
|
to.blit(&self.1.sw(), area.x(), area.y() + area.h() - 1, self.1.style());
|
|
to.blit(&self.1.se(), area.x() + area.w() - 1, area.y() + area.h() - 1, self.1.style());
|
|
for x in area.x()+1..area.x()+area.w()-1 {
|
|
to.blit(&self.1.n(), x, area.y(), self.1.style());
|
|
to.blit(&self.1.s(), x, area.y() + area.h() - 1, self.1.style());
|
|
}
|
|
for y in area.y()+1..area.y()+area.h()-1 {
|
|
to.blit(&self.1.w(), area.x(), y, self.1.style());
|
|
to.blit(&self.1.e(), area.x() + area.w() - 1, y, self.1.style());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait BorderStyle: Render<TuiOut> + Copy {
|
|
fn enabled (&self) -> bool;
|
|
fn enclose <W: Render<TuiOut>> (self, w: W) -> impl Render<TuiOut> {
|
|
Bsp::b(Fill::xy(Border(self.enabled(), self)), w)
|
|
}
|
|
fn enclose2 <W: Render<TuiOut>> (self, w: W) -> impl Render<TuiOut> {
|
|
Bsp::b(Margin::xy(1, 1, Fill::xy(Border(self.enabled(), self))), w)
|
|
}
|
|
fn enclose_bg <W: Render<TuiOut>> (self, w: W) -> impl Render<TuiOut> {
|
|
Tui::bg(self.style().unwrap().bg.unwrap_or(Color::Reset),
|
|
Bsp::b(Fill::xy(Border(self.enabled(), self)), w))
|
|
}
|
|
const NW: &'static str = "";
|
|
const N: &'static str = "";
|
|
const NE: &'static str = "";
|
|
const E: &'static str = "";
|
|
const SE: &'static str = "";
|
|
const S: &'static str = "";
|
|
const SW: &'static str = "";
|
|
const W: &'static str = "";
|
|
|
|
const N0: &'static str = "";
|
|
const S0: &'static str = "";
|
|
const W0: &'static str = "";
|
|
const E0: &'static str = "";
|
|
|
|
fn n (&self) -> &str { Self::N }
|
|
fn s (&self) -> &str { Self::S }
|
|
fn e (&self) -> &str { Self::E }
|
|
fn w (&self) -> &str { Self::W }
|
|
fn nw (&self) -> &str { Self::NW }
|
|
fn ne (&self) -> &str { Self::NE }
|
|
fn sw (&self) -> &str { Self::SW }
|
|
fn se (&self) -> &str { Self::SE }
|
|
#[inline] fn draw <'a> (
|
|
&self, to: &mut TuiOut
|
|
) -> Usually<()> {
|
|
if self.enabled() {
|
|
self.draw_horizontal(to, None)?;
|
|
self.draw_vertical(to, None)?;
|
|
self.draw_corners(to, None)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
#[inline] fn draw_horizontal (
|
|
&self, to: &mut TuiOut, style: Option<Style>
|
|
) -> Usually<[u16;4]> {
|
|
let area = to.area();
|
|
let style = style.or_else(||self.style_horizontal());
|
|
let [x, x2, y, y2] = area.lrtb();
|
|
for x in x..x2.saturating_sub(1) {
|
|
to.blit(&Self::N, x, y, style);
|
|
to.blit(&Self::S, x, y2.saturating_sub(1), style)
|
|
}
|
|
Ok(area)
|
|
}
|
|
#[inline] fn draw_vertical (
|
|
&self, to: &mut TuiOut, style: Option<Style>
|
|
) -> Usually<[u16;4]> {
|
|
let area = to.area();
|
|
let style = style.or_else(||self.style_vertical());
|
|
let [x, x2, y, y2] = area.lrtb();
|
|
let h = y2 - y;
|
|
if h > 1 {
|
|
for y in y..y2.saturating_sub(1) {
|
|
to.blit(&Self::W, x, y, style);
|
|
to.blit(&Self::E, x2.saturating_sub(1), y, style);
|
|
}
|
|
} else if h > 0 {
|
|
to.blit(&Self::W0, x, y, style);
|
|
to.blit(&Self::E0, x2.saturating_sub(1), y, style);
|
|
}
|
|
Ok(area)
|
|
}
|
|
#[inline] fn draw_corners (
|
|
&self, to: &mut TuiOut, style: Option<Style>
|
|
) -> Usually<[u16;4]> {
|
|
let area = to.area();
|
|
let style = style.or_else(||self.style_corners());
|
|
let [x, y, width, height] = area.xywh();
|
|
if width > 1 && height > 1 {
|
|
to.blit(&Self::NW, x, y, style);
|
|
to.blit(&Self::NE, x + width - 1, y, style);
|
|
to.blit(&Self::SW, x, y + height - 1, style);
|
|
to.blit(&Self::SE, x + width - 1, y + height - 1, style);
|
|
}
|
|
Ok(area)
|
|
}
|
|
#[inline] fn style (&self) -> Option<Style> { None }
|
|
#[inline] fn style_horizontal (&self) -> Option<Style> { self.style() }
|
|
#[inline] fn style_vertical (&self) -> Option<Style> { self.style() }
|
|
#[inline] fn style_corners (&self) -> Option<Style> { self.style() }
|
|
}
|
|
|
|
macro_rules! border {
|
|
($($T:ident {
|
|
$nw:literal $n:literal $ne:literal $w:literal $e:literal $sw:literal $s:literal $se:literal
|
|
$($x:tt)*
|
|
}),+) => {$(
|
|
impl BorderStyle for $T {
|
|
const NW: &'static str = $nw;
|
|
const N: &'static str = $n;
|
|
const NE: &'static str = $ne;
|
|
const W: &'static str = $w;
|
|
const E: &'static str = $e;
|
|
const SW: &'static str = $sw;
|
|
const S: &'static str = $s;
|
|
const SE: &'static str = $se;
|
|
$($x)*
|
|
fn enabled (&self) -> bool { self.0 }
|
|
}
|
|
#[derive(Copy, Clone)] pub struct $T(pub bool, pub Style);
|
|
impl Render<TuiOut> for $T {
|
|
fn render (&self, to: &mut TuiOut) {
|
|
if self.enabled() { let _ = self.draw(to); }
|
|
}
|
|
}
|
|
)+}
|
|
}
|
|
|
|
border! {
|
|
Square {
|
|
"┌" "─" "┐"
|
|
"│" "│"
|
|
"└" "─" "┘" fn style (&self) -> Option<Style> { Some(self.1) }
|
|
},
|
|
SquareBold {
|
|
"┏" "━" "┓"
|
|
"┃" "┃"
|
|
"┗" "━" "┛" fn style (&self) -> Option<Style> { Some(self.1) }
|
|
},
|
|
TabLike {
|
|
"╭" "─" "╮"
|
|
"│" "│"
|
|
"│" " " "│" fn style (&self) -> Option<Style> { Some(self.1) }
|
|
},
|
|
Lozenge {
|
|
"╭" "─" "╮"
|
|
"│" "│"
|
|
"╰" "─" "╯" fn style (&self) -> Option<Style> { Some(self.1) }
|
|
},
|
|
Brace {
|
|
"╭" "" "╮"
|
|
"│" "│"
|
|
"╰" "" "╯" fn style (&self) -> Option<Style> { Some(self.1) }
|
|
},
|
|
LozengeDotted {
|
|
"╭" "┅" "╮"
|
|
"┇" "┇"
|
|
"╰" "┅" "╯" fn style (&self) -> Option<Style> { Some(self.1) }
|
|
},
|
|
Quarter {
|
|
"▎" "▔" "🮇"
|
|
"▎" "🮇"
|
|
"▎" "▁" "🮇" fn style (&self) -> Option<Style> { Some(self.1) }
|
|
},
|
|
QuarterV {
|
|
"▎" "" "🮇"
|
|
"▎" "🮇"
|
|
"▎" "" "🮇" fn style (&self) -> Option<Style> { Some(self.1) }
|
|
},
|
|
Chamfer {
|
|
"🭂" "▔" "🭍"
|
|
"▎" "🮇"
|
|
"🭓" "▁" "🭞" fn style (&self) -> Option<Style> { Some(self.1) }
|
|
},
|
|
Corners {
|
|
"🬆" "" "🬊" // 🬴 🬸
|
|
"" ""
|
|
"🬱" "" "🬵" fn style (&self) -> Option<Style> { Some(self.1) }
|
|
},
|
|
CornersTall {
|
|
"🭽" "" "🭾"
|
|
"" ""
|
|
"🭼" "" "🭿" fn style (&self) -> Option<Style> { Some(self.1) }
|
|
},
|
|
Outer {
|
|
"🭽" "▔" "🭾"
|
|
"▏" "▕"
|
|
"🭼" "▁" "🭿"
|
|
const W0: &'static str = "[";
|
|
const E0: &'static str = "]";
|
|
const N0: &'static str = "⎴";
|
|
const S0: &'static str = "⎵";
|
|
fn style (&self) -> Option<Style> { Some(self.1) }
|
|
},
|
|
Thick {
|
|
"▄" "▄" "▄"
|
|
"█" "█"
|
|
"▀" "▀" "▀"
|
|
fn style (&self) -> Option<Style> { Some(self.1) }
|
|
},
|
|
Rugged {
|
|
"▄" "▂" "▄"
|
|
"▐" "▌"
|
|
"▀" "🮂" "▀"
|
|
fn style (&self) -> Option<Style> { Some(self.1) }
|
|
},
|
|
Skinny {
|
|
"▗" "▄" "▖"
|
|
"▐" "▌"
|
|
"▝" "▀" "▘"
|
|
fn style (&self) -> Option<Style> { Some(self.1) }
|
|
},
|
|
Brackets {
|
|
"⎡" "" "⎤"
|
|
"" ""
|
|
"⎣" "" "⎦"
|
|
const W0: &'static str = "[";
|
|
const E0: &'static str = "]";
|
|
const N0: &'static str = "⎴";
|
|
const S0: &'static str = "⎵";
|
|
fn style (&self) -> Option<Style> { Some(self.1) }
|
|
},
|
|
Reticle {
|
|
"⎡" "" "⎤"
|
|
"" ""
|
|
"⎣" "" "⎦"
|
|
const W0: &'static str = "╟";
|
|
const E0: &'static str = "╢";
|
|
const N0: &'static str = "┯";
|
|
const S0: &'static str = "┷";
|
|
fn style (&self) -> Option<Style> { Some(self.1) }
|
|
}
|
|
}
|
|
|
|
|
|
//impl<S: BorderStyle, R: Content<TuiOut>> Content<TuiOut> for Bordered<S, R> {
|
|
//fn content (&self) -> impl Render<TuiOut> {
|
|
//let content: &dyn Content<TuiOut> = &self.1;
|
|
//lay! { content.padding_xy(1, 1), Border(self.0) }.fill_xy()
|
|
//}
|
|
//}
|