Module

Reactor

Package
purescript-grid-reactors
Repository
Eugleo/purescript-grid-reactors

As the Pyret documentation puts it, a reactor is a value enabling the creation of time-based animations, simulations, and interactive programs. During the creation of a reactor, the user supplies a function for handling clock ticks, and functions for handling mouse and keyboard input events. The reactor calls these event handlers whenever the respective event occurs. From within the event handlers, the reactor's state — or, the world, as we call it — can be updated. After each world update, the reactor renders the new world with the supplied drawing function.

You can read more in the documentation of the type Reactor below, under the documentation for the module Reactor.Types. The base Reactor module re-exports some useful functions, apart from the base color pallete which is best imported by import Reactor.Graphics.Colors as Color.

In our implementation, the rendering is done using a hooks-based Halogen component. Also, the world needs to have at least a boolean field named paused, that signalizes to the reactor whether the internal clock should be ticking or not.

#runReactor Source

runReactor :: forall world. Reactor world -> Configuration -> Effect Unit

Start a Reactor and render it asynchronously as a Halogen component with the given Configuration. The reactor's world is required to have a field named paused that is used to decide whether the reactor's clock should be running (paused: false) or not (paused: true).

Re-exports from Reactor.Internal.Helpers

#withJust Source

withJust :: forall a b m. Applicative m => Maybe a -> (a -> m b) -> m Unit

Re-exports from Reactor.Internal.Widget

#Widget Source

data Widget

Currently we support two types of widgets: sections and labels. Those two differ only in the way they are rendered. We recommend using labels for presenting important dynamic information to the user, and using sections to give short, descriptive labels to these pieces of information.

Constructors

  • Section { title :: String }
  • Label { content :: String }

    Button { importance :: Importance, label :: String, action :: world -> world }

Re-exports from Reactor.Reaction

#updateW_ Source

updateW_ :: forall changes t c world. Union changes t world => Nub changes c => Record changes -> Reaction (Record world)

Modify certain fields of the world by passing new values for them. Behaves the same as the updateW function, but returns unit instead of the new value of the world.

type World = { player :: { x :: Int, y :: Int } }

handleEvent event = case event of
  KeyPress { key : "ArrowUp" } -> do
    oldWorld <- getW
    updateW_
      { player: { x : oldWorld.player.x, y: oldWorld.player.y + 1 } }
    newWorld <- getW
    log $ "New world is: " <> show newWorld

#updateW Source

updateW :: forall changes t c world. Union changes t world => Nub changes c => Record changes -> ReactionM (Record world) (Record world)

Modify certain fields of the world by passing new values for them.

Returns the value of the new world.

type World = { player :: { x :: Int, y :: Int } }

handleEvent event = case event of
  KeyPress { key : "ArrowUp" } ->
    oldWorld <- getW
    newWorld <-
      updateW
        { player: { x : oldWorld.player.x, y: oldWorld.player.y + 1 } }
    log $ "New world is: " <> show newWorld

#modifyW_ Source

modifyW_ :: forall world. (world -> world) -> Reaction world

Similar to modifyW, but doesn't return the value of the new world.

#modifyW Source

modifyW :: forall world. (world -> world) -> ReactionM world world

Modify the current world by passing in a (world -> world) updating function. The function will receive the current value of the world, and should return the new updated value.

Returns the value of the new world.

type World = { player :: { x :: Int, y :: Int } }

handleEvent event = case event of
  KeyPress { key : "ArrowUp" } ->
    newWorld <- modifyW \oldWorld ->
      { player: { x : oldWorld.player.x, y: oldWorld.player.y + 1 } }
    log $ "New world is: " <> show newWorld

#getW Source

getW :: forall world. ReactionM world world

Obtain the current value of the world.

type World = { player :: { x :: Int, y :: Int } }

handleEvent event = case event of
  KeyPress { key : "Space" } -> do
    w <- getW
    log $ "The player is at position: "
      <> show w.player.x
      <> ", "
      <> show w.player.y

#executeDefaultBehavior Source

executeDefaultBehavior :: forall world. Reaction world

After handling the event, execute the default behavior as well. You can read more in the documentation of Reactor.Events.

Usually you'll call this in the handleEvent function, for events that you don't want to handle, e.g. when pressing 'J' doesn't do anything in your game.

#dimensions Source

dimensions :: forall world. ReactionM world Dimensions

Get a record of the following:

  • height :: Int, the height of the grid, in tiles
  • width :: Int, the width of the grid, in tiles
  • tileSize :: Int the size of one grid tile, in points. The size is set internally, and this is the only way to get its value.

Re-exports from Reactor.Types

#Reactor Source

type Reactor world = { draw :: world -> Drawing, handleEvent :: Event -> Reaction world, initial :: world, isPaused :: world -> Boolean }

The reactor is a simple record. Reactors are parametrized over the type of the world which is saved in the reactor.

The fields in the record are the following:

  • initial is the initial state of the reactor's world
  • isPaused is a function used by the reactor to find out whether its internal clock should be paused or not. The clock is paused when isPaused returns true.
  • draw is a function used to render the world anytime it is changed
  • handleEvent is a function for handling the three types of events that can occur during your game or simulation: keypress events, mouse events, and tick events. The events are handled by running the Reaction returned by handleEvent.
    • Tick events are fired on every tick of the reactor's clock, around 60 times a second, provided the reactor is not paused.
    • Keypress events are fired when a key on a keyboard is pressed.
    • Mouse events are fired whenever a mouse is moved above the canvas (the drawing area of the reactor), when the canvas is clicked, or when the user drags something (i.e. clicks and then moves the mouse).

#Configuration Source

type Configuration = { height :: Int, title :: String, widgets :: Array (String /\ Widget), width :: Int }

Configuration for the Halogen component that renders the reactor. The component creates everything on the webpage you see when you run the reactor. Although a reactor is a general structure, this library focuses on reactors with grid-based rendering. The component thus needs a little more info than just what is in already the reactor, namely:

  • title, the title of the webpage where the reactor is rendered
  • width, the width of the grid, in tiles
  • height the height of the grid, in tiles
  • widgets is an array of widgets that are present in the Reactor from the start, ordered from top-most to bottom-most Notably, the size of the rendered tiles isn't customizable and is set to 30pts.