# `Atui.TextInput`
[🔗](https://github.com/iboard/atui/blob/v0.3.0/lib/atui/text_input.ex#L1)

A single-line text field a view holds in its own state.

Like `Atui.Panes`, this is a struct with callbacks rather than a view: it owns
no process and no part of the screen. The host keeps one in its state, offers
keys to it, and draws it into a row of its own screen — so a field can sit in
a border, in a table header, in a popup, wherever the host has a row to spare.

## Holding one

    defmodule Search do
      use Atui.View

      alias Atui.TextInput

      def mount(_opts), do: {:ok, %{input: TextInput.new(prompt: "search: ")}}

      def handle_key(:enter, state), do: {:ok, submit(state)}

      def handle_key(key, state) do
        case state.input |> TextInput.handle_key(key) |> TextInput.into(state) do
          {:pass, state} -> shortcut(state, key)
          reply -> reply
        end
      end

      def render(state, rect) do
        Screen.new(rect.width, rect.height)
        |> Screen.box(rect)
        |> TextInput.draw(state.input, Rect.new(1, 1, rect.width - 2, 1))
      end
    end

`into/3` rewrites a reply about the field into a reply about the state that
holds it, which keeps the delegation one line long. Bind the keys the host
wants for itself *before* delegating: a field claims every printable
character, so a list underneath one navigates with the arrows and Enter, not
with letters.

## Which keys it claims

Printable characters, and the editing keys a readline user expects:

| | |
| --- | --- |
| `←` `→` | one character |
| Ctrl/Alt with `←` `→` | one word |
| Home, End, Ctrl-A, Ctrl-E | either end |
| Backspace, Delete | one character either side of the cursor |
| Ctrl-W | the word before the cursor |
| Ctrl-U, Ctrl-K | to the start, to the end |

Everything else — Enter, Tab, ESC, the vertical arrows — comes back as
`{:pass, input}` for the host to deal with. A field never swallows a key it
has no use for.

## The cursor

Atui paints cells, and the terminal's own cursor stays hidden in the
alternate screen, so the cursor here *is* a cell: `draw/4` reverses the one
the cursor sits on. That is why a field needs no cooperation from the runtime
to be drawn anywhere — and why an unfocused field, drawn with `focus: false`,
simply has no cursor in it.

Long values scroll horizontally: what is drawn is the window of the value
that keeps the cursor in view, which is a pure function of the value, the
cursor and the width, so nothing about scrolling is kept in the struct.

# `reply`

```elixir
@type reply() :: {:ok, t()} | {:pass, t()}
```

# `t`

```elixir
@type t() :: %Atui.TextInput{
  cursor: non_neg_integer(),
  max_length: pos_integer() | nil,
  placeholder: String.t(),
  prompt: String.t(),
  value: String.t()
}
```

# `clear`

```elixir
@spec clear(t()) :: t()
```

Empties the field.

# `draw`

```elixir
@spec draw(Atui.Screen.t(), t(), Atui.Rect.t(), keyword()) :: Atui.Screen.t()
```

Draws the field into one row of `screen`.

`rect` is where it goes; only its first row is used. Options:

  * `:style` — the value's style
  * `:prompt_style` — the prompt's (defaults to `:style`)
  * `:placeholder_style` — the placeholder's (defaults to `:style`)
  * `:cursor_style` — the cell the cursor is on (reverse video by default)
  * `:focus` — draw the cursor at all (default `true`)

# `empty?`

```elixir
@spec empty?(t()) :: boolean()
```

True while there is nothing in the field.

# `handle_key`

```elixir
@spec handle_key(t(), Atui.Key.t()) :: reply()
```

Offers a key to the field.

Answers `{:ok, input}` for a key it used and `{:pass, input}` for one it did
not, the same contract `c:Atui.View.handle_key/2` has — see the module docs
for which keys are which.

# `into`

```elixir
@spec into(reply(), map(), atom()) :: {:ok | :pass, map()}
```

Rewrites a reply about the field into a reply about the state holding it.

    state.input |> TextInput.handle_key(key) |> TextInput.into(state)

`key` is where the field sits in the state, `:input` by default.

# `new`

```elixir
@spec new(keyword()) :: t()
```

Builds a field.

Options:

  * `:value` — the initial text (the cursor starts after it)
  * `:placeholder` — what to draw while the value is empty
  * `:prompt` — a label drawn before the value, e.g. `"search: "`
  * `:max_length` — the most characters the value may hold

# `put_value`

```elixir
@spec put_value(t(), String.t()) :: t()
```

Replaces the text, putting the cursor at the end of it.

# `value`

```elixir
@spec value(t()) :: String.t()
```

The text in the field.

# `window`

```elixir
@spec window(t(), pos_integer()) :: {String.t(), non_neg_integer()}
```

The window of the value that `draw/4` would show in `width` columns.

Returns `{text, cursor_column}`, the column being where in `text` the cursor
falls. Exposed because a host that draws the value itself still wants the
scrolling rule, and because it is the honest thing to assert on in a test.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
