button_array.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. /*************************************************************************/
  2. /* button_array.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* http://www.godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. #include "button_array.h"
  31. bool ButtonArray::_set(const StringName &p_name, const Variant &p_value) {
  32. String n = String(p_name);
  33. if (n.begins_with("button/")) {
  34. String what = n.get_slicec('/', 1);
  35. if (what == "count") {
  36. int new_size = p_value;
  37. if (new_size > 0 && buttons.size() == 0) {
  38. selected = 0;
  39. }
  40. if (new_size < buttons.size()) {
  41. if (selected >= new_size)
  42. selected = new_size - 1;
  43. }
  44. buttons.resize(new_size);
  45. _change_notify();
  46. minimum_size_changed();
  47. } else if (what == "align") {
  48. set_align(Align(p_value.operator int()));
  49. } else if (what == "selected") {
  50. set_selected(p_value);
  51. } else if (what == "min_button_size") {
  52. min_button_size = p_value;
  53. } else {
  54. int idx = what.to_int();
  55. ERR_FAIL_INDEX_V(idx, buttons.size(), false);
  56. String f = n.get_slicec('/', 2);
  57. if (f == "text") {
  58. buttons[idx].text = p_value;
  59. buttons[idx].xl_text = XL_MESSAGE(p_value);
  60. } else if (f == "tooltip")
  61. buttons[idx].tooltip = p_value;
  62. else if (f == "icon")
  63. buttons[idx].icon = p_value;
  64. else
  65. return false;
  66. }
  67. update();
  68. return true;
  69. }
  70. return false;
  71. }
  72. bool ButtonArray::_get(const StringName &p_name, Variant &r_ret) const {
  73. String n = String(p_name);
  74. if (n.begins_with("button/")) {
  75. String what = n.get_slicec('/', 1);
  76. if (what == "count") {
  77. r_ret = buttons.size();
  78. } else if (what == "align") {
  79. r_ret = get_align();
  80. } else if (what == "selected") {
  81. r_ret = get_selected();
  82. } else if (what == "min_button_size") {
  83. r_ret = min_button_size;
  84. } else {
  85. int idx = what.to_int();
  86. ERR_FAIL_INDEX_V(idx, buttons.size(), false);
  87. String f = n.get_slicec('/', 2);
  88. if (f == "text")
  89. r_ret = buttons[idx].text;
  90. else if (f == "tooltip")
  91. r_ret = buttons[idx].tooltip;
  92. else if (f == "icon")
  93. r_ret = buttons[idx].icon;
  94. else
  95. return false;
  96. }
  97. return true;
  98. }
  99. return false;
  100. }
  101. void ButtonArray::_get_property_list(List<PropertyInfo> *p_list) const {
  102. p_list->push_back(PropertyInfo(Variant::INT, "button/count", PROPERTY_HINT_RANGE, "0,512,1"));
  103. p_list->push_back(PropertyInfo(Variant::INT, "button/min_button_size", PROPERTY_HINT_RANGE, "0,1024,1"));
  104. p_list->push_back(PropertyInfo(Variant::INT, "button/align", PROPERTY_HINT_ENUM, "Begin,Center,End,Fill,Expand"));
  105. for (int i = 0; i < buttons.size(); i++) {
  106. String base = "button/" + itos(i) + "/";
  107. p_list->push_back(PropertyInfo(Variant::STRING, base + "text"));
  108. p_list->push_back(PropertyInfo(Variant::STRING, base + "tooltip"));
  109. p_list->push_back(PropertyInfo(Variant::OBJECT, base + "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"));
  110. }
  111. if (buttons.size() > 0) {
  112. p_list->push_back(PropertyInfo(Variant::INT, "button/selected", PROPERTY_HINT_RANGE, "0," + itos(buttons.size() - 1) + ",1"));
  113. }
  114. }
  115. Size2 ButtonArray::get_minimum_size() const {
  116. Ref<StyleBox> style_normal = get_stylebox("normal");
  117. Ref<StyleBox> style_selected = get_stylebox("selected");
  118. Ref<Font> font_normal = get_font("font");
  119. Ref<Font> font_selected = get_font("font_selected");
  120. int icon_sep = get_constant("icon_separator");
  121. int button_sep = get_constant("button_separator");
  122. Size2 minsize;
  123. for (int i = 0; i < buttons.size(); i++) {
  124. Ref<StyleBox> sb = i == selected ? style_selected : style_normal;
  125. Ref<Font> f = i == selected ? font_selected : font_normal;
  126. Size2 ms;
  127. ms = f->get_string_size(buttons[i].xl_text);
  128. if (buttons[i].icon.is_valid()) {
  129. Size2 bs = buttons[i].icon->get_size();
  130. ms.height = MAX(ms.height, bs.height);
  131. ms.width += bs.width + icon_sep;
  132. }
  133. ms += sb->get_minimum_size();
  134. buttons[i]._ms_cache = ms[orientation];
  135. minsize[orientation] += ms[orientation];
  136. if (i > 0)
  137. minsize[orientation] += button_sep;
  138. minsize[!orientation] = MAX(minsize[!orientation], ms[!orientation]);
  139. }
  140. return minsize;
  141. }
  142. void ButtonArray::_notification(int p_what) {
  143. switch (p_what) {
  144. case NOTIFICATION_MOUSE_EXIT: {
  145. hover = -1;
  146. update();
  147. } break;
  148. case NOTIFICATION_READY: {
  149. MethodInfo mi;
  150. mi.name = "mouse_sub_enter";
  151. add_user_signal(mi);
  152. } break;
  153. case NOTIFICATION_DRAW: {
  154. Size2 size = get_size();
  155. Size2 minsize = get_combined_minimum_size();
  156. Ref<StyleBox> style_normal = get_stylebox("normal");
  157. Ref<StyleBox> style_selected = get_stylebox("selected");
  158. Ref<StyleBox> style_focus = get_stylebox("focus");
  159. Ref<StyleBox> style_hover = get_stylebox("hover");
  160. Ref<Font> font_normal = get_font("font");
  161. Ref<Font> font_selected = get_font("font_selected");
  162. int icon_sep = get_constant("icon_separator");
  163. int button_sep = get_constant("button_separator");
  164. Color color_normal = get_color("font_color");
  165. Color color_selected = get_color("font_color_selected");
  166. int sep = button_sep;
  167. int ofs = 0;
  168. int expand = 0;
  169. switch (align) {
  170. case ALIGN_BEGIN: {
  171. ofs = 0;
  172. } break;
  173. case ALIGN_CENTER: {
  174. ofs = Math::floor((size[orientation] - minsize[orientation]) / 2);
  175. } break;
  176. case ALIGN_END: {
  177. ofs = Math::floor((size[orientation] - minsize[orientation]));
  178. } break;
  179. case ALIGN_FILL: {
  180. if (buttons.size() > 1)
  181. sep += Math::floor((size[orientation] - minsize[orientation]) / (buttons.size() - 1.0));
  182. ofs = 0;
  183. } break;
  184. case ALIGN_EXPAND_FILL: {
  185. ofs = 0;
  186. expand = size[orientation] - minsize[orientation];
  187. } break;
  188. }
  189. int op_size = orientation == VERTICAL ? size.width : size.height;
  190. for (int i = 0; i < buttons.size(); i++) {
  191. int ms = buttons[i]._ms_cache;
  192. int s = ms;
  193. if (expand > 0) {
  194. s += expand / buttons.size();
  195. }
  196. if (min_button_size != -1 && s < min_button_size) {
  197. s = min_button_size;
  198. }
  199. Rect2 r;
  200. r.position[orientation] = ofs;
  201. r.position[!orientation] = 0;
  202. r.size[orientation] = s;
  203. r.size[!orientation] = op_size;
  204. Ref<Font> f;
  205. Color c;
  206. Point2 sbsize;
  207. Point2 sbofs;
  208. if (i == selected) {
  209. draw_style_box(style_selected, r);
  210. sbsize = style_selected->get_minimum_size();
  211. sbofs = style_selected->get_offset();
  212. f = font_selected;
  213. c = color_selected;
  214. if (has_focus())
  215. draw_style_box(style_focus, r);
  216. } else {
  217. if (hover == i)
  218. draw_style_box(style_hover, r);
  219. else if (!flat)
  220. draw_style_box(style_normal, r);
  221. sbsize = style_normal->get_minimum_size();
  222. sbofs = style_normal->get_offset();
  223. f = font_normal;
  224. c = color_normal;
  225. }
  226. Size2 ssize = f->get_string_size(buttons[i].xl_text);
  227. if (buttons[i].icon.is_valid()) {
  228. ssize.x += buttons[i].icon->get_width();
  229. }
  230. Point2 text_ofs = ((r.size - ssize - sbsize) / 2.0 + Point2(0, f->get_ascent())).floor() + sbofs;
  231. if (buttons[i].icon.is_valid()) {
  232. draw_texture(buttons[i].icon, r.position + Point2(text_ofs.x, Math::floor((r.size.height - buttons[i].icon->get_height()) / 2.0)));
  233. text_ofs.x += buttons[i].icon->get_width() + icon_sep;
  234. }
  235. draw_string(f, text_ofs + r.position, buttons[i].xl_text, c);
  236. buttons[i]._pos_cache = ofs;
  237. buttons[i]._size_cache = s;
  238. ofs += s;
  239. ofs += sep;
  240. }
  241. } break;
  242. }
  243. }
  244. void ButtonArray::_gui_input(const Ref<InputEvent> &p_event) {
  245. if (
  246. ((orientation == HORIZONTAL && p_event->is_action("ui_left")) ||
  247. (orientation == VERTICAL && p_event->is_action("ui_up"))) &&
  248. p_event->is_pressed() && selected > 0) {
  249. set_selected(selected - 1);
  250. accept_event();
  251. emit_signal("button_selected", selected);
  252. return;
  253. }
  254. if (
  255. ((orientation == HORIZONTAL && p_event->is_action("ui_right")) ||
  256. (orientation == VERTICAL && p_event->is_action("ui_down"))) &&
  257. p_event->is_pressed() && selected < (buttons.size() - 1)) {
  258. set_selected(selected + 1);
  259. accept_event();
  260. emit_signal("button_selected", selected);
  261. return;
  262. }
  263. Ref<InputEventMouseButton> mb = p_event;
  264. if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
  265. int ofs = orientation == HORIZONTAL ? mb->get_position().x : mb->get_position().y;
  266. for (int i = 0; i < buttons.size(); i++) {
  267. if (ofs >= buttons[i]._pos_cache && ofs < buttons[i]._pos_cache + buttons[i]._size_cache) {
  268. set_selected(i);
  269. emit_signal("button_selected", i);
  270. return;
  271. }
  272. }
  273. }
  274. Ref<InputEventMouseMotion> mm = p_event;
  275. if (mm.is_valid()) {
  276. int ofs = orientation == HORIZONTAL ? mm->get_position().x : mm->get_position().y;
  277. int new_hover = -1;
  278. for (int i = 0; i < buttons.size(); i++) {
  279. if (ofs >= buttons[i]._pos_cache && ofs < buttons[i]._pos_cache + buttons[i]._size_cache) {
  280. new_hover = i;
  281. break;
  282. }
  283. }
  284. if (new_hover != hover) {
  285. hover = new_hover;
  286. emit_signal("mouse_sub_enter");
  287. update();
  288. }
  289. }
  290. }
  291. String ButtonArray::get_tooltip(const Point2 &p_pos) const {
  292. int ofs = orientation == HORIZONTAL ? p_pos.x : p_pos.y;
  293. for (int i = 0; i < buttons.size(); i++) {
  294. if (ofs >= buttons[i]._pos_cache && ofs < buttons[i]._pos_cache + buttons[i]._size_cache)
  295. return buttons[i].tooltip;
  296. }
  297. return Control::get_tooltip(p_pos);
  298. }
  299. void ButtonArray::set_align(Align p_align) {
  300. align = p_align;
  301. update();
  302. }
  303. ButtonArray::Align ButtonArray::get_align() const {
  304. return align;
  305. }
  306. void ButtonArray::set_flat(bool p_flat) {
  307. flat = p_flat;
  308. update();
  309. }
  310. bool ButtonArray::is_flat() const {
  311. return flat;
  312. }
  313. void ButtonArray::add_button(const String &p_text, const String &p_tooltip) {
  314. Button button;
  315. button.text = p_text;
  316. button.xl_text = XL_MESSAGE(p_text);
  317. button.tooltip = p_tooltip;
  318. buttons.push_back(button);
  319. update();
  320. if (selected == -1)
  321. selected = 0;
  322. minimum_size_changed();
  323. }
  324. void ButtonArray::add_icon_button(const Ref<Texture> &p_icon, const String &p_text, const String &p_tooltip) {
  325. Button button;
  326. button.text = p_text;
  327. button.xl_text = XL_MESSAGE(p_text);
  328. button.icon = p_icon;
  329. button.tooltip = p_tooltip;
  330. buttons.push_back(button);
  331. if (selected == -1)
  332. selected = 0;
  333. update();
  334. }
  335. void ButtonArray::set_button_text(int p_button, const String &p_text) {
  336. ERR_FAIL_INDEX(p_button, buttons.size());
  337. buttons[p_button].text = p_text;
  338. buttons[p_button].xl_text = XL_MESSAGE(p_text);
  339. update();
  340. minimum_size_changed();
  341. }
  342. void ButtonArray::set_button_tooltip(int p_button, const String &p_text) {
  343. ERR_FAIL_INDEX(p_button, buttons.size());
  344. buttons[p_button].tooltip = p_text;
  345. }
  346. void ButtonArray::set_button_icon(int p_button, const Ref<Texture> &p_icon) {
  347. ERR_FAIL_INDEX(p_button, buttons.size());
  348. buttons[p_button].icon = p_icon;
  349. update();
  350. minimum_size_changed();
  351. }
  352. String ButtonArray::get_button_text(int p_button) const {
  353. ERR_FAIL_INDEX_V(p_button, buttons.size(), "");
  354. return buttons[p_button].text;
  355. }
  356. String ButtonArray::get_button_tooltip(int p_button) const {
  357. ERR_FAIL_INDEX_V(p_button, buttons.size(), "");
  358. return buttons[p_button].tooltip;
  359. }
  360. Ref<Texture> ButtonArray::get_button_icon(int p_button) const {
  361. ERR_FAIL_INDEX_V(p_button, buttons.size(), Ref<Texture>());
  362. return buttons[p_button].icon;
  363. }
  364. int ButtonArray::get_selected() const {
  365. return selected;
  366. }
  367. int ButtonArray::get_hovered() const {
  368. return hover;
  369. }
  370. void ButtonArray::set_selected(int p_selected) {
  371. ERR_FAIL_INDEX(p_selected, buttons.size());
  372. selected = p_selected;
  373. update();
  374. }
  375. void ButtonArray::erase_button(int p_button) {
  376. ERR_FAIL_INDEX(p_button, buttons.size());
  377. buttons.remove(p_button);
  378. if (p_button >= selected)
  379. selected--;
  380. if (selected < 0)
  381. selected = 0;
  382. if (selected >= buttons.size())
  383. selected = buttons.size() - 1;
  384. update();
  385. }
  386. void ButtonArray::clear() {
  387. buttons.clear();
  388. selected = -1;
  389. update();
  390. }
  391. int ButtonArray::get_button_count() const {
  392. return buttons.size();
  393. }
  394. void ButtonArray::get_translatable_strings(List<String> *p_strings) const {
  395. for (int i = 0; i < buttons.size(); i++) {
  396. p_strings->push_back(buttons[i].text);
  397. p_strings->push_back(buttons[i].tooltip);
  398. }
  399. }
  400. void ButtonArray::_bind_methods() {
  401. ClassDB::bind_method(D_METHOD("add_button", "text", "tooltip"), &ButtonArray::add_button, DEFVAL(""));
  402. ClassDB::bind_method(D_METHOD("add_icon_button", "icon:Texture", "text", "tooltip"), &ButtonArray::add_icon_button, DEFVAL(""), DEFVAL(""));
  403. ClassDB::bind_method(D_METHOD("set_button_text", "button_idx", "text"), &ButtonArray::set_button_text);
  404. ClassDB::bind_method(D_METHOD("set_button_tooltip", "button_idx", "text"), &ButtonArray::set_button_tooltip);
  405. ClassDB::bind_method(D_METHOD("set_button_icon", "button_idx", "icon:Texture"), &ButtonArray::set_button_icon);
  406. ClassDB::bind_method(D_METHOD("get_button_text", "button_idx"), &ButtonArray::get_button_text);
  407. ClassDB::bind_method(D_METHOD("get_button_tooltip", "button_idx"), &ButtonArray::get_button_tooltip);
  408. ClassDB::bind_method(D_METHOD("get_button_icon:Texture", "button_idx"), &ButtonArray::get_button_icon);
  409. ClassDB::bind_method(D_METHOD("get_button_count"), &ButtonArray::get_button_count);
  410. ClassDB::bind_method(D_METHOD("set_flat", "enabled"), &ButtonArray::set_flat);
  411. ClassDB::bind_method(D_METHOD("is_flat"), &ButtonArray::is_flat);
  412. ClassDB::bind_method(D_METHOD("get_selected"), &ButtonArray::get_selected);
  413. ClassDB::bind_method(D_METHOD("get_hovered"), &ButtonArray::get_hovered);
  414. ClassDB::bind_method(D_METHOD("set_selected", "button_idx"), &ButtonArray::set_selected);
  415. ClassDB::bind_method(D_METHOD("erase_button", "button_idx"), &ButtonArray::erase_button);
  416. ClassDB::bind_method(D_METHOD("clear"), &ButtonArray::clear);
  417. ClassDB::bind_method(D_METHOD("_gui_input"), &ButtonArray::_gui_input);
  418. BIND_CONSTANT(ALIGN_BEGIN);
  419. BIND_CONSTANT(ALIGN_CENTER);
  420. BIND_CONSTANT(ALIGN_END);
  421. BIND_CONSTANT(ALIGN_FILL);
  422. BIND_CONSTANT(ALIGN_EXPAND_FILL);
  423. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
  424. ADD_SIGNAL(MethodInfo("button_selected", PropertyInfo(Variant::INT, "button_idx")));
  425. }
  426. ButtonArray::ButtonArray(Orientation p_orientation) {
  427. orientation = p_orientation;
  428. selected = -1;
  429. set_focus_mode(FOCUS_ALL);
  430. hover = -1;
  431. flat = false;
  432. min_button_size = -1;
  433. }