123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985(*********************************************************************************)(* 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 *)(* *)(*********************************************************************************)(** Canvas.
A canvas widget is composed of a root canvas {!class-group}. A
group can contain {!class-full_item}s (a {!class-group}s is
a [fullitem] too).
Coordinates of an item are relative to its parent group.
All item classes takes optional [x] and [y] arguments to define
their coordinates relatively to their group, with default value [0].
The group can be given with the optional [group] argument,
or set with the [set_group] method. These three arguments are
also handled by {{!convenient_functions}convenient functions
to create items}.
*)openTsdl(**/**)moduleBounds=Set.Make(structtypet=int*Oid.tletcompare(n1,id1)(n2,id2)=matchStdlib.comparen1n2with|0->Oid.compareid1id2|n->nend)(**/**)(** {2 Items} *)(** This virtual class defines the item interface. Most of the
virtual methods correspond to methods of {!Widget.class-widget}
and are defined by inherting from {!Widget.class-widget} or other
widgets. *)classvirtualitem?(group:groupoption)?(x=0)?(y=0)()=letinit_group=groupinobject(self)methodas_item=(self:>item)methodvirtualid:Oid.t(**/**)methodvirtualset_geometry:G.t->unitvalvirtualmutableg:G.t(**/**)methodvirtualgeometry:G.t(**/**)valvirtualmutablefrozen:bool(**/**)methodvirtualget_p:'a.'aProps.prop->'amethodvirtualcoerce:Widget.widgetmethodvirtualwtree:Widget.widgetWidget.treemethodvirtualme:string(**/**)methodvirtualset_parent:?with_rend:((Sdl.renderer->unitLwt.t)->unitLwt.t)->Widget.widgetoption->unitvalvirtualmutablemin_width:intoptionvalvirtualmutablemin_height:intoptionmethodvirtualprivatemin_width_:intmethodvirtualprivatemin_height_:int(**/**)methodvirtualmin_width:intmethodvirtualmin_height:intmethodvirtualas_full_item:full_item(**/**)methodupdate_bounds~(recur:bool)=()(**/**)(** {2 Coordinates} *)methodx=self#geometry.xmethody=self#geometry.ymethodset_x(x_:int)=self#set_xy(x_,self#y)methodset_y(y_:int)=self#set_xy(self#x,y_)methodset_xy(x,y)=[%debug"%s#set_xy (%d,%d)"self#mexy];letg0=self#geometryinself#set_geometry{g0withx;y};letg1=self#geometryinself#update_bounds_in_parent_group~g0~g1methodmove?(x=self#x)?(y=self#y)()=self#set_xy(x,y)methodto_canvas_coords~x~y=matchgroupwith|None->(x,y)|Someg->g#to_canvas_coords~x~y(** {2 Group} *)(**/**)valmutablegroup=(None:groupoption)(**/**)methodgroup=groupmethodset_groupg=Option.iter(fun(g:group)->g#remove_item(self#as_full_item))group;group<-g(**/**)(** this method should be used only is the group class, to prevent
a recursive call to remove_item, trying to remove the item
twice from the same group, issuing a warning. *)methodset_group_none=group<-None(**/**)(** {2 Accessing items} *)methodon_items_at(f:full_item->boolLwt.t)~(x:int)~(y:int)=Lwt.return_falsemethodget_item_at~x~y=ifG.inside~x~yself#geometrythenSomeself#as_full_itemelseNonemethodget_leaf_items_atacc~x~y=matchself#get_item_at~x~ywith|None->acc|Somei->i::acc(**/**)methodvirtualrender:layer:Layer.t->Sdl.renderer->offset:(int*int)->G.t->unitmethodneed_render~layergeom=ifnotfrozenthenmatchgroupwith|None->()|Somegroup->[%debug"item%s#need_render: geom=%a"self#meG.ppgeom];group#child_need_render~layergeommethodneed_resize=letg0=ginmin_width<-None;min_height<-None;letw=self#min_widthinleth=self#min_heightinself#set_geometry{g0withw;h};letg1=self#geometryin[%debug"%s[item]#need_resize: g0=%a g1=%a"self#meG.ppg0G.ppg1];self#update_bounds_in_parent_group~g0~g1methodupdate_bounds_in_parent_group~g0~g1=matchgroupwith|None->()|Somegroup->letid=self#idingroup#set_item_hboundg0idg1;group#set_item_vboundg0idg1initializermatchinit_groupwith|None->()|Somegroup->group#add_item?x:(Somex)?y:(Somey)(self#as_full_item)end(** A [full_item] is a {!Widget.class-widget} with item interface.*)andvirtualfull_item?classes?name?props?wdata?group?x?y()=object(self)inheritWidget.widget?classes?name?props?wdata()aswidgetinherititem?group?x?y()assupermethodas_full_item=(self:>full_item)(**/**)method!need_resize=min_width<-None;min_height<-None;matchgroupwith|None->widget#need_resize|_->(* child should call set_item_vbound and set_item_hbound *)(*Log.warn
(fun m -> m "%s#child_need_resize should not be called when item has a group"
self#me)*)()end(** a [container_item] is a {!Container.class-container} with item interface. *)andvirtual['a]container_item?classes?name?props?wdata?group?x?y()=object(self)inherit['a]Container.container_list?classes?name?props?wdata()aswidgetinherititem?group?x?y()assupermethodas_full_item=((self:>'acontainer_item):>full_item)(**/**)methodneed_resize=min_width<-None;min_height<-None;matchgroupwith|None->widget#need_resize|_->(* child should call set_item_vbound and set_item_hbound *)(*Log.warn
(fun m -> m "%s#child_need_resize should not be called when item has a group"
self#me)*)()end(** A group is a \[[full_item]\] {!class-container_item}.
Its kind is ["canvas_group"]. *)andgroup?classes?name?props?wdata?group?x?y()=object(self)inherit[full_item]container_item?classes?name?props?wdata?group?x?y()assuper(**/**)methodkind="canvas_group"valmutablehbounds=Bounds.emptyvalmutablevbounds=Bounds.empty(**/**)methodas_group=(self:>group)methoditems=List.map(func->c.Container.data)children(** Removes all items from group. *)methodclear=List.iter(funi->self#remove_itemi)self#items(**/**)method!set_geometryg=[%debug"%s#set_geometry %a"self#meG.ppg];super#set_geometrygmethod!to_canvas_coords~x~y=let(x,y)=g.x+g_inner.x+x,g.y+g_inner.y+yinmatchgroupwith|None->(x,y)|Someg->g#to_canvas_coords~x~y(**/**)methodof_canvas_coords~x~y=let(x,y)=x-g.x-g_inner.x,y-g.y+g_inner.yinmatchgroupwith|None->(x,y)|Someg->g#of_canvas_coords~x~y(**/**)method!freeze=super#freeze;List.iter(func->c.Container.data#freeze)childrenmethod!unfreeze=super#unfreeze;List.iter(func->c.Container.data#unfreeze)childrenmethod!get_item_at~x:x_~y:y_=ifG.inside~x:x_~y:y_gthen(letx=x_-g.x-g_inner.xinlety=y_-g.y-g_inner.yinletreciter=function|[]->Some(self#as_full_item)|c::q->matchc.Container.data#get_item_at~x~ywith|None->iterq|x->xiniterchildren)elseNonemethod!get_leaf_items_atacc~x:x_~y:y_=ifG.inside~x:x_~y:y_gthen(letx=x_-g.x-g_inner.xinlety=y_-g.y-g_inner.yinList.fold_left(funaccc->c.Container.data#get_leaf_items_atacc~x~y)accchildren)elseaccmethod!on_items_atf~x:x_~y:y_=[%debug"%s#on_items_at ~x:%d ~y:%d"self#mex_y_];letx=x_-g.x-g_inner.xinlety=y_-g.y-g_inner.yinletreciter=function|[]->Lwt.return_false|c::q->leti=c.Container.datain[%debug"%s#on_items_at i=%s i#g=%a x=%d, y=%d"self#mei#meG.ppi#geometryxy];ifG.inside~x~yi#geometrythenmatch%lwtfiwith|false->i#on_items_atf~x~y|true->Lwt.return_trueelseiterqiniterchildren(**/**)methodwidth=letw=matchBounds.max_elt_opthboundswith|None->0|Some(w,_)->winsuper#min_width_+wmethodheight=leth=matchBounds.max_elt_optvboundswith|None->0|Some(h,_)->hinsuper#min_height_+h(**/**)method!privatemin_width_=self#widthmethod!privatemin_height_=self#heightmethodupdate_bounds~recur=ifrecurthenList.iter(func->c.Container.data#update_bounds~recur)children;self#update_hbound;self#update_vbound;self#set_geometry{gwithw=self#width;h=self#height}methodupdate_hbound=hbounds<-List.fold_left(funaccc->leti=c.Container.datainletg=i#geometryinBounds.add(g.G.x+g.w,i#id)acc)Bounds.emptychildren;methodupdate_vbound=vbounds<-List.fold_left(funaccc->leti=c.Container.datainletg=i#geometryinBounds.add(g.G.y+g.h,i#id)acc)Bounds.emptychildrenmethodset_item_hbound(oldg:G.t)id(g:G.t)=[%debug"%s#set_item_hbound oldg=%a id=%a g=%a"self#meG.ppoldgOid.ppidG.ppg];letmy_oldg=self#geometryinletoldw=self#widthinlets=Bounds.remove(oldg.x+oldg.w,id)hboundsinhbounds<-Bounds.add(g.x+g.w,id)s;self#set_geometry{my_oldgwithw=self#width};letw=self#widthin[%debug"%s#set_item_hbound oldw=%d, w=%d, id=%a\nbounds=%s"self#meoldwwOid.ppid(String.concat", "(List.map(fun(x,id)->Printf.sprintf"(%d,%s)"x(Oid.to_stringid))(Bounds.elementshbounds)))];ifoldw<>wthenmatchgroupwith|Somegroup->group#set_item_hboundmy_oldgself#idself#geometry;ifnotfrozenthenself#need_render~layer:(self#get_pProps.layer)g|None->ifnotfrozenthensuper#need_resizemethodset_item_vbound(oldg:G.t)id(g:G.t)=[%debug"%s#set_item_vbound oldg=%a id=%a g=%a"self#meG.ppoldgOid.ppidG.ppg];letmy_oldg=self#geometryinletoldh=self#heightinlets=Bounds.remove(oldg.y+oldg.h,id)vboundsinvbounds<-Bounds.add(g.y+g.h,id)s;self#set_geometry{my_oldgwithh=self#height};leth=self#heightin[%debug"%s#set_item_vbound oldh=%d, h=%d, id=%a\nbounds=%s"self#meoldhhOid.ppid(String.concat", "(List.map(fun(y,id)->Printf.sprintf"(%d,%s)"y(Oid.to_stringid))(Bounds.elementsvbounds)))];ifoldh<>hthenmatchgroupwith|Somegroup->group#set_item_vboundmy_oldgself#idself#geometry;ifnotfrozenthenself#need_render~layer:(self#get_pProps.layer)g|None->ifnotfrozenthensuper#need_resize(**/**)(** Adds the given item to group. The item's coordinates
can be set with [x] and [y] optional arguments. *)methodadd_item?x?y(i:full_item)=matchsuper#addi#coerceiwith|false->()|true->i#set_group(Some(self:>group));matchx,ywith|None,None->i#set_xy(i#x,i#y)|Somex,None->i#set_xx|None,Somey->i#set_yy|Somex,Somey->i#set_xy(x,y)(** Removes the given item from the group. *)methodremove_item(i:full_item)=[%debug"%s#remove_item %s"self#mei#me];matchsuper#removei#coercewith|false->()|true->letg0=self#geometryini#set_group_none;let_g=i#geometryinself#update_bounds~recur:false;letg1=self#geometryinLog.info(funm->m"%s#remove_item %s: g0=%a, g1=%a"self#mei#meG.ppg0G.ppg1);ifg0<>g1thenself#update_bounds_in_parent_group~g0~g1(**/**)valmutablechild_need_render_cb=(None:(layer:Layer.t->G.t->unit)option)methodset_child_need_render_cbx=child_need_render_cb<-xmethodchild_need_render~layergeom=ifnotfrozenthen([%debug"%s#child_need_render: geom=%a"self#meG.ppgeom];letg=self#geometryinletg_inner=self#g_innerinletgeom={geomwithG.x=geom.G.x+g.x+g_inner.x;y=geom.y+g.y+g_inner.y;}inself#need_render~layergeom)methodneed_render~layergeom=matchgroupwith|Somegroup->group#child_need_render~layergeom|None->matchchild_need_render_cbwith|None->()|Somef->f~layergeom(**/**)(** Raises the given item above all the other items of the group. *)methodraise(item:full_item)=matchself#widget_dataitem#coercewith|None->()|Somedata->super#removeitem#coerce;ignore(super#additem#coercedata)end(** An item to draw a rectangle.
Its kind is ["rect"]. Width and height of the
rectangle can be specified with optional arguments [w] and [h]. *)classrect?classes?name?props?wdata?group?x?y~w~h()=object(self)inheritfull_item?classes?name?props?wdata?group?x?y()assuper(**/**)methodkind="rect"valmutableh=hvalmutablew=wmethodprivateset_widthw_=w<-w_methodprivateset_heighth_=h<-h_method!privatemin_width_=wmethod!privatemin_height_=hmethod!max_width=Somewmethod!max_height=Somehmethod!geometry={gwithh;w}method!set_geometryg=super#set_geometry{gwithh;w}(**/**)methodresize?w?h()=letg0=self#geometryinOption.iterself#set_widthw;Option.iterself#set_heighth;self#set_geometryg;letg1=self#geometryinself#update_bounds_in_parent_group~g0~g1(**/**)method!is_leaf_widget=true(**/**)initializerself#set_geometry{gwithw;h};end(* From https://www.geeksforgeeks.org/cubic-bezier-curve-implementation-in-c/ *)letbezier_points=letpow=Float.powinletfpu=letu'=1.-.uinletr=(powu'3.)*.p.(0)+.3.*.u*.(powu'2.)*.p.(1)+.3.*.u'*.(powu2.)*.p.(2)+.(powu3.)*.p.(3)intruncaterinletrecsplinesteppoints_xpoints_yaccmin_xmin_ymax_xmax_ylast_xlast_yu=ifu>1.then(acc,min_x,min_y,max_x,max_y,last_x,last_y)elseletx=fpoints_xuinlety=fpoints_yuinifx=last_x&&y=last_ythensplinesteppoints_xpoints_yaccmin_xmin_ymax_xmax_ylast_xlast_y(u+.step)elseletmin_x=minmin_xxinletmin_y=minmin_yyinletmax_x=maxmax_xxinletmax_y=maxmax_yyinsplinesteppoints_xpoints_y((x,y)::acc)min_xmin_ymax_xmax_yxy(u+.step)inletrecsplinesstepaccmin_xmin_ymax_xmax_ylast_xlast_y=function|[_]->(acc,min_x,min_y,max_x,max_y)|(x1,y1)::(x2,y2)::(x3,y3)::(x4,y4)::q->let(acc,min_x,min_y,max_x,max_y,last_x,last_y)=letpoints_x=[|floatx1;floatx2;floatx3;floatx4|]inletpoints_y=[|floaty1;floaty2;floaty3;floaty4|]insplinesteppoints_xpoints_yaccmin_xmin_ymax_xmax_ylast_xlast_y0.insplinesstepaccmin_xmin_ymax_xmax_ylast_xlast_y((x4,y4)::q)|_->assertfalseinfunpoints->matchList.lengthpointswith|nwhennmod3<>1->Log.warn(funm->m"Invalid Bezier curve points: [| %s |]"(String.concat", "(List.map(fun(x,y)->Printf.sprintf"(%d, %d)"xy)points)));None|len->let(min_x,min_y,max_x,max_y)=List.fold_right(fun(x,y)(min_x,min_y,max_x,max_y)->(minxmin_x,minymin_y,maxxmax_x,maxymax_y))points(max_int,max_int,min_int,min_int)inletw=max_x-min_xinleth=max_y-min_yinletsteps=50*maxwhinletstep=1./.(floatsteps)inlet(p,min_x,min_y,max_x,max_y)=splinesstep[]max_intmax_intmin_intmin_intmin_intmin_intpointsinletg={G.x=min_x;y=min_y;w=max0(max_x-min_x+1);h=max0(max_y-min_y+1);}in(* normalize by translating all points by -min{x,y} *)letp=List.rev_map(fun(x,y)->(x-min_x,y-min_y))pinSome(g,p)classbezier_curve?classes?name?props?wdata?group?x?y?points()=object(self)inheritfull_item?classes?name?props?wdata?group?x?y()assuper(**/**)methodkind="bezier_curve"valmutablerpoints=Nonevalmutableh=0valmutablew=0method!privatemin_width_=wmethod!privatemin_height_=hmethod!max_width=Somewmethod!max_height=Somehmethod!geometry={gwithh;w}method!set_geometryg=super#set_geometry{gwithh;w}(**/**)methodset_pointsp=letg0=self#geometryinletg1=matchpwith|None->w<-0;h<-0;rpoints<-None;g|Somep->matchbezier_pointspwith|None->rpoints<-None;g|Some(bounds,rp)->w<-bounds.w;h<-bounds.h;(*Log.warn (fun m -> m "bezier curve: w=%d, h=%d" w h);*)rpoints<-Some(p,rp);boundsinself#set_geometryg1;self#update_bounds_in_parent_group~g0~g1methodprivatedraw_pointsrend~offset:(x,y)=matchrpointswith|None->()|Some(spec,p)->letox=x+g.xinletoy=y+g.yinletopenMiscin(*let (x0, y0) = List.hd p in
let (x3, y3) = List.hd (List.rev p) in
Log.warn (fun m -> m "%s: x0=%d, y0=%d, x3=%d, y3=%d" self#me x0 y0 x3 y3);
Render.draw_circle rend ~x:(ox+x0) ~y:(oy+y0) ~r:2 Color.green ;
Render.draw_circle rend ~x:(ox+x3) ~y:(oy+y3) ~r:2 Color.red ;*)List.iter(fun(x,y)->let>()=Sdl.render_draw_pointrend(ox+x)(oy+y)in())pmethodprivatedraw_curverend~offset:(x,y)rg=matchpointswith|None->()|Somep->letclip=G.translate~x~yrginletfrend=Render.with_colorrendself#fg_color(self#draw_points~offset:(x,y))in(*Log.warn (fun m -> m "%s: clip %a, g=%a, offset=%d,%d" self#me G.pp clip G.pp g x y) ;*)Render.with_cliprend(G.to_rectclip)fmethodrender~layerrend~offsetgeom=ifself#visible&&layer=self#get_pProps.layerthen(matchself#need_renderinggeomwith|None->()|Somerg->self#draw_curverend~offsetrg;ifnotself#sensitivethenself#render_insensitiverend~offsetrg)(**/**)method!is_leaf_widget=true(**/**)initializerself#set_pointspointsend(** Widgets inheriting a widget and an {!class-item} keeps
the original widget kind but can be identified specifically
in theming for example by using ["canvas > <kind>"]. *)(** A {!Bin.class-bin} as item. *)classbin?classes?name?props?wdata?group?x?y()=object(self)inheritBin.bin?classes?name?props?wdata()asbininherititem?group?x?y()assupermethodas_full_item=(self:>full_item)methodset_geometryg=bin#set_geometrygend(** A {!Text.class-label} as item. *)classlabel?classes?name?props?wdata?group?x?y()=object(self)inheritText.label?classes?name?props?wdata()aswidgetinherititem?group?x?y()assupermethodas_full_item=(self:>full_item)end(** A {!Text.class-glyph} as item. *)classglyph?classes?name?props?wdata?group?x?y()=object(self)inheritText.glyph?classes?name?props?wdata()aswidgetinherititem?group?x?y()assupermethodas_full_item=(self:>full_item)end(** A {!Pack.class-box} as item. *)class['a]box?classes?name?props?wdata?group?x?y()=object(self)inherit['a]Pack.box?classes?name?props?wdata()aswidgetinherititem?group?x?y()assupermethodas_full_item=(self:>full_item)methodset_geometryg=widget#set_geometrygend(** A {!Pack.class-paned} as item. *)classpaned?classes?name?props?wdata?group?x?y()=object(self)inheritPack.paned?classes?name?props?wdata()aswidgetinherititem?group?x?y()assupermethodas_full_item=(self:>full_item)methodset_geometryg=widget#set_geometrygend(** A {!Bin.class-fixed_size} as item. *)classfixed_size?classes?name?props?wdata?group?x?y?w?h()=object(self)inheritBin.fixed_size?classes?name?props?wdata?w?h()aswidgetinherititem?group?x?y()assupermethodas_full_item=(self:>full_item)methodset_geometryg=widget#set_geometrygend(** A {!Flex.class-flex} as item. *)class['a]flex?classes?name?props?wdata?group?x?y()=object(self)inherit['a]Flex.flex?classes?name?props?wdata()asflexinherititem?group?x?y()assupermethodas_full_item=(self:>full_item)methodset_geometryg=flex#set_geometrygend(** {2:convenient_functions Convenient functions to create items}
These convenient functions are used to create items.
They all take [x] and [y] optional arguments to
specify the coordinates of the created item, and
[group] to specify the group the item belongs to.
Other arguments are {{!Widget.widget_arguments}generic widget arguments}
or arguments specific to the kind of item.
*)(** Creates a {!class-group}. *)letgroup?classes?name?props?wdata?group?x?y()=newgroup?classes?name?props?wdata?group?x?y()(** Creates a {!class-rect}. *)letrect?classes?name?props?wdata?group?x?y~w~h()=newrect?classes?name?props?wdata?group?x?y~w~h()(** Creates a {!class-bezier_curve}. *)letbezier_curve?classes?name?props?wdata?group?x?y?points()=newbezier_curve?classes?name?props?wdata?group?x?y?points()(** Creates a {!class-bin}. *)letbin?classes?name?props?wdata?group?x?y()=lett=newbin?classes?name?props?wdata?group?x?y()int(** Creates a {!class-label}. *)letlabel?classes?name?props?wdata?group?x?ytext=lett=newlabel?classes?name?props?wdata?group?x?y()int#set_texttext;t(** Creates a {!class-glyph}. *)letglyph?classes?name?props?wdata?group?x?ygly=lett=newglyph?classes?name?props?wdata?group?x?y()int#set_glyphgly;t(** Creates a {!class-box} with vertical orientation.*)letvbox?classes?name?props?wdata?group?x?y()=letw=newbox?classes?name?props?wdata?group?x?y()inw#set_orientationProps.Vertical;w(** Creates a {!class-box} with horizontal orientation.*)lethbox?classes?name?props?wdata?group?x?y()=letw=newbox?classes?name?props?wdata?group?x?y()inw#set_orientationProps.Horizontal;w(** Creates a {!class-paned} with vertical orientation. *)letvpaned?classes?name?props?wdata?group?x?y()=letw=newpaned?classes?name?props?wdata?group?x?y()inw#set_orientationProps.Vertical;w(** Creates a {!class-paned} with horizontal orientation. *)lethpaned?classes?name?props?wdata?group?x?y()=letw=newpaned?classes?name?props?wdata?group?x?y()inw#set_orientationProps.Horizontal;w(** Creates a {!class-fixed_size}.*)letfixed_size?classes?name?props?wdata?group?x?y?w?h()=newfixed_size?classes?name?props?wdata?group?x?y?w?h()(** Creates a {!class-flex}.*)letflex?classes?name?props?wdata?group?x?y?(orientation=Props.Horizontal)?justification?items_alignment?content_alignment?inter_space?wrap?wrap_on_break?collapse_spaces()=letw=newflex?classes?name?props?wdata?group?x?y()inw#set_orientationorientation;Option.mapw#set_justificationjustification;Option.mapw#set_items_alignmentitems_alignment;Option.mapw#set_content_alignmentcontent_alignment;Option.mapw#set_inter_spaceinter_space;Option.mapw#set_wrapwrap;Option.mapw#set_wrap_on_breakwrap_on_break;Option.mapw#set_collapse_spacescollapse_spaces;w(** {2 Canvas widget} *)classcanvas?classes?name?props?wdata()=object(self)inheritWidget.widget?classes?name?props?wdata()assuper(**/**)methodkind="canvas"valmutableroot=newgroup()method!do_apply_theme~root:rprops~parentparent_pathrules=super#do_apply_theme~root:rprops~parentparent_pathrules;letpath=self#css_path~parent_path()inroot#do_apply_theme~root:rprops~parent:theme_propspathrules;min_width<-None;min_height<-None(**/**)(** Returns root group. *)methodroot=root(** Sets root group. *)methodset_rootr=root#set_child_need_render_cbNone;root<-r;r#set_parent?with_rend:with_renderer(Someself#coerce);root#set_child_need_render_cb(Someself#child_need_render)(** Freezes, removes all items from root group and unfreezes. *)methodclear=self#freeze;root#clear;self#unfreeze(** Returns item at the given coordinates, if any. Coordinates have
the same origin as the canvas coordinates. *)methodget_item_at~x~y=letg=root#geometryinroot#get_item_at~x:(x-g.x-g_inner.x)~y:(y-g.y-g_inner.y)(** Get leaf items at the given coordinates. Coordinates have
the same origin as the canvas coordinates. *)methodget_leaf_items_at~x~y=letg=root#geometryinletitems=root#get_leaf_items_at[]~x:(x-g.x-g_inner.x)~y:(y-g.y-g_inner.y)in[%debug"%s#get_leaf_items => %s"self#me(String.concat", "(List.map(funi->i#me)items))];items(**/**)method!wtree=Widget.N(self#coerce,[root#wtree])valmutablefrozen_to_render=(None:G.toption)method!freeze=frozen_to_render<-SomeG.zero;root#freezemethod!unfreeze=matchfrozen_to_renderwith|None->()|Somerg->frozen_to_render<-None;root#unfreeze;root#update_bounds~recur:true;self#need_resize;self#need_render~layer:(self#get_pProps.layer)rgmethod!child_need_render~layergeom=letgeom=letg=self#geometryinletg_inner=self#g_innerin{geomwithx=geom.x+g.x+g_inner.x;y=geom.y+g.y+g_inner.y}inmatchfrozen_to_renderwith|None->self#need_render~layergeom|Somerg->frozen_to_render<-Some(G.unionrggeom)method!privatemin_width_=letw=super#min_width_+root#widthin[%debug"%s#min_width => %d"self#mew];wmethod!privatemin_height_=leth=super#min_height_+root#heightin[%debug"%s#min_height => %d"self#meh];hmethod!render_me~layerrend~offset:(x,y)rg=let(x,y)=(x+g.x+g_inner.x,y+g.y+g_inner.y)inletrg=G.translate~x:(-g.x-g_inner.x)~y:(-g.y-g_inner.y)rginroot#render~layerrend~offset:(x,y)rgmethod!set_geometryg=letg={gwithw=maxself#min_widthg.w;h=maxself#min_heightg.h}in[%debug"%s#set_geometry with g=%a"self#meG.ppg];super#set_geometryg(* propagate only events with position = Some (mouse events)
or keyboard related events (sent to the widget only if
is_focus property is true. *)method!on_sdl_event_down~oldpospose=ifself#sensitivethenletb=letpass_event=matchSdl.Event.(enum(getetyp))with|`Key_down|`Key_up|`Text_input|`Text_editing->true|_->pos<>Noneinifpass_eventthenletf(x,y)=(x-g.x-g_inner.x,y-g.y-g_inner.y)inletchild_oldpos=Option.mapfoldposinletchild_pos=Option.mapfposinroot#on_sdl_event_down~oldpos:child_oldposchild_poseelsefalsein(*Log.warn (fun m -> m "%s#on_sdl_event_down => return %b" self#me b);*)matchbwith|true->true|false->self#on_sdl_eventposeelsefalsemethod!release_focus=matchroot#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->root#set_has_focusbmethod!grab_focus?(last=false)()=[%debug"%s#grab_focus ~last:%b"self#melast];ifself#visiblethenmatchself#get_pProps.focusablewith|true->[%debug"%s#grab_focus: i'm focusable"self#me];letb=matchself#get_focuswith|None->false|Some_->truein[%debug"%s#grab_focus: get_focus => %b"self#meb];b|_->matchself#get_pProps.can_focuswith|false->false|true->root#grab_focus~last()elsefalseinitializerself#set_rootroot;end(** Convenient function to create a {!class-canvas}.
See {!Widget.widget_arguments} for arguments. *)letcanvas?classes?name?props?wdata?pack()=letw=newcanvas?classes?name?props?wdata()inWidget.may_pack?packw#coerce;w