Browse Source

Merge branch 'master' of https://gitgud.io/odilitime/netrunner

# Conflicts:
#	README.md
master
despair 2 years ago
parent
commit
b82a2a8a2d

+ 10
- 5
README.md View File

@@ -1,5 +1,5 @@
NT5+ (i386): [![Build Status](https://nt-build-bot.rvx86.net/job/netrunner-winnt-i686/badge/icon)](https://nt-build-bot.rvx86.net:7421/job/netrunner-winnt-i686/)
.NET Server+ (AMD64): [![Build Status](https://nt-build-bot.rvx86.net/buildStatus/icon?job=netrunner-winnt-amd64)](https://nt-build-bot.rvx86.net:7421/job/netrunner-winnt-amd64/)
NT5+ (i386): [![Build Status](https://nt-build-bot.rvx86.net/job/netrunner-winnt-i686/badge/icon)](https://nt-build-bot.rvx86.net/job/netrunner-winnt-i686/)
.NET Server+ (AMD64): [![Build Status](https://nt-build-bot.rvx86.net/buildStatus/icon?job=netrunner-winnt-amd64)](https://nt-build-bot.rvx86.net/job/netrunner-winnt-amd64/)

# Netrunner

@@ -24,7 +24,6 @@ I'd like to develop text-based structures for communication between each piece.
- [freetype2 (2.8 works)](https://www.freetype.org/download.html)
- [harfbuzz (1.4.6 works)](https://github.com/behdad/harfbuzz/releases)
- [mbedtls (2.5.1 works, 2.3 doesn't)](https://github.com/ARMmbed/mbedtls/releases)
- [md5sum (for shader compilation)](https://spit.mixtape.moe/view/765a1dc1)

### Package shortucts
Some of these do not include mbedtls yet, if you know the correct package name for your distro, please let us know
@@ -51,16 +50,21 @@ Be sure to use gmake too

## Binaries
### Linux
[2017-08-22 binary package](https://my.mixtape.moe/pejuau.tar.gz)
[2017-08-29v2 binary package](https://my.mixtape.moe/xsivqb.tar.gz)

GyroNinja.net is back online

[nightly binary only (no font/pnm files)](https://gyroninja.net:1615/job/NetRunner/lastSuccessfulBuild/artifact/netrunner)

[Jenkins](https://gyroninja.net:1615/job/NetRunner/)

### OSX
Tested
[2017-08-22v2 binary package](https://my.mixtape.moe/gbxees.zip)

[2017-08-29v2 binary package](https://my.mixtape.moe/katvqh.zip)

Untested

[2017-08-27 binary package](https://my.mixtape.moe/yxasht.zip)

### Windows
@@ -70,6 +74,7 @@ Untested
[64bit nightly package](https://nt-build-bot.rvx86.net/job/netrunner-winnt-amd64/lastSuccessfulBuild/artifact/*zip*/archive.zip)



## Milestones
- Browse 4chan /g/ board


+ 1
- 1
res/shaders/FontShader.frag View File

@@ -4,6 +4,6 @@ in vec4 colorPipe;
in vec2 texCoordPipe;
out vec4 colorOut;
void main() {
// colorOut = vec4(0.0, 0.0, 0.0, texture(textureIn, texCoordPipe).r); // TODO blend alpha
//colorOut = vec4(0.0, 0.0, 0.0, texture(textureIn, texCoordPipe).r); // TODO blend alpha
colorOut = vec4(colorPipe.rgb, texture(textureIn, texCoordPipe).r); // TODO blend alpha
}

+ 2
- 1
res/shaders/TextureShader.vert View File

@@ -1,8 +1,9 @@
#version 330
uniform mat4 transform;
layout (location = 0) in vec3 vertexIn;
layout (location = 1) in vec2 texCoordIn;
out vec2 texCoordPipe;
void main() {
gl_Position = vec4(vertexIn, 1.0);
gl_Position = transform * vec4(vertexIn, 1.0);
texCoordPipe = texCoordIn;
}

+ 14
- 1
src/URL.cpp View File

@@ -185,6 +185,7 @@ std::tuple<std::unique_ptr<URL>,enum URIParseError> parseUri(std::string raw) {
return std::make_tuple(std::move(uri), URI_PARSE_ERROR_PORT);
}
} else if (state == PATH) {
//std::cout << "state == PATH char:" << raw[cursor] << std::endl;
if (raw[cursor] == '?' || raw[cursor] == '#') {
uri->path = raw.substr(last, cursor - last);
last = cursor;
@@ -198,12 +199,15 @@ std::tuple<std::unique_ptr<URL>,enum URIParseError> parseUri(std::string raw) {
break;
}
} else if (state == QUERY) {
//std::cout << "parsing query " << raw[cursor] << " @ " << (cursor + 1) << "/" << raw.length() << std::endl;
if (raw[cursor] == '#') {
uri->query = raw.substr(last + 1, cursor - last - 1);
std::cout << "Parsed query " << uri->query << std::endl;
last = cursor;
state = FRAGMENT;
} else if (cursor + 1 == raw.length()) {
uri->query = raw.substr(last + 1, cursor + 1 - last);
std::cout << "Parsed query " << uri->query << std::endl;
break;
}
} else if (state == FRAGMENT) {
@@ -240,7 +244,7 @@ URL::URL(std::string const& url) {
}

std::string URL::toString() const {
//std::cout << "scheme[" << scheme << "] host[" << host << "] path [" << path << "]" << std::endl;
//std::cout << "scheme[" << scheme << "] host[" << host << "] path [" << path << "] query[" << query << "]" << std::endl;
if (isRelative()) {
return path;
}
@@ -248,6 +252,9 @@ std::string URL::toString() const {
if (scheme == "file") {
return scheme + "://" + path;
} else {
if (query != "") {
return scheme + "://" + host + path + "?" + query;
}
return scheme + "://" + host + path;
}
}
@@ -262,6 +269,8 @@ URL URL::merge(URL const& url) const {
}
URL returnURL = copy();
//std::cout << "URL::merge - source " << url.toString() << std::endl;
//std::cout << "URL::merge - copy " << returnURL.toString() << std::endl;

if (url.path[0] == '/' && url.path[1] == '/') {
auto slashPos = url.path.find('/', 2);
@@ -273,6 +282,10 @@ URL URL::merge(URL const& url) const {
}
} else if (url.path[0] == '/') {
returnURL.path = url.path;
// FIXME: make smarter about merging query strings
if (url.query !="") {
returnURL.query += "&"+url.query;
}
} else {
if (returnURL.path.back() != '/') {
auto finalSlashPos = returnURL.path.find_last_of('/');

+ 168
- 67
src/WebResource.cpp View File

@@ -18,8 +18,8 @@ namespace {
{"html", ResourceType::HTML},
{"txt", ResourceType::TXT},
{"text", ResourceType::TXT},
{"css", ResourceType::CSS},
{"js", ResourceType::JS}
{"css", ResourceType::CSS}, //TXT?
{"js", ResourceType::JS} //TXT?
};

bool isOnlineResource(URL const& url) {
@@ -89,81 +89,80 @@ WebResource getOnlineWebResource(URL const& url) {

std::string redirectLocation = "";

if (uri->scheme == "http")
{
HTTPRequest request(uri);
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;
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");
}
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 {
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;
// 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);
}
returnRes.raw = std::move(response.body);
}
});
}
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;
}, 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");
}
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 {
location = response.properties.at("Location");
// TODO: Set resourceType based on Content-Type field.
returnRes.resourceType = ResourceType::HTML;
returnRes.raw = std::move(response.body);
}
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) {
@@ -176,3 +175,105 @@ WebResource getOnlineWebResource(URL const& url) {

return returnRes;
}

WebResource postWebResource(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;
if (formData) {
std::string postBody = "";
// iterator over formData
for (auto &entry : *formData) {
postBody += entry.first + "=" + entry.second + "&";
}
// strip last & off
postBody = postBody.substr(0, postBody.size() - 1);
std::cout << "postBody is " << postBody << std::endl;
// add to string
ptrPostBody = std::make_unique<std::string>(postBody);
}
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");
}
} 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);
}
}, 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));
}
return returnRes;
}

+ 3
- 0
src/WebResource.h View File

@@ -2,6 +2,7 @@
#define WEBRESOURCE_H

#include <string>
#include <map>
#include "URL.h"

enum class ResourceType {
@@ -24,6 +25,8 @@ struct WebResource {
// 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);


+ 1
- 1
src/browser.cpp View File

@@ -23,7 +23,7 @@ void Browser::addWindow() {
newWindow->init(); // load our UI into it
this->windows.push_back(std::move(newWindow));
if (!activeWindow) {
std::cout << "Browser::addWindow - setting active window" << std::endl;
//std::cout << "Browser::addWindow - setting active window" << std::endl;
activeWindow = this->windows.back().get();
}
}

+ 181
- 0
src/graphics/components/ButtonComponent.cpp View File

@@ -0,0 +1,181 @@
#include "ButtonComponent.h"

extern TextRasterizerCache *rasterizerCache;

ButtonComponent::ButtonComponent(const float rawX, const float rawY, const float rawWidth, const float rawHeight, const int passedWindowWidth, const int passedWindowHeight) : BoxComponent(rawX, rawY, rawWidth, rawHeight, 0xA0A0A0FF, passedWindowWidth, passedWindowHeight) {
// BoxComponent should set up everything we need
//std::cout << "ButtonComponent::ButtonComponent - boxShader " << this->useBoxShader << std::endl;
// looks like it is
}

void ButtonComponent::render() {
//std::cout << "ButtonComponent::render" << std::endl;
//std::cout << "ButtonComponent::render at " << (int)x << "," << (int)y << std::endl;
GLenum glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "ButtonComponent::render - start not ok: " << glErr << std::endl;
}
if (verticesDirty) {
//std::cout << "ButtonComponent::render - update dirty vertex" << std::endl;
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "ButtonComponent::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 << "ButtonComponent::render - glBufferData not ok: " << glErr << std::endl;
}
verticesDirty = false;
}
glBindVertexArray(vertexArrayObject);
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "ButtonComponent::render - glBindVertexArray not ok: " << glErr << std::endl;
}
glBindTexture(GL_TEXTURE_2D, texture);
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "ButtonComponent::render - glBindTexture not ok: " << glErr << std::endl;
}
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "ButtonComponent::render - glDrawElements not ok: " << glErr << std::endl;
}
glBindVertexArray(0);
// can actuall delete vertices here
if (textLabel) {
// make sure we're using win's transformMatrix
//glUseProgram(this->win->fontProgram);
Shader *fontShader = this->win->shaderLoader.getShader(VertexShader("FontShader.vert"),
FragmentShader("FontShader.frag"));
fontShader->bind();
if (!boundToPage) {
GLint transformLocation = fontShader->uniform("transform");
//GLint transformLocation = glGetUniformLocation(this->win->fontProgram, "transform");
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "ButtonComponent::render - glGetUniformLocation not ok: " << glErr << std::endl;
}
glUniformMatrix4fv(transformLocation, 1, GL_FALSE, this->win->transformMatrix);
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "ButtonComponent::render - glUniformMatrix4fv not ok: " << glErr << std::endl;
}
}
//std::cout << "rendering some text" << std::endl;
textLabel->render();
//glUseProgram(this->win->textureProgram);
Shader *textureShader = this->win->shaderLoader.getShader(VertexShader("TextureShader.vert"),
FragmentShader("TextureShader.frag"));
textureShader->bind();
}
}

void ButtonComponent::resize(const int passedWindowWidth, const int passedWindowHeight) {
//std::cout << "ButtonComponent::resize" << std::endl;
//std::cout << "ButtonComponent::resize - rasterizing at " << (int)x << "x" << (int)y << " size: " << (int)width << "x" << (int)height << std::endl;
// maybe already done at component::resize
// set up state
windowWidth = passedWindowWidth;
windowHeight = passedWindowHeight;
//std::cout << "ButtonComponent::resize - placing box at " << (int)x << "," << (int)y << " size: " << (int)width << "x" << (int)height << std::endl;
float vx = x;
float vy = y;
//std::cout << "ButtonComponent::resize - boundToPage " << this->boundToPage << std::endl;
if (this->boundToPage) {
vy = this->windowHeight + y - height;
//std::cout << "InputComponent::resize - Adjust y to " << vy << " from " << y << " h: " << (int)height << std::endl;
}

pointToViewport(vx, vy);
float vWidth = width;
float vHeight = height;
distanceToViewport(vWidth, vHeight);
float vx1 = vx + vWidth;
float vy1 = vy + vHeight;
vertices[(0 * 5) + 0] = vx;
vertices[(0 * 5) + 1] = vy1;
vertices[(1 * 5) + 0] = vx1;
vertices[(1 * 5) + 1] = vy1;
vertices[(2 * 5) + 0] = vx1;
vertices[(2 * 5) + 1] = vy;
vertices[(3 * 5) + 0] = vx;
vertices[(3 * 5) + 1] = vy;
glBindVertexArray(vertexArrayObject);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindVertexArray(0); // protect what we created against any further modification
//updateText();
if (textLabel) {
textLabel->resize(passedWindowWidth, passedWindowHeight);
}
}

// most of this functionality should be inside TextComponent
void ButtonComponent::updateText() {
//std::cout << "ButtonComponent::updateText - input value is now: " << value << std::endl;
// maybe tie the fontsize to height
if (textLabel) {
delete textLabel;
}
textLabel=new TextComponent(value, 0, 0, 12, false, 0x000000FF, windowWidth, windowHeight);
if (!textLabel->win && this->win) {
textLabel->win = this->win;
}
//std::cout << "placing userInputText at " << static_cast<int>(x) << "," << static_cast<int>(y) << std::endl;
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<std::pair<int, int>> 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 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;
}
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, width + x); // need to make sure there's a texture
this->win->renderDirty = true;
}

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

@@ -0,0 +1,21 @@
#ifndef BUTTONCOMPONENT_H
#define BUTTONCOMPONENT_H

#include <GL/glew.h>
#include "BoxComponent.h"
#include "TextComponent.h"
#include "../opengl/Window.h"

// we may not need this if textComponent took a background color
// I guess we'll be inline for now
class ButtonComponent : public BoxComponent {
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 updateText();
std::string value="";
TextComponent *textLabel = nullptr;
};

#endif

+ 5
- 0
src/graphics/components/Component.cpp View File

@@ -4,6 +4,7 @@
#include <cmath>
#include "TextComponent.h"
#include "InputComponent.h"
#include "ButtonComponent.h"

#include "ComponentBuilder.h"

@@ -421,7 +422,11 @@ void Component::printComponentTree(const std::shared_ptr<Component> &component,
for (int i = 0; i < depth; i++) {
std::cout << '\t';
}
ButtonComponent *butComponent = dynamic_cast<ButtonComponent*>(component.get());
InputComponent *inputComponent = dynamic_cast<InputComponent*>(component.get());
if (butComponent) {
std::cout << std::fixed << "X: " << static_cast<int>(butComponent->x) << " Y: " << static_cast<int>(butComponent->y) << " WIDTH: " << static_cast<int>(butComponent->width) << " HEIGHT: " << static_cast<int>(butComponent->height) << " INLINE: " << butComponent->isInline << " Bound: " << butComponent->boundToPage << " BUTTON: " << butComponent->value << std::endl;
} else
if (inputComponent) {
std::cout << std::fixed << "X: " << static_cast<int>(inputComponent->x) << " Y: " << static_cast<int>(inputComponent->y) << " WIDTH: " << static_cast<int>(inputComponent->width) << " HEIGHT: " << static_cast<int>(inputComponent->height) << " INLINE: " << inputComponent->isInline << " Bound: " << inputComponent->boundToPage << " INPUT: " << inputComponent->value << std::endl;
} else {

+ 2
- 1
src/graphics/components/Component.h View File

@@ -167,7 +167,8 @@ public:
std::function<void(int x, int y)> onMousemove = nullptr;
std::function<void(int x, int y)> onWheel = nullptr;
// https://www.quirksmode.org/dom/events/keys.html
std::function<void(int key, int scancode, int action, int mods)> onKeyup = nullptr;
// FIXME: there's only one action on a keyUp and that's keyRelease
std::function<void(int key, int scancode, int action, int mods)> onKeyUp = nullptr;
// "Fires when an actual character is being inserted in, for instance, a text input. It repeats while the user keeps the key depressed."
std::function<void(int key, int scancode, int action, int mods)> onKeyPress = nullptr;
std::function<void()> onClick = nullptr;

+ 29
- 41
src/graphics/components/ComponentBuilder.cpp View File

@@ -10,6 +10,10 @@
#include "../elements/DIVElement.h"
#include "../elements/STRONGElement.h"
#include "../elements/INPUTElement.h"
#include "../elements/BUTTONElement.h"

#include "InputComponent.h"
#include "ButtonComponent.h"

// fwd decl
std::shared_ptr<Component> searchComponentTree(const std::shared_ptr<Component> &component, const int passedX, const int passedY);
@@ -28,11 +32,12 @@ 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>()},
{"button", std::make_shared<BUTTONElement>()},
{"b", std::make_shared<STRONGElement>()},
{"i", std::make_shared<SPANElement>()}
};

std::shared_ptr<Component> ComponentBuilder::build(const std::shared_ptr<Node> node, const std::shared_ptr<Component> &parentComponent, const std::shared_ptr<Window> win) {
std::shared_ptr<Component> ComponentBuilder::build(const std::shared_ptr<Node> node, const std::shared_ptr<Component> &parentComponent, const std::shared_ptr<Window> win, DocumentComponent *docComponent) {
std::shared_ptr<Component> component;
std::string tag;
if (node == nullptr) {
@@ -69,6 +74,7 @@ std::shared_ptr<Component> ComponentBuilder::build(const std::shared_ptr<Node> n
ElementRenderRequest request;
request.node = node;
request.parentComponent = parentComponent;
request.docComponent = docComponent;
component = element->renderer(request); // doesn't always make a component
} else {
//std::cout << "Unknown tag: " << tag << std::endl;
@@ -81,6 +87,7 @@ std::shared_ptr<Component> ComponentBuilder::build(const std::shared_ptr<Node> n
TextComponent *textComponent = dynamic_cast<TextComponent*>(component.get());
if (textComponent) {
//std::cout << "compositing [" << textComponent->text << "]" << std::endl;
// FIXME: put in textcomponent
if (!textComponent->onMousedown) {
textComponent->onMousedown = [textComponent, win](int x, int y) {
if (win->selectionList.size()) {
@@ -137,6 +144,7 @@ std::shared_ptr<Component> ComponentBuilder::build(const std::shared_ptr<Node> n
}
}
InputComponent *inputComponent = dynamic_cast<InputComponent*>(component.get());
ButtonComponent *buttonComponent = dynamic_cast<ButtonComponent*>(component.get());
//if (inputComponent) {
// any input set up we need to do?
// boundToPage defaults to true for components, InputComponent doesn't have it overridded
@@ -165,16 +173,19 @@ std::shared_ptr<Component> ComponentBuilder::build(const std::shared_ptr<Node> n
// figure out our position, size, texture
component->layout();
/*
InputComponent *inputComponent = dynamic_cast<InputComponent*>(component.get());
if (inputComponent) {
std::cout << "ComponentBuilder::build - Just laid out input component" << std::endl;
}
*/

// not sure why only this component needs this but it fixes it
if (inputComponent) {
//std::cout << "ComponentBuilder::build - Just laid out input component" << std::endl;
//std::cout << "ComponentBuilder::build - inputComponent at " << (int)inputComponent->x << "," << (int)inputComponent->y << std::endl;
inputComponent->updateParentSize();
}
if (buttonComponent) {
//std::cout << "ComponentBuilder::build - buttonComponent at " << (int)buttonComponent->x << "," << (int)buttonComponent->y << std::endl;
// since win is set
buttonComponent->updateText();
buttonComponent->updateParentSize();
}
//std::cout << "post layout placed: " << (int)component->x << "x" << (int)component->y << " w/h: " << (int)component->width << "x" << (int)component->height << std::endl;
return component;
}
@@ -185,19 +196,7 @@ std::shared_ptr<Component> ComponentBuilder::build(const std::shared_ptr<Node> n
#include "AnimeComponent.h"

std::string typeOfComponent(const std::shared_ptr<Component> &component) {
TabbedComponent *tabComponent = dynamic_cast<TabbedComponent*>(component.get());
if (tabComponent) return "tab";
DocumentComponent *docComponent = dynamic_cast<DocumentComponent*>(component.get());
if (docComponent) return "doc";
TextComponent *textComponent = dynamic_cast<TextComponent*>(component.get());
if (textComponent) return "text";
InputComponent *inputComponent = dynamic_cast<InputComponent*>(component.get());
if (inputComponent) return "input";
AnimeComponent *animeComponent = dynamic_cast<AnimeComponent*>(component.get());
if (animeComponent) return "anime";
BoxComponent *boxComponent = dynamic_cast<BoxComponent*>(component.get());
if (boxComponent) return "box";
return "";
return typeOfComponent(component.get());
}

std::string typeOfComponent(Component *component) {
@@ -209,6 +208,8 @@ std::string typeOfComponent(Component *component) {
if (textComponent) return "text";
InputComponent *inputComponent = dynamic_cast<InputComponent*>(component);
if (inputComponent) return "input";
ButtonComponent *buttonComponent = dynamic_cast<ButtonComponent*>(component);
if (buttonComponent) return "button";
AnimeComponent *animeComponent = dynamic_cast<AnimeComponent*>(component);
if (animeComponent) return "anime";
BoxComponent *boxComponent = dynamic_cast<BoxComponent*>(component);
@@ -216,31 +217,18 @@ std::string typeOfComponent(Component *component) {
return "";
}

// used for picking
// used for picking for text selection
std::shared_ptr<Component> searchComponentTree(const std::shared_ptr<Component> &component, const int passedX, const int passedY) {
if (component->children.empty()) {
//std::cout << "MultiComponent::searchComponentTree - component at " << static_cast<int>(component->x) << "," << static_cast<int>(component->y) << " size " << static_cast<int>(component->width) << "," << static_cast<int>(component->height) << std::endl;
/*
if (tabbed) {
//std::cout << "MultiComponent::searchComponentTree:Tabbed - y search: " << static_cast<int>(component->windowHeight - component->y - component->height) << "<" << static_cast<int>(passedY) << "<" << static_cast<int>(component->windowHeight - component->y) << std::endl;
if (component->windowHeight - component->y - component->height < passedY && component->windowHeight - component->y > passedY) {
//std::cout << "DocumentComponent::searchComponentTree:Tabbed - x search: " << static_cast<int>(component->x) << "<" << static_cast<int>(passedX) << "<" << static_cast<int>(component->x + component->width) << std::endl;
if (component->x < passedX && component->x + component->width > passedX) {
//std::cout << "MultiComponent::searchComponentTree:Tabbed - hit " << typeOfComponent(component) << std::endl;
return component;
}
}
} else {
*/
//std::cout << "MultiComponent::searchComponentTree - y search: " << static_cast<int>(-component->y) << "<" << static_cast<int>(passedY) << "<" << static_cast<int>(-component->y + component->height) << std::endl;
if (-component->y < passedY && -component->y + component->height > passedY) {
//std::cout << "DocumentComponent::searchComponentTree - x search: " << static_cast<int>(component->x) << "<" << static_cast<int>(passedX) << "<" << static_cast<int>(component->x + component->width) << std::endl;
if (component->x < passedX && component->x + component->width > passedX) {
//std::cout << "MultiComponent::searchComponentTree - hit " << typeOfComponent(component) << std::endl;
return component;
}
//std::cout << "MultiComponent::searchComponentTree - y search: " << static_cast<int>(-component->y) << "<" << static_cast<int>(passedY) << "<" << static_cast<int>(-component->y + component->height) << std::endl;
if (-component->y < passedY && -component->y + component->height > passedY) {
//std::cout << "DocumentComponent::searchComponentTree - x search: " << static_cast<int>(component->x) << "<" << static_cast<int>(passedX) << "<" << static_cast<int>(component->x + component->width) << std::endl;
if (component->x < passedX && component->x + component->width > passedX) {
//std::cout << "MultiComponent::searchComponentTree - hit " << typeOfComponent(component) << std::endl;
return component;
}
//}
}
}
else {
for (std::shared_ptr<Component> child : component->children) {

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

@@ -5,11 +5,13 @@
#include <memory>
#include <unordered_map>
#include "Component.h"
//#include "DocumentComponent.h"
//#include "../elements/Element.h"
#include "../../html/TagNode.h"
#include "../../html/TextNode.h"
#include "../../html/Node.h"

class DocumentComponent;
class Element;

typedef std::unordered_map<std::string, std::function<std::unique_ptr<Component>(const Node &node, int y, int windowWidth, int windowHeight)>> ElementRendererMap;
@@ -19,7 +21,7 @@ private:
const static std::unordered_map<std::string, std::shared_ptr<Element>> elementMap;
public:
// FIXME: consider passing the documentComponent we're bound too
std::shared_ptr<Component> build(const std::shared_ptr<Node> node, const std::shared_ptr<Component> &parentComponent, const std::shared_ptr<Window> win);
std::shared_ptr<Component> build(const std::shared_ptr<Node> node, const std::shared_ptr<Component> &parentComponent, const std::shared_ptr<Window> win, DocumentComponent *docComponent);
};

// getComponentType

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

@@ -3,6 +3,7 @@
#include <iostream>
#include "../../Log.h"
#include "InputComponent.h"
#include "ButtonComponent.h"
#include "TabbedComponent.h"
#include <ctime>

@@ -50,7 +51,7 @@ DocumentComponent::DocumentComponent(const float rawX, const float rawY, const f
// this could communicate the cursor to use
this->hoverComponent->onMousemove(passedX, passedY);
}
if (this->hoverComponent->onClick) {
if (this->hoverComponent->onClick || typeOfComponent(this->hoverComponent) == "button") {
glfwSetCursor(this->win->window, this->win->cursorHand);
} else {
glfwSetCursor(this->win->window, this->win->cursorIbeam);
@@ -60,15 +61,17 @@ DocumentComponent::DocumentComponent(const float rawX, const float rawY, const f
}
};
onWheel=[this](int passedX, int passedY) {
//std::cout << "DocumentComponent::DocumentComponent:::onWheel - scroll yDelta: " << y << std::endl;
//std::cout << "DocumentComponent::DocumentComponent:::onWheel - scroll yDelta: " << passedY << std::endl;
//Component::printComponentTree(rootComponent, 0);
double pY = passedY / 10.0;
//double pY = passedY * 0.1;
/*
this->scrollY -= pY;
if (this->scrollY < 0) {
this->scrollY = 0;
} else if (this->scrollY > this->scrollHeight) {
this->scrollY = this->scrollHeight;
}
*/
//std::cout << "scroll pos: " << scrollY << "/" << scrollHeight << std::endl;
//std::cout << "y: " << static_cast<int>(this->y) << " - " << this->scrollY << std::endl;
//std::cout << "root.y: " << static_cast<int>(rootComponent->y) << std::endl;
@@ -76,8 +79,10 @@ DocumentComponent::DocumentComponent(const float rawX, const float rawY, const f
// new system
//std::cout << "DocumentComponent::DocumentComponent:::onWheel - old position: " << this->transformMatrix[13] << " adjustment:" << (-pY*0.1) << std::endl;
this->transformMatrix[13] += -pY * 0.01;

//this->transformMatrix[13] += -pY * 0.01;
this->scrollY(passedY * 0.1);
/*
// 2.0 is one screen height
// we draw from 0 downwards (y+), so can't scroll past our starting draw point
if (this->transformMatrix[13] < 2) {
@@ -89,6 +94,7 @@ DocumentComponent::DocumentComponent(const float rawX, const float rawY, const f
}
this->transformMatrixDirty = true;
*/
/*
// adjust root position
rootComponent->y = this->y + this->scrollY;
@@ -165,17 +171,18 @@ DocumentComponent::DocumentComponent(const float rawX, const float rawY, const f
}
}
};
onKeyup=[this](int key, int scancode, int action, int mods) {
onKeyUp=[this](int key, int scancode, int action, int mods) {
//std::cout << "DocumentComponent::DocumentComponent:onKeyup" << typeOfComponent(this->focusedComponent) << std::endl;
InputComponent *inputComponent = dynamic_cast<InputComponent*>(this->focusedComponent.get());
if (inputComponent) {
InputComponent *focusedInputComponent = dynamic_cast<InputComponent*>(this->focusedComponent.get());
if (focusedInputComponent) {
//std::cout << "inputComponent is focused, key pressed " << key << " action: " <<action << std::endl;
// FIXME: not going to get repeat events here...
// action 1 is down, 0 is up, 2 is a repeat
if (action == 0 || action == 2) {
// key up
// it's always uppercase...
if (key == 259) {
inputComponent->backSpace();
focusedInputComponent->backSpace();
} else if (key == 257) {
std::cout << "enter!" << std::endl;
} else {
@@ -201,12 +208,65 @@ DocumentComponent::DocumentComponent(const float rawX, const float rawY, const f
key += 'a' - 'A';
}
}
inputComponent->addChar(key);
focusedInputComponent->addChar(key);
} // otherwise I think it's some weird control char
}
}
}
};
onKeyPress=[this](int key, int scancode, int action, int mods) {
InputComponent *focusedInputComponent = dynamic_cast<InputComponent*>(this->focusedComponent.get());
if (focusedInputComponent) {
return;
}
std::cout << "DocumentComponent onKeyPresss - no input component focused" << std::endl;
int yOffsetScroll = 1;
if (key == GLFW_KEY_PAGE_UP && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
std::cout << "PgUp is/was pressed. Scrolling down." << std::endl;
this->scrollY(-yOffsetScroll*0.1);
}
if (key == GLFW_KEY_PAGE_DOWN && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
std::cout << "PgDn is/was pressed. Scrolling up." << std::endl;
this->scrollY(yOffsetScroll*0.1);
}
// FIXME Scrolling with this can scroll past boundary whereas the same doesn't happen with
// scrolling wheel
if (key == GLFW_KEY_J && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
std::cout << "J is/was pressed. Scrolling down." << std::endl;
this->scrollY(-yOffsetScroll*0.1);
}
// FIXME Scrolling with this can scroll past boundary whereas the same doesn't happen with
// scrolling wheel
if (key == GLFW_KEY_K && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
std::cout << "K is/was pressed. Scrolling up." << std::endl;
this->scrollY(yOffsetScroll*0.1);
}
};
}

void DocumentComponent::scrollY(int py) {
this->transformMatrix[13] -= py;
this->textureTransformMatrix[13] -= py;
//std::cout << "transformMatrix : " << this->transformMatrix[13] << std::endl;
//std::cout << "textureTransformMatrix: " << this->textureTransformMatrix[13] << std::endl;
// check bounds
if (this->transformMatrix[13] < 2) {
this->transformMatrix[13] = 2;
}
if (this->textureTransformMatrix[13] < 0) {
this->textureTransformMatrix[13] = 0;
}
// calculate scroll max by calculating how many screens are in the rootComponent's Height
if (this->transformMatrix[13] > std::max( (this->rootComponent->height - this->rootComponent->y) / this->windowHeight * 2.0f, 2.0f)) {
this->transformMatrix[13] = std::max( (this->rootComponent->height - this->rootComponent->y) / this->windowHeight * 2.0f, 2.0f);
}
if (this->textureTransformMatrix[13] > std::max( (this->rootComponent->height - this->rootComponent->y) / this->windowHeight * 2.0f, 2.0f) - 2.0) {
this->textureTransformMatrix[13] = std::max( (this->rootComponent->height - this->rootComponent->y) / this->windowHeight * 2.0f, 2.0f) - 2.0;
}
this->transformMatrixDirty = true;
}

void deleteComponent(std::shared_ptr<Component> &component) {
@@ -250,6 +310,7 @@ void DocumentComponent::setDOM(const std::shared_ptr<Node> rootNode) {
rootComponent->y = y;
domRootNode = rootNode;
//printNode(domRootNode, 0);
domDirty = true;
}

@@ -265,7 +326,7 @@ void DocumentComponent::render() {
//Component::printComponentTree(rootComponent, 0);
domDirty = false;
//std::cout << "root Height: " << static_cast<int>(rootComponent->height) << " window Height: " << windowHeight << " y " << static_cast<int>(this->y) << std::endl;
scrollHeight = std::max(0, static_cast<int>(rootComponent->height - (windowHeight + (this->y * 2))));
//scrollHeight = std::max(0, static_cast<int>(rootComponent->height - (windowHeight + (this->y * 2))));

// recalculate scroll max by calculating how many screens are in the rootComponent's Height
if (transformMatrix[13]>std::max((rootComponent->height)/(windowHeight)*2.0f, 2.0f)) {
@@ -274,8 +335,7 @@ void DocumentComponent::render() {
}
}
// we have to do this each time
// because window resets it
// we have to do this each time because window resets it
//if (transformMatrixDirty) {
//const std::clock_t begin = clock();
@@ -285,31 +345,53 @@ void DocumentComponent::render() {
glUniformMatrix4fv(transformLocation, 1, GL_FALSE, transformMatrix);
//const std::clock_t end = clock();
//std::cout << "Updated font matrix in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
Shader *textureShader = this->win->shaderLoader.getShader(VertexShader("TextureShader.vert"),
FragmentShader("TextureShader.frag"));
GLint transformLocation2 = textureShader->uniform("transform");
glUniformMatrix4fv(transformLocation2, 1, GL_FALSE, transformMatrix);
Shader *textureShader = this->win->shaderLoader.getShader(VertexShader("TextureShader.vert"),
FragmentShader("TextureShader.frag"));

transformMatrixDirty = false;
//}
//std::cout << "DocumentComponent::render - renderDirty" << std::endl;
textureShader->bind();
GLint transformLocation2 = textureShader->uniform("transform");
glUniformMatrix4fv(transformLocation2, 1, GL_FALSE, textureTransformMatrix);
//std::cout << "DocumentComponent::render - start Box components" << std::endl;
renderBoxComponents(rootComponent);
//std::cout << "DocumentComponent::render - end Box components" << std::endl;
textureShader->release();

fontShader->bind();
//std::cout << "DocumentComponent::render - start components" << std::endl;
renderComponents(rootComponent);
//std::cout << "DocumentComponent::render - end components" << std::endl;
fontShader->release();
}

// create this component and all it's children
void DocumentComponent::createComponentTree(const std::shared_ptr<Node> node, const std::shared_ptr<Component> &parentComponent) {
std::shared_ptr<Component> component = componentBuilder.build(node, parentComponent, this->win);
std::shared_ptr<Component> component = componentBuilder.build(node, parentComponent, this->win, this);
//std::cout << "DocumentComponent::createComponentTree" << std::endl;
if (!component) {
//std::cout << "DocumentComponent::createComponentTree - no component" << std::endl;
return;
}
// don't build text node of button component because it'll mess with the picking
if (typeOfComponent(component) == "button") {
// if this button node has children, extract the text before discarding it
if (node->children.size()) {
TextNode *textNode = dynamic_cast<TextNode*>(node->children.front().get());
if (textNode) {
//std::cout << "Button text: " << textNode->text << std::endl;
ButtonComponent *buttonComponent = dynamic_cast<ButtonComponent*>(component.get());
if (buttonComponent) {
buttonComponent->value = textNode->text;
buttonComponent->updateText();
}
}
}
return;
}
if (node==domRootNode) {
// if this is the root node
component->reqWidth = windowWidth;
@@ -325,18 +407,18 @@ void DocumentComponent::createComponentTree(const std::shared_ptr<Node> node, co

// used for picking
std::shared_ptr<Component> DocumentComponent::searchComponentTree(const std::shared_ptr<Component> &component, const int passedX, const int passedY) {
// only get hits on leaves (and not to hit the rootComponent every time)
if (component->children.empty()) {
//std::cout << "DocumentComponent::searchComponentTree - component at " << static_cast<int>(component->x) << "," << static_cast<int>(component->y) << " size " << static_cast<int>(component->width) << "," << static_cast<int>(component->height) << std::endl;
//std::cout << "DocumentComponent::searchComponentTree - y search: " << static_cast<int>(-component->y) << "<" << static_cast<int>(passedY) << "<" << static_cast<int>(-component->y + component->height) << std::endl;
if (-component->y < passedY && -component->y + component->height > passedY) {
//std::cout << "DocumentComponent::searchComponentTree - x search: " << static_cast<int>(component->x) << "<" << static_cast<int>(passedX) << "<" << static_cast<int>(component->x + component->width) << std::endl;
if (component->x < passedX && component->x + component->width > passedX) {
//std::cout << "DocumentComponent::searchComponentTree - hit " << typeOfComponent(component) << std::endl;
//std::cout << "DocumentComponent::searchComponentTree - hit " << typeOfComponent(component) << " name: " << component->name << std::endl;
return component;
}
}
}
else {
} else {
for (std::shared_ptr<Component> child : component->children) {
std::shared_ptr<Component> found = searchComponentTree(child, passedX, passedY);
if (found) {
@@ -380,6 +462,7 @@ void DocumentComponent::navTo(const std::string url) {
// 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

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

@@ -14,6 +14,7 @@ class DocumentComponent : public MultiComponent {
public:
DocumentComponent(const float rawX, const float rawY, const float rawWidth, const float rawHeight, const int passedWindowWidth, const int passedWindowHeight);
void render();
void scrollY(int y);
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);
@@ -23,9 +24,15 @@ public:
URL currentURL;
bool domDirty = false;
//
int scrollY = 0;
int scrollHeight = 0;
//int scrollY = 0;
//int scrollHeight = 0;
float textureTransformMatrix[16] = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
};
float transformMatrix[16] = {
1, 0, 0, 0,
0, 1, 0, 0,

+ 18
- 5
src/graphics/components/InputComponent.cpp View File

@@ -63,9 +63,9 @@ InputComponent::InputComponent(const float rawX, const float rawY, const float r
initialWindowWidth = windowWidth;
initialWindowHeight = windowHeight;
for (int i = 0; i < 3; i++) {
data[0][0][i] = 0xf0; // set RGB color
}
data[0][0][0] = 0xf0; // set R color
data[0][0][1] = 0xf0; // set G color
data[0][0][2] = 0xf0; // set B color
data[0][0][3] = 0xff; // set alpha
float vx = rawX;
@@ -139,6 +139,7 @@ void InputComponent::render() {
}
verticesDirty = false;
}
glBindVertexArray(vertexArrayObject);
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
@@ -190,7 +191,9 @@ void InputComponent::render() {
cursorTimer = scheduler->setInterval([this]() {
this->showCursor = !this->showCursor;
//std::cout << "showCursor " << this->showCursor << std::endl;
this->win->renderDirty = true;
if (this->win) {
this->win->renderDirty = true;
}
}, 500);
// render it if we need to
@@ -237,7 +240,7 @@ void InputComponent::resize(const int passedWindowWidth, const int passedWindowH
float vx = x;
float vy = y;
if (boundToPage) {
if (this->boundToPage) {
vy = this->windowHeight + y - height;
//std::cout << "InputComponent::resize - Adjust y to " << vy << " from " << y << " h: " << (int)height << std::endl;
}
@@ -301,6 +304,16 @@ void InputComponent::updateCursor() {

void InputComponent::updateText() {
//std::cout << "InputComponent::updateText - input value is now: " << value << std::endl;
if (this->node) {
std::map<std::string, std::string>::iterator it = this->node->properties.find("value");
if (it == this->node->properties.end()) {
this->node->properties.insert(std::make_pair("value", this->value));
} else {
it->second = this->value;
}
}
// maybe tie the fontsize to height
if (userInputText) {
delete userInputText;

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

@@ -32,6 +32,7 @@ public:
std::shared_ptr<timer_handle> cursorTimer = nullptr;
// store for cursor
int estWidth;
TagNode *node = nullptr;
};

#endif

+ 16
- 6
src/graphics/components/MultiComponent.cpp View File

@@ -112,13 +112,13 @@ MultiComponent::MultiComponent(const float rawX, const float rawY, const float r
//renderDirty = true;
// should we mark win->renderDirty = true?
};
onKeyup=[this](int key, int scancode, int action, int mods) {
onKeyUp=[this](int key, int scancode, int action, int mods) {
//std::cout << "MultiComponent::MultiComponent:onKeyup - focused on " << typeOfComponent(this->focusedComponent) << std::endl;
DocumentComponent *docComponent = dynamic_cast<DocumentComponent*>(this->focusedComponent.get());
if (docComponent) {
if (action == 0) {
if (docComponent->onKeyup) {
docComponent->onKeyup(key, scancode, action, mods);
if (docComponent->onKeyUp) {
docComponent->onKeyUp(key, scancode, action, mods);
}
}
return;
@@ -226,23 +226,33 @@ void MultiComponent::render() {
Shader *textureShader = this->win->shaderLoader.getShader(VertexShader("TextureShader.vert"),
FragmentShader("TextureShader.frag"));
textureShader->bind();
GLint transformLocation = textureShader->uniform("transform");
glUniformMatrix4fv(transformLocation, 1, GL_FALSE, this->win->textureTransformMatrix);
GLenum glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "MultiComponent::render - glUniformMatrix4fv - not ok: " << glErr << std::endl;
}
//std::cout << "MultiComponent::render - start Box components" << std::endl;
renderBoxComponents(rootComponent);
//std::cout << "MultiComponent::render - end Box components" << std::endl;
textureShader->release();
// if we flip, we can't put tab labels on top of the tab
fontShader->bind();
if (!boundToPage) {
GLint transformLocation = fontShader->uniform("transform");
GLenum glErr=glGetError();
GLint transformLocation2 = fontShader->uniform("transform");
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "MultiComponent::render - glGetUniformLocation not ok: " << glErr << std::endl;
}
glUniformMatrix4fv(transformLocation, 1, GL_FALSE, win->transformMatrix);
glUniformMatrix4fv(transformLocation2, 1, GL_FALSE, win->transformMatrix);
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "MultiComponent::render - glUniformMatrix4fv not ok: " << glErr << std::endl;
}
}
//std::cout << "MultiComponent::render - start text components" << std::endl;
renderComponents(rootComponent);
//std::cout << "MultiComponent::render - end text components" << std::endl;
}

// draw this component and all it's children

+ 95
- 0
src/graphics/elements/BUTTONElement.cpp View File

@@ -0,0 +1,95 @@
#include "BUTTONElement.h"
#include "INPUTElement.h"
#include "../components/InputComponent.h"
#include "../components/ButtonComponent.h"
#include "../components/DocumentComponent.h"
#include "../../WebResource.h"
#include "../../Log.h"

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

std::unique_ptr<Component> BUTTONElement::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::cout << "Creating submit" << std::endl;
std::unique_ptr<ButtonComponent> butComponent = std::make_unique<ButtonComponent>(0, 0, 62.0f, 13.0f, request.parentComponent->win->windowWidth, request.parentComponent->win->windowHeight);
butComponent->value = "Button";
butComponent->name = "button";
butComponent->onClick=[tagNode, request]() {
// recurse up to find oug form tag
std::shared_ptr<Node> formNode = findFormNode(request.node);
if (!formNode) {
std::cout << "BUTTONElement::renderer:butComponent->onClick - Can't find form parent for submit" << std::endl;
return;
}
if (!request.docComponent) {
std::cout << "BUTTONElement::renderer:butComponent->onClick - Can't find documentComponent for submit" << std::endl;
return;
}
TagNode *formTagNode = dynamic_cast<TagNode*>(formNode.get());
auto formNodeActionIter = formTagNode->properties.find("action");
if (formNodeActionIter == formTagNode->properties.end()) {
std::cout << "Form has no action" << std::endl;
return;
}
auto formData = buildFormData(formNode, nullptr);
/*
// add our name/value (because we skip all submit buttons, there can only be one)
auto submitButtonNameValue = getTagNodeNameValue(*tagNode);
if (submitButtonNameValue) {
formData->insert(*submitButtonNameValue);
}
*/

auto formNodeMethodIter = formTagNode->properties.find("method");
std::string method="GET";
if (formNodeMethodIter != formTagNode->properties.end()) {
method=formNodeMethodIter->second;
}
std::cout << "Form method is " << method << std::endl;
if (method=="POST" || method=="post") {
// need documentComponent
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);
} else {
// need documentComponent
std::string queryString = "";
if (formData) {
// iterator over formData
for (auto &entry : *formData) {
queryString += entry.first + "=" + entry.second + "&";
}
// strip last & off
queryString = queryString.substr(0, queryString.size() - 1);
std::cout << "queryString is " << queryString << std::endl;
// The moral of the story is, if you have binary (non-alphanumeric) data (or a significantly sized payload) to transmit, use multipart/form-data
auto search = queryString.find(" ");
if (search != std::string::npos) {
queryString.replace(search, 1, "+");
}
}
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);
}

};
return std::move(butComponent);
}
return nullptr;
}

+ 14
- 0
src/graphics/elements/BUTTONElement.h View File

@@ -0,0 +1,14 @@
#ifndef BUTTONELEMENT_H
#define BUTTONELEMENT_H

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

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

#endif

+ 1
- 0
src/graphics/elements/Element.h View File

@@ -21,6 +21,7 @@ public:
std::shared_ptr<Component> parentComponent;
// can get this from parentComponent
//std::shared_ptr<Window> win;
DocumentComponent *docComponent = nullptr;
};

// we define tokens in a language

+ 237
- 2
src/graphics/elements/INPUTElement.cpp View File

@@ -1,15 +1,250 @@
#include "INPUTElement.h"
#include "../components/InputComponent.h"
#include "../components/ButtonComponent.h"
#include "../components/DocumentComponent.h"
#include "../../Log.h"
#include "../../html/HTMLParser.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?
//std::cout << "INPUTElement::renderer - creating InputComponent at " << x << "x" << y << std::endl;
std::unique_ptr<Component> component = std::make_unique<InputComponent>(0, 0, 125.0f, 13.0f, request.parentComponent->win->windowWidth, request.parentComponent->win->windowHeight);
return component;
TagNode *tagNode = dynamic_cast<TagNode*>(request.node.get());
if (tagNode) {
std::string type = "text";
auto propIter = tagNode->properties.find("type");
if (propIter != tagNode->properties.end()) {
//std::cout << "input is of type: " << propIter->second << std::endl;
type = propIter->second;
}
//std::cout << "type is " << type << std::endl;
if (type == "text") {
std::unique_ptr<InputComponent> inputComponent = std::make_unique<InputComponent>(0, 0, 125.0f, 13.0f, request.parentComponent->win->windowWidth, request.parentComponent->win->windowHeight);
inputComponent->node = tagNode;
inputComponent->name = "textInput";
auto propIter2 = tagNode->properties.find("value");
if (propIter2 != tagNode->properties.end()) {
inputComponent->value = propIter2->second;
}
return std::move(inputComponent);
} else if (type == "submit") {
//std::cout << "Creating submit" << std::endl;
std::unique_ptr<ButtonComponent> butComponent = std::make_unique<ButtonComponent>(0, 0, 62.0f, 13.0f, request.parentComponent->win->windowWidth, request.parentComponent->win->windowHeight);
auto propIter3 = tagNode->properties.find("value");
if (propIter3 != tagNode->properties.end()) {
butComponent->value = propIter3->second;
} else {
butComponent->value = "submit";
}
butComponent->onClick=[tagNode, request]() {
// recurse up to find oug form tag
std::shared_ptr<Node> formNode = findFormNode(request.node);
if (!formNode) {
std::cout << "INPUTElement::renderer:butComponent->onClick - Can't find form parent for submit" << std::endl;
return;
}
if (!request.docComponent) {
std::cout << "INPUTElement::renderer:butComponent->onClick - Can't find documentComponent for submit" << std::endl;
return;
}
TagNode *formTagNode = dynamic_cast<TagNode*>(formNode.get());
auto formNodeActionIter = formTagNode->properties.find("action");
if (formNodeActionIter == formTagNode->properties.end()) {
std::cout << "Form has no action" << std::endl;
return;
}
auto formNodeMethodIter = formTagNode->properties.find("method");
std::string method="GET";
if (formNodeMethodIter != formTagNode->properties.end()) {
method=formNodeMethodIter->second;
}
std::cout << "Form method is " << method << std::endl;
auto formData = buildFormData(formNode, nullptr);
// add our name/value (because we skip all submit buttons, there can only be one)
auto submitButtonNameValue = getTagNodeNameValue(*tagNode);
if (submitButtonNameValue) {
formData->insert(*submitButtonNameValue);
}
if (method=="POST" || method=="post") {
// need documentComponent
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);
} else {
// need documentComponent

std::string queryString = "";
if (formData) {
// iterator over formData
for (auto &entry : *formData) {
queryString += entry.first + "=" + entry.second + "&";
}
// strip last & off
queryString = queryString.substr(0, queryString.size() - 1);
std::cout << "queryString is " << queryString << std::endl;

// The moral of the story is, if you have binary (non-alphanumeric) data (or a significantly sized payload) to transmit, use multipart/form-data
auto search = queryString.find(" ");
if (search != std::string::npos) {
queryString.replace(search, 1, "+");
}
}
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);
}

};
return std::move(butComponent);
} else if (type == "hidden") {
// we don't need a UI component but we do belong in the form
} else {
// submit, button, checkbox, file
//std::cout << "INPUTElement::renderer - ignoring input is of type: " << type << std::endl;
}
}

/*
TextNode *textNode = dynamic_cast<TextNode*>(node.get());

+ 8
- 1
src/graphics/elements/INPUTElement.h View File

@@ -3,8 +3,15 @@

#include "Element.h"
#include "../components/Component.h"
#include "../components/InputComponent.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:

+ 71
- 44
src/graphics/opengl/Window.cpp View File

@@ -199,7 +199,7 @@ bool Window::init() {
std::shared_ptr<InputComponent> navAddressBar = std::make_unique<InputComponent>(192.0f, windowHeight - 48.0f, windowWidth - 384.0f, 24.0f, windowWidth, windowHeight);
// add it to our components
navAddressBar->x = 192.0f;
navAddressBar->y = windowHeight-48.0f;
navAddressBar->y = windowHeight - 48.0f;
navAddressBar->uiControl.x = { 0, 192 }; // 192px
navAddressBar->uiControl.y = { 100, -48 }; // top - 48px
navAddressBar->uiControl.w = { 100, -384 }; // w - 384px
@@ -367,7 +367,7 @@ bool Window::initGLFW() {
//std::cout << "scroll - hovering over a component" << std::endl;
if (thiz->hoverComponent->onWheel) {
//std::cout << "scroll - hovering over a scrollabel component" << std::endl;
thiz->hoverComponent->onWheel(xOffset * 100, yOffset * 100);
thiz->hoverComponent->onWheel(xOffset * 10, yOffset * 10);
}
}

@@ -470,8 +470,8 @@ bool Window::initGLFW() {
p_tabComponent->onKeyPress(key, scancode, action, mods);
}
if (action == 0) {
if (p_tabComponent->onKeyup) {
p_tabComponent->onKeyup(key, scancode, action, mods);
if (p_tabComponent->onKeyUp) {
p_tabComponent->onKeyUp(key, scancode, action, mods);
}
}
}
@@ -481,8 +481,8 @@ bool Window::initGLFW() {
DocumentComponent *docComponent = dynamic_cast<DocumentComponent*>(thiz->focusedComponent.get());
if (docComponent) {
if (action == 0) {
if (docComponent->onKeyup) {
docComponent->onKeyup(key, scancode, action, mods);
if (docComponent->onKeyUp) {
docComponent->onKeyUp(key, scancode, action, mods);
}
}
return;
@@ -580,58 +580,33 @@ bool Window::initGLFW() {
Component::printComponentTree(thiz->rootComponent, 0);
printf("\n\n");
}
// basically ignored over tabbed or doc component
/*
int yOffsetScroll = 1;
if (key == GLFW_KEY_PAGE_UP && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
if (p_TabComponent) {
DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
if (p_DocComponent) {
p_DocComponent->transformMatrix[13] += -yOffsetScroll * 0.1;
p_DocComponent->transformMatrixDirty = true;
thiz->renderDirty = true;
}
}
std::cout << "PgUp is/was pressed. Scrolling down." << std::endl;
thiz->scrollDocument(-yOffsetScroll*0.1);

}
if (key == GLFW_KEY_PAGE_DOWN && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
if (p_TabComponent) {
DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
if (p_DocComponent) {
p_DocComponent->transformMatrix[13] += yOffsetScroll * 0.1;
p_DocComponent->transformMatrixDirty = true;
thiz->renderDirty = true;
}
}
std::cout << "PgDn is/was pressed. Scrolling up." << std::endl;
thiz->scrollDocument(yOffsetScroll*0.1);
}

// FIXME Scrolling with this can scroll past boundary whereas the same doesn't happen with
// scrolling wheel
if (key == GLFW_KEY_J && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
std::cout << "J is/was pressed. Scrolling down." << std::endl;
TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
if (p_TabComponent) {
DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
if (p_DocComponent) {
p_DocComponent->transformMatrix[13] += yOffsetScroll * 0.1;
p_DocComponent->transformMatrixDirty = true;
thiz->renderDirty = true;
}
}
thiz->scrollDocument(-yOffsetScroll*0.1);

}
// FIXME Scrolling with this can scroll past boundary whereas the same doesn't happen with
// scrolling wheel
if (key == GLFW_KEY_K && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
std::cout << "K is/was pressed. Scrolling up." << std::endl;
TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
if (p_TabComponent) {
DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
if (p_DocComponent) {
p_DocComponent->transformMatrix[13] += -yOffsetScroll * 0.1;
p_DocComponent->transformMatrixDirty = true;
thiz->renderDirty = true;
}
}
thiz->scrollDocument(yOffsetScroll*0.1);
}
*/
});

glfwMakeContextCurrent(window);
@@ -679,6 +654,24 @@ void Window::onResize(int passedWidth, int passedHeight) {
this->delayResize = 1;
}

void Window::scrollDocument(int y) {
TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(this->tabComponent.get());
if (p_TabComponent) {
DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
if (p_DocComponent) {
p_DocComponent->transformMatrix[13] += y;
p_DocComponent->textureTransformMatrix[13] += y;
p_DocComponent->transformMatrixDirty = true;
this->renderDirty = true;
this->transformMatrixDirty = true;
} else {
std::cout << "Window::scrollDocument - no docComponent" << std::endl;
}
} else {
std::cout << "Window::scrollDocument - no tabComponent" << std::endl;
}
}

void Window::resize() {
//std::cout << "Window::resize" << std::endl;
@@ -741,6 +734,23 @@ void Window::render() {
std::cout << "window::render - glUseProgram - not ok: " << glErr << std::endl;
}

if (transformMatrixDirty) {
//const std::clock_t begin = clock();
GLint transformLocation = textureShader->uniform("transform");
glUniformMatrix4fv(transformLocation, 1, GL_FALSE, textureTransformMatrix);
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "window::render - glUniformMatrix4fv - not ok: " << glErr << std::endl;
}
// didn't change much (could be we didnt' select the textureProgram first)
//GLint transformLocation2 = glGetUniformLocation(textureProgram, "transform");
//glUniformMatrix4fv(transformLocation2, 1, GL_FALSE, transformMatrix);
//const std::clock_t end = clock();
//std::cout << "Updated font matrix in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
}
// draw anime 1st, tabbed components 2nd
// then box shit
// text last
@@ -748,7 +758,15 @@ void Window::render() {

renderComponentType("tab", rootComponent);
textureShader->bind();
// reset it our textureTransformationMatrix
GLint transformLocation = textureShader->uniform("transform");
glUniformMatrix4fv(transformLocation, 1, GL_FALSE, textureTransformMatrix);
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "window::render - glUniformMatrix4fv - not ok: " << glErr << std::endl;
}

//GLint transformLocation = glGetUniformLocation(textureProgram, "transform");
//glUniformMatrix4fv(transformLocation, 1, GL_FALSE, transformMatrix);
renderComponentType("box", rootComponent);
@@ -763,12 +781,12 @@ void Window::render() {
// maybe we don't need too
if (transformMatrixDirty) {
//const std::clock_t begin = clock();
GLint transformLocation = fontShader->uniform("transform");
GLint transformLocation2 = fontShader->uniform("transform");
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "window::render - glGetUniformLocation - not ok: " << glErr << std::endl;
}
glUniformMatrix4fv(transformLocation, 1, GL_FALSE, transformMatrix);
glUniformMatrix4fv(transformLocation2, 1, GL_FALSE, transformMatrix);
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "window::render - glUniformMatrix4fv - not ok: " << glErr << std::endl;
@@ -792,6 +810,13 @@ void Window::render() {
renderComponentType("anime", rootComponent);
renderComponentType("tab", rootComponent);
textureShader->bind();
// reset it our textureTransformationMatrix
glUniformMatrix4fv(transformLocation, 1, GL_FALSE, textureTransformMatrix);
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "window::render - glUniformMatrix4fv - not ok: " << glErr << std::endl;
}

renderComponentType("box", rootComponent);
renderComponentType("input", rootComponent);
// it's quick but done on scroll
@@ -829,6 +854,7 @@ void Window::setDOM(const std::shared_ptr<Node> rootNode) {
}
*/

/*
void Window::createComponentTree(const std::shared_ptr<Node> node, const std::shared_ptr<Component> &parentComponent) {
std::shared_ptr<Component> component = componentBuilder.build(node, parentComponent, shared_from_this());

@@ -857,6 +883,7 @@ void Window::createComponentTree(const std::shared_ptr<Node> node, const std::sh
// update parents, creation brings them up to date
//updateComponentSize(component);
}
*/

void Window::renderComponentType(std::string str, std::shared_ptr<Component> component) {
if (!component) {

+ 8
- 1
src/graphics/opengl/Window.h View File

@@ -26,12 +26,13 @@ public:

void render();
void setDOM(const std::shared_ptr<Node> rootNode);
void createComponentTree(const std::shared_ptr<Node> rootNode, const std::shared_ptr<Component> &parentComponent);
//void createComponentTree(const std::shared_ptr<Node> rootNode, const std::shared_ptr<Component> &parentComponent);
void renderComponentType(std::string str, std::shared_ptr<Component> component);

void onResize(int passedWidth, int passedHeight);
void checkForResize();
void resize();
void scrollDocument(int y);
void resizeComponentTree(const std::shared_ptr<Component> &component, const int windowWidth, const int windowHeight);
std::shared_ptr<Component> searchComponentTree(const std::shared_ptr<Component> &component, const int x, const int y);
@@ -40,6 +41,12 @@ public:

// properties
int maxTextureSize = 0;
float textureTransformMatrix[16] = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
};
float transformMatrix[16] = {
1, 0, 0, 0,
0, 1, 0, 0,

+ 13
- 7
src/html/HTMLParser.cpp View File

@@ -98,11 +98,13 @@ std::shared_ptr<Node> HTMLParser::parse(const std::string &html) const {
else if (html[cursor] == '<') {
// HTML comments
if (html[cursor + 1] == '!' && html[cursor + 2] == '-' && html[cursor + 3] == '-' ) {
//std::cout << "HTMLParser::Parse - starting HTML comment at " << cursor << std::endl;
state = 4;
}
// close tag
else if (html[cursor + 1] == '/') {
// start closing tag
//std::cout << "HTMLParser::Parse - starting closing tag at " << html.substr(cursor, 7) << std::endl;
if (currentNode && currentNode->parent) {
// we should snap to the level we started at (as we maybe a couple levels deep <ul><li></ul>
// but it's the matching part of this tag
@@ -111,7 +113,6 @@ std::shared_ptr<Node> HTMLParser::parse(const std::string &html) const {
std::cout << "HTMLParser::Parse - currentNode/parent is null - close tag" << std::endl;
}
state = 1; // ignore closing tags
//starts.push_back(cursor);
}
// these have never have a closing tag
else if (
@@ -123,6 +124,7 @@ std::shared_ptr<Node> HTMLParser::parse(const std::string &html) const {
(html[cursor + 1] == 'm' && html[cursor + 2] == 'e' && html[cursor + 3] == 't' && html[cursor + 4] == 'a') ||
(html[cursor + 1] == 'i' && html[cursor + 2] == 'n' && html[cursor + 3] == 'p' && html[cursor + 4] == 'u' && html[cursor + 5] == 't')
) {
//std::cout << "HTMLParser::Parse - Starting single tag " << html.substr(cursor, 6) << std::endl;
std::shared_ptr<TagNode> tagNode = std::make_shared<TagNode>();
if (currentNode) {
currentNode->children.push_back(tagNode);
@@ -138,7 +140,7 @@ std::shared_ptr<Node> HTMLParser::parse(const std::string &html) const {
cursor ++;
} else {
std::string element = html.substr(cursor, closeTagPos + 2);
//std::cout << "creating element, tag: " << element << std::endl;
//std::cout << "HTMLParser::Parse - creating element, tag: " << element << std::endl;
parseTag(element, *dynamic_cast<TagNode*>(currentNode.get()));
cursor += 2 + closeTagPos;
}
@@ -155,6 +157,7 @@ std::shared_ptr<Node> HTMLParser::parse(const std::string &html) const {
}
// start tag (<bob> <bob part)
else {
//std::cout << "HTMLParser::Parse - start oc tag " << html.substr(cursor, 6) << std::endl;
std::shared_ptr<TagNode> tagNode = std::make_shared<TagNode>();
if (currentNode) {
currentNode->children.push_back(tagNode);
@@ -168,6 +171,7 @@ std::shared_ptr<Node> HTMLParser::parse(const std::string &html) const {
}
}
else { // start text node
//std::cout << "HTMLParser::Parse - start text node " << html.substr(cursor, 6) << std::endl;
std::shared_ptr<TextNode> textNode = std::make_shared<TextNode>();
// not sure why currentNode is null but it is
if (currentNode) {
@@ -182,17 +186,16 @@ std::shared_ptr<Node> HTMLParser::parse(const std::string &html) const {
}
cursor--;
}
else if (state == 1) { // Skip Over Element
else if (state == 1) { // Skip Over Element (used by closing tag)
if (html[cursor] == '>') {
//std::string element = html.substr(starts.back(), cursor - starts.back() + 1);
//starts.pop_back();
//std::cout << "HTMLParser::parse - close tag: " << element << std::endl;
//std::cout << "HTMLParser::parse - close tag: " << html.substr(starts.back(), cursor - starts.back() + 1) << std::endl;
state = 0;
prependWhiteSpace = false;
}
}
else if (state == 4) { // HTML Comment
if (html[cursor] == '-' && html[cursor + 1] == '-' && html[cursor + 2] == '>') {
//std::cout << "HTMLParser::Parse - Found end HTML comment at " << html.substr(cursor, 6) << std::endl;
state = 0;
cursor += 2; // advance cursor to end of comment
prependWhiteSpace = false;
@@ -201,7 +204,7 @@ std::shared_ptr<Node> HTMLParser::parse(const std::string &html) const {
else if (state == 2) { // Search for end tag node
if (html[cursor] == '>') { // end tag node
std::string element = html.substr(starts.back(), cursor - starts.back() + 1);
//std::cout << "HTMLParser::parse - close tag: " << element << std::endl;
//std::cout << "HTMLParser::parse - end open tag: " << element << std::endl;
if (element == "<li>") {
// this will close previous li before starting a new one
autoCloseTag(currentNode, rootNode, "ul", "li");
@@ -221,6 +224,7 @@ std::shared_ptr<Node> HTMLParser::parse(const std::string &html) const {
else if (state == 3) { // End text node
if (html[cursor + 1] == '<') {
dynamic_cast<TextNode*>(currentNode.get())->text = (prependWhiteSpace?" ":"") + html.substr(starts.back(), cursor - starts.back() + 1);
//std::cout << "HTMLParser::parse - end text node: " << html.substr(starts.back(), cursor - starts.back() + 1) << std::endl;
starts.pop_back();
if (currentNode && currentNode->parent) {
currentNode = currentNode->parent;
@@ -273,6 +277,8 @@ void HTMLParser::parseTag(const std::string &element, TagNode &tagNode) const {
}
else if (state == 3) {
if (element[cursor] == '"') {
// was suggested to use tagNode.properties[propertyKey] = element.substr(start, cursor - start);
// for better readabiilty
tagNode.properties.insert(std::pair<std::string, std::string>(propertyKey, element.substr(start, cursor - start)));
start = cursor + 1;
state = 1;

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

@@ -3,6 +3,7 @@
TagNode::TagNode() : Node(NodeType::TAG) {
}