~alcinnz/CatTrap

ref: 7301a4893d99b4a5bf016000097369b492f23aea CatTrap/Graphics/Layout/Flex.hs -rw-r--r-- 6.1 KiB
7301a489 — Adrian Cochrane Integrate flexbox CSS property parsing. 11 months ago
                                                                                
4afd3f9a Adrian Cochrane
1a650ede Adrian Cochrane
4afd3f9a Adrian Cochrane
7301a489 Adrian Cochrane
4afd3f9a Adrian Cochrane
7301a489 Adrian Cochrane
4afd3f9a Adrian Cochrane
7301a489 Adrian Cochrane
bc72114a Adrian Cochrane
7301a489 Adrian Cochrane
4afd3f9a Adrian Cochrane
7301a489 Adrian Cochrane
4afd3f9a Adrian Cochrane
5a9105c7 Adrian Cochrane
1a650ede Adrian Cochrane
5a9105c7 Adrian Cochrane
bc72114a Adrian Cochrane
5a9105c7 Adrian Cochrane
1a650ede Adrian Cochrane
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
module Graphics.Layout.Flex(FlexParent(..), FlexChild(..),
    Direction(..), FlexWrapping(..), Justification(..), Alignment(..),
    flexMaxBasis, flexSumBasis, flexWrap) where

import Graphics.Layout.Box as B (Length, lowerLength, Size(..), PaddedBox(..),
        maxWidth, width, minWidth, maxHeight, height, minHeight)
import Data.List (intersperse)

data FlexParent a b = FlexParent {
    direction :: Direction,
    reverseRows :: Bool,
    wrap :: FlexWrapping,
    justify :: Justification,
    alignLines :: Maybe Justification, -- `Nothing` is "stretch"
    baseGap :: b,
    crossGap :: b,
    children :: [[FlexChild a b]] -- 2D list to store lines once split.
} deriving (Eq, Show, Read)
data FlexChild a b = FlexChild {
    grow :: Double,
    shrink :: Double,
    basis :: b,
    alignment :: Alignment,
    flexInner :: a
} deriving (Eq, Show, Read)

data Direction = Row | Column deriving (Eq, Show, Read)
data FlexWrapping = NoWrap | Wrap | WrapReverse deriving (Eq, Show, Read)
data Justification = JStart | JEnd | JCenter | JSpaceBetween | JSpaceAround | JSpaceEvenly
    deriving (Eq, Show, Read)
data Alignment = AlStretch | AlStart | AlEnd | AlCenter | AlBaseline
    deriving (Eq, Show, Read)

flexMaxBasis :: FlexParent a Length -> Double -> Double
flexMaxBasis self outersize = maximum [lowerLength outersize $ basis child |
        row <- children self, child <- row]
flexSumBasis :: FlexParent a Length -> Double -> Double
flexSumBasis self size = maximum [Prelude.sum $ map (lowerLength size) $
        intersperse (baseGap self) $ map basis row | row <- children self]

flexWrap :: FlexParent a Length -> Double -> FlexParent a Double
flexWrap self size
    | NoWrap <- wrap self = post self'
    | Wrap <- wrap self = post wrapped
    | WrapReverse <- wrap self = post wrapped {children=reverse$children wrapped}
  where
    self' = FlexParent {
        direction = direction self,
        reverseRows = reverseRows self,
        wrap = wrap self,
        justify = justify self,
        alignLines = alignLines self,
        baseGap = lowerLength size $ baseGap self,
        crossGap = lowerLength size $ crossGap self,
        children = map (map $ child' size) $ children self
      }
    child' size x = FlexChild {
        grow = grow x, shrink = shrink x,
        basis = lowerLength size $ basis x,
        alignment = alignment x, flexInner = flexInner x
      }
    wrapped = self' {
        children = concatMap wrapRow $ children self'
    }
    wrapRow :: [FlexChild a Double] -> [[FlexChild a Double]]
    wrapRow [] = []
    wrapRow kids@(kid:_) = let (row, rest) = splitRow kids $ basis kid
        in (row):wrapRow rest
    splitRow (kid:kids) end
        | end > size = ([], kid:kids)
        | otherwise = let (kids', rest) = splitRow kids (end + baseGap self' + basis kid)
            in (kid:kids', rest)
    splitRow [] _ = ([], [])

    post flex
        | reverseRows self = post' flex { children = map reverse $ children flex }
        | otherwise = post' flex
    post' flex = flex { children = map resizeRow $ children flex }
    resizeRow row
        | rowSize > size = [kid { basis = basis kid - shrink kid * sfr } | kid <- row]
        | rowSize < size = [kid { basis = basis kid + grow kid * gfr } | kid <- row]
        | otherwise = row
      where
        rowSize = Prelude.sum $ intersperse (baseGap self') $ map basis row
        sfr = (rowSize - size)/(Prelude.sum $ map shrink row)
        gfr = (size - rowSize)/(Prelude.sum $ map grow row)

flexRowSize :: (a -> Double) -> [FlexChild a b] -> Double
flexRowSize cb row = maximum $ map (cb . flexInner) row
flexRowsSize :: (a -> Double) -> FlexParent a Double -> Double
flexRowsSize cb FlexParent { crossGap = gap, children = kids } =
    sum $ intersperse gap $ flexRowSize cb `map` kids

justifyOffset, justifySpacing :: Double -> [Double] -> Double -> Justification -> Double
justifyOffset _ _ _ JStart = 0
justifyOffset outersize ks g JEnd = outersize - innersize g ks
justifyOffset outersize ks g JCenter = half $ outersize - innersize g ks
justifyOffset _ _ _ JSpaceBetween = 0
justifyOffset outersize ks g JSpaceAround =
    half $ (outersize - innersize g ks)/length' ks
justifyOffset size ks g JSpaceEvenly = (size - innersize g ks)/(length' ks + 1)
justifySpacing size ks g JSpaceBetween = (size - innersize g ks)/(length' ks - 1)
justifySpacing size ks g JSpaceAround = (size - innersize g ks)/length' ks
justifySpacing size ks g JSpaceEvenly = (size - innersize g ks)/(length' ks + 1)
justifySpacing _ _ _ _ = 0

alignOffset :: Double -> Double -> Alignment -> Double
alignOffset _ _ AlStretch = 0 -- Needs special handling elsewhere
alignOffset _ _ AlStart = 0
alignOffset outer inner AlEnd = outer - inner
alignOffset outer inner AlCenter = half $ outer - inner
alignOffset outer inner AlBaseline = half $ outer - inner -- FIXME: Implement properly!

innersize gap = sum . intersperse gap
half = (/2)
length' :: [a] -> Double
length' = toEnum . length

------
--- Mapping Box Model axes <-> Flex Box axes
------

outerMinMain, outerMain, outerMaxMain :: Num m => PaddedBox m m -> Direction -> m
outerMinMain box Row = minWidth box
outerMinMain box Column = minHeight box
outerMain box Row = width box
outerMain box Column = height box
outerMaxMain box Row = maxWidth box
outerMaxMain box Column = maxHeight box

outerMinCross, outerCross, outerMaxCross :: Num m => PaddedBox m m -> Direction -> m
outerMinCross box Row = minHeight box
outerMinCross box Column = minWidth box
outerCross box Row = height box
outerCross box Column = width box
outerMaxCross box Row = maxHeight box
outerMaxCross box Column = maxWidth box

innerMinMain, innerMain, innerMaxMain :: Num m => PaddedBox m m -> Direction -> m
innerMinMain box = innerMain' $ B.min box
innerMain box = innerMain' $ B.size box
innerMaxMain box = innerMain' $ B.max box

innerMinCross, innerCross, innerMaxCross :: Num m => PaddedBox m m -> Direction -> m
innerMinCross box = innerCross' $ B.min box
innerCross box = innerCross' $ B.size box
innerMaxCross box = innerCross' $ B.max box

innerMain', innerCross' :: Num m => Size m m -> Direction -> m
innerMain' self Row = inline self
innerMain' self Column = block self
innerCross' self Row = block self
innerCross' self Column = inline self