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

410 lines
8.7 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  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 "ui.hpp"
  20. #include "../application.hpp"
  21. // UI callbacks
  22. void menuPrint(Application* application, const std::string& string)
  23. {
  24. std::cout << string << std::endl;
  25. }
  26. UIMaterial::UIMaterial()
  27. {
  28. texture = addVariable<const Texture2D*>("texture");
  29. textureOffset = addVariable<Vector2>("offset");
  30. textureScale = addVariable<Vector2>("scale");
  31. texture->setValue(nullptr);
  32. textureOffset->setValue(Vector2(0.0f));
  33. textureScale->setValue(Vector2(1.0f));
  34. }
  35. UIElement::UIElement():
  36. parent(nullptr),
  37. anchor(Anchor::TOP_LEFT),
  38. layerOffset(0),
  39. layer(0),
  40. origin(0.0f),
  41. translation(0.0f),
  42. rotation(0.0f),
  43. dimensions(0.0f),
  44. position(0.0f),
  45. bounds(position, position),
  46. tintColor(1.0f),
  47. color(tintColor),
  48. visible(true),
  49. active(true),
  50. mouseOver(false),
  51. mouseOverCallback(nullptr),
  52. mouseOutCallback(nullptr),
  53. mouseMovedCallback(nullptr),
  54. mousePressedCallback(nullptr),
  55. mouseReleasedCallback(nullptr)
  56. {}
  57. UIElement::~UIElement()
  58. {}
  59. void UIElement::update()
  60. {
  61. // Calculate position
  62. if (parent != nullptr)
  63. {
  64. // Calculate world-space position
  65. Vector2 anchorPoint = parent->position + parent->dimensions * anchor - dimensions * anchor;
  66. position = anchorPoint + origin + translation;
  67. // Calculate layer
  68. layer = parent->layer + 1 + layerOffset;
  69. // Calculate color
  70. color = parent->color * tintColor;
  71. }
  72. else
  73. {
  74. position = origin + translation;
  75. layer = layerOffset;
  76. color = tintColor;
  77. }
  78. // Calculate bounds
  79. bounds.setMin(position);
  80. bounds.setMax(position + dimensions);
  81. // Update children
  82. for (UIElement* child: children)
  83. {
  84. child->update();
  85. }
  86. }
  87. void UIElement::addChild(UIElement* element)
  88. {
  89. children.push_back(element);
  90. element->parent = this;
  91. }
  92. void UIElement::removeChild(UIElement* element)
  93. {
  94. for (auto it = children.begin(); it != children.end(); ++it)
  95. {
  96. if (*it == element)
  97. {
  98. children.erase(it);
  99. return;
  100. }
  101. }
  102. }
  103. void UIElement::setMouseOverCallback(std::function<void()> callback)
  104. {
  105. mouseOverCallback = callback;
  106. }
  107. void UIElement::setMouseOutCallback(std::function<void()> callback)
  108. {
  109. mouseOutCallback = callback;
  110. }
  111. void UIElement::setMouseMovedCallback(std::function<void(int, int)> callback)
  112. {
  113. mouseMovedCallback = callback;
  114. }
  115. void UIElement::setMousePressedCallback(std::function<void(int, int, int)> callback)
  116. {
  117. mousePressedCallback = callback;
  118. }
  119. void UIElement::setMouseReleasedCallback(std::function<void(int, int, int)> callback)
  120. {
  121. mouseReleasedCallback = callback;
  122. }
  123. void UIElement::mouseMoved(int x, int y)
  124. {
  125. if (!active)
  126. {
  127. return;
  128. }
  129. if (bounds.contains(Vector2(x, y)))
  130. {
  131. if (!mouseOver)
  132. {
  133. mouseOver = true;
  134. if (mouseOverCallback)
  135. {
  136. mouseOverCallback();
  137. }
  138. }
  139. if (mouseMovedCallback)
  140. {
  141. mouseMovedCallback(x, y);
  142. }
  143. }
  144. else if (mouseOver)
  145. {
  146. mouseOver = false;
  147. if (mouseOutCallback)
  148. {
  149. mouseOutCallback();
  150. }
  151. }
  152. for (UIElement* child: children)
  153. {
  154. child->mouseMoved(x, y);
  155. }
  156. }
  157. void UIElement::mouseButtonPressed(int button, int x, int y)
  158. {
  159. if (!active)
  160. {
  161. return;
  162. }
  163. if (bounds.contains(Vector2(x, y)))
  164. {
  165. if (mousePressedCallback)
  166. {
  167. mousePressedCallback(button, x, y);
  168. }
  169. for (UIElement* child: children)
  170. {
  171. child->mouseButtonPressed(button, x, y);
  172. }
  173. }
  174. }
  175. void UIElement::mouseButtonReleased(int button, int x, int y)
  176. {
  177. if (!active)
  178. {
  179. return;
  180. }
  181. if (bounds.contains(Vector2(x, y)))
  182. {
  183. if (mouseReleasedCallback)
  184. {
  185. mouseReleasedCallback(button, x , y);
  186. }
  187. for (UIElement* child: children)
  188. {
  189. child->mouseButtonReleased(button, x, y);
  190. }
  191. }
  192. }
  193. UILabel::UILabel():
  194. font(nullptr)
  195. {}
  196. UILabel::~UILabel()
  197. {}
  198. void UILabel::setFont(Font* font)
  199. {
  200. this->font = font;
  201. material.texture->setValue(font->getTexture());
  202. calculateDimensions();
  203. }
  204. void UILabel::setText(const std::u32string& text)
  205. {
  206. this->text = text;
  207. calculateDimensions();
  208. }
  209. void UILabel::calculateDimensions()
  210. {
  211. if (font != nullptr && !text.empty())
  212. {
  213. float width = font->getWidth(text);
  214. float height = font->getMetrics().getHeight();
  215. setDimensions(Vector2(width, height));
  216. }
  217. else
  218. {
  219. setDimensions(Vector2(0.0f));
  220. }
  221. }
  222. UIImage::UIImage():
  223. textureBounds(Vector2(0.0f), Vector2(1.0f))
  224. {}
  225. UIImage::~UIImage()
  226. {}
  227. void UIBatcher::batch(BillboardBatch* result, const UIElement* ui)
  228. {
  229. // Create list of visible UI elements
  230. std::list<const UIElement*> elements;
  231. queueElements(&elements, ui);
  232. // Sort UI elements according to layer and texture
  233. elements.sort([](const UIElement* a, const UIElement* b)
  234. {
  235. if (a->getLayer() < b->getLayer())
  236. {
  237. return true;
  238. }
  239. else if (b->getLayer() < a->getLayer())
  240. {
  241. return false;
  242. }
  243. return (a->getMaterial()->texture->getValue() < b->getMaterial()->texture->getValue());
  244. });
  245. // Clear previous ranges
  246. result->removeRanges();
  247. // Batch UI elements
  248. for (const UIElement* element: elements)
  249. {
  250. batchElement(result, element);
  251. }
  252. // Update batch
  253. result->update();
  254. }
  255. void UIBatcher::queueElements(std::list<const UIElement*>* elements, const UIElement* element) const
  256. {
  257. if (element->isVisible())
  258. {
  259. elements->push_back(element);
  260. for (std::size_t i = 0; i < element->getChildCount(); ++i)
  261. {
  262. queueElements(elements, element->getChild(i));
  263. }
  264. }
  265. }
  266. BillboardBatch::Range* UIBatcher::getRange(BillboardBatch* result, const UIElement* element) const
  267. {
  268. BillboardBatch::Range* range = nullptr;
  269. if (!result->getRangeCount())
  270. {
  271. // Create initial range
  272. range = result->addRange();
  273. range->material = (Material*)element->getMaterial();
  274. range->start = 0;
  275. range->length = 0;
  276. }
  277. else
  278. {
  279. range = result->getRange(result->getRangeCount() - 1);
  280. const UIMaterial* material = static_cast<UIMaterial*>(range->material);
  281. if (material->texture->getValue() != element->getMaterial()->texture->getValue())
  282. {
  283. // Create new range for the element
  284. range = result->addRange();
  285. BillboardBatch::Range* precedingRange = result->getRange(result->getRangeCount() - 2);
  286. range->material = (Material*)element->getMaterial();
  287. range->start = precedingRange->start + precedingRange->length;
  288. range->length = 0;
  289. }
  290. }
  291. return range;
  292. }
  293. void UIBatcher::batchElement(BillboardBatch* result, const UIElement* element)
  294. {
  295. switch (element->getElementType())
  296. {
  297. case UIElement::Type::LABEL:
  298. batchLabel(result, static_cast<const UILabel*>(element));
  299. break;
  300. case UIElement::Type::IMAGE:
  301. batchImage(result, static_cast<const UIImage*>(element));
  302. break;
  303. case UIElement::Type::CONTAINER:
  304. break;
  305. default:
  306. break;
  307. }
  308. }
  309. void UIBatcher::batchLabel(BillboardBatch* result, const UILabel* label)
  310. {
  311. if (label->getFont() != nullptr && !label->getText().empty())
  312. {
  313. // Get range
  314. BillboardBatch::Range* range = getRange(result, label);
  315. // Pixel-perfect
  316. Vector3 origin = Vector3((int)label->getPosition().x, (int)label->getPosition().y, label->getLayer() * 0.01f);
  317. // Print billboards
  318. const Font* font = label->getFont();
  319. std::size_t index = range->start + range->length;
  320. std::size_t count = 0;
  321. font->puts(result, origin, label->getText(), label->getColor(), index, &count);
  322. // Increment range length
  323. range->length += count;
  324. }
  325. }
  326. void UIBatcher::batchImage(BillboardBatch* result, const UIImage* image)
  327. {
  328. // Get range
  329. BillboardBatch::Range* range = getRange(result, image);
  330. // Pixel-perfect
  331. //Vector3 translation = Vector3((int)(image->getPosition().x + image->getDimensions().x * 0.5f), (int)(image->getPosition().y + image->getDimensions().y * 0.5f), image->getLayer() * 0.01f);
  332. Vector3 translation = Vector3(image->getPosition() + image->getDimensions() * 0.5f, image->getLayer() * 0.01f);
  333. // Create billboard
  334. std::size_t index = range->start + range->length;
  335. Billboard* billboard = result->getBillboard(index);
  336. billboard->setDimensions(image->getDimensions());
  337. billboard->setTranslation(translation);
  338. if (image->getRotation() != 0.0f)
  339. {
  340. billboard->setRotation(glm::angleAxis(image->getRotation(), Vector3(0, 0, -1.0f)));
  341. }
  342. billboard->setTextureCoordinates(image->getTextureBounds().getMin(), image->getTextureBounds().getMax());
  343. billboard->setTintColor(image->getColor());
  344. // Increment range length
  345. ++(range->length);
  346. }