elm/libraries/Graphics/Element.elm

264 lines
9 KiB
Elm
Raw Normal View History

module Graphics.Element (widthOf, heightOf, sizeOf,
width, height, size, opacity, color, tag, link,
image, fittedImage, croppedImage, tiledImage,
flow, up, down, left, right, inward, outward,
above, below, beside, layers,
container, absolute, relative,
middle, topLeft, topRight, bottomLeft, bottomRight,
midLeft, midRight, midTop, midBottom, middleAt,
topLeftAt, topRightAt, bottomLeftAt, bottomRightAt,
midLeftAt, midRightAt, midTopAt, midBottomAt,
2013-03-12 08:51:54 +00:00
spacer, newElement
) where
import Native.Utils (guid, max, htmlHeight)
import JavaScript as JS
import List as List
2013-04-28 12:21:46 +00:00
import Color
import Maybe (Just, Nothing)
type Properties = {
id : Int,
width : Int,
height : Int,
opacity : Float,
2013-03-07 19:06:48 +00:00
color : Maybe Color,
href : JSString,
2013-06-15 07:49:22 +00:00
tag : JSString,
hover : ()
}
2013-03-07 19:06:48 +00:00
type Element = { props : Properties, element : ElementPrim }
-- Get the width of an Element
widthOf : Element -> Int
widthOf e = e.props.width
-- Get the height of an Element
heightOf : Element -> Int
2013-03-07 19:06:48 +00:00
heightOf e = e.props.height
-- Get the width and height of an Element
sizeOf : Element -> (Int,Int)
sizeOf e = (e.props.width, e.props.height)
-- Create an `Element` with a given width.
width : Int -> Element -> Element
width nw e = let p = e.props
props = case e.element of
Image _ w h _ -> {p| height <- h/w*nw }
2013-04-15 01:07:26 +00:00
RawHtml html -> {p| height <- let (w,h) = htmlHeight nw html in h}
_ -> p
in { element=e.element, props={props| width <- nw} }
-- Create an `Element` with a given height.
height : Int -> Element -> Element
height nh e = let p = e.props
props = case e.element of
Image _ w h _ -> {p| width <- w/h*nh }
_ -> p
in { element=e.element, props={p| height <- nh} }
-- Create an `Element` with a new width and height.
size : Int -> Int -> Element -> Element
size w h e = height h (width w e)
-- Create an `Element` with a given opacity. Opacity is a number between 0 and 1
-- where 0 means totally clear.
opacity : Float -> Element -> Element
2013-03-07 19:06:48 +00:00
opacity o e = let p = e.props in { element=e.element, props={p| opacity <- o} }
-- Create an `Element` with a given background color.
color : Color -> Element -> Element
color c e = let p = e.props in
{ element=e.element, props={p| color <- Just c} }
-- Create an `Element` with a tag. This lets you link directly to it.
-- The element `(tag "all-about-badgers" thirdParagraph)` can be reached
-- with a link lik this: `/facts-about-animals.elm#all-about-badgers`
tag : String -> Element -> Element
2013-03-07 19:06:48 +00:00
tag name e = let p = e.props in
{ element=e.element, props={p| tag <- JS.fromString name} }
-- Create an `Element` that is a hyper-link.
link : String -> Element -> Element
2013-03-07 19:06:48 +00:00
link href e = let p = e.props in
{ element=e.element, props={p| href <- JS.fromString href} }
emptyStr = JS.fromString ""
newElement w h e =
2013-06-15 07:49:22 +00:00
{ props = Properties (guid ()) w h 1 Nothing emptyStr emptyStr (), element = e }
data ElementPrim
2013-03-07 19:06:48 +00:00
= Image ImageStyle Int Int JSString
| Container Position Element
| Flow Direction [Element]
| Spacer
| RawHtml JSString
| Custom -- for custom Elements implemented in JS, see collage for example
data ImageStyle = Plain | Fitted | Cropped (Int,Int) | Tiled
2013-03-07 19:06:48 +00:00
-- Create an image given a width, height, and image source.
image : Int -> Int -> String -> Element
2013-03-07 19:06:48 +00:00
image w h src = newElement w h (Image Plain w h (JS.fromString src))
-- Create a fitted image given a width, height, and image source.
-- This will crop the picture to best fill the given dimensions.
fittedImage : Int -> Int -> String -> Element
2013-03-07 19:06:48 +00:00
fittedImage w h src = newElement w h (Image Fitted w h (JS.fromString src))
-- Create a cropped image. Take a rectangle out of the picture starting
-- at the given top left coordinate. If you have a 140-by-140 image,
-- the following will cut a 100-by-100 square out of the middle of it.
--
-- croppedImage (20,20) 100 100 "yogi.jpg"
croppedImage : (Int,Int) -> Int -> Int -> String -> Element
croppedImage pos w h src =
2013-03-07 19:06:48 +00:00
newElement w h (Image (Cropped pos) w h (JS.fromString src))
tiledImage : Int -> Int -> String -> Element
tiledImage w h src =
newElement w h (Image Tiled w h (JS.fromString src))
2013-03-07 19:06:48 +00:00
data Three = P | Z | N
data Pos = Absolute Int | Relative Float
type Position = { horizontal : Three, vertical : Three, x : Pos, y : Pos }
-- Put an element in a container. This lets you position the element really
-- easily, and there are tons of ways to set the `Position`.
-- To center `element` exactly in a 300-by-300 square you would say:
--
-- container 300 300 middle element
--
-- By setting the color of the container, you can create borders.
container : Int -> Int -> Position -> Element -> Element
container w h pos e = newElement w h (Container pos e)
-- Create an empty box. This is useful for getting your spacing right and
-- for making borders.
spacer : Int -> Int -> Element
spacer w h = newElement w h Spacer
2013-03-07 19:06:48 +00:00
data Direction = DUp | DDown | DLeft | DRight | DIn | DOut
-- Have a list of elements flow in a particular direction.
-- The `Direction` starts from the first element in the list.
--
-- flow right [a,b,c]
--
-- +---+---+---+
-- | a | b | c |
-- +---+---+---+
flow : Direction -> [Element] -> Element
flow dir es =
2013-03-12 08:51:54 +00:00
let ws = List.map widthOf es
hs = List.map heightOf es
newFlow w h = newElement w h (Flow dir es)
in
if es == [] then spacer 0 0 else
case dir of
DUp -> newFlow (List.maximum ws) (List.sum hs)
DDown -> newFlow (List.maximum ws) (List.sum hs)
DLeft -> newFlow (List.sum ws) (List.maximum hs)
DRight -> newFlow (List.sum ws) (List.maximum hs)
DIn -> newFlow (List.maximum ws) (List.maximum hs)
DOut -> newFlow (List.maximum ws) (List.maximum hs)
-- Stack elements vertically. To put `a` above `b` you would say:
--
-- a `above` b
above : Element -> Element -> Element
above hi lo =
newElement (max (widthOf hi) (widthOf lo))
(heightOf hi + heightOf lo)
(Flow DDown [hi,lo])
-- Stack elements vertically. To put `a` below `b` you would say:
--
-- a `below` b
below : Element -> Element -> Element
below lo hi =
newElement (max (widthOf hi) (widthOf lo))
(heightOf hi + heightOf lo)
(Flow DDown [hi,lo])
-- Put elements beside each other horizontally.
beside : Element -> Element -> Element
beside lft rht =
newElement (widthOf lft + widthOf rht)
(max (heightOf lft) (heightOf rht))
(Flow right [lft,rht])
2013-03-07 19:06:48 +00:00
-- Layer elements on top of each other, starting from the bottom.
-- `(layers == flow outward)`
layers : [Element] -> Element
2013-03-07 19:06:48 +00:00
layers es =
2013-03-12 08:51:54 +00:00
let ws = List.map widthOf es
hs = List.map heightOf es
in newElement (List.maximum ws) (List.maximum hs) (Flow DOut es)
2013-03-07 19:06:48 +00:00
-- Repetitive things --
absolute : Int -> Pos
2013-03-07 19:06:48 +00:00
absolute = Absolute
relative : Float -> Pos
2013-03-07 19:06:48 +00:00
relative = Relative
middle : Position
middle = { horizontal=Z, vertical=Z, x=Relative 0.5, y=Relative 0.5 }
topLeft : Position
topLeft = { horizontal=N, vertical=P, x=Absolute 0, y=Absolute 0 }
topRight : Position
2013-03-07 19:06:48 +00:00
topRight = { topLeft | horizontal <- P }
bottomLeft : Position
2013-03-07 19:06:48 +00:00
bottomLeft = { topLeft | vertical <- N }
bottomRight : Position
2013-03-07 19:06:48 +00:00
bottomRight = { bottomLeft | horizontal <- P }
midLeft : Position
2013-03-07 19:06:48 +00:00
midLeft = { middle | horizontal <- N, x <- Absolute 0 }
midRight : Position
2013-03-07 19:06:48 +00:00
midRight = { midLeft | horizontal <- P }
midTop : Position
2013-03-07 19:06:48 +00:00
midTop = { middle | vertical <- P, y <- Absolute 0 }
midBottom : Position
2013-03-07 19:06:48 +00:00
midBottom = { midTop | vertical <- N }
middleAt : Pos -> Pos -> Position
2013-03-07 19:06:48 +00:00
middleAt x y = { horizontal = Z, vertical = Z, x = x, y = y }
topLeftAt : Pos -> Pos -> Position
2013-03-07 19:06:48 +00:00
topLeftAt x y = { horizontal = N, vertical = P, x = x, y = y }
topRightAt : Pos -> Pos -> Position
2013-03-07 19:06:48 +00:00
topRightAt x y = { horizontal = P, vertical = P, x = x, y = y }
bottomLeftAt : Pos -> Pos -> Position
2013-03-07 19:06:48 +00:00
bottomLeftAt x y = { horizontal = N, vertical = N, x = x, y = y }
bottomRightAt : Pos -> Pos -> Position
2013-03-07 19:06:48 +00:00
bottomRightAt x y = { horizontal = P, vertical = N, x = x, y = y }
midLeftAt : Pos -> Pos -> Position
2013-03-07 19:06:48 +00:00
midLeftAt x y = { horizontal = N, vertical = Z, x = x, y = y }
midRightAt : Pos -> Pos -> Position
2013-03-07 19:06:48 +00:00
midRightAt x y = { horizontal = P, vertical = Z, x = x, y = y }
midTopAt : Pos -> Pos -> Position
2013-03-07 19:06:48 +00:00
midTopAt x y = { horizontal = Z, vertical = P, x = x, y = y }
midBottomAt : Pos -> Pos -> Position
2013-03-07 19:06:48 +00:00
midBottomAt x y = { horizontal = Z, vertical = N, x = x, y = y }
up : Direction
2013-03-07 19:06:48 +00:00
up = DUp
down : Direction
2013-03-07 19:06:48 +00:00
down = DDown
left : Direction
2013-03-07 19:06:48 +00:00
left = DLeft
right : Direction
2013-03-07 19:06:48 +00:00
right = DRight
inward : Direction
2013-03-07 19:06:48 +00:00
inward = DIn
outward : Direction
2013-03-07 19:06:48 +00:00
outward = DOut