module Graphics.UI.WX.Attributes
(
Attr, Prop((:=),(:~),(::=),(::~)), ReadAttr, WriteAttr, CreateAttr
, get, set, swap
, mapAttr, mapAttrW
, newAttr, readAttr, writeAttr, nullAttr, constAttr, makeAttr
, attrName, propName, containsProperty
, reflectiveAttr, createAttr, withProperty, findProperty
, withStyleProperty, withStylePropertyNot
, PropValue(..)
, filterProperty
, castAttr, castProp, castProps
) where
import Graphics.UI.WX.Types
import Data.Dynamic
infixr 0 :=,:~,::=,::~
data Prop w = forall a. Attr w a := a
| forall a. Attr w a :~ (a -> a)
| forall a. Attr w a ::= (w -> a)
| forall a. Attr w a ::~ (w -> a -> a)
instance Show a => Show (Prop a) where
show ((Attr string _ _ _ _) := _) = "Attr \"" ++ string ++ "\" _ _ _ _ := _"
show ((Attr string _ _ _ _) :~ _) = "Attr \"" ++ string ++ "\" _ _ _ _ :~ _"
show ((Attr string _ _ _ _) ::= _) = "Attr \"" ++ string ++ "\" _ _ _ _ ::= _"
show ((Attr string _ _ _ _) ::~ _) = "Attr \"" ++ string ++ "\" _ _ _ _ ::~ _"
type CreateAttr w a = Attr w a
type ReadAttr w a = Attr w a
type WriteAttr w a = Attr w a
data Attr w a = Attr String (Maybe (a -> Dynamic, Dynamic -> Maybe a))
(w -> IO a) (w -> a -> IO ())
(w -> (a -> a) -> IO a)
instance Show a => Show (Attr w a) where
show (Attr string _ _ _ _) =
"Attr \"" ++ string ++ "\" _ _ _ _"
castAttr :: (v -> w) -> Attr w a -> Attr v a
castAttr coerce (Attr name mbdyn getter setter upd)
= Attr name mbdyn (\v -> getter (coerce v)) (\v x -> (setter (coerce v) x))
(\v f -> upd (coerce v) f)
castProp :: (v -> w) -> Prop w -> Prop v
castProp coerce prop
= case prop of
(attr := x) -> (castAttr coerce attr) := x
(attr :~ f) -> (castAttr coerce attr) :~ f
(attr ::= f) -> (castAttr coerce attr) ::= (\v -> f (coerce v))
(attr ::~ f) -> (castAttr coerce attr) ::~ (\v x -> f (coerce v) x)
castProps :: (v -> w) -> [Prop w] -> [Prop v]
castProps coerce props
= map (castProp coerce) props
reflectiveAttr :: Typeable a => String -> (w -> IO a) -> (w -> a -> IO ()) -> Attr w a
reflectiveAttr name getter setter
= Attr name (Just (toDyn, fromDynamic)) getter setter updater
where
updater w f = do x <- getter w; setter w (f x); return x
createAttr :: Typeable a => String -> (w -> IO a) -> (w -> a -> IO ()) -> CreateAttr w a
createAttr name getter setter
= reflectiveAttr name getter setter
makeAttr :: String -> (w -> IO a) -> (w -> a -> IO ()) -> (w -> (a -> a) -> IO a) -> Attr w a
makeAttr name getter setter updater
= Attr name Nothing getter setter updater
newAttr :: String -> (w -> IO a) -> (w -> a -> IO ()) -> Attr w a
newAttr name getter setter
= makeAttr name getter setter updater
where
updater w f = do x <- getter w; setter w (f x); return x
readAttr :: String -> (w -> IO a) -> ReadAttr w a
readAttr name getter
= newAttr name getter (\w x -> ioError (userError ("attribute '" ++ name ++ "' is read-only.")))
writeAttr :: String -> (w -> a -> IO ()) -> WriteAttr w a
writeAttr name setter
= newAttr name (\w -> ioError (userError ("attribute '" ++ name ++ "' is write-only."))) setter
nullAttr :: String -> WriteAttr w a
nullAttr name
= writeAttr name (\w x -> return ())
constAttr :: Typeable a => String -> a -> Attr w a
constAttr name x
= newAttr name (\w -> return x) (\w x -> return ())
mapAttr :: (a -> b) -> (a -> b -> a) -> Attr w a -> Attr w b
mapAttr get set (Attr name reflect getter setter updater)
= Attr name Nothing
(\w -> do a <- getter w; return (get a))
(\w b -> do a <- getter w; setter w (set a b))
(\w f -> do a <- updater w (\a -> set a (f (get a))); return (get a))
mapAttrW :: (v -> w) -> Attr w a -> Attr v a
mapAttrW f attr
= castAttr f attr
get :: w -> Attr w a -> IO a
get w (Attr name reflect getter setter updater)
= getter w
set :: w -> [Prop w] -> IO ()
set w props
= mapM_ setprop props
where
setprop ((Attr name reflect getter setter updater) := x)
= setter w x
setprop ((Attr name reflect getter setter updater) :~ f)
= do updater w f; return ()
setprop ((Attr name reflect getter setter updater) ::= f)
= setter w (f w)
setprop ((Attr name reflect getter setter updater) ::~ f)
= do updater w (f w); return ()
swap :: w -> Attr w a -> a -> IO a
swap w (Attr name reflect getter setter updater) x
= updater w (const x)
attrName :: Attr w a -> String
attrName (Attr name _ _ _ _)
= name
propName :: Prop w -> String
propName (attr := x) = attrName attr
propName (attr :~ f) = attrName attr
propName (attr ::= f) = attrName attr
propName (attr ::~ f) = attrName attr
containsProperty :: Attr w a -> [Prop w] -> Bool
containsProperty attr props
= containsPropName (attrName attr) props
containsPropName :: String -> [Prop w] -> Bool
containsPropName name props
= any (\p -> propName p == name) props
data PropValue a = PropValue a
| PropModify (a -> a)
| PropNone
instance Show a => Show (PropValue a) where
show (PropValue x) = "PropValue " ++ show x
show (PropModify f) = "PropModify"
show (PropNone) = "PropNone"
filterProperty :: Typeable a => Attr w a -> [Prop w] -> (PropValue a, [Prop w])
filterProperty (Attr name _ _ _ _) props
= walk [] PropNone props
where
walk :: Typeable a => [Prop w] -> PropValue a -> [Prop w] -> (PropValue a, [Prop w])
walk acc res props
= case props of
(((Attr attr (Just (todyn,fromdyn)) _ _ _) := x):rest) | name == attr
-> case fromDynamic (todyn x) of
Just x -> walk acc (PropValue x) rest
Nothing -> walk acc res props
(((Attr attr (Just (todyn,fromdyn)) _ _ _) :~ f):rest) | name == attr
-> let dynf x = case fromdyn (toDyn x) of
Just xx -> case fromDynamic (todyn (f xx)) of
Just y -> y
Nothing -> x
Nothing -> x
in case res of
PropValue x -> walk acc (PropValue (dynf x)) rest
PropModify g -> walk acc (PropModify (dynf . g)) rest
PropNone -> walk acc (PropModify dynf) rest
(((Attr attr _ _ _ _) := _):rest) | name == attr -> stop
(((Attr attr _ _ _ _) :~ _):rest) | name == attr -> stop
(((Attr attr _ _ _ _) ::= _):rest) | name == attr -> stop
(((Attr attr _ _ _ _) ::~ _):rest) | name == attr -> stop
(prop:rest)
-> walk (prop:acc) res rest
[] -> stop
where
stop = (res, reverse acc ++ props)
withProperty :: Typeable a => Attr w a -> a -> (a -> [Prop w] -> b) -> [Prop w] -> b
withProperty attr def cont props
= case filterProperty attr props of
(PropValue x, ps) -> cont x ps
(PropModify f, ps) -> cont (f def) ps
(PropNone, ps) -> cont def ps
findProperty :: Typeable a => Attr w a -> a -> [Prop w] -> Maybe (a,[Prop w])
findProperty attr def props
= case filterProperty attr props of
(PropValue x, ps) -> Just (x,ps)
(PropModify f, ps) -> Just (f def,ps)
(PropNone, ps) -> Nothing
withStyleProperty :: Attr w Bool -> Style -> ([Prop w] -> Style -> a) -> [Prop w] -> Style -> a
withStyleProperty prop flag
= withStylePropertyEx prop (bitsSet flag) (\isSet style -> if isSet then (style .+. flag) else (style .-. flag))
withStylePropertyNot :: Attr w Bool -> Style -> ([Prop w] -> Style -> a) -> [Prop w] -> Style -> a
withStylePropertyNot prop flag
= withStylePropertyEx prop (not . bitsSet flag) (\isSet style -> if isSet then (style .-. flag) else (style .+. flag))
withStylePropertyEx :: Attr w Bool -> (Style -> Bool) -> (Bool -> Style -> Style) -> ([Prop w] -> Style -> a) -> [Prop w] -> Style -> a
withStylePropertyEx prop def transform cont props style
= case filterProperty prop props of
(PropValue x, ps) -> cont ps (transform x style)
(PropModify f, ps) -> cont ps (transform (f (def style)) style)
(PropNone, ps) -> cont ps style