elm/libraries/Graphics/Input.elm
2014-02-18 12:23:54 -05:00

203 lines
6.7 KiB
Elm

module Graphics.Input where
{-| This module is for creating standard input widgets such as buttons and
text fields. All functions in this library follow a general pattern in which
you create an `Input` that many elements can report to:
```haskell
clicks : Input ()
clicks = input ()
clickableYogi : Element
clickableYogi = clickable clicks.handle () (image 40 40 "/yogi.jpg")
```
Whenever the user clicks on the resulting `clickableYogi` element, it sends an
update to the `clicks` input. You will see this pattern again and again in
examples in this library, so just read on to get a better idea of how it works!
# Creating Inputs
@docs Input, input
# Basic Input Elements
Text fields come later.
@docs button, customButton, checkbox, dropDown
# Clicks and Hovers
@docs clickable, hoverable
# Text Fields
@docs field, password, email, noContent, FieldContent, Selection, Direction
-}
import Signal (Signal)
import Graphics.Element (Element)
import Native.Graphics.Input
data Handle a = Handle
{-| This is the key abstraction of this library. An `Input` is a record
of two fields:
1. `signal` — all values coming to this input from “the world”
2. `handle` — a way to refer to this particular input and send it values
This will make more sense as you see more examples.
-}
type Input a = { signal : Signal a, handle : Handle a }
{-| Create a new `Input`. You just give the initial value for the
input’s `signal`.
Note for Advanced Users: creating an `Input` is an inherently imperative
action, so this is one of very few impure functions in Elm. That means
`(input ())` and `(input ())` are actually two different inputs with different
signals and handles. By design, Elm’s impure functions can only be useful
as you build your signal graph at startup, so Elm is still pure at runtime.
-}
input : a -> Input a
input = Native.Graphics.Input.input
{-| Create a standard button. The following example begins making a basic
calculator:
data Keys = Number Int | Plus | Minus | Clear
keys : Input Keys
keys = input Clear
calculator : Element
calculator =
flow right [ button keys.handle (Number 1) "1"
, button keys.handle (Number 2) "2"
, button keys.handle Plus "+"
]
If the user presses the "+" button, `keys.signal` will update to `Plus`. If the
users presses "2", `keys.signal` will update to `(Number 2)`.
-}
button : Handle a -> a -> String -> Element
button = Native.Graphics.Input.button
customButton : Handle a -> a -> Element -> Element -> Element -> Element
customButton = Native.Graphics.Input.customButton
checkbox : Handle a -> (Bool -> a) -> Bool -> Element
checkbox = Native.Graphics.Input.checkbox
{-| Create a drop-down menu. The following drop-down lets you choose your
favorite British sport:
data Sport = Football | Cricket | Snooker
sport : Input Sport
sport = input Nothing
sportDropDown : Element
sportDropDown =
dropDown sport.handle
[ ("" , Nothing)
, ("Football", Just Football)
, ("Cricket" , Just Cricket)
, ("Snooker" , Just Snooker)
]
If the user selects "Football" from the drop down menue, `sport.signal`
will update to `Just Football`.
-}
dropDown : Handle a -> [(String,a)] -> Element
dropDown = Native.Graphics.Input.dropDown
{-| Detect mouse hovers over a specific `Element`. In the following example,
we will create a hoverable picture called `cat`.
hover : Input Bool
hover = input False
cat : Element
cat = image 30 30 "/cat.jpg"
|> hoverable hover.handle id
When the mouse hovers above the `cat` element, `hover.signal` will become
`True`. When the mouse leaves it, `hover.signal` will become `False`.
-}
hoverable : Handle a -> (Bool -> a) -> Element -> Element
hoverable = Native.Graphics.Input.hoverable
{-| Detect mouse clicks on a specific `Element`. In the following example,
we will create a clickable picture called `cat`.
data Picture = Cat | Hat
picture : Input Picture
picture = input Cat
cat : Element
cat = image 30 30 "/cat.jpg"
|> clickable picture.handle Cat
hat : Element
hat = image 30 30 "/hat.jpg"
|> clickable picture.handle Hat
When the user clicks on the `cat` element, `picture.signal` receives
an update containing the value `Cat`. When the user clicks on the `hat` element,
`picture.signal` receives an update containing the value `Hat`. This lets you
distinguish which element was clicked. In a more complex example, they could be
distinguished with IDs or more complex data structures.
-}
clickable : Handle a -> a -> Element -> Element
clickable = Native.Graphics.Input.clickable
{-| Represents the current content of a text field. For example:
FieldContent "She sells sea shells" (Selection 0 3 Backward)
This means the user highlighted the substring `"She"` backwards.
-}
type FieldContent = { string:String, selection:Selection }
{-| The selection within a text field. `start` is never greater than `end`:
Selection 0 0 Forward -- cursor precedes all characters
Selection 5 9 Backward -- highlighting characters starting after
-- the 5th and ending after the 9th
-}
type Selection = { start:Int, end:Int, direction:Direction }
{-| The direction of selection.-}
data Direction = Forward | Backward
{-| A field with no content:
FieldContent "" (Selection 0 0 Forward)
-}
noContent : FieldContent
noContent = FieldContent "" (Selection 0 0 Forward)
{-| Create a text field. The following example creates a time-varying element
called `nameField`. As the user types their name, the field will be updated
to match what they have entered.
name : Input FieldContent
name = input noContent
nameField : Signal Element
nameField = field name.handle (\content -> content) "Name" <~ nameContent
-}
field : Handle a -> (FieldContent -> a) -> String -> FieldContent -> Element
field = Native.Graphics.Input.field
{-| Same as `field` but the UI element blocks out each characters. -}
password : Handle a -> (FieldContent -> a) -> String -> FieldContent -> Element
password = Native.Graphics.Input.password
{-| Same as `field` but it adds an annotation that this field is for email
addresses. This is helpful for auto-complete and for mobile users who may
get a custom keyboard with an `@` and `.com` button.
-}
email : Handle a -> (FieldContent -> a) -> String -> FieldContent -> Element
email = Native.Graphics.Input.email
-- area : Handle a -> (FieldContent -> a) -> Handle b -> ((Int,Int) -> b) -> (Int,Int) -> String -> FieldContent -> Element
-- area = Native.Graphics.Input.area