module GUI.Gtk.Structures.IO
( runWidget
) where
import Control.Category
import Control.Monad
import Control.Monad.State
import Control.Monad.Writer
import Control.Concurrent
import Data.Maybe
import Data.List hiding (union)
import Prelude hiding ((.), id)
import Graphics.UI.Gtk hiding (Widget, Release)
import qualified Graphics.UI.Gtk as Gtk
import Control.Monad.Restricted
import Control.Monad.ExtRef
import Control.Monad.ExtRef.Pure
import Control.Monad.EffRef
import GUI.Gtk.Structures
import Diagrams.Prelude
import Diagrams.Backend.Cairo
import Diagrams.Backend.Cairo.Internal
runWidget :: (forall m . EffIORef m => Widget (EffectM m) m (CallbackM m)) -> IO ()
runWidget desc = gtkContext $ \postGUISync -> mdo
postActionsRef <- newRef' $ return ()
let addPostAction = runMorphD postActionsRef . modify . flip (>>)
runPostActions = join $ runMorphD postActionsRef $ state $ \m -> (m, return ())
actionChannel <- newChan
((widget, (act, _)), s) <- flip runStateT initLSt $ runWriterT $ evalRegister' (writeChan actionChannel) $
runWidget_ id addPostAction postGUISync id id liftIO__ liftIO desc
runPostActions
_ <- forkIO $ void $ flip execStateT s $ forever $ do
join $ lift $ readChan actionChannel
runMonadMonoid act
lift $ runPostActions
return widget
gtkContext :: (Morph IO IO -> IO SWidget) -> IO ()
gtkContext m = do
_ <- unsafeInitGUIForThreadedRTS
tid <- myThreadId
let post :: Morph IO IO
post e = do
tid' <- myThreadId
if tid' == tid then e else postGUISync e
c <- m post
window <- windowNew
set window [ containerBorderWidth := 10, containerChild := snd c ]
_ <- window `on` deleteEvent $ liftIO mainQuit >> return False
widgetShowAll window
mainGUI
type SWidget = (IO (), Gtk.Widget)
runWidget_
:: forall n m k o . (Monad m, Monad o)
=> (k () -> IO ())
-> (IO () -> IO ())
-> Morph IO IO
-> Morph m o
-> Morph o m
-> Morph IO o
-> (IO () -> n ())
-> Widget n m k
-> o SWidget
runWidget_ nio post' post liftO liftOBack liftIO_ liftION = toWidget
where
liftIO' :: IO a -> o a
liftIO' = liftIO_ . post
reg :: Receive n m k a -> ((a -> IO ()) -> IO (Command -> IO ())) -> o (Command -> IO ())
reg s f = do
rer <- liftIO_ newEmptyMVar
u <- liftIO_ $ f $ \x -> do
re <- readMVar rer
nio $ re x
re <- liftO $ s $ liftION . post . u
liftIO_ $ putMVar rer re
return u
ger :: (Command -> IO ()) -> Send n m a -> Send IO o a
ger hd s f = liftO $ s $ \a -> liftION $ post $ do
hd Block
f a
hd Unblock
nhd :: Command -> IO ()
nhd = const $ return ()
toWidget :: Widget n m k -> o SWidget
toWidget m = liftO m >>= \i -> case i of
Label s -> do
w <- liftIO' $ labelNew Nothing
ger nhd s $ labelSetLabel w
return' w
Canvas w h sc_ me r diaFun -> do
cur <- liftIO_ $ newMVar Nothing
cur' <- liftIO_ $ newMVar Nothing
v <- liftIO_ newEmptyMVar
(canvasDraw, canvas, af, dims) <- liftIO' $ do
canvas <- drawingAreaNew
widgetAddEvents canvas [PointerMotionMask]
af <- aspectFrameNew 0.5 0.5 (Just $ fromIntegral w / fromIntegral h)
_ <- canvas `onSizeRequest` return (Requisition w h)
_ <- containerAdd af canvas
let
dims = do
win <- widgetGetDrawWindow canvas
(w, h) <- drawableGetSize win
let (w', h') = (fromIntegral w, fromIntegral h)
let sc = w' / sc_
return (sc, w', h', w, h)
tr sc w h dia = translate (r2 (w/2, h/2)) $ dia # scaleY (1) # scale sc `atop` rect w h # fc white # lw 0
draw dia_ = do
_ <- swapMVar cur $ Just dia_
let dia = freeze $ clearValue dia_
(sc, w, h, wi, he) <- dims
win <- widgetGetDrawWindow canvas
drawWindowBeginPaintRect win $ Rectangle 0 0 wi he
renderWithDrawable win $ snd $ renderDia Cairo (CairoOptions "" (Width w) RenderOnly True) $ tr sc w h dia
drawWindowEndPaint win
return (draw, canvas, af, dims)
let
compCoords (x,y) = do
(sc, w, h, _, _) <- dims
d <- readMVar cur
let p = ((x w / 2) / sc, (h / 2 y) / sc)
return $ MousePos p $ maybe mempty (`sample` p2 p) d
_ <- reg me $ \re -> do
_ <- on' canvas buttonPressEvent $ tryEvent $ do
p <- eventCoordinates >>= liftIO . compCoords
liftIO $ re $ Click p
_ <- on' canvas buttonReleaseEvent $ tryEvent $ do
p <- eventCoordinates >>= liftIO . compCoords
liftIO $ re $ Release p
_ <- on' canvas enterNotifyEvent $ tryEvent $ do
p <- eventCoordinates >>= liftIO . compCoords
liftIO $ re $ MouseEnter p
_ <- on' canvas leaveNotifyEvent $ tryEvent $ do
p <- eventCoordinates >>= liftIO . compCoords
liftIO $ re $ MouseLeave p
_ <- on' canvas motionNotifyEvent $ tryEvent $ do
p <- eventCoordinates >>= liftIO . compCoords
liftIO $ re $ MoveTo p
_ <- on' canvas scrollEvent $ tryEvent $ do
p <- eventCoordinates >>= liftIO . compCoords
dir <- eventScrollDirection
liftIO $ re $ ScrollTo dir p
on' canvas keyPressEvent $ tryEvent $ do
m <- eventModifier
c <- eventKeyVal
liftIO $ re $ KeyPress m c
_ <- liftIO_ $ on canvas exposeEvent $ tryEvent $ liftIO $ do
d <- readMVar cur'
case d of
Just x -> putMVar v x
_ -> return ()
canvasDraw' <- liftIO_ $ do
v2 <- newMVar False
_ <- forkIO $ do
threadDelay 200000
forever $ do
threadDelay 10000
dia <- takeMVar v
_ <- swapMVar cur' $ Just dia
_ <- swapMVar v2 True
let d = diaFun dia
post $ canvasDraw d
_ <- swapMVar v2 False
return ()
return $ \dia -> do
b <- readMVar v2
unless b $ do
_ <- tryTakeMVar v
putMVar v dia
ger nhd r canvasDraw'
return' af
Button s sens col m -> do
w <- liftIO' buttonNew
hd <- reg m $ \re -> on' w buttonActivated $ re ()
ger hd s $ buttonSetLabel w
ger hd sens $ widgetSetSensitive w
ger hd col $ \c -> do
widgetModifyBg w StateNormal c
widgetModifyBg w StatePrelight c
return' w
Entry (r, s) -> do
w <- liftIO' entryNew
hd <- reg s $ \re -> on' w entryActivate $ entryGetText w >>= re
hd' <- reg s $ \re -> on' w focusOutEvent $ lift $ entryGetText w >>= re >> return False
ger (\x -> hd x >> hd' x) r $ entrySetText w
return' w
Checkbox (r, s) -> do
w <- liftIO' checkButtonNew
hd <- reg s $ \re -> on' w toggled $ toggleButtonGetActive w >>= re
ger hd r $ toggleButtonSetActive w
return' w
Scale a b c (r, s) -> do
w <- liftIO' $ hScaleNewWithRange a b c
_ <- liftIO' $ w `onSizeRequest` return (Requisition 200 40)
hd <- reg s $ \re -> on' w valueChanged $ rangeGetValue w >>= re
ger hd r $ rangeSetValue w
return' w
Combobox ss (r, s) -> do
w <- liftIO' comboBoxNewText
_ <- liftIO' $ w `onSizeRequest` return (Requisition 50 30)
liftIO' $ flip mapM_ ss $ comboBoxAppendText w
hd <- reg s $ \re -> on' w changed $ fmap (max 0) (comboBoxGetActive w) >>= re
ger hd r $ comboBoxSetActive w
return' w
List o xs -> do
ws <- mapM toWidget xs
w <- liftIO' $ case o of
Vertical -> fmap castToBox $ vBoxNew False 1
Horizontal -> fmap castToBox $ hBoxNew False 1
shs <- forM ws $ liftIO' . containerAdd'' w . snd
liftM (mapFst (sequence_ shs >>)) $ return'' ws w
Notebook' s xs -> do
ws <- mapM (toWidget . snd) xs
w <- liftIO' notebookNew
forM_ (zip ws xs) $ \(ww, (s, _)) -> do
liftIO' . flip (notebookAppendPage w) s $ snd $ ww
_ <- reg s $ \re -> on' w switchPage $ re
return'' ws w
Cell onCh f -> do
let b = False
w <- liftIO' $ case b of
True -> fmap castToContainer $ hBoxNew False 1
False -> fmap castToContainer $ alignmentNew 0 0 1 1
sh <- liftIO_ $ newMVar $ return ()
liftO $ onCh $ \bv -> do
mx <- f (liftOBack . toWidget) bv
return $ mx >>= \(x, y) -> liftOBack $ liftIO' $ do
_ <- swapMVar sh x
containerForeach w $ if b then widgetHideAll else containerRemove w
post' $ post $ do
ch <- containerGetChildren w
when (y `notElem` ch) $ containerAdd w y
x
liftM (mapFst (join (readMVar sh) >>)) $ return'' [] w
on' :: GObjectClass x => x -> Signal x c -> c -> IO (Command -> IO ())
on' o s c = liftM (flip f) $ on o s c where
f Kill = signalDisconnect
f Block = signalBlock
f Unblock = signalUnblock
return' :: Monad m => WidgetClass x => x -> m SWidget
return' w = return (widgetShowAll w, castToWidget w)
return'' :: Monad m => WidgetClass x => [SWidget] -> x -> m SWidget
return'' ws w = return (mapM_ fst ws >> widgetShow w, castToWidget w)
mapFst f (a, b) = (f a, b)
containerAdd'' w x = do
a <- alignmentNew 0 0 0 0
containerAdd a x
containerAdd w a
set w [ boxChildPacking a := PackNatural ]
return $ widgetShow a