960 likes | 1.1k Views
Generic Graphical User Interfaces for the WEB ___________. Rinus Plasmeijer - Peter Achten University of Nijmegen www.cs.kun.nl/~clean. Snapshot from Clean's CD shop. Overview of the talk. Clean’s generic G raphical E ditor C omponents
E N D
GenericGraphical User Interfacesfor the WEB___________ Rinus Plasmeijer - Peter Achten University of Nijmegen www.cs.kun.nl/~clean
Overview of the talk • Clean’s generic Graphical Editor Components • Architecture Web applications <–> Architecture normal Clean application • Generation of Html code • Automatic Generation of Html code for any Clean type • Automatic Generation of simple interactive forms for any Clean type • Separating Data from Views allowing to make any kind of form • Implementation • Arrows • Conclusion & Future Research
Overview of the talk • Clean’s generic Graphical Editor Components • Architecture Web applications <–> Architecture normal Clean application • Generation of Html code • Automatic Generation of Html code for any Clean type • Automatic Generation of simple interactive forms for any Clean type • Separating Data from Views allowing to make any kind of form • Implementation • Arrows • Conclusion & Future Research
Graphical Editor Components in Clean • Start :: *World *World • Start world = startFormCircuit myEditor [1,5,2] world • myEditor= edit "List" >>> arr toBalancedTree >>> display "Balanced Tree"
mutual dependent arrows • myEditor = feedback ( arr toPounds >>> • edit “Pounds” >>> • arr toEuros >>> • edit“Euros” )
Graphical Editor Components • Given any type, an editor can be generated for it automatically • Editors can be connected: the output of one can be used as input for another • Editors even may be mutual dependent • One can make "circuits" of editors using special combinators (arrows) • Even editors for higher order types can be generated (editing of functions) • You can easily define your favourite "look" for any type (buttons, menu's) • Great for rapid prototyping: no low level IO programming needed
Overview of the talk • Clean’s generic Graphical Editor Components • Architecture Web applications <–> Architecture normal Clean application • Generation of Html code • Automatic Generation of Html code for any Clean type • Automatic Generation of simple interactive forms for any Clean type • Separating Data from Views allowing to make any kind of form • Implementation • Arrows • Conclusion & Future Research
Architecture "normal" Clean Application OS Clean Application
Architecture Clean application for the Web Browser MyPage + info Web Server MyPage.exe + info htmlcode htmlcode Clean Application
Architecture Clean application for the Web Browser Web Server
How to achieve Graphical Editor Components for the Web ? • We need to be able to generate Html code • For any Clean type, an Html Form has to be generatedautomatically • Deal with interaction: a change made in a Form has to be handled automatically • We need to be able to connect Forms • We have to be able to make sophisticated Forms with any look we like • Clean application is a pure function: it "just" need its previous state
Overview of the talk • Clean’s generic Graphical Editor Components • Architecture Web applications <–> Architecture normal Clean application • Generation of Html code • Automatic Generation of Html code for any Clean type • Automatic Generation of simple interactive forms for any Clean type • Separating Data from Views allowing to make any kind of form • Implementation • Arrows • Conclusion & Future Research
Generating Html Code • An Algebraic Data Type has been defined isomorphic with Html, Style sheets • :: Html = Html Head Rest • :: Head = Head [HeadAttr] [HeadTag] • :: Rest = Body [BodyAttr] [BodyTag] • :: BodyTag = A [A_Attr] [BodyTag] • … • | Var [Std_Attr] String • | Stable [Table_Attr] [[BodyTag]] • | BodyTag [BodyTag] | EmptyBody • any value of Html ADT can be converted to real Html-code, using a generic function • generic gHpra :: *File a *File
Playing with the world of web pages • An interactive Clean program has type: • Start:: *World *World • An interactive Clean Web program has type: • myPage:: *HSt (Html,*HSt) • So, a wrapper function is defined: • doHtml:: (*HSt (Html,*HSt)) *World *World
General Structure of a Clean Web application • module example • import StdEnv, StdHtml • Start world = doHtmlhelloWorldPage world • helloWorldPage hst • = mkHtml "Hello World Example" • [Txt "Hello World"] hst • mkHtml title tags hst = (Html (Head [`Hd_Std [Std_Title s]] []) • (Body [] tags) • ,hst)
General Structure of a Clean Web application • helloWorldPage hst • = mkHtml "Hello World Example" • [Txt "Hello World"] hst
What did we gain with our ADT definition ? • Disadvantage • Slightly more verbose than plain Html, caused by restrictions of an ADT • Advantage • We have at least the expressive power of Html • Everything is typed, so many html errors cannot be made anymore • ADT is a handy Html grammar for beginners • Full power of Clean, let's use it….
Overview of the talk • Clean’s generic Graphical Editor Components • Architecture Web applications <–> Architecture normal Clean application • Generation of Html code • Automatic Generation of Html code for any Clean type • Automatic Generation of simple interactive forms for any Clean type • Separating Data from Views allowing to make any kind of form • Implementation • Arrows • Conclusion & Future Research
Add ability to show any type • toHtml:: a BodyTag | gForm {|*|} a • toHtml converts any Clean type to Html code using the generic function gForm
Add ability to show any type showBalancedTreePage hst = mkHtml "Balanced Tree" [ H1 [] "Balanced Tree" , toHtml (fromListToBalTree [1..5]) ] hst :: Tree a = Node (Tree a) a (Tree a) | Leaf derive gFormTree
Overview of the talk • Clean’s generic Graphical Editor Components • Architecture Web applications <–> Architecture normal Clean application • Generation of Html code • Automatic Generation of Html code for any Clean type • Automatic Generation of simple interactive forms for any Clean type • Separating Data from Views allowing to make any kind of form • Implementation • Arrows • Conclusion & Future Research
Automatic creation of Interactive Forms • mkEditForm :: FormId d *HSt *(Form d, *HSt) | gHTML d • :: FormId • = { id :: String // id *uniquely* identifying the form • , lifespan :: Lifespan // lifespan of form • , mode :: Mode // editable or not • } • :: Lifespan • = Persistent // form will live "forever" in a file • | Session // form will live a session long • | Page // form will live a page long • :: Mode • = Edit // an editable form • | Display // a non-editable form • :: Form d • = { changed :: Bool // the user has edited the form • , value :: d // current value in data domain (the feel) • , form :: [BodyTag] // html code to create the form (the look) • }
Example: an editable record • :: Person = { name :: String • , address :: String • , city :: String • } • initPerson = { name = "", address = "" , city = "" } • personPage hst • # (person,hst) = mkEditForm (nFormId "person") initPerson hst • = mkHtml "Person" • [ H1 [] "Person" • , BodyTag person.form • ] hst • nFormId s = {id = s, lifespan = Page, mode = Edit} • ndFormId s = {id = s, lifespan = Page, mode = Display}
Example: an editable tree • treePage hst • # (tree,hst) = mkEditForm (nFormId "tree") (Node Leaf 1 Leaf) hst • = mkHtml "Simple Tree" • [ H1 [] "Simple Tree" • , BodyTag tree.form • ] hst
Example: an editable list converted to a balanced tree • listToBalancedTreePage hst • # (list,hst) = mkEditForm (nFormId "list") [1] hst • = mkHtml "List to Balanced Tree" • [ H1 [] "List to Balanced Tree" • , BodyTag list.form • , Br • , toHtml (fromListToBalTree list.value) • ] hst
Example: showing a list as an vertical table • vertlistForm:: FormId [a] *HSt (Form [a],*HSt) | gHTML a • vertlistForm_ [] hst = ({changed = False,value = [] ,form = [] },hst) • vertlistFormformid [x:xs] hst • # (nxs,hst) = vertlistFormformid xs hst • # (nx, hst) = mkEditForm nformid x hst • = ( {changed = nxs.changed || nx.changed • ,value = [nx.value : nxs.value] • ,form = [nx.form <||> nxs.form] • } ,hst ) • where • nformid = {formid & id = formid.id +++ toString (length xs)}
Example: taken the sum of a table • vertTablePage hst • # (vtable,hst) = vertlistForm (nFormId "vertlist") [1..5] hst • = mkHtml "Vertical Table" • [ H1 [] "Vertical Table" • , BodyTag vtable.form • , toHtml (sum vtable.value) • ] hst
Simple Spreadsheet • spreadsheet hst • # (tablef, hst) = table_hv_Form (nFormId "table") (inittable 8 10) hst • # (rowsumf,hst) = vertlistForm (ndFormId "rsum") (rowsum tablef.value) hst • # (colsumf,hst) = horlistForm (ndFormId "csum") (colsum tablef.value) hst • # (totsumf,hst) = mkEditForm (ndFormId "tsum") (sum (rowsum tablef.value)) hst • = mkHtml "Spreadsheet" • [ H1 [] "Simple Spreadsheet Example: " • , tablef.form <=> rowsumf.form • , colsumf.form <=> totsumf.form • ] hst • rowsum table = map sum table • colsum table = map sum (transpose table) • transpose table = [[table!!i!!j \\ i <- [0..(length table) - 1]] • \\ j <- [0..length (table!!0) - 1] • ] • inittable n m = [ [i..i+n] \\ i <- [0,n+1 .. n*m+1]]
Spreadsheet using toHtml(Form) • spreadsheet hst • # (table, hst) = table_hv_Form (nFormId "table") (inittable 8 10) hst • = mkHtml "Spreadsheet" • [ H1 [] "Simple Spreadsheet Example: " • , table.form <=> rowsumF table.value • , colsumF table.value <=> totsumF table.value • ] hst • rowsumF table = toHtmlForm (vertlistForm (ndFormId "rsum") (rowsum table)) • colsumF table = toHtmlForm (horlistForm (ndFormId "csum") (colsum table)) • totsumF table = toHtmlForm (mkEditForm (ndFormId "tsum") (sum (rowsum table))) • toHtmlForm :: (*HSt *(Form a,*HSt)) [BodyTag] | gHTML a
Spreadsheet defined in Monadic Style • spreadsheet • = table_hv_Form (nFormId "table") (inittable 8 10) >>= \table • vertlistForm (ndFormId "rsum") (rowsum table.value) >>= \rowsum • horlistForm (ndFormId "csum") (colsum table.value) >>= \colsum • mkEditForm (ndFormId "tsum") (sum (rowsum table.value)) >>= \totsum • mkHtmlM "Spreadsheet" • [ H1 [] "Spreadsheet Example: " • , table.form <=> rowsum.form • , colsum.form <=> totsum.form • ] • :: *HStM a :== *HSt -> *(a, *HSt) • (>>=) infixr 5 :: (HStM .a) (.a -> HStM .b) -> HStM .b • mkHtmlM:: String [BodyTag] -> HStM Html
What did we gain so far ? • Advantage • We obtain an editor for free for (almost) any Clean type • No low level Html form handling required • We want to do more • Editable forms which contents depend on value of other forms • Make a complete separation between "data" and "view" • One function that can fulfil all wishes
Overview of the talk • Clean’s generic Graphical Editor Components • Architecture Web applications <–> Architecture normal Clean application • Generation of Html code • Automatic Generation of Html code for any Clean type • Automatic Generation of simple interactive forms for any Clean type • Separating Data from Views allowing to make any kind of form • Implementation • Arrows • Conclusion & Future Research
Separation of Data Model and View Model • Instead of • mkEditForm:: FormId d *HSt *(Form d, *HSt) | gHTML d • We define the more general function • mkViewForm :: FormId d (ViewBimap d v) *HSt *(Form d, *HSt) | gHTML v • :: ViewBimap d v • = { toForm :: d (Maybe v) v // converts data to view domain • , updForm :: Bool v v // update, True when form edited • , fromForm :: Bool v d // back to data domain • , resetForm :: Maybe (v v) // reset view option • }
mkEditForm is just a simple variant of mkViewForm • mkEditForm:: FormId d *HSt (Form d,*HSt) | gHtml d • mkEditForm formid data hst = mkViewForm formid data • { toForm = toFormid • , updForm = \_ d d • , fromForm = \_ d d • , resetForm = Nothing} hst • toFormid d Nothing = d • toFormid d (Just old_d) = old_d
Counter example • counterForm:: FormId a *HSt (Form a,*HSt) | +, -, one, gHtml a • counterForm name i hst = mkViewForm name i counterView hst • counterView = { toForm = \n toFormid (n,down,up) • , updForm = updCounter • , fromForm = \_ (n,_,_) n • , resetForm = Nothing • } • where • updCounter _ (n,Pressed,_) = (n - one,down,up) • updCounter _ (n,_,Pressed) = (n + one,down,up) • updCounter _ else = else • down = LButton (defpixel / 6) "-" • up = LButton (defpixel / 6) "+"
Counter Example • counterExamplePage hst • # (counter1,hst) = counterForm (nFormId "counter1") 0 hst • # (counter2,hst) = counterForm (nFormId "counter2") 0 hst • = mkHtml "Counter Example" • [ H1 [] "Counter Example" • , BodyTag counter1.form • , BodyTag counter2.form • , toHtml (counter1.value + counter2.value) • ] hst
mkViewForm variants: mkSelfForm for self correcting forms • mkSelfForm:: FormId d (d d) *HSt (Form d,*HSt) | gHtml d • mkSelfForm formid initdata cbf hst = mkViewForm formid data • { toForm = toFormid • , updForm = update • , fromForm = \_ d d • , resetForm = Nothing} hst • where • update True newval = cbf newval • update _ val = val
Self Balancing Tree • selfBalancingTreePage hst • # (balancedtree,hst) = mkSelfForm (nFormId "BalancedTree") • (fromListToBalTree [0]) balanceTree hst • = mkHtml "Balanced Tree" • [ H1 [] "Balanced Tree" • , BodyTag balancedtree.form • ] hst
mkViewForm variants: a Store • mkStoreForm:: FormId d (d d) *HSt (Form d,*HSt) | gHtml d • mkStoreForm formid data cbf hst • = mkViewForm formid data { toForm = toFormid • , updForm = \_ d cbf d • , fromForm = \_ d d • , resetForm = Nothing} hst
Page Visitors Counter • pageCount hst • # (pagecount,hst) = mkStoreForm (pFormId "pageCount") 0 (\n inc n) hst • = mkHtml "Balanced Tree" • [ H1 [] "Page Count" • , Txt ("You are visitor nr: " +++ toString pagecount.value) • ] hst • pdFormId s = {id = s, lifespan = Persistent, mode = Display}
Types with special views • :: Button • = Pressed // button pressed • | LButton Int String // label button, size in pixels, label of button • | PButton (Int,Int) String // picture button, (height,width), ref to picture • :: CheckBox • = CBChecked String // checkbox checked • | CBNotChecked String // checkbox not checked • :: RadioButton • = RBChecked String // radiobutton checked • | RBNotChecked String // radiobutton not checked • :: PullDownMenu • = PullDown (Int,Int) (Int,[String]) // pulldownmenu (number visible,width) • (item chosen,menulist)
Calculator • TableFuncBut:: FormId [[(Button, a a)]] *HSt (Form (a a) ,*HSt) • calcbuttons :: [[(Button, (Int,Int) (Int,Int)]] • calcbuttons = [ [(but "7",set 7), (but "8",set 8), (but "9",set 9) ] • , [(but "4",set 4), (but "5",set 5), (but "6",set 6) ] • , [(but "1",set 1), (but "2",set 2), (but "3",set 3) ] • , [(but "0",set 0), (but "C",clear) ] • , [(but "+",app (+)), (but "-",app (-)), (but "*",app (*))] • ] • where • set ni (mem,i) = (mem, i*10 + ni) • clear (mem,i) = (mem,0) • app fun (mem,i) = (fun mem i , 0) • but i = LButton (defpixel / 3) i
Calculator • calculator hst • # (calcfun,hst) = TableFuncBut (nFormId "calcbut") calcbuttons hst • # (display,hst) = mkStoreForm (ndFormId "display") (0,0) calcfun.value hst = mkHtml "Calculator" • [ H1 [] "Calculator Example: " • , mem display <.||.> input display • , toBody calcfun • ] hst • where • mem display = toHtml (fst (display.value)) • input display = toHtml (snd (display.value))
Simple Login Administration (I) • :: Login • = { loginName :: String • , password :: String • } • loginHandler hst • # (login,hst) = mkEditForm (sFormId "login") (mklogin "" "") hst • # (loginDatabase,hst) = loginStore id hst • # (page,hst) = if (isMember login.value loginDatabase.value) • (memberPage login hst) • (loginPage login hst) • = mkHtml "login test" • [BodyTag page] hst • where • loginStore upd hst = mkStoreForm (pFormId "logindatabase") [] upd hst • mklogin name password = { loginName = name, password = password}
Simple Login Administration (II) • loginPagelogin hst • # (addlogin,hst) = addLoginButton login.value hst • # (loginDatabase,hst) = loginStore (addLoginlogin.valueaddlogin.changed) hst • | isMember login.value loginDatabase.value = memberPage login hst • = ( [ Txt "Please log in ..." • , Br, Br • , BodyTag login.form • , Br • , BodyTag addlogin.form • ], hst) • addLoginButton value hst = ListFuncBut False (formid "addlogin") addbutton hst • addbutton = [ (LButton defpixel "addLogin", id)] • formid = if (value.loginName <> "" && value.password <> "") nFormId ndFormId
Simple Login Administration (III) • memberPagelogin hst • = ( [ Txt ("Welcome " +++ login.value.loginName) • ], hst)
Overview of the talk • Clean’s generic Graphical Editor Components • Architecture Web applications <–> Architecture normal Clean application • Generation of Html code • Automatic Generation of Html code for any Clean type • Automatic Generation of simple interactive forms for any Clean type • Separating Data from Views allowing to make any kind of form • Implementation • Arrows • Conclusion & Future Research
Architecture Clean application for the Web Browser Web Server File Server Clean Application Html Code + Serialized State of Forms Serialized State of Forms + Changed input Serialized State of Forms