aosd-0.2.1: Graphics/Aosd/Pango.hs
{-# LANGUAGE ViewPatterns, NoMonomorphismRestriction, ScopedTypeVariables, GeneralizedNewtypeDeriving, TypeSynonymInstances, NamedFieldPuns, RecordWildCards #-}
{-# OPTIONS -Wall #-}
module Graphics.Aosd.Pango(
-- * Base
TextRenderer(..),Width(..),textRenderer,
-- * PangoText
PangoText,pText,pRaw,pEmpty,pTag,
-- ** Markup tags
pSpan,pBold,pBig,pItalic,pStrikethrough,pSmall,pSub,pSup,pUnderline,pMono,
-- *** Span shorthands
pSized,
-- ** Standard text combinators
pIntercalate,pUnlines,pUnwords,pShow,
-- * Reexports
SpanAttribute(..),
LayoutAlignment(..),
LayoutWrapMode(..),
TabPosition,
Size(..),
sRGB,
module Graphics.Aosd,
module Data.Monoid,
module Data.Colour.Names
) where
import Graphics.Aosd hiding(width)
import Graphics.Aosd.Util
import Graphics.Rendering.Pango.Cairo
import Graphics.Rendering.Pango.Enums
import Graphics.Rendering.Pango.Layout
import Control.Monad(void)
import Graphics.Rendering.Pango.Markup
import Data.Monoid
import Data.String
import Data.List(intersperse)
import Data.Colour
import Data.Colour.SRGB
import Data.Colour.Names
data TextRenderer = TextRenderer {
-- | Hint: Use 'sRGB' or "Data.Colour.Names".
colour :: Colour Double,
-- | 0: Transparent, 1: Opaque.
opacity :: Double,
-- | Uses 'layoutSetWidth' if set.
width :: Maybe Width,
-- | Uses 'layoutSetWrap' if set.
wrapMode :: Maybe LayoutWrapMode,
-- | Uses 'layoutSetJustify' if set.
justify :: Maybe Bool,
-- | Uses 'layoutSetAlignment' if set.
alignment :: Maybe LayoutAlignment,
-- | Uses 'layoutSetSpacing' if set.
lineSpacing :: Maybe Double,
-- | Uses 'layoutSetTabs' if set.
tabs :: Maybe [TabPosition],
-- | Uses 'layoutSetSingleParagraphMode' if set.
singleParagraphMode :: Maybe Bool,
-- | The actual text
tcText :: PangoText
}
data Width = Unlimited -- ^ The layout will be as wide as necessary to hold all the lines without wrapping
| Width Double -- ^ The layout will be wrapped (according to 'wrapMode') to the given width in Pango units
deriving(Show,Eq)
unsup :: String -> t
unsup s = error (s ++ " unsupported for Graphics.Aosd.Pango.Width")
-- | Supports only 'fromInteger'.
instance Num Width where
fromInteger = Width . fromIntegral
(+) = unsup "(+)"
(*) = unsup "(*)"
abs = unsup "abs"
signum = unsup "signum"
(-) = unsup "(-)"
negate = unsup "negate"
-- | Supports only 'fromRational'.
instance Fractional Width where
fromRational = Width . fromRational
(/) = unsup "/"
recip = unsup "recip"
-- | Construct a 'TextConf' with most fields set to 'Nothing'
textRenderer :: PangoText -> TextRenderer
textRenderer t = TextRenderer {
colour = green,
opacity = 1,
width = Nothing,
wrapMode = Nothing,
justify = Nothing,
alignment = Nothing,
lineSpacing = Nothing,
tabs = Nothing,
singleParagraphMode = Nothing,
tcText = t
}
-- | Plain text or some Pango markup. Suggestion: Use {-\# LANGUAGE OverloadedStrings \#-}.
data PangoText = PlainText ShowS
| PangoMarkup ShowS
| Empty
toMarkup :: PangoText -> ShowS
toMarkup (PlainText s) = showString (escapeMarkup (s ""))
toMarkup (PangoMarkup s) = s
toMarkup Empty = mempty
-- | Uses 'pText' (not 'pRaw').
instance IsString PangoText where
fromString = pText
instance Monoid PangoText where
mempty = Empty
mappend Empty x2 = x2
mappend x1 Empty = x1
mappend (PlainText s1) (PlainText s2) = PlainText (s1 . s2)
mappend x1 x2 = PangoMarkup (toMarkup x1 . toMarkup x2)
instance Show PangoText where
showsPrec _ Empty = showString "pEmpty"
showsPrec prec (PlainText s) = showParen (prec >= 11) (showString "pText " . shows (s ""))
showsPrec prec (PangoMarkup s) = showParen (prec >= 11) (showString "pRaw " . shows (s ""))
pEmpty :: PangoText
pEmpty = Empty
-- | Raw Pango markup, see <http://developer.gnome.org/pango/stable/PangoMarkupFormat.html>.
pRaw :: String -> PangoText
pRaw = PangoMarkup . showString
-- | Plain text.
pText :: String -> PangoText
pText = PlainText . showString
pSpan :: [SpanAttribute] -> PangoText -> PangoText
pSpan (attrs :: [SpanAttribute]) inner = PangoMarkup it
where
it = -- adapted from 'Graphics.Rendering.Pango.Markup.markSpan'
showString "<span" .
foldr (.) (showChar '>') (map shows attrs) .
toMarkup inner .
showString "</span>"
pIntercalate :: PangoText -> [PangoText] -> PangoText
pIntercalate x = mconcat . intersperse x
pUnlines :: [PangoText] -> PangoText
pUnlines = pIntercalate (pText "\n")
pUnwords :: [PangoText] -> PangoText
pUnwords = pIntercalate (pText " ")
pShow :: Show a => a -> PangoText
pShow = pText . show
-- | Set font size in points
pSized :: Double -> PangoText -> PangoText
pSized pt = pSpan [FontSize (SizePoint pt)]
pTag :: String -- ^ Tag name
-> PangoText -> PangoText
pTag tagName inner = PangoMarkup $
showChar '<' . showString tagName . showChar '>' .
toMarkup inner .
showString "</" . showString tagName . showChar '>'
pBold :: PangoText -> PangoText
pBold = pTag "b"
pBig :: PangoText -> PangoText
pBig = pTag "big"
pItalic :: PangoText -> PangoText
pItalic = pTag "i"
pStrikethrough :: PangoText -> PangoText
pStrikethrough = pTag "s"
-- | Subscript
pSub :: PangoText -> PangoText
pSub = pTag "sub"
-- | Superscript
pSup :: PangoText -> PangoText
pSup = pTag "sup"
pSmall :: PangoText -> PangoText
pSmall = pTag "small"
-- | Monospace font
pMono :: PangoText -> PangoText
pMono = pTag "tt"
-- | Underline
pUnderline :: PangoText -> PangoText
pUnderline = pTag "u"
layoutSetWidth' :: PangoLayout -> Width -> IO ()
layoutSetWidth' layout w = layoutSetWidth layout (case w of
Unlimited -> Nothing
Width x -> Just x)
instance AosdRenderer TextRenderer where
toGeneralRenderer TextRenderer{..} = do
fm <- cairoFontMapGetDefault
-- resolution <- cairoFontMapGetResolution fm
cxt <- cairoCreateContext (Just fm)
layout <- layoutEmpty cxt
case tcText of
Empty -> return ()
PlainText s -> layoutSetText layout (s "")
PangoMarkup s -> void (layoutSetMarkup layout (s ""))
let go :: (PangoLayout -> a -> IO ()) -> Maybe a -> IO ()
go f = maybeDo (f layout)
go layoutSetWidth' width
go layoutSetWrap wrapMode
go layoutSetJustify justify
go layoutSetAlignment alignment
go layoutSetSpacing lineSpacing
go layoutSetTabs tabs
go layoutSetSingleParagraphMode singleParagraphMode
(grInkExtent,grPositioningExtent) <- layoutGetPixelExtents layout
let render = do
--updateLayout layout -- No idea when this is neccessary
setSourceColour colour opacity
showLayout layout
return GeneralRenderer { grInkExtent, grPositioningExtent, grRender = render }
-- getSize l = do
-- -- (ink,logical)
-- a@(Rectangle xi yi wi hi, Rectangle xl yl wl hl) <- layoutGetPixelExtents l
--
-- -- print ("ink",fst a)
-- -- print ("logical",snd a)
--
-- let w = max (xi+wi) (xl+wl)
-- h = max (yi+hi) (yl+hl)
--
--
-- return (fi w, fi h)
--
-- where
-- fi = fromIntegral
--
--