These widgets can be used to represent a Boolean value. The idea is pretty simple, the widget can be in one of two states which represent the two boolean values.
Checked / On : True
Unchecked / Off : False
In [1]:
{-# LANGUAGE OverloadedStrings #-}
import IHaskell.Display.Widgets
import Data.Text (pack, unpack)
import Text.Printf (printf)
In [2]:
-- Constructors
chk <- mkCheckBox
tgb <- mkToggleButton
-- For demonstration
o <- mkHTMLWidget
Below, we represent one boolean using a checkbox, and the other using a toggle button. The logical and (&&
) of the two is displayed below.
In [3]:
-- Display
chk
tgb
o
The BoolValue
field represents the underlying boolean value.
In [4]:
setField chk Description "Bool 1: "
setField tgb Description "Bool 2"
-- Helper function
refresh b =
let stat = if b then "green" else "red"
fmt = "<div style=\"background:%s;color:#ffffff\"><b>%s</b></div>"
in setField o StringValue $ pack $ printf fmt stat (show b)
-- Cosmetic changes
setField o Description "Bool 1 && Bool 2"
setField o Padding 10
-- And (&&) the two values, and send output to html widget
setHandler w = setField w ChangeHandler $ do
b1 <- getField chk BoolValue
b2 <- getField tgb BoolValue
refresh (b1 && b2)
setHandler chk
setHandler tgb
Let's try to create a graphical 8-bit-binary to decimal converter. We'll represent seven bits using ToggleButton
widgets, and the negative bit using a CheckBox
. The binary number is represented using 1+7-bit sign-and-magnitude representation for simplicity.
Boxes are used to layout the widgets in an appealing manner, and the output widget is used to display the result.
In [5]:
-- First, some library functions
import Control.Monad (replicateM, forM_)
import Data.IORef
import IHaskell.Display (plain)
Now, we create a CheckBox
and seven ToggleButton
s.
In [6]:
sign <- mkCheckBox
bits <- replicateM 7 mkToggleButton
setField sign Description "Negative"
forM_ bits $ \t -> do
setField t ButtonStyle PrimaryButton
setField t BorderRadius 20
Then we create a FlexBox
to hold the widgets, and an HTMLWidget
to display the output.
In [7]:
box <- mkFlexBox
out <- mkHTMLWidget
-- Sub-containers
box1 <- mkFlexBox
setField box1 Children [ChildWidget sign, ChildWidget out]
box2 <- mkFlexBox
setField box2 Children (map ChildWidget $ reverse bits)
-- Add widgets to the container
setField box Children (map ChildWidget [box1, box2])
setField box Orientation VerticalOrientation
-- Add some UI chrome
setField box BoxStyle InfoBox
setField box BorderRadius 20
setField out BorderStyle GrooveBorder
setField out BorderRadius 20
setField out BorderWidth 4
setField out Width 100
setField out Height 30
setField out Margin 10
setField sign Padding 10
setField box2 Padding 10
setField box2 Pack BaselineLocation
-- Display the container
box
Now, we implement the logic of our converter, and make it send the output to the HTMLWidget
we created above.
In [8]:
import Control.Arrow (first, second)
-- Mutable value, with a sign bit
val <- newIORef (0 :: Int, False)
-- Helper function to redraw output
refresh :: (Int, Bool) -> IO ()
refresh (x, b) =
let val = x * if b then (-1) else 1
fmt = "<div align=\"center\"><b>%d</b></div>"
in setField out StringValue (pack $ printf fmt val)
setField sign ChangeHandler $ do
-- Change sign for value
modifyIORef val (second not)
-- Redraw output
readIORef val >>= refresh
forM_ (zip bits (iterate (*2) 1)) $ \(t, n) -> do
setField t Description "0"
setField t ChangeHandler $ do
f <- getField t BoolValue
setField t Description (if f then "1" else "0")
modifyIORef val (first $ if f then (+n) else (\x->x-n))
readIORef val >>= refresh