mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2026-08-07 22:17:07 +02:00
layout (no-draw) mode for tui
This commit is contained in:
parent
a27dacff93
commit
64c83b5460
4 changed files with 167 additions and 105 deletions
124
src/lib.rs
124
src/lib.rs
|
|
@ -1325,21 +1325,45 @@ macro_rules! eval_xy (
|
|||
crossterm::event::read,
|
||||
};
|
||||
|
||||
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 AsMut<Buffer> for Tui { fn as_mut (&mut self) -> &mut Buffer { &mut self.0 } }
|
||||
impl Wide<u16> for Tui { fn w (&self) -> u16 { self.1.2 } }
|
||||
impl Tall<u16> for Tui { fn h (&self) -> u16 { self.1.3 } }
|
||||
//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 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 {
|
||||
fn w (&self) -> u16 { self.as_ref().2 }
|
||||
}
|
||||
|
||||
impl Tall<u16> for Tui {
|
||||
fn h (&self) -> u16 { self.as_ref().3 }
|
||||
}
|
||||
|
||||
impl Xy<u16> for Tui {
|
||||
fn x (&self) -> u16 { self.as_ref().0 }
|
||||
fn y (&self) -> u16 { self.as_ref().1 }
|
||||
}
|
||||
|
||||
impl HasOrigin for Tui { fn origin (&self) -> Azimuth { Azimuth::NW } }
|
||||
impl Xy<u16> for Tui { fn x (&self) -> u16 { self.1.0 } fn y (&self) -> u16 { self.1.1 } }
|
||||
|
||||
/// Terminal output.
|
||||
pub struct Tui(
|
||||
pub enum Tui {
|
||||
Draw (
|
||||
/// Ratatui buffer; area is screen size
|
||||
pub Buffer,
|
||||
Buffer,
|
||||
/// Current draw area
|
||||
pub XYWH<u16>
|
||||
);
|
||||
XYWH<u16>
|
||||
),
|
||||
Layout (
|
||||
XYWH<u16>
|
||||
)
|
||||
}
|
||||
|
||||
impl Tui {
|
||||
pub fn setup_panic () {
|
||||
|
|
@ -1402,14 +1426,16 @@ macro_rules! eval_xy (
|
|||
disable_raw_mode().map_err(Into::into)
|
||||
}
|
||||
pub fn new (width: u16, height: u16) -> Self {
|
||||
Self(Buffer::empty(Rect { x: 0, y: 0, width, height }), XYWH(0, 0, width, height))
|
||||
Self::Draw(Buffer::empty(Rect { x: 0, y: 0, width, height }), XYWH(0, 0, width, height))
|
||||
}
|
||||
pub fn resize <W: Write> (&mut self, back: &mut CrosstermBackend<W>, width: u16, height: u16) {
|
||||
let size = Rect { x: 0, y: 0, width, height };
|
||||
if self.0.area != size {
|
||||
if let Self::Draw(buffer, ..) = self {
|
||||
if buffer.area != size {
|
||||
back.clear_region(ClearType::All).unwrap();
|
||||
self.0.resize(size);
|
||||
self.0.reset();
|
||||
buffer.resize(size);
|
||||
buffer.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn redraw <'b, W: Write> (
|
||||
|
|
@ -1417,40 +1443,49 @@ macro_rules! eval_xy (
|
|||
back: &mut CrosstermBackend<W>,
|
||||
mut next: &'b mut Self
|
||||
) {
|
||||
let updates = self.0.diff(&next.0);
|
||||
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");
|
||||
std::mem::swap(self, &mut next);
|
||||
next.0.reset();
|
||||
std::mem::swap(prev, next);
|
||||
next.reset();
|
||||
}
|
||||
}
|
||||
pub fn update (&mut self, callback: &impl Fn(&mut Cell, u16, u16)) -> XYWH<u16> {
|
||||
for row in 0..self.h() {
|
||||
let y = self.y() + row;
|
||||
for col in 0..self.w() {
|
||||
let x = self.x() + col;
|
||||
if x < self.0.area.width && y < self.0.area.height {
|
||||
if let Some(cell) = self.0.cell_mut(Position { x, y }) {
|
||||
let XYWH(x0, y0, w, h) = self.area();
|
||||
if let Self::Draw(buffer, ..) = self {
|
||||
for row in 0..h {
|
||||
let y = y0 + row;
|
||||
for col in 0..w {
|
||||
let x = x0 + col;
|
||||
if x < buffer.area.width && y < buffer.area.height {
|
||||
if let Some(cell) = buffer.cell_mut(Position { x, y }) {
|
||||
callback(cell, col, row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.xywh()
|
||||
}
|
||||
pub fn blit (&mut self, text: &impl AsRef<str>, x: u16, y: u16, style: Option<Style>) {
|
||||
if let Self::Draw(buffer, ..) = self {
|
||||
let text = text.as_ref();
|
||||
let style = style.unwrap_or(Style::default());
|
||||
if x < self.0.area.width && y < self.0.area.height {
|
||||
self.0.set_string(x, y, text, style);
|
||||
if x < buffer.area.width && y < buffer.area.height {
|
||||
buffer.set_string(x, y, text, style);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn tint_all (&mut self, fg: Color, bg: Color, modifier: Modifier) {
|
||||
for cell in self.0.content.iter_mut() {
|
||||
if let Self::Draw(buffer, ..) = self {
|
||||
for cell in buffer.content.iter_mut() {
|
||||
cell.fg = fg;
|
||||
cell.bg = bg;
|
||||
cell.modifier = modifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Spawn the TUI output thread which writes colored characters to the terminal.
|
||||
///
|
||||
/// ```
|
||||
|
|
@ -1488,7 +1523,7 @@ macro_rules! eval_xy (
|
|||
prev.redraw(&mut backend, &mut next);
|
||||
}
|
||||
let timer = format!("{:>3.3}ms", perf.used.load(Relaxed));
|
||||
prev.set_string(0, 0, &timer, Style::default());
|
||||
prev.blit(&timer, 0, 0, Some(Style::default()));
|
||||
})?)
|
||||
}
|
||||
/// Draw TUI content or its error message.
|
||||
|
|
@ -1647,25 +1682,35 @@ macro_rules! eval_xy (
|
|||
if string_width > max_width {
|
||||
break
|
||||
}
|
||||
if let Some(cell) = self.0.cell_mut(ratatui::prelude::Position { x, y }) {
|
||||
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)))
|
||||
}
|
||||
|
||||
/// Get mutable reference to current clipping area
|
||||
fn area_mut (&mut self) -> &mut XYWH<u16> {
|
||||
match self {
|
||||
Self::Draw(_, area) => area,
|
||||
Self::Layout(area) => area,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Screen for Tui {
|
||||
type Unit = u16;
|
||||
/// Render drawable in subarea specified by `area`
|
||||
fn show (&mut self, content: impl Draw<Self>) -> Perhaps<XYWH<u16>> {
|
||||
let previous_area = self.1;
|
||||
Ok(if let Some(area) = content.layout(self.1)? {
|
||||
self.1 = area;
|
||||
let previous_area = self.area();
|
||||
Ok(if let Some(area) = content.layout(self.area())? {
|
||||
*self.area_mut() = area;
|
||||
if let Some(result_area) = content.draw(self)? {
|
||||
self.1 = previous_area;
|
||||
*self.area_mut() = previous_area;
|
||||
Some(result_area)
|
||||
} else {
|
||||
None
|
||||
|
|
@ -1677,7 +1722,10 @@ macro_rules! eval_xy (
|
|||
|
||||
/// Get current clipping area
|
||||
fn area (&self) -> XYWH<Self::Unit> {
|
||||
self.1
|
||||
match self {
|
||||
Self::Draw(_, area) => *area,
|
||||
Self::Layout(area) => *area,
|
||||
}
|
||||
}
|
||||
|
||||
fn clip <T> (
|
||||
|
|
@ -1685,12 +1733,12 @@ macro_rules! eval_xy (
|
|||
area: impl Into<Option<XYWH<u16>>>,
|
||||
draw: impl FnOnce(&mut Self)->T
|
||||
) -> T {
|
||||
let prev = self.1;
|
||||
let prev = self.area();
|
||||
if let Some(area) = area.into() {
|
||||
self.1 = area.into();
|
||||
*self.area_mut() = area.into();
|
||||
}
|
||||
let result = draw(self);
|
||||
self.1 = prev;
|
||||
*self.area_mut() = prev;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
|
@ -1776,9 +1824,11 @@ macro_rules! eval_xy (
|
|||
break
|
||||
}
|
||||
let pos = Position { x: x + width - 1, y };
|
||||
if let Some(cell) = to.0.cell_mut(pos) {
|
||||
if let Tui::Draw(buffer, _) = to {
|
||||
if let Some(cell) = buffer.cell_mut(pos) {
|
||||
cell.set_char(c);
|
||||
}
|
||||
}
|
||||
width += c.width().unwrap_or(0) as u16;
|
||||
}
|
||||
let XYWH(x, y, w, ..) = XYWH(
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use super::{*, draw::*};
|
|||
pub fn border (on: bool, style: impl BorderStyle, item: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||
let content = item.pad_wh(1, 1);
|
||||
let outline = when(on, draw(move|to: &mut Tui|{
|
||||
let XYWH(x, y, w, h) = to.1;
|
||||
let XYWH(x, y, w, h) = to.area();
|
||||
if w > 0 && h > 0 {
|
||||
to.blit(&style.border_nw(), x, y, style.style());
|
||||
to.blit(&style.border_ne(), x + w - 1, y, style.style());
|
||||
|
|
@ -178,7 +178,7 @@ pub trait BorderStyle: Draw<Tui> + Copy {
|
|||
self.draw_v(to, None)?;
|
||||
self.draw_c(to, None)?;
|
||||
}
|
||||
Ok(to.1)
|
||||
Ok(to.area())
|
||||
}
|
||||
#[inline] fn draw_h (self, to: &mut Tui, style: Option<Style>) -> Usually<XYWH<u16>> {
|
||||
let style = style.or_else(||self.style_horizontal());
|
||||
|
|
@ -191,7 +191,7 @@ pub trait BorderStyle: Draw<Tui> + Copy {
|
|||
Ok(to.xywh())
|
||||
}
|
||||
#[inline] fn draw_v (self, to: &mut Tui, style: Option<Style>) -> Usually<XYWH<u16>> {
|
||||
let area = to.1;
|
||||
let area = to.area();
|
||||
let style = style.or_else(||self.style_vertical());
|
||||
let [x, x2, y, y2] = area.lrtb();
|
||||
let h = y2 - y;
|
||||
|
|
@ -207,7 +207,7 @@ pub trait BorderStyle: Draw<Tui> + Copy {
|
|||
Ok(area)
|
||||
}
|
||||
#[inline] fn draw_c (self, to: &mut Tui, style: Option<Style>) -> Usually<XYWH<u16>> {
|
||||
let area = to.1;
|
||||
let area = to.area();
|
||||
let style = style.or_else(||self.style_corners());
|
||||
let XYWH(x, y, w, h) = area;
|
||||
if w > 1 && h > 1 {
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@ pub const fn x_repeat (c: &str) -> impl Draw<Tui> {
|
|||
draw(move|to: &mut Tui|{
|
||||
let XYWH(x, y, w, _h) = to.xywh();
|
||||
for x in x..x+w {
|
||||
if let Some(cell) = to.0.cell_mut(Position::from((x, y))) {
|
||||
if let Tui::Draw(buf, ..) = to {
|
||||
if let Some(cell) = buf.cell_mut(Position::from((x, y))) {
|
||||
cell.set_symbol(&c);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Some(XYWH(x, y, w, 1)))
|
||||
})
|
||||
}
|
||||
|
|
@ -17,10 +19,12 @@ pub const fn y_repeat (c: &str) -> impl Draw<Tui> {
|
|||
draw(move|to: &mut Tui|{
|
||||
let XYWH(x, y, _w, h) = to.xywh();
|
||||
for y in y..y+h {
|
||||
if let Some(cell) = to.0.cell_mut(Position::from((x, y))) {
|
||||
if let Tui::Draw(buf, ..) = to {
|
||||
if let Some(cell) = buf.cell_mut(Position::from((x, y))) {
|
||||
cell.set_symbol(&c);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Some(XYWH(x, y, 1, h)))
|
||||
})
|
||||
}
|
||||
|
|
@ -31,12 +35,14 @@ pub const fn xy_repeat (c: &str) -> impl Draw<Tui> {
|
|||
let a = c.len();
|
||||
for (_v, y) in (y..y+h).enumerate() {
|
||||
for (u, x) in (x..x+w).enumerate() {
|
||||
if let Some(cell) = to.0.cell_mut(Position::from((x, y))) {
|
||||
if let Tui::Draw(buf, ..) = to {
|
||||
if let Some(cell) = buf.cell_mut(Position::from((x, y))) {
|
||||
let u = u % a;
|
||||
cell.set_symbol(&c[u..u+1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Some(XYWH(x, y, w, h)))
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,18 +7,20 @@ pub const ICON_DEC_H: &[char] = &[' ', '🞀', ' '];
|
|||
pub const ICON_INC_H: &[char] = &[' ', '🞂', ' '];
|
||||
|
||||
pub fn x_scroll () -> impl Draw<Tui> {
|
||||
draw(|Tui(buf, XYWH(x1, y1, w, _h)): &mut Tui|{
|
||||
let x2 = *x1 + *w;
|
||||
for (i, x) in (*x1..=x2).enumerate() {
|
||||
if let Some(cell) = buf.cell_mut(Position::from((x, *y1))) {
|
||||
draw(|to: &mut Tui|{
|
||||
let XYWH(x1, y1, w, _h) = to.area();
|
||||
let x2 = x1 + w;
|
||||
for (i, x) in (x1..=x2).enumerate() {
|
||||
if let Tui::Draw(buf, ..) = to {
|
||||
if let Some(cell) = buf.cell_mut(Position::from((x, y1))) {
|
||||
if i < (ICON_DEC_H.len()) {
|
||||
cell.set_fg(Rgb(255, 255, 255));
|
||||
cell.set_bg(Rgb(0, 0, 0));
|
||||
cell.set_char(ICON_DEC_H[i as usize]);
|
||||
} else if i > (*w as usize - ICON_INC_H.len()) {
|
||||
} else if i > (w as usize - ICON_INC_H.len()) {
|
||||
cell.set_fg(Rgb(255, 255, 255));
|
||||
cell.set_bg(Rgb(0, 0, 0));
|
||||
cell.set_char(ICON_INC_H[*w as usize - i]);
|
||||
cell.set_char(ICON_INC_H[w as usize - i]);
|
||||
} else if false {
|
||||
cell.set_fg(Rgb(255, 255, 255));
|
||||
cell.set_bg(Reset);
|
||||
|
|
@ -30,23 +32,26 @@ pub fn x_scroll () -> impl Draw<Tui> {
|
|||
}
|
||||
}
|
||||
}
|
||||
Ok(Some(XYWH(*x1, *y1, *w, 1)))
|
||||
}
|
||||
Ok(Some(XYWH(x1, y1, w, 1)))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn y_scroll () -> impl Draw<Tui> {
|
||||
draw(|Tui(buf, XYWH(x1, y1, _w, h)): &mut Tui|{
|
||||
let y2 = *y1 + *h;
|
||||
for (i, y) in (*y1..=y2).enumerate() {
|
||||
if let Some(cell) = buf.cell_mut(Position::from((*x1, y))) {
|
||||
draw(|to: &mut Tui|{
|
||||
let XYWH(x1, y1, _w, h) = to.area();
|
||||
let y2 = y1 + h;
|
||||
for (i, y) in (y1..=y2).enumerate() {
|
||||
if let Tui::Draw(buf, ..) = to {
|
||||
if let Some(cell) = buf.cell_mut(Position::from((x1, y))) {
|
||||
if (i as usize) < (ICON_DEC_V.len()) {
|
||||
cell.set_fg(Rgb(255, 255, 255));
|
||||
cell.set_bg(Rgb(0, 0, 0));
|
||||
cell.set_char(ICON_DEC_V[i as usize]);
|
||||
} else if (i as usize) > (*h as usize - ICON_INC_V.len()) {
|
||||
} else if (i as usize) > (h as usize - ICON_INC_V.len()) {
|
||||
cell.set_fg(Rgb(255, 255, 255));
|
||||
cell.set_bg(Rgb(0, 0, 0));
|
||||
cell.set_char(ICON_INC_V[*h as usize - i]);
|
||||
cell.set_char(ICON_INC_V[h as usize - i]);
|
||||
} else if false {
|
||||
cell.set_fg(Rgb(255, 255, 255));
|
||||
cell.set_bg(Reset);
|
||||
|
|
@ -58,7 +63,8 @@ pub fn y_scroll () -> impl Draw<Tui> {
|
|||
}
|
||||
}
|
||||
}
|
||||
Ok(Some(XYWH(*x1, *y1, 1, *h)))
|
||||
}
|
||||
Ok(Some(XYWH(x1, y1, 1, h)))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue