From c9a89eeb1d3f229e12e95062d6d029ea96e3deaf Mon Sep 17 00:00:00 2001 From: facile pop culture reference Date: Tue, 1 Sep 2026 18:28:43 +0300 Subject: [PATCH 1/2] separate mod text; perf: don't branch with Tui --- src/layout/axis.rs | 2 +- src/layout/azimuth.rs | 13 ++- src/lib.rs | 62 +----------- src/term.rs | 223 ++++++++---------------------------------- src/term/modify.rs | 11 ++- src/term/repeat.rs | 20 ++-- src/term/scroll.rs | 8 +- src/term/text.rs | 1 + src/text.rs | 198 +++++++++++++++++++++++++++++++++++++ 9 files changed, 271 insertions(+), 267 deletions(-) create mode 100644 src/term/text.rs create mode 100644 src/text.rs diff --git a/src/layout/axis.rs b/src/layout/axis.rs index 1a02ab8..5d1767b 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::Layout(XYWH(1u16, 1, 80, 25)).size(None, t) + Tui(XYWH(1u16, 1, 80, 25), None).size(None, t) } diff --git a/src/layout/azimuth.rs b/src/layout/azimuth.rs index c948a9a..122ab16 100644 --- a/src/layout/azimuth.rs +++ b/src/layout/azimuth.rs @@ -226,7 +226,10 @@ pub fn bar_areas <'a, S: Screen> ( to.size(XYWH(x0 + w0 - n, y0, n, h0), a)?, to.size(XYWH(x0, y0, w0 - n, h0), b)?, ), - _ => todo!() + Split::Above | Split::Below => ( + to.size(XYWH(x0, y0, w0, h0), a)?, + to.size(XYWH(x0, y0, w0, h0), b)?, + ), }) } @@ -238,7 +241,7 @@ pub struct Pair(Split, A, B); /// use ::tengri::*; /// use Split::*; /// fn size_of <'a, A: Draw, B: Draw> (stack: &Pair) -> Drawn { -/// Tui::Layout(XYWH(0, 0, 80, 25)).size(None, stack) +/// Tui(XYWH(0, 0, 80, 25), None).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))); @@ -293,11 +296,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::Layout(area), &"foo", &"bar")?, ( +/// assert_eq!(stack_areas(&Split::East, &mut Tui(area, None), &"foo", &"bar")?, ( /// Some(XYWH(0u16, 0, 3, 1)), /// Some(XYWH(3u16, 0, 3, 1)), /// )); -/// assert_eq!(stack_areas(&Split::South, &mut Tui::Layout(area), &"foo", &"bar")?, ( +/// assert_eq!(stack_areas(&Split::South, &mut Tui(area, None), &"foo", &"bar")?, ( /// Some(XYWH(0u16, 0, 3, 1)), /// Some(XYWH(0u16, 1, 3, 1)), /// )); @@ -522,7 +525,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::Layout(XYWH(0, 0, 80, 25)); + let mut screen = Tui(XYWH(0, 0, 80, 25), None); 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 92d0b69..2d617e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1792,67 +1792,7 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } } #[cfg(feature = "text")] pub use self::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 = "text")] mod text; #[cfg(feature = "term")] pub use self::term::*; #[cfg(feature = "term")] mod term; diff --git a/src/term.rs b/src/term.rs index dffe505..45c9f5b 100644 --- a/src/term.rs +++ b/src/term.rs @@ -29,6 +29,7 @@ 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)* }) => { @@ -75,30 +76,44 @@ 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 - ), - /// Discard mode: only computes sizes - Layout ( - /// Draw area; no buffer - XYWH - ) +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 + } } impl Screen for Tui { type Unit = u16; fn area (&self) -> XYWH { - self.into() + self.0 } fn size <'a> ( &mut self, area: impl Into>>, draw: impl Draw, ) -> Drawn { - Self::Layout(self.area()).clip(area, &|to|draw.draw(to)) + Self(self.area(), None).clip(area, &|to|draw.draw(to)) } fn draw <'a> ( &mut self, area: impl Into>>, draw: impl Draw @@ -177,24 +192,28 @@ impl Tui { disable_raw_mode().map_err(Into::into) } pub fn new (width: u16, height: u16) -> Self { - Self::Draw(Buffer::empty(Rect { x: 0, y: 0, width, height }), XYWH(0, 0, width, height)) + Self( + XYWH(0, 0, width, height), + Some(Buffer::empty(Rect { x: 0, y: 0, width, height })) + ) } pub fn resize (&mut self, back: &mut CrosstermBackend, width: u16, height: u16) { let size = Rect { x: 0, y: 0, width, height }; - if let Self::Draw(buffer, ..) = self { + self.buffer().map(|buffer|{ 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 Self::Draw(prev, ..) = self && let Self::Draw(next, ..) = next { + if let Some(prev) = self.buffer() + && let Some(next) = next.buffer() { let updates = prev.diff(&next); back.draw(updates.into_iter()).expect("failed to render"); Backend::flush(back).expect("failed to flush output new"); @@ -204,7 +223,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 Self::Draw(buffer, ..) = self { + if let Some(buffer) = self.buffer() { for row in 0..h { let y = y0 + row; for col in 0..w { @@ -220,7 +239,7 @@ impl Tui { self.xywh() } pub fn blit (&mut self, text: &impl AsRef, x: u16, y: u16, style: Option