con ui widget value = {Render : widget -> xbody, Value : widget -> signal value} fun mkUi [widget] [value] (x : ui widget value) = x fun render [widget] [value] (u : ui widget value) (x : widget) = u.Render x fun value [widget] [value] (u : ui widget value) (x : widget) = u.Value x con input _ = source string fun ui_input [t] (_ : read t) = mkUi {Render = fn s => <xml><ctextbox source={s}/></xml>, Value = fn s => v <- signal s; return (read v)} val input [t] = source "" con select t = {Options : list t, Source : source string} fun ui_select [t] (_ : show t) = mkUi {Render = fn s => <xml><cselect source={s.Source}> {List.mapXi (fn i x => <xml><coption value={show i}>{[x]}</coption></xml>) s.Options} </cselect></xml>, Value = fn s => n <- signal s.Source; case List.nth s.Options (readError n) of None => error <xml>Invalid option</xml> | Some x => return x} fun select [t] (options : list t) = s <- source "0"; return {Options = options, Source = s}