on e4e8d89 help sign in
elk/libraries/Graphics/Element.elm
259 lines 8.6 kB history raw
2module Graphics.Element where
4import open Basics
5import Native.Utils
6import open JavaScript
7import List as List
8import open Color
9import Maybe (Maybe, Just, Nothing)
11type Properties = {
12 id : Int,
13 width : Int,
14 height : Int,
15 opacity : Float,
16 color : Maybe Color,
17 href : JSString,
18 tag : JSString,
19 hover : ()
20 }
22type Element = { props : Properties, element : ElementPrim }
24-- Get the width of an Element
25widthOf : Element -> Int
26widthOf e = e.props.width
28-- Get the height of an Element
29heightOf : Element -> Int
30heightOf e = e.props.height
32-- Get the width and height of an Element
33sizeOf : Element -> (Int,Int)
34sizeOf e = (e.props.width, e.props.height)
36-- Create an `Element` with a given width.
37width : Int -> Element -> Element
38width nw e = let p = e.props
39 props = case e.element of
40 Image _ w h _ -> {p| height <- h `div` w * nw }
41 RawHtml html -> {p| height <- let (w,h) = Native.Utils.htmlHeight nw html in h}
42 _ -> p
43 in { element=e.element, props={ props | width <- nw } }
45-- Create an `Element` with a given height.
46height : Int -> Element -> Element
47height nh e = let p = e.props
48 props = case e.element of
49 Image _ w h _ -> {p| width <- w `div` h * nh }
50 _ -> p
51 in { element=e.element, props={p| height <- nh} }
53-- Create an `Element` with a new width and height.
54size : Int -> Int -> Element -> Element
55size w h e = height h (width w e)
57-- Create an `Element` with a given opacity. Opacity is a number between 0 and 1
58-- where 0 means totally clear.
59opacity : Float -> Element -> Element
60opacity o e = let p = e.props in { element=e.element, props={p| opacity <- o} }
62-- Create an `Element` with a given background color.
63color : Color -> Element -> Element
64color c e = let p = e.props in
65 { element = e.element
66 , props = { p | color <- Just c}
67 }
69-- Create an `Element` with a tag. This lets you link directly to it.
70-- The element `(tag "all-about-badgers" thirdParagraph)` can be reached
71-- with a link lik this: `/facts-about-animals.elm#all-about-badgers`
72tag : String -> Element -> Element
73tag name e = let p = e.props in
74 { element=e.element, props={p | tag <- fromString name} }
76-- Create an `Element` that is a hyper-link.
77link : String -> Element -> Element
78link href e = let p = e.props in
79 { element=e.element, props={p | href <- fromString href} }
81emptyStr = fromString ""
82newElement w h e =
83 { props = Properties (Native.Utils.guid ()) w h 1 Nothing emptyStr emptyStr (), element = e }
85data ElementPrim
86 = Image ImageStyle Int Int JSString
87 | Container Position Element
88 | Flow Direction [Element]
89 | Spacer
90 | RawHtml JSString
91 | Custom -- for custom Elements implemented in JS, see collage for example
93data ImageStyle = Plain | Fitted | Cropped (Int,Int) | Tiled
95-- Create an image given a width, height, and image source.
96image : Int -> Int -> String -> Element
97image w h src = newElement w h (Image Plain w h (fromString src))
99-- Create a fitted image given a width, height, and image source.
100-- This will crop the picture to best fill the given dimensions.
101fittedImage : Int -> Int -> String -> Element
102fittedImage w h src = newElement w h (Image Fitted w h (fromString src))
104-- Create a cropped image. Take a rectangle out of the picture starting
105-- at the given top left coordinate. If you have a 140-by-140 image,
106-- the following will cut a 100-by-100 square out of the middle of it.
107--
108-- croppedImage (20,20) 100 100 "yogi.jpg"
109croppedImage : (Int,Int) -> Int -> Int -> String -> Element
110croppedImage pos w h src =
111 newElement w h (Image (Cropped pos) w h (fromString src))
113tiledImage : Int -> Int -> String -> Element
114tiledImage w h src =
115 newElement w h (Image Tiled w h (fromString src))
117markdown : Element
118markdown = Native.Utils.undefined
120data Three = P | Z | N
121data Pos = Absolute Int | Relative Float
122type Position = { horizontal : Three, vertical : Three, x : Pos, y : Pos }
124-- Put an element in a container. This lets you position the element really
125-- easily, and there are tons of ways to set the `Position`.
126-- To center `element` exactly in a 300-by-300 square you would say:
127--
128-- container 300 300 middle element
129--
130-- By setting the color of the container, you can create borders.
131container : Int -> Int -> Position -> Element -> Element
132container w h pos e = newElement w h (Container pos e)
134-- Create an empty box. This is useful for getting your spacing right and
135-- for making borders.
136spacer : Int -> Int -> Element
137spacer w h = newElement w h Spacer
139data Direction = DUp | DDown | DLeft | DRight | DIn | DOut
141-- Have a list of elements flow in a particular direction.
142-- The `Direction` starts from the first element in the list.
143--
144-- flow right [a,b,c]
145--
146-- +---+---+---+
147-- | a | b | c |
148-- +---+---+---+
149flow : Direction -> [Element] -> Element
150flow dir es =
151 let ws = List.map widthOf es
152 hs = List.map heightOf es
153 newFlow w h = newElement w h (Flow dir es)
154 in
155 if es == [] then spacer 0 0 else
156 case dir of
157 DUp -> newFlow (List.maximum ws) (List.sum hs)
158 DDown -> newFlow (List.maximum ws) (List.sum hs)
159 DLeft -> newFlow (List.sum ws) (List.maximum hs)
160 DRight -> newFlow (List.sum ws) (List.maximum hs)
161 DIn -> newFlow (List.maximum ws) (List.maximum hs)
162 DOut -> newFlow (List.maximum ws) (List.maximum hs)
164-- Stack elements vertically. To put `a` above `b` you would say:
165--
166-- a `above` b
167above : Element -> Element -> Element
168above hi lo =
169 newElement (max (widthOf hi) (widthOf lo))
170 (heightOf hi + heightOf lo)
171 (Flow DDown [hi,lo])
173-- Stack elements vertically. To put `a` below `b` you would say:
174--
175-- a `below` b
176below : Element -> Element -> Element
177below lo hi =
178 newElement (max (widthOf hi) (widthOf lo))
179 (heightOf hi + heightOf lo)
180 (Flow DDown [hi,lo])
182-- Put elements beside each other horizontally.
183beside : Element -> Element -> Element
184beside lft rht =
185 newElement (widthOf lft + widthOf rht)
186 (max (heightOf lft) (heightOf rht))
187 (Flow right [lft,rht])
189-- Layer elements on top of each other, starting from the bottom.
190-- `(layers == flow outward)`
191layers : [Element] -> Element
192layers es =
193 let ws = List.map widthOf es
194 hs = List.map heightOf es
195 in newElement (List.maximum ws) (List.maximum hs) (Flow DOut es)
198-- Repetitive things --
200absolute : Int -> Pos
201absolute = Absolute
202relative : Float -> Pos
203relative = Relative
205middle : Position
206middle = { horizontal=Z, vertical=Z, x=Relative 0.5, y=Relative 0.5 }
207topLeft : Position
208topLeft = { horizontal=N, vertical=P, x=Absolute 0, y=Absolute 0 }
209topRight : Position
210topRight = { topLeft | horizontal <- P }
211bottomLeft : Position
212bottomLeft = { topLeft | vertical <- N }
213bottomRight : Position
214bottomRight = { bottomLeft | horizontal <- P }
215midLeft : Position
216midLeft = { middle | horizontal <- N, x <- Absolute 0 }
217midRight : Position
218midRight = { midLeft | horizontal <- P }
219midTop : Position
220midTop = { middle | vertical <- P, y <- Absolute 0 }
221midBottom : Position
222midBottom = { midTop | vertical <- N }
224middleAt : Pos -> Pos -> Position
225middleAt x y = { horizontal = Z, vertical = Z, x = x, y = y }
226topLeftAt : Pos -> Pos -> Position
227topLeftAt x y = { horizontal = N, vertical = P, x = x, y = y }
228topRightAt : Pos -> Pos -> Position
229topRightAt x y = { horizontal = P, vertical = P, x = x, y = y }
230bottomLeftAt : Pos -> Pos -> Position
231bottomLeftAt x y = { horizontal = N, vertical = N, x = x, y = y }
232bottomRightAt : Pos -> Pos -> Position
233bottomRightAt x y = { horizontal = P, vertical = N, x = x, y = y }
234midLeftAt : Pos -> Pos -> Position
235midLeftAt x y = { horizontal = N, vertical = Z, x = x, y = y }
236midRightAt : Pos -> Pos -> Position
237midRightAt x y = { horizontal = P, vertical = Z, x = x, y = y }
238midTopAt : Pos -> Pos -> Position
239midTopAt x y = { horizontal = Z, vertical = P, x = x, y = y }
240midBottomAt : Pos -> Pos -> Position
241midBottomAt x y = { horizontal = Z, vertical = N, x = x, y = y }
243up : Direction
244up = DUp
246down : Direction
247down = DDown
249left : Direction
250left = DLeft
252right : Direction
253right = DRight
255inward : Direction
256inward = DIn
258outward : Direction
259outward = DOut