2011-08-21 08:40:08 +02:00
|
|
|
{-# LANGUAGE MultiParamTypeClasses, StandaloneDeriving, ExistentialQuantification, FlexibleInstances #-}
|
2011-07-14 00:09:20 +02:00
|
|
|
|
2011-07-13 02:13:01 +02:00
|
|
|
module Phi.Border ( BorderWidth(..)
|
|
|
|
, simpleBorderWidth
|
2011-07-15 15:31:46 +02:00
|
|
|
, borderH
|
|
|
|
, borderV
|
2011-07-14 02:21:26 +02:00
|
|
|
, BorderConfig(..)
|
|
|
|
, defaultBorderConfig
|
2011-07-15 09:17:57 +02:00
|
|
|
, drawBorder
|
|
|
|
, roundRectangle
|
2011-08-21 08:40:08 +02:00
|
|
|
, Border
|
2011-07-13 02:13:01 +02:00
|
|
|
, border
|
|
|
|
) where
|
|
|
|
|
2011-07-14 00:09:20 +02:00
|
|
|
import Phi.Types
|
|
|
|
import Phi.Widget
|
2011-07-13 02:13:01 +02:00
|
|
|
|
2011-07-14 20:21:30 +02:00
|
|
|
import Control.Monad
|
2011-08-21 21:39:26 +02:00
|
|
|
import Control.Monad.State.Strict
|
|
|
|
|
|
|
|
import Data.Maybe
|
2011-07-14 20:21:30 +02:00
|
|
|
|
2011-07-13 02:13:01 +02:00
|
|
|
import Graphics.Rendering.Cairo
|
|
|
|
|
|
|
|
|
|
|
|
data BorderWidth = BorderWidth { borderTop :: !Int
|
|
|
|
, borderRight :: !Int
|
|
|
|
, borderBottom :: !Int
|
|
|
|
, borderLeft :: !Int
|
2011-08-21 05:38:37 +02:00
|
|
|
} deriving (Show, Eq)
|
2011-07-13 02:13:01 +02:00
|
|
|
|
|
|
|
simpleBorderWidth :: Int -> BorderWidth
|
|
|
|
simpleBorderWidth w = BorderWidth w w w w
|
|
|
|
|
|
|
|
borderH :: BorderWidth -> Int
|
|
|
|
borderH bw = borderLeft bw + borderRight bw
|
|
|
|
|
|
|
|
borderV :: BorderWidth -> Int
|
|
|
|
borderV bw = borderTop bw + borderBottom bw
|
|
|
|
|
2011-07-14 02:21:26 +02:00
|
|
|
data BorderConfig = BorderConfig { margin :: !BorderWidth
|
|
|
|
, borderWidth :: !Int
|
|
|
|
, padding :: !BorderWidth
|
|
|
|
, borderColor :: !Color
|
|
|
|
, backgroundColor :: !Color
|
2011-07-17 19:20:19 +02:00
|
|
|
, cornerRadius :: !Int
|
2011-07-14 02:21:26 +02:00
|
|
|
, borderWeight :: !Float
|
2011-08-21 05:38:37 +02:00
|
|
|
} deriving (Show, Eq)
|
2011-07-14 02:21:26 +02:00
|
|
|
|
|
|
|
defaultBorderConfig = BorderConfig { margin = simpleBorderWidth 0
|
|
|
|
, borderWidth = 1
|
|
|
|
, padding = simpleBorderWidth 1
|
|
|
|
, borderColor = (0, 0, 0, 1)
|
|
|
|
, backgroundColor = (0, 0, 0, 0)
|
|
|
|
, cornerRadius = 0
|
|
|
|
, borderWeight = 1
|
|
|
|
}
|
|
|
|
|
2011-08-21 19:34:16 +02:00
|
|
|
data Border w s c = (Widget w s c) => Border !BorderConfig !w
|
2011-07-13 02:13:01 +02:00
|
|
|
|
2011-08-21 19:34:16 +02:00
|
|
|
data BorderCache w s c = (Widget w s c) => BorderCache !c
|
|
|
|
|
|
|
|
instance Eq s => Widget (Border w s c) s (BorderCache w s c) where
|
2011-08-21 08:40:08 +02:00
|
|
|
initWidget (Border _ w) = initWidget w
|
2011-08-21 21:39:26 +02:00
|
|
|
initCache (Border _ w) = BorderCache $ initCache w
|
2011-07-14 00:09:20 +02:00
|
|
|
|
2011-08-21 19:34:16 +02:00
|
|
|
minSize (Border config w) s height screen =
|
2011-07-18 20:57:19 +02:00
|
|
|
case True of
|
|
|
|
_ | childSize == 0 -> 0
|
|
|
|
| otherwise -> max (borderH m+2*(bw+cr)) (childSize + borderH p + 2*bw + borderH m)
|
2011-07-13 02:13:01 +02:00
|
|
|
where
|
2011-08-21 19:34:16 +02:00
|
|
|
childSize = minSize w s height' screen
|
2011-07-18 20:57:19 +02:00
|
|
|
|
2011-07-14 02:21:26 +02:00
|
|
|
m = margin config
|
|
|
|
bw = borderWidth config
|
|
|
|
p = padding config
|
2011-07-17 19:20:19 +02:00
|
|
|
cr = cornerRadius config
|
|
|
|
height' = height - borderV m - 2*bw - borderV p
|
2011-07-13 02:13:01 +02:00
|
|
|
|
2011-07-14 02:21:26 +02:00
|
|
|
weight (Border config _) = borderWeight config
|
2011-07-13 02:13:01 +02:00
|
|
|
|
2011-08-21 19:34:16 +02:00
|
|
|
layout (Border config w) s width height screen = case True of
|
|
|
|
_ | width' > 0 -> layout w s width' height' screen
|
|
|
|
| otherwise -> s
|
2011-07-14 02:21:26 +02:00
|
|
|
where
|
|
|
|
m = margin config
|
|
|
|
bw = borderWidth config
|
|
|
|
p = padding config
|
|
|
|
|
|
|
|
width' = width - borderH m - 2*bw - borderH p
|
|
|
|
height' = height - borderV m - 2*bw - borderV p
|
|
|
|
|
2011-08-21 19:34:16 +02:00
|
|
|
render (Border config w) s x y width height screen = case () of
|
|
|
|
_ | (width > borderH m - 2*bw - borderH p) -> do
|
2011-08-21 21:39:26 +02:00
|
|
|
border <- liftIO $ createImageSurface FormatARGB32 width height
|
2011-08-21 19:34:16 +02:00
|
|
|
renderWith border $ do
|
|
|
|
setOperator OperatorClear
|
|
|
|
paint
|
|
|
|
setOperator OperatorOver
|
|
|
|
drawBorder config 0 0 width height
|
2011-08-21 21:39:26 +02:00
|
|
|
BorderCache c <- get
|
|
|
|
(surfaces, c') <- liftIO $ flip runStateT c $ render w s (x+dx) (y+dy) width' height' screen
|
|
|
|
put $ BorderCache c'
|
2011-08-21 19:34:16 +02:00
|
|
|
let surfaces' = (True, 0, Nothing):(map (\(updated, SurfaceSlice x surf) -> (updated, x+dx, Just surf)) surfaces)++[(True, width-rightWidth, Nothing)]
|
|
|
|
surfacesWidths = zipWith (\(updated, x, surf) (_, x', _) -> (updated, x, x'-x, surf)) surfaces' (tail surfaces' ++ [(False, width, Nothing)])
|
|
|
|
forM surfacesWidths $ \(updated, x, surfWidth, surf) -> do
|
2011-08-21 21:39:26 +02:00
|
|
|
surf' <- liftIO $ createImageSurface FormatARGB32 surfWidth height
|
2011-08-21 19:34:16 +02:00
|
|
|
renderWith surf' $ do
|
|
|
|
setOperator OperatorClear
|
|
|
|
paint
|
|
|
|
setOperator OperatorOver
|
|
|
|
|
|
|
|
save
|
|
|
|
translate (fromIntegral (-x)) 0
|
|
|
|
withPatternForSurface border setSource
|
|
|
|
paint
|
|
|
|
restore
|
|
|
|
|
|
|
|
case surf of
|
|
|
|
Just surface -> do
|
|
|
|
translate 0 (fromIntegral dy)
|
|
|
|
withPatternForSurface surface setSource
|
|
|
|
paint
|
|
|
|
Nothing -> return ()
|
|
|
|
|
|
|
|
return (updated, SurfaceSlice x surf')
|
|
|
|
| otherwise -> do
|
2011-08-21 21:39:26 +02:00
|
|
|
surface <- liftIO $ createImageSurface FormatARGB32 width height
|
2011-08-21 19:34:16 +02:00
|
|
|
return [(True, SurfaceSlice 0 surface)]
|
2011-07-18 20:57:19 +02:00
|
|
|
where
|
|
|
|
m = margin config
|
|
|
|
bw = borderWidth config
|
|
|
|
p = padding config
|
2011-08-21 08:40:08 +02:00
|
|
|
|
2011-08-21 19:34:16 +02:00
|
|
|
leftWidth = borderLeft m + bw + borderLeft p
|
|
|
|
rightWidth = borderRight m + bw + borderRight p
|
|
|
|
dx = leftWidth
|
2011-08-21 08:40:08 +02:00
|
|
|
dy = borderTop m + bw + borderTop p
|
|
|
|
width' = width - borderH m - 2*bw - borderH p
|
|
|
|
height' = height - borderV m - 2*bw - borderV p
|
2011-07-18 20:57:19 +02:00
|
|
|
|
2011-08-21 08:40:08 +02:00
|
|
|
handleMessage (Border _ w) = handleMessage w
|
2011-07-15 09:17:57 +02:00
|
|
|
|
|
|
|
drawBorder :: BorderConfig -> Int -> Int -> Int -> Int -> Render ()
|
|
|
|
drawBorder config dx dy w h = do
|
|
|
|
roundRectangle x y width height radius
|
|
|
|
|
|
|
|
save
|
|
|
|
setSourceRGBA fr fg fb fa
|
|
|
|
fillPreserve
|
|
|
|
|
|
|
|
setSourceRGBA br bg bb ba
|
|
|
|
setLineWidth $ fromIntegral bw
|
|
|
|
strokePreserve
|
|
|
|
restore
|
|
|
|
where
|
|
|
|
m = margin config
|
|
|
|
bw = borderWidth config
|
|
|
|
p = padding config
|
2011-07-17 19:20:19 +02:00
|
|
|
radius = fromIntegral $ cornerRadius config
|
2011-07-15 09:17:57 +02:00
|
|
|
|
|
|
|
x = (fromIntegral dx) + (fromIntegral $ borderLeft m) + (fromIntegral bw)/2
|
|
|
|
y = (fromIntegral dy) + (fromIntegral $ borderTop m) + (fromIntegral bw)/2
|
|
|
|
width = fromIntegral $ w - borderH m - bw
|
|
|
|
height = fromIntegral $ h - borderV m - bw
|
|
|
|
|
|
|
|
(br, bg, bb, ba) = borderColor config
|
|
|
|
(fr, fg, fb, fa) = backgroundColor config
|
|
|
|
|
|
|
|
roundRectangle :: Double -> Double -> Double -> Double -> Double -> Render ()
|
|
|
|
roundRectangle x y width height radius = do
|
|
|
|
newPath
|
|
|
|
arc (x + width - radius) (y + radius) radius (-pi/2) 0
|
|
|
|
arc (x + width - radius) (y + height - radius) radius 0 (pi/2)
|
|
|
|
arc (x + radius) (y + height - radius) radius (pi/2) pi
|
|
|
|
arc (x + radius) (y + radius) radius pi (pi*3/2)
|
|
|
|
closePath
|
2011-07-13 02:13:01 +02:00
|
|
|
|
2011-08-21 19:34:16 +02:00
|
|
|
border :: (Widget w s c) => BorderConfig -> w -> Border w s c
|
2011-08-21 08:40:08 +02:00
|
|
|
border = Border
|