2 releases

new 0.21.2 Jun 15, 2024
0.21.0 Jun 10, 2024

#338 in Command-line interface

Download history 366/week @ 2024-06-10

366 downloads per month

MIT/Apache

42KB
804 lines

rat-salsa

An application event-loop with ratatui and crossterm.

companion crates

rat-salsa covers only the event-loop and application building.

There is more:

  • rat-widget widget library. +focus-handling +scrolling +ratatui-wrappers
    • button
    • calender
    • date-input, number-input
    • text-input
    • text-input with masks
    • text-area
    • menuline
    • table
    • ... more to come ...
  • rat-scrolled scrolling for widgets, stateful widgets. viewports.
    • Scrolled widget + support traits
    • View and Viewport widget for Widget/StatefulWidget -> reexported by rat-widget
  • rat-input baseline implementation of the widgets without strapped on focus-handling and without the scrolling traits. Should be compatible with any existing ratatui application. Can be hooked into your own focus-handling. Widgets have builtin scrolling where useful, just the trait impl from rat-scrolled are missing. -> wrapped up & reexported by rat-widget
  • rat-ftable table implementation mostly api-compatible with the ratatui table.
    • Adds TableData and TableDataIter traits which allow it to render only the visible cells. Rendering the individual cells is solely done by these traits, so you can render whatever. Have tried it with 1,000,000 rows and worked nicely. It also supports rendering endless iterators with some restrictions.
    • Pluggable selection-models. Builtin are NoSelection, RowSelection, RowSetSelection and CellSelection.
    • Currently, it has column-wise horizontal scrolling. Plans are to extend this to char-wise scrolling.
    • There is a FEditTable widget too, which supports inline editing of the table-data.
  • rat-focus Defines the primitives for focus-handling as used by rat-widget.
    • Can collect data from sub-widgets/container like widgets.
    • Can support widget-groups with a collective focus-state.
    • Easy to add to existing widgets: Add FocusFlag to your state & impl the trait HasFocusFlag.
    • Lost & Gained flags for logic.
  • rat-event Defines the primitives for event-handling used by all of the above.
    • Build around HandleEvent<EventType, Qualifier, Outcome>.
      • open for any type of event
      • qualifier can be many things
        • Allows for a type-state pattern, predefined types for this are FocusKey and MouseOnly, but the other libraries define their own (DoubleClick, EditKeys, ReadOnly)
        • Applications can override the keybindings for every widget if needed.
        • Can be used as a Context-Parameter if needed.
      • open outcome of event-handling allows widgets to return whatever they need to.
    • There is a very basic type Outcome with
      • NotUsed - Event not recognized.
      • Unchanged - Event recognized, but no changes.
      • Changed - Event recognized, state has changed. Please repaint. It is encouraged for other outcome-types to provide conversions to and from this type. That makes life much easier for users, as everything is just one .into() away :)
    • A control-flow macro flow! which allows to break event-handling as soon as a responsible widget has been found.
  • rat-salsa Implements the event-loop as a single function call to run_tui().
    • Defines the traits AppWidget and AppEvents to echo Widget/StatefulWidget and HandleEvent, with added application context.
    • timer-support and background-tasks

run-tui

This function runs the event-loop.

  • app - The main AppWidget that handles the whole application.
  • global - Globale state stuff. Put your config, theme, logging, database connection and the like here.
  • state - Initial state of the app widget.
  • cfg - Some tweaks for the event loop.

Polls all event-sources and ensures an equal time-share for each source, should one of them start flooding. For now the event-sources are fixed as timers, responses from background tasks, input events.

Control

The result-type for event-handling:

  • Continue - poll the next event.
  • Break - Does nothing for the main loop, but can be used with flow_ok! to break early from event-handling.
  • Repaint - Renders the application.
  • Action - Calls into the action handlers.
  • Quit - Quit the application.

The result of an event is processed immediately after the function returns, before polling new events. This way an action can trigger another action which triggers the repaint without other events intervening.

If you ever need to return more than one result from event-handling, you can hand it to AppContext/RenderContext::queue(). Events in the queue are processed in order, and the return value of the event-handler comes last. If an error is returned, everything send to the queue will be executed nonetheless.

AppWidget and AppEvents

AppWidget is styled after StatefulWidget.

Additionaly it gets

  • event - RepaintEvent to differentiate a timed repaint from an application driven repaint.
  • ctx - RenderContext

AppEvents packs together the currently supported event-handlers.

  • init - called at application startup before the first repaint.
  • timer - application timers
  • crossterm - crossterm events.
  • action - application supplied actions.
  • error - error handling

Each of them get some event and an AppContext.

AppContext and RenderContext

AppContext and RenderContext are not the same, the latter has rendering specific information not available in the general case.

AppContext contains

  • field g for the global state data.

  • add_timer(), remove_timer()

  • spawn() - Runs the closure given and returns an Arc<Mutex<bool>> that is shared with the worker thread to support basic cancellation support.

  • queue() - Queues additional results from event-handling.

      Remark: _The main reason for this is focus-handling.
              When handling the click to focus a widget, the same
              click event should interact with the widget. This gives
              two results from event-handling. The focus change wants
              a Control::Repaint, and the widget might have its own 
              ideas. So now you queue() the focus result and go on
              with event-handling. 
    

RenderContext has the same as AppContext plus

  • frame counter
  • frame area
  • cursor position for displaying the cursor.

Example

There is no example here, that would be too much. The examples directory contains minimal.rs and showcase.rs.

Dependencies

~15–22MB
~205K SLoC