Gui Object.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************/
  5. // GUI PARENT CHILD
  6. /******************************************************************************/
  7. GuiPC::GuiPC(C GuiPC &old, Bool visible, Bool enabled)
  8. {
  9. T=old;
  10. T.visible&=visible;
  11. T.enabled&=enabled;
  12. }
  13. /******************************************************************************/
  14. // GUI OBJECT
  15. /******************************************************************************/
  16. static Int CompareLevel(C GuiObj &a, C GuiObj &b)
  17. {
  18. if(Int c=Compare(a. baseLevel(), b. baseLevel()))return c;
  19. if(a.type()==GO_WINDOW && b.type()==GO_WINDOW)if(Int c=Compare(a.asWindow(). level(), b.asWindow(). level()))return c;
  20. if(a.type()==GO_MENU && b.type()==GO_MENU )if(Int c=Compare(b.asMenu ().parents(), a.asMenu ().parents()))return c; // order swapped, sort Menu by parents, so Menus attached to Windows are processed before those attached to Desktop, so Window Menu's process keyboard shortcuts first, and unprocessed shortcuts are checked later by Desktop Menu's.
  21. return 0;
  22. }
  23. /******************************************************************************/
  24. void GuiObj::zero()
  25. {
  26. user =null;
  27. _type =GO_NONE;
  28. _visible =false;
  29. _disabled =false;
  30. _base_level=GBL_DEFAULT;
  31. _desc.clear();
  32. _parent=null;
  33. _rect.zero();
  34. }
  35. GuiObj& GuiObj::del()
  36. {
  37. deactivate();
  38. if(contains(Gui.msSrc()))Gui._ms_src=null;
  39. if(_parent)*_parent-=T; // detach from the parent first without clearing members as it may use them
  40. zero(); return T;
  41. }
  42. GuiObj::GuiObj() {zero();}
  43. /******************************************************************************/
  44. GuiObj& GuiObj::create(C GuiObj &src)
  45. {
  46. // don't use 'type' as they may be set to GO_NONE if objects were not created yet
  47. // process extended classes first, because for example 'ComboBox' can be casted to both 'Button' and 'ComboBox'
  48. if(CAST(ComboBox , this) && CAST(C ComboBox , &src))asComboBox().create(src.asComboBox());else
  49. if(CAST(Tab , this) && CAST(C Tab , &src))asTab ().create(src.asTab ());else
  50. if(CAST(Button , this) && CAST(C Button , &src))asButton ().create(src.asButton ());else
  51. if(CAST(CheckBox , this) && CAST(C CheckBox , &src))asCheckBox().create(src.asCheckBox());else
  52. if(CAST(GuiCustom, this) && CAST(C GuiCustom, &src))asCustom ().create(src.asCustom ());else
  53. if(CAST(Desktop , this) && CAST(C Desktop , &src))asDesktop ().create(src.asDesktop ());else
  54. if(CAST(GuiImage , this) && CAST(C GuiImage , &src))asImage ().create(src.asImage ());else
  55. if(CAST(_List , this) && CAST(C _List , &src))asList ().create(src.asList ());else
  56. if(CAST(Menu , this) && CAST(C Menu , &src))asMenu ().create(src.asMenu ());else
  57. if(CAST(MenuBar , this) && CAST(C MenuBar , &src))asMenuBar ().create(src.asMenuBar ());else
  58. if(CAST(Progress , this) && CAST(C Progress , &src))asProgress().create(src.asProgress());else
  59. if(CAST(Region , this) && CAST(C Region , &src))asRegion ().create(src.asRegion ());else
  60. if(CAST(SlideBar , this) && CAST(C SlideBar , &src))asSlideBar().create(src.asSlideBar());else
  61. if(CAST(Slider , this) && CAST(C Slider , &src))asSlider ().create(src.asSlider ());else
  62. if(CAST(Tabs , this) && CAST(C Tabs , &src))asTabs ().create(src.asTabs ());else
  63. if(CAST(Text , this) && CAST(C Text , &src))asText ().create(src.asText ());else
  64. if(CAST(TextBox , this) && CAST(C TextBox , &src))asTextBox ().create(src.asTextBox ());else
  65. if(CAST(TextLine , this) && CAST(C TextLine , &src))asTextLine().create(src.asTextLine());else
  66. if(CAST(Viewport , this) && CAST(C Viewport , &src))asViewport().create(src.asViewport());else
  67. if(CAST(Window , this) && CAST(C Window , &src))asWindow ().create(src.asWindow ());
  68. return T;
  69. }
  70. void GuiObj::copyParams(C GuiObj &src)
  71. {
  72. user =src. user;
  73. _visible =src._visible;
  74. _disabled =src._disabled;
  75. _base_level=src._base_level;
  76. //_type =src._type; cannot copy type because this is called inside GuiObj create methods, and 'Button' can be called with create from for example 'ComboBox' which has GO_COMBOBOX
  77. _desc =src._desc;
  78. _rect =src._rect;
  79. }
  80. /******************************************************************************/
  81. void GuiObj::operator+=(GuiObj &child)
  82. {
  83. if(child.is() // if child exists
  84. && this!=&child // is not this
  85. && !child.contains(this)) // child doesn't contain us
  86. switch(type())
  87. {
  88. case GO_DESKTOP: asDesktop().addChild(child); break;
  89. //case GO_LIST : asList ().addChild(child); break; this requires specifying row and optionally column, so we can't do it here
  90. case GO_REGION : asRegion ().addChild(child); break;
  91. case GO_TAB : asTab ().addChild(child); break;
  92. case GO_WINDOW : asWindow ().addChild(child); break;
  93. }
  94. }
  95. void GuiObj::operator-=(GuiObj &child)
  96. {
  97. switch(type())
  98. {
  99. case GO_DESKTOP: asDesktop().removeChild(child); break;
  100. case GO_LIST : asList ().removeChild(child); break;
  101. case GO_REGION : asRegion ().removeChild(child); break;
  102. case GO_TAB : asTab ().removeChild(child); break;
  103. case GO_TABS : asTabs ().removeChild(child); break;
  104. case GO_WINDOW : asWindow ().removeChild(child); break;
  105. }
  106. }
  107. void GuiObj::operator+=(GuiObjs &children)
  108. {
  109. FREPA(children._objs) // add from start to preserve order
  110. {
  111. GuiObjs::Obj &goi=children._objs[i];
  112. if(!goi.parent_type)if(GuiObj *go=children.go(goi.type, goi.index))T+=*go; // add all main (which don't have a parent)
  113. }
  114. }
  115. void GuiObj::operator-=(GuiObjs &children)
  116. {
  117. REPA(children._objs) // remove from end to minimize overhead (moving indexes)
  118. {
  119. GuiObjs::Obj &goi=children._objs[i];
  120. if(!goi.parent_type)if(GuiObj *go=children.go(goi.type, goi.index))T-=*go; // remove all main (which don't have a parent)
  121. }
  122. }
  123. GuiObjChildren* GuiObj::children()
  124. {
  125. switch(_type)
  126. {
  127. case GO_DESKTOP: return &asDesktop()._children;
  128. case GO_LIST : return &asList ()._children;
  129. case GO_REGION : return &asRegion ()._children;
  130. case GO_TAB : return &asTab ()._children;
  131. case GO_WINDOW : return &asWindow ()._children;
  132. }
  133. return null;
  134. }
  135. GuiObj* GuiObj::child(Int i)
  136. {
  137. if(GuiObjChildren *children=T.children())
  138. if(InRange(i, *children))
  139. return (*children)[i];
  140. if(type()==GO_TABS && InRange(i, asTabs()))return &asTabs().tab(i);
  141. return null;
  142. }
  143. Int GuiObj::childNum()
  144. {
  145. if(GuiObjChildren *children=T.children())return children->children.elms();
  146. if(type()==GO_TABS)return asTabs().tabs();
  147. return 0;
  148. }
  149. void GuiObj::notifyChildrenOfClientRectChange(C Rect *old_client, C Rect *new_client)
  150. {
  151. REP(childNum())if(GuiObj *go=child(i))go->parentClientRectChanged(old_client, new_client);
  152. }
  153. void GuiObj::notifyParentOfRectChange(C Rect &old_rect, Bool old_visible)
  154. {
  155. if(parent())parent()->childRectChanged(old_visible ? &old_rect : null, visible() ? &rect() : null, T);
  156. }
  157. /******************************************************************************/
  158. Bool GuiObj::is (GUI_OBJ_TYPE type)C {return this && T.type()==type;}
  159. Bool GuiObj::contains(C GuiObj *child)C // !! this method is safe to work with "this==null" upon changing that, fix all calls to this !!
  160. {
  161. for(; child; child=child->owner())if(child==this)return true;
  162. return false;
  163. }
  164. GuiObj* GuiObj::last (GUI_OBJ_TYPE type) {GuiObj *last=null; for(GuiObj *go=this; go; go=go->owner())if(go->type()==type)last=go; return last;}
  165. GuiObj* GuiObj::first (GUI_OBJ_TYPE type) { for(GuiObj *go=this; go; go=go->owner())if(go->type()==type) return go; return null;} // !! this method is safe to work with "this==null" upon changing that, fix all calls to this !!
  166. GuiObj* GuiObj::firstNon (GUI_OBJ_TYPE type) { for(GuiObj *go=this; go; go=go->owner())if(go->type()!=type) return go; return null;}
  167. GuiObj* GuiObj::firstContainer( )
  168. {
  169. for(GuiObj *go=this; go; go=go->owner())switch(go->type())
  170. {
  171. case GO_DESKTOP:
  172. case GO_REGION :
  173. case GO_WINDOW : return go;
  174. }
  175. return null;
  176. }
  177. GuiObj* GuiObj::firstKbParent()
  178. {
  179. for(GuiObj *go=parent(); go; go=go->parent())switch(go->type())
  180. {
  181. case GO_DESKTOP:
  182. case GO_LIST :
  183. case GO_MENU :
  184. case GO_REGION :
  185. case GO_TAB :
  186. case GO_WINDOW : return go;
  187. }
  188. return null;
  189. }
  190. Region* GuiObj::firstScrollableRegion()
  191. {
  192. for(GuiObj *go=this; go; go=go->parent())if(go->type()==GO_REGION)
  193. {
  194. Region &region=go->asRegion();
  195. if(region.slidebar[0]._usable || region.slidebar[1]._usable)return &region;
  196. }
  197. return null;
  198. }
  199. /******************************************************************************/
  200. Int GuiObj::parents()C
  201. {
  202. Int parents=0; for(GuiObj *go=owner(); go; go=go->owner())parents++;
  203. return parents;
  204. }
  205. /******************************************************************************/
  206. Bool GuiObj::kbCatch()C
  207. {
  208. switch(type())
  209. {
  210. case GO_MENU :
  211. case GO_MENU_BAR:
  212. case GO_REGION :
  213. case GO_TABS :
  214. case GO_TEXTBOX :
  215. case GO_TEXTLINE:
  216. case GO_VIEWPORT:
  217. case GO_WINDOW : return visible() && enabled();
  218. case GO_BUTTON : return asButton ().focusable() && visible() && enabled();
  219. case GO_CHECKBOX: return asCheckBox().focusable() && visible() && enabled();
  220. case GO_COMBOBOX: return asComboBox().focusable() && visible() && enabled();
  221. case GO_CUSTOM : return asCustom ().focusable() && visible() && enabled();
  222. case GO_SLIDEBAR: return asSlideBar().focusable() && visible() && enabled();
  223. case GO_SLIDER : return asSlider ().focusable() && visible() && enabled();
  224. case GO_TAB : return asTab ().focusable() && visible() && enabled();
  225. case GO_LIST : return visible() && enabled();
  226. default : return false;
  227. }
  228. }
  229. /******************************************************************************/
  230. static void AdjustGuiKb()
  231. {
  232. Gui._window=&Gui.kb()->first(GO_WINDOW)->asWindow();
  233. }
  234. GuiObj& GuiObj::kbSet() // this means setting keyboard focus to this element
  235. {
  236. if(is()) // allow setting focus only if created, to keep consistency with clearing kb focus when deleting object
  237. {
  238. if(MOBILE // do this for all types on Mobile platforms, because of the soft keyboard overlay, which could keep popping up annoyingly and occlude big portion of the screen
  239. || type()==GO_DESKTOP || type()==GO_LIST) // if this is a 'Desktop' or a 'List' then clear the sub kb focus so children won't have it, and only this object will
  240. if(GuiObjChildren *children=T.children())children->kb=null;
  241. // set kb from 'this' to parents
  242. for(GuiObj *go=this; go; )
  243. {
  244. GuiObj *parent=go->firstKbParent();
  245. if(parent)
  246. {
  247. GuiObj *kb, *cur=go; // find first object between 'parent' .. 'go' that can catch keyboard focus
  248. for(;;)
  249. {
  250. if(cur->kbCatch()){kb=cur; break;}
  251. if(cur->type()==GO_TAB && cur->asTab()._children.kb){kb=cur; break;} // force setting tab when it owns a child with keyboard focus, do not set 'kb' to child directly, because kb pointers must be only 1 level !!
  252. cur=cur->parent();
  253. if(cur==parent || !cur){kb=null; break;} // if reached the parent, or for some reason null, then stop
  254. }
  255. switch(parent->type())
  256. {
  257. case GO_MENU : parent->asMenu (). _kb=kb; break;
  258. case GO_DESKTOP: parent->asDesktop()._children.kb=kb; break;
  259. case GO_LIST : parent->asList ()._children.kb=kb; break;
  260. case GO_REGION : parent->asRegion ()._children.kb=kb; break;
  261. case GO_TAB : parent->asTab ()._children.kb=kb; break;
  262. case GO_WINDOW : parent->asWindow ()._children.kb=kb; break;
  263. }
  264. }
  265. go=parent;
  266. }
  267. // set kb from root to leaf
  268. for(GuiObj *go=Gui.desktop(); go; )
  269. {
  270. Gui._kb=go;
  271. switch(go->type())
  272. {
  273. case GO_MENU_BAR: {MenuBar &menubar=go->asMenuBar(); Menu *menu=(InRange(menubar._lit, menubar.elms()) ? &menubar.elm(menubar._lit).menu : null); go=menu;} break;
  274. case GO_TABS : {Tabs &tabs =go->asTabs (); Tab *tab =(InRange(tabs() , tabs ) ? &tabs .tab(tabs() ) : null); go=((tab && (tab->kbCatch() || tab->_children.kb)) ? tab : null);} break; // set this tab only if it can catch keyboard focus or has a child with kb focus
  275. case GO_TAB : go=go->asTab ()._children.kb; break;
  276. case GO_DESKTOP : go=go->asDesktop()._children.kb; break;
  277. case GO_LIST : go=go->asList ()._children.kb; break;
  278. case GO_REGION : go=go->asRegion ()._children.kb; break;
  279. case GO_WINDOW : go=go->asWindow ()._children.kb; break;
  280. case GO_MENU : go=go->asMenu (). _kb; break;
  281. default : go= null; break;
  282. }
  283. }
  284. if(Gui.kb() && (Gui.kb()->type()==GO_TEXTLINE || Gui.kb()->type()==GO_TEXTBOX))Kb.refreshTextInput(); // force showing soft keyboard if we're clicking on 'TextLine' or 'TextBox'
  285. AdjustGuiKb();
  286. }
  287. return T;
  288. }
  289. GuiObj& GuiObj::kbClear()
  290. {
  291. // remove from parents kb focus
  292. if(GuiObj *kb=firstKbParent())switch(kb->type())
  293. {
  294. case GO_DESKTOP: { Desktop &desktop=kb->asDesktop(); if(desktop._children.kb==this)desktop._children.kb=null ;} break;
  295. case GO_LIST : {_List &list =kb->asList (); if(list ._children.kb==this)list ._children.kb=null ;} break;
  296. case GO_MENU : { Menu &menu =kb->asMenu (); if(menu . _kb==this)menu . _kb=&menu.list;} break;
  297. case GO_REGION : { Region &region =kb->asRegion (); if(region ._children.kb==this)region ._children.kb=null ;} break;
  298. case GO_TAB : { Tab &tab =kb->asTab (); if(tab ._children.kb==this)tab ._children.kb=null ;} break;
  299. case GO_WINDOW : { Window &window =kb->asWindow (); if(window ._children.kb==this)window ._children.kb=null ;} break;
  300. }
  301. // adjust global focus
  302. if(contains(Gui.kb()))
  303. {
  304. Gui._kb=(_parent ? _parent : null);
  305. for(; Gui.kb() && !Gui.kb()->kbCatch(); )Gui._kb=Gui.kb()->parent();
  306. }
  307. AdjustGuiKb();
  308. return T;
  309. }
  310. /******************************************************************************/
  311. Int GuiObj::compareLevel(C GuiObj &obj)C
  312. {
  313. if(parent() && obj.parent()==parent())
  314. {
  315. if(GuiObjChildren *children=parent()->children())return children->compareLevel(T, obj);
  316. // check for single 'Tab's in 'Tabs' (this is required for correct order when saving gui objects)
  317. if(parent()->type()==GO_TABS && type()==GO_TAB && obj.type()==GO_TAB)
  318. return parent()->asTabs()._tabs.validIndex(&asTab()) - parent()->asTabs()._tabs.validIndex(&obj.asTab());
  319. }
  320. return 0;
  321. }
  322. GuiObj& GuiObj:: validateLevel( ) {if(parent())if(GuiObjChildren *children=parent()->children()) children-> validateLevel(T); return T;}
  323. Bool GuiObj::partiallyOccludedInSameLevel( )C {if(parent())if(GuiObjChildren *children=parent()->children())return children->partiallyOccludedInSameLevel(T); return false;}
  324. Bool GuiObj::moveUp ( )C {if(parent())if(GuiObjChildren *children=parent()->children())return children->moveUp (T); return false;}
  325. Bool GuiObj::moveDown ( )C {if(parent())if(GuiObjChildren *children=parent()->children())return children->moveDown (T); return false;}
  326. GuiObj& GuiObj::moveToTop ( ) {if(parent())if(GuiObjChildren *children=parent()->children()) children->moveToTop (T); return T;}
  327. GuiObj& GuiObj::moveToBottom ( ) {if(parent())if(GuiObjChildren *children=parent()->children()) children->moveToBottom (T); return T;}
  328. GuiObj& GuiObj::moveAbove (C GuiObj &obj) {if(parent() && obj.parent()==parent())if(GuiObjChildren *children=parent()->children())children->moveAbove(T, obj); return T;}
  329. GuiObj& GuiObj::moveBelow (C GuiObj &obj) {if(parent() && obj.parent()==parent())if(GuiObjChildren *children=parent()->children())children->moveBelow(T, obj); return T;}
  330. GuiObj& GuiObj::windowsToTop ( )
  331. {
  332. for(GuiObj *c=this; c; )
  333. {
  334. GuiObj *parent=c->parent();
  335. if(parent)switch(c->type())
  336. {
  337. case GO_WINDOW: if(GuiObjChildren *children=parent->children())children->moveToTop(*c); break;
  338. }
  339. c=parent;
  340. }
  341. return T;
  342. }
  343. Bool GuiObj::visibleFull()C
  344. {
  345. if(hidden())return false; // if we're starting from 'Tab' then check this
  346. for(C GuiObj *go=this; go; go=go->parent())
  347. {
  348. if(go->type()==GO_TAB)
  349. {
  350. if(go->parent() && go->parent()->type()==GO_TABS) // check if parent is 'Tabs' and it's set to that tab
  351. {
  352. Tabs &tabs =go->parent()->asTabs();
  353. Tab *tabs_tab=tabs._tabs.addr(tabs()); // get active 'Tab' in 'Tabs'
  354. if( tabs_tab!=go)return false; // if active tab is not the processed one, then it means it's not selected and children should be hidden
  355. }
  356. continue; // ignore visibility of a 'Tab'
  357. }
  358. if(go->hidden())return false;
  359. }
  360. return true;
  361. }
  362. Bool GuiObj::enabledFull()C
  363. {
  364. for(C GuiObj *go=this; go; go=go->parent())if(go->disabled())return false;
  365. return true;
  366. }
  367. Bool GuiObj::disabledFull()C
  368. {
  369. for(C GuiObj *go=this; go; go=go->parent())if(go->disabled())return true;
  370. return false;
  371. }
  372. /******************************************************************************/
  373. GuiObj& GuiObj::hide()
  374. {
  375. if(visible())
  376. {
  377. _visible=false;
  378. deactivate();
  379. notifyParentOfRectChange(rect(), true);
  380. }
  381. return T;
  382. }
  383. GuiObj& GuiObj::show()
  384. {
  385. if(hidden() && is()) // can't show a deleted object
  386. {
  387. _visible=true;
  388. notifyParentOfRectChange(rect(), false);
  389. }
  390. return T;
  391. }
  392. GuiObj& GuiObj::activate()
  393. {
  394. // hide all menus (from child to parent) until we reach any that belongs to 'this'
  395. for(GuiObj *menu=Gui.menu(); menu->is(GO_MENU); menu=menu->parent())if(!menu->contains(this))menu->hide();else break;
  396. if(type()==GO_DESKTOP)Gui._desktop=&asDesktop();
  397. // show
  398. if(type()==GO_WINDOW) // treat window as a special case because it supports fading
  399. {
  400. Window &window=asWindow(); switch(window._fade_type)
  401. {
  402. case FADE_OUT : window.fadeIn(); break; // if window was fading out then fade in
  403. case FADE_NONE: window.show (); break; // show immediately
  404. //case FADE_IN : break; // do nothing and let it keep fading in
  405. }
  406. }else show();
  407. kbSet();
  408. windowsToTop();
  409. // check if the object is fully visible
  410. if(visibleFull())
  411. {
  412. Gui._menu=&first(GO_MENU)->asMenu();
  413. }
  414. return T;
  415. }
  416. GuiObj& GuiObj::deactivate()
  417. {
  418. // clear keyboard focus
  419. kbClear();
  420. // hide all menus that belong to 'this'
  421. for(GuiObj *menu=Gui.menu(); menu->is(GO_MENU); menu=menu->parent())if(contains(menu))menu->hide();else break;
  422. if(this == Gui.desktop () )Gui._desktop =null;
  423. if(contains(Gui.menu ()))Gui._menu =&_parent->first(GO_MENU )->asMenu ();
  424. if(contains(Gui.windowLit ()))Gui._window_lit =&_parent->first(GO_WINDOW)->asWindow();
  425. if(contains(Gui.ms ()))Gui._ms =null;
  426. if(contains(Gui.msLit ()))Gui._ms_lit =_parent;
  427. if(contains(Gui.wheel ()))Gui._wheel =null;
  428. if(contains(Gui._overlay_textline))Gui._overlay_textline=null;
  429. if(contains(Gui. _desc ))Gui. _desc =null;
  430. if(contains(Gui._touch_desc ))Gui._touch_desc =null;
  431. REPA(Touches)if(contains(Touches[i].guiObj()))Touches[i]._gui_obj=null;
  432. return T;
  433. }
  434. GuiObj& GuiObj::setText()
  435. {
  436. REP(childNum())child(i)->setText();
  437. return T;
  438. }
  439. /******************************************************************************/
  440. // GET / SET
  441. /******************************************************************************/
  442. GuiObj& GuiObj::enabled(Bool enabled)
  443. {
  444. T._disabled=!enabled;
  445. return T;
  446. }
  447. GuiObj& GuiObj::disabled(Bool disabled)
  448. {
  449. T._disabled=disabled;
  450. return T;
  451. }
  452. GuiObj& GuiObj::rect(C Rect &rect)
  453. {
  454. if(T.rect()!=rect)
  455. {
  456. Rect old_client=localClientRect(), old_rect=T.rect(); T._rect=rect;
  457. Rect new_client=localClientRect();
  458. notifyChildrenOfClientRectChange(&old_client, &new_client);
  459. notifyParentOfRectChange ( old_rect , visible() );
  460. }
  461. return T;
  462. }
  463. GuiObj& GuiObj::hidden( Bool hidden) {return visible(!hidden);}
  464. GuiObj& GuiObj::desc (C Str &desc ) {T._desc=desc; return T;}
  465. GuiObj& GuiObj::move (C Vec2 &delta ) {if(delta.any()){Rect old_rect=rect(); T._rect+=delta; notifyParentOfRectChange(old_rect, visible());} return T;}
  466. GuiObj& GuiObj::pos (C Vec2 &pos ) {return move(pos-T.pos ());}
  467. GuiObj& GuiObj::posRU (C Vec2 &pos ) {return move(pos-T.posRU());}
  468. GuiObj& GuiObj::posLD (C Vec2 &pos ) {return move(pos-T.posLD());}
  469. GuiObj& GuiObj::posL (C Vec2 &pos ) {return move(pos-T.posL ());}
  470. GuiObj& GuiObj::posR (C Vec2 &pos ) {return move(pos-T.posR ());}
  471. GuiObj& GuiObj::posD (C Vec2 &pos ) {return move(pos-T.posD ());}
  472. GuiObj& GuiObj::posU (C Vec2 &pos ) {return move(pos-T.posU ());}
  473. GuiObj& GuiObj::posC (C Vec2 &pos ) {return move(pos-T.posC ());}
  474. GuiObj& GuiObj::resize(C Vec2 &delta ) {return rect(Rect(rect().min.x, Min(rect().max.y, rect().min.y-delta.y), Max(rect().min.x, rect().max.x+delta.x), rect().max.y));}
  475. GuiObj& GuiObj::size (C Vec2 &size ) {return rect(Rect(rect().min.x, rect().max.y -Max(0, size.y), rect().min.x +Max(0, size.x), rect().max.y));}
  476. Vec2 GuiObj::screenClientPos ()C {return screenPos()+clientOffset();}
  477. Rect GuiObj::screenClientRect()C {return Rect_LU(screenClientPos(), clientSize());}
  478. Rect GuiObj::screenRect ()C {return Rect_LU(screenPos (), size());}
  479. Vec2 GuiObj::screenPos ()C
  480. {
  481. Vec2 pos=T.pos();
  482. for(GuiObj *go=parent(); go; go=go->parent())switch(go->type())
  483. {
  484. case GO_DESKTOP: break; // desktop shouldn't influence position as it stores elements relative to center
  485. case GO_TAB : break; // elements assigned to 'Tab' aren't relative to 'Tab' position
  486. case GO_TABS : break; // elements assigned to 'Tabs' aren't relative to 'Tabs' position
  487. case GO_WINDOW : pos+=go->asWindow().clientRect().lu(); break;
  488. case GO_MENU : pos+=go->asMenu ().clientRect().lu(); goto finished; // menus are always on top
  489. default : pos+=go->pos(); break;
  490. case GO_REGION:
  491. {
  492. Region &region=go->asRegion();
  493. pos +=region.clientRect().lu();
  494. pos.x-=region.slidebar[0].offset();
  495. pos.y+=region.slidebar[1].offset();
  496. }break;
  497. case GO_LIST:
  498. {
  499. pos+=go->asList().childOffset(T);
  500. pos+=go->pos();
  501. }break;
  502. }
  503. finished:;
  504. return pos;
  505. }
  506. Vec2 GuiObj::clientOffset()C
  507. {
  508. switch(type())
  509. {
  510. case GO_MENU : return asMenu ().clientRect().lu()-pos();
  511. case GO_WINDOW: return asWindow().clientRect().lu()-pos();
  512. case GO_REGION: return asRegion().clientRect().lu()-pos();
  513. }
  514. return 0;
  515. }
  516. Vec2 GuiObj::clientSize()C
  517. {
  518. switch(type())
  519. {
  520. case GO_MENU : return asMenu ().clientSize();
  521. case GO_WINDOW: return asWindow().clientSize();
  522. case GO_REGION: return asRegion().clientSize();
  523. default : return size();
  524. }
  525. }
  526. Rect GuiObj::localClientRect()C
  527. {
  528. C GuiObj *go=this;
  529. for(; go; )
  530. {
  531. if(go->type()==GO_TAB )go=go->parent();else
  532. if(go->type()==GO_TABS)go=go->parent();else break;
  533. }
  534. if(go)switch(go->type())
  535. {
  536. case GO_DESKTOP: return go->rect();
  537. default : return Rect_LU(Vec2Zero, go->clientSize());
  538. }
  539. return Rect(0, 0, 0, 0);
  540. }
  541. CChar* GuiObj::typeName()C {return GuiObjTypeName(type());}
  542. GuiObj& GuiObj::baseLevel(Int level) {if(_base_level!=level){_base_level=level; validateLevel();} return T;}
  543. /******************************************************************************/
  544. // MAIN
  545. /******************************************************************************/
  546. GuiObj* GuiObj::test(C GuiPC &gpc, C Vec2 &pos, GuiObj* &mouse_wheel)
  547. {
  548. return (visible() && gpc.visible && Cuts(pos, (rect()+gpc.offset)&gpc.clip) /*&& is()*/) ? this : null; // no need to check for 'is' because we already check for 'visible' and deleted objects can't be visible
  549. }
  550. /******************************************************************************/
  551. // IO
  552. /******************************************************************************/
  553. Bool GuiObj::save(File &f, CChar *path)C
  554. {
  555. f.putMulti(Byte(4), _visible, _disabled, _rect)<<_desc; // version
  556. return f.ok();
  557. }
  558. Bool GuiObj::load(File &f, CChar *path)
  559. {
  560. switch(f.decUIntV()) // version
  561. {
  562. case 4:
  563. {
  564. f.getMulti(_visible, _disabled, _rect)>>_desc;
  565. if(f.ok())return true;
  566. }break;
  567. case 3:
  568. {
  569. f.getMulti(_visible, _disabled, _rect)._getStr2(_desc);
  570. if(f.ok())return true;
  571. }break;
  572. case 2:
  573. {
  574. _visible=!f.getBool(); f>>_disabled>>_rect; f._getStr(_desc);
  575. if(f.ok())return true;
  576. }break;
  577. case 1:
  578. {
  579. _visible=!f.getBool(); f>>_disabled>>_rect; _desc=f._getStr16();
  580. if(f.ok())return true;
  581. }break;
  582. case 0:
  583. {
  584. _visible=!f.getBool(); f>>_disabled>>_rect; _desc.clear();
  585. if(f.ok())return true;
  586. }break;
  587. }
  588. return false;
  589. }
  590. /******************************************************************************/
  591. // GUI OBJ CHILDREN
  592. /******************************************************************************/
  593. Bool GuiObjChildren::find(C GuiObj &child, Int &index)C
  594. {
  595. REPA(children)if(T[i]==&child){index=i; return true;}
  596. return false;
  597. }
  598. Bool GuiObjChildren::find(C GuiObj &child_a, C GuiObj &child_b, Int &index_a, Int &index_b)C
  599. {
  600. index_a=index_b=-1;
  601. REPA(children)
  602. {
  603. C GuiObj *go=T[i];
  604. if(go==&child_a){index_a=i; if(index_b>=0)return true;}
  605. if(go==&child_b){index_b=i; if(index_a>=0)return true;}
  606. }
  607. return false;
  608. }
  609. void GuiObjChildren::del()
  610. {
  611. #if 1 // disconnect children (this should be used instead of deleting children, as we may still use them)
  612. REPAO(children).go->_parent=null;
  613. #else // delete children
  614. for(GuiObj *last=null; children.elms(); )
  615. {
  616. GuiObj *go=children.last().go; // can't do 'pop' because calling 'del' assumes that object is still in this container, and 'GuiObjChildren.remove' can find it
  617. if(go!=last) // deleting object for the first time
  618. {
  619. go->del();
  620. last=go;
  621. }else // if we already tried to delete this object then it means that 'del' method didn't succeed (for example it was overrided and super wasn't called)
  622. {
  623. #if DEBUG // crash in debug
  624. Str s=S+"Removing Gui Object ("+go->typeName()+") from Parent ";
  625. if(go->parent())s+=S+"("+go->parent()->typeName()+") ";
  626. s+="failed.\nDid you override 'GuiObj.del' method and didn't call 'super.del' ?";
  627. Exit(s);
  628. #else // silently disconnect in release
  629. go->_parent=null;
  630. children.removeLast();
  631. #endif
  632. }
  633. }
  634. #endif
  635. children.del(); kb=null; changed=true;
  636. }
  637. Bool GuiObjChildren::remove(GuiObj &child)
  638. {
  639. Int i; if(find(child, i))
  640. {
  641. children.remove(i, true); if(child.contains(kb))kb=null; child._parent=null; changed=true; // don't deactivate 'child' because we may be just changing parents for it
  642. return true;
  643. }
  644. return false;
  645. }
  646. GuiObjChildren::Child* GuiObjChildren::add(GuiObj &child, GuiObj &parent)
  647. {
  648. GuiObj *old_parent=child.parent();
  649. if(old_parent!=&parent)
  650. {
  651. if(old_parent)*old_parent-=child;
  652. Int l=0, r=children.elms(); for(; l<r; )
  653. {
  654. Int m=UInt(l+r)/2;
  655. if(CompareLevel(child, *children[m].go)<0)r=m;else l=m+1;
  656. }
  657. Child &c=children.NewAt(l); c.go=&child; child._parent=&parent; child.parentClientRectChanged(old_parent ? &old_parent->localClientRect() : null, &parent.localClientRect());
  658. if(!kb && child.kbCatch())kb=&child;
  659. changed=true;
  660. return &c;
  661. }
  662. Int index; if(find(child, index))return &children[index]; // if that's the same parent then return its existing reference
  663. return null;
  664. }
  665. void GuiObjChildren::validateLevel(C GuiObj &child)
  666. {
  667. Int i; if(find(child, i))
  668. {
  669. for(; InRange(i-1, children) && CompareLevel(*children[i].go, *children[i-1].go)<0; i--){children.swapOrder(i, i-1); changed=true;}
  670. for(; InRange(i+1, children) && CompareLevel(*children[i].go, *children[i+1].go)>0; i++){children.swapOrder(i, i+1); changed=true;}
  671. }
  672. }
  673. Int GuiObjChildren::compareLevel(C GuiObj &child_a, C GuiObj &child_b)C
  674. {
  675. Int ia, ib; if(find(child_a, child_b, ia, ib))return ia-ib; // if both were found
  676. return 0;
  677. }
  678. Bool GuiObjChildren::partiallyOccludedInSameLevel(C GuiObj &child)
  679. {
  680. C Rect &rect=child.rect();
  681. REPA(children) // go from the end to process top objects first
  682. {
  683. C GuiObj &test=*children[i].go; if(&test==&child)return false; // found self
  684. if(test.visible() && Cuts(test.rect(), rect) && CompareLevel(child, test)>=0)return true; // if visible and partially occludes and level of 'test' is not higher (but same or lower), we test the level because we use this function for focusing windows (to know if activating it, and thus moving it to front, would make any difference)
  685. }
  686. return false;
  687. }
  688. Bool GuiObjChildren::moveUp(C GuiObj &child)
  689. {
  690. Int i; if(find(child, i))if(InRange(i+1, children) && CompareLevel(child, *children[i+1].go)>=0){children.swapOrder(i, i+1); changed=true; return true;}
  691. return false;
  692. }
  693. Bool GuiObjChildren::moveDown(C GuiObj &child)
  694. {
  695. Int i; if(find(child, i))if(InRange(i-1, children) && CompareLevel(child, *children[i-1].go)<=0){children.swapOrder(i, i-1); changed=true; return true;}
  696. return false;
  697. }
  698. void GuiObjChildren::moveToTop(C GuiObj &child)
  699. {
  700. Int i; if(find(child, i))for(; InRange(i+1, children) && CompareLevel(child, *children[i+1].go)>=0; i++){children.swapOrder(i, i+1); changed=true;}
  701. }
  702. void GuiObjChildren::moveToBottom(C GuiObj &child)
  703. {
  704. Int i; if(find(child, i))for(; InRange(i-1, children) && CompareLevel(child, *children[i-1].go)<=0; i--){children.swapOrder(i, i-1); changed=true;}
  705. }
  706. void GuiObjChildren::moveAbove(C GuiObj &child_a, C GuiObj &child_b)
  707. {
  708. Int ia, ib; if(find(child_a, child_b, ia, ib))for(; ia<ib && InRange(ia+1, children) && CompareLevel(child_a, *children[ia+1].go)>=0; ia++){children.swapOrder(ia, ia+1); changed=true;}
  709. }
  710. void GuiObjChildren::moveBelow(C GuiObj &child_a, C GuiObj &child_b)
  711. {
  712. Int ia, ib; if(find(child_a, child_b, ia, ib))for(; ia>ib && InRange(ia-1, children) && CompareLevel(child_a, *children[ia-1].go)<=0; ia--){children.swapOrder(ia, ia-1); changed=true;}
  713. }
  714. static Bool ValidForSwitch(GuiObj *go)
  715. {
  716. if(go && go->kbCatch())switch(go->type())
  717. {
  718. case GO_WINDOW :
  719. case GO_VIEWPORT:
  720. case GO_TABS : break;
  721. case GO_BUTTON: if(GuiSkin *skin=go->asButton().getSkin())return skin->keyboard_highlight_color.a!=0; break; // only if there's visual indication of keyboard highlight
  722. default: return true;
  723. }
  724. return false;
  725. }
  726. Bool GuiObjChildren::Switch(C GuiObj &go, Bool next)
  727. {
  728. Int i; if(find(go, i))
  729. {
  730. Int dir=SignBool(next);
  731. for(Int j=i+dir+children.elms(); (j%children.elms())!=i; j+=dir) // add 'children.elms()' at start to solve negative modulo issue
  732. {
  733. GuiObj *go=T[j%children.elms()]; if(ValidForSwitch(go))
  734. {
  735. go->activate();
  736. if(go->type()==GO_TEXTLINE && go->enabled())go->asTextLine().selectAll();
  737. return true;
  738. }
  739. }
  740. }
  741. return false;
  742. }
  743. GuiObj* GuiObjChildren::test (C GuiPC &gpc, C Vec2 &pos, GuiObj* &mouse_wheel) { REPA(children)if(GuiObj *go=T[i])if(go=go->test(gpc, pos, mouse_wheel))return go; return null;} // order is important, go from the end (objects on top) and stop on first found
  744. void GuiObjChildren::draw (C GuiPC &gpc ) {FREPA(children)if(GuiObj *go=T[i]) go->draw(gpc ) ; } // order is important
  745. void GuiObjChildren::update(C GuiPC &gpc )
  746. {
  747. // clear all children at start
  748. FREPA(children)if(GuiObj *go=T[i])go->_updated=false;
  749. again:
  750. changed=false; // set no change at start
  751. FREPA(children)if(GuiObj *go=T[i])if(!go->_updated) // if this object wasn't updated yet
  752. {
  753. go->_updated=true; // set as updated
  754. go-> update(gpc); // update
  755. if(changed)goto again; // if detected any change in children container, then start again
  756. }
  757. }
  758. /******************************************************************************/
  759. CChar* GuiObjTypeName(GUI_OBJ_TYPE type)
  760. {
  761. switch(type)
  762. {
  763. default : return null;
  764. case GO_BUTTON : return u"Button";
  765. case GO_CHECKBOX: return u"CheckBox";
  766. case GO_COMBOBOX: return u"ComboBox";
  767. case GO_CUSTOM : return u"Custom";
  768. case GO_DESKTOP : return u"Desktop";
  769. case GO_IMAGE : return u"Image";
  770. case GO_LIST : return u"List";
  771. case GO_MENU : return u"Menu";
  772. case GO_MENU_BAR: return u"MenuBar";
  773. case GO_PROGRESS: return u"ProgressBar";
  774. case GO_REGION : return u"Region";
  775. case GO_SLIDEBAR: return u"SlideBar";
  776. case GO_SLIDER : return u"Slider";
  777. case GO_TAB : return u"Tab";
  778. case GO_TABS : return u"Tabs";
  779. case GO_TEXT : return u"Text";
  780. case GO_TEXTBOX : return u"TextBox";
  781. case GO_TEXTLINE: return u"TextLine";
  782. case GO_VIEWPORT: return u"Viewport";
  783. case GO_WINDOW : return u"Window";
  784. }
  785. }
  786. /******************************************************************************/
  787. }
  788. /******************************************************************************/