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 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. #include "DocumentComponent.h"
  2. #include <cmath>
  3. #include <iostream>
  4. #include "../../Log.h"
  5. #include "InputComponent.h"
  6. #include <ctime>
  7. #include "../opengl/Shader.h"
  8. #include "../opengl/ShaderLoader.h"
  9. void deleteComponent(std::shared_ptr<Component> &component);
  10. void deleteNode(std::shared_ptr<Node> node);
  11. 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) {
  12. //std::cout << "DocumentComponent::DocumentComponent" << std::endl;
  13. windowWidth = passedWindowWidth;
  14. windowHeight = passedWindowHeight;
  15. //std::cout << "DocumentComponent::DocumentComponent - window size: " << windowWidth << "x" << windowHeight << std::endl;
  16. x = rawX;
  17. y = rawY;
  18. width = rawWidth;
  19. height = rawHeight;
  20. if (height < 0) {
  21. std::cout << "DocumentComponent::DocumentComponent - height was less than zero" << std::endl;
  22. height = 0;
  23. }
  24. //std::cout << "DocumentComponent::DocumentComponent - our size" << static_cast<int>(width) << "x" << static_cast<int>(height) << std::endl;
  25. onMousemove=[this](int passedX, int passedY) {
  26. // set hover component
  27. static int lx = 0;
  28. static int ly = 0;
  29. //std::cout << "DocumentComponent::DocumentComponent:onMousemove - at " << passedX << "," << passedY << std::endl;
  30. if (lx == passedX && ly == passedY) {
  31. return;
  32. }
  33. lx = passedX;
  34. ly = passedY;
  35. //std::cout << "DocumentComponent::DocumentComponent:onMousemove - size " << this->windowWidth << "," << this->windowHeight << std::endl;
  36. //std::cout << "DocumentComponent::DocumentComponent:onMousemove - at " << passedX << "," << passedY << " scroll: " << (int)(((this->transformMatrix[13] / 2) - 1) * this->windowHeight) << std::endl;
  37. this->hoverComponent = this->searchComponentTree(this->rootComponent, passedX, passedY + (((this->transformMatrix[13] / 2) - 1) * this->windowHeight));
  38. if (this->hoverComponent) {
  39. //std::cout << "DocumentComponent::DocumentComponent:onMousemove - hovering over " << typeOfComponent(this->hoverComponent) << " component" << std::endl;
  40. if (this->hoverComponent->onMousemove) {
  41. // this could communicate the cursor to use
  42. this->hoverComponent->onMousemove(passedX, passedY);
  43. }
  44. if (this->hoverComponent->onClick) {
  45. glfwSetCursor(this->win->window, this->win->cursorHand);
  46. } else {
  47. glfwSetCursor(this->win->window, this->win->cursorIbeam);
  48. }
  49. } else {
  50. glfwSetCursor(this->win->window, this->win->cursorArrow);
  51. }
  52. };
  53. onWheel=[this](int passedX, int passedY) {
  54. //std::cout << "DocumentComponent::DocumentComponent:::onWheel - scroll yDelta: " << y << std::endl;
  55. //Component::printComponentTree(rootComponent, 0);
  56. double pY = passedY / 10.0;
  57. this->scrollY -= pY;
  58. if (this->scrollY < 0) {
  59. this->scrollY = 0;
  60. } else if (this->scrollY > this->scrollHeight) {
  61. this->scrollY = this->scrollHeight;
  62. }
  63. //std::cout << "scroll pos: " << scrollY << "/" << scrollHeight << std::endl;
  64. //std::cout << "y: " << static_cast<int>(this->y) << " - " << this->scrollY << std::endl;
  65. //std::cout << "root.y: " << static_cast<int>(rootComponent->y) << std::endl;
  66. //std::cout << "windowSize: " << windowWidth << "," << windowHeight << std::endl;
  67. // new system
  68. //std::cout << "DocumentComponent::DocumentComponent:::onWheel - old position: " << this->transformMatrix[13] << " adjustment:" << (-pY*0.1) << std::endl;
  69. this->transformMatrix[13] += -pY * 0.01;
  70. // 2.0 is one screen height
  71. // we draw from 0 downwards (y+), so can't scroll past our starting draw point
  72. if (this->transformMatrix[13] < 2) {
  73. this->transformMatrix[13] = 2;
  74. }
  75. // calculate scroll max by calculating how many screens are in the rootComponent's Height
  76. if (this->transformMatrix[13] > std::max( (this->rootComponent->height - this->rootComponent->y) / this->windowHeight * 2.0f, 2.0f)) {
  77. this->transformMatrix[13] = std::max( (this->rootComponent->height - this->rootComponent->y) / this->windowHeight * 2.0f, 2.0f);
  78. }
  79. this->transformMatrixDirty = true;
  80. /*
  81. // adjust root position
  82. rootComponent->y = this->y + this->scrollY;
  83. //std::cout << "now root.y: " << static_cast<int>(rootComponent->y) << std::endl;
  84. // reset root size
  85. rootComponent->windowWidth = windowWidth;
  86. rootComponent->windowHeight = windowHeight;
  87. //Component::printComponentTree(rootComponent, 0);
  88. // this takes so long, we may want to delay until the input is adjusted
  89. const std::clock_t begin = clock();
  90. rootComponent->layout(); // need to update the vertices
  91. //renderDirty = true;
  92. // should we mark win->renderDirty = true?
  93. const std::clock_t end = clock();
  94. std::cout << "Scrolled document in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
  95. */
  96. //rootComponent->y = this->y - this->scrollY;
  97. //std::cout << "after root.y: " << static_cast<int>(rootComponent->y) << std::endl;
  98. // don't need this - why not?
  99. //this->renderDirty = true;
  100. };
  101. onMousedown=[this](int passedX, int passedY) {
  102. //std::cout << "document left press" << std::endl;
  103. if (this->hoverComponent) {
  104. if (this->focusedComponent != this->hoverComponent) {
  105. // blur old component
  106. if (this->focusedComponent) {
  107. if (this->focusedComponent->onBlur) {
  108. this->focusedComponent->onBlur();
  109. }
  110. }
  111. // focus new component
  112. if (this->hoverComponent->onFocus) {
  113. this->hoverComponent->onFocus();
  114. }
  115. }
  116. this->focusedComponent = this->hoverComponent;
  117. if (this->focusedComponent->onMousedown) {
  118. //std::cout << "click event" << std::endl;
  119. this->focusedComponent->onMousedown(passedX, passedY);
  120. }
  121. }
  122. };
  123. onMouseup=[this](int passedX, int passedY) {
  124. //std::cout << "document left release" << std::endl;
  125. if (this->hoverComponent) {
  126. //std::cout << "DocumentComponent::DocumentComponent:onMouseup - hovering over " << typeOfComponent(this->hoverComponent) << " component" << std::endl;
  127. if (this->focusedComponent != this->hoverComponent) {
  128. // blur old component
  129. if (this->focusedComponent) {
  130. if (this->focusedComponent->onBlur) {
  131. this->focusedComponent->onBlur();
  132. }
  133. }
  134. // focus new component
  135. if (this->hoverComponent->onFocus) {
  136. this->hoverComponent->onFocus();
  137. }
  138. }
  139. this->focusedComponent = this->hoverComponent;
  140. //std::cout << "DocumentComponent::DocumentComponent:onMouseup - hovering over " << typeOfComponent(this->hoverComponent) << " component, focused on " << typeOfComponent(this->focusedComponent) << std::endl;
  141. if (this->focusedComponent->onMouseup) {
  142. //std::cout << "click event" << std::endl;
  143. this->focusedComponent->onMouseup(passedX, passedY);
  144. }
  145. if (this->focusedComponent->onClick) {
  146. //std::cout << "click event" << std::endl;
  147. this->focusedComponent->onClick();
  148. }
  149. }
  150. };
  151. onKeyup=[this](int key, int scancode, int action, int mods) {
  152. //std::cout << "DocumentComponent::DocumentComponent:onKeyup" << typeOfComponent(this->focusedComponent) << std::endl;
  153. InputComponent *inputComponent = dynamic_cast<InputComponent*>(this->focusedComponent.get());
  154. if (inputComponent) {
  155. //std::cout << "inputComponent is focused, key pressed " << key << " action: " <<action << std::endl;
  156. // action 1 is down, 0 is up, 2 is a repeat
  157. if (action == 0 || action == 2) {
  158. // key up
  159. // it's always uppercase...
  160. if (key == 259) {
  161. inputComponent->backSpace();
  162. } else if (key == 257) {
  163. std::cout << "enter!" << std::endl;
  164. } else {
  165. if (key < 256) {
  166. if (mods & GLFW_MOD_SHIFT) {
  167. // SHIFT
  168. if (key == GLFW_KEY_SLASH) key='?';
  169. if (key == GLFW_KEY_APOSTROPHE) key='"';
  170. if (key == GLFW_KEY_COMMA) key='<';
  171. if (key == GLFW_KEY_MINUS) key='_';
  172. if (key == GLFW_KEY_PERIOD) key='>';
  173. if (key == GLFW_KEY_SEMICOLON) key=':';
  174. if (key == GLFW_KEY_EQUAL) key='+';
  175. if (key == GLFW_KEY_LEFT_BRACKET) key='{';
  176. if (key == GLFW_KEY_BACKSLASH) key='|';
  177. if (key == GLFW_KEY_RIGHT_BRACKET) key='}';
  178. if (key == GLFW_KEY_GRAVE_ACCENT) key='~';
  179. } else {
  180. // no shift or caplocks
  181. // basically: when SHIFT isn't pressed but key is in A-Z range, add ascii offset to make it lower case
  182. if (key >= 'A' && key <= 'Z') {
  183. key += 'a' - 'A';
  184. }
  185. }
  186. inputComponent->addChar(key);
  187. } // otherwise I think it's some weird control char
  188. }
  189. }
  190. }
  191. };
  192. }
  193. void deleteComponent(std::shared_ptr<Component> &component) {
  194. // delete all my child first
  195. for (std::shared_ptr<Component> child : component->children) {
  196. deleteComponent(child);
  197. }
  198. component->parent=nullptr;
  199. component->previous=nullptr;
  200. component->children.clear();
  201. component.reset();
  202. // now delete self
  203. }
  204. void deleteNode(std::shared_ptr<Node> node) {
  205. for (std::shared_ptr<Node> child : node->children) {
  206. deleteNode(child);
  207. }
  208. node->parent=nullptr;
  209. node->children.clear();
  210. node->component=nullptr; // disassociate component
  211. node.reset();
  212. }
  213. void DocumentComponent::setDOM(const std::shared_ptr<Node> rootNode) {
  214. // reset rootComponent
  215. if (rootComponent) {
  216. deleteComponent(rootComponent);
  217. }
  218. if (domRootNode) {
  219. deleteNode(domRootNode);
  220. }
  221. // reset scroll position
  222. transformMatrix[13] = 2;
  223. transformMatrixDirty = true;
  224. // new root component
  225. rootComponent = std::make_shared<Component>();
  226. rootComponent->name = "rootComponent of " + name;
  227. rootComponent->y = y;
  228. domRootNode = rootNode;
  229. domDirty = true;
  230. }
  231. void DocumentComponent::render() {
  232. //std::cout << "DocumentComponent::render" << std::endl;
  233. if (domDirty) {
  234. const std::clock_t begin = clock();
  235. createComponentTree(domRootNode, rootComponent);
  236. const std::clock_t end = clock();
  237. // root component here doesn't have any children...
  238. std::cout << "built & laid out document components in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
  239. //Component::printComponentTree(rootComponent, 0);
  240. domDirty = false;
  241. //std::cout << "root Height: " << static_cast<int>(rootComponent->height) << " window Height: " << windowHeight << " y " << static_cast<int>(this->y) << std::endl;
  242. scrollHeight = std::max(0, static_cast<int>(rootComponent->height - (windowHeight + (this->y * 2))));
  243. // recalculate scroll max by calculating how many screens are in the rootComponent's Height
  244. if (transformMatrix[13]>std::max((rootComponent->height)/(windowHeight)*2.0f, 2.0f)) {
  245. transformMatrix[13]=std::max((rootComponent->height)/(windowHeight)*2.0f, 2.0f);
  246. transformMatrixDirty = true;
  247. }
  248. }
  249. // we have to do this each time
  250. // because window resets it
  251. //if (transformMatrixDirty) {
  252. //const std::clock_t begin = clock();
  253. Shader *fontShader = ShaderLoader::getShader(VertexShader("FontShader.vert"),
  254. FragmentShader("FontShader.frag"));
  255. GLint transformLocation = fontShader->uniform("transform");
  256. glUniformMatrix4fv(transformLocation, 1, GL_FALSE, transformMatrix);
  257. //const std::clock_t end = clock();
  258. //std::cout << "Updated font matrix in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
  259. Shader *textureShader = ShaderLoader::getShader(VertexShader("TextureShader.vert"),
  260. FragmentShader("TextureShader.frag"));
  261. GLint transformLocation2 = textureShader->uniform("transform");
  262. glUniformMatrix4fv(transformLocation2, 1, GL_FALSE, transformMatrix);
  263. transformMatrixDirty = false;
  264. //}
  265. //std::cout << "DocumentComponent::render - renderDirty" << std::endl;
  266. textureShader->bind();
  267. renderBoxComponents(rootComponent);
  268. textureShader->release();
  269. fontShader->bind();
  270. renderComponents(rootComponent);
  271. fontShader->release();
  272. }
  273. // create this component and all it's children
  274. void DocumentComponent::createComponentTree(const std::shared_ptr<Node> node, const std::shared_ptr<Component> &parentComponent) {
  275. std::shared_ptr<Component> component = componentBuilder.build(node, parentComponent, windowWidth, windowHeight);
  276. //std::cout << "DocumentComponent::createComponentTree" << std::endl;
  277. if (!component) {
  278. //std::cout << "DocumentComponent::createComponentTree - no component" << std::endl;
  279. return;
  280. }
  281. if (node==domRootNode) {
  282. // if this is the root node
  283. component->reqWidth = windowWidth;
  284. component->reqHeight = windowHeight;
  285. }
  286. // create children elements
  287. for (std::shared_ptr<Node> child : node->children) {
  288. createComponentTree(child, component);
  289. }
  290. }
  291. // used for picking
  292. std::shared_ptr<Component> DocumentComponent::searchComponentTree(const std::shared_ptr<Component> &component, const int passedX, const int passedY) {
  293. if (component->children.empty()) {
  294. //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;
  295. //std::cout << "DocumentComponent::searchComponentTree - y search: " << static_cast<int>(-component->y) << "<" << static_cast<int>(passedY) << "<" << static_cast<int>(-component->y + component->height) << std::endl;
  296. if (-component->y < passedY && -component->y + component->height > passedY) {
  297. //std::cout << "DocumentComponent::searchComponentTree - x search: " << static_cast<int>(component->x) << "<" << static_cast<int>(passedX) << "<" << static_cast<int>(component->x + component->width) << std::endl;
  298. if (component->x < passedX && component->x + component->width > passedX) {
  299. //std::cout << "DocumentComponent::searchComponentTree - hit " << typeOfComponent(component) << std::endl;
  300. return component;
  301. }
  302. }
  303. }
  304. else {
  305. for (std::shared_ptr<Component> child : component->children) {
  306. std::shared_ptr<Component> found = searchComponentTree(child, passedX, passedY);
  307. if (found) {
  308. return found;
  309. }
  310. }
  311. }
  312. return nullptr;
  313. }
  314. // moving naviagtion closer to window, as window is now the owner of currentURL
  315. // preparation for multiple HTML documents
  316. void DocumentComponent::navTo(const std::string url) {
  317. logDebug() << "DocumentComponent::navTo(" << url << ")" << std::endl;
  318. currentURL = currentURL.merge(URL(url));
  319. logDebug() << "DocumentComponent::navTo - go to: " << currentURL << std::endl;
  320. setWindowContent(currentURL);
  321. }