#![doc = include_str!("../readme.md")]
use crossbeam::channel::{SendError, Sender};
use rat_widget::button::ButtonOutcome;
use rat_widget::event::{
CalOutcome, ConsumedEvent, DoubleClickOutcome, EditOutcome, FileOutcome, Outcome,
ScrollOutcome, TabbedOutcome, TextOutcome,
};
use rat_widget::menuline::MenuOutcome;
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use std::fmt::Debug;
pub(crate) mod control_queue;
mod framework;
pub mod poll;
pub(crate) mod poll_queue;
mod run_config;
pub mod terminal;
mod threadpool;
pub mod timer;
use crate::control_queue::ControlQueue;
use crate::threadpool::ThreadPool;
use crate::timer::{TimeOut, TimerDef, TimerHandle, Timers};
use rat_widget::focus::Focus;
pub use framework::*;
pub use run_config::*;
pub use threadpool::Cancel;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[must_use]
pub enum Control<Message> {
Continue,
Unchanged,
Changed,
Message(Message),
Quit,
}
impl<Message> ConsumedEvent for Control<Message> {
fn is_consumed(&self) -> bool {
!matches!(self, Control::Continue)
}
}
impl<Message> From<Outcome> for Control<Message> {
fn from(value: Outcome) -> Self {
match value {
Outcome::Continue => Control::Continue,
Outcome::Unchanged => Control::Unchanged,
Outcome::Changed => Control::Changed,
}
}
}
impl<Message> From<bool> for Control<Message> {
fn from(value: bool) -> Self {
Outcome::from(value).into()
}
}
impl<Message> From<MenuOutcome> for Control<Message> {
fn from(value: MenuOutcome) -> Self {
Outcome::from(value).into()
}
}
impl<Message> From<ButtonOutcome> for Control<Message> {
fn from(value: ButtonOutcome) -> Self {
Outcome::from(value).into()
}
}
impl<Message> From<TextOutcome> for Control<Message> {
fn from(value: TextOutcome) -> Self {
Outcome::from(value).into()
}
}
impl<Message> From<ScrollOutcome> for Control<Message> {
fn from(value: ScrollOutcome) -> Self {
Outcome::from(value).into()
}
}
impl<Message> From<DoubleClickOutcome> for Control<Message> {
fn from(value: DoubleClickOutcome) -> Self {
Outcome::from(value).into()
}
}
impl<Message> From<EditOutcome> for Control<Message> {
fn from(value: EditOutcome) -> Self {
Outcome::from(value).into()
}
}
impl<Message> From<FileOutcome> for Control<Message> {
fn from(value: FileOutcome) -> Self {
Outcome::from(value).into()
}
}
impl<Message> From<TabbedOutcome> for Control<Message> {
fn from(value: TabbedOutcome) -> Self {
Outcome::from(value).into()
}
}
impl<Message> From<CalOutcome> for Control<Message> {
fn from(value: CalOutcome) -> Self {
Outcome::from(value).into()
}
}
#[allow(unused_variables)]
pub trait AppWidget<Global, Message, Error>
where
Message: 'static + Send + Debug,
Error: 'static + Send + Debug,
{
type State: AppState<Global, Message, Error> + Debug;
fn render(
&self,
area: Rect,
buf: &mut Buffer,
state: &mut Self::State,
ctx: &mut RenderContext<'_, Global>,
) -> Result<(), Error>;
}
#[allow(unused_variables)]
pub trait AppState<Global, Message, Error>
where
Message: 'static + Send + Debug,
Error: 'static + Send + Debug,
{
fn init(&mut self, ctx: &mut AppContext<'_, Global, Message, Error>) -> Result<(), Error> {
Ok(())
}
fn timer(
&mut self,
event: &TimeOut,
ctx: &mut AppContext<'_, Global, Message, Error>,
) -> Result<Control<Message>, Error> {
Ok(Control::Continue)
}
fn crossterm(
&mut self,
event: &crossterm::event::Event,
ctx: &mut AppContext<'_, Global, Message, Error>,
) -> Result<Control<Message>, Error> {
Ok(Control::Continue)
}
fn message(
&mut self,
event: &mut Message,
ctx: &mut AppContext<'_, Global, Message, Error>,
) -> Result<Control<Message>, Error> {
Ok(Control::Continue)
}
fn error(
&self,
event: Error,
ctx: &mut AppContext<'_, Global, Message, Error>,
) -> Result<Control<Message>, Error> {
Ok(Control::Continue)
}
}
#[derive(Debug)]
pub struct AppContext<'a, Global, Message, Error>
where
Message: 'static + Send + Debug,
Error: 'static + Send + Debug,
{
pub g: &'a mut Global,
pub focus: Option<Focus>,
pub(crate) timers: &'a Timers,
pub(crate) tasks: &'a ThreadPool<Message, Error>,
pub(crate) queue: &'a ControlQueue<Message, Error>,
}
#[derive(Debug)]
pub struct RenderContext<'a, Global> {
pub g: &'a mut Global,
pub timeout: Option<TimeOut>,
pub count: usize,
pub cursor: Option<(u16, u16)>,
pub(crate) timers: &'a Timers,
}
impl<'a, Global, Message, Error> AppContext<'a, Global, Message, Error>
where
Message: 'static + Send + Debug,
Error: 'static + Send + Debug,
{
#[inline]
pub fn add_timer(&self, t: TimerDef) -> TimerHandle {
self.timers.add(t)
}
#[inline]
pub fn remove_timer(&self, tag: TimerHandle) {
self.timers.remove(tag);
}
#[inline]
pub fn replace_timer(&self, h: Option<TimerHandle>, t: TimerDef) -> TimerHandle {
if let Some(h) = h {
self.remove_timer(h);
}
self.add_timer(t)
}
#[inline]
pub fn spawn(
&self,
task: impl FnOnce(
Cancel,
&Sender<Result<Control<Message>, Error>>,
) -> Result<Control<Message>, Error>
+ Send
+ 'static,
) -> Result<Cancel, SendError<()>>
where
Message: 'static + Send + Debug,
Error: 'static + Send + Debug,
{
self.tasks.send(Box::new(task))
}
#[inline]
pub fn queue(&self, ctrl: impl Into<Control<Message>>) {
self.queue.push(Ok(ctrl.into()), None);
}
#[inline]
pub fn queue_err(&self, err: Error) {
self.queue.push(Err(err), None);
}
#[inline]
pub fn focus(&self) -> &Focus {
self.focus.as_ref().expect("focus")
}
#[inline]
pub fn focus_mut(&mut self) -> &mut Focus {
self.focus.as_mut().expect("focus")
}
}
impl<'a, Global> RenderContext<'a, Global> {
#[inline]
pub fn add_timer(&self, t: TimerDef) -> TimerHandle {
self.timers.add(t)
}
#[inline]
pub fn remove_timer(&self, tag: TimerHandle) {
self.timers.remove(tag)
}
#[inline]
pub fn replace_timer(&self, h: Option<TimerHandle>, t: TimerDef) -> TimerHandle {
if let Some(h) = h {
self.remove_timer(h);
}
self.add_timer(t)
}
pub fn set_screen_cursor(&mut self, cursor: Option<(u16, u16)>) {
if let Some(c) = cursor {
self.cursor = Some(c);
}
}
}
pub mod event {
pub use rat_widget::event::{ct_event, try_flow};
}