Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file b_radiolist.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224(* a radiolist set of widgets *)(* This module offers a general way to connect a set of widgets so that thay
behave like a radiolist: at most one is selected at any time. Of course it
can also create from scratch a layout containg a standard radiolist with
round button and labels. *)(* TODO offer a horizontal layout too *)openTsdlopenB_utilsmoduleW=B_widgetmoduleLayout=B_layoutmoduleVar=B_varmoduleTrigger=B_triggermoduleDraw=B_drawmoduleCheck=B_checkmoduleUpdate=B_updatetypetoggle_widget={widget:W.t;trigger:Trigger.t;set_state:bool->unit;get_state:unit->bool}typet={index:(intoption)Var.t;(* the index of selected entry, starting form 0. *)widgets:(toggle_widget*toggle_widgetoption)array;(* For a standard radiolist, each entry of the [widgets] array is (check_button, label) *)layout:Layout.toption;(* This module optionally creates a layout containing the widgets *)click_on_label:bool;allow_no_choice:bool}(* Return all widgets that are active for selecting an entry. Useful for adding
further connections, cf Example30 *)letactive_widgetst=ift.click_on_labelthent.widgets|>Array.to_list|>List.map(fun(a,b)->matchbwith|Someb->[a.widget;b.widget]|None->[a.widget])|>List.flattenelset.widgets|>Array.to_list|>List.map(fun(a,_)->a.widget)(* Return the first toogle_widget of the selected entry, or None if nothing is
selected *)letget_buttont=map_option(Var.gett.index)(funi->fstt.widgets.(i))(* Note that [b] is redundant since it can be obtained via [fst t.widgets.(i)]
*)letselect_actiontib=matchb.get_state(),Var.gett.index<>Someiwith|true,true->(* We select a new choice *)do_option(get_buttont)(funold_w->old_w.set_statefalse);Var.sett.index(Somei);(*Update.push b*)|false,false->(* We deselect current choice *)ift.allow_no_choicethenVar.sett.indexNoneelseb.set_statetrue|true,false->(* we are already on the selected widget *)()|false,true->(* the click was probably invalid *)()letmake_connectionst=fori=0toArray.lengtht.widgets-1dolet(b,w)=t.widgets.(i)inletaction=select_actiontiinift.click_on_labelthendo_optionw(funw->letc=W.connect_mainw.widgetb.widget(fun___->b.set_state(not(b.get_state()));(* TODO ça pourrait dépendre du fonctionnement de b: mouse_down
ou up, etc...*)actionb)[w.trigger]inW.add_connectionw.widgetc);letw=defaultwbin(* if there is no [w] we use [b] itself as target of the connection *)letc'=W.connect_mainb.widgetw.widget(fun___->actionb)[b.trigger]inW.add_connectionb.widgetc'done(* If click_on_label is true, we add a connection on the label with
Trigger=mouse_button_down to un/select the radio button. Nothing prevents you
to do this by hand, hence not using this option. *)letcreate?selected?layout?(click_on_label=true)widgets=Array.iteri(funi(tw,two)->letstate=selected=Someiintw.set_statestate;do_optiontwo(funtw->tw.set_statestate))widgets;lett={index=Var.createselected;widgets;layout;click_on_label;allow_no_choice=(selected=None)}inmake_connectionst;t(* Create a radio-like behaviour by connecting a list of toggle_widgets *)letof_toggle_widgets?selectedtws=letwidgets=List.map(funtw->(tw,None))tws|>Array.of_listincreate?selected~click_on_label:falsewidgetslettoggle_widget_of_widgetwidget=letset_state,get_state,trigger=matchwidget.W.kindwith|W.Button_->W.set_statewidget,(fun()->W.get_statewidget),Trigger.E.mouse_button_up|W.Check_->W.set_statewidget,(fun()->W.get_statewidget),Trigger.E.mouse_button_down|W.Label_->(fun_->printd(debug_error)"[Radiolist] Cannot set state of a Label"),(fun()->printddebug_error"[Radiolist] Label does not have any state";false),Trigger.E.mouse_button_down|_->invalid_arg"[Radiolist.toggle_widget_of_widget] widget %i"(W.idwidget)in(* W.remove_trigger widget trigger; *)(* We remove possibly conflicting connections *){widget;trigger;set_state;get_state}(* from a bare Button.t *)letmake_button_widgetb=letopenWinletb=create_empty(Buttonb)inletpress=fun___->Button.press(get_buttonb)inletc=connect_mainbbpressTrigger.buttons_downinadd_connectionbc;letc=connect_mainbb(funb__->Button.mouse_enter(get_buttonb))[Trigger.mouse_enter]inadd_connectionbc;letc=connect_mainbb(funb__->Button.mouse_leave(get_buttonb))[Trigger.mouse_leave]inadd_connectionbc;b(* Create a radio-like behaviour from a list of already existing buttons. TODO
create layout! *)letof_buttons?selectedbuttons=lettws=List.mapmake_button_widgetbuttons|>List.maptoggle_widget_of_widgetinof_toggle_widgets?selectedtwsletof_widgets?selectedwidgets=lettws=List.maptoggle_widget_of_widgetwidgetsinof_toggle_widgets?selectedtws(* string -> label toggle_widget *)letmake_label?(click_on_label=true)entry=letl=W.labelentryinifclick_on_labelthenl.W.cursor<-Some(go(Draw.create_system_cursorSdl.System_cursor.hand));toggle_widget_of_widgetlletmake_check()=letstyle=Check.Circlein(* W.create_empty (W.Check (Check.create ~style ())) *)W.check_box~style()|>toggle_widget_of_widget(* Create widgets from string entries *)letmake_widgets?(click_on_label=true)entries=Array.map(funentry->(make_check(),Some(make_label~click_on_labelentry)))entries(* create a vertical (ie. standard) layout *)letvertical?(name="radiolist")?(click_on_label=true)?selectedentries=letwidgets=make_widgets~click_on_labelentriesinletlayout=Layout.tower~sep:0~margins:0~name(List.map(function|(b,Somel)->Layout.flat_of_w~sep:2~align:Draw.Center~resize:Layout.Resize.Disable[b.widget;l.widget]|_->invalid_arg"[Radiolist.vertical] this should not happen")(Array.to_listwidgets))|>Option.someincreate?selected?layout~click_on_labelwidgets(* get index of selected entry, or None *)letget_indext=Var.gett.index(* Set the selected entry to i and directly activate the button's connections *)letset_indextio=letioo,state=matchiowith|Somei->Somei,true|None->Var.gett.index,falseindo_optionioo(funi->let(b,_w)=t.widgets.(i)inb.set_statestate;select_actiontib;(* This will wake up the widget b even if it doesn't have mouse focus: *)Update.pushb.widget)(* another possibility, if using Update sounds like a bad idea, is to directly
wake the widget up with *)(* let e = Trigger.(create_event var_changed) in List.iter *)(* (W.wake_up e) b.W.connections;; *)(* but then it is possible that the connections be triggered too many times *)letlayoutt=matcht.layoutwith|Somel->l|None->printd(debug_error+debug_user)"This type of radiolist doesn't carry its own layout";failwith"This type of radiolist doesn't carry its own layout"