mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2026-09-18 05:16:44 +02:00
This commit is contained in:
parent
cf67185ffd
commit
799e497622
10 changed files with 456 additions and 460 deletions
246
src/term.rs
246
src/term.rs
|
|
@ -32,7 +32,6 @@ mod phat; pub use self::phat::*;
|
|||
mod repeat; pub use self::repeat::*;
|
||||
mod scroll; pub use self::scroll::*;
|
||||
|
||||
#[cfg(feature = "term")]
|
||||
#[macro_export] macro_rules! tui_app {
|
||||
($Struct:ident { $($fields:tt)* }) => {
|
||||
#[dizzle::namespace(bool)]
|
||||
|
|
@ -47,18 +46,19 @@ mod scroll; pub use self::scroll::*;
|
|||
}
|
||||
|
||||
/// Implement standard [main] entrypoint for TUI apps.
|
||||
#[cfg(feature = "term")]
|
||||
#[macro_export] macro_rules! tui_main {
|
||||
($state:expr) => {
|
||||
pub fn main () -> Usually<()> {
|
||||
tengri::Tui::setup_panic();
|
||||
tengri::Tui::run_main(::std::sync::Arc::new(::std::sync::RwLock::new($state)))
|
||||
Exit::run(|exit|tengri::Tui::run_main(
|
||||
exit,
|
||||
::std::sync::Arc::new(::std::sync::RwLock::new($state))
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable TUI output for state struct.
|
||||
#[cfg(feature = "term")]
|
||||
#[macro_export] macro_rules! tui_view {
|
||||
($self:ident: $State:ty $body:block) => {
|
||||
impl tengri::View<Tui> for $State {
|
||||
|
|
@ -68,7 +68,6 @@ mod scroll; pub use self::scroll::*;
|
|||
}
|
||||
|
||||
/// Enable TUI keyboard input for main state struct.
|
||||
#[cfg(feature = "term")]
|
||||
#[macro_export] macro_rules! tui_keys {
|
||||
($self:ident:$State:ty,$input:ident $($body:tt)+) => {
|
||||
impl dizzle::Apply<TuiEvent, Usually<()>> for $State {
|
||||
|
|
@ -79,12 +78,14 @@ mod scroll; pub use self::scroll::*;
|
|||
|
||||
/// Terminal output.
|
||||
pub enum Tui {
|
||||
/// Draw mode: updates contained [Buffer].
|
||||
Draw (
|
||||
/// Ratatui buffer; area is screen size
|
||||
Buffer,
|
||||
/// Current draw area
|
||||
XYWH<u16>
|
||||
),
|
||||
/// Discard mode: only computes sizes
|
||||
Layout (
|
||||
/// Draw area; no buffer
|
||||
XYWH<u16>
|
||||
|
|
@ -96,6 +97,16 @@ impl Screen for Tui {
|
|||
fn area (&self) -> XYWH<Self::Unit> {
|
||||
self.into()
|
||||
}
|
||||
fn size (
|
||||
&mut self, area: impl Into<Option<XYWH<u16>>>, draw: impl Draw<Self>,
|
||||
) -> Perhaps<XYWH<Self::Unit>> {
|
||||
Self::Layout(self.area()).clip(area, &|to|draw.draw(to))
|
||||
}
|
||||
fn draw (
|
||||
&mut self, area: impl Into<Option<XYWH<u16>>>, draw: impl Draw<Tui>
|
||||
) -> Perhaps<XYWH<Self::Unit>> {
|
||||
self.clip(area, &|to|draw.draw(to))
|
||||
}
|
||||
fn clip <T> (
|
||||
&mut self, area: impl Into<Option<XYWH<u16>>>, draw: &impl Fn(&mut Self)->T,
|
||||
) -> T {
|
||||
|
|
@ -107,30 +118,6 @@ impl Screen for Tui {
|
|||
*self.area_mut() = prev;
|
||||
result
|
||||
}
|
||||
fn size (
|
||||
&mut self, area: impl Into<Option<XYWH<u16>>>, draw: impl Draw<Self>,
|
||||
) -> Perhaps<XYWH<Self::Unit>> {
|
||||
Self::Layout(self.area()).clip(area, &|to|draw.draw(to))
|
||||
}
|
||||
fn draw (
|
||||
&mut self, area: impl Into<Option<XYWH<u16>>>, draw: impl Draw<Tui>
|
||||
) -> Perhaps<XYWH<Self::Unit>> {
|
||||
self.clip(area, &|to|draw.draw(to))
|
||||
}
|
||||
|
||||
//fn draw (
|
||||
//&mut self,
|
||||
//area: impl Into<Option<XYWH<u16>>>,
|
||||
//draw: impl Draw<Self>
|
||||
//) -> Perhaps<XYWH<Self::Unit>> {
|
||||
//let prev = self.area();
|
||||
//if let Some(area) = area.into() {
|
||||
//*self.area_mut() = area.into();
|
||||
//}
|
||||
//let result = draw.draw(self);
|
||||
//*self.area_mut() = prev;
|
||||
//result
|
||||
//}
|
||||
}
|
||||
|
||||
impl Tui {
|
||||
|
|
@ -316,33 +303,6 @@ impl Tui {
|
|||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "text")]
|
||||
/// Write a line of text
|
||||
///
|
||||
/// TODO: do a paragraph (handle newlines)
|
||||
pub fn text (&mut self, text: &impl AsRef<str>, x0: u16, y: u16, max_width: u16)
|
||||
-> Perhaps<XYWH<u16>>
|
||||
{
|
||||
let text = text.as_ref();
|
||||
let mut string_width: u16 = 0;
|
||||
for character in text.chars() {
|
||||
let x = x0 + string_width;
|
||||
let character_width = character.width().unwrap_or(0) as u16;
|
||||
string_width += character_width;
|
||||
if string_width > max_width {
|
||||
break
|
||||
}
|
||||
if let Self::Draw(buffer, ..) = self {
|
||||
if let Some(cell) = buffer.cell_mut(ratatui::prelude::Position { x, y }) {
|
||||
cell.set_char(character);
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Some(XYWH(x0, y, string_width, 1)))
|
||||
}
|
||||
|
||||
pub fn buffer (&mut self) -> Option<&mut Buffer> {
|
||||
if let Tui::Draw(buffer, _) = self {
|
||||
Some(buffer)
|
||||
|
|
@ -416,67 +376,7 @@ pub const fn fill_char (c: char) -> impl Draw<Tui> {
|
|||
}))))
|
||||
}
|
||||
|
||||
#[cfg(feature = "text")] impl_draw!(|self: String, to: Tui|{
|
||||
self.as_str().draw(to)
|
||||
});
|
||||
|
||||
#[cfg(feature = "text")] impl_draw!(|self: std::sync::Arc<str>, to: Tui|{
|
||||
self.as_ref().draw(to)
|
||||
});
|
||||
|
||||
#[cfg(feature = "text")] impl_draw!(<T: AsRef<str>,>|self: TrimString<T>, to: Tui|{
|
||||
self.as_ref().draw(to)
|
||||
});
|
||||
|
||||
#[cfg(feature = "text")]
|
||||
impl Draw<Tui> for &str {
|
||||
fn draw (&self, to: &mut Tui) -> Drawn<u16> {
|
||||
let XYWH(x, y, w, h) = to.area();
|
||||
let mut max_w = 0u16;
|
||||
let mut max_h = 0u16;
|
||||
for (index, line) in self.split("\n").enumerate() {
|
||||
max_h += 1;
|
||||
max_w = max_w.max(line.len() as u16);
|
||||
let _ = to.text(&line, x, y + index as u16, width_chars_max(w, line) as u16)?;
|
||||
}
|
||||
Ok(Some(XYWH(x, y, w.min(max_w), h.min(max_h))))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "text")]
|
||||
impl<'t, T: AsRef<str>> Draw<Tui> for TrimStr<'_, T> {
|
||||
fn draw (&self, to: &mut Tui) -> Drawn<u16> {
|
||||
let text = self.1.as_ref();
|
||||
let area = layout_text_u16(text, to.area())?.unwrap();
|
||||
let XYWH(x, y, w, ..) = to.area();
|
||||
let mut width: u16 = 1;
|
||||
let mut chars = text.chars();
|
||||
while let Some(c) = chars.next() {
|
||||
if width > self.0 || width > w {
|
||||
break
|
||||
}
|
||||
to.set_char(x + width - 1, y, c);
|
||||
width += c.width().unwrap_or(0) as u16;
|
||||
}
|
||||
let XYWH(x, y, w, ..) = XYWH(
|
||||
to.x(), to.y(), to.w().min(self.0).min(self.1.as_ref().width() as u16), to.h()
|
||||
);
|
||||
to.text(&self.as_ref(), x, y, w)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "text")]
|
||||
fn layout_text_u16 (text: &str, area: XYWH<u16>) -> Perhaps<XYWH<u16>> {
|
||||
let XYWH(x, y, w, h) = area;
|
||||
let mut max_w = 0u16;
|
||||
let mut max_h = 0u16;
|
||||
for line in text.split("\n") {
|
||||
max_h += 1;
|
||||
max_w = max_w.max(line.len() as u16);
|
||||
}
|
||||
Ok(Some(XYWH(x, y, w.min(max_w), h.min(max_h))))
|
||||
}
|
||||
|
||||
/// FIXME: Don't use `format!` but implement number blitting
|
||||
pub struct ShowSize;
|
||||
|
||||
impl Draw<Tui> for ShowSize {
|
||||
|
|
@ -500,16 +400,116 @@ impl<T: Draw<Tui>> Draw<Tui> for ShowSizeOf<T> {
|
|||
|
||||
#[cfg(feature = "eval")] fn_kw_layout_tui!(kw_tui_text |_state, output, expr| {
|
||||
Ok(matches!(expr.head()?, Some("text")).then(||{
|
||||
if let Some(src) = expr.tail().src()? {
|
||||
src.draw(output)
|
||||
if let Some(src) = expr.tail().src()? && src.len() > 0 {
|
||||
(&src[1..]).draw(output)
|
||||
} else {
|
||||
return Ok(None)
|
||||
}
|
||||
}).transpose()?.flatten())
|
||||
});
|
||||
|
||||
#[cfg(feature = "text")] #[cfg(test)] #[test] fn test_layout_text_u16 () -> Usually<()> {
|
||||
assert_eq!(layout_text_u16("foo", XYWH(5, 6, 10, 10))?, Some(XYWH(5, 6, 3, 1)));
|
||||
assert_eq!(layout_text_u16("foo\nbarz", XYWH(5, 6, 10, 10))?, Some(XYWH(5, 6, 4, 2)));
|
||||
Ok(())
|
||||
#[cfg(feature = "text")] pub use self::tui_text::*;
|
||||
#[cfg(feature = "text")] mod tui_text {
|
||||
use super::*;
|
||||
|
||||
impl Tui {
|
||||
/// Write a line of text
|
||||
///
|
||||
/// TODO: do a paragraph (handle newlines)
|
||||
pub fn text (&mut self, text: &impl AsRef<str>, x0: u16, y: u16, max_width: u16)
|
||||
-> Perhaps<XYWH<u16>>
|
||||
{
|
||||
let text = text.as_ref();
|
||||
let mut string_width: u16 = 0;
|
||||
for character in text.chars() {
|
||||
let x = x0 + string_width;
|
||||
let character_width = character.width().unwrap_or(0) as u16;
|
||||
string_width += character_width;
|
||||
if string_width > max_width {
|
||||
break
|
||||
}
|
||||
if let Self::Draw(buffer, ..) = self {
|
||||
if let Some(cell) = buffer.cell_mut(ratatui::prelude::Position { x, y }) {
|
||||
cell.set_char(character);
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Some(XYWH(x0, y, string_width, 1)))
|
||||
}
|
||||
}
|
||||
|
||||
impl_draw!(|self: String, to: Tui|{
|
||||
self.as_str().draw(to)
|
||||
});
|
||||
|
||||
impl_draw!(|self: std::sync::Arc<str>, to: Tui|{
|
||||
self.as_ref().draw(to)
|
||||
});
|
||||
|
||||
impl_draw!(<T: AsRef<str>,>|self: TrimString<T>, to: Tui|{
|
||||
self.as_ref().draw(to)
|
||||
});
|
||||
|
||||
impl Draw<Tui> for &str {
|
||||
fn draw (&self, to: &mut Tui) -> Drawn<u16> {
|
||||
let XYWH(x, y, w, h) = to.area();
|
||||
let mut max_w = 0u16;
|
||||
let mut max_h = 0u16;
|
||||
for (index, line) in self.split("\n").enumerate() {
|
||||
let length = width_chars_max(w, line) as u16;
|
||||
max_w = max_w.max(length);
|
||||
max_h += 1;
|
||||
if let Tui::Draw(..) = to {
|
||||
let _ = to.text(&line, x, y + index as u16, length)?;
|
||||
}
|
||||
if max_h >= h {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(Some(XYWH(x, y, max_w, max_h)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'t, T: AsRef<str>> Draw<Tui> for TrimStr<'_, T> {
|
||||
fn draw (&self, to: &mut Tui) -> Drawn<u16> {
|
||||
let text = self.1.as_ref();
|
||||
let area = layout_text_u16(text, to.area())?.unwrap();
|
||||
let XYWH(x, y, w, ..) = to.area();
|
||||
let mut width: u16 = 1;
|
||||
let mut chars = text.chars();
|
||||
while let Some(c) = chars.next() {
|
||||
if width > self.0 || width > w {
|
||||
break
|
||||
}
|
||||
if let Tui::Draw(..) = to {
|
||||
to.set_char(x + width - 1, y, c);
|
||||
}
|
||||
width += c.width().unwrap_or(0) as u16;
|
||||
}
|
||||
let XYWH(x, y, w, ..) = XYWH(
|
||||
to.x(), to.y(), to.w().min(self.0).min(self.1.as_ref().width() as u16), to.h()
|
||||
);
|
||||
to.text(&self.as_ref(), x, y, w)
|
||||
}
|
||||
}
|
||||
|
||||
fn layout_text_u16 (text: &str, area: XYWH<u16>) -> Perhaps<XYWH<u16>> {
|
||||
let XYWH(x, y, w, h) = area;
|
||||
let mut max_w = 0u16;
|
||||
let mut max_h = 0u16;
|
||||
for line in text.split("\n") {
|
||||
max_h += 1;
|
||||
max_w = max_w.max(line.len() as u16);
|
||||
}
|
||||
Ok(Some(XYWH(x, y, w.min(max_w), h.min(max_h))))
|
||||
}
|
||||
|
||||
#[cfg(test)] #[test] fn test_layout_text_u16 () -> Usually<()> {
|
||||
assert_eq!(layout_text_u16("foo", XYWH(5, 6, 10, 10))?, Some(XYWH(5, 6, 3, 1)));
|
||||
assert_eq!(layout_text_u16("foo\nbarz", XYWH(5, 6, 10, 10))?, Some(XYWH(5, 6, 4, 2)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue