View Source Scenic.ViewPort (Scenic v0.11.2)

overview

Overview

The job of the ViewPort is to coordinate the flow of information between the scenes and the drivers. Scenes and Drivers should not know anything about each other. An app should work identically from its point of view no matter if there is one, multiple, or no drivers currently running.

Drivers are all about rendering output and collecting input from a single source. Usually hardware, but can also be the network or files. Drivers only care about graphs and should not need to know anything about the logic or state encapsulated in Scenes.

The goal is to isolate app data and logic from render data and logic. The ViewPort is the connection between them that makes sense of the flow of information.

output

OUTPUT

Practically speaking, the ViewPort is the owner of the ETS tables that carry the graphs (and any other necessary support info). If the ViewPort crashes, then all that information needs to be rebuilt. The ViewPort monitors all running scenes and does the appropriate cleanup when any of them goes DOWN.

The scene is responsible for generating graphs and writing them to the graph ETS table. (Note: We also tried casting the graph to the ViewPort so that the table could be non-public, but that had issues)

The drivers should only read from the graph tables.

input

INPUT

When user input happens, the drivers send it to the ViewPort. Input that does not depend on screen position (key presses, audio window events, etc.) is sent to the root scene unless some other scene has captured that type of input (see captured input below).

If the input event does depend on position (cursor position, cursor button presses, scrolling, etc.) then the ViewPort needs to scan the hierarchical graph of graphs, to find the correct scene, and the item in that scene that was "hit". The ViewPort then sends the event to that scene, with the position projected into the scene's local coordinate space (via the built-up stack of matrix transformations)

captured-input

CAPTURED INPUT

A scene can request to "capture" all input events of a certain type. This means that all events of that type are sent to a certain scene process regardless of position or root. In this way, a text input scene nested deep in the tree can capture key presses. Or a button can capture cursor_pos events after it has been pressed.

If a scene has "captured" a position dependent input type, that position is projected into the scene's coordinate space before sending the event. Note that instead of walking the graph of graphs, the transforms provided in the input "context" field are used. You could, in theory change that to something else before capturing, but I wouldn't really recommend it.

Any scene can cancel the current capture. This would probably leave the scene that thinks it has "captured" the input in an inconsistent state, so this is not recommended.

dynamically-creating-view-ports

Dynamically Creating View Ports

Pass in the same set of opts that you would use when starting Scenic in your supervision tree. For example:

# Assuming you use the default view port name from the generator
{:ok, view_port} = Scenic.ViewPort.info(:main_viewport)

opts = [
  module: Scenic.Driver.Local,
  window: [resizeable: false, title: "My Example Scenic App"],
  on_close: :stop_system
]

{:ok, [opts]} = Scenic.Driver.validate([opts])
Scenic.ViewPort.start_driver(view_port, opts)

Link to this section Summary

Functions

Retrieves a list of all registered script ids.

Returns a specification to start this module under a supervisor.

Delete a graph by name.

Delete a script by name.

Find a scene_pid/primitive under the given point in global coordinates

Retrieve a script

Retrieve a %ViewPort{} struct given just the viewport's pid

Send raw input to a viewport.

Returns a list of the valid input types

Returns the id of the first script in the drawing tree

Set the root scene/graph of the ViewPort.

Set the root theme for the ViewPort.

Start a new ViewPort

Stop a running viewport

Link to this section Types

@type event() :: {event :: atom(), data :: any()}
@type t() :: %Scenic.ViewPort{
  name: atom(),
  pid: pid(),
  script_table: reference(),
  size: {number(), number()}
}

Link to this section Functions

Link to this function

all_script_ids(view_port)

View Source
@spec all_script_ids(viewport :: t()) :: list()

Retrieves a list of all registered script ids.

Returns a specification to start this module under a supervisor.

See Supervisor.

Link to this function

del_graph(viewport, name)

View Source
@spec del_graph(viewport :: t(), name :: any()) :: :ok

Delete a graph by name.

Same as del_script/2

Link to this function

del_script(viewport, name)

View Source
@spec del_script(viewport :: t(), name :: any()) :: :ok | {:error, :not_found}

Delete a script by name.

Also unregisters the name/id pairing

Link to this function

find_point(view_port, global_point)

View Source
@spec find_point(viewport :: t(), global_point :: Scenic.Math.point()) ::
  {:ok, scene_pid :: pid(), id :: any()} | {:error, :not_found}

Find a scene_pid/primitive under the given point in global coordinates

Link to this function

get_script(view_port, name)

View Source
@spec get_script(viewport :: t(), name :: any()) ::
  {:ok, Scenic.Script.t()} | {:error, :not_found}

Retrieve a script

Link to this function

handle_continue_input(raw_input, state)

View Source
@spec info(pid :: t() | GenServer.server()) :: {:ok, map()}

Retrieve a %ViewPort{} struct given just the viewport's pid

@spec input(
  viewport :: t(),
  input :: Scenic.ViewPort.Input.t()
) :: :ok | {:error, atom()}

Send raw input to a viewport.

This is used primarily by drivers to send raw user input to the viewport. Having said that, nothing stops a scene from using it to send input into the system. There are a few cases where that is useful.

See the input docs for the input formats you can send.

Returns a list of the valid input types

Link to this function

put_graph(viewport, name, graph, opts \\ [])

View Source
@spec put_graph(
  viewport :: t(),
  name :: any(),
  graph :: Scenic.Graph.t(),
  opts :: Keyword.t()
) :: {:ok, name :: any()}

Put a graph by name.

This compiles the graph to a collection of scripts

Link to this function

put_script(view_port, name, script, opts \\ [])

View Source
@spec put_script(
  viewport :: t(),
  name :: any(),
  script :: Scenic.Script.t(),
  opts :: Keyword.t()
) :: {:ok, non_neg_integer()} | {:error, atom()}

Put a script by name.

returns {:ok, id}

@spec root_id() :: String.t()

Returns the id of the first script in the drawing tree

Used by drivers

Link to this function

set_root(viewport, scene, args \\ nil)

View Source
@spec set_root(
  viewport :: t(),
  scene :: atom(),
  args :: any()
) :: :ok

Set the root scene/graph of the ViewPort.

This will stop the currently running scene, including all of it's child components. Then it starts the new scene including all of it's child components.

Link to this function

set_theme(viewport, theme)

View Source
@spec set_theme(viewport :: t(), theme :: atom() | map()) :: :ok

Set the root theme for the ViewPort.

Warning

This will restart the current root scene

@spec start(opts :: Keyword.t()) :: {:ok, t()}

Start a new ViewPort

Link to this function

start_driver(view_port, opts)

View Source
@spec start_driver(
  viewport :: t(),
  opts :: list()
) :: {:ok, pid :: GenServer.server()} | :error
@spec stop(viewport :: t()) :: :ok

Stop a running viewport

Link to this function

stop_driver(view_port, driver_pid)

View Source
@spec stop_driver(
  viewport :: t(),
  driver_pid :: GenServer.server()
) :: :ok