Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file container.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418(*********************************************************************************)(* 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 *)(* *)(*********************************************************************************)(** Container widget. *)openMiscopenTsdlopenWidget(** Type of data which can be associated to child widgets. *)typechild_data=..(** A container child, with optional data associated.*)typechild={widget:Widget.widget;mutabledata:child_dataoption;}(** A container widget is a widget which can contain other widgets.
The type parameter is the type of the position of a widget in the container.
This class must be inherited, as it is for example
by {!class-container_list}, which specialises this class by using
a list to store children widgets.
*)classvirtual['pos]container?classes?name?props?wdata()=object(self)inheritwidget?classes?name?props?wdata()assuper(**/**)valmutablepacked_widgets=Oid.Set.emptymethodprivatevirtualchild_by_widget:Widget.widget->childoptionmethodprivatevirtualfind_child_opt:(child->bool)->childoptionmethodprivatevirtualfind_child_pos_opt:(child->bool)->'posoptionmethodprivatevirtualiter_children:(child->unit)->unitmethodprivatevirtualfold_children_right:'b.(child->'b->'b)->'b->'bmethodprivatevirtualfold_children_left:'b.('b->child->'b)->'b->'bmethodprivatevirtualadd_to_children:?pos:'pos->child->unitmethodprivatevirtualremove_from_children:Widget.widget->boolmethodprivatechild_by_widget(w:Widget.widget)=letid=w#idinself#find_child_opt(func->Oid.equalc.widget#idid)methodprivatechild_by_coords~x~y=self#find_child_opt(func->c.widget#visible&&G.inside~x~yc.widget#geometry)methodprivatewidget_index(w:Widget.widget)=letid=w#idinself#find_child_pos_opt(func->Oid.equalc.widget#idid)methodprivatewidget_data(w:Widget.widget)=matchself#child_by_widgetwwith|None->None|Somec->c.datamethodprivateset_widget_datawdata=matchself#child_by_widgetwwith|None->Log.warn(funm->m"No widget %s in %s"w#meself#me)|Somec->c.data<-datamethod!set_pp?delay?(propagate=false)v=[%debug"%s#set_p %s ~propagate:%b"self#me(Props.namep)propagate];super#set_p?delay~propagatepv;matchdelay,Props.transitionpwith|Some_,Some_->()|_->ifpropagatethenself#iter_children(func->c.widget#set_p~propagatepv)method!do_apply_theme~root~parentparent_pathrules=super#do_apply_theme~root~parentparent_pathrules;letpath=self#css_path~parent_path()inself#iter_children(func->c.widget#do_apply_theme~root~parent:theme_propspathrules);width_constraints<-None;height_constraints<-Nonemethodprivatefocused_child=self#find_child_opt(func->c.widget#get_pProps.is_focus)method!focused_widget=ifself#is_focusthenmatchself#focused_childwith|None->Someself#coerce|Somec->c.widget#focused_widgetelseNonemethod!release_focus=matchmatchself#focused_childwith|None->true|Somec->c.widget#release_focuswith|true->self#set_pProps.is_focusfalse;self#set_pProps.has_focusfalse;true|_->falsemethod!get_focus=matchsuper#get_focuswith|None->None|Somehas_focus->self#iter_children(func->c.widget#set_pProps.is_focusfalse);Somehas_focusmethod!set_has_focusb=matchsuper#set_has_focusbwith|true->true|false->matchself#focused_childwith|None->false|Somec->c.widget#set_has_focusbmethodprivatevisible_children=self#fold_children_right(funcacc->ifc.widget#visiblethenc.widget::accelseacc)[]methodprivatefold_children_for_sdl_event_down=self#fold_children_leftmethodprivateon_sdl_event_down_stop_on_true=falsemethod!on_sdl_event_down~oldpospose=[%debug"%s#on_sdl_event_down e=%a"self#meFmts.pp_evente];ifself#sensitivethenmatchletf(x,y)=(x-g.x-g_inner.x,y-g.y-g_inner.y)inletoldpos=Option.mapfoldposinletpos=Option.mapfposinmatchSdl.Event.(enum(getetyp))with|`Key_down|`Key_up|`Text_input|`Text_editing->(matchself#find_child_opt(func->c.widget#get_pProps.is_focus)with|None->false|Somec->c.widget#on_sdl_event_down~oldpospose)|_->letstop_on_true=self#on_sdl_event_down_stop_on_trueinself#fold_children_for_sdl_event_down(funaccc->matchacc&&stop_on_truewith|true->acc|false->letw=c.widgetinifw#visible&&((matcholdposwith|Some(x,y)->G.inside~x~yw#geometry|None->false)||matchposwith|Some(x,y)->G.inside~x~yw#geometry|None->false)then([%debug"%s#on_sdl_event_down: propagating event to %s"self#mew#me];letb=w#on_sdl_event_down~oldposposeinacc||b)elseacc)falsewith|true->true|false->self#on_sdl_eventposeelsefalsemethod!child_reparentedw=self#removew;self#need_resizemethodprivateadd?pos?dataw=Log.info(funm->m"%s#add %s"self#mew#me);matchOid.Set.memw#idpacked_widgetswith|true->Log.warn(funm->m"%s is already packed in %s"w#meself#me);false|false->letold_parent=w#parentinmatchold_parentwith|Somepwhenp#equalself#as_widget->false|_->width_constraints<-None;height_constraints<-None;packed_widgets<-Oid.Set.addw#idpacked_widgets;self#add_to_children?pos{widget=w;data};Option.iter(funp->p#child_reparentedw)old_parent;w#set_parent?with_rend:with_renderer(Someself#coerce);truemethodprivateremove(w:widget)=letb=self#remove_from_childrenwinifbthen(packed_widgets<-Oid.Set.removew#idpacked_widgets;width_constraints<-None;height_constraints<-None;w#set_parentNone)elseLog.warn(funm->m"%s is not packed in %s, not removing"w#meself#me);bmethod!set_parent?with_rendw=super#set_parent?with_rendw;self#iter_children(func->c.widget#set_parent?with_rend(Someself#coerce))method!child_need_resizew=matchself#find_child_opt(func->Oid.equalc.widget#idw#id)with|None->Log.warn(funm->m"%s#child_need_resize: %s not in children"self#mew#me);()|Somec->ifw#visiblethenself#need_resizemethod!privaterender_merend~offset:(x,y)rg=letoff_x=g.x+g_inner.xinletoff_y=g.y+g_inner.yinletoffset=(x+off_x,y+off_y)inletrg=G.translate~x:(-off_x)~y:(-off_y)rgin[%debug"%s#render_me x=%d, y=%d, g=%a, g_inner=%a, rg=%a"self#mexyG.ppgG.ppg_innerG.pprg];self#iter_children(func->c.widget#render~offsetrendrg)method!is_leaf_widget=falsemethod!leaf_widget_at~x~y=letx=x-g.x-g_inner.xinlety=y-g.y-g_inner.yinmatchself#child_by_coords~x~ywith|None->None|Somec->c.widget#leaf_widget_at~x~ymethod!destroy=self#iter_children(func->c.widget#destroy);super#destroy;end(* This class specialises {!class-container} to store children widgets in a list.
It must be inherited (as it is for example by {!Box.class-box} or {!Box.class-paned}).
*)classvirtualcontainer_list?classes?name?props?wdata()=object(self)inherit[int]container?classes?name?props?wdata()assuper(**/**)valmutablechildren=([]:childlist)methodprivatechildren=childrenmethodprivatefind_child_optpred=List.find_optpredchildrenmethodprivatefind_child_pos_optpred=List.find_indexpredchildrenmethodprivateiter_childrenf=List.iterfchildrenmethodprivatefold_children_rightfacc=List.fold_rightfchildrenaccmethodprivatefold_children_leftfacc=List.fold_leftfaccchildrenmethodprivatereorder_childwpos=letid=w#idinmatchself#child_by_widgetwwith|None->()|Somechild->letreciteraccp=function|[]->List.revacc|lwhenp=pos->iter(child::acc)(p+1)l|c::q->ifOid.equalc.widget#ididtheniteraccpqelseiter(c::acc)(p+1)qinchildren<-iter[]0children;self#need_resizemethod!wtree=N(self#coerce,List.map(func->c.widget#wtree)children)method!grab_focus?(last=false)()=[%debug"%s#grab_focus ~last:%b"self#melast];ifself#visiblethenmatchself#get_pProps.can_focuswith|false->false|true->matchsuper#grab_focus~last()with|true->true|false->letreciter=function|[]->false|c::q->matchc.widget#visible,c.widget#get_pProps.can_focuswith|true,true->(matchc.widget#grab_focus~last()with|false->iterq|true->true)|_->iterqiniter(iflastthenList.revchildrenelsechildren)elsefalsemethodprivatechild_move_focus(w:widget)lastchildrenon_none=letreciter=function|c::next::q->ifOid.equalc.widget#idw#idthen([%debug"%s#child_move_focus next.widget=%s, visible=%b"self#menext.widget#menext.widget#visible];ifnext.widget#visiblethenmatchnext.widget#grab_focus~last()with|false->iter(c::q)|true->trueelseiter(c::q))elseiter(next::q)|[]|[_]->on_none()initerchildrenmethod!child_focus_next(w:widget)=self#child_move_focuswfalsechildren(fun()->self#focus_next)method!child_focus_prev(w:widget)=self#child_move_focuswtrue(List.revchildren)(fun()->self#focus_prev)methodprivateadd_to_children?posc=children<-Misc.list_add?poschildrencmethodprivateremove_from_childrenw=letlen=List.lengthchildreninchildren<-List.filter(func->not(Oid.equalc.widget#idw#id))children;letlen2=List.lengthchildreninlen<>len2method!next_widget?inside~looppredw=letreciter=function|[]->(matchinside,parentwith|Somei,_whenself#equali->ifloopthenself#next_widget?inside~looppredNoneelseNone|_,None->None|_,Somep->p#next_widget?inside~looppred(Someself#coerce))|c::qwhenpredc.widget->Somec.widget|c::q->matchc.widget#next_widget?inside~looppredNonewith|None->iterq|x->xinmatchwwith|None->iterchildren|Somew->letrecfind=function|[]->iter[]|c::qwhenc.widget#equalw->iterq|_::q->findqinfindchildrenmethod!prev_widget?inside~looppredw=letreciter=function|[]->(matchinside,parentwith|Somei,_whenself#equali->ifloopthenself#prev_widget?inside~looppredNoneelseNone|_,None->None|_,Somep->p#prev_widget?inside~looppred(Someself#coerce))|c::qwhenpredc.widget->Somec.widget|c::q->matchc.widget#prev_widget?inside~looppredNonewith|None->iterq|x->xinmatchwwith|None->iter(List.revchildren)|Somew->letrecfind=function|[]->iter[]|c::qwhenc.widget#equalw->iterq|_::q->findqinfind(List.revchildren)end