gtk-0.12.0: demo/inputmethod/Layout.hs
-- Example of using a PangoLayout
import Data.IORef
import Graphics.UI.Gtk
import Graphics.UI.Gtk.Gdk.EventM
import Graphics.Rendering.Cairo
loremIpsum = "Lorem ipsum dolor sit amet, consectetur adipisicing elit,\
\ sed do eiusmod tempor incididunt ut labore et dolore magna\
\ aliqua. Ut enim ad minim veniam, quis nostrud exercitation\
\ ullamco laboris nisi ut aliquip ex ea commodo consequat.\
\ Duis aute irure dolor in reprehenderit in voluptate\
\ velit esse cillum dolore eu fugiat nulla pariatur.\
\ Excepteur sint occaecat cupidatat non proident, sunt in culpa\
\ qui officia deserunt mollit anim id est laborum."
data Buffer = Buffer String Int
defaultBuffer = Buffer loremIpsum (length loremIpsum)
displayBuffer (Buffer str pos) =
before ++ "<CURSOR>" ++ after
where (before,after) = splitAt pos str
displayBufferPreedit (Buffer str pos) preeditStr preeditPos =
before ++ "[" ++ prebefore ++ "<CURSOR>" ++ preafter ++ "]" ++ after
where (before,after) = splitAt pos str
(prebefore, preafter) = splitAt preeditPos preeditStr
insertStr new (Buffer str pos) = Buffer (before++new++after) (pos+length new)
where (before,after) = splitAt pos str
deleteChar b@(Buffer str 0) = b
deleteChar (Buffer str pos) = Buffer (init before ++ after) (pos-1)
where (before,after) = splitAt pos str
moveLeft b@(Buffer str pos) | pos==0 = b
| otherwise = Buffer str (pos-1)
moveRight b@(Buffer str pos) | pos==length str = b
| otherwise = Buffer str (pos+1)
main = do
initGUI
-- Create the main window.
win <- windowNew
on win objectDestroy mainQuit
-- Create a drawing area in which we can render text.
area <- drawingAreaNew
containerAdd win area
on area sizeRequest $ return (Requisition 100 100)
-- Our widget's data
buffer <- newIORef defaultBuffer
preeditRef <- newIORef Nothing
-- Create a Cairo Context that contains information about the current font,
-- etc.
ctxt <- cairoCreateContext Nothing
lay <- layoutEmpty ctxt
layoutSetWrap lay WrapWholeWords
let relayout = do
buffer@(Buffer _ cursor) <- readIORef buffer
preedit <- readIORef preeditRef
case preedit of
Nothing -> do
layoutSetText lay (displayBuffer buffer)
layoutSetAttributes lay []
Just (str,attrs,pos) -> do
layoutSetText lay (displayBufferPreedit buffer str pos)
layoutSetAttributes lay (map (shiftAttribute (cursor + 1))
(concat attrs))
widgetQueueDraw area
relayout
-- Wrap the layout to a different width each time the window is resized.
on area sizeAllocate $ \(Rectangle _ _ w _) ->
layoutSetWidth lay (Just (fromIntegral w))
-- Setup the handler to draw the layout.
on area exposeEvent $ updateArea area lay
-- Set up input method
im <- imMulticontextNew
on im imContextPreeditStart $ do
writeIORef preeditRef (Just ("",[],0))
relayout
on im imContextPreeditEnd $ do
writeIORef preeditRef Nothing
relayout
on im imContextPreeditChanged $ do
writeIORef preeditRef . Just =<< imContextGetPreeditString im
relayout
on im imContextCommit $ \str -> do
modifyIORef buffer (insertStr str)
relayout
on im imContextRetrieveSurrounding $ do
Buffer text pos <- readIORef buffer
imContextSetSurrounding im text pos
return True
on im imContextDeleteSurrounding' $ \off nchars -> do
putStrLn $ "delete-surrounding("++show off++","++show nchars++")"
return False
on win realize $ do
imContextSetClientWindow im . Just =<< widgetGetDrawWindow win
on win focusInEvent $ liftIO (imContextFocusIn im) >> return False
on win focusOutEvent $ liftIO (imContextFocusOut im) >> return False
on win keyReleaseEvent $ imContextFilterKeypress im
on win keyPressEvent $ do
imHandled <- imContextFilterKeypress im
if imHandled then return True else do
mod <- interpretKeyPress
case mod of
Just f -> liftIO $ modifyIORef buffer f >> relayout >> return True
Nothing -> return False
widgetShowAll win
mainGUI
updateArea :: DrawingArea -> PangoLayout -> EventM EExpose Bool
updateArea area lay = do
win <- eventWindow
liftIO $ do
renderWithDrawable win $ do
moveTo 0 0
showLayout lay
return True
interpretKeyPress :: EventM EKey (Maybe (Buffer -> Buffer))
interpretKeyPress = do
modifiers <- eventModifier
if modifiers /= [] then return Nothing else do
keyName <- eventKeyName
keyChar <- fmap keyToChar eventKeyVal
case keyChar of
Just ch -> do
-- This does not appear to get called; the IM handles
-- unmodified keypresses.
liftIO $ putStrLn "Literal character not handled by IM"
returnJust (insertStr [ch])
Nothing -> do
case keyName of
"Left" -> returnJust moveLeft
"Right" -> returnJust moveRight
"BackSpace" -> returnJust deleteChar
_ -> return Nothing
where returnJust = return . Just
shiftAttribute :: Int -> PangoAttribute -> PangoAttribute
shiftAttribute x attr = attr { paStart = x + paStart attr,
paEnd = x + paEnd attr }