Skip to content
Snippets Groups Projects
DocumentComponent.cpp 13.4 KiB
Newer Older
Odilitime's avatar
Odilitime committed
#include "DocumentComponent.h"
#include <cmath>
#include <iostream>
#include "../../Log.h"
#include "InputComponent.h"
#include <ctime>
Odilitime's avatar
Odilitime committed

void deleteComponent(std::shared_ptr<Component> &component);
void deleteNode(std::shared_ptr<Node> 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) {
Odilitime's avatar
Odilitime committed
    //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<int>(width) << "x" << static_cast<int>(height) << std::endl;
    onMousemove=[this](int passedX, int passedY) {
Odilitime's avatar
Odilitime committed
        // set hover component
        static int lx = 0;
        static int ly = 0;
Odilitime's avatar
Odilitime committed
        //std::cout << "DocumentComponent::DocumentComponent:onMousemove - at " << passedX << "," << passedY << std::endl;
        if (lx == passedX && ly == passedY) {
Odilitime's avatar
Odilitime committed
            return;
        }
        lx = passedX;
        ly = passedY;
Odilitime's avatar
Odilitime committed
        //std::cout << "DocumentComponent::DocumentComponent:onMousemove - size " << this->windowWidth << "," << this->windowHeight << std::endl;
Odilitime's avatar
Odilitime committed
        this->hoverComponent = this->searchComponentTree(this->rootComponent, passedX, passedY);
Odilitime's avatar
Odilitime committed
        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);
Odilitime's avatar
Odilitime committed
            }
            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;
        this->scrollY -= pY;
Odilitime's avatar
Odilitime committed
        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<int>(this->y) << " - " << this->scrollY << std::endl;
        //std::cout << "root.y: " << static_cast<int>(rootComponent->y) << std::endl;
        //std::cout << "windowSize: " << windowWidth << "," << windowHeight << std::endl;
        
Odilitime's avatar
Odilitime committed
        rootComponent->y = this->y + this->scrollY;
        //std::cout << "now root.y: " << static_cast<int>(rootComponent->y) << std::endl;
        
        // reset root size
        rootComponent->windowWidth = windowWidth;
        rootComponent->windowHeight = windowHeight;
        
        //Component::printComponentTree(rootComponent, 0);
Odilitime's avatar
Odilitime committed
        
        // this takes so long, we may want to delay until the input is adjusted
        const std::clock_t begin = clock();
Odilitime's avatar
Odilitime committed
        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<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;

Odilitime's avatar
Odilitime committed
        //rootComponent->y = this->y - this->scrollY;
        //std::cout << "after root.y: " << static_cast<int>(rootComponent->y) << std::endl;
        
        // don't need this - why not?
Odilitime's avatar
Odilitime committed
        //this->renderDirty = true;
    };
    onMousedown=[this](int passedX, int passedY) {
Odilitime's avatar
Odilitime committed
        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) {
Odilitime's avatar
Odilitime committed
        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;
            if (this->focusedComponent->onMouseup) {
                //std::cout << "click event" << std::endl;
                this->focusedComponent->onMouseup(passedX, passedY);
Odilitime's avatar
Odilitime committed
            }
            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" << std::endl;
        InputComponent *inputComponent = dynamic_cast<InputComponent*>(this->focusedComponent.get());
        if (inputComponent) {
            //std::cout << "inputComponent is focused, key pressed " << key << " action: " <<action << std::endl;
            // action 1 is down, 0 is up, 2 is a repeat
            if (action == 0 || action == 2) {
                // key up
                // it's always uppercase...
                if (key == 259) {
                    inputComponent->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> &component) {
    // delete all my child first
    for (std::shared_ptr<Component> child : component->children) {
        deleteComponent(child);
    }
    component->parent=nullptr;
    component->previous=nullptr;
    component->children.clear();
    // now delete self
void deleteNode(std::shared_ptr<Node> node) {
    for (std::shared_ptr<Node> child : node->children) {
        deleteNode(child);
Odilitime's avatar
Odilitime committed
    }
    node->parent=nullptr;
    node->children.clear();
    node->component=nullptr; // disassociate component
}

void DocumentComponent::setDOM(const std::shared_ptr<Node> rootNode) {
    // reset rootComponent
    if (rootComponent) {
        deleteComponent(rootComponent);
Odilitime's avatar
Odilitime committed
    }
    if (domRootNode) {
        deleteNode(domRootNode);
Odilitime's avatar
Odilitime committed
    }
    // reset scroll position ?
    
    // new root component
    rootComponent = std::make_shared<Component>();
    rootComponent->name = "rootComponent of " + name;
    rootComponent->y = y;
    
    domRootNode = rootNode;
    domDirty = true;
Odilitime's avatar
Odilitime committed
}

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...
Odilitime's avatar
Odilitime committed
        std::cout << "built & laid out document components in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
        
        //Component::printComponentTree(rootComponent, 0);
Odilitime's avatar
Odilitime committed
        domDirty = false;
        //std::cout << "root Height: " << static_cast<int>(rootComponent->height) << " window Height: " << windowHeight << " y " << static_cast<int>(this->y) << std::endl;
        scrollHeight = std::max(0, static_cast<int>(rootComponent->height - (windowHeight + (this->y * 2))));
    }
    //std::cout << "DocumentComponent::render - renderDirty" << std::endl;
    glUseProgram(win->textureProgram);
    renderBoxComponents(rootComponent);
    glUseProgram(win->fontProgram);
    renderComponents(rootComponent);
Odilitime's avatar
Odilitime committed
}

// create this component and all it's children
void DocumentComponent::createComponentTree(const std::shared_ptr<Node> node, const std::shared_ptr<Component> &parentComponent) {
    std::shared_ptr<Component> component = componentBuilder.build(node, parentComponent, windowWidth, windowHeight);
    //std::cout << "DocumentComponent::createComponentTree" << std::endl;
Odilitime's avatar
Odilitime committed
    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<Node> child : node->children) {
        createComponentTree(child, component);
    }
}



// used for picking
std::shared_ptr<Component> DocumentComponent::searchComponentTree(const std::shared_ptr<Component> &component, const int passedX, const int passedY) {
Odilitime's avatar
Odilitime committed
    if (component->children.empty()) {
        //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;
Odilitime's avatar
Odilitime committed
        //std::cout << "DocumentComponent::searchComponentTree - y search: " << static_cast<int>(-component->y) << "<" << static_cast<int>(passedY) << "<" << static_cast<int>(-component->y + component->height) << std::endl;
        if (-component->y < passedY && -component->y + component->height > passedY) {
Odilitime's avatar
Odilitime committed
            //std::cout << "DocumentComponent::searchComponentTree - x search: " << static_cast<int>(component->x) << "<" << static_cast<int>(passedX) << "<" << static_cast<int>(component->x + component->width) << std::endl;
            if (component->x < passedX && component->x + component->width > passedX) {
Odilitime's avatar
Odilitime committed
                //std::cout << "hit " << typeOfComponent(component) << std::endl;
                return component;
            }
        }
    }
    else {
        for (std::shared_ptr<Component> child : component->children) {
Odilitime's avatar
Odilitime committed
            std::shared_ptr<Component> found = searchComponentTree(child, passedX, passedY);
Odilitime's avatar
Odilitime committed
            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) {
Odilitime's avatar
Odilitime committed
    logDebug() << "DocumentComponent::navTo(" << url << ")" << std::endl;
Odilitime's avatar
Odilitime committed
    currentURL = currentURL.merge(URL(url));
Odilitime's avatar
Odilitime committed
    logDebug() << "DocumentComponent::navTo - go to: " << currentURL << std::endl;
Odilitime's avatar
Odilitime committed
    setWindowContent(currentURL);
}