Mailing list for all users of the OCaml language and system.
 help / color / mirror / Atom feed
From: Anthony Tavener <anthony.tavener@gmail.com>
To: Raoul Duke <raould@gmail.com>
Cc: Philippe Veber <philippe.veber@gmail.com>,
	caml users <caml-list@inria.fr>
Subject: Re: [Caml-list] Functional GUI programming: looking for good practices
Date: Mon, 13 Feb 2012 17:36:47 -0700	[thread overview]
Message-ID: <CAN=ouMTQCqrvXF6iGu_a0HQr+qB4NobXbDwESQdj0LYM2bPyAw@mail.gmail.com> (raw)
In-Reply-To: <CAJ7XQb49oD8dFBaPKC69rb4Q3n8A3eAyb9Djsu+J7w2ey7+cwg@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 2993 bytes --]

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 <raould@gmail.com> wrote:

> On Mon, Feb 13, 2012 at 3:01 AM, Philippe Veber
> <philippe.veber@gmail.com> 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
>
>

[-- Attachment #2: Type: text/html, Size: 4334 bytes --]

  reply	other threads:[~2012-02-14  0:36 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-02-13 11:01 Philippe Veber
2012-02-13 15:27 ` Adrien
2012-02-14 10:02   ` Philippe Veber
2012-02-14 10:21     ` Daniel Bünzli
2012-02-14 10:39       ` Philippe Veber
2012-02-14 11:52         ` Adrien
2012-02-14 13:00           ` Daniel Bünzli
2012-02-14 13:29     ` Adrien
2012-02-13 18:13 ` Raoul Duke
2012-02-14  0:36   ` Anthony Tavener [this message]
2012-02-14 10:17     ` Philippe Veber
2012-02-14 18:02       ` Anthony Tavener
2012-02-15  4:47         ` Anthony Tavener
2012-02-22 11:57           ` Philippe Veber
2012-02-28 10:10             ` Adrien

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAN=ouMTQCqrvXF6iGu_a0HQr+qB4NobXbDwESQdj0LYM2bPyAw@mail.gmail.com' \
    --to=anthony.tavener@gmail.com \
    --cc=caml-list@inria.fr \
    --cc=philippe.veber@gmail.com \
    --cc=raould@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox