popup_menu.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960
  1. /*************************************************************************/
  2. /* popup_menu.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* http://www.godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
  9. /* */
  10. /* Permission is hereby granted, free of charge, to any person obtaining */
  11. /* a copy of this software and associated documentation files (the */
  12. /* "Software"), to deal in the Software without restriction, including */
  13. /* without limitation the rights to use, copy, modify, merge, publish, */
  14. /* distribute, sublicense, and/or sell copies of the Software, and to */
  15. /* permit persons to whom the Software is furnished to do so, subject to */
  16. /* the following conditions: */
  17. /* */
  18. /* The above copyright notice and this permission notice shall be */
  19. /* included in all copies or substantial portions of the Software. */
  20. /* */
  21. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  22. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  23. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  24. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  25. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  26. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  27. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  28. /*************************************************************************/
  29. #include "popup_menu.h"
  30. #include "print_string.h"
  31. #include "os/keyboard.h"
  32. #include "translation.h"
  33. #include "os/input.h"
  34. String PopupMenu::_get_accel_text(uint32_t p_accel) const {
  35. return keycode_get_string(p_accel);
  36. /*
  37. String atxt;
  38. if (p_accel&KEY_MASK_SHIFT)
  39. atxt+="Shift+";
  40. if (p_accel&KEY_MASK_ALT)
  41. atxt+="Alt+";
  42. if (p_accel&KEY_MASK_CTRL)
  43. atxt+="Ctrl+";
  44. if (p_accel&KEY_MASK_META)
  45. atxt+="Meta+";
  46. p_accel&=KEY_CODE_MASK;
  47. atxt+=String::chr(p_accel).to_upper();
  48. return atxt;
  49. */
  50. }
  51. Size2 PopupMenu::get_minimum_size() const {
  52. int vseparation = get_constant("vseparation");
  53. int hseparation = get_constant("hseparation");
  54. Size2 minsize = get_stylebox("panel")->get_minimum_size();
  55. Ref<Font> font = get_font("font");
  56. float max_w=0;
  57. int font_h = font->get_height();
  58. int check_w = get_icon("checked")->get_width();
  59. int accel_max_w=0;
  60. for (int i=0;i<items.size();i++) {
  61. Size2 size;
  62. if (!items[i].icon.is_null()) {
  63. Size2 icon_size = items[i].icon->get_size();
  64. size.height = MAX( icon_size.height, font_h );
  65. size.width+=icon_size.width;
  66. size.width+=hseparation;
  67. } else {
  68. size.height=font_h;
  69. }
  70. if (items[i].checkable) {
  71. size.width+=check_w+hseparation;
  72. }
  73. size.width+=font->get_string_size(items[i].text).width;
  74. if (i>0)
  75. size.height+=vseparation;
  76. if (items[i].accel) {
  77. int accel_w = hseparation*2;
  78. accel_w+=font->get_string_size(_get_accel_text(items[i].accel)).width;
  79. accel_max_w = MAX( accel_w, accel_max_w );
  80. }
  81. minsize.height+=size.height;
  82. max_w = MAX( max_w, size.width );
  83. }
  84. minsize.width+=max_w+accel_max_w;
  85. return minsize;
  86. }
  87. int PopupMenu::_get_mouse_over(const Point2& p_over) const {
  88. if (p_over.x<0 || p_over.x>=get_size().width)
  89. return -1;
  90. Ref<StyleBox> style = get_stylebox("panel");
  91. Point2 ofs=style->get_offset();
  92. if (ofs.y>p_over.y)
  93. return -1;
  94. Ref<Font> font = get_font("font");
  95. int vseparation = get_constant("vseparation");
  96. // int hseparation = get_constant("hseparation");
  97. float font_h=font->get_height();
  98. for (int i=0;i<items.size();i++) {
  99. if (i>0)
  100. ofs.y+=vseparation;
  101. float h;
  102. if (!items[i].icon.is_null()) {
  103. Size2 icon_size = items[i].icon->get_size();
  104. h = MAX( icon_size.height, font_h );
  105. } else {
  106. h=font_h;
  107. }
  108. ofs.y+=h;
  109. if (p_over.y < ofs.y) {
  110. return i;
  111. }
  112. }
  113. return -1;
  114. }
  115. void PopupMenu::_activate_submenu(int over) {
  116. Node* n = get_node(items[over].submenu);
  117. ERR_EXPLAIN("item subnode does not exist: "+items[over].submenu);
  118. ERR_FAIL_COND(!n);
  119. Popup *pm = n->cast_to<Popup>();
  120. ERR_EXPLAIN("item subnode is not a Popup: "+items[over].submenu);
  121. ERR_FAIL_COND(!pm);
  122. if (pm->is_visible())
  123. return; //already visible!
  124. Point2 p = get_global_pos();
  125. Rect2 pr(p,get_size());
  126. Ref<StyleBox> style = get_stylebox("panel");
  127. Point2 pos = p+Point2(get_size().width,items[over]._ofs_cache-style->get_offset().y);
  128. Size2 size = pm->get_size();
  129. // fix pos
  130. if (pos.x+size.width > get_viewport_rect().size.width)
  131. pos.x=p.x-size.width;
  132. pm->set_pos(pos);
  133. pm->popup();
  134. PopupMenu *pum = pm->cast_to<PopupMenu>();
  135. if (pum) {
  136. pr.pos-=pum->get_global_pos();
  137. pum->clear_autohide_areas();
  138. pum->add_autohide_area(Rect2(pr.pos.x,pr.pos.y,pr.size.x,items[over]._ofs_cache));
  139. if (over<items.size()-1) {
  140. int from = items[over+1]._ofs_cache;
  141. pum->add_autohide_area(Rect2(pr.pos.x,pr.pos.y+from,pr.size.x,pr.size.y-from));
  142. }
  143. }
  144. }
  145. void PopupMenu::_submenu_timeout() {
  146. if (mouse_over==submenu_over) {
  147. _activate_submenu(mouse_over);
  148. submenu_over=-1;
  149. }
  150. }
  151. void PopupMenu::_input_event(const InputEvent &p_event) {
  152. switch( p_event.type) {
  153. case InputEvent::KEY: {
  154. if (!p_event.key.pressed)
  155. break;
  156. switch(p_event.key.scancode) {
  157. case KEY_DOWN: {
  158. for(int i=mouse_over+1;i<items.size();i++) {
  159. if (i<0 || i>=items.size())
  160. continue;
  161. if (!items[i].separator && !items[i].disabled) {
  162. mouse_over=i;
  163. update();
  164. break;
  165. }
  166. }
  167. } break;
  168. case KEY_UP: {
  169. for(int i=mouse_over-1;i>=0;i--) {
  170. if (i<0 || i>=items.size())
  171. continue;
  172. if (!items[i].separator && !items[i].disabled) {
  173. mouse_over=i;
  174. update();
  175. break;
  176. }
  177. }
  178. } break;
  179. case KEY_RETURN:
  180. case KEY_ENTER: {
  181. if (mouse_over>=0 && mouse_over<items.size() && !items[mouse_over].separator) {
  182. activate_item(mouse_over);
  183. }
  184. } break;
  185. }
  186. } break;
  187. case InputEvent::MOUSE_BUTTON: {
  188. const InputEventMouseButton &b=p_event.mouse_button;
  189. if (b.pressed)
  190. break;
  191. switch(b.button_index) {
  192. case BUTTON_WHEEL_DOWN: {
  193. if (get_global_pos().y + get_size().y > get_viewport_rect().size.y) {
  194. int vseparation = get_constant("vseparation");
  195. Ref<Font> font = get_font("font");
  196. Point2 pos = get_pos();
  197. int s = (vseparation+font->get_height())*3;
  198. pos.y-=s;
  199. set_pos(pos);
  200. //update hover
  201. InputEvent ie;
  202. ie.type=InputEvent::MOUSE_MOTION;
  203. ie.mouse_motion.x=b.x;
  204. ie.mouse_motion.y=b.y+s;
  205. _input_event(ie);
  206. }
  207. } break;
  208. case BUTTON_WHEEL_UP: {
  209. if (get_global_pos().y < 0) {
  210. int vseparation = get_constant("vseparation");
  211. Ref<Font> font = get_font("font");
  212. Point2 pos = get_pos();
  213. int s = (vseparation+font->get_height())*3;
  214. pos.y+=s;
  215. set_pos(pos);
  216. //update hover
  217. InputEvent ie;
  218. ie.type=InputEvent::MOUSE_MOTION;
  219. ie.mouse_motion.x=b.x;
  220. ie.mouse_motion.y=b.y-s;
  221. _input_event(ie);
  222. }
  223. } break;
  224. case BUTTON_LEFT: {
  225. int over=_get_mouse_over(Point2(b.x,b.y));
  226. if (invalidated_click) {
  227. invalidated_click=false;
  228. break;
  229. }
  230. if (over<0) {
  231. hide();
  232. break; //non-activable
  233. }
  234. if (items[over].separator || items[over].disabled)
  235. break;
  236. if (items[over].submenu!="") {
  237. _activate_submenu(over);
  238. return;
  239. }
  240. activate_item(over);
  241. } break;
  242. }
  243. //update();
  244. } break;
  245. case InputEvent::MOUSE_MOTION: {
  246. if (invalidated_click) {
  247. moved+=Vector2(p_event.mouse_motion.relative_x,p_event.mouse_motion.relative_y);
  248. if (moved.length()>4)
  249. invalidated_click=false;
  250. }
  251. const InputEventMouseMotion &m=p_event.mouse_motion;
  252. for(List<Rect2>::Element *E=autohide_areas.front();E;E=E->next()) {
  253. if (!Rect2(Point2(),get_size()).has_point(Point2(m.x,m.y)) && E->get().has_point(Point2(m.x,m.y))) {
  254. call_deferred("hide");
  255. return;
  256. }
  257. }
  258. int over=_get_mouse_over(Point2(m.x,m.y));
  259. int id = (over<0 || items[over].separator || items[over].disabled)?-1:(items[over].ID>=0?items[over].ID:over);
  260. if (id<0) {
  261. mouse_over=-1;
  262. update();
  263. break;
  264. }
  265. if (items[over].submenu!="" && submenu_over!=over) {
  266. submenu_over=over;
  267. submenu_timer->start();
  268. }
  269. if (over!=mouse_over) {
  270. mouse_over=over;
  271. update();
  272. }
  273. } break;
  274. }
  275. }
  276. bool PopupMenu::has_point(const Point2& p_point) const {
  277. if (parent_rect.has_point(p_point))
  278. return true;
  279. for(const List<Rect2>::Element *E=autohide_areas.front();E;E=E->next()) {
  280. if (E->get().has_point(p_point))
  281. return true;
  282. }
  283. return Control::has_point(p_point);
  284. }
  285. void PopupMenu::_notification(int p_what) {
  286. switch(p_what) {
  287. case NOTIFICATION_DRAW: {
  288. RID ci = get_canvas_item();
  289. Size2 size=get_size();
  290. Ref<StyleBox> style = get_stylebox("panel");
  291. Ref<StyleBox> hover = get_stylebox("hover");
  292. Ref<Font> font = get_font("font");
  293. Ref<Texture> check = get_icon("checked");
  294. Ref<Texture> uncheck = get_icon("unchecked");
  295. Ref<Texture> submenu= get_icon("submenu");
  296. Ref<StyleBox> separator = get_stylebox("separator");
  297. style->draw( ci, Rect2( Point2(), get_size() ) );
  298. Point2 ofs=style->get_offset();
  299. int vseparation = get_constant("vseparation");
  300. int hseparation = get_constant("hseparation");
  301. Color font_color = get_color("font_color");
  302. Color font_color_disabled = get_color("font_color_disabled");
  303. Color font_color_accel = get_color("font_color_accel");
  304. Color font_color_hover = get_color("font_color_hover");
  305. float font_h=font->get_height();
  306. for (int i=0;i<items.size();i++) {
  307. if (i>0)
  308. ofs.y+=vseparation;
  309. Point2 item_ofs=ofs;
  310. float h;
  311. Size2 icon_size;
  312. if (!items[i].icon.is_null()) {
  313. icon_size = items[i].icon->get_size();
  314. h = MAX( icon_size.height, font_h );
  315. } else {
  316. h=font_h;
  317. }
  318. if (i==mouse_over) {
  319. hover->draw(ci, Rect2( ofs+Point2(-hseparation,-vseparation), Size2( get_size().width - style->get_minimum_size().width + hseparation*2, h+vseparation*2 ) ));
  320. }
  321. if (items[i].separator) {
  322. int sep_h=separator->get_center_size().height+separator->get_minimum_size().height;
  323. separator->draw(ci, Rect2( ofs+Point2(0,Math::floor((h-sep_h)/2.0)), Size2( get_size().width - style->get_minimum_size().width , sep_h ) ));
  324. }
  325. if (items[i].checkable) {
  326. if (items[i].checked)
  327. check->draw(ci, item_ofs+Point2(0,Math::floor((h-check->get_height())/2.0)));
  328. else
  329. uncheck->draw(ci, item_ofs+Point2(0,Math::floor((h-check->get_height())/2.0)));
  330. item_ofs.x+=check->get_width()+hseparation;
  331. }
  332. if (!items[i].icon.is_null()) {
  333. items[i].icon->draw( ci, item_ofs+Point2(0,Math::floor((h-icon_size.height)/2.0)));
  334. item_ofs.x+=items[i].icon->get_width();
  335. item_ofs.x+=hseparation;
  336. }
  337. if (items[i].submenu!="") {
  338. submenu->draw( ci, Point2(size.width - style->get_margin(MARGIN_RIGHT) - submenu->get_width(),item_ofs.y+Math::floor(h-submenu->get_height())/2));
  339. }
  340. item_ofs.y+=font->get_ascent();
  341. if (!items[i].separator)
  342. font->draw(ci,item_ofs+Point2(0,Math::floor((h-font_h)/2.0)),items[i].text,items[i].disabled?font_color_disabled:(i==mouse_over?font_color_hover:font_color));
  343. if (items[i].accel) {
  344. //accelerator
  345. String text = _get_accel_text(items[i].accel);
  346. item_ofs.x=size.width-style->get_margin(MARGIN_RIGHT)-font->get_string_size(text).width;
  347. font->draw(ci,item_ofs+Point2(0,Math::floor((h-font_h)/2.0)),text,i==mouse_over?font_color_hover:font_color_accel);
  348. }
  349. items[i]._ofs_cache=ofs.y;
  350. ofs.y+=h;
  351. }
  352. } break;
  353. case NOTIFICATION_MOUSE_ENTER: {
  354. grab_focus();
  355. } break;
  356. case NOTIFICATION_MOUSE_EXIT: {
  357. if (mouse_over>=0) {
  358. mouse_over=-1;
  359. update();
  360. }
  361. } break;
  362. }
  363. }
  364. void PopupMenu::add_icon_item(const Ref<Texture>& p_icon,const String& p_label,int p_ID,uint32_t p_accel) {
  365. Item item;
  366. item.icon=p_icon;
  367. item.text=p_label;
  368. item.accel=p_accel;
  369. item.ID=p_ID;
  370. items.push_back(item);
  371. update();
  372. }
  373. void PopupMenu::add_item(const String& p_label,int p_ID,uint32_t p_accel) {
  374. Item item;
  375. item.text=XL_MESSAGE(p_label);
  376. item.accel=p_accel;
  377. item.ID=p_ID;
  378. items.push_back(item);
  379. update();
  380. }
  381. void PopupMenu::add_submenu_item(const String& p_label, const String& p_submenu,int p_ID){
  382. Item item;
  383. item.text=XL_MESSAGE(p_label);
  384. item.ID=p_ID;
  385. item.submenu=p_submenu;
  386. items.push_back(item);
  387. update();
  388. }
  389. void PopupMenu::add_icon_check_item(const Ref<Texture>& p_icon,const String& p_label,int p_ID,uint32_t p_accel) {
  390. Item item;
  391. item.icon=p_icon;
  392. item.text=XL_MESSAGE(p_label);
  393. item.accel=p_accel;
  394. item.ID=p_ID;
  395. item.checkable=true;
  396. items.push_back(item);
  397. update();
  398. }
  399. void PopupMenu::add_check_item(const String& p_label,int p_ID,uint32_t p_accel) {
  400. Item item;
  401. item.text=XL_MESSAGE(p_label);
  402. item.accel=p_accel;
  403. item.ID=p_ID;
  404. item.checkable=true;
  405. items.push_back(item);
  406. update();
  407. }
  408. void PopupMenu::set_item_text(int p_idx,const String& p_text) {
  409. ERR_FAIL_INDEX(p_idx,items.size());
  410. items[p_idx].text=XL_MESSAGE(p_text);
  411. update();
  412. }
  413. void PopupMenu::set_item_icon(int p_idx,const Ref<Texture>& p_icon) {
  414. ERR_FAIL_INDEX(p_idx,items.size());
  415. items[p_idx].icon=p_icon;
  416. update();
  417. }
  418. void PopupMenu::set_item_checked(int p_idx,bool p_checked) {
  419. ERR_FAIL_INDEX(p_idx,items.size());
  420. items[p_idx].checked=p_checked;
  421. update();
  422. }
  423. void PopupMenu::set_item_ID(int p_idx,int p_ID) {
  424. ERR_FAIL_INDEX(p_idx,items.size());
  425. items[p_idx].ID=p_ID;
  426. update();
  427. }
  428. void PopupMenu::set_item_accelerator(int p_idx,uint32_t p_accel) {
  429. ERR_FAIL_INDEX(p_idx,items.size());
  430. items[p_idx].accel=p_accel;
  431. update();
  432. }
  433. void PopupMenu::set_item_metadata(int p_idx,const Variant& p_meta) {
  434. ERR_FAIL_INDEX(p_idx,items.size());
  435. items[p_idx].metadata=p_meta;
  436. update();
  437. }
  438. void PopupMenu::set_item_disabled(int p_idx,bool p_disabled) {
  439. ERR_FAIL_INDEX(p_idx,items.size());
  440. items[p_idx].disabled=p_disabled;
  441. update();
  442. }
  443. void PopupMenu::set_item_submenu(int p_idx, const String& p_submenu) {
  444. ERR_FAIL_INDEX(p_idx,items.size());
  445. items[p_idx].submenu=p_submenu;
  446. update();
  447. }
  448. String PopupMenu::get_item_text(int p_idx) const {
  449. ERR_FAIL_INDEX_V(p_idx,items.size(),"");
  450. return items[p_idx].text;
  451. }
  452. Ref<Texture> PopupMenu::get_item_icon(int p_idx) const {
  453. ERR_FAIL_INDEX_V(p_idx,items.size(),Ref<Texture>());
  454. return items[p_idx].icon;
  455. }
  456. uint32_t PopupMenu::get_item_accelerator(int p_idx) const {
  457. ERR_FAIL_INDEX_V(p_idx,items.size(),0);
  458. return items[p_idx].accel;
  459. }
  460. Variant PopupMenu::get_item_metadata(int p_idx) const {
  461. ERR_FAIL_INDEX_V(p_idx,items.size(),Variant());
  462. return items[p_idx].metadata;
  463. }
  464. bool PopupMenu::is_item_disabled(int p_idx) const {
  465. ERR_FAIL_INDEX_V(p_idx,items.size(),false);
  466. return items[p_idx].disabled;
  467. }
  468. bool PopupMenu::is_item_checked(int p_idx) const {
  469. ERR_FAIL_INDEX_V(p_idx,items.size(),false);
  470. return items[p_idx].checked;
  471. }
  472. int PopupMenu::get_item_ID(int p_idx) const {
  473. ERR_FAIL_INDEX_V(p_idx,items.size(),0);
  474. return items[p_idx].ID;
  475. }
  476. int PopupMenu::get_item_index(int p_ID) const {
  477. for(int i=0;i<items.size();i++) {
  478. if (items[i].ID==p_ID)
  479. return i;
  480. }
  481. return -1;
  482. }
  483. String PopupMenu::get_item_submenu(int p_idx) const {
  484. ERR_FAIL_INDEX_V(p_idx,items.size(),"");
  485. return items[p_idx].submenu;
  486. }
  487. String PopupMenu::get_item_tooltip(int p_idx) const {
  488. ERR_FAIL_INDEX_V(p_idx,items.size(),"");
  489. return items[p_idx].tooltip;
  490. }
  491. void PopupMenu::set_item_as_separator(int p_idx, bool p_separator) {
  492. ERR_FAIL_INDEX(p_idx,items.size());
  493. items[p_idx].separator=p_separator;
  494. update();
  495. }
  496. bool PopupMenu::is_item_separator(int p_idx) const {
  497. ERR_FAIL_INDEX_V(p_idx,items.size(),false);
  498. return items[p_idx].separator;
  499. }
  500. void PopupMenu::set_item_as_checkable(int p_idx, bool p_checkable) {
  501. ERR_FAIL_INDEX(p_idx,items.size());
  502. items[p_idx].checkable=p_checkable;
  503. update();
  504. }
  505. void PopupMenu::set_item_tooltip(int p_idx,const String& p_tooltip) {
  506. ERR_FAIL_INDEX(p_idx,items.size());
  507. items[p_idx].tooltip=p_tooltip;
  508. update();
  509. }
  510. bool PopupMenu::is_item_checkable(int p_idx) const {
  511. ERR_FAIL_INDEX_V(p_idx,items.size(),false);
  512. return items[p_idx].checkable;
  513. }
  514. int PopupMenu::get_item_count() const {
  515. return items.size();
  516. }
  517. int PopupMenu::find_item_by_accelerator(uint32_t p_accel) const {
  518. int il=items.size();
  519. for(int i=0;i<il;i++) {
  520. if (items[i].accel==p_accel)
  521. return i;
  522. }
  523. return -1;
  524. }
  525. void PopupMenu::activate_item(int p_item) {
  526. ERR_FAIL_INDEX(p_item,items.size());
  527. ERR_FAIL_COND(items[p_item].separator);
  528. int id = items[p_item].ID>=0?items[p_item].ID:p_item;
  529. emit_signal("item_pressed",id);
  530. //hide all parent PopupMenue's
  531. Node *next = get_parent();
  532. PopupMenu *pop = next->cast_to<PopupMenu>();
  533. while (pop) {
  534. pop->hide();
  535. next = next->get_parent();
  536. pop = next->cast_to<PopupMenu>();
  537. }
  538. hide();
  539. }
  540. void PopupMenu::remove_item(int p_idx) {
  541. items.remove(p_idx);
  542. update();
  543. }
  544. void PopupMenu::add_separator() {
  545. Item sep;
  546. sep.separator=true;
  547. sep.ID=-1;
  548. items.push_back(sep);
  549. update();
  550. }
  551. void PopupMenu::clear() {
  552. items.clear();
  553. mouse_over=-1;
  554. update();
  555. }
  556. Array PopupMenu::_get_items() const {
  557. Array items;
  558. for(int i=0;i<get_item_count();i++) {
  559. items.push_back(get_item_text(i));
  560. items.push_back(get_item_icon(i));
  561. items.push_back(is_item_checkable(i));
  562. items.push_back(is_item_checked(i));
  563. items.push_back(is_item_disabled(i));
  564. items.push_back(get_item_ID(i));
  565. items.push_back(get_item_accelerator(i));
  566. items.push_back(get_item_metadata(i));
  567. items.push_back(get_item_submenu(i));
  568. items.push_back(is_item_separator(i));
  569. }
  570. return items;
  571. }
  572. void PopupMenu::_set_items(const Array& p_items){
  573. ERR_FAIL_COND(p_items.size() % 10);
  574. clear();
  575. for(int i=0;i<p_items.size();i+=10) {
  576. String text=p_items[i+0];
  577. Ref<Texture> icon=p_items[i+1];
  578. bool checkable=p_items[i+2];
  579. bool checked=p_items[i+3];
  580. bool disabled=p_items[i+4];
  581. int id=p_items[i+5];
  582. int accel=p_items[i+6];
  583. Variant meta=p_items[i+7];
  584. String subm=p_items[i+8];
  585. bool sep=p_items[i+9];
  586. int idx=get_item_count();
  587. add_item(text,id);
  588. set_item_icon(idx,icon);
  589. set_item_as_checkable(idx,checkable);
  590. set_item_checked(idx,checked);
  591. set_item_disabled(idx,disabled);
  592. set_item_ID(idx,id);
  593. set_item_metadata(idx,meta);
  594. set_item_as_separator(idx,sep);
  595. set_item_accelerator(idx,accel);
  596. set_item_submenu(idx,subm);
  597. }
  598. }
  599. String PopupMenu::get_tooltip(const Point2& p_pos) const {
  600. int over=_get_mouse_over(p_pos);
  601. if (over<0 || over>=items.size())
  602. return "";
  603. return items[over].tooltip;
  604. }
  605. void PopupMenu::set_parent_rect(const Rect2& p_rect) {
  606. parent_rect=p_rect;
  607. }
  608. void PopupMenu::get_translatable_strings(List<String> *p_strings) const {
  609. for(int i=0;i<items.size();i++) {
  610. if (items[i].text!="")
  611. p_strings->push_back(items[i].text);
  612. }
  613. }
  614. void PopupMenu::add_autohide_area(const Rect2& p_area) {
  615. autohide_areas.push_back(p_area);
  616. }
  617. void PopupMenu::clear_autohide_areas(){
  618. autohide_areas.clear();
  619. }
  620. void PopupMenu::_bind_methods() {
  621. ObjectTypeDB::bind_method(_MD("_input_event"),&PopupMenu::_input_event);
  622. ObjectTypeDB::bind_method(_MD("add_icon_item","texture","label","id","accel"),&PopupMenu::add_icon_item,DEFVAL(-1),DEFVAL(0));
  623. ObjectTypeDB::bind_method(_MD("add_item","label","id","accel"),&PopupMenu::add_item,DEFVAL(-1),DEFVAL(0));
  624. ObjectTypeDB::bind_method(_MD("add_icon_check_item","texture","label","id","accel"),&PopupMenu::add_icon_check_item,DEFVAL(-1),DEFVAL(0));
  625. ObjectTypeDB::bind_method(_MD("add_check_item","label","id","accel"),&PopupMenu::add_check_item,DEFVAL(-1),DEFVAL(0));
  626. ObjectTypeDB::bind_method(_MD("add_submenu_item","label","submenu","id"),&PopupMenu::add_submenu_item,DEFVAL(-1));
  627. ObjectTypeDB::bind_method(_MD("set_item_text","idx","text"),&PopupMenu::set_item_text);
  628. ObjectTypeDB::bind_method(_MD("set_item_icon","idx","icon"),&PopupMenu::set_item_icon);
  629. ObjectTypeDB::bind_method(_MD("set_item_accelerator","idx","accel"),&PopupMenu::set_item_accelerator);
  630. ObjectTypeDB::bind_method(_MD("set_item_metadata","idx","metadata"),&PopupMenu::set_item_metadata);
  631. ObjectTypeDB::bind_method(_MD("set_item_checked","idx","checked"),&PopupMenu::set_item_checked);
  632. ObjectTypeDB::bind_method(_MD("set_item_disabled","idx","disabled"),&PopupMenu::set_item_disabled);
  633. ObjectTypeDB::bind_method(_MD("set_item_submenu","idx","submenu"),&PopupMenu::set_item_submenu);
  634. ObjectTypeDB::bind_method(_MD("set_item_as_separator","idx","enable"),&PopupMenu::set_item_as_separator);
  635. ObjectTypeDB::bind_method(_MD("set_item_as_checkable","idx","enable"),&PopupMenu::set_item_as_checkable);
  636. ObjectTypeDB::bind_method(_MD("set_item_ID","idx","id"),&PopupMenu::set_item_ID);
  637. ObjectTypeDB::bind_method(_MD("get_item_text","idx"),&PopupMenu::get_item_text);
  638. ObjectTypeDB::bind_method(_MD("get_item_icon","idx"),&PopupMenu::get_item_icon);
  639. ObjectTypeDB::bind_method(_MD("get_item_metadata","idx"),&PopupMenu::get_item_metadata);
  640. ObjectTypeDB::bind_method(_MD("get_item_accelerator","idx"),&PopupMenu::get_item_accelerator);
  641. ObjectTypeDB::bind_method(_MD("get_item_submenu","idx"),&PopupMenu::get_item_submenu);
  642. ObjectTypeDB::bind_method(_MD("is_item_separator","idx"),&PopupMenu::is_item_separator);
  643. ObjectTypeDB::bind_method(_MD("is_item_checkable","idx"),&PopupMenu::is_item_checkable);
  644. ObjectTypeDB::bind_method(_MD("is_item_checked","idx"),&PopupMenu::is_item_checked);
  645. ObjectTypeDB::bind_method(_MD("is_item_disabled","idx"),&PopupMenu::is_item_disabled);
  646. ObjectTypeDB::bind_method(_MD("get_item_ID","idx"),&PopupMenu::get_item_ID);
  647. ObjectTypeDB::bind_method(_MD("get_item_index","id"),&PopupMenu::get_item_index);
  648. ObjectTypeDB::bind_method(_MD("get_item_count"),&PopupMenu::get_item_count);
  649. ObjectTypeDB::bind_method(_MD("add_separator"),&PopupMenu::add_separator);
  650. ObjectTypeDB::bind_method(_MD("remove_item","idx"),&PopupMenu::remove_item);
  651. ObjectTypeDB::bind_method(_MD("clear"),&PopupMenu::clear);
  652. ObjectTypeDB::bind_method(_MD("_set_items"),&PopupMenu::_set_items);
  653. ObjectTypeDB::bind_method(_MD("_get_items"),&PopupMenu::_get_items);
  654. ObjectTypeDB::bind_method(_MD("_submenu_timeout"),&PopupMenu::_submenu_timeout);
  655. ADD_PROPERTY( PropertyInfo(Variant::ARRAY,"items",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR), _SCS("_set_items"),_SCS("_get_items") );
  656. ADD_SIGNAL( MethodInfo("item_pressed", PropertyInfo( Variant::INT,"ID") ) );
  657. }
  658. void PopupMenu::set_invalidate_click_until_motion() {
  659. moved=Vector2();
  660. invalidated_click=true;
  661. }
  662. PopupMenu::PopupMenu() {
  663. mouse_over=-1;
  664. set_focus_mode(FOCUS_ALL);
  665. set_as_toplevel(true);
  666. submenu_timer = memnew( Timer );
  667. submenu_timer->set_wait_time(0.3);
  668. submenu_timer->set_one_shot(true);
  669. submenu_timer->connect("timeout",this,"_submenu_timeout");
  670. add_child(submenu_timer);
  671. }
  672. PopupMenu::~PopupMenu() {
  673. }