1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283(* ───── Points And Bounds ───── *)typepoint={x:int;y:int}letpp_pointppfp=Format.fprintfppf"(%d, %d)"p.xp.yletequal_pointab=a.x=b.x&&a.y=b.ytypebounds={x:int;y:int;width:int;height:int}letpp_boundsppfb=Format.fprintfppf"{x=%d; y=%d; w=%d; h=%d}"b.xb.yb.widthb.heightletequal_boundsab=a.x=b.x&&a.y=b.y&&a.width=b.width&&a.height=b.heighttypelocal_bounds={anchor:point;focus:point}letpp_local_boundsppflb=Format.fprintfppf"{anchor=%a; focus=%a}"pp_pointlb.anchorpp_pointlb.focusletequal_local_boundsab=equal_pointa.anchorb.anchor&&equal_pointa.focusb.focus(* ───── Selections ───── *)(* anchor_position is a thunk rather than a plain point so that scrollable
containers can install a callback that recomputes the anchor relative to the
current scroll offset. set_anchor replaces it with a constant. *)typet={mutableanchor_position:unit->point;mutablefocus:point;mutableis_active:bool;mutableis_dragging:bool;mutableis_start:bool;}letppppft=Format.fprintfppf"Selection(anchor=%a, focus=%a, active=%b, dragging=%b)"pp_point(t.anchor_position())pp_pointt.focust.is_activet.is_draggingletcreate?anchor_position~anchor~focus()=letanchor_position=Option.valueanchor_position~default:(fun()->anchor)in{anchor_position;focus;is_active=true;is_dragging=true;is_start=true;}(* ───── Position ───── *)letanchort=t.anchor_position()letfocust=t.focusletset_anchortp=t.anchor_position<-(fun()->p)letset_focustp=t.focus<-pletboundst=leta=anchortandf=focustinletx0=mina.xf.xandy0=mina.yf.yinletx1=maxa.xf.xandy1=maxa.yf.yin{x=x0;y=y0;width=x1-x0+1;height=y1-y0+1}(* ───── State ───── *)letis_activet=t.is_activeletset_is_activetv=t.is_active<-vletis_draggingt=t.is_draggingletset_is_draggingtv=t.is_dragging<-vletis_startt=t.is_startletset_is_starttv=t.is_start<-v(* ───── Coordinate Transformation ───── *)letto_localt~(origin:point)=leta=anchortandf=focustin{anchor={x=a.x-origin.x;y=a.y-origin.y};focus={x=f.x-origin.x;y=f.y-origin.y};}