123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118(* This file is part of BOGUE, by San Vu Ngoc *)(* Contrary to SDL events (which are more like triggers than messages),
mailboxes can receive any kind of semantic events. For instance:
[send mbx (`Open_directory "/home")]
This small messaging API is proposed as a convenience for the reader, but no
Bogue widget relies on it (and in fact, even for the example above it is
always possible to create controller widgets without using mailboxes, see the
tutorial
https://sanette.github.io/bogue-tutorials/bogue-tutorials/modif_parent.html
)
On the other hand, in more complicated cases, using mailboxes is cleary
easier and reduces the boilerplate.
WARNING: API not stabilized yet
*)openB_utilsmoduleSync=B_syncmoduleTrigger=B_triggermoduleUpdate=B_updatemoduleVar=B_varmoduleW=B_widgettype'ambx={owner:W.t;(* Only the owner widget should be authorized to read the mail *)queue:('alist)Var.t;mutableactive:bool;(* if not active, sending mail will be loggued as an error, but the messages
are still stored in the queue and can be read once the mailbox is
activated.*)}(* Create a mailbox for widget [owner] whose messages are of type ['a]. *)letcreate?owner()=letowner=matchownerwith|Somewidget->widget|None->letw=W.empty~w:0~h:0()inprintd(debug_board)"Creating an empty Widget #%u for hosting new Mailbox"(W.idw);win{owner;queue=Var.create[];active=false}(* By default, "the mailman delivers at each frame": messages are handled one by
one in the Sync queue, in the order of reception (FIFO). Setting [sync=false]
will on the contrary execute the handler in a separate thread. (Only one
thread for all messages.) *)letactivate?(sync=true)mbxhandler=letc=W.connectmbx.ownermbx.owner(fun__ev->ifmbx.activethenbeginmatchTrigger.event_kindevwith|`Bogue_new_mail->letwid=Trigger.E.(getevuser_code)inifwid<>W.idmbx.ownerthenbeginprintd(debug_error)"Event sent by widget #%u trying to read a mailbox belonging to #%u!"wid(W.idmbx.owner)endelsebeginletmessages=List.rev(Var.getmbx.queue)inVar.setmbx.queue[];ifsyncthenList.iter(funmsg->Sync.push(fun()->handlermsg))messageselseList.iterhandlermessagesend|_->print_endline"OTHER EV";printddebug_error"Reading a mailbox should be triggered only by \
the Bogue_new_mail event. "endelseprintd(debug_warning+debug_event)"Mailbox #%u is currently inactive. Please re-enable it to handle \
incoming mails"(W.idmbx.owner))[Trigger.new_mail]inW.add_connectionmbx.ownerc;mbx.active<-trueletsendmbxmsg=Var.updatembx.queue(List.consmsg);Trigger.push_new_mail(W.idmbx.owner);printd(debug_user)"Message was sent to the (active=%b) mailbox #%u."mbx.active(W.idmbx.owner)(* We should manually feed the widget with the event, because the widget may not
have focus and hence is not reached by the usual connection strategy. (Like
we do for the update event.) *)letreach_widgetev=letwid=Trigger.(E.getevwidget_id)intryletw=W.of_idwidinW.wake_up_allevwwithNot_found->printddebug_error"The mailbox widget #%u has disappeared."widletenablembx=mbx.active<-true;Trigger.push_new_mail(W.idmbx.owner)letdisablembx=mbx.active<-falseletclearmbx=Var.updatembx.queue(function|[]->[]|_->printddebug_user"Clearing the Mailbox #%u"(W.idmbx.owner);[])