Handle terminal events

Custom components can have local state. To make them becomes interactive, they need to react to terminal events. There are two ways to achieve that:

Init configuration

In component's init callback, we can return a configuration with events_subscription: true to subscribe events for the component.

defmodule Example do
  @behaviour Orange.Component

  import Orange.Macro

  @impl true
  def init(_attrs), do: %{state: nil, events_subscription: true}

  @impl true
  def handle_event(event, state, _attrs) do
    case event do
      %Orange.Terminal.KeyEvent{code: {:char, "q"}} ->
        Orange.stop()
        state

      %Orange.Terminal.KeyEvent{code: {:char, char}} ->
        IO.puts("Key pressed: #{char}")
        state

      _ ->
        state
    end
  end

  @impl true
  def render(state, _attrs, _update) do
    rect style: [width: 20, height: 20] do
      "Hello"
    end
  end
end

# Start the application. To quit, press 'q'.
Orange.start(Example)

Your component can handle events with the handle_event/3 callback and returns the updated state.

Orange.subscribe/1

If you want to manually subscribe, use Orange.subscribe/1.

defmodule Example do
  @behaviour Orange.Component

  import Orange.Macro

  @impl true
  def init(_attrs), do: %{state: nil}

  @impl true
  def after_mount(_state, _attrs, _update) do
    Orange.subscribe(:root)
  end

  @impl true
  def handle_event(event, state, _attrs) do
    case event do
      %Orange.Terminal.KeyEvent{code: {:char, "q"}} ->
        Orange.stop()
        state

      _ ->
        state
    end
  end

  @impl true
  def render(state, _attrs, _update) do
    rect style: [width: 20, height: 20] do
      "Hello"
    end
  end
end

# Start the application. To quit, press 'q'.
Orange.start({Example, id: :root})

Remove events subscription

To unsubcribe for events, use Orange.unsubcribe/1.

Component focus mode

There are certain cases that you only want one component to receive events and prevent other components. Look at the above example, an input component should receive all the events and prevent users from quitting the application when hitting "q". To do that, we can set a component as focused with Orange.focus/1. Only the focused component can receive terminal events. To remove the focus status, use Orange.unfocus/1.