on e8dffac 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 JavaScript as JS
7import JavaScript (JSString)
8import List as List
9import open Color
10import Maybe (Maybe, Just, Nothing)
12type Properties = {
13 id : Int,
14 width : Int,
15 height : Int,
16 opacity : Float,
17 color : Maybe Color,
18 href : JSString,
19 tag : JSString,
20 hover : ()
21 }
23type Element = { props : Properties, element : ElementPrim }
25-- Get the width of an Element
26widthOf : Element -> Int
27widthOf e = e.props.width
29-- Get the height of an Element
30heightOf : Element -> Int
31heightOf e = e.props.height
33-- Get the width and height of an Element
34sizeOf : Element -> (Int,Int)
35sizeOf e = (e.props.width, e.props.height)
37-- Create an `Element` with a given width.
38width : Int -> Element -> Element
39width nw e =
40 let p = e.props
41 props = case e.element of
42 Image _ w h _ -> {p| height <- round (toFloat h / toFloat w * toFloat nw) }
43 RawHtml html -> {p| height <- snd (Native.Utils.htmlHeight nw html)}
44 _ -> p
45 in { element=e.element, props={ props | width <- nw } }
47-- Create an `Element` with a given height.
48height : Int -> Element -> Element
49height nh e =
50 let p = e.props
51 props = case e.element of
52 Image _ w h _ -> {p| width <- round (toFloat w / toFloat h * toFloat nh) }
53 _ -> p
54 in { element=e.element, props={ p | height <- nh} }
56-- Create an `Element` with a new width and height.
57size : Int -> Int -> Element -> Element
58size w h e = height h (width w e)
60-- Create an `Element` with a given opacity. Opacity is a number between 0 and 1
61-- where 0 means totally clear.
62opacity : Float -> Element -> Element
63opacity o e = let p = e.props in { element=e.element, props={p| opacity <- o} }
65-- Create an `Element` with a given background color.
66color : Color -> Element -> Element
67color c e = let p = e.props in
68 { element = e.element
69 , props = { p | color <- Just c}
70 }
72-- Create an `Element` with a tag. This lets you link directly to it.
73-- The element `(tag "all-about-badgers" thirdParagraph)` can be reached
74-- with a link lik this: `/facts-about-animals.elm#all-about-badgers`
75tag : String -> Element -> Element
76tag name e = let p = e.props in
77 { element=e.element, props={p | tag <- JS.fromString name} }
79-- Create an `Element` that is a hyper-link.
80link : String -> Element -> Element
81link href e = let p = e.props in
82 { element=e.element, props={p | href <- JS.fromString href} }
84emptyStr = JS.fromString ""
85newElement w h e =
86 { props = Properties (Native.Utils.guid ()) w h 1 Nothing emptyStr emptyStr (), element = e }
88data ElementPrim
89 = Image ImageStyle Int Int JSString
90 | Container Position Element
91 | Flow Direction [Element]
92 | Spacer
93 | RawHtml JSString
94 | Custom -- for custom Elements implemented in JS, see collage for example
96data ImageStyle = Plain | Fitted | Cropped (Int,Int) | Tiled
98-- Create an image given a width, height, and image source.
99image : Int -> Int -> String -> Element
100image w h src = newElement w h (Image Plain w h (JS.fromString src))
102-- Create a fitted image given a width, height, and image source.
103-- This will crop the picture to best fill the given dimensions.
104fittedImage : Int -> Int -> String -> Element
105fittedImage w h src = newElement w h (Image Fitted w h (JS.fromString src))
107-- Create a cropped image. Take a rectangle out of the picture starting
108-- at the given top left coordinate. If you have a 140-by-140 image,
109-- the following will cut a 100-by-100 square out of the middle of it.
110--
111-- croppedImage (20,20) 100 100 "yogi.jpg"
112croppedImage : (Int,Int) -> Int -> Int -> String -> Element
113croppedImage pos w h src =
114 newElement w h (Image (Cropped pos) w h (JS.fromString src))
116tiledImage : Int -> Int -> String -> Element
117tiledImage w h src =
118 newElement w h (Image Tiled w h (JS.fromString src))
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