{-# LANGUAGE OverloadedStrings #-} module Main where import System.Environment (getArgs) import qualified Data.Map as M import Data.Maybe (fromJust, fromMaybe) import qualified Data.Text as Txt import Graphics.Layout.CSS (CSSBox(..), finalizeCSS') import Graphics.Layout.CSS.Font (placeholderFont) import Graphics.Layout (LayoutItem, boxLayout, layoutGetBox, layoutGetChilds, layoutGetInner) import Graphics.Layout.Box (zeroBox) import qualified Graphics.Layout.Box as B import Stylist.Tree (StyleTree(..)) import Stylist (PropertyParser(..)) import Network.URI.Fetch.XML (Page(..), fetchDocument) import Network.URI.Fetch (newSession) import SDL hiding (rotate) import Foreign.C.Types (CInt) import Data.Function (fix) import Control.Monad (unless) initReferer = do cwd <- getCurrentDirectory return $ Page { -- Default to URIs being relative to CWD. pageURL = URI {uriScheme = "file:", uriPath = cwd, uriAuthority = Nothing, uriQuery = "", uriFragment = ""}, -- Blank values: css = conditionalStyles nullURI "temp", domain = "temp", html = Document { documentPrologue = Prologue [] Nothing [], documentRoot = Element "temp" M.empty [], documentEpilogue = [] }, pageTitle = "", pageMIME = "", apps = [], backStack = [], forwardStack = [], visitedURLs = M.empty, initCSS = conditionalStyles, appName = "cattrap" } stylize' style = preorder inner where inner parent _ el = Style.cascade style el [] $ Style.inherit $ fromMaybe Style.temp parent main :: IO () main = do SDL.initializeAll let wcfg = defaultWindow { windowInitialSize = V2 640 480, windowResizable = True } w <- createWindow "CatTrap" wcfg renderer <- createRenderer w (-1) defaultRenderer args <- getArgs url <- readFile $ case args of (url:_) -> url [] -> "https://git.argonaut-constellation.org/~alcinnz/CatTrap" sess <- newSession let xml = fetchDocument sess initReferer $ parseURIReference url let styles = CSSTxt.resolve $ treeMap Style.innerParser $ stylize' (css xml) $ el2stylist $ html xml let layout = finalizeCSS' placeholderFont styles fix $ \loop -> do events <- fmap eventPayload <$> pollEvents rendererDrawColor renderer $= V4 255 255 255 255 clear renderer V2 x y <- get $ windowSize w let (display:_) = boxLayout zeroBox { B.size = B.Size (fromIntegral x) (fromIntegral y) } layout False renderDisplay renderer display present renderer unless (QuitEvent `elem` events) loop xml2styles :: CSSBox Nil -> X.Element -> StyleTree (CSSBox Nil) xml2styles parent el = StyleTree { style = self', children = [xml2styles self' child | X.Elem child <- X.elContent el] } where self' = foldl (applyStyle parent) temp $ X.elAttribs el applyStyle parent style (X.Attr (X.QName name _ _) val) = fromMaybe style $ longhand parent style (Txt.pack name) $ filter (/= Whitespace) $ tokenize $ Txt.pack val data Nil = Nil deriving Eq instance PropertyParser Nil where temp = Nil inherit _ = Nil longhand _ _ _ _ = Nothing renderDisplay :: Renderer -> LayoutItem Double Double ((Double, Double), Nil) -> IO () renderDisplay renderer display = do let ((x, y), _) = layoutGetInner display let box = layoutGetBox display rendererDrawColor renderer $= V4 255 0 0 255 drawBox renderer x y (B.width box) (B.height box) rendererDrawColor renderer $= V4 0 255 0 255 drawBox renderer (x + B.left (B.margin box)) (y + B.top (B.margin box)) (B.width box - B.left (B.margin box) - B.right (B.margin box)) (B.height box - B.top (B.margin box) - B.bottom (B.margin box)) rendererDrawColor renderer $= V4 0 0 255 255 drawBox renderer (x + B.left (B.margin box) + B.left (B.border box)) (y + B.top (B.margin box) + B.top (B.border box)) (B.inline (B.size box) + B.left (B.padding box) + B.right (B.padding box)) (B.block (B.size box) + B.top (B.padding box) + B.bottom (B.padding box)) rendererDrawColor renderer $= V4 255 255 0 255 drawBox renderer (x + B.left (B.margin box) + B.left (B.border box) + B.left (B.padding box)) (y + B.top (B.margin box) + B.top (B.border box) + B.top (B.padding box)) (B.inline $ B.size box) (B.block $ B.size box) mapM (renderDisplay renderer) $ layoutGetChilds display return () drawBox :: Renderer -> Double -> Double -> Double -> Double -> IO () drawBox renderer x y width height = do fillRect renderer $ Just $ Rectangle (P $ V2 (c x) (c y)) (V2 (c width) (c height)) c :: (Enum a, Enum b) => a -> b c = toEnum . fromEnum