mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
wip: add TuiStyle, unifying Modal
This commit is contained in:
parent
7c555848e4
commit
29f11b5977
6 changed files with 467 additions and 434 deletions
|
|
@ -144,6 +144,83 @@ impl Input<Tui> for TuiInput {
|
|||
self.exited.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
impl TuiInput {
|
||||
pub fn handle_keymap <T> (&self, state: &mut T, keymap: &KeyMap<T>) -> Usually<bool> {
|
||||
match self.event() {
|
||||
TuiEvent::Input(crossterm::event::Event::Key(event)) => {
|
||||
for (code, modifiers, _, _, command) in keymap.iter() {
|
||||
if *code == event.code && modifiers.bits() == event.modifiers.bits() {
|
||||
return command(state)
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
};
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
pub type KeyHandler<T> = &'static dyn Fn(&mut T)->Usually<bool>;
|
||||
pub type KeyBinding<T> = (KeyCode, KeyModifiers, &'static str, &'static str, KeyHandler<T>);
|
||||
pub type KeyMap<T> = [KeyBinding<T>];
|
||||
/// Define a keymap
|
||||
#[macro_export] macro_rules! keymap {
|
||||
($T:ty { $([$k:ident $(($char:literal))?, $m:ident, $n: literal, $d: literal, $f: expr]),* $(,)? }) => {
|
||||
&[
|
||||
$((KeyCode::$k $(($char))?, KeyModifiers::$m, $n, $d, &$f as KeyHandler<$T>)),*
|
||||
] as &'static [KeyBinding<$T>]
|
||||
}
|
||||
}
|
||||
/// Define a key in a keymap
|
||||
#[macro_export] macro_rules! map_key {
|
||||
($k:ident $(($char:literal))?, $m:ident, $n: literal, $d: literal, $f: expr) => {
|
||||
(KeyCode::$k $(($char))?, KeyModifiers::$m, $n, $d, &$f as &dyn Fn()->Usually<bool>)
|
||||
}
|
||||
}
|
||||
/// Shorthand for key match statement
|
||||
#[macro_export] macro_rules! match_key {
|
||||
($event:expr, {
|
||||
$($key:pat=>$block:expr),* $(,)?
|
||||
}) => {
|
||||
match $event {
|
||||
$(crossterm::event::Event::Key(crossterm::event::KeyEvent {
|
||||
code: $key,
|
||||
modifiers: crossterm::event::KeyModifiers::NONE,
|
||||
kind: crossterm::event::KeyEventKind::Press,
|
||||
state: crossterm::event::KeyEventState::NONE
|
||||
}) => {
|
||||
$block
|
||||
})*
|
||||
_ => Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Define key pattern in key match statement
|
||||
#[macro_export] macro_rules! key {
|
||||
($code:pat) => {
|
||||
TuiEvent::Input(crossterm::event::Event::Key(crossterm::event::KeyEvent {
|
||||
code: $code,
|
||||
modifiers: crossterm::event::KeyModifiers::NONE,
|
||||
kind: crossterm::event::KeyEventKind::Press,
|
||||
state: crossterm::event::KeyEventState::NONE
|
||||
}))
|
||||
};
|
||||
(Ctrl-$code:pat) => {
|
||||
TuiEvent::Input(crossterm::event::Event::Key(crossterm::event::KeyEvent {
|
||||
code: $code,
|
||||
modifiers: crossterm::event::KeyModifiers::CONTROL,
|
||||
kind: crossterm::event::KeyEventKind::Press,
|
||||
state: crossterm::event::KeyEventState::NONE
|
||||
}))
|
||||
};
|
||||
(Alt-$code:pat) => {
|
||||
TuiEvent::Input(crossterm::event::Event::Key(crossterm::event::KeyEvent {
|
||||
code: $code,
|
||||
modifiers: crossterm::event::KeyModifiers::ALT,
|
||||
kind: crossterm::event::KeyEventKind::Press,
|
||||
state: crossterm::event::KeyEventState::NONE
|
||||
}))
|
||||
}
|
||||
}
|
||||
pub struct TuiOutput {
|
||||
pub buffer: Buffer,
|
||||
pub area: [u16;4],
|
||||
|
|
@ -291,8 +368,19 @@ impl Widget for &str {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Styled<T: Widget<Engine = Tui>>(pub Option<Style>, pub T);
|
||||
impl<T: Widget<Engine = Tui>> Widget for DebugOverlay<Tui, T> {
|
||||
type Engine = Tui;
|
||||
fn layout (&self, to: [u16;2]) -> Perhaps<[u16;2]> {
|
||||
self.0.layout(to)
|
||||
}
|
||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
||||
let [x, y, w, h] = to.area();
|
||||
self.0.render(to)?;
|
||||
Ok(to.blit(&format!("{w}x{h}+{x}+{y}"), x, y, Some(Style::default().green())))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Styled<T: Widget<Engine = Tui>>(pub Option<Style>, pub T);
|
||||
impl Widget for Styled<&str> {
|
||||
type Engine = Tui;
|
||||
fn layout (&self, _: [u16;2]) -> Perhaps<[u16;2]> {
|
||||
|
|
@ -306,8 +394,49 @@ impl Widget for Styled<&str> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Background(pub Color);
|
||||
#[macro_export] macro_rules! tui_style {
|
||||
($NAME:ident = $fg:expr, $bg:expr, $ul:expr, $add:expr, $sub:expr) => {
|
||||
pub const $NAME: Style = Style {
|
||||
fg: $fg, bg: $bg, underline_color: $ul, add_modifier: $add, sub_modifier: $sub,
|
||||
};
|
||||
}
|
||||
}
|
||||
pub trait TuiStyle: Widget<Engine = Tui> + Sized {
|
||||
fn foreground (self, color: Color) -> impl Widget<Engine = Tui> {
|
||||
Layers::new(move |add|{ add(&Foreground(color))?; add(&self) })
|
||||
}
|
||||
fn background (self, color: Color) -> impl Widget<Engine = Tui> {
|
||||
Layers::new(move |add|{ add(&Background(color))?; add(&self) })
|
||||
}
|
||||
}
|
||||
impl<W: Widget<Engine = Tui>> TuiStyle for W {}
|
||||
impl<M: Widget<Engine = Tui>, W: Widget<Engine = Tui>> Widget for ModalHost<Tui, M, W> {
|
||||
type Engine = Tui;
|
||||
fn layout (&self, to: [u16;2]) -> Perhaps<[u16;2]> {
|
||||
Ok(Some(to))
|
||||
}
|
||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
||||
self.2.render(to)?;
|
||||
if self.0 {
|
||||
to.fill_bg(to.area(), COLOR_BG1);
|
||||
to.fill_fg(to.area(), COLOR_BG2);
|
||||
self.1.render(to)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Foreground(pub Color);
|
||||
impl Widget for Foreground {
|
||||
type Engine = Tui;
|
||||
fn layout (&self, _: [u16;2]) -> Perhaps<[u16;2]> {
|
||||
Ok(Some([0,0]))
|
||||
}
|
||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
||||
Ok(to.fill_fg(to.area(), self.0))
|
||||
}
|
||||
}
|
||||
pub struct Background(pub Color);
|
||||
impl Widget for Background {
|
||||
type Engine = Tui;
|
||||
fn layout (&self, _: [u16;2]) -> Perhaps<[u16;2]> {
|
||||
|
|
@ -318,34 +447,7 @@ impl Widget for Background {
|
|||
}
|
||||
}
|
||||
|
||||
//impl<F> Widget for Layers<Tui, F>
|
||||
//where
|
||||
//F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget<Engine = Tui>)->Usually<()>)->Usually<()>
|
||||
//{
|
||||
//type Engine = Tui;
|
||||
//fn layout (&self, area: [u16;2]) -> Perhaps<[u16;2]> {
|
||||
//let mut w = 0;
|
||||
//let mut h = 0;
|
||||
//(self.0)(&mut |layer| {
|
||||
//if let Some(layer_area) = layer.layout(area)? {
|
||||
//w = w.max(layer_area.w());
|
||||
//h = h.max(layer_area.h());
|
||||
//}
|
||||
//Ok(())
|
||||
//})?;
|
||||
//Ok(Some([w, h]))
|
||||
//}
|
||||
//fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
||||
//if let Some(size) = self.layout(to.area().wh())? {
|
||||
//(self.0)(&mut |layer|to.render_in(to.area().clip(size), &layer))
|
||||
//} else {
|
||||
//Ok(())
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
|
||||
pub struct Border<S: BorderStyle>(pub S);
|
||||
|
||||
impl<S: BorderStyle> Widget for Border<S> {
|
||||
type Engine = Tui;
|
||||
fn layout (&self, _: [u16;2]) -> Perhaps<[u16;2]> {
|
||||
|
|
@ -380,42 +482,25 @@ pub trait BorderStyle: Send + Sync {
|
|||
const S: &'static str = "";
|
||||
const SW: &'static str = "";
|
||||
const W: &'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 TuiOutput) -> Usually<()> {
|
||||
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 TuiOutput
|
||||
) -> Usually<()> {
|
||||
self.draw_horizontal(to, None)?;
|
||||
self.draw_vertical(to, None)?;
|
||||
self.draw_corners(to, None)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_horizontal (&self, to: &mut TuiOutput, style: Option<Style>) -> Usually<[u16;4]> {
|
||||
#[inline] fn draw_horizontal (
|
||||
&self, to: &mut TuiOutput, 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();
|
||||
|
|
@ -425,17 +510,19 @@ pub trait BorderStyle: Send + Sync {
|
|||
}
|
||||
Ok(area)
|
||||
}
|
||||
#[inline]
|
||||
fn draw_north (&self, to: &mut TuiOutput, x: u16, y: u16, style: Option<Style>) -> () {
|
||||
#[inline] fn draw_north (
|
||||
&self, to: &mut TuiOutput, x: u16, y: u16, style: Option<Style>
|
||||
) -> () {
|
||||
to.blit(&Self::N, x, y, style)
|
||||
}
|
||||
#[inline]
|
||||
fn draw_south (&self, to: &mut TuiOutput, x: u16, y: u16, style: Option<Style>) -> () {
|
||||
#[inline] fn draw_south (
|
||||
&self, to: &mut TuiOutput, x: u16, y: u16, style: Option<Style>
|
||||
) -> () {
|
||||
to.blit(&Self::S, x, y, style)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_vertical (&self, to: &mut TuiOutput, style: Option<Style>) -> Usually<[u16;4]> {
|
||||
#[inline] fn draw_vertical (
|
||||
&self, to: &mut TuiOutput, 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();
|
||||
|
|
@ -445,9 +532,9 @@ pub trait BorderStyle: Send + Sync {
|
|||
}
|
||||
Ok(area)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_corners (&self, to: &mut TuiOutput, style: Option<Style>) -> Usually<[u16;4]> {
|
||||
#[inline] fn draw_corners (
|
||||
&self, to: &mut TuiOutput, 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();
|
||||
|
|
@ -459,23 +546,10 @@ pub trait BorderStyle: Send + Sync {
|
|||
}
|
||||
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()
|
||||
}
|
||||
#[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 {
|
||||
|
|
@ -497,12 +571,8 @@ macro_rules! border {
|
|||
pub struct $T(pub Style);
|
||||
impl Widget for $T {
|
||||
type Engine = Tui;
|
||||
fn layout (&self, _: [u16;2]) -> Perhaps<[u16;2]> {
|
||||
Ok(Some([0,0]))
|
||||
}
|
||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
||||
self.draw(to)
|
||||
}
|
||||
fn layout (&self, _: [u16;2]) -> Perhaps<[u16;2]> { Ok(Some([0,0])) }
|
||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> { self.draw(to) }
|
||||
}
|
||||
)+}
|
||||
}
|
||||
|
|
@ -576,91 +646,14 @@ border! {
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! tui_style {
|
||||
($NAME:ident = $fg:expr, $bg:expr, $ul:expr, $add:expr, $sub:expr) => {
|
||||
pub const $NAME: Style = Style {
|
||||
fg: $fg,
|
||||
bg: $bg,
|
||||
underline_color: $ul,
|
||||
add_modifier: $add,
|
||||
sub_modifier: $sub,
|
||||
};
|
||||
}
|
||||
}
|
||||
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 const COLOR_RED: Color = Color::Rgb(191, 97, 106);
|
||||
pub const COLOR_YELLOW: Color = Color::Rgb(235, 203, 139);
|
||||
pub const COLOR_GREEN: Color = Color::Rgb(163, 190, 140);
|
||||
pub const COLOR_PLAYING: Color = Color::Rgb(60, 100, 50);
|
||||
pub const COLOR_SEPARATOR: Color = Color::Rgb(0, 0, 0);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue