{-# LANGUAGE OverloadedStrings #-} ----------------------------------------------------------------------------- -- | -- Module : Hasmin.Types.Position -- Copyright : (c) 2017 Cristian Adrián Ontivero -- License : BSD3 -- Stability : experimental -- Portability : non-portable -- ----------------------------------------------------------------------------- module Hasmin.Types.Position ( Position(..) , PosKeyword(..) , minifyPosition , p50 , l0 , centerpos ) where import Data.Text (Text) import qualified Data.Text as T import Data.Text.Lazy.Builder (singleton, fromText) import Data.Maybe (isJust, isNothing) import Hasmin.Class import Hasmin.Types.Dimension import Hasmin.Types.PercentageLength import Hasmin.Utils data PosKeyword = PosCenter | PosLeft | PosRight | PosTop | PosBottom deriving (Show, Eq, Enum, Bounded) instance ToText PosKeyword where toBuilder PosCenter = "center" toBuilder PosTop = "top" toBuilder PosRight = "right" toBuilder PosBottom = "bottom" toBuilder PosLeft = "left" -- TODO turn this into a proper algebraic type with all the cases, avoding Maybe -- and making invalid Position values impossible. data Position = Position { origin1 :: Maybe PosKeyword , offset1 :: Maybe PercentageLength , origin2 :: Maybe PosKeyword , offset2 :: Maybe PercentageLength } deriving (Show) instance Minifiable Position where minify p = pure $ minifyPosition p instance ToText Position where toBuilder (Position a b c d) = mconcatIntersperse fromText (singleton ' ') $ filter (not . T.null) [f a, f b, f c, f d] where f :: ToText a => Maybe a -> Text f = maybe mempty toText minifyPosition :: Position -> Position -- Single keyword minifyPosition p@(Position (Just x) Nothing Nothing Nothing) = f x where mkPos1 y = Position Nothing y Nothing Nothing f PosCenter = mkPos1 p50 f PosRight = mkPos1 p100 f PosLeft = mkPos1 l0 f _ = p -- Keyword and minifyPosition p@(Position (Just x) Nothing Nothing (Just y)) = minifyPos2 x y where mkPos2 i j = if isZero j then if i == p50 then Position (Just PosTop) Nothing Nothing Nothing else Position Nothing i Nothing l0 else Position Nothing i Nothing (Just j) minifyPos2 PosLeft a = mkPos2 l0 a minifyPos2 PosRight a = mkPos2 p100 a minifyPos2 PosCenter a = mkPos2 p50 a minifyPos2 _ _ = p minifyPosition p@(Position Nothing (Just x) (Just y) Nothing) = minifyPos2 x y where mkPos2 i j = if isZero i then Position Nothing l0 Nothing j else Position Nothing (Just i) Nothing j minifyPos2 a PosTop = mkPos2 a l0 minifyPos2 a PosBottom = mkPos2 a p100 minifyPos2 a PosCenter = mkPos2 a Nothing minifyPos2 _ _ = p -- Two minifyPosition p@(Position Nothing (Just x) Nothing (Just y)) = f x y where f :: PercentageLength -> PercentageLength -> Position f (Left 50) (Left 50) = Position Nothing p50 Nothing Nothing f (Left 50) (Left 100) = Position (Just PosBottom) Nothing Nothing Nothing f (Left 100) (Left 100) = Position Nothing p100 Nothing p100 f (Left 100) (Left 50) = Position Nothing p100 Nothing Nothing f a b | isZero a = if isZero b then Position Nothing l0 Nothing l0 else case b of Left 50 -> Position Nothing l0 Nothing Nothing Left 100 -> Position Nothing l0 Nothing p100 _ -> p { offset1 = l0 } | isZero b = case a of Left 50 -> Position (Just PosTop) Nothing Nothing Nothing Left 100 -> Position Nothing p100 Nothing l0 _ -> p { offset2 = l0 } | b == Left 50 = Position Nothing (Just a) Nothing Nothing | otherwise = p minifyPosition (Position (Just x) (Just y) Nothing Nothing) = uncurry (\a b -> Position a b Nothing Nothing) (minAxis x y) -- 2 keywords minifyPosition p@(Position (Just x) Nothing (Just y) Nothing) = f x y where f :: PosKeyword -> PosKeyword -> Position -- 'center', 'center center' == '50% 50%'. f PosCenter PosCenter = Position Nothing p50 Nothing Nothing -- 'left', 'left center', 'center left' == '0% 50%'. f PosLeft PosCenter = Position Nothing l0 Nothing Nothing f PosCenter PosLeft = Position Nothing l0 Nothing Nothing -- 'top left', 'left top' == '0% 0%'. f PosTop PosLeft = Position Nothing l0 Nothing l0 f PosLeft PosTop = Position Nothing l0 Nothing l0 -- 'top', 'top center', 'center top' == '50% 0%'. f PosTop PosCenter = p { origin2 = Nothing } f PosCenter PosTop = p { origin1 = Just PosTop, origin2 = Nothing } -- 'right top' and 'top right' == '100% 0%'. f PosRight PosTop = Position Nothing p100 Nothing l0 f PosTop PosRight = Position Nothing p100 Nothing l0 -- 'right', 'right center', 'center right' == '100% 50%'. f PosRight PosCenter = Position Nothing p100 Nothing Nothing f PosCenter PosRight = Position Nothing p100 Nothing Nothing -- 'bottom left' and 'left bottom' == '0% 100%'. f PosBottom PosLeft = Position Nothing l0 Nothing p100 f PosLeft PosBottom = Position Nothing l0 Nothing p100 -- 'bottom', 'bottom center', 'center bottom' == '50% 100%'. f PosBottom PosCenter = p { origin2 = Nothing } f PosCenter PosBottom = p { origin1 = Just PosBottom, origin2 = Nothing } -- 'bottom right', 'right bottom' == '100% 100%'. f PosBottom PosRight = Position Nothing p100 Nothing p100 f PosRight PosBottom = Position Nothing p100 Nothing p100 f _ _ = p -- keyword pl keyword syntax minifyPosition p@(Position (Just x) (Just y) (Just z) Nothing) | x == PosTop || x == PosBottom || z == PosLeft || z == PosRight = minifyPosition $ Position (Just z) Nothing (Just x) (Just y) | otherwise = minifyPos3 x y z where minifyPos3 PosLeft b PosBottom | isZero b = Position Nothing l0 Nothing p100 | b == Left 50 = Position (Just PosBottom) Nothing Nothing Nothing | otherwise = Position Nothing (Just b) Nothing p100 minifyPos3 PosLeft b PosTop | isZero b = Position Nothing l0 Nothing l0 | b == Left 50 = Position (Just PosTop) Nothing Nothing Nothing | otherwise = Position Nothing (Just b) Nothing l0 minifyPos3 PosLeft b PosCenter | isZero b = Position Nothing l0 Nothing Nothing | b == Left 50 = Position Nothing p50 Nothing Nothing | otherwise = Position Nothing (Just b) Nothing Nothing minifyPos3 PosRight b PosTop | isZero b = Position Nothing p100 Nothing l0 | otherwise = Position (Just PosRight) (Just b) Nothing l0 minifyPos3 PosRight b PosBottom | isZero b = Position Nothing p100 Nothing p100 | otherwise = Position (Just PosRight) (Just b) Nothing p100 minifyPos3 PosRight b PosCenter | isZero b = Position Nothing p100 Nothing Nothing | otherwise = Position (Just PosRight) (Just b) Nothing p50 minifyPos3 _ _ _ = p minifyPosition p@(Position (Just c) Nothing (Just a) (Just b)) = f $ minAxis a b where isHorizontal i = i == PosLeft || i == PosRight f (x, y) | c == PosLeft && x == Just PosTop && isJust y = minifyPosition $ Position Nothing l0 Nothing y | c == PosCenter && isHorizontal a = Position x y Nothing Nothing | c == PosTop && isNothing x = if y == p50 then Position (Just PosTop) Nothing Nothing Nothing else Position x y Nothing l0 | c == PosBottom && isNothing x = if y == p50 then Position (Just PosBottom) Nothing Nothing Nothing else Position x y Nothing p100 | otherwise = if Just a == x && Just b == y then p else minifyPosition $ p {origin2 = x, offset2 = y } -- 4 value syntax minifyPosition p@(Position (Just PosLeft) (Just _) (Just PosTop) (Just _)) = minifyPosition p { origin1 = Nothing, origin2 = Nothing } minifyPosition (Position (Just a) (Just b) (Just c) (Just d)) = minifyPos4 a b c d minifyPosition p = p -- Sort values so that the Xs are first, then the Ys, and later minify. minifyPos4 :: PosKeyword -> PercentageLength -> PosKeyword -> PercentageLength -> Position minifyPos4 v1 v2 v3 v4 | v1 == PosTop || v1 == PosBottom || v3 == PosLeft || v3 == PosRight = minifyPos4' v3 v4 v1 v2 | otherwise = minifyPos4' v1 v2 v3 v4 where minifyPos4' PosLeft a PosTop b = minifyPosition $ Position Nothing (Just a) Nothing (Just b) minifyPos4' PosRight a PosTop b | isZero a = minifyPosition $ Position Nothing p100 Nothing (Just b) | isZero b = minifyPosition $ Position (Just PosRight) (Just a) (Just PosTop) Nothing | otherwise = Position (Just PosRight) (Just a) (Just PosTop) (Just b) minifyPos4' PosLeft a PosBottom b | isZero b = minifyPosition $ Position Nothing (Just a) Nothing p100 | isZero a = minifyPosition $ Position (Just PosLeft) Nothing (Just PosTop) (Just b) | otherwise = Position (Just PosLeft) (Just a) (Just PosBottom) (Just b) minifyPos4' PosRight a PosBottom b | isZero a && isZero b = Position Nothing p100 Nothing p100 | otherwise = Position (Just PosRight) (Just a) (Just PosBottom) (Just b) minifyPos4' a b c d = Position (Just a) (Just b) (Just c) (Just d) minAxis :: PosKeyword -> PercentageLength -> (Maybe PosKeyword, Maybe PercentageLength) minAxis PosTop x = case x of Left 50 -> (Just PosCenter, Nothing) b -> if isZero b then (Just PosTop, Nothing) else (Just PosTop, Just x) minAxis PosLeft x = case x of Left 50 -> (Nothing, p50) smth -> if isZero smth then (Nothing, l0) else (Just PosLeft, Just x) minAxis PosRight x | isZero x = (Nothing, p100) | otherwise = (Just PosRight, Just x) minAxis PosBottom x | isZero x = (Just PosBottom, Nothing) | otherwise = (Just PosBottom, Just x) minAxis PosCenter x = (Just PosCenter, Just x) instance Eq Position where x == y = let (Position a b c d) = minifyPosition x (Position e f g h) = minifyPosition y in a == e && b == f && c == g && d == h || a == g && b == h && c == e && d == f l0 :: Maybe PercentageLength l0 = Just $ Right NullLength p100 :: Maybe PercentageLength p100 = Just $ Left 100 p50 :: Maybe PercentageLength p50 = Just $ Left 50 centerpos :: Position centerpos = Position (Just PosCenter) Nothing Nothing Nothing