ComboBox.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************/
  5. static void MenuPushed(C Str &path, ComboBox &cb)
  6. {
  7. cb.setPath(GetStart(path), GetBase(path));
  8. }
  9. static void ChangedButton(ComboBox &cb)
  10. {
  11. if(cb.Button::operator()())
  12. {
  13. Bool touch=false; REPA(Touches)if(Touches[i].pd() && Touches[i].guiObj()==&cb){touch=true; break;} cb.menu.setSize(touch);
  14. cb.menu.list.cur=-1;
  15. if(0 && D.smallSize())cb.menu.posC(0);else cb.menu.posAround(cb.screenRect(), cb.menu_align);
  16. cb.menu.activate();
  17. }else
  18. {
  19. cb.menu.hide();
  20. }
  21. }
  22. /******************************************************************************/
  23. void ComboBox::zero()
  24. {
  25. flag =COMBOBOX_MOUSE_WHEEL;
  26. menu_align= 1;
  27. _abs_sel =-1;
  28. _func_immediate=false;
  29. _func_user =null;
  30. _func =null;
  31. }
  32. ComboBox::ComboBox() {zero();}
  33. ComboBox& ComboBox::del()
  34. {
  35. menu.del();
  36. super::del(); zero(); return T;
  37. }
  38. void ComboBox::setParams()
  39. {
  40. _type =GO_COMBOBOX;
  41. _sub_type=BUTTON_TYPE_COMBOBOX;
  42. super::func(ChangedButton, T); // this must be 'Button.func'
  43. menu.create().func(MenuPushed, T).skin(skin())._owner=this;
  44. Gui+=menu; // add after setting '_owner' because it may affect level in children array
  45. }
  46. ComboBox& ComboBox::create()
  47. {
  48. del();
  49. super::create();
  50. setParams(); // call after 'create'
  51. mode =BUTTON_TOGGLE; // call after 'create'
  52. text_align=1; // call after 'create'
  53. return T;
  54. }
  55. ComboBox& ComboBox::create(C ComboBox &src)
  56. {
  57. if(this!=&src)
  58. {
  59. if(!src.is())del();else
  60. {
  61. super::create(src);
  62. _type =GO_COMBOBOX;
  63. flag =src. flag;
  64. menu_align =src. menu_align;
  65. _abs_sel =src._abs_sel;
  66. _func_immediate=src._func_immediate;
  67. _func_user =src._func_user;
  68. _func =src._func;
  69. super::func(ChangedButton, T); // this must be 'Button.func'
  70. T.menu.create(src.menu).func(MenuPushed, T)._owner=this;
  71. Gui+=T.menu; // add after setting '_owner' because it may affect level in children array
  72. }
  73. }
  74. return T;
  75. }
  76. /******************************************************************************/
  77. ComboBox& ComboBox::clear()
  78. {
  79. menu.list.clear();
  80. return T;
  81. }
  82. /******************************************************************************/
  83. ComboBox& ComboBox::resetText()
  84. {
  85. if(!(flag&COMBOBOX_CONST_TEXT))
  86. {
  87. if(absSel()>=0)if(ListColumn *lc=menu.listColumn()){text=lc->md.asText(menu.list.absToData(absSel())); goto end;}
  88. text.clear();
  89. }
  90. end: return T;
  91. }
  92. ComboBox& ComboBox::set(Int abs_sel, SET_MODE mode)
  93. {
  94. if(flag&COMBOBOX_SET_CLAMP)MIN(abs_sel, menu.list.totalElms()-1); // clamp to last value
  95. if(menu.list.absToVis(abs_sel)<0)abs_sel=-1; // disable selecting hidden and out of range elements
  96. if(T._abs_sel!=abs_sel)
  97. {
  98. T._abs_sel=abs_sel;
  99. if(!(flag&COMBOBOX_CONST_TEXT))
  100. {
  101. text.clear();
  102. if(abs_sel>=0)if(ListColumn *lc=menu.listColumn())text=lc->md.asText(menu.list.absToData(abs_sel));
  103. }
  104. if(mode!=QUIET)call();
  105. }else resetText(); // if we're not changing selection, the element's text can be now different, so set it in case it got changed
  106. return T;
  107. }
  108. ComboBox& ComboBox::setText(C Str &text, Bool force, SET_MODE mode)
  109. {
  110. // find element that matches desired text
  111. if(ListColumn *lc=menu.listColumn())
  112. {
  113. REPA(menu.list)if(Equal(lc->md.asText(menu.list.visToData(i), lc->precision), text))
  114. {
  115. return set(menu.list.visToAbs(i), mode);
  116. }
  117. }
  118. // element was not found
  119. if(!Equal(T.text, text, true)) // new text is different
  120. {
  121. _abs_sel=-1;
  122. //if(!(flag&COMBOBOX_CONST_TEXT))
  123. {
  124. if(force)T.text=text;
  125. else T.text.clear();
  126. }
  127. if(mode!=QUIET)call();
  128. }
  129. return T;
  130. }
  131. ComboBox& ComboBox::setPath(C Str &start, C Str &end)
  132. {
  133. Int new_sel=-1;
  134. if(ListColumn *lc=menu.listColumn())
  135. {
  136. REPA(menu.list)if(Equal(lc->md.asText(menu.list.visToData(i), lc->precision), start))
  137. {
  138. new_sel=menu.list.visToAbs(i);
  139. break;
  140. }
  141. }
  142. if(new_sel!=_abs_sel || !Equal(text, end, true))
  143. {
  144. _abs_sel=new_sel;
  145. if(!(flag&COMBOBOX_CONST_TEXT))text=end;
  146. call();
  147. }
  148. return T;
  149. }
  150. /******************************************************************************/
  151. ComboBox& ComboBox::rect(C Rect &rect)
  152. {
  153. if(T.rect()!=rect)
  154. {
  155. super::rect(rect);
  156. menu.list.setRects();
  157. }
  158. return T;
  159. }
  160. /******************************************************************************/
  161. ComboBox& ComboBox::skin(C GuiSkinPtr &skin, Bool sub_objects)
  162. {
  163. super::skin=skin;
  164. if(sub_objects)menu.skin(skin, true);
  165. return T;
  166. }
  167. /******************************************************************************/
  168. ComboBox& ComboBox::func(void (*func)(Ptr), Ptr user, Bool immediate)
  169. {
  170. T._func =func;
  171. T._func_user =user;
  172. T._func_immediate=immediate;
  173. return T;
  174. }
  175. void ComboBox::call()
  176. {
  177. if(_func)if(_func_immediate)_func(_func_user);else Gui.addFuncCall(_func, _func_user);
  178. }
  179. /******************************************************************************/
  180. GuiObj* ComboBox::test(C GuiPC &gpc, C Vec2 &pos, GuiObj* &mouse_wheel)
  181. {
  182. if(GuiObj *go=super::test(gpc, pos, mouse_wheel))
  183. {
  184. if(flag&COMBOBOX_MOUSE_WHEEL)
  185. if(!mouse_wheel || mouse_wheel->type()!=GO_SLIDEBAR) // don't grab wheel focus if it already belongs to a SlideBar (for example ComboBox located in a Region with SlideBars)
  186. mouse_wheel=this;
  187. return go;
  188. }
  189. return null;
  190. }
  191. /******************************************************************************/
  192. static void Next(ComboBox &cb, Int delta)
  193. {
  194. if(Int sign=Sign(delta))for(Int i=cb.visSel()+sign; InRange(i, cb); i+=sign)
  195. {
  196. if(InRange(i, cb.menu.elms()))
  197. {
  198. MenuElm &elm=cb.menu.elm(i);
  199. if(elm.disabled || (elm.flag()&MENU_NOT_SELECTABLE) || elm.menu())continue; // if element is disabled, can't be selected or has children (normally we'd need to select one of the children, but for simplicity let's just skip it)
  200. }
  201. if(cb.menu._selectable_offset>=0)if(Ptr data=cb.menu.list.visToData(i))if(!*(Bool*)((Byte*)data+cb.menu._selectable_offset))continue;
  202. if(delta-=sign)continue; // want to keep going
  203. cb.set(cb.menu.list.visToAbs(i));
  204. break;
  205. }
  206. }
  207. void ComboBox::update(C GuiPC &gpc)
  208. {
  209. GuiPC gpc2(gpc, visible(), enabled());
  210. if( gpc2.enabled)
  211. {
  212. Bool menu_active=Button::operator()(); // remember state before button update
  213. super::update(gpc2);
  214. if(gpc2.visible)
  215. {
  216. if(Gui.kb()==this)
  217. {
  218. if(Kb.k(KB_UP ))Next(T, -1);
  219. if(Kb.k(KB_DOWN))Next(T, +1);
  220. }
  221. if(Gui.wheel()==this)Next(T, -Ms.wheelI());
  222. if(menu_active && !menu.contains(Gui.menu()))super::set(false, QUIET);
  223. if(Gui.kb()==this && (Kb.k(KB_ENTER) || Kb.k(KB_NPENTER)) && Kb.k.first())
  224. {
  225. Kb.eatKey();
  226. super::set(!Button::operator()());
  227. }
  228. }
  229. }
  230. }
  231. /******************************************************************************/
  232. Bool ComboBox::save(File &f, CChar *path)C
  233. {
  234. if(super::save(f, path))
  235. {
  236. f.cmpUIntV(0); // version
  237. f<<flag<<_abs_sel<<menu_align;
  238. return f.ok();
  239. }
  240. return false;
  241. }
  242. Bool ComboBox::load(File &f, CChar *path)
  243. {
  244. del();
  245. {Long f_pos=f.pos(); UInt ver=f.decUIntV(); f.pos(f_pos); if(ver<3)goto old;} // in the past the base class of ComboBox was GuiObj, so detect that and jump to old load if needed, '3' is the GuiObj save file version from the past
  246. if(super::load(f, path))switch(f.decUIntV()) // version
  247. {
  248. case 0:
  249. {
  250. f>>flag>>_abs_sel>>menu_align;
  251. if(f.ok()){setParams(); return true;}
  252. }break;
  253. }
  254. // old format
  255. if(0)
  256. {
  257. old:
  258. if(GuiObj::load(f, path))switch(f.decUIntV()) // version
  259. {
  260. case 1:
  261. {
  262. f>>_focusable>>flag>>_abs_sel;
  263. if(super::load(f, path))
  264. if(f.ok()){setParams(); return true;}
  265. }break;
  266. case 0:
  267. {
  268. f>>_focusable>>flag; f.skip(4); f>>_abs_sel;
  269. if(TextLine().load(f, path))
  270. if(super ::load(f, path))
  271. if(f.ok()){setParams(); return true;}
  272. }break;
  273. }
  274. }
  275. del(); return false;
  276. }
  277. /******************************************************************************/
  278. }
  279. /******************************************************************************/