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

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