mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2026-02-21 10:39:03 +01:00
393 lines
10 KiB
Rust
393 lines
10 KiB
Rust
#[cfg(test)] use proptest_derive::Arbitrary;
|
|
use crate::*;
|
|
|
|
/// The `Tui` struct (the *engine*) implements the
|
|
/// `tengri_input::Input` and `tengri_output::Out` traits.
|
|
/// At launch, the `Tui` engine spawns two threads, the render thread and the input thread.
|
|
/// the application may further spawn other threads. All threads communicate using shared ownership:
|
|
/// `Arc<RwLock<T>>` and `Arc<AtomicT>`. Thus, at launch the engine and application instances are expected to be wrapped in `Arc<RwLock>`.
|
|
pub struct Tui {
|
|
pub exited: Arc<AtomicBool>,
|
|
pub backend: CrosstermBackend<Stdout>,
|
|
pub buffer: Buffer,
|
|
pub area: [u16;4],
|
|
pub perf: PerfModel,
|
|
}
|
|
|
|
#[derive(Debug, Clone)] pub struct TuiIn {
|
|
/// Input event
|
|
pub event: TuiEvent,
|
|
/// Exit flag
|
|
pub exited: Arc<AtomicBool>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)] pub struct TuiEvent(
|
|
pub Event
|
|
);
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)] pub struct TuiKey(
|
|
pub Option<KeyCode>,
|
|
pub KeyModifiers
|
|
);
|
|
|
|
#[derive(Default)] pub struct TuiOut {
|
|
pub buffer: Buffer,
|
|
pub area: XYWH<u16>,
|
|
}
|
|
|
|
/// TUI buffer sized by `usize` instead of `u16`.
|
|
#[derive(Default)] pub struct BigBuffer {
|
|
pub width: usize,
|
|
pub height: usize,
|
|
pub content: Vec<Cell>
|
|
}
|
|
|
|
/// A color in OKHSL and RGB representations.
|
|
#[derive(Debug, Default, Copy, Clone, PartialEq)] pub struct ItemColor {
|
|
pub okhsl: Okhsl<f32>,
|
|
pub rgb: Color,
|
|
}
|
|
/// A color in OKHSL and RGB with lighter and darker variants.
|
|
#[derive(Debug, Default, Copy, Clone, PartialEq)] pub struct ItemTheme {
|
|
pub base: ItemColor,
|
|
pub light: ItemColor,
|
|
pub lighter: ItemColor,
|
|
pub lightest: ItemColor,
|
|
pub dark: ItemColor,
|
|
pub darker: ItemColor,
|
|
pub darkest: ItemColor,
|
|
}
|
|
|
|
pub struct Modify<T>(pub bool, pub Modifier, pub T);
|
|
|
|
pub struct Styled<T>(pub Option<Style>, pub 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 TrimStringRef<'a, T: AsRef<str>>(pub u16, pub &'a T);
|
|
|
|
/// Thunks can be natural error boundaries!
|
|
pub struct ErrorBoundary<O: Out, T: Draw<O>>(
|
|
pub std::marker::PhantomData<O>,
|
|
pub Perhaps<T>
|
|
);
|
|
|
|
|
|
/// A point (X, Y).
|
|
///
|
|
/// ```
|
|
/// let xy = tengri::XY(0u16, 0);
|
|
/// ```
|
|
#[cfg_attr(test, derive(Arbitrary))]
|
|
#[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct XY<C: Coord>(
|
|
pub C, pub C
|
|
);
|
|
|
|
/// A size (Width, Height).
|
|
///
|
|
/// ```
|
|
/// let wh = tengri::WH(0u16, 0);
|
|
/// ```
|
|
#[cfg_attr(test, derive(Arbitrary))]
|
|
#[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct WH<C: Coord>(
|
|
pub C, pub C
|
|
);
|
|
|
|
/// Point with size.
|
|
///
|
|
/// ```
|
|
/// let xywh = tengri::XYWH(0u16, 0, 0, 0);
|
|
/// assert_eq!(XYWH(10u16, 10, 20, 20).center(), XY(20, 20));
|
|
/// ```
|
|
///
|
|
/// * [ ] TODO: anchor field (determines at which corner/side is X0 Y0)
|
|
///
|
|
#[cfg_attr(test, derive(Arbitrary))]
|
|
#[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct XYWH<C: Coord>(
|
|
pub C, pub C, pub C, pub C
|
|
);
|
|
|
|
/// A cardinal direction.
|
|
///
|
|
/// ```
|
|
/// let direction = tengri::Direction::Above;
|
|
/// ```
|
|
#[cfg_attr(test, derive(Arbitrary))]
|
|
#[derive(Copy, Clone, PartialEq, Debug)] pub enum Direction {
|
|
North, South, East, West, Above, Below
|
|
}
|
|
|
|
/// 9th of area to place.
|
|
///
|
|
/// ```
|
|
/// let alignment = tengri::Alignment::Center;
|
|
/// ```
|
|
#[cfg_attr(test, derive(Arbitrary))]
|
|
#[derive(Debug, Copy, Clone, Default)] pub enum Alignment {
|
|
#[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W
|
|
}
|
|
|
|
/// A widget that tracks its rendered width and height.
|
|
///
|
|
/// ```
|
|
/// let measure = tengri::Measure::<tengri::tui::TuiOut>::default();
|
|
/// ```
|
|
#[derive(Default)] pub struct Measure<O: Out> {
|
|
pub __: PhantomData<O>,
|
|
pub x: Arc<AtomicUsize>,
|
|
pub y: Arc<AtomicUsize>,
|
|
}
|
|
|
|
/// Show an item only when a condition is true.
|
|
///
|
|
/// ```
|
|
/// fn test () -> impl tengri::Draw<tengri::tui::TuiOut> {
|
|
/// tengri::when(true, "Yes")
|
|
/// }
|
|
/// ```
|
|
pub struct When<O, T>(pub bool, pub T, pub PhantomData<O>);
|
|
pub const fn when<O, T>(condition: bool, content: T) -> When<O, T> {
|
|
When(condition, content, PhantomData)
|
|
}
|
|
|
|
/// Show one item if a condition is true and another if the condition is false.
|
|
///
|
|
/// ```
|
|
/// fn test () -> impl tengri::Draw<tengri::tui::TuiOut> {
|
|
/// tengri::either(true, "Yes", "No")
|
|
/// }
|
|
/// ```
|
|
pub struct Either<E, A, B>(pub bool, pub A, pub B, pub PhantomData<E>);
|
|
pub const fn either<E, A, B>(condition: bool, content_a: A, content_b: B) -> Either<E, A, B> {
|
|
Either(condition, content_a, content_b, PhantomData)
|
|
}
|
|
|
|
/// Increment X and/or Y coordinate.
|
|
///
|
|
/// ```
|
|
/// let pushed = tengri::Push::XY(2, 2, "Hello");
|
|
/// ```
|
|
pub enum Push<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
|
|
/// Decrement X and/or Y coordinate.
|
|
///
|
|
/// ```
|
|
/// let pulled = tengri::Pull::XY(2, 2, "Hello");
|
|
/// ```
|
|
pub enum Pull<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
|
|
/// Set the content to fill the container.
|
|
///
|
|
/// ```
|
|
/// let filled = tengri::Fill::XY("Hello");
|
|
/// ```
|
|
pub enum Fill<A> { X(A), Y(A), XY(A) }
|
|
|
|
/// Set fixed size for content.
|
|
///
|
|
/// ```
|
|
/// let fixed = tengri::Fixed::XY(3, 5, "Hello"); // 3x5
|
|
/// ```
|
|
pub enum Fixed<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
|
|
/// Set the maximum width and/or height of the content.
|
|
///
|
|
/// ```
|
|
/// let maximum = tengri::Min::XY(3, 5, "Hello"); // 3x1
|
|
/// ```
|
|
pub enum Max<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
|
|
/// Set the minimum width and/or height of the content.
|
|
///
|
|
/// ```
|
|
/// let minimam = tengri::Min::XY(3, 5, "Hello"); // 5x5
|
|
/// ```
|
|
pub enum Min<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
|
|
/// Decrease the width and/or height of the content.
|
|
///
|
|
/// ```
|
|
/// let shrunk = tengri::Shrink::XY(2, 0, "Hello"); // 1x1
|
|
/// ```
|
|
pub enum Shrink<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
|
|
/// Increaase the width and/or height of the content.
|
|
///
|
|
/// ```
|
|
/// let expanded = tengri::Expand::XY(5, 3, "HELLO"); // 15x3
|
|
/// ```
|
|
pub enum Expand<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
|
|
/// Align position of inner area to middle, side, or corner of outer area.
|
|
///
|
|
///
|
|
/// ```
|
|
/// use ::tengri::{output::*, tui::*};
|
|
/// let area = XYWH(10u16, 10, 20, 20);
|
|
/// fn test (area: XYWH<u16>, item: &impl Draw<TuiOut>, expected: [u16;4]) {
|
|
/// //assert_eq!(Lay::layout(item, area), expected);
|
|
/// //assert_eq!(Draw::layout(item, area), expected);
|
|
/// };
|
|
///
|
|
/// let four = ||Fixed::XY(4, 4, "");
|
|
/// test(area, &Align::nw(four()), [10, 10, 4, 4]);
|
|
/// test(area, &Align::n(four()), [18, 10, 4, 4]);
|
|
/// test(area, &Align::ne(four()), [26, 10, 4, 4]);
|
|
/// test(area, &Align::e(four()), [26, 18, 4, 4]);
|
|
/// test(area, &Align::se(four()), [26, 26, 4, 4]);
|
|
/// test(area, &Align::s(four()), [18, 26, 4, 4]);
|
|
/// test(area, &Align::sw(four()), [10, 26, 4, 4]);
|
|
/// test(area, &Align::w(four()), [10, 18, 4, 4]);
|
|
///
|
|
/// let two_by_four = ||Fixed::XY(4, 2, "");
|
|
/// test(area, &Align::nw(two_by_four()), [10, 10, 4, 2]);
|
|
/// test(area, &Align::n(two_by_four()), [18, 10, 4, 2]);
|
|
/// test(area, &Align::ne(two_by_four()), [26, 10, 4, 2]);
|
|
/// test(area, &Align::e(two_by_four()), [26, 19, 4, 2]);
|
|
/// test(area, &Align::se(two_by_four()), [26, 28, 4, 2]);
|
|
/// test(area, &Align::s(two_by_four()), [18, 28, 4, 2]);
|
|
/// test(area, &Align::sw(two_by_four()), [10, 28, 4, 2]);
|
|
/// test(area, &Align::w(two_by_four()), [10, 19, 4, 2]);
|
|
/// ```
|
|
pub struct Align<T>(pub Alignment, pub T);
|
|
|
|
// TODO DOCUMENTME
|
|
pub enum Pad<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
|
|
/// TODO DOCUMENTME
|
|
///
|
|
/// ```
|
|
/// use tengri::{Bounded, XYWH};
|
|
/// let area = XYWH(0, 0, 0, 0);
|
|
/// let content = "";
|
|
/// let bounded: Bounded<tengri::tui::TuiOut, _> = Bounded(area, content);
|
|
/// ```
|
|
pub struct Bounded<O: Out, D>(pub XYWH<O::Unit>, pub D);
|
|
|
|
/// Draws items from an iterator.
|
|
///
|
|
/// ```
|
|
/// // FIXME let map = tengri::Map(||[].iter(), |_|{});
|
|
/// ```
|
|
pub struct Map<O, A, B, I, F, G>
|
|
where
|
|
I: Iterator<Item = A> + Send + Sync,
|
|
F: Fn() -> I + Send + Sync,
|
|
{
|
|
/// Function that returns iterator over stacked components
|
|
pub get_iter: F,
|
|
/// Function that returns each stacked component
|
|
pub get_item: G,
|
|
|
|
pub __: PhantomData<(O, B)>,
|
|
}
|
|
|
|
// TODO DOCUMENTME
|
|
pub struct Lazy<O, T, F>(
|
|
pub F,
|
|
pub PhantomData<(O, T)>
|
|
);
|
|
|
|
// TODO DOCUMENTME
|
|
pub struct Thunk<O: Out, F: Fn(&mut O)>(
|
|
pub PhantomData<O>,
|
|
pub F
|
|
);
|
|
|
|
// TODO DOCUMENTME
|
|
#[derive(Debug, Default)] pub struct Memo<T, U> {
|
|
pub value: T,
|
|
pub view: Arc<RwLock<U>>
|
|
}
|
|
|
|
/// A binary split or layer.
|
|
pub struct Bsp<Head, Tail>(
|
|
/// Direction of split
|
|
pub(crate) Direction,
|
|
/// First element.
|
|
pub(crate) Head,
|
|
/// Second element.
|
|
pub(crate) Tail,
|
|
);
|
|
|
|
// TODO DOCUMENTME
|
|
pub struct Bordered<S, W>(pub bool, pub S, pub W);
|
|
|
|
// TODO DOCUMENTME
|
|
pub struct Border<S>(pub bool, pub S);
|
|
|
|
// TODO DOCUMENTME
|
|
pub struct Foreground<Color, Item>(pub Color, pub Item);
|
|
|
|
// TODO DOCUMENTME
|
|
pub struct Background<Color, Item>(pub Color, pub Item);
|
|
|
|
// TODO DOCUMENTME
|
|
pub struct FieldH<Theme, Label, Value>(pub Theme, pub Label, pub Value);
|
|
|
|
// TODO DOCUMENTME
|
|
pub struct FieldV<Theme, Label, Value>(pub Theme, pub Label, pub Value);
|
|
|
|
/// A three-column layout.
|
|
pub struct Tryptich<A, B, C> {
|
|
pub top: bool,
|
|
pub h: u16,
|
|
pub left: (u16, A),
|
|
pub middle: (u16, B),
|
|
pub right: (u16, C),
|
|
}
|
|
|
|
// TODO:
|
|
pub struct Field<C, T, U> {
|
|
pub direction: Direction,
|
|
pub label: Option<T>,
|
|
pub label_fg: Option<C>,
|
|
pub label_bg: Option<C>,
|
|
pub label_align: Option<Direction>,
|
|
pub value: Option<U>,
|
|
pub value_fg: Option<C>,
|
|
pub value_bg: Option<C>,
|
|
pub value_align: Option<Direction>,
|
|
}
|
|
|
|
/// Performance counter
|
|
#[derive(Debug)]
|
|
pub struct PerfModel {
|
|
pub enabled: bool,
|
|
|
|
pub clock: quanta::Clock,
|
|
// In nanoseconds. Time used by last iteration.
|
|
pub used: AtomicF64,
|
|
// In microseconds. Max prescribed time for iteration (frame, chunk...).
|
|
pub window: AtomicF64,
|
|
}
|
|
|
|
/// Repeat a string, e.g. for background
|
|
pub enum Repeat<'a> {
|
|
X(&'a str),
|
|
Y(&'a str),
|
|
XY(&'a str)
|
|
}
|
|
|
|
/// Scroll indicator
|
|
pub enum Scrollbar {
|
|
/// Horizontal scrollbar
|
|
X { offset: usize, length: usize, total: usize, },
|
|
/// Vertical scrollbar
|
|
Y { offset: usize, length: usize, total: usize, }
|
|
}
|
|
|
|
/// A cell that takes up 3 rows on its own,
|
|
/// but stacks, giving (N+1)*2 rows per N cells.
|
|
pub struct Phat<T> {
|
|
pub width: u16,
|
|
pub height: u16,
|
|
pub content: T,
|
|
pub colors: [Color;4],
|
|
}
|