Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file b_button.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222(** a clickable button *)(* TODO click on an image *)(* TODO in case of Switch, dim the label when not selected *)(* ==> label_on, label_off ? *)openB_utilsmoduleTheme=B_thememoduleVar=B_varmoduleDraw=B_drawmoduleStyle=B_stylemoduleBox=B_boxmoduleLabel=B_labelmoduleTrigger=B_triggertypekind=|Trigger(* one action when pressed. TODO, better to avoid name clash with
Trigger module? *)|Switch(* two states *)typeaction=bool->unittypet={kind:kind;label_on:Label.t;label_off:Label.t;state:boolVar.t;pressed:boolVar.t;mutablemouse_over:bool;keyboard_focus:boolVar.t;box_on:Box.t;(* TODO Var.t ? *)box_off:Box.t;(* TODO Var.t ? *)box_over:Box.toption;action:actionoption}letcolor_on=Draw.find_colorTheme.button_color_onletcolor_off=Draw.find_colorTheme.button_color_offletbg_over=Style.gradientDraw.[opaquecolor_off;opaquecolor_off;opaquecolor_on](* if label_on and/or label_off is provided, then label is ignored *)letcreate?size?border_radius?border_color?fg?(bg_on=Style.color_bgDraw.(opaquecolor_on))?(bg_off=Style.color_bgDraw.(opaquecolor_off))?(bg_over=Somebg_over)?label?label_on?label_off?(state=false)?actionkindtext=letlabel_on,label_off=matchlabel,label_on,label_offwith|None,None,None->letl=Label.create?size?fgtextinl,l|Somel,None,None->l,l|None,_,_->default_lazylabel_on(lazy(Label.create?size?fgtext)),default_lazylabel_off(lazy(Label.create?size?fgtext))|_->printddebug_warning"label argument was ignored because label_on and/or \
label_off was provided";default_lazylabel_on(lazy(Label.create?size?fgtext)),default_lazylabel_off(lazy(Label.create?size?fgtext))inletborder_on,border_off=matchborder_color,border_radiuswith|None,None->None,None|None,Someradius->SomeStyle.(mk_border~radius(mk_line~color:(Style.get_colorbg_on)())),SomeStyle.(mk_border~radius(mk_line~color:(Style.get_colorbg_off)()))|_->lets=Style.(mk_border?radius:border_radius(mk_line?color:border_color()))inSomes,Somesinletstyle_on=Style.create~background:bg_on?border:border_on()inletstyle_off=Style.create~background:bg_off?border:border_off()in{kind;action;label_on;label_off;state=Var.createstate;pressed=Var.create(ifkind=Triggerthenfalseelsestate);mouse_over=false;keyboard_focus=Var.createfalse;box_on=Box.(create~style:style_on());box_off=Box.(create~style:style_off());box_over=map_optionbg_over(funbg->letstyle=Style.with_bgbgstyle_offinBox.create~style())}letunloadl=Label.unloadl.label_on;Label.unloadl.label_off;Box.unloadl.box_on;Box.unloadl.box_off;do_optionl.box_overBox.unload(* TODO *)letfree=unloadlethas_keyboard_focusb=Var.getb.keyboard_focusletset_focusb=Var.setb.keyboard_focustrueletunfocusb=Var.setb.keyboard_focusfalseletstateb=Var.getb.statelettextb=ifstatebthenLabel.textb.label_onelseLabel.textb.label_offletset_labelbtext=ifstatebthenLabel.setb.label_ontextelseLabel.setb.label_offtextletis_pressedb=Var.getb.pressed(* called on button_down *)letpressb=Var.setb.pressedtrueletresetb=Var.setb.pressedfalse;Var.setb.statefalseletsetbs=Var.setb.presseds;Var.setb.states(* called on button up for Trigger *)letreleaseb=(* TODO: verify true click *)ifis_pressedbthenbeginVar.setb.pressedfalse;lets=not(Var.getb.state)inVar.setb.states;do_optionb.action(funf->fs)(* TODO; this is not exactly what we want with Trigger *)end(* called by button_up in case of kind=Switch *)letswitch?(keyboard=false)bev=ifkeyboard||Trigger.has_full_clickevthenbeginlets=not(Var.getb.state)inVar.setb.states;printddebug_event"Switch button to [pressed=%b] [state=%b]"(is_pressedb)s;do_optionb.action(funf->fs)end;Var.setb.pressed(Var.getb.state)letmouse_enterb=b.mouse_over<-true;set_focusbletmouse_leaveb=b.mouse_over<-false;unfocusbletcheck_keybev=has_keyboard_focusb&&Tsdl.Sdl.Event.(getevkeyboard_keycode)=Tsdl.Sdl.K.return(* TODO use also TAB or ENTER or SPACE...? *)letreceive_keybev=ifcheck_keybevthenmatchTrigger.event_kindevwith|`Key_down->pressb|`Key_up->beginmatchb.kindwith|Trigger->releaseb|Switch->switch~keyboard:truebevend|_->printd(debug_event+debug_error)"Wrong event (%s) for Button.receive_key"(Trigger.sprint_evev)(************* display ***********)letbutton_margin=6;;(* logical size - TODO theme this var ? *)letbm=Theme.scale_intbutton_margin(* The size of the widget is dictated by the size of the labels *)letsizeb=let(w,h)=Label.sizeb.label_oninlet(w',h')=Label.sizeb.label_offinletw=imaxww'andh=imaxhh'in(w+2*button_margin,h+2*button_margin)(* For safety (?), if the size is too small, the check icon is not clipped (see
[display] below). *)letresize(w,h)b=letsize=w-2*button_margin,h-2*button_margininList.iter(Label.resizesize)[b.label_on;b.label_off];List.iter(Box.resize(w,h))[b.box_on;b.box_off];do_optionb.box_over(Box.resize(w,h))letdisplaycanvaslayerbg=let(dx,dy)=ifis_pressedbthen(0,1)else(0,0)inletbox=ifis_pressedbthenb.box_onelseifb.mouse_over||has_keyboard_focusbthendefaultb.box_overb.box_offelseb.box_offin(*let margin = if is_pressed b then 0 else button_margin in*)(* Draw.box canvas.Draw.renderer ~bg (x+margin) (y+margin) (w-2*margin) (h-2*margin); *)letbox_blit=Box.displaycanvaslayerboxDraw.({x=g.x(* + margin *);y=g.y(* + margin *);w=g.w;h=g.h;voffset=g.voffset})inletlabel_blit=Label.displaycanvaslayer(ifstatebthenb.label_onelseb.label_off)Draw.({x=g.x+bm+dx;y=g.y+bm+dy;w=g.w-2*bm;h=g.h-2*bm;voffset=g.voffset})inList.concat[box_blit;label_blit]