mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2026-09-18 13:26:42 +02:00
Compare commits
No commits in common. "48aaa3933aadd2d795b6652a244fce179d7072bf" and "b318d498e544fa65bbf3a3ff4dabde166352b045" have entirely different histories.
48aaa3933a
...
b318d498e5
10 changed files with 279 additions and 300 deletions
2
dizzle
2
dizzle
|
|
@ -1 +1 @@
|
||||||
Subproject commit 3d04622e4266eac65021da6cd3d378e84a843b87
|
Subproject commit b6c1bb5f9400c28de1709df78c18e0d4d5618a15
|
||||||
|
|
@ -454,5 +454,5 @@ impl<'a, S: Screen, T: Draw<S>> LayoutPull<'a, S> for T {}
|
||||||
impl<'a, S: Screen, T: Draw<S>> LayoutPad<'a, S> for T {}
|
impl<'a, S: Screen, T: Draw<S>> LayoutPad<'a, S> for T {}
|
||||||
|
|
||||||
pub fn check_layout <'a> (t: impl Draw<Tui>) -> Drawn<u16> {
|
pub fn check_layout <'a> (t: impl Draw<Tui>) -> Drawn<u16> {
|
||||||
Tui(XYWH(1u16, 1, 80, 25), None).size(None, t)
|
Tui::Layout(XYWH(1u16, 1, 80, 25)).size(None, t)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -211,25 +211,22 @@ pub fn bar_areas <'a, S: Screen> (
|
||||||
let XYWH(x0, y0, w0, h0) = to.area();
|
let XYWH(x0, y0, w0, h0) = to.area();
|
||||||
Ok(match split {
|
Ok(match split {
|
||||||
Split::South => (
|
Split::South => (
|
||||||
Some(XYWH(x0, y0, w0, n)),
|
to.size(XYWH(x0, y0, w0, n), a)?,
|
||||||
Some(XYWH(x0, y0 + n, w0, h0 - n)),
|
to.size(XYWH(x0, y0 + n, w0, h0 - n), b)?,
|
||||||
),
|
),
|
||||||
Split::East => (
|
Split::East => (
|
||||||
Some(XYWH(x0, y0, n, h0)),
|
to.size(XYWH(x0, y0, n, h0), a)?,
|
||||||
Some(XYWH(x0 + n, y0, w0 - n, h0)),
|
to.size(XYWH(x0 + n, y0, w0 - n, h0), b)?,
|
||||||
),
|
),
|
||||||
Split::North => (
|
Split::North => (
|
||||||
Some(XYWH(x0, y0 + h0 - n, w0, n)),
|
to.size(XYWH(x0, y0 + h0 - n, w0, n), a)?,
|
||||||
Some(XYWH(x0, y0, w0, h0 - n)),
|
to.size(XYWH(x0, y0, w0, h0 - n), b)?,
|
||||||
),
|
),
|
||||||
Split::West => (
|
Split::West => (
|
||||||
Some(XYWH(x0 + w0 - n, y0, n, h0)),
|
to.size(XYWH(x0 + w0 - n, y0, n, h0), a)?,
|
||||||
Some(XYWH(x0, y0, w0 - n, h0)),
|
to.size(XYWH(x0, y0, w0 - n, h0), b)?,
|
||||||
),
|
|
||||||
Split::Above | Split::Below => (
|
|
||||||
Some(XYWH(x0, y0, w0, h0)),
|
|
||||||
Some(XYWH(x0, y0, w0, h0)),
|
|
||||||
),
|
),
|
||||||
|
_ => todo!()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -241,7 +238,7 @@ pub struct Pair<A, B>(Split, A, B);
|
||||||
/// use ::tengri::*;
|
/// use ::tengri::*;
|
||||||
/// use Split::*;
|
/// use Split::*;
|
||||||
/// fn size_of <'a, A: Draw<Tui>, B: Draw<Tui>> (stack: &Pair<Tui, A, B>) -> Drawn<S::Unit> {
|
/// fn size_of <'a, A: Draw<Tui>, B: Draw<Tui>> (stack: &Pair<Tui, A, B>) -> Drawn<S::Unit> {
|
||||||
/// 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(East, "foo", "bar"))?, Some(XYWH(0, 0, 6, 1)));
|
||||||
/// assert_eq!(size_of(&split(South, "foo", "bar"))?, Some(XYWH(0, 0, 3, 2)));
|
/// 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<()> {
|
/// # use ::tengri::*; fn test_stack_areas () -> Usually<()> {
|
||||||
/// let area = XYWH(0u16, 0, 80, 25);
|
/// 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(0u16, 0, 3, 1)),
|
||||||
/// Some(XYWH(3u16, 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, 0, 3, 1)),
|
||||||
/// Some(XYWH(0u16, 1, 3, 1)),
|
/// Some(XYWH(0u16, 1, 3, 1)),
|
||||||
/// ));
|
/// ));
|
||||||
|
|
@ -525,7 +522,7 @@ pub const fn below <'a, S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)] #[test] fn test_align () -> Usually<()> {
|
#[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".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_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)));
|
assert_eq!("FOOBAR\nKILROY".align_n().draw(&mut screen)?, Some(XYWH(37, 0, 6, 2)));
|
||||||
|
|
|
||||||
62
src/lib.rs
62
src/lib.rs
|
|
@ -1792,7 +1792,67 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "text")] pub use self::text::*;
|
#[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<T: AsRef<str>>(pub u16, pub T);
|
||||||
|
|
||||||
|
impl<T: AsRef<str>> AsRef<str> for TrimString<T> {
|
||||||
|
fn as_ref (&self) -> &str {
|
||||||
|
self.1.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: AsRef<str>> TrimString<T> {
|
||||||
|
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<str>>(pub u16, pub &'a T);
|
||||||
|
|
||||||
|
impl<T: AsRef<str>> AsRef<str> for TrimStr<'_, T> {
|
||||||
|
fn as_ref (&self) -> &str {
|
||||||
|
self.1.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn width_chars_max (max: u16, text: impl AsRef<str>) -> 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<str>) -> 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")] pub use self::term::*;
|
||||||
#[cfg(feature = "term")] mod term;
|
#[cfg(feature = "term")] mod term;
|
||||||
|
|
|
||||||
231
src/term.rs
231
src/term.rs
|
|
@ -29,7 +29,6 @@ mod modify; pub use self::modify::*;
|
||||||
mod phat; pub use self::phat::*;
|
mod phat; pub use self::phat::*;
|
||||||
mod repeat; pub use self::repeat::*;
|
mod repeat; pub use self::repeat::*;
|
||||||
mod scroll; pub use self::scroll::*;
|
mod scroll; pub use self::scroll::*;
|
||||||
#[cfg(feature = "text")] mod text;
|
|
||||||
|
|
||||||
#[macro_export] macro_rules! tui_app {
|
#[macro_export] macro_rules! tui_app {
|
||||||
($Struct:ident { $($fields:tt)* }) => {
|
($Struct:ident { $($fields:tt)* }) => {
|
||||||
|
|
@ -76,44 +75,30 @@ mod scroll; pub use self::scroll::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Terminal output.
|
/// Terminal output.
|
||||||
pub struct Tui(
|
pub enum Tui {
|
||||||
/// Current draw area
|
/// Draw mode: updates contained [Buffer].
|
||||||
pub(crate) XYWH<u16>,
|
Draw (
|
||||||
/// Ratatui buffer.
|
/// Ratatui buffer; area is screen size
|
||||||
///
|
Buffer,
|
||||||
/// - Area is screen size.
|
/// Current draw area
|
||||||
/// - If present, we're in draw mode.
|
XYWH<u16>
|
||||||
/// - If absent, we're in layout mode.
|
),
|
||||||
pub(crate) Option<Buffer>
|
/// Discard mode: only computes sizes
|
||||||
);
|
Layout (
|
||||||
|
/// Draw area; no buffer
|
||||||
impl AsRef<XYWH<u16>> for Tui {
|
XYWH<u16>
|
||||||
fn as_ref (&self) -> &XYWH<u16> {
|
)
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&Tui> for XYWH<u16> {
|
|
||||||
fn from (screen: &Tui) -> XYWH<u16> {
|
|
||||||
screen.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a: 'b, 'b> From<&'a mut Tui> for &'b mut XYWH<u16> {
|
|
||||||
fn from (screen: &'a mut Tui) -> &'b mut XYWH<u16> {
|
|
||||||
&mut screen.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Screen for Tui {
|
impl Screen for Tui {
|
||||||
type Unit = u16;
|
type Unit = u16;
|
||||||
fn area (&self) -> XYWH<Self::Unit> {
|
fn area (&self) -> XYWH<Self::Unit> {
|
||||||
self.0
|
self.into()
|
||||||
}
|
}
|
||||||
fn size <'a> (
|
fn size <'a> (
|
||||||
&mut self, area: impl Into<Option<XYWH<u16>>>, draw: impl Draw<Self>,
|
&mut self, area: impl Into<Option<XYWH<u16>>>, draw: impl Draw<Self>,
|
||||||
) -> Drawn<Self::Unit> {
|
) -> Drawn<Self::Unit> {
|
||||||
Self(self.area(), None).clip(area, &|to|draw.draw(to))
|
Self::Layout(self.area()).clip(area, &|to|draw.draw(to))
|
||||||
}
|
}
|
||||||
fn draw <'a> (
|
fn draw <'a> (
|
||||||
&mut self, area: impl Into<Option<XYWH<u16>>>, draw: impl Draw<Tui>
|
&mut self, area: impl Into<Option<XYWH<u16>>>, draw: impl Draw<Tui>
|
||||||
|
|
@ -126,12 +111,10 @@ impl Screen for Tui {
|
||||||
let prev = self.area();
|
let prev = self.area();
|
||||||
if let Some(area) = area.into() {
|
if let Some(area) = area.into() {
|
||||||
*self.area_mut() = 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)
|
disable_raw_mode().map_err(Into::into)
|
||||||
}
|
}
|
||||||
pub fn new (width: u16, height: u16) -> Self {
|
pub fn new (width: u16, height: u16) -> Self {
|
||||||
Self(
|
Self::Draw(Buffer::empty(Rect { x: 0, y: 0, width, height }), XYWH(0, 0, width, height))
|
||||||
XYWH(0, 0, width, height),
|
|
||||||
Some(Buffer::empty(Rect { x: 0, y: 0, width, height }))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
pub fn resize <W: Write> (&mut self, back: &mut CrosstermBackend<W>, width: u16, height: u16) {
|
pub fn resize <W: Write> (&mut self, back: &mut CrosstermBackend<W>, width: u16, height: u16) {
|
||||||
let size = Rect { x: 0, y: 0, width, height };
|
let size = Rect { x: 0, y: 0, width, height };
|
||||||
self.buffer().map(|buffer|{
|
if let Self::Draw(buffer, ..) = self {
|
||||||
if buffer.area != size {
|
if buffer.area != size {
|
||||||
back.clear_region(ClearType::All).unwrap();
|
back.clear_region(ClearType::All).unwrap();
|
||||||
buffer.resize(size);
|
buffer.resize(size);
|
||||||
buffer.reset();
|
buffer.reset();
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
pub fn redraw <'b, W: Write> (
|
pub fn redraw <'b, W: Write> (
|
||||||
&'b mut self,
|
&'b mut self,
|
||||||
back: &mut CrosstermBackend<W>,
|
back: &mut CrosstermBackend<W>,
|
||||||
next: &'b mut Self
|
next: &'b mut Self
|
||||||
) {
|
) {
|
||||||
if let Some(prev) = self.buffer()
|
if let Self::Draw(prev, ..) = self && let Self::Draw(next, ..) = next {
|
||||||
&& let Some(next) = next.buffer() {
|
|
||||||
let updates = prev.diff(&next);
|
let updates = prev.diff(&next);
|
||||||
back.draw(updates.into_iter()).expect("failed to render");
|
back.draw(updates.into_iter()).expect("failed to render");
|
||||||
Backend::flush(back).expect("failed to flush output new");
|
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<u16> {
|
pub fn update (&mut self, callback: &impl Fn(&mut Cell, u16, u16)) -> XYWH<u16> {
|
||||||
let XYWH(x0, y0, w, h) = self.area();
|
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 {
|
for row in 0..h {
|
||||||
let y = y0 + row;
|
let y = y0 + row;
|
||||||
for col in 0..w {
|
for col in 0..w {
|
||||||
|
|
@ -241,7 +220,7 @@ impl Tui {
|
||||||
self.xywh()
|
self.xywh()
|
||||||
}
|
}
|
||||||
pub fn blit (&mut self, text: &impl AsRef<str>, x: u16, y: u16, style: Option<Style>) {
|
pub fn blit (&mut self, text: &impl AsRef<str>, x: u16, y: u16, style: Option<Style>) {
|
||||||
if let Some(buffer) = self.buffer() {
|
if let Self::Draw(buffer, ..) = self {
|
||||||
let text = text.as_ref();
|
let text = text.as_ref();
|
||||||
let style = style.unwrap_or(Style::default());
|
let style = style.unwrap_or(Style::default());
|
||||||
if x < buffer.area.width && y < buffer.area.height {
|
if x < buffer.area.width && y < buffer.area.height {
|
||||||
|
|
@ -250,7 +229,7 @@ impl Tui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn tint_all (&mut self, fg: Color, bg: Color, modifier: Modifier) {
|
pub fn tint_all (&mut self, fg: Color, bg: Color, modifier: Modifier) {
|
||||||
if let Some(buffer) = self.buffer() {
|
if let Self::Draw(buffer, ..) = self {
|
||||||
for cell in buffer.content.iter_mut() {
|
for cell in buffer.content.iter_mut() {
|
||||||
cell.fg = fg;
|
cell.fg = fg;
|
||||||
cell.bg = bg;
|
cell.bg = bg;
|
||||||
|
|
@ -324,7 +303,11 @@ impl Tui {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn buffer (&mut self) -> Option<&mut Buffer> {
|
pub fn buffer (&mut self) -> Option<&mut Buffer> {
|
||||||
self.1.as_mut()
|
if let Tui::Draw(buffer, _) = self {
|
||||||
|
Some(buffer)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_char (&mut self, x: u16, y: u16, c: char) {
|
pub fn set_char (&mut self, x: u16, y: u16, c: char) {
|
||||||
|
|
@ -342,6 +325,14 @@ impl Tui {
|
||||||
//impl Deref for Tui { type Target = Buffer; fn deref (&self) -> &Buffer { &self.0 } }
|
//impl Deref for Tui { type Target = Buffer; fn deref (&self) -> &Buffer { &self.0 } }
|
||||||
//impl DerefMut for Tui { fn deref_mut (&mut self) -> &mut Buffer { &mut self.0 } }
|
//impl DerefMut for Tui { fn deref_mut (&mut self) -> &mut Buffer { &mut self.0 } }
|
||||||
//impl AsMut<Buffer> for Tui { fn as_mut (&mut self) -> &mut Buffer { &mut self.0 } }
|
//impl AsMut<Buffer> for Tui { fn as_mut (&mut self) -> &mut Buffer { &mut self.0 } }
|
||||||
|
impl AsRef<XYWH<u16>> for Tui {
|
||||||
|
fn as_ref (&self) -> &XYWH<u16> {
|
||||||
|
match self {
|
||||||
|
Self::Draw(_, area) => area,
|
||||||
|
Self::Layout(area) => area
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Wide<u16> for Tui {
|
impl Wide<u16> for Tui {
|
||||||
fn w (&self) -> u16 { self.as_ref().2 }
|
fn w (&self) -> u16 { self.as_ref().2 }
|
||||||
|
|
@ -360,6 +351,24 @@ impl HasOrigin for Tui {
|
||||||
fn origin (&self) -> Azimuth { Azimuth::NW }
|
fn origin (&self) -> Azimuth { Azimuth::NW }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&Tui> for XYWH<u16> {
|
||||||
|
fn from (screen: &Tui) -> XYWH<u16> {
|
||||||
|
match screen {
|
||||||
|
Tui::Draw(_, area) => *area,
|
||||||
|
Tui::Layout(area) => *area,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a: 'b, 'b> From<&'a mut Tui> for &'b mut XYWH<u16> {
|
||||||
|
fn from (screen: &'a mut Tui) -> &'b mut XYWH<u16> {
|
||||||
|
match screen {
|
||||||
|
Tui::Draw(_, area) => area,
|
||||||
|
Tui::Layout(area) => area,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const fn fill_char <'a> (c: char) -> impl Draw<Tui> {
|
pub const fn fill_char <'a> (c: char) -> impl Draw<Tui> {
|
||||||
draw(move|to: &mut Tui|Ok(Some(to.update(&|cell,_,_|{
|
draw(move|to: &mut Tui|Ok(Some(to.update(&|cell,_,_|{
|
||||||
cell.set_char(c);
|
cell.set_char(c);
|
||||||
|
|
@ -398,3 +407,131 @@ impl<'a, T: Draw<Tui>> Draw<Tui> for ShowSizeOf<T> {
|
||||||
None
|
None
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//#[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: usize, to: Tui|{
|
||||||
|
let XYWH(x0, y0, w0, h0) = to.area();
|
||||||
|
let mut v = *self;
|
||||||
|
let w = (1 + if v > 0 { self.ilog10() } else { 0 }) as u16;
|
||||||
|
let h = 1u16;
|
||||||
|
Ok((w0 >= w && h0 >= h).then(||{
|
||||||
|
if let Tui::Draw(buffer, ..) = to {
|
||||||
|
let mut x = x0 + w - 1;
|
||||||
|
while x >= x0 {
|
||||||
|
if let Some(cell) = buffer.cell_mut(Position { x, y: y0 }) {
|
||||||
|
cell.set_char([
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
|
||||||
|
][(v % 10) as usize]);
|
||||||
|
}
|
||||||
|
x -= 1;
|
||||||
|
v = v / 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XYWH(x0, y0, w, h)
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
|
||||||
|
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<'a> 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<'a, T: AsRef<str>> Draw<Tui> for TrimStr<'a, 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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -113,13 +113,12 @@ pub const fn modify <'a> (on: bool, modifier: Modifier, item: impl Draw<Tui>)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn fill_mod <'a> (on: bool, modifier: Modifier) -> impl Draw<Tui> {
|
pub const fn fill_mod <'a> (on: bool, modifier: Modifier) -> impl Draw<Tui> {
|
||||||
let modify = if on {
|
|
||||||
Modifier::insert
|
|
||||||
} else {
|
|
||||||
Modifier::remove
|
|
||||||
};
|
|
||||||
draw(move|to: &mut Tui|Ok(Some({
|
draw(move|to: &mut Tui|Ok(Some({
|
||||||
to.update(&|cell,_,_|modify(&mut cell.modifier, modifier))
|
if on {
|
||||||
|
to.update(&|cell,_,_|cell.modifier.insert(modifier))
|
||||||
|
} else {
|
||||||
|
to.update(&|cell,_,_|cell.modifier.remove(modifier))
|
||||||
|
}
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@ use ratatui::{prelude::{Position}};
|
||||||
pub const fn x_repeat (c: &str) -> impl Draw<Tui> {
|
pub const fn x_repeat (c: &str) -> impl Draw<Tui> {
|
||||||
draw(move|to: &mut Tui|{
|
draw(move|to: &mut Tui|{
|
||||||
let XYWH(x, y, w, _h) = to.xywh();
|
let XYWH(x, y, w, _h) = to.xywh();
|
||||||
to.buffer().map(|buf|{
|
for x in x..x+w {
|
||||||
for x in x..x+w {
|
if let Tui::Draw(buf, ..) = to {
|
||||||
if let Some(cell) = buf.cell_mut(Position::from((x, y))) {
|
if let Some(cell) = buf.cell_mut(Position::from((x, y))) {
|
||||||
cell.set_symbol(&c);
|
cell.set_symbol(&c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
Ok(Some(XYWH(x, y, w, 1)))
|
Ok(Some(XYWH(x, y, w, 1)))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -18,13 +18,13 @@ pub const fn x_repeat (c: &str) -> impl Draw<Tui> {
|
||||||
pub const fn y_repeat (c: &str) -> impl Draw<Tui> {
|
pub const fn y_repeat (c: &str) -> impl Draw<Tui> {
|
||||||
draw(move|to: &mut Tui|{
|
draw(move|to: &mut Tui|{
|
||||||
let XYWH(x, y, _w, h) = to.xywh();
|
let XYWH(x, y, _w, h) = to.xywh();
|
||||||
to.buffer().map(|buf|{
|
for y in y..y+h {
|
||||||
for y in y..y+h {
|
if let Tui::Draw(buf, ..) = to {
|
||||||
if let Some(cell) = buf.cell_mut(Position::from((x, y))) {
|
if let Some(cell) = buf.cell_mut(Position::from((x, y))) {
|
||||||
cell.set_symbol(&c);
|
cell.set_symbol(&c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
Ok(Some(XYWH(x, y, 1, h)))
|
Ok(Some(XYWH(x, y, 1, h)))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -33,16 +33,16 @@ pub const fn xy_repeat (c: &str) -> impl Draw<Tui> {
|
||||||
draw(move|to: &mut Tui|{
|
draw(move|to: &mut Tui|{
|
||||||
let XYWH(x, y, w, h) = to.xywh();
|
let XYWH(x, y, w, h) = to.xywh();
|
||||||
let a = c.len();
|
let a = c.len();
|
||||||
to.buffer().map(|buf|{
|
for (_v, y) in (y..y+h).enumerate() {
|
||||||
for (_v, y) in (y..y+h).enumerate() {
|
for (u, x) in (x..x+w).enumerate() {
|
||||||
for (u, x) in (x..x+w).enumerate() {
|
if let Tui::Draw(buf, ..) = to {
|
||||||
if let Some(cell) = buf.cell_mut(Position::from((x, y))) {
|
if let Some(cell) = buf.cell_mut(Position::from((x, y))) {
|
||||||
let u = u % a;
|
let u = u % a;
|
||||||
cell.set_symbol(&c[u..u+1]);
|
cell.set_symbol(&c[u..u+1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
Ok(Some(XYWH(x, y, w, h)))
|
Ok(Some(XYWH(x, y, w, h)))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ pub fn x_scroll <'a> () -> impl Draw<Tui> {
|
||||||
let XYWH(x1, y1, w, _h) = to.area();
|
let XYWH(x1, y1, w, _h) = to.area();
|
||||||
let x2 = x1 + w;
|
let x2 = x1 + w;
|
||||||
for (i, x) in (x1..=x2).enumerate() {
|
for (i, x) in (x1..=x2).enumerate() {
|
||||||
to.buffer().map(|buf|{
|
if let Tui::Draw(buf, ..) = to {
|
||||||
if let Some(cell) = buf.cell_mut(Position::from((x, y1))) {
|
if let Some(cell) = buf.cell_mut(Position::from((x, y1))) {
|
||||||
if i < (ICON_DEC_H.len()) {
|
if i < (ICON_DEC_H.len()) {
|
||||||
cell.set_fg(Rgb(255, 255, 255));
|
cell.set_fg(Rgb(255, 255, 255));
|
||||||
|
|
@ -31,7 +31,7 @@ pub fn x_scroll <'a> () -> impl Draw<Tui> {
|
||||||
cell.set_char('╌');
|
cell.set_char('╌');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
Ok(Some(XYWH(x1, y1, w, 1)))
|
Ok(Some(XYWH(x1, y1, w, 1)))
|
||||||
})
|
})
|
||||||
|
|
@ -42,7 +42,7 @@ pub fn y_scroll <'a> () -> impl Draw<Tui> {
|
||||||
let XYWH(x1, y1, _w, h) = to.area();
|
let XYWH(x1, y1, _w, h) = to.area();
|
||||||
let y2 = y1 + h;
|
let y2 = y1 + h;
|
||||||
for (i, y) in (y1..=y2).enumerate() {
|
for (i, y) in (y1..=y2).enumerate() {
|
||||||
to.buffer().map(|buf|{
|
if let Tui::Draw(buf, ..) = to {
|
||||||
if let Some(cell) = buf.cell_mut(Position::from((x1, y))) {
|
if let Some(cell) = buf.cell_mut(Position::from((x1, y))) {
|
||||||
if (i as usize) < (ICON_DEC_V.len()) {
|
if (i as usize) < (ICON_DEC_V.len()) {
|
||||||
cell.set_fg(Rgb(255, 255, 255));
|
cell.set_fg(Rgb(255, 255, 255));
|
||||||
|
|
@ -62,7 +62,7 @@ pub fn y_scroll <'a> () -> impl Draw<Tui> {
|
||||||
cell.set_char('╎'); // ━
|
cell.set_char('╎'); // ━
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
Ok(Some(XYWH(x1, y1, 1, h)))
|
Ok(Some(XYWH(x1, y1, 1, h)))
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
213
src/text.rs
213
src/text.rs
|
|
@ -1,213 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
pub(crate) use ::unicode_width::*;
|
|
||||||
|
|
||||||
/// Displays a multi-line text string.
|
|
||||||
pub struct Text<T: AsRef<str>>(T);
|
|
||||||
|
|
||||||
/// Displays an owned [str]-like with fixed maximum width.
|
|
||||||
///
|
|
||||||
/// Width is computed using [unicode_width].
|
|
||||||
pub struct TrimString<T: AsRef<str>>(pub u16, pub T);
|
|
||||||
|
|
||||||
/// Displays a borrowed [str]-like with fixed maximum width
|
|
||||||
///
|
|
||||||
/// Width is computed using [unicode_width].
|
|
||||||
pub struct TrimStr<'a, T: AsRef<str>>(pub u16, pub &'a T);
|
|
||||||
|
|
||||||
impl<T: AsRef<str>> AsRef<str> for TrimString<T> {
|
|
||||||
fn as_ref (&self) -> &str {
|
|
||||||
self.1.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: AsRef<str>> TrimString<T> {
|
|
||||||
fn to_ref (&self) -> TrimStr<'_, T> {
|
|
||||||
TrimStr(self.0, &self.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsRef<str>> AsRef<str> for TrimStr<'_, T> {
|
|
||||||
fn as_ref (&self) -> &str {
|
|
||||||
self.1.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn width_chars_max (max: u16, text: impl AsRef<str>) -> 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<str>) -> 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")]
|
|
||||||
mod text_term {
|
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
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 Some(true) == self.buffer().map(|buffer|{
|
|
||||||
if let Some(cell) = buffer.cell_mut(ratatui::prelude::Position { x, y }) {
|
|
||||||
cell.set_char(character);
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Some(XYWH(x0, y, string_width, 1)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_draw!(|self: usize, to: Tui|{
|
|
||||||
let XYWH(x0, y0, w0, h0) = to.area();
|
|
||||||
let mut v = *self;
|
|
||||||
let w = (1 + if v > 0 { self.ilog10() } else { 0 }) as u16;
|
|
||||||
let h = 1u16;
|
|
||||||
Ok((w0 >= w && h0 >= h).then(||{
|
|
||||||
to.buffer().map(|buffer|{
|
|
||||||
let mut x = x0 + w - 1;
|
|
||||||
while x >= x0 {
|
|
||||||
if let Some(cell) = buffer.cell_mut(Position { x, y: y0 }) {
|
|
||||||
cell.set_char([
|
|
||||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
|
|
||||||
][(v % 10) as usize]);
|
|
||||||
}
|
|
||||||
x -= 1;
|
|
||||||
v = v / 10;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
XYWH(x0, y0, w, h)
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
|
|
||||||
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<'a> Draw<Tui> for &str {
|
|
||||||
fn draw (&self, to: &mut Tui) -> Drawn<u16> {
|
|
||||||
let XYWH(x, y, w, h) = to.area();
|
|
||||||
if h > 0 {
|
|
||||||
to.buffer().map(|buf|buf.set_stringn(x, y, self, w as usize, Style::default()));
|
|
||||||
Ok(Some(XYWH(x, y, w.min(self.len() as u16), 1)))
|
|
||||||
} else {
|
|
||||||
Ok(Some(XYWH(x, y, 0, 0)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsRef<str>> Draw<Tui> for Text<T> {
|
|
||||||
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;
|
|
||||||
if to.buffer().is_some() {
|
|
||||||
for (index, line) in self.0.as_ref().split("\n").enumerate() {
|
|
||||||
let length = width_chars_max(w, line) as u16;
|
|
||||||
max_w = max_w.max(length);
|
|
||||||
max_h += 1;
|
|
||||||
let _ = to.text(&line, x, y + index as u16, length)?;
|
|
||||||
if max_h >= h {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for line in self.0.as_ref().split("\n") {
|
|
||||||
let length = width_chars_max(w, line) as u16;
|
|
||||||
max_w = max_w.max(length);
|
|
||||||
max_h += 1;
|
|
||||||
if max_h >= h {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Some(XYWH(x, y, max_w, max_h)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: AsRef<str>> Draw<Tui> for TrimStr<'a, 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 to.buffer().is_some() {
|
|
||||||
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