123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183(* Select list *)(* based on the Menu module *)(* This is a simple select list with no submenus *)(* TODO highlight the selected entry on OPENING the menu. (standard behaviour,
cf: https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_select ).
But with the current implementation, this is not so obvious. We probably have
to modify Menu.
Example: 28
*)(* TODO: scroll when navigating with keyboard *)openB_utilsmoduleLayout=B_layoutmoduleWidget=B_widgetmoduleVar=B_varmoduleLabel=B_labelmoduleMenu=B_menumoduleSync=B_sync(* module Print = B_print *)letpre=if!debugthenfuns->print_endline("[Select] "^s)(* for local debugging *)elsenop(* We will create a menu with a unique entry, being a submenu. This function
returns the submenu. *)letget_submenumenu=letopenMenu.Enginein(* pre (Printf.sprintf "#entries=%u" (List.length menu.entries)); *)matchmenu.entrieswith|[entry]->beginmatchentry.kindwith|Menusub->Somesub|Action_->Noneend|_->None(* We construct a simple Menu with a custom Layout for the main entry, and
automatically generated labels for the menu entries. Using a custom layout
makes it easier to modify its text, but in principle we could also use the
automatically generated layout and recover its resident widget. *)letnew_id=fresh_int()(* Return [dst] (created if necessary). [name] will be the name of the selected
entry, not the name of the whole [dst] layout. *)letcreate?dst?name?(action=fun_->())?fgentriesselected=letname=matchnamewith|Somename->name|None->"selected_"^(string_of_int(new_id()))inletselected=Var.createselectedinletaction=Var.createactionin(* let background = Layout.Solid(Draw.(transp white)) in *)letselected_widget=Widget.label?fgentries.(Var.getselected)inletselected_layout=Layout.flat_of_w~name(* ~background *)~sep:0[selected_widget]inletselected_label=Widget.get_labelselected_widgetinletentries=Array.to_listentries|>List.mapi(funis->letaction()=Label.setselected_labelentries.(i);Var.setselectedi;Var.getactioniinMenu.{label=Texts;content=Actionaction})inletentry=Menu.{label=Layoutselected_layout;content=Towerentries}in(* First pass just to obtain the menu width. This could probably be done more
economically, but well... *)letmenu=Menu.(raw_engine(Flat[entry]))inletsubmenu=matchget_submenumenuwith|Somes->s|None->failwith"Menu should have a unique submenu"in(* The main entry should have the width of the menu *)letw=Layout.width(Menu.layout_of_menusubmenu)inletmenu_layout=Menu.layout_of_menumenuin(* Here the structure of menu_layout is
[ menu_layout =
[ formatted_label =
[ selected_layout = [label "banana"]] [ caret-down ];
]
]
*)(* pre (Print.layout_down menu_layout); *)List.iter(funl->Layout.set_widthl(w-Menu.suffix_width))[menu_layout;Layout.get_roomsmenu_layout|>List.hd;selected_layout;Layout.get_roomsselected_layout|>List.hd;];(* Now the principal pass with the corrected entry. *)letmenu=Menu.(raw_engine(Flat[entry]))inletmenu_layout=Menu.layout_of_menumenuinletsubmenu=matchget_submenumenuwith|Somes->s|None->failwith"Menu should have a unique submenu"inlettmp_dst=matchdstwith|Somer->r|None->(* Just a horizontal line *)letline=Widget.empty~w~h:1()in(* let background = Layout.Solid(Draw.(transp grey)) in *)(* DEBUG *)Layout.flat_of_w~sep:0(* ~background *)[line]inMenu.Engine.init~dst:tmp_dstmenu;ifdst=NonethenbeginLayout.set_heighttmp_dst(Layout.heightmenu_layout);(* We need to relocate to the top layout *)(fun()->(* pre "RELOCATE!"; *)letroom=Menu.layout_of_menusubmenuinletscreen=Layout.get_roomstmp_dst|>List.rev|>List.hdin(* We move the submenu layout to the top layout, otherwise it will be
clipped by its house; unfortunately, the menu and the submenu end up
being in different houses, so we have to recode the resize
function. If the menu is too big, we add a scrollbar. Note that,
currently, this has the effect of hiding the shadow. TODO: correct
this... TODO?[see Layout.here(++)] currently if the menu is small
enough, we don't add a scrollbar, and hence the scrollbar will not
magically happen if we shrink the window...*)letnew_room=Layout.relocate~scroll:true~dst:(Layout.top_housetmp_dst)roominletresize(_w,h)=letopenLayoutinletopenResizeinletx,y=compute_posmenu_layoutinlethm=heightmenu_layoutinsetxnew_roomx;setynew_room(y+hm);(* if h-y-hm > height new_room
* then print_endline "One could enlarge menu!!"; *)set_heightnew_room(imin(heightroom)(h-y-hm))innew_room.Layout.resize<-resize;(* We expand the screen to full size: *)letscreen=Layout.relocate~scroll:false~dst:(Layout.top_housetmp_dst)screeninLayout.maximizescreen;Layout.resize_follow_housescreen;ifnotLayout.(new_room==room)thenbeginMenu.set_layoutsubmenunew_room;Layout.hide~duration:0new_roomend;(* pre (Print.layout_down screen);
* pre (Print.layout_down new_room); *)(* pre (Print.layout_down (Layout.top_house screen)); *)(* TODO si on crée plusieurs select dans la même page, le deuxième va être
tracé sur un layer + élevé que celui du screen du premier select... et
donc il va PAS être caché correctement par ce screen: bref le premier
menu ne va pas se fermer quand on clique sur le deuxième... A la place
on pourrait créer le screen dynamiquement quand on clique sur le menu.
*))|>Sync.push;endelseiftruethenbegin(* TODO parameter adjust = true*)letw,h=Layout.get_size(Menu.layout_of_menusubmenu)inLayout.set_sizetmp_dst~w~h:(Layout.heightmenu_layout+h);(* TODO ou plutôt faire un relocate, comme au-dessus, mais dans le dst *)(* on peut aussi fournir en sortir la fonction qui fait le relocate dans un
layout de son choix, qu'on n'est pas obligé de construire exprès --cf
examples/displays *)end;tmp_dst