mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
wip: a little broken rendering (as a treat)
This commit is contained in:
parent
80086b9a8b
commit
eb122585d6
9 changed files with 457 additions and 474 deletions
|
|
@ -13,9 +13,9 @@ impl Direction {
|
|||
}
|
||||
|
||||
pub struct Split<'a, E: Engine> {
|
||||
items: Collection<'a, E>,
|
||||
direction: Direction,
|
||||
focus: Option<usize>
|
||||
pub items: Collection<'a, E>,
|
||||
pub direction: Direction,
|
||||
pub focus: Option<usize>
|
||||
}
|
||||
|
||||
impl<'a, E: Engine> Split<'a, E> {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,12 @@ use crossterm::terminal::{
|
|||
EnterAlternateScreen, LeaveAlternateScreen,
|
||||
enable_raw_mode, disable_raw_mode
|
||||
};
|
||||
submod! {
|
||||
tui_border
|
||||
tui_buffer
|
||||
tui_colors
|
||||
tui_layout
|
||||
}
|
||||
pub struct Tui {
|
||||
exited: Arc<AtomicBool>,
|
||||
buffer: usize,
|
||||
|
|
@ -98,7 +104,7 @@ impl Tui {
|
|||
if engine.exited() {
|
||||
break
|
||||
}
|
||||
engine.render(&*state).expect("render failed");
|
||||
state.render(&mut engine).expect("render failed");
|
||||
engine.flip();
|
||||
}
|
||||
std::thread::sleep(sleep);
|
||||
|
|
@ -219,449 +225,3 @@ pub fn half_block (lower: bool, upper: bool) -> Option<char> {
|
|||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FillBg(pub Color);
|
||||
|
||||
impl Render<Tui> for FillBg {
|
||||
fn render (&self, to: &mut Tui) -> Perhaps<Rect> {
|
||||
to.fill_bg(to.area, self.0);
|
||||
Ok(Some(to.area))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BorderStyle {
|
||||
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 = "";
|
||||
|
||||
#[inline]
|
||||
fn draw <'a> (&self, to: &mut Tui) -> Perhaps<Rect> {
|
||||
self.draw_horizontal(to, None)?;
|
||||
self.draw_vertical(to, None)?;
|
||||
self.draw_corners(to, None)?;
|
||||
Ok(Some(to.area))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_horizontal (&self, to: &mut Tui, style: Option<Style>) -> Usually<Rect> {
|
||||
let area = to.area();
|
||||
let buf = to.buffer();
|
||||
let style = style.or_else(||self.style_horizontal());
|
||||
for x in area.x..(area.x+area.width).saturating_sub(1) {
|
||||
self.draw_north(to, x, area.y, style)?;
|
||||
self.draw_south(to, x, (area.y + area.height).saturating_sub(1), style)?;
|
||||
}
|
||||
Ok(area)
|
||||
}
|
||||
#[inline]
|
||||
fn draw_north (&self, to: &mut Tui, x: u16, y: u16, style: Option<Style>) -> Perhaps<Rect> {
|
||||
to.blit(&Self::N, x, y, style)
|
||||
}
|
||||
#[inline]
|
||||
fn draw_south (&self, to: &mut Tui, x: u16, y: u16, style: Option<Style>) -> Perhaps<Rect> {
|
||||
to.blit(&Self::S, x, y, style)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_vertical (&self, to: &mut Tui, style: Option<Style>) -> Usually<Rect> {
|
||||
let area = to.area();
|
||||
let style = style.or_else(||self.style_vertical());
|
||||
let Rect { x, y, width, height } = area;
|
||||
for y in y..(y+height).saturating_sub(1) {
|
||||
to.blit(&Self::W, x, y, style)?;
|
||||
to.blit(&Self::E, x + width - 1, y, style)?;
|
||||
}
|
||||
Ok(area)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_corners (&self, to: &mut Tui, style: Option<Style>) -> Usually<Rect> {
|
||||
let area = to.area();
|
||||
let style = style.or_else(||self.style_corners());
|
||||
let Rect { x, y, width, height } = area;
|
||||
if width > 0 && height > 0 {
|
||||
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:ty {
|
||||
$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)*
|
||||
})+
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Lozenge(pub Style);
|
||||
pub struct LozengeV(pub Style);
|
||||
pub struct LozengeDotted(pub Style);
|
||||
pub struct Quarter(pub Style);
|
||||
pub struct QuarterV(pub Style);
|
||||
pub struct Chamfer(pub Style);
|
||||
pub struct Corners(pub Style);
|
||||
|
||||
border! {
|
||||
Lozenge {
|
||||
"╭" "─" "╮"
|
||||
"│" "│"
|
||||
"╰" "─" "╯"
|
||||
fn style (&self) -> Option<Style> {
|
||||
Some(self.0)
|
||||
}
|
||||
},
|
||||
LozengeV {
|
||||
"╭" "" "╮"
|
||||
"│" "│"
|
||||
"╰" "" "╯"
|
||||
fn style (&self) -> Option<Style> {
|
||||
Some(self.0)
|
||||
}
|
||||
},
|
||||
LozengeDotted {
|
||||
"╭" "┅" "╮"
|
||||
"┇" "┇"
|
||||
"╰" "┅" "╯"
|
||||
fn style (&self) -> Option<Style> {
|
||||
Some(self.0)
|
||||
}
|
||||
},
|
||||
Quarter {
|
||||
"▎" "▔" "🮇"
|
||||
"▎" "🮇"
|
||||
"▎" "▁" "🮇"
|
||||
fn style (&self) -> Option<Style> {
|
||||
Some(self.0)
|
||||
}
|
||||
},
|
||||
QuarterV {
|
||||
"▎" "" "🮇"
|
||||
"▎" "🮇"
|
||||
"▎" "" "🮇"
|
||||
fn style (&self) -> Option<Style> {
|
||||
Some(self.0)
|
||||
}
|
||||
},
|
||||
Chamfer {
|
||||
"🭂" "▔" "🭍"
|
||||
"▎" "🮇"
|
||||
"🭓" "▁" "🭞"
|
||||
fn style (&self) -> Option<Style> {
|
||||
Some(self.0)
|
||||
}
|
||||
},
|
||||
Corners {
|
||||
"🬆" "" "🬊" // 🬴 🬸
|
||||
"" ""
|
||||
"🬱" "" "🬵"
|
||||
fn style (&self) -> Option<Style> {
|
||||
Some(self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const COLOR_BG0: Color = Color::Rgb(30, 33, 36);
|
||||
pub const COLOR_BG1: Color = Color::Rgb(41, 46, 57);
|
||||
pub const COLOR_BG2: Color = Color::Rgb(46, 52, 64);
|
||||
pub const COLOR_BG3: Color = Color::Rgb(59, 66, 82);
|
||||
pub const COLOR_BG4: Color = Color::Rgb(67, 76, 94);
|
||||
pub const COLOR_BG5: Color = Color::Rgb(76, 86, 106);
|
||||
|
||||
pub trait Theme {
|
||||
const BG0: Color;
|
||||
const BG1: Color;
|
||||
const BG2: Color;
|
||||
const BG3: Color;
|
||||
const BG4: Color;
|
||||
const RED: Color;
|
||||
const YELLOW: Color;
|
||||
const GREEN: Color;
|
||||
|
||||
const PLAYING: Color;
|
||||
const SEPARATOR: Color;
|
||||
|
||||
fn bg_hier (focused: bool, entered: bool) -> Color {
|
||||
if focused && entered {
|
||||
Self::BG3
|
||||
} else if focused {
|
||||
Self::BG2
|
||||
} else {
|
||||
Self::BG1
|
||||
}
|
||||
}
|
||||
|
||||
fn bg_hi (focused: bool, entered: bool) -> Color {
|
||||
if focused && entered {
|
||||
Self::BG2
|
||||
} else if focused {
|
||||
Self::BG1
|
||||
} else {
|
||||
Self::BG0
|
||||
}
|
||||
}
|
||||
|
||||
fn bg_lo (focused: bool, entered: bool) -> Color {
|
||||
if focused && entered {
|
||||
Self::BG1
|
||||
} else if focused {
|
||||
Self::BG0
|
||||
} else {
|
||||
Color::Reset
|
||||
}
|
||||
}
|
||||
|
||||
fn style_hi (focused: bool, highlight: bool) -> Style {
|
||||
if highlight && focused {
|
||||
Style::default().yellow().not_dim()
|
||||
} else if highlight {
|
||||
Style::default().yellow().dim()
|
||||
} else {
|
||||
Style::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Nord;
|
||||
|
||||
impl Theme for Nord {
|
||||
const BG0: Color = Color::Rgb(41, 46, 57);
|
||||
const BG1: Color = Color::Rgb(46, 52, 64);
|
||||
const BG2: Color = Color::Rgb(59, 66, 82);
|
||||
const BG3: Color = Color::Rgb(67, 76, 94);
|
||||
const BG4: Color = Color::Rgb(76, 86, 106);
|
||||
const RED: Color = Color::Rgb(191, 97, 106);
|
||||
const YELLOW: Color = Color::Rgb(235, 203, 139);
|
||||
const GREEN: Color = Color::Rgb(163, 190, 140);
|
||||
|
||||
const PLAYING: Color = Color::Rgb(60, 100, 50);
|
||||
const SEPARATOR: Color = Color::Rgb(0, 0, 0);
|
||||
}
|
||||
|
||||
pub const GRAY: Style = Style {
|
||||
fg: Some(Color::Gray),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::empty(),
|
||||
sub_modifier: Modifier::empty(),
|
||||
};
|
||||
|
||||
pub const GRAY_NOT_DIM: Style = Style {
|
||||
fg: Some(Color::Gray),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::empty(),
|
||||
sub_modifier: Modifier::DIM,
|
||||
};
|
||||
|
||||
pub const DIM: Style = Style {
|
||||
fg: None,
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::DIM,
|
||||
sub_modifier: Modifier::empty(),
|
||||
};
|
||||
|
||||
pub const GRAY_DIM: Style = Style {
|
||||
fg: Some(Color::Gray),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::DIM,
|
||||
sub_modifier: Modifier::empty(),
|
||||
};
|
||||
|
||||
pub const WHITE_NOT_DIM_BOLD: Style = Style {
|
||||
fg: Some(Color::White),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::BOLD,
|
||||
sub_modifier: Modifier::DIM,
|
||||
};
|
||||
|
||||
pub const GRAY_NOT_DIM_BOLD: Style = Style {
|
||||
fg: Some(Color::Gray),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::BOLD,
|
||||
sub_modifier: Modifier::DIM,
|
||||
};
|
||||
|
||||
pub const NOT_DIM: Style = Style {
|
||||
fg: None,
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::empty(),
|
||||
sub_modifier: Modifier::DIM,
|
||||
};
|
||||
|
||||
pub const NOT_DIM_GREEN: Style = Style {
|
||||
fg: Some(Color::Rgb(96, 255, 32)),
|
||||
bg: Some(COLOR_BG1),
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::empty(),
|
||||
sub_modifier: Modifier::DIM,
|
||||
};
|
||||
|
||||
pub const NOT_DIM_BOLD: Style = Style {
|
||||
fg: None,
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::BOLD,
|
||||
sub_modifier: Modifier::DIM,
|
||||
};
|
||||
|
||||
impl<'a> Render<Tui> for Layered<'a, Tui> {
|
||||
fn render (&self, to: &mut Tui) -> Perhaps<Rect> {
|
||||
let area = to.area();
|
||||
for layer in self.0.0.iter() {
|
||||
layer.render(to)?;
|
||||
}
|
||||
Ok(Some(area))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Render<Tui> for Split<'a, Tui> {
|
||||
fn render (&self, to: &mut Tui) -> Perhaps<Rect> {
|
||||
Ok(Some(self.render_areas(to)?.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Split<'a, Tui> {
|
||||
pub fn render_areas (&self, to: &mut Tui) -> Usually<(Rect, Vec<Rect>)> {
|
||||
Ok((Rect::default(), vec![]))
|
||||
//let Rect { mut x, mut y, mut width, mut height } = to.area;
|
||||
//let TuiOutput { buffer, area } = to;
|
||||
//let mut areas = vec![];
|
||||
//for (index, item) in self.items.0.iter().enumerate() {
|
||||
//if width == 0 || height == 0 {
|
||||
//break
|
||||
//}
|
||||
//if let Some(result) = item.render(&mut TuiOutput {
|
||||
//buffer: to.buffer,
|
||||
//area: Rect { x, y, width, height }
|
||||
//})? {
|
||||
//match self.direction {
|
||||
//Direction::Down => {
|
||||
//y += result.height;
|
||||
//height = height.saturating_sub(result.height);
|
||||
//},
|
||||
//Direction::Right => {
|
||||
//x += result.width;
|
||||
//width = width.saturating_sub(result.width);
|
||||
//},
|
||||
//_ => unimplemented!()
|
||||
//};
|
||||
//areas.push(result);
|
||||
//if self.focus == Some(index) {
|
||||
//Corners(Style::default().green().not_dim()).draw(to.buffer, result)?;
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
//Ok((to.area, areas))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BigBuffer {
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
pub content: Vec<Cell>
|
||||
}
|
||||
|
||||
impl BigBuffer {
|
||||
pub fn new (width: usize, height: usize) -> Self {
|
||||
Self { width, height, content: vec![Cell::default(); width*height] }
|
||||
}
|
||||
pub fn get (&self, x: usize, y: usize) -> Option<&Cell> {
|
||||
let i = self.index_of(x, y);
|
||||
self.content.get(i)
|
||||
}
|
||||
pub fn get_mut (&mut self, x: usize, y: usize) -> Option<&mut Cell> {
|
||||
let i = self.index_of(x, y);
|
||||
self.content.get_mut(i)
|
||||
}
|
||||
pub fn index_of (&self, x: usize, y: usize) -> usize {
|
||||
y * self.width + x
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Render<Tui>> Render<Tui> for (Offset<u16>, R) {
|
||||
fn render (&self, engine: &mut Tui) -> Perhaps<Rect> {
|
||||
self.1.render(engine.alter_area(|x, y, width, height|(
|
||||
x + self.0.0,
|
||||
y + self.0.1,
|
||||
width.saturating_sub(self.0.0),
|
||||
height.saturating_sub(self.0.1),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Render<Tui>> Render<Tui> for (Inset<u16>, R) {
|
||||
fn render (&self, engine: &mut Tui) -> Perhaps<Rect> {
|
||||
self.1.render(engine.alter_area(|x, y, width, height|(
|
||||
x + self.0.0,
|
||||
y + self.0.1,
|
||||
width.saturating_sub(self.0.0 + self.0.2),
|
||||
height.saturating_sub(self.0.1 + self.0.3),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Render<Tui>> Render<Tui> for (Outset<u16>, R) {
|
||||
fn render (&self, engine: &mut Tui) -> Perhaps<Rect> {
|
||||
self.1.render(engine.alter_area(|x, y, width, height|(
|
||||
x.saturating_sub(self.0.0),
|
||||
y.saturating_sub(self.0.1),
|
||||
width + self.0.0 + self.0.2,
|
||||
height + self.0.1 + self.0.3,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn buffer_update (buf: &mut Buffer, area: Rect, callback: &impl Fn(&mut Cell, u16, u16)) {
|
||||
for row in 0..area.height {
|
||||
let y = area.y + row;
|
||||
for col in 0..area.width {
|
||||
let x = area.x + col;
|
||||
if x < buf.area.width && y < buf.area.height {
|
||||
callback(buf.get_mut(x, y), col, row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
169
crates/tek_core/src/tui/tui_border.rs
Normal file
169
crates/tek_core/src/tui/tui_border.rs
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
use crate::*;
|
||||
|
||||
pub trait BorderStyle {
|
||||
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 = "";
|
||||
|
||||
#[inline]
|
||||
fn draw <'a> (&self, to: &mut Tui) -> Perhaps<Rect> {
|
||||
self.draw_horizontal(to, None)?;
|
||||
self.draw_vertical(to, None)?;
|
||||
self.draw_corners(to, None)?;
|
||||
Ok(Some(to.area))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_horizontal (&self, to: &mut Tui, style: Option<Style>) -> Usually<Rect> {
|
||||
let area = to.area();
|
||||
let buf = to.buffer();
|
||||
let style = style.or_else(||self.style_horizontal());
|
||||
for x in area.x..(area.x+area.width).saturating_sub(1) {
|
||||
self.draw_north(to, x, area.y, style)?;
|
||||
self.draw_south(to, x, (area.y + area.height).saturating_sub(1), style)?;
|
||||
}
|
||||
Ok(area)
|
||||
}
|
||||
#[inline]
|
||||
fn draw_north (&self, to: &mut Tui, x: u16, y: u16, style: Option<Style>) -> Perhaps<Rect> {
|
||||
to.blit(&Self::N, x, y, style)
|
||||
}
|
||||
#[inline]
|
||||
fn draw_south (&self, to: &mut Tui, x: u16, y: u16, style: Option<Style>) -> Perhaps<Rect> {
|
||||
to.blit(&Self::S, x, y, style)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_vertical (&self, to: &mut Tui, style: Option<Style>) -> Usually<Rect> {
|
||||
let area = to.area();
|
||||
let style = style.or_else(||self.style_vertical());
|
||||
let Rect { x, y, width, height } = area;
|
||||
for y in y..(y+height).saturating_sub(1) {
|
||||
to.blit(&Self::W, x, y, style)?;
|
||||
to.blit(&Self::E, x + width - 1, y, style)?;
|
||||
}
|
||||
Ok(area)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_corners (&self, to: &mut Tui, style: Option<Style>) -> Usually<Rect> {
|
||||
let area = to.area();
|
||||
let style = style.or_else(||self.style_corners());
|
||||
let Rect { x, y, width, height } = area;
|
||||
if width > 0 && height > 0 {
|
||||
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:ty {
|
||||
$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)*
|
||||
})+
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Lozenge(pub Style);
|
||||
pub struct LozengeV(pub Style);
|
||||
pub struct LozengeDotted(pub Style);
|
||||
pub struct Quarter(pub Style);
|
||||
pub struct QuarterV(pub Style);
|
||||
pub struct Chamfer(pub Style);
|
||||
pub struct Corners(pub Style);
|
||||
|
||||
border! {
|
||||
Lozenge {
|
||||
"╭" "─" "╮"
|
||||
"│" "│"
|
||||
"╰" "─" "╯"
|
||||
fn style (&self) -> Option<Style> {
|
||||
Some(self.0)
|
||||
}
|
||||
},
|
||||
LozengeV {
|
||||
"╭" "" "╮"
|
||||
"│" "│"
|
||||
"╰" "" "╯"
|
||||
fn style (&self) -> Option<Style> {
|
||||
Some(self.0)
|
||||
}
|
||||
},
|
||||
LozengeDotted {
|
||||
"╭" "┅" "╮"
|
||||
"┇" "┇"
|
||||
"╰" "┅" "╯"
|
||||
fn style (&self) -> Option<Style> {
|
||||
Some(self.0)
|
||||
}
|
||||
},
|
||||
Quarter {
|
||||
"▎" "▔" "🮇"
|
||||
"▎" "🮇"
|
||||
"▎" "▁" "🮇"
|
||||
fn style (&self) -> Option<Style> {
|
||||
Some(self.0)
|
||||
}
|
||||
},
|
||||
QuarterV {
|
||||
"▎" "" "🮇"
|
||||
"▎" "🮇"
|
||||
"▎" "" "🮇"
|
||||
fn style (&self) -> Option<Style> {
|
||||
Some(self.0)
|
||||
}
|
||||
},
|
||||
Chamfer {
|
||||
"🭂" "▔" "🭍"
|
||||
"▎" "🮇"
|
||||
"🭓" "▁" "🭞"
|
||||
fn style (&self) -> Option<Style> {
|
||||
Some(self.0)
|
||||
}
|
||||
},
|
||||
Corners {
|
||||
"🬆" "" "🬊" // 🬴 🬸
|
||||
"" ""
|
||||
"🬱" "" "🬵"
|
||||
fn style (&self) -> Option<Style> {
|
||||
Some(self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
37
crates/tek_core/src/tui/tui_buffer.rs
Normal file
37
crates/tek_core/src/tui/tui_buffer.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
use crate::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BigBuffer {
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
pub content: Vec<Cell>
|
||||
}
|
||||
|
||||
impl BigBuffer {
|
||||
pub fn new (width: usize, height: usize) -> Self {
|
||||
Self { width, height, content: vec![Cell::default(); width*height] }
|
||||
}
|
||||
pub fn get (&self, x: usize, y: usize) -> Option<&Cell> {
|
||||
let i = self.index_of(x, y);
|
||||
self.content.get(i)
|
||||
}
|
||||
pub fn get_mut (&mut self, x: usize, y: usize) -> Option<&mut Cell> {
|
||||
let i = self.index_of(x, y);
|
||||
self.content.get_mut(i)
|
||||
}
|
||||
pub fn index_of (&self, x: usize, y: usize) -> usize {
|
||||
y * self.width + x
|
||||
}
|
||||
}
|
||||
|
||||
pub fn buffer_update (buf: &mut Buffer, area: Rect, callback: &impl Fn(&mut Cell, u16, u16)) {
|
||||
for row in 0..area.height {
|
||||
let y = area.y + row;
|
||||
for col in 0..area.width {
|
||||
let x = area.x + col;
|
||||
if x < buf.area.width && y < buf.area.height {
|
||||
callback(buf.get_mut(x, y), col, row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
159
crates/tek_core/src/tui/tui_colors.rs
Normal file
159
crates/tek_core/src/tui/tui_colors.rs
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
use crate::*;
|
||||
|
||||
pub struct FillBg(pub Color);
|
||||
|
||||
impl Render<Tui> for FillBg {
|
||||
fn render (&self, to: &mut Tui) -> Perhaps<Rect> {
|
||||
to.fill_bg(to.area, self.0);
|
||||
Ok(Some(to.area))
|
||||
}
|
||||
}
|
||||
|
||||
pub const COLOR_BG0: Color = Color::Rgb(30, 33, 36);
|
||||
pub const COLOR_BG1: Color = Color::Rgb(41, 46, 57);
|
||||
pub const COLOR_BG2: Color = Color::Rgb(46, 52, 64);
|
||||
pub const COLOR_BG3: Color = Color::Rgb(59, 66, 82);
|
||||
pub const COLOR_BG4: Color = Color::Rgb(67, 76, 94);
|
||||
pub const COLOR_BG5: Color = Color::Rgb(76, 86, 106);
|
||||
|
||||
pub trait Theme {
|
||||
const BG0: Color;
|
||||
const BG1: Color;
|
||||
const BG2: Color;
|
||||
const BG3: Color;
|
||||
const BG4: Color;
|
||||
const RED: Color;
|
||||
const YELLOW: Color;
|
||||
const GREEN: Color;
|
||||
|
||||
const PLAYING: Color;
|
||||
const SEPARATOR: Color;
|
||||
|
||||
fn bg_hier (focused: bool, entered: bool) -> Color {
|
||||
if focused && entered {
|
||||
Self::BG3
|
||||
} else if focused {
|
||||
Self::BG2
|
||||
} else {
|
||||
Self::BG1
|
||||
}
|
||||
}
|
||||
|
||||
fn bg_hi (focused: bool, entered: bool) -> Color {
|
||||
if focused && entered {
|
||||
Self::BG2
|
||||
} else if focused {
|
||||
Self::BG1
|
||||
} else {
|
||||
Self::BG0
|
||||
}
|
||||
}
|
||||
|
||||
fn bg_lo (focused: bool, entered: bool) -> Color {
|
||||
if focused && entered {
|
||||
Self::BG1
|
||||
} else if focused {
|
||||
Self::BG0
|
||||
} else {
|
||||
Color::Reset
|
||||
}
|
||||
}
|
||||
|
||||
fn style_hi (focused: bool, highlight: bool) -> Style {
|
||||
if highlight && focused {
|
||||
Style::default().yellow().not_dim()
|
||||
} else if highlight {
|
||||
Style::default().yellow().dim()
|
||||
} else {
|
||||
Style::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Nord;
|
||||
|
||||
impl Theme for Nord {
|
||||
const BG0: Color = Color::Rgb(41, 46, 57);
|
||||
const BG1: Color = Color::Rgb(46, 52, 64);
|
||||
const BG2: Color = Color::Rgb(59, 66, 82);
|
||||
const BG3: Color = Color::Rgb(67, 76, 94);
|
||||
const BG4: Color = Color::Rgb(76, 86, 106);
|
||||
const RED: Color = Color::Rgb(191, 97, 106);
|
||||
const YELLOW: Color = Color::Rgb(235, 203, 139);
|
||||
const GREEN: Color = Color::Rgb(163, 190, 140);
|
||||
|
||||
const PLAYING: Color = Color::Rgb(60, 100, 50);
|
||||
const SEPARATOR: Color = Color::Rgb(0, 0, 0);
|
||||
}
|
||||
|
||||
pub const GRAY: Style = Style {
|
||||
fg: Some(Color::Gray),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::empty(),
|
||||
sub_modifier: Modifier::empty(),
|
||||
};
|
||||
|
||||
pub const GRAY_NOT_DIM: Style = Style {
|
||||
fg: Some(Color::Gray),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::empty(),
|
||||
sub_modifier: Modifier::DIM,
|
||||
};
|
||||
|
||||
pub const DIM: Style = Style {
|
||||
fg: None,
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::DIM,
|
||||
sub_modifier: Modifier::empty(),
|
||||
};
|
||||
|
||||
pub const GRAY_DIM: Style = Style {
|
||||
fg: Some(Color::Gray),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::DIM,
|
||||
sub_modifier: Modifier::empty(),
|
||||
};
|
||||
|
||||
pub const WHITE_NOT_DIM_BOLD: Style = Style {
|
||||
fg: Some(Color::White),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::BOLD,
|
||||
sub_modifier: Modifier::DIM,
|
||||
};
|
||||
|
||||
pub const GRAY_NOT_DIM_BOLD: Style = Style {
|
||||
fg: Some(Color::Gray),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::BOLD,
|
||||
sub_modifier: Modifier::DIM,
|
||||
};
|
||||
|
||||
pub const NOT_DIM: Style = Style {
|
||||
fg: None,
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::empty(),
|
||||
sub_modifier: Modifier::DIM,
|
||||
};
|
||||
|
||||
pub const NOT_DIM_GREEN: Style = Style {
|
||||
fg: Some(Color::Rgb(96, 255, 32)),
|
||||
bg: Some(COLOR_BG1),
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::empty(),
|
||||
sub_modifier: Modifier::DIM,
|
||||
};
|
||||
|
||||
pub const NOT_DIM_BOLD: Style = Style {
|
||||
fg: None,
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::BOLD,
|
||||
sub_modifier: Modifier::DIM,
|
||||
};
|
||||
81
crates/tek_core/src/tui/tui_layout.rs
Normal file
81
crates/tek_core/src/tui/tui_layout.rs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
use crate::*;
|
||||
|
||||
impl<R: Render<Tui>> Render<Tui> for (Offset<u16>, R) {
|
||||
fn render (&self, engine: &mut Tui) -> Perhaps<Rect> {
|
||||
self.1.render(engine.alter_area(|x, y, width, height|(
|
||||
x + self.0.0,
|
||||
y + self.0.1,
|
||||
width.saturating_sub(self.0.0),
|
||||
height.saturating_sub(self.0.1),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Render<Tui>> Render<Tui> for (Inset<u16>, R) {
|
||||
fn render (&self, engine: &mut Tui) -> Perhaps<Rect> {
|
||||
self.1.render(engine.alter_area(|x, y, width, height|(
|
||||
x + self.0.0,
|
||||
y + self.0.1,
|
||||
width.saturating_sub(self.0.0 + self.0.2),
|
||||
height.saturating_sub(self.0.1 + self.0.3),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Render<Tui>> Render<Tui> for (Outset<u16>, R) {
|
||||
fn render (&self, engine: &mut Tui) -> Perhaps<Rect> {
|
||||
self.1.render(engine.alter_area(|x, y, width, height|(
|
||||
x.saturating_sub(self.0.0),
|
||||
y.saturating_sub(self.0.1),
|
||||
width + self.0.0 + self.0.2,
|
||||
height + self.0.1 + self.0.3,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Render<Tui> for Layered<'a, Tui> {
|
||||
fn render (&self, to: &mut Tui) -> Perhaps<Rect> {
|
||||
let area = to.area();
|
||||
for layer in self.0.0.iter() {
|
||||
layer.render(to)?;
|
||||
}
|
||||
Ok(Some(area))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Render<Tui> for Split<'a, Tui> {
|
||||
fn render (&self, to: &mut Tui) -> Perhaps<Rect> {
|
||||
Ok(Some(self.render_areas(to)?.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Split<'a, Tui> {
|
||||
pub fn render_areas (&self, to: &mut Tui) -> Usually<(Rect, Vec<Rect>)> {
|
||||
//Ok((Rect::default(), vec![]))
|
||||
let Rect { mut x, mut y, mut width, mut height } = to.area();
|
||||
let mut areas = vec![];
|
||||
for (index, item) in self.items.0.iter().enumerate() {
|
||||
if width == 0 || height == 0 {
|
||||
break
|
||||
}
|
||||
if let Some(result) = item.render(to.with_area(x, y, width, height))? {
|
||||
match self.direction {
|
||||
Direction::Down => {
|
||||
y += result.height;
|
||||
height = height.saturating_sub(result.height);
|
||||
},
|
||||
Direction::Right => {
|
||||
x += result.width;
|
||||
width = width.saturating_sub(result.width);
|
||||
},
|
||||
_ => unimplemented!()
|
||||
};
|
||||
areas.push(result);
|
||||
if self.focus == Some(index) {
|
||||
Corners(Style::default().green().not_dim()).draw(to.with_rect(result))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok((to.area, areas))
|
||||
}
|
||||
}
|
||||
|
|
@ -21,9 +21,8 @@ submod! {
|
|||
sequencer_handle
|
||||
sequencer_view
|
||||
transport
|
||||
transport_focus
|
||||
transport_handle
|
||||
transport_render
|
||||
transport_view
|
||||
}
|
||||
|
||||
pubmod! {
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
use crate::*;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
/// Which section of the transport is focused
|
||||
pub enum TransportFocus { BPM, Quant, Sync }
|
||||
|
||||
impl TransportFocus {
|
||||
pub fn prev (&mut self) {
|
||||
*self = match self {
|
||||
Self::BPM => Self::Sync,
|
||||
Self::Quant => Self::BPM,
|
||||
Self::Sync => Self::Quant,
|
||||
}
|
||||
}
|
||||
pub fn next (&mut self) {
|
||||
*self = match self {
|
||||
Self::BPM => Self::Quant,
|
||||
Self::Quant => Self::Sync,
|
||||
Self::Sync => Self::BPM,
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue