diff --git a/dizzle b/dizzle index 3d04622..b6c1bb5 160000 --- a/dizzle +++ b/dizzle @@ -1 +1 @@ -Subproject commit 3d04622e4266eac65021da6cd3d378e84a843b87 +Subproject commit b6c1bb5f9400c28de1709df78c18e0d4d5618a15 diff --git a/src/layout/axis.rs b/src/layout/axis.rs index 5d1767b..1a02ab8 100644 --- a/src/layout/axis.rs +++ b/src/layout/axis.rs @@ -454,5 +454,5 @@ impl<'a, S: Screen, T: Draw> LayoutPull<'a, S> for T {} impl<'a, S: Screen, T: Draw> LayoutPad<'a, S> for T {} pub fn check_layout <'a> (t: impl Draw) -> Drawn { - Tui(XYWH(1u16, 1, 80, 25), None).size(None, t) + Tui::Layout(XYWH(1u16, 1, 80, 25)).size(None, t) } diff --git a/src/layout/azimuth.rs b/src/layout/azimuth.rs index 8c6fc86..c948a9a 100644 --- a/src/layout/azimuth.rs +++ b/src/layout/azimuth.rs @@ -211,25 +211,22 @@ pub fn bar_areas <'a, S: Screen> ( let XYWH(x0, y0, w0, h0) = to.area(); Ok(match split { Split::South => ( - Some(XYWH(x0, y0, w0, n)), - Some(XYWH(x0, y0 + n, w0, h0 - n)), + to.size(XYWH(x0, y0, w0, n), a)?, + to.size(XYWH(x0, y0 + n, w0, h0 - n), b)?, ), Split::East => ( - Some(XYWH(x0, y0, n, h0)), - Some(XYWH(x0 + n, y0, w0 - n, h0)), + to.size(XYWH(x0, y0, n, h0), a)?, + to.size(XYWH(x0 + n, y0, w0 - n, h0), b)?, ), Split::North => ( - Some(XYWH(x0, y0 + h0 - n, w0, n)), - Some(XYWH(x0, y0, w0, h0 - n)), + to.size(XYWH(x0, y0 + h0 - n, w0, n), a)?, + to.size(XYWH(x0, y0, w0, h0 - n), b)?, ), Split::West => ( - Some(XYWH(x0 + w0 - n, y0, n, h0)), - Some(XYWH(x0, y0, w0 - n, h0)), - ), - Split::Above | Split::Below => ( - Some(XYWH(x0, y0, w0, h0)), - Some(XYWH(x0, y0, w0, h0)), + to.size(XYWH(x0 + w0 - n, y0, n, h0), a)?, + to.size(XYWH(x0, y0, w0 - n, h0), b)?, ), + _ => todo!() }) } @@ -241,7 +238,7 @@ pub struct Pair(Split, A, B); /// use ::tengri::*; /// use Split::*; /// fn size_of <'a, A: Draw, B: Draw> (stack: &Pair) -> Drawn { -/// Tui(XYWH(0, 0, 80, 25), None).size(None, stack) +/// Tui::Layout(XYWH(0, 0, 80, 25)).size(None, stack) /// } /// assert_eq!(size_of(&split(East, "foo", "bar"))?, Some(XYWH(0, 0, 6, 1))); /// assert_eq!(size_of(&split(South, "foo", "bar"))?, Some(XYWH(0, 0, 3, 2))); @@ -296,11 +293,11 @@ fn draw_stacks <'a, S: Screen> ( /// ``` /// # use ::tengri::*; fn test_stack_areas () -> Usually<()> { /// let area = XYWH(0u16, 0, 80, 25); -/// assert_eq!(stack_areas(&Split::East, &mut Tui(area, None), &"foo", &"bar")?, ( +/// assert_eq!(stack_areas(&Split::East, &mut Tui::Layout(area), &"foo", &"bar")?, ( /// Some(XYWH(0u16, 0, 3, 1)), /// Some(XYWH(3u16, 0, 3, 1)), /// )); -/// assert_eq!(stack_areas(&Split::South, &mut Tui(area, None), &"foo", &"bar")?, ( +/// assert_eq!(stack_areas(&Split::South, &mut Tui::Layout(area), &"foo", &"bar")?, ( /// Some(XYWH(0u16, 0, 3, 1)), /// Some(XYWH(0u16, 1, 3, 1)), /// )); @@ -525,7 +522,7 @@ pub const fn below <'a, S: Screen, A: Draw, B: Draw> (a: A, b: B) -> impl } #[cfg(test)] #[test] fn test_align () -> Usually<()> { - let mut screen = Tui(XYWH(0, 0, 80, 25), None); + let mut screen = Tui::Layout(XYWH(0, 0, 80, 25)); assert_eq!("FOOBAR\nKILROY".draw(&mut screen)?, Some(XYWH(0, 0, 6, 2))); assert_eq!("FOOBAR\nKILROY".align_nw().draw(&mut screen)?, Some(XYWH(0, 0, 6, 2))); assert_eq!("FOOBAR\nKILROY".align_n().draw(&mut screen)?, Some(XYWH(37, 0, 6, 2))); diff --git a/src/lib.rs b/src/lib.rs index 2d617e0..92d0b69 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1792,7 +1792,67 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } } #[cfg(feature = "text")] pub use self::text::*; -#[cfg(feature = "text")] mod text; +#[cfg(feature = "text")] mod text { + #![allow(unused)] + + pub(crate) use ::unicode_width::*; + + /// Displays an owned [str]-like with fixed maximum width. + /// + /// Width is computed using [unicode_width]. + pub struct TrimString>(pub u16, pub T); + + impl> AsRef for TrimString { + fn as_ref (&self) -> &str { + self.1.as_ref() + } + } + + impl<'a, T: AsRef> TrimString { + fn to_ref (&self) -> TrimStr<'_, T> { + TrimStr(self.0, &self.1) + } + } + + /// Displays a borrowed [str]-like with fixed maximum width + /// + /// Width is computed using [unicode_width]. + pub struct TrimStr<'a, T: AsRef>(pub u16, pub &'a T); + + impl> AsRef for TrimStr<'_, T> { + fn as_ref (&self) -> &str { + self.1.as_ref() + } + } + + pub(crate) fn width_chars_max (max: u16, text: impl AsRef) -> u16 { + let mut width: u16 = 0; + let mut chars = text.as_ref().chars(); + while let Some(c) = chars.next() { + width += c.width().unwrap_or(0) as u16; + if width >= max { + break + } + } + return width + } + + /// Trim string with [unicode_width]. + pub fn trim_string (max_width: usize, input: impl AsRef) -> String { + let input = input.as_ref(); + let mut output = Vec::with_capacity(input.len()); + let mut width: usize = 1; + let mut chars = input.chars(); + while let Some(c) = chars.next() { + if width > max_width { + break + } + output.push(c); + width += c.width().unwrap_or(0); + } + return output.into_iter().collect() + } +} #[cfg(feature = "term")] pub use self::term::*; #[cfg(feature = "term")] mod term; diff --git a/src/term.rs b/src/term.rs index a5ed594..dffe505 100644 --- a/src/term.rs +++ b/src/term.rs @@ -29,7 +29,6 @@ mod modify; pub use self::modify::*; mod phat; pub use self::phat::*; mod repeat; pub use self::repeat::*; mod scroll; pub use self::scroll::*; -#[cfg(feature = "text")] mod text; #[macro_export] macro_rules! tui_app { ($Struct:ident { $($fields:tt)* }) => { @@ -76,44 +75,30 @@ mod scroll; pub use self::scroll::*; } /// Terminal output. -pub struct Tui( - /// Current draw area - pub(crate) XYWH, - /// Ratatui buffer. - /// - /// - Area is screen size. - /// - If present, we're in draw mode. - /// - If absent, we're in layout mode. - pub(crate) Option -); - -impl AsRef> for Tui { - fn as_ref (&self) -> &XYWH { - &self.0 - } -} - -impl From<&Tui> for XYWH { - fn from (screen: &Tui) -> XYWH { - screen.0 - } -} - -impl<'a: 'b, 'b> From<&'a mut Tui> for &'b mut XYWH { - fn from (screen: &'a mut Tui) -> &'b mut XYWH { - &mut screen.0 - } +pub enum Tui { + /// Draw mode: updates contained [Buffer]. + Draw ( + /// Ratatui buffer; area is screen size + Buffer, + /// Current draw area + XYWH + ), + /// Discard mode: only computes sizes + Layout ( + /// Draw area; no buffer + XYWH + ) } impl Screen for Tui { type Unit = u16; fn area (&self) -> XYWH { - self.0 + self.into() } fn size <'a> ( &mut self, area: impl Into>>, draw: impl Draw, ) -> Drawn { - Self(self.area(), None).clip(area, &|to|draw.draw(to)) + Self::Layout(self.area()).clip(area, &|to|draw.draw(to)) } fn draw <'a> ( &mut self, area: impl Into>>, draw: impl Draw @@ -126,12 +111,10 @@ impl Screen for Tui { let prev = self.area(); if let Some(area) = area.into() { *self.area_mut() = area.into(); - let result = draw(self); - *self.area_mut() = prev; - result - } else { - draw(self) } + let result = draw(self); + *self.area_mut() = prev; + result } } @@ -194,28 +177,24 @@ impl Tui { disable_raw_mode().map_err(Into::into) } pub fn new (width: u16, height: u16) -> Self { - Self( - XYWH(0, 0, width, height), - Some(Buffer::empty(Rect { x: 0, y: 0, width, height })) - ) + Self::Draw(Buffer::empty(Rect { x: 0, y: 0, width, height }), XYWH(0, 0, width, height)) } pub fn resize (&mut self, back: &mut CrosstermBackend, width: u16, height: u16) { let size = Rect { x: 0, y: 0, width, height }; - self.buffer().map(|buffer|{ + if let Self::Draw(buffer, ..) = self { if buffer.area != size { back.clear_region(ClearType::All).unwrap(); buffer.resize(size); buffer.reset(); } - }); + } } pub fn redraw <'b, W: Write> ( &'b mut self, back: &mut CrosstermBackend, next: &'b mut Self ) { - if let Some(prev) = self.buffer() - && let Some(next) = next.buffer() { + if let Self::Draw(prev, ..) = self && let Self::Draw(next, ..) = next { let updates = prev.diff(&next); back.draw(updates.into_iter()).expect("failed to render"); Backend::flush(back).expect("failed to flush output new"); @@ -225,7 +204,7 @@ impl Tui { } pub fn update (&mut self, callback: &impl Fn(&mut Cell, u16, u16)) -> XYWH { let XYWH(x0, y0, w, h) = self.area(); - if let Some(buffer) = self.buffer() { + if let Self::Draw(buffer, ..) = self { for row in 0..h { let y = y0 + row; for col in 0..w { @@ -241,7 +220,7 @@ impl Tui { self.xywh() } pub fn blit (&mut self, text: &impl AsRef, x: u16, y: u16, style: Option