code:
#include "SCRIPTElement.h"
#include "../components/DocumentComponent.h"
#include "../../tools/WebResource.h"
#include "../../tools/Log.h"
#include "../../parsers/scripting/javascript/JSParser.h"
#include <ctime>

std::unique_ptr<Component> SCRIPTElement::renderer(const ElementRenderRequest &request) {
    TagNode *tagNode = dynamic_cast<TagNode*>(request.node.get());
    if (tagNode) {
        //std::cout << "SCRIPTElement::renderer - start" << std::endl;
        bool gotSource = false;
        std::map<std::string, std::string>::const_iterator srcPair = tagNode->properties.find("src");
        std::string source = "";
        if (srcPair != tagNode->properties.end()) {
            //std::cout << "SCRIPTElement::renderer - src: " << srcPair->second << std::endl;
            URL uRequest = URL(srcPair->second);
            //std::cout << "SCRIPTElement::renderer - srcURL: " << uRequest << std::endl;
            //std::cout << "SCRIPTElement::renderer - bseURL: " << request.docComponent->currentURL << std::endl;
            // ok download this URL
            URL uScriptSrc = request.docComponent->currentURL.merge(uRequest);
            //std::cout << "SCRIPTElement::renderer - Canonical: " << uScriptSrc << std::endl;

            // download URL
            WebResource res = getWebResource(uScriptSrc);
            if (res.resourceType == ResourceType::INVALID) {
                logError() << "SCRIPTElement::renderer - Invalid resource type: " << res.raw << std::endl;
            } else {
                //std::cout << "body: " << res.raw << std::endl;
                //std::cout << "type: " << res.resourceType << std::endl;
                source = res.raw;
                gotSource = true;
            }
        }
        if (request.node->children.size()) {
            TextNode *textNode = dynamic_cast<TextNode*>(request.node->children.front().get());
            if (textNode) {
                //std::cout << "SCRIPTElement::renderer - source: " << textNode->text << std::endl;
                source = textNode->text;
                gotSource = true;
            }
        }
        if (!gotSource) {
            std::cout << "SCRIPTElement::renderer - no source!" << std::endl;
            return nullptr;
        }
        // parse JS
        //std::cout << "SCRIPTElement::renderer - source: " << source << std::endl;
        std::shared_ptr<JavaScript> newScript = std::make_shared<JavaScript>();
        // import current state (scope)
        newScript->applyScope(request.docComponent->mainScript);
        // parse it
        const std::clock_t begin = clock();
        newScript->parse(source);
        //newScript->tokens = parser.getTokens(source);
        //parser.parseTokens(newScript->tokens, &newScript->rootScope);
        newScript->execute();
        const std::clock_t end = clock();
        logDebug() << "SCRIPTElement::renderer - Parsed javascript in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
        // and flush our state back into mainScript
        //request.docComponent->mainScript = parser.append(request.docComponent->mainScript, newScript);
        request.docComponent->mainScript->applyScope(newScript);
        request.docComponent->mainScript->append(newScript);
    }
    return nullptr;
}


code:
#ifndef SCRIPTELEMENT_H
#define SCRIPTELEMENT_H

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

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

#endif

repo_name: despair/netrunner

license: mit


code:
#include "SPANElement.h"

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

std::unique_ptr<Component> SPANElement::renderer(const ElementRenderRequest &request) {
    TextNode *textNode = dynamic_cast<TextNode*>(request.node.get());
    if (textNode) {
        // only create a dummy component if we have one child, why?
        if (request.node->parent->children.size() == 1) {
            std::unique_ptr<Component> component = std::make_unique<TextComponent>(textNode->text, 0, 0, 12, false, 0x000000FF, request.parentComponent->win->windowWidth, request.parentComponent->win->windowHeight);
            return component;
        }
    }
    return nullptr;
}


code:
#ifndef SPANELEMENT_H
#define SPANELEMENT_H

#include "Element.h"
#include "../components/Component.h"
#include "../components/TextComponent.h"
#include "../../parsers/markup/TextNode.h"

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

#endif


code:
#include "STRONGElement.h"

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

std::unique_ptr<Component> STRONGElement::renderer(const ElementRenderRequest &request) {
    TextNode *textNode = dynamic_cast<TextNode*>(request.node.get());
    if (textNode) {
        //if (node->parent->children.size() == 1) {
            std::unique_ptr<Component> component = std::make_unique<TextComponent>(textNode->text, 0, 0, 12, true, 0x000000FF, request.parentComponent->win->windowWidth, request.parentComponent->win->windowHeight);
            return component;
        //}
    }
    return nullptr;
}


code:
#ifndef STRONGELEMENT_H
#define STRONGELEMENT_H

#include "Element.h"
#include "../components/Component.h"
#include "../components/TextComponent.h"
#include "../../parsers/markup/TextNode.h"

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

#endif


code:
#include "TEXTAREAElement.h"
#include "../components/InputComponent.h"
#include "../components/ButtonComponent.h"
#include "../components/DocumentComponent.h"
#include "../../parsers/markup/html/HTMLParser.h"
#include "../../tools/Log.h"
#include "../../tools/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;
}


code:
#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


code:
#include "TextRasterizer.h"
#include <string>
#include <cmath>
#include <limits>
#include <iostream>

#include "../../../environment/Environment.h"

TextRasterizer::TextRasterizer(const std::string &fontPath, const int size, const unsigned int resolution, const bool bold) {
    fontSize = size;
    FT_Init_FreeType(&lib);

    face = std::make_unique<FT_Face>();
    std::string path = Environment::getResourceDir() + "/" + fontPath;
    //std::cout << "font path [" << path << "]" << std::endl;
    if (bold) {
      path = path.substr(0, path.length() -4) + "-Bold.ttf";
    }
    //std::cout << "font path [" << path << "]" << std::endl;
    if (FT_New_Face(lib, path.c_str(), 0, face.get())) {
        std::cout << "Could not open font, bold: " << bold << std::endl;
        return;
    }
    if (!isUnicodeBMP(*face)) {
        std::cout << "Font is not Unicode BMP" << std::endl;
        return;
    }
    if (FT_Set_Char_Size(*face, 0, fontSize * 64, resolution, resolution)) {
        std::cout << "Could not set font size" << std::endl;
        return;
    }

    font = hb_ft_font_create(*face, nullptr);

    buffer = hb_buffer_create();
    if (!hb_buffer_allocation_successful(buffer)) {
        std::cout << "Could not create HarfBuzz buffer" << std::endl;
        return;
    }
}

TextRasterizer::~TextRasterizer() {
    hb_buffer_destroy(buffer);
    hb_font_destroy(font);
    FT_Done_FreeType(lib);
}

std::unique_ptr<sizeResponse> TextRasterizer::size(const rasterizationRequest &request) const {
    if (!request.availableWidth) {
        // window is likely minimized, so no output
        return nullptr;
    }
    if (request.startX == request.availableWidth) {
        std::cout << "TextRasterizer::size - x [" << static_cast<int>(request.startX) << "] matches window width [" << static_cast<int>(request.availableWidth)<< "] for text[" << request.text << "] no room to render anything" << std::endl;
        return nullptr;

    }
    if (request.startX > request.availableWidth) {
        std::cout << "TextRasterizer::size - x [" << static_cast<int>(request.startX) << "] requested outside of window width [" << static_cast<int>(request.availableWidth)<< "] for text[" << request.text << "]" << std::endl;
        return nullptr;
    }
    if (!buffer) {
        std::cout << "TextRasterizer::size - no font loaded" << std::endl;
        return nullptr;
    }

    unsigned int glyphCount;

    hb_buffer_reset(buffer);
    hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
    hb_buffer_set_language(buffer, hb_language_from_string("en", 2));

    hb_buffer_add_utf8(buffer, request.text.c_str(), request.text.length(), 0, request.text.length());

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
    const hb_feature_t KerningOn = { HB_TAG('k', 'e', 'r', 'n'), 1, 0, std::numeric_limits<unsigned int>::max() };
#pragma GCC diagnostic pop
    hb_shape(font, buffer, &KerningOn, 1);

    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<sizeResponse> response = std::make_unique<sizeResponse>();

    // figure out width/height
    int cx = 0;
    int cy = 0;
    int xmax = 0;
    //int y0max = 0;
    int y1max = 0;
    //int lines = 1;
    response->wrapped = false;
    int lineXStart = request.startX;
    //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;
            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

        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
                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::size - newline found no wrap is on [" << request.text << "]" << 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;
            }
        }

        // wrap to next line on width
        if (cx + lineXStart >= request.availableWidth) {
            response->wrapped = true;
            if (request.noWrap) {
                glyphCount = i;
            } 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 - response->wrapToX; // the whole width of parent to the edge of windowWidth
                //std::cout << "new width: " << xmax << std::endl;
                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
                response->lines++;
                lineXStart = response->wrapToX; // change where we start
            }
        }

        //std::cout << "glyph:" << xa << "x" << ya << std::endl;

        // update glyph space allocation
        cx += xa;
        // note all over cy is in -
        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);
        response->y0max = std::max(response->y0max, y0);
        y1max=std::max(y1max, y1);

        // track new max width
        xmax=std::max(xmax, cx);

        // crop overflow
        if (request.cropHeight && cy > request.cropHeight) {
            break; // stop adding characters
        }
    }
    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;
    // confirmed we aren't the same
    /*
    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 < textureHeight) {
        response->height = textureHeight;
        //std::cout << "adjusted:" << (int)width << "x" << (int)height << std::endl;
    }

    // inline position where we leave off
    response->endingX = cx - request.sourceStartX;
    response->endingY = cy + 2 + request.sourceStartY;

    return response;
}

#include <GL/glew.h>

std::unique_ptr<rasterizationResponse> TextRasterizer::rasterize(const rasterizationRequest &request) const {

    if (request.startX == request.availableWidth) {
        std::cout << "TextRasterizer::rasterize - x [" << static_cast<int>(request.startX) << "] matches window width [" << static_cast<int>(request.availableWidth)<< "] for text[" << request.text << "] no room to render anything" << std::endl;
        return nullptr;

    }
    if (request.startX > request.availableWidth) {
        std::cout << "TextRasterizer::rasterize - x [" << static_cast<int>(request.startX) << "] requested outside of window width [" << static_cast<int>(request.availableWidth)<< "] for text[" << request.text << "]" << std::endl;
        return nullptr;
    }
    if (!buffer) {
        std::cout << "TextRasterizer::rasterize - no font loaded" << std::endl;
        return nullptr;
    }
    unsigned int glyphCount;

    hb_buffer_reset(buffer);
    hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
    hb_buffer_set_language(buffer, hb_language_from_string("en", 2));

    hb_buffer_add_utf8(buffer, request.text.c_str(), request.text.length(), 0, request.text.length());

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
    const hb_feature_t KerningOn = { HB_TAG('k', 'e', 'r', 'n'), 1, 0, std::numeric_limits<unsigned int>::max() };
#pragma GCC diagnostic pop
    hb_shape(font, buffer, &KerningOn, 1);

    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> sizeResponse = this->size(request);

    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;
    response->height -= request.sourceStartY;
    //std::cout << "TextRasterizer::rasterize - final:" << static_cast<int>(response->width) << "x" << static_cast<int>(response->height) << std::endl;

    /*
     if (xmax==windowWidth - x) {
     std::cout << "Wrapped text[" << text << "] over " << lines << " lines " << xmax << "x" << static_cast<int>(height) << std::endl;
     }
     */
    //std::cout << "text size: " << (int)width << "x" << (int)height << std::endl;

    response->textureWidth = pow(2, ceil(log(response->width) / log(2)));
    response->textureHeight = pow(2, ceil(log(response->height) / log(2)));
    //std::cout << "text texture size:" << line->textureWidth << "x" << line->textureHeight << std::endl;
    size_t size = static_cast<size_t>(response->textureWidth * response->textureHeight); // here?
    response->textureData = std::make_unique<unsigned char[]>(size); // here?
    if (!response->textureData) {
        std::cout << "failed to create texture" << static_cast<int>(response->width) << "X" << static_cast<int>(response->height) << std::endl;
        return nullptr;
    }

    // translation information
    response->x0 = -sizeResponse->leftPadding; // wrap to element start (wrapToX (0) or x)
    response->y0 = 0;
    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;

    //std::cout << "Requesting texture of " << response->textureWidth << "x" << response->textureHeight << "max: " << win->maxTextureSize << std::endl;
    if (response->textureHeight > request.maxTextureSize) {
        std::cout << "Truncating text texture height to fit in your video card: " << response->textureHeight << ">" << request.maxTextureSize << std::endl;
        response->textureHeight = request.maxTextureSize;
    }

    // texture coords
    response->s0 = 0.0f;
    response->t0 = 0.0f;
    response->s1 = response->width / response->textureWidth;
    response->t1 = response->height / response->textureHeight;
    //std::cout << "s1: " << response->s1 << " t1: " << response->t1 << std::endl;

    // copy all glyphs into one single glyph
    // still neds to start at X
    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;
    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;
        }

        const FT_Bitmap ftBitmap = slot->bitmap;

        // figure out glyph starting point
        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);

        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 = 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;
        }
        //std::cout << "placing glyph[" << text[i] << "] at " <<  cx << " cy is now: " << cy << std::endl;

        // crop overflow
        if (request.cropHeight && cy > request.cropHeight) {
            break; // stop adding characters
        }

        if (cx < request.sourceStartX) {
            // skip ahead
            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 + 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;
            }
            unsigned char *src = ftBitmap.buffer + iy * static_cast<unsigned int>(ftBitmap.width);
            unsigned char *dest = response->textureData.get() + destPos;
            for(unsigned int ix = 0; ix < ftBitmap.width; ix++) { // pixel by pixel
                if (destPos + ix >= size) {
                    // we're done with this line
                    continue;
                }
                //std::cout << "putting:" << (int)src[ix] << " over " << (int)dest[ix] << std::endl;
                // probably could be or'd
                if (src[ix] && src[ix] > dest[ix]) {
                    dest[ix] = src[ix];
                }
            }
        }

        cx += xa;
    }
    //std::cout << "final size: " << (int)width << "x" << (int)height << std::endl;
    //std::cout << "at: " << (int)line->x0 << "x" << (int)line->y0 << " to: " << (int)line->x1 << "x" << (int)line->y1 <<std::endl;
    response->endingX = cx - request.sourceStartX; // maybe should be one xa less?
    //std::ceil(0.5 * 1.2f * fontSize)+2
    // should this be - request.sourceStartY?
    response->endingY = cy + maxy0 + request.sourceStartY; // definitely should be one lineheight higher
    //std::cout << "ending at " << response->endingX << "," << response->endingY << std::endl;

    return response;
}

bool TextRasterizer::isUnicodeBMP(const FT_Face &ftFace) const {
    for (int i = 0; i < ftFace->num_charmaps; i++) {
        if (((ftFace->charmaps[i]->platform_id == 0) && (ftFace->charmaps[i]->encoding_id == 3)) || ((ftFace->charmaps[i]->platform_id == 3) && (ftFace->charmaps[i]->encoding_id == 1))) {
            FT_Set_Charmap(ftFace, ftFace->charmaps[i]);
            return true;
        }
    }
    return false;
}


code:
#ifndef TEXTRASTERIZER_H
#define TEXTRASTERIZER_H

#include "hb.h"
#include "hb-ft.h"
#include <memory>
#include <string>
#include <algorithm>
#include "../renderers/glfw/Window.h"

struct Glyph {
    float x0;
    float y0;
    float x1;
    float y1;
    float s0;
    float t0;
    float s1;
    float t1;
    unsigned int textureWidth;
    unsigned int textureHeight;
    std::unique_ptr<unsigned char[]> textureData;
};

// we don't need x,y here except we do need to know how much width we have available
// we need to know x and y it left off at
// x in case we line wrap, so we know where we left off for inline
// y in case of multiline wrap, where the last line ended for inline
// and we'll need to know the starting x
// we don't need the starting y tbh
// even if windowWidth isn't a windowWidth, we still need some type of point to wrap on
struct rasterizationRequest {
    std::string text;
    int availableWidth; // (relative to 0)
    // not availableHeight because we're either going to wrap or not
    // is X relative to parent or absolute to window? well since no wrapTo, it's relative to parent
    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;
    // overflow (0 means no limit)
    int cropWidth = 0;
    int cropHeight = 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;
    int glyphCount;
    int endingX;
    int endingY;
    bool wrapped;
    float x0;
    float y0;
    float x1;
    float y1; // calculated on other members here
    float s0;
    float t0;
    float s1; // calculated on other members here
    float t1; // calculated on other members here
    unsigned int textureWidth;
    unsigned int textureHeight;
    std::unique_ptr<unsigned char[]> textureData;
};

// container for a font at size bold/regular
class TextRasterizer {
private:
    int fontSize;
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<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;
    hb_font_t *font;
    hb_buffer_t *buffer;
    std::unique_ptr<FT_Face> face;
};

#endif


code:
#include "TextRasterizerCache.h"
#include <iostream>

TextRasterizerCache *rasterizerCache=new TextRasterizerCache;

// reduces this:
// Updated DOM in: 6.787870 seconds
// to this:
// Updated DOM in: 6.104453 seconds
std::shared_ptr<TextRasterizer> TextRasterizerCache::loadFont(const unsigned int size, const bool bold) {
    if (bold) {
        if (fontSizes_bold.find(size) == fontSizes_bold.end()) {
            //std::cout << "Making bold font" << std::endl;
            fontSizes_bold[size]=std::make_shared<TextRasterizer>("DejaVuSerif.ttf", size, 72, bold);
        }
        return fontSizes_bold[size];
    }
    else {
        if (fontSizes_notbold.find(size) == fontSizes_notbold.end()) {
            //std::cout << "Making font" << std::endl;
            fontSizes_notbold[size]=std::make_shared<TextRasterizer>("DejaVuSerif.ttf", size, 72, bold);
        }
        return fontSizes_notbold[size];
    }
}


code:
#ifndef TEXTRASTERIZERCACHE_H
#define TEXTRASTERIZERCACHE_H

#include "TextRasterizer.h"
#include <map>

class TextRasterizerCache {
private:
    std::map<unsigned int, std::shared_ptr<TextRasterizer> > fontSizes_bold;
    std::map<unsigned int, std::shared_ptr<TextRasterizer> > fontSizes_notbold;
public:
    std::shared_ptr<TextRasterizer> loadFont(const unsigned int size, const bool bold);
};

#endif


code:
// STUB! This will eventually be replaced with a pdcurses-window
#if 0
#include "../../URL.h"
#include "TextWindow.h"
#include "../../html/TagNode.h"
#include "../../html/TextNode.h"

#include "../../networking/HTTPRequest.h"
#include "../../html/HTMLParser.h"
#include "../../StringUtils.h"
#include "../../URL.h"
#include "../../Log.h"
#include "../components/BoxComponent.h"
#include "../components/AnimeComponent.h"
#include "../components/DocumentComponent.h"
#include "../components/TabbedComponent.h"
#include "../components/TextComponent.h"
#include "../components/TabbedComponent.h"
#include "../components/InputComponent.h"
#include "../opengl/Shader.h"

#include <cmath>
#include <ctime>
#include <iostream>

Window::~Window() {
    glfwTerminate();
}

bool Window::init() {
    if (!initGLFW()) {
        return false;
    }
    if (!initGLEW()) {
        return false;
    }
    initGL();
    GLenum glErr=glGetError();
    if(glErr != GL_NO_ERROR) {
        std::cout << "window::init - post initGL - not ok: " << glErr << std::endl;
    }

    // set proper scroll position
    transformMatrix[13] = 2;
    transformMatrixDirty = true;

    // configure root component
    //rootComponent = std::make_shared<Component>();
    rootComponent->name = "rootComponent";
    //rootComponent->y = 0;

    // set up our UI with a new window

    // (re)build UI (top to bottom)
    //std::cout << "Window::setDOM window size: " << windowWidth << "x" << windowHeight << std::endl;

    // layout back to front
    // 4 multigroups
    // 1 -> mascot
    // 2 -> tabbed
    // 3 -> box
    // 4 -> input

    //Mascot
    std::shared_ptr<AnimeComponent> mascot = std::make_unique<AnimeComponent>(windowWidth * 0.75f, 0.0f, 512.0f, 512.0f, windowWidth, windowHeight);
    mascot->uiControl.x = { 75,   0 }; // 75% width
    mascot->uiControl.y = {  0,   0 }; // 0px
    mascot->uiControl.w = {  0, 512 }; // 512px
    mascot->uiControl.h = {  0, 512 }; // 512px
    mascot->boundToPage = false;
    mascot->isPickable = false;
    mascot->name = "mascot";
    // add it to our components
    mascot->setParent(rootComponent);
    rootComponent->children.push_back(mascot);

    // create tabbed component
    // doc text is still showing up ontop of buttons
    std::shared_ptr<TabbedComponent> tabbedComponent = std::make_shared<TabbedComponent>(0, 0, static_cast<float>(windowWidth), static_cast<float>(windowHeight - 64), windowWidth, windowHeight);
    tabbedComponent->name = "tabbedComponent";
    tabbedComponent->y = -64;
    tabbedComponent->uiControl.x = { 0  ,   0 }; // 0
    tabbedComponent->uiControl.y = { 0  , -64 }; // -64px
    tabbedComponent->uiControl.w = { 100,   0 }; // 100%
    tabbedComponent->uiControl.h = { 100, -64 }; // 100% - 64px
    tabbedComponent->boundToPage = false;
    tabbedComponent->win = this;
    // add it to our components
    tabbedComponent->setParent(rootComponent);
    rootComponent->children.push_back(tabbedComponent);

    // Nav background
    std::shared_ptr<BoxComponent> navBackground = std::make_unique<BoxComponent>(0.0f, windowHeight - 64.0f, windowWidth, 64.0f, 0x888888FF, windowWidth, windowHeight);
    //navBackground->y = -64;
    navBackground->uiControl.y = { 100, -64 }; // -64px
    navBackground->uiControl.w = { 100,   0 }; // 100%
    navBackground->uiControl.h = {   0,  64 }; // 64px
    navBackground->isPickable = false;
    navBackground->name = "navBackground";
    navBackground->boundToPage = false;
    // add it to our components
    navBackground->setParent(rootComponent);
    rootComponent->children.push_back(navBackground);

    std::shared_ptr<BoxComponent> navBackButton = std::make_unique<BoxComponent>(32.0f, windowHeight - 48.0f, 32.0f, 32.0f, 0x000000FF, windowWidth, windowHeight);
    navBackButton->uiControl.x = {   0,  32 }; // 32px
    navBackButton->uiControl.y = { 100, -48 }; // top - 48px
    navBackButton->uiControl.w = {   0,  32 }; // 32px
    navBackButton->uiControl.h = {   0,  32 }; // 32px
    navBackButton->onClick=[this]() {
        //std::cout << "Back" << std::endl;
        TabbedComponent *pTabComponent = dynamic_cast<TabbedComponent*>(this->tabComponent.get());
        if (pTabComponent) {
            if (pTabComponent->selectedTabId) {
                pTabComponent->selectedTab->get()->history->back();
            }
        }
    };
    navBackButton->onMouseover = [navBackButton, this]() {
        //std::cout << "navBackButton->onMouseover" << std::endl;
        navBackButton->changeColor(0x880000FF);
        this->renderDirty = true;
    };
    navBackButton->onMouseout = [navBackButton, this]() {
        //std::cout << "navBackButton->onMouseout" << std::endl;
        navBackButton->changeColor(0x000000FF);
        this->renderDirty = true;
    };

    navBackButton->name = "navBackButton";
    navBackButton->boundToPage = false;
    // add it to our components
    navBackButton->setParent(rootComponent);
    rootComponent->children.push_back(navBackButton);

    std::shared_ptr<BoxComponent> navForwardButton = std::make_unique<BoxComponent>(74.0f, windowHeight - 48.0f, 32.0f, 32.0f, 0x000000FF, windowWidth, windowHeight);
    navForwardButton->uiControl.x = {   0,  74 }; // 74px
    navForwardButton->uiControl.y = { 100, -48 }; // top - 48px
    navForwardButton->uiControl.w = {   0,  32 }; // 32px
    navForwardButton->uiControl.h = {   0,  32 }; // 32px

    navForwardButton->onClick=[this]() {
        //std::cout << "Forward" << std::endl;
        TabbedComponent *pTabComponent = dynamic_cast<TabbedComponent*>(this->tabComponent.get());
        if (pTabComponent) {
            if (pTabComponent->selectedTabId) {
                pTabComponent->selectedTab->get()->history->forward();
            }
        }
    };
    navForwardButton->onMouseover = [navForwardButton, this]() {
        //std::cout << "navForwardButton->onMouseover" << std::endl;
        navForwardButton->changeColor(0x008800FF);
        this->renderDirty = true;
    };
    navForwardButton->onMouseout = [navForwardButton, this]() {
        //std::cout << "navForwardButton->onMouseout" << std::endl;
        navForwardButton->changeColor(0x000000FF);
        this->renderDirty = true;
    };
    navForwardButton->name = "navForwardButton";
    navForwardButton->boundToPage = false;
    // add it to our components
    //navForwardButton->parent = rootComponent;
    navForwardButton->setParent(rootComponent);
    rootComponent->children.push_back(navForwardButton);

    std::shared_ptr<BoxComponent> navRefreshButton = std::make_unique<BoxComponent>(116.0f, windowHeight - 48.0f, 32.0f, 32.0f, 0x000000FF, windowWidth, windowHeight);
    navRefreshButton->uiControl.x = {   0, 116 }; // 116px
    navRefreshButton->uiControl.y = { 100, -48 }; // top - 48px
    navRefreshButton->uiControl.w = {   0,  32 }; // 32px
    navRefreshButton->uiControl.h = {   0,  32 }; // 32px
    navRefreshButton->name = "navRefreshButton";
    navRefreshButton->boundToPage = false;
    navRefreshButton->onClick=[this]() {
        std::cout << "Refreshing " << this->currentURL << std::endl;
        this->navTo(this->currentURL.toString());
    };
    navRefreshButton->onMouseover = [navRefreshButton, this]() {
        //std::cout << "navRefreshButton->onMouseover" << std::endl;
        navRefreshButton->changeColor(0x000088FF);
        this->renderDirty = true;
    };
    navRefreshButton->onMouseout = [navRefreshButton, this]() {
        //std::cout << "navRefreshButton->onMouseout" << std::endl;
        navRefreshButton->changeColor(0x000000FF);
        this->renderDirty = true;
    };    // add it to our components
    navRefreshButton->setParent(rootComponent);
    rootComponent->children.push_back(navRefreshButton);

    // Address Bar
    std::shared_ptr<InputComponent> navAddressBar = std::make_unique<InputComponent>(192.0f, windowHeight - 48.0f, windowWidth - 384.0f, 24.0f, windowWidth, windowHeight);
    // add it to our components
    navAddressBar->x = 192.0f;
    navAddressBar->y = windowHeight-48.0f;
    navAddressBar->uiControl.x = {   0,  192 }; // 192px
    navAddressBar->uiControl.y = { 100,  -48 }; // top - 48px
    navAddressBar->uiControl.w = { 100, -384 }; // w - 384px
    navAddressBar->uiControl.h = {   0,   24 }; // 24px
    navAddressBar->name = "navAddressBar";
    navAddressBar->boundToPage = false;
    navAddressBar->win = this;
    //navAddressBar->y = -48; // this works but breaks picking
    navAddressBar->value = currentURL.toString();
    navAddressBar->updateText();
    navAddressBar->onEnter=[this](std::string value) {
        std::cout << "navAddressBar::onEnter got " << value << std::endl;
        this->navTo(value);
    };
    this->addressComponent = navAddressBar;

    // we do this to prevent flashing
    render();
    renderDirty = true;

    //std::cout << "placing inputComponent at " << static_cast<int>(navAddressBar->x) << "," << static_cast<int>(navAddressBar->y) << std::endl;
    navAddressBar->setParent(rootComponent);
    rootComponent->children.push_back(navAddressBar);

    /*
     // textTest
     // TextComponent(const std::string &rawText, const int rawX, const int rawY, const unsigned int size, const bool bolded, const unsigned int hexColor, const int passedWindowWidth, const int passedWindowHeight);
     std::shared_ptr<TextComponent> textTest = std::make_unique<TextComponent>("Test", -99, -99, 12, false, 0x000000ff, windowWidth, windowHeight);
     textTest->x=192.0f;
     textTest->y=-64.0f;
     textTest->isPickable = false;
     // add it to our components
     textTest->parent = rootComponent;
     rootComponent->children.push_back(textTest);
     // we really don't want to layout because that's moves us relative to parent
     // and this is static layout we want, not a free flow
     //textTest->layout(); // to rasterize
     textTest->resize(windowWidth, windowHeight);
     */

    // set tab component
    this->tabComponent = tabbedComponent;

    return true;
}

bool Window::initGLFW() {
    if (!glfwInit()) {
        std::cout << "Could not initialize GLFW" << std::endl;
        return false;
    }

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);

    glfwSetErrorCallback([](int error, const char* description) {
        std::cout << "glfw error [" << error << "]:" << description << std::endl;
    });

    window = glfwCreateWindow(windowWidth, windowHeight, "NetRunner", nullptr, nullptr);
    if (!window) {
        glfwTerminate();
        std::cout << "Could not create window" << std::endl;
        return false;
    }
    // replace first parameter of all these callbacks with our window object instead of a GLFWwindow
    glfwSetWindowUserPointer(window, this);
    // set window w/h
    //glfwGetFramebufferSize(window, &windowWidth, &windowHeight); // in pixels
    glfwGetWindowSize(window, &windowWidth, &windowHeight); // use screen coordinates (not pixels) more retina friendly

    // set up event callbacks
    glfwSetFramebufferSizeCallback(window, [](GLFWwindow *win, int width, int height) {
        glViewport(0, 0, width, height);
    });
    glfwSetWindowSizeCallback(window, [](GLFWwindow *win, int width, int height) {
        Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
        thiz->onResize(width, height);
    });
    cursorHand = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
    cursorArrow = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
    cursorIbeam = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
    glfwSetCursorPosCallback(window, [](GLFWwindow *win, double xPos, double yPos) {
        Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
        thiz->cursorX = xPos;
        thiz->cursorY = yPos;
        //std::cout << "Window::Window:onMousemove - at " << static_cast<int>(xPos) << "," << static_cast<int>(yPos) << std::endl;
        if (xPos < 0 || yPos < 0) return;
        if (xPos > thiz->windowWidth || yPos > thiz->windowHeight) return;
        // p. much worthless on double
        /*
        static double lx = 0;
        static double ly = 0;
        if (lx == xPos && ly == yPos) {
            return;
        }
        lx = xPos;
        ly = yPos;
        std::cout << "Window::Window:onMousemove - noCache" << std::endl;
        */
        //std::cout << "Window::Window:onMousemove - begin search" << std::endl;
        //std::cout << "Window::Window:onMousemove - windowHeight: " << thiz->windowHeight << " cursorY: " << thiz->cursorY << " scrollY: " << thiz->transformMatrix[13] << std::endl;
        std::shared_ptr<Component> newHover = thiz->searchComponentTree(thiz->rootComponent, thiz->cursorX, (thiz->windowHeight - thiz->cursorY) + ((-thiz->transformMatrix[13] / 2) * thiz->windowHeight));
        if (newHover != thiz->hoverComponent) {
            if (thiz->hoverComponent && thiz->hoverComponent->onMouseout) {
                thiz->hoverComponent->onMouseout();
            }
            if (newHover && newHover->onMouseover) {
                newHover->onMouseover();
            }
            thiz->hoverComponent = newHover;
        }
        if (thiz->hoverComponent) {
            //std::cout << "Window::Window:onMousemove - hover " << typeOfComponent(thiz->hoverComponent) << std::endl;
            if (thiz->hoverComponent->onClick) {
                glfwSetCursor(thiz->window, thiz->cursorHand);
            } else {
                TextComponent *textComponent = dynamic_cast<TextComponent*>(thiz->hoverComponent.get());
                InputComponent *inputComponent = dynamic_cast<InputComponent*>(thiz->hoverComponent.get());
                if (textComponent || inputComponent) {
                    glfwSetCursor(thiz->window, thiz->cursorIbeam);
                }
                // otherwise we could be an Box/Anime or Document Component
            }
            if (thiz->hoverComponent->onMousemove) {
                // this could communicate the cursor to use
                thiz->hoverComponent->onMousemove(thiz->cursorX, thiz->cursorY);
            }
        } else {
            glfwSetCursor(thiz->window, thiz->cursorArrow);
        }
    });
    glfwSetScrollCallback(window, [](GLFWwindow *win, double xOffset, double yOffset) {
        Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
        // yOffset is a delta vector
        thiz->transformMatrix[13] += -yOffset * 0.1;
        if (thiz->hoverComponent) {
            //std::cout << "scroll - hovering over a component" << std::endl;
            if (thiz->hoverComponent->onWheel) {
                //std::cout << "scroll - hovering over a scrollabel component" << std::endl;
                thiz->hoverComponent->onWheel(xOffset * 100, yOffset * 100);
            }
        }

        // 2.0 is one screen height
        // we draw from 0 downwards (y+), so can't scroll past our starting draw point
        if (thiz->transformMatrix[13] < 2) {
            thiz->transformMatrix[13] = 2;
        }
        // calculate scroll max by calculating how many screens are in the rootComponent's Height
        if (thiz->transformMatrix[13] > std::max( thiz->rootComponent->height / thiz->windowHeight * 2.0f, 2.0f)) {
            thiz->transformMatrix[13] = std::max( thiz->rootComponent->height / thiz->windowHeight * 2.0f, 2.0f);
        }
        //std::cout << "scroll y is at " << thiz->transformMatrix[13] << "/" << static_cast<int>((thiz->transformMatrix[13]*10000) << std::endl;
        thiz->transformMatrixDirty = true;
    });
    glfwSetMouseButtonCallback(window, [](GLFWwindow *win, int button, int action, int mods) {
        Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
        if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
            //std::cout << "left press" << std::endl;
            if (thiz->hoverComponent) {
                //std::cout << "focus event" << std::endl;
                if (thiz->focusedComponent != thiz->hoverComponent) {
                    // blur old component
                    if (thiz->focusedComponent) {
                        if (thiz->focusedComponent->onBlur) {
                            thiz->focusedComponent->onBlur();
                        }
                    }
                    // focus new component
                    if (thiz->hoverComponent->onFocus) {
                        thiz->hoverComponent->onFocus();
                    }
                }
                thiz->focusedComponent = thiz->hoverComponent;
                if (thiz->focusedComponent->onMousedown) {
                    //std::cout << "click event" << std::endl;
                    thiz->focusedComponent->onMousedown(thiz->cursorX, thiz->cursorY);
                }
            }
        }
        if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE) {
            //std::cout << "left release" << std::endl;
            if (thiz->hoverComponent) {
                //std::cout << "focus event" << std::endl;
                if (thiz->focusedComponent != thiz->hoverComponent) {
                    // blur old component
                    if (thiz->focusedComponent) {
                        if (thiz->focusedComponent->onBlur) {
                            thiz->focusedComponent->onBlur();
                        }
                    }
                    // focus new component
                    if (thiz->hoverComponent->onFocus) {
                        thiz->hoverComponent->onFocus();
                    }
                }
                thiz->focusedComponent = thiz->hoverComponent;
                /*
                InputComponent *inputComponent = dynamic_cast<InputComponent*>(thiz->focusedComponent.get());
                if (inputComponent) {
                    std::cout << "inputComponent focus" << std::endl;
                }
                 */
                if (thiz->focusedComponent && thiz->focusedComponent->onMouseup) {
                    //std::cout << "click event" << std::endl;
                    thiz->focusedComponent->onMouseup(thiz->cursorX, thiz->cursorY);
                }
                if (thiz->focusedComponent && thiz->focusedComponent->onClick) {
                    //std::cout << "click event" << std::endl;
                    thiz->focusedComponent->onClick();
                }
            }
        }
    });

    // works with utf-32 but we'll lkeep the low level for now
    /*
    glfwSetCharCallback(window, [](GLFWwindow* win, unsigned int codepoint) {
        Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
        std::cout << "Window::glfwSetCharCallback - codepoint: " << codepoint << std::endl;
    });
    */

    glfwSetKeyCallback(window, [](GLFWwindow *win, int key, int scancode, int action, int mods) {
        Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
        // we're focused on something
        //std::cout << "glfwSetKeyCallback" << std::endl;
        if (thiz->focusedComponent) {
            //std::cout << "glfwSetKeyCallback - focused on " << typeOfComponent(thiz->focusedComponent) << std::endl;
            TabbedComponent *p_tabComponent = dynamic_cast<TabbedComponent*>(thiz->focusedComponent.get());
            if (p_tabComponent) {
                // repeat or key up
                if (action == 2 || action == 0) {
                    if (p_tabComponent->onKeyPress) {
                        p_tabComponent->onKeyPress(key, scancode, action, mods);
                    }
                    if (action == 0) {
                        if (p_tabComponent->onKeyup) {
                            p_tabComponent->onKeyup(key, scancode, action, mods);
                        }
                    }
                }
                return;
            }

            DocumentComponent *docComponent = dynamic_cast<DocumentComponent*>(thiz->focusedComponent.get());
            if (docComponent) {
                if (action == 0) {
                    if (docComponent->onKeyup) {
                        docComponent->onKeyup(key, scancode, action, mods);
                    }
                }
                return;
            }
            InputComponent *inputComponent = dynamic_cast<InputComponent*>(thiz->focusedComponent.get());
            if (inputComponent) {
                //std::cout << "inputComponent is focsued, key pressed " << key << " action: " <<action << std::endl;
                // action 1 is down, 0 is up, 2 is a repeat
                if (action == 0 || action == 2) {
                    // key up
                    // it's always uppercase...
                    if (key == 259) {
                        inputComponent->backSpace();
                    } else if (key == 257) {
                        std::cout << "enter!" << std::endl;
                        if (inputComponent->onEnter) {
                            inputComponent->onEnter(inputComponent->value);
                        }
                    } else {
                        if (key < 256) {
                            if (mods & GLFW_MOD_SHIFT) {
                                // SHIFT
                                if (key == GLFW_KEY_SLASH) key='?';
                                if (key == GLFW_KEY_APOSTROPHE) key='"';
                                if (key == GLFW_KEY_COMMA) key='<';
                                if (key == GLFW_KEY_MINUS) key='_';
                                if (key == GLFW_KEY_PERIOD) key='>';
                                if (key == GLFW_KEY_SEMICOLON) key=':';
                                if (key == GLFW_KEY_EQUAL) key='+';
                                if (key == GLFW_KEY_LEFT_BRACKET) key='{';
                                if (key == GLFW_KEY_BACKSLASH) key='|';
                                if (key == GLFW_KEY_RIGHT_BRACKET) key='}';
                                if (key == GLFW_KEY_GRAVE_ACCENT) key='~';

                            } else {
                                // no shift or caplocks
                                // basically: when SHIFT isn't pressed but key is in A-Z range, add ascii offset to make it lower case
                                if (key >= 'A' && key <= 'Z') {
                                    key += 'a' - 'A';
                                }
                            }
                            inputComponent->addChar(key);
                        } // otherwise I think it's some weird control char
                    }
                }
                return;
            }
        }
        switch (key) {
            case GLFW_KEY_1:
            case GLFW_KEY_2:
            case GLFW_KEY_3:
            case GLFW_KEY_4:
            case GLFW_KEY_5:
            case GLFW_KEY_6:
            case GLFW_KEY_7:
            case GLFW_KEY_8:
            case GLFW_KEY_9:
            case GLFW_KEY_0:
                std::cout << "Key was pressed: " << key << std::endl;
                //thiz->transformMatrixIndex = key - GLFW_KEY_0;
                //std::cout << thiz->transformMatrixIndex << std::endl;
                break;
            default:
                break;
        }
        if (key == GLFW_KEY_Q && action == GLFW_PRESS) {
            std::cout << "Q was pressed. Exiting." << std::endl;
            exit(0);
        }
        if (key == GLFW_KEY_E && action == GLFW_RELEASE) {
            printf("Printing NodeTree\n\n");
            TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
            if (p_TabComponent) {
                DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
                if (p_DocComponent) {
                    printNode(p_DocComponent->domRootNode, 1);
                }
            }
            printf("\n\n");
        }
        if (key == GLFW_KEY_D && action == GLFW_RELEASE) {
            printf("Printing ComponentTree\n\n");
            TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
            if (p_TabComponent) {
                DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
                if (p_DocComponent) {
                    Component::printComponentTree(p_DocComponent->rootComponent, 0);
                }
            }
            printf("\n\n");
        }
        if (key == GLFW_KEY_F && action == GLFW_RELEASE) {
            printf("Printing UI ComponentTree\n\n");
            Component::printComponentTree(thiz->rootComponent, 0);
            printf("\n\n");
        }
        int yOffsetScroll = 1;
        if (key == GLFW_KEY_PAGE_UP && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
            TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
            if (p_TabComponent) {
                DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
                if (p_DocComponent) {
                    p_DocComponent->transformMatrix[13] += -yOffsetScroll * 0.1;
                    p_DocComponent->transformMatrixDirty = true;
                    thiz->renderDirty = true;
                }
            }
        }
        if (key == GLFW_KEY_PAGE_DOWN && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
            TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
            if (p_TabComponent) {
                DocumentComponent *p_DocComponent = dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
                if (p_DocComponent) {
                    p_DocComponent->transformMatrix[13] += yOffsetScroll * 0.1;
                    p_DocComponent->transformMatrixDirty = true;
                    thiz->renderDirty = true;
                }
            }
        }

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

    glfwMakeContextCurrent(window);

    return true;
}

bool Window::initGLEW() const {
    glewExperimental = GL_TRUE;
    const GLenum err = glewInit();
    if (err != GLEW_OK) {
        std::cout << "Could not initialize GLEW: " << glewGetErrorString(err) << std::endl;
        return false;
    }
    return true;
}

bool Window::initGL() {
    const GLubyte *renderer = glGetString(GL_RENDERER);
    const GLubyte *version = glGetString(GL_VERSION);
    std::cout << "Renderer: " << renderer << std::endl;
    std::cout << "Version: " << version << std::endl;

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glClearColor(0.8f, 0.8f, 0.8f, 0.8f);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    GLenum glErr=glGetError();
    if(glErr != GL_NO_ERROR) {
        std::cout << "window::initGL - blend, clear, texParam - not ok: " << glErr << std::endl;
    }

    //std::cout << "OpenGL is set up" << std::endl;

    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);

    return true;
}

void Window::onResize(int passedWidth, int passedHeight) {
    this->windowWidth = passedWidth;
    this->windowHeight = passedHeight;
    this->delayResize = 1;
}

void Window::resize() {
    //std::cout << "Window::resize" << std::endl;
    const std::clock_t begin = clock();
    //resizeComponentTree(rootComponent, windowWidth, windowHeight);

    //this->printComponentTree(rootComponent, 0);

    //this->buildUI();
    rootComponent->windowWidth = windowWidth;
    rootComponent->windowHeight = windowHeight;
    rootComponent->layout();

    const std::clock_t end = clock();
    std::cout << "Window::resize - resized components in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
    //this->printComponentTree(rootComponent, 0);

    // recalculate scroll max by calculating how many screens are in the rootComponent's Height
    if (transformMatrix[13]>std::max((rootComponent->height)/(windowHeight)*2.0f, 2.0f)) {
        transformMatrix[13]=std::max((rootComponent->height)/(windowHeight)*2.0f, 2.0f);
        transformMatrixDirty = true;
    }

    //thiz->printComponentTree(thiz->rootComponent, 0);
    //printComponentTree(rootComponent, 0);
    renderDirty = true;
}

void Window::render() {
    if (delayResize) {
        delayResize--;
        if (delayResize) {
            return;
        }
        //std::cout << "Window::render - restarting drawing" << std::endl;
        this->resize();
    }
    /*
    if (domDirty) {
        const std::clock_t begin = clock();
        createComponentTree(domRootNode, rootComponent);
        const std::clock_t end = clock();
        std::cout << "built & laid out window components in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
        //printComponentTree(rootComponent, 0);
        //std::cout << "Window printComponentTree end" << std::endl;
        domDirty = false;
        renderDirty = true;
    }
    */
    if (renderDirty || transformMatrixDirty) {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        GLenum glErr=glGetError();
        if(glErr != GL_NO_ERROR) {
            std::cout << "window::render - box render start - not ok: " << glErr << std::endl;
        }

		Shader *textureShader = shaderLoader.getShader(VertexShader("TextureShader.vert"),
				FragmentShader("TextureShader.frag"));
        if (!textureShader) {
            std::cout << "Cant load shader" << std::endl;
            return;
        }
		textureShader->bind();
        glErr=glGetError();
        if(glErr != GL_NO_ERROR) {
            std::cout << "window::render - glUseProgram - not ok: " << glErr << std::endl;
        }

        // draw anime 1st, tabbed components 2nd
        // then box shit
        // text last
        renderComponentType("anime", rootComponent);

        renderComponentType("tab", rootComponent);
		textureShader->bind();

        //GLint transformLocation = glGetUniformLocation(textureProgram, "transform");
        //glUniformMatrix4fv(transformLocation, 1, GL_FALSE, transformMatrix);
        renderComponentType("box", rootComponent);
        renderComponentType("input", rootComponent);

        // it's quick but done on scroll
		Shader *fontShader = shaderLoader.getShader(VertexShader("FontShader.vert"),
				FragmentShader("FontShader.frag"));
		fontShader->bind();
        // reset both, since components can change this
        // god we may have to reset this after each component render...
        // maybe we don't need too
        if (transformMatrixDirty) {
            //const std::clock_t begin = clock();
            GLint transformLocation = fontShader->uniform("transform");
            glErr=glGetError();
            if(glErr != GL_NO_ERROR) {
                std::cout << "window::render - glGetUniformLocation - not ok: " << glErr << std::endl;
            }
            glUniformMatrix4fv(transformLocation, 1, GL_FALSE, transformMatrix);
            glErr=glGetError();
            if(glErr != GL_NO_ERROR) {
                std::cout << "window::render - glUniformMatrix4fv - not ok: " << glErr << std::endl;
            }

            // didn't change much (could be we didnt' select the textureProgram first)
            //GLint transformLocation2 = glGetUniformLocation(textureProgram, "transform");
            //glUniformMatrix4fv(transformLocation2, 1, GL_FALSE, transformMatrix);

            //const std::clock_t end = clock();
            //std::cout << "Updated font matrix in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
            transformMatrixDirty = false;
        }
        //renderComponents(rootComponent);
        renderComponentType("text", rootComponent);
        glfwSwapBuffers(window);

        // update 2nd buffer
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		textureShader->bind();
        renderComponentType("anime", rootComponent);
        renderComponentType("tab", rootComponent);
		textureShader->bind();
        renderComponentType("box", rootComponent);
        renderComponentType("input", rootComponent);
        // it's quick but done on scroll
		fontShader->bind();
        renderComponentType("text", rootComponent);
        glfwSwapBuffers(window);

        renderDirty = false;
    }
    glfwSwapBuffers(window);
    glfwPollEvents();
}

void Window::setDOM(const std::shared_ptr<Node> rootNode) {

    // reset scroll position
    transformMatrix[13] = 2;
    transformMatrixDirty = true;

    if (!tabComponent) {
        std::cout << "Window::setDOM - no tabComponent" << std::endl;
        return;
    }

    std::cout << "Window::setDOM - tabComponent is already set up" << std::endl;
    TabbedComponent *tabbedComponent = dynamic_cast<TabbedComponent*>(tabComponent.get());
    if (!tabbedComponent->tabs.size()) {
        // if no tabs, make one
        tabbedComponent->addTab(currentURL.toString());
        //tabbedComponent->selectTab(tabbedComponent->tabs.back()->id);
        tabbedComponent->selectTab(tabbedComponent->tabs.back());
    }
    tabbedComponent->loadDomIntoTab(rootNode, currentURL.toString());
}

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

    // ComponentBuilder now calls setParent and setParents adds children
    //component->setParent(parentComponent);
    // set parent of the newly created component
    //component->parent = parentComponent;
    //if (parentComponent) {
        // add new component as child to parent
        //parentComponent->children.push_back(component);
    //}
    if (component && component==this->rootComponent) {
        // if this is the root node
        component->reqWidth = windowWidth;
        component->reqHeight = windowHeight;
    }

    if (!node) {
        return;
    }

    // create children elements
    for (std::shared_ptr<Node> child : node->children) {
        createComponentTree(child, component);
    }
    // update parents, creation brings them up to date
    //updateComponentSize(component);
}

void Window::renderComponentType(std::string str, std::shared_ptr<Component> component) {
    if (!component) {
        std::cout << "Window::renderComponentType - got null passed" << std::endl;
        return;
    }
    if (typeOfComponent(component) == str) {
        // how slow is this?
        if (str == "doc") {
            DocumentComponent *docComponent = dynamic_cast<DocumentComponent*>(component.get());
            docComponent->render();
        } else if (str =="tab") {
            TabbedComponent *pTabComponent = dynamic_cast<TabbedComponent*>(component.get());
            pTabComponent->render();
        } else if (str =="text") {
            TextComponent *textComponent = dynamic_cast<TextComponent*>(component.get());
            textComponent->render();
        } else if (str =="input") {
            InputComponent *inputComponent = dynamic_cast<InputComponent*>(component.get());
            inputComponent->render();
        } else if (str =="anime") {
            AnimeComponent *animeComponent = dynamic_cast<AnimeComponent*>(component.get());
            animeComponent->render();
        } else if (str =="box") {
            //AnimeComponent *animeComponent = dynamic_cast<AnimeComponent*>(component.get());
            //if (!animeComponent) {
                BoxComponent *boxComponent = dynamic_cast<BoxComponent*>(component.get());
                boxComponent->render();
            //}
        } else {
            std::cout << "Unknown type " << str << std::endl;
        }
    //} else {
        //std::cout << "type: " << typeOfComponent(component) << "!=" << str << std::endl;
    }
    // is this needed?
    if (component->children.empty()) {
        return;
    }
    for (std::shared_ptr<Component> &child : component->children) {
        this->renderComponentType(str, child);
    }
}

// used for picking
// should return multiple components
std::shared_ptr<Component> Window::searchComponentTree(const std::shared_ptr<Component> &component, const int x, const int y) {
    if (component->children.empty()) {
        // x,y: 0,0 is the upper left
        //std::cout << "cursor at: " << x << "," << y << " component at " << static_cast<int>(component->x) << "," << static_cast<int>(component->y) << " size " << static_cast<int>(component->width) << "," << static_cast<int>(component->height) << std::endl;
        int ty = component->y;
        int ty1 = component->height + component->y;
        if (ty < 0) {
            ty = 0;
            ty1 += 64; // FIXME: hack (for what?)
        }
        //std::cout << "Window::searchComponentTree - y search: " << ty1 << ">" << static_cast<int>(this->windowHeight + y) << ">" << static_cast<int>(ty) << std::endl;
        if (ty1 > this->windowHeight + y && this->windowHeight + y > ty) {
            //std::cout << "y match" << std::endl;
            if (component->x < x && component->x + component->width > x) {
                //std::cout << "Window::searchComponentTree - hit " << typeOfComponent(component) << std::endl;
                return component;
            }
        }
    }
    else {
        for (std::shared_ptr<Component> child : component->children) {
            if (!child->isPickable) continue;
            std::shared_ptr<Component> found = searchComponentTree(child, x, y);
            if (found) {
                return found;
            }
        }
    }
    return nullptr;
}

// moving naviagtion closer to window, as window is now the owner of currentURL
// preparation for multiple HTML documents
void Window::navTo(std::string url) {
    logDebug() << "Window::navTo(" << url << ")" << std::endl;
    URL link=URL(url);
    logDebug() << "Window::navTo - URL marshalled [" << link << "]" << std::endl;
    currentURL = currentURL.merge(link);
    logDebug() << "go to: " << currentURL << std::endl;
    focusedComponent = nullptr;
    hoverComponent = nullptr;
    setWindowContent(currentURL);
}

/*
 void handleRequest(const HTTPResponse &response) {
 std::cout << "main:::handleRequest - statusCode: " << response.statusCode << std::endl;
 if (response.statusCode == 200) {
 const std::unique_ptr<HTMLParser> parser = std::make_unique<HTMLParser>();
 const std::clock_t begin = clock();
 std::shared_ptr<Node> rootNode = parser->parse(response.body);
 const std::clock_t end = clock();
 std::cout << "Parsed document in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
 window->setDOM(rootNode);
 }
 else if (response.statusCode == 301) {
 std::string location;
 if (response.properties.find("Location")==response.properties.end()) {
 if (response.properties.find("location")==response.properties.end()) {
 std::cout << "::handleRequest - got 301 without a location" << std::endl;
 for(auto const &row : response.properties) {
 std::cout << "::handleRequest - " << row.first << "=" << response.properties.at(row.first) << std::endl;
 }
 return;
 } else {
 location = response.properties.at("location");
 }
 } else {
 location = response.properties.at("Location");
 }
 std::cout << "Redirect To: " << location << std::endl;
 std::shared_ptr<URI> uri = parseUri(location);
 const std::unique_ptr<HTTPRequest> request = std::make_unique<HTTPRequest>(uri);
 request->sendRequest(handleRequest);
 return;
 }
 else {
 std::cout << "Unknown Status Code: " << response.statusCode << std::endl;
 }
 }
 */

// tried to make a window method
void handleRequest(const HTTPResponse &response) {
    std::cout << "Window:::handleRequest - statusCode: " << response.statusCode << std::endl;
    if (response.statusCode == 200) {
        const std::unique_ptr<HTMLParser> parser = std::make_unique<HTMLParser>();
        const std::clock_t begin = clock();
        std::shared_ptr<Node> rootNode = parser->parse(response.body);
        const std::clock_t end = clock();
        std::cout << "Parsed document in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
        window->setDOM(rootNode);
    }
    else if (response.statusCode == 301) {
        std::string location;
        if (response.properties.find("Location")==response.properties.end()) {
            if (response.properties.find("location")==response.properties.end()) {
                std::cout << "::handleRequest - got 301 without a location" << std::endl;
                for(auto const &row : response.properties) {
                    std::cout << "::handleRequest - " << row.first << "=" << response.properties.at(row.first) << std::endl;
                }
                return;
            } else {
                location = response.properties.at("location");
            }
        } else {
            location = response.properties.at("Location");
        }
        std::cout << "Redirect To: " << location << std::endl;
        std::tuple<std::unique_ptr<URL>,enum URIParseError> result = parseUri(location);
        if (std::get<1>(result) != URI_PARSE_ERROR_NONE) {
            // TODO We probably wanna handle this better..
            std::cerr << "error parsing uri" << std::endl;
            return;
        }
        std::unique_ptr<URL> uri = std::move(std::get<0>(result));
        const std::unique_ptr<HTTPRequest> request = std::make_unique<HTTPRequest>(std::move(uri));
        request->sendRequest(handleRequest);
        return;
    }
    else {
        std::cout << "Unknown Status Code: " << response.statusCode << std::endl;
    }
}
#endif


code:
#ifndef TEXTWINDOW_H
#define TEXTWINDOW_H

#include "../components/ComponentBuilder.h"
#include "../components/Component.h"
#include "ShaderLoader.h"
#include "../../html/Node.h"
#include "Window.h"
#include <memory>
#include <vector>
#include <algorithm>
#include "../../networking/HTTPResponse.h"
#include "../../URL.h"

class TextWindow : public Window {
private:
public:
    ~TextWindow();
    bool init();

    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 renderComponentType(std::string str, std::shared_ptr<Component> component);

    void onResize(int passedWidth, int passedHeight);
    void checkForResize();
    void resize();

    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);
    void navTo(std::string url);

    // properties
    int maxTextureSize = 0;
    float transformMatrix[16] = {
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1
    };
    bool transformMatrixDirty = true;
    GLFWwindow *window;
    int windowWidth;
    int windowHeight;
    //std::shared_ptr<Node> domRootNode = nullptr;
    bool domDirty = false;
    bool renderDirty = true;

    ComponentBuilder componentBuilder;
    std::shared_ptr<Component> rootComponent = std::make_shared<Component>();

    // I hate doing this but we currently require this
    std::shared_ptr<Component> tabComponent = nullptr;
    std::shared_ptr<Component> addressComponent = nullptr;

    // could break these out in some sort of cursor class
    // to minimize dependencies
    std::shared_ptr<Component> focusedComponent = nullptr;
    std::shared_ptr<Component> hoverComponent = nullptr;
    double cursorX = 0;
    double cursorY = 0;
    unsigned int delayResize = 0;
    GLFWcursor* cursorHand;
    GLFWcursor* cursorArrow;
    GLFWcursor* cursorIbeam;
    URL currentURL;
	ShaderLoader shaderLoader;
    // is there only one selection per window? (probably not)
    int highlightStartX = 0;
    int highlightStartY = 0;
    std::vector<std::shared_ptr<Component>> selectionList;
};

bool setWindowContent(URL const& url);
void handleRequest(const HTTPResponse &response);

extern const std::unique_ptr<Window> window;

#endif


code:
#include "Shader.h"
#include <iostream>

void Shader::bind() const {
	glUseProgram(handle);
}

void Shader::release() const {
	glUseProgram(0);
}

int Shader::uniform(const std::string &name) {
	// Is it already in the map?
	auto it = locationMap.find(name);
	if (it != locationMap.end()) {
		return it->second;
	}

	// It isn't, get the uniform location and store it
	GLint location = glGetUniformLocation(handle, name.c_str());
    GLenum glErr=glGetError();
    if(glErr != GL_NO_ERROR) {
        std::cout << "Shader::uniform " << name << " - glGetUniformLocation - not ok: " << glErr << std::endl;
    }
    locationMap.insert(std::make_pair(name, location));

	return location;
}

int Shader::attribute(const std::string &name) {
	// Is it already in the map?
	auto it = locationMap.find(name);
	if (it != locationMap.end()) {
		return it->second;
	}

	// It isn't, get the attribute location and store it
	GLint location = glGetAttribLocation(handle, name.c_str());
	locationMap.insert(std::make_pair(name, location));

	return location;
}


code:
#ifndef SHADER_H
#define SHADER_H

#include <GL/glew.h>
#include <string>
#include <map>

struct VertexShader {
	VertexShader(std::string n) : name(n) { }
	std::string name;
};

struct FragmentShader {
	FragmentShader(std::string n) : name(n) { }
	std::string name;
};

class Shader {
public:
	enum Type {
		Vertex,
		Fragment
	};

	Shader(unsigned int h) : handle(h) { }
	void bind() const;
	void release() const;

	unsigned int handle;

	int uniform(const std::string &name);
	int attribute(const std::string &name);
private:
	std::map<std::string, int> locationMap;
};

#endif


code:
#include "ShaderLoader.h"

#include <stdlib.h>
#include <fstream>
#include <iostream>

#include "Shader.h"
#include "../../../../tools/Log.h"
#include "../../../../platform/tlsf.h"
#include "../../../../environment/Environment.h"
#include "../../../../environment/Path.h"

constexpr GLint maxLogSize = 1024 * 1024 * 2; //2 MiB

GLuint ShaderLoader::loadShader(const std::string &shaderName, Shader::Type type) {
	GLenum glType = getGlShaderType(type);
	if (!glType) {
		logError() << "Unknown shader type: " << static_cast<int>(type) << std::endl;
		return 0;
	}
	GLuint shader = glCreateShader(glType);

	std::string path = Path::fromUnixPath(Environment::getResourceDir() + "/shaders/" + shaderName);
	std::ifstream file(path, std::ifstream::ate);
	if (!file.is_open()) {
		logError() << "Could not open shader " << shaderName << std::endl;
		return 0;
	}

	long fileSize = file.tellg();
	file.seekg(std::ifstream::beg);
	char *buffer = static_cast<char *>(tlsf_malloc(static_cast<unsigned int>(fileSize) + 1));
	file.read(buffer, fileSize);
	buffer[fileSize] = 0;
	glShaderSource(shader, 1, &buffer, nullptr);
	glCompileShader(shader);

	tlsf_free(buffer);

	if(checkShader(shader, shaderName, type))
		return 0;

	return shader;
}

GLuint ShaderLoader::createProgram(const std::string &shader1Name, Shader::Type shader1Type,
	   	const std::string &shader2Name, Shader::Type shader2Type) {
	GLuint shader1 = loadShader(shader1Name, shader1Type);
	GLuint shader2 = loadShader(shader2Name, shader2Type);

	if (!shader1 || !shader2)
		return 0;

	GLuint program = glCreateProgram();
	glAttachShader(program, shader1);
	glAttachShader(program, shader2);

	glLinkProgram(program);
	if (checkProgram(program, GL_LINK_STATUS, shader1Name))
		return 0;
	/* glValidateProgram(program);
	if (checkProgram(program, GL_VALIDATE_STATUS, shader1Name))
		return 0; */

	return program;
}

Shader *ShaderLoader::getShader(VertexShader vertexShader, FragmentShader fragmentShader) {
	// concatenate strings to create unique key for the shader
    //  + "_" + std::to_string(windowId)
    std::string key = vertexShader.name + "_" + fragmentShader.name;

	// has the shader been cached?
	auto it = shaderCache.find(key);
	if (it != shaderCache.end())
		return it->second;

	// Ok. It isn't. Let's create the shader Q.Q
	GLuint program = createProgram(vertexShader.name, Shader::Type::Vertex,
		   	fragmentShader.name, Shader::Type::Fragment);
	if (!program) //reee it failed
		return NULL;

	Shader *shader = new Shader(program);
	shaderCache.insert(std::make_pair(key, shader));
	return shader;
}

int ShaderLoader::checkShader(GLuint shader, const std::string &shaderName, Shader::Type type) {
	GLint success = 0;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &success);

	if (success == GL_TRUE) {
		//logDebug() << "Shader " << shaderName << " compiled successfully!" << std::endl;
	} else {
		GLint logSize = 0;
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logSize);
		if (logSize > maxLogSize) {
			logError() << "Shader " << shaderName << " failed to compile but \
				the log is too long to show." << std::endl;
			return 1;
		}
		char *log = new char[logSize];
		glGetShaderInfoLog(shader, logSize, &logSize, log);
		logError() << "Shader " << shaderName << " failed to compile. GL log output: "
			<< log << std::endl;
		delete[] log;
		glDeleteShader(shader);
		return 1;
	}
	return 0;
}

int ShaderLoader::checkProgram(GLuint program, GLenum pname, const std::string &shaderName) {
	GLint success = 0;
	glGetProgramiv(program, pname, &success);
	if (success == GL_TRUE) {
		//logDebug() << "Program with shader1 " << shaderName << " " << getProgramStatusString(pname) << std::endl;
	} else {
		GLint logSize = 0;
		glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logSize);
		if (logSize > maxLogSize) {
			logError() << "Program with shader1 " << shaderName << " failed to be "
				<< getProgramStatusString(pname) << " but the log is too long to show" << std::endl;
			return 1;
		}
		char *log = new char[logSize];
		glGetProgramInfoLog(program, logSize, &logSize, log);
		logError() << "Program with shader1 " << shaderName << " failed to be "
			<< getProgramStatusString(pname) << ". GL log output: " << log << std::endl;
		delete[] log;
		glDeleteProgram(program);
		return 1;
	}
	return 0;
}

const char *ShaderLoader::getProgramStatusString(GLenum pname) {
	switch (pname) {
		case GL_LINK_STATUS:
			return "linked";
		case GL_VALIDATE_STATUS:
			return "validated";
		default:
		break;
	}
	return "";
}

GLenum ShaderLoader::getGlShaderType(const Shader::Type type) {
	switch (type) {
		case Shader::Vertex:
			return GL_VERTEX_SHADER;
		case Shader::Fragment:
			return GL_FRAGMENT_SHADER;
		default:
		break;
	}
	return 0;
}


code:
#ifndef SHADERLOADER_H
#define SHADERLOADER_H

#include <GL/glew.h>
#include <string>
#include <map>

#include "Shader.h"

class ShaderLoader {
public:
	using ShaderCache = std::map<std::string, Shader*>;

	// Get a shader from the shader cache and load it from disk it isn't there
	Shader *getShader(VertexShader vertexShader, FragmentShader fragmentShader);

	// Load a shader from disk and return the its handle
	GLuint loadShader(const std::string &shader, Shader::Type shaderType);

	GLuint createProgram(const std::string &shader1Name, Shader::Type shader1Type,
			const std::string &shader2Name, Shader::Type shader2Type);
private:
	ShaderCache shaderCache;

	int checkProgram(GLuint program, GLenum programName,
			const std::string &shaderName);

	int checkShader(GLuint shader, const std::string &shaderName, Shader::Type shaderType);

	GLenum getGlShaderType(const Shader::Type type);

	const char *getProgramStatusString(GLenum pname);
};
#endif


code:
#include "Window.h"
#include "../../../../parsers/markup/TagNode.h"
#include "../../../../parsers/markup/TextNode.h"

#include "../../../../networking/HTTPRequest.h"
#include "../../../../parsers/markup/html/HTMLParser.h"
#include "../../../../tools/StringUtils.h"
#include "../../../../tools/Log.h"
#include "../../../components/BoxComponent.h"
#include "../../../components/ImageComponent.h"
#include "../../../components/DocumentComponent.h"
#include "../../../components/TabbedComponent.h"
#include "../../../components/TextComponent.h"
#include "../../../components/TabbedComponent.h"
#include "../../../components/InputComponent.h"
#include "Shader.h"
#include "../../../../app/browser/Browser.h"

#include <cmath>
#include <ctime>
#include <iostream>

extern std::unique_ptr<Browser> browser;

Window::~Window() {
    glfwTerminate();
}

bool Window::init() {

    Rect windowPosSize;
    windowPosSize.x = 0;
    windowPosSize.y = 0;
    // these are set before init is called
    windowPosSize.w = this->windowWidth;
    windowPosSize.h = this->windowHeight;

    OpenGL *pRenderer = dynamic_cast<OpenGL *>(renderer.get());
    if (!pRenderer) {
        //WindowHandle *tWin = renderer->createWindow("NeTRunner", &windowPosSize, 0);
        //this->window = tWin->window;
        std::cout << "Window::init - we only support OpenGL renderer" << std::endl;
        return false;
    }
    this->openglWindow = pRenderer->createWindow("NeTRunner", &windowPosSize, 0);
    this->window = this->openglWindow->window;

    //std::cout << "OpenGL is set up" << std::endl;
    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &this->maxTextureSize);
    std::cout << "Max texture size: " << this->maxTextureSize << std::endl;

    // set up our UI with a new window
    this->ui = std::make_unique<MultiComponent>(0, 0, this->windowWidth, this->windowHeight, this->windowWidth, this->windowHeight);
    this->ui->windowed = true;
    this->ui->win = shared_from_this();

    this->cursorHand = pRenderer->cursorHand;
    this->cursorArrow = pRenderer->cursorArrow;
    this->cursorIbeam = pRenderer->cursorIbeam;

    // relay events to our ui
    // window
    //this->openglWindow->onResize = this->ui->onResize;
    // use our delayed resizer
    this->openglWindow->onResize=[&](int w, int h) {
        this->onResize(w, h);
    };
    // mouse
    this->openglWindow->onMouseMove = this->ui->onMousemove;
    this->openglWindow->onMouseDown = this->ui->onMousedown;
    this->openglWindow->onMouseUp = this->ui->onMouseup;
    this->openglWindow->onWheel = this->ui->onWheel;
    // keyboard
    this->openglWindow->onKeyUp = [&](int key, int scancode, int mods) {
        this->ui->onKeyUp(key, scancode, 0, mods);
    };
    return true;
}

/*
bool Window::initGLFW() {
    std::cout << "Window::initGLFW" << std::endl;
    if (!glfwInit()) {
        std::cout << "Could not initialize GLFW" << std::endl;
        return false;
    }

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);

    glfwSetErrorCallback([](int error, const char* description) {
        std::cout << "glfw error [" << error << "]:" << description << std::endl;
    });

    GLFWwindow *context = nullptr;
    if (browser->windows.size()) {
        context = browser->windows.front().get()->window;
    }
    // after title, it's monitor
    window = glfwCreateWindow(windowWidth, windowHeight, "NetRunner", nullptr, context);
    if (!window) {
        glfwTerminate();
        std::cout << "Could not create window" << std::endl;
        return false;
    }
    // replace first parameter of all these callbacks with our window object instead of a GLFWwindow
    glfwSetWindowUserPointer(window, this);
    // set window w/h
    //glfwGetFramebufferSize(window, &windowWidth, &windowHeight); // in pixels
    glfwGetWindowSize(window, &windowWidth, &windowHeight); // use screen coordinates (not pixels) more retina friendly

    // set up event callbacks
    glfwSetFramebufferSizeCallback(window, [](GLFWwindow *win, int width, int height) {
        glViewport(0, 0, width, height);
    });
    glfwSetWindowSizeCallback(window, [](GLFWwindow *win, int width, int height) {
        Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
        thiz->onResize(width, height);
    });
    cursorHand = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
    cursorArrow = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
    cursorIbeam = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
    glfwSetCursorPosCallback(window, [](GLFWwindow *win, double xPos, double yPos) {
        Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
        thiz->cursorX = xPos;
        thiz->cursorY = yPos;
        //std::cout << "Window::Window:onMousemove - at " << static_cast<int>(xPos) << "," << static_cast<int>(yPos) << std::endl;
        if (xPos < 0 || yPos < 0) return;
        if (xPos > thiz->windowWidth || yPos > thiz->windowHeight) return;
        // p. much worthless on double
 */
        /*
        static double lx = 0;
        static double ly = 0;
        if (lx == xPos && ly == yPos) {
            return;
        }
        lx = xPos;
        ly = yPos;
        std::cout << "Window::Window:onMousemove - noCache" << std::endl;
        */
/*
        //std::cout << "Window::Window:onMousemove - begin search" << std::endl;
        //std::cout << "Window::Window:onMousemove - windowHeight: " << thiz->windowHeight << " cursorY: " << thiz->cursorY << " scrollY: " << thiz->transformMatrix[13] << std::endl;
        thiz->ui->onMousemove(thiz->cursorX, thiz->cursorY);

    });
    glfwSetScrollCallback(window, [](GLFWwindow *win, double xOffset, double yOffset) {
        Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
        //std::cout << "glfwSetScrollCallback " << yOffset << " int" << (int)(yOffset * 10) << std::endl;
        thiz->ui->onWheel(xOffset * 10, yOffset * 10);
    });
    glfwSetMouseButtonCallback(window, [](GLFWwindow *win, int button, int action, int mods) {
        Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
        if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
            //std::cout << "left press" << std::endl;
            thiz->ui->onMousedown(thiz->cursorX, thiz->cursorY);
        }
        if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE) {
            //std::cout << "left release" << std::endl;
            thiz->ui->onMouseup(thiz->cursorX, thiz->cursorY);
        }
    });

    // works with utf-32 and os keyboard layout but we'll lkeep the low level for now
    glfwSetCharCallback(window, [](GLFWwindow* win, unsigned int codepoint) {
        Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
        //std::cout << "Window::glfwSetCharCallback - codepoint: " << codepoint << std::endl;

        // should be used for inputComponent
    });

    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;

// 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;
        }
        if (key == GLFW_KEY_L && action == GLFW_RELEASE && mods == attentionMod) {
            if (thiz->ui->focusedComponent && thiz->ui->focusedComponent->onBlur) {
                thiz->ui->focusedComponent->onBlur();
            }
            if (thiz->addressComponent && thiz->addressComponent->onFocus) {
                thiz->addressComponent->onFocus();
            }
            thiz->ui->focusedComponent = thiz->addressComponent;
            return;
        }
        if (key == GLFW_KEY_T && action == GLFW_RELEASE && mods == attentionMod) {
            TabbedComponent *p_tabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
            if (p_tabComponent) {
                p_tabComponent->selectTab(p_tabComponent->addTab("New Tab"));
            }
            return;
        }
        if (action == GLFW_RELEASE) {
            thiz->ui->onKeyUp(key, scancode, action, mods);
        }
        if (action == GLFW_REPEAT) {
            //thiz->ui->onKeyRepeat(key, scancode, mods);
            //thiz->ui->onKeyPress(key, scancode, action, mods);
        }
        if (action == GLFW_PRESS) {
            //thiz->ui->onKeyDown(key, scancode, mods);
            //thiz->ui->onKeyPress(key, scancode, action, mods);
        }
    });

    glfwMakeContextCurrent(window);

    return true;
}
*/

void Window::onResize(int passedWidth, int passedHeight) {
    //std::cout << "Window::onResize" << std::endl;
    this->windowWidth = passedWidth;
    this->windowHeight = passedHeight;
    this->delayResize = 1;
}

void Window::scrollDocument(int y) {
    //TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(this->tabComponent.get());
    //if (p_TabComponent) {
        // dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
        DocumentComponent *p_DocComponent = this->getActiveDocumentComponent().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 active docComponent" << std::endl;
        }
    //} else {
        //std::cout << "Window::scrollDocument - no tabComponent" << std::endl;
    //}
}

void Window::resize() {
    ui->resize(this->windowWidth, this->windowHeight);
    renderDirty = true;
}

void Window::render() {
    if (delayResize) {
        delayResize--;
        if (delayResize) {
            return;
        }
        //std::cout << "Window::render - restarting drawing" << std::endl;
        this->resize();
    }
    if (renderDirty) {

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        ui->render();
        glfwSwapBuffers(window);

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        ui->render();
        glfwSwapBuffers(window);

        renderDirty = false;
    }
    glfwSwapBuffers(window);
    glfwPollEvents();
}

std::shared_ptr<DocumentComponent> Window::getActiveDocumentComponent() {
    // need to cast it first
    TabbedComponent *p_tabComponent = dynamic_cast<TabbedComponent*>(this->tabComponent.get());
    if (!p_tabComponent) {
        std::cout << "Window::getActiveDocumentComponent - No active tab" << std::endl;
        return nullptr;
    }
    std::shared_ptr<DocumentComponent> docComponent = std::static_pointer_cast<DocumentComponent>(p_tabComponent->documentComponent);
    if (!docComponent) {
        std::cout << "Window::getActiveDocumentComponent - No active document" << std::endl;
        return nullptr;
    }
    return docComponent;
}


code:
#ifndef WINDOW_H
#define WINDOW_H

#include "../../../components/ComponentBuilder.h"
#include "../../../components/MultiComponent.h"
//#include "../components/Component.h"
#include "ShaderLoader.h"
#include "../../../../parsers/markup/Node.h"
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <memory>
#include <vector>
#include <algorithm>
#include "../../../../networking/HTTPResponse.h"
#include "../../../../tools/URL.h"
#include "opengl.h"

class DocumentComponent;

extern std::unique_ptr<BaseRenderer> renderer;

// we don't want to extend WindowHandle because this is an API for the system in general
// so we'll embed it as a member, and we can add the calls
class Window : public std::enable_shared_from_this<Window> {
private:
public:
    ~Window();
    bool init();
    bool initGLFW();

    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 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);

    //void navTo(std::string url);

    // properties
    int maxTextureSize = 0;
    unsigned int oglActiveShader = 0; // cache to minimize opengl state changes

    // just a default matrix to reset UI since it won't scroll
    float textureTransformMatrix[16] = {
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1
    };

    // text uses a different one, since we have a different clamp on it
    float transformMatrix[16] = {
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 2, 0, 1
    };
    //bool transformMatrixDirty = true;
    GLFWwindow *window;
    OpenGLWindowHandle *openglWindow;
    int windowWidth;
    int windowHeight;
    //std::shared_ptr<Node> domRootNode = nullptr;
    //bool domDirty = false;
    bool renderDirty = true;

    //ComponentBuilder componentBuilder;
    //std::shared_ptr<Component> rootComponent = std::make_shared<Component>();

    // could break these out in some sort of cursor class
    // to minimize dependencies
    //std::shared_ptr<Component> focusedComponent = nullptr;
    //std::shared_ptr<Component> hoverComponent = nullptr;
    double cursorX = 0;
    double cursorY = 0;
    unsigned int delayResize = 0;
    GLFWcursor* cursorHand;
    GLFWcursor* cursorArrow;
    GLFWcursor* cursorIbeam;

    // why is this a pointer? I think we thought it'd make moving the UI between windows easier
    // open to opinions on changing this to a not-pointer
    std::unique_ptr<MultiComponent> ui = nullptr;

    ShaderLoader shaderLoader;

    // is there only one selection per window? (probably not)
    int highlightStartX = 0;
    int highlightStartY = 0;
    std::vector<std::shared_ptr<Component>> selectionList;
    // multiwindow support
    size_t id; // key for shader caches
    unsigned int clearColor = 0xCCCCCCCC;

    // I hate doing this but we currently require this
    // FIXME: move these out into browser somehow
    std::shared_ptr<DocumentComponent> getActiveDocumentComponent();
    std::shared_ptr<Component> tabComponent = nullptr;
    std::shared_ptr<Component> addressComponent = nullptr;
};

//bool setWindowContent(URL const& url);
//void handleRequest(const HTTPResponse &response);
//extern const std::unique_ptr<Window> window;

#endif