From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail4-relais-sop.national.inria.fr (mail4-relais-sop.national.inria.fr [192.134.164.105]) by walapai.inria.fr (8.13.6/8.13.6) with ESMTP id q1E0arC7024036 for ; Tue, 14 Feb 2012 01:36:53 +0100 X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: AhUBADyrOU/RVdS2kGdsb2JhbABDnx40iBkBiDAIIgEBAQEJCQ0HFAQjgXIBAQEDARICExkBGxILAQMBCwYFCw0NISEBAREBBQEKEgYTEhCHWgmcFwqLcYJwhSc/iHMCBQuIOYMKKwUFHx4Ig32EJASCXIVujGiLEIMVPYQi X-IronPort-AV: E=Sophos;i="4.73,414,1325458800"; d="scan'208";a="131217254" Received: from mail-wi0-f182.google.com ([209.85.212.182]) by mail4-smtp-sop.national.inria.fr with ESMTP/TLS/RC4-SHA; 14 Feb 2012 01:36:47 +0100 Received: by wibhn14 with SMTP id hn14so6390358wib.27 for ; Mon, 13 Feb 2012 16:36:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; bh=q76s1rkBgBX+ljome8kge/VaNZJYzuSBK3BQYX94eX4=; b=Xy3hi+TpGTWb43ivf5kghW0ZK+f1Y0TGUE2G1xqLA2dodyxLINaw5TeOZt9vAMxXHo 3dSG9rZbumu4J8yzJJQwDHVtkl5+5W/HSlOlCfbPS5L96kEHFyIcplNnneeyEApmWRL9 ETI7RAI4OZRo6EW8LABs/MKauM2nj/20NIla0= MIME-Version: 1.0 Received: by 10.216.139.9 with SMTP id b9mr7030207wej.23.1329179807285; Mon, 13 Feb 2012 16:36:47 -0800 (PST) Received: by 10.223.7.69 with HTTP; Mon, 13 Feb 2012 16:36:47 -0800 (PST) In-Reply-To: References: Date: Mon, 13 Feb 2012 17:36:47 -0700 Message-ID: From: Anthony Tavener To: Raoul Duke Cc: Philippe Veber , caml users Content-Type: multipart/alternative; boundary=0016e6da7d7641802604b8e1ca97 Subject: Re: [Caml-list] Functional GUI programming: looking for good practices --0016e6da7d7641802604b8e1ca97 Content-Type: text/plain; charset=ISO-8859-1 I've been taking a stab at this too; for a video game. The approach I'm taking is with delimited continuations (using Oleg's delimcc). This allows me to run UI code as a coroutine. The UI code then looks like any other code, where user inputs are results from a function. Really, the UI code stops, waiting for input (while the mainline coroutine runs), then returns the input value. An example: let act = user (menu_of_list [ ("Equip",`Equip); ("Spell",`Spell); ("End",`End) ] ) in match act with | Some `Spell -> try let spellList = (* build a list of casting options for this character *) in let spell = ?.(user (menu_of_list spellList)) in let target = ?.(user (select (target_filter spell) scene)) in Cast.cast id spell target with OptionNone -> () This example creates a small menu of special options with the given string labels "Equip", etc. The "user" function is a yield of the UI coroutine, which means we await resuming with the input value. Interacting with GUI elements is handled by querying the scene or OpenGL state based on SDL input events. In this case clicking on the created "Spell" menu item will return a value of Some `Spell. Later in the code, more user interactions are handled for providing specific spell options and then targets. (The "?." prefix operator is just turning None values into exceptions, to implement user abort.) I have a yield function, using Delimcc's shift and abort: let yield level fn = shift level (fun k -> fn k; abort level () ) which is the basis of the 'user' function in the UI: let user fn = yield ui_process fn So far, I'm not sure how well this works out for a complete project. I like it so far, but I have complexity growing in some "update loop" stuff, which are little closures I add to be run each frame-update for reacting to mouse-over/hover. I've tried dabbling with FRP, but it kept getting hairy too. I will probably use it to replace my kludge of "update loop" closures. Someday, someone will figure out something slick, whether it's a way to use these tools, or something new. I'm at least happy not using "signals and slots"! Good luck in the hunt for elegant UI (code)! Tony Inside the UI code, the 'process' delimits the UI coroutine On Mon, Feb 13, 2012 at 11:13 AM, Raoul Duke wrote: > On Mon, Feb 13, 2012 at 3:01 AM, Philippe Veber > wrote: > > than expected, for example with layout management. In order to compute a > > layout for the widgets, some information has to travel bottom up the > widget > > hierarchy, and some goes top down. While there is a well-founded order > for > > academic thoughts from others: > > http://lambda-the-ultimate.org/node/2913 > > -- > Caml-list mailing list. Subscription management and archives: > https://sympa-roc.inria.fr/wws/info/caml-list > Beginner's list: http://groups.yahoo.com/group/ocaml_beginners > Bug reports: http://caml.inria.fr/bin/caml-bugs > > --0016e6da7d7641802604b8e1ca97 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable I've been taking a stab at this too; for a video game. The approach I&#= 39;m taking is with delimited continuations (using Oleg's delimcc). Thi= s allows me to run UI code as a coroutine. The UI code then looks like any = other code, where user inputs are results from a function. Really, the UI c= ode stops, waiting for input (while the mainline coroutine runs), then retu= rns the input value.

An example:

let act =3D user (= menu_of_list
=A0 [ ("Equip",`Equip);
=A0 =A0 = ("Spell",`Spell);
=A0 =A0 ("End",`End) ] ) in=

match act with
| Some `Spell ->
=A0 = =A0 try
=A0 =A0 =A0 let spellList =3D (* build a list of casting = options for this character *) in
=A0 =A0 =A0 let spell =3D ?.(use= r (menu_of_list spellList)) in
=A0 =A0 =A0 let target =3D ?.(user (select (target_filter spell) scene= )) in
=A0 =A0 =A0 Cast.cast id spell target
=A0 =A0 wit= h OptionNone -> ()

This example creates a small= menu of special options with the given string labels "Equip", et= c. The "user" function is a yield of the UI coroutine, which mean= s we await resuming with the input value. Interacting with GUI elements is = handled by querying the scene or OpenGL state based on SDL input events. In= this case clicking on the created "Spell" menu item will return = a value of Some `Spell.

Later in the code, more user interactions are handled f= or providing specific spell options and then targets. (The "?." p= refix operator is just turning None values into exceptions, to implement us= er abort.)

I have a yield function, using Delimcc's shift and abort= :

let yield level fn =3D
=A0 shift = level (fun k ->
=A0 =A0 fn k;
=A0 =A0 abort level ()= )

which is the basis of the 'user' function in th= e UI:

=A0 let user fn =3D yield ui_process fn

So far, I'm not sure how well this works out for a= complete project. I like it so far, but I have complexity growing in some = "update loop" stuff, which are little closures I add to be run ea= ch frame-update for reacting to mouse-over/hover.

I've tried dabbling with FRP, but it kept getting h= airy too. I will probably use it to replace my kludge of "update loop&= quot; closures. Someday, someone will figure out something slick, whether i= t's a way to use these tools, or something new. I'm at least happy = not using "signals and slots"!

Good luck in the hunt for elegant UI (code)!
=
=A0Tony

Inside the UI code, the = 9;process' delimits the UI coroutine

--0016e6da7d7641802604b8e1ca97--