2011-08-21 05:38:37 +02:00
|
|
|
{-# LANGUAGE ExistentialQuantification, StandaloneDeriving, FlexibleContexts, MultiParamTypeClasses, FunctionalDependencies #-}
|
2011-07-14 00:09:20 +02:00
|
|
|
|
2011-07-14 22:50:03 +02:00
|
|
|
module Phi.Widget ( Display(..)
|
|
|
|
, withDisplay
|
2011-07-15 09:17:57 +02:00
|
|
|
, getAtoms
|
2011-07-19 11:16:50 +02:00
|
|
|
, getScreenWindows
|
2011-07-16 15:55:31 +02:00
|
|
|
, getScreens
|
|
|
|
, unionArea
|
2011-07-14 20:21:30 +02:00
|
|
|
, Widget(..)
|
2011-07-14 00:09:20 +02:00
|
|
|
, WidgetClass(..)
|
|
|
|
, WidgetState(..)
|
|
|
|
, separator
|
|
|
|
, createWidgetState
|
|
|
|
, layoutWidgets
|
|
|
|
, renderWidgets
|
2011-07-14 20:21:30 +02:00
|
|
|
, handleMessageWidgets
|
2011-07-14 00:09:20 +02:00
|
|
|
) where
|
|
|
|
|
2011-08-21 05:38:37 +02:00
|
|
|
import Control.Arrow
|
|
|
|
import Control.Arrow.Transformer
|
|
|
|
import Control.CacheArrow
|
2011-07-15 02:51:50 +02:00
|
|
|
import Control.Concurrent.MVar
|
2011-07-14 00:09:20 +02:00
|
|
|
import Control.Monad
|
2011-07-15 09:17:57 +02:00
|
|
|
import Control.Monad.IO.Class
|
2011-07-14 20:21:30 +02:00
|
|
|
|
2011-08-21 05:38:37 +02:00
|
|
|
import Data.Traversable hiding (forM)
|
2011-07-14 00:09:20 +02:00
|
|
|
|
2011-07-16 15:55:31 +02:00
|
|
|
import qualified Graphics.X11.Xlib as Xlib
|
2011-07-14 00:09:20 +02:00
|
|
|
import Graphics.Rendering.Cairo
|
|
|
|
|
2011-07-14 06:16:04 +02:00
|
|
|
import Phi.Phi
|
2011-07-15 09:17:57 +02:00
|
|
|
import Phi.X11.Atoms
|
2011-07-14 06:16:04 +02:00
|
|
|
|
2011-07-14 00:09:20 +02:00
|
|
|
|
2011-08-12 03:18:46 +02:00
|
|
|
data Display = Display !(MVar Xlib.Display) !Atoms ![(Xlib.Rectangle, Xlib.Window)]
|
2011-07-14 22:50:03 +02:00
|
|
|
|
2011-07-16 15:55:31 +02:00
|
|
|
withDisplay :: MonadIO m => Display -> (Xlib.Display -> m a) -> m a
|
|
|
|
withDisplay (Display dispvar _ _) f = do
|
2011-07-15 09:17:57 +02:00
|
|
|
disp <- liftIO $ takeMVar dispvar
|
2011-07-14 22:50:03 +02:00
|
|
|
a <- f disp
|
2011-07-15 09:17:57 +02:00
|
|
|
liftIO $ putMVar dispvar disp
|
2011-07-14 22:50:03 +02:00
|
|
|
return a
|
2011-07-14 20:21:30 +02:00
|
|
|
|
2011-07-15 09:17:57 +02:00
|
|
|
getAtoms :: Display -> Atoms
|
2011-07-16 15:55:31 +02:00
|
|
|
getAtoms (Display _ atoms _) = atoms
|
|
|
|
|
2011-07-19 11:16:50 +02:00
|
|
|
getScreenWindows :: Display -> [(Xlib.Rectangle, Xlib.Window)]
|
|
|
|
getScreenWindows (Display _ _ screenWindows) = screenWindows
|
|
|
|
|
2011-07-16 15:55:31 +02:00
|
|
|
getScreens :: Display -> [Xlib.Rectangle]
|
2011-07-19 11:16:50 +02:00
|
|
|
getScreens = map fst . getScreenWindows
|
|
|
|
|
2011-07-16 15:55:31 +02:00
|
|
|
|
|
|
|
unionArea :: Xlib.Rectangle -> Xlib.Rectangle -> Int
|
|
|
|
unionArea a b = fromIntegral $ uw*uh
|
|
|
|
where
|
|
|
|
uw = max 0 $ (min ax2 bx2) - (max ax1 bx1)
|
|
|
|
uh = max 0 $ (min ay2 by2) - (max ay1 by1)
|
|
|
|
|
|
|
|
Xlib.Rectangle ax1 ay1 aw ah = a
|
|
|
|
Xlib.Rectangle bx1 by1 bw bh = b
|
|
|
|
|
|
|
|
ax2 = ax1 + fromIntegral aw
|
|
|
|
ay2 = ay1 + fromIntegral ah
|
|
|
|
|
|
|
|
bx2 = bx1 + fromIntegral bw
|
|
|
|
by2 = by1 + fromIntegral bh
|
2011-07-15 09:17:57 +02:00
|
|
|
|
2011-07-14 20:21:30 +02:00
|
|
|
|
2011-08-21 05:38:37 +02:00
|
|
|
class (Show a, Eq a, Eq d) => WidgetClass a d | a -> d where
|
|
|
|
initWidget :: a -> Phi -> Display -> IO d
|
2011-07-14 00:09:20 +02:00
|
|
|
|
2011-08-21 05:38:37 +02:00
|
|
|
minSize :: a -> d -> Int -> Xlib.Rectangle -> Int
|
2011-07-14 00:09:20 +02:00
|
|
|
|
|
|
|
weight :: a -> Float
|
|
|
|
weight _ = 0
|
|
|
|
|
2011-08-21 05:38:37 +02:00
|
|
|
layout :: a -> d -> Int -> Int -> Xlib.Rectangle -> d
|
2011-07-18 20:57:19 +02:00
|
|
|
layout _ priv _ _ _ = priv
|
2011-07-14 00:09:20 +02:00
|
|
|
|
2011-08-21 05:38:37 +02:00
|
|
|
render :: a -> d -> Int -> Int -> Int -> Int -> Xlib.Rectangle -> Render ()
|
2011-07-14 06:16:04 +02:00
|
|
|
|
2011-08-21 05:38:37 +02:00
|
|
|
handleMessage :: a -> d -> Message -> d
|
2011-07-14 06:16:04 +02:00
|
|
|
handleMessage _ priv _ = priv
|
2011-07-14 00:09:20 +02:00
|
|
|
|
2011-08-21 05:38:37 +02:00
|
|
|
data Widget = forall a d. WidgetClass a d => Widget !a
|
2011-07-14 01:47:10 +02:00
|
|
|
deriving instance Show Widget
|
2011-07-14 00:09:20 +02:00
|
|
|
|
2011-08-21 05:38:37 +02:00
|
|
|
instance Eq Widget where
|
|
|
|
_ == _ = False
|
|
|
|
|
|
|
|
data WidgetState = forall a d. WidgetClass a d =>
|
|
|
|
WidgetState { stateWidget :: !a
|
|
|
|
, stateX :: !Int
|
|
|
|
, stateY :: !Int
|
|
|
|
, stateWidth :: !Int
|
|
|
|
, stateHeight :: !Int
|
|
|
|
, statePrivateData :: !d
|
|
|
|
, stateRender :: !(CacheArrow (Kleisli IO) (a, d, Int, Int, Int, Int, Xlib.Rectangle) Surface)
|
|
|
|
}
|
|
|
|
|
|
|
|
instance Eq WidgetState where
|
|
|
|
_ == _ = False
|
|
|
|
|
|
|
|
createStateRender :: WidgetClass a d => CacheArrow (Kleisli IO) (a, d, Int, Int, Int, Int, Xlib.Rectangle) Surface
|
|
|
|
createStateRender = lift . Kleisli $ \(widget, state, x, y, w, h, screen) -> do
|
|
|
|
surface <- createImageSurface FormatARGB32 w h
|
|
|
|
renderWith surface $ do
|
|
|
|
setOperator OperatorClear
|
|
|
|
paint
|
|
|
|
setOperator OperatorOver
|
|
|
|
render widget state x y w h screen
|
|
|
|
return surface
|
2011-07-14 00:09:20 +02:00
|
|
|
|
2011-07-14 20:21:30 +02:00
|
|
|
createWidgetState :: Phi -> Display -> Widget -> IO WidgetState
|
|
|
|
createWidgetState phi disp (Widget w) = do
|
2011-08-12 02:11:09 +02:00
|
|
|
priv <- initWidget w phi disp
|
2011-07-14 20:21:30 +02:00
|
|
|
return WidgetState { stateWidget = w
|
|
|
|
, stateX = 0
|
|
|
|
, stateY = 0
|
|
|
|
, stateWidth = 0
|
|
|
|
, stateHeight = 0
|
|
|
|
, statePrivateData = priv
|
2011-08-21 05:38:37 +02:00
|
|
|
, stateRender = createStateRender
|
2011-07-14 20:21:30 +02:00
|
|
|
}
|
2011-07-14 00:09:20 +02:00
|
|
|
|
2011-07-18 20:57:19 +02:00
|
|
|
layoutWidgets :: [WidgetState] -> Int -> Int -> Int -> Int -> Xlib.Rectangle -> [WidgetState]
|
|
|
|
layoutWidgets widgets x y width height screen = snd $ mapAccumL layoutWidgetAndX x widgets
|
2011-07-14 00:09:20 +02:00
|
|
|
where
|
2011-07-18 20:57:19 +02:00
|
|
|
sizesum = sum $ map (\(WidgetState {stateWidget = w, statePrivateData = priv} ) -> nneg $ minSize w priv height screen) widgets
|
2011-07-14 01:47:10 +02:00
|
|
|
wsum = let wsum = sum $ map (\(WidgetState {stateWidget = w} ) -> nneg . weight $ w) widgets
|
|
|
|
in if wsum > 0 then wsum else 1
|
|
|
|
|
|
|
|
surplus = width - sizesum
|
|
|
|
|
|
|
|
layoutWidgetAndX wX state = let lw = layoutWidget wX state
|
|
|
|
in (wX + stateWidth lw, lw)
|
|
|
|
|
|
|
|
layoutWidget wX state = case state of
|
2011-08-21 05:38:37 +02:00
|
|
|
WidgetState {stateWidget = w, statePrivateData = priv, stateRender = render} ->
|
2011-07-18 20:57:19 +02:00
|
|
|
let wWidth = floor $ (fromIntegral $ minSize w priv height screen) + (fromIntegral surplus)*(nneg $ weight w)/wsum
|
|
|
|
priv' = layout w priv wWidth height screen
|
2011-08-21 05:38:37 +02:00
|
|
|
in WidgetState w wX y wWidth height priv' render
|
2011-07-14 01:47:10 +02:00
|
|
|
|
|
|
|
nneg :: (Num a, Ord a) => a -> a
|
|
|
|
nneg x = max 0 x
|
2011-07-14 00:09:20 +02:00
|
|
|
|
2011-08-21 05:38:37 +02:00
|
|
|
renderWidgets :: [WidgetState] -> Xlib.Rectangle -> Int -> Int -> Render [WidgetState]
|
|
|
|
renderWidgets widgets screen winX winY = forM widgets $ \(WidgetState widget x y w h priv render) -> do
|
|
|
|
(surface, render') <- liftIO $ runKleisli (runCache render) (widget, priv, winX+x, winY+y, w, h, screen)
|
|
|
|
|
2011-07-14 01:47:10 +02:00
|
|
|
save
|
|
|
|
translate (fromIntegral x) (fromIntegral y)
|
2011-08-21 05:38:37 +02:00
|
|
|
withPatternForSurface surface setSource
|
|
|
|
paint
|
2011-07-14 01:47:10 +02:00
|
|
|
restore
|
2011-08-21 05:38:37 +02:00
|
|
|
|
|
|
|
return $ WidgetState widget x y w h priv render'
|
2011-07-14 00:09:20 +02:00
|
|
|
|
2011-07-14 06:16:04 +02:00
|
|
|
handleMessageWidgets :: Message -> [WidgetState] -> [WidgetState]
|
|
|
|
handleMessageWidgets message = map handleMessageWidget
|
|
|
|
where
|
2011-08-21 05:38:37 +02:00
|
|
|
handleMessageWidget (WidgetState w x y width height priv render) = WidgetState w x y width height (handleMessage w priv message) render
|
2011-07-14 00:09:20 +02:00
|
|
|
|
2011-08-21 05:38:37 +02:00
|
|
|
data Separator = Separator Int Float deriving (Show, Eq)
|
2011-07-14 00:09:20 +02:00
|
|
|
|
2011-08-21 05:38:37 +02:00
|
|
|
instance WidgetClass Separator () where
|
2011-07-14 20:21:30 +02:00
|
|
|
initWidget _ _ _ = return ()
|
2011-07-14 00:09:20 +02:00
|
|
|
|
2011-07-18 20:57:19 +02:00
|
|
|
minSize (Separator s _) _ _ _ = s
|
2011-07-14 00:09:20 +02:00
|
|
|
weight (Separator _ w) = w
|
2011-08-21 05:38:37 +02:00
|
|
|
render _ _ _ _ _ _ _ = return ()
|
2011-07-14 00:09:20 +02:00
|
|
|
|
|
|
|
separator :: Int -> Float -> Widget
|
|
|
|
separator s w = Widget $ Separator s w
|