Go to the first, previous, next, last section, table of contents.

A larger example: Calculator

The implementation of the desktop calculator is derived from the Fudgets example calculator:

type CalcStack = [Integer]

type CalcState = 
 (CalcStack, 
  Integer, 
  Bool,
  Bool)

numOp :: Integer -> CalcState -> CalcState
numOp n (x:xs, b, c, q) = ((if c then n:x:xs else if n < b then x*b+n:xs else x:xs), b, False,q)

unaryOp :: (Integer->Integer) -> CalcState -> CalcState
unaryOp f (x:xs, b, c, q) = (f x:xs, b, True,q)

quitCalc :: CalcState -> CalcState
quitCalc (ls, b, c, q) = (ls, b, c, True)

binaryOp :: (Integer -> Integer -> Integer) -> CalcState -> CalcState
binaryOp f ([x], b, c, q) = binaryOp f ([x,x], b, True, q)
binaryOp f (x:y:xs, b, c, q) = (f y x:xs, b, True, q)

dispCalcState :: CalcState -> String
dispCalcState (x:xs, b, _, _) = (if x < 0 then '-':bconv b (0-x) else bconv b x)++"    "++show b

startstate = ([0::Integer], 10, False, False)

digit d = ["0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"!!(d)]
bconv b x = if x < b then digit x else bconv b (x`div`b) ++ digit (x`mod`b)

button_ls :: [Component (Button (CalcState -> CalcState), DisplayHandle)]
button_ls =
 [button (text "Quit")  (quitCalc),
  button (text "Enter") (\ (x:xs, b, c, q) -> (x : x : xs, b, True, q)),
  button (text "Clear") (\ (_:xs, b, c, q) -> (0:xs, b, False, q)),
  button (text "BS")    (\ (x:xs, b, c, q) -> 
                           ((if c then x else x`div`b):xs, b, c, q)),
  button (text "D")     (numOp 13), button (text "E")     (numOp 14),
  button (text "F")     (numOp 15), button (text "mod")   (binaryOp (mod)),
  button (text "A")     (numOp 10), button (text "B")     (numOp 11),
  button (text "C")     (numOp 12), button (text "/")     (binaryOp (div)),
  button (text "7")     (numOp 7),  button (text "8")     (numOp 8),
  button (text "9")     (numOp 9),  button (text "*")     (binaryOp (*)),
  button (text "4")     (numOp 4),  button (text "5")     (numOp 5),
  button (text "6")     (numOp 6),  button (text "-")     (binaryOp (-)),
  button (text "1")     (numOp 1),  button (text "2")     (numOp 2),
  button (text "3")     (numOp 3),  button (text "+")     (binaryOp (+)),
  button (text "0")     (numOp 0),  button (text "Chs")   (unaryOp (0-)),
  button (text "Base")  (\ (x:xs, b, c, q) -> 
                           (x:xs, (if x <= 36 && x > 1 then x else b), True, q)),
  button (text "Pop")   (\ (x:xs, b, c, q) -> 
                           (if null xs then ([x],b,c, q) else (xs,b,c, q)))]

main =
 mkDC ["*title: Calc"]           >>= \ env ->
  mapIO (\ x -> x env) button_ls >>= \ ls ->
  let
   (btns, dhs) = unzip ls
   
   vs = zip
         [(x,y) | y<-[1..7],x<-[1..4]]
	 dhs
  in
  fixedTable (4,7) (2,2) vs env         >>= \ (tab, tab_dh) ->
  label "0" env                         >>= \ (lab, lab_dh) ->
  realiseDH env (vbox [lab_dh, tab_dh]) >>
  combineButtons btns                   >>= \ btn ->
  let
   loop st =
    getButtonClick btn >>= \ op ->
    case op st of
      st1@(_,val,_,quit) ->
	 if quit then
	    shutdownShop
	 else
	    setLabel lab (dispCalcState st1) >>
	    loop st1
  in
  loop startstate

Go to the first, previous, next, last section, table of contents.