123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403(*********************************************************************************)(* 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(** A container child, parametrized by the type of
the associated data. *)type'achild={widget:Widget.widget;mutabledata:'a;}(** A container widget is a widget which can contain other widgets.
Each widget can be associated to additional data ['a],
so each container has type [['a] 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['a,'pos]container?classes?name?props?wdata()=object(self)inheritwidget?classes?name?props?wdata()assuper(**/**)methodprivatevirtualchild_by_widget:Widget.widget->'achildoptionmethodprivatevirtualfind_child_opt:('achild->bool)->'achildoptionmethodprivatevirtualfind_child_pos_opt:('achild->bool)->'posoptionmethodprivatevirtualiter_children:('achild->unit)->unitmethodprivatevirtualfold_children_right:'b.('achild->'b->'b)->'b->'bmethodprivatevirtualfold_children_left:'b.('b->'achild->'b)->'b->'bmethodprivatevirtualadd_to_children:?pos:'pos->'achild->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->Somec.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)methodprivatefocused_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)[]method!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)|_->self#fold_children_left(funaccc->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?poswdata=Log.info(funm->m"%s#add %s"self#mew#me);matchself#find_child_opt(func->Oid.equalc.widget#idw#id)with|Some_->Log.warn(funm->m"%s is already packed in %s"w#meself#me);false|None->letold_parent=w#parentinmatchold_parentwith|Somepwhenp#equalself#as_widget->false|_->min_width<-None;min_height<-None;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(min_width<-None;min_height<-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_me~layerrend~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)rginself#iter_children(func->c.widget#render~layer~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 {!Pack.class-box} or {!Pack.class-paned}).
*)classvirtual['a]container_list?classes?name?props?wdata()=object(self)inherit['a,int]container?classes?name?props?wdata()assuper(**/**)valmutablechildren=([]:'achildlist)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