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.

DocumentComponent.cpp 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. #include "DocumentComponent.h"
  2. #include <cmath>
  3. #include <iostream>
  4. #include "InputComponent.h"
  5. #include "ButtonComponent.h"
  6. #include "TabbedComponent.h"
  7. #include <ctime>
  8. #include "../graphical/renderers/glfw/Shader.h"
  9. #include "../../tools/Log.h"
  10. #include "../../tools/WebResource.h"
  11. #include "../../app/browser/Browser.h"
  12. #include "../../parsers/markup/html/HTMLParser.h"
  13. void deleteComponent(std::shared_ptr<Component> &component);
  14. void deleteNode(std::shared_ptr<Node> node);
  15. extern std::unique_ptr<Browser> browser;
  16. #include <utility>
  17. // well we need to know how many chars in a lines
  18. // we also need to know where a line starts (pos)
  19. // lines start at 0 for first line
  20. std::pair<size_t, size_t> getLine(std::string text, int findLine) {
  21. //std::cout << "DocumentComponent.getLine [" << text << "] findLine: " << findLine << std::endl;
  22. size_t pos = 0;
  23. size_t lPos = 0;
  24. pos = text.find("\r");
  25. size_t line = 0;
  26. while(pos != std::string::npos) {
  27. lPos = pos;
  28. pos = text.find("\r", lPos + 1);
  29. if (line == static_cast<unsigned int>(findLine)) {
  30. //std::cout << "lPos: " << lPos << " pos: " << pos << " line: " << line << std::endl;
  31. //std::cout << "DocumentComponent.getLine start " << (lPos + line + 1) << " end " << (pos == std::string::npos ? text.length() : (lPos + pos)) << std::endl;
  32. return std::make_pair(lPos + line + 1, pos == std::string::npos ? text.length() : (lPos + pos));
  33. }
  34. line++;
  35. }
  36. //std::cout << "end lPos: " << lPos << " pos: " << pos << " line: " << line << std::endl;
  37. //std::cout << "DocumentComponent.getLine result end of text" << findLine << std::endl;
  38. return std::make_pair(lPos + line, text.size());
  39. }
  40. size_t getNumberOfLines(std::string text) {
  41. size_t pos = text.find("\r");
  42. size_t lines = 0;
  43. while(pos != std::string::npos) {
  44. lines++;
  45. pos = text.find("\r", pos + 1);
  46. }
  47. return lines;
  48. }
  49. #include <fstream>
  50. DocumentComponent::DocumentComponent(const float rawX, const float rawY, const float rawWidth, const float rawHeight, const int passedWindowWidth, const int passedWindowHeight) : MultiComponent(rawX, rawY, rawWidth, rawHeight, passedWindowWidth, passedWindowHeight) {
  51. //std::cout << "DocumentComponent::DocumentComponent" << std::endl;
  52. windowWidth = passedWindowWidth;
  53. windowHeight = passedWindowHeight;
  54. //std::cout << "DocumentComponent::DocumentComponent - window size: " << windowWidth << "x" << windowHeight << std::endl;
  55. std::shared_ptr<Component> rootComponent = std::make_shared<Component>();
  56. this->layers.push_back(rootComponent);
  57. x = rawX;
  58. y = rawY;
  59. width = rawWidth;
  60. height = rawHeight;
  61. if (height < 0) {
  62. std::cout << "DocumentComponent::DocumentComponent - height was less than zero" << std::endl;
  63. height = 0;
  64. }
  65. std::ifstream fin("res/ntr_bootstrap.js");
  66. std::stringstream buffer;
  67. buffer << fin.rdbuf();
  68. std::string bootstrap_js = buffer.str();
  69. if (bootstrap_js == "") {
  70. std::cout << "DocumentComponent::DocumentComponent - ERROR: Could not load res/ntr_bootstrap.js" << std::endl;
  71. } else {
  72. //std::cout << "DocumentComponent::DocumentComponent - Loaded [" << bootstrap_js << "] ntr_bootstrap.js\n";
  73. this->bootstrapScript = std::make_shared<JavaScript>();
  74. this->bootstrapScript->parse(bootstrap_js);
  75. }
  76. //std::cout << "DocumentComponent::DocumentComponent - our size" << static_cast<int>(width) << "x" << static_cast<int>(height) << std::endl;
  77. onMousemove=[this](int passedX, int passedY) {
  78. // set hover component
  79. static int lx = 0;
  80. static int ly = 0;
  81. //std::cout << "DocumentComponent::DocumentComponent:onMousemove - at " << passedX << "," << passedY << std::endl;
  82. if (lx == passedX && ly == passedY) {
  83. return;
  84. }
  85. lx = passedX;
  86. ly = passedY;
  87. //std::cout << "DocumentComponent::DocumentComponent:onMousemove - size " << this->windowWidth << "," << this->windowHeight << std::endl;
  88. //std::cout << "DocumentComponent::DocumentComponent:onMousemove - at " << passedX << "," << passedY << " scroll: " << (int)(((this->transformMatrix[13] / 2) - 1) * this->windowHeight) << std::endl;
  89. std::shared_ptr<Component> newHover = nullptr;
  90. // FIXME: we may need to iterator backwards here (top layer to back layer)
  91. for(auto layer: this->layers) {
  92. //this->hoverComponent = this->searchComponentTree(this->rootComponent, passedX, passedY + (((this->transformMatrix[13] / 2) - 1) * this->windowHeight));
  93. // apply scroll transformation
  94. std::shared_ptr<Component> found = this->searchComponentTree(layer, passedX, passedY + (((this->transformMatrix[13] / 2) - 1) * this->windowHeight));
  95. if (found) {
  96. newHover = found;
  97. break;
  98. }
  99. }
  100. // FIXME: no mouseOver or out?
  101. this->hoverComponent = newHover;
  102. if (this->hoverComponent) {
  103. //std::cout << "DocumentComponent::DocumentComponent:onMousemove - hovering over " << typeOfComponent(this->hoverComponent) << " component" << std::endl;
  104. if (this->hoverComponent->onMousemove) {
  105. // this could communicate the cursor to use
  106. this->hoverComponent->onMousemove(passedX, passedY);
  107. }
  108. if (this->hoverComponent->onClick || typeOfComponent(this->hoverComponent) == "button") {
  109. glfwSetCursor(this->win->window, this->win->cursorHand);
  110. } else {
  111. glfwSetCursor(this->win->window, this->win->cursorIbeam);
  112. }
  113. } else {
  114. glfwSetCursor(this->win->window, this->win->cursorArrow);
  115. }
  116. };
  117. onWheel=[this](int passedX, int passedY) {
  118. //std::cout << "DocumentComponent::DocumentComponent:::onWheel - scroll yDelta: " << passedY << std::endl;
  119. this->scrollY(passedY);
  120. };
  121. onMousedown=[this](int passedX, int passedY) {
  122. //std::cout << "document left press" << std::endl;
  123. if (this->hoverComponent) {
  124. // if we're hovering over a component that's not focused
  125. if (this->focusedComponent != this->hoverComponent) {
  126. // blur old component
  127. if (this->focusedComponent) {
  128. if (this->focusedComponent->onBlur) {
  129. this->focusedComponent->onBlur();
  130. }
  131. }
  132. // focus new component
  133. if (this->hoverComponent->onFocus) {
  134. this->hoverComponent->onFocus();
  135. }
  136. }
  137. // set hover component as focused
  138. this->focusedComponent = this->hoverComponent;
  139. if (this->focusedComponent->onMousedown) {
  140. //std::cout << "click event" << std::endl;
  141. this->focusedComponent->onMousedown(passedX, passedY);
  142. }
  143. }
  144. };
  145. onMouseup=[this](int passedX, int passedY) {
  146. //std::cout << "document left release" << std::endl;
  147. if (this->hoverComponent) {
  148. //std::cout << "DocumentComponent::DocumentComponent:onMouseup - hovering over " << typeOfComponent(this->hoverComponent) << " component" << std::endl;
  149. if (this->focusedComponent != this->hoverComponent) {
  150. // blur old component
  151. if (this->focusedComponent) {
  152. if (this->focusedComponent->onBlur) {
  153. this->focusedComponent->onBlur();
  154. }
  155. }
  156. // focus new component
  157. if (this->hoverComponent->onFocus) {
  158. this->hoverComponent->onFocus();
  159. }
  160. }
  161. this->focusedComponent = this->hoverComponent;
  162. //std::cout << "DocumentComponent::DocumentComponent:onMouseup - hovering over " << typeOfComponent(this->hoverComponent) << " component, focused on " << typeOfComponent(this->focusedComponent) << std::endl;
  163. if (this->focusedComponent->onMouseup) {
  164. //std::cout << "click event" << std::endl;
  165. this->focusedComponent->onMouseup(passedX, passedY);
  166. }
  167. if (this->focusedComponent && this->focusedComponent->onClick) {
  168. //std::cout << "click event" << std::endl;
  169. this->focusedComponent->onClick();
  170. }
  171. }
  172. };
  173. onKeyUp=[this](int key, int scancode, int action, int mods) {
  174. ///std::cout << "DocumentComponent::DocumentComponent:onKeyup - focus: " << typeOfComponent(this->focusedComponent) << " key: " << key << " scancode: " << scancode << " mods: " << mods << std::endl;
  175. InputComponent *focusedInputComponent = dynamic_cast<InputComponent*>(this->focusedComponent.get());
  176. if (focusedInputComponent) {
  177. //std::cout << "inputComponent is focused, key pressed " << key << " action: " <<action << std::endl;
  178. // FIXME: not going to get repeat events here...
  179. // action 1 is down, 0 is up, 2 is a repeat
  180. if (action == 0 || action == 2) {
  181. // key up
  182. // it's always uppercase...
  183. // FIXME: switch statement this
  184. if (key == GLFW_KEY_BACKSPACE) {
  185. focusedInputComponent->backSpace();
  186. return;
  187. }
  188. if (key == GLFW_KEY_UP) {
  189. focusedInputComponent->updateCursor(0, -1);
  190. return;
  191. }
  192. if (key == GLFW_KEY_DOWN) {
  193. focusedInputComponent->updateCursor(0, 1);
  194. return;
  195. }
  196. if (key == GLFW_KEY_LEFT) {
  197. focusedInputComponent->updateCursor(-1, 0);
  198. return;
  199. }
  200. if (key == GLFW_KEY_RIGHT) {
  201. focusedInputComponent->updateCursor(1, 0);
  202. return;
  203. }
  204. if (key == GLFW_KEY_ENTER) {
  205. //std::cout << "DocumentComponent::onKeyUp - enter!" << std::endl;
  206. if (focusedInputComponent->multiLine) {
  207. // harfbuzz or freetype2 (something?) doesn't like \n //focusedInputComponent->value += "\r";
  208. focusedInputComponent->addChar('\r');
  209. } else {
  210. std::cout << "should submit form!" << std::endl;
  211. if (focusedInputComponent->onEnter) {
  212. focusedInputComponent->onEnter(focusedInputComponent->getValue());
  213. }
  214. return;
  215. }
  216. } else {
  217. if (key < 256) {
  218. if (mods & GLFW_MOD_SHIFT) {
  219. // SHIFT
  220. if (key >= '1' && key <= '9') {
  221. key -= 16;
  222. }
  223. switch(key) {
  224. case GLFW_KEY_SLASH: key='?'; break;
  225. case GLFW_KEY_APOSTROPHE: key='"'; break;
  226. case GLFW_KEY_COMMA: key='<'; break;
  227. case GLFW_KEY_MINUS: key='_'; break;
  228. case GLFW_KEY_PERIOD: key='>'; break;
  229. case GLFW_KEY_SEMICOLON: key=':'; break;
  230. case GLFW_KEY_EQUAL: key='+'; break;
  231. case GLFW_KEY_LEFT_BRACKET: key='{'; break;
  232. case GLFW_KEY_BACKSLASH: key='|'; break;
  233. case GLFW_KEY_RIGHT_BRACKET: key='}'; break;
  234. case GLFW_KEY_GRAVE_ACCENT: key='~'; break;
  235. case GLFW_KEY_0: key=')'; break;
  236. default:
  237. // a key we don't care about atm
  238. break;
  239. }
  240. } else {
  241. // no shift or caplocks
  242. // basically: when SHIFT isn't pressed but key is in A-Z range, add ascii offset to make it lower case
  243. if (key >= 'A' && key <= 'Z') {
  244. key += 'a' - 'A';
  245. }
  246. }
  247. focusedInputComponent->addChar(key);
  248. } // otherwise I think it's some weird control char
  249. }
  250. }
  251. }
  252. //std::cout << "nothing focused " << GLFW_RELEASE << "==" << action << std::endl;
  253. // was press but we don't get those events here
  254. if (key == GLFW_KEY_Q && action == GLFW_RELEASE) {
  255. std::cout << "Q was pressed. Exiting." << std::endl;
  256. exit(0);
  257. }
  258. if (key == GLFW_KEY_E && action == GLFW_RELEASE) {
  259. printf("Printing NodeTree\n\n");
  260. printNode(this->domRootNode, 1);
  261. printf("\n\n");
  262. }
  263. if (key == GLFW_KEY_D && action == GLFW_RELEASE) {
  264. printf("Printing ComponentTree\n\n");
  265. Component::printComponentTree(this->layers[0], 0);
  266. printf("\n\n");
  267. }
  268. if (key == GLFW_KEY_F && action == GLFW_RELEASE) {
  269. printf("Printing UI ComponentTree\n\n");
  270. for(auto layer: this->win->ui->layers) {
  271. Component::printComponentTree(layer, 0);
  272. }
  273. printf("\n\n");
  274. }
  275. if (key == GLFW_KEY_V && action == GLFW_RELEASE) {
  276. printf("Printing JS Variables\n\n");
  277. //std::cout << "DocumentComponent::render - JS var state" << std::endl;
  278. for(auto it : this->mainScript->rootScope.locals.value) {
  279. std::cout << "[" << it.first << "=" << it.second << "]" << std::endl;
  280. }
  281. printf("\n\n");
  282. }
  283. if (key == GLFW_KEY_T && action == GLFW_RELEASE) {
  284. browser->NextTheme();
  285. }
  286. // FIXME: probably should just direct set the scroll
  287. // we should have a function for setting scrollY position
  288. if (key == GLFW_KEY_HOME && (action == GLFW_RELEASE || action == GLFW_REPEAT)) {
  289. std::cout << "Home is/was pressed. Scrolling up." << std::endl;
  290. this->scrollY(INT_MAX);
  291. }
  292. if (key == GLFW_KEY_END && (action == GLFW_RELEASE || action == GLFW_REPEAT)) {
  293. std::cout << "End is/was pressed. Scrolling down." << std::endl;
  294. this->scrollY(-INT_MAX);
  295. }
  296. if (key == GLFW_KEY_PAGE_UP && (action == GLFW_RELEASE || action == GLFW_REPEAT)) {
  297. std::cout << "PgUp is/was pressed. Scrolling up." << std::endl;
  298. this->scrollY(170);
  299. }
  300. if (key == GLFW_KEY_PAGE_DOWN && (action == GLFW_RELEASE || action == GLFW_REPEAT)) {
  301. std::cout << "PgDn is/was pressed. Scrolling down." << std::endl;
  302. this->scrollY(-170);
  303. }
  304. if ((key == GLFW_KEY_J || key == GLFW_KEY_UP) && (action == GLFW_RELEASE || action == GLFW_REPEAT)) {
  305. std::cout << "J is/was pressed. Scrolling up." << std::endl;
  306. this->scrollY(4);
  307. }
  308. if ((key == GLFW_KEY_K || key == GLFW_KEY_DOWN) && (action == GLFW_RELEASE || action == GLFW_REPEAT)) {
  309. std::cout << "K is/was pressed. Scrolling down." << std::endl;
  310. this->scrollY(-4);
  311. }
  312. };
  313. onKeyPress=[this](int key, int scancode, int action, int mods) {
  314. InputComponent *focusedInputComponent = dynamic_cast<InputComponent*>(this->focusedComponent.get());
  315. if (focusedInputComponent) {
  316. return;
  317. }
  318. std::cout << "DocumentComponent onKeyPresss - no input component focused" << std::endl;
  319. int yOffsetScroll = 1;
  320. if (key == GLFW_KEY_PAGE_UP && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
  321. std::cout << "PgUp is/was pressed. Scrolling down." << std::endl;
  322. this->scrollY(-yOffsetScroll*0.1);
  323. }
  324. if (key == GLFW_KEY_PAGE_DOWN && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
  325. std::cout << "PgDn is/was pressed. Scrolling up." << std::endl;
  326. this->scrollY(yOffsetScroll*0.1);
  327. }
  328. // FIXME Scrolling with this can scroll past boundary whereas the same doesn't happen with
  329. // scrolling wheel
  330. if (key == GLFW_KEY_J && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
  331. std::cout << "J is/was pressed. Scrolling down." << std::endl;
  332. this->scrollY(-yOffsetScroll*0.1);
  333. }
  334. // FIXME Scrolling with this can scroll past boundary whereas the same doesn't happen with
  335. // scrolling wheel
  336. if (key == GLFW_KEY_K && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
  337. std::cout << "K is/was pressed. Scrolling up." << std::endl;
  338. this->scrollY(yOffsetScroll*0.1);
  339. }
  340. };
  341. }
  342. void DocumentComponent::scrollY(int py) {
  343. //std::cout << "DocumentComponent::scrollY - adjusting Y by " << (py * 0.01) << std::endl;
  344. // we're going to reduce here, so we can get the full signal all the way here
  345. this->transformMatrix[13] -= py * 0.01;
  346. this->textureTransformMatrix[13] -= py * 0.01;
  347. //std::cout << "transformMatrix : " << this->transformMatrix[13] << std::endl;
  348. //std::cout << "textureTransformMatrix: " << this->textureTransformMatrix[13] << std::endl;
  349. // check bounds
  350. if (this->transformMatrix[13] < 2) {
  351. this->transformMatrix[13] = 2;
  352. }
  353. if (this->textureTransformMatrix[13] < 0) {
  354. this->textureTransformMatrix[13] = 0;
  355. }
  356. std::shared_ptr<Component> rootComponent = this->layers[0]; // we only have one layer (currently)
  357. // calculate scroll max by calculating how many screens are in the rootComponent's Height
  358. if (this->transformMatrix[13] > std::max( (rootComponent->height - rootComponent->y) / this->windowHeight * 2.0f, 2.0f)) {
  359. this->transformMatrix[13] = std::max( (rootComponent->height - rootComponent->y) / this->windowHeight * 2.0f, 2.0f);
  360. }
  361. if (this->textureTransformMatrix[13] > std::max( (rootComponent->height - rootComponent->y) / this->windowHeight * 2.0f, 2.0f) - 2.0) {
  362. this->textureTransformMatrix[13] = std::max( (rootComponent->height - rootComponent->y) / this->windowHeight * 2.0f, 2.0f) - 2.0;
  363. }
  364. this->transformMatrixDirty = true;
  365. this->win->renderDirty = true;
  366. }
  367. void deleteComponent(std::shared_ptr<Component> &component) {
  368. // delete all my child first
  369. for (std::shared_ptr<Component> child : component->children) {
  370. deleteComponent(child);
  371. }
  372. component->parent=nullptr;
  373. component->previous=nullptr;
  374. component->children.clear();
  375. component.reset();
  376. // now delete self
  377. }
  378. void deleteNode(std::shared_ptr<Node> node) {
  379. for (std::shared_ptr<Node> child : node->children) {
  380. deleteNode(child);
  381. }
  382. node->parent=nullptr;
  383. node->children.clear();
  384. node->component=nullptr; // disassociate component
  385. node.reset();
  386. }
  387. void DocumentComponent::setDOM(const std::shared_ptr<Node> rootNode) {
  388. std::shared_ptr<Component> rootComponent = this->layers[0]; // we only have one layer (currently)
  389. // reset rootComponent
  390. if (rootComponent) {
  391. deleteComponent(rootComponent);
  392. }
  393. if (domRootNode) {
  394. deleteNode(domRootNode);
  395. }
  396. // reset JS engine
  397. //mainScript.clear();
  398. mainScript = std::make_shared<JavaScript>();
  399. if (this->bootstrapScript) {
  400. mainScript->applyScope(this->bootstrapScript);
  401. mainScript->append(this->bootstrapScript);
  402. } else {
  403. std::cout << "DocumentComponent::setDOM - no bootstrap JS loaded\n";
  404. }
  405. // reset scroll position
  406. //transformMatrix[13] = 2;
  407. //transformMatrixDirty = true;
  408. this->scrollY(0);
  409. // new root component
  410. rootComponent = std::make_shared<Component>();
  411. rootComponent->name = "rootComponent of " + name;
  412. rootComponent->y = y;
  413. domRootNode = rootNode;
  414. //std::cout << "DocumentComponent::setDOM - printing nodes" << endl; printNode(domRootNode, 0);
  415. domDirty = true;
  416. }
  417. void DocumentComponent::render() {
  418. //std::cout << "DocumentComponent::render" << std::endl;
  419. std::shared_ptr<Component> rootComponent = this->layers[0]; // we only have one layer (currently)
  420. if (domDirty) {
  421. const std::clock_t begin = clock();
  422. createComponentTree(domRootNode, rootComponent);
  423. const std::clock_t end = clock();
  424. // root component here doesn't have any children...
  425. std::cout << "built & laid out document components in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
  426. //Component::printComponentTree(rootComponent, 0);
  427. domDirty = false;
  428. /*
  429. std::cout << "DocumentComponent::render - JS var state" << std::endl;
  430. for(auto it : this->mainScript->rootScope.variables) {
  431. std::cout << "[" << it.first << "=" << it.second << "]" << std::endl;
  432. }
  433. */
  434. //std::cout << "root Height: " << static_cast<int>(rootComponent->height) << " window Height: " << windowHeight << " y " << static_cast<int>(this->y) << std::endl;
  435. //scrollHeight = std::max(0, static_cast<int>(rootComponent->height - (windowHeight + (this->y * 2))));
  436. // FIXME recalculate both right?
  437. // recalculate scroll max by calculating how many screens are in the rootComponent's Height
  438. if (transformMatrix[13]>std::max((rootComponent->height)/(windowHeight)*2.0f, 2.0f)) {
  439. transformMatrix[13]=std::max((rootComponent->height)/(windowHeight)*2.0f, 2.0f);
  440. transformMatrixDirty = true;
  441. }
  442. // after we load in the document, allow scroll to work
  443. this->updateMouse();
  444. }
  445. // we have to do this each time because window resets it
  446. //if (transformMatrixDirty) {
  447. //const std::clock_t begin = clock();
  448. Shader *fontShader = this->win->shaderLoader.getShader(VertexShader("FontShader.vert"),
  449. FragmentShader("FontShader.frag"));
  450. GLint transformLocation = fontShader->uniform("transform");
  451. glUniformMatrix4fv(transformLocation, 1, GL_FALSE, transformMatrix);
  452. //const std::clock_t end = clock();
  453. //std::cout << "Updated font matrix in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
  454. Shader *textureShader = this->win->shaderLoader.getShader(VertexShader("TextureShader.vert"),
  455. FragmentShader("TextureShader.frag"));
  456. transformMatrixDirty = false;
  457. //}
  458. //std::cout << "DocumentComponent::render - renderDirty" << std::endl;
  459. textureShader->bind();
  460. GLint transformLocation2 = textureShader->uniform("transform");
  461. glUniformMatrix4fv(transformLocation2, 1, GL_FALSE, textureTransformMatrix);
  462. //std::cout << "DocumentComponent::render - start Box components" << std::endl;
  463. //renderBoxComponents(rootComponent);
  464. this->renderComponentType("input", rootComponent);
  465. this->renderComponentType("button", rootComponent);
  466. //std::cout << "DocumentComponent::render - end Box components" << std::endl;
  467. //textureShader->release(); // just set it to 0, bind is better call
  468. fontShader->bind();
  469. //std::cout << "DocumentComponent::render - start components" << std::endl;
  470. //renderComponents(rootComponent);
  471. this->renderComponentType("text", rootComponent);
  472. //std::cout << "DocumentComponent::render - end components" << std::endl;
  473. fontShader->release();
  474. }
  475. // create this component and all it's children
  476. void DocumentComponent::createComponentTree(const std::shared_ptr<Node> node, const std::shared_ptr<Component> &parentComponent) {
  477. std::shared_ptr<Component> component = componentBuilder.build(node, parentComponent, this->win, this);
  478. //std::cout << "DocumentComponent::createComponentTree" << std::endl;
  479. if (!component) {
  480. //std::cout << "DocumentComponent::createComponentTree - no component" << std::endl;
  481. return;
  482. }
  483. // don't build text node of button component because it'll mess with the picking
  484. if (typeOfComponent(component) == "button") {
  485. // if this button node has children, extract the text before discarding it
  486. if (node->children.size()) {
  487. TextNode *textNode = dynamic_cast<TextNode*>(node->children.front().get());
  488. if (textNode) {
  489. //std::cout << "Button text: " << textNode->text << std::endl;
  490. ButtonComponent *buttonComponent = dynamic_cast<ButtonComponent*>(component.get());
  491. if (buttonComponent) {
  492. // FIXME: strip out more than 1 new line
  493. std::string line(textNode->text);
  494. line.erase(line.find_last_not_of(" \t\n\r\f\v") + 1);
  495. buttonComponent->value = line;
  496. buttonComponent->resizeToTextSize();
  497. buttonComponent->updateText();
  498. }
  499. }
  500. }
  501. return;
  502. }
  503. if (node==domRootNode) {
  504. // if this is the root node
  505. component->reqWidth = windowWidth;
  506. component->reqHeight = windowHeight;
  507. }
  508. // create children elements
  509. for (std::shared_ptr<Node> child : node->children) {
  510. createComponentTree(child, component);
  511. }
  512. }
  513. // used for picking
  514. std::shared_ptr<Component> DocumentComponent::searchComponentTree(const std::shared_ptr<Component> &component, const int passedX, const int passedY) {
  515. // only get hits on leaves (and not to hit the rootComponent every time)
  516. if (component->children.empty()) {
  517. //std::cout << "DocumentComponent::searchComponentTree - component at " << static_cast<int>(component->x) << "," << static_cast<int>(component->y) << " size " << static_cast<int>(component->width) << "," << static_cast<int>(component->height) << std::endl;
  518. //std::cout << "DocumentComponent::searchComponentTree - y search: " << static_cast<int>(-component->y) << "<" << static_cast<int>(passedY) << "<" << static_cast<int>(-component->y + component->height) << std::endl;
  519. if (-component->y < passedY && -component->y + component->height > passedY) {
  520. //std::cout << "DocumentComponent::searchComponentTree - x search: " << static_cast<int>(component->x) << "<" << static_cast<int>(passedX) << "<" << static_cast<int>(component->x + component->width) << std::endl;
  521. if (component->x < passedX && component->x + component->width > passedX) {
  522. //std::cout << "DocumentComponent::searchComponentTree - hit " << typeOfComponent(component) << " name: " << component->name << std::endl;
  523. return component;
  524. }
  525. }
  526. } else {
  527. for (std::shared_ptr<Component> child : component->children) {
  528. std::shared_ptr<Component> found = searchComponentTree(child, passedX, passedY);
  529. if (found) {
  530. return found;
  531. }
  532. }
  533. }
  534. return nullptr;
  535. }
  536. // moving naviagtion closer to window, as window is now the owner of currentURL
  537. // preparation for multiple HTML documents
  538. void DocumentComponent::navTo(const std::string url) {
  539. logDebug() << "DocumentComponent::navTo(" << url << ")" << std::endl;
  540. this->currentURL = currentURL.merge(URL(url));
  541. logDebug() << "DocumentComponent::navTo - go to: " << currentURL << std::endl;
  542. //setWindowContent(currentURL);
  543. logDebug() << "main::setWindowContent - " << url << std::endl;
  544. // integrity check
  545. TabbedComponent *tabComponent = dynamic_cast<TabbedComponent*>(this->win->tabComponent.get());
  546. if (!tabComponent->tabs.size()) {
  547. std::cout << "DocumentComponent::navTo - There's a document when there's not tabs" << std::endl;
  548. }
  549. // download URL
  550. WebResource res = getWebResource(this->currentURL);
  551. this->handleResource(res, currentURL.toString());
  552. }
  553. void DocumentComponent::handleResource(WebResource &res, std::string url) {
  554. if (res.resourceType == ResourceType::INVALID) {
  555. logError() << "DocumentComponent::handleResource - Invalid resource type: " << res.raw << std::endl;
  556. std::shared_ptr<Node> rootNode = std::make_shared<Node>(NodeType::ROOT);
  557. std::shared_ptr<TagNode> tagNode = std::make_shared<TagNode>();
  558. tagNode->tag="p";
  559. // bind tag to root
  560. tagNode->parent = rootNode;
  561. rootNode->children.push_back(tagNode);
  562. std::shared_ptr<TextNode> textNode = std::make_shared<TextNode>();
  563. textNode->text = "Invalid Resource Type, HTTP Result Status: " + res.raw;
  564. // bind text to tag
  565. textNode->parent = tagNode;
  566. tagNode->children.push_back(textNode);
  567. // send NodeTree to window
  568. //this->win->setDOM(rootNode);
  569. this->setDOM(rootNode);
  570. }
  571. //std::cout << "body: " << res.raw << std::endl;
  572. //std::cout << "type: " << res.resourceType << std::endl;
  573. // parse HTML
  574. if (res.resourceType == ResourceType::HTML) {
  575. HTMLParser parser;
  576. const std::clock_t begin = clock();
  577. std::shared_ptr<Node> rootNode = parser.parse(res.raw);
  578. const std::clock_t end = clock();
  579. logDebug() << "DocumentComponent::handleResource - Parsed document in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
  580. // send NodeTree to window
  581. //this->win->setDOM(rootNode);
  582. //printNode(rootNode, 0);
  583. // we need a way to communicate with our tabComponent
  584. // maybe an event is best
  585. if (this->onBeforeLoad) {
  586. this->onBeforeLoad(url);
  587. }
  588. this->setDOM(rootNode);
  589. } else if (res.resourceType == ResourceType::TXT) {
  590. std::cout << "DocumentComponent::handleResource - Rendering text document" << std::endl;
  591. std::shared_ptr<Node> rootNode = std::make_shared<Node>(NodeType::ROOT);
  592. std::shared_ptr<TagNode> tagNode = std::make_shared<TagNode>();
  593. tagNode->tag = "p";
  594. // bind tag to root
  595. tagNode->parent = rootNode;
  596. rootNode->children.push_back(tagNode);
  597. std::shared_ptr<TextNode> textNode = std::make_shared<TextNode>();
  598. textNode->text = res.raw;
  599. // bind text to tag
  600. textNode->parent = tagNode;
  601. tagNode->children.push_back(textNode);
  602. // send NodeTree to window
  603. //this->win->setDOM(rootNode);
  604. this->setDOM(rootNode);
  605. } else {
  606. std::cout << "DocumentComponent::handleResource - I don't know how to render non-html files" << std::endl;
  607. }
  608. if (this->win) {
  609. this->win->renderDirty = true;
  610. }
  611. }