{-# LANGUAGE OverloadedStrings #-} module GTK.Chat ( addChat ) where import Control.Concurrent.STM import Control.Monad.Loops import Control.Monad.Reader import Data.DateTime import qualified Data.Map.Strict as M import Data.Maybe import qualified Data.String.Class as S import Data.Text as T import Data.Time import Graphics.UI.Gtk import Network.Xmpp import Types renderMessage :: (UTCTime, Maybe Nickname, Msg) -> Text renderMessage (t, mn, m) = T.concat [ "\n" , S.fromString $ formatDateTime "[%T]" t , "<" , fromMaybe "" mn , ">" , m ] delChat :: TVar (M.Map Jid ChatHandler) -> Jid -> IO () delChat chatv jid = do atomically $ modifyTVar' chatv $ M.delete jid addChat :: Jid -> (Msg -> IO ()) -> Hate () addChat jid message_cb = do s <- ask handler <- liftIO $ postGUISync $ do w <- windowNew set w [windowTitle := jidToText jid] windowSetDefaultSize w 500 500 on w deleteEvent $ liftIO $ do delChat (chats s) jid pure False logb <- textBufferNew Nothing logv <- textViewNewWithBuffer logb textViewSetWrapMode logv WrapWordChar textViewSetEditable logv False textViewSetCursorVisible logv False inputb <- textBufferNew Nothing inputv <- textViewNewWithBuffer inputb textViewSetAcceptsTab inputv True inputv `on` keyPressEvent $ inputKeyPressed inputb message_cb logScroll <- scrolledWindowNew Nothing Nothing scrolledWindowSetPolicy logScroll PolicyNever PolicyAutomatic containerAdd logScroll logv panels <- vPanedNew panedPack1 panels logScroll True False panedPack2 panels inputv True False set w [ containerChild := panels ] widgetShowAll w Rectangle _ _ _ height <- widgetGetAllocation panels panedSetPosition panels (round (0.9 * fromIntegral height)) -- wait for the widgets to pop up to catch the upcoming messages whileM_ (fmap (> 0) eventsPending) $ mainIterationDo False pure (\entry -> postGUISync $ do bufferAdd logb $ renderMessage entry -- wait for the buffer to draw // https://stackoverflow.com/a/40917718/4095104 whileM_ (fmap (> 0) eventsPending) $ mainIterationDo False -- scroll the log to the newly received message ei <- textBufferGetEndIter logb textViewScrollToIter logv ei 0 $ Just (1, 1) pure () ) liftIO $ atomically $ modifyTVar (chats s) $ M.insert jid handler bufferGet :: TextBuffer -> IO Text bufferGet tb = do si <- textBufferGetStartIter tb ei <- textBufferGetEndIter tb textBufferGetText tb si ei True bufferAdd :: TextBuffer -> Text -> IO () bufferAdd tb s = do ei <- textBufferGetEndIter tb textBufferInsert tb ei s -- Keypress event handler inputKeyPressed inputb message_cb = do key <- eventKeyName mods <- eventModifier liftIO $ if (key == "Return") && (notElem Shift mods) then do t <- bufferGet inputb message_cb t textBufferSetText inputb ("" :: Text) return True else return False