Button.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************/
  5. static void WindowButtonCheck(Button &button)
  6. {
  7. if(GuiObj *parent=button.parent())
  8. if(parent->type()==GO_WINDOW)
  9. {
  10. Window &window= parent->asWindow();
  11. IntPtr index =&button-window.button;
  12. if(InRange(index, window.button))window.setButtons();
  13. }
  14. }
  15. /******************************************************************************/
  16. void Button::zero()
  17. {
  18. mode =BUTTON_DEFAULT;
  19. sound =true;
  20. image_color =WHITE;
  21. text_align =0;
  22. text_size =1;
  23. _push_button =false;
  24. _on =false;
  25. _vertical =false;
  26. _focusable =true;
  27. _pixel_align =true;
  28. _sub_type =BUTTON_TYPE_DEFAULT;
  29. _func_immediate=false;
  30. _func_user =null;
  31. _func =null;
  32. _lit =0;
  33. }
  34. Button::Button() {zero();}
  35. Button& Button::del()
  36. {
  37. text .clear();
  38. image.clear();
  39. skin .clear();
  40. super::del(); zero(); return T;
  41. }
  42. void Button::setParams()
  43. {
  44. _type =GO_BUTTON;
  45. _sub_type=BUTTON_TYPE_DEFAULT;
  46. }
  47. Button& Button::create(C Str &text)
  48. {
  49. del();
  50. setParams();
  51. T._visible = true;
  52. T._rect.max.x = 0.27f;
  53. T._rect.min.y =-0.06f;
  54. T. text = text;
  55. return T;
  56. }
  57. Button& Button::create(C Button &src)
  58. {
  59. if(this!=&src)
  60. {
  61. if(!src.is())del();else
  62. {
  63. copyParams(src);
  64. _type =GO_BUTTON;
  65. mode =src. mode;
  66. sound =src. sound;
  67. image_color =src. image_color;
  68. text_align =src. text_align;
  69. text_size =src. text_size;
  70. text =src. text;
  71. image =src. image;
  72. skin =src. skin;
  73. _push_button =src._push_button;
  74. _on =src._on;
  75. _vertical =src._vertical;
  76. _focusable =src._focusable;
  77. _pixel_align =src._pixel_align;
  78. _sub_type =src._sub_type;
  79. _func =src._func;
  80. _func_user =src._func_user;
  81. _func_immediate=src._func_immediate;
  82. _lit =src._lit;
  83. }
  84. }
  85. return T;
  86. }
  87. /******************************************************************************/
  88. Button& Button::func(void (*func)(Ptr), Ptr user, Bool immediate)
  89. {
  90. T._func =func;
  91. T._func_user =user;
  92. T._func_immediate=immediate;
  93. return T;
  94. }
  95. void Button::call(Bool sound)
  96. {
  97. if(sound && T.sound && mode!=BUTTON_CONTINUOUS && _sub_type==BUTTON_TYPE_DEFAULT)Gui.playClickSound(); // don't play sounds for 'Tab' because they're played by 'Tabs'
  98. if(_func)if(_func_immediate)_func(_func_user);else Gui.addFuncCall(_func, _func_user);
  99. }
  100. /******************************************************************************/
  101. TextStyle* Button::textParams(Flt &text_size, Flt &text_padd, C Flt *height)C
  102. {
  103. if(GuiSkin *skin=getSkin())
  104. {
  105. TextStyle *text_style;
  106. switch(_sub_type)
  107. {
  108. default : text_style=skin->button .text_style(); text_size=skin->button .text_size; text_padd=skin->button .text_padd; break; // BUTTON_TYPE_DEFAULT
  109. case BUTTON_TYPE_COMBOBOX : text_style=skin->combobox .text_style(); text_size=skin->combobox .text_size; text_padd=skin->combobox .text_padd; break;
  110. case BUTTON_TYPE_LIST_COLUMN: text_style=skin->list.column.text_style(); text_size=skin->list.column.text_size; text_padd=skin->list.column.text_padd; break;
  111. case BUTTON_TYPE_TAB_LEFT :
  112. case BUTTON_TYPE_TAB_HORIZONTAL :
  113. case BUTTON_TYPE_TAB_RIGHT :
  114. case BUTTON_TYPE_TAB_TOP :
  115. case BUTTON_TYPE_TAB_VERTICAL :
  116. case BUTTON_TYPE_TAB_BOTTOM :
  117. case BUTTON_TYPE_TAB_TOP_LEFT :
  118. case BUTTON_TYPE_TAB_TOP_RIGHT :
  119. case BUTTON_TYPE_TAB_BOTTOM_LEFT :
  120. case BUTTON_TYPE_TAB_BOTTOM_RIGHT:
  121. text_style=skin->tab.horizontal.text_style(); text_size=skin->tab.horizontal.text_size; text_padd=skin->tab.horizontal.text_padd; // for simplicity return the same tab as they should be all the same
  122. break;
  123. // others don't use text
  124. }
  125. Flt h=(height ? *height : rect().h());
  126. text_size*=h*T.text_size;
  127. text_padd*=h;
  128. return text_style;
  129. }
  130. return null;
  131. }
  132. Flt Button::textWidth(C Flt *height)C
  133. {
  134. if(text.is())
  135. {
  136. Flt text_size, text_padd; if(TextStyle *text_style=textParams(text_size, text_padd, height))
  137. {
  138. TextStyleParams ts=*text_style; ts.size=text_size;
  139. #if DEFAULT_FONT_FROM_CUSTOM_SKIN
  140. if(!ts.font())if(GuiSkin *skin=getSkin())ts.font(skin->font()); // adjust font in case it's empty and the custom skin has a different font than the 'Gui.skin'
  141. #endif
  142. return ts.textWidth(text);
  143. }
  144. }
  145. return 0;
  146. }
  147. /******************************************************************************/
  148. Button& Button::set(Bool on, SET_MODE mode)
  149. {
  150. if(T._on!=on && T.mode==BUTTON_TOGGLE)
  151. {
  152. T._on=on;
  153. if(mode!=QUIET)call(mode!=NO_SOUND);
  154. }
  155. return T;
  156. }
  157. Button& Button::push ( ) {T._push_button=true ; return T;}
  158. Button& Button::setText (C Str &text ) {T. text =text ; return T;}
  159. Button& Button::setImage(C ImagePtr &image) {T. image =image; return T;}
  160. Button& Button::subType (BUTTON_TYPE type ) {T._sub_type =type ; return T;}
  161. /******************************************************************************/
  162. Button& Button:: enabled (Bool enabled) {if(T. enabled()!= enabled){if(!enabled && mode!=BUTTON_TOGGLE)_on=false; super:: enabled( enabled);} return T;}
  163. Button& Button::disabled (Bool disabled) {if(T.disabled()!=disabled){if(disabled && mode!=BUTTON_TOGGLE)_on=false; super::disabled(disabled);} return T;}
  164. Button& Button::focusable(Bool on ) {if(T._focusable!=on ){_focusable=on; if(!on)kbClear();} return T;}
  165. /******************************************************************************/
  166. Button& Button::hide()
  167. {
  168. Bool visible =T.visible(); super::hide();
  169. if( visible!=T.visible())WindowButtonCheck(T);
  170. return T;
  171. }
  172. Button& Button::show()
  173. {
  174. Bool visible =T.visible(); super::show();
  175. if( visible!=T.visible())WindowButtonCheck(T);
  176. return T;
  177. }
  178. /******************************************************************************/
  179. void Button::update(C GuiPC &gpc)
  180. {
  181. Bool manual_push=_push_button; _push_button=false;
  182. Bool enabled=(T.enabled() && gpc.enabled);
  183. if( enabled)
  184. {
  185. Bool call_func=false, lit=false;
  186. Byte state=0;
  187. if(Gui.ms()==this)
  188. {
  189. lit =true;
  190. state|=((Ms.b (0) && (mode!=BUTTON_DEFAULT || Gui.msLit()==this)) ? BS_ON : 0) // mode==BUTTON_DEFAULT requires Gui.msLit()==this while other modes don't
  191. | ((Ms.bp(0) ) ? BS_PUSHED : 0)
  192. | ((Ms.br(0) && Gui.msLit()==this ) ? BS_RELEASED : 0);
  193. }
  194. if(Gui.kb()==this)
  195. {
  196. if(mode==BUTTON_CONTINUOUS)state|=((Kb.b(KB_ENTER) || Kb.b(KB_NPENTER)) ? BS_ON : 0);
  197. if((Kb.k(KB_ENTER) || Kb.k(KB_NPENTER)) && Kb.k.first()){Kb.eatKey(); state|=(BS_PUSHED|BS_RELEASED|BS_ON);} // 'BS_RELEASED' to work for 'BUTTON_DEFAULT', this is because there's no way to cancel keyboard activation (mouse cursor for example can be moved away before releasing button, while keyboard can't)
  198. }
  199. REPA(Touches)
  200. {
  201. Touch &t=Touches[i]; if(t.guiObj()==this)
  202. {
  203. lit|=t.stylus();
  204. if(t.scrolling()) // if touch is used for scrolling, then we need to process the button differently
  205. {
  206. state|=((t.on() && (mode!=BUTTON_DEFAULT || Gui.objAtPos(t.pos())==this)) ? BS_ON : 0) // mode==BUTTON_DEFAULT requires Gui.objAtPos(t.pos())==this while other modes don't
  207. //| ((t.pd() ) ? BS_PUSHED : 0) // ignore the first push
  208. | ((t.rs() && Gui.objAtPos(t.pos())==this ) ? BS_RELEASED|BS_PUSHED : 0); // process touch release as both BS_RELEASED|BS_PUSHED so it can be used instead of touch pushes
  209. }else
  210. {
  211. state|=((t.on() && (mode!=BUTTON_DEFAULT || Gui.objAtPos(t.pos())==this)) ? BS_ON : 0) // mode==BUTTON_DEFAULT requires Gui.objAtPos(t.pos())==this while other modes don't
  212. | ((t.pd() ) ? BS_PUSHED : 0)
  213. | ((t.rs() && Gui.objAtPos(t.pos())==this ) ? BS_RELEASED : 0);
  214. }
  215. }
  216. }
  217. switch(mode)
  218. {
  219. case BUTTON_DEFAULT:
  220. {
  221. if(ButtonRs(state))manual_push=true;
  222. _on =ButtonOn(state);
  223. call_func=manual_push;
  224. }break;
  225. case BUTTON_CONTINUOUS:
  226. {
  227. if(ButtonPd(state))manual_push=true;
  228. _on =(manual_push || ButtonOn(state));
  229. call_func=_on;
  230. }break;
  231. case BUTTON_TOGGLE:
  232. {
  233. if(ButtonPd(state))manual_push=true;
  234. if(manual_push)_on^=1;
  235. call_func=manual_push;
  236. }break;
  237. case BUTTON_IMMEDIATE:
  238. {
  239. if(ButtonPd(state))manual_push=true;
  240. _on =(manual_push || ButtonOn(state));
  241. call_func= manual_push;
  242. }break;
  243. }
  244. if(call_func)call(true);
  245. //AdjustValBool(_lit, lit, Gui._time_d_fade_in, Gui._time_d_fade_out);
  246. if(lit)_lit=1;else MAX(_lit-=Gui._time_d_fade_out, 0);
  247. }
  248. }
  249. /******************************************************************************/
  250. void Button::draw(C GuiPC &gpc)
  251. {
  252. if(visible() && gpc.visible)
  253. if(GuiSkin *skin=getSkin())
  254. {
  255. Rect r=rect()+gpc.offset;
  256. C GuiSkin::Button *button_skin ; // never null
  257. C GuiSkin::ButtonImage *button_image; // may be null
  258. Int image_x ; // valid only if "button_image!=null"
  259. switch(_sub_type)
  260. {
  261. default : button_skin=&skin->button ; button_image= null ; break; // BUTTON_TYPE_DEFAULT
  262. case BUTTON_TYPE_COMBOBOX : button_skin= button_image=&skin->combobox ; image_x=1; break;
  263. case BUTTON_TYPE_LIST_COLUMN : button_skin=&skin->list.column ; button_image= null ; break;
  264. case BUTTON_TYPE_PROPERTY_VALUE : button_skin= button_image=&skin->property.value ; image_x=0; break;
  265. case BUTTON_TYPE_REGION_VIEW : button_skin= button_image=&skin->region .view ; image_x=0; break;
  266. case BUTTON_TYPE_SLIDEBAR_LEFT : button_skin= button_image=&skin->slidebar.left ; image_x=0; break;
  267. case BUTTON_TYPE_SLIDEBAR_CENTER : button_skin= button_image=&skin->slidebar.center; image_x=0; break;
  268. case BUTTON_TYPE_SLIDEBAR_RIGHT : button_skin= button_image=&skin->slidebar.right ; image_x=0; break;
  269. case BUTTON_TYPE_TAB_LEFT : button_skin=&skin->tab.left ; button_image= null ; break;
  270. case BUTTON_TYPE_TAB_HORIZONTAL : button_skin=&skin->tab.horizontal ; button_image= null ; break;
  271. case BUTTON_TYPE_TAB_RIGHT : button_skin=&skin->tab.right ; button_image= null ; break;
  272. case BUTTON_TYPE_TAB_TOP : button_skin=&skin->tab.top ; button_image= null ; break;
  273. case BUTTON_TYPE_TAB_VERTICAL : button_skin=&skin->tab.vertical ; button_image= null ; break;
  274. case BUTTON_TYPE_TAB_BOTTOM : button_skin=&skin->tab.bottom ; button_image= null ; break;
  275. case BUTTON_TYPE_TAB_TOP_LEFT : button_skin=&skin->tab.top_left ; button_image= null ; break;
  276. case BUTTON_TYPE_TAB_TOP_RIGHT : button_skin=&skin->tab.top_right ; button_image= null ; break;
  277. case BUTTON_TYPE_TAB_BOTTOM_LEFT : button_skin=&skin->tab.bottom_left ; button_image= null ; break;
  278. case BUTTON_TYPE_TAB_BOTTOM_RIGHT: button_skin=&skin->tab.bottom_right; button_image= null ; break;
  279. case BUTTON_TYPE_TEXTLINE_CLEAR : button_skin= button_image=&skin->textline.clear ; image_x=0; break;
  280. case BUTTON_TYPE_WINDOW_MINIMIZE : button_skin= button_image=&skin->window.minimize; image_x=0; break;
  281. case BUTTON_TYPE_WINDOW_MAXIMIZE : button_skin= button_image=&skin->window.maximize; image_x=0; break;
  282. case BUTTON_TYPE_WINDOW_CLOSE : button_skin= button_image=&skin->window.close ; image_x=0; break;
  283. }
  284. C PanelImage *panel_image ; // may be null
  285. C Color *pushed_color; // never null
  286. Bool enabled=(gpc.enabled && T.enabled());
  287. if(enabled)
  288. {
  289. if(T()){panel_image=button_skin->pushed(); pushed_color=&button_skin->pushed_color;}
  290. else {panel_image=button_skin->normal(); pushed_color=&button_skin->normal_color;}
  291. }else
  292. {
  293. if(T()){panel_image=button_skin->pushed_disabled(); pushed_color=&button_skin->pushed_disabled_color;}
  294. else {panel_image=button_skin-> disabled(); pushed_color=&button_skin-> disabled_color;}
  295. }
  296. Rect ext_rect; if(panel_image)panel_image->extendedRect(r, ext_rect);else ext_rect=r;
  297. if(Cuts(ext_rect, gpc.clip))
  298. {
  299. D.clip(gpc.clip);
  300. if(_pixel_align)D.alignScreenToPixel(r);
  301. Color highlight=ColorMulZeroAlpha(skin->mouse_highlight_color, lit());
  302. Flt text_size, text_padd;
  303. Rect text_rect;
  304. TextStyle *text_style=null;
  305. if(text.is())
  306. {
  307. text_rect=r;
  308. if(text_style=textParams(text_size, text_padd)){text_rect.min.x+=text_padd; text_rect.max.x-=text_padd;}
  309. }
  310. // draw panel image
  311. if(panel_image)
  312. {
  313. if(_vertical)panel_image->drawVertical(*pushed_color, highlight, r);
  314. else panel_image->draw (*pushed_color, highlight, r);
  315. }//else .. there's no Rect draw because to avoid it we would need to set 'pushed_color' to TRANSPARENT, however it also affects the 'text' and images as well
  316. // draw custom image
  317. if(image)
  318. {
  319. Color col=ColorMul(image_color, *pushed_color); // 'image_color' is user modification, 'pushed_color' is push/disabled modification
  320. if(!panel_image)image->draw (col, highlight, r);else // if there's no 'panel_image' then there's no way to determine the button boundaries, so let's stretch the custom image to entire rect
  321. if( text.is() )image->draw (col, highlight, Rect(text_rect.min.x, text_rect.min.y, text_rect.min.x+image->aspect()*text_rect.h(), text_rect.max.y));else // draw on the left if there's text available
  322. image->drawFit(col, highlight, r);
  323. }
  324. // draw text
  325. if(text.is() && text_style)
  326. {
  327. TextStyleParams ts=*text_style; ts.size=text_size; ts.align.x=text_align; ts.color=ColorMul(ts.color, *pushed_color); // modify text color based on pushed/disabled
  328. #if DEFAULT_FONT_FROM_CUSTOM_SKIN
  329. if(!ts.font())ts.font(skin->font()); // adjust font in case it's empty and the custom skin has a different font than the 'Gui.skin'
  330. #endif
  331. D.text(ts, text_rect, text);
  332. }
  333. // draw special image (draw after text in case of combobox arrows)
  334. if(button_image)
  335. if(C Image *special_image=button_image->image())
  336. {
  337. Color col =ColorMul (button_image->image_color , *pushed_color); // modify image color based on pushed/disabled
  338. //col_add=ColorMulZeroAlpha(button_image->image_color_add, *pushed_color);
  339. if(_vertical)
  340. {
  341. if(!image_x)special_image->drawFitVertical(col, TRANSPARENT, r);
  342. else special_image->drawVertical (col, TRANSPARENT, Rect(r.min.x, r.max.y-special_image->aspect()*r.w(), r.max.x, r.max.y));
  343. }else
  344. {
  345. if(!image_x)special_image->drawFit(col, TRANSPARENT, r);else
  346. {
  347. Rect rect; rect.setY(r.min.y, r.max.y);
  348. Flt w=special_image->aspect()*r.h();
  349. if(r.w()>=w)rect.setX(r.max.x-w, r.max.x); // if image can fit in rectangle
  350. else {Flt c=r.centerX(); w*=0.5f; rect.setX(c-w, c+w);} // if image width is bigger than rect width, then draw at the center of the rectangle
  351. special_image->draw(col, TRANSPARENT, rect);
  352. }
  353. }
  354. }
  355. if(Gui.kb()==this)Gui.kbLit(this, r, skin);
  356. }
  357. }
  358. }
  359. /******************************************************************************/
  360. Bool Button::save(File &f, CChar *path)C
  361. {
  362. if(super::save(f, path))
  363. {
  364. f.putMulti(Byte(7), mode, sound, image_color, text_align, text_size, _on, _focusable)<<text; // version
  365. f.putAsset(image.id());
  366. f.putAsset(skin .id());
  367. // '_pixel_align' doesn't need to be saved as it's currently being used only by Tab and there it's always set manually
  368. // '_vertical' doesn't need to be saved as it's currently being used only by SlideBar and there it's always set manually
  369. // '_sub_type' doesn't need to be saved as it's assigned by parents
  370. return f.ok();
  371. }
  372. return false;
  373. }
  374. Bool Button::load(File &f, CChar *path)
  375. {
  376. del(); if(super::load(f, path))switch(f.decUIntV()) // version
  377. {
  378. case 7:
  379. {
  380. setParams();
  381. f.getMulti(mode, sound, image_color, text_align, text_size, _on, _focusable)>>text;
  382. image.require(f.getAssetID(), path);
  383. skin .require(f.getAssetID(), path);
  384. if(f.ok())return true;
  385. }break;
  386. case 6:
  387. {
  388. setParams();
  389. f.getMulti(mode, sound, image_color, text_align, text_size, _on, _focusable)._getStr2(text);
  390. image.require(f.getAssetID(), path);
  391. skin .require(f.getAssetID(), path);
  392. if(f.ok())return true;
  393. }break;
  394. case 5:
  395. {
  396. setParams();
  397. f>>mode>>image_color>>text_align>>text_size>>_on>>_focusable; f._getStr2(text);
  398. image.require(f._getAsset(), path);
  399. skin .require(f._getAsset(), path);
  400. if(f.ok())return true;
  401. }break;
  402. case 4:
  403. {
  404. setParams();
  405. f>>mode; f.skip(1); f>>_focusable; f.skip(1); f>>image_color>>text_align>>text_size; f.skip(4); f>>_on; f._getStr(text); f._getStr(); f._getStr();
  406. if(f.ok())return true;
  407. }break;
  408. case 3:
  409. {
  410. setParams();
  411. f>>mode; f.skip(1); f>>_focusable; f.skip(1); f>>image_color>>text_align; f.skip(4); f>>_on; text_size=1; f._getStr(text); f._getStr(); f._getStr();
  412. if(f.ok())return true;
  413. }break;
  414. case 2:
  415. {
  416. setParams();
  417. f>>mode; f.skip(1); f>>_focusable; f.skip(1); f>>image_color>>text_align; f.skip(4); f>>_on; text_size=1; f._getStr(text); f._getStr();
  418. if(f.ok())return true;
  419. }break;
  420. case 1:
  421. {
  422. setParams();
  423. f>>mode; f.skip(1); f>>_focusable; f.skip(1); f>>image_color>>text_align; f.skip(4); f>>_on; Swap(image_color.r, image_color.b); text_size=1; text=f._getStr16(); f._getStr16();
  424. if(f.ok())return true;
  425. }break;
  426. case 0:
  427. {
  428. setParams();
  429. Char8 text[32]; f>>text;
  430. f>>mode; f.skip(1); f>>_focusable; f.skip(1); f>>image_color>>text_align; f.skip(4); f>>_on; Swap(image_color.r, image_color.b); T.text=text; text_size=1; f._getStr8();
  431. if(f.ok())return true;
  432. }break;
  433. }
  434. del(); return false;
  435. }
  436. /******************************************************************************/
  437. }
  438. /******************************************************************************/