# Getting Started with Terminal::Widgets

## Overview

As the name implies, Terminal::Widgets (AKA "T-W") is a collection of modules
for creating Terminal User Interfaces (TUIs) using various visual *widgets*.
Widgets are rectangular tools that can be placed arbitrarily within the
terminal window, each doing a single thing.  Simple widgets include text
labels, buttons, checkboxes, menus, and specialty add-ons such as scrollbars.
More complex widgets include editor inputs, tree navigators, rich text viewers,
smoke charts, and more.

The smaller widgets used to design a particular TUI are laid out within a
special large widget called a *toplevel* that covers the entire terminal
window/viewport.  An *application* can be made up of many toplevels, each of
which lays out a single "screen" within the app, such as the main menu,
settings menu, online help docs, and so on.

While a toplevel manages screen layout, another critical object represents the
terminal itself -- terminal emulator capabilities, input decoding, active user
themes and preferences, the user's current locale and translation context, and
the reactive event dispatcher for that terminal session.

Here's a diagram of a trivial T-W UI in use:

```
USER         WINDOW        OBJECTS
                           App
👤💻─────╒════Hello════╕ ⟵ ├─Terminal
    ╲    │             │   │    ┆
     ╲   │             │ ⟵ └─TopLevel (HelloUI)
      ╲  │Hello, World!│     ├─PlainText
       ╲ │⌈Quit⌋       │     └─Button
        ╲│             │
         └─────────────┘
```


## Hello, World!

With that short overview in mind, let's start off by creating the classic
"Hello, World!" app from the diagram above.  Here's what that looks like in
Raku code:

```raku
use Terminal::Widgets::Simple;

#| A top level UI container based on Terminal::Widgets::Simple::TopLevel
class HelloUI is TopLevel {

    #| Define the initial UI layout when the TopLevel first starts up
    method initial-layout($builder, $width, $height) {

        # Use the layout builder to add a PlainText widget and a quit button,
        # centered in the terminal window and taking minimal space.
        with $builder {
            .center(:vertical, style => %(:minimize-h, :minimize-w),
                     .plain-text(text => 'Hello, World!', color => 'bold'),
                     .button(label => 'Quit',
                             process-input => { $.terminal.quit }),
                    )
        }
    }
}

sub MAIN() {
    # Boot a Terminal::Widgets::Simple::App and jump right to the main screen
    App.new.boot-to-screen('hello-world', HelloUI, title => 'Hello');
}
```

Using `Terminal::Widgets::Simple` defines and imports classes that handle all
of the basic TUI behaviors, including `App` (representing the application
lifecycle) and `TopLevel` (representing a full-window UI).  The `App` startup
process will eventually call our `initial-layout` method (more on this in the
next section) to define the widget layout constraints, and that's where most of
the code in this example resides.

A `Layout::Builder` object is provided as the first argument, and we use that
to request a centered, vertically stacked, minimum-size UI layout, containing a
simple `plain-text` message and a 'Quit' `button` that when clicked will tell
the terminal event reactor to quit (thus exiting the program as a whole).

For your convenience the above program has been saved in the
[first hello-world example](../examples/hello-world-1.raku); go ahead and run
this to see what the result looks like.  You can mouse-click the Quit button
or even just press Enter to quit, since the first active input is automatically
focused for you.


## Startup Behind the Scenes

*This section details the startup process and explains why `MAIN` looks the way
it does; feel free to skip to the next section if this doesn't interest you
yet.*

The trivial `MAIN` used above creates a default `App` object and immediately
calls its `boot-to-screen` helper method to start up the application.  The
arguments are:

  1. An internal moniker for the initial screen (used by multi-screen apps)
  2. The `TopLevel` UI subclass that the helper method should instantiate first
  3. A title for the terminal window to use while showing the initial screen

As the name implies, `boot-to-screen` starts a UI bootstrapping sequence,
eventually ending with a fully rendered and interactive initial screen.

After some initial housekeeping, the user's terminal is autodetected and a
`Terminal` object created to manage it.  Next the specified `TopLevel` subclass
(in this case `HelloUI`) is instantiated with a reference to its controlling
`Terminal` object.  The boot sequence continues by calling
`Terminal.initialize` to blank the terminal window and start its input decoder,
and then calling `Terminal.set-toplevel` to prepare the `HelloUI` screen for
display.

`set-toplevel` begins by setting the terminal window title and then setting the
UI's height and width to the current window size, measured in character cells.
Finally it asks `HelloUI` to perform a `relayout` on itself.

A `relayout` begins by computing the actual layout details based on the
constraints specified by `HelloUI.initial-layout`.  It then builds the
requested child widgets and places them in the layout's computed rectangular
layout slots, and sends a `LayoutBuilt` event to all widgets to let them know
their siblings all exist and have been placed.  `relayout` finishes by setting
the input focus, then requesting a redraw and recomposite of all placed
widgets.

Finally, with the bootup process complete, the `App` object hands off control
to the user by starting `Terminal`'s primary input/event reactor.


## Event Handling

T-W event handling is similar to web browser event handling.  Events such as
`KeyboardEvent`, `MouseEvent`, `TakeFocus`, or `LayoutBuilt` are injected into
the currently visible `TopLevel`, which trickles each event down to its
children recursively until reaching the event's target (if there is one) or the
leaves of the widget tree, where it begins a journey of bubbling back up to the
TopLevel.

Thus there are three phases to an event's journey that widgets can act on:

  * `TrickleDown`
  * `AtTarget`
  * `BubbleUp`

This allows any widget to choose to handle an event either before or after its
children, or only when it is the event target, simply by choosing which phase
to listen to.

In order to better control which widgets should receive a particular event,
there are several event classes:

  * `GlobalEvent`         - Sent to every widget
  * `TargetedEvent`       - Sent to one particular widget, ignored by others
  * `LocalizedEvent`      - Sent to widgets overlapping a particular X,Y location
  * `FocusFollowingEvent` - Sent to only the focused widget and its parents

Here's how the standard event types fit into those categories:

  * `GlobalEvent`         - `LayoutBuilt`
  * `TargetedEvent`       - `TakeFocus`
  * `LocalizedEvent`      - `MouseEvent`
  * `FocusFollowingEvent` - `KeyboardEvent`

The base `Widget` class does the `EventHandling` role, which allows a widget
to handle some subset of events by adding a `multi method handle-event` with
sufficiently precise arguments.  Here's an example of a mouse event handler
shared among several of the `Input` widget types:

```raku
    #| Handle basic mouse click event
    multi method handle-event(Terminal::Widgets::Events::MouseEvent:D
                              $event where !*.mouse.pressed, AtTarget) {
        # Always focus on mouse click, but only perform click action if enabled
        self.toplevel.focus-on(self);
        self.click if $.enabled;
    }
```

Note how the multi method parameters specify only `MouseEvent`s where the
mouse button is being released (the end of a click), and only when the event
has reached its target (phase `AtTarget`).


### Event Handling in `hello-world`

As a concrete case, the `hello-world` program above has a Quit button.  The
standard `Input::Button` class has builtin `handle-event` multi methods for
both keyboard and mouse inputs.  When an `Event` of either type is sent from
the `Terminal` reactor to the current `TopLevel`, that `Event` will trickle
down through the widget hierarchy until it reaches its target widget.

If the current focus (for a `KeyboardEvent`) or the current mouse cursor
location (for a `MouseEvent`) target the Quit button, the appropriate
`handle-event` method will be called, which in turn will call the
`process-input` block that was specified in the layout.

In this particular case, our `process-input` finds the current controlling
`Terminal` object and requests that it quit and shutdown, eventually exiting
the program completely.


## Widget Layout and the Box Model

Widgets are laid out in a hierarchy of X-Y grids, each laying flat within a
stack of Z-planes.  Even without any Z-offset, child widgets are assumed to be
infinitesimally closer to the viewer than their parent so that the painting
and compositing orders are well-defined:

```
┌PARENT─────────────────────────────┐
│ ┌CHILD─────────┐ ┌CHILD─────────┐ │
│ │ ┌GRANDCHILD┐ │ │ ┌GRANDCHILD┐ │ │
│ │ └──────────┘ │ │ └──────────┘ │ │
│ │ ┌GRANDCHILD┐ │ │ ┌GRANDCHILD┐ │ │
│ │ └──────────┘ │ │ └──────────┘ │ │
│ └──────────────┘ └──────────────┘ │
└───────────────────────────────────┘
```

Within each widget, T-W uses a similar layout to the CSS box model.  The active
*content-area* sits in the middle and is surrounded by three types of *framing*
-- from innermost to outermost, the *padding*, *border*, and *margin*:

```
┌WIDGET────────────────────────────┐
│              margin              │
│                                  │
│    ╔═════════border═════════╗    │
│ m  ║                        ║  m │
│ a  ║         padding        ║  a │
│ r  ║  p ┌──────────────┐ p  ║  r │
│ g  ║  a │ Content Area │ a  ║  g │
│ i  ║  d └──────────────┘ d  ║  i │
│ n  ║         padding        ║  n │
│    ║                        ║    │
│    ╚═════════border═════════╝    │
│                                  │
│              margin              │
└──────────────────────────────────┘
```

The upper left corner within a widget grid is at x=0,y=0,z=0, but may be offset
by arbitrary integer offsets from its parent (and through the chain of parents,
the entire `TopLevel` screen).  Positive values are to the RIGHT, DOWN, and
CLOSER to the viewer.

Here's the widget box model again, with coordinates added:

```
    │
    │ +y
    ▼
───▶┌WIDGET────────────────────────────┐╶╮
 +x │(0,0)         margin              │ │
    │                                  │ │
    │    ╔═════════border═════════╗    │ │
    │ m  ║                        ║  m │ │
    │ a  ║         padding        ║  a │ │
    │ r  ║  p ┌──────────────┐ p  ║  r │ │
    │ g  ║  a │ Content Area │ a  ║  g │ ├ h (height)
    │ i  ║  d └──────────────┘ d  ║  i │ │
    │ n  ║         padding        ║  n │ │
    │    ║                        ║    │ │
    │    ╚═════════border═════════╝    │ │
    │                                  │ │
    │              margin     (w-1,h-1)│ │
    └──────────────────────────────────┘╶╯
    ╰────────────────┬─────────────────╯
                     w (width)
```

Let's add some framing to our `hello-world` widgets by replacing the original
layout constraint request:

```raku
.center(:vertical, style => %(:minimize-h, :minimize-w),
         .plain-text(text => 'Hello, World!', color => 'bold',
                     style => %(border-width  => 1,
                                margin-width  => (0,0,1,0),
                                padding-width => (0,1,2,3))),
         .button(label => 'Quit',
                 style => %(border-width => 1),
                 process-input => { $.terminal.quit }),
        )
```

When a framing width is specified as a single value, it is applied on all four
sides of the widget equally.  If it is specified as multiple values, they apply
to the top, right, bottom, and left sides respectively.

This version has been saved in the
[second hello-world example](../examples/hello-world-2.raku).
Here's the result when you run it:

```
╒═══════Hello═══════╕
│╔═════════════════╗│
│║   Hello, World! ║│
│║                 ║│
│║                 ║│
│╚═════════════════╝│
│                   │
│╔═════════════════╗│
│║Quit             ║│
│╚═════════════════╝│
└───────────────────┘
```

Both widgets have a width-1 border all the way around, and they are separated
by the width-1 bottom margin of the plain-text widget.  The content of the
top widget has padding that differs on each side: none on the top, 1 space on
the right, 2 spaces on the bottom, and 3 spaces on the left.

There are a couple interesting things to note about the Quit button:

  1. The `⌈⌋` button corner markers disappeared automatically.
  2. The button has been laid out to be as wide as the plain-text widget.

The first of these is default button behavior in order to avoid placing
button corner markers inside of border corners (which looks rather odd).

The second is because our layout has requested only that the `center` node be
minimized, not the individual layout nodes within it.  By default widgets
stacked vertically (`:vertical`) are given the same layout width, and widgets
stacked horizontally (the default) are given the same layout height.

In this case that looks a bit odd, so we can change the layout request to
push the button into the left portion of the area reserved for it:

```raku
.center(:vertical, style => %(:minimize-h, :minimize-w),
         .plain-text(text => 'Hello, World!', color => 'bold',
                     style => %(border-width  => 1,
                                margin-width  => (0,0,1,0),
                                padding-width => (0,1,2,3))),
         .push-left(style => %(:minimize-w),
                    .button(label => 'Quit',
                            style => %(border-width => 1),
                            process-input => { $.terminal.quit })),
        )
```

Here we've requested a new `push-left` node with `minimize-w` set, meaning
that the button within it will be pushed to the left and shrunk horizontally
as much as possible.

This is now the [third hello-world example](../examples/hello-world-3.raku).
Here's the new result:

```
╒═══════Hello═══════╕
│╔═════════════════╗│
│║   Hello, World! ║│
│║                 ║│
│║                 ║│
│╚═════════════════╝│
│                   │
│╔════╗             │
│║Quit║             │
│╚════╝             │
└───────────────────┘
```


## Drawing Sequence

### Overall Refresh Sequence

To fully refresh its contents, a widget goes through a simple sequence:

  1. `clear-frame`
  2. `draw-frame`
  3. `composite`

Each of these has several sub-steps; `clear-frame` is the simplest:

  1. Clear widget's underlying grid
  2. Mark the entire widget dirty, so the widget will composite later

We'll get back to `draw-frame` in a moment, since it varies considerably for
different widgets.  For now let's take a look at the final step (`composite`),
which copies the widget's newly drawn content to its parent and/or the terminal
screen.  Most of its complexity comes from optimizing for several different
possible paths:

  1. Snapshot this widget's current dirty areas (and implicitly clear them)
  2. Depending on the widget's `parent`:
     * If `parent` is the current T-W toplevel:
       * `parent.print-to-content-area`
     * If `parent` is a non-toplevel T-W widget:
       * `parent.copy-to-content-area`
       * `parent.add-dirty-rect`
     * Otherwise (`parent` is probably the T-P screen):
       * `parent.add-dirty-rect` if parent understands the `DirtyAreas` protocol
       * Invalidate the underlying T-P grid-string cache
       * Hand off to the underlying T-P `composite` method (AKA the "old path")


### Drawing Leaf Widgets

The complexity of `draw-frame` depends on whether the widget has any children;
if none (meaning the widget is a leaf node), it's just two steps:

  1. `draw-framing`
  2. `draw-content`

Of course, those unroll a bit; `draw-framing` breaks down as follows:

  1. Check the widget's computed layout to see which framing has been requested
  2. `draw-margin`  (if requested)
  3. `draw-border`  (if requested)
  4. `draw-padding` (if requested)

`draw-content` is specific to each widget type; the version in the `Widget`
base class is just a stub that does nothing.

Summarizing, here's the full refresh sequence for a leaf widget:

  1. `clear-frame`
     * Clear underlying grid
     * Mark widget all-dirty
  2. `draw-frame`
     * `draw-framing`
       * Check requested framing
       * `draw-margin`
       * `draw-border`
       * `draw-padding`
     * `draw-content` **(unique per widget type)**
  3. `composite`
     * Snapshot current dirty areas
     * Ask `parent` to do one of:
       * `print-to-content-area`
       * `copy-to-content-area` + `add-dirty-rect`
       * `add-dirty-rect` + invalidate T-P cache + T-P `composite`


### Drawing Parent Widgets

The major difference for parent widgets (those with children) is that
`draw-frame` now has to account for the Z-ordering of those children relative
to itself:

  1. Sort children by Z order, back to front
  2. For each child *behind* the parent widget (Z <= 0):
     * `composite` the child onto the parent widget's grid
     * Add a dirty rect for the child's area (if it didn't do so itself)
  3. `draw-framing` for the parent widget itself *(same as for leaf)*
  4. `draw-content` for the parent widget itself *(same as for leaf)*
  5. Repeat step 2 for each child *in front of* the parent widget (Z > 0)


## Widget Builtin Roles

In order to provide many common behaviors and utility methods, the base
`Widget` class is fairly extensive and does numerous roles.  Most of these
you won't have to think about when building your own apps and custom widget
classes, except maybe to use provided helpers and standard boilerplate; they
mostly Just Work.

Here's the list for the curious:

  * `Terminal::Print::Widget`       - Use T-P's low-level cells, grid, and compositor
  * `Terminal::Print::Animated`     - Allow timestamp-aware full-hierarchy redraws
  * `Terminal::Print::BoxDrawing`   - Draw borders using various line styles
  * `Terminal::Widgets::Common`     - Provide common debugging and profiling helpers
  * `Terminal::Widgets::Themable`   - Control color and attributes via semantic states
  * `Terminal::Widgets::DirtyAreas` - Support dirty-area compositing optimization
  * `Terminal::Widgets::WidgetRegistry`        - Register new widget types on load
  * `Terminal::Widgets::Events::EventHandling` - See [Event Handling](#event-handling) section

Note that the first three of those are from Terminal::Print ('T-P'), which T-W
is based on and interoperable with.


## Debugging and Profiling

Because of the highly-interconnected nature of T-W objects, it can be confusing
to log, debug, trace, or profile a T-W app's execution; `dd` or `.raku` on any
widget are likely to end up dumping many pages of output.  To make this easier,
the `Common` role and `Widget` class together provide a number of helper methods:

  * `gist`             - Avoids recursive dumping and summarizes key attributes
  * `gist-name`        - Class name shortened for readability
  * `gist-flags`       - Used by `gist` to report special flags on the widget
  * `gist-dirty-areas` - Used by `gist` to summarize the widget's dirty areas

  * `debug`         - Cache of `$*DEBUG` verbosity at time of object creation
  * `debug-grid`    - Return an optionally framed snapshot of a single widget
  * `debug-elapsed` - Write a debug note for elapsed time during an operation

  * `toplevel`      - Chase parent links to find widget's `TopLevel`
  * `terminal`      - Find this widget's controlling `Terminal` (via `toplevel`)
  * `default-focus` - Find descendent widget that should get focus by default

  * `first-widget`  - FIRST matching widget in subtree, starting at `self`
  * `last-widget`   - LAST matching widget in subtree, ending with `self`
  * `next-widget`   - Next matching widget AFTER `self` in full tree
  * `prev-widget`   - Previous matching widget BEFORE `self` in full tree


## TopLevel Widgets

## Standard Widget Classes

## The Application Object

## The Terminal Object

## Terminal Capabilities


## Further Reading

Now that you've gotten through this document, you're ready to take a more
detailed look at various parts of Terminal::Widgets.  Here are a few
suggestions:

* [Design Goals and Philosophy](design-goals.md) - The overall design goals
  that led to T-W's current design and implementation

* [Configuration and User Preferences](preferences.md) - Environment variables
  and terminal instance attributes used to configure the user's T-W experience

* [Text Content Model](content-model.md) - Deeper details about the RenderSpan
  content model and the associated string conversion pipeline

* [Concurrency Model](concurrency-model.md) - Deeper details on how concurrency
  is generated and managed by T-W, including startup and event handling

* [Adding New Widget Types](adding-new-widgets.md) - A guide to creating your
  own custom widgets to supplement the premade widget collection


----

## XXXX: UNUSED PIECES

As simple as it is, our `initial-layout` method doesn't use its other two
arguments, which provide the width and height of the terminal window (measured
in character cells) in case the programmer wants to provide entirely different
layouts for small or large terminal windows.  This isn't needed when simply
*resizing* the same basic layout -- the builder's layout constraint solver does
that automatically -- but could for example instead be used to switch between
overview and detailed screen layouts based on available terminal real estate.

---

For example, `Widget` does the `WidgetRegistry` role and provides a `register`
helper method, which introspects various subclass declarations to set up a
proper call to `self.register-widget` with all the right arguments.  But you
don't have to care about how any of that works.  You just have to know that
when you're creating a new widget type, if you follow the boilerplate in the
[Adding New Widget Types](adding-new-widgets.md) doc, and put a simple
`register` call at the end of your implementation file like this:

```raku
Terminal::Widgets::Your::Classname.register;
```

... your new widget type can then be used just like any builtin type would be.
