packages feed

xbattbar-0.2: src/XBattBar/Widgets.hs

module XBattBar.Widgets (ProgressBar(colorBack, colorBar, progress), mkProgressBar,
                         Label(text), mkLabel) where

import Graphics.X11.Types (EventMask, cWOverrideRedirect)
import Graphics.X11.Xlib.Types hiding (Position)
import Graphics.X11.Xlib.Display (blackPixel, whitePixel)
import Graphics.X11.Xlib.Window (createSimpleWindow)
import Graphics.X11.Xlib.Event (flush, selectInput)
import Graphics.X11.Xlib.Misc (fillRectangles,
                               drawString,
                               allocaSetWindowAttributes,
                               set_override_redirect)
import Graphics.X11.Xlib.Context (setForeground, createGC)
import Graphics.X11.Xlib.Font (FontStruct,
                               ascentFromFontStruct,
                               descentFromFontStruct,
                               textWidth, loadQueryFont)
import Graphics.X11.Xlib.Extras (unmapWindow, changeWindowAttributes)

import XBattBar.Types

-- | progress bar-like widget
data ProgressBar = ProgressBar {
                 pbXContext     :: XContext,
                 pbExContext    :: ExtContext,
                 colorBack      :: Pixel,
                 colorBar       :: Pixel,
                 progress       :: Double,
                 orientation    :: Orientation
               }

instance XWidget ProgressBar
    where xContext = pbXContext
          widgetContext = pbExContext
          drawWidget bar = do
                let ctx'    = xContext bar
                    ectx'   = widgetContext bar
                    dpy'    = dpy ctx'
                    screen' = screen ctx'
                    window' = window ectx'
                    gc'     = gc ectx'
                    geom'   = geom ectx'
                    fg      = colorBar bar
                    bg      = colorBack bar
                setForeground dpy' gc' bg
                fillRectangles dpy' window' gc' [geom']
                setForeground dpy' gc' fg
                fillRectangles dpy' window' gc' [getIndicatorRect (orientation bar) (progress bar) geom']
                flush dpy'

getIndicatorRect :: Orientation -> Double -> Rectangle -> Rectangle
getIndicatorRect pos perc rect = case pos of
                        Horizontal ->
                            rect { rect_x = p (rect_width rect) - fromIntegral (rect_width rect), rect_y = 0 }
                        Vertical ->
                            rect { rect_y = fromIntegral (rect_height rect) - p (rect_height rect), rect_x = 0 }
                        where p x = floor $ perc * fromIntegral x

-- | creates a progress bar-like widget
mkProgressBar :: XContext -> Rectangle -> Pixel -> Pixel -> Orientation -> EventMask -> IO ProgressBar
mkProgressBar xctx geom fg bg orientation mask = do
    let dpy' = dpy xctx
        screen' = screen xctx
    f <- mkWidget xctx geom mask 0 ProgressBar
    return $ f fg bg 0.0 orientation

-- | multiline non-editable text widget with centered text
data Label = Label {
                 lXContext      :: XContext,
                 lExContext     :: ExtContext,
                 colorBG        :: Pixel,
                 colorFont      :: Pixel,
                 font           :: FontStruct,
                 text           :: [String]
             }

instance XWidget Label
    where xContext = lXContext
          widgetContext = lExContext
          drawWidget label = do
                let ctx'    = xContext label
                    ectx'   = widgetContext label
                    dpy'    = dpy ctx'
                    screen' = screen ctx'
                    window' = window ectx'
                    gc'     = gc ectx'
                    geom'   = geom ectx'
                    fg      = colorFont label
                    bg      = colorBG label
                    text'   = text label
                    font'   = font label
                    h       = ascentFromFontStruct font' + descentFromFontStruct font'
                    tw      = fromIntegral . textWidth font'
                    tx t    = fromIntegral $ rect_width geom' `div` 2 - (tw t) `div` 2
                    ty      = fromIntegral $ rect_height geom' `div` 2
                setForeground dpy' gc' bg
                fillRectangles dpy' window' gc' [geom']
                setForeground dpy' gc' fg
                mapM (\(s,y) -> drawString dpy' window' gc' (tx s) y s) $ zip text' [ty, (ty+h)..]
                flush dpy'
          handleWidgetEvent label ev et = drawWidget label

-- | creates a multiline non-editable text widget
mkLabel :: XContext -> Rectangle -> Pixel -> Pixel -> String -> [String] -> EventMask -> IO Label
mkLabel xctx geom fg bg fontName text mask = do
    let dpy' = dpy xctx
        screen' = screen xctx
    font <- loadQueryFont dpy' fontName
    f <- mkWidget xctx geom mask 2 Label
    return $ f bg fg font text

-- | wraps X11 window creation process
mkWidget :: XContext -> Rectangle -> EventMask -> Int -> (XContext -> ExtContext -> b) -> IO b
mkWidget ctx geom mask bw which = do
    let borderWidth = fromIntegral bw 
        dpy'    = dpy ctx
        screen' = screen ctx
        parent' = parent ctx
    window <- createSimpleWindow dpy' parent'
                                (rect_x geom)
                                (rect_y geom)
                                (rect_width geom)
                                (rect_height geom)
                                borderWidth
                                (blackPixel dpy' screen')
                                (whitePixel dpy' screen')
    allocaSetWindowAttributes $ \attrs -> do 
        set_override_redirect attrs True
        changeWindowAttributes dpy' window cWOverrideRedirect attrs
    gc <- createGC dpy' window
    selectInput dpy' window mask
    let ectx = ExtContext window geom gc
    return $ which ctx ectx