musicScroll-0.1.0.0: src/MusicScroll/UI.hs
{-# language NamedFieldPuns, RecordWildCards, OverloadedStrings,
BangPatterns #-}
module MusicScroll.UI (setupUIThread) where
import Control.Concurrent.Async
(withAsyncBound, waitAnyCancel, withAsync)
import Control.Concurrent.STM (atomically)
import Control.Concurrent.STM.TBQueue (TBQueue, readTBQueue)
import Control.Concurrent.STM.TMVar
(TMVar, newEmptyTMVar, takeTMVar, putTMVar)
import Control.Exception (throwIO, AsyncException(UserInterrupt))
import Control.Monad (forever)
import Data.Functor (void)
import Data.GI.Gtk.Threading
(setCurrentThreadAsGUIThread, postGUISync)
import Data.Maybe (fromJust)
import Data.Text (Text)
import Data.Text as T
import qualified GI.Gtk as Gtk
import MusicScroll.TrackInfo (TrackInfo(..))
import Paths_musicScroll
data AppContext = AppContext
{ mainWindow :: Gtk.Window
, titleLabel :: Gtk.Label
, artistLabel :: Gtk.Label
, lyricsTextView :: Gtk.TextView
}
-- Remember to use Gtk.init Nothing before calling this.
getGtkScene :: IO AppContext
getGtkScene = do
file <- getDataFileName "app.glade"
builder <- Gtk.builderNewFromFile (T.pack file)
-- We *know* these ids are defined
let getWidget wid id =
Gtk.builderGetObject builder id
>>= Gtk.castTo wid . fromJust >>= return . fromJust
AppContext <$> getWidget Gtk.Window "mainWindow"
<*> getWidget Gtk.Label "titleLabel"
<*> getWidget Gtk.Label "artistLabel"
<*> getWidget Gtk.TextView "lyricsTextView"
setupUIThread :: TBQueue (TrackInfo, [Text]) -> IO ()
setupUIThread trackUpdates =
do appCtxMVar <- atomically newEmptyTMVar
withAsyncBound (uiThread appCtxMVar) $ \a1 ->
withAsync (uiUpdateThread trackUpdates appCtxMVar) $ \a2 ->
void (waitAnyCancel [a1, a2]) >> throwIO UserInterrupt
uiThread :: TMVar AppContext -> IO ()
uiThread ctxMVar = do
setCurrentThreadAsGUIThread
_ <- Gtk.init Nothing
appCtx@(AppContext {..}) <- getGtkScene
atomically (putTMVar ctxMVar appCtx)
Gtk.labelSetText titleLabel "MusicScroll"
Gtk.widgetShowAll mainWindow
_ <- Gtk.onWidgetDestroy mainWindow Gtk.mainQuit
Gtk.main
---
uiUpdateThread :: TBQueue (TrackInfo, [Text]) -> TMVar AppContext -> IO a
uiUpdateThread input ctxMVar = do
AppContext {..} <- atomically (takeTMVar ctxMVar)
forever $
do (track, lyrics) <- atomically (readTBQueue input)
let !singleLyrics = T.unlines lyrics
!bytesToUpdate = fromIntegral $ T.length singleLyrics
postGUISync $ do
Gtk.labelSetText titleLabel (tTitle track)
Gtk.labelSetText artistLabel (tArtist track)
lyricsBuffer <- Gtk.textViewGetBuffer lyricsTextView
Gtk.textBufferSetText lyricsBuffer singleLyrics bytesToUpdate