# include "DocumentComponent.h"
# include <cmath>
# include <iostream>
# include "../../Log.h"
# include "InputComponent.h"
# include "TabbedComponent.h"
# include <ctime>
# include "../opengl/Shader.h"
# include "../../WebResource.h"
# include "../../html/HTMLParser.h"
void deleteComponent ( std : : shared_ptr < Component > & component ) ;
void deleteNode ( std : : shared_ptr < Node > node ) ;
DocumentComponent : : DocumentComponent ( const float rawX , const float rawY , const float rawWidth , const float rawHeight , const int passedWindowWidth , const int passedWindowHeight ) : MultiComponent ( rawX , rawY , rawWidth , rawHeight , passedWindowWidth , passedWindowHeight ) {
//std::cout << "DocumentComponent::DocumentComponent" << std::endl;
windowWidth = passedWindowWidth ;
windowHeight = passedWindowHeight ;
//std::cout << "DocumentComponent::DocumentComponent - window size: " << windowWidth << "x" << windowHeight << std::endl;
x = rawX ;
y = rawY ;
width = rawWidth ;
height = rawHeight ;
if ( height < 0 ) {
std : : cout < < " DocumentComponent::DocumentComponent - height was less than zero " < < std : : endl ;
height = 0 ;
}
//std::cout << "DocumentComponent::DocumentComponent - our size" << static_cast<int>(width) << "x" << static_cast<int>(height) << std::endl;
onMousemove = [ this ] ( int passedX , int passedY ) {
// set hover component
static int lx = 0 ;
static int ly = 0 ;
//std::cout << "DocumentComponent::DocumentComponent:onMousemove - at " << passedX << "," << passedY << std::endl;
if ( lx = = passedX & & ly = = passedY ) {
return ;
}
lx = passedX ;
ly = passedY ;
//std::cout << "DocumentComponent::DocumentComponent:onMousemove - size " << this->windowWidth << "," << this->windowHeight << std::endl;
//std::cout << "DocumentComponent::DocumentComponent:onMousemove - at " << passedX << "," << passedY << " scroll: " << (int)(((this->transformMatrix[13] / 2) - 1) * this->windowHeight) << std::endl;
this - > hoverComponent = this - > searchComponentTree ( this - > rootComponent , passedX , passedY + ( ( ( this - > transformMatrix [ 13 ] / 2 ) - 1 ) * this - > windowHeight ) ) ;
if ( this - > hoverComponent ) {
//std::cout << "DocumentComponent::DocumentComponent:onMousemove - hovering over " << typeOfComponent(this->hoverComponent) << " component" << std::endl;
if ( this - > hoverComponent - > onMousemove ) {
// this could communicate the cursor to use
this - > hoverComponent - > onMousemove ( passedX , passedY ) ;
}
if ( this - > hoverComponent - > onClick ) {
glfwSetCursor ( this - > win - > window , this - > win - > cursorHand ) ;
} else {
glfwSetCursor ( this - > win - > window , this - > win - > cursorIbeam ) ;
}
} else {
glfwSetCursor ( this - > win - > window , this - > win - > cursorArrow ) ;
}
} ;
onWheel = [ this ] ( int passedX , int passedY ) {
//std::cout << "DocumentComponent::DocumentComponent:::onWheel - scroll yDelta: " << y << std::endl;
//Component::printComponentTree(rootComponent, 0);
double pY = passedY / 10.0 ;
this - > scrollY - = pY ;
if ( this - > scrollY < 0 ) {
this - > scrollY = 0 ;
} else if ( this - > scrollY > this - > scrollHeight ) {
this - > scrollY = this - > scrollHeight ;
}
//std::cout << "scroll pos: " << scrollY << "/" << scrollHeight << std::endl;
//std::cout << "y: " << static_cast<int>(this->y) << " - " << this->scrollY << std::endl;
//std::cout << "root.y: " << static_cast<int>(rootComponent->y) << std::endl;
//std::cout << "windowSize: " << windowWidth << "," << windowHeight << std::endl;
// new system
//std::cout << "DocumentComponent::DocumentComponent:::onWheel - old position: " << this->transformMatrix[13] << " adjustment:" << (-pY*0.1) << std::endl;
this - > transformMatrix [ 13 ] + = - pY * 0.01 ;
// 2.0 is one screen height
// we draw from 0 downwards (y+), so can't scroll past our starting draw point
if ( this - > transformMatrix [ 13 ] < 2 ) {
this - > transformMatrix [ 13 ] = 2 ;
}
// calculate scroll max by calculating how many screens are in the rootComponent's Height
if ( this - > transformMatrix [ 13 ] > std : : max ( ( this - > rootComponent - > height - this - > rootComponent - > y ) / this - > windowHeight * 2.0f , 2.0f ) ) {
this - > transformMatrix [ 13 ] = std : : max ( ( this - > rootComponent - > height - this - > rootComponent - > y ) / this - > windowHeight * 2.0f , 2.0f ) ;
}
this - > transformMatrixDirty = true ;
/*
// adjust root position
rootComponent - > y = this - > y + this - > scrollY ;
//std::cout << "now root.y: " << static_cast<int>(rootComponent->y) << std::endl;
// reset root size
rootComponent - > windowWidth = windowWidth ;
rootComponent - > windowHeight = windowHeight ;
//Component::printComponentTree(rootComponent, 0);
// this takes so long, we may want to delay until the input is adjusted
const std : : clock_t begin = clock ( ) ;
rootComponent - > layout ( ) ; // need to update the vertices
//renderDirty = true;
// should we mark win->renderDirty = true?
const std : : clock_t end = clock ( ) ;
std : : cout < < " Scrolled document in: " < < std : : fixed < < ( ( static_cast < double > ( end - begin ) ) / CLOCKS_PER_SEC ) < < std : : scientific < < " seconds " < < std : : endl ;
*/
//rootComponent->y = this->y - this->scrollY;
//std::cout << "after root.y: " << static_cast<int>(rootComponent->y) << std::endl;
// don't need this - why not?
//this->renderDirty = true;
} ;
onMousedown = [ this ] ( int passedX , int passedY ) {
//std::cout << "document left press" << std::endl;
if ( this - > hoverComponent ) {
if ( this - > focusedComponent ! = this - > hoverComponent ) {
// blur old component
if ( this - > focusedComponent ) {
if ( this - > focusedComponent - > onBlur ) {
this - > focusedComponent - > onBlur ( ) ;
}
}
// focus new component
if ( this - > hoverComponent - > onFocus ) {
this - > hoverComponent - > onFocus ( ) ;
}
}
this - > focusedComponent = this - > hoverComponent ;
if ( this - > focusedComponent - > onMousedown ) {
//std::cout << "click event" << std::endl;
this - > focusedComponent - > onMousedown ( passedX , passedY ) ;
}
}
} ;
onMouseup = [ this ] ( int passedX , int passedY ) {
//std::cout << "document left release" << std::endl;
if ( this - > hoverComponent ) {
//std::cout << "DocumentComponent::DocumentComponent:onMouseup - hovering over " << typeOfComponent(this->hoverComponent) << " component" << std::endl;
if ( this - > focusedComponent ! = this - > hoverComponent ) {
// blur old component
if ( this - > focusedComponent ) {
if ( this - > focusedComponent - > onBlur ) {
this - > focusedComponent - > onBlur ( ) ;
}
}
// focus new component
if ( this - > hoverComponent - > onFocus ) {
this - > hoverComponent - > onFocus ( ) ;
}
}
this - > focusedComponent = this - > hoverComponent ;
//std::cout << "DocumentComponent::DocumentComponent:onMouseup - hovering over " << typeOfComponent(this->hoverComponent) << " component, focused on " << typeOfComponent(this->focusedComponent) << std::endl;
if ( this - > focusedComponent - > onMouseup ) {
//std::cout << "click event" << std::endl;
this - > focusedComponent - > onMouseup ( passedX , passedY ) ;
}
if ( this - > focusedComponent - > onClick ) {
//std::cout << "click event" << std::endl;
this - > focusedComponent - > onClick ( ) ;
}
}
} ;
onKeyup = [ this ] ( int key , int scancode , int action , int mods ) {
//std::cout << "DocumentComponent::DocumentComponent:onKeyup" << typeOfComponent(this->focusedComponent) << std::endl;
InputComponent * inputComponent = dynamic_cast < InputComponent * > ( this - > focusedComponent . get ( ) ) ;
if ( inputComponent ) {
//std::cout << "inputComponent is focused, 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 ;
} 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
}
}
}
} ;
}
void deleteComponent ( std : : shared_ptr < Component > & component ) {
// delete all my child first
for ( std : : shared_ptr < Component > child : component - > children ) {
deleteComponent ( child ) ;
}
component - > parent = nullptr ;
component - > previous = nullptr ;
component - > children . clear ( ) ;
component . reset ( ) ;
// now delete self
}
void deleteNode ( std : : shared_ptr < Node > node ) {
for ( std : : shared_ptr < Node > child : node - > children ) {
deleteNode ( child ) ;
}
node - > parent = nullptr ;
node - > children . clear ( ) ;
node - > component = nullptr ; // disassociate component
node . reset ( ) ;
}
void DocumentComponent : : setDOM ( const std : : shared_ptr < Node > rootNode ) {
// reset rootComponent
if ( rootComponent ) {
deleteComponent ( rootComponent ) ;
}
if ( domRootNode ) {
deleteNode ( domRootNode ) ;
}
// reset scroll position
transformMatrix [ 13 ] = 2 ;
transformMatrixDirty = true ;
// new root component
rootComponent = std : : make_shared < Component > ( ) ;
rootComponent - > name = " rootComponent of " + name ;
rootComponent - > y = y ;
domRootNode = rootNode ;
domDirty = true ;
}
void DocumentComponent : : render ( ) {
//std::cout << "DocumentComponent::render" << std::endl;
if ( domDirty ) {
const std : : clock_t begin = clock ( ) ;
createComponentTree ( domRootNode , rootComponent ) ;
const std : : clock_t end = clock ( ) ;
// root component here doesn't have any children...
std : : cout < < " built & laid out document components in: " < < std : : fixed < < ( ( static_cast < double > ( end - begin ) ) / CLOCKS_PER_SEC ) < < std : : scientific < < " seconds " < < std : : endl ;
//Component::printComponentTree(rootComponent, 0);
domDirty = false ;
//std::cout << "root Height: " << static_cast<int>(rootComponent->height) << " window Height: " << windowHeight << " y " << static_cast<int>(this->y) << std::endl;
scrollHeight = std : : max ( 0 , static_cast < int > ( rootComponent - > height - ( windowHeight + ( this - > y * 2 ) ) ) ) ;
// 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 ;
}
}
// we have to do this each time
// because window resets it
//if (transformMatrixDirty) {
//const std::clock_t begin = clock();
Shader * fontShader = this - > win - > shaderLoader . getShader ( VertexShader ( " FontShader.vert " ) ,
FragmentShader ( " FontShader.frag " ) ) ;
GLint transformLocation = fontShader - > uniform ( " transform " ) ;
glUniformMatrix4fv ( transformLocation , 1 , GL_FALSE , transformMatrix ) ;
//const std::clock_t end = clock();
//std::cout << "Updated font matrix in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
Shader * textureShader = this - > win - > shaderLoader . getShader ( VertexShader ( " TextureShader.vert " ) ,
FragmentShader ( " TextureShader.frag " ) ) ;
GLint transformLocation2 = textureShader - > uniform ( " transform " ) ;
glUniformMatrix4fv ( transformLocation2 , 1 , GL_FALSE , transformMatrix ) ;
transformMatrixDirty = false ;
//}
//std::cout << "DocumentComponent::render - renderDirty" << std::endl;
textureShader - > bind ( ) ;
renderBoxComponents ( rootComponent ) ;
textureShader - > release ( ) ;
fontShader - > bind ( ) ;
renderComponents ( rootComponent ) ;
fontShader - > release ( ) ;
}
// create this component and all it's children
void DocumentComponent : : createComponentTree ( const std : : shared_ptr < Node > node , const std : : shared_ptr < Component > & parentComponent ) {
std : : shared_ptr < Component > component = componentBuilder . build ( node , parentComponent , this - > win ) ;
//std::cout << "DocumentComponent::createComponentTree" << std::endl;
if ( ! component ) {
//std::cout << "DocumentComponent::createComponentTree - no component" << std::endl;
return ;
}
if ( node = = domRootNode ) {
// if this is the root node
component - > reqWidth = windowWidth ;
component - > reqHeight = windowHeight ;
}
// create children elements
for ( std : : shared_ptr < Node > child : node - > children ) {
createComponentTree ( child , component ) ;
}
}
// used for picking
std : : shared_ptr < Component > DocumentComponent : : searchComponentTree ( const std : : shared_ptr < Component > & component , const int passedX , const int passedY ) {
if ( component - > children . empty ( ) ) {
//std::cout << "DocumentComponent::searchComponentTree - component at " << static_cast<int>(component->x) << "," << static_cast<int>(component->y) << " size " << static_cast<int>(component->width) << "," << static_cast<int>(component->height) << std::endl;
//std::cout << "DocumentComponent::searchComponentTree - y search: " << static_cast<int>(-component->y) << "<" << static_cast<int>(passedY) << "<" << static_cast<int>(-component->y + component->height) << std::endl;
if ( - component - > y < passedY & & - component - > y + component - > height > passedY ) {
//std::cout << "DocumentComponent::searchComponentTree - x search: " << static_cast<int>(component->x) << "<" << static_cast<int>(passedX) << "<" << static_cast<int>(component->x + component->width) << std::endl;
if ( component - > x < passedX & & component - > x + component - > width > passedX ) {
//std::cout << "DocumentComponent::searchComponentTree - hit " << typeOfComponent(component) << std::endl;
return component ;
}
}
}
else {
for ( std : : shared_ptr < Component > child : component - > children ) {
std : : shared_ptr < Component > found = searchComponentTree ( child , passedX , passedY ) ;
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 DocumentComponent : : navTo ( const std : : string url ) {
logDebug ( ) < < " DocumentComponent::navTo( " < < url < < " ) " < < std : : endl ;
this - > currentURL = currentURL . merge ( URL ( url ) ) ;
logDebug ( ) < < " DocumentComponent::navTo - go to: " < < currentURL < < std : : endl ;
//setWindowContent(currentURL);
logDebug ( ) < < " main::setWindowContent - " < < url < < std : : endl ;
// integrity check
TabbedComponent * tabComponent = dynamic_cast < TabbedComponent * > ( this - > win - > tabComponent . get ( ) ) ;
if ( ! tabComponent - > tabs . size ( ) ) {
std : : cout < < " DocumentComponent::navTo - There's a document when there's not tabs " < < std : : endl ;
}
// download URL
WebResource res = getWebResource ( url ) ;
if ( res . resourceType = = ResourceType : : INVALID ) {
logError ( ) < < " Invalid resource type: " < < res . raw < < std : : endl ;
return ;
}
// parse HTML
if ( res . resourceType = = ResourceType : : HTML ) {
HTMLParser parser ;
const std : : clock_t begin = clock ( ) ;
std : : shared_ptr < Node > rootNode = parser . parse ( res . raw ) ;
const std : : clock_t end = clock ( ) ;
logDebug ( ) < < " main::setWindowContent - Parsed document in: " < < std : : fixed < < ( ( static_cast < double > ( end - begin ) ) / CLOCKS_PER_SEC ) < < std : : scientific < < " seconds " < < std : : endl ;
// send NodeTree to window
//this->win->setDOM(rootNode);
// we need a way to communicate with our tabComponent
// maybe an event is best
if ( this - > onBeforeLoad ) {
this - > onBeforeLoad ( url ) ;
}
this - > setDOM ( rootNode ) ;
} else if ( res . resourceType = = ResourceType : : TXT ) {
std : : cout < < " Rendering text document " < < std : : endl ;
std : : shared_ptr < Node > rootNode = std : : make_shared < Node > ( NodeType : : ROOT ) ;
std : : shared_ptr < TagNode > tagNode = std : : make_shared < TagNode > ( ) ;
tagNode - > tag = " p " ;
// bind tag to root
tagNode - > parent = rootNode ;
rootNode - > children . push_back ( tagNode ) ;
std : : shared_ptr < TextNode > textNode = std : : make_shared < TextNode > ( ) ;
textNode - > text = res . raw ;
// bind text to tag
textNode - > parent = tagNode ;
tagNode - > children . push_back ( textNode ) ;
// send NodeTree to window
//this->win->setDOM(rootNode);
this - > setDOM ( rootNode ) ;
} else {
std : : cout < < " setWindowContent() - I don't know how to render non-html files " < < std : : endl ;
}
}