View Source Garnish.App behaviour (garnish v0.2.0)
Defines the Garnish.App behaviour. It provides the structure for
architecting both large and small terminal applications, over SSH.
The entrypoint to the application is defined by parameters to the ssh_cli daemon option.
A Simple Example
# Assumes that `:ssh_cli` daemon option was set to:
# ssh_cli: {Garnish, {Counter.App, []}}
defmodule Counter.App do
@behaviour Garnish.App
# Initialize our model state
def init(_context), do: {:ok, 0}
# Respond to key presses
def handle_key(%{key: key}, model) do
case key do
?+ -> {:ok, model + 1}
?- -> {:ok, model - 1}
_ -> {:ok, model, render: false}
end
# Turn the model into view
def render(model) do
view do
label(content: "Counter is #{model} (+/-)")
end
end
endCallbacks
The behaviour describes 3 mandatory and 3 optional callbacks. The mandatory callbacks are:
init/1for setting the initial model statehandle_key/2for handling keys from the clientrender/1for rendering the model to a view
The three optional callbacks are:
handle_resize/2for handling resize eventshandle_info/2for handling all other messagesterminate/2for handling process termination
For all the handle_/2 callbacks, it is possible to disable re-rendering,
even after a model update, by returning [render: false] from the callback.
This is useful if the model has 'private' data that does not itself affect
the view.
Summary
Callbacks
Handle an event.
Handle a key-press from the client.
Handle a resize event from the client.
The init/1 callback defines the initial model. The context is a map with the
following keys
The render/1 callback defines how to render the model as a view.
Handle termination of the application.
Types
@type col_count() :: non_neg_integer()
@type context() :: %{ args: term(), size: dimension(), environment: %{required(String.t()) => String.t()}, connection: :ssh.connection_ref() }
@type handle_opt() :: {:render, boolean()}
@type handle_opts() :: [handle_opt()]
@type init_opt() :: {:quit_keys, [integer()]}
@type init_opts() :: [init_opt()]
@type model() :: term()
@type msg() :: term()
@type reason() :: term()
@type row_count() :: non_neg_integer()
Callbacks
@callback handle_info(term(), model()) :: {:ok, model()} | {:ok, model(), handle_opts()} | {:stop, reason(), model()}
Handle an event.
Used to handle any non-key/non-resize message. This callback is optional. If not specified, the message is ignored.
@callback handle_key(key_event(), model()) :: {:ok, model()} | {:ok, model(), handle_opts()} | {:stop, reason(), model()}
Handle a key-press from the client.
The key event is a map containing the following entries:
keyan integer or mnenomic representing the key pressed. Most literal keys are expressed as an integer. Other keys are expressed as an atom based on their terminal settings such as:kcud1for left-arrow. The mnemomics are derived from their terminfo specifications (see the terminfo manual page) for more details.alta boolean indicating whether the alt-key was presseddatathe raw data from the connection
Handle a resize event from the client.
This callback is optional. If not specified, the default behaviour is render the existing model again (for the new size).
@callback init(context()) :: {:ok, model()} | {:ok, model(), init_opts()} | {:stop | :error, reason()}
The init/1 callback defines the initial model. The context is a map with the
following keys:
argsthe args passed as passed tossh_cli, defaults to[]sizethe size of the initial window passed as{rows, cols}environmentthe environment passed from the clientconnectionthe ssh connection reference. This may be used to query information via:ssh.connection_info/1
The callback should return the initial state on success or an error otherwise. Additionally, on success, the client may return some options that adjust the behaviour of the app. The currently supported values are:
quit_keysa list of integers that determine which keys will automatically close the app. If not specified, defaults to ctrl-c ([3]).
@callback render(model()) :: Garnish.Renderer.Element.t()
The render/1 callback defines how to render the model as a view.
It should return a Garnish.Renderer.Element with the :view tag. For example:
@impl true
def render(model) do
view do
label(content: "Hello, #{model.name}!")
end
end
@callback terminate( reason :: :normal | :shutdown | {:shutdown, term()} | term(), state :: term() ) :: term()
Handle termination of the application.
This callback is optional and can be used for arbitrary clean-up. Note that the client's session has already been closed by the time this callback in invoked.