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.
716 lines
31 KiB
716 lines
31 KiB
#include "app.h" |
|
#include "../tools/Scheduler.h" |
|
#include "../interfaces/components/TabbedComponent.h" |
|
#include "../parsers/markup/ntrml/NTRMLParser.h" |
|
#include "../parsers/markup/html/HTMLParser.h" |
|
|
|
#include "../interfaces/components/DocumentComponent.h" |
|
//#include "interfaces/components/ComponentBuilder.h" |
|
#include "../interfaces/components/InputComponent.h" |
|
#include "../interfaces/components/ImageComponent.h" |
|
#include "../interfaces/components/TabbedComponent.h" |
|
#include "../interfaces/components/ButtonComponent.h" |
|
|
|
#include <fstream> |
|
#ifndef _WIN32 |
|
#include <glob.h> |
|
#else |
|
#include "../win32-glob.h" |
|
#endif |
|
|
|
// why can't I const this? |
|
std::unique_ptr<Scheduler> scheduler = std::make_unique<Scheduler>(); |
|
|
|
void doOnClick(std::string type, Component *component, Window *win) { |
|
if (type=="back") { |
|
component->onClick=[win]() { |
|
std::cout << "Browser::doOnClick.navBackButton->onClick - Back" << std::endl; |
|
TabbedComponent *pTabComponent = dynamic_cast<TabbedComponent*>(win->tabComponent.get()); |
|
if (pTabComponent) { |
|
if (pTabComponent->selectedTabId) { |
|
pTabComponent->selectedTab->get()->history->print(); |
|
pTabComponent->selectedTab->get()->history->back(); |
|
} else { |
|
std::cout << "Browser::doOnClick.navBackButton->onClick - no tab selected" << std::endl; |
|
} |
|
} else { |
|
std::cout << "Browser::doOnClick.navBackButton->onClick - no tabbed component" << std::endl; |
|
} |
|
}; |
|
} else if (type=="forward") { |
|
component->onClick=[win]() { |
|
//std::cout << "Forward" << std::endl; |
|
TabbedComponent *pTabComponent = dynamic_cast<TabbedComponent*>(win->tabComponent.get()); |
|
if (pTabComponent) { |
|
if (pTabComponent->selectedTabId) { |
|
pTabComponent->selectedTab->get()->history->forward(); |
|
} |
|
} |
|
}; |
|
} else if (type=="refresh") { |
|
component->onClick=[win]() { |
|
std::shared_ptr<DocumentComponent> docComponent = win->getActiveDocumentComponent(); |
|
if (docComponent) { |
|
std::cout << "Refreshing " << docComponent->currentURL << std::endl; |
|
// now tell it to navigate somewhere |
|
docComponent->navTo(docComponent->currentURL.toString()); |
|
} |
|
}; |
|
} else if (type=="go") { |
|
component->onClick=[win]() { |
|
// get address bar value and nav away |
|
std::string value; |
|
InputComponent *p_addressComponent = dynamic_cast<InputComponent*>(win->addressComponent.get()); |
|
if (!p_addressComponent) { |
|
std::cout << "Browser:zdoOnClick.navAddressBar->onClick - can't find address bar in windows " << std::endl; |
|
return; |
|
} |
|
//std::cout << "Browser:zdoOnClick.navAddressBar->onClick - got " << value << std::endl; |
|
|
|
TabbedComponent *p_tabComponent = dynamic_cast<TabbedComponent*>(win->tabComponent.get()); |
|
if (p_tabComponent) { |
|
if (!p_tabComponent->tabs.size()) { |
|
p_tabComponent->addTab("Loading..."); |
|
p_tabComponent->selectTab(p_tabComponent->tabs.back()); |
|
|
|
win->renderDirty = true; |
|
win->render(); // display loading tab before IO |
|
} |
|
} |
|
|
|
std::shared_ptr<DocumentComponent> docComponent = win->getActiveDocumentComponent(); |
|
if (docComponent) { |
|
//std::cout << "Browser:zdoOnClick.navAddressBar->onClick - Found an active document component" << std::endl; |
|
// now tell it to navigate somewhere |
|
docComponent->navTo(value); |
|
} else { |
|
std::cout << "Browser:zdoOnClick.navAddressBar->onClick - No active document component" << std::endl; |
|
} |
|
}; |
|
} else if (type=="nextTab") { |
|
component->onClick=[win]() { |
|
// get tab value and nav away |
|
}; |
|
} else if (type=="prevTab") { |
|
component->onClick=[win]() { |
|
// get tab value and nav away |
|
}; |
|
} |
|
} |
|
|
|
std::shared_ptr<Node> App::loadTheme(std::string filename) { |
|
std::string ntrml, line; |
|
std::ifstream myfile(filename); |
|
if (myfile.is_open()) { |
|
while(getline(myfile, line)) { |
|
ntrml += line; |
|
} |
|
myfile.close(); |
|
} else { |
|
std::cout << "Couldnt read " << filename << std::endl; |
|
} |
|
NTRMLParser uiParser; |
|
//std::cout << "Browser read [" << ntrml << "]" << std::endl; |
|
return uiParser.parse(ntrml); |
|
} |
|
|
|
void App::destroyTheme() { |
|
for(auto win: this->windows) { |
|
if (win->tabComponent) { |
|
TabbedComponent *tabbedComponent = dynamic_cast<TabbedComponent *>(win->tabComponent.get()); |
|
for(auto tab: tabbedComponent->tabs) { |
|
tabbedComponent->removeTab(tab->id); |
|
} |
|
} |
|
win->addressComponent = nullptr; |
|
win->tabComponent = nullptr; |
|
for(auto layer: win->ui->layers) { |
|
layer.reset(); |
|
} |
|
win->ui->layers.clear(); |
|
} |
|
} |
|
|
|
void App::rebuildTheme() { |
|
for(auto win: this->windows) { |
|
|
|
// convert uiRootNode into a component list |
|
std::shared_ptr<Component> rootComponent = std::make_shared<Component>(); |
|
rootComponent->name = "rootComponent of browser"; |
|
// we build one global tree that each layer has a child node in |
|
this->layers.clear(); // nuke any layers we have |
|
this->createComponentTree(this->uiRootNode, rootComponent, win); |
|
//Component::printComponentTree(rootComponent, 0); |
|
|
|
// these probably should be fatal |
|
if (!win->addressComponent) { |
|
std::cout << "ERROR: no addressComponent" << std::endl; |
|
} |
|
if (!win->tabComponent) { |
|
std::cout << "ERROR: no tabComponent" << std::endl; |
|
} |
|
|
|
// we want each window to has it's own component tree, so each address bar can have different values |
|
size_t layerCount = 0; |
|
for(auto layer: this->layers) { |
|
// set proper vertices |
|
layer->resize(win->windowWidth, win->windowHeight); |
|
// transfer layer |
|
win->ui->layers.push_back(layer); |
|
// debug |
|
/* |
|
std::cout << "layer: " << layerCount << std::endl; |
|
Component::printComponentTree(layer, 0); |
|
*/ |
|
layerCount++; |
|
layer.reset(); // deallocate our local layers |
|
} |
|
rootComponent.reset(); |
|
|
|
//newWindow->rootComponent = rootComponent; |
|
//newWindow->ui->rootComponent = rootComponent; |
|
win->renderDirty = true; |
|
|
|
} |
|
} |
|
|
|
void App::transferTheme(std::string filename) { |
|
// assuming we don't need the NodeTree to get our values |
|
this->uiRootNode.reset(); |
|
this->uiRootNode = this->loadTheme(filename); |
|
|
|
for(auto win: this->windows) { |
|
glfwMakeContextCurrent(win->window); |
|
|
|
TabbedComponent *oldTabbedComponent = nullptr; |
|
if (win->tabComponent) { |
|
oldTabbedComponent = dynamic_cast<TabbedComponent *>(win->tabComponent.get()); |
|
} |
|
win->tabComponent = nullptr; // release to release |
|
win->addressComponent = nullptr; // I think this is ok to release now |
|
|
|
// convert uiRootNode into a component list |
|
std::shared_ptr<Component> rootComponent = std::make_shared<Component>(); |
|
rootComponent->name = "rootComponent of browser"; |
|
this->layers.clear(); // nuke any layers we had (doesn't nuke window layers) |
|
// we build one global tree that each layer has a child node in |
|
this->createComponentTree(this->uiRootNode, rootComponent, win); |
|
//Component::printComponentTree(rootComponent, 0); |
|
|
|
// these probably should be fatal |
|
if (!win->addressComponent) { |
|
std::cout << "ERROR: no addressComponent" << std::endl; |
|
} |
|
if (!win->tabComponent) { |
|
std::cout << "ERROR: no tabComponent" << std::endl; |
|
} |
|
|
|
// ok now we need to transfer the tabs over |
|
if (oldTabbedComponent) { |
|
// should have been set in createComponentTree |
|
TabbedComponent *newTabbedComponent = dynamic_cast<TabbedComponent *>(win->tabComponent.get()); |
|
if (newTabbedComponent) { |
|
size_t oldSelectedTabId = oldTabbedComponent->selectedTabId; |
|
std::shared_ptr<Tab> newSelectedTab = nullptr; |
|
std::cout << "going to copy " << oldTabbedComponent->tabs.size() << " tab(s) over to new theme" << std::endl; |
|
for(auto tab: oldTabbedComponent->tabs) { |
|
DocumentComponent *oldDocComponent = dynamic_cast<DocumentComponent *>(tab->contents.get()); |
|
if (oldDocComponent) { |
|
//std::cout << "Reading DOM" << std::endl; |
|
//printNode(oldDocComponent->domRootNode, 0); |
|
|
|
std::shared_ptr<Tab> nTab = newTabbedComponent->addTab(tab->titleBox->text); |
|
newTabbedComponent->selectTab(nTab); |
|
// maybe faster if we can skip the component tree rebuild |
|
newTabbedComponent->loadDomIntoTab(oldDocComponent->domRootNode, tab->titleBox->text); |
|
//newTabbedComponent->documentComponent.get() |
|
DocumentComponent *newDocComponent = dynamic_cast<DocumentComponent *>(newTabbedComponent->tabs.back()->contents.get()); |
|
if (newDocComponent) { |
|
// really slow and we don't need to do this for all tabs |
|
// not sure why this is required... |
|
//newDocComponent->setDOM(oldDocComponent->domRootNode); |
|
//std::cout << "Checking DOM" << std::endl; |
|
//printNode(newTabbedComponent->documentComponent->, 0); |
|
newDocComponent->render(); // flush out components |
|
//newDocComponent->domDirty = true; // is most accurate |
|
} else { |
|
std::cout << "new tab had no doc" << std::endl; |
|
} |
|
if (tab->id == oldSelectedTabId) { |
|
// translate old selected id into new id |
|
newSelectedTab = nTab; |
|
} |
|
} else { |
|
std::cout << "old tab had no doc" << std::endl; |
|
} |
|
// this breaks everything |
|
//oldTabbedComponent->removeTab(tab->id); |
|
} |
|
if (newSelectedTab) { |
|
newTabbedComponent->selectTab(newSelectedTab); |
|
/* |
|
DocumentComponent *newDocComponent = dynamic_cast<DocumentComponent *>(newTabbedComponent->documentComponent.get()); |
|
if (newDocComponent) { |
|
// FIXME: can only in render active window/ogl context |
|
newDocComponent->render(); // flush out components |
|
} else { |
|
std::cout << "new selected tab had no doc" << std::endl; |
|
} |
|
*/ |
|
} else { |
|
std::cout << "no new selected tab" << std::endl; |
|
} |
|
/* |
|
std::cout << "new tabbed component has " << newTabbedComponent->tabs.size() << " tab(s) over from old theme" << std::endl; |
|
for(auto tab: newTabbedComponent->tabs) { |
|
DocumentComponent *newDocComponent = dynamic_cast<DocumentComponent *>(tab->contents.get()); |
|
std::cout << "checking tab DOM" << std::endl; |
|
printNode(newDocComponent->domRootNode, 0); |
|
} |
|
if (newTabbedComponent->documentComponent) { |
|
DocumentComponent *newDocComponent = dynamic_cast<DocumentComponent *>(newTabbedComponent->documentComponent.get()); |
|
std::cout << "checking selected DOM" << std::endl; |
|
printNode(newDocComponent->domRootNode, 0); |
|
} |
|
*/ |
|
} else { |
|
std::cout << "new theme didnt have tab component" << std::endl; |
|
} |
|
} else { |
|
std::cout << "old theme didnt have tab component" << std::endl; |
|
} |
|
|
|
// now we're officially done with old layer, nuked them |
|
for(auto layer: win->ui->layers) { |
|
layer.reset(); |
|
} |
|
win->ui->layers.clear(); |
|
|
|
// we want each window to has it's own component tree, so each address bar can have different values |
|
size_t layerCount = 0; |
|
for(auto layer: this->layers) { |
|
// set proper vertices |
|
layer->resize(win->windowWidth, win->windowHeight); |
|
// transfer layer |
|
win->ui->layers.push_back(layer); |
|
// debug |
|
/* |
|
std::cout << "layer: " << layerCount << std::endl; |
|
Component::printComponentTree(layer, 0); |
|
*/ |
|
layerCount++; |
|
layer.reset(); // deallocate our local layers |
|
} |
|
rootComponent.reset(); |
|
|
|
//newWindow->rootComponent = rootComponent; |
|
//newWindow->ui->rootComponent = rootComponent; |
|
win->renderDirty = true; |
|
} |
|
} |
|
|
|
void App::NextTheme() { |
|
glob_t glob_result; |
|
static unsigned int t = 0; |
|
#ifndef _WIN32 |
|
glob("*.ntrml", GLOB_TILDE, nullptr, &glob_result); |
|
#else |
|
glob("*.ntrml", GLOB_NOCHECK, nullptr, &glob_result); |
|
#endif |
|
t++; |
|
if (t == glob_result.gl_pathc) t = 0; |
|
/* |
|
for(unsigned int i = 0; i < glob_result.gl_pathc; ++i){ |
|
if (t == i) std::cout << "*"; |
|
std::cout << glob_result.gl_pathv[i] << std::endl; |
|
} |
|
*/ |
|
// this works decently, but only destroys first window |
|
/* |
|
this->destroyTheme(); |
|
//deleteNode(this->uiRootNode); |
|
this->uiRootNode.reset(); |
|
this->uiRootNode = this->loadTheme(std::string(glob_result.gl_pathv[t])); |
|
this->rebuildTheme(); |
|
*/ |
|
this->transferTheme(glob_result.gl_pathv[t]); |
|
// start with removing existing theme |
|
// and replacing with new version of existing theme |
|
} |
|
|
|
// Previously defined components with functionality |
|
// 1+ image (no functionality) |
|
// 1+ tabbed document |
|
// 1+ back/fwd/reload/stop components |
|
// 1+ address bar |
|
void App::createComponentTree(const std::shared_ptr<Node> node, std::shared_ptr<Component> &parentComponent, std::shared_ptr<Window> win) { |
|
// FIXME: remove these 2 vars |
|
int winWidth = win->windowWidth; |
|
int winHeight = win->windowHeight; |
|
std::string tag; |
|
if (node == nullptr) { |
|
std::cout << "ComponentBuilder::build - node is null" << std::endl; |
|
return; |
|
} |
|
TagNode *tagNode = nullptr; |
|
if (node->nodeType == NodeType::TAG) { |
|
tagNode = dynamic_cast<TagNode*>(node.get()); |
|
if (tagNode) { |
|
tag = tagNode->tag; |
|
} |
|
} else if (node->nodeType == NodeType::TEXT) { |
|
// just ignore text blocks |
|
return; |
|
tagNode = dynamic_cast<TagNode*>(node->parent.get()); |
|
if (tagNode) { |
|
tag = tagNode->tag; |
|
} |
|
} |
|
std::shared_ptr<Component> component = nullptr; |
|
if (!tagNode) { |
|
// usually the root node... |
|
//std::cout << "!tagnode" << std::endl; |
|
component = std::make_shared<Component>(); |
|
for (std::shared_ptr<Node> child : node->children) { |
|
createComponentTree(child, component, win); |
|
} |
|
return; |
|
} |
|
|
|
//std::cout << "Looking at tag[" << tag << "]" << std::endl; |
|
if (tag == "layer") { |
|
// the layering only works for default layout due to the ordering we're doign in window.render |
|
component = std::make_shared<Component>(); |
|
this->layers.push_back(component); |
|
//parentComponent = component; |
|
//std::swap(component, parentComponent); |
|
//std::cout << "there are now " << this->layers.size() << " layers" << std::endl; |
|
} else if (tag == "body") { |
|
if (tagNode->properties.find("bgcolor") != tagNode->properties.end()) { |
|
std::stringstream ss; |
|
ss << std::hex << tagNode->properties["bgcolor"]; |
|
ss >> win->clearColor; |
|
//std::cout << "set clear color " << std::hex << win->clearColor << std::dec << std::endl; |
|
|
|
float r = (static_cast<float>((win->clearColor >> 24) & 0xFF)) / 255; |
|
float g = (static_cast<float>((win->clearColor >> 16) & 0xFF)) / 255; |
|
float b = (static_cast<float>((win->clearColor >> 8) & 0xFF)) / 255; |
|
float a = (static_cast<float>((win->clearColor >> 0) & 0xFF)) / 255; |
|
glClearColor(r, g, b, a); |
|
} |
|
} else if (tag == "img") { |
|
std::string src = "anime.pnm"; |
|
if (tagNode->properties.find("src") != tagNode->properties.end()) { |
|
src = tagNode->properties["src"]; |
|
} |
|
//std::shared_ptr<ImageComponent> img = std::make_unique<ImageComponent>(src, winWidth * imgSetup.left.pct * 0.01 + imgSetup.left.px, winHeight * imgSetup.top.pct * 0.01 + imgSetup.top.px, imgSetup.width.px, imgSetup.height.px, winWidth, winHeight); |
|
std::shared_ptr<ImageComponent> img = std::make_unique<ImageComponent>(src, 0, 0, 512, 512, winWidth, winHeight); |
|
img->boundToPage = false; // have to set this before setUpUI |
|
img->setUpUI(tagNode->properties, win.get()); |
|
// set up interactivity |
|
if (tagNode->properties.find("onClick") != tagNode->properties.end()) { |
|
img->isPickable = true; |
|
//doOnClick(std::string type, Component &component, Window *win) |
|
doOnClick(tagNode->properties["onClick"], img.get(), win.get()); |
|
} |
|
img->isPickable = false; |
|
img->name = "img"; |
|
if (tagNode->properties.find("name") != tagNode->properties.end()) { |
|
img->name = tagNode->properties["name"]; |
|
} |
|
component = img; |
|
//std::cout << "Added img component to ui" << std::endl; |
|
} else if (tag == "font") { |
|
// 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::string label = "NeTRunner"; |
|
if (node->children.size()) { |
|
TextNode *textNode = dynamic_cast<TextNode*>(node->children.front().get()); |
|
if (textNode) { |
|
label = textNode->text; |
|
} |
|
} |
|
std::shared_ptr<TextComponent> text = std::make_unique<TextComponent>(label, 0, 0, 12, false, 0x000000FF, winWidth, winHeight); |
|
text->boundToPage = false; // have to set this before setUpUI |
|
text->win = win; |
|
// this may not work for the text shader... yea doesn't seem to |
|
text->setUpUI(tagNode->properties, win.get()); |
|
text->isPickable = false; |
|
//text->resize(win->windowWidth, win->windowHeight); // force a re-raster |
|
// set up interactivity |
|
if (tagNode->properties.find("onClick") != tagNode->properties.end()) { |
|
text->isPickable = true; |
|
//doOnClick(std::string type, Component &component, Window *win) |
|
doOnClick(tagNode->properties["onClick"], text.get(), win.get()); |
|
} |
|
component = text; |
|
} else if (tag == "box") { |
|
unsigned int color = 0x888888FF; |
|
if (tagNode->properties.find("color") != tagNode->properties.end()) { |
|
std::stringstream ss; |
|
ss << std::hex << tagNode->properties["color"]; |
|
ss >> color; |
|
//std::cout << "read color " << tagNode->properties["color"] << " as " << std::hex << color << std::dec << std::endl; |
|
} |
|
|
|
// winHeight minus because box coordinates? yes |
|
//std::cout << "placing box at " << cX << "," << cY << " " << cW << "x" << cH << " color: " << std::hex << color << std::dec << std::endl; |
|
//std::shared_ptr<BoxComponent> box = std::make_unique<BoxComponent>(cX, winHeight - cH - cY, cW, cH, color, winWidth, winHeight); |
|
std::shared_ptr<BoxComponent> box = nullptr; |
|
if (node->children.size()) { |
|
box = std::make_unique<ButtonComponent>(0, 0, 1, 1, color, winWidth, winHeight); |
|
} else { |
|
box = std::make_unique<BoxComponent>(0, 0, 1, 1, color, winWidth, winHeight); |
|
} |
|
box->boundToPage = false; // have to set this before setUpUI |
|
box->setUpUI(tagNode->properties, win.get()); |
|
box->isPickable = false; |
|
box->name = "box"; |
|
if (tagNode->properties.find("name") != tagNode->properties.end()) { |
|
box->name = tagNode->properties["name"]; |
|
} |
|
// set up interactivity |
|
if (tagNode->properties.find("hover") != tagNode->properties.end()) { |
|
box->isPickable = true; |
|
unsigned int hoverColor = 0; |
|
std::stringstream ss; |
|
ss << std::hex << tagNode->properties["hover"]; |
|
ss >> hoverColor; |
|
//std::cout << "setHover Color " << std::hex << hoverColor << std::dec << std::endl; |
|
box->onMouseover = [box, win, hoverColor]() { |
|
//std::cout << "box->onMouseover" << std::endl; |
|
box->changeColor(hoverColor); |
|
win->renderDirty = true; |
|
}; |
|
box->onMouseout = [box, win, color]() { |
|
//std::cout << "box->onMouseout" << std::endl; |
|
box->changeColor(color); |
|
win->renderDirty = true; |
|
}; |
|
} |
|
if (tagNode->properties.find("onClick") != tagNode->properties.end()) { |
|
box->isPickable = true; |
|
//doOnClick(std::string type, Component &component, Window *win) |
|
doOnClick(tagNode->properties["onClick"], box.get(), win.get()); |
|
} |
|
component = box; |
|
// 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) { |
|
// so now cast back to a ButtonComponent and set it |
|
std::cout << "Box text: " << textNode->text << std::endl; |
|
/* |
|
ButtonComponent *buttonComponent = dynamic_cast<ButtonComponent*>(component.get()); |
|
if (buttonComponent) { |
|
buttonComponent->value = textNode->text; |
|
buttonComponent->resizeToTextSize(); |
|
buttonComponent->updateText(); |
|
} |
|
*/ |
|
} |
|
} |
|
// manual quick add before we bail |
|
component->setParent(parentComponent); |
|
parentComponent->children.push_back(component); |
|
// skip my children |
|
return; |
|
} else if (tag == "input") { |
|
//std::shared_ptr<InputComponent> navAddressBar = std::make_unique<InputComponent>(192.0f, winHeight - 48.0f, winWidth - 384.0f, 24.0f, winWidth, winHeight); |
|
std::shared_ptr<InputComponent> navAddressBar = std::make_unique<InputComponent>(0, 0, 384.0f, 24.0f, winWidth, winHeight); |
|
navAddressBar->boundToPage = false; // have to set this before setUpUI |
|
navAddressBar->setUpUI(tagNode->properties, win.get()); |
|
navAddressBar->name = "navAddressBar"; |
|
|
|
navAddressBar->onEnter=[win](std::string value) { |
|
std::cout << "navAddressBar::onEnter got " << value << std::endl; |
|
|
|
TabbedComponent *p_tabComponent = dynamic_cast<TabbedComponent*>(win->tabComponent.get()); |
|
if (p_tabComponent) { |
|
if (!p_tabComponent->tabs.size()) { |
|
p_tabComponent->addTab("Loading..."); |
|
p_tabComponent->selectTab(p_tabComponent->tabs.back()); |
|
|
|
win->renderDirty = true; |
|
win->render(); // display loading tab before IO |
|
} |
|
} |
|
|
|
std::shared_ptr<DocumentComponent> docComponent = win->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; |
|
} |
|
}; |
|
|
|
navAddressBar->win = win; |
|
win->addressComponent = navAddressBar; |
|
component = navAddressBar; |
|
} else if (tag == "tabselector") { |
|
std::shared_ptr<TabbedComponent> tabbedComponent = std::make_shared<TabbedComponent>(0, 0, static_cast<float>(winWidth), static_cast<float>(winHeight - 64), winWidth, winHeight); |
|
tabbedComponent->win = win; |
|
win->tabComponent = tabbedComponent; |
|
tabbedComponent->boundToPage = false; // have to set this before setUpUI |
|
tabbedComponent->setUpUI(tagNode->properties, win.get()); |
|
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; |
|
*/ |
|
component = tabbedComponent; |
|
// make the UI focus on it to relay keyboard events |
|
win->ui->relayKeyboardComponent = tabbedComponent; |
|
} else { |
|
std::cout << "Browser::createComponentTree - unknown tag[" << tag << "]" << std::endl; |
|
} |
|
if (!component) { |
|
component = std::make_shared<Component>(); |
|
} |
|
// add it to our parent |
|
component->setParent(parentComponent); |
|
parentComponent->children.push_back(component); |
|
// , children count: " << parentComponent->children.size() |
|
|
|
// create children elements |
|
for (std::shared_ptr<Node> child : node->children) { |
|
createComponentTree(child, component, win); |
|
} |
|
component.reset(); |
|
} |
|
|
|
void App::addJSDebuggerWindow() { |
|
windowCounter++; |
|
std::shared_ptr<Window> newWindow = std::make_shared<Window>(); |
|
newWindow->id = windowCounter; |
|
newWindow->windowWidth = 1024; |
|
newWindow->windowHeight = 640; |
|
newWindow->init(); // load our UI into it |
|
std::shared_ptr<Component> p_rootComponent = std::make_shared<Component>(); |
|
p_rootComponent->name = "rootComponent of jsconsole"; |
|
|
|
std::shared_ptr<TextComponent> outputComp = std::make_shared<TextComponent>("NeTRunner JavaScript console", 0, 0, 12, false, 0x000000FF, newWindow->windowWidth, newWindow->windowHeight); |
|
outputComp->win = newWindow; |
|
newWindow->ui->layers.push_back(outputComp); |
|
outputComp.reset(); |
|
|
|
/* |
|
std::shared_ptr<InputComponent> inputComp = std::make_shared<InputComponent>(0, 0, 1024, 12, newWindow->windowWidth, newWindow->windowHeight); |
|
inputComp->win = newWindow; |
|
newWindow->ui->layers.push_back(inputComp); |
|
inputComp.reset(); |
|
*/ |
|
|
|
this->windows.push_back(newWindow); |
|
newWindow.reset(); |
|
} |
|
|
|
void App::addWindow() { |
|
windowCounter++; |
|
std::shared_ptr<Window> newWindow = std::make_shared<Window>(); |
|
newWindow->id = windowCounter; |
|
newWindow->windowWidth = 1024; |
|
newWindow->windowHeight = 640; |
|
newWindow->init(); // load our UI into it |
|
newWindow->openglWindow->app = this; |
|
|
|
// convert uiRootNode into a component list |
|
std::shared_ptr<Component> p_rootComponent = std::make_shared<Component>(); |
|
p_rootComponent->name = "rootComponent of browser"; |
|
// we build one global tree that each layer has a child node in |
|
|
|
this->createComponentTree(this->uiRootNode, p_rootComponent, newWindow); |
|
//Component::printComponentTree(p_rootComponent, 0); |
|
|
|
// these probably should be fatal |
|
if (!newWindow->addressComponent) { |
|
std::cout << "ERROR: no addressComponent" << std::endl; |
|
} |
|
if (!newWindow->tabComponent) { |
|
std::cout << "ERROR: no tabComponent" << std::endl; |
|
} |
|
|
|
// we want each window to has it's own component tree, so each address bar can have different values |
|
size_t layerCount = 0; |
|
for(auto layer: this->layers) { |
|
// set proper vertices |
|
layer->resize(newWindow->windowWidth, newWindow->windowHeight); |
|
// transfer layer |
|
newWindow->ui->layers.push_back(layer); |
|
// debug |
|
/* |
|
std::cout << "layer: " << layerCount << std::endl; |
|
Component::printComponentTree(layer, 0); |
|
*/ |
|
layerCount++; |
|
layer.reset(); // deallocate our local layers |
|
} |
|
this->layers.clear(); // nuke any layers we have |
|
p_rootComponent.reset(); |
|
|
|
//newWindow->rootComponent = rootComponent; |
|
//newWindow->ui->rootComponent = rootComponent; |
|
newWindow->renderDirty = true; |
|
|
|
this->windows.push_back(newWindow); |
|
newWindow.reset(); |
|
|
|
if (!activeWindow) { |
|
//std::cout << "Browser::addWindow - setting active window" << std::endl; |
|
activeWindow = this->windows.back().get(); |
|
} |
|
} |
|
|
|
// FIXME: put quit check in here and clean up windows |
|
void App::render() { |
|
// guessing during render this iterator can be invalidated |
|
size_t w = this->windows.size(); |
|
//for(std::vector<std::shared_ptr<Window>>::iterator it = this->windows.begin(); it != this->windows.end(); ++it) { |
|
//it->get()->render(); |
|
// definitely safer |
|
for(size_t i = 0; i < w; ++i) { |
|
glfwMakeContextCurrent(this->windows[i]->window); |
|
this->windows[i]->render(); |
|
} |
|
} |
|
|
|
void App::loop() { |
|
bool shouldQuit = false; |
|
while (!shouldQuit) { |
|
//const std::clock_t begin = clock(); |
|
this->render(); |
|
scheduler->fireTimers(); // render may have taken some time |
|
double next = scheduler->getNext(); |
|
//if (!next) std::cout << "timer starvation, refiring" << std::endl; |
|
/* |
|
while(!next) { |
|
std::cout << "timer starvation, refiring" << std::endl; |
|
scheduler->fireTimers(); // render may have taken some time |
|
next = scheduler->getNext(); |
|
} |
|
*/ |
|
//std::cout << "Browser::loop - next timer at " << next << std::endl; |
|
if (next == LONG_MAX) { |
|
glfwWaitEvents(); // block until something changes |
|
} else { |
|
glfwWaitEventsTimeout(next / 1000); |
|
} |
|
scheduler->fireTimers(); // check before we go into render again |
|
//glfwWaitEventsTimeout(1.0 / 60.0); // increase the cpu from 0% to 2% on idle |
|
//const std::clock_t end = clock(); |
|
//std::cout << '\r' << std::fixed << (((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) * 1000) << std::scientific << " ms/f " << std::flush; |
|
// FIXME: one close shouldn't close the app |
|
for(std::vector<std::shared_ptr<Window>>::iterator it = this->windows.begin(); it != this->windows.end(); ++it) { |
|
if (glfwWindowShouldClose(it->get()->window)) { |
|
// FIXME: just remove window from stack |
|
shouldQuit = true; |
|
break; |
|
} |
|
} |
|
} |
|
}
|
|
|