grafana-0.2: src/Grafana/Layout.hs
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
module Grafana.Layout where
import Data.Bifunctor
import Data.Foldable
data Pos = Pos !Int !Int
deriving (Eq, Show)
data Rect = Rect
{ left :: !Int
, right :: !Int
, top :: !Int
, bottom :: !Int
} deriving (Eq, Show)
instance Semigroup Rect where
Rect l r t b <> Rect l' r' t' b' =
Rect (min l l') (max r r') (min t t') (max b b')
data Layout a
= Layout [(Rect, a)] Rect
| Empty
deriving (Eq, Show, Functor)
instance Semigroup (Layout a) where
Layout ars ab <> Layout brs bb = Layout (ars <> brs) (ab <> bb)
a <> Empty = a
Empty <> a = a
instance Monoid (Layout a) where
mempty = Empty
moveLayout :: Pos -> Layout a -> Layout a
moveLayout _ Empty = Empty
moveLayout p (Layout xs rect) =
Layout
(map (first (moveRect p)) xs)
(moveRect p rect)
moveRect :: Pos -> Rect -> Rect
moveRect (Pos x y) (Rect l r t b) = Rect (l + x) (r + x) (t + y) (b + y)
atop :: Layout a -> Layout a -> Layout a
atop a b = a <> moveLayout (Pos 0 (bottoml a)) b
nextTo :: Layout a -> Layout a -> Layout a
nextTo a b = a <> moveLayout (Pos (rightl a) 0) b
rightl :: Layout a -> Int
rightl Empty = 0
rightl (Layout _ r) = right r
bottoml :: Layout a -> Int
bottoml Empty = 0
bottoml (Layout _ r) = bottom r
rowFits :: Int -> Int -> Int -> [Rect]
rowFits maxWidth itemCount rowHeight =
fmap (\x -> Rect x (x+width) 0 rowHeight) xs
where
xs = [padding, width + padding..]
width = maxWidth `div` itemCount
padding = maxWidth `mod` itemCount `div` 2
fitToRow :: Int -> Int -> [Rect -> Layout a] -> Layout a
fitToRow maxWidth rowHeight items = fold $
zipWith ($) items (rowFits maxWidth (length items) rowHeight)
fillRows :: Int -> Int -> Int -> [Rect -> Layout a] -> Layout a
fillRows maxWidth rowHeight cols items
| null thisRow = Empty
| otherwise =
fold (zipWith ($) thisRow (rowFits maxWidth cols rowHeight))
`atop` fillRows maxWidth rowHeight cols rest
where
(thisRow, rest) = splitAt cols items