MacCASOutreach/graphicsvg/6.1.0

NOTE: the latest version is 9.0.0

GraphicSVG.Widget

Include GraphicSVG animations and functionality within your own elm/html app! Your existing code can utilize as many widgets as it likes; each one receives its own co-ordinate system and view!

See GitHub for a full example of widgets embedded inside Elm Html.

Static Icons

Static icons can render shapes without requiring you to modify your models and messages to include special things, as in the case of the Interactive Widgets below. Use this when you don't need the user to interact with the shapes (i.e. you don't need to receieve any messages from the shape). For example, avatars and icons can use this.

userMsg
-> String
-> Float
-> Float
-> List (Shape userMsg)
-> Html userMsg

A static graphic that returns no messages. Use this when you don't need to handle messages about clicking shapes, getting mouse positions, etc. This function is simpler and requires no other modifications to your model and message types.

You must include a type like NoOp meaning "do nothing" as well as a unique ID for the icon. For example to draw a circle with the name "Circle", use the following:

-- your message type should include a dummy NoOp message
type Msg =
      ..
    | NoOp

Then, use icon as follows in your view function:

icon NoOp "Circle" 50 50
    [
        circle 5
            |> filled blue
    ]

Interactive Widgets

Basic Types

The basic message and model types must be wrapped and contained within your app's message type and model type respectively. These are mostly opaque data types of which you do not need to know the gritty details. Simply include them in your app and you're all set!

type alias Wrapper userMsg =
Msg userMsg -> userMsg

A type alias representing a constructor of your type which wraps the Widget.Msg type. This will allow you to catch and pipe all messages to the Widget.update function to maintain the states of your app's various widgets. Most often, this is simply a constructor of your main type that looks something like:

-- your app's Message type
type Msg =
      ...
    | WidgetMessage Widget.Msg
    | ...

You can easily expand this constructor to include, for example, an integer or custom union type representing each of the widgets in your app. Your update function could then route the messages to where they need to go, and save the states of your various widgets where they need to be saved!

type alias Msg userMsg =
Msg userMsg

The messages a widget can send and receive. It is only necessary to pipe these by including them in your message type. Ensure that you send each and every message received in your main update function to the Widget.update function to maintain consistency. See the above discussion under Wrapper for a more detailed idea of how to use this.

type alias Model userMsg =
{ cw : Float
, ch : Float
, ww : Int
, wh : Int
, msgWrapper : Wrapper userMsg
, id : String
}

The state of a given widget. This is considered an opaque type; as such it is not necessary to know what is inside for your app to function. It stores information about the size of the widget and the co-ordinate system for purposes of rendering and sending messages with mouse position. It must be kept up to date using subscriptions and commands, and can be created with the init function. This is most often included in your app's model, e.g.

-- your app's Model type
type alias Model = 
    {
        ...
    ,   widgetState : Widget.Model
        ...
    }

Several of these could easily be saved in a structure such as a dictionary to maintain the state of several widgets at once!

Initialization

Some helper functions for initializing your widget(s).

Float
-> Float
-> Wrapper userMsg
-> String
-> ( Model userMsg, Cmd userMsg )

The init function takes the size of the widget as well as the wrapper message and a unique string identifier of the widget. You must ensure that the identifier is unique, otherwise undefined behaviour can occur related to notification functions (especially the *At functions). You are free to change the size or idenfier at any time throughout the execution of your program, simply by calling init again and storing the model. However, note that each time init is called for a given widget, its accompanying command must also be executed.

Note: You should issue the command given by init each time the widget becomes visible again after being offscreen, to ensure that the co-ordinate system is kept up-to-date. Therefore, storing the command for use later is a good idea.

subscriptions : Wrapper userMsg -> Sub userMsg

Generate a subscription related to each widget in your app. It must be active whenever the widget in question is onscreen. It can be active when the widget is not onscreen, however this is not necessary and may be a waste of resources.

Updating a widget model

update : Msg userMsg -> Model userMsg -> ( Model userMsg, Cmd userMsg )

Helper function to update the state of the widget. This can be considered a black box and should be used to simply update the Model stored inside of your app's model. Be sure to pipe every message through this update, to ensure the consistency of the widget's model and that you receive all notifications coming from the widget.

Rendering a widget

view : Model userMsg -> List (Shape userMsg) -> Html userMsg

Helper function which takes the widget's opaque model as well as a list of shapes to include in the widget. Widgets are compatible with all notification functions available in GraphicSVG. Each widget is given its own co-ordinate system defined by the widget's model. The *At notification functions return points relative to the widget. Widgets are clipped so they fit the aspect ratio defined in the widget's model.