Crate yeehaw

Source
Expand description

yeehaw is a batteries included TUI framework with a rendering strategy, full event routing system and a plethora of elements included.

§Quickstart:

A hello world example with a label and a reactive button:

use yeehaw::*;

#[tokio::main]
async fn main() -> Result<(), Error> {
    let (mut tui, ctx) = Tui::new()?;
    let main_el = ParentPane::new(&ctx, "main_element");

    // place the label at 30% of the screen width and height
    let label = Label::new(&ctx, "Hello, World!").at(0.3, 0.3);

    // place the button 1 character below the label
    let x = DynVal::new_flex(0.3); // 30% screen width
    let y = DynVal::new_flex(0.3).plus(1.into()); // 30% screen height + 1 ch

    let label_ = label.clone(); // clone for closure move
    let button = Button::new(&ctx, "Click Here!")
        .with_fn(Box::new(move |_, _| {
            label_.set_text("Button clicked!".to_string());
            EventResponses::default()
        }))
        .at(x, y);

    main_el.add_element(Box::new(label));
    main_el.add_element(Box::new(button));
    tui.run(Box::new(main_el)).await
}

§Existing Elements:

§Widgets
  • $EDITOR textbox (e.g. ACTUAL neovim… wow!)
  • basic textbox
  • top-down menus
  • right click menu
  • image viewer (thanks to ratatui-image)
  • file viewer
  • file navigator (think nerdtree)
  • terminal (that can open other TUIs! ..dang)
  • figlet fonts (aka MEGA TEXT)
  • buttons
  • checkboxes
  • dropdown-lists
  • labels
  • listboxes
  • radio-buttons
  • scrollbars
  • toggles
  • generalized label decorators on all elements
  • sliders
  • dials
§Containers
  • tabs
  • windows
  • stack panes (think vim-buffers)
  • scrollable panes with scrollbars

§Cool Features

  • mini-TUIs in the CLI (aka. use a TUI in-line with your command without taking up the whole terminal)

§Planned Stuff:

  • embed a whole dang yeehaw TUI into stateful ratatui widget, why not!
  • accordion stack container
  • hover comments for elements
  • vertical tabs (like brave browser)
  • [.ans]-animation player (using an extended-ansi format)
  • optional mouse pixel support (there’s already mouse support)
  • wire-connectors between elements
  • color selector element
  • table element
  • an interactive debugging application for yeehaw TUIs
  • TUI Snapshot Tester
  • drag and drop TUI application builder (as a TUI of course)
  • build out a system of feature-flags for feature restriction / compile time improvement.
  • a sync version of the TUI for those who don’t want async

§Design Overview

Elements are arranged in a hierarchical manner while retaining semi-autonomy. Events (keyboard/mouse/custom) are routed from the top down (adjusted to be relative to the each destination Element) and responses then propagate back upwards along the Element ownership tree. Parent elements retain general authority over child elements; they determine how the flow of events are channeled and the location and size of child elements. Simple elements are only required to have spatial awareness within the confines provided to them (events are made relative to them by their parents) - although autonomy is still given for them to change their ordering and position within their immediate parent element (with great power comes great responsibility).

In addition to the event-response communication structure elements may directly interact through element-specific hooks (e.g. the button click function on a button element).

The core Element Trait has designed to be extensible for custom event/response kinds enabling developers to create entirely new sub-classes of elements which can reuse the event routing system logic.

Looking to understand more? Checkout:

  • Examples
  • Element Trait
  • Pane <- the standard base for all built in elements
  • ParentPane <- the standard base for all elements which hold other elements
  • Context <- an object which can be found nearly everywhere
  • DynVal <- the basis for all layouts and sizes

§Stability, Upcoming Refactors, Bugs

If you plan on building/experimenting with yeehaw right now, that’s great news! I’d like to keep you apprised of some upcoming changes. If you do wish to experiment and or start development on yeehaw I wouldn’t be too afraid of these upcoming changes, the majority of foreseeable major refactors have already been completed. While yeehaw is pre-1.0.0 all breaking changes will bd semver minor version upgrades - in other words I don’t plan on providing patch updates for bug fixes for minor versions.

I’ll try’n help out anyone who needs a hand understanding how to update their code if its been broken by a new release. Additionally a breaking changes doc with upgrade instructions shall be maintained.

HAVE NO FEAR

  • There ain’t much automated testing in here at all, soon a TUI snapshot tester is going to be developed, which should bring up coverage from about 0% as it stands (yikes!).
  • Taffy is going to be integrated in as an extension to the DynLocationSet system. It won’t change the existing location mechanisms just build on top of them.
  • Proper window minimization behaviour is blocking on the Taffy integration such that the minimized windows can follow a nice easy grid pattern. Currently minimization still somewhat works, however multiple minimized windows will stack on each other in the same location.
  • Default colors everywhere are going to be replaced with a defaults in a theme manager. Using the Theme Manager the developer can start from a nice overall default then modify it to their liking. Note the Theme manager will not inhibit users from specifying specific colors anywhere they choose.
  • Gradients on irregular angles are not stable, the goal is to have the gradient actually reflect a visual angle taking into account the width and the height of each cell. Currently the angles work under an assumption of equal cell width and height, sometimes it produces funny/unexpected results for a gradient which has is supposed to just be at a 45-degree angle and occur only once across the whole target area (DynVal::FULL). Gradients on angles which are repetitive (DynVal::fixed(..)) work good, however the way the angle is interpreted will likely change to account for cell dimensions. Gradients on right-angles (0, 90, 180, 270 degrees) are stable.
  • All the fancy color types (gradients, patterns) will likely get refactored and lumped together into a common Trait whereby entirely new classes of colors can be created by developers.

§Performance Considerations

TL;DR - Elements should provide drawing updates only when actually necessary, also things may be slightly laggy while in debug mode if deeply nested containers are used.

The current design of the drawing system favours graphical flexibility over performance. Each element must implement the Drawing function which in turn returns DrawUpdate’s which can list of individual cell updates. For each redraw, container elements will then reposition all the draw information of their respective sub-elements. All this reprocessing which takes place in container elements is computationally inefficient as it occurs with each redraw cycle where changes occur. The inefficiency introduced by the current design may lead to slightly laggy interfaces but primarily only when compiled in debug mode and if deeply nested containers are used AND if those elements are providing continuous drawing updates. Use of parallel computation with rayon has been implemented to help mitigate these inefficiencies. In conclusion, to minimize the computational burden at render time, Elements should track their own state and only respond with drawing updates (in fn drawing(..)) when their are actual state changes which warrant redrawing.

Re-exports§

pub use ch::ChPlus;
pub use ch::DrawCh;
pub use ch::DrawChPos;
pub use ch::DrawChPosVec;
pub use ch::DrawChs2D;
pub use color::Color;
pub use color::ColorStore;
pub use color::Gradient;
pub use color::Pattern;
pub use color::RadialGradient;
pub use color::Rgba;
pub use color::TimeGradient;
pub use context::Context;
pub use draw_region::DrawRegion;
pub use dyn_location::DynLocation;
pub use dyn_location::DynLocationSet;
pub use dyn_location::Loc;
pub use dyn_location::Point;
pub use dyn_location::Size;
pub use dyn_location::ZIndex;
pub use dyn_value::DynVal;
pub use element::DrawAction;
pub use element::DrawUpdate;
pub use element::DrawingCache;
pub use element::Element;
pub use element::HookFn as ElementHookFn;
pub use element::Parent;
pub use elements::border::Corner as BorderCorner;
pub use elements::border::PropertyCnr as BorderPropertyCnr;
pub use errors::Error;
pub use event::CommandEvent;
pub use event::Event;
pub use event::EventResponse;
pub use event::EventResponses;
pub use event::KeyPossibility;
pub use event::MouseEvent;
pub use event::MoveResponse;
pub use event::ReceivableEvent;
pub use event::ReceivableEvents;
pub use event::ResizeResponse;
pub use keyboard::Keyboard;
pub use organizer::ElementOrganizer;
pub use sorting_hat::ElementID;
pub use sorting_hat::SortingHat;
pub use style::Attributes;
pub use style::BgTranspSrc;
pub use style::FgTranspSrc;
pub use style::Style;
pub use style::UlTranspSrc;
pub use tui::Tui;
pub use elements::*;

Modules§

ch
color
context
draw_region
dyn_location
dyn_value
element
elements
errors
event
keyboard
log
organizer
sorting_hat
style
tui

Macros§

debug
log_err

Structs§

Rc
A single-threaded reference-counting pointer. ‘Rc’ stands for ‘Reference Counted’.
Ref
Wraps a borrowed reference to a value in a RefCell box. A wrapper type for an immutably borrowed value from a RefCell<T>.
RefCell
A mutable memory location with dynamically checked borrow rules
RefMut
A wrapper type for a mutably borrowed value from a RefCell<T>.