💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

333 lines
6.7 KiB

  1. /*
  2. * Copyright (C) 2017 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper Source Code.
  5. *
  6. * Antkeeper Source Code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper Source Code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "menu.hpp"
  20. #include "ui.hpp"
  21. #include <algorithm>
  22. MenuItem::MenuItem(Menu* parent, std::size_t index):
  23. parent(parent),
  24. index(index),
  25. selectedCallback(nullptr),
  26. deselectedCallback(nullptr),
  27. activatedCallback(nullptr),
  28. valueChangedCallback(nullptr),
  29. valueIndex(0),
  30. nameLabel(nullptr),
  31. valueLabel(nullptr),
  32. rowContainer(nullptr)
  33. {
  34. nameLabel = new UILabel();
  35. nameLabel->setAnchor(Vector2(0.0f, 0.0f));
  36. valueLabel = new UILabel();
  37. valueLabel->setAnchor(Vector2(1.0f, 0.0f));
  38. rowContainer = new UIContainer();
  39. rowContainer->addChild(nameLabel);
  40. rowContainer->addChild(valueLabel);
  41. rowContainer->setMouseOverCallback(std::bind(&Menu::select, parent, index));
  42. rowContainer->setMouseMovedCallback(std::bind(&Menu::select, parent, index));
  43. rowContainer->setMousePressedCallback(std::bind(&Menu::activate, parent));
  44. }
  45. MenuItem::~MenuItem()
  46. {
  47. delete nameLabel;
  48. delete valueLabel;
  49. delete rowContainer;
  50. }
  51. void MenuItem::select()
  52. {
  53. if (selectedCallback != nullptr)
  54. {
  55. selectedCallback();
  56. }
  57. }
  58. void MenuItem::deselect()
  59. {
  60. if (deselectedCallback != nullptr)
  61. {
  62. deselectedCallback();
  63. }
  64. }
  65. void MenuItem::activate()
  66. {
  67. if (activatedCallback != nullptr)
  68. {
  69. activatedCallback();
  70. }
  71. }
  72. void MenuItem::setSelectedCallback(std::function<void()> callback)
  73. {
  74. this->selectedCallback = callback = callback;
  75. }
  76. void MenuItem::setDeselectedCallback(std::function<void()> callback)
  77. {
  78. this->deselectedCallback = callback;
  79. }
  80. void MenuItem::setActivatedCallback(std::function<void()> callback)
  81. {
  82. this->activatedCallback = callback;
  83. }
  84. void MenuItem::setValueChangedCallback(std::function<void(std::size_t)> callback)
  85. {
  86. this->valueChangedCallback = callback;
  87. }
  88. void MenuItem::setName(const std::string& text)
  89. {
  90. nameLabel->setText(text);
  91. parent->resize();
  92. }
  93. void MenuItem::addValue(const std::string& text)
  94. {
  95. values.push_back(text);
  96. valueLabel->setText(values[valueIndex]);
  97. parent->resize();
  98. }
  99. void MenuItem::removeValues()
  100. {
  101. values.clear();
  102. valueIndex = 0;
  103. valueLabel->setText(std::string());
  104. parent->resize();
  105. }
  106. void MenuItem::setValueIndex(std::size_t index)
  107. {
  108. if (valueIndex != index)
  109. {
  110. valueIndex = index;
  111. valueLabel->setText(values[index]);
  112. if (valueChangedCallback != nullptr)
  113. {
  114. valueChangedCallback(index);
  115. }
  116. }
  117. }
  118. bool MenuItem::isSelected() const
  119. {
  120. return (parent->getSelectedItem() == this);
  121. }
  122. Menu::Menu():
  123. selectedItem(nullptr),
  124. enteredCallback(nullptr),
  125. exitedCallback(nullptr),
  126. font(nullptr),
  127. lineSpacing(1.0f),
  128. columnMargin(0.0f)
  129. {
  130. container = new UIContainer();
  131. resize();
  132. }
  133. Menu::~Menu()
  134. {
  135. removeItems();
  136. delete container;
  137. }
  138. void Menu::enter()
  139. {
  140. if (enteredCallback != nullptr)
  141. {
  142. enteredCallback();
  143. }
  144. }
  145. void Menu::exit()
  146. {
  147. if (exitedCallback != nullptr)
  148. {
  149. exitedCallback();
  150. }
  151. }
  152. MenuItem* Menu::addItem()
  153. {
  154. // Allocate item and add to items
  155. MenuItem* item = new MenuItem(this, items.size());
  156. items.push_back(item);
  157. // Set item fonts
  158. item->nameLabel->setFont(font);
  159. item->valueLabel->setFont(font);
  160. // Set item colors
  161. item->nameLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f));
  162. item->valueLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f));
  163. // Add item labels to UI container
  164. container->addChild(item->rowContainer);
  165. // Resize UI container
  166. resize();
  167. return item;
  168. }
  169. void Menu::removeItems()
  170. {
  171. for (MenuItem* item: items)
  172. {
  173. // Remove labels from UI container
  174. container->removeChild(item->rowContainer);
  175. delete item;
  176. }
  177. items.clear();
  178. resize();
  179. }
  180. void Menu::setEnteredCallback(std::function<void()> callback)
  181. {
  182. this->enteredCallback = callback;
  183. }
  184. void Menu::setExitedCallback(std::function<void()> callback)
  185. {
  186. this->exitedCallback = callback;
  187. }
  188. void Menu::setFont(Font* font)
  189. {
  190. this->font = font;
  191. for (MenuItem* item: items)
  192. {
  193. item->nameLabel->setFont(font);
  194. item->valueLabel->setFont(font);
  195. }
  196. resize();
  197. }
  198. void Menu::setLineSpacing(float spacing)
  199. {
  200. lineSpacing = spacing;
  201. resize();
  202. }
  203. void Menu::setColumnMargin(float margin)
  204. {
  205. columnMargin = margin;
  206. resize();
  207. }
  208. void Menu::deselect()
  209. {
  210. if (selectedItem != nullptr)
  211. {
  212. selectedItem->deselect();
  213. selectedItem->nameLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f));
  214. selectedItem->valueLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f));
  215. selectedItem = nullptr;
  216. }
  217. }
  218. void Menu::select(std::size_t index)
  219. {
  220. deselect();
  221. MenuItem* item = items[index];
  222. item->select();
  223. selectedItem = item;
  224. selectedItem->nameLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 1.0f));
  225. selectedItem->valueLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 1.0f));
  226. }
  227. void Menu::activate()
  228. {
  229. if (selectedItem != nullptr)
  230. {
  231. selectedItem->activate();
  232. }
  233. }
  234. void Menu::resize()
  235. {
  236. if (!font)
  237. {
  238. container->setDimensions(Vector2(0.0f));
  239. }
  240. else
  241. {
  242. // Determine maximum width of menu
  243. float menuWidth = 0.0f;
  244. // For each menu item
  245. for (std::size_t i = 0; i < items.size(); ++i)
  246. {
  247. const MenuItem* item = items[i];
  248. // Calculate width of item name label
  249. float nameLabelWidth = item->nameLabel->getDimensions().x;
  250. // For each item value
  251. for (std::size_t j = 0; j < item->getValueCount(); ++j)
  252. {
  253. // Calculate width of item value label
  254. float valueLabelWidth = font->getWidth(item->getValue(j).c_str());
  255. menuWidth = std::max<float>(menuWidth, nameLabelWidth + columnMargin + valueLabelWidth);
  256. }
  257. menuWidth = std::max<float>(menuWidth, nameLabelWidth);
  258. }
  259. Vector2 dimensions(menuWidth, 0.0f);
  260. for (std::size_t i = 0; i < items.size(); ++i)
  261. {
  262. const MenuItem* item = items[i];
  263. float translationY = static_cast<float>(static_cast<int>(font->getMetrics().getHeight() * lineSpacing * static_cast<float>(i)));
  264. item->rowContainer->setDimensions(Vector2(menuWidth, font->getMetrics().getHeight()));
  265. item->rowContainer->setTranslation(Vector2(0.0f, translationY));
  266. if (!i)
  267. {
  268. dimensions.y += font->getMetrics().getHeight();
  269. }
  270. else
  271. {
  272. dimensions.y += font->getMetrics().getHeight() * lineSpacing;
  273. }
  274. }
  275. container->setDimensions(dimensions);
  276. }
  277. }