Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file object.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251(*********************************************************************************)(* OCaml-Stk *)(* *)(* Copyright (C) 2023-2024 INRIA All rights reserved. *)(* Author: Maxence Guesdon, INRIA Saclay *)(* *)(* This program is free software; you can redistribute it and/or modify *)(* it under the terms of the GNU General Public License as *)(* published by the Free Software Foundation, version 3 of the License. *)(* *)(* This program is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* GNU General Public License for more details. *)(* *)(* You should have received a copy of the GNU General Public *)(* License along with this program; if not, write to the Free Software *)(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA *)(* 02111-1307 USA *)(* *)(* As a special exception, you have permission to link this program *)(* with the OCaml compiler and distribute executables, as long as you *)(* follow the requirements of the GNU GPL in regard to all of the *)(* software in the executable aside from the OCaml compiler. *)(* *)(* Contact: Maxence.Guesdon@inria.fr *)(* *)(*********************************************************************************)(** Objects.
Class {!class-o} is the base class for widget and other classes which
can trigger events and hold properties {!Props.props}.
*)(** Introduce the [Prop_changed] property to register callbacks called
when the value of a given property changed. Previous and current
values of the property are passed to the callback. *)type_Events.ev+=|Prop_changed:'aProps.prop->(prev:'aoption->now:'a->unit)Events.ev(** Object id. Each object of {!class-o} is given a unique id. *)typeid=Oid.t(** Transition step minimum delay, in milliseconds. *)lettransition_step_delay=50(** An object. See {!Widget.widget_arguments} for [props] arguments.
Registered callbacks are unregistered when object is destroyed.
*)classo?props()=letprops=matchpropswithNone->Props.create()|Somep->pinletid=Oid.gen()inobject(self)(** The unique id of the object. *)methodid=id(** Coercion to {!class-o}. *)methodas_o=(self:>o)(** Returns a string representation of the object's name and id.*)methodme=Printf.sprintf"[%s]"(Oid.to_stringself#id)(**/**)valcallbacks=Events.callbacks()valmutableprops=Props.duppropsvalmutabledelayed_props:unitLwt.uProps.Map.t=Props.Map.empty(**/**)(** Returns the object properties. This is not a copy and should
not be modified directly except if you know what you're doing.
To set a property value, use the [set_*] methods provided by
inheriting classes. If no such method is available for a
property, you can use the [set_p] method below.*)methodprops=props(** [o#get_p p] returns value of property [p] in properties of [o].
If no value for [p] is set in [o] and [p] has no default value,
then {!Misc.type-error.Missing_prop} is raised.
*)methodget_p:'a.'aProps.prop->'a=funp->Props.getpropsp(** [o#opt_p p] returns value of property [p] in properties of [o],
if such a value is set. *)methodopt_p:'a.'aProps.prop->'aoption=funp->Props.optpropsp(** [o#set_p p v] sets value [v] to property [p] in properties of [o].
Optional argument [propagate] (defaults: [false]) can be used
to indicate that this change must be propagated to children objects.
Optional argument [delay] can be used to specify a transition delay (in seconds)
if the property has a transition function. Default is no delay.
Class {!class-o} has no children, but inheriting classes may have and
will override this method to implement propagation.
The [Prop_changed] event is triggered if the value of [p] changed
(properties are defined with a comparison function, allowing
to detect a change).
*)methodset_p:'a.'aProps.prop->?delay:float->?propagate:bool->'a->unit=funp?delay?(propagate=false)v->matchdelaywith|None->(matchProps.updatepropspvwith|None->(* no change *)()|Someprev->try[%debug"%s property %S updated => %a (propagate=%b)"self#me(Props.namep)(Props.pp_propp)vpropagate];self#on_prop_changedp~prevv;withNot_found->Log.err(funm->m"Not found! %s"(Printexc.get_backtrace())))|Somedelay->(* if we have no transition fun we a delay is required,
create a dummy transition fun and set a transition step
equal to the delay. *)lettrans,transition_step_delay=matchProps.transitionpwith|Somef->f,transition_step_delay|None->(fun~start~stopr->ifr>=1.thenstopelsestart),(truncate(delay*.1000.))in(matchProps.Map.find_optpdelayed_propswith|None->()|Someresolver->delayed_props<-Props.Map.removepdelayed_props;Lwt.wakeupresolver());letstart=self#get_ppinletstop=vinletstart_t=Tsdl.Sdl.get_ticks()inletdelay=delay*.1000.inletwaiter,resolver=Lwt.wait()indelayed_props<-Props.Map.addpresolverdelayed_props;letrecf()=let%lwt()=Lwt_unix.sleep(floattransition_step_delay/.1000.)inmatchLwt.statewaiterwith|Return_|Fail_->Lwt.return_unit|Sleep->lett=Tsdl.Sdl.get_ticks()inletr=min1.0(Int32.(to_float(subtstart_t))/.delay)inletv=trans~start~stoprinself#set_pp~propagatev;ifr<1.thenf()else(delayed_props<-Props.Map.removepdelayed_props;Lwt.return_unit)inLwt.asyncf(**/**)methodon_prop_changed:'a.'aProps.prop->prev:'aoption->'a->unit=funp~prevnow->letcbs=Events.getcallbacks(Prop_changedp)inList.iter(funcb->cb~prev~now)cbs(**/**)(** [o#set_props ?propagate props] calls [o#set_p ?propagate] for
each pair (property, value) in [props]. *)methodset_props?delay?propagateprops=Props.fold(funpv()->self#set_p?delay?propagatepv)props()(**/**)valmutablecallback_ids=([]:Events.callback_idlist)(**/**)(** [o#connect event cb] registers [cb] to be called when [event]
is triggered by [o]. Returns a callback id which can be used
to unregister the callback, either with {!method-disconnect} or with
{!Events.unregister}.
Optional parameter [count] indicates the number of times the
callback is called before being unregistered. Default is [None],
i.e. callback is not unregistered.
*)methodconnect:'a.?count:int->'aEvents.ev->'a->Events.callback_id=fun?countecb->letid=Events.registercallbacks?countecbincallback_ids<-id::callback_ids;id(** [disconnect id] unregisters the callback with the given [id]. *)methoddisconnect=Events.unregister(**/**)methodtrigger_event:'a.('a->bool)Events.ev->'a->bool=funevv->[%debug"%s#trigger_event %s"self#me(Printexc.to_string(Obj.magicev))];matchEvents.getcallbacksevwith|[]->false|cbs->List.fold_left(funacccb->letb=cbvin(b||acc))falsecbsmethodtrigger_event_unit:'a.('a->bool)Events.ev->'a->unit=funevv->ignore(self#trigger_eventevv)methodtrigger_unit_event:'a.('a->unit)Events.ev->'a->unit=funevv->matchEvents.getcallbacksevwith|[]->()|cbs->List.iter(funf->tryfvwithe->Log.err(funm->m"%s"(Printexc.to_stringe)))cbsmethoddestroy=List.iterEvents.unregistercallback_ids(*
initializer
prerr_endline (Printf.sprintf "object %s created" (Oid.to_string id));
let str = self#me in
Gc.finalise_last
(fun () -> prerr_endline (Printf.sprintf "object %s finalised" str)
(*List.iter Events.unregister callback_ids*))
self
*)end(** [add_mirror ~src:(object1,prop1) ~dst:(object2,prop2) ~map_to ~map_from]
registers event handlers to that a change on [prop1] in [object1] triggers a
change of propery [prop2] of [object2], using [map_to] to map the new value of
[prop1] to the value given to [prop2], and conversely (using [map_from]).
The function returns the event handlers on [object1] and [object2], so that
this mirror can be removed using [object1#disconnect id1; object2#disconnect id2].
*)letadd_mirror:src:(o*'aProps.prop)->dst:(o*'bProps.prop)->map_to:('a->'b)->map_from:('b->'a)->(Events.callback_id*Events.callback_id)=fun~src:(o1,p1)~dst:(o2,p2)~map_to~map_from->letf_from~prev:_~now=letv=map_fromnowino1#set_pp1vinletid_from=(o2)#connect(Prop_changedp2)f_frominletf_to~prev:_~now=letv=map_tonowino2#set_pp2vinletid_to=(o1)#connect(Prop_changedp1)f_toin(id_to,id_from)(** Same as {!add_mirror}, using [Fun.id] as mappers, i.e. when both properties
have the same time and meaning. *)letadd_mirror_id~src~dst=add_mirror~src~dst~map_to:Fun.id~map_from:Fun.id