Mercurial > gui
changeset 16:2e397d373289
Add RemotePager.
author | Karn Kallio <kkallio@eka> |
---|---|
date | Thu, 18 Aug 2011 12:53:17 -0430 |
parents | 8300d5f0dc19 |
children | 2947170fcfd6 |
files | examples/remotePager.ur examples/remotePager.urp examples/remotePager.urs lib.urp remotePager.ur remotePager.urs |
diffstat | 6 files changed, 287 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/remotePager.ur Thu Aug 18 12:53:17 2011 -0430 @@ -0,0 +1,30 @@ +fun getPage pg = + return {Content = <xml><h2>This is page {[pg]}.</h2></xml>, + Available = 44} + +structure Pager = RemotePager.Make(struct + type errorMarker = {} + type pageGroup = {} + val initPage = fn _ => getPage 0 + val getPage = fn _ pg => + tm <- now; + if mod (toSeconds tm) 5 = 0 then + return (RemotePager.Bad ()) + else + p <- getPage pg; + return (RemotePager.Good p) + end) + +fun main () = + + pager <- Pager.create (); + + return + <xml> + <head><title>RemotePager Example</title></head> + <body onload={Pager.onError pager (fn _ => alert "bad found")}> + <h1>RemotePager Example</h1> + <div>{Pager.panelXml pager}</div> + <div>{Pager.ctlXml pager}</div> + </body> + </xml>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/remotePager.urp Thu Aug 18 12:53:17 2011 -0430 @@ -0,0 +1,7 @@ +path META=../../meta +library ../ +rewrite url RemotePager/* +allow url http://* +prefix http://localhost:8080/ + +remotePager
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/remotePager.urs Thu Aug 18 12:53:17 2011 -0430 @@ -0,0 +1,1 @@ +val main : {} -> transaction page
--- a/lib.urp Sun Aug 07 14:38:52 2011 -0400 +++ b/lib.urp Thu Aug 18 12:53:17 2011 -0430 @@ -15,3 +15,4 @@ popupNav navigation clock +remotePager
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/remotePager.ur Thu Aug 18 12:53:17 2011 -0430 @@ -0,0 +1,170 @@ +type formatCtl = {Width : int, + RangeCtl : $(mapU (transaction {} -> xbody) [First, Back, Next, Last]), + PageCtl : $(mapU (int -> transaction {} -> xbody) [Unsel, Sel]), + WrapPageCtl : xbody -> xbody, + Wrap : $(mapU xbody [First, Back, Pages, Next, Last]) -> xbody} + +style firstCtl +style backCtl +style nextCtl +style lastCtl +style curPage +style rangeCtl + +fun defaultFormatLbl {First = fLbl, Back = bLbl, Next = nLbl, Last = lLbl} = + let + fun mkLink c txt = fn a => <xml><li onclick={a} class={c}><a class={c} onclick={return ()}>{[txt]}</a></li></xml> + in + {Width = 5, + RangeCtl = {First = mkLink firstCtl fLbl, + Back = mkLink backCtl bLbl, + Next = mkLink nextCtl nLbl, + Last = mkLink lastCtl lLbl}, + PageCtl = {Unsel = fn n a => <xml><li onclick={a}><a onclick={return ()}>{[n + 1]}</a></li></xml>, + Sel = fn n a => <xml><li onclick={a} class={curPage}><a class={curPage} onclick={return ()}>{[n + 1]}</a></li></xml>}, + WrapPageCtl = fn x => x, + Wrap = fn {First = f, Back = b, Pages = p, Next = n, Last = l} => <xml><ul class={rangeCtl}>{f}{b}{p}{n}{l}</ul></xml>} + end + +val defaultFormat = defaultFormatLbl {First = "<<", Back = "<", Next = ">", Last = ">>"} + +type pageData = {Content : xbody, Available : int} +datatype response err = Good of pageData | Bad of err + +functor Make(M : sig + type errorMarker + type pageGroup + val initPage : pageGroup -> transaction pageData + val getPage : pageGroup -> int -> transaction (response errorMarker) + end) : sig + type t + val createFmt : formatCtl -> M.pageGroup -> transaction t + val create : M.pageGroup -> transaction t + val onError : t -> (M.errorMarker -> transaction {}) -> transaction {} + val panelXml : t -> xbody + val ctlXml : t -> xbody +end = struct + open M + + type t = {FormatCtl : formatCtl, + PageGroup : pageGroup, + OnError : source (errorMarker -> transaction {}), + CurrentPage : source int, + RangeStart : source int, + Available : source int, + Content : source xbody} + + fun max x y = if x > y then x else y + fun min x y = if x < y then x else y + + fun loadPage t pg = + ret <- rpc (getPage t.PageGroup pg); + + case ret of + Good pageData => + let + val lst = max 0 (pageData.Available - 1) + val rgtRange = max 0 (pageData.Available - t.FormatCtl.Width) + in + if pg > lst then + loadPage t lst + else + rg <- get t.RangeStart; + (if rg > rgtRange then + set t.RangeStart rgtRange + else + return ()); + set t.CurrentPage pg; + set t.Content pageData.Content; + set t.Available pageData.Available + end + | Bad e => + handler <- get t.OnError; + handler e + + fun createFmt fmt grp = + pg <- source 0; + rg <- source 0; + + pageData <- initPage grp; + + av <- source (pageData.Available); + c <- source (pageData.Content); + + e <- source (fn _ => return {}); + + return {FormatCtl = fmt, + PageGroup = grp, + OnError = e, + CurrentPage = pg, + RangeStart = rg, + Available = av, + Content = c} + + val create = createFmt defaultFormat + + fun onError t f = + set t.OnError f + + fun panelXml t = + <xml> + <dyn signal={signal t.Content}/> + </xml> + + fun ctlXml t = + let + val fmt = t.FormatCtl + val width = fmt.Width + + fun floor pg = + return (max 0 pg) + + fun lastPage () = + av <- get t.Available; + floor (av - 1) + + fun rightRange () = + lst <- lastPage (); + floor (lst - width + 1) + + fun ceil pg = + rgt <- rightRange (); + return (min rgt pg) + + fun setRange startPage = + startPage <- ceil startPage; + startPage <- floor startPage; + set t.RangeStart startPage + + fun printLinks start cur available = + let + val wanted = start + width - 1 + val have = max 0 (available - 1) + val lst = min wanted have + + fun printLinks' n (acc : xbody) = + if n > lst then + acc + else + printLinks' (n + 1) + <xml>{acc}{(if n = cur then + fmt.PageCtl.Sel + else + fmt.PageCtl.Unsel) n (loadPage t n)}</xml> + in + printLinks' start <xml/> + end + in + fmt.Wrap {First = fmt.RangeCtl.First (setRange 0), + Back = fmt.RangeCtl.Back (pg <- get t.RangeStart; setRange (pg - 1)), + Pages = fmt.WrapPageCtl + <xml> + <dyn signal={start <- signal t.RangeStart; + av <- signal t.Available; + cur <- signal t.CurrentPage; + return (printLinks start cur av)}/> + </xml>, + Next = fmt.RangeCtl.Next (pg <- get t.RangeStart; setRange (pg + 1)), + Last = fmt.RangeCtl.Last (pg <- rightRange (); setRange pg)} + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/remotePager.urs Thu Aug 18 12:53:17 2011 -0430 @@ -0,0 +1,78 @@ +(* How to format the control section. *) +type formatCtl = {Width : int, + (* Maximum number of page options to show. *) + RangeCtl : $(mapU (transaction {} -> xbody) [First, Back, Next, Last]), + (* A group of four functions taking transactions which will be used to return + * an element having that transaction as onclick. Used to format the buttons + * controlling the selection of page options. First starts the options at the + * first page, Back starts the options one page lower, Next one page higher + * and Last has them finish at the final page. *) + PageCtl : $(mapU (int -> transaction {} -> xbody) [Unsel, Sel]), + (* Used to generate the xml bearing the page selection onclick action. Sel + * is used for the currently shown page and Unsel for the others. *) + WrapPageCtl : xbody -> xbody, + (* The sequence of page selection controls will be wrapped by this. *) + Wrap : $(mapU xbody [First, Back, Pages, Next, Last]) -> xbody + (* Draws the control by placing the individual controls in the holes. *)} + +style firstCtl +style backCtl +style nextCtl +style lastCtl +style curPage +style rangeCtl +(* Default styles. *) + +val defaultFormat : formatCtl +(* A reasonable default format. *) + +val defaultFormatLbl : $(mapU string [First, Back, Next, Last]) -> formatCtl +(* A default format with configurable labels on the page range controls. *) + +type pageData = {Content : xbody, Available : int} +(* Content and page count needed from the RPC. *) + +datatype response err = Good of pageData | Bad of err +(* The RPC gives us either page information or an error code. *) + +functor Make(M : sig + type errorMarker + (* Classifies the possible error conditions returned by the rpc call. *) + + type pageGroup + (* Classifies families of pages. *) + + val initPage : pageGroup -> transaction pageData + (* Used once to initialize the first page and page count. + * The RPC mechanism is not used and no error is possible. *) + + val getPage : pageGroup -> int -> transaction (response errorMarker) + (* RPC giving the content of a requested page as well as the count + * of how many pages are available. The first page should be given + * an index of 0. *) + end) : sig + + type t + (* The type of remote pagers. A remote pager is a + * widget which shows a list of available "pages" of + * information. The user can click on a page they want + * displayed, which is then fetched from the server via + * a RPC. *) + + val createFmt : formatCtl -> M.pageGroup -> transaction t + (* Get a remote pager widget with a custom format showing pages from the selected group. *) + + val create : M.pageGroup -> transaction t + (* Get a remote pager widget with the default format (partly customizable via CSS). *) + + val onError : t -> (M.errorMarker -> transaction {}) -> transaction {} + (* Allows setting an error handler. In case of an rpc error code return, this will + * be called with the received error marker. If not set, the default error handler + * does nothing. *) + + val panelXml : t -> xbody + (* Returns a piece of xml holding the contents of the current page. *) + + val ctlXml : t -> xbody + (* Returns a xml widget representing the clickable page selection controls. *) +end