#include "DocumentComponent.h" #include #include #include "../../Log.h" #include "InputComponent.h" #include "TabbedComponent.h" #include #include "../opengl/Shader.h" #include "../../WebResource.h" #include "../../html/HTMLParser.h" void deleteComponent(std::shared_ptr &component); void deleteNode(std::shared_ptr node); 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) { //std::cout << "DocumentComponent::DocumentComponent" << std::endl; windowWidth = passedWindowWidth; windowHeight = passedWindowHeight; //std::cout << "DocumentComponent::DocumentComponent - window size: " << windowWidth << "x" << windowHeight << std::endl; x = rawX; y = rawY; width = rawWidth; height = rawHeight; if (height < 0) { std::cout << "DocumentComponent::DocumentComponent - height was less than zero" << std::endl; height = 0; } //std::cout << "DocumentComponent::DocumentComponent - our size" << static_cast(width) << "x" << static_cast(height) << std::endl; onMousemove=[this](int passedX, int passedY) { // set hover component static int lx = 0; static int ly = 0; //std::cout << "DocumentComponent::DocumentComponent:onMousemove - at " << passedX << "," << passedY << std::endl; if (lx == passedX && ly == passedY) { return; } lx = passedX; ly = passedY; //std::cout << "DocumentComponent::DocumentComponent:onMousemove - size " << this->windowWidth << "," << this->windowHeight << std::endl; //std::cout << "DocumentComponent::DocumentComponent:onMousemove - at " << passedX << "," << passedY << " scroll: " << (int)(((this->transformMatrix[13] / 2) - 1) * this->windowHeight) << std::endl; this->hoverComponent = this->searchComponentTree(this->rootComponent, passedX, passedY + (((this->transformMatrix[13] / 2) - 1) * this->windowHeight)); if (this->hoverComponent) { //std::cout << "DocumentComponent::DocumentComponent:onMousemove - hovering over " << typeOfComponent(this->hoverComponent) << " component" << std::endl; if (this->hoverComponent->onMousemove) { // this could communicate the cursor to use this->hoverComponent->onMousemove(passedX, passedY); } if (this->hoverComponent->onClick) { glfwSetCursor(this->win->window, this->win->cursorHand); } else { glfwSetCursor(this->win->window, this->win->cursorIbeam); } } else { glfwSetCursor(this->win->window, this->win->cursorArrow); } }; onWheel=[this](int passedX, int passedY) { //std::cout << "DocumentComponent::DocumentComponent:::onWheel - scroll yDelta: " << y << std::endl; //Component::printComponentTree(rootComponent, 0); double pY = passedY / 10.0; this->scrollY -= pY; if (this->scrollY < 0) { this->scrollY = 0; } else if (this->scrollY > this->scrollHeight) { this->scrollY = this->scrollHeight; } //std::cout << "scroll pos: " << scrollY << "/" << scrollHeight << std::endl; //std::cout << "y: " << static_cast(this->y) << " - " << this->scrollY << std::endl; //std::cout << "root.y: " << static_cast(rootComponent->y) << std::endl; //std::cout << "windowSize: " << windowWidth << "," << windowHeight << std::endl; // new system //std::cout << "DocumentComponent::DocumentComponent:::onWheel - old position: " << this->transformMatrix[13] << " adjustment:" << (-pY*0.1) << std::endl; this->transformMatrix[13] += -pY * 0.01; // 2.0 is one screen height // we draw from 0 downwards (y+), so can't scroll past our starting draw point if (this->transformMatrix[13] < 2) { this->transformMatrix[13] = 2; } // calculate scroll max by calculating how many screens are in the rootComponent's Height if (this->transformMatrix[13] > std::max( (this->rootComponent->height - this->rootComponent->y) / this->windowHeight * 2.0f, 2.0f)) { this->transformMatrix[13] = std::max( (this->rootComponent->height - this->rootComponent->y) / this->windowHeight * 2.0f, 2.0f); } this->transformMatrixDirty = true; /* // adjust root position rootComponent->y = this->y + this->scrollY; //std::cout << "now root.y: " << static_cast(rootComponent->y) << std::endl; // reset root size rootComponent->windowWidth = windowWidth; rootComponent->windowHeight = windowHeight; //Component::printComponentTree(rootComponent, 0); // this takes so long, we may want to delay until the input is adjusted const std::clock_t begin = clock(); rootComponent->layout(); // need to update the vertices //renderDirty = true; // should we mark win->renderDirty = true? const std::clock_t end = clock(); std::cout << "Scrolled document in: " << std::fixed << ((static_cast(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl; */ //rootComponent->y = this->y - this->scrollY; //std::cout << "after root.y: " << static_cast(rootComponent->y) << std::endl; // don't need this - why not? //this->renderDirty = true; }; onMousedown=[this](int passedX, int passedY) { //std::cout << "document left press" << std::endl; if (this->hoverComponent) { if (this->focusedComponent != this->hoverComponent) { // blur old component if (this->focusedComponent) { if (this->focusedComponent->onBlur) { this->focusedComponent->onBlur(); } } // focus new component if (this->hoverComponent->onFocus) { this->hoverComponent->onFocus(); } } this->focusedComponent = this->hoverComponent; if (this->focusedComponent->onMousedown) { //std::cout << "click event" << std::endl; this->focusedComponent->onMousedown(passedX, passedY); } } }; onMouseup=[this](int passedX, int passedY) { //std::cout << "document left release" << std::endl; if (this->hoverComponent) { //std::cout << "DocumentComponent::DocumentComponent:onMouseup - hovering over " << typeOfComponent(this->hoverComponent) << " component" << std::endl; if (this->focusedComponent != this->hoverComponent) { // blur old component if (this->focusedComponent) { if (this->focusedComponent->onBlur) { this->focusedComponent->onBlur(); } } // focus new component if (this->hoverComponent->onFocus) { this->hoverComponent->onFocus(); } } this->focusedComponent = this->hoverComponent; //std::cout << "DocumentComponent::DocumentComponent:onMouseup - hovering over " << typeOfComponent(this->hoverComponent) << " component, focused on " << typeOfComponent(this->focusedComponent) << std::endl; if (this->focusedComponent->onMouseup) { //std::cout << "click event" << std::endl; this->focusedComponent->onMouseup(passedX, passedY); } if (this->focusedComponent->onClick) { //std::cout << "click event" << std::endl; this->focusedComponent->onClick(); } } }; onKeyup=[this](int key, int scancode, int action, int mods) { //std::cout << "DocumentComponent::DocumentComponent:onKeyup" << typeOfComponent(this->focusedComponent) << std::endl; InputComponent *inputComponent = dynamic_cast(this->focusedComponent.get()); if (inputComponent) { //std::cout << "inputComponent is focused, key pressed " << key << " action: " <backSpace(); } else if (key == 257) { std::cout << "enter!" << std::endl; } else { if (key < 256) { if (mods & GLFW_MOD_SHIFT) { // SHIFT if (key == GLFW_KEY_SLASH) key='?'; if (key == GLFW_KEY_APOSTROPHE) key='"'; if (key == GLFW_KEY_COMMA) key='<'; if (key == GLFW_KEY_MINUS) key='_'; if (key == GLFW_KEY_PERIOD) key='>'; if (key == GLFW_KEY_SEMICOLON) key=':'; if (key == GLFW_KEY_EQUAL) key='+'; if (key == GLFW_KEY_LEFT_BRACKET) key='{'; if (key == GLFW_KEY_BACKSLASH) key='|'; if (key == GLFW_KEY_RIGHT_BRACKET) key='}'; if (key == GLFW_KEY_GRAVE_ACCENT) key='~'; } else { // no shift or caplocks // basically: when SHIFT isn't pressed but key is in A-Z range, add ascii offset to make it lower case if (key >= 'A' && key <= 'Z') { key += 'a' - 'A'; } } inputComponent->addChar(key); } // otherwise I think it's some weird control char } } } }; } void deleteComponent(std::shared_ptr &component) { // delete all my child first for (std::shared_ptr child : component->children) { deleteComponent(child); } component->parent=nullptr; component->previous=nullptr; component->children.clear(); component.reset(); // now delete self } void deleteNode(std::shared_ptr node) { for (std::shared_ptr child : node->children) { deleteNode(child); } node->parent=nullptr; node->children.clear(); node->component=nullptr; // disassociate component node.reset(); } void DocumentComponent::setDOM(const std::shared_ptr rootNode) { // reset rootComponent if (rootComponent) { deleteComponent(rootComponent); } if (domRootNode) { deleteNode(domRootNode); } // reset scroll position transformMatrix[13] = 2; transformMatrixDirty = true; // new root component rootComponent = std::make_shared(); rootComponent->name = "rootComponent of " + name; rootComponent->y = y; domRootNode = rootNode; domDirty = true; } void DocumentComponent::render() { //std::cout << "DocumentComponent::render" << std::endl; if (domDirty) { const std::clock_t begin = clock(); createComponentTree(domRootNode, rootComponent); const std::clock_t end = clock(); // root component here doesn't have any children... std::cout << "built & laid out document components in: " << std::fixed << ((static_cast(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl; //Component::printComponentTree(rootComponent, 0); domDirty = false; //std::cout << "root Height: " << static_cast(rootComponent->height) << " window Height: " << windowHeight << " y " << static_cast(this->y) << std::endl; scrollHeight = std::max(0, static_cast(rootComponent->height - (windowHeight + (this->y * 2)))); // recalculate scroll max by calculating how many screens are in the rootComponent's Height if (transformMatrix[13]>std::max((rootComponent->height)/(windowHeight)*2.0f, 2.0f)) { transformMatrix[13]=std::max((rootComponent->height)/(windowHeight)*2.0f, 2.0f); transformMatrixDirty = true; } } // we have to do this each time // because window resets it //if (transformMatrixDirty) { //const std::clock_t begin = clock(); Shader *fontShader = this->win->shaderLoader.getShader(VertexShader("FontShader.vert"), FragmentShader("FontShader.frag")); GLint transformLocation = fontShader->uniform("transform"); glUniformMatrix4fv(transformLocation, 1, GL_FALSE, transformMatrix); //const std::clock_t end = clock(); //std::cout << "Updated font matrix in: " << std::fixed << ((static_cast(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl; Shader *textureShader = this->win->shaderLoader.getShader(VertexShader("TextureShader.vert"), FragmentShader("TextureShader.frag")); GLint transformLocation2 = textureShader->uniform("transform"); glUniformMatrix4fv(transformLocation2, 1, GL_FALSE, transformMatrix); transformMatrixDirty = false; //} //std::cout << "DocumentComponent::render - renderDirty" << std::endl; textureShader->bind(); renderBoxComponents(rootComponent); textureShader->release(); fontShader->bind(); renderComponents(rootComponent); fontShader->release(); } // create this component and all it's children void DocumentComponent::createComponentTree(const std::shared_ptr node, const std::shared_ptr &parentComponent) { std::shared_ptr component = componentBuilder.build(node, parentComponent, this->win); //std::cout << "DocumentComponent::createComponentTree" << std::endl; if (!component) { //std::cout << "DocumentComponent::createComponentTree - no component" << std::endl; return; } if (node==domRootNode) { // if this is the root node component->reqWidth = windowWidth; component->reqHeight = windowHeight; } // create children elements for (std::shared_ptr child : node->children) { createComponentTree(child, component); } } // used for picking std::shared_ptr DocumentComponent::searchComponentTree(const std::shared_ptr &component, const int passedX, const int passedY) { if (component->children.empty()) { //std::cout << "DocumentComponent::searchComponentTree - component at " << static_cast(component->x) << "," << static_cast(component->y) << " size " << static_cast(component->width) << "," << static_cast(component->height) << std::endl; //std::cout << "DocumentComponent::searchComponentTree - y search: " << static_cast(-component->y) << "<" << static_cast(passedY) << "<" << static_cast(-component->y + component->height) << std::endl; if (-component->y < passedY && -component->y + component->height > passedY) { //std::cout << "DocumentComponent::searchComponentTree - x search: " << static_cast(component->x) << "<" << static_cast(passedX) << "<" << static_cast(component->x + component->width) << std::endl; if (component->x < passedX && component->x + component->width > passedX) { //std::cout << "DocumentComponent::searchComponentTree - hit " << typeOfComponent(component) << std::endl; return component; } } } else { for (std::shared_ptr child : component->children) { std::shared_ptr found = searchComponentTree(child, passedX, passedY); if (found) { return found; } } } return nullptr; } // moving naviagtion closer to window, as window is now the owner of currentURL // preparation for multiple HTML documents void DocumentComponent::navTo(const std::string url) { logDebug() << "DocumentComponent::navTo(" << url << ")" << std::endl; this->currentURL = currentURL.merge(URL(url)); logDebug() << "DocumentComponent::navTo - go to: " << currentURL << std::endl; //setWindowContent(currentURL); logDebug() << "main::setWindowContent - " << url << std::endl; // integrity check TabbedComponent *tabComponent = dynamic_cast(this->win->tabComponent.get()); if (!tabComponent->tabs.size()) { std::cout << "DocumentComponent::navTo - There's a document when there's not tabs" << std::endl; } // download URL WebResource res = getWebResource(url); if (res.resourceType == ResourceType::INVALID) { logError() << "Invalid resource type: " << res.raw << std::endl; return; } // parse HTML if (res.resourceType == ResourceType::HTML) { HTMLParser parser; const std::clock_t begin = clock(); std::shared_ptr rootNode = parser.parse(res.raw); const std::clock_t end = clock(); logDebug() << "main::setWindowContent - Parsed document in: " << std::fixed << ((static_cast(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl; // send NodeTree to window //this->win->setDOM(rootNode); // we need a way to communicate with our tabComponent // maybe an event is best if (this->onBeforeLoad) { this->onBeforeLoad(url); } this->setDOM(rootNode); } else if (res.resourceType == ResourceType::TXT) { std::cout << "Rendering text document" << std::endl; std::shared_ptr rootNode = std::make_shared(NodeType::ROOT); std::shared_ptr tagNode = std::make_shared(); tagNode->tag="p"; // bind tag to root tagNode->parent = rootNode; rootNode->children.push_back(tagNode); std::shared_ptr textNode = std::make_shared(); textNode->text = res.raw; // bind text to tag textNode->parent = tagNode; tagNode->children.push_back(textNode); // send NodeTree to window //this->win->setDOM(rootNode); this->setDOM(rootNode); } else { std::cout << "setWindowContent() - I don't know how to render non-html files" << std::endl; } }