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.

Window.cpp 43KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994
  1. #include "../../URL.h"
  2. #include "Window.h"
  3. #include "../../html/TagNode.h"
  4. #include "../../html/TextNode.h"
  5. #include "../../networking/HTTPRequest.h"
  6. #include "../../html/HTMLParser.h"
  7. #include "../../StringUtils.h"
  8. #include "../../URL.h"
  9. #include "../../Log.h"
  10. #include "../components/BoxComponent.h"
  11. #include "../components/AnimeComponent.h"
  12. #include "../components/DocumentComponent.h"
  13. #include "../components/TabbedComponent.h"
  14. #include "../components/TextComponent.h"
  15. #include "../components/TabbedComponent.h"
  16. #include "../components/InputComponent.h"
  17. #include "../opengl/Shader.h"
  18. #include "../opengl/ShaderLoader.h"
  19. #include <cmath>
  20. #include <ctime>
  21. #include <iostream>
  22. Window::~Window() {
  23. glfwTerminate();
  24. }
  25. bool Window::init() {
  26. if (!initGLFW()) {
  27. return false;
  28. }
  29. if (!initGLEW()) {
  30. return false;
  31. }
  32. initGL();
  33. GLenum glErr=glGetError();
  34. if(glErr != GL_NO_ERROR) {
  35. std::cout << "window::init - post initGL - not ok: " << glErr << std::endl;
  36. }
  37. // set proper scroll position
  38. transformMatrix[13] = 2;
  39. transformMatrixDirty = true;
  40. // configure root component
  41. //rootComponent = std::make_shared<Component>();
  42. rootComponent->name = "rootComponent";
  43. //rootComponent->y = 0;
  44. // set up our UI with a new window
  45. // (re)build UI (top to bottom)
  46. //std::cout << "Window::setDOM window size: " << windowWidth << "x" << windowHeight << std::endl;
  47. // layout back to front
  48. // 4 multigroups
  49. // 1 -> mascot
  50. // 2 -> tabbed
  51. // 3 -> box
  52. // 4 -> input
  53. //Mascot
  54. std::shared_ptr<AnimeComponent> mascot = std::make_unique<AnimeComponent>(windowWidth * 0.75f, 0.0f, 512.0f, 512.0f, windowWidth, windowHeight);
  55. mascot->uiControl.x = { 75, 0 }; // 75% width
  56. mascot->uiControl.y = { 0, 0 }; // 0px
  57. mascot->uiControl.w = { 0, 512 }; // 512px
  58. mascot->uiControl.h = { 0, 512 }; // 512px
  59. mascot->boundToPage = false;
  60. mascot->isPickable = false;
  61. mascot->name = "mascot";
  62. // add it to our components
  63. mascot->setParent(rootComponent);
  64. rootComponent->children.push_back(mascot);
  65. // create tabbed component
  66. // doc text is still showing up ontop of buttons
  67. std::shared_ptr<TabbedComponent> tabbedComponent = std::make_shared<TabbedComponent>(0, 0, static_cast<float>(windowWidth), static_cast<float>(windowHeight - 64), windowWidth, windowHeight);
  68. tabbedComponent->name = "tabbedComponent";
  69. tabbedComponent->y = -64;
  70. tabbedComponent->uiControl.x = { 0 , 0 }; // 0
  71. tabbedComponent->uiControl.y = { 0 , -64 }; // -64px
  72. tabbedComponent->uiControl.w = { 100, 0 }; // 100%
  73. tabbedComponent->uiControl.h = { 100, -64 }; // 100% - 64px
  74. tabbedComponent->boundToPage = false;
  75. tabbedComponent->win = this;
  76. // add it to our components
  77. tabbedComponent->setParent(rootComponent);
  78. rootComponent->children.push_back(tabbedComponent);
  79. // Nav background
  80. std::shared_ptr<BoxComponent> navBackground = std::make_unique<BoxComponent>(0.0f, windowHeight - 64.0f, windowWidth, 64.0f, 0x888888FF, windowWidth, windowHeight);
  81. //navBackground->y = -64;
  82. navBackground->uiControl.y = { 100, -64 }; // -64px
  83. navBackground->uiControl.w = { 100, 0 }; // 100%
  84. navBackground->uiControl.h = { 0, 64 }; // 64px
  85. navBackground->isPickable = false;
  86. navBackground->name = "navBackground";
  87. navBackground->boundToPage = false;
  88. // add it to our components
  89. navBackground->setParent(rootComponent);
  90. rootComponent->children.push_back(navBackground);
  91. std::shared_ptr<BoxComponent> navBackButton = std::make_unique<BoxComponent>(32.0f, windowHeight - 48.0f, 32.0f, 32.0f, 0x000000FF, windowWidth, windowHeight);
  92. navBackButton->uiControl.x = { 0, 32 }; // 32px
  93. navBackButton->uiControl.y = { 100, -48 }; // top - 48px
  94. navBackButton->uiControl.w = { 0, 32 }; // 32px
  95. navBackButton->uiControl.h = { 0, 32 }; // 32px
  96. navBackButton->onClick=[this]() {
  97. //std::cout << "Back" << std::endl;
  98. TabbedComponent *pTabComponent = dynamic_cast<TabbedComponent*>(this->tabComponent.get());
  99. if (pTabComponent) {
  100. if (pTabComponent->selectedTabId) {
  101. pTabComponent->selectedTab->get()->history->back();
  102. }
  103. }
  104. };
  105. navBackButton->onMouseover = [navBackButton, this]() {
  106. //std::cout << "navBackButton->onMouseover" << std::endl;
  107. navBackButton->changeColor(0x880000FF);
  108. this->renderDirty = true;
  109. };
  110. navBackButton->onMouseout = [navBackButton, this]() {
  111. //std::cout << "navBackButton->onMouseout" << std::endl;
  112. navBackButton->changeColor(0x000000FF);
  113. this->renderDirty = true;
  114. };
  115. navBackButton->name = "navBackButton";
  116. navBackButton->boundToPage = false;
  117. // add it to our components
  118. navBackButton->setParent(rootComponent);
  119. rootComponent->children.push_back(navBackButton);
  120. std::shared_ptr<BoxComponent> navForwardButton = std::make_unique<BoxComponent>(74.0f, windowHeight - 48.0f, 32.0f, 32.0f, 0x000000FF, windowWidth, windowHeight);
  121. navForwardButton->uiControl.x = { 0, 74 }; // 74px
  122. navForwardButton->uiControl.y = { 100, -48 }; // top - 48px
  123. navForwardButton->uiControl.w = { 0, 32 }; // 32px
  124. navForwardButton->uiControl.h = { 0, 32 }; // 32px
  125. navForwardButton->onClick=[this]() {
  126. //std::cout << "Forward" << std::endl;
  127. TabbedComponent *pTabComponent = dynamic_cast<TabbedComponent*>(this->tabComponent.get());
  128. if (pTabComponent) {
  129. if (pTabComponent->selectedTabId) {
  130. pTabComponent->selectedTab->get()->history->forward();
  131. }
  132. }
  133. };
  134. navForwardButton->onMouseover = [navForwardButton, this]() {
  135. //std::cout << "navForwardButton->onMouseover" << std::endl;
  136. navForwardButton->changeColor(0x008800FF);
  137. this->renderDirty = true;
  138. };
  139. navForwardButton->onMouseout = [navForwardButton, this]() {
  140. //std::cout << "navForwardButton->onMouseout" << std::endl;
  141. navForwardButton->changeColor(0x000000FF);
  142. this->renderDirty = true;
  143. };
  144. navForwardButton->name = "navForwardButton";
  145. navForwardButton->boundToPage = false;
  146. // add it to our components
  147. //navForwardButton->parent = rootComponent;
  148. navForwardButton->setParent(rootComponent);
  149. rootComponent->children.push_back(navForwardButton);
  150. std::shared_ptr<BoxComponent> navRefreshButton = std::make_unique<BoxComponent>(116.0f, windowHeight - 48.0f, 32.0f, 32.0f, 0x000000FF, windowWidth, windowHeight);
  151. navRefreshButton->uiControl.x = { 0, 116 }; // 116px
  152. navRefreshButton->uiControl.y = { 100, -48 }; // top - 48px
  153. navRefreshButton->uiControl.w = { 0, 32 }; // 32px
  154. navRefreshButton->uiControl.h = { 0, 32 }; // 32px
  155. navRefreshButton->name = "navRefreshButton";
  156. navRefreshButton->boundToPage = false;
  157. navRefreshButton->onClick=[this]() {
  158. std::cout << "Refreshing " << this->currentURL << std::endl;
  159. this->navTo(this->currentURL.toString());
  160. };
  161. navRefreshButton->onMouseover = [navRefreshButton, this]() {
  162. //std::cout << "navRefreshButton->onMouseover" << std::endl;
  163. navRefreshButton->changeColor(0x000088FF);
  164. this->renderDirty = true;
  165. };
  166. navRefreshButton->onMouseout = [navRefreshButton, this]() {
  167. //std::cout << "navRefreshButton->onMouseout" << std::endl;
  168. navRefreshButton->changeColor(0x000000FF);
  169. this->renderDirty = true;
  170. }; // add it to our components
  171. navRefreshButton->setParent(rootComponent);
  172. rootComponent->children.push_back(navRefreshButton);
  173. // Address Bar
  174. std::shared_ptr<InputComponent> navAddressBar = std::make_unique<InputComponent>(192.0f, windowHeight - 48.0f, windowWidth - 384.0f, 24.0f, windowWidth, windowHeight);
  175. // add it to our components
  176. navAddressBar->x = 192.0f;
  177. navAddressBar->y = windowHeight-48.0f;
  178. navAddressBar->uiControl.x = { 0, 192 }; // 192px
  179. navAddressBar->uiControl.y = { 100, -48 }; // top - 48px
  180. navAddressBar->uiControl.w = { 100, -384 }; // w - 384px
  181. navAddressBar->uiControl.h = { 0, 24 }; // 24px
  182. navAddressBar->name = "navAddressBar";
  183. navAddressBar->boundToPage = false;
  184. navAddressBar->win = this;
  185. //navAddressBar->y = -48; // this works but breaks picking
  186. navAddressBar->value = currentURL.toString();
  187. navAddressBar->updateText();
  188. navAddressBar->onEnter=[this](std::string value) {
  189. std::cout << "navAddressBar::onEnter got " << value << std::endl;
  190. this->navTo(value);
  191. };
  192. this->addressComponent = navAddressBar;
  193. // we do this to prevent flashing
  194. render();
  195. renderDirty = true;
  196. //std::cout << "placing inputComponent at " << static_cast<int>(navAddressBar->x) << "," << static_cast<int>(navAddressBar->y) << std::endl;
  197. navAddressBar->setParent(rootComponent);
  198. rootComponent->children.push_back(navAddressBar);
  199. /*
  200. // textTest
  201. // TextComponent(const std::string &rawText, const int rawX, const int rawY, const unsigned int size, const bool bolded, const unsigned int hexColor, const int passedWindowWidth, const int passedWindowHeight);
  202. std::shared_ptr<TextComponent> textTest = std::make_unique<TextComponent>("Test", -99, -99, 12, false, 0x000000ff, windowWidth, windowHeight);
  203. textTest->x=192.0f;
  204. textTest->y=-64.0f;
  205. textTest->isPickable = false;
  206. // add it to our components
  207. textTest->parent = rootComponent;
  208. rootComponent->children.push_back(textTest);
  209. // we really don't want to layout because that's moves us relative to parent
  210. // and this is static layout we want, not a free flow
  211. //textTest->layout(); // to rasterize
  212. textTest->resize(windowWidth, windowHeight);
  213. */
  214. // set tab component
  215. this->tabComponent = tabbedComponent;
  216. return true;
  217. }
  218. bool Window::initGLFW() {
  219. if (!glfwInit()) {
  220. std::cout << "Could not initialize GLFW" << std::endl;
  221. return false;
  222. }
  223. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  224. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
  225. glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
  226. glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  227. //glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
  228. glfwSetErrorCallback([](int error, const char* description) {
  229. std::cout << "glfw error [" << error << "]:" << description << std::endl;
  230. });
  231. window = glfwCreateWindow(windowWidth, windowHeight, "NetRunner", nullptr, nullptr);
  232. if (!window) {
  233. glfwTerminate();
  234. std::cout << "Could not create window" << std::endl;
  235. return false;
  236. }
  237. // replace first parameter of all these callbacks with our window object instead of a GLFWwindow
  238. glfwSetWindowUserPointer(window, this);
  239. // set window w/h
  240. //glfwGetFramebufferSize(window, &windowWidth, &windowHeight); // in pixels
  241. glfwGetWindowSize(window, &windowWidth, &windowHeight); // use screen coordinates (not pixels) more retina friendly
  242. // set up event callbacks
  243. glfwSetFramebufferSizeCallback(window, [](GLFWwindow *win, int width, int height) {
  244. glViewport(0, 0, width, height);
  245. });
  246. glfwSetWindowSizeCallback(window, [](GLFWwindow *win, int width, int height) {
  247. Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
  248. thiz->onResize(width, height);
  249. });
  250. cursorHand = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
  251. cursorArrow = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
  252. cursorIbeam = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
  253. glfwSetCursorPosCallback(window, [](GLFWwindow *win, double xPos, double yPos) {
  254. Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
  255. thiz->cursorX = xPos;
  256. thiz->cursorY = yPos;
  257. //std::cout << "Window::Window:onMousemove - at " << static_cast<int>(xPos) << "," << static_cast<int>(yPos) << std::endl;
  258. if (xPos < 0 || yPos < 0) return;
  259. if (xPos > thiz->windowWidth || yPos > thiz->windowHeight) return;
  260. // p. much worthless on double
  261. /*
  262. static double lx = 0;
  263. static double ly = 0;
  264. if (lx == xPos && ly == yPos) {
  265. return;
  266. }
  267. lx = xPos;
  268. ly = yPos;
  269. std::cout << "Window::Window:onMousemove - noCache" << std::endl;
  270. */
  271. //std::cout << "Window::Window:onMousemove - begin search" << std::endl;
  272. //std::cout << "Window::Window:onMousemove - windowHeight: " << thiz->windowHeight << " cursorY: " << thiz->cursorY << " scrollY: " << thiz->transformMatrix[13] << std::endl;
  273. std::shared_ptr<Component> newHover = thiz->searchComponentTree(thiz->rootComponent, thiz->cursorX, (thiz->windowHeight - thiz->cursorY) + ((-thiz->transformMatrix[13] / 2) * thiz->windowHeight));
  274. if (newHover != thiz->hoverComponent) {
  275. if (thiz->hoverComponent && thiz->hoverComponent->onMouseout) {
  276. thiz->hoverComponent->onMouseout();
  277. }
  278. if (newHover && newHover->onMouseover) {
  279. newHover->onMouseover();
  280. }
  281. thiz->hoverComponent = newHover;
  282. }
  283. if (thiz->hoverComponent) {
  284. //std::cout << "Window::Window:onMousemove - hover " << typeOfComponent(thiz->hoverComponent) << std::endl;
  285. if (thiz->hoverComponent->onClick) {
  286. glfwSetCursor(thiz->window, thiz->cursorHand);
  287. } else {
  288. TextComponent *textComponent = dynamic_cast<TextComponent*>(thiz->hoverComponent.get());
  289. InputComponent *inputComponent = dynamic_cast<InputComponent*>(thiz->hoverComponent.get());
  290. if (textComponent || inputComponent) {
  291. glfwSetCursor(thiz->window, thiz->cursorIbeam);
  292. }
  293. // otherwise we could be an Box/Anime or Document Component
  294. }
  295. if (thiz->hoverComponent->onMousemove) {
  296. // this could communicate the cursor to use
  297. thiz->hoverComponent->onMousemove(thiz->cursorX, thiz->cursorY);
  298. }
  299. } else {
  300. glfwSetCursor(thiz->window, thiz->cursorArrow);
  301. }
  302. });
  303. glfwSetScrollCallback(window, [](GLFWwindow *win, double xOffset, double yOffset) {
  304. Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
  305. // yOffset is a delta vector
  306. thiz->transformMatrix[13] += -yOffset * 0.1;
  307. if (thiz->hoverComponent) {
  308. //std::cout << "scroll - hovering over a component" << std::endl;
  309. if (thiz->hoverComponent->onWheel) {
  310. //std::cout << "scroll - hovering over a scrollabel component" << std::endl;
  311. thiz->hoverComponent->onWheel(xOffset * 100, yOffset * 100);
  312. }
  313. }
  314. // 2.0 is one screen height
  315. // we draw from 0 downwards (y+), so can't scroll past our starting draw point
  316. if (thiz->transformMatrix[13] < 2) {
  317. thiz->transformMatrix[13] = 2;
  318. }
  319. // calculate scroll max by calculating how many screens are in the rootComponent's Height
  320. if (thiz->transformMatrix[13] > std::max( thiz->rootComponent->height / thiz->windowHeight * 2.0f, 2.0f)) {
  321. thiz->transformMatrix[13] = std::max( thiz->rootComponent->height / thiz->windowHeight * 2.0f, 2.0f);
  322. }
  323. //std::cout << "scroll y is at " << thiz->transformMatrix[13] << "/" << static_cast<int>((thiz->transformMatrix[13]*10000) << std::endl;
  324. thiz->transformMatrixDirty = true;
  325. });
  326. glfwSetMouseButtonCallback(window, [](GLFWwindow *win, int button, int action, int mods) {
  327. Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
  328. if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
  329. //std::cout << "left press" << std::endl;
  330. if (thiz->hoverComponent) {
  331. //std::cout << "focus event" << std::endl;
  332. if (thiz->focusedComponent != thiz->hoverComponent) {
  333. // blur old component
  334. if (thiz->focusedComponent) {
  335. if (thiz->focusedComponent->onBlur) {
  336. thiz->focusedComponent->onBlur();
  337. }
  338. }
  339. // focus new component
  340. if (thiz->hoverComponent->onFocus) {
  341. thiz->hoverComponent->onFocus();
  342. }
  343. }
  344. thiz->focusedComponent = thiz->hoverComponent;
  345. if (thiz->focusedComponent->onMousedown) {
  346. //std::cout << "click event" << std::endl;
  347. thiz->focusedComponent->onMousedown(thiz->cursorX, thiz->cursorY);
  348. }
  349. }
  350. }
  351. if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE) {
  352. //std::cout << "left release" << std::endl;
  353. if (thiz->hoverComponent) {
  354. //std::cout << "focus event" << std::endl;
  355. if (thiz->focusedComponent != thiz->hoverComponent) {
  356. // blur old component
  357. if (thiz->focusedComponent) {
  358. if (thiz->focusedComponent->onBlur) {
  359. thiz->focusedComponent->onBlur();
  360. }
  361. }
  362. // focus new component
  363. if (thiz->hoverComponent->onFocus) {
  364. thiz->hoverComponent->onFocus();
  365. }
  366. }
  367. thiz->focusedComponent = thiz->hoverComponent;
  368. /*
  369. InputComponent *inputComponent = dynamic_cast<InputComponent*>(thiz->focusedComponent.get());
  370. if (inputComponent) {
  371. std::cout << "inputComponent focus" << std::endl;
  372. }
  373. */
  374. if (thiz->focusedComponent && thiz->focusedComponent->onMouseup) {
  375. //std::cout << "click event" << std::endl;
  376. thiz->focusedComponent->onMouseup(thiz->cursorX, thiz->cursorY);
  377. }
  378. if (thiz->focusedComponent && thiz->focusedComponent->onClick) {
  379. //std::cout << "click event" << std::endl;
  380. thiz->focusedComponent->onClick();
  381. }
  382. }
  383. }
  384. });
  385. // works with utf-32 but we'll lkeep the low level for now
  386. /*
  387. glfwSetCharCallback(window, [](GLFWwindow* win, unsigned int codepoint) {
  388. Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
  389. std::cout << "Window::glfwSetCharCallback - codepoint: " << codepoint << std::endl;
  390. });
  391. */
  392. glfwSetKeyCallback(window, [](GLFWwindow *win, int key, int scancode, int action, int mods) {
  393. Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
  394. // we're focused on something
  395. //std::cout << "glfwSetKeyCallback" << std::endl;
  396. if (thiz->focusedComponent) {
  397. //std::cout << "glfwSetKeyCallback - focused on " << typeOfComponent(thiz->focusedComponent) << std::endl;
  398. TabbedComponent *p_tabComponent = dynamic_cast<TabbedComponent*>(thiz->focusedComponent.get());
  399. if (p_tabComponent) {
  400. // repeat or key up
  401. if (action == 2 || action == 0) {
  402. if (p_tabComponent->onKeyPress) {
  403. p_tabComponent->onKeyPress(key, scancode, action, mods);
  404. }
  405. if (action == 0) {
  406. if (p_tabComponent->onKeyup) {
  407. p_tabComponent->onKeyup(key, scancode, action, mods);
  408. }
  409. }
  410. }
  411. return;
  412. }
  413. DocumentComponent *docComponent = dynamic_cast<DocumentComponent*>(thiz->focusedComponent.get());
  414. if (docComponent) {
  415. if (action == 0) {
  416. if (docComponent->onKeyup) {
  417. docComponent->onKeyup(key, scancode, action, mods);
  418. }
  419. }
  420. return;
  421. }
  422. InputComponent *inputComponent = dynamic_cast<InputComponent*>(thiz->focusedComponent.get());
  423. if (inputComponent) {
  424. //std::cout << "inputComponent is focsued, key pressed " << key << " action: " <<action << std::endl;
  425. // action 1 is down, 0 is up, 2 is a repeat
  426. if (action == 0 || action == 2) {
  427. // key up
  428. // it's always uppercase...
  429. if (key == 259) {
  430. inputComponent->backSpace();
  431. } else if (key == 257) {
  432. std::cout << "enter!" << std::endl;
  433. if (inputComponent->onEnter) {
  434. inputComponent->onEnter(inputComponent->value);
  435. }
  436. } else {
  437. if (key < 256) {
  438. if (mods & GLFW_MOD_SHIFT) {
  439. // SHIFT
  440. if (key == GLFW_KEY_SLASH) key='?';
  441. if (key == GLFW_KEY_APOSTROPHE) key='"';
  442. if (key == GLFW_KEY_COMMA) key='<';
  443. if (key == GLFW_KEY_MINUS) key='_';
  444. if (key == GLFW_KEY_PERIOD) key='>';
  445. if (key == GLFW_KEY_SEMICOLON) key=':';
  446. if (key == GLFW_KEY_EQUAL) key='+';
  447. if (key == GLFW_KEY_LEFT_BRACKET) key='{';
  448. if (key == GLFW_KEY_BACKSLASH) key='|';
  449. if (key == GLFW_KEY_RIGHT_BRACKET) key='}';
  450. if (key == GLFW_KEY_GRAVE_ACCENT) key='~';
  451. } else {
  452. // no shift or caplocks
  453. // basically: when SHIFT isn't pressed but key is in A-Z range, add ascii offset to make it lower case
  454. if (key >= 'A' && key <= 'Z') {
  455. key += 'a' - 'A';
  456. }
  457. }
  458. inputComponent->addChar(key);
  459. } // otherwise I think it's some weird control char
  460. }
  461. }
  462. return;
  463. }
  464. }
  465. switch (key) {
  466. case GLFW_KEY_1:
  467. case GLFW_KEY_2:
  468. case GLFW_KEY_3:
  469. case GLFW_KEY_4:
  470. case GLFW_KEY_5:
  471. case GLFW_KEY_6:
  472. case GLFW_KEY_7:
  473. case GLFW_KEY_8:
  474. case GLFW_KEY_9:
  475. case GLFW_KEY_0:
  476. std::cout << "Key was pressed: " << key << std::endl;
  477. //thiz->transformMatrixIndex = key - GLFW_KEY_0;
  478. //std::cout << thiz->transformMatrixIndex << std::endl;
  479. break;
  480. default:
  481. break;
  482. }
  483. if (key == GLFW_KEY_Q && action == GLFW_PRESS) {
  484. std::cout << "Q was pressed. Exiting." << std::endl;
  485. exit(0);
  486. }
  487. if (key == GLFW_KEY_E && action == GLFW_RELEASE) {
  488. printf("Printing NodeTree\n\n");
  489. TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
  490. if (p_TabComponent) {
  491. DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
  492. if (p_DocComponent) {
  493. printNode(p_DocComponent->domRootNode, 1);
  494. }
  495. }
  496. printf("\n\n");
  497. }
  498. if (key == GLFW_KEY_D && action == GLFW_RELEASE) {
  499. printf("Printing ComponentTree\n\n");
  500. TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
  501. if (p_TabComponent) {
  502. DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
  503. if (p_DocComponent) {
  504. Component::printComponentTree(p_DocComponent->rootComponent, 0);
  505. }
  506. }
  507. printf("\n\n");
  508. }
  509. if (key == GLFW_KEY_F && action == GLFW_RELEASE) {
  510. printf("Printing UI ComponentTree\n\n");
  511. Component::printComponentTree(thiz->rootComponent, 0);
  512. printf("\n\n");
  513. }
  514. int yOffsetScroll = 1;
  515. if (key == GLFW_KEY_PAGE_UP && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
  516. TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
  517. if (p_TabComponent) {
  518. DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
  519. if (p_DocComponent) {
  520. p_DocComponent->transformMatrix[13] += -yOffsetScroll * 0.1;
  521. p_DocComponent->transformMatrixDirty = true;
  522. thiz->renderDirty = true;
  523. }
  524. }
  525. }
  526. if (key == GLFW_KEY_PAGE_DOWN && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
  527. TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
  528. if (p_TabComponent) {
  529. DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
  530. if (p_DocComponent) {
  531. p_DocComponent->transformMatrix[13] += yOffsetScroll * 0.1;
  532. p_DocComponent->transformMatrixDirty = true;
  533. thiz->renderDirty = true;
  534. }
  535. }
  536. }
  537. // FIXME Scrolling with this can scroll past boundary whereas the same doesn't happen with
  538. // scrolling wheel
  539. if (key == GLFW_KEY_J && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
  540. std::cout << "J is/was pressed. Scrolling down." << std::endl;
  541. TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
  542. if (p_TabComponent) {
  543. DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
  544. if (p_DocComponent) {
  545. p_DocComponent->transformMatrix[13] += yOffsetScroll * 0.1;
  546. p_DocComponent->transformMatrixDirty = true;
  547. thiz->renderDirty = true;
  548. }
  549. }
  550. }
  551. // FIXME Scrolling with this can scroll past boundary whereas the same doesn't happen with
  552. // scrolling wheel
  553. if (key == GLFW_KEY_K && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
  554. std::cout << "K is/was pressed. Scrolling up." << std::endl;
  555. TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
  556. if (p_TabComponent) {
  557. DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
  558. if (p_DocComponent) {
  559. p_DocComponent->transformMatrix[13] += -yOffsetScroll * 0.1;
  560. p_DocComponent->transformMatrixDirty = true;
  561. thiz->renderDirty = true;
  562. }
  563. }
  564. }
  565. });
  566. glfwMakeContextCurrent(window);
  567. return true;
  568. }
  569. bool Window::initGLEW() const {
  570. glewExperimental = GL_TRUE;
  571. const GLenum err = glewInit();
  572. if (err != GLEW_OK) {
  573. std::cout << "Could not initialize GLEW: " << glewGetErrorString(err) << std::endl;
  574. return false;
  575. }
  576. return true;
  577. }
  578. bool Window::initGL() {
  579. const GLubyte *renderer = glGetString(GL_RENDERER);
  580. const GLubyte *version = glGetString(GL_VERSION);
  581. std::cout << "Renderer: " << renderer << std::endl;
  582. std::cout << "Version: " << version << std::endl;
  583. glEnable(GL_BLEND);
  584. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  585. glClearColor(0.8f, 0.8f, 0.8f, 0.8f);
  586. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  587. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  588. GLenum glErr=glGetError();
  589. if(glErr != GL_NO_ERROR) {
  590. std::cout << "window::initGL - blend, clear, texParam - not ok: " << glErr << std::endl;
  591. }
  592. //std::cout << "OpenGL is set up" << std::endl;
  593. glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
  594. return true;
  595. }
  596. void Window::onResize(int passedWidth, int passedHeight) {
  597. this->windowWidth = passedWidth;
  598. this->windowHeight = passedHeight;
  599. this->delayResize = 1;
  600. }
  601. void Window::resize() {
  602. //std::cout << "Window::resize" << std::endl;
  603. const std::clock_t begin = clock();
  604. //resizeComponentTree(rootComponent, windowWidth, windowHeight);
  605. //this->printComponentTree(rootComponent, 0);
  606. //this->buildUI();
  607. rootComponent->windowWidth = windowWidth;
  608. rootComponent->windowHeight = windowHeight;
  609. rootComponent->layout();
  610. const std::clock_t end = clock();
  611. std::cout << "Window::resize - resized components in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
  612. //this->printComponentTree(rootComponent, 0);
  613. // recalculate scroll max by calculating how many screens are in the rootComponent's Height
  614. if (transformMatrix[13]>std::max((rootComponent->height)/(windowHeight)*2.0f, 2.0f)) {
  615. transformMatrix[13]=std::max((rootComponent->height)/(windowHeight)*2.0f, 2.0f);
  616. transformMatrixDirty = true;
  617. }
  618. //thiz->printComponentTree(thiz->rootComponent, 0);
  619. //printComponentTree(rootComponent, 0);
  620. renderDirty = true;
  621. }
  622. void Window::render() {
  623. if (delayResize) {
  624. delayResize--;
  625. if (delayResize) {
  626. return;
  627. }
  628. //std::cout << "Window::render - restarting drawing" << std::endl;
  629. this->resize();
  630. }
  631. /*
  632. if (domDirty) {
  633. const std::clock_t begin = clock();
  634. createComponentTree(domRootNode, rootComponent);
  635. const std::clock_t end = clock();
  636. std::cout << "built & laid out window components in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
  637. //printComponentTree(rootComponent, 0);
  638. //std::cout << "Window printComponentTree end" << std::endl;
  639. domDirty = false;
  640. renderDirty = true;
  641. }
  642. */
  643. if (renderDirty || transformMatrixDirty) {
  644. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  645. GLenum glErr=glGetError();
  646. if(glErr != GL_NO_ERROR) {
  647. std::cout << "window::render - box render start - not ok: " << glErr << std::endl;
  648. }
  649. Shader *textureShader = ShaderLoader::getShader(VertexShader("TextureShader.vert"),
  650. FragmentShader("TextureShader.frag"));
  651. textureShader->bind();
  652. glErr=glGetError();
  653. if(glErr != GL_NO_ERROR) {
  654. std::cout << "window::render - glUseProgram - not ok: " << glErr << std::endl;
  655. }
  656. // draw anime 1st, tabbed components 2nd
  657. // then box shit
  658. // text last
  659. renderComponentType("anime", rootComponent);
  660. renderComponentType("tab", rootComponent);
  661. textureShader->bind();
  662. //GLint transformLocation = glGetUniformLocation(textureProgram, "transform");
  663. //glUniformMatrix4fv(transformLocation, 1, GL_FALSE, transformMatrix);
  664. renderComponentType("box", rootComponent);
  665. renderComponentType("input", rootComponent);
  666. // it's quick but done on scroll
  667. Shader *fontShader = ShaderLoader::getShader(VertexShader("FontShader.vert"),
  668. FragmentShader("FontShader.frag"));
  669. fontShader->bind();
  670. // reset both, since components can change this
  671. // god we may have to reset this after each component render...
  672. // maybe we don't need too
  673. if (transformMatrixDirty) {
  674. //const std::clock_t begin = clock();
  675. GLint transformLocation = fontShader->uniform("transform");
  676. glErr=glGetError();
  677. if(glErr != GL_NO_ERROR) {
  678. std::cout << "window::render - glGetUniformLocation - not ok: " << glErr << std::endl;
  679. }
  680. glUniformMatrix4fv(transformLocation, 1, GL_FALSE, transformMatrix);
  681. glErr=glGetError();
  682. if(glErr != GL_NO_ERROR) {
  683. std::cout << "window::render - glUniformMatrix4fv - not ok: " << glErr << std::endl;
  684. }
  685. // didn't change much (could be we didnt' select the textureProgram first)
  686. //GLint transformLocation2 = glGetUniformLocation(textureProgram, "transform");
  687. //glUniformMatrix4fv(transformLocation2, 1, GL_FALSE, transformMatrix);
  688. //const std::clock_t end = clock();
  689. //std::cout << "Updated font matrix in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
  690. transformMatrixDirty = false;
  691. }
  692. //renderComponents(rootComponent);
  693. renderComponentType("text", rootComponent);
  694. glfwSwapBuffers(window);
  695. // update 2nd buffer
  696. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  697. textureShader->bind();
  698. renderComponentType("anime", rootComponent);
  699. renderComponentType("tab", rootComponent);
  700. textureShader->bind();
  701. renderComponentType("box", rootComponent);
  702. renderComponentType("input", rootComponent);
  703. // it's quick but done on scroll
  704. fontShader->bind();
  705. renderComponentType("text", rootComponent);
  706. glfwSwapBuffers(window);
  707. renderDirty = false;
  708. }
  709. glfwSwapBuffers(window);
  710. glfwPollEvents();
  711. }
  712. void Window::setDOM(const std::shared_ptr<Node> rootNode) {
  713. // reset scroll position
  714. transformMatrix[13] = 2;
  715. transformMatrixDirty = true;
  716. if (!tabComponent) {
  717. std::cout << "Window::setDOM - no tabComponent" << std::endl;
  718. return;
  719. }
  720. std::cout << "Window::setDOM - tabComponent is already set up" << std::endl;
  721. TabbedComponent *tabbedComponent = dynamic_cast<TabbedComponent*>(tabComponent.get());
  722. if (!tabbedComponent->tabs.size()) {
  723. // if no tabs, make one
  724. tabbedComponent->addTab(currentURL.toString());
  725. //tabbedComponent->selectTab(tabbedComponent->tabs.back()->id);
  726. tabbedComponent->selectTab(tabbedComponent->tabs.back());
  727. }
  728. tabbedComponent->loadDomIntoTab(rootNode, currentURL.toString());
  729. }
  730. void Window::createComponentTree(const std::shared_ptr<Node> node, const std::shared_ptr<Component> &parentComponent) {
  731. std::shared_ptr<Component> component = componentBuilder.build(node, parentComponent, windowWidth, windowHeight);
  732. // ComponentBuilder now calls setParent and setParents adds children
  733. //component->setParent(parentComponent);
  734. // set parent of the newly created component
  735. //component->parent = parentComponent;
  736. //if (parentComponent) {
  737. // add new component as child to parent
  738. //parentComponent->children.push_back(component);
  739. //}
  740. if (component && component==this->rootComponent) {
  741. // if this is the root node
  742. component->reqWidth = windowWidth;
  743. component->reqHeight = windowHeight;
  744. }
  745. if (!node) {
  746. return;
  747. }
  748. // create children elements
  749. for (std::shared_ptr<Node> child : node->children) {
  750. createComponentTree(child, component);
  751. }
  752. // update parents, creation brings them up to date
  753. //updateComponentSize(component);
  754. }
  755. void Window::renderComponentType(std::string str, std::shared_ptr<Component> component) {
  756. if (!component) {
  757. std::cout << "Window::renderComponentType - got null passed" << std::endl;
  758. return;
  759. }
  760. if (typeOfComponent(component) == str) {
  761. // how slow is this?
  762. if (str == "doc") {
  763. DocumentComponent *docComponent = dynamic_cast<DocumentComponent*>(component.get());
  764. docComponent->render();
  765. } else if (str =="tab") {
  766. TabbedComponent *pTabComponent = dynamic_cast<TabbedComponent*>(component.get());
  767. pTabComponent->render();
  768. } else if (str =="text") {
  769. TextComponent *textComponent = dynamic_cast<TextComponent*>(component.get());
  770. textComponent->render();
  771. } else if (str =="input") {
  772. InputComponent *inputComponent = dynamic_cast<InputComponent*>(component.get());
  773. inputComponent->render();
  774. } else if (str =="anime") {
  775. AnimeComponent *animeComponent = dynamic_cast<AnimeComponent*>(component.get());
  776. animeComponent->render();
  777. } else if (str =="box") {
  778. //AnimeComponent *animeComponent = dynamic_cast<AnimeComponent*>(component.get());
  779. //if (!animeComponent) {
  780. BoxComponent *boxComponent = dynamic_cast<BoxComponent*>(component.get());
  781. boxComponent->render();
  782. //}
  783. } else {
  784. std::cout << "Unknown type " << str << std::endl;
  785. }
  786. //} else {
  787. //std::cout << "type: " << typeOfComponent(component) << "!=" << str << std::endl;
  788. }
  789. // is this needed?
  790. if (component->children.empty()) {
  791. return;
  792. }
  793. for (std::shared_ptr<Component> &child : component->children) {
  794. this->renderComponentType(str, child);
  795. }
  796. }
  797. // used for picking
  798. // should return multiple components
  799. std::shared_ptr<Component> Window::searchComponentTree(const std::shared_ptr<Component> &component, const int x, const int y) {
  800. if (component->children.empty()) {
  801. // x,y: 0,0 is the upper left
  802. //std::cout << "cursor at: " << x << "," << y << " 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;
  803. int ty = component->y;
  804. int ty1 = component->height + component->y;
  805. if (ty < 0) {
  806. ty = 0;
  807. ty1 += 64; // FIXME: hack (for what?)
  808. }
  809. //std::cout << "Window::searchComponentTree - y search: " << ty1 << ">" << static_cast<int>(this->windowHeight + y) << ">" << static_cast<int>(ty) << std::endl;
  810. if (ty1 > this->windowHeight + y && this->windowHeight + y > ty) {
  811. //std::cout << "y match" << std::endl;
  812. if (component->x < x && component->x + component->width > x) {
  813. //std::cout << "Window::searchComponentTree - hit " << typeOfComponent(component) << std::endl;
  814. return component;
  815. }
  816. }
  817. }
  818. else {
  819. for (std::shared_ptr<Component> child : component->children) {
  820. if (!child->isPickable) continue;
  821. std::shared_ptr<Component> found = searchComponentTree(child, x, y);
  822. if (found) {
  823. return found;
  824. }
  825. }
  826. }
  827. return nullptr;
  828. }
  829. // moving naviagtion closer to window, as window is now the owner of currentURL
  830. // preparation for multiple HTML documents
  831. void Window::navTo(std::string url) {
  832. logDebug() << "Window::navTo(" << url << ")" << std::endl;
  833. URL link=URL(url);
  834. logDebug() << "Window::navTo - URL marshalled [" << link << "]" << std::endl;
  835. currentURL = currentURL.merge(link);
  836. logDebug() << "go to: " << currentURL << std::endl;
  837. focusedComponent = nullptr;
  838. hoverComponent = nullptr;
  839. setWindowContent(currentURL);
  840. }
  841. /*
  842. void handleRequest(const HTTPResponse &response) {
  843. std::cout << "main:::handleRequest - statusCode: " << response.statusCode << std::endl;
  844. if (response.statusCode == 200) {
  845. const std::unique_ptr<HTMLParser> parser = std::make_unique<HTMLParser>();
  846. const std::clock_t begin = clock();
  847. std::shared_ptr<Node> rootNode = parser->parse(response.body);
  848. const std::clock_t end = clock();
  849. std::cout << "Parsed document in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
  850. window->setDOM(rootNode);
  851. }
  852. else if (response.statusCode == 301) {
  853. std::string location;
  854. if (response.properties.find("Location")==response.properties.end()) {
  855. if (response.properties.find("location")==response.properties.end()) {
  856. std::cout << "::handleRequest - got 301 without a location" << std::endl;
  857. for(auto const &row : response.properties) {
  858. std::cout << "::handleRequest - " << row.first << "=" << response.properties.at(row.first) << std::endl;
  859. }
  860. return;
  861. } else {
  862. location = response.properties.at("location");
  863. }
  864. } else {
  865. location = response.properties.at("Location");
  866. }
  867. std::cout << "Redirect To: " << location << std::endl;
  868. std::shared_ptr<URI> uri = parseUri(location);
  869. const std::unique_ptr<HTTPRequest> request = std::make_unique<HTTPRequest>(uri);
  870. request->sendRequest(handleRequest);
  871. return;
  872. }
  873. else {
  874. std::cout << "Unknown Status Code: " << response.statusCode << std::endl;
  875. }
  876. }
  877. */
  878. // tried to make a window method
  879. void handleRequest(const HTTPResponse &response) {
  880. std::cout << "Window:::handleRequest - statusCode: " << response.statusCode << std::endl;
  881. if (response.statusCode == 200) {
  882. const std::unique_ptr<HTMLParser> parser = std::make_unique<HTMLParser>();
  883. const std::clock_t begin = clock();
  884. std::shared_ptr<Node> rootNode = parser->parse(response.body);
  885. const std::clock_t end = clock();
  886. std::cout << "Parsed document in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
  887. window->setDOM(rootNode);
  888. }
  889. else if (response.statusCode == 301) {
  890. std::string location;
  891. if (response.properties.find("Location")==response.properties.end()) {
  892. if (response.properties.find("location")==response.properties.end()) {
  893. std::cout << "::handleRequest - got 301 without a location" << std::endl;
  894. for(auto const &row : response.properties) {
  895. std::cout << "::handleRequest - " << row.first << "=" << response.properties.at(row.first) << std::endl;
  896. }
  897. return;
  898. } else {
  899. location = response.properties.at("location");
  900. }
  901. } else {
  902. location = response.properties.at("Location");
  903. }
  904. std::cout << "Redirect To: " << location << std::endl;
  905. std::tuple<std::unique_ptr<URL>,enum URIParseError> result = parseUri(location);
  906. if (std::get<1>(result) != URI_PARSE_ERROR_NONE) {
  907. // TODO We probably wanna handle this better..
  908. std::cerr << "error parsing uri" << std::endl;
  909. return;
  910. }
  911. std::unique_ptr<URL> uri = std::move(std::get<0>(result));
  912. const std::unique_ptr<HTTPRequest> request = std::make_unique<HTTPRequest>(std::move(uri));
  913. request->sendRequest(handleRequest);
  914. return;
  915. }
  916. else {
  917. std::cout << "Unknown Status Code: " << response.statusCode << std::endl;
  918. }
  919. }