mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 04:06:45 +01:00
cmdsys: scaffold menu
This commit is contained in:
parent
d3718f0b78
commit
1aaad23691
2 changed files with 134 additions and 169 deletions
|
|
@ -1,28 +1,7 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub trait Command<T>: Sized {
|
pub trait Command<S>: Sized {
|
||||||
fn run (&self, state: &mut T) -> Perhaps<Self>;
|
fn run (&self, state: &mut S) -> Perhaps<Self>;
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn key (code: KeyCode) -> KeyEvent {
|
|
||||||
KeyEvent {
|
|
||||||
code,
|
|
||||||
modifiers: KeyModifiers::NONE,
|
|
||||||
kind: crossterm::event::KeyEventKind::Press,
|
|
||||||
state: crossterm::event::KeyEventState::NONE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn ctrl (key: KeyEvent) -> KeyEvent {
|
|
||||||
KeyEvent { modifiers: key.modifiers.union(KeyModifiers::CONTROL), ..key }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn alt (key: KeyEvent) -> KeyEvent {
|
|
||||||
KeyEvent { modifiers: key.modifiers.union(KeyModifiers::ALT), ..key }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn shift (key: KeyEvent) -> KeyEvent {
|
|
||||||
KeyEvent { modifiers: key.modifiers.union(KeyModifiers::SHIFT), ..key }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HandleKey<C: Command<Self> + Clone + 'static>: Sized {
|
pub trait HandleKey<C: Command<Self> + Clone + 'static>: Sized {
|
||||||
|
|
@ -62,3 +41,23 @@ pub trait HandleKey<C: Command<Self> + Clone + 'static>: Sized {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Menu<E: Engine, S: Handle<E>, C: Command<S>> {
|
||||||
|
pub items: Vec<MenuItem<E, S, C>>,
|
||||||
|
pub index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine, S: Handle<E>, C: Command<S>> Menu<E, S, C> {
|
||||||
|
pub const fn item (command: C, name: &'static str, key: &'static str) -> MenuItem<E, S, C> {
|
||||||
|
MenuItem::Command(command, name, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum MenuItem<E: Engine, S: Handle<E>, C: Command<S>> {
|
||||||
|
/// Unused.
|
||||||
|
__(PhantomData<E>, PhantomData<S>),
|
||||||
|
/// A separator. Skip it.
|
||||||
|
Separator,
|
||||||
|
/// A menu item with command, description and hotkey.
|
||||||
|
Command(C, &'static str, &'static str)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
pub(crate) use ratatui::buffer::Cell;
|
pub(crate) use ratatui::buffer::Cell;
|
||||||
pub(crate) use crossterm::{ExecutableCommand};
|
pub(crate) use crossterm::{ExecutableCommand};
|
||||||
pub use crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers};
|
pub use crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState};
|
||||||
pub use ratatui::prelude::{Rect, Style, Color, Buffer};
|
pub use ratatui::prelude::{Rect, Style, Color, Buffer};
|
||||||
pub use ratatui::style::{Stylize, Modifier};
|
pub use ratatui::style::{Stylize, Modifier};
|
||||||
use ratatui::backend::{Backend, CrosstermBackend, ClearType};
|
use ratatui::backend::{Backend, CrosstermBackend, ClearType};
|
||||||
|
|
@ -133,23 +133,15 @@ impl Tui {
|
||||||
buffer
|
buffer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub struct TuiInput {
|
pub struct TuiInput { event: TuiEvent, exited: Arc<AtomicBool>, }
|
||||||
event: TuiEvent,
|
|
||||||
exited: Arc<AtomicBool>,
|
|
||||||
}
|
|
||||||
impl Input<Tui> for TuiInput {
|
impl Input<Tui> for TuiInput {
|
||||||
type Event = TuiEvent;
|
type Event = TuiEvent;
|
||||||
fn event (&self) -> &TuiEvent {
|
fn event (&self) -> &TuiEvent { &self.event }
|
||||||
&self.event
|
fn is_done (&self) -> bool { self.exited.fetch_and(true, Ordering::Relaxed) }
|
||||||
}
|
fn done (&self) { self.exited.store(true, Ordering::Relaxed); }
|
||||||
fn is_done (&self) -> bool {
|
|
||||||
self.exited.fetch_and(true, Ordering::Relaxed)
|
|
||||||
}
|
|
||||||
fn done (&self) {
|
|
||||||
self.exited.store(true, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl TuiInput {
|
impl TuiInput {
|
||||||
|
// TODO remove
|
||||||
pub fn handle_keymap <T> (&self, state: &mut T, keymap: &KeyMap<T>) -> Usually<bool> {
|
pub fn handle_keymap <T> (&self, state: &mut T, keymap: &KeyMap<T>) -> Usually<bool> {
|
||||||
match self.event() {
|
match self.event() {
|
||||||
TuiEvent::Input(crossterm::event::Event::Key(event)) => {
|
TuiEvent::Input(crossterm::event::Event::Key(event)) => {
|
||||||
|
|
@ -167,84 +159,10 @@ impl TuiInput {
|
||||||
pub type KeyHandler<T> = &'static dyn Fn(&mut T)->Usually<bool>;
|
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 KeyBinding<T> = (KeyCode, KeyModifiers, &'static str, &'static str, KeyHandler<T>);
|
||||||
pub type KeyMap<T> = [KeyBinding<T>];
|
pub type KeyMap<T> = [KeyBinding<T>];
|
||||||
/// Define a keymap
|
pub struct TuiOutput { pub buffer: Buffer, pub area: [u16;4] }
|
||||||
#[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
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
(Shift-$code:pat) => {
|
|
||||||
TuiEvent::Input(crossterm::event::Event::Key(crossterm::event::KeyEvent {
|
|
||||||
code: $code,
|
|
||||||
modifiers: crossterm::event::KeyModifiers::SHIFT,
|
|
||||||
kind: crossterm::event::KeyEventKind::Press,
|
|
||||||
state: crossterm::event::KeyEventState::NONE
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub struct TuiOutput {
|
|
||||||
pub buffer: Buffer,
|
|
||||||
pub area: [u16;4],
|
|
||||||
}
|
|
||||||
impl Output<Tui> for TuiOutput {
|
impl Output<Tui> for TuiOutput {
|
||||||
#[inline] fn area (&self) -> [u16;4] {
|
#[inline] fn area (&self) -> [u16;4] { self.area }
|
||||||
self.area
|
#[inline] fn area_mut (&mut self) -> &mut [u16;4] { &mut self.area }
|
||||||
}
|
|
||||||
#[inline] fn area_mut (&mut self) -> &mut [u16;4] {
|
|
||||||
&mut self.area
|
|
||||||
}
|
|
||||||
#[inline] fn render_in (&mut self,
|
#[inline] fn render_in (&mut self,
|
||||||
area: [u16;4],
|
area: [u16;4],
|
||||||
widget: &dyn Widget<Engine = Tui>
|
widget: &dyn Widget<Engine = Tui>
|
||||||
|
|
@ -322,14 +240,12 @@ pub enum TuiEvent {
|
||||||
// /// JACK notification
|
// /// JACK notification
|
||||||
// Jack(JackEvent)
|
// Jack(JackEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Area<u16> for Rect {
|
impl Area<u16> for Rect {
|
||||||
fn x (&self) -> u16 { self.x }
|
fn x (&self) -> u16 { self.x }
|
||||||
fn y (&self) -> u16 { self.y }
|
fn y (&self) -> u16 { self.y }
|
||||||
fn w (&self) -> u16 { self.width }
|
fn w (&self) -> u16 { self.width }
|
||||||
fn h (&self) -> u16 { self.height }
|
fn h (&self) -> u16 { self.height }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn half_block (lower: bool, upper: bool) -> Option<char> {
|
pub fn half_block (lower: bool, upper: bool) -> Option<char> {
|
||||||
match (lower, upper) {
|
match (lower, upper) {
|
||||||
(true, true) => Some('█'),
|
(true, true) => Some('█'),
|
||||||
|
|
@ -338,14 +254,12 @@ pub fn half_block (lower: bool, upper: bool) -> Option<char> {
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct BigBuffer {
|
pub struct BigBuffer {
|
||||||
pub width: usize,
|
pub width: usize,
|
||||||
pub height: usize,
|
pub height: usize,
|
||||||
pub content: Vec<Cell>
|
pub content: Vec<Cell>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BigBuffer {
|
impl BigBuffer {
|
||||||
pub fn new (width: usize, height: usize) -> Self {
|
pub fn new (width: usize, height: usize) -> Self {
|
||||||
Self { width, height, content: vec![Cell::default(); width*height] }
|
Self { width, height, content: vec![Cell::default(); width*height] }
|
||||||
|
|
@ -362,7 +276,6 @@ impl BigBuffer {
|
||||||
y * self.width + x
|
y * self.width + x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn buffer_update (buf: &mut Buffer, area: [u16;4], callback: &impl Fn(&mut Cell, u16, u16)) {
|
pub fn buffer_update (buf: &mut Buffer, area: [u16;4], callback: &impl Fn(&mut Cell, u16, u16)) {
|
||||||
for row in 0..area.h() {
|
for row in 0..area.h() {
|
||||||
let y = area.y() + row;
|
let y = area.y() + row;
|
||||||
|
|
@ -374,7 +287,6 @@ pub fn buffer_update (buf: &mut Buffer, area: [u16;4], callback: &impl Fn(&mut C
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for &str {
|
impl Widget for &str {
|
||||||
type Engine = Tui;
|
type Engine = Tui;
|
||||||
fn layout (&self, _: [u16;2]) -> Perhaps<[u16;2]> {
|
fn layout (&self, _: [u16;2]) -> Perhaps<[u16;2]> {
|
||||||
|
|
@ -399,7 +311,6 @@ impl Widget for String {
|
||||||
Ok(to.blit(&self, x, y, None))
|
Ok(to.blit(&self, x, y, None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Widget<Engine = Tui>> Widget for DebugOverlay<Tui, T> {
|
impl<T: Widget<Engine = Tui>> Widget for DebugOverlay<Tui, T> {
|
||||||
type Engine = Tui;
|
type Engine = Tui;
|
||||||
fn layout (&self, to: [u16;2]) -> Perhaps<[u16;2]> {
|
fn layout (&self, to: [u16;2]) -> Perhaps<[u16;2]> {
|
||||||
|
|
@ -411,7 +322,6 @@ impl<T: Widget<Engine = Tui>> Widget for DebugOverlay<Tui, T> {
|
||||||
Ok(to.blit(&format!("{w}x{h}+{x}+{y}"), x, y, Some(Style::default().green())))
|
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);
|
pub struct Styled<T: Widget<Engine = Tui>>(pub Option<Style>, pub T);
|
||||||
impl Widget for Styled<&str> {
|
impl Widget for Styled<&str> {
|
||||||
type Engine = Tui;
|
type Engine = Tui;
|
||||||
|
|
@ -425,7 +335,6 @@ impl Widget for Styled<&str> {
|
||||||
Ok(to.blit(&self.1, x, y, None))
|
Ok(to.blit(&self.1, x, y, None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait TuiStyle: Widget<Engine = Tui> + Sized {
|
pub trait TuiStyle: Widget<Engine = Tui> + Sized {
|
||||||
fn fg (self, color: Color) -> impl Widget<Engine = Tui> {
|
fn fg (self, color: Color) -> impl Widget<Engine = Tui> {
|
||||||
Layers::new(move |add|{ add(&Foreground(color))?; add(&self) })
|
Layers::new(move |add|{ add(&Foreground(color))?; add(&self) })
|
||||||
|
|
@ -441,38 +350,24 @@ pub trait TuiStyle: Widget<Engine = Tui> + Sized {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<W: Widget<Engine = Tui>> TuiStyle for W {}
|
impl<W: Widget<Engine = Tui>> TuiStyle for W {}
|
||||||
|
|
||||||
pub struct Bold(pub bool);
|
pub struct Bold(pub bool);
|
||||||
impl Widget for Bold {
|
impl Widget for Bold {
|
||||||
type Engine = Tui;
|
type Engine = Tui;
|
||||||
fn layout (&self, _: [u16;2]) -> Perhaps<[u16;2]> {
|
fn layout (&self, _: [u16;2]) -> Perhaps<[u16;2]> { Ok(Some([0,0])) }
|
||||||
Ok(Some([0,0]))
|
fn render (&self, to: &mut TuiOutput) -> Usually<()> { Ok(to.fill_bold(to.area(), self.0)) }
|
||||||
}
|
|
||||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
|
||||||
Ok(to.fill_bold(to.area(), self.0))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub struct Foreground(pub Color);
|
pub struct Foreground(pub Color);
|
||||||
impl Widget for Foreground {
|
impl Widget for Foreground {
|
||||||
type Engine = Tui;
|
type Engine = Tui;
|
||||||
fn layout (&self, _: [u16;2]) -> Perhaps<[u16;2]> {
|
fn layout (&self, _: [u16;2]) -> Perhaps<[u16;2]> { Ok(Some([0,0])) }
|
||||||
Ok(Some([0,0]))
|
fn render (&self, to: &mut TuiOutput) -> Usually<()> { Ok(to.fill_fg(to.area(), self.0)) }
|
||||||
}
|
|
||||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
|
||||||
Ok(to.fill_fg(to.area(), self.0))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub struct Background(pub Color);
|
pub struct Background(pub Color);
|
||||||
impl Widget for Background {
|
impl Widget for Background {
|
||||||
type Engine = Tui;
|
type Engine = Tui;
|
||||||
fn layout (&self, _: [u16;2]) -> Perhaps<[u16;2]> {
|
fn layout (&self, _: [u16;2]) -> Perhaps<[u16;2]> { Ok(Some([0,0])) }
|
||||||
Ok(Some([0,0]))
|
fn render (&self, to: &mut TuiOutput) -> Usually<()> { Ok(to.fill_bg(to.area(), self.0)) }
|
||||||
}
|
|
||||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
|
||||||
Ok(to.fill_bg(to.area(), self.0))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Border<S: BorderStyle>(pub S);
|
pub struct Border<S: BorderStyle>(pub S);
|
||||||
impl<S: BorderStyle> Widget for Border<S> {
|
impl<S: BorderStyle> Widget for Border<S> {
|
||||||
type Engine = Tui;
|
type Engine = Tui;
|
||||||
|
|
@ -506,7 +401,6 @@ impl<S: BorderStyle, W: Widget<Engine = Tui>> Content for Bordered<S, W> {
|
||||||
lay! { content.inset_xy(1, 1), Border(self.0) }.fill_xy()
|
lay! { content.inset_xy(1, 1), Border(self.0) }.fill_xy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait BorderStyle: Send + Sync + Copy {
|
pub trait BorderStyle: Send + Sync + Copy {
|
||||||
const NW: &'static str = "";
|
const NW: &'static str = "";
|
||||||
const N: &'static str = "";
|
const N: &'static str = "";
|
||||||
|
|
@ -585,7 +479,6 @@ pub trait BorderStyle: Send + Sync + Copy {
|
||||||
#[inline] fn style_vertical (&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_corners (&self) -> Option<Style> { self.style() }
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! border {
|
macro_rules! border {
|
||||||
($($T:ident {
|
($($T:ident {
|
||||||
$nw:literal $n:literal $ne:literal $w:literal $e:literal $sw:literal $s:literal $se:literal
|
$nw:literal $n:literal $ne:literal $w:literal $e:literal $sw:literal $s:literal $se:literal
|
||||||
|
|
@ -611,76 +504,63 @@ macro_rules! border {
|
||||||
}
|
}
|
||||||
)+}
|
)+}
|
||||||
}
|
}
|
||||||
|
|
||||||
border! {
|
border! {
|
||||||
Square {
|
Square {
|
||||||
"┌" "─" "┐"
|
"┌" "─" "┐"
|
||||||
"│" "│"
|
"│" "│"
|
||||||
"└" "─" "┘"
|
"└" "─" "┘" fn style (&self) -> Option<Style> { Some(self.0) }
|
||||||
fn style (&self) -> Option<Style> { Some(self.0) }
|
|
||||||
},
|
},
|
||||||
SquareBold {
|
SquareBold {
|
||||||
"┏" "━" "┓"
|
"┏" "━" "┓"
|
||||||
"┃" "┃"
|
"┃" "┃"
|
||||||
"┗" "━" "┛"
|
"┗" "━" "┛" fn style (&self) -> Option<Style> { Some(self.0) }
|
||||||
fn style (&self) -> Option<Style> { Some(self.0) }
|
|
||||||
},
|
},
|
||||||
Tab {
|
Tab {
|
||||||
"╭" "─" "╮"
|
"╭" "─" "╮"
|
||||||
"│" "│"
|
"│" "│"
|
||||||
"│" " " "│"
|
"│" " " "│" fn style (&self) -> Option<Style> { Some(self.0) }
|
||||||
fn style (&self) -> Option<Style> { Some(self.0) }
|
|
||||||
},
|
},
|
||||||
Lozenge {
|
Lozenge {
|
||||||
"╭" "─" "╮"
|
"╭" "─" "╮"
|
||||||
"│" "│"
|
"│" "│"
|
||||||
"╰" "─" "╯"
|
"╰" "─" "╯" fn style (&self) -> Option<Style> { Some(self.0) }
|
||||||
fn style (&self) -> Option<Style> { Some(self.0) }
|
|
||||||
},
|
},
|
||||||
Brace {
|
Brace {
|
||||||
"╭" "" "╮"
|
"╭" "" "╮"
|
||||||
"│" "│"
|
"│" "│"
|
||||||
"╰" "" "╯"
|
"╰" "" "╯" fn style (&self) -> Option<Style> { Some(self.0) }
|
||||||
fn style (&self) -> Option<Style> { Some(self.0) }
|
|
||||||
},
|
},
|
||||||
LozengeDotted {
|
LozengeDotted {
|
||||||
"╭" "┅" "╮"
|
"╭" "┅" "╮"
|
||||||
"┇" "┇"
|
"┇" "┇"
|
||||||
"╰" "┅" "╯"
|
"╰" "┅" "╯" fn style (&self) -> Option<Style> { Some(self.0) }
|
||||||
fn style (&self) -> Option<Style> { Some(self.0) }
|
|
||||||
},
|
},
|
||||||
Quarter {
|
Quarter {
|
||||||
"▎" "▔" "🮇"
|
"▎" "▔" "🮇"
|
||||||
"▎" "🮇"
|
"▎" "🮇"
|
||||||
"▎" "▁" "🮇"
|
"▎" "▁" "🮇" fn style (&self) -> Option<Style> { Some(self.0) }
|
||||||
fn style (&self) -> Option<Style> { Some(self.0) }
|
|
||||||
},
|
},
|
||||||
QuarterV {
|
QuarterV {
|
||||||
"▎" "" "🮇"
|
"▎" "" "🮇"
|
||||||
"▎" "🮇"
|
"▎" "🮇"
|
||||||
"▎" "" "🮇"
|
"▎" "" "🮇" fn style (&self) -> Option<Style> { Some(self.0) }
|
||||||
fn style (&self) -> Option<Style> { Some(self.0) }
|
|
||||||
},
|
},
|
||||||
Chamfer {
|
Chamfer {
|
||||||
"🭂" "▔" "🭍"
|
"🭂" "▔" "🭍"
|
||||||
"▎" "🮇"
|
"▎" "🮇"
|
||||||
"🭓" "▁" "🭞"
|
"🭓" "▁" "🭞" fn style (&self) -> Option<Style> { Some(self.0) }
|
||||||
fn style (&self) -> Option<Style> { Some(self.0) }
|
|
||||||
},
|
},
|
||||||
Corners {
|
Corners {
|
||||||
"🬆" "" "🬊" // 🬴 🬸
|
"🬆" "" "🬊" // 🬴 🬸
|
||||||
"" ""
|
"" ""
|
||||||
"🬱" "" "🬵"
|
"🬱" "" "🬵" fn style (&self) -> Option<Style> { Some(self.0) }
|
||||||
fn style (&self) -> Option<Style> { Some(self.0) }
|
|
||||||
},
|
},
|
||||||
CornersTall {
|
CornersTall {
|
||||||
"🭽" "" "🭾"
|
"🭽" "" "🭾"
|
||||||
"" ""
|
"" ""
|
||||||
"🭼" "" "🭿"
|
"🭼" "" "🭿" fn style (&self) -> Option<Style> { Some(self.0) }
|
||||||
fn style (&self) -> Option<Style> { Some(self.0) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const CORNERS: CornersTall = CornersTall(Style {
|
pub const CORNERS: CornersTall = CornersTall(Style {
|
||||||
fg: Some(Color::Rgb(96, 255, 32)),
|
fg: Some(Color::Rgb(96, 255, 32)),
|
||||||
bg: None,
|
bg: None,
|
||||||
|
|
@ -688,3 +568,89 @@ pub const CORNERS: CornersTall = CornersTall(Style {
|
||||||
add_modifier: Modifier::empty(),
|
add_modifier: Modifier::empty(),
|
||||||
sub_modifier: Modifier::DIM
|
sub_modifier: Modifier::DIM
|
||||||
});
|
});
|
||||||
|
/// Define a key
|
||||||
|
pub const fn key (code: KeyCode) -> KeyEvent {
|
||||||
|
let modifiers = KeyModifiers::NONE;
|
||||||
|
let kind = KeyEventKind::Press;
|
||||||
|
let state = KeyEventState::NONE;
|
||||||
|
KeyEvent { code, modifiers, kind, state }
|
||||||
|
}
|
||||||
|
/// Add Ctrl modifier to key
|
||||||
|
pub const fn ctrl (key: KeyEvent) -> KeyEvent {
|
||||||
|
KeyEvent { modifiers: key.modifiers.union(KeyModifiers::CONTROL), ..key }
|
||||||
|
}
|
||||||
|
/// Add Alt modifier to key
|
||||||
|
pub const fn alt (key: KeyEvent) -> KeyEvent {
|
||||||
|
KeyEvent { modifiers: key.modifiers.union(KeyModifiers::ALT), ..key }
|
||||||
|
}
|
||||||
|
/// Add Shift modifier to key
|
||||||
|
pub const fn shift (key: KeyEvent) -> KeyEvent {
|
||||||
|
KeyEvent { modifiers: key.modifiers.union(KeyModifiers::SHIFT), ..key }
|
||||||
|
}
|
||||||
|
/// 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
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
(Shift-$code:pat) => {
|
||||||
|
TuiEvent::Input(crossterm::event::Event::Key(crossterm::event::KeyEvent {
|
||||||
|
code: $code,
|
||||||
|
modifiers: crossterm::event::KeyModifiers::SHIFT,
|
||||||
|
kind: crossterm::event::KeyEventKind::Press,
|
||||||
|
state: crossterm::event::KeyEventState::NONE
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue