hbro-1.2.0.0: library/Hbro/Gui/PromptBar.hs
{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, TemplateHaskell #-}
-- | Designed to be imported as @qualified@.
module Hbro.Gui.PromptBar (
-- * Types
PromptBar
-- description
, HasPromptBar(..)
, labelName
, entryName
, boxName
-- * Functions
, initialize
, open
, unhide
, hide
, Hbro.Gui.PromptBar.clean
-- * Prompts
, prompt
, prompt'
, promptURI
, getEntryValue
) where
-- {{{ Imports
import Hbro.Error
import Hbro.Gui.Buildable
import Hbro.Gui.PromptBar.Signals
import Hbro.Gui.PromptBar.Hooks hiding(clean, set)
import qualified Hbro.Gui.PromptBar.Hooks as Hooks
import Hbro.Logger hiding(initialize)
import Hbro.Prelude
import Control.Lens.Getter
import Control.Lens.Setter
import Control.Lens.Lens
import Control.Lens.TH
import Control.Monad.Reader hiding(when)
import Graphics.Rendering.Pango.Enums
import Graphics.UI.Gtk.Abstract.Widget
import Graphics.UI.Gtk.Builder
import Graphics.UI.Gtk.Display.Label
import Graphics.UI.Gtk.Entry.Editable
import Graphics.UI.Gtk.Entry.Entry
import Graphics.UI.Gtk.Layout.HBox
import Network.URI.Monadic
-- }}}
-- {{{ Types
-- | No exported constructor, please use 'buildWith'
data PromptBar = PromptBar
{ _box :: HBox
, _description :: Label
, _entry :: Entry
}
makeLensesWith ?? ''PromptBar $ classyRules
& lensField .~ (\name -> Just (tail name ++ "L"))
& lensClass .~ (\name -> Just ("Has" ++ name, "_" ++ toLower name))
-- | A 'PromptBar' can be built from an XML file.
instance Buildable PromptBar where
buildWith b = PromptBar <$> gSync (builderGetObject b castToHBox boxName)
<*> gSync (builderGetObject b castToLabel labelName)
<*> gSync (builderGetObject b castToEntry entryName)
-- | Widget name used in the XML file that describes the UI
labelName, entryName, boxName :: Text
labelName = "promptDescription"
entryName = "promptEntry"
boxName = "promptBox"
-- | Error message
promptInterrupted :: Text
promptInterrupted = "Prompt interrupted."
-- }}}
initialize :: (BaseIO m) => PromptBar -> m ()
initialize aPromptBar = gAsync $ do
description `labelSetAttributes` [allItalic, allBold]
description `labelSetAttributes` [AttrForeground {paStart = 0, paEnd = -1, paColor = gray}]
widgetModifyBase entry StateNormal black
widgetModifyText entry StateNormal gray
where
description = aPromptBar^.descriptionL
entry = aPromptBar^.entryL
-- | Pops-up the prompt bar, filling it with given arguments.
open :: (BaseIO m, MonadReader t m, HasPromptBar t)
=> Text -- ^ Description
-> Text -- ^ Pre-fill value
-> m ()
open a b = do
debugM "hbro.promptbar" "Opening prompt."
void . open' a b =<< askL _promptbar
open' :: (BaseIO m) => Text -> Text -> PromptBar -> m PromptBar
open' newDescription defaultText =
withM_ descriptionL (gAsync . (`labelSetText` newDescription))
>=> withM_ entryL (gAsync . (`entrySetText` defaultText))
>=> withM_ boxL (gAsync . widgetShow)
>=> withM_ entryL (gAsync . widgetGrabFocus)
>=> withM_ entryL (gAsync . (`editableSetPosition` (-1)))
unhide, hide :: (BaseIO m, MonadReader t m, HasPromptBar t) => m ()
unhide = gAsync . widgetShow =<< askL (_promptbar.boxL)
hide = gAsync . widgetHide =<< askL (_promptbar.boxL)
-- | Close prompt, that is: clean its content, signals and callbacks
clean :: (BaseIO m, MonadReader t m, HasPromptBar t, HasPromptHooks n t) => m ()
clean = do
gAsync . (`widgetRestoreText` StateNormal) =<< askL (_promptbar.entryL)
gAsync . (\e -> widgetModifyText e StateNormal gray) =<< askL (_promptbar.entryL)
hide
Hooks.clean
-- {{{ Prompts
-- | Same as 'prompt\'' without the value-changed callback
prompt :: (BaseIO m, MonadError Text m, MonadReader t m, HasPromptBar t, HasPromptHooks n t, BaseIO n)
=> Text -- ^ Prompt description
-> Text -- ^ Initial value
-> m Text
prompt a b = prompt' a b (const $ return ())
-- | Open prompt bar with given description and default value,
-- register a callback to trigger when value is changed, and another one when value is validated.
prompt' :: (BaseIO m, MonadError Text m, MonadReader t m, HasPromptBar t, HasPromptHooks n t, BaseIO n)
=> Text -- ^ Prompt description
-> Text -- ^ Pre-fill value
-> (Text -> n ()) -- ^ Callback triggered each time prompt value changes
-> m Text
prompt' description startValue f = do
clean
open description startValue
result <- newEmptyMVar
Hooks.set onChangedL $ \(Changed x) -> f x
Hooks.set onCancelledL $ \_ -> putMVar result Nothing
Hooks.set onValidatedL $ \(Activated x) -> putMVar result (Just x)
takeMVar result <!> promptInterrupted
-- | Same as 'prompt\'' for URI values
promptURI :: (BaseIO m, BaseIO n, MonadError Text m, MonadError Text n, MonadReader t m, MonadReader t n, HasPromptBar t, HasPromptHooks n t)
=> Text -> Text -> m URI
promptURI description startValue = do
clean
open description startValue
checkURI (Changed startValue)
result <- newEmptyMVar
Hooks.set onChangedL checkURI
Hooks.set onCancelledL $ \_ -> putMVar result Nothing
Hooks.set onValidatedL $ \(Activated uri) -> putMVar result (Just uri)
parseURIReference =<< takeMVar result <!> promptInterrupted
checkURI :: (BaseIO m, MonadReader t m, HasPromptBar t) => Changed -> m ()
checkURI (Changed v) = do
debugM "hbro.prompt" $ "Is URI ? " ++ tshow (isURIReference $ unpack v)
(gAsync . \e -> widgetModifyText e StateNormal (green <| isURIReference (unpack v) |> red)) =<< askL (_promptbar.entryL)
getEntryValue :: (BaseIO m, MonadReader t m, HasPromptBar t) => m Text
getEntryValue = gSync . entryGetText =<< askL (_promptbar.entryL)