frpnow-gtk3-0.2.0: Control/FRPNow/GTK/DataWidgets.hs
{- |
Module : Control.FRPNow.GTK.DataWidgets
Copyright : (c) George Steel 2017
License : BSD3
Maintainer : george.steel@gmail.org
Functions for creating widgets allowing entry and display of variable data, reperesented as 'Behavior's
-}
module Control.FRPNow.GTK.DataWidgets where
import Control.FRPNow.GTK.Core
import Control.FRPNow.GTK.MissingFFI
import Graphics.UI.Gtk
import Control.Applicative
import Control.Monad
import Control.Monad.Fix
import Control.Monad.IO.Class
import Control.FRPNow
import Data.Maybe
import qualified Data.Text as T
import Data.Text (Text)
-- | Version of 'show' which outputs 'Text'
showtext :: (Show a) => a -> Text
showtext = T.pack . show
-- * Labels
-- | Creates a label containing static text
createLabel :: (MonadIO m) => Text -> m Label
createLabel t = liftIO $ do
l <- labelNew (Just t)
set l [miscXalign := 0]
return l
-- | Creates a label containing dynamic text
createLabelDisplay :: Behavior Text -> Now Label
createLabelDisplay s = do
l <- sync $ labelNew (Nothing :: Maybe Text)
sync $ set l [miscXalign := 0, labelWrap := True]
setAttr labelLabel l s
return l
-- | Creates a TextView with editing disabled containing dynamic text. Useful for showing text too long for a Label.
createTextViewDisplay :: Behavior Text -> Now TextView
createTextViewDisplay dyntext = do
tv <- sync textViewNew
done <- getUnrealize tv
buf <- sync $ textBufferNew Nothing
inittext <- sample dyntext
let updates = toChanges dyntext `beforeEs` done
sync $ set buf [textBufferText := inittext]
callIOStream (\x -> set buf [textBufferText := x]) updates
sync $ textViewSetBuffer tv buf
return tv
-- * Entry
-- | Creates an Entey with an initial value
createEntry :: String -> Now (Entry, Behavior String)
createEntry inittext = do
entry <- sync $ entryNew
sync $ set entry [entryText := inittext]
edits <- getSignal editableChanged entry (entryGetText entry >>=)
btext <- sample $ fromChanges inittext edits
return (entry, btext)
-- | Creates an Entry which only allows characters satisfying as predicate. Useful for numeric entry.
createFilteredEntry :: (Char -> Bool) -> String -> Now (Entry, Behavior String)
createFilteredEntry f inittext = do
entry <- sync $ entryNew
sync $ set entry [entryText := filter f inittext]
sync . mfix $ \cid ->
entry `on` insertText $ \str pos -> do
signalBlock cid
pos' <- editableInsertText entry (filter f str) pos
signalUnblock cid
stopInsertText cid
return pos'
edits <- getSignal editableChanged entry (entryGetText entry >>=)
btext <- sample $ fromChanges inittext edits
return (entry, btext)
-- | Creates a floating-point SpinButton with range, step size, and initial value.
createSpinEntry :: (Double, Double) -> Double -> Double -> Now (SpinButton, Behavior Double)
createSpinEntry (a,b) stepsize initval = do
sbtn <- sync $ spinButtonNewWithRange a b stepsize
sync $ spinButtonSetValue sbtn initval
(changes, changecb) <- callbackStream
sync $ onValueSpinned sbtn (changecb =<< spinButtonGetValue sbtn)
val <- sample $ fromChanges initval changes
return (sbtn,val)
-- | Creates a integer SpinButton with range, step size, and initial value.
createIntSpinEntry :: (Int, Int) -> Int -> Int -> Now (SpinButton, Behavior Int)
createIntSpinEntry (a,b) stepsize initval = do
sbtn <- sync $ spinButtonNewWithRange (fromIntegral a) (fromIntegral b) (fromIntegral stepsize)
sync $ spinButtonSetValue sbtn (fromIntegral initval)
(changes, changecb) <- callbackStream
sync $ onValueSpinned sbtn (changecb =<< spinButtonGetValueAsInt sbtn)
val <- sample $ fromChanges initval changes
return (sbtn,val)
-- * Progress
-- | Creates a progress bar which displays an annotation and can optionally be disabled.
createProgressBar :: Behavior (Maybe (Double, Text)) -> Now ProgressBar
createProgressBar mprogress = do
let isactive = fmap isJust mprogress
position = fmap (maybe 0 fst) mprogress
lbl = fmap (maybe T.empty snd) mprogress
bar <- sync progressBarNew
sync $ set bar [progressBarShowText := True]
setAttr progressBarFraction bar position
setAttr progressBarText bar lbl
setAttr widgetSensitive bar isactive
return bar
-- | Creates a progress bar which is always on and displays the progres value only.
createSimpleProgressBar :: Behavior Double -> Now ProgressBar
createSimpleProgressBar progress = do
bar <- sync $ progressBarNew
setAttr progressBarFraction bar progress
return bar
-- | Creates a spinner which spins when its parameter is True.
createSpinner :: Behavior Bool -> Now Spinner
createSpinner b = do
spn <- sync $ spinnerNew
setAttr spinnerActive spn b
return spn
-- * Sliders
-- | Creates a slider with range, step size, and initial value.
createSlider :: (Double, Double) -> Double -> Double -> Now (HScale, Behavior Double)
createSlider (a,b) stepsize initval = do
slider <- sync $ hScaleNewWithRange a b stepsize
sync $ set slider [rangeValue := initval]
changed <- getSignal valueChanged slider (rangeGetValue slider >>=)
val <- sample $ fromChanges initval changed
return (slider,val)
-- | Creates a slider which displays a dynamic value and emits attempts by the user to change that value.
createMotorizedSlider :: (Double, Double) -> Double -> Behavior Double -> Now (HScale,EvStream Double)
createMotorizedSlider (a,b) stepsize dat = do
i <- sample dat
slider <- sync $ hScaleNewWithRange a b stepsize
setAttr rangeValue slider dat
stream <- getSignal changeValue slider (\f _ d -> f d >> return True)
return (slider,stream)