This repository has been archived on 2025-03-02. You can view files and clone it, but cannot push or open issues or pull requests.
phi/lib/Phi/Widget.hs

164 lines
5.4 KiB
Haskell
Raw Permalink Normal View History

2011-09-08 19:15:23 +02:00
{-# LANGUAGE ExistentialQuantification, StandaloneDeriving, MultiParamTypeClasses, FunctionalDependencies, TypeSynonymInstances, FlexibleInstances, TypeFamilies #-}
2011-07-14 00:09:20 +02:00
2011-09-08 19:15:23 +02:00
module Phi.Widget ( Rectangle(..)
2011-09-07 16:38:36 +02:00
, Display(..)
2011-07-16 15:55:31 +02:00
, unionArea
, SurfaceSlice(..)
2011-07-14 20:21:30 +02:00
, Widget(..)
, CompoundWidget
, (<~>)
2011-08-21 21:39:26 +02:00
, IOCache
, RenderCache
, createIOCache
, runIOCache
2011-08-21 21:39:26 +02:00
, createRenderCache
, renderCached
, Separator
2011-07-14 00:09:20 +02:00
, separator
) where
import Control.Arrow
import Control.Arrow.Transformer
import Control.CacheArrow
2011-07-14 00:09:20 +02:00
import Control.Monad
2011-08-21 21:39:26 +02:00
import Control.Monad.State.Strict hiding (lift)
2011-07-15 09:17:57 +02:00
import Control.Monad.IO.Class
2011-07-14 20:21:30 +02:00
2011-08-21 21:39:26 +02:00
import Data.Maybe
2011-08-29 15:10:55 +02:00
import Data.Typeable
2011-08-21 21:39:26 +02:00
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-09-08 19:15:23 +02:00
data Rectangle = Rectangle { rect_x :: !Int
, rect_y :: !Int
, rect_width :: !Int
, rect_height :: !Int
} deriving (Show, Eq)
2011-07-14 22:50:03 +02:00
2011-09-08 19:15:23 +02:00
class Display d where
type Window d :: *
2011-09-07 16:38:36 +02:00
2011-09-08 19:15:23 +02:00
unionArea :: Rectangle -> Rectangle -> Int
2011-09-07 19:35:59 +02:00
unionArea a b = uw*uh
2011-07-16 15:55:31 +02:00
where
2011-09-08 19:15:23 +02:00
uw = max 0 $ (min ax2 bx2) - (max ax1 bx1)
uh = max 0 $ (min ay2 by2) - (max ay1 by1)
2011-07-16 15:55:31 +02:00
2011-09-08 19:15:23 +02:00
Rectangle ax1 ay1 aw ah = a
Rectangle bx1 by1 bw bh = b
2011-07-16 15:55:31 +02:00
2011-09-08 19:15:23 +02:00
ax2 = ax1 + aw
ay2 = ay1 + ah
2011-07-16 15:55:31 +02:00
2011-09-08 19:15:23 +02:00
bx2 = bx1 + bw
by2 = by1 + bh
2011-07-15 09:17:57 +02:00
2011-07-14 20:21:30 +02:00
data SurfaceSlice = SurfaceSlice !Int !Surface
2011-09-08 19:15:23 +02:00
class (Eq s, Display d) => Widget w s c d | w -> s, w -> c, w -> d where
initWidget :: w -> Phi -> d -> [(Rectangle, Window d)] -> IO s
2011-08-21 21:39:26 +02:00
initCache :: w -> c
2011-07-14 00:09:20 +02:00
2011-09-08 19:15:23 +02:00
minSize :: w -> s -> Int -> Rectangle -> Int
2011-07-14 00:09:20 +02:00
2011-08-21 21:39:26 +02:00
weight :: w -> Float
2011-07-14 00:09:20 +02:00
weight _ = 0
2011-09-08 19:15:23 +02:00
render :: w -> s -> Int -> Int -> Int -> Int -> Rectangle -> StateT c IO [(Bool, SurfaceSlice)]
2011-07-14 06:16:04 +02:00
2011-08-21 21:39:26 +02:00
handleMessage :: w -> s -> Message -> s
2011-07-14 06:16:04 +02:00
handleMessage _ priv _ = priv
2011-07-14 00:09:20 +02:00
2011-08-21 21:39:26 +02:00
type IOCache = CacheArrow (Kleisli IO)
2011-09-08 19:15:23 +02:00
type RenderCache s = IOCache (s, Int, Int, Int, Int, Rectangle) Surface
2011-08-21 21:39:26 +02:00
createIOCache :: Eq a => (a -> IO b) -> IOCache a b
createIOCache = lift . Kleisli
2011-08-22 21:10:59 +02:00
runIOCache :: Eq a => a -> StateT (IOCache a b) IO b
runIOCache a = do
cache <- get
2011-08-22 21:10:59 +02:00
(b, cache') <- liftIO $ runKleisli (runCache cache) a
put cache'
2011-08-22 21:10:59 +02:00
return b
2011-09-08 19:15:23 +02:00
createRenderCache :: (s -> Int -> Int -> Int -> Int -> Rectangle -> Render ())
-> CacheArrow (Kleisli IO) (s, Int, Int, Int, Int, Rectangle) Surface
createRenderCache f = lift . Kleisli $ \(state, x, y, w, h, screen) -> do
surface <- createImageSurface FormatARGB32 w h
renderWith surface $ do
setOperator OperatorClear
paint
setOperator OperatorOver
f state x y w h screen
2011-08-21 21:39:26 +02:00
return surface
2011-09-08 19:15:23 +02:00
renderCached :: Eq s => s -> Int -> Int -> Int -> Int -> Rectangle -> StateT (RenderCache s) IO [(Bool, SurfaceSlice)]
renderCached state x y w h screen = do
2011-08-21 21:39:26 +02:00
cache <- get
(surf, updated, cache') <- liftIO $ runKleisli (runCache' cache) (state, x, y, w, h, screen)
2011-08-21 21:39:26 +02:00
put cache'
return [(updated, SurfaceSlice 0 surf)]
2011-09-08 19:15:23 +02:00
data CompoundWidget a sa ca b sb cb d = (Widget a sa ca d, Widget b sb cb d) => CompoundWidget !a !b
2011-09-08 19:15:23 +02:00
data CompoundState a sa ca b sb cb d = (Widget a sa ca d, Widget b sb cb d) => CompoundState !sa !sb
deriving instance Eq (CompoundState a sa ca b sb cb d)
2011-09-08 19:15:23 +02:00
data CompoundCache a sa ca b sb cb d = (Widget a sa ca d, Widget b sb cb d) => CompoundCache !ca !cb
2011-09-08 19:15:23 +02:00
instance Display d => Widget (CompoundWidget a sa ca b sb cb d) (CompoundState a sa ca b sb cb d) (CompoundCache a sa ca b sb cb d) d where
2011-08-29 15:10:55 +02:00
initWidget (CompoundWidget a b) phi disp screens = liftM2 CompoundState (initWidget a phi disp screens) (initWidget b phi disp screens)
2011-08-21 21:39:26 +02:00
initCache (CompoundWidget a b) = CompoundCache (initCache a) (initCache b)
2011-08-29 15:10:55 +02:00
minSize (CompoundWidget a b) (CompoundState da db) height screen = minSize a da height screen + minSize b db height screen
weight (CompoundWidget a b) = weight' a + weight' b
2011-08-29 15:10:55 +02:00
render c@(CompoundWidget a b) s@(CompoundState sa sb) x y w h screen = do
let sizesum = minSize c s h screen
wsum = let wsum = weight c
in if wsum > 0 then wsum else 1
surplus = w - sizesum
xb = floor $ (fromIntegral $ minSize a sa h screen) + (fromIntegral surplus)*(weight' a)/wsum
2011-07-14 01:47:10 +02:00
2011-08-21 21:39:26 +02:00
CompoundCache ca cb <- get
2011-08-29 15:10:55 +02:00
2011-08-21 21:39:26 +02:00
(surfacea, ca') <- liftIO $ flip runStateT ca $ render a sa x y xb h screen
(surfaceb, cb') <- liftIO $ flip runStateT cb $ render b sb (x+xb) y (w-xb) h screen
put $ CompoundCache ca' cb'
return $ surfacea ++ map (\(updated, SurfaceSlice x surface) -> (updated, SurfaceSlice (x+xb) surface)) surfaceb
2011-07-14 01:47:10 +02:00
2011-08-29 15:10:55 +02:00
handleMessage (CompoundWidget a b) (CompoundState sa sb) message = CompoundState (handleMessage a sa message) (handleMessage b sb message)
2011-07-14 00:09:20 +02:00
2011-09-08 19:15:23 +02:00
weight' :: (Widget a sa ca d) => a -> Float
weight' = max 0 . weight
2011-07-14 00:09:20 +02:00
2011-09-08 19:15:23 +02:00
(<~>) :: (Widget a sa ca d, Widget b sb cb d) => a -> b -> CompoundWidget a sa ca b sb cb d
a <~> b = CompoundWidget a b
2011-07-14 00:09:20 +02:00
2011-09-08 19:15:23 +02:00
data Separator d = Separator !Int !Float deriving (Show, Eq)
2011-07-14 00:09:20 +02:00
2011-09-08 19:15:23 +02:00
instance Display d => Widget (Separator d) () (RenderCache ()) d where
2011-08-29 15:10:55 +02:00
initWidget _ _ _ _ = return ()
initCache _ = createRenderCache $ \_ _ _ _ _ _ -> do
2011-08-21 21:39:26 +02:00
setOperator OperatorClear
paint
2011-07-14 00:09:20 +02:00
minSize (Separator s _) _ _ _ = s
2011-07-14 00:09:20 +02:00
weight (Separator _ w) = w
render _ = renderCached
2011-07-14 00:09:20 +02:00
2011-09-08 19:15:23 +02:00
separator :: Int -> Float -> Separator d
separator = Separator