123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465moduleMake(LiteralIntf:LiteralIntf.Type)=structopenLTerm_geomclasst=LTerm_widget_base_impl.texceptionOut_of_rangeletrecinsertxln=ifn<0thenraiseOut_of_rangeelseifn=0thenx::lelsematchlwith|[]->raiseOut_of_range|y::l->y::insertxl(n-1)typebox_child={widget:t;expand:bool;mutablelength:int;}classtypebox=objectinherittmethodadd:?position:int->?expand:bool->#t->unitmethodremove:#t->unitendclassvirtualaboxrc=object(self)inherittrcassupervalmutablechildren=[]method!children=List.map(funchild->child.widget)childrenvalmutablesize_request={rows=0;cols=0}method!size_request=size_requestmethodprivatevirtualcompute_allocations:unit(* Compute sizes of children. *)methodprivatevirtualcompute_size_request:unit(* Compute the size request. *)method!set_allocationrect=super#set_allocationrect;self#compute_allocationsmethodadd:'a.?position:int->?expand:bool->(#tas'a)->unit=fun?position?(expand=true)widget->letchild={widget=(widget:>t);expand=expand;length=0;}in(matchpositionwith|Somen->children<-insertchildchildrenn|None->children<-children@[child]);widget#set_parent(Some(self:>t));self#compute_size_request;self#compute_allocations;self#queue_drawmethodremove:'a.(#tas'a)->unit=funwidget->children<-List.filter(funchild->ifchild.widget=(widget:>t)then(child.widget#set_parentNone;false)elsetrue)children;self#compute_size_request;self#compute_allocations;self#queue_drawendclasshbox=object(self)inheritabox"hbox"methodprivatecompute_size_request=size_request<-(List.fold_left(funaccchild->letsize=child.widget#size_requestin{rows=maxacc.rowssize.rows;cols=acc.cols+size.cols}){rows=0;cols=0}children)methodprivatecompute_allocations=letrect=self#allocationinletcols=rect.col2-rect.col1inlettotal_requested_cols=List.fold_left(funaccchild->acc+child.widget#size_request.cols)0childreniniftotal_requested_cols<=colsthenbegin(* There is enough space for everybody, we split free space
between children that can expand. *)(* Count the number of children that can expand. *)letcount_can_expand=List.fold_left(funaccchild->ifchild.expandthenacc+1elseacc)0childrenin(* Divide free space between these children. *)letwidthf=ifcount_can_expand=0then0.elsefloat(cols-total_requested_cols)/.floatcount_can_expandinletrecloopcolf=function|[]->()|[child]->letwidth=cols-truncatecolfinchild.length<-width|child::rest->letreq_cols=child.widget#size_request.colsinifchild.expandthenbeginletcol=truncatecolfinletwidth=req_cols+truncate(colf+.widthf)-colinchild.length<-width;loop(colf+.floatreq_cols+.widthf)restendelsebeginchild.length<-req_cols;loop(colf+.floatreq_cols)restendinloop0.childrenendelsebegin(* There is not enough space for everybody. *)iftotal_requested_cols=0thenList.iter(funchild->child.length<-0)childrenelseletrecloopcol=function|[]->()|[child]->letwidth=cols-colinchild.length<-width|child::rest->letwidth=child.widget#size_request.cols*cols/total_requested_colsinchild.length<-width;loop(col+width)restinloop0childrenend;ignore(List.fold_left(funcolchild->child.widget#set_allocation{row1=rect.row1;col1=col;row2=rect.row2;col2=col+child.length;};col+child.length)rect.col1children)method!drawctxfocused=letrect=self#allocationinletrecloopcolchildren=matchchildrenwith|[]->()|child::rest->child.widget#draw(LTerm_draw.subctx{row1=0;col1=col;row2=rect.row2-rect.row1;col2=col+child.length;})focused;loop(col+child.length)restinloop0childrenendclassvbox=object(self)inheritabox"vbox"methodprivatecompute_size_request=size_request<-(List.fold_left(funaccchild->letsize=child.widget#size_requestin{rows=acc.rows+size.rows;cols=maxacc.colssize.cols}){rows=0;cols=0}children)methodprivatecompute_allocations=letrect=self#allocationinletrows=rect.row2-rect.row1inlettotal_requested_rows=List.fold_left(funaccchild->acc+child.widget#size_request.rows)0childreniniftotal_requested_rows<=rowsthenbegin(* There is enough space for everybody, we split free space
between children that can expand. *)(* Count the number of children that can expand. *)letcount_can_expand=List.fold_left(funaccchild->ifchild.expandthenacc+1elseacc)0childrenin(* Divide free space between these children. *)letheightf=ifcount_can_expand=0then0.elsefloat(rows-total_requested_rows)/.floatcount_can_expandinletreclooprowf=function|[]->()|[child]->letheight=rows-truncaterowfinchild.length<-height|child::rest->letreq_rows=child.widget#size_request.rowsinifchild.expandthenbeginletrow=truncaterowfinletheight=req_rows+truncate(rowf+.heightf)-rowinchild.length<-height;loop(rowf+.floatreq_rows+.heightf)restendelsebeginchild.length<-req_rows;loop(rowf+.floatreq_rows)restendinloop0.childrenendelsebegin(* There is not enough space for everybody. *)iftotal_requested_rows=0thenList.iter(funchild->child.length<-0)childrenelseletreclooprow=function|[]->()|[child]->letheight=rows-rowinchild.length<-height|child::rest->letheight=child.widget#size_request.rows*rows/total_requested_rowsinchild.length<-height;loop(row+height)restinloop0childrenend;ignore(List.fold_left(funrowchild->child.widget#set_allocation{row1=row;col1=rect.col1;row2=row+child.length;col2=rect.col2;};row+child.length)rect.row1children)method!drawctxfocused=letrect=self#allocationinletreclooprowchildren=matchchildrenwith|[]->()|child::rest->child.widget#draw(LTerm_draw.subctx{row1=row;col1=0;row2=row+child.length;col2=rect.col2-rect.col1;})focused;loop(row+child.length)restinloop0childrenendclassframe=object(self)inheritt"frame"assupervalmutablechild=Nonemethod!children=matchchildwith|Somewidget->[widget]|None->[]valmutablesize_request={rows=2;cols=2}method!size_request=size_requestvalmutablestyle=LTerm_style.nonevalmutableconnection=LTerm_draw.Lightmethod!update_resources=letrc=self#resource_classandresources=self#resourcesinstyle<-LTerm_resources.get_stylercresources;connection<-LTerm_resources.get_connection(rc^".connection")resourcesmethodprivatecompute_size_request=matchchildwith|Somewidget->letsize=widget#size_requestinsize_request<-{rows=size.rows+2;cols=size.cols+2}|None->size_request<-{rows=2;cols=2}methodprivatecompute_allocation=matchchildwith|Somewidget->letrect=self#allocationinletrow1=minrect.row2(rect.row1+1)andcol1=minrect.col2(rect.col1+1)inwidget#set_allocation{row1=row1;col1=col1;row2=maxrow1(rect.row2-1);col2=maxcol1(rect.col2-1);}|None->()method!set_allocationrect=super#set_allocationrect;self#compute_allocationmethodset:'a.(#tas'a)->unit=funwidget->child<-Some(widget:>t);widget#set_parent(Some(self:>t));self#compute_size_request;self#compute_allocation;self#queue_drawmethodempty=matchchildwith|Somewidget->widget#set_parentNone;child<-None;self#compute_size_request;self#queue_draw|None->()valmutablelabel=Zed_string.empty()valmutablealign=H_align_leftmethodset_label?(alignment=H_align_left)l=label<-LiteralIntf.to_string_exnl;align<-alignmentmethod!drawctxfocused=letsize=LTerm_draw.sizectxinLTerm_draw.fill_stylectxstyle;ifsize.rows>=1&&size.cols>=1thenbeginletrect={row1=0;col1=0;row2=size.rows;col2=size.cols}in(ifZed_string.byteslabel=0thenLTerm_draw.draw_framectxrectconnectionelseLTerm_draw.draw_frame_labelledctxrect~alignment:alignlabelconnection);ifsize.rows>2&&size.cols>2thenmatchchildwith|Somewidget->widget#draw(LTerm_draw.subctx{row1=1;col1=1;row2=size.rows-1;col2=size.cols-1})focused|None->()endendclassmodal_frame=object(self)inheritframevalmutablework_area=Nonemethod!privatecompute_allocation=matchchildwith|Somewidget->(* The desired layout is as following:
*
* ..............................
* . .
* . --------------------- .
* . || || .
* . || child widget is || .
* . || centered || .
* . || || .
* . --------------------- .
* . .
* ..............................
*)letrect=self#allocationin(* First find out how much space we have *)letalloc_height=rect.row2-rect.row1inletalloc_width=rect.col2-rect.col1in(* Then how much child widget wants *)letrequest=widget#size_requestin(* Now we calculate how big margins could be, taking into account:
* - for vertical margin two lines of the frame and two empty lines
* between it and the child widget
* - for horizontal margin four lines of the frame and two empty lines
* between it and the child widget *)letmargin_height=max0(alloc_height-request.rows-4)/2inletmargin_width=max0(alloc_width-request.cols-6)/2in(* the child widget would like to be here (again taking into account
* frame lines and emty lines between frame and the child widget *)letdesired_row1=rect.row1+margin_height+2inletdesired_row2=desired_row1+request.rowsinletdesired_col1=rect.col1+margin_width+3inletdesired_col2=desired_col1+request.colsin(* make sure we stay inside the modal_frame *)(* Remember that right and left margins for the widget inside the frame
* are 3, and top and bottom margins are 2 *)letrow1=mindesired_row1(rect.row2-2)inletrow2=mindesired_row2(rect.row2-2)inletcol1=mindesired_col1(rect.col2-3)inletcol2=mindesired_col2(rect.col2-3)in(* now inform the child widget about its area *)widget#set_allocation{row1=row1;col1=col1;row2=row2;col2=col2;};(* modal_frame is not going to touch anything outside of the child
* widget and frame around *)work_area<-Some{row1=maxrect.row1(row1-2);row2=minrect.row2(row2+2);col1=maxrect.col1(col1-3);col2=minrect.col2(col2+3)};|None->()method!drawctxfocused=matchwork_areawith|None->()|Somearea->letwork_ctx=LTerm_draw.subctxareain(* modal_frame is drawing only inside centered area (the child widget
* and frame around) so create appropriate drawing context *)letsize=LTerm_draw.sizework_ctxinifsize.rows>=1&&size.cols>=1thenbeginLTerm_draw.fill_stylework_ctxstyle;LTerm_draw.clearwork_ctx;letwidth=area.col2-area.col1inletheight=area.row2-area.row1in(* outer part of the frame *)LTerm_draw.draw_framework_ctx{row1=0;col1=0;row2=height;col2=width}connection;(* inner part of the frame *)LTerm_draw.draw_framework_ctx{row1=0;col1=1;row2=height;col2=width-1}connection;ifsize.rows>4&&size.cols>6thenmatchchildwith|Somewidget->(* decorations around the child widget take 4 columns and 6
* rows *)letwidget_ctx=LTerm_draw.subwork_ctx{row1=2;row2=height-2;col1=3;col2=width-3}inwidget#drawwidget_ctxfocused|None->()endinitializerself#set_resource_class"modal_frame"endend