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.

Component.cpp 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. #include "Component.h"
  2. #include <iostream>
  3. #include <algorithm>
  4. #include <cmath>
  5. #include "TextComponent.h"
  6. #include "InputComponent.h"
  7. #include "ButtonComponent.h"
  8. #include "ComponentBuilder.h"
  9. // pos, size
  10. std::unique_ptr<std::pair<UImetricV2, UImetricV2>> getUIMetric(UImetricV2 &s1, UImetricV2 &s2, UImetricV2 &size) {
  11. // only 2 of these can be requested
  12. if ((s1.requested && s2.requested && size.requested) || (!s1.requested && !s2.requested && !size.requested)) {
  13. // FIXME: better message
  14. std::cout << "component.getUIMetric - can't resolve" << std::endl;
  15. return nullptr;
  16. }
  17. std::unique_ptr<std::pair<UImetricV2, UImetricV2>> result=std::make_unique<std::pair<UImetricV2, UImetricV2>>();
  18. if (s1.requested && s2.requested) {
  19. // left/right top/bottom
  20. //std::cout << "left/right px " << s1.px << " & " << s2.px << std::endl;
  21. //std::cout << "left/right % " << s1.pct << " & " << s2.pct << std::endl;
  22. result->first = { true, s1.pct, s1.px }; // pos
  23. result->second = { true, (100.0 - s2.pct) - s1.pct, -s2.px - s1.px }; // size
  24. /*
  25. if (s1.pct && s2.pct) {
  26. // right 25% = left 75%
  27. result->second = { true, (100 - s2.pct) - s1.pct, 0 }; // size
  28. } else
  29. if (!s1.pct && !s2.pct) {
  30. result->second = { true, 100.0 - s2.pct, -s2.px };
  31. } else if (s1.pct) {
  32. result->second = { true, 100.0 - s2.pct, -s2.px };
  33. } else if (s2.pct) {
  34. // right 25% = left 75%
  35. result->second = { true, 100.0 - s2.pct, -s2.px };
  36. } else {
  37. // unknown
  38. }
  39. */
  40. } else
  41. if (s1.requested && size.requested) {
  42. // left + width top+height
  43. result->first = { true, s1.pct, s1.px }; // pos
  44. result->second = { true, size.pct, size.px };
  45. } else
  46. if (s2.requested && size.requested) {
  47. // right - width bottom-height
  48. // right 25% = left 75%
  49. // + width 50% means start at 25%
  50. result->first = { true, (100.0 - s2.pct) - size.pct, -s2.px - size.px }; // pos
  51. result->second = { true, size.pct, size.px };
  52. } else {
  53. std::cout << "component.getUIMetric - Unknown state" << std::endl;
  54. }
  55. return result;
  56. }
  57. Component::~Component() {
  58. }
  59. // translate properties (left/right/width && top/bottom/height) into resizeable layout
  60. // we may not need pwin (we have this->win)
  61. void Component::setUpUI(std::map<std::string, std::string> &properties, Window *pwin) {
  62. UILayoutV2 boxSetup;
  63. boxSetup.width.px = 32;
  64. boxSetup.height.px = 32;
  65. //std::cout << "Component::setUpUI - type: " << typeOfComponent(this) << std::endl;
  66. if (properties.find("width") != properties.end()) {
  67. //std::cout << "Component::setUpUI - has width" << std::endl;
  68. boxSetup.width.requested = true;
  69. if (properties["width"][properties["width"].size() - 1] == '%') {
  70. //std::cout << "Component::setUpUI - found width %" << std::endl;
  71. boxSetup.width.pct = std::stoi(properties["width"]);
  72. } else {
  73. boxSetup.width.px = std::stoi(properties["width"]);
  74. }
  75. }
  76. if (properties.find("height") != properties.end()) {
  77. boxSetup.height.requested = true;
  78. if (properties["height"][properties["height"].size() - 1] == '%') {
  79. //std::cout << "Component::setUpUI - found height %" << std::endl;
  80. boxSetup.height.pct = std::stoi(properties["height"]);
  81. } else {
  82. boxSetup.height.px = std::stoi(properties["height"]);
  83. }
  84. }
  85. if (properties.find("top") != properties.end()) {
  86. boxSetup.top.requested = true;
  87. if (properties["top"][properties["top"].size() - 1] == '%') {
  88. //std::cout << "Component::setUpUI - found top %" << std::endl;
  89. boxSetup.top.pct = std::stoi(properties["top"]);
  90. } else {
  91. boxSetup.top.px = std::stoi(properties["top"]);
  92. }
  93. }
  94. if (properties.find("bottom") != properties.end()) {
  95. boxSetup.bottom.requested = true;
  96. if (properties["bottom"][properties["bottom"].size() - 1] == '%') {
  97. //std::cout << "Component::setUpUI - found bottom %" << std::endl;
  98. boxSetup.bottom.pct = std::stoi(properties["bottom"]);
  99. } else {
  100. boxSetup.bottom.px = std::stoi(properties["bottom"]);
  101. }
  102. }
  103. if (properties.find("left") != properties.end()) {
  104. //std::cout << "Component::setUpUI - has left" << std::endl;
  105. boxSetup.left.requested = true;
  106. if (properties["left"][properties["left"].size() - 1] == '%') {
  107. //std::cout << "Component::setUpUI - found left %" << std::endl;
  108. boxSetup.left.pct = std::stoi(properties["left"]);
  109. } else {
  110. boxSetup.left.px = std::stoi(properties["left"]);
  111. }
  112. }
  113. if (properties.find("right") != properties.end()) {
  114. //std::cout << "Component::setUpUI - has right" << std::endl;
  115. boxSetup.right.requested = true;
  116. if (properties["right"][properties["right"].size() - 1] == '%') {
  117. //std::cout << "Component::setUpUI - found right %" << std::endl;
  118. boxSetup.right.pct = std::stoi(properties["right"]);
  119. } else {
  120. boxSetup.right.px = std::stoi(properties["right"]);
  121. }
  122. }
  123. /*
  124. std::cout << "boxSetup left : " << boxSetup.left.requested << " " << boxSetup.left.pct << "% @" << boxSetup.left.px << std::endl;
  125. std::cout << "boxSetup right: " << boxSetup.right.requested << " " << boxSetup.right.pct << "% @" << boxSetup.right.px << std::endl;
  126. std::cout << "boxSetup width: " << boxSetup.width.requested << " " << boxSetup.width.pct << "% @" << boxSetup.width.px << std::endl;
  127. std::cout << "boxSetup top : " << boxSetup.top.requested << " " << boxSetup.top.pct << "% @" << boxSetup.top.px << std::endl;
  128. std::cout << "boxSetup bottom: " << boxSetup.bottom.requested << " " << boxSetup.bottom.pct << "% @" << boxSetup.bottom.px << std::endl;
  129. std::cout << "boxSetup height: " << boxSetup.height.requested << " " << boxSetup.height.pct << "% @" << boxSetup.height.px << std::endl;
  130. */
  131. std::unique_ptr<std::pair<UImetricV2, UImetricV2>> xUI = getUIMetric(boxSetup.left, boxSetup.right, boxSetup.width);
  132. std::unique_ptr<std::pair<UImetricV2, UImetricV2>> yUI = getUIMetric(boxSetup.top, boxSetup.bottom, boxSetup.height);
  133. //int cX = 0, cY = 0;
  134. int cW = boxSetup.width.px, cH = boxSetup.height.px;
  135. int xPx = 0, yPx = 0;
  136. double xPct = 0, yPct = 0;
  137. double wPct = 100, hPct = 100;
  138. int wPx = 0, hPx = 0;
  139. //int winWidth = pwin->windowWidth;
  140. int winHeight = pwin->windowHeight;
  141. //std::cout << "Component::setUpUI - laying out a " << typeOfComponent(this) << std::endl;
  142. if (xUI) {
  143. //std::cout << "box XUI at " << xUI->first.pct << "% +" << xUI->first.px << std::endl;
  144. //std::cout << "box XUI for " << xUI->second.pct << "% +" << xUI->second.px << std::endl;
  145. // QUESTION: I don't think cx should take pct into account
  146. // I think it does because 25% into starting px is important
  147. // no because we could just set uiControl.x correctly...
  148. // and if we did that we could pull out win
  149. // well we still need winHeight tho, because of the coords
  150. //cX = (xUI->first.pct * 0.01 * winWidth) + xUI->first.px;
  151. xPct = xUI->first.pct;
  152. xPx = xUI->first.px;
  153. //cW = (xUI->second.pct * 0.01 * winWidth) + xUI->second.px;
  154. wPct = xUI->second.pct;
  155. wPx = xUI->second.px;
  156. //std::cout << "Component::setUpUI X at " << cX << " for " << cW << std::endl;
  157. //std::cout << "Component::setUpUI W at " << wPct << "% for " << wPx << std::endl;
  158. }
  159. if (yUI) {
  160. //std::cout << "Component::setUpUI pct" << yUI->first.pct << "/100 - " << yUI->first.px << std::endl;
  161. //cY = (yUI->first.pct * 0.01 * winHeight) - yUI->first.px;
  162. // bottom=0 is 0, 0, so we do 100- here
  163. yPct = 100 - yUI->first.pct;
  164. yPx = yUI->first.px;
  165. cH = (yUI->second.pct * 0.01 * winHeight) + yUI->second.px;
  166. hPct = yUI->second.pct;
  167. hPx = yUI->second.px;
  168. //std::cout << "Component::setUpUI Y at " << cY << " for " << cH << std::endl;
  169. //std::cout << "Component::setUpUI Y would be " << (-cH - cY) << " and new is " << yPx << std::endl;
  170. //std::cout << "Component::setUpUI H at " << hPct << "% for " << hPx << std::endl;
  171. }
  172. // set up proper component information for resizing
  173. this->uiControl.x = { xPct, xPx }; //
  174. this->uiControl.y = { yPct, -cH - yPx }; //
  175. this->uiControl.w = { wPct, wPx }; //
  176. this->uiControl.h = { hPct, hPx }; //
  177. // recalculate new vertices
  178. this->layout(); // move from uiContorl to x,y
  179. // we don't need to set to call resize because layout already calls it (always calls wrap->resize)
  180. //this->resize(winWidth, winHeight); // apply x,y state to vertices
  181. }
  182. // resize is where width/height can change within the confines of the window
  183. void Component::resize(const int passedWindowWidth, const int passedWindowHeight) {
  184. //std::cout << "Component::resize - doing nothing" << std::endl;
  185. windowWidth = passedWindowWidth;
  186. windowHeight = passedWindowHeight;
  187. /*
  188. if (boundToPage) {
  189. } else {
  190. */
  191. // no vertices in component
  192. /*
  193. float vx = x;
  194. float vy = y;
  195. float vWidth = width;
  196. float vHeight = height;
  197. pointToViewport(vx, vy);
  198. distanceToViewport(vWidth, vHeight);
  199. vertices[(0 * 5) + 0] = vx;
  200. vertices[(0 * 5) + 1] = vy + vHeight;
  201. vertices[(1 * 5) + 0] = vx + vWidth;
  202. vertices[(1 * 5) + 1] = vy + vHeight;
  203. vertices[(2 * 5) + 0] = vx + vWidth;
  204. vertices[(2 * 5) + 1] = vy;
  205. vertices[(3 * 5) + 0] = vx;
  206. vertices[(3 * 5) + 1] = vy;
  207. verticesDirty = true;
  208. */
  209. //}
  210. // do we recurse down through children?
  211. // well if a parent resizes, yea we should tell our children the window size has changed
  212. // btw a layout already does this
  213. // yea let's let layout handle this
  214. /*
  215. for (std::shared_ptr<Component> child : children) {
  216. // update it's size
  217. // maybe we should called wrap
  218. child->resize(windowWidth, windowHeight);
  219. }
  220. */
  221. }
  222. void Component::setParent(std::shared_ptr<Component> passedParent) {
  223. if (!passedParent) {
  224. std::cout << "Component::setParent is empty" << std::endl;
  225. }
  226. if (parent) {
  227. std::cout << "Component::setParent - triggering component move" << std::endl;
  228. }
  229. // set parent of the newly created component
  230. parent = passedParent;
  231. // update our position
  232. // probably could move this out of here
  233. if (boundToPage) {
  234. x = passedParent->x;
  235. y = passedParent->y - passedParent->height;
  236. }
  237. //std::cout << "Component::setParent - placing at " << static_cast<int>(x) << "," << static_cast<int>(y) << std::endl;
  238. if (passedParent) {
  239. if (passedParent->children.size()) {
  240. this->previous = passedParent->children.back();
  241. }
  242. // need to figure out how to make this work
  243. //passedParent->children.push_back(std::make_shared<Component>(this));
  244. }
  245. // add new component as child to parent
  246. //std::shared_ptr<Component> child=*new std::shared_ptr<Component>(this);
  247. //parent->children.push_back(child);
  248. }
  249. // window size is required because we need to know if our x is placed outside bounds
  250. // our size is not required
  251. // but all previous children sizes and positions are
  252. // this function should be smart enough to read state, figure out what changes are actually needed and execute them
  253. void Component::layout() {
  254. // back up current position
  255. //int lx = x;
  256. //int ly = y;
  257. // if we want to position the root component and layout (because to move things we need to trigger a resize)
  258. // then we need this to not reset because it will affect the position we're trying to relayout them out at
  259. // reset position
  260. //x = 0;
  261. //y = 0;
  262. /*
  263. TextComponent *textComponent = dynamic_cast<TextComponent*>(this);
  264. if (textComponent) {
  265. std::cout << "Component::layout[" << textComponent->text << "]" << std::endl;
  266. }
  267. */
  268. //std::cout << "Component::layout - name: " << name << " type " << typeOfComponent(this) << " boundToPage: " << boundToPage << std::endl;
  269. // if we're a child, get our parents position
  270. if (parent && boundToPage) {
  271. //std::cout << "Component::layout - copying position from parent: " << (int)parent->x << "x" << (int)parent->y << std::endl;
  272. x = parent->x;
  273. y = parent->y;
  274. // if we have sibilings see if they're inline or block
  275. if (parent->children.size()) {
  276. //std::cout << "Component::layout - parent children: " << parent->children.size() << std::endl;
  277. if (previous) {
  278. //std::cout << "Component::layout - has previous " << std::endl;
  279. /*
  280. TextComponent *prevTextComponent = dynamic_cast<TextComponent*>(previous.get());
  281. if (prevTextComponent) {
  282. std::cout << "Component::layout - previous [" << prevTextComponent->text << "] ending at: " << (int)prevTextComponent->endingX << "x" << (int)prevTextComponent->endingY << "" << std::endl;
  283. }
  284. */
  285. //std::cout << "Component::layout - previous at: " << (int)previous->x << "x" << (int)previous->y << " size: " << (int)previous->width << "x" << (int)previous->height << " ending at: " << (int)previous->endingX << "x" << (int)previous->endingY << std::endl;
  286. // 2nd or last
  287. if (previous->isInline) {
  288. // last was inline
  289. if (isInline) {
  290. x = previous->x + previous->width;
  291. y = previous->y; // keep on same line
  292. //std::cout << "Component::layout - inLine (" << (int)previous->width << " wide) inLine" << std::endl;
  293. if (x >= windowWidth) {
  294. //std::cout << "Component::layout - inline inline wrapping because x: " << (int)x << " window: " << windowWidth << std::endl;
  295. x = previous->endingX;
  296. //std::cout << "Component::layout - p.y: " << (int)previous->y << " p.ey: " << previous->endingY << " p.h" << (int)previous->height << std::endl;
  297. y = previous->y - previous->height + previous->endingY;
  298. }
  299. } else {
  300. // we're block
  301. y = previous->y - previous->height; // advance down one height
  302. if (!previous->height) {
  303. // ok if last is a nothing burger (0x0 size)
  304. // then we need the parent height
  305. // or we could spool to get the height for the last inline that's been set
  306. // this isn't right
  307. //y -= this->parent->height;
  308. // this is bad because we assume there's only going to be one
  309. std::shared_ptr<Component> pos = this->previous;
  310. bool found = false;
  311. while(pos->previous) {
  312. pos = pos->previous;
  313. if (!pos->isInline) {
  314. std::cout << "Component::layout - found block element at " << pos->y << std::endl;
  315. break;
  316. }
  317. if (pos->height) {
  318. //std::cout << "no size, grabbing height " << static_cast<int>(pos->height) << std::endl;
  319. y -= pos->height;
  320. found = true;
  321. break;
  322. }
  323. }
  324. if (!found) {
  325. std::cout << "Component::layout - couldnt adjust height properly" << std::endl;
  326. }
  327. }
  328. //std::cout << "Component::layout - inLine block" << std::endl;
  329. }
  330. } else {
  331. // last was block
  332. y = previous->y - previous->height;
  333. //std::cout << "Component::layout - block * p.x: " << (int)previous->x << " p.h: " << (int)previous->height << std::endl;
  334. }
  335. // really only inline but can't hurt block AFAICT
  336. if (x >= windowWidth) {
  337. //std::cout << "Component::layout - wrapping because x: " << (int)x << " window: " << windowWidth << std::endl;
  338. x = 0;
  339. y -= previous->height; // how far down do we need to wrap?, the previous height?
  340. }
  341. } else {
  342. // first, there will be no width to add
  343. }
  344. } else {
  345. // first component for this parent
  346. }
  347. }
  348. if (!boundToPage) {
  349. // select new x,y & w/h bassed on UImetric
  350. //std::cout << "!boundToPage " << name << " from: " << (int)x << "," << (int)y << " " << (int)width << "," << (int)height << std::endl;
  351. //std::cout << "uic.x.lenghtPct: " << uiControl.x.lengthPct << " width: " << windowWidth << " uic.x.offset: " << uiControl.x.offset << std::endl;
  352. this->x = (uiControl.x.lengthPct / 100.0) * this->windowWidth + uiControl.x.offset;
  353. //std::cout << "uic.y.lenghtPct: " << uiControl.y.lengthPct << " height: " << windowHeight << " uic.y.offset: " << uiControl.y.offset << std::endl;
  354. this->y = (uiControl.y.lengthPct / 100.0) * this->windowHeight + uiControl.y.offset;
  355. this->width = (uiControl.w.lengthPct / 100.0) * this->windowWidth + uiControl.w.offset;
  356. this->height = (uiControl.h.lengthPct / 100.0) * this->windowHeight + uiControl.h.offset;
  357. //std::cout << "!boundToPage " << name << " to: " << (int)x << "," << (int)y << " " << (int)width << "," << (int)height << std::endl;
  358. }
  359. //std::cout << "Component::layout - adjusted by prev: " << (int)x << "x" << (int)y << std::endl;
  360. //std::cout << "Component::layout - moving component to " << (int)x << "x" << (int)y << std::endl;
  361. // change in X position
  362. //std::cout << "Component::layout - component was at " << lx << " moved(?) to " << (int)x << std::endl;
  363. // also need to update texture if our width (which is calculated from windowWidth) changes
  364. // it's more than width because a change to windowWidth fucks up all coordinates in it
  365. // so it has to be recalculated (??)
  366. //if (x!=lx || !textureSetup) {
  367. // this line may have more or less room
  368. // we'll need to rewrap component
  369. wrap();
  370. //}
  371. // recurse down over my children and update their position
  372. // if parent x or y changes or windowWindow changes, all children need to be adjust
  373. // what about recurse up, if this element changes? well wrap recurses up the parent sizes
  374. //std::cout << "Component::layout - I have " << children.size() << " children affected by my moved" << std::endl;
  375. for (std::shared_ptr<Component> child : children) {
  376. // maybe it should be like this:
  377. // hey new w/h
  378. // update wras
  379. // where do we update layout?
  380. // So no, because this is a relayout, we don't know sf w/h has changes
  381. // most component resize()s already update the w/h
  382. //std::cout << "component::layout - " << name << " Doing relayout of a " << typeOfComponent(child) << "-" << child->name << std::endl;
  383. // update new sizes
  384. child->windowWidth = windowWidth;
  385. child->windowHeight = windowHeight;
  386. // update it's layout
  387. child->layout();
  388. }
  389. // also maybe bump siblings after us (inline if x/y moved, block if y moved)
  390. }
  391. // resize/wordwrap to available width
  392. // our position is required
  393. void Component::wrap() {
  394. //std::cout << "Component::wrap - " << typeOfComponent(this) << std::endl;
  395. float lW = width;
  396. float lH = height;
  397. resize(windowWidth, windowHeight); // this may change our w/h
  398. textureSetup = true;
  399. if (lW != width || lH != height) {
  400. //std::cout << "Component::wrap - component was " << (int)lW << "x" << (int)lH << " now " << (int)width << "x" << (int)height << std::endl;
  401. // size has change, update our parent
  402. updateParentSize();
  403. } else {
  404. //std::cout << "Component::wrap - component same size" << std::endl;
  405. }
  406. }
  407. // measure current, apply changes to parent
  408. // our size is required
  409. void Component::updateParentSize() {
  410. //std::cout << "Component::updateParentSize - " << typeOfComponent(this) << std::endl;
  411. if (!parent) {
  412. //std::cout << "Component::updateParentSize - can't update parent size, no parent" << std::endl;
  413. return;
  414. }
  415. // back up current size
  416. unsigned int lastParentWidth = parent->width;
  417. unsigned int lastParentHeight = parent->height;
  418. parent->endingX = endingX;
  419. parent->endingY = endingY;
  420. // find max width of all siblings
  421. unsigned int maxWidth = width; // float?
  422. unsigned int heightAccum = 0;
  423. unsigned int widthAccum = 0;
  424. unsigned int totalHeight = 0;
  425. // integrity check
  426. if (!parent->children.size()) {
  427. // currently means creation
  428. std::cout << "Component::updateParentSize - parent's has no children, strange since we are one" << std::endl;
  429. totalHeight = height;
  430. }
  431. // look at siblings
  432. bool wasInline = false;
  433. //std::cout << "Component::updateParentSize - assessing " << parent->children.size() << " children" << std::endl;
  434. for (std::shared_ptr<Component> child : parent->children) {
  435. maxWidth = std::max(maxWidth, static_cast<unsigned int>(child->width));
  436. if (child->isInline) {
  437. heightAccum = std::max(heightAccum, static_cast<unsigned int>(child->height));
  438. widthAccum += child->width;
  439. /*
  440. if (wasInline) {
  441. // in in
  442. heightAccum = std::max(heightAccum, static_cast<int>(child->height));
  443. } else {
  444. // bl in
  445. heightAccum = static_cast<int>(child->height);
  446. }
  447. */
  448. } else {
  449. if (wasInline) {
  450. // in bl
  451. // flush height of previous line + our height
  452. totalHeight += heightAccum + child->height;
  453. heightAccum = 0; // reset
  454. maxWidth = std::max(maxWidth, widthAccum);
  455. widthAccum = 0;
  456. } else {
  457. // bl bl
  458. totalHeight += child->height;
  459. // than a max of all widths of the two is fine
  460. }
  461. }
  462. wasInline = child->isInline;
  463. }
  464. // flush any remaining width/heightAccum
  465. totalHeight += heightAccum;
  466. maxWidth = std::max(maxWidth, widthAccum);
  467. //std::cout << "new size " << maxWidth << "x" << totalHeight << std::endl;
  468. // did our size actually change
  469. if (lastParentWidth != maxWidth || lastParentHeight != totalHeight) {
  470. //std::cout << "Component::updateParentSize - from " << (int)height << " totalHeight: " << totalHeight << std::endl;
  471. parent->width = maxWidth;
  472. parent->height = totalHeight;
  473. parent->updateParentSize(); // our parent size can only be recalculated by looking at it's children
  474. }
  475. }
  476. GLuint elementBufferObject = 0;
  477. void Component::initTheElementBufferObject() const {
  478. glGenBuffers(1, &elementBufferObject);
  479. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBufferObject);
  480. glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
  481. }
  482. GLuint Component::CreateVertexArrayObject() const {
  483. GLuint vertexArrayObject = 0;
  484. glGenVertexArrays(1, &vertexArrayObject);
  485. glBindVertexArray(vertexArrayObject);
  486. // we may need the VBO here...
  487. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBufferObject);
  488. glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), nullptr);
  489. glEnableVertexAttribArray(0);
  490. glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(3 * sizeof(float)));
  491. glEnableVertexAttribArray(1);
  492. //glBindVertexArray(0);
  493. return vertexArrayObject;
  494. }
  495. GLuint Component::CreateVertexBufferObject() const {
  496. GLuint vertexBufferObject = 0;
  497. glGenBuffers(1, &vertexBufferObject);
  498. return vertexBufferObject;
  499. }
  500. // this always creates, we probably should just respond to existing state
  501. bool Component::setPosition4(GLuint vertexBufferObject, int x0, int y0, int x1, int y1) const {
  502. float vertices[20] = {
  503. 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
  504. 0.0f, 0.0f, 0.0f, 1.0f, 1.0f,
  505. 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
  506. 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
  507. };
  508. vertices[(0 * 5) + 0] = x0;
  509. vertices[(0 * 5) + 1] = y1;
  510. vertices[(1 * 5) + 0] = x1;
  511. vertices[(1 * 5) + 1] = y1;
  512. vertices[(2 * 5) + 0] = x1;
  513. vertices[(2 * 5) + 1] = y0;
  514. vertices[(3 * 5) + 0] = x0;
  515. vertices[(3 * 5) + 1] = y0;
  516. glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject); // selects buffer
  517. glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // writes buffer
  518. return true;
  519. }
  520. GLuint Component::CreateTexture() const {
  521. GLuint textureNum = 0;
  522. glGenTextures(1, &textureNum);
  523. return textureNum;
  524. }
  525. bool Component::setTexture(GLuint textureNum, GLsizei w, GLsizei h, const unsigned char *texture) const {
  526. glBindTexture(GL_TEXTURE_2D, textureNum);
  527. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture);
  528. glGenerateMipmap(GL_TEXTURE_2D);
  529. return true;
  530. }
  531. // converts 0-1 to screen
  532. // but centered
  533. void Component::pointToViewport(float &rawX, float &rawY) const {
  534. if (useBoxShader) {
  535. //std::cout << "Component::pointToViewport - notBoundToPage converting from " << static_cast<int>(rawX) << "," << static_cast<int>(rawY) << std::endl;
  536. //std::cout << "BoundToPage using " << screenWidth << "x" << screenHeight << std::endl;
  537. rawX = ((rawX / windowWidth) * 2) - 1;
  538. rawY = ((rawY / windowHeight) * 2) - 1;
  539. //std::cout << "Component::pointToViewport - BoundToPage using " << static_cast<int>(rawX) << "x" << static_cast<int>(rawY) << std::endl;
  540. //std::cout << "Component::pointToViewport - BoundToPage converted to " << rawX << "," << rawY << std::endl;
  541. } else {
  542. //std::cout << "notBoundToPage using " << screenWidth << "x" << screenHeight << std::endl;
  543. //std::cout << "Component::pointToViewport - notBoundToPage converting from " << static_cast<int>(rawX) << "," << static_cast<int>(rawY) << std::endl;
  544. if (rawX < 0) {
  545. rawX += windowWidth;
  546. }
  547. if (rawY < 0) {
  548. rawY += windowHeight;
  549. }
  550. if (rawX > 1) {
  551. rawX /= windowWidth;
  552. }
  553. if (rawY > 1) {
  554. rawY /= windowHeight;
  555. }
  556. rawX = (rawX * 2) - 1;
  557. rawY = (rawY * 2) - 1;
  558. //std::cout << "Component::pointToViewport - notBoundToPage converted to " << rawX << "," << rawY << std::endl;
  559. }
  560. }
  561. // keeps 0-1 (and *2 to convert to screen)
  562. // but also takes pixels (and converts to screen)
  563. // anchors to upperleft
  564. void Component::distanceToViewport(float &rawX, float &rawY) const {
  565. if (std::abs(rawX) > 1) {
  566. rawX /= windowWidth;
  567. }
  568. if (std::abs(rawY) > 1) {
  569. rawY /= windowHeight;
  570. }
  571. rawX *= 2;
  572. rawY *= 2;
  573. }
  574. void Component::printComponentTree(const std::shared_ptr<Component> &component, int depth) {
  575. for (int i = 0; i < depth; i++) {
  576. std::cout << '\t';
  577. }
  578. ButtonComponent *butComponent = dynamic_cast<ButtonComponent*>(component.get());
  579. InputComponent *inputComponent = dynamic_cast<InputComponent*>(component.get());
  580. if (butComponent) {
  581. std::cout << std::fixed << "X: " << static_cast<int>(butComponent->x) << " Y: " << static_cast<int>(butComponent->y) << " WIDTH: " << static_cast<int>(butComponent->width) << " HEIGHT: " << static_cast<int>(butComponent->height) << " INLINE: " << butComponent->isInline << " Bound: " << butComponent->boundToPage << " BUTTON: " << butComponent->value << std::endl;
  582. } else
  583. if (inputComponent) {
  584. std::cout << std::fixed << "X: " << static_cast<int>(inputComponent->x) << " Y: " << static_cast<int>(inputComponent->y) << " WIDTH: " << static_cast<int>(inputComponent->width) << " HEIGHT: " << static_cast<int>(inputComponent->height) << " INLINE: " << inputComponent->isInline << " Bound: " << inputComponent->boundToPage << " INPUT: " << inputComponent->getValue() << std::endl;
  585. } else {
  586. TextComponent *textComponent = dynamic_cast<TextComponent*>(component.get());
  587. if (textComponent) {
  588. std::cout << std::fixed << "X: " << static_cast<int>(textComponent->x) << " Y: " << static_cast<int>(textComponent->y) << " WIDTH: " << static_cast<int>(textComponent->width) << " HEIGHT: " << static_cast<int>(textComponent->height) << " INLINE: " << textComponent->isInline << " TEXT: " << textComponent->text << std::endl;
  589. }
  590. else {
  591. std::cout << std::fixed << "X: " << static_cast<int>(component->x) << " Y: " << static_cast<int>(component->y) << " WIDTH: " << static_cast<int>(component->width) << " HEIGHT: " << static_cast<int>(component->height) << " INLINE: " << component->isInline << " NAME: " << component->name << std::endl;
  592. }
  593. }
  594. for (std::shared_ptr<Component> child : component->children) {
  595. Component::printComponentTree(child, depth + 1);
  596. }
  597. }