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

343 lines
6.8 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. {
  138. container = new UIContainer();
  139. resize();
  140. }
  141. Menu::~Menu()
  142. {
  143. removeItems();
  144. delete container;
  145. }
  146. void Menu::enter()
  147. {
  148. if (enteredCallback != nullptr)
  149. {
  150. enteredCallback();
  151. }
  152. }
  153. void Menu::exit()
  154. {
  155. if (exitedCallback != nullptr)
  156. {
  157. exitedCallback();
  158. }
  159. }
  160. MenuItem* Menu::addItem()
  161. {
  162. // Allocate item and add to items
  163. MenuItem* item = new MenuItem(this, items.size());
  164. items.push_back(item);
  165. // Set item fonts
  166. item->nameLabel->setFont(font);
  167. item->valueLabel->setFont(font);
  168. // Set item colors
  169. item->nameLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f));
  170. item->valueLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f));
  171. // Add item labels to UI container
  172. container->addChild(item->rowContainer);
  173. // Resize UI container
  174. resize();
  175. return item;
  176. }
  177. void Menu::removeItems()
  178. {
  179. for (MenuItem* item: items)
  180. {
  181. // Remove labels from UI container
  182. container->removeChild(item->rowContainer);
  183. delete item;
  184. }
  185. items.clear();
  186. resize();
  187. }
  188. void Menu::setEnteredCallback(std::function<void()> callback)
  189. {
  190. this->enteredCallback = callback;
  191. }
  192. void Menu::setExitedCallback(std::function<void()> callback)
  193. {
  194. this->exitedCallback = callback;
  195. }
  196. void Menu::setFont(Font* font)
  197. {
  198. this->font = font;
  199. for (MenuItem* item: items)
  200. {
  201. item->nameLabel->setFont(font);
  202. item->valueLabel->setFont(font);
  203. }
  204. resize();
  205. }
  206. void Menu::setLineSpacing(float spacing)
  207. {
  208. lineSpacing = spacing;
  209. resize();
  210. }
  211. void Menu::setColumnMargin(float margin)
  212. {
  213. columnMargin = margin;
  214. resize();
  215. }
  216. void Menu::deselect()
  217. {
  218. if (selectedItem != nullptr)
  219. {
  220. selectedItem->deselect();
  221. selectedItem->nameLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f));
  222. selectedItem->valueLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f));
  223. selectedItem = nullptr;
  224. }
  225. }
  226. void Menu::select(std::size_t index)
  227. {
  228. deselect();
  229. MenuItem* item = items[index];
  230. item->select();
  231. selectedItem = item;
  232. selectedItem->nameLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 1.0f));
  233. selectedItem->valueLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 1.0f));
  234. }
  235. void Menu::activate()
  236. {
  237. if (selectedItem != nullptr)
  238. {
  239. selectedItem->activate();
  240. }
  241. }
  242. void Menu::resize()
  243. {
  244. if (!font)
  245. {
  246. container->setDimensions(Vector2(0.0f));
  247. }
  248. else
  249. {
  250. // Determine maximum width of menu
  251. float menuWidth = 0.0f;
  252. // For each menu item
  253. for (std::size_t i = 0; i < items.size(); ++i)
  254. {
  255. const MenuItem* item = items[i];
  256. // Calculate width of item name label
  257. float nameLabelWidth = item->nameLabel->getDimensions().x;
  258. // For each item value
  259. for (std::size_t j = 0; j < item->getValueCount(); ++j)
  260. {
  261. // Calculate width of item value label
  262. float valueLabelWidth = font->getWidth(item->getValue(j).c_str());
  263. menuWidth = std::max<float>(menuWidth, nameLabelWidth + columnMargin + valueLabelWidth);
  264. }
  265. menuWidth = std::max<float>(menuWidth, nameLabelWidth);
  266. }
  267. Vector2 dimensions(menuWidth, 0.0f);
  268. for (std::size_t i = 0; i < items.size(); ++i)
  269. {
  270. const MenuItem* item = items[i];
  271. float translationY = static_cast<float>(static_cast<int>(font->getMetrics().getHeight() * lineSpacing * static_cast<float>(i)));
  272. item->rowContainer->setDimensions(Vector2(menuWidth, font->getMetrics().getHeight()));
  273. item->rowContainer->setTranslation(Vector2(0.0f, translationY));
  274. if (!i)
  275. {
  276. dimensions.y += font->getMetrics().getHeight();
  277. }
  278. else
  279. {
  280. dimensions.y += font->getMetrics().getHeight() * lineSpacing;
  281. }
  282. }
  283. container->setDimensions(dimensions);
  284. }
  285. }