The Bool Widgets

  • CheckBox
  • ToggleButton

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)

Simple demonstration


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



Extended example

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 ToggleButtons.


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