mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2026-04-03 13:30:44 +02:00
This commit is contained in:
parent
ad070a4cbb
commit
8f0a2accce
13 changed files with 1819 additions and 2157 deletions
|
|
@ -1,148 +1,27 @@
|
|||
pub use self::logical::*; mod logical {
|
||||
use crate::*;
|
||||
use crate::*;
|
||||
|
||||
/// Thunks can be natural error boundaries!
|
||||
///
|
||||
/// ```
|
||||
/// let _ = tengri::ErrorBoundary::<tengri::Tui, &'static str>::new(Ok(Some("hello")));
|
||||
/// let _ = tengri::ErrorBoundary::<tengri::Tui, &'static str>::new(Ok(None));
|
||||
/// let _ = tengri::ErrorBoundary::<tengri::Tui, &'static str>::new(Err("draw fail".into()));
|
||||
/// ```
|
||||
pub struct ErrorBoundary<O: Out, T: Draw<O>>(
|
||||
pub Perhaps<T>,
|
||||
pub PhantomData<O>,
|
||||
);
|
||||
|
||||
/// Late-evaluate a rendering.
|
||||
///
|
||||
/// ```
|
||||
/// let _ = tengri::Thunk::<tengri::Tui, _>::new(|out|{});
|
||||
/// ```
|
||||
pub struct Thunk<O: Out, F: Fn(&mut O)>(
|
||||
pub F,
|
||||
pub PhantomData<O>,
|
||||
);
|
||||
|
||||
/// Memoize a rendering.
|
||||
///
|
||||
/// ```
|
||||
/// let _ = tengri::Memo::new((), ());
|
||||
/// ```
|
||||
#[derive(Debug, Default)] pub struct Memo<T, U> {
|
||||
pub value: T,
|
||||
pub view: Arc<RwLock<U>>
|
||||
}
|
||||
/// Memoize a rendering.
|
||||
///
|
||||
/// ```
|
||||
/// let _ = tengri::Memo::new((), ());
|
||||
/// ```
|
||||
#[derive(Debug, Default)] pub struct Memo<T, U> {
|
||||
pub value: T,
|
||||
pub view: Arc<RwLock<U>>
|
||||
}
|
||||
|
||||
pub use self::spatial::*; mod spatial {
|
||||
use crate::*;
|
||||
|
||||
/// A binary split or layer.
|
||||
///
|
||||
/// ```
|
||||
/// let _ = tengri::Bsp::n((), ());
|
||||
/// let _ = tengri::Bsp::s((), ());
|
||||
/// let _ = tengri::Bsp::e((), ());
|
||||
/// let _ = tengri::Bsp::w((), ());
|
||||
/// let _ = tengri::Bsp::a((), ());
|
||||
/// let _ = tengri::Bsp::b((), ());
|
||||
/// ```
|
||||
pub struct Bsp<Head, Tail>(
|
||||
/// Direction of split
|
||||
pub(crate) Direction,
|
||||
/// First element.
|
||||
pub(crate) Head,
|
||||
/// Second element.
|
||||
pub(crate) Tail,
|
||||
);
|
||||
|
||||
/// 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!(tengri::XYWH(10u16, 10, 20, 20).center(), tengri::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>::default();
|
||||
/// let measure = tengri::Measure::<u16>::default();
|
||||
/// ```
|
||||
#[derive(Default)] pub struct Measure<O: Out> {
|
||||
pub __: PhantomData<O>,
|
||||
#[derive(Default)] pub struct Measure<N: Coord> {
|
||||
pub x: Arc<AtomicUsize>,
|
||||
pub y: Arc<AtomicUsize>,
|
||||
}
|
||||
|
||||
/// Show an item only when a condition is true.
|
||||
///
|
||||
/// ```
|
||||
/// fn test () -> impl tengri::Draw<tengri::Tui> {
|
||||
/// 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> {
|
||||
/// 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)
|
||||
pub __: PhantomData<N>,
|
||||
}
|
||||
|
||||
/// Increment X and/or Y coordinate.
|
||||
|
|
@ -173,20 +52,6 @@ pub use self::spatial::*; mod spatial {
|
|||
/// ```
|
||||
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::Max::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 minimum = 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.
|
||||
///
|
||||
/// ```
|
||||
|
|
@ -247,11 +112,9 @@ pub use self::spatial::*; mod spatial {
|
|||
///
|
||||
/// ```
|
||||
/// use tengri::{Bounded, XYWH};
|
||||
/// let area = XYWH(0, 0, 0, 0);
|
||||
/// let content = "";
|
||||
/// let bounded: Bounded<tengri::Tui, _> = Bounded(area, content);
|
||||
/// let bounded: Bounded<tengri::Tui, _> = Bounded(0, 0 ,0 ,0 ,"");
|
||||
/// ```
|
||||
pub struct Bounded<O: Out, D>(pub XYWH<O::Unit>, pub D);
|
||||
pub struct Bounded<N: Coord, D>(N, N, N, N, pub D);
|
||||
|
||||
/// Draws items from an iterator.
|
||||
///
|
||||
|
|
@ -303,75 +166,44 @@ pub use self::spatial::*; mod spatial {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)] pub struct Exit(Arc<AtomicBool>);
|
||||
impl Exit {
|
||||
pub fn run <T> (run: impl Fn()->Usually<T>) -> Usually<T> {
|
||||
run(Self(Arc::new(AtomicBool::new(false))))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)] pub struct Thread {
|
||||
/// Exit flag.
|
||||
pub exit: Arc<AtomicBool>,
|
||||
/// Performance counter.
|
||||
pub perf: Arc<PerfModel>,
|
||||
/// Use this to wait for the thread to finish.
|
||||
pub join: JoinHandle<()>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "tui")] pub use self::terminal::*;
|
||||
#[cfg(feature = "tui")] mod terminal {
|
||||
use crate::*;
|
||||
|
||||
/// The TUI engine.
|
||||
///
|
||||
/// ```
|
||||
/// # fn main () -> tengri::Usually<()> {
|
||||
/// let tui = tengri::Tui::new(Box::new(vec![0u8;0]))?;
|
||||
/// # Ok(()) }
|
||||
/// ```
|
||||
pub struct Tui {
|
||||
/// Exit flag
|
||||
pub exited: Arc<AtomicBool>,
|
||||
/// Error message
|
||||
pub error: Option<Arc<str>>,
|
||||
/// Performance counter
|
||||
pub perf: PerfModel,
|
||||
/// Ratatui backend
|
||||
pub backend: RwLock<CrosstermBackend<Box<dyn Write + Send + Sync>>>,
|
||||
/// Current tnput event
|
||||
pub event: Option<TuiEvent>,
|
||||
/// Current output buffer
|
||||
pub buffer: RwLock<Buffer>,
|
||||
/// FIXME unused?
|
||||
pub area: XYWH<u16>,
|
||||
}
|
||||
|
||||
#[derive(Debug)] pub struct TuiThread {
|
||||
pub exit: Arc<AtomicBool>,
|
||||
pub perf: Arc<PerfModel>,
|
||||
pub join: JoinHandle<()>,
|
||||
}
|
||||
|
||||
#[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
|
||||
);
|
||||
|
||||
/// TUI input loop event.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)]
|
||||
pub struct TuiEvent(pub Event);
|
||||
/// TUI key spec.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)]
|
||||
pub struct TuiKey(pub Option<KeyCode>, pub KeyModifiers);
|
||||
/// TUI buffer sized by `usize` instead of `u16`.
|
||||
#[derive(Default)] pub struct BigBuffer {
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
pub content: Vec<Cell>
|
||||
}
|
||||
|
||||
// TODO DOCUMENTME
|
||||
pub struct Foreground<Color, Item>(pub Color, pub Item);
|
||||
|
||||
// TODO DOCUMENTME
|
||||
pub struct Background<Color, Item>(pub Color, pub Item);
|
||||
|
||||
pub struct Modify<T>(pub bool, pub Modifier, pub T);
|
||||
|
||||
pub struct Styled<T>(pub Option<Style>, pub T);
|
||||
|
||||
/// 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],
|
||||
}
|
||||
|
||||
pub struct Phat<T> { pub width: u16, pub height: u16, pub content: T, pub colors: [Color;4], }
|
||||
/// A three-column layout.
|
||||
pub struct Tryptich<A, B, C> {
|
||||
pub top: bool,
|
||||
|
|
@ -380,14 +212,8 @@ pub use self::spatial::*; mod spatial {
|
|||
pub middle: (u16, B),
|
||||
pub right: (u16, C),
|
||||
}
|
||||
|
||||
/// Repeat a string, e.g. for background
|
||||
pub enum Repeat<'a> {
|
||||
X(&'a str),
|
||||
Y(&'a str),
|
||||
XY(&'a str)
|
||||
}
|
||||
|
||||
/// Repeat a string, e.g. for background fill
|
||||
pub enum Repeat<'a> { X(&'a str), Y(&'a str), XY(&'a str) }
|
||||
/// Scroll indicator
|
||||
pub enum Scrollbar {
|
||||
/// Horizontal scrollbar
|
||||
|
|
@ -411,86 +237,6 @@ mod textual {
|
|||
|
||||
#[cfg(feature = "jack")] pub use self::aural::*;
|
||||
#[cfg(feature = "jack")] mod aural {
|
||||
use crate::*;
|
||||
use ::jack::*;
|
||||
|
||||
/// Wraps [JackState], and through it [jack::Client] when connected.
|
||||
///
|
||||
/// ```
|
||||
/// let jack = tengri::Jack::default();
|
||||
/// ```
|
||||
#[derive(Clone, Debug, Default)] pub struct Jack<'j> (
|
||||
pub(crate) Arc<RwLock<JackState<'j>>>
|
||||
);
|
||||
|
||||
/// This is a connection which may be [Inactive], [Activating], or [Active].
|
||||
/// In the [Active] and [Inactive] states, [JackState::client] returns a
|
||||
/// [jack::Client], which you can use to talk to the JACK API.
|
||||
///
|
||||
/// ```
|
||||
/// let state = tengri::JackState::default();
|
||||
/// ```
|
||||
#[derive(Debug, Default)] pub enum JackState<'j> {
|
||||
/// Unused
|
||||
#[default] Inert,
|
||||
/// Before activation.
|
||||
Inactive(Client),
|
||||
/// During activation.
|
||||
Activating,
|
||||
/// After activation. Must not be dropped for JACK thread to persist.
|
||||
Active(DynamicAsyncClient<'j>),
|
||||
}
|
||||
|
||||
/// Event enum for JACK events.
|
||||
///
|
||||
/// ```
|
||||
/// let event = tengri::JackEvent::XRun;
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq)] pub enum JackEvent {
|
||||
ThreadInit,
|
||||
Shutdown(ClientStatus, Arc<str>),
|
||||
Freewheel(bool),
|
||||
SampleRate(Frames),
|
||||
ClientRegistration(Arc<str>, bool),
|
||||
PortRegistration(PortId, bool),
|
||||
PortRename(PortId, Arc<str>, Arc<str>),
|
||||
PortsConnected(PortId, PortId, bool),
|
||||
GraphReorder,
|
||||
XRun,
|
||||
}
|
||||
|
||||
/// Generic notification handler that emits [JackEvent]
|
||||
///
|
||||
/// ```
|
||||
/// let notify = tengri::JackNotify(|_|{});
|
||||
/// ```
|
||||
pub struct JackNotify<T: Fn(JackEvent) + Send>(pub T);
|
||||
|
||||
/// Running JACK [AsyncClient] with maximum type erasure.
|
||||
///
|
||||
/// One [Box] contains function that handles [JackEvent]s.
|
||||
///
|
||||
/// Another [Box] containing a function that handles realtime IO.
|
||||
///
|
||||
/// That's all it knows about them.
|
||||
pub type DynamicAsyncClient<'j>
|
||||
= AsyncClient<DynamicNotifications<'j>, DynamicAudioHandler<'j>>;
|
||||
|
||||
/// Notification handler wrapper for [BoxedAudioHandler].
|
||||
pub type DynamicAudioHandler<'j> =
|
||||
::jack::contrib::ClosureProcessHandler<(), BoxedAudioHandler<'j>>;
|
||||
|
||||
/// Boxed realtime callback.
|
||||
pub type BoxedAudioHandler<'j> =
|
||||
Box<dyn FnMut(&Client, &ProcessScope) -> Control + Send + Sync + 'j>;
|
||||
|
||||
/// Notification handler wrapper for [BoxedJackEventHandler].
|
||||
pub type DynamicNotifications<'j> =
|
||||
JackNotify<BoxedJackEventHandler<'j>>;
|
||||
|
||||
/// Boxed [JackEvent] callback.
|
||||
pub type BoxedJackEventHandler<'j> =
|
||||
Box<dyn Fn(JackEvent) + Send + Sync + 'j>;
|
||||
}
|
||||
|
||||
pub use self::temporal::*; mod temporal {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue