mirror of
https://github.com/jtdaugherty/brick.git
synced 2024-12-02 11:05:10 +03:00
252 lines
9.6 KiB
ReStructuredText
252 lines
9.6 KiB
ReStructuredText
brick documentation
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
.. contents:: `Table of Contents`
|
|
|
|
Introduction
|
|
============
|
|
|
|
``brick`` is a Haskell library for programming terminal user interfaces.
|
|
Its main goal is to make terminal user interface development as painless
|
|
and as direct as possible. ``brick`` builds on `vty`_; `vty` provides
|
|
the terminal input and output interface and drawing primitives,
|
|
while ``brick`` builds on those to provide a high-level application
|
|
abstraction and combinators for expressing user interface layouts.
|
|
|
|
This documentation is intended to provide a high-level overview of
|
|
the library's design along with guidance for using it, but details on
|
|
specific functions can be found in the Haddock documentation.
|
|
|
|
The process of writing an application using ``brick`` entails writing
|
|
two important functions:
|
|
|
|
- A *drawing function* that turns your application state into a
|
|
specification of how your interface should look, and
|
|
- An *event handler* that takes your application state and an input
|
|
event and decides whether to change the state or quit the program.
|
|
|
|
We write drawing functions in ``brick`` using an extensive set of
|
|
primitives and combinators to place text on the screen, set its
|
|
attributes (e.g. foreground color), and express layout constraints (e.g.
|
|
padding, centering, box layouts, scrolling viewports, etc.).
|
|
|
|
These functions get packaged into a structure that we hand off to the
|
|
``brick`` library's main event loop.
|
|
|
|
Installation
|
|
------------
|
|
|
|
``brick`` can be installed in the "usual way," either by installing
|
|
the latest `Hackage`_ release or by cloning the GitHub repository and
|
|
building locally.
|
|
|
|
To install from Hackage::
|
|
|
|
$ cabal update
|
|
$ cabal install brick
|
|
|
|
To clone and build locally::
|
|
|
|
$ git clone https://github.com/jtdaugherty/brick.git
|
|
$ cd brick
|
|
$ cabal sandbox init
|
|
$ cabal install -j
|
|
|
|
Building the Demonstration Programs
|
|
-----------------------------------
|
|
|
|
``brick`` includes a large collection of feature-specific demonstration
|
|
programs. These programs are not built by default but can be built by
|
|
passing the ``demos`` flag to `cabal install`, e.g.::
|
|
|
|
$ cabal install brick -f demos
|
|
|
|
API Conventions
|
|
===============
|
|
|
|
``brick`` has some API conventions worth knowing about as you read this
|
|
documentation and as you explore the library source and write your own
|
|
programs.
|
|
|
|
- Use of `lens`_: ``brick`` uses ``lens`` functions internally and also
|
|
exposes lenses for many types in the library. However, if you prefer
|
|
not to use the ``lens`` interface in your program, all ``lens``
|
|
interfaces have non-`lens` equivalents exported by the same module. In
|
|
general, the "``L``" suffix on something tells you it is a ``lens``;
|
|
the name without the "``L``" suffix is the non-`lens` version. You can
|
|
get by without using ``brick``'s ``lens`` interface but your life will
|
|
probably be much more pleasant once your application state becomes
|
|
sufficiently complex if you use lenses to modify it (see
|
|
`appHandleEvent: Handling Events`_).
|
|
- Attribute names: some modules export attribute names (see `How
|
|
Attributes Work`_) associated with user interface elements. These tend
|
|
to end in an "``Attr``" suffix (e.g. ``borderAttr``). In addition,
|
|
hierarchical relationships between attributes are documented in
|
|
Haddock documentation.
|
|
|
|
The App Type
|
|
============
|
|
|
|
To use the library we must provide it with a value of type
|
|
``Brick.Main.App``. This type is a record type whose fields perform
|
|
various functions:
|
|
|
|
.. code:: haskell
|
|
|
|
data App s e =
|
|
App { appDraw :: s -> [Widget]
|
|
, appChooseCursor :: s -> [CursorLocation] -> Maybe CursorLocation
|
|
, appHandleEvent :: s -> e -> EventM (Next s)
|
|
, appStartEvent :: s -> EventM s
|
|
, appAttrMap :: s -> AttrMap
|
|
, appLiftVtyEvent :: Event -> e
|
|
}
|
|
|
|
The ``App`` type is polymorphic over two types: your application state
|
|
type ``s`` and event type ``e``.
|
|
|
|
The application state type is the type of data that will evolve over the
|
|
course of the application's execution; we will provide the library with
|
|
its starting value and event handling will transform it as the program
|
|
executes.
|
|
|
|
The event type is the type of events that your event handler
|
|
(``appHandleEvent``) will handle. The underlying ``vty`` library
|
|
provides ``Graphics.Vty.Event``, and this forms the basis of all events
|
|
we will handle with ``brick`` applications. However, that is often not
|
|
enough. Imagine an application with multiple threads and network or
|
|
disk I/O. Such an application will need to have its own internal events
|
|
to pass to the event handler as (for example) network data arrives. To
|
|
accommodate this we allow an ``App`` to use an event type of your own
|
|
design, so long as it provides a constructor for ``vty``'s ``Event``
|
|
type (``appLiftVtyEvent``). For more details, see `Using Your Own Event
|
|
Type`_.
|
|
|
|
The various fields of ``App`` will be described in the sections below.
|
|
|
|
To run an ``App``, we pass it to ``Brick.Main.defaultMain`` or
|
|
``Brick.Main.customMain`` along with an initial application state value.
|
|
|
|
appDraw: Drawing an Interface
|
|
-----------------------------
|
|
|
|
The value of ``appDraw`` is a function that turns the current
|
|
application state into a list of *layers* of type ``Widget``, listed
|
|
topmost first, that will make up the interface. Each ``Widget`` gets
|
|
turned into a ``vty`` layer and the resulting layers are drawn to the
|
|
terminal.
|
|
|
|
The ``Widget`` type is the type of *drawing instructions*. The body of
|
|
your drawing function will use one or more drawing functions to build or
|
|
transform ``Widget`` values to describe your interface. These
|
|
instructions will then be executed with respect to three things:
|
|
|
|
- The size of the terminal: the size of the terminal determines how many
|
|
``Widget`` values behave. For example, fixed-size ``Widget`` values
|
|
such as text strings behave the same under all conditions (and get
|
|
cropped if the terminal is too small) but layout combinators such as
|
|
``vBox`` or ``center`` use the size of the terminal to determine how
|
|
to lay other widgets out. See `How Widgets and Rendering Work`_.
|
|
- The application's attribute map (``appAttrMap``): drawing functions
|
|
requesting the use of attributes cause the attribute map to be
|
|
consulted. See `How Attributes Work`_.
|
|
- The state of scrollable viewports: the state of any scrollable
|
|
viewports on the *previous* drawing will be considered. For more
|
|
details, see `Viewports`_.
|
|
|
|
The ``appDraw`` function is called when the event loop begins to draw
|
|
the application as it initially appears. It is also called right after
|
|
an event is processed by ``appHandleEvent``. Even though the function
|
|
returns a specification of how to draw the entire screen, the underlying
|
|
``vty`` library goes to some trouble to efficiently update only the
|
|
parts of the screen that have changed so you don't need to worry about
|
|
this.
|
|
|
|
appHandleEvent: Handling Events
|
|
-------------------------------
|
|
|
|
The value of ``appHandleEvent`` is a function that decides how to modify
|
|
the application state as a result of an event. It also decides whether
|
|
to continue program execution.
|
|
|
|
The function takes the current application state and the event and
|
|
returns the *next application state*:
|
|
|
|
.. code::
|
|
appHandleEvent :: s -> e -> EventM (Next s)
|
|
|
|
The ``EventM`` monad is the event-handling monad. This monad is a
|
|
transformer around ``IO``, so you are free to do I/O in this monad by
|
|
using ``liftIO``. Keep in mind that time spent blocking in your event
|
|
handler is time during which your UI is unresponsive, so consider this
|
|
when deciding whether to have background threads do work instead of
|
|
inlining the work in the event handler.
|
|
|
|
The ``Next s`` value describes what should happen after the event
|
|
handler is finished. We have three choices:
|
|
|
|
* ``Brick.Main.continue s``: continue executing the event loop with the
|
|
specified application state ``s`` as the next value. Commonly this is
|
|
where you'd modify the state based on the event and return it.
|
|
* ``Brick.Main.halt s``: halt the event loop and return the final
|
|
application state value ``s``. This state value is returned to the
|
|
caller of ``defaultMain`` or ``customMain`` where it can be used prior
|
|
to finally exiting ``main``.
|
|
* ``Brick.Main.suspendAndResume act``: suspend the ``brick`` event loop
|
|
and execute the specified ``IO`` action ``act``. The action ``act``
|
|
must be of type ``IO s``, so when it executes it must return the next
|
|
application state. When ``suspendAndResume`` is used, the ``brick``
|
|
event loop is shut down and the terminal state is restored to its
|
|
state when the ``brick`` event loop began execution. When it finishes
|
|
executing, the event loop will be resumed using the returned state
|
|
value. This is useful for situations where your program needs to
|
|
suspend your interface and execute some other program that needs to
|
|
gain control of the terminal (such as an external editor).
|
|
|
|
Using Your Own Event Type
|
|
*************************
|
|
|
|
appLiftVtyEvent
|
|
|
|
customMain
|
|
|
|
Starting up: appStartEvent
|
|
**************************
|
|
|
|
appChooseCursor: Placing the Cursor
|
|
-----------------------------------
|
|
|
|
appAttrMap: Providing Attributes
|
|
--------------------------------
|
|
|
|
How Widgets and Rendering Work
|
|
==============================
|
|
|
|
How Attributes Work
|
|
===================
|
|
|
|
Implementing Your Own Widgets
|
|
=============================
|
|
|
|
Using the Rendering Context
|
|
---------------------------
|
|
|
|
Rendering Sub-Widgets
|
|
---------------------
|
|
|
|
Viewports
|
|
=========
|
|
|
|
Drawing Viewports
|
|
-----------------
|
|
|
|
Scrolling Viewports With Visibility Requests
|
|
--------------------------------------------
|
|
|
|
Scrolling Viewports in Event Handlers
|
|
-------------------------------------
|
|
|
|
.. _vty: https://github.com/coreyoconnor/vty
|
|
.. _Hackage: http://hackage.haskell.org/
|
|
.. _lens: http://hackage.haskell.org/package/lens
|