123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341(** Labelled frame widget. *)openMiscopenWidgetopenTsdl(** Property ["frame_border_width"] to specify the width of the
frame's border. *)letborder_width=Props.int_prop~after:[Resize]~default:1"frame_border_width"(** The frame widget. Optional argument [label] can be used
to pass the widget used in label position. Any widget can be
used as label. *)classframe?(class_="frame")?name?props?label()=object(self)inheritBin.bin~class_?name?props()assuper(**/**)valmutablelabel=(label:Widget.widgetoption)method!set_p?(propagate=false)pv=Log.debug(funm->m"%s#set_p ~propagate:%b"self#mepropagate);super#set_p~propagatepv;ifpropagatethenmatchlabelwith|None->()|Somew->w#set_p~propagatepvelse()method!child_reparentedw=matchlabelwith|Somecwhenc#equalw->self#set_labelNone|_->super#child_reparentedw(**/**)(** {2 Properties} *)methodframe_border_width=self#get_pborder_widthmethodset_frame_border_width=self#set_pborder_width(**/**)method!wtree=letl=matchlabelwith|None->[]|Somew->[w#wtree]inletl=matchchildwith|None->l|Somew->l@[w#wtree]inWidget.N(self#coerce,l)method!render_child~layerrenderer~offset~g_none~g_child=matchchildwith|None->()|Some_->super#render_child~layerrenderer~offset~g_none~g_childmethod!render_me_parent~layerrend~offsetgeom=super#render_with_prepare~layerrend~offsetgeom;self#render_label~layerrend~offsetgeommethodrender_label~layerrend~offset:(x,y)geom=matchlabelwith|None->()|Somew->letoffset=(x+g.x+g_inner.x,y+g.y+g_inner.x)inletgeom=G.translate~x:(-g.x-g_inner.x)~y:(-g.y-g_inner.x)geominw#render~layerrend~offsetgeommethodlabel_min_width=matchlabelwith|None->0|Somew->w#min_widthmethodlabel_min_height=matchlabelwith|None->0|Somew->w#min_heightmethodlabel_margin=matchlabelwith|None->Props.trbl__0|Somew->w#marginmethodprivatetop_padding=letp=self#paddinginletlabel_h=self#label_min_heightinmaxp.top(label_h/2)method!privatemin_width_=letp=self#paddinginletcm=self#child_margininletcw=self#child_min_widthinletbw=self#frame_border_widthin(* use super#min_width but remove child_min_width *)super#min_width_-cw+bw+(max(maxp.leftcm.left+cw-cm.left-cm.right+maxp.rightcm.right)(p.left+self#label_min_width+p.right))+bwmethod!privatemin_height_=letp=self#paddinginletbw=self#frame_border_widthinletlh=self#label_min_heightinletlm=self#label_margininletcm=self#child_margininletch=self#child_min_heightin(* use super#min_height but remove child_min_height *)super#min_height_-ch+(max(max0(lm.top-p.top)+lh-lm.top-lm.bottom+maxlm.bottomcm.top)(bw+maxp.topcm.top))+ch-cm.top-cm.bottom+maxp.bottomcm.bottom+bwmethod!compute_child_geometryw=letglabel=matchlabelwith|None->G.zero|Somew->w#geometryinletlm=self#label_margininletcm=w#margininletbw=self#frame_border_widthinletp=self#paddinginletx=bw+maxcm.leftp.leftinlety=max(bw+maxcm.topp.top)(max0(lm.top-p.top)+glabel.h+maxlm.bottomcm.top)inletgc={G.x;y;w=g_inner.w-x-(bw+maxp.rightcm.right);h=g_inner.h-y-(bw+maxp.bottomcm.bottom);}inLog.debug(funm->m"%s#compute_child_geometry: g=%a, g_inner=%a, gc=%a"self#meG.ppgG.ppg_innerG.ppgc);gcmethodprivateset_label_parent=matchlabelwith|None->()|Somew->w#set_parent?with_rend:self#with_renderer(Someself#coerce)(**/**)(** {2 Label} *)(** Returns the label widget, if any. *)methodlabel=label(** [f#set_label w] sets widget [w] as label, replacing the
previous one if present. *)methodset_labelw=matchlabel,wwith|Somel,Somewwhenl#equalw#as_widget->Log.warn(funm->m"%s is already label for %s"w#meself#me)|_->Option.iter(funl->l#set_parent?with_rend:NoneNone)label;label<-w;let()=matchlabelwith|None->()|Somew->Option.iter(funp->p#child_reparentedw)w#parent;self#set_label_parentinself#need_resize(**/**)method!set_geometrygeom=(matchlabelwith|None->()|Somec->letold_geo=c#geometryinletw=self#label_min_widthinleth=self#label_min_heightinletm=self#label_margininletp=self#paddinginletbw=self#frame_border_widthinletgeo={G.x=bw+p.left+m.left;y=max0(m.top-p.top);w=w-m.left-m.right;h=h-m.top-m.bottom}inLog.debug(funm->m"%s#set_geometry label#geometry=>%a"self#meG.ppgeo);c#set_geometrygeo;(* if label's geometry changed, we destroy our texture
or else the frame border will not be redrawn *)ifold_geo<>geothenself#destroy_texture);super#set_geometrygeom;method!prepare~layerrendgeom=iflayer=self#get_pProps.layerthen(matchself#texturerendwith|None->Log.debug(funm->m"%s#prepare: no texture"self#me);None|Some(`Existt)->Somet|Some(`Newt)->Log.debug(funm->m"%s#prepare: drawing on my texture"self#me);letp=self#paddinginletbw=self#frame_border_widthinletlabel_m=self#label_marginin(*let cm = self#child_margin in*)letglabel=matchlabelwith|None->G.zero|Somew->letge=w#geometryin{G.x=p.left+bw;y=ge.y;w=ge.w+label_m.left+label_m.right;h=ge.h(*+ max 0 (label_m.top - p.top) + max label_m.bottom cm.top*);}inlet()=letx=0inlety=max0(glabel.y+(glabel.h-bw)/2)inletw=g_inner.winleth=max0(g_inner.h-y)inletr=ref{G.x;y;w;h}inleti=refbwinwhile!i>0doLog.debug(funm->m"%s#prepare draw_rect %a glabel=%a"self#meG.pp!rG.ppglabel);Texture.draw_rect_rrendt!rself#fg_color_now;decri;r:=G.enlarge~w:(-1)~h:(-1)!r;donein(matchlabelwith|Some_->Texture.fill_rectrendt(Someglabel)self#bg_color_now|None->());Somet)elseNonemethod!on_sdl_event_down~oldpospose=ifself#sensitivethenletb=matchlabelwith|None->false|Somew->letchild_pos=Option.mapself#to_child_coordsposinletchild_oldpos=Option.mapself#to_child_coordsoldposinw#on_sdl_event_down~oldpos:child_oldposchild_poseinmatchbwith|true->true|false->super#on_sdl_event_down~oldposposeelsefalsemethod!is_leaf_widget=falsemethod!leaf_widget_at~x~y=matchG.insideg~x~ywith|false->None|true->matchmatchlabelwith|None->None|Somew->let(x,y)=self#to_child_coords(x,y)inw#leaf_widget_at~x~ywith|None->super#leaf_widget_at~x~y|(Some_)asx->xmethod!next_widget?inside~looppredw=matchw,labelwith|None,None->super#next_widget?inside~looppredNone|None,Somel->(matchl#next_widget?inside~looppredNonewith|None->super#next_widget?inside~looppredNone|x->x)|Somew,Somelwhenw#equall->l#next_widget?inside~looppredNone|Some_,_->super#next_widget?inside~looppredNonemethod!prev_widget?inside~looppredw=matchw,child,label,parent,insidewith|None,None,None,_,Someiwhenself#equali#coerce->None|None,None,None,None,_->None|None,None,None,Somep,_->p#prev_widget?inside~looppred(Someself#coerce)|None,Somec,None,_,_->super#prev_widget?inside~looppredNone|None,None,Somel,_,Someiwhenself#equali#coerce->(matchl#prev_widget?inside~looppredNonewith|None->ifloopthenself#prev_widget?inside~looppredNoneelseNone|x->x)|None,None,Somel,None,_->(matchl#prev_widget?inside~looppredNonewith|None->None|x->x)|None,None,Somel,Somep,_->(matchl#prev_widget?inside~looppredNonewith|None->p#prev_widget?inside~looppred(Someself#coerce)|x->x)|Somew,Somec,Somel,_,Someiwhenself#equali&&w#equalc->(matchl#prev_widget?inside~looppredNonewith|None->ifloopthenself#prev_widget?inside~looppredNoneelseNone|x->x)|Somew,Somec,Somel,None,_whenw#equalc->(matchl#prev_widget?inside~looppredNonewith|None->None|x->x)|Somew,Somec,Somel,Somep,_whenw#equalc->(matchl#prev_widget?inside~looppredNonewith|None->p#prev_widget?inside~looppred(Someself#coerce)|x->x)|_,_,_,_,Someiwhenself#equali->ifloopthenself#prev_widget?inside~looppredNoneelseNone|_,_,_,None,_->None|_,_,_,Somep,_->p#prev_widget?inside~looppred(Someself#coerce)initializerself#set_label_parent;(* TODO: handle properties which should trigger invalidating the texture *)ignore(self#connect(Object.Prop_changedProps.is_focus)(fun~prev~now->Log.debug(funm->m"%s: is_focus -> %b, invalidating texture"self#menow);self#invalidate_texture))end(** Convenient function to create a {!class-frame}.
Optional arguments [label] can be used to add the widget to
be placed in label position.
See {!Widget.widget_arguments} for other arguments. *)letframe?name?props?label?pack()=letw=newframe?name?props?label()inWidget.may_pack?packw;w