4 changed files with 1354 additions and 0 deletions
@ -0,0 +1,696 @@
@@ -0,0 +1,696 @@
|
||||
#include "browser.h" |
||||
#include "scheduler.h" |
||||
#include "interfaces/components/TabbedComponent.h" |
||||
#include "parsers/markup/ntrml/NTRMLParser.h" |
||||
#include "parsers/markup/html/HTMLParser.h" |
||||
|
||||
#include "interfaces/components/InputComponent.h" |
||||
#include "interfaces/components/ImageComponent.h" |
||||
#include "interfaces/components/TabbedComponent.h" |
||||
#include "interfaces/components/ButtonComponent.h" |
||||
|
||||
#include <fstream> |
||||
#include <glob.h> |
||||
|
||||
// why can't I const this?
|
||||
std::unique_ptr<Scheduler> scheduler = std::make_unique<Scheduler>(); |
||||
|
||||
// Previously defined components with functionality
|
||||
// 1+ image (no functionality)
|
||||
// 1+ tabbed document
|
||||
// 1+ back/fwd/reload/stop components
|
||||
// 1+ address bar
|
||||
|
||||
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; |
||||
glob("*.ntrml", GLOB_TILDE, NULL, &glob_result); |
||||
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
|
||||
} |
||||
|
||||
Browser::Browser() { |
||||
/*
|
||||
std::string ntrml, line; |
||||
std::ifstream myfile("browser.ntrml"); |
||||
if (myfile.is_open()) { |
||||
while(getline(myfile, line)) { |
||||
ntrml += line; |
||||
} |
||||
myfile.close(); |
||||
} else { |
||||
std::cout << "Couldnt read browser.ntrml" << std::endl; |
||||
} |
||||
NTRMLParser uiParser; |
||||
//std::cout << "Browser read [" << ntrml << "]" << std::endl;
|
||||
*/ |
||||
//this->uiRootNode = uiParser.parse(ntrml);
|
||||
this->uiRootNode = this->loadTheme("browser.ntrml"); |
||||
//printNode(this->uiRootNode, 0);
|
||||
} |
||||
|
||||
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) { |
||||
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::shared_ptr<TextComponent> text = std::make_unique<TextComponent>("NeTRunner", 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...
|
||||
text->setUpUI(tagNode->properties, win.get()); |
||||
text->isPickable = false; |
||||
// 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::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
|
||||
|
||||
// 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->layers.clear(); // nuke any layers we have
|
||||
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
|
||||
} |
||||
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(); |
||||
} |
||||
} |
||||
|
||||
std::shared_ptr<DocumentComponent> Browser::getActiveDocumentComponent() { |
||||
if (!activeWindow) { |
||||
std::cout << "Browser::getActiveDocumentComponent - No active window" << std::endl; |
||||
return nullptr; |
||||
} |
||||
return this->activeWindow->getActiveDocumentComponent(); |
||||
} |
||||
|
||||
// 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)) { |
||||
shouldQuit = true; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
|
||||
#ifndef BROWSER_H |
||||
#define BROWSER_H |
||||
|
||||
#include "interfaces/graphical/renderers/glfw/Window.h" |
||||
#include "interfaces/components/Component.h" |
||||
#include "interfaces/components/DocumentComponent.h" |
||||
#include "interfaces/components/ComponentBuilder.h" |
||||
#include "parsers/markup/Node.h" |
||||
#include <GL/glew.h> |
||||
#include <GLFW/glfw3.h> |
||||
#include <memory> |
||||
#include <vector> |
||||
#include <algorithm> |
||||
#include "networking/HTTPResponse.h" |
||||
#include "URL.h" |
||||
#include "interfaces/graphical/renderers/glfw/Window.h" |
||||
|
||||
void doOnClick(std::string type, Component *component, Window *win); |
||||
|
||||
class App { |
||||
public: |
||||
std::shared_ptr<Node> loadTheme(std::string filename); |
||||
void destroyTheme(); |
||||
// maybe applyTheme?
|
||||
void rebuildTheme(); |
||||
void transferTheme(std::string filename); |
||||
void NextTheme(); |
||||
void createComponentTree(const std::shared_ptr<Node> node, std::shared_ptr<Component> &parentComponent, std::shared_ptr<Window> win); |
||||
void addWindow(); |
||||
void render(); |
||||
void loop(); |
||||
|
||||
//
|
||||
// properties
|
||||
//
|
||||
std::shared_ptr<Node> uiRootNode; |
||||
std::vector<std::shared_ptr<Component>> layers; |
||||
|
||||
// because components take a shared_ptr for win, this has to be shared too
|
||||
std::vector<std::shared_ptr<Window>> windows; |
||||
Window *activeWindow = nullptr; |
||||
size_t windowCounter = 0; |
||||
}; |
||||
|
||||
// this is the actually application that
|
||||
// contains a list of windows
|
||||
// separate window functions from browser functions
|
||||
class Browser : public App { |
||||
private: |
||||
public: |
||||
Browser(); |
||||
std::shared_ptr<DocumentComponent> getActiveDocumentComponent(); |
||||
|
||||
std::shared_ptr<Component> tabComponent = nullptr; |
||||
std::shared_ptr<Component> addressComponent = nullptr; |
||||
|
||||
//URL currentURL;
|
||||
}; |
||||
|
||||
//bool setWindowContent(URL const& url);
|
||||
//void handleRequest(const HTTPResponse &response);
|
||||
|
||||
#endif |
@ -0,0 +1,448 @@
@@ -0,0 +1,448 @@
|
||||
#include "opengl.h" |
||||
|
||||
#include <iostream> |
||||
#include <cmath> |
||||
#include <cstring> // for memset |
||||
|
||||
// maybe change to return std::pair<GLuint, GLuint>
|
||||
bool 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; |
||||
|
||||
GLenum glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::initGL - start - not ok: " << glErr << std::endl; |
||||
} |
||||
|
||||
glEnable(GL_BLEND); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::initGL - glEnable blend - not ok: " << glErr << std::endl; |
||||
} |
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::initGL - glBlendFunc - not ok: " << glErr << std::endl; |
||||
} |
||||
glClearColor(0.8f, 0.8f, 0.8f, 0.8f); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::initGL - glClearColor - not ok: " << glErr << std::endl; |
||||
} |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::initGL - glTexParameteri min - not ok: " << glErr << std::endl; |
||||
} |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::initGL - glTexParameteri mag - not ok: " << glErr << std::endl; |
||||
} |
||||
|
||||
//std::cout << "OpenGL is set up" << std::endl;
|
||||
|
||||
return true; |
||||
} |
||||
|
||||
bool initGLEW() { |
||||
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; |
||||
} |
||||
|
||||
// only called once
|
||||
bool opengl::initialize() { |
||||
if (!glfwInit()) { |
||||
std::cout << "Could not initialize GLFW" << std::endl; |
||||
return false; |
||||
} |
||||
|
||||
cursorHand = glfwCreateStandardCursor(GLFW_HAND_CURSOR); |
||||
cursorArrow = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); |
||||
cursorIbeam = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); |
||||
glfwSetErrorCallback([](int error, const char* description) { |
||||
std::cout << "glfw error [" << error << "]:" << description << std::endl; |
||||
}); |
||||
|
||||
// I don't think you can init GLEW without a window
|
||||
return true; |
||||
} |
||||
|
||||
opengl_window_handle * opengl::createWindow(std::string title, Rect *position, unsigned int flags) { |
||||
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); |
||||
|
||||
int windowWidth = position->w; |
||||
int windowHeight = position->h; |
||||
opengl_window_handle *win = new opengl_window_handle(); |
||||
win->width = position->w; |
||||
win->height = position->h; |
||||
GLFWwindow *window = glfwCreateWindow(windowWidth, windowHeight, title.c_str(), nullptr, nullptr); |
||||
win->window = window; |
||||
|
||||
// replace first parameter of all these callbacks with our window object instead of a GLFWwindow
|
||||
glfwSetWindowUserPointer(window, this); |
||||
// probably should set up all the hooks here
|
||||
glfwMakeContextCurrent(window); |
||||
|
||||
// init GLEW
|
||||
if (!initGLEW()) { |
||||
// FIXME: un do glfw create window
|
||||
// can't delete without a non-virtual deconstrcutor
|
||||
//delete win;
|
||||
return nullptr; |
||||
} |
||||
// init shaders?
|
||||
// we only want the box (texture shader)
|
||||
// I don't think you can init GLEW without a window
|
||||
initGL(); |
||||
|
||||
// load the shaders
|
||||
win->textureShader = win->shaderLoader.getShader(VertexShader("TextureShader.vert"), |
||||
FragmentShader("TextureShader.frag")); |
||||
win->fontShader = win->shaderLoader.getShader(VertexShader("FontShader.vert"), |
||||
FragmentShader("FontShader.frag")); |
||||
|
||||
|
||||
// configure opengl coordinates
|
||||
//std::cout << "position: " << position->w << "x" << position->h << std::endl;
|
||||
glViewport(0, 0, static_cast<GLsizei>(position->w), static_cast<GLsizei>(position->h)); |
||||
GLenum glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createWindow - glViewport - not ok: " << gluErrorString(glErr) << std::endl; |
||||
} |
||||
|
||||
/*
|
||||
// Change to the projection matrix, reset the matrix and set up orthagonal projection (i.e. 2D)
|
||||
glMatrixMode(GL_PROJECTION); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createWindow - glMatrixMode - not ok: " << gluErrorString(glErr) << std::endl; |
||||
} |
||||
glLoadIdentity(); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createWindow - glLoadIdentity - not ok: " << gluErrorString(glErr) << std::endl; |
||||
} |
||||
glOrtho(0, position->w, position->h, 0, 0, 1); // Paramters: left, right, bottom, top, near, far
|
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createWindow - glOrtho - not ok: " << gluErrorString(glErr) << std::endl; |
||||
} |
||||
*/ |
||||
|
||||
// set up box shader
|
||||
GLuint elementBufferObject = 0; |
||||
|
||||
glGenVertexArrays(1, &win->vertexArrayObject); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createWindow - genVAO - not ok: " << glErr << std::endl; |
||||
} |
||||
|
||||
glBindVertexArray(win->vertexArrayObject); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createWindow - bindVAO - not ok: " << glErr << std::endl; |
||||
} |
||||
|
||||
glGenBuffers(1, &elementBufferObject); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createWindow - genEBO - not ok: " << glErr << std::endl; |
||||
} |
||||
|
||||
// we may need the VBO here...
|
||||
glGenBuffers(1, &win->vertexBufferObject); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createWindow - genVBO - not ok: " << glErr << std::endl; |
||||
} |
||||
float vertices[20] = { |
||||
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, |
||||
0.0f, 0.0f, 0.0f, 1.0f, 1.0f, |
||||
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, |
||||
0.0f, 0.0f, 0.0f, 0.0f, 0.0f |
||||
}; |
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, win->vertexBufferObject); // selects buffer
|
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createWindow - bindVBO - not ok: " << glErr << std::endl; |
||||
} |
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // writes buffer
|
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createWindow - bufVBO - not ok: " << glErr << std::endl; |
||||
} |
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBufferObject); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createWindow - bindEBO - not ok: " << glErr << std::endl; |
||||
} |
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(this->indices), this->indices, GL_STATIC_DRAW); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createWindow - buffEBO - not ok: " << glErr << std::endl; |
||||
} |
||||
|
||||
// attribute 0 = 3 floats
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), nullptr); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createWindow - glVertexAttribPointer0 - not ok: " << glErr << std::endl; |
||||
} |
||||
glEnableVertexAttribArray(0); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createWindow - glEnableVertexAttribArray0 - not ok: " << glErr << std::endl; |
||||
} |
||||
|
||||
// attribute 1 = 2 floats, at 3
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(3 * sizeof(float))); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createWindow - glVertexAttribPointer1 - not ok: " << glErr << std::endl; |
||||
} |
||||
glEnableVertexAttribArray(1); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createWindow - glEnableVertexAttribArray1 - not ok: " << glErr << std::endl; |
||||
} |
||||
|
||||
//glBindVertexArray(0);
|
||||
|
||||
return win; |
||||
} |
||||
|
||||
void opengl_window_handle::clear() { |
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
||||
|
||||
GLenum glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::clear - not ok: " << glErr << std::endl; |
||||
} |
||||
} |
||||
|
||||
void opengl_window_handle::swapBuffers() { |
||||
glfwSwapBuffers(this->window); |
||||
GLenum glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::swapBuffers - not ok: " << glErr << std::endl; |
||||
} |
||||
} |
||||
|
||||
opengl_texture_handle* opengl_window_handle::createTexture(unsigned char* texture, GLsizei w, GLsizei h) { |
||||
opengl_texture_handle *handle = new opengl_texture_handle; |
||||
glGenTextures(1, &handle->number); |
||||
|
||||
// make sure texture size is a power of two
|
||||
size_t potw = pow(2, ceil(log(w) / log(2))); |
||||
size_t poth = pow(2, ceil(log(h) / log(2))); |
||||
// FIXME: need to dynamically allocate for c++
|
||||
//unsigned char data[16384][16384][4];
|
||||
//unsigned char ***image = allocate_dynamic_bitmap(poth, potw, 4);
|
||||
unsigned char *data = (unsigned char *)malloc(sizeof(unsigned char) * poth * potw * 4); |
||||
//memset(data, 0, poth * potw * 4);
|
||||
//memset(image, 0, poth * potw * 4);
|
||||
// unflip texture
|
||||
unsigned int loadWidth = static_cast<unsigned int>(w); |
||||
unsigned int loadHeight = static_cast<unsigned int>(h); |
||||
for (unsigned int py = 0; py < loadHeight; py++) { |
||||
for (unsigned int px = 0; px < loadWidth; px++) { |
||||
for (unsigned int i = 0; i < 4; i++) { |
||||
//data[1023 - py][px][i] = anime.pixel_data[((px * 4) + (py * 4 * 1024)) + i];
|
||||
size_t read = ((px * 4) + (py * 4 * loadWidth)) + i; |
||||
unsigned char val = texture[read]; |
||||
size_t ypos = potw - 1 - py; // flip y
|
||||
size_t write = ((px * 4) + (ypos * 4 * potw)) + i; |
||||
data[write] = val; |
||||
//data[ypos][px][i] = val;
|
||||
} |
||||
} |
||||
} |
||||
|
||||
// upload
|
||||
glBindTexture(GL_TEXTURE_2D, handle->number); // select
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, static_cast<int>(potw), static_cast<int>(poth), 0, GL_RGBA, GL_UNSIGNED_BYTE, data); // upload
|
||||
glGenerateMipmap(GL_TEXTURE_2D); // process it
|
||||
glBindTexture(GL_TEXTURE_2D, 0); // unselect
|
||||
|
||||
return handle; |
||||
} |
||||
|
||||
opengl_texture_handle* opengl_window_handle::createTextureFromColor(const unsigned int hexColor) { |
||||
//std::cout << "createTextureFromColor: " << hexColor << std::endl;
|
||||
unsigned char texture[1][1][4]; |
||||
texture[0][0][0]=(hexColor >> 24) & 0xFF; |
||||
texture[0][0][1]=(hexColor >> 16) & 0xFF; |
||||
texture[0][0][2]=(hexColor >> 8) & 0xFF; |
||||
texture[0][0][3]=(hexColor >> 0) & 0xFF; |
||||
//std::cout << "opengl::createTextureFromColor R: " << (int)texture[0][0][0] << " G: " << (int)texture[0][0][1] << " B: " << (int)texture[0][0][2] << " A: " << (int)texture[0][0][3] << std::endl;
|
||||
// I can't figure out how to pass data to opengl::createTexture
|
||||
// so we'll just do this for now
|
||||
opengl_texture_handle *handle=new opengl_texture_handle; |
||||
|
||||
glGenTextures(1, &handle->number); |
||||
GLenum glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createTextureFromColor - glGenTextures - not ok: " << gluErrorString(glErr) << std::endl; |
||||
} |
||||
|
||||
glBindTexture(GL_TEXTURE_2D, handle->number); // select
|
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createTextureFromColor - bindTexture - not ok: " << glErr << std::endl; |
||||
} |
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture); // upload
|
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createTextureFromColor - glTexImage2D - not ok: " << glErr << std::endl; |
||||
} |
||||
glGenerateMipmap(GL_TEXTURE_2D); // process it
|
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createTextureFromColor - glGenerateMipmap - not ok: " << glErr << std::endl; |
||||
} |
||||
glBindTexture(GL_TEXTURE_2D, 0); // unselect
|
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "opengl::createTextureFromColor - unBindTex - not ok: " << glErr << std::endl; |
||||
} |
||||
return handle; |
||||
} |
||||
|
||||
#include <cmath> |
||||
|
||||
// converts 0-1 to screen
|
||||
// but centered
|
||||
void pointToViewport(float &rawX, float &rawY, size_t windowWidth, size_t windowHeight) { |
||||
|
||||
//if (useBoxShader) {
|
||||
//std::cout << "Component::pointToViewport - notBoundToPage converting from " << static_cast<int>(rawX) << "," << static_cast<int>(rawY) << std::endl;
|
||||
//std::cout << "BoundToPage using " << screenWidth << "x" << screenHeight << std::endl;
|
||||
rawX = ((rawX / windowWidth) * 2) - 1; |
||||
rawY = ((rawY / windowHeight) * 2) - 1; |
||||
//std::cout << "Component::pointToViewport - BoundToPage using " << static_cast<int>(rawX) << "x" << static_cast<int>(rawY) << std::endl;
|
||||
//std::cout << "Component::pointToViewport - BoundToPage converted to " << rawX << "," << rawY << std::endl;
|
||||
/*
|
||||
} else { |
||||
//std::cout << "notBoundToPage using " << screenWidth << "x" << screenHeight << std::endl;
|
||||
//std::cout << "Component::pointToViewport - notBoundToPage converting from " << static_cast<int>(rawX) << "," << static_cast<int>(rawY) << std::endl;
|
||||
if (rawX < 0) { |
||||
rawX += windowWidth; |
||||
} |
||||
if (rawY < 0) { |
||||
rawY += windowHeight; |
||||
} |
||||
if (rawX > 1) { |
||||
rawX /= windowWidth; |
||||
} |
||||
if (rawY > 1) { |
||||
rawY /= windowHeight; |
||||
} |
||||
rawX = (rawX * 2) - 1; |
||||
rawY = (rawY * 2) - 1; |
||||
//std::cout << "Component::pointToViewport - notBoundToPage converted to " << rawX << "," << rawY << std::endl;
|
||||
} |
||||
*/ |
||||
} |
||||
|
||||
// keeps 0-1 (and *2 to convert to screen)
|
||||
// but also takes pixels (and converts to screen)
|
||||
// anchors to upperleft
|
||||
void distanceToViewport(float &rawX, float &rawY, size_t windowWidth, size_t windowHeight) { |
||||
if (std::abs(rawX) > 1) { |
||||
rawX /= windowWidth; |
||||
} |
||||
if (std::abs(rawY) > 1) { |
||||
rawY /= windowHeight; |
||||
} |
||||
rawX *= 2; |
||||
rawY *= 2; |
||||
} |
||||
|
||||
void opengl_window_handle::drawTexturedBox(opengl_texture_handle *texture, Rect *position) { |
||||
std::cout << "Window size: " << this->width << "," << this->height << std::endl; |
||||
// figure out new vertices
|
||||
float vx = position->x; |
||||
// 320, 320 is mid to top
|
||||
// 0, 320 to bottom to top
|
||||
//float vy = (int)position->h - (int)position->y - (int)this->height;
|
||||
float vy = static_cast<int>(position->h) - position->y; |
||||
float vWidth = position->w; |
||||
float vHeight = position->h; |
||||
//std::cout << "placing box at " << (int)vx << "x" << (int)vy << " size: " << (int)vWidth << "x" << (int)vHeight << std::endl;
|
||||
pointToViewport(vx, vy, this->width, this->height); |
||||
|
||||
//std::cout << "vWidth before: " << (int)vWidth << std::endl;
|
||||
distanceToViewport(vWidth, vHeight, this->width, this->height); |
||||
//std::cout << "vWidth after: " << (int)vWidth << std::endl;
|
||||
|
||||
//std::cout << "placing box at GL " << (int)vx << "x" << (int)vy << " size: " << (int)(vWidth*10000) << "x" << (int)(vHeight*10000) << std::endl;
|
||||
float vertices[20] = { |
||||
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, |
||||
0.0f, 0.0f, 0.0f, 1.0f, 1.0f, |
||||
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, |
||||
0.0f, 0.0f, 0.0f, 0.0f, 0.0f |
||||
}; |
||||
vertices[(0 * 5) + 0] = vx; |
||||
vertices[(0 * 5) + 1] = vy + vHeight; |
||||
vertices[(1 * 5) + 0] = vx + vWidth; |
||||
vertices[(1 * 5) + 1] = vy + vHeight; |
||||
vertices[(2 * 5) + 0] = vx + vWidth; |
||||
vertices[(2 * 5) + 1] = vy; |
||||
vertices[(3 * 5) + 0] = vx; |
||||
vertices[(3 * 5) + 1] = vy; |
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject); |
||||
GLenum glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "BoxComponent::render - glBindBuffer not ok: " << glErr << std::endl; |
||||
} |
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "BoxComponent::render - glBufferData not ok: " << glErr << std::endl; |
||||
} |
||||
float textureTransformMatrix[16] = { |
||||
1, 0, 0, 0, |
||||
0, 1, 0, 0, |
||||
0, 0, 1, 0, |
||||
0, 0, 0, 1 |
||||
}; |
||||
textureShader->bind(); |
||||
GLint transformLocation = textureShader->uniform("transform"); |
||||
glUniformMatrix4fv(transformLocation, 1, GL_FALSE, textureTransformMatrix); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "MultiComponent::render - glUniformMatrix4fv - not ok: " << glErr << std::endl; |
||||
} |
||||
|
||||
glBindVertexArray(vertexArrayObject); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "BoxComponent::render - glBindVertexArray not ok: " << glErr << std::endl; |
||||
} |
||||
glBindTexture(GL_TEXTURE_2D, texture->number); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "BoxComponent::render - glBindTexture not ok: " << glErr << std::endl; |
||||
} |
||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); |
||||
glErr=glGetError(); |
||||
if(glErr != GL_NO_ERROR) { |
||||
std::cout << "BoxComponent::render - glDrawElements not ok: " << glErr << std::endl; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,147 @@
@@ -0,0 +1,147 @@
|
||||
#ifndef OPENGL_H |
||||
#define OPENGL_H |
||||
|
||||
#include <GL/glew.h> |
||||
#include <GLFW/glfw3.h> |
||||
#include <memory> // for unique_ptr |
||||
#include <vector> |
||||
#include <string> |
||||
#include "ShaderLoader.h" |
||||
|
||||
#ifdef FLOATCOORDS |
||||
typedef double coordinates; |
||||
typedef double sizes; |
||||
#else |
||||
typedef short coordinates; |
||||
typedef unsigned short sizes; |
||||
#endif |
||||
|
||||
typedef struct{ |
||||
coordinates x, y; |
||||
sizes w, h; |
||||
} Rect; |
||||
|
||||
class texture_handle { |
||||
}; |
||||
class window_handle { |
||||
public: |
||||
virtual void clear() { } |
||||
virtual void swapBuffers() { } |
||||
// can we const texture?
|
||||
virtual texture_handle* createTexture(unsigned char *texture, GLsizei w, GLsizei h) { return nullptr; } |
||||
virtual texture_handle* createTextureFromColor(const unsigned int hexColor) { return nullptr; } |
||||
virtual void drawTexturedBox(texture_handle *texture, Rect *position) {} |
||||
//
|
||||
size_t width; |
||||
size_t height; |
||||
}; |
||||
|
||||
// maybe convert to interface?
|
||||
class baseRenderer { |
||||
public: |
||||
virtual bool initialize() { return true; } |
||||
// maybe it should return a window object?
|
||||
virtual window_handle *createWindow(std::string title, Rect *position, unsigned int flags) { return nullptr; } |
||||
}; |
||||
|
||||
struct quadVector { |
||||
GLfloat vector[4][3]; |
||||
}; |
||||
|
||||
class texture_atlas { |
||||
public: |
||||
GLuint texture; |
||||
sizes w, h; // texture width/height
|
||||
sizes mx, my; // max x,y currently available
|
||||
std::vector<Rect*> used; // small struct to loop on vs. unused
|
||||
std::vector<Rect*> available; // have to generate it, might as well cache it
|
||||
bool smooth; |
||||
unsigned char bpp; // 0-4
|
||||
|
||||
~texture_atlas(); |
||||
void updateMaxAvailArea(); |
||||
void markAreaUsed(Rect *it); |
||||
void rebuildAvailable(); |
||||
}; |
||||
|
||||
class opengl_texture_handle : public texture_handle { |
||||
public: |
||||
// future
|
||||
texture_atlas *mp_atlas; |
||||
quadVector m_texureVector; |
||||
// temp for now
|
||||
GLuint number; |
||||
float s0; |
||||
float t0; |
||||
float s1; |
||||
float t1; |
||||
}; |
||||
|
||||
// unlike a texture more like a sprite
|
||||
class OpenglTexturedQuad { |
||||
public: |
||||
bool fontShader; |
||||
sizes width; |
||||
sizes height; |
||||
// vertices
|
||||
coordinates x0; |
||||
coordinates y0; |
||||
coordinates x1; |
||||
coordinates y1; |
||||
// texture map
|
||||
float s0; |
||||
float t0; |
||||
float s1; |
||||
float t1; |
||||
sizes textureWidth; |
||||
sizes textureHeight; |
||||
std::unique_ptr<unsigned char[]> textureData; |
||||
}; |
||||
|
||||
class opengl; // few declr
|
||||
|
||||
bool initGL(); |
||||
bool initGLEW(); |
||||
|
||||
class opengl_window_handle : public window_handle { |
||||
public: |
||||
void clear(); |
||||
void swapBuffers(); |
||||
opengl_texture_handle* createTexture(unsigned char* texture, GLsizei w, GLsizei h); |
||||
opengl_texture_handle* createTextureFromColor(const unsigned int hexColor); |
||||
using window_handle::drawTexturedBox; |
||||
void drawTexturedBox(opengl_texture_handle *texture, Rect *position); |
||||
// property
|
||||
GLFWwindow *window; |
||||
opengl *renderer; |
||||
// our singular vao
|
||||
GLuint vertexArrayObject = 0; |
||||
// our singular vbo
|
||||
GLuint vertexBufferObject = 0; |
||||
ShaderLoader shaderLoader; |
||||
Shader *textureShader; |
||||
Shader *fontShader; |
||||
}; |
||||
|
||||
// singleton, there should only be one of these in the system
|
||||
class opengl : public baseRenderer { |
||||
public: |
||||
bool initialize(); |
||||
opengl_window_handle *createWindow(std::string title, Rect *position, unsigned int flags); |
||||
// need texture/sprite functions (for elements that are going to be used across windows)
|
||||
// maybe take in opengl_window_handle*, nullptr meaning all
|
||||
// properties
|
||||
const unsigned int indices[6] = { |
||||
0, 1, 2, |
||||
0, 2, 3 |
||||
}; |
||||
GLFWcursor* cursorHand; |
||||
GLFWcursor* cursorArrow; |
||||
GLFWcursor* cursorIbeam; |
||||
}; |
||||
|
||||
void pointToViewport(float&, float&, size_t, size_t); |
||||
void distanceToViewport(float&, float&, size_t, size_t); |
||||
|
||||
#endif |
||||
|
Loading…
Reference in new issue