123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867(** Widgets which can contain one widget. *)openMiscopenTsdlopenWidget(** Widget containing one child. This widget is usually not used
directly but inherited. *)classbin?(class_="bin")?name?props?(mutex=Lwt_mutex.create())()=object(self)inheritWidget.widget~class_?name?props()assuper(**/**)valmutablechild=(None:widgetoption)methodcompute_child_geometry:Widget.widget->G.t=funw->letcg={super#g_innerwithx=0;y=0}inletm=w#margininletcg={G.x=m.left;y=m.top;w=max0(cg.w-m.left-m.right);h=max0(cg.h-m.top-m.bottom);}inLog.debug(funm->m"%s#compute_child_geometry: %a"self#meG.ppcg);cgmethodset_p?(propagate=false)pv=Log.debug(funm->m"%s#set_p ~propagate:%b"self#mepropagate);super#set_p~propagatepv;ifpropagatethenmatchchildwith|None->()|Somew->w#set_p~propagatepvelse()methodwtree=letl=matchchildwithNone->[]|Somew->[w#wtree]inWidget.N(self#coerce,l)methodto_child_coords(x,y)=(x-g.x-g_inner.x,y-g.y-g_inner.y)method!baseline=matchchildwith|None->super#baseline|Somec->letb=c#baselineinletcg=c#geometryinb+cg.y+g_inner.ymethod!focused_widget=matchchildwith|None->Someself#coerce|Somec->matchc#focused_widgetwith|None->Someself#coerce|Somew->Somewmethod!release_focus=matchmatchchildwith|None->true|Somec->c#release_focuswith|true->self#set_pProps.is_focusfalse;self#set_pProps.has_focusfalse;true|_->falsemethod!set_has_focusb=matchsuper#set_has_focusbwith|true->true|false->matchchildwith|None->false|Somew->w#set_has_focusbmethod!grab_focus?(last=false)()=Log.debug(funm->m"%s#grab_focus ~last:%b"self#melast);ifself#visiblethenmatchself#get_pProps.focusablewith|true->(matchself#get_focuswith|None->false|Some_->true)|_->matchself#get_pProps.can_focuswith|false->false|true->matchchildwith|None->false|Somec->c#grab_focus~last()elsefalsemethod!on_sdl_event_down~oldpospose=ifself#sensitivethenletb=matchchildwith|None->false|Somew->Log.debug(funm->m"%s#on_sdl_event_down: propagating event to %s"self#mew#me);letchild_pos=Option.mapself#to_child_coordsposinletchild_oldpos=Option.mapself#to_child_coordsoldposinw#on_sdl_event_down~oldpos:child_oldposchild_poseinmatchbwith|true->true|false->self#on_sdl_eventposeelsefalsemethodon_sdl_event_me(pos:(int*int)option)(e:Sdl.event)=falsemethod!on_sdl_event(pos:(int*int)option)(e:Sdl.event)=matchself#on_sdl_event_meposewith|true->true|false->super#on_sdl_eventposemethod!set_parent?with_rendw=super#set_parent?with_rendw;matchchildwith|None->()|Somec->c#set_parent?with_rend(Someself#coerce)method!child_reparentedw=matchchildwith|Somecwhenc#equalw->Log.debug(funm->m"%s#child_reparented %s; child <- None"self#mew#me);child<-None;self#need_resize|_->()(**/**)methodremove_child=(matchchildwith|None->()|Somew->child<-None;w#set_parentNone);self#need_resizemethodchild=childmethodset_childw=letold_parent=w#parentinmatchold_parentwith|Somepwhenp#equalself#as_widget->()|_->self#remove_child;Log.debug(funm->m"%s#set_child %s"self#mew#me);child<-Somew;Option.iter(funp->p#child_reparentedw)old_parent;w#set_parent?with_rend:self#with_renderer(Someself#coerce);self#need_resize;(**/**)methodupdate_child_geometry=matchchildwith|None->()|Somew->w#set_geometry(self#compute_child_geometryw)methodchild_geometry=matchchildwith|None->G.zero|Somew->w#geometrymethodrender_child~layerrenderer~offset:(x,y)~(g_none:G.t)~g_child=(* coordinates are still using current widget's origin (i.e. relative to parent) *)Log.debug(funm->m"%s#render_child layer=%a ~offset=%d,%d g_none=%a g_child=%a"self#meLayer.pplayerxyG.ppg_noneG.ppg_child);matchchildwith|None->iflayer=self#get_pProps.layerthenletg_none=G.translate~x~yg_noneinRender.fill_rectrenderer(Someg_none)self#bg_color_nowelse()|Somew->letoff_x=g.x+g_inner.xinletoff_y=g.y+g_inner.yinletoffset=(x+off_x,y+off_y)inletg_child=G.translate~x:(-off_x)~y:(-off_y)g_childinw#render~layerrenderer~offsetg_childmethodrender_me_parent~layerrend~offsetrg=Log.debug(funm->m"%s#render_me_parent offset=(%d,%d) rg=%a"self#me(fstoffset)(sndoffset)G.pprg);()methodrender_me~layerrend~offsetrg=self#render_me_parent~layerrend~offsetrg;self#render_child~layerrend~offset~g_none:self#child_geometry~g_child:rgmethodchild_min_width=matchchildwithNone->0|Somew->w#min_widthmethodchild_min_height=matchchildwithNone->0|Somew->w#min_heightmethodchild_max_width=matchchildwithNone->None|Somew->w#max_widthmethodchild_max_height=matchchildwithNone->None|Somew->w#max_heightmethodchild_margin=matchchildwithNone->Props.trbl__0|Somec->c#marginmethod!privatemin_width_=super#min_width_+self#child_min_widthmethod!privatemin_height_=super#min_height_+self#child_min_heightmethod!max_width=matchself#child_max_widthwith|None->super#max_width|Somen->Some(super#min_width_+n)method!max_height=matchself#child_max_heightwith|None->super#max_height|Somen->Some(super#min_height_+n)methodprivatewidget_min_width_=super#min_width_methodprivatewidget_min_height_=super#min_height_method!set_geometrygeom=super#set_geometrygeom;self#update_child_geometrymethod!is_leaf_widget=falsemethod!leaf_widget_at~x~y=matchchildwith|None->None|Somew->let(x,y)=self#to_child_coords(x,y)inw#leaf_widget_at~x~ymethod!next_widget?inside~looppredw=matchw,childwith|None,Somec->c#next_widget?inside~looppredNone|_->super#next_widget?inside~looppred(Someself#coerce)method!prev_widget?inside~looppredw=matchw,childwith|None,Somec->c#prev_widget?inside~looppredNone|_->super#prev_widget?inside~looppred(Someself#coerce)method!destroy=super#destroy;Log.debug(funm->m"%s#child_destroy"self#me);matchchildwith|None->()|Somew->w#destroyend(** Convenient function to create a {!class-bin}.
See {!Widget.widget_arguments} for arguments. *)letbin?class_?name?props?pack()=letw=newbin?class_?name?props()inWidget.may_pack?packw#coerce;w(** {2 Event boxes} *)(** An event_box is a {!class-bin} widget which differs in the way events are
propagated: if an event is not handled by this widget, then it is
propagated to its child. This is useful for example to catch
keystrokes for keyboard shortcuts. *)classevent_box?(class_="event_box")?name?props()=object(self)inheritbin~class_?name?props()assuper(**/**)method!on_sdl_event_down~oldpospose=ifself#sensitivethenmatchself#on_sdl_eventposewith|true->true|false->matchchildwith|None->false|Somew->letchild_pos=Option.mapself#to_child_coordsposinletchild_oldpos=Option.mapself#to_child_coordsoldposinw#on_sdl_event_down~oldpos:child_oldposchild_poseelsefalseend(** Convenient function to create a {!class-event_box}.
See {!Widget.widget_arguments} for arguments. *)letevent_box?class_?name?props?pack()=letw=newevent_box?class_?name?props()inWidget.may_pack?packw#coerce;w(** {2 Fixed-size widget} *)(** A [fixed_size] widget is a {!class-bin} widget whose width and
height can be fixed. *)classfixed_size?(class_="fixed_size")?name?props?w?h()=object(self)inheritbin~class_?name?props()assuper(**{3 Properties} *)methodset_heighth=self#set_pProps.heighthmethodset_widthw=self#set_pProps.widthw(**/**)methodprivatemin_width_=matchself#opt_pProps.widthwith|Somewwhenw>=0->w|_->self#child_min_widthmethodprivatemin_height_=matchself#opt_pProps.heightwith|Somehwhenh>=0->h|_->self#child_min_heightmethod!max_width=matchself#opt_pProps.widthwith|Somex->Some(absx)|_->self#child_max_widthmethod!max_height=matchself#opt_pProps.heightwith|Somex->Some(absx)|_->self#child_max_heightinitializer(* modify props here rather than before object(self) so
that they have been duplicated in widget init *)let()=matchwwithNone->()|Somew->Props.setpropsProps.widthwinlet()=matchhwithNone->()|Someh->Props.setpropsProps.heighthin()end(** Convenient function to create a {!class-fixed_size}.
[w] and [h] optional arguments specify width and height.
See {!Widget.widget_arguments} for other arguments. *)letfixed_size?class_?name?props?w?h?pack()=letw=newfixed_size?class_?name?props?w?h()inWidget.may_pack?packw#coerce;w(** {2 Scrollboxes} *)(** Policy to display scrollbars.
{ul
{- [`ALWAYS]: always displays scrollbar, even when not needed.}
{- [`NEVER]: never displays scrollbar, even when needed.}
{- [`AUTOMATIC]: displays scrollbar only when needed, i.e. when
child content is larger than the scrollbox in the considered
direction (horizontal or vertical).}
}
*)typescrollbar_policy=[`ALWAYS|`NEVER|`AUTOMATIC](** {!Ocf} wrapper for {!scrollbar_policy}. *)letscrollbar_policy_wrapper:scrollbar_policyOcf.Wrapper.t=letto_json?with_doc=function|`ALWAYS->`String"always"|`NEVER->`String"never"|`AUTOMATIC->`String"automatic"inletfrom_json?defjson=matchjsonwith|`Strings->(matchString.lowercase_asciiswith|"always"->`ALWAYS|"never"->`NEVER|"auto"|"automatic"->`AUTOMATIC|_->Ocf.invalid_valuejson)|_->Ocf.invalid_valuejsoninOcf.Wrapper.maketo_jsonfrom_jsonmoduleTScrollbar_policy=structtypet=scrollbar_policyletcompare=Stdlib.compareletwrapper=Somescrollbar_policy_wrapperendmodulePScrollbar_policy=Props.Add_prop_type(TScrollbar_policy)letscrollbar_policy_prop:scrollbar_policyProps.mk_prop=PScrollbar_policy.mk_proplethscrollbar_policy=scrollbar_policy_prop~default:`AUTOMATIC"hscrollbar_policy"letvscrollbar_policy=scrollbar_policy_prop~default:`AUTOMATIC"vscrollbar_policy"lethscrollbar_covers_child=Props.bool_prop~after:[Props.Resize]~default:true~inherits:true"hscrollbar_covers_child"letvscrollbar_covers_child=Props.bool_prop~after:[Props.Resize]~default:true~inherits:true"vscrollbar_covers_child"(* FIXME: use render layer to render scrollbars ? *)classscrollbox?(class_="scrollbox")?name?props()=object(self)inheritbin~class_?name?props()assuper(**/**)valmutablecontent_w=0valmutablecontent_h=0valmutableoffset_x=0valmutableoffset_y=0valmutableg_handle_v=G.zerovalmutableg_handle_h=G.zerovalmutablestate_machine:[`Base|`Moving_handleofint*int*Props.orientation]Misc.state_machine=Misc.empty_state_machine(**/**)(** {3 Properties} *)methodhscrollbar_policy=self#get_phscrollbar_policymethodset_hscrollbar_policy=self#set_phscrollbar_policymethodvscrollbar_policy=self#get_pvscrollbar_policymethodset_vscrollbar_policy=self#set_pvscrollbar_policymethodhscrollbar_covers_child=self#get_phscrollbar_covers_childmethodset_hscrollbar_covers_child=self#set_phscrollbar_covers_childmethodvscrollbar_covers_child=self#get_pvscrollbar_covers_childmethodset_vscrollbar_covers_child=self#set_pvscrollbar_covers_child(** {3 Other methods} *)(**/**)method!to_child_coords(x,y)=(offset_x+x-g.x-g_inner.x,offset_y+y-g.y-g_inner.y)method!to_desktop_coords~x~y=letx=x-offset_xandy=y-offset_yinsuper#to_desktop_coords~x~ymethod!child_visible_rectw=G.{g_innerwithx=offset_x;y=offset_y}(**/**)(** [#vscroll off] vertically scrolls by off. A negative offset
moves up, a positive one moves down. Checks are performed not to
scroll out of bounds.*)methodvscrolloffset=letgch=self#gchild_hinletnew_off_y=max0(min(offset_y+offset)(content_h-gch))inifnew_off_y<>offset_ythen(Log.debug(funm->m"%s#vscroll offset_y: %d => %d"self#meoffset_ynew_off_y);offset_y<-new_off_y;self#set_g_handle_v;super#need_render~layer:(self#get_pProps.layer)g)(** [#hscroll off] horizontally scrolls by off. A negative offset
moves left, a positive one moves right. Checks are performed not to
scroll out of bounds.*)methodhscrolloffset=letgcw=self#gchild_winletnew_off_x=max0(min(offset_x+offset)(content_w-gcw))inifnew_off_x<>offset_xthen(offset_x<-new_off_x;self#set_g_handle_h;super#need_render~layer:(self#get_pProps.layer)g)(** [#offsets] return x-offset and y-offset, i.e. the top left corner
coordinates displayed of the child content. *)methodoffsets=(offset_x,offset_y)(** [#scroll_to ~x ~y] sets x-offset and y-offset to [x] and [y].
[x] and [y] are corrected to valid bounds
(> 0 and < content - displayed rect).
*)methodscroll_to~x~y=Log.debug(funm->m"%s#scroll_to ~x:%d ~y:%d"self#mexy);letold_x=offset_xinletold_y=offset_yinoffset_x<-max0(minx(content_w-self#gchild_w));offset_y<-max0(miny(content_h-self#gchild_h));Log.debug(funm->m"%s#scroll_to offset_y: %d => %d"self#meold_yoffset_y);letbx=ifoffset_x<>old_xthen(self#set_g_handle_h;true)elsefalseinletby=ifoffset_y<>old_ythen(self#set_g_handle_v;true)elsefalseinifbx||bythensuper#need_render~layer:(self#get_pProps.layer)g(**/**)method!show_child_rectr=letexposed=G.{x=offset_x;y=offset_y;w=g_inner.w;h=g_inner.h}inLog.debug(funm->m"%s#show_child_rect r=%a exposed=%a"self#meG.pprG.ppexposed);letfrxrwexew=ifrw>=ewthenrxelseifrx>=exthenifrx+rw<=ex+ewthenexelseex+(rx+rw)-(ex+ew)elserxinletx=fr.xr.wexposed.xexposed.winlety=fr.yr.hexposed.yexposed.hinLog.debug(funm->m"%s#show_child_rect scroll to x:%d y:%d"self#mexy);self#scroll_to~x~y;self#showmethodon_key_downposeventkeymods=Log.debug(funm->m"%s#on_key_down: %s"self#me(Tsdl.Sdl.get_key_namekey));letold_off_x=offset_xinletold_off_y=offset_yinlethandled=matchkeywith|kwhenk=Sdl.K.home->self#vscroll(-max_int);true|kwhenk=Sdl.K.kend->self#vscroll(max_int-offset_y);true|kwhenk=Sdl.K.pageup->self#vscroll(-g_inner.h);true|kwhenk=Sdl.K.pagedown->self#vscrollg_inner.h;true|kwhenk=Sdl.K.up->self#vscroll(-25);true|kwhenk=Sdl.K.down->self#vscroll25;true|kwhenk=Sdl.K.left->self#hscroll(-25);true|kwhenk=Sdl.K.right->self#hscroll(25);true|_->falseinmatchhandledwith|true->(offset_x<>old_off_x||offset_y<>old_off_y)|false->super#on_key_downposeventkeymodsmethodon_sdl_event_meposev=ifself#sensitivethenmatchstate_machine.fposevwith|false->super#on_sdl_event_meposev|true->trueelsefalsemethod!on_sdl_event_down~oldpospose=ifself#sensitivethenletb=matchchildwith|None->false|Somew->(* propagate events with coords only if pos or oldpos is in child *)matcholdpos,poswith|None,None->w#on_sdl_event_down~oldpos:NoneNonee|_->letgc=self#g_childinletf=function|None->false|Some(x,y)->G.inside~x~ygciniffoldpos||fposthen(Log.debug(funm->m"%s#on_sdl_event_down: propagating event to %s"self#mew#me);letchild_pos=Option.mapself#to_child_coordsposinletchild_oldpos=Option.mapself#to_child_coordsoldposinw#on_sdl_event_down~oldpos:child_oldposchild_pose)elsefalseinmatchbwith|true->true|false->self#on_sdl_eventposeelsefalsemethodon_mouse_leave=(matchstate_machine.state()with|`Moving_handle_->state_machine.set_state`Base|_->());super#on_mouse_leavemethodstate_on_eventstateposev=matchstate,pos,Sdl.Event.(enum(getevtyp))with|`Base,_,`Mouse_wheel->ifmouse_on_widgetthen(letold_off_x=offset_xinletold_off_y=offset_yinletx=Sdl.Event.(getevmouse_wheel_x)inlety=Sdl.Event.(getevmouse_wheel_y)inLog.debug(funm->m"%s#on_even mouse wheel (%d,%d) content_w=%d, g.w=%d, content_h=%d, g.h=%d"self#mexycontent_wg.wcontent_hg.h);self#vscroll(-y*25);self#hscroll(-x*25);Some(`Base,offset_x<>old_off_x||offset_y<>old_off_y))elseNone|`Base,Some(x,y),`Mouse_button_down->letx=x-g.x-g_inner.xinlety=y-g.y-g_inner.yinifG.inside~x~yg_handle_hthenSome(`Moving_handle(offset_x,x,Props.Horizontal),true)elseifG.inside~x~yg_handle_vthenSome(`Moving_handle(offset_y,y,Props.Vertical),true)elseNone|`Moving_handle(off,cursor_offset,Props.Horizontal),Some(x,_),`Mouse_motion->letx=x-g.x-g_inner.xinletr=floatcontent_w/.floatself#gchild_winletx=off+(truncate(float(x-cursor_offset)*.r))inself#scroll_to~x~y:offset_y;None|`Moving_handle(off,cursor_offset,Props.Vertical),Some(_,y),`Mouse_motion->lety=y-g.y-g_inner.yinletr=floatcontent_h/.floatself#gchild_hinlety=off+(truncate(float(y-cursor_offset)*.r))inself#scroll_to~x:offset_x~y;None|`Moving_handle_,_,`Mouse_button_up->Some(`Base,true)|(`Base|`Moving_handle_),_,_->Nonemethodprivategchild_w=matchself#vscrollbar_covers_childwith|true->g_inner.w|false->g_inner.w-g_handle_v.w-2methodprivategchild_h=matchself#hscrollbar_covers_childwith|true->g_inner.h|false->g_inner.h-g_handle_h.h-2methodprivatemust_show_scroll_h=matchself#hscrollbar_policywith|`NEVER->false|`ALWAYS->true|`AUTOMATIC->letgc_w=self#gchild_winLog.debug(funm->m"%s#must_show_scroll_h content_w=%d, gc_w=%d"self#mecontent_wgc_w);content_w>gc_wmethodprivatemust_show_scroll_v=matchself#vscrollbar_policywith|`NEVER->false|`ALWAYS->true|`AUTOMATIC->letgc_h=self#gchild_hincontent_h>gc_hmethodprivateg_child=letgc={g_innerwithw=self#gchild_w;h=self#gchild_h}inLog.debug(funm->m"%s#g_child => %a"self#meG.ppgc);gcmethodprivateset_g_handle_h=letgcw=self#gchild_winletw=letratio=floatgcw/.floatcontent_winletw=truncate(floatg_inner.w*.ratio)inmax(self#get_pProps.scrollbar_handle_min_size)winletx=letratio=floatoffset_x/.float(content_w-gcw)inmax0(truncate(float(g_inner.w-w)*.ratio))inletsw=self#get_pProps.scrollbar_widthinletgh={G.x;G.y=g_inner.h-1-sw;w;h=sw}inLog.debug(funm->m"scroll: g_handle_h set to %a"G.ppgh);g_handle_h<-ghmethodprivateset_g_handle_v=letgch=self#gchild_hinleth=letratio=floatgch/.floatcontent_hinleth=truncate(floatg_inner.h*.ratio)inmax(self#get_pProps.scrollbar_handle_min_size)hinlety=letratio=floatoffset_y/.float(content_h-gch)inmax0(truncate(float(g_inner.h-h)*.ratio))inletsw=self#get_pProps.scrollbar_widthinletgh={G.x=g_inner.w-1-sw;y;w=sw;h}inLog.debug(funm->m"scroll: g_handle_v set to %a"G.ppgh);g_handle_v<-ghmethodrender_hscroll_if_neededrenderer~offset:(x,y)geom=ifself#must_show_scroll_hthenletsw=self#get_pProps.scrollbar_widthinletg_scroll={G.x=g.x+g_inner.x;y=g.y+g_inner.y+g_inner.h-1-sw;w=g_inner.w;h=sw;}inmatchG.intergeomg_scrollwith|None->()|Someg_bg->letg_bg=G.translate~x~yg_bginLog.debug(funm->m"%s rendering hscroll: %a"self#meG.ppg_bg);Render.fill_rectrenderer(Someg_bg)(self#get_pProps.scrollbar_bg_color);letg_handle=G.translate~x:(g.x+g_inner.x)~y:(g.y+g_inner.y)g_handle_hinmatchG.intergeomg_handlewith|None->()|Somegh->letgh=G.translate~x~yghinLog.debug(funm->m"%s rendering hscroll handle: %a"self#meG.ppgh);Render.fill_rectrenderer(Somegh)(self#get_pProps.scrollbar_handle_color)methodrender_vscroll_if_neededrenderer~offset:(x,y)geom=ifself#must_show_scroll_vthenletsw=self#get_pProps.scrollbar_widthinletg_scroll={G.x=g.x+g_inner.x+g_inner.w-1-sw;y=g.y+g_inner.y;w=sw;h=g_inner.h;}inmatchG.intergeomg_scrollwith|None->()|Someg_bg->letg_bg=G.translate~x~yg_bginLog.debug(funm->m"%s rendering vscroll: %a"self#meG.ppg_bg);Render.fill_rectrenderer(Someg_bg)(self#get_pProps.scrollbar_bg_color);letg_handle=G.translate~x:(g.x+g_inner.x)~y:(g.y+g_inner.y)g_handle_vinmatchG.intergeomg_handlewith|None->()|Somegh->letgh=G.translate~x~yghinLog.debug(funm->m"%s rendering vscroll handle: %a"self#meG.ppgh);Render.fill_rectrenderer(Somegh)(self#get_pProps.scrollbar_handle_color)methodrender_scrolls_if_neededrenderer~offsetgeom=self#render_hscroll_if_neededrenderer~offsetgeom;self#render_vscroll_if_neededrenderer~offsetgeommethodrender_me~layerrend~offset:(x,y)rg=(letgc=self#g_childinmatchG.interrg{gcwithx=g_inner.x+g.x;y=g_inner.y+g.y}with|None->Log.debug(funm->m"%s#render_me G.inter %a %a = None"self#meG.pprgG.ppg_inner)|Somerg->Log.debug(funm->m"%s#render_me rendering g=%a (rg ∩ g_inner)=%a"self#meG.ppgG.pprg);(* ask child to render the exposed rectangle *)letg_child={rgwithx=offset_x+rg.x;y=offset_y+rg.y;}inLog.debug(funm->m"%s#render_me g_child=%a, offset_x=%d, offset_y=%d\n"self#meG.ppg_childoffset_xoffset_y);(* coordinates in render_child are still relative to
current widget; they will be translated to child's coordinates
when render_child calls child#render *)let(x,y)=(x-offset_x,y-offset_y)inself#render_child~layerrend~offset:(x,y)~g_none:rg~g_child);(*Log.debug (fun m -> m "%s#render Texture.copy src=%a ~x:%d y:%d"
self#me G.pp g_child rg.x rg.y);
Texture.copy ~from:t ~src:g_child ~x ~y rend target ;*)iflayer=self#get_pProps.layerthenself#render_scrolls_if_neededrend~offset:(x,y)rg;methodset_geometrygeom=super#set_geometrygeom;Log.debug(funm->m"%s#set_geometry: offset_x=%d, content_w=%d, g_inner.w=%d, offset_y=%d, content_h=%d, g_inner.h=%d"self#meoffset_xcontent_wg_inner.woffset_ycontent_hg_inner.y);offset_x<-max0(minoffset_x(content_w-g_inner.w));letold_offset_y=offset_yinoffset_y<-max0(minoffset_y(content_h-g_inner.h));self#set_g_handle_h;self#set_g_handle_v;Log.debug(funm->m"%s#set_geometry: offset_y: %d => %d"self#meold_offset_yoffset_y)method!compute_child_geometryw=letcm=w#margininletcw=w#min_widthinletch=w#min_heightincontent_w<-maxcwself#gchild_w;content_h<-maxchself#gchild_h;{G.x=cm.left;y=cm.top;w=content_w-cm.left-cm.right;h=content_h-cm.top-cm.bottom;}method!privatemin_width_=letm=self#get_pProps.margininletp=self#get_pProps.paddinginletb=self#get_pProps.border_widthinm.left+b.left+p.left+p.right+b.right+m.rightmethod!privatemin_height_=letm=self#get_pProps.margininletp=self#get_pProps.paddinginletb=self#get_pProps.border_widthinm.top+b.top+p.top+p.bottom+b.bottom+m.bottommethod!max_width=self#child_max_widthmethod!max_height=self#child_max_heightmethodchild_need_render~layergeom=Log.debug(funm->m"%s#child_need_render ~layer:%a on %a"self#meLayer.pplayerG.ppgeom);(* convert exposed part to child coord and see what part
of required rendering should effectively be asked
for rendering. *)letexposed=G.{g_innerwithx=offset_x;y=offset_y}inmatchG.interexposedgeomwith|None->()|Somerg->(* if child shrinked, we must render also the rest of
the canvas *)letcg=matchchildwith|None->rg(* strange *)|Somec->c#geometryinletw=ifg_inner.w>cg.wtheng_inner.w-rg.xelserg.winleth=ifg_inner.h>cg.htheng_inner.h-rg.yelserg.hinletrg=G.translate~x:(g.x+g_inner.x-offset_x)~y:(g.y+g_inner.y-offset_y){rgwithw;h}inLog.debug(funm->m"%s#child_need_render final rg=%a"self#meG.pprg);self#need_render~layerrginitializerstate_machine<-Misc.mk_state_machine`Baseself#state_on_event;end(** Convenient function to create a {!class-fixed_size}.
[hpolicy] and [vpolicy] optional arguments specify horizontal
and vertical scrollbar policies.
See {!Widget.widget_arguments} for other arguments. *)letscrollbox?class_?name?props?hpolicy?vpolicy?pack()=letw=newscrollbox?class_?name?props()inOption.iterw#set_hscrollbar_policyhpolicy;Option.iterw#set_vscrollbar_policyvpolicy;Widget.may_pack?packw#coerce;w