glyph_ui 0.1.0

TUI library utilizing the Elm architecture
Documentation
//! Glyph User Interface
//!
//! Not your typical GUI library: Glyph UI is actually for implementing
//! text-based interfaces. In particular, its usage is designed to result in
//! maintainable applications even at large numbers of components by leveraging
//! [the Elm architecture].
//!
//! [the Elm architecture]: https://guide.elm-lang.org/architecture/
//!
//! # Examples
//!
//! Glyph UI's hello world isn't the most succinct, but that's a non-goal. With
//! that understanding, let's get started by setting up our imports:
//!
//! ```
//! use glyph_ui::{
//!     // Here we import the absolutely necessary items to implement the `Gui`
//!     // trait and start the `Runtime`
//!     prelude::*,
//!
//!     // The `view` module contains various premade views
//!     view::{
//!         // This gives us extensions to the `View` trait; we'll use two later
//!         prelude::*,
//!
//!         // Like all modules inside the `view` module (except `prelude`),
//!         // this contains a `View` trait implementer as well as a shorthand
//!         // for instantiating it. Some modules also have a `State` (think
//!         // "model") and/or other items for configuring the view's behavior
//!         text,
//!     },
//!
//!     // This type describes view-level events, which are not to be confused
//!     // with the Elm architecture's concept of "messages"
//!     event::Event,
//! };
//!
//! // We'll use this later for detecting specific keypresses
//! use keyboard_types::Key;
//! ```
//!
//! Next, let's define our model:
//!
//! ```
//! struct HelloWorld {
//!     // Keep track of whether it's time to exit the application
//!     shutdown: bool,
//! }
//! ```
//!
//! Now we can implement the [`Gui`](crate::Gui) trait for our model, which
//! describes what our view will look like, how to handle messages, and when to
//! exit the application:
//!
//! ```
//! # use glyph_ui::{
//! #    prelude::*,
//! #    event::Event,
//! #    view::{
//! #        prelude::*,
//! #        text,
//! #    },
//! # };
//! # use keyboard_types::Key;
//! # struct HelloWorld {
//! #     shutdown: bool,
//! # }
//! impl Gui for HelloWorld {
//!     // Our message type will be `bool`, which we'll set during updates to
//!     // to be read when queried about control flow
//!     type Message = bool;
//!
//!     // Glyph UI's event type has a variant in which a custom type can be
//!     // produced. This is a more advanced feature that we don't need at the
//!     // moment, so the unit type will do
//!     type Event = ();
//!
//!     // In this function, we build up our application's appearance by
//!     // combining views
//!     fn view(&mut self) -> element::View<Self::Event, Self::Message> {
//!         // It's about time we actually wrote the hello world part, isn't it?
//!         text::new("Hello, world!")
//!             // This is one of the two extension functions mentioned earlier.
//!             // It allows us to wrap a view in another view that makes it
//!             // easier to respond to events (not messages) in a custom manner
//!             .on_event(|e, _f| {
//!                 // Detect whether the user pressed the 'q' key, for "quit"
//!                 if let Event::Key(k) = e {
//!                     if let Key::Character(c) = &k.key {
//!                         if c == "q" {
//!                             // Views can produce multiple messages from a
//!                             // single event, but we only need to produce one
//!                             return Box::new(std::iter::once(true));
//!                         }
//!                     }
//!                 }
//!
//!                 // Some other key was pressed, so we produce no messages
//!                 Box::new(std::iter::empty())
//!             })
//!             // This is the second extension function. It turns a view into
//!             // a more generic object, which is useful for defining
//!             // abstraction layers between UI components. It also happens to
//!             // be the required return type of `Gui::view()`
//!             .into_element()
//!     }
//!
//!     // This function will only get called when there are new messages
//!     fn update(&mut self, m: Self::Message) {
//!         // Update our internal state
//!         self.shutdown = m;
//!     }
//!
//!     // This function gets called after `update`, which means it also only
//!     // runs when there are new messages
//!     fn control_flow(&self) -> ControlFlow {
//!         if self.shutdown {
//!             // If the user pressed 'q', we'll have received a message of
//!             // `true` in `update`, which means it's time to exit
//!             ControlFlow::Exit
//!         } else {
//!             // Otherwise, we continue to run the event loop, waiting until
//!             // the next message
//!             ControlFlow::Wait
//!         }
//!     }
//! }
//! ```
//!
//! Nearly there! The final piece is to create and start the
//! [`Runtime`](crate::Runtime), which we'll do now:
//!
//! ```no_run
//! # use glyph_ui::{
//! #    prelude::*,
//! #    event::Event,
//! #    view::{
//! #        prelude::*,
//! #        text,
//! #    },
//! # };
//! # use keyboard_types::Key;
//! # struct HelloWorld {
//! #     shutdown: bool,
//! # }
//! # impl Gui for HelloWorld {
//! #     type Message = bool;
//! #     type Event = ();
//! #     fn view(&mut self) -> element::View<Self::Event, Self::Message> {
//! #         text::new("Hello, world!")
//! #             .on_event(|e, _f| {
//! #                 if let Event::Key(k) = e {
//! #                     if let Key::Character(c) = &k.key {
//! #                         if c == "q" {
//! #                             return Box::new(std::iter::once(true));
//! #                         }
//! #                     }
//! #                 }
//! #                 Box::new(std::iter::empty())
//! #             })
//! #             .into_element()
//! #     }
//! #     fn update(&mut self, m: Self::Message) {
//! #         self.shutdown = m;
//! #     }
//! #     fn control_flow(&self) -> ControlFlow {
//! #         if self.shutdown {
//! #             ControlFlow::Exit
//! #         } else {
//! #             ControlFlow::Wait
//! #         }
//! #     }
//! # }
//! #[tokio::main]
//! async fn main() {
//!     // Instantiate our model
//!     let gui = HelloWorld {
//!         shutdown: false,
//!     };
//!
//!     // Create and start the runtime
//!     Runtime::new(gui, |task| tokio::spawn(task)).run().await;
//! }
//! ```
//!
//! Now if we compile and run our code, we'll see `Hello, world!` in the top
//! left corner of our terminal, and we'll get our shell back if we press the
//! `q` key. For more examples that demonstrate more complex applications, see
//! [here](https://forge.typ3.tech/charles/glyph_ui/-/tree/master/examples).

use std::sync::Arc;

use parking_lot::Mutex;

mod command;
pub mod event;
mod gui;
mod printer;
mod runtime;
pub mod unit;
pub mod view;
mod view_trait;

/// Prelude for commonly used items
pub mod prelude {
    pub use crate::{view::element, ControlFlow, Gui, Runtime};
}

pub use gui::Gui;
pub use printer::{OutOfBounds, Printer};
pub use runtime::{ControlFlow, Runtime};
pub use view_trait::View;

pub(crate) type Amv<T> = Arc<Mutex<Vec<T>>>;
pub(crate) type Am<T> = Arc<Mutex<T>>;
pub(crate) use command::{Command, CommandBuf};