~alcinnz/CatTrap

ref: 4afd3f9a625475d017e8353c1596d58c5691f731 CatTrap/Graphics/Layout/Flex.hs -rw-r--r-- 3.2 KiB
4afd3f9a — Adrian Cochrane Draft flexbox layout 11 months ago
                                                                                
4afd3f9a 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
module Graphics.Layout.Flex(FlexParent(..), FlexChild(..),
    Direction(..), FlexWrapping(..), Justification(..), Alignment(..),
    flexMaxBasis, flexSumBasis, flexWrap) where

import Graphics.Layout.Box (Length, lowerLength)
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.
}
data FlexChild a b = FlexChild {
    grow :: Double,
    shrink :: Double,
    basis :: b,
    alignment :: Alignment,
    flexInner :: a
}

data Direction = Row | Column
data FlexWrapping = NoWrap | Wrap | WrapReverse
data Justification = JLeft | JRight | JCenter | JSpaceBetween | JSpaceAround | JSpaceEvenly
data Alignment = AlStretch | AlStart | AlEnd | AlCenter | AlBaseline

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)