{-# LANGUAGE OverloadedStrings, FlexibleInstances #-}
module Graphics.Rendering.Rect.CSS.Backgrounds (Backgrounds(..), Pattern(..),
Resize(..), Length(..), resolveSize) where
import Stylist (PropertyParser(..), parseUnorderedShorthand, parseOperands)
import Data.CSS.Syntax.Tokens (Token(..), NumericValue(..))
import Data.Maybe (isJust, catMaybes)
import Data.Text (Text)
import Data.Scientific (scientific, toRealFloat)
import Graphics.Rendering.Rect.CSS.Colour (ColourPallet, parseColour)
import Data.Colour (AlphaColour, transparent)
import Graphics.Rendering.Rect.Types (Rects(..), Rect(..))
import Debug.Trace (traceShow)
data Backgrounds img = Backgrounds {
pallet :: ColourPallet,
background :: C,
clip :: [Rects -> Rect],
image :: [Pattern img],
bgSize :: [Resize]
} deriving (Eq, Show, Read)
type C = AlphaColour Float
data Pattern img = None | Img img | Linear Float C C deriving (Eq, Show, Read)
-- We need to resolve images before we can compute the actual lengths!
data Resize = Cover | Contain | Size Length Length deriving (Eq, Show, Read)
data Length = Absolute Float | Scale Float | Auto deriving (Eq, Show, Read)
instance PropertyParser (Backgrounds Text) where
temp = Backgrounds {
pallet = temp, background = transparent, clip = [borderBox],
image = [None], bgSize = [Size Auto Auto]
}
inherit _ = temp
priority _ = []
longhand _ self@Backgrounds{ pallet = c } "background-color" toks
| Just ([], val) <- parseColour c toks = Just self { background = val }
longhand _ self "background-clip" t | val@(_:_) <- parseCSSList inner t =
Just self { clip = reverse val }
where
inner [Ident "content-box"] = Just contentBox
inner [Ident "padding-box"] = Just paddingBox
inner [Ident "border-box"] = Just borderBox
inner [Ident "initial"] = Just borderBox -- To aid shorthand implementation.
inner _ = Nothing
longhand _ self "background-image" t | val@(_:_) <- parseCSSList inner t =
Just self { image = reverse val }
where
inner [Ident "none"] = Just None
inner [Ident "initial"] = Just None
inner [Url ret] = Just $ Img ret
inner [Function "url", String ret, RightParen] = Just $ Img ret
inner (Function "linear-gradient":toks)
| Just [s, e] <- colourStops (Comma:toks) = Just $ Linear pi s e
inner (Function "linear-gradient":Dimension _ x unit:toks)
| Just rad <- lookup unit [("deg", pi/180), ("grad", pi/200),
("rad", 1), ("turn", 2*pi)],
Just [s, e] <- colourStops toks = Just $ Linear (f x*rad) s e
inner (Function "linear-gradient":Ident "to":Ident a:Ident b:toks)
| Just angle <- corner a b, Just [s, e] <- colourStops toks =
Just $ Linear angle s e
| Just angle <- corner b a, Just [s, e] <- colourStops toks =
Just $ Linear angle s e
where
corner "top" "right" = Just $ 0.25*pi
corner "bottom" "right" = Just $ 0.75*pi
corner "bottom" "left" = Just $ 1.25*pi
corner "top" "left" = Just $ 1.75*pi
corner _ _ = Nothing
inner (Function "linear-gradient":Ident "to":Ident side:toks)
| Just angle <- lookup side [
("top", 0), ("right", pi/2), ("bottom", pi), ("left", pi*1.5)],
Just [s, e] <- colourStops toks = Just $ Linear angle s e
inner _ = Nothing
colourStops [RightParen] = Just []
colourStops (Comma:toks)
| Just (toks', c) <- parseColour (pallet self) toks,
Just ret <- colourStops toks' = Just $ c:ret
colourStops _ = Nothing
longhand _ self "background-size" t | val@(_:_) <- parseCSSList inner t =
Just self { bgSize = reverse val }
where -- TODO: Add shorthand support, after background-position.
inner [x, y] | Just a <- length x, Just b <- length y = Just $ Size a b
inner [Ident "contain"] = Just Contain
inner [Ident "cover"] = Just Cover
inner [Ident "auto"] = Just $ Size Auto Auto
inner [Ident "initial"] = Just $ Size Auto Auto
inner _ = Nothing
-- NOTE: Leave lowering other units to CatTrap.
length (Ident "auto") = Just Auto
length (Dimension _ x "px") = Just $ Absolute $ f x
length (Percentage _ x) = Just $ Scale $ p x
length _ = Nothing
longhand _ _ _ _ = Nothing
-- The multi-layered shorthand is one source of parsing complexity.
shorthand self "background" t = catProps $ reverse $ parseCSSList inner t
where
catProps [] = []
catProps (props:pss)
| Just [Ident "initial"] <- "background-color" `lookup` catProps pss =
map (catProp $ catProps pss) props
| otherwise = [] -- Only allow background-color in bottommost layer.
catProp _ ret@("background-color", _) = ret
catProp bases (key, val)
| Just base <- key `lookup` bases = (key, base ++ Comma:val)
-- Shouldn't happen, `inner` expands all props at least to "initial"!
| otherwise = (key, val)
inner toks | ret@(_:_) <- parseUnorderedShorthand self [
"background-color", "background-clip", "background-image"
] toks = Just ret
| otherwise = Nothing
shorthand self key val | Just _ <- longhand self self key val = [(key, val)]
| otherwise = []
parseCSSList cb toks | all isJust ret = catMaybes ret
| otherwise = []
where ret = map cb $ concat $ splitList [Comma] $ parseOperands toks
f :: NumericValue -> Float
f (NVInteger x) = fromInteger x
f (NVNumber x) = toRealFloat x
p :: NumericValue -> Float
p (NVInteger x) = fromInteger x / 100
-- Do the division while we're in base-10!
p (NVNumber x) = toRealFloat (x/scientific 1 2)
------
--- Utils taken from HappStack
------
-- | Repeadly splits a list by the provided separator and collects the results
splitList :: Eq a => a -> [a] -> [[a]]
splitList _ [] = []
splitList sep list = h:splitList sep t
where (h,t)=split (==sep) list
-- | Split is like break, but the matching element is dropped.
split :: (a -> Bool) -> [a] -> ([a], [a])
split f s = (left,right)
where
(left,right')=break f s
right = if null right' then [] else tail right'
------
--- Dynamically-computed properties
------
resolveSize :: (Float, Float) -> (Float, Float) -> Resize -> (Float, Float)
resolveSize (owidth, oheight) (width, height) Contain
| width > owidth, height*sw > oheight, height > width = (width*sh, height*sh)
| width > owidth = (width*sw, height*sw)
| height > oheight = (width*sh, height*sh)
| height > width = (width*sw, height*sw)
| width > height = (width*sh, height*sh)
where
sh = oheight/height
sw = owidth/width
resolveSize (owidth, oheight) (width, height) Cover
| owidth > width, oheight > height*sw = (width*sh, height*sh)
| oheight > height, owidth > width*sh = (width*sw, height*sw)
| owidth > width = (width*sw, height*sw)
| oheight > height = (width*sh, height*sh)
| oheight > height*sw = (width*sh, height*sh)
| owidth > width*sh = (width*sw, height*sw)
| height > width = (width*sw, height*sw)
| width > height = (width*sh, height*sh)
where
sh = oheight/height
sw = owidth/width
resolveSize _ ret (Size Auto Auto) = ret
resolveSize _ (width, height) (Size x y) = (x', y')
where
x' | Absolute ret <- x = ret
| Scale s <- x = width*s
| Auto <- x = y' * width/height
y' | Absolute ret <- y = ret
| Scale s <- y = height*s
-- NOTE: If Auto,Auto case wasn't handled above this'd be an infinite loop.
| Auto <- y = x' * height/width