-- | GUI programming in a web browser -- Examples introducing ThreepennyFudgets -- Functional Programming course 2018. -- Thomas Hallgren {- This started as a skeleton, the definitions were filled in during the lecture. -} import qualified Graphics.UI.Threepenny as UI import Graphics.UI.Threepenny.Core import ThreepennyFudgets main :: IO () main = runF' init mainF where init w = UI.addStyleSheet w "demo.css" mainF = h1F (textF "Hello!") +< pF (textF "Introducining ThreepennyFudgets") +< pF ex1 +< pF ex2 +< pF ex3 +< pF ex4 +< pF ex5 +< pF ex6 +< pF ex7 -------------------------------------------------------------------------- -- * Example 1: a program to test the factorial function --ex1 = numberF =>= mapF fac =>= showF --ex1 = showF =<= mapF fac =<= numberF ex1 = pF (textF "n = " +< numberF) =>= mapF fac =>= pF (textF "n! = " +< showF) fac n = product [1..n] -------------------------------------------------------------------------- -- * Example 2: counter example ex2 = showF =<= stateF count 0 =<= buttonF "+1" where count n _ = (n+1,[n+1]) -------------------------------------------------------------------------------- -- * Example 3: up/down counter ex3 = showF =<= stateF count 0 =<= (buttonF "+1" >+< buttonF "-1") where count n (Left _) = (n+1,[n+1]) count n (Right _) = (n-1,[n-1]) -------------------------------------------------------------------------------- -- * Example 4: up/down/reset counter data CounterButton = Up | Down | Reset deriving (Eq,Show,Enum) counterButtonsF = listF [(b,buttonF (show b)) | b <- [Up ..]] ex4 = showF =<= stateF count 0 =<= counterButtonsF count n (Up,_) = (n+1,[n+1]) count n (Down,_) = (n-1,[n-1]) count n (Reset,_) = (0,[0]) -------------------------------------------------------------------------------- -- * Example 5: loadable up/down/reset counter ex5 = loopLeftF (mapF Left =<= numberF =<= stateF count' 0) =<= counterButtonsF where count' n (Left new) = (new,[new]) count' n (Right b) = count n b -------------------------------------------------------------------------------- -- * Example 6: prototype user interface for a board game ex6 = loopF (stateF game False =<= tableF width squaresF `classF` "board") game b (p,_) = (not b,[(p,if b then "X" else "O")]) squaresF = listF [((x,y),squareF (x,y)) | y<-[1..height], x<-[1..width]] squareF _ = mapMaybeF fromLeft =<= buttonGroupF stringDisplayF `withF` [style=:css] =<= mapF Right where css = [("width","30px"),("height","30px")] width = 8 height = 8 ------------------------------------------------------------------------- -- * Illustrates the use of a canvas for graphical output ex7 = (pF sidesF >+< pF angleF) =>= gatherF =>= mapF polygon =>= canvasF (300,300) `withF` [set style css] where sidesF = textF "Draw a polygon with " +< numberF >+ textF " sides." angleF = toggleButtonF "Spin" =>= mapF setTimer =>= timerF =>= putF 0 (stateF angle 0) setTimer spin = if spin then Just 40 else Nothing angle a _ = ((a+1) `mod` 360 ,[pi*real a/180]) css = [("border","1px solid black"), ("background","white")] -- A function to draw a polygon and some text polygon :: (Int,Double) -> UI.Canvas -> UI () polygon (n,a0) c = do fillPath (UI.solidColor (UI.RGB 255 255 128)) ps c strokePath "#00f" ps c UI.set' UI.fillStyle (UI.solidColor (UI.RGB 0 0 0)) c UI.fillText "A simple canvas example" (20,20) c where ps = map corner [0..n] (cx,cy) = (150,150) -- center r = 100 -- radius of enclosing circle corner i = (cx+r*cos a,cy+r*sin a) where a = 2*pi*(real i/ real n+0.25-0.5/real n) + a0 real x = fromIntegral x