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.
 
 
 
 

1010 lines
43 KiB

#include "../../URL.h"
#include "Window.h"
#include "../../html/TagNode.h"
#include "../../html/TextNode.h"
#include "../../networking/HTTPRequest.h"
#include "../../html/HTMLParser.h"
#include "../../StringUtils.h"
#include "../../URL.h"
#include "../../Log.h"
#include "../components/BoxComponent.h"
#include "../components/AnimeComponent.h"
#include "../components/DocumentComponent.h"
#include "../components/TabbedComponent.h"
#include "../components/TextComponent.h"
#include "../components/TabbedComponent.h"
#include "../components/InputComponent.h"
#include "../opengl/Shader.h"
#include "../../browser.h"
#include <cmath>
#include <ctime>
#include <iostream>
extern std::unique_ptr<Browser> browser;
Window::~Window() {
glfwTerminate();
}
bool Window::init() {
if (!initGLFW()) {
return false;
}
if (!initGLEW()) {
return false;
}
initGL();
GLenum glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "window::init - post initGL - not ok: " << glErr << std::endl;
}
// set proper scroll position
transformMatrix[13] = 2;
transformMatrixDirty = true;
// configure root component
//rootComponent = std::make_shared<Component>();
rootComponent->name = "rootComponent";
//rootComponent->y = 0;
// set up our UI with a new window
// (re)build UI (top to bottom)
//std::cout << "Window::setDOM window size: " << windowWidth << "x" << windowHeight << std::endl;
// layout back to front
// 4 multigroups
// 1 -> mascot
// 2 -> tabbed
// 3 -> box
// 4 -> input
//Mascot
std::shared_ptr<AnimeComponent> mascot = std::make_unique<AnimeComponent>(windowWidth * 0.75f, 0.0f, 512.0f, 512.0f, windowWidth, windowHeight);
mascot->uiControl.x = { 75, 0 }; // 75% width
mascot->uiControl.y = { 0, 0 }; // 0px
mascot->uiControl.w = { 0, 512 }; // 512px
mascot->uiControl.h = { 0, 512 }; // 512px
mascot->boundToPage = false;
mascot->isPickable = false;
mascot->name = "mascot";
// add it to our components
mascot->setParent(rootComponent);
rootComponent->children.push_back(mascot);
// create tabbed component
// doc text is still showing up ontop of buttons
std::shared_ptr<TabbedComponent> tabbedComponent = std::make_shared<TabbedComponent>(0, 0, static_cast<float>(windowWidth), static_cast<float>(windowHeight - 64), windowWidth, windowHeight);
tabbedComponent->name = "tabbedComponent";
tabbedComponent->y = -64;
tabbedComponent->uiControl.x = { 0 , 0 }; // 0
tabbedComponent->uiControl.y = { 0 , -64 }; // -64px
tabbedComponent->uiControl.w = { 100, 0 }; // 100%
tabbedComponent->uiControl.h = { 100, -64 }; // 100% - 64px
tabbedComponent->boundToPage = false;
tabbedComponent->win = shared_from_this();
// add it to our components
tabbedComponent->setParent(rootComponent);
rootComponent->children.push_back(tabbedComponent);
// Nav background
std::shared_ptr<BoxComponent> navBackground = std::make_unique<BoxComponent>(0.0f, windowHeight - 64.0f, windowWidth, 64.0f, 0x888888FF, windowWidth, windowHeight);
//navBackground->y = -64;
navBackground->uiControl.y = { 100, -64 }; // -64px
navBackground->uiControl.w = { 100, 0 }; // 100%
navBackground->uiControl.h = { 0, 64 }; // 64px
navBackground->isPickable = false;
navBackground->name = "navBackground";
navBackground->boundToPage = false;
// add it to our components
navBackground->setParent(rootComponent);
rootComponent->children.push_back(navBackground);
std::shared_ptr<BoxComponent> navBackButton = std::make_unique<BoxComponent>(32.0f, windowHeight - 48.0f, 32.0f, 32.0f, 0x000000FF, windowWidth, windowHeight);
navBackButton->uiControl.x = { 0, 32 }; // 32px
navBackButton->uiControl.y = { 100, -48 }; // top - 48px
navBackButton->uiControl.w = { 0, 32 }; // 32px
navBackButton->uiControl.h = { 0, 32 }; // 32px
navBackButton->onClick=[this]() {
//std::cout << "Back" << std::endl;
TabbedComponent *pTabComponent = dynamic_cast<TabbedComponent*>(this->tabComponent.get());
if (pTabComponent) {
if (pTabComponent->selectedTabId) {
pTabComponent->selectedTab->get()->history->back();
}
}
};
navBackButton->onMouseover = [navBackButton, this]() {
//std::cout << "navBackButton->onMouseover" << std::endl;
navBackButton->changeColor(0x880000FF);
this->renderDirty = true;
};
navBackButton->onMouseout = [navBackButton, this]() {
//std::cout << "navBackButton->onMouseout" << std::endl;
navBackButton->changeColor(0x000000FF);
this->renderDirty = true;
};
navBackButton->name = "navBackButton";
navBackButton->boundToPage = false;
// add it to our components
navBackButton->setParent(rootComponent);
rootComponent->children.push_back(navBackButton);
std::shared_ptr<BoxComponent> navForwardButton = std::make_unique<BoxComponent>(74.0f, windowHeight - 48.0f, 32.0f, 32.0f, 0x000000FF, windowWidth, windowHeight);
navForwardButton->uiControl.x = { 0, 74 }; // 74px
navForwardButton->uiControl.y = { 100, -48 }; // top - 48px
navForwardButton->uiControl.w = { 0, 32 }; // 32px
navForwardButton->uiControl.h = { 0, 32 }; // 32px
navForwardButton->onClick=[this]() {
//std::cout << "Forward" << std::endl;
TabbedComponent *pTabComponent = dynamic_cast<TabbedComponent*>(this->tabComponent.get());
if (pTabComponent) {
if (pTabComponent->selectedTabId) {
pTabComponent->selectedTab->get()->history->forward();
}
}
};
navForwardButton->onMouseover = [navForwardButton, this]() {
//std::cout << "navForwardButton->onMouseover" << std::endl;
navForwardButton->changeColor(0x008800FF);
this->renderDirty = true;
};
navForwardButton->onMouseout = [navForwardButton, this]() {
//std::cout << "navForwardButton->onMouseout" << std::endl;
navForwardButton->changeColor(0x000000FF);
this->renderDirty = true;
};
navForwardButton->name = "navForwardButton";
navForwardButton->boundToPage = false;
// add it to our components
//navForwardButton->parent = rootComponent;
navForwardButton->setParent(rootComponent);
rootComponent->children.push_back(navForwardButton);
std::shared_ptr<BoxComponent> navRefreshButton = std::make_unique<BoxComponent>(116.0f, windowHeight - 48.0f, 32.0f, 32.0f, 0x000000FF, windowWidth, windowHeight);
navRefreshButton->uiControl.x = { 0, 116 }; // 116px
navRefreshButton->uiControl.y = { 100, -48 }; // top - 48px
navRefreshButton->uiControl.w = { 0, 32 }; // 32px
navRefreshButton->uiControl.h = { 0, 32 }; // 32px
navRefreshButton->name = "navRefreshButton";
navRefreshButton->boundToPage = false;
navRefreshButton->onClick=[this]() {
std::shared_ptr<DocumentComponent> docComponent = this->getActiveDocumentComponent();
if (docComponent) {
std::cout << "Refreshing " << docComponent->currentURL << std::endl;
// now tell it to navigate somewhere
docComponent->navTo(docComponent->currentURL.toString());
}
};
navRefreshButton->onMouseover = [navRefreshButton, this]() {
//std::cout << "navRefreshButton->onMouseover" << std::endl;
navRefreshButton->changeColor(0x000088FF);
this->renderDirty = true;
};
navRefreshButton->onMouseout = [navRefreshButton, this]() {
//std::cout << "navRefreshButton->onMouseout" << std::endl;
navRefreshButton->changeColor(0x000000FF);
this->renderDirty = true;
}; // add it to our components
navRefreshButton->setParent(rootComponent);
rootComponent->children.push_back(navRefreshButton);
// Address Bar
std::shared_ptr<InputComponent> navAddressBar = std::make_unique<InputComponent>(192.0f, windowHeight - 48.0f, windowWidth - 384.0f, 24.0f, windowWidth, windowHeight);
// add it to our components
navAddressBar->x = 192.0f;
navAddressBar->y = windowHeight-48.0f;
navAddressBar->uiControl.x = { 0, 192 }; // 192px
navAddressBar->uiControl.y = { 100, -48 }; // top - 48px
navAddressBar->uiControl.w = { 100, -384 }; // w - 384px
navAddressBar->uiControl.h = { 0, 24 }; // 24px
navAddressBar->name = "navAddressBar";
navAddressBar->boundToPage = false;
navAddressBar->win = shared_from_this();
//navAddressBar->y = -48; // this works but breaks picking
//navAddressBar->value = currentURL.toString();
//navAddressBar->updateText();
navAddressBar->onEnter=[this](std::string value) {
std::cout << "navAddressBar::onEnter got " << value << std::endl;
TabbedComponent *p_tabComponent = dynamic_cast<TabbedComponent*>(this->tabComponent.get());
if (p_tabComponent) {
if (!p_tabComponent->tabs.size()) {
p_tabComponent->addTab("Loading...");
p_tabComponent->selectTab(p_tabComponent->tabs.back());
this->renderDirty = true;
this->render(); // display loading tab before IO
}
}
std::shared_ptr<DocumentComponent> docComponent = this->getActiveDocumentComponent();
if (docComponent) {
std::cout << "Found an active document component" << std::endl;
// now tell it to navigate somewhere
docComponent->navTo(value);
} else {
std::cout << "No active document component" << std::endl;
}
};
this->addressComponent = navAddressBar;
// we do this to prevent flashing
render();
renderDirty = true;
//std::cout << "placing inputComponent at " << static_cast<int>(navAddressBar->x) << "," << static_cast<int>(navAddressBar->y) << std::endl;
navAddressBar->setParent(rootComponent);
rootComponent->children.push_back(navAddressBar);
/*
// textTest
// 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);
std::shared_ptr<TextComponent> textTest = std::make_unique<TextComponent>("Test", -99, -99, 12, false, 0x000000ff, windowWidth, windowHeight);
textTest->x=192.0f;
textTest->y=-64.0f;
textTest->isPickable = false;
// add it to our components
textTest->parent = rootComponent;
rootComponent->children.push_back(textTest);
// we really don't want to layout because that's moves us relative to parent
// and this is static layout we want, not a free flow
//textTest->layout(); // to rasterize
textTest->resize(windowWidth, windowHeight);
*/
// set tab component
this->tabComponent = tabbedComponent;
return true;
}
bool Window::initGLFW() {
if (!glfwInit()) {
std::cout << "Could not initialize GLFW" << std::endl;
return false;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
glfwSetErrorCallback([](int error, const char* description) {
std::cout << "glfw error [" << error << "]:" << description << std::endl;
});
GLFWwindow *context = nullptr;
if (browser->windows.size()) {
context = browser->windows.front().get()->window;
}
// after title, it's monitor
window = glfwCreateWindow(windowWidth, windowHeight, "NetRunner", nullptr, context);
if (!window) {
glfwTerminate();
std::cout << "Could not create window" << std::endl;
return false;
}
// replace first parameter of all these callbacks with our window object instead of a GLFWwindow
glfwSetWindowUserPointer(window, this);
// set window w/h
//glfwGetFramebufferSize(window, &windowWidth, &windowHeight); // in pixels
glfwGetWindowSize(window, &windowWidth, &windowHeight); // use screen coordinates (not pixels) more retina friendly
// set up event callbacks
glfwSetFramebufferSizeCallback(window, [](GLFWwindow *win, int width, int height) {
glViewport(0, 0, width, height);
});
glfwSetWindowSizeCallback(window, [](GLFWwindow *win, int width, int height) {
Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
thiz->onResize(width, height);
});
cursorHand = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
cursorArrow = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
cursorIbeam = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
glfwSetCursorPosCallback(window, [](GLFWwindow *win, double xPos, double yPos) {
Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
thiz->cursorX = xPos;
thiz->cursorY = yPos;
//std::cout << "Window::Window:onMousemove - at " << static_cast<int>(xPos) << "," << static_cast<int>(yPos) << std::endl;
if (xPos < 0 || yPos < 0) return;
if (xPos > thiz->windowWidth || yPos > thiz->windowHeight) return;
// p. much worthless on double
/*
static double lx = 0;
static double ly = 0;
if (lx == xPos && ly == yPos) {
return;
}
lx = xPos;
ly = yPos;
std::cout << "Window::Window:onMousemove - noCache" << std::endl;
*/
//std::cout << "Window::Window:onMousemove - begin search" << std::endl;
//std::cout << "Window::Window:onMousemove - windowHeight: " << thiz->windowHeight << " cursorY: " << thiz->cursorY << " scrollY: " << thiz->transformMatrix[13] << std::endl;
std::shared_ptr<Component> newHover = thiz->searchComponentTree(thiz->rootComponent, thiz->cursorX, (thiz->windowHeight - thiz->cursorY) + ((-thiz->transformMatrix[13] / 2) * thiz->windowHeight));
if (newHover != thiz->hoverComponent) {
if (thiz->hoverComponent && thiz->hoverComponent->onMouseout) {
thiz->hoverComponent->onMouseout();
}
if (newHover && newHover->onMouseover) {
newHover->onMouseover();
}
thiz->hoverComponent = newHover;
}
if (thiz->hoverComponent) {
//std::cout << "Window::Window:onMousemove - hover " << typeOfComponent(thiz->hoverComponent) << std::endl;
if (thiz->hoverComponent->onClick) {
glfwSetCursor(thiz->window, thiz->cursorHand);
} else {
TextComponent *textComponent = dynamic_cast<TextComponent*>(thiz->hoverComponent.get());
InputComponent *inputComponent = dynamic_cast<InputComponent*>(thiz->hoverComponent.get());
if (textComponent || inputComponent) {
glfwSetCursor(thiz->window, thiz->cursorIbeam);
}
// otherwise we could be an Box/Anime or Document Component
}
if (thiz->hoverComponent->onMousemove) {
// this could communicate the cursor to use
thiz->hoverComponent->onMousemove(thiz->cursorX, thiz->cursorY);
}
} else {
glfwSetCursor(thiz->window, thiz->cursorArrow);
}
});
glfwSetScrollCallback(window, [](GLFWwindow *win, double xOffset, double yOffset) {
Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
// yOffset is a delta vector
thiz->transformMatrix[13] += -yOffset * 0.1;
if (thiz->hoverComponent) {
//std::cout << "scroll - hovering over a component" << std::endl;
if (thiz->hoverComponent->onWheel) {
//std::cout << "scroll - hovering over a scrollabel component" << std::endl;
thiz->hoverComponent->onWheel(xOffset * 100, yOffset * 100);
}
}
// 2.0 is one screen height
// we draw from 0 downwards (y+), so can't scroll past our starting draw point
if (thiz->transformMatrix[13] < 2) {
thiz->transformMatrix[13] = 2;
}
// calculate scroll max by calculating how many screens are in the rootComponent's Height
if (thiz->transformMatrix[13] > std::max( thiz->rootComponent->height / thiz->windowHeight * 2.0f, 2.0f)) {
thiz->transformMatrix[13] = std::max( thiz->rootComponent->height / thiz->windowHeight * 2.0f, 2.0f);
}
//std::cout << "scroll y is at " << thiz->transformMatrix[13] << "/" << static_cast<int>((thiz->transformMatrix[13]*10000) << std::endl;
thiz->transformMatrixDirty = true;
});
glfwSetMouseButtonCallback(window, [](GLFWwindow *win, int button, int action, int mods) {
Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
//std::cout << "left press" << std::endl;
if (thiz->hoverComponent) {
//std::cout << "focus event" << std::endl;
if (thiz->focusedComponent != thiz->hoverComponent) {
// blur old component
if (thiz->focusedComponent) {
if (thiz->focusedComponent->onBlur) {
thiz->focusedComponent->onBlur();
}
}
// focus new component
if (thiz->hoverComponent->onFocus) {
thiz->hoverComponent->onFocus();
}
}
thiz->focusedComponent = thiz->hoverComponent;
if (thiz->focusedComponent->onMousedown) {
//std::cout << "click event" << std::endl;
thiz->focusedComponent->onMousedown(thiz->cursorX, thiz->cursorY);
}
}
}
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE) {
//std::cout << "left release" << std::endl;
if (thiz->hoverComponent) {
//std::cout << "focus event" << std::endl;
if (thiz->focusedComponent != thiz->hoverComponent) {
// blur old component
if (thiz->focusedComponent) {
if (thiz->focusedComponent->onBlur) {
thiz->focusedComponent->onBlur();
}
}
// focus new component
if (thiz->hoverComponent->onFocus) {
thiz->hoverComponent->onFocus();
}
}
thiz->focusedComponent = thiz->hoverComponent;
/*
InputComponent *inputComponent = dynamic_cast<InputComponent*>(thiz->focusedComponent.get());
if (inputComponent) {
std::cout << "inputComponent focus" << std::endl;
}
*/
if (thiz->focusedComponent && thiz->focusedComponent->onMouseup) {
//std::cout << "click event" << std::endl;
thiz->focusedComponent->onMouseup(thiz->cursorX, thiz->cursorY);
}
if (thiz->focusedComponent && thiz->focusedComponent->onClick) {
//std::cout << "click event" << std::endl;
thiz->focusedComponent->onClick();
}
}
}
});
// works with utf-32 but we'll lkeep the low level for now
/*
glfwSetCharCallback(window, [](GLFWwindow* win, unsigned int codepoint) {
Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
std::cout << "Window::glfwSetCharCallback - codepoint: " << codepoint << std::endl;
});
*/
glfwSetKeyCallback(window, [](GLFWwindow *win, int key, int scancode, int action, int mods) {
Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
//std::cout << "Key " << key << " action: " << action << "mods: " << mods << std::endl;
if (key == GLFW_KEY_N && action == GLFW_RELEASE && mods == GLFW_MOD_CONTROL) {
browser->addWindow();
return;
}
// we're focused on something
//std::cout << "glfwSetKeyCallback" << std::endl;
if (thiz->focusedComponent) {
//std::cout << "glfwSetKeyCallback - focused on " << typeOfComponent(thiz->focusedComponent) << std::endl;
TabbedComponent *p_tabComponent = dynamic_cast<TabbedComponent*>(thiz->focusedComponent.get());
if (p_tabComponent) {
// repeat or key up
if (action == 2 || action == 0) {
if (p_tabComponent->onKeyPress) {
p_tabComponent->onKeyPress(key, scancode, action, mods);
}
if (action == 0) {
if (p_tabComponent->onKeyup) {
p_tabComponent->onKeyup(key, scancode, action, mods);
}
}
}
return;
}
DocumentComponent *docComponent = dynamic_cast<DocumentComponent*>(thiz->focusedComponent.get());
if (docComponent) {
if (action == 0) {
if (docComponent->onKeyup) {
docComponent->onKeyup(key, scancode, action, mods);
}
}
return;
}
InputComponent *inputComponent = dynamic_cast<InputComponent*>(thiz->focusedComponent.get());
if (inputComponent) {
//std::cout << "inputComponent is focsued, 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;
if (inputComponent->onEnter) {
inputComponent->onEnter(inputComponent->value);
}
} 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
}
}
return;
}
}
switch (key) {
case GLFW_KEY_1:
case GLFW_KEY_2:
case GLFW_KEY_3:
case GLFW_KEY_4:
case GLFW_KEY_5:
case GLFW_KEY_6:
case GLFW_KEY_7:
case GLFW_KEY_8:
case GLFW_KEY_9:
case GLFW_KEY_0:
std::cout << "Key was pressed: " << key << std::endl;
//thiz->transformMatrixIndex = key - GLFW_KEY_0;
//std::cout << thiz->transformMatrixIndex << std::endl;
break;
default:
break;
}
if (key == GLFW_KEY_Q && action == GLFW_PRESS) {
std::cout << "Q was pressed. Exiting." << std::endl;
exit(0);
}
if (key == GLFW_KEY_E && action == GLFW_RELEASE) {
printf("Printing NodeTree\n\n");
TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
if (p_TabComponent) {
DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
if (p_DocComponent) {
printNode(p_DocComponent->domRootNode, 1);
}
}
printf("\n\n");
}
if (key == GLFW_KEY_D && action == GLFW_RELEASE) {
printf("Printing ComponentTree\n\n");
TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
if (p_TabComponent) {
DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
if (p_DocComponent) {
Component::printComponentTree(p_DocComponent->rootComponent, 0);
}
}
printf("\n\n");
}
if (key == GLFW_KEY_F && action == GLFW_RELEASE) {
printf("Printing UI ComponentTree\n\n");
Component::printComponentTree(thiz->rootComponent, 0);
printf("\n\n");
}
int yOffsetScroll = 1;
if (key == GLFW_KEY_PAGE_UP && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
if (p_TabComponent) {
DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
if (p_DocComponent) {
p_DocComponent->transformMatrix[13] += -yOffsetScroll * 0.1;
p_DocComponent->transformMatrixDirty = true;
thiz->renderDirty = true;
}
}
}
if (key == GLFW_KEY_PAGE_DOWN && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
if (p_TabComponent) {
DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
if (p_DocComponent) {
p_DocComponent->transformMatrix[13] += yOffsetScroll * 0.1;
p_DocComponent->transformMatrixDirty = true;
thiz->renderDirty = true;
}
}
}
// 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;
TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
if (p_TabComponent) {
DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
if (p_DocComponent) {
p_DocComponent->transformMatrix[13] += yOffsetScroll * 0.1;
p_DocComponent->transformMatrixDirty = true;
thiz->renderDirty = true;
}
}
}
// 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;
TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
if (p_TabComponent) {
DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
if (p_DocComponent) {
p_DocComponent->transformMatrix[13] += -yOffsetScroll * 0.1;
p_DocComponent->transformMatrixDirty = true;
thiz->renderDirty = true;
}
}
}
});
glfwMakeContextCurrent(window);
return true;
}
bool Window::initGLEW() const {
glewExperimental = GL_TRUE;
const GLenum err = glewInit();
if (err != GLEW_OK) {
std::cout << "Could not initialize GLEW: " << glewGetErrorString(err) << std::endl;
return false;
}
return true;
}
bool Window::initGL() {
const GLubyte *renderer = glGetString(GL_RENDERER);
const GLubyte *version = glGetString(GL_VERSION);
std::cout << "Renderer: " << renderer << std::endl;
std::cout << "Version: " << version << std::endl;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0.8f, 0.8f, 0.8f, 0.8f);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GLenum glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "window::initGL - blend, clear, texParam - not ok: " << glErr << std::endl;
}
//std::cout << "OpenGL is set up" << std::endl;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
return true;
}
void Window::onResize(int passedWidth, int passedHeight) {
this->windowWidth = passedWidth;
this->windowHeight = passedHeight;
this->delayResize = 1;
}
void Window::resize() {
//std::cout << "Window::resize" << std::endl;
//this->printComponentTree(rootComponent, 0);
const std::clock_t begin = clock();
rootComponent->windowWidth = windowWidth;
rootComponent->windowHeight = windowHeight;
rootComponent->layout();
const std::clock_t end = clock();
std::cout << "Window::resize - resized components in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
//this->printComponentTree(rootComponent, 0);
// 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;
}
renderDirty = true;
}
void Window::render() {
if (delayResize) {
delayResize--;
if (delayResize) {
return;
}
//std::cout << "Window::render - restarting drawing" << std::endl;
this->resize();
}
/*
if (domDirty) {
const std::clock_t begin = clock();
createComponentTree(domRootNode, rootComponent);
const std::clock_t end = clock();
std::cout << "built & laid out window components in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
//printComponentTree(rootComponent, 0);
//std::cout << "Window printComponentTree end" << std::endl;
domDirty = false;
renderDirty = true;
}
*/
if (renderDirty || transformMatrixDirty) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GLenum glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "window::render - box render start - not ok: " << glErr << std::endl;
}
Shader *textureShader = shaderLoader.getShader(VertexShader("TextureShader.vert"),
FragmentShader("TextureShader.frag"));
if (!textureShader) {
std::cout << "Cant load shader" << std::endl;
return;
}
textureShader->bind();
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "window::render - glUseProgram - not ok: " << glErr << std::endl;
}
// draw anime 1st, tabbed components 2nd
// then box shit
// text last
renderComponentType("anime", rootComponent);
renderComponentType("tab", rootComponent);
textureShader->bind();
//GLint transformLocation = glGetUniformLocation(textureProgram, "transform");
//glUniformMatrix4fv(transformLocation, 1, GL_FALSE, transformMatrix);
renderComponentType("box", rootComponent);
renderComponentType("input", rootComponent);
// it's quick but done on scroll
Shader *fontShader = shaderLoader.getShader(VertexShader("FontShader.vert"),
FragmentShader("FontShader.frag"));
fontShader->bind();
// reset both, since components can change this
// god we may have to reset this after each component render...
// maybe we don't need too
if (transformMatrixDirty) {
//const std::clock_t begin = clock();
GLint transformLocation = fontShader->uniform("transform");
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "window::render - glGetUniformLocation - not ok: " << glErr << std::endl;
}
glUniformMatrix4fv(transformLocation, 1, GL_FALSE, transformMatrix);
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "window::render - glUniformMatrix4fv - not ok: " << glErr << std::endl;
}
// didn't change much (could be we didnt' select the textureProgram first)
//GLint transformLocation2 = glGetUniformLocation(textureProgram, "transform");
//glUniformMatrix4fv(transformLocation2, 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;
transformMatrixDirty = false;
}
//renderComponents(rootComponent);
renderComponentType("text", rootComponent);
glfwSwapBuffers(window);
// update 2nd buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
textureShader->bind();
renderComponentType("anime", rootComponent);
renderComponentType("tab", rootComponent);
textureShader->bind();
renderComponentType("box", rootComponent);
renderComponentType("input", rootComponent);
// it's quick but done on scroll
fontShader->bind();
renderComponentType("text", rootComponent);
glfwSwapBuffers(window);
renderDirty = false;
}
glfwSwapBuffers(window);
glfwPollEvents();
}
/*
void Window::setDOM(const std::shared_ptr<Node> rootNode) {
// reset scroll position
transformMatrix[13] = 2;
transformMatrixDirty = true;
if (!tabComponent) {
std::cout << "Window::setDOM - no tabComponent" << std::endl;
return;
}
std::cout << "Window::setDOM - tabComponent is already set up" << std::endl;
TabbedComponent *tabbedComponent = dynamic_cast<TabbedComponent*>(tabComponent.get());
if (!tabbedComponent->tabs.size()) {
// if no tabs, make one
tabbedComponent->addTab("New Tab");
//tabbedComponent->selectTab(tabbedComponent->tabs.back()->id);
tabbedComponent->selectTab(tabbedComponent->tabs.back());
}
tabbedComponent->loadDomIntoTab(rootNode, currentURL.toString());
}
*/
void Window::createComponentTree(const std::shared_ptr<Node> node, const std::shared_ptr<Component> &parentComponent) {
std::shared_ptr<Component> component = componentBuilder.build(node, parentComponent, shared_from_this());
// ComponentBuilder now calls setParent and setParents adds children
//component->setParent(parentComponent);
// set parent of the newly created component
//component->parent = parentComponent;
//if (parentComponent) {
// add new component as child to parent
//parentComponent->children.push_back(component);
//}
if (component && component==this->rootComponent) {
// if this is the root node
component->reqWidth = windowWidth;
component->reqHeight = windowHeight;
}
if (!node) {
return;
}
// create children elements
for (std::shared_ptr<Node> child : node->children) {
createComponentTree(child, component);
}
// update parents, creation brings them up to date
//updateComponentSize(component);
}
void Window::renderComponentType(std::string str, std::shared_ptr<Component> component) {
if (!component) {
std::cout << "Window::renderComponentType - got null passed" << std::endl;
return;
}
if (typeOfComponent(component) == str) {
// how slow is this?
if (str == "doc") {
DocumentComponent *docComponent = dynamic_cast<DocumentComponent*>(component.get());
docComponent->render();
} else if (str =="tab") {
TabbedComponent *pTabComponent = dynamic_cast<TabbedComponent*>(component.get());
pTabComponent->render();
} else if (str =="text") {
TextComponent *textComponent = dynamic_cast<TextComponent*>(component.get());
textComponent->render();
} else if (str =="input") {
InputComponent *inputComponent = dynamic_cast<InputComponent*>(component.get());
inputComponent->render();
} else if (str =="anime") {
AnimeComponent *animeComponent = dynamic_cast<AnimeComponent*>(component.get());
animeComponent->render();
} else if (str =="box") {
//AnimeComponent *animeComponent = dynamic_cast<AnimeComponent*>(component.get());
//if (!animeComponent) {
BoxComponent *boxComponent = dynamic_cast<BoxComponent*>(component.get());
boxComponent->render();
//}
} else {
std::cout << "Unknown type " << str << std::endl;
}
//} else {
//std::cout << "type: " << typeOfComponent(component) << "!=" << str << std::endl;
}
// is this needed?
if (component->children.empty()) {
return;
}
for (std::shared_ptr<Component> &child : component->children) {
this->renderComponentType(str, child);
}
}
// used for picking
// should return multiple components
std::shared_ptr<Component> Window::searchComponentTree(const std::shared_ptr<Component> &component, const int x, const int y) {
if (component->children.empty()) {
// x,y: 0,0 is the upper left
//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;
int ty = component->y;
int ty1 = component->height + component->y;
if (ty < 0) {
ty = 0;
ty1 += 64; // FIXME: hack (for what?)
}
//std::cout << "Window::searchComponentTree - y search: " << ty1 << ">" << static_cast<int>(this->windowHeight + y) << ">" << static_cast<int>(ty) << std::endl;
if (ty1 > this->windowHeight + y && this->windowHeight + y > ty) {
//std::cout << "y match" << std::endl;
if (component->x < x && component->x + component->width > x) {
//std::cout << "Window::searchComponentTree - hit " << typeOfComponent(component) << std::endl;
return component;
}
}
}
else {
for (std::shared_ptr<Component> child : component->children) {
if (!child->isPickable) continue;
std::shared_ptr<Component> found = searchComponentTree(child, x, y);
if (found) {
return found;
}
}
}
return nullptr;
}
std::shared_ptr<DocumentComponent> Window::getActiveDocumentComponent() {
// need to cast it first
TabbedComponent *p_tabComponent = dynamic_cast<TabbedComponent*>(this->tabComponent.get());
if (!p_tabComponent) {
std::cout << "Window::getActiveDocumentComponent - No active tab" << std::endl;
return nullptr;
}
std::shared_ptr<DocumentComponent> docComponent = std::static_pointer_cast<DocumentComponent>(p_tabComponent->documentComponent);
if (!docComponent) {
std::cout << "Window::getActiveDocumentComponent - No active document" << std::endl;
return nullptr;
}
return docComponent;
}
/*
// moving naviagtion closer to window, as window is now the owner of currentURL
// preparation for multiple HTML documents
void Window::navTo(std::string url) {
logDebug() << "Window::navTo(" << url << ")" << std::endl;
URL link=URL(url);
logDebug() << "Window::navTo - URL marshalled [" << link << "]" << std::endl;
currentURL = currentURL.merge(link);
logDebug() << "go to: " << currentURL << std::endl;
focusedComponent = nullptr;
hoverComponent = nullptr;
setWindowContent(currentURL);
}
// tried to make a window method
void handleRequest(std::shared_ptr<Window> tWin, const HTTPResponse &response) {
std::cout << "Window:::handleRequest - statusCode: " << response.statusCode << std::endl;
if (response.statusCode == 200) {
const std::unique_ptr<HTMLParser> parser = std::make_unique<HTMLParser>();
const std::clock_t begin = clock();
std::shared_ptr<Node> rootNode = parser->parse(response.body);
const std::clock_t end = clock();
std::cout << "Parsed document in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
tWin->setDOM(rootNode);
}
else if (response.statusCode == 301) {
std::string location;
if (response.properties.find("Location")==response.properties.end()) {
if (response.properties.find("location")==response.properties.end()) {
std::cout << "::handleRequest - got 301 without a location" << std::endl;
for(auto const &row : response.properties) {
std::cout << "::handleRequest - " << row.first << "=" << response.properties.at(row.first) << std::endl;
}
return;
} else {
location = response.properties.at("location");
}
} else {
location = response.properties.at("Location");
}
std::cout << "Redirect To: " << location << std::endl;
std::tuple<std::unique_ptr<URL>,enum URIParseError> result = parseUri(location);
if (std::get<1>(result) != URI_PARSE_ERROR_NONE) {
// TODO We probably wanna handle this better..
std::cerr << "error parsing uri" << std::endl;
return;
}
std::unique_ptr<URL> uri = std::move(std::get<0>(result));
const std::unique_ptr<HTTPRequest> request = std::make_unique<HTTPRequest>(std::move(uri));
request->sendRequest([tWin](const HTTPResponse &response2) {
handleRequest(tWin, response2);
});
return;
}
else {
std::cout << "Unknown Status Code: " << response.statusCode << std::endl;
}
}
*/