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.
675 lines
30 KiB
675 lines
30 KiB
#include "DocumentComponent.h" |
|
#include <cmath> |
|
#include <iostream> |
|
#include "InputComponent.h" |
|
#include "ButtonComponent.h" |
|
#include "TabbedComponent.h" |
|
#include <ctime> |
|
|
|
#include "../graphical/renderers/glfw/Shader.h" |
|
#include "../../tools/Log.h" |
|
#include "../../tools/WebResource.h" |
|
#include "../../app/browser/browser.h" |
|
#include "../../parsers/markup/html/HTMLParser.h" |
|
|
|
void deleteComponent(std::shared_ptr<Component> &component); |
|
void deleteNode(std::shared_ptr<Node> node); |
|
|
|
extern std::unique_ptr<Browser> browser; |
|
|
|
#include <utility> |
|
|
|
// well we need to know how many chars in a lines |
|
// we also need to know where a line starts (pos) |
|
// lines start at 0 for first line |
|
std::pair<size_t, size_t> getLine(std::string text, int findLine) { |
|
//std::cout << "DocumentComponent.getLine [" << text << "] findLine: " << findLine << std::endl; |
|
size_t pos = 0; |
|
size_t lPos = 0; |
|
pos = text.find("\r"); |
|
size_t line = 0; |
|
while(pos != std::string::npos) { |
|
lPos = pos; |
|
pos = text.find("\r", lPos + 1); |
|
if (line == static_cast<unsigned int>(findLine)) { |
|
//std::cout << "lPos: " << lPos << " pos: " << pos << " line: " << line << std::endl; |
|
//std::cout << "DocumentComponent.getLine start " << (lPos + line + 1) << " end " << (pos == std::string::npos ? text.length() : (lPos + pos)) << std::endl; |
|
return std::make_pair(lPos + line + 1, pos == std::string::npos ? text.length() : (lPos + pos)); |
|
} |
|
line++; |
|
} |
|
//std::cout << "end lPos: " << lPos << " pos: " << pos << " line: " << line << std::endl; |
|
//std::cout << "DocumentComponent.getLine result end of text" << findLine << std::endl; |
|
return std::make_pair(lPos + line, text.size()); |
|
} |
|
|
|
size_t getNumberOfLines(std::string text) { |
|
size_t pos = text.find("\r"); |
|
size_t lines = 0; |
|
while(pos != std::string::npos) { |
|
lines++; |
|
pos = text.find("\r", pos + 1); |
|
} |
|
return lines; |
|
} |
|
|
|
#include <fstream> |
|
|
|
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; |
|
|
|
std::shared_ptr<Component> rootComponent = std::make_shared<Component>(); |
|
this->layers.push_back(rootComponent); |
|
|
|
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::ifstream fin("res/ntr_bootstrap.js"); |
|
std::stringstream buffer; |
|
buffer << fin.rdbuf(); |
|
std::string bootstrap_js = buffer.str(); |
|
if (bootstrap_js == "") { |
|
std::cout << "DocumentComponent::DocumentComponent - ERROR: Could not load res/ntr_bootstrap.js" << std::endl; |
|
} else { |
|
//std::cout << "DocumentComponent::DocumentComponent - Loaded [" << bootstrap_js << "] ntr_bootstrap.js\n"; |
|
this->bootstrapScript = std::make_shared<JavaScript>(); |
|
this->bootstrapScript->parse(bootstrap_js); |
|
} |
|
|
|
//std::cout << "DocumentComponent::DocumentComponent - our size" << static_cast<int>(width) << "x" << static_cast<int>(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; |
|
std::shared_ptr<Component> newHover = nullptr; |
|
// FIXME: we may need to iterator backwards here (top layer to back layer) |
|
for(auto layer: this->layers) { |
|
//this->hoverComponent = this->searchComponentTree(this->rootComponent, passedX, passedY + (((this->transformMatrix[13] / 2) - 1) * this->windowHeight)); |
|
// apply scroll transformation |
|
std::shared_ptr<Component> found = this->searchComponentTree(layer, passedX, passedY + (((this->transformMatrix[13] / 2) - 1) * this->windowHeight)); |
|
if (found) { |
|
newHover = found; |
|
break; |
|
} |
|
} |
|
// FIXME: no mouseOver or out? |
|
this->hoverComponent = newHover; |
|
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 || typeOfComponent(this->hoverComponent) == "button") { |
|
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: " << passedY << std::endl; |
|
this->scrollY(passedY); |
|
}; |
|
onMousedown=[this](int passedX, int passedY) { |
|
//std::cout << "document left press" << std::endl; |
|
if (this->hoverComponent) { |
|
// if we're hovering over a component that's not focused |
|
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(); |
|
} |
|
} |
|
// set hover component as focused |
|
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 && 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 - focus: " << typeOfComponent(this->focusedComponent) << " key: " << key << " scancode: " << scancode << " mods: " << mods << std::endl; |
|
InputComponent *focusedInputComponent = dynamic_cast<InputComponent*>(this->focusedComponent.get()); |
|
if (focusedInputComponent) { |
|
//std::cout << "inputComponent is focused, key pressed " << key << " action: " <<action << std::endl; |
|
// FIXME: not going to get repeat events here... |
|
// action 1 is down, 0 is up, 2 is a repeat |
|
if (action == 0 || action == 2) { |
|
// key up |
|
// it's always uppercase... |
|
// FIXME: switch statement this |
|
if (key == GLFW_KEY_BACKSPACE) { |
|
focusedInputComponent->backSpace(); |
|
return; |
|
} |
|
if (key == GLFW_KEY_UP) { |
|
focusedInputComponent->updateCursor(0, -1); |
|
return; |
|
} |
|
if (key == GLFW_KEY_DOWN) { |
|
focusedInputComponent->updateCursor(0, 1); |
|
return; |
|
} |
|
if (key == GLFW_KEY_LEFT) { |
|
focusedInputComponent->updateCursor(-1, 0); |
|
return; |
|
} |
|
if (key == GLFW_KEY_RIGHT) { |
|
focusedInputComponent->updateCursor(1, 0); |
|
return; |
|
} |
|
if (key == GLFW_KEY_ENTER) { |
|
//std::cout << "DocumentComponent::onKeyUp - enter!" << std::endl; |
|
if (focusedInputComponent->multiLine) { |
|
// harfbuzz or freetype2 (something?) doesn't like \n //focusedInputComponent->value += "\r"; |
|
focusedInputComponent->addChar('\r'); |
|
} else { |
|
std::cout << "should submit form!" << std::endl; |
|
if (focusedInputComponent->onEnter) { |
|
focusedInputComponent->onEnter(focusedInputComponent->getValue()); |
|
} |
|
return; |
|
} |
|
} else { |
|
if (key < 256) { |
|
if (mods & GLFW_MOD_SHIFT) { |
|
// SHIFT |
|
if (key >= '1' && key <= '9') { |
|
key -= 16; |
|
} |
|
switch(key) { |
|
case GLFW_KEY_SLASH: key='?'; break; |
|
case GLFW_KEY_APOSTROPHE: key='"'; break; |
|
case GLFW_KEY_COMMA: key='<'; break; |
|
case GLFW_KEY_MINUS: key='_'; break; |
|
case GLFW_KEY_PERIOD: key='>'; break; |
|
case GLFW_KEY_SEMICOLON: key=':'; break; |
|
case GLFW_KEY_EQUAL: key='+'; break; |
|
case GLFW_KEY_LEFT_BRACKET: key='{'; break; |
|
case GLFW_KEY_BACKSLASH: key='|'; break; |
|
case GLFW_KEY_RIGHT_BRACKET: key='}'; break; |
|
case GLFW_KEY_GRAVE_ACCENT: key='~'; break; |
|
case GLFW_KEY_0: key=')'; break; |
|
default: |
|
// a key we don't care about atm |
|
break; |
|
} |
|
} 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'; |
|
} |
|
} |
|
focusedInputComponent->addChar(key); |
|
} // otherwise I think it's some weird control char |
|
} |
|
} |
|
} |
|
//std::cout << "nothing focused " << GLFW_RELEASE << "==" << action << std::endl; |
|
// was press but we don't get those events here |
|
if (key == GLFW_KEY_Q && action == GLFW_RELEASE) { |
|
std::cout << "Q was pressed. Exiting." << std::endl; |
|
exit(0); |
|
} |
|
if (key == GLFW_KEY_E && action == GLFW_RELEASE) { |
|
printf("Printing NodeTree\n\n"); |
|
printNode(this->domRootNode, 1); |
|
printf("\n\n"); |
|
} |
|
if (key == GLFW_KEY_D && action == GLFW_RELEASE) { |
|
printf("Printing ComponentTree\n\n"); |
|
Component::printComponentTree(this->layers[0], 0); |
|
printf("\n\n"); |
|
} |
|
if (key == GLFW_KEY_F && action == GLFW_RELEASE) { |
|
printf("Printing UI ComponentTree\n\n"); |
|
for(auto layer: this->win->ui->layers) { |
|
Component::printComponentTree(layer, 0); |
|
} |
|
printf("\n\n"); |
|
} |
|
if (key == GLFW_KEY_V && action == GLFW_RELEASE) { |
|
printf("Printing JS Variables\n\n"); |
|
//std::cout << "DocumentComponent::render - JS var state" << std::endl; |
|
for(auto it : this->mainScript->rootScope.locals.value) { |
|
std::cout << "[" << it.first << "=" << it.second << "]" << std::endl; |
|
} |
|
printf("\n\n"); |
|
} |
|
if (key == GLFW_KEY_T && action == GLFW_RELEASE) { |
|
browser->NextTheme(); |
|
} |
|
if (key == GLFW_KEY_N && action == GLFW_RELEASE) { |
|
browser->addWindow(); |
|
} |
|
// FIXME: probably should just direct set the scroll |
|
// we should have a function for setting scrollY position |
|
if (key == GLFW_KEY_HOME && (action == GLFW_RELEASE || action == GLFW_REPEAT)) { |
|
std::cout << "Home is/was pressed. Scrolling up." << std::endl; |
|
this->scrollY(INT_MAX); |
|
} |
|
if (key == GLFW_KEY_END && (action == GLFW_RELEASE || action == GLFW_REPEAT)) { |
|
std::cout << "End is/was pressed. Scrolling down." << std::endl; |
|
this->scrollY(-INT_MAX); |
|
} |
|
if (key == GLFW_KEY_PAGE_UP && (action == GLFW_RELEASE || action == GLFW_REPEAT)) { |
|
std::cout << "PgUp is/was pressed. Scrolling up." << std::endl; |
|
this->scrollY(170); |
|
} |
|
if (key == GLFW_KEY_PAGE_DOWN && (action == GLFW_RELEASE || action == GLFW_REPEAT)) { |
|
std::cout << "PgDn is/was pressed. Scrolling down." << std::endl; |
|
this->scrollY(-170); |
|
} |
|
if ((key == GLFW_KEY_J || key == GLFW_KEY_UP) && (action == GLFW_RELEASE || action == GLFW_REPEAT)) { |
|
std::cout << "J is/was pressed. Scrolling up." << std::endl; |
|
this->scrollY(4); |
|
} |
|
if ((key == GLFW_KEY_K || key == GLFW_KEY_DOWN) && (action == GLFW_RELEASE || action == GLFW_REPEAT)) { |
|
std::cout << "K is/was pressed. Scrolling down." << std::endl; |
|
this->scrollY(-4); |
|
} |
|
}; |
|
onKeyPress=[this](int key, int scancode, int action, int mods) { |
|
InputComponent *focusedInputComponent = dynamic_cast<InputComponent*>(this->focusedComponent.get()); |
|
if (focusedInputComponent) { |
|
return; |
|
} |
|
std::cout << "DocumentComponent onKeyPresss - no input component focused" << std::endl; |
|
int yOffsetScroll = 1; |
|
if (key == GLFW_KEY_PAGE_UP && (action == GLFW_PRESS || action == GLFW_REPEAT)) { |
|
std::cout << "PgUp is/was pressed. Scrolling down." << std::endl; |
|
this->scrollY(-yOffsetScroll*0.1); |
|
|
|
} |
|
if (key == GLFW_KEY_PAGE_DOWN && (action == GLFW_PRESS || action == GLFW_REPEAT)) { |
|
std::cout << "PgDn is/was pressed. Scrolling up." << std::endl; |
|
this->scrollY(yOffsetScroll*0.1); |
|
} |
|
|
|
// FIXME Scrolling with this can scroll past boundary whereas the same doesn't happen with |
|
// scrolling wheel |
|
if (key == GLFW_KEY_J && (action == GLFW_PRESS || action == GLFW_REPEAT)) { |
|
std::cout << "J is/was pressed. Scrolling down." << std::endl; |
|
this->scrollY(-yOffsetScroll*0.1); |
|
|
|
} |
|
// FIXME Scrolling with this can scroll past boundary whereas the same doesn't happen with |
|
// scrolling wheel |
|
if (key == GLFW_KEY_K && (action == GLFW_PRESS || action == GLFW_REPEAT)) { |
|
std::cout << "K is/was pressed. Scrolling up." << std::endl; |
|
this->scrollY(yOffsetScroll*0.1); |
|
} |
|
}; |
|
} |
|
|
|
void DocumentComponent::scrollY(int py) { |
|
//std::cout << "DocumentComponent::scrollY - adjusting Y by " << (py * 0.01) << std::endl; |
|
// we're going to reduce here, so we can get the full signal all the way here |
|
this->transformMatrix[13] -= py * 0.01; |
|
this->textureTransformMatrix[13] -= py * 0.01; |
|
//std::cout << "transformMatrix : " << this->transformMatrix[13] << std::endl; |
|
//std::cout << "textureTransformMatrix: " << this->textureTransformMatrix[13] << std::endl; |
|
|
|
// check bounds |
|
if (this->transformMatrix[13] < 2) { |
|
this->transformMatrix[13] = 2; |
|
} |
|
if (this->textureTransformMatrix[13] < 0) { |
|
this->textureTransformMatrix[13] = 0; |
|
} |
|
std::shared_ptr<Component> rootComponent = this->layers[0]; // we only have one layer (currently) |
|
// calculate scroll max by calculating how many screens are in the rootComponent's Height |
|
if (this->transformMatrix[13] > std::max( (rootComponent->height - rootComponent->y) / this->windowHeight * 2.0f, 2.0f)) { |
|
this->transformMatrix[13] = std::max( (rootComponent->height - rootComponent->y) / this->windowHeight * 2.0f, 2.0f); |
|
} |
|
if (this->textureTransformMatrix[13] > std::max( (rootComponent->height - rootComponent->y) / this->windowHeight * 2.0f, 2.0f) - 2.0) { |
|
this->textureTransformMatrix[13] = std::max( (rootComponent->height - rootComponent->y) / this->windowHeight * 2.0f, 2.0f) - 2.0; |
|
} |
|
this->transformMatrixDirty = true; |
|
this->win->renderDirty = true; |
|
} |
|
|
|
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(); |
|
component.reset(); |
|
// now delete self |
|
} |
|
|
|
void deleteNode(std::shared_ptr<Node> node) { |
|
for (std::shared_ptr<Node> 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<Node> rootNode) { |
|
std::shared_ptr<Component> rootComponent = this->layers[0]; // we only have one layer (currently) |
|
// reset rootComponent |
|
if (rootComponent) { |
|
deleteComponent(rootComponent); |
|
} |
|
if (domRootNode) { |
|
deleteNode(domRootNode); |
|
} |
|
// reset JS engine |
|
//mainScript.clear(); |
|
mainScript = std::make_shared<JavaScript>(); |
|
|
|
if (this->bootstrapScript) { |
|
mainScript->applyScope(this->bootstrapScript); |
|
mainScript->append(this->bootstrapScript); |
|
} else { |
|
std::cout << "DocumentComponent::setDOM - no bootstrap JS loaded\n"; |
|
} |
|
|
|
// reset scroll position |
|
//transformMatrix[13] = 2; |
|
//transformMatrixDirty = true; |
|
this->scrollY(0); |
|
|
|
// new root component |
|
rootComponent = std::make_shared<Component>(); |
|
rootComponent->name = "rootComponent of " + name; |
|
rootComponent->y = y; |
|
|
|
domRootNode = rootNode; |
|
//std::cout << "DocumentComponent::setDOM - printing nodes" << endl; printNode(domRootNode, 0); |
|
domDirty = true; |
|
} |
|
|
|
void DocumentComponent::render() { |
|
//std::cout << "DocumentComponent::render" << std::endl; |
|
std::shared_ptr<Component> rootComponent = this->layers[0]; // we only have one layer (currently) |
|
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<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl; |
|
|
|
//Component::printComponentTree(rootComponent, 0); |
|
domDirty = false; |
|
|
|
/* |
|
std::cout << "DocumentComponent::render - JS var state" << std::endl; |
|
for(auto it : this->mainScript->rootScope.variables) { |
|
std::cout << "[" << it.first << "=" << it.second << "]" << std::endl; |
|
} |
|
*/ |
|
|
|
//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)))); |
|
|
|
// FIXME recalculate both right? |
|
// 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; |
|
} |
|
|
|
// after we load in the document, allow scroll to work |
|
this->updateMouse(); |
|
} |
|
|
|
// 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<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl; |
|
|
|
Shader *textureShader = this->win->shaderLoader.getShader(VertexShader("TextureShader.vert"), |
|
FragmentShader("TextureShader.frag")); |
|
|
|
transformMatrixDirty = false; |
|
//} |
|
//std::cout << "DocumentComponent::render - renderDirty" << std::endl; |
|
textureShader->bind(); |
|
GLint transformLocation2 = textureShader->uniform("transform"); |
|
glUniformMatrix4fv(transformLocation2, 1, GL_FALSE, textureTransformMatrix); |
|
|
|
//std::cout << "DocumentComponent::render - start Box components" << std::endl; |
|
//renderBoxComponents(rootComponent); |
|
this->renderComponentType("input", rootComponent); |
|
this->renderComponentType("button", rootComponent); |
|
//std::cout << "DocumentComponent::render - end Box components" << std::endl; |
|
//textureShader->release(); // just set it to 0, bind is better call |
|
|
|
fontShader->bind(); |
|
//std::cout << "DocumentComponent::render - start components" << std::endl; |
|
//renderComponents(rootComponent); |
|
this->renderComponentType("text", rootComponent); |
|
|
|
//std::cout << "DocumentComponent::render - end components" << std::endl; |
|
fontShader->release(); |
|
} |
|
|
|
// 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, this->win, this); |
|
//std::cout << "DocumentComponent::createComponentTree" << std::endl; |
|
if (!component) { |
|
//std::cout << "DocumentComponent::createComponentTree - no component" << std::endl; |
|
return; |
|
} |
|
// don't build text node of button component because it'll mess with the picking |
|
if (typeOfComponent(component) == "button") { |
|
// if this button node has children, extract the text before discarding it |
|
if (node->children.size()) { |
|
TextNode *textNode = dynamic_cast<TextNode*>(node->children.front().get()); |
|
if (textNode) { |
|
//std::cout << "Button text: " << textNode->text << std::endl; |
|
ButtonComponent *buttonComponent = dynamic_cast<ButtonComponent*>(component.get()); |
|
if (buttonComponent) { |
|
// FIXME: strip out more than 1 new line |
|
std::string line(textNode->text); |
|
line.erase(line.find_last_not_of(" \t\n\r\f\v") + 1); |
|
buttonComponent->value = line; |
|
buttonComponent->resizeToTextSize(); |
|
buttonComponent->updateText(); |
|
} |
|
} |
|
} |
|
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) { |
|
// only get hits on leaves (and not to hit the rootComponent every time) |
|
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; |
|
//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) { |
|
//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) { |
|
//std::cout << "DocumentComponent::searchComponentTree - hit " << typeOfComponent(component) << " name: " << component->name << std::endl; |
|
return component; |
|
} |
|
} |
|
} else { |
|
for (std::shared_ptr<Component> child : component->children) { |
|
std::shared_ptr<Component> 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<TabbedComponent*>(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(this->currentURL); |
|
this->handleResource(res, currentURL.toString()); |
|
} |
|
|
|
void DocumentComponent::handleResource(WebResource &res, std::string url) { |
|
if (res.resourceType == ResourceType::INVALID) { |
|
logError() << "DocumentComponent::handleResource - Invalid resource type: " << res.raw << std::endl; |
|
|
|
std::shared_ptr<Node> rootNode = std::make_shared<Node>(NodeType::ROOT); |
|
std::shared_ptr<TagNode> tagNode = std::make_shared<TagNode>(); |
|
tagNode->tag="p"; |
|
// bind tag to root |
|
tagNode->parent = rootNode; |
|
rootNode->children.push_back(tagNode); |
|
|
|
|
|
std::shared_ptr<TextNode> textNode = std::make_shared<TextNode>(); |
|
textNode->text = "Invalid Resource Type, HTTP Result Status: " + 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); |
|
} |
|
|
|
//std::cout << "body: " << res.raw << std::endl; |
|
//std::cout << "type: " << res.resourceType << std::endl; |
|
|
|
// parse HTML |
|
if (res.resourceType == ResourceType::HTML) { |
|
HTMLParser parser; |
|
const std::clock_t begin = clock(); |
|
std::shared_ptr<Node> rootNode = parser.parse(res.raw); |
|
const std::clock_t end = clock(); |
|
logDebug() << "DocumentComponent::handleResource - Parsed document in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl; |
|
|
|
// send NodeTree to window |
|
//this->win->setDOM(rootNode); |
|
//printNode(rootNode, 0); |
|
|
|
// 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 << "DocumentComponent::handleResource - Rendering text document" << std::endl; |
|
std::shared_ptr<Node> rootNode = std::make_shared<Node>(NodeType::ROOT); |
|
std::shared_ptr<TagNode> tagNode = std::make_shared<TagNode>(); |
|
tagNode->tag = "p"; |
|
// bind tag to root |
|
tagNode->parent = rootNode; |
|
rootNode->children.push_back(tagNode); |
|
|
|
|
|
std::shared_ptr<TextNode> textNode = std::make_shared<TextNode>(); |
|
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 << "DocumentComponent::handleResource - I don't know how to render non-html files" << std::endl; |
|
} |
|
if (this->win) { |
|
this->win->renderDirty = true; |
|
} |
|
}
|
|
|