packages feed

WidgetRattus-0.3: examples/gui/src/Calculator.hs

{-# OPTIONS -fplugin=WidgetRattus.Plugin #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}

import WidgetRattus
import WidgetRattus.Signal
import WidgetRattus.Widgets
import Prelude hiding (map, const, zipWith, zip, filter, getLine, putStrLn,null)
import Data.Text (Text)

nums :: List Int
nums = [0..9]

data Op = Plus | Minus | Equals | Reset

compute :: (Int :* Op :* Bool -> Maybe' (Int :* Op) -> Int :* Op :* Bool)
compute (n :* op     :* _) Nothing'          = (n :* op :* False)
compute _                  (Just' (_ :* Reset)) = (0 :* Reset :* True)
compute (n :* Plus   :* _) (Just' (m :* op)) = (n + m) :* op :* True
compute (n :* Minus  :* _) (Just' (m :* op)) = (n - m) :* op :* True
compute (_ :* Equals :* _) (Just' (m :* op)) = m :* op :* True
compute (_ :* Reset  :* _) (Just' (m :* op)) = m :* op :* True


window :: C VStack
window = do

    -- construct number buttons
    numBtns :: List Button  
        <- mapM (mkButton . const) nums
    let [b0, b1, b2, b3, b4, b5, b6, b7, b8, b9] = numBtns
    -- construct operator buttons
    resetBut <- mkButton (const ("C"::Text))
    addBut   <- mkButton (const ("+"::Text))
    subBut   <- mkButton (const ("-"::Text))
    eqBut    <- mkButton (const ("="::Text))

    -- signal to construct numbers
    let numClicks :: List (O (Sig (Int -> Int))) 
          = zipWith' (\b n -> mapAwait (box (\ _ x -> x * 10 + n)) (btnOnClickSig b)) numBtns nums
          


    -- signal to reset the current number to 0, after clicking an
    -- operator button
    let resetSig :: O (Sig (Int -> Int))
          = mapAwait (box (\ _ _ -> 0))
            $ interleaveAll (box (\ a _ -> a))
            $ map' btnOnClickSig [addBut, subBut, eqBut,resetBut]

    -- combine signals to construct the number signal
    let sigList = resetSig :! numClicks :: List (O (Sig (Int -> Int)))
    let combinedSig = interleaveAll (box (\ a _ -> a)) sigList

    -- number signal (i.e. the multidigit number that has been
    -- constructed)
    let numberSig :: Sig Int
         = scanAwait (box (\ a f-> f a)) 0 combinedSig
    -- operator signal
    let opSig :: O (Sig Op)
         = interleaveAll (box (\ a _ -> a))
          $ map' (\ (op :* btn) -> mapAwait (box (\ _ -> op)) (btnOnClickSig btn) )
            [(Plus :* addBut), (Minus :* subBut), (Equals :* eqBut), (Reset :* resetBut)]

    -- signal consisting of an operand (i.e. a number) @n@ and an
    -- operator @op@. @n@ is the value of @numberSig@ just before
    -- clicking an operator button, and op is taken from opSig
    let operand :: Sig (Maybe' (Int :* Op))
         = Nothing' ::: triggerAwaitM (box (\op n -> Just' (n :* op))) opSig (buffer 0 numberSig)

    -- The result signal consisting of a number n that is the result
    -- of the current computation, an operator op that still needs to
    -- applied to n and a Boolean b that indicates whether we have
    -- just calculated n (and thus n should be displayed)
    let resSig :: Sig (Int :* Op :* Bool)
         = scan (box compute) (0 :* Plus :* True) operand
    -- The signal that should be displayed
    let displaySig :: Sig Int
         = zipWith (box (\ (n :* _ :* b) m -> if b then n else m)) resSig numberSig
    
    -- label to display the result (and operands)
    result <- mkLabel displaySig
    
    -- lay out widgets
    operators <- mkConstVStack (resetBut :* addBut :* subBut :* eqBut)
    row1 <- mkConstHStack (b7 :* b8 :* b9)
    row2 <- mkConstHStack (b4 :* b5 :* b6)
    row3 <- mkConstHStack (b1 :* b2 :* b3)

    numbers <- mkConstVStack (row1 :* row2 :* row3 :* b0)

    input <- mkConstHStack (numbers :* operators)

    mkConstVStack (result :* input)

main :: IO ()
main = runApplication window