💿🐜 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.

353 lines
7.0 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::u32string& text)
  89. {
  90. nameLabel->setText(text);
  91. parent->resize();
  92. }
  93. std::size_t MenuItem::addValue()
  94. {
  95. values.push_back(std::u32string());
  96. return (values.size() - 1);
  97. }
  98. void MenuItem::removeValues()
  99. {
  100. values.clear();
  101. valueIndex = 0;
  102. valueLabel->setText(std::u32string());
  103. parent->resize();
  104. }
  105. void MenuItem::setValueName(std::size_t index, const std::u32string& text)
  106. {
  107. values[index] = text;
  108. if (index == valueIndex)
  109. {
  110. valueLabel->setText(text);
  111. parent->resize();
  112. }
  113. }
  114. void MenuItem::setValueIndex(std::size_t index)
  115. {
  116. if (valueIndex != index)
  117. {
  118. valueIndex = index;
  119. valueLabel->setText(values[index]);
  120. if (valueChangedCallback != nullptr)
  121. {
  122. valueChangedCallback(index);
  123. }
  124. }
  125. }
  126. bool MenuItem::isSelected() const
  127. {
  128. return (parent->getSelectedItem() == this);
  129. }
  130. Menu::Menu():
  131. selectedItem(nullptr),
  132. enteredCallback(nullptr),
  133. exitedCallback(nullptr),
  134. font(nullptr),
  135. lineSpacing(1.0f),
  136. columnMargin(0.0f),
  137. activationDelay(0)
  138. {
  139. container = new UIContainer();
  140. resize();
  141. }
  142. Menu::~Menu()
  143. {
  144. removeItems();
  145. delete container;
  146. }
  147. void Menu::enter()
  148. {
  149. if (enteredCallback != nullptr)
  150. {
  151. enteredCallback();
  152. }
  153. }
  154. void Menu::exit()
  155. {
  156. if (exitedCallback != nullptr)
  157. {
  158. exitedCallback();
  159. }
  160. }
  161. MenuItem* Menu::addItem()
  162. {
  163. // Allocate item and add to items
  164. MenuItem* item = new MenuItem(this, items.size());
  165. items.push_back(item);
  166. // Set item fonts
  167. item->nameLabel->setFont(font);
  168. item->valueLabel->setFont(font);
  169. // Set item colors
  170. item->nameLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f));
  171. item->valueLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f));
  172. // Add item labels to UI container
  173. container->addChild(item->rowContainer);
  174. // Resize UI container
  175. resize();
  176. return item;
  177. }
  178. void Menu::removeItems()
  179. {
  180. for (MenuItem* item: items)
  181. {
  182. // Remove labels from UI container
  183. container->removeChild(item->rowContainer);
  184. delete item;
  185. }
  186. items.clear();
  187. resize();
  188. }
  189. void Menu::setEnteredCallback(std::function<void()> callback)
  190. {
  191. this->enteredCallback = callback;
  192. }
  193. void Menu::setExitedCallback(std::function<void()> callback)
  194. {
  195. this->exitedCallback = callback;
  196. }
  197. void Menu::setFont(Font* font)
  198. {
  199. this->font = font;
  200. for (MenuItem* item: items)
  201. {
  202. item->nameLabel->setFont(font);
  203. item->valueLabel->setFont(font);
  204. }
  205. resize();
  206. }
  207. void Menu::setLineSpacing(float spacing)
  208. {
  209. lineSpacing = spacing;
  210. resize();
  211. }
  212. void Menu::setColumnMargin(float margin)
  213. {
  214. columnMargin = margin;
  215. resize();
  216. }
  217. void Menu::update(float dt)
  218. {
  219. if (activationDelay > 0)
  220. {
  221. --activationDelay;
  222. }
  223. }
  224. void Menu::deselect()
  225. {
  226. if (selectedItem != nullptr)
  227. {
  228. selectedItem->deselect();
  229. selectedItem->nameLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f));
  230. selectedItem->valueLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f));
  231. selectedItem = nullptr;
  232. }
  233. }
  234. void Menu::select(std::size_t index)
  235. {
  236. deselect();
  237. MenuItem* item = items[index];
  238. item->select();
  239. selectedItem = item;
  240. selectedItem->nameLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 1.0f));
  241. selectedItem->valueLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 1.0f));
  242. }
  243. void Menu::activate()
  244. {
  245. if (selectedItem != nullptr && !activationDelay)
  246. {
  247. selectedItem->activate();
  248. activationDelay = 1;
  249. }
  250. }
  251. void Menu::resize()
  252. {
  253. if (!font)
  254. {
  255. container->setDimensions(Vector2(0.0f));
  256. }
  257. else
  258. {
  259. // Determine maximum width of menu
  260. float menuWidth = 0.0f;
  261. // For each menu item
  262. for (std::size_t i = 0; i < items.size(); ++i)
  263. {
  264. const MenuItem* item = items[i];
  265. // Calculate width of item name label
  266. float nameLabelWidth = item->nameLabel->getDimensions().x;
  267. // For each item value
  268. for (std::size_t j = 0; j < item->getValueCount(); ++j)
  269. {
  270. // Calculate width of item value label
  271. float valueLabelWidth = font->getWidth(item->getValue(j).c_str());
  272. menuWidth = std::max<float>(menuWidth, nameLabelWidth + columnMargin + valueLabelWidth);
  273. }
  274. menuWidth = std::max<float>(menuWidth, nameLabelWidth);
  275. }
  276. Vector2 dimensions(menuWidth, 0.0f);
  277. for (std::size_t i = 0; i < items.size(); ++i)
  278. {
  279. const MenuItem* item = items[i];
  280. float translationY = static_cast<float>(static_cast<int>(font->getMetrics().getHeight() * lineSpacing * static_cast<float>(i)));
  281. item->rowContainer->setDimensions(Vector2(menuWidth, font->getMetrics().getHeight()));
  282. item->rowContainer->setTranslation(Vector2(0.0f, translationY));
  283. if (!i)
  284. {
  285. dimensions.y += font->getMetrics().getHeight();
  286. }
  287. else
  288. {
  289. dimensions.y += font->getMetrics().getHeight() * lineSpacing;
  290. }
  291. }
  292. container->setDimensions(dimensions);
  293. }
  294. }