on 20e2ff0 help sign in
elk/libraries/Graphics/Element.elm
256 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))
117data Three = P | Z | N
118data Pos = Absolute Int | Relative Float
119type Position = { horizontal : Three, vertical : Three, x : Pos, y : Pos }
121-- Put an element in a container. This lets you position the element really
122-- easily, and there are tons of ways to set the `Position`.
123-- To center `element` exactly in a 300-by-300 square you would say:
124--
125-- container 300 300 middle element
126--
127-- By setting the color of the container, you can create borders.
128container : Int -> Int -> Position -> Element -> Element
129container w h pos e = newElement w h (Container pos e)
131-- Create an empty box. This is useful for getting your spacing right and
132-- for making borders.
133spacer : Int -> Int -> Element
134spacer w h = newElement w h Spacer
136data Direction = DUp | DDown | DLeft | DRight | DIn | DOut
138-- Have a list of elements flow in a particular direction.
139-- The `Direction` starts from the first element in the list.
140--
141-- flow right [a,b,c]
142--
143-- +---+---+---+
144-- | a | b | c |
145-- +---+---+---+
146flow : Direction -> [Element] -> Element
147flow dir es =
148 let ws = List.map widthOf es
149 hs = List.map heightOf es
150 newFlow w h = newElement w h (Flow dir es)
151 in
152 if es == [] then spacer 0 0 else
153 case dir of
154 DUp -> newFlow (List.maximum ws) (List.sum hs)
155 DDown -> newFlow (List.maximum ws) (List.sum hs)
156 DLeft -> newFlow (List.sum ws) (List.maximum hs)
157 DRight -> newFlow (List.sum ws) (List.maximum hs)
158 DIn -> newFlow (List.maximum ws) (List.maximum hs)
159 DOut -> newFlow (List.maximum ws) (List.maximum hs)
161-- Stack elements vertically. To put `a` above `b` you would say:
162--
163-- a `above` b
164above : Element -> Element -> Element
165above hi lo =
166 newElement (max (widthOf hi) (widthOf lo))
167 (heightOf hi + heightOf lo)
168 (Flow DDown [hi,lo])
170-- Stack elements vertically. To put `a` below `b` you would say:
171--
172-- a `below` b
173below : Element -> Element -> Element
174below lo hi =
175 newElement (max (widthOf hi) (widthOf lo))
176 (heightOf hi + heightOf lo)
177 (Flow DDown [hi,lo])
179-- Put elements beside each other horizontally.
180beside : Element -> Element -> Element
181beside lft rht =
182 newElement (widthOf lft + widthOf rht)
183 (max (heightOf lft) (heightOf rht))
184 (Flow right [lft,rht])
186-- Layer elements on top of each other, starting from the bottom.
187-- `(layers == flow outward)`
188layers : [Element] -> Element
189layers es =
190 let ws = List.map widthOf es
191 hs = List.map heightOf es
192 in newElement (List.maximum ws) (List.maximum hs) (Flow DOut es)
195-- Repetitive things --
197absolute : Int -> Pos
198absolute = Absolute
199relative : Float -> Pos
200relative = Relative
202middle : Position
203middle = { horizontal=Z, vertical=Z, x=Relative 0.5, y=Relative 0.5 }
204topLeft : Position
205topLeft = { horizontal=N, vertical=P, x=Absolute 0, y=Absolute 0 }
206topRight : Position
207topRight = { topLeft | horizontal <- P }
208bottomLeft : Position
209bottomLeft = { topLeft | vertical <- N }
210bottomRight : Position
211bottomRight = { bottomLeft | horizontal <- P }
212midLeft : Position
213midLeft = { middle | horizontal <- N, x <- Absolute 0 }
214midRight : Position
215midRight = { midLeft | horizontal <- P }
216midTop : Position
217midTop = { middle | vertical <- P, y <- Absolute 0 }
218midBottom : Position
219midBottom = { midTop | vertical <- N }
221middleAt : Pos -> Pos -> Position
222middleAt x y = { horizontal = Z, vertical = Z, x = x, y = y }
223topLeftAt : Pos -> Pos -> Position
224topLeftAt x y = { horizontal = N, vertical = P, x = x, y = y }
225topRightAt : Pos -> Pos -> Position
226topRightAt x y = { horizontal = P, vertical = P, x = x, y = y }
227bottomLeftAt : Pos -> Pos -> Position
228bottomLeftAt x y = { horizontal = N, vertical = N, x = x, y = y }
229bottomRightAt : Pos -> Pos -> Position
230bottomRightAt x y = { horizontal = P, vertical = N, x = x, y = y }
231midLeftAt : Pos -> Pos -> Position
232midLeftAt x y = { horizontal = N, vertical = Z, x = x, y = y }
233midRightAt : Pos -> Pos -> Position
234midRightAt x y = { horizontal = P, vertical = Z, x = x, y = y }
235midTopAt : Pos -> Pos -> Position
236midTopAt x y = { horizontal = Z, vertical = P, x = x, y = y }
237midBottomAt : Pos -> Pos -> Position
238midBottomAt x y = { horizontal = Z, vertical = N, x = x, y = y }
240up : Direction
241up = DUp
243down : Direction
244down = DDown
246left : Direction
247left = DLeft
249right : Direction
250right = DRight
252inward : Direction
253inward = DIn
255outward : Direction
256outward = DOut