despair 4 years ago
parent
commit
b18859d652

+ 2
- 4
README.md View File

@@ -102,14 +102,12 @@ I don't think one on here could ever say they know what their doing without bein
- Repo: https://gitgud.io/odilitime/netrunner/

- Despair
- Windows Dev, SSL Support
- Windows Dev, SSL Support, cfg parser
- repo: https://gitgud.io/despair/netrunner/

- Geertiebear
- OpenGL Shader rework
- repo:

-
- repo: https://gitgud.io/geertiebear/netrunner

- Nubben
- contributed local file code, WebResource, CommandLineParams, URL object

+ 62
- 0
src/FormData.cpp View File

@@ -0,0 +1,62 @@
#include "FormData.h"

// how do we match up nodes to components to extract the current values out of the components?
// well we link input components back to node (and/or nodes back to components)
// or we create a form UI component
std::unique_ptr<std::map<std::string, std::string>> buildFormData(std::shared_ptr<Node> formNode, std::unique_ptr<std::map<std::string, std::string>> result) {
if (!result) {
result = std::make_unique<std::map<std::string, std::string>>();
}
// check this node
TagNode *tagNode = dynamic_cast<TagNode*>(formNode.get());
if (tagNode) {
auto typePropIter = tagNode->properties.find("type");
bool skip = false;
std::cout << "formData.buildFormData - tagNode element " << tagNode->tag << std::endl;
if (typePropIter != tagNode->properties.end()) {
if (typePropIter->second == "submit") {
skip = true;
}
if (typePropIter->second == "checkbox") {
// FIXME: see if it's checked or not
skip = true;
}
if (typePropIter->second == "radio") {
// FIXME: see if it's checked or not
skip = true;
}
}
auto namePropIter = tagNode->properties.find("name");
if (!skip && namePropIter != tagNode->properties.end()) {
auto valuePropIter = tagNode->properties.find("value");
if (valuePropIter != tagNode->properties.end()) {
auto it = result->find(namePropIter->second);
if (it == result->end()) {
std::cout << "INPUTElement.buildFormData - setting " << namePropIter->second << " to [" << valuePropIter->second << "]" << std::endl;
result->insert(std::pair<std::string, std::string>(namePropIter->second, valuePropIter->second));
} else {
(*result)[namePropIter->second] = valuePropIter->second;
}
}
}
}
// check children noedes
for (auto &child : formNode->children) {
//std::shared_ptr<TagNode> tagNode = std::dynamic_pointer_cast<TagNode>(child);
//if (tagNode) {
result = buildFormData(child, std::move(result));
//}
}
return result;
}

std::unique_ptr<std::vector<Node>> searchNodeTree(const std::shared_ptr<Node> &node, std::unique_ptr<std::vector<Node>> ret) {
return ret;
}

FormData::FormData(std::shared_ptr<TagNode> start) {
// recurse down find all interesting tags
std::unique_ptr<std::vector<Node>> formNodes = std::make_unique<std::vector<Node>>();
searchNodeTree(start, std::move(formNodes));
}

+ 29
- 0
src/FormData.h View File

@@ -0,0 +1,29 @@
#ifndef FORMDATA_H
#define FORMDATA_H

#include <string>
#include <map>
#include "html/TagNode.h"

std::unique_ptr<std::map<std::string, std::string>> buildFormData(std::shared_ptr<Node> formNode, std::unique_ptr<std::map<std::string, std::string>> result);
std::unique_ptr<std::vector<Node>> searchNodeTree(const std::shared_ptr<Node> &node, std::unique_ptr<std::vector<Node>> ret);

class FormData {
public:
FormData();
FormData(std::shared_ptr<TagNode>);
// creates arrays out of values
//void append(std::string name, std::string value);
//void append(std::string name, std::string value, str::string filename);
// overwrites value
//void set(std::string name, std::string value);
//void set(std::string name, std::string value, str::string filename);
// gets value (list if array)
//void getAll(std::string name);
std::map<std::string, std::string>::iterator entries();
//std::map<std::string, std::string>::iterator keys();
//std::map<std::string, std::string>::iterator values();
std::map<std::string, std::string> data;
};

#endif

+ 48
- 167
src/WebResource.cpp View File

@@ -41,7 +41,7 @@ WebResource::WebResource(ResourceType rtype, std::string const& rraw) {
WebResource getWebResource(URL const& url) {
if (isOnlineResource(url)) {
//std::cout << "WebReousrce::getWebResource - isOnline" << std::endl;
return getOnlineWebResource(url);
return getOnlineWebResource(url, nullptr);
}
//std::cout << "WebReousrce::getWebResource - isOffline" << std::endl;
return getLocalWebResource(url);
@@ -79,104 +79,7 @@ WebResource getLocalWebResource(URL const& url) {
"Could not open file " + url.path);
}

WebResource getOnlineWebResource(URL const& url) {
std::shared_ptr<URL> uri=std::make_shared<URL>(url);

//window->currentURL=url;
//request->sendRequest(handleRequest);
WebResource returnRes;

std::string redirectLocation = "";

if (uri->scheme == "http") {
HTTPRequest request(uri);
request.sendRequest([&](HTTPResponse const& response){
logDebug() << "getOnlineWebResource request.sendRequest got " << response.statusCode << std::endl;
if (response.statusCode == 301 || response.statusCode == 302 || response.statusCode == 303) {
std::string location;
if (response.properties.find("Location")==response.properties.end()) {
if (response.properties.find("location")==response.properties.end()) {
logDebug() << "getOnlineWebResource - got " << response.statusCode << " without a location" << std::endl;
for(auto const &row : response.properties) {
logDebug() << "getOnlineWebResource - " << row.first << "=" << response.properties.at(row.first) << std::endl;
}
redirectLocation = "_";
} else {
location = response.properties.at("location");
}
} else {
location = response.properties.at("Location");
}
logDebug() << "Redirect To: " << location << std::endl;
redirectLocation = location;
} else if (response.statusCode != 200) {
returnRes.resourceType = ResourceType::INVALID;
returnRes.raw = "Unsupported status code";
} else {
// TODO: Set resourceType based on Content-Type field.
std::string contentType = "";
if (response.properties.find("Content-type") != response.properties.end()) {
contentType = response.properties.at("Content-type");
}
if (response.properties.find("Content-Type") != response.properties.end()) {
contentType = response.properties.at("Content-Type");
}
std::cout << "Content-type: " << contentType << std::endl;
if (contentType == "text/plain") {
returnRes.resourceType = ResourceType::TXT;
} else if (contentType == "application/json") {
returnRes.resourceType = ResourceType::TXT;
} else {
returnRes.resourceType = ResourceType::HTML;
}
returnRes.raw = std::move(response.body);
}
}, nullptr);
} else {
HTTPSRequest secure(uri);
secure.sendRequest([&](HTTPResponse const& response){
logDebug() << "getOnlineWebResource secure.sendRequest" << std::endl;
if (response.statusCode == 301 || response.statusCode == 302 || response.statusCode == 303) {
std::string location;
if (response.properties.find("Location")==response.properties.end()) {
if (response.properties.find("location")==response.properties.end()) {
logDebug() << "getOnlineWebResource - got " << response.statusCode << " without a location" << std::endl;
for(auto const &row : response.properties) {
logDebug() << "getOnlineWebResource - " << row.first << "=" << response.properties.at(row.first) << std::endl;
}
redirectLocation = "_";
} else {
location = response.properties.at("location");
}
} else {
location = response.properties.at("Location");
}
logDebug() << "Redirect To: " << location << std::endl;
redirectLocation = location;
} else if (response.statusCode != 200) {
returnRes.resourceType = ResourceType::INVALID;
returnRes.raw = "Unsupported status code";
} else {
// TODO: Set resourceType based on Content-Type field.
returnRes.resourceType = ResourceType::HTML;
returnRes.raw = std::move(response.body);
}
}, nullptr);
}

if (redirectLocation.size() > 0) {
if (redirectLocation == "_") {
return WebResource(ResourceType::INVALID,
"Got a 301 without a location");
}
return getOnlineWebResource(URL(redirectLocation));
}

return returnRes;
}

WebResource postWebResource(URL const& url, std::unique_ptr<std::map<std::string, std::string>> formData) {
WebResource getOnlineWebResource(URL const& url, std::unique_ptr<std::map<std::string, std::string>> formData) {
std::shared_ptr<URL> uri=std::make_shared<URL>(url);

std::unique_ptr<std::string> ptrPostBody = nullptr;
@@ -195,85 +98,63 @@ WebResource postWebResource(URL const& url, std::unique_ptr<std::map<std::string
WebResource returnRes;
std::string redirectLocation = "";
if (uri->scheme == "https") {
HTTPSRequest secure(uri);
if (formData) {
secure.method = Method::POST;
}
//secure.version = Version::HTTP11;
secure.sendRequest([&](HTTPResponse const& response){
logDebug() << "WebResource.postWebResource secure.sendRequest" << std::endl;
if (response.statusCode == 301 || response.statusCode == 302 || response.statusCode == 303) {
std::string location;
if (response.properties.find("Location")==response.properties.end()) {
if (response.properties.find("location")==response.properties.end()) {
logDebug() << "WebResource.postWebResource - got " << response.statusCode << " without a location" << std::endl;
for(auto const &row : response.properties) {
logDebug() << "WebResource.postWebResource - " << row.first << "=" << response.properties.at(row.first) << std::endl;
}
redirectLocation = "_";
} else {
location = response.properties.at("location");

std::function<void(const HTTPResponse&)> responseCallback = [&](HTTPResponse const& response){
logDebug() << "getOnlineWebResource responseCallback got " << response.statusCode << std::endl;
if (response.statusCode == 301 || response.statusCode == 302 || response.statusCode == 303) {
std::string location;
if (response.properties.find("Location")==response.properties.end()) {
if (response.properties.find("location")==response.properties.end()) {
logDebug() << "getOnlineWebResource responseCallback - got " << response.statusCode << " without a location" << std::endl;
for(auto const &row : response.properties) {
logDebug() << "getOnlineWebResource responseCallback - " << row.first << "=" << response.properties.at(row.first) << std::endl;
}
redirectLocation = "_";
} else {
location = response.properties.at("Location");
location = response.properties.at("location");
}
logDebug() << "Redirect To: " << location << std::endl;
redirectLocation = location;
} else if (response.statusCode != 200) {
returnRes.resourceType = ResourceType::INVALID;
returnRes.raw = "Unsupported status code";
} else {
// TODO: Set resourceType based on Content-Type field.
location = response.properties.at("Location");
}
logDebug() << "Redirect To: " << location << std::endl;
redirectLocation = location;
} else if (response.statusCode != 200) {
returnRes.resourceType = ResourceType::INVALID;
returnRes.raw = "Unsupported status code";
} else {
// TODO: Set resourceType based on Content-Type field.
std::string contentType = "";
if (response.properties.find("Content-type") != response.properties.end()) {
contentType = response.properties.at("Content-type");
}
if (response.properties.find("Content-Type") != response.properties.end()) {
contentType = response.properties.at("Content-Type");
}
std::cout << "Content-type: " << contentType << std::endl;
if (contentType == "text/plain") {
returnRes.resourceType = ResourceType::TXT;
} else if (contentType == "application/json") {
returnRes.resourceType = ResourceType::TXT;
} else {
returnRes.resourceType = ResourceType::HTML;
returnRes.raw = std::move(response.body);
}
}, std::move(ptrPostBody));
returnRes.raw = std::move(response.body);
}
};
if (uri->scheme == "https") {
HTTPSRequest secure(uri);
if (formData) {
secure.method = Method::POST;
}
//secure.version = Version::HTTP11;
secure.sendRequest(responseCallback, std::move(ptrPostBody));
} else {
HTTPRequest request(uri);
if (formData) {
request.method = Method::POST;
}
request.sendRequest([&](HTTPResponse const& response){
logDebug() << "getOnlineWebResource request.sendRequest" << std::endl;
if (response.statusCode == 301 || response.statusCode == 302 || response.statusCode == 303) {
std::string location;
if (response.properties.find("Location")==response.properties.end()) {
if (response.properties.find("location")==response.properties.end()) {
logDebug() << "getOnlineWebResource - got " << response.statusCode << " without a location" << std::endl;
for(auto const &row : response.properties) {
logDebug() << "getOnlineWebResource - " << row.first << "=" << response.properties.at(row.first) << std::endl;
}
redirectLocation = "_";
} else {
location = response.properties.at("location");
}
} else {
location = response.properties.at("Location");
}
logDebug() << "Redirect To: " << location << std::endl;
redirectLocation = location;
} else if (response.statusCode != 200) {
returnRes.resourceType = ResourceType::INVALID;
returnRes.raw = "Unsupported status code";
} else {
// TODO: Set resourceType based on Content-Type field.
std::string contentType = "";
if (response.properties.find("Content-type") != response.properties.end()) {
contentType = response.properties.at("Content-type");
}
if (response.properties.find("Content-Type") != response.properties.end()) {
contentType = response.properties.at("Content-Type");
}
std::cout << "Content-type: " << contentType << std::endl;
if (contentType == "text/plain") {
returnRes.resourceType = ResourceType::TXT;
} else {
returnRes.resourceType = ResourceType::HTML;
}
returnRes.raw = std::move(response.body);
}
}, std::move(ptrPostBody));
request.sendRequest(responseCallback, std::move(ptrPostBody));
}
return returnRes;
}

+ 4
- 3
src/WebResource.h View File

@@ -4,6 +4,7 @@
#include <string>
#include <map>
#include "URL.h"
#include "networking/HTTPResponse.h"

enum class ResourceType {
INVALID,
@@ -19,18 +20,18 @@ struct WebResource {

ResourceType resourceType;
std::string raw;
static std::function<void(const HTTPResponse&)> responseCallback;
};


// Depending on the resourceName specified, this function will forward the call
// to either getLocalWebResource or getOnlineWebResource.
WebResource getWebResource(URL const& url);

WebResource postWebResource(URL const& url, std::unique_ptr<std::map<std::string, std::string>> formData);

// Loads a resource from the local file storage.
WebResource getLocalWebResource(URL const& url);

// Loads a resource from an internet address.
WebResource getOnlineWebResource(URL const& url);
WebResource getOnlineWebResource(URL const& url, std::unique_ptr<std::map<std::string, std::string>> formData);

#endif

+ 56
- 4
src/graphics/components/ButtonComponent.cpp View File

@@ -127,6 +127,57 @@ void ButtonComponent::resize(const int passedWindowWidth, const int passedWindow
}
}

void ButtonComponent::resizeToTextSize() {
if (!textLabel->win && this->win) {
textLabel->win = this->win;
}
textLabel->x = x;
if (y < 0) {
textLabel->y = y;
} else {
textLabel->y = y - windowHeight + 16;
}
//std::cout << "placed userInputText at " << static_cast<int>(x) << "," << static_cast<int>(y - windowHeight) << std::endl;
// 62 pixels width
// but first we need to know how wide the text is
const std::shared_ptr<TextRasterizer> textRasterizer=rasterizerCache->loadFont(12, false); // fontSize, bold
rasterizationRequest request;
request.text = value;
request.startX = x;
request.availableWidth = windowWidth; // have to include x, because width=62, and we need to render that
request.sourceStartX = 0;
request.sourceStartY = 0;
request.noWrap = true;
// FIXME: center this!
std::unique_ptr<sizeResponse> textInfo = textRasterizer->size(request);
if (textInfo.get() == nullptr) {
std::cout << "ButtonComponent::updateText - couldn't estimate value[" << value << "] size" << std::endl;
return;
}
//int textWidth = textInfo->first;
int estWidth = textInfo->width;
//int estHeight = std::get<1>(*textInfo.get());
textLabel->rasterStartX = 0;
textLabel->rasterStartY = 0;
//std::cout << "ButtonComponent::updateText - estWidth: " << estWidth << " width: " << static_cast<int>(width) << std::endl;
/*
if (estWidth > width) {
//std::cout << "scrolling text" << std::endl;
textLabel->rasterStartX = estWidth - width;
}
*/
this->width = estWidth + 10; // 5px padding on each side
this->resize(windowWidth, windowHeight);
textLabel->noWrap = true;
// why does changing the width mess shit up?
//std::cout << "ButtonComponent::updateText - our width: " << static_cast<int>(width) << " windowWidth: " << windowWidth << std::endl;
textLabel->resize(windowWidth, windowHeight, windowWidth); // need to make sure there's a texture
if (this->win) {
this->win->renderDirty = true;
}
}

// most of this functionality should be inside TextComponent
void ButtonComponent::updateText() {
//std::cout << "ButtonComponent::updateText - input value is now: " << value << std::endl;
@@ -157,13 +208,12 @@ void ButtonComponent::updateText() {
request.sourceStartY = 0;
request.noWrap = true;
// FIXME: center this!
std::unique_ptr<std::pair<int, int>> textInfo = textRasterizer->size(request);
std::unique_ptr<sizeResponse> textInfo = textRasterizer->size(request);
if (textInfo.get() == nullptr) {
std::cout << "ButtonComponent::updateText - couldn't estimate value[" << value << "] size" << std::endl;
return;
}
//int textWidth = textInfo->first;
int estWidth = std::get<0>(*textInfo.get());
int estWidth = textInfo->width;
//int estHeight = std::get<1>(*textInfo.get());
textLabel->rasterStartX = 0;
textLabel->rasterStartY = 0;
@@ -177,5 +227,7 @@ void ButtonComponent::updateText() {
//std::cout << "ButtonComponent::updateText - our width: " << static_cast<int>(width) << " windowWidth: " << windowWidth << std::endl;
textLabel->resize(windowWidth, windowHeight, width + x); // need to make sure there's a texture
this->win->renderDirty = true;
if (this->win) {
this->win->renderDirty = true;
}
}

+ 1
- 0
src/graphics/components/ButtonComponent.h View File

@@ -13,6 +13,7 @@ public:
ButtonComponent(const float rawX, const float rawY, const float rawWidth, const float rawHeight, const int passedWindowWidth, const int passedWindowHeight);
void render();
void resize(const int passedWindowWidth, const int passedWindowHeight);
void resizeToTextSize();
void updateText();
std::string value="";
TextComponent *textLabel = nullptr;

+ 2
- 0
src/graphics/components/ComponentBuilder.cpp View File

@@ -11,6 +11,7 @@
#include "../elements/STRONGElement.h"
#include "../elements/INPUTElement.h"
#include "../elements/BUTTONElement.h"
#include "../elements/TEXTAREAElement.h"

#include "InputComponent.h"
#include "ButtonComponent.h"
@@ -32,6 +33,7 @@ const std::unordered_map<std::string, std::shared_ptr<Element>> ComponentBuilder
{"br", std::make_shared<DIVElement>()},
{"strong", std::make_shared<STRONGElement>()},
{"input", std::make_shared<INPUTElement>()},
{"textarea", std::make_shared<TEXTAREAElement>()},
{"button", std::make_shared<BUTTONElement>()},
{"b", std::make_shared<STRONGElement>()},
{"i", std::make_shared<SPANElement>()}

+ 21
- 7
src/graphics/components/DocumentComponent.cpp View File

@@ -184,7 +184,14 @@ DocumentComponent::DocumentComponent(const float rawX, const float rawY, const f
if (key == 259) {
focusedInputComponent->backSpace();
} else if (key == 257) {
std::cout << "enter!" << std::endl;
//std::cout << "DocumentComponent::onKeyUp - enter!" << std::endl;
if (focusedInputComponent->multiLine) {
// harfbuzz or freetype2 (something?) doesn't like \n
focusedInputComponent->value += "\r";
focusedInputComponent->updateText();
} else {
std::cout << "should submit form!" << std::endl;
}
} else {
if (key < 256) {
if (mods & GLFW_MOD_SHIFT) {
@@ -310,7 +317,7 @@ void DocumentComponent::setDOM(const std::shared_ptr<Node> rootNode) {
rootComponent->y = y;
domRootNode = rootNode;
//printNode(domRootNode, 0);
printNode(domRootNode, 0);
domDirty = true;
}

@@ -386,6 +393,7 @@ void DocumentComponent::createComponentTree(const std::shared_ptr<Node> node, co
ButtonComponent *buttonComponent = dynamic_cast<ButtonComponent*>(component.get());
if (buttonComponent) {
buttonComponent->value = textNode->text;
buttonComponent->resizeToTextSize();
buttonComponent->updateText();
}
}
@@ -447,18 +455,25 @@ void DocumentComponent::navTo(const std::string url) {
// download URL
WebResource res = getWebResource(this->currentURL);
this->handleResource(res, currentURL.toString());
}

void DocumentComponent::handleResource(WebResource &res, std::string url) {
if (res.resourceType == ResourceType::INVALID) {
logError() << "Invalid resource type: " << res.raw << std::endl;
logError() << "DocumentComponent::handleResource - Invalid resource type: " << res.raw << std::endl;
return;
}
//std::cout << "body: " << res.raw << std::endl;
//std::cout << "type: " << res.resourceType << std::endl;
// parse HTML
if (res.resourceType == ResourceType::HTML) {
HTMLParser parser;
const std::clock_t begin = clock();
std::shared_ptr<Node> rootNode = parser.parse(res.raw);
const std::clock_t end = clock();
logDebug() << "main::setWindowContent - Parsed document in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
logDebug() << "INPUTElement.handleResource - Parsed document in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
// send NodeTree to window
//this->win->setDOM(rootNode);
@@ -471,7 +486,7 @@ void DocumentComponent::navTo(const std::string url) {
}
this->setDOM(rootNode);
} else if (res.resourceType == ResourceType::TXT) {
std::cout << "Rendering text document" << std::endl;
std::cout << "INPUTElement.handleResource - Rendering text document" << std::endl;
std::shared_ptr<Node> rootNode = std::make_shared<Node>(NodeType::ROOT);
std::shared_ptr<TagNode> tagNode = std::make_shared<TagNode>();
tagNode->tag="p";
@@ -491,7 +506,6 @@ void DocumentComponent::navTo(const std::string url) {
//this->win->setDOM(rootNode);
this->setDOM(rootNode);
} else {
std::cout << "setWindowContent() - I don't know how to render non-html files" << std::endl;
std::cout << "INPUTElement.handleResource - I don't know how to render non-html files" << std::endl;
}

}

+ 2
- 0
src/graphics/components/DocumentComponent.h View File

@@ -8,6 +8,7 @@
#include "../../html/Node.h"
#include "../../URL.h"
#include "../../networking/HTTPResponse.h"
#include "../../WebResource.h"

// document is scrollable until multicomponent
class DocumentComponent : public MultiComponent {
@@ -18,6 +19,7 @@ public:
void createComponentTree(const std::shared_ptr<Node> node, const std::shared_ptr<Component> &parentComponent);
std::shared_ptr<Component> searchComponentTree(const std::shared_ptr<Component> &component, const int x, const int y);
void navTo(const std::string url);
void handleResource(WebResource &res, std::string url);
void setDOM(const std::shared_ptr<Node> rootNode);
std::shared_ptr<Node> domRootNode = nullptr;
ComponentBuilder componentBuilder;

+ 70
- 51
src/graphics/components/InputComponent.cpp View File

@@ -30,7 +30,8 @@ InputComponent::InputComponent(const float rawX, const float rawY, const float r
lastRenderedWindowHeight = windowHeight;
// const float rawX, const float rawY, const float rawWidth, const float rawHeight, const unsigned int hexColor, const int passedWindowWidth, const int passedWindowHeight
this->cursorBox = new BoxComponent(x, y, 2, rawHeight, 0x000000FF, windowWidth, windowHeight);
// we only want it one line high (esp for text area)
this->cursorBox = new BoxComponent(x, y, 2, 13, 0x000000FF, windowWidth, windowHeight);
this->cursorBox->boundToPage = this->boundToPage;

this->cursorBox->x = x;
@@ -39,16 +40,25 @@ InputComponent::InputComponent(const float rawX, const float rawY, const float r
this->cursorBox->y = this->windowHeight + y - 13;
}
this->cursorBox->resize(this->windowWidth, this->windowHeight);
// can't updateText because no win set yet
// don't seem to need it either
//this->updateText();
this->updateText();
onFocus=[this]() {
this->focused = true;
// blink cursor
this->cursorTimer = scheduler->setInterval([this]() {
this->showCursor = !this->showCursor;
//std::cout << "showCursor " << this->showCursor << std::endl;
if (this->win) {
this->win->renderDirty = true;
}
}, 500);
this->updateCursor();
this->win->renderDirty = true;
};
onBlur=[this]() {
if (this->cursorTimer != nullptr) {
scheduler->clearInterval(this->cursorTimer);
}
this->focused = false;
this->win->renderDirty = true;
};
@@ -184,18 +194,6 @@ void InputComponent::render() {
if (focused) {
//std::cout << "Rendering cursor" << std::endl;
// blink cursor
if (cursorTimer != nullptr) {
scheduler->clearInterval(cursorTimer);
}
cursorTimer = scheduler->setInterval([this]() {
this->showCursor = !this->showCursor;
//std::cout << "showCursor " << this->showCursor << std::endl;
if (this->win) {
this->win->renderDirty = true;
}
}, 500);
// render it if we need to
if (showCursor) {
cursorBox->render();
@@ -292,11 +290,13 @@ void InputComponent::backSpace() {

void InputComponent::updateCursor() {
// this is texture shader coordinates now (not text shader coords)
cursorBox->x = x + this->estWidth;
//
cursorBox->x = x + this->cursorStartAtX;
if (boundToPage) {
cursorBox->y = windowHeight + y - 13;
cursorBox->y = windowHeight + y + this->cursorStartAtY;
} else {
cursorBox->y = y;
// + this->cursorStartAtY
cursorBox->y = y + 5;
}
//std::cout << "placing cursor at " << (int)cursorBox->x << "," << (int)cursorBox->y << std::endl;
cursorBox->resize(windowWidth, windowHeight);
@@ -314,56 +314,75 @@ void InputComponent::updateText() {
}
}
// maybe tie the fontsize to height
if (userInputText) {
delete userInputText;
}
userInputText=new TextComponent(value, 0, 0, 12, false, 0x000000FF, windowWidth, windowHeight);
if (!userInputText->win && this->win) {
userInputText->win = this->win;
}
//std::cout << "placing userInputText at " << static_cast<int>(x) << "," << static_cast<int>(y) << std::endl;
userInputText->x = x;
if (y < 0) {
userInputText->y = y;
} else {
userInputText->y = y - windowHeight + 16;
}
userInputText->boundToPage = this->boundToPage;
//std::cout << "placed userInputText at " << static_cast<int>(x) << "," << static_cast<int>(y - windowHeight) << std::endl;
// 125 pixels width
// but first we need to know how wide the text is
const std::shared_ptr<TextRasterizer> textRasterizer=rasterizerCache->loadFont(12, false); // fontSize, bold
const std::shared_ptr<TextRasterizer> textRasterizer = rasterizerCache->loadFont(12, false); // fontSize, bold
rasterizationRequest request;
request.text = value;
request.startX = x;
request.availableWidth = windowWidth;
request.sourceStartX = 0;
request.sourceStartY = 0;
request.noWrap = true;
std::unique_ptr<std::pair<int, int>> textInfo = textRasterizer->size(request);
if (this->multiLine) {
request.noWrap = false;
} else {
request.noWrap = true;
}
std::unique_ptr<sizeResponse> textInfo = textRasterizer->size(request);
if (textInfo.get() == nullptr) {
std::cout << "InputComponent::updateText - couldn't estimate value[" << value << "] size" << std::endl;
std::cout << "InputComponent::updateText - couldn't estimate value[" << this->value << "] size" << std::endl;
return;
}
//int textWidth = textInfo->first;
this->estWidth = std::get<0>(*textInfo.get());
//int estHeight = std::get<1>(*textInfo.get());
userInputText->rasterStartX = 0;
userInputText->rasterStartY = 0;
//std::cout << "InputComponent::updateText - estWidth: " << estWidth << " width: " << static_cast<int>(width) << std::endl;
if (estWidth > width) {
//std::cout << "scrolling text" << std::endl;
userInputText->rasterStartX = estWidth - width;
int estWidth = textInfo->width;
int estHeight = textInfo->height;
this->cursorStartAtX = textInfo->endingX;
this->cursorStartAtY = textInfo->endingY;
if (estHeight > this->height) {
this->cursorStartAtY += estHeight - this->height;
}
this->updateCursor();

userInputText->noWrap = true;
// why does changing the width mess shit up?
//std::cout << "InputComponent::updateText - our width: " << static_cast<int>(width) << " windowWidth: " << windowWidth << std::endl;
userInputText->resize(windowWidth, windowHeight, width); // need to make sure there's a texture
if (this->win) {
// maybe tie the fontsize to height
if (this->userInputText) {
delete this->userInputText;
}
this->userInputText = new TextComponent(value, 0, 0, 12, false, 0x000000FF, this->windowWidth, this->windowHeight);
if (!userInputText->win && this->win) {
userInputText->win = this->win;
}
//std::cout << "placing userInputText at " << static_cast<int>(x) << "," << static_cast<int>(y) << std::endl;
userInputText->x = this->x;
//std::cout << "InputComponent::updateText - y: " << static_cast<int>(this->y) << " boundToPage: " << this->boundToPage << std::endl;
//if (this->y < 0) {
if (this->boundToPage) {
userInputText->y = this->y;
} else {
userInputText->y = this->y - this->windowHeight + 16;
}
userInputText->boundToPage = this->boundToPage;

userInputText->rasterStartX = 0;
userInputText->rasterStartY = 0;
//std::cout << "InputComponent::updateText - estWidth: " << estWidth << " width: " << static_cast<int>(this->width) << " eX: " << textInfo->endingX << std::endl;
if (estWidth > this->width) {
//std::cout << "scrolling text" << std::endl;
userInputText->rasterStartX = estWidth - this->width;
}
//std::cout << "InputComponent::updateText - estHeight: " << estHeight << " height: " << static_cast<int>(this->height) << " eY: " << textInfo->endingY << std::endl;
if (this->multiLine && estHeight > this->height) {
//std::cout << "scrolling text" << std::endl;
userInputText->rasterStartY = estHeight - this->height; // start at one widget height from the bottom
}
//std::cout << "InputComponent::updateText - textHeight: " << estHeight << " inputHeight: " << static_cast<int>(height) << " eY: " << textInfo->endingY << " y0max:" << textInfo->y0max << " rasterStartY: " << userInputText->rasterStartY << " lines: " << textInfo->lines << std::endl;
// wrap if multiLine, otherwise don't
userInputText->noWrap = !this->multiLine;
userInputText->resize(this->windowWidth, this->windowHeight, this->width); // need to make sure there's a texture
this->win->renderDirty = true;
} // else it was called from cstr, and window isn't set up yet but no worrites, render will be dirty
}

+ 3
- 1
src/graphics/components/InputComponent.h View File

@@ -31,8 +31,10 @@ public:
bool showCursor = true;
std::shared_ptr<timer_handle> cursorTimer = nullptr;
// store for cursor
int estWidth;
int cursorStartAtX;
int cursorStartAtY;
TagNode *node = nullptr;
bool multiLine = false; // textarea control
};

#endif

+ 6
- 4
src/graphics/components/TabbedComponent.cpp View File

@@ -67,9 +67,9 @@ void TabbedComponent::addTab(std::string passedTitle) {
std::shared_ptr<TextComponent> newTabTitle = std::make_shared<TextComponent>(passedTitle, 0, 0, 12, false, this->tabTextColor, windowWidth, windowHeight);
size_t textWidth = 0;
newTabTitle->win = this->win;
std::unique_ptr<std::pair<int, int>> textInfo = newTabTitle->size();
std::unique_ptr<sizeResponse> textInfo = newTabTitle->size();
if (textInfo) {
textWidth = static_cast<size_t>(textInfo->first);
textWidth = static_cast<size_t>(textInfo->width);
//std::cout << "tab text width: " << textWidth << std::endl;
}
textWidth += 10; // 5px padding each side
@@ -182,6 +182,8 @@ void TabbedComponent::addTab(std::string passedTitle) {
// recalc what that just did to our fellow tabs
this->layoutTabs(this->selectedTab, 0);
this->selectedTab->get()->history->pushState(nullptr, url, url);
}
} else {
std::cout << "tabbedComponent::addTab:::docComponent->onBeforeLoad - document component but no tab selected" << std::endl;
@@ -433,9 +435,9 @@ void TabbedComponent::layoutTab(std::vector<std::shared_ptr<Tab>>::iterator tab)
// get text size for it's current string
// we don't always need to adjust textWidth, only on text change
int textWidthWPad = 0;
std::unique_ptr<std::pair<int, int>> textInfo = textComponent->size();
std::unique_ptr<sizeResponse> textInfo = textComponent->size();
if (textInfo) {
textWidthWPad = textInfo->first;
textWidthWPad = textInfo->width;
//std::cout << "tab text width: " << textWidth << std::endl;
}
// pad

+ 1
- 0
src/graphics/components/TabbedComponent.h View File

@@ -28,6 +28,7 @@ struct Tab {
std::shared_ptr<Node> domRootNode = nullptr;
//BrowsingHistory history;
// FIXME: we should move this into document component
// but then you can't unloaded delete the document component if you do that...
std::unique_ptr<BrowsingHistory> history = nullptr;
std::shared_ptr<Tab> previousTab = nullptr;
};

+ 12
- 12
src/graphics/components/TextComponent.cpp View File

@@ -110,16 +110,16 @@ void TextComponent::rasterize(const int rawX, const int rawY) {
//bool wrapped;
//glyphs = textRasterizer->rasterize(text, rawX, windowWidth, wrapToX, width, height, glyphCount, endingX, endingY, wrapped);
rasterizationRequest request;
request.text = text;
request.text = this->text;
request.startX = rawX; // - x
if (!boundToPage) {
// startX needs to be relative to the parent x. Why?
request.startX -= x;
request.startX -= this->x;
}
//std::cout << "TextComponent::rasterize - [" << text << "] request.startX: " << request.startX << " x: " << x << " rawX: " << rawX << std::endl;
request.availableWidth = availableWidth;
request.sourceStartX = rasterStartX;
request.sourceStartY = rasterStartY;
request.availableWidth = this->availableWidth;
request.sourceStartX = this->rasterStartX;
request.sourceStartY = this->rasterStartY;
if (this->win) {
request.maxTextureSize = this->win->maxTextureSize & INT_MAX;
} // else called from input cstr, no win yet
@@ -330,16 +330,16 @@ void TextComponent::sanitize(std::string &str) {
}
}

std::unique_ptr<std::pair<int, int>> TextComponent::size() const {
std::unique_ptr<sizeResponse> TextComponent::size() const {
const std::shared_ptr<TextRasterizer> textRasterizer=rasterizerCache->loadFont(fontSize, bold);
rasterizationRequest request;
request.text = text;
request.text = this->text;
// startX needs to be relative to the parent x
request.startX = x;
request.availableWidth = windowWidth;
request.sourceStartX = rasterStartX;
request.sourceStartY = rasterStartY;
request.noWrap = noWrap;
request.startX = this->x;
request.availableWidth = this->windowWidth;
request.sourceStartX = this->rasterStartX;
request.sourceStartY = this->rasterStartY;
request.noWrap = this->noWrap;
return textRasterizer->size(request);
}


+ 1
- 1
src/graphics/components/TextComponent.h View File

@@ -31,7 +31,7 @@ public:
void resize(const int passedWindowWidth, const int passedWindowHeight, const int passedAvailableWidth); // more detailed control
void sanitize(std::string &str);
void updateHighlight();
std::unique_ptr<std::pair<int, int>> size() const;
std::unique_ptr<sizeResponse> size() const;
// input needed stuff
int rasterStartX = 0; // start reading text source at and place at destination 0

+ 6
- 5
src/graphics/elements/BUTTONElement.cpp View File

@@ -5,6 +5,7 @@
#include "../components/DocumentComponent.h"
#include "../../WebResource.h"
#include "../../Log.h"
#include "../../FormData.h"

BUTTONElement::BUTTONElement() {
isInline = true;
@@ -23,7 +24,7 @@ std::unique_ptr<Component> BUTTONElement::renderer(const ElementRenderRequest &r
butComponent->name = "button";
butComponent->onClick=[tagNode, request]() {
// recurse up to find oug form tag
std::shared_ptr<Node> formNode = findFormNode(request.node);
std::shared_ptr<Node> formNode = Node::findTagNodeParent("form", request.node);
if (!formNode) {
std::cout << "BUTTONElement::renderer:butComponent->onClick - Can't find form parent for submit" << std::endl;
return;
@@ -59,8 +60,8 @@ std::unique_ptr<Component> BUTTONElement::renderer(const ElementRenderRequest &r
URL uAction = request.docComponent->currentURL.merge(URL(formNodeActionIter->second));
std::cout << "Action URL is " << uAction.toString() << std::endl;
// download URL
WebResource res = postWebResource(uAction, std::move(formData));
handleResource(res, uAction.toString(), request.docComponent);
WebResource res = getOnlineWebResource(uAction, std::move(formData));
request.docComponent->handleResource(res, uAction.toString());
} else {
// need documentComponent
@@ -84,8 +85,8 @@ std::unique_ptr<Component> BUTTONElement::renderer(const ElementRenderRequest &r
URL uAction = request.docComponent->currentURL.merge(URL(formNodeActionIter->second+"?"+queryString));
std::cout << "Action URL is " << uAction.toString() << std::endl;
// download URL
WebResource res = postWebResource(uAction, nullptr);
handleResource(res, uAction.toString(), request.docComponent);
WebResource res = getOnlineWebResource(uAction, nullptr);
request.docComponent->handleResource(res, uAction.toString());
}

};

+ 10
- 145
src/graphics/elements/INPUTElement.cpp View File

@@ -4,140 +4,13 @@
#include "../components/DocumentComponent.h"
#include "../../Log.h"
#include "../../html/HTMLParser.h"
#include "../../FormData.h"
#include <ctime>

INPUTElement::INPUTElement() {
isInline = true;
}

void handleResource(WebResource &res, std::string url, DocumentComponent *docComponent) {
if (res.resourceType == ResourceType::INVALID) {
logError() << "Invalid resource type: " << res.raw << std::endl;
return;
}
//std::cout << "body: " << res.raw << std::endl;
//std::cout << "type: " << res.resourceType << std::endl;
// parse HTML
if (res.resourceType == ResourceType::HTML) {
HTMLParser parser;
const std::clock_t begin = clock();
std::shared_ptr<Node> rootNode = parser.parse(res.raw);
const std::clock_t end = clock();
logDebug() << "main::setWindowContent - Parsed document in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
// send NodeTree to window
//this->win->setDOM(rootNode);
//printNode(rootNode, 0);
// we need a way to communicate with our tabComponent
// maybe an event is best
if (docComponent->onBeforeLoad) {
docComponent->onBeforeLoad(url);
}
docComponent->setDOM(rootNode);
} else if (res.resourceType == ResourceType::TXT) {
std::cout << "Rendering text document" << std::endl;
std::shared_ptr<Node> rootNode = std::make_shared<Node>(NodeType::ROOT);
std::shared_ptr<TagNode> tagNode = std::make_shared<TagNode>();
tagNode->tag="p";
// bind tag to root
tagNode->parent = rootNode;
rootNode->children.push_back(tagNode);
std::shared_ptr<TextNode> textNode = std::make_shared<TextNode>();
textNode->text = res.raw;
// bind text to tag
textNode->parent = tagNode;
tagNode->children.push_back(textNode);
// send NodeTree to window
//this->win->setDOM(rootNode);
docComponent->setDOM(rootNode);
} else {
std::cout << "setWindowContent() - I don't know how to render non-html files" << std::endl;
}
}

std::shared_ptr<Node> findFormNode(std::shared_ptr<Node> node) {
if (!node.get()) {
// found root?
return nullptr;
}
TagNode *tagNode = dynamic_cast<TagNode*>(node.get());
if (tagNode) {
if (tagNode->tag == "form") {
return node;
}
}
return findFormNode(node->parent);
}

std::unique_ptr<std::pair<std::string, std::string>> getTagNodeNameValue(TagNode &tagNode) {
auto namePropIter = tagNode.properties.find("name");
if (namePropIter == tagNode.properties.end()) {
return nullptr;
}
auto valuePropIter = tagNode.properties.find("value");
if (valuePropIter == tagNode.properties.end()) {
return nullptr;
}
return std::make_unique<std::pair<std::string, std::string>>(namePropIter->second, valuePropIter->second);
}

// how do we match up nodes to components to extract the current values out of the components?
// well we link input components back to node (and/or nodes back to components)
// or we create a form UI component
std::unique_ptr<std::map<std::string, std::string>> buildFormData(std::shared_ptr<Node> formNode, std::unique_ptr<std::map<std::string, std::string>> result) {
if (!result) {
result = std::make_unique<std::map<std::string, std::string>>();
}
// check this node
TagNode *tagNode = dynamic_cast<TagNode*>(formNode.get());
if (tagNode) {
auto typePropIter = tagNode->properties.find("type");
bool skip = false;
if (typePropIter != tagNode->properties.end()) {
if (typePropIter->second == "submit") {
skip = true;
}
if (typePropIter->second == "checkbox") {
// FIXME: see if it's checked or not
skip = true;
}
if (typePropIter->second == "radio") {
// FIXME: see if it's checked or not
skip = true;
}
}
auto namePropIter = tagNode->properties.find("name");
if (!skip && namePropIter != tagNode->properties.end()) {
auto valuePropIter = tagNode->properties.find("value");
if (valuePropIter != tagNode->properties.end()) {
auto it = result->find(namePropIter->second);
if (it == result->end()) {
std::cout << "INPUTElement.buildFormData - setting " << namePropIter->second << " to [" << valuePropIter->second << "]" << std::endl;
result->insert(std::pair<std::string, std::string>(namePropIter->second, valuePropIter->second));
} else {
(*result)[namePropIter->second] = valuePropIter->second;
}
}
}
}
// check children noedes
for (auto &child : formNode->children) {
//std::shared_ptr<TagNode> tagNode = std::dynamic_pointer_cast<TagNode>(child);
//if (tagNode) {
result = buildFormData(child, std::move(result));
//}
}
return result;
}

std::unique_ptr<Component> INPUTElement::renderer(const ElementRenderRequest &request) {
// const float rawX, const float rawY, const float rawWidth, const float rawHeight, const int windowWidth, const int windowHeight
// what should our default size be?
@@ -166,12 +39,14 @@ std::unique_ptr<Component> INPUTElement::renderer(const ElementRenderRequest &re
auto propIter3 = tagNode->properties.find("value");
if (propIter3 != tagNode->properties.end()) {
butComponent->value = propIter3->second;
// resize button to text size
butComponent->resizeToTextSize();
} else {
butComponent->value = "submit";
butComponent->value = "Submit";
}
butComponent->onClick=[tagNode, request]() {
// recurse up to find oug form tag
std::shared_ptr<Node> formNode = findFormNode(request.node);
std::shared_ptr<Node> formNode = Node::findTagNodeParent("form", request.node);
if (!formNode) {
std::cout << "INPUTElement::renderer:butComponent->onClick - Can't find form parent for submit" << std::endl;
return;
@@ -197,7 +72,7 @@ std::unique_ptr<Component> INPUTElement::renderer(const ElementRenderRequest &re
auto formData = buildFormData(formNode, nullptr);
// add our name/value (because we skip all submit buttons, there can only be one)
auto submitButtonNameValue = getTagNodeNameValue(*tagNode);
auto submitButtonNameValue = tagNode->getTagNodeNameValue();
if (submitButtonNameValue) {
formData->insert(*submitButtonNameValue);
}
@@ -207,8 +82,8 @@ std::unique_ptr<Component> INPUTElement::renderer(const ElementRenderRequest &re
URL uAction = request.docComponent->currentURL.merge(URL(formNodeActionIter->second));
std::cout << "Action URL is " << uAction.toString() << std::endl;
// download URL
WebResource res = postWebResource(uAction, std::move(formData));
handleResource(res, uAction.toString(), request.docComponent);
WebResource res = getOnlineWebResource(uAction, std::move(formData));
request.docComponent->handleResource(res, uAction.toString());
} else {
// need documentComponent

@@ -232,8 +107,8 @@ std::unique_ptr<Component> INPUTElement::renderer(const ElementRenderRequest &re
URL uAction = request.docComponent->currentURL.merge(URL(formNodeActionIter->second+"?"+queryString));
std::cout << "Action URL is " << uAction.toString() << std::endl;
// download URL
WebResource res = postWebResource(uAction, nullptr);
handleResource(res, uAction.toString(), request.docComponent);
WebResource res = getOnlineWebResource(uAction, nullptr);
request.docComponent->handleResource(res, uAction.toString());
}

};
@@ -245,15 +120,5 @@ std::unique_ptr<Component> INPUTElement::renderer(const ElementRenderRequest &re
//std::cout << "INPUTElement::renderer - ignoring input is of type: " << type << std::endl;
}
}

/*
TextNode *textNode = dynamic_cast<TextNode*>(node.get());
if (textNode) {
if (node->parent->children.size() == 1) {
std::unique_ptr<Component> component = std::make_unique<InputComponent>(textNode->text, x, y, 12, false, 0x000000FF, windowWidth, windowHeight);
return component;
}
}
*/
return nullptr;
}

+ 0
- 9
src/graphics/elements/INPUTElement.h View File

@@ -3,15 +3,6 @@

#include "Element.h"
#include "../components/Component.h"
#include "../../html/TextNode.h"
#include "../../WebResource.h"

class DocumentComponent;

std::shared_ptr<Node> findFormNode(std::shared_ptr<Node> node);
std::unique_ptr<std::pair<std::string, std::string>> getTagNodeNameValue(TagNode &tagNode);
std::unique_ptr<std::map<std::string, std::string>> buildFormData(std::shared_ptr<Node> formNode, std::unique_ptr<std::map<std::string, std::string>> result);
void handleResource(WebResource &res, std::string url, DocumentComponent *docComponent);

class INPUTElement : public Element {
public:

+ 28
- 0
src/graphics/elements/TEXTAREAElement.cpp View File

@@ -0,0 +1,28 @@
#include "TEXTAREAElement.h"
#include "../components/InputComponent.h"
#include "../components/ButtonComponent.h"
#include "../components/DocumentComponent.h"
#include "../../Log.h"
#include "../../html/HTMLParser.h"
#include "../../FormData.h"
#include <ctime>

TEXTAREAElement::TEXTAREAElement() {
isInline = true;
}

std::unique_ptr<Component> TEXTAREAElement::renderer(const ElementRenderRequest &request) {
// const float rawX, const float rawY, const float rawWidth, const float rawHeight, const int windowWidth, const int windowHeight
// what should our default size be?
//std::cout << "INPUTElement::renderer - creating InputComponent at " << x << "x" << y << std::endl;
TagNode *tagNode = dynamic_cast<TagNode*>(request.node.get());
if (tagNode) {
std::unique_ptr<InputComponent> inputComponent = std::make_unique<InputComponent>(0, 0, 250.0f, 52.0f, request.parentComponent->win->windowWidth, request.parentComponent->win->windowHeight);
inputComponent->node = tagNode;
inputComponent->multiLine = true;
//inputComponent->name = "textareaInput";
return std::move(inputComponent);

}
return nullptr;
}

+ 13
- 0
src/graphics/elements/TEXTAREAElement.h View File

@@ -0,0 +1,13 @@
#ifndef TEXTAREAELEMENT_H
#define TEXTAREAELEMENT_H

#include "Element.h"
#include "../components/Component.h"

class TEXTAREAElement : public Element {
public:
TEXTAREAElement();
virtual std::unique_ptr<Component> renderer(const ElementRenderRequest &request);
};

#endif

+ 14
- 2
src/graphics/opengl/Window.cpp View File

@@ -109,12 +109,17 @@ bool Window::init() {
navBackButton->uiControl.w = { 0, 32 }; // 32px
navBackButton->uiControl.h = { 0, 32 }; // 32px
navBackButton->onClick=[this]() {
//std::cout << "Back" << std::endl;
std::cout << "Window::init.navBackButton->onClick - Back" << std::endl;
TabbedComponent *pTabComponent = dynamic_cast<TabbedComponent*>(this->tabComponent.get());
if (pTabComponent) {
if (pTabComponent->selectedTabId) {
pTabComponent->selectedTab->get()->history->print();
pTabComponent->selectedTab->get()->history->back();
} else {
std::cout << "Window::init.navBackButton->onClick - no tab selected" << std::endl;
}
} else {
std::cout << "Window::init.navBackButton->onClick - no tabbed component" << std::endl;
}
};
navBackButton->onMouseover = [navBackButton, this]() {
@@ -454,7 +459,14 @@ bool Window::initGLFW() {
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) {

// command for OSX, otherwise control
#if defined(__APPLE__) && defined(__MACH__)
int attentionMod = GLFW_MOD_SUPER;
#else
int attentionMod = GLFW_MOD_CONTROL;
#endif
if (key == GLFW_KEY_N && action == GLFW_RELEASE && mods == attentionMod) {
browser->addWindow();
return;
}

+ 97
- 123
src/graphics/text/TextRasterizer.cpp View File

@@ -45,7 +45,7 @@ TextRasterizer::~TextRasterizer() {
FT_Done_FreeType(lib);
}

std::unique_ptr<std::pair<int, int>> TextRasterizer::size(const rasterizationRequest &request) const {
std::unique_ptr<sizeResponse> TextRasterizer::size(const rasterizationRequest &request) const {
if (!request.availableWidth) {
// window is likely minimized, so no output
return nullptr;
@@ -81,18 +81,20 @@ std::unique_ptr<std::pair<int, int>> TextRasterizer::size(const rasterizationReq
const hb_glyph_info_t *glyphInfo = hb_buffer_get_glyph_infos(buffer, &glyphCount);
const hb_glyph_position_t *glyphPos = hb_buffer_get_glyph_positions(buffer, &glyphCount);
std::unique_ptr<rasterizationResponse> response = std::make_unique<rasterizationResponse>();
std::unique_ptr<sizeResponse> response = std::make_unique<sizeResponse>();
// figure out width/height
int cx = 0;
int cy = 0;
int xmax = 0;
int y0max = 0, y1max = 0;
int lines = 1;
//int y0max = 0;
int y1max = 0;
//int lines = 1;
response->wrapped = false;
int lineXStart = request.startX;
int leftPadding = 0;
int wrapToX = 0;
//int leftPadding = 0;
//int wrapToX = 0;
int maxy0 = 0;
for (unsigned int i = 0; i < glyphCount; i++) {
if (FT_Load_Glyph(*face, glyphInfo[i].codepoint, FT_LOAD_DEFAULT)) {
std::cout << "Could not load glyph" << std::endl;
@@ -108,12 +110,39 @@ std::unique_ptr<std::pair<int, int>> TextRasterizer::size(const rasterizationReq
// how much space does this character take
const float xa = static_cast<float>(glyphPos[i].x_advance) / 64;
const float ya = static_cast<float>(glyphPos[i].y_advance) / 64; //mostly 0s

const float yo = static_cast<float>(glyphPos[i].y_offset) / 64;
int y0 = static_cast<int>(floor(yo + slot->bitmap_top));
//miny0 = std::min(y0, miny0);
maxy0 = std::max(y0, maxy0);
// do we need to padding the texture to the left for any lines
if (cx == 0) {
if (slot->bitmap_left < 0) {
// figure out max amount of padding we need
leftPadding = std::max(leftPadding, -slot->bitmap_left);
response->leftPadding = std::max(response->leftPadding, -slot->bitmap_left);
}
}
// manual request to wrap (new line)
//std::cout << "char " << request.text[i] << " codepoint: " << glyphInfo[i].codepoint << std::endl;
if (request.text[i] == '\n' || request.text[i] == '\r'){
//std::cout << "new line|return" << std::endl;
response->wrapped = true;
if (request.noWrap) {
glyphCount = i;
// we're done
std::cout << "TextRasterizer::rasterize - newline found no wrap is on" << std::endl;
break;
} else {
xmax=std::max(xmax, cx);
cx = response->wrapToX;
cy -= std::ceil(1.2f * fontSize); // 1.2 scalar from https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
response->lines++;
lineXStart = response->wrapToX; // change where we start
// don't add this character
continue;
}
}
@@ -125,12 +154,12 @@ std::unique_ptr<std::pair<int, int>> TextRasterizer::size(const rasterizationReq
} else {
//std::cout << "multine text: [" << text << "] new line:" << cy << " x: " << (int)x << "+ cx:" << (int)cx << std::endl;
//std::cout << "line #" << lines << " starts at " << lineXStart << " ends at " << lineXStart + cx << std::endl;
xmax = request.availableWidth - wrapToX; // the whole width of parent to the edge of windowWidth
xmax = request.availableWidth - response->wrapToX; // the whole width of parent to the edge of windowWidth
//std::cout << "new width: " << xmax << std::endl;
cx = wrapToX; // wrapToX (was 0) (was -= xmax)
cx = response->wrapToX; // wrapToX (was 0) (was -= xmax)
cy -= std::ceil(1.2f * fontSize); // 1.2 scalar from https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
lines++;
lineXStart = wrapToX; // change where we start
response->lines++;
lineXStart = response->wrapToX; // change where we start
}
}
@@ -142,33 +171,43 @@ std::unique_ptr<std::pair<int, int>> TextRasterizer::size(const rasterizationReq
// update glyph maxes
const FT_Bitmap ftBitmap = slot->bitmap;
const float yo = static_cast<float>(glyphPos[i].y_offset) / 64;
int y0 = static_cast<int>(floor(yo + slot->bitmap_top));
//const float yo = static_cast<float>(glyphPos[i].y_offset) / 64;
//int y0 = static_cast<int>(floor(yo + slot->bitmap_top));
int y1 = y0 + static_cast<int>(ftBitmap.rows);
y0max=std::max(y0max, y0);
response->y0max = std::max(response->y0max, y0);
y1max=std::max(y1max, y1);
// track new max width
xmax=std::max(xmax, cx);
}
if (leftPadding) {
xmax+=leftPadding; // increase width;
if (response->leftPadding) {
xmax += response->leftPadding; // increase width;
}
// at least one line
cy -= std::ceil(1.2f * fontSize); // 1.2 scalar from https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
response->height = -cy;
response->width = xmax;
//std::cout << "lines: " << lines << " wrapToX: " << wrapToX << " startX: " << x << " xmax: " << xmax << std::endl;
//std::cout << "y1max: " << y1max << " lines: " << lines << std::endl;
y1max *= lines;
if (response->y0max != maxy0) {
std::cout << "TextRasterizer::size - resp.y0max: " << response->y0max << " maxy0: " << maxy0 << std::endl;
}
//std::cout << "y1max: " << y1max << " lines: " << response->lines << std::endl;
int textureHeight = (y1max - response->y0max) * static_cast<int>(response->lines);
//int textureHeight = y1max * response->lines;
//y1max *= response->lines;
//std::cout << "initial:" << (int)width << "x" << (int)height << std::endl;
if (response->height < y1max) {
response->height = y1max;
if (response->height < textureHeight) {
response->height = textureHeight;
//std::cout << "adjusted:" << (int)width << "x" << (int)height << std::endl;
}

response->endingX = cx - request.sourceStartX;
response->endingY = cy + 2 + request.sourceStartY;

// FIXME: how about endingX,Y?
return std::make_unique<std::pair<int, int>>(std::pair<int, int>(response->width, response->height));
//return std::make_unique<std::pair<int, int>>(std::pair<int, int>(response->width, response->height));
return response;
}

#include <GL/glew.h>
@@ -206,100 +245,12 @@ std::unique_ptr<rasterizationResponse> TextRasterizer::rasterize(const rasteriza
const hb_glyph_position_t *glyphPos = hb_buffer_get_glyph_positions(buffer, &glyphCount);
std::unique_ptr<rasterizationResponse> response = std::make_unique<rasterizationResponse>();
std::unique_ptr<sizeResponse> sizeResponse = this->size(request);

// figure out width/height
int cx = 0;
int cy = 0;
int xmax = 0;
int y0max = 0, y1max = 0;
int lines = 1;
response->wrapped = false;
int lineXStart = request.startX;
int leftPadding = 0;
int wrapToX = 0;
for (unsigned int i = 0; i < glyphCount; i++) {
if (FT_Load_Glyph(*face, glyphInfo[i].codepoint, FT_LOAD_DEFAULT)) {
std::cout << "Could not load glyph" << std::endl;
return nullptr;
}
const FT_GlyphSlot slot = (*face)->glyph;
if (FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL)) {
std::cout << "Could not render glyph" << std::endl;
return nullptr;
}
// how much space does this character take
const float xa = static_cast<float>(glyphPos[i].x_advance) / 64;
const float ya = static_cast<float>(glyphPos[i].y_advance) / 64; //mostly 0s
// do we need to padding the texture to the left for any lines
if (cx == 0) {
if (slot->bitmap_left < 0) {
// figure out max amount of padding we need
leftPadding = std::max(leftPadding, -slot->bitmap_left);
}
}
// wrap to next line on width
if ((cx + lineXStart) - request.sourceStartX >= request.availableWidth) {
response->wrapped = true;
if (request.noWrap) {
std::cout << "TextRasterizer::rasterize - we've hit wrap & noWrap at " << cx << " + " << lineXStart << " < " << request.availableWidth << std::endl;
// we can only fit partial of the last char
std::cout << "TextRasterizer::rasterize - truncating " << glyphCount << " char(s) to " << i << " char(s)" << std::endl;
glyphCount = i;
// we're done
break;
} else {
//std::cout << "multine text: [" << text << "] new line:" << cy << " x: " << (int)x << "+ cx:" << (int)cx << std::endl;
//std::cout << "line #" << lines << " starts at " << lineXStart << " ends at " << lineXStart + cx << std::endl;
xmax = request.availableWidth - wrapToX; // the whole width of parent to the edge of windowWidth
//std::cout << "new width: " << xmax << std::endl;
cx = wrapToX; // wrapToX (was 0) (was -= xmax)
cy -= std::ceil(1.2f * fontSize); // 1.2 scalar from https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
lines++;
lineXStart = wrapToX; // change where we start
}
}
//std::cout << "glyph:" << xa << "x" << ya << std::endl;
// update glyph space allocation
cx += xa;
cy += ya; // is normal for y0 at bottom
// update glyph maxes
const FT_Bitmap ftBitmap = slot->bitmap;
const float yo = static_cast<float>(glyphPos[i].y_offset) / 64;
int y0 = static_cast<int>(floor(yo + slot->bitmap_top));
int y1 = y0 + static_cast<int>(ftBitmap.rows);
y0max=std::max(y0max, y0);
y1max=std::max(y1max, y1);
// track new max width
xmax=std::max(xmax, cx);
}
//std::cout << "TextRasterizer::rasterize - xmax exits loop at " << xmax << std::endl;
if (leftPadding) {
//std::cout << "TextRasterizer::rasterize - leftPadding " << leftPadding << std::endl;
xmax += leftPadding; // increase width;
}
// at least one line
cy -= std::ceil(1.2f * fontSize); // 1.2 scalar from https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
//std::cout << "TextRasterizer::rasterize - initial width " << xmax << std::endl;
response->width = xmax;
response->height = -cy;
//std::cout << "lines: " << lines << " wrapToX: " << wrapToX << " startX: " << x << " xmax: " << xmax << std::endl;
//std::cout << "y1max: " << y1max << " lines: " << lines << std::endl;
y1max *= lines;
//std::cout << "initial:" << (int)width << "x" << (int)height << std::endl;
if (response->height < y1max) {
response->height = y1max;
//std::cout << "adjusted:" << (int)width << "x" << (int)height << std::endl;
}
response->width = sizeResponse->width;
response->height = sizeResponse->height;

//std::cout << "after adjust:" << (int)response->width << "x" << (int)response->height << " lines" << lines << std::endl;
// adjust sourceStart
//std::cout << "TextRasterizer::rasterize - adjust sourceStart: " << request.sourceStartX << "," << request.sourceStartY << std::endl;
response->width -= request.sourceStartX;
@@ -324,9 +275,9 @@ std::unique_ptr<rasterizationResponse> TextRasterizer::rasterize(const rasteriza
}
// translation information
response->x0 = -leftPadding; // wrap to element start (wrapToX (0) or x)
response->x0 = -sizeResponse->leftPadding; // wrap to element start (wrapToX (0) or x)
response->y0 = 0;
response->x1 = -leftPadding + response->width;
response->x1 = -sizeResponse->leftPadding + response->width;
response->y1 = -response->height;
//std::cout << "xd: " << static_cast<int>(response->x1-response->x0) << " yd: " << static_cast<int>(response->y0-response->y1) << std::endl;
@@ -345,8 +296,8 @@ std::unique_ptr<rasterizationResponse> TextRasterizer::rasterize(const rasteriza

// copy all glyphs into one single glyph
// still neds to start at X
cx = response->wrapped ? request.startX : 0; // reset
cy = 0;
int cx = response->wrapped ? request.startX : 0; // reset
int cy = 0;
//std::cout << "[" << request.text << "] wrapped? " << response->wrapped << " starting at: " << cx << std::endl;
//int miny0 = 99;
int maxy0 = 0;
@@ -371,11 +322,26 @@ std::unique_ptr<rasterizationResponse> TextRasterizer::rasterize(const rasteriza
//miny0 = std::min(y0, miny0);
maxy0 = std::max(y0, maxy0);
int bump = y0max - y0; // Y adjust for this glyph
int bump = sizeResponse->y0max - y0; // Y adjust for this glyph
const float xa = static_cast<float>(glyphPos[i].x_advance) / 64;

if (request.text[i] == '\n' || request.text[i] == '\r'){
//std::cout << "new line" << std::endl;
if (request.noWrap) {
// we're done
//std::cout << "TextRasterize::rasterize - noWrap on, ending" << std::endl;
break;
} else {
cx = sizeResponse->wrapToX;
cy += std::ceil(1.2f * fontSize); // 1.2 scalar from https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
// no need to write new line char
continue;
}
}
// if this char is too width for this line, advance to next line
if (!request.noWrap && cx + xa >= request.availableWidth) {
cx = wrapToX;
cx = sizeResponse->wrapToX;
cy += std::ceil(1.2f * fontSize);// 1.2 scalar from https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
//std::cout << "textWrap - cx reset to: " << cx << " cy is now: " << cy << std::endl;
}
@@ -386,13 +352,20 @@ std::unique_ptr<rasterizationResponse> TextRasterizer::rasterize(const rasteriza
cx += xa;
continue;
}
if (cy < request.sourceStartY) {
// skip ahead
// seems to just an optimization
//std::cout << "cy: " << cy << " < sourceStartY: " << request.sourceStartY << std::endl;
continue;
}
//std::cout << "Writing " << request.text[i] << " to " << cx << ", " << cy << std::endl;
// place glyph bitmap data into texture
for (unsigned int iy = 0; iy < ftBitmap.rows; iy++) { // line by line
// source is 0 to (0:iy:rows)
// dest is cx+bl, (0:iy:rows)+(0:cy:height)+bump
//std::cout << "placing glyph row at " << (cx + slot->bitmap_left) << "x" << ((iy + cy) + bump) << std::endl;
unsigned int destPos = static_cast<unsigned int>(cx - request.sourceStartX + leftPadding + slot->bitmap_left) + ((iy + static_cast<unsigned int>(cy - request.sourceStartY)) + static_cast<unsigned int>(bump)) * response->textureWidth;
unsigned int destPos = static_cast<unsigned int>(cx - request.sourceStartX + sizeResponse->leftPadding + slot->bitmap_left) + ((iy + static_cast<unsigned int>(cy - request.sourceStartY)) + static_cast<unsigned int>(bump)) * response->textureWidth;
if (destPos >= size) {
// we're done with this line
continue;
@@ -405,8 +378,9 @@ std::unique_ptr<rasterizationResponse> TextRasterizer::rasterize(const rasteriza
continue;
}
//std::cout << "putting:" << (int)src[ix] << " over " << (int)dest[ix] << std::endl;
if (src[ix] && src[ix]>dest[ix]) {
dest[ix]=src[ix];
// probably could be or'd
if (src[ix] && src[ix] > dest[ix]) {
dest[ix] = src[ix];
}
}
}

+ 15
- 1
src/graphics/text/TextRasterizer.h View File

@@ -37,12 +37,26 @@ struct rasterizationRequest {
int startX; // starting x point (relative to 0) this is currently
//int wrapToX; // next line starts at X (was always 0)

// scrolling basically:
int sourceStartX = 0; // start reading text source at and place at destination 0
int sourceStartY = 0;
bool noWrap = false; // different than overflow but related
unsigned int maxTextureSize = 0;
};

struct sizeResponse {
float width;
float height;
int glyphCount;
int endingX;
int endingY;
bool wrapped;
int leftPadding = 0;
int y0max = 0;
int wrapToX = 0;
unsigned int lines = 1;
};

struct rasterizationResponse {
float width;
float height;
@@ -71,7 +85,7 @@ public:
TextRasterizer(const std::string &fontPath, const int size, const unsigned int resolution, const bool bold);
~TextRasterizer();
//std::unique_ptr<Glyph[]> rasterize(const std::string &text, const int x, const int windowWidth, const int wrapToX, float &width, float &height, unsigned int &glyphCount, int &endingX, int &endingY, bool &wrapped) const;
std::unique_ptr<std::pair<int, int>> size(const rasterizationRequest &request) const;
std::unique_ptr<sizeResponse> size(const rasterizationRequest &request) const;
std::unique_ptr<rasterizationResponse> rasterize(const rasterizationRequest &request) const;
bool isUnicodeBMP(const FT_Face &face) const;
FT_Library lib;

+ 15
- 0
src/html/Node.cpp View File

@@ -1,4 +1,5 @@
#include "Node.h"
#include "TagNode.h"

Node::Node(NodeType type) {
nodeType = type;
@@ -19,3 +20,17 @@ std::vector<std::string> Node::getSourceList() {
return returnVec;
}

std::shared_ptr<Node> Node::findTagNodeParent(std::string tag, std::shared_ptr<Node> node) {
if (!node.get()) {
// found root?
return nullptr;
}
TagNode *tagNode = dynamic_cast<TagNode*>(node.get());
if (tagNode) {
if (tagNode->tag == tag) {
return node;
}
}
return Node::findTagNodeParent(tag, node->parent);
}

+ 1
- 0
src/html/Node.h View File

@@ -24,6 +24,7 @@ public:
std::vector<std::shared_ptr<Node>> children;
std::shared_ptr<Component> component;

static std::shared_ptr<Node> findTagNodeParent(std::string tag, std::shared_ptr<Node> node);
virtual std::vector<std::string> getSourceList();
};


+ 12
- 0
src/html/TagNode.cpp View File

@@ -21,3 +21,15 @@ std::vector<std::string> TagNode::getSourceList() {
return returnVec;
}

std::unique_ptr<std::pair<std::string, std::string>> TagNode::getTagNodeNameValue() {
auto namePropIter = this->properties.find("name");
if (namePropIter == this->properties.end()) {
return nullptr;
}
auto valuePropIter = this->properties.find("value");
if (valuePropIter == this->properties.end()) {
return nullptr;
}
return std::make_unique<std::pair<std::string, std::string>>(namePropIter->second, valuePropIter->second);
}

+ 1
- 0
src/html/TagNode.h View File

@@ -11,6 +11,7 @@ public:
std::map<std::string, std::string> properties;

std::vector<std::string> getSourceList() override;
std::unique_ptr<std::pair<std::string, std::string>> getTagNodeNameValue();
};

#endif

Loading…
Cancel
Save