diff --git a/README.md b/README.md
index 87dab56..2aa1dbf 100644
--- a/README.md
+++ b/README.md
@@ -3,3 +3,20 @@
[](https://travis-ci.org/local-projects/ofxInterface) [](https://ci.appveyor.com/project/armadillu/ofxinterface)
flexible and lightweight GUI helper with a scene-graph and a multitouch manager for OpenFrameworks
+
+Compatible with openFrameworks 0.11.0+
+
+Generate all project files using the openFrameworks Project Generator.
+
+## Features
+
+* tool to create a touch focused interface
+* includes standard components like button and keyboard (see [definitions](https://github.com/brinoausrino/ofxInterface/blob/master/components.md))
+* create a GUI from json
+
+## Required Addons
+
+* [ofxAnimatable](https://github.com/armadillu/ofxAnimatable)
+* [ofxAssetManager](https://github.com/brinoausrino/ofxAssetManager)
+* [ofxEasing](https://github.com/arturoc/ofxEasing)
+* [ofxFontStash2](https://github.com/armadillu/ofxFontStash2)
diff --git a/addon_config.mk b/addon_config.mk
new file mode 100644
index 0000000..9cdd6f2
--- /dev/null
+++ b/addon_config.mk
@@ -0,0 +1,72 @@
+# All variables and this file are optional, if they are not present the PG and the
+# makefiles will try to parse the correct values from the file system.
+#
+# Variables that specify exclusions can use % as a wildcard to specify that anything in
+# that position will match. A partial path can also be specified to, for example, exclude
+# a whole folder from the parsed paths from the file system
+#
+# Variables can be specified using = or +=
+# = will clear the contents of that variable both specified from the file or the ones parsed
+# from the file system
+# += will add the values to the previous ones in the file or the ones parsed from the file
+# system
+#
+# The PG can be used to detect errors in this file, just create a new project with this addon
+# and the PG will write to the console the kind of error and in which line it is
+
+meta:
+ ADDON_NAME = ofxInterface
+ ADDON_DESCRIPTION = flexible and lightweight GUI helper with a scene-graph and a multitouch manager
+ ADDON_AUTHOR = galsasson, armadillu, brinoausrino
+ ADDON_TAGS = "sceneManager" "gui" "multitouch"
+ ADDON_URL = https://github.com/brinoausrino/ofxInterface
+
+common:
+ # dependencies with other addons, a list of them separated by spaces
+ # or use += in several lines
+ ADDON_DEPENDENCIES = ofxAnimatable
+ ADDON_DEPENDENCIES += ofxAssetManager
+ ADDON_DEPENDENCIES += ofxEasing
+ ADDON_DEPENDENCIES += ofxFontStash2
+
+ # include search paths, this will be usually parsed from the file system
+ # but if the addon or addon libraries need special search paths they can be
+ # specified here separated by spaces or one per line using +=
+ # ADDON_INCLUDES +=
+
+ # any special flag that should be passed to the compiler when using this
+ # addon
+ # ADDON_CFLAGS
+
+ # any special flag that should be passed to the linker when using this
+ # addon, also used for system libraries with -lname
+ # ADDON_LDFLAGS =
+
+ # linux only, any library that should be included in the project using
+ # pkg-config
+ # ADDON_PKG_CONFIG_LIBRARIES =
+
+ # osx/iOS only, any framework that should be included in the project
+ # ADDON_FRAMEWORKS =
+
+ # source files, these will be usually parsed from the file system looking
+ # in the src folders in libs and the root of the addon. if your addon needs
+ # to include files in different places or a different set of files per platform
+ # they can be specified here
+ # ADDON_SOURCES =
+
+ # some addons need resources to be copied to the bin/data folder of the project
+ # specify here any files that need to be copied, you can use wildcards like * and ?
+ # ADDON_DATA =
+
+ # when parsing the file system looking for libraries exclude this for all or
+ # a specific platform
+ # ADDON_LIBS_EXCLUDE =
+
+
+linux64:
+linux:
+linuxarmv6l:
+linuxarmv7l:
+android/armeabi:
+android/armeabi-v7a:
diff --git a/components.drawio b/components.drawio
new file mode 100644
index 0000000..a9c1ee2
--- /dev/null
+++ b/components.drawio
@@ -0,0 +1 @@
+7ZpNU9swEIZ/TY7t+CtfxxJCSwsMM2mHclTiJTYoXo8ik5hfXxlLsRWFGBoc+5AT1nplye/uI8lLOu5osf7OSBxcow+041j+uuOedxzHsXqO+JNZ0txi246bW+Ys9KWtMEzCF5BGS1qT0Iel5sgRKQ9j3TjDKIIZ12yEMVzpbg9I9VFjMgfDMJkRalrvQp8HuXXg9Av7DwjngRrZ7g3zOwuinOWbLAPi46pkcscdd8QQeX61WI+AZuopXfJ+F2/c3UyMQcTf0+H+5/z6xud/LvE8PCOPtv3ij7/IYDwTmsgXlpPlqVIAfCGIbCLjAc4xInRcWM8YJpEP2TCWaBU+V4ixMNrC+AicpzK6JOEoTAFfUHkXIv9bFivRjDCC3HIRUiofueSEceUxpTh7UsaSUz7xbLZvCiRNS0zYDPaoohKNsDnwPX7OJowCAMAFcJaKfgwo4eGzPg8iE3G+8StiJS5kuD4QOtsIHT7cCACNAOrhWQUhh0lMXgVYCWr1UMiHAuOw3q+j+d6yg6e6KOYtScCqAMhWVAQleHpWTVI5hlQ7haqQRtfxEKGkMJuloSTMsHdMYbqtwF/DWy4A23SXlgi1AOhrxCfi7x0J/9eu4q1IWnKIMYz4svTk28xQ0KVyQeXQcGvV/5C7uMjHL1Jo8yL/n1WekVUjpMhuSZSdDppanaQC7g7ojrsa9U7Q7VCl/xkwGenvWPpe5Ay2oppDLnsVgf0olcY4FVhW+NfDZd9IvN+w5gmDVhwbbE+XpHlQBydQd6gyrAVU1zsOqMY4FaBW+NcD6tBIvCsybXLvVKvWoG2IqjLBiVFdljfieCCk3uA4kBrjVEBa4V8PpLb5AX6NfkJhTGEBUoA2fYe3gFa3CTiFXCz9m/X/2lXN+/K98/UG0ayVdnZUndoAtVzaKr9LD6b/sCCb1ZbsmHkZxUnzTLhd/XvYcxtnotsIE+uQl5AQrXuV0eK6ACJrtJYHr6ZNTk8R952b3KftK2b5ZBIuYgqjAGZPwBpnyBu2jqFTSWWnLPXUVHqOHv+6ToHGOFWlzv3+NZ0CzaLKZMaQUjIVxGLESRi1gNnuVh24BWfBU3Vlpyz1lFf62/8HqIlZY5wKZiv8a2LWrK9M8IH/gnSKhPmNw9q32garGuwEqy5LPWWWoXUcWI1xKmCt8K8HVscss0xo6LdgTx14x8NUNItfCuXSFj+4csf/AA==
\ No newline at end of file
diff --git a/components.md b/components.md
new file mode 100644
index 0000000..6f3c8f7
--- /dev/null
+++ b/components.md
@@ -0,0 +1,143 @@
+# ofxInterface - components
+
+
+
+## Properties
+
+### Node
+
+The base element.
+
+| property | type | description |
+| ------------- |---------------| -----|
+| id | string | unique identifier (should always be set) |
+| position | ofVec2f | position in px|
+| size | ofVec2f | size in px |
+| plane | float | the bigger the more in front (default 10) |
+| renderClip | bool | clip element parts out of element size |
+| active | bool | set element visible and enabled |
+
+### ModalElement
+
+Element that describes buttons, checkers and radios.
+
+Inherits from Node.
+
+| property | type | description |
+| ------------- |---------------| -----|
+| type | string | ether "button","checker" or "radio" |
+| colorActive | ofColor | color for activated state|
+| colorInactive | ofColor | color for inactive state|
+| colorSelected | ofColor | color for selected state|
+
+### TextInput
+
+An editable Text.
+
+Inherits from ModalElement.
+
+| property | type | description |
+| ------------- |---------------| -----|
+| font | string | id of the font |
+| maxChars | int | maximum length of input|
+| description | string | text when line empty|
+
+### SimpleChecker
+
+A simple checkbox ;) -
+
+Inherits from ModalElement.
+
+### ColorPanel
+
+A colored panel
+
+Inherits from Node.
+
+| property | type | description |
+| ------------- |---------------| -----|
+| strokeWidth | float | stroke width |
+| borderRadius | float | border radius |
+| color | ofColor | panel color |
+| strokeColor | ofColor | border color |
+
+### TextureNode
+
+A Node that displays a texture
+
+Inherits from Node.
+
+| property | type | description |
+| ------------- |---------------| -----|
+| texture | string | texture id |
+| tint | string | texture tinting (color id is set)|
+| blendmode | string | "alpha","none","add","multiply","screen" and "subract" allowed |
+| strokeColor | ofColor | border color |
+
+### Label
+
+A short text
+
+Inherits from Node.
+
+| property | type | description |
+| ------------- |---------------| -----|
+| font | string | id of the font |
+| text | string | text to be drawn|
+| alignment | string | "left","center" or "right"|
+| shadow | object | activate dropshadow|
+
+#### shadow parameters
+| property | type | description |
+| ------------- |---------------| -----|
+| size | float | shadow size (bigger is blurrier)|
+| x | float | x-offset|
+| y | float | y-offset|
+| color | ofColor | shadow color |
+
+### ScrollableContainer
+
+A container to contain elements bigger than screen size.
+
+Inherits from Node.
+
+| property | type | description |
+| ------------- |---------------| -----|
+| sizeScrollableArea | ofVec2f | the scrollable area size |
+| bgColor | ofColor | background color|
+| scrollActiveColor | ofColor | scrollbar color when selected|
+| scrollInactiveColor | ofColor | scrollbar color when not selected|
+
+### SoftKeyboard
+
+An on-screen keyboard.
+
+Inherits from Node. Height of Element depends on width and is automatically set.
+
+| property | type | description |
+| ------------- |---------------| -----|
+| font | string | id of the font |
+| bgColor | ofColor | background color|
+| colorActive | ofColor | key background color when enabled|
+| colorInactive | ofColor | key background color when disabled|
+| colorSelected | ofColor | key background color when selected|
+| borderRadius | float | border radius (also effects keys)|
+| borderWidth | float | border width (also effects keys)|
+| margin | float | space between border and keys|
+| padding | float | space between keys|
+
+
+### TextureNode
+
+A simple Slider.
+
+Inherits from Node.
+
+| property | type | description |
+| ------------- |---------------| -----|
+| direction | string | "horizontal", "vertical" |
+| colorActive | ofColor | key background color when enabled|
+| colorInactive | ofColor | key background color when disabled|
+| colorSelected | ofColor | key background color when selected|
+| colorSelected | ofColor | key background color when selected|
+| lineWidth | int | thickness of the bar|
\ No newline at end of file
diff --git a/components.png b/components.png
new file mode 100644
index 0000000..baa8657
Binary files /dev/null and b/components.png differ
diff --git a/example-advanced/addons.make b/example-advanced/addons.make
index 51aff7e..a004113 100644
--- a/example-advanced/addons.make
+++ b/example-advanced/addons.make
@@ -1,2 +1,5 @@
+ofxAnimatable
+ofxAssetManager
+ofxEasing
+ofxFontStash2
ofxInterface
-ofxAnimatable
\ No newline at end of file
diff --git a/example-basic/addons.make b/example-basic/addons.make
index 3f201f2..a004113 100644
--- a/example-basic/addons.make
+++ b/example-basic/addons.make
@@ -1 +1,5 @@
-ofxInterface
\ No newline at end of file
+ofxAnimatable
+ofxAssetManager
+ofxEasing
+ofxFontStash2
+ofxInterface
diff --git a/exampleJson/addons.make b/exampleJson/addons.make
new file mode 100644
index 0000000..a004113
--- /dev/null
+++ b/exampleJson/addons.make
@@ -0,0 +1,5 @@
+ofxAnimatable
+ofxAssetManager
+ofxEasing
+ofxFontStash2
+ofxInterface
diff --git a/exampleJson/bin/data/.gitkeep b/exampleJson/bin/data/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/exampleJson/bin/data/assets.json b/exampleJson/bin/data/assets.json
new file mode 100644
index 0000000..c29a992
--- /dev/null
+++ b/exampleJson/bin/data/assets.json
@@ -0,0 +1,18 @@
+{
+ "textures":{
+ "folder":"textures",
+ "info":"files [[id,path]]",
+ "files":[
+ ["cat1","cat1.jpg"]
+ ]
+ },
+ "fonts":{
+ "folder":"fonts",
+ "info":"files [[id,path]]",
+ "files":[
+ ["Verdana","verdana.ttf"],
+ ["Franklin Gothic","frabk.ttf"]
+ ]
+ }
+
+}
\ No newline at end of file
diff --git a/exampleJson/bin/data/fonts/frabk.ttf b/exampleJson/bin/data/fonts/frabk.ttf
new file mode 100644
index 0000000..21c4ecf
Binary files /dev/null and b/exampleJson/bin/data/fonts/frabk.ttf differ
diff --git a/exampleJson/bin/data/fonts/verdana.ttf b/exampleJson/bin/data/fonts/verdana.ttf
new file mode 100644
index 0000000..8f25a64
Binary files /dev/null and b/exampleJson/bin/data/fonts/verdana.ttf differ
diff --git a/exampleJson/bin/data/gui.json b/exampleJson/bin/data/gui.json
new file mode 100644
index 0000000..bd7aafb
--- /dev/null
+++ b/exampleJson/bin/data/gui.json
@@ -0,0 +1,83 @@
+{
+ "elements":[
+ {
+ "id":"background",
+ "class":"colorPanel",
+ "color":"bg",
+ "size":[1280,768]
+ },{
+ "id":"Head",
+ "class":"label",
+ "font":"Headline",
+ "position":[80,44],
+ "size":[1280,40],
+ "text":"GUI from json"
+ },{
+ "id":"text",
+ "class":"textField",
+ "size":[820,40],
+ "position":[80,100],
+ "text":[
+ ""
+ ]
+ },{
+ "id":"button",
+ "class":"modalElement",
+ "position":[80,160],
+ "size":[32,32],
+ "colorActive":"black",
+ "colorSelected":"white",
+ "colorInactive":"black",
+ "info": "sub elements can be defined",
+ "elements":[
+ {
+ "id":"description",
+ "class":"labelSmall",
+ "position":[40,10],
+ "text": "a button"
+ }
+ ]
+ },{
+ "id":"checker",
+ "class":"simpleChecker",
+ "position":[250,160],
+ "size":[32,32],
+ "colorActive":"black",
+ "colorSelected":"white",
+ "colorInactive":"black",
+ "elements":[
+ {
+ "id":"description",
+ "class":"labelSmall",
+ "position":[40,10],
+ "text": "a simpleChecker"
+ }
+ ]
+ },
+ {
+ "id":"pic1",
+ "class":"texture",
+ "position":[950,44],
+ "texture":"cat1",
+ "size":[150,150]
+ }
+ ,{
+ "id":"input",
+ "class":"textInput",
+ "position":[80,300],
+ "size":[300,50],
+ "font":"Input",
+ "maxChars":20,
+ "description":"write something"
+ },{
+ "id":"keyboard",
+ "class":"softKeyboard",
+ "position":[80,380],
+ "size":[1000,500],
+ "font":"Keyboard"
+
+ }
+
+ ]
+}
\ No newline at end of file
diff --git a/exampleJson/bin/data/guiElements.json b/exampleJson/bin/data/guiElements.json
new file mode 100644
index 0000000..e649bee
--- /dev/null
+++ b/exampleJson/bin/data/guiElements.json
@@ -0,0 +1,46 @@
+{
+ "colors":[
+ {"id":"black","color":[0,0,0]},
+ {"id":"white","color":[255,255,255]},
+ {"id":"bg","color":[30,30,30]},
+ {"id":"tintRed","color":[250,0,0,120]},
+ {"id":"inactiveSlider","color":[31,31,31]},
+ {"id":"dropShadow","color":[0,0,0,50]},
+ {"id":"iconBg","color":[31,31,31]}
+ ],
+ "fontStyles":[
+ {
+ "name":"Headline",
+ "fontId":"Franklin Gothic",
+ "fontSize":40,
+ "color":"white"
+ },
+ {
+ "name":"Text",
+ "fontId":"Verdana",
+ "fontSize":12,
+ "color":"white"
+ },
+ {
+ "name":"Keyboard",
+ "fontId":"Verdana",
+ "fontSize":20,
+ "color":"black"
+ },
+ {
+ "name":"Input",
+ "fontId":"Verdana",
+ "fontSize":24,
+ "color":"white"
+ }
+],
+"elements":[
+ {
+ "info":"a prefab label class",
+ "id":"labelSmall",
+ "class":"label",
+ "size":[125,30],
+ "font":"Text"
+ }
+]
+}
\ No newline at end of file
diff --git a/exampleJson/bin/data/textures/cat1.jpg b/exampleJson/bin/data/textures/cat1.jpg
new file mode 100644
index 0000000..1302d97
Binary files /dev/null and b/exampleJson/bin/data/textures/cat1.jpg differ
diff --git a/exampleJson/src/main.cpp b/exampleJson/src/main.cpp
new file mode 100644
index 0000000..56e5041
--- /dev/null
+++ b/exampleJson/src/main.cpp
@@ -0,0 +1,13 @@
+#include "ofMain.h"
+#include "ofApp.h"
+
+//========================================================================
+int main( ){
+ ofSetupOpenGL(1280,768,OF_WINDOW); // <-------- setup the GL context
+
+ // this kicks off the running of my app
+ // can be OF_WINDOW or OF_FULLSCREEN
+ // pass in width and height too:
+ ofRunApp(new ofApp());
+
+}
diff --git a/exampleJson/src/ofApp.cpp b/exampleJson/src/ofApp.cpp
new file mode 100644
index 0000000..3ba73d5
--- /dev/null
+++ b/exampleJson/src/ofApp.cpp
@@ -0,0 +1,109 @@
+#include "ofApp.h"
+
+using namespace ofxInterface;
+
+//--------------------------------------------------------------
+void ofApp::setup(){
+
+ // create asset manager and load assets from "assets.json"
+ assets = make_shared(ofxAssetManager());
+ assets->setup();
+
+ // create a base Node
+ scene = new Node();
+ scene->setSize(ofGetWidth(), ofGetHeight());
+ ofxInterface::TouchManager::one().setup(scene);
+
+ // create the scene from scene definition
+ GuiFactory factory;
+ factory.setup(ofLoadJson("guiElements.json"), assets);
+ factory.createElements(scene, ofLoadJson("gui.json"));
+
+ // register key events from soft keyboard to input
+ // use static_cast to access special element functions
+ static_cast(scene->getChildWithName("input"))->registerKeyInput(static_cast(scene->getChildWithName("keyboard"))->keyPressed);
+
+ // you can use lambda functions to create a callback for each node
+ // touchUp, touchDown, touchMoved and clicked are callbacks for every node
+ // we add a callback for the button changing the button description
+ scene->getChildWithName("button")->setTouchDownFunction([this](ofxInterface::TouchEvent& t) {
+ auto label = static_cast(scene->getChildWithName("button")->getChildWithName("description"));
+ label->setText("clicked");
+ });
+ scene->getChildWithName("button")->setTouchUpFunction([this](ofxInterface::TouchEvent& t) {
+ auto label = static_cast(scene->getChildWithName("button")->getChildWithName("description"));
+ label->setText("a button");
+ });
+}
+
+//--------------------------------------------------------------
+void ofApp::update(){
+ scene->update(ofGetLastFrameTime());
+}
+
+//--------------------------------------------------------------
+void ofApp::draw(){
+ scene->render();
+
+ if (isDebug) {
+ scene->renderDebug();
+ }
+
+}
+
+//--------------------------------------------------------------
+void ofApp::keyPressed(int key){
+ if (key == 'd') {
+ isDebug = !isDebug;
+ }
+}
+
+//--------------------------------------------------------------
+void ofApp::keyReleased(int key){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::mouseMoved(int x, int y ){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::mouseDragged(int x, int y, int button){
+ ofxInterface::TouchManager::one().touchMove(0, ofVec2f(x, y));
+}
+
+//--------------------------------------------------------------
+void ofApp::mousePressed(int x, int y, int button){
+ ofxInterface::TouchManager::one().touchDown(0, ofVec2f(x, y));
+}
+
+//--------------------------------------------------------------
+void ofApp::mouseReleased(int x, int y, int button){
+ ofxInterface::TouchManager::one().touchUp(0, ofVec2f(x, y));
+}
+
+//--------------------------------------------------------------
+void ofApp::mouseEntered(int x, int y){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::mouseExited(int x, int y){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::windowResized(int w, int h){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::gotMessage(ofMessage msg){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::dragEvent(ofDragInfo dragInfo){
+
+}
diff --git a/exampleJson/src/ofApp.h b/exampleJson/src/ofApp.h
new file mode 100644
index 0000000..4cf1285
--- /dev/null
+++ b/exampleJson/src/ofApp.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "ofMain.h"
+#include "ofxAssetManager.h"
+#include "ofxInterface.h"
+
+class ofApp : public ofBaseApp{
+
+ public:
+ void setup();
+ void update();
+ void draw();
+
+ void keyPressed(int key);
+ void keyReleased(int key);
+ void mouseMoved(int x, int y );
+ void mouseDragged(int x, int y, int button);
+ void mousePressed(int x, int y, int button);
+ void mouseReleased(int x, int y, int button);
+ void mouseEntered(int x, int y);
+ void mouseExited(int x, int y);
+ void windowResized(int w, int h);
+ void dragEvent(ofDragInfo dragInfo);
+ void gotMessage(ofMessage msg);
+
+ shared_ptr assets;
+ ofxInterface::Node* scene;
+
+ bool isDebug = false;
+};
diff --git a/exampleJsonLiveEdit/addons.make b/exampleJsonLiveEdit/addons.make
new file mode 100644
index 0000000..333745d
--- /dev/null
+++ b/exampleJsonLiveEdit/addons.make
@@ -0,0 +1,7 @@
+ofxAnimatable
+ofxAssetManager
+ofxEasing
+ofxFontStash2
+ofxInterface
+ofxIO
+ofxPoco
diff --git a/exampleJsonLiveEdit/bin/data/.gitkeep b/exampleJsonLiveEdit/bin/data/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/exampleJsonLiveEdit/bin/data/assets.json b/exampleJsonLiveEdit/bin/data/assets.json
new file mode 100644
index 0000000..3c9216d
--- /dev/null
+++ b/exampleJsonLiveEdit/bin/data/assets.json
@@ -0,0 +1,20 @@
+{
+ "textures":{
+ "folder":"textures",
+ "info":"files [[id,path]]",
+ "files":[
+ ["cat1","cat1.jpg"],
+ ["cat2","cat2.jpg"],
+ ["cat3","cat3.jpg"]
+ ]
+ },
+ "fonts":{
+ "folder":"fonts",
+ "info":"files [[id,path]]",
+ "files":[
+ ["Verdana","verdana.ttf"],
+ ["Franklin Gothic","frabk.ttf"]
+ ]
+ }
+
+}
\ No newline at end of file
diff --git a/exampleJsonLiveEdit/bin/data/fonts/frabk.ttf b/exampleJsonLiveEdit/bin/data/fonts/frabk.ttf
new file mode 100644
index 0000000..21c4ecf
Binary files /dev/null and b/exampleJsonLiveEdit/bin/data/fonts/frabk.ttf differ
diff --git a/exampleJsonLiveEdit/bin/data/fonts/verdana.ttf b/exampleJsonLiveEdit/bin/data/fonts/verdana.ttf
new file mode 100644
index 0000000..8f25a64
Binary files /dev/null and b/exampleJsonLiveEdit/bin/data/fonts/verdana.ttf differ
diff --git a/exampleJsonLiveEdit/bin/data/gui.json b/exampleJsonLiveEdit/bin/data/gui.json
new file mode 100644
index 0000000..46745df
--- /dev/null
+++ b/exampleJsonLiveEdit/bin/data/gui.json
@@ -0,0 +1,105 @@
+{
+ "elements":[
+ {
+ "id":"background",
+ "class":"colorPanel",
+ "color":"bg",
+ "size":[1280,768]
+ },{
+ "id":"Head",
+ "class":"label",
+ "font":"Headline",
+ "position":[80,44],
+ "size":[1280,40],
+ "text":"Interactive GUI edit"
+ },{
+ "id":"text",
+ "class":"textField",
+ "size":[820,40],
+ "position":[80,100],
+ "text":[
+ ""
+ ]
+ },{
+ "id":"button",
+ "class":"modalElement",
+ "position":[80,160],
+ "size":[32,32],
+ "colorActive":"black",
+ "colorSelected":"white",
+ "colorInactive":"black",
+ "info": "sub elements can be defined",
+ "elements":[
+ {
+ "id":"description",
+ "class":"labelSmall",
+ "position":[40,10],
+ "text": "a button"
+ }
+ ]
+ },{
+ "id":"checker",
+ "class":"simpleChecker",
+ "position":[250,160],
+ "size":[32,32],
+ "colorActive":"black",
+ "colorSelected":"white",
+ "colorInactive":"black",
+ "elements":[
+ {
+ "id":"description",
+ "class":"labelSmall",
+ "position":[40,10],
+ "text": "a simpleChecker"
+ }
+ ]
+ },{
+ "id":"border",
+ "class":"colorPanel",
+ "color":"white",
+ "position":[0,210],
+ "size":[1280,2]
+ },{
+ "id":"container",
+ "class":"scrollableContainer",
+ "position":[0,220],
+ "size":[1280,548],
+ "sizeScrollableArea":[1280,1600],
+ "bgColor":"bg",
+ "elements":[
+ {
+ "id":"description2",
+ "class":"labelSmall",
+ "position":[40,10],
+ "size":[400,40],
+ "text": "a scrollable container"
+ },
+ {
+ "id":"pic1",
+ "class":"texture",
+ "position":[40,30],
+ "texture":"cat1",
+ "size":[500,500]
+
+ },
+ {
+ "id":"pic2",
+ "class":"texture",
+ "position":[40,560],
+ "texture":"cat2",
+ "size":[500,500]
+
+ },
+ {
+ "id":"pic3",
+ "class":"texture",
+ "position":[40,1090],
+ "texture":"cat3",
+ "size":[500,500],
+ "tint":"tintRed"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/exampleJsonLiveEdit/bin/data/guiElements.json b/exampleJsonLiveEdit/bin/data/guiElements.json
new file mode 100644
index 0000000..2263d9d
--- /dev/null
+++ b/exampleJsonLiveEdit/bin/data/guiElements.json
@@ -0,0 +1,34 @@
+{
+ "colors":[
+ {"id":"black","color":[0,0,0]},
+ {"id":"white","color":[255,255,255]},
+ {"id":"bg","color":[30,30,30]},
+ {"id":"tintRed","color":[250,0,0,120]},
+ {"id":"inactiveSlider","color":[31,31,31]},
+ {"id":"dropShadow","color":[0,0,0,50]},
+ {"id":"iconBg","color":[31,31,31]}
+ ],
+ "fontStyles":[
+ {
+ "name":"Headline",
+ "fontId":"Franklin Gothic",
+ "fontSize":40,
+ "color":"white"
+ },
+ {
+ "name":"Text",
+ "fontId":"Verdana",
+ "fontSize":12,
+ "color":"white"
+ }
+],
+"elements":[
+ {
+ "info":"a prefab label class",
+ "id":"labelSmall",
+ "class":"label",
+ "size":[125,30],
+ "font":"Text"
+ }
+]
+}
\ No newline at end of file
diff --git a/exampleJsonLiveEdit/bin/data/textures/cat1.jpg b/exampleJsonLiveEdit/bin/data/textures/cat1.jpg
new file mode 100644
index 0000000..1302d97
Binary files /dev/null and b/exampleJsonLiveEdit/bin/data/textures/cat1.jpg differ
diff --git a/exampleJsonLiveEdit/bin/data/textures/cat2.jpg b/exampleJsonLiveEdit/bin/data/textures/cat2.jpg
new file mode 100644
index 0000000..f548a73
Binary files /dev/null and b/exampleJsonLiveEdit/bin/data/textures/cat2.jpg differ
diff --git a/exampleJsonLiveEdit/bin/data/textures/cat3.jpg b/exampleJsonLiveEdit/bin/data/textures/cat3.jpg
new file mode 100644
index 0000000..49fcdb1
Binary files /dev/null and b/exampleJsonLiveEdit/bin/data/textures/cat3.jpg differ
diff --git a/exampleJsonLiveEdit/src/main.cpp b/exampleJsonLiveEdit/src/main.cpp
new file mode 100644
index 0000000..56e5041
--- /dev/null
+++ b/exampleJsonLiveEdit/src/main.cpp
@@ -0,0 +1,13 @@
+#include "ofMain.h"
+#include "ofApp.h"
+
+//========================================================================
+int main( ){
+ ofSetupOpenGL(1280,768,OF_WINDOW); // <-------- setup the GL context
+
+ // this kicks off the running of my app
+ // can be OF_WINDOW or OF_FULLSCREEN
+ // pass in width and height too:
+ ofRunApp(new ofApp());
+
+}
diff --git a/exampleJsonLiveEdit/src/ofApp.cpp b/exampleJsonLiveEdit/src/ofApp.cpp
new file mode 100644
index 0000000..21db340
--- /dev/null
+++ b/exampleJsonLiveEdit/src/ofApp.cpp
@@ -0,0 +1,111 @@
+#include "ofApp.h"
+
+using namespace ofxInterface;
+
+//--------------------------------------------------------------
+void ofApp::setup(){
+
+ // create asset manager and load assets from "assets.json"
+ assets = make_shared(ofxAssetManager());
+ assets->setup();
+
+ // create a base Node
+ scene = new Node();
+ scene->setSize(ofGetWidth(), ofGetHeight());
+ ofxInterface::TouchManager::one().setup(scene);
+
+ // create the scene from scene definition
+ GuiFactory factory;
+ factory.setup(ofLoadJson("guiElements.json"), assets);
+ factory.createElements(scene, ofLoadJson("gui.json"));
+
+ // add the file watcher to recreate scene when file changed
+ ofAddListener(watcher.events.onItemModified, this, &ofApp::onGuiJsonChanged);
+ watcher.addPath(ofToDataPath("", true), true, &fileFilter);
+}
+
+//--------------------------------------------------------------
+void ofApp::update(){
+ scene->update(ofGetLastFrameTime());
+}
+
+//--------------------------------------------------------------
+void ofApp::draw(){
+ scene->render();
+
+ if (isDebug) {
+ scene->renderDebug();
+ }
+
+}
+
+//--------------------------------------------------------------
+void ofApp::keyPressed(int key){
+ if (key == 'd') {
+ isDebug = !isDebug;
+ }
+}
+
+//--------------------------------------------------------------
+void ofApp::keyReleased(int key){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::mouseMoved(int x, int y ){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::mouseDragged(int x, int y, int button){
+ ofxInterface::TouchManager::one().touchMove(0, ofVec2f(x, y));
+}
+
+//--------------------------------------------------------------
+void ofApp::mousePressed(int x, int y, int button){
+ ofxInterface::TouchManager::one().touchDown(0, ofVec2f(x, y));
+}
+
+//--------------------------------------------------------------
+void ofApp::mouseReleased(int x, int y, int button){
+ ofxInterface::TouchManager::one().touchUp(0, ofVec2f(x, y));
+}
+
+//--------------------------------------------------------------
+void ofApp::mouseEntered(int x, int y){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::mouseExited(int x, int y){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::windowResized(int w, int h){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::gotMessage(ofMessage msg){
+
+}
+
+void ofApp::onGuiJsonChanged(const ofxIO::DirectoryWatcherManager::DirectoryEvent & evt)
+{
+ // delete all children from base node
+ while (scene->getChildren().size() >0)
+ {
+ scene->removeChild(0);
+ }
+
+ // create new elements from definition
+ GuiFactory factory;
+ factory.setup(ofLoadJson("guiElements.json"), assets);
+ factory.createElements(scene, ofLoadJson("gui.json"));
+}
+
+//--------------------------------------------------------------
+void ofApp::dragEvent(ofDragInfo dragInfo){
+
+}
diff --git a/exampleJsonLiveEdit/src/ofApp.h b/exampleJsonLiveEdit/src/ofApp.h
new file mode 100644
index 0000000..3ff9e99
--- /dev/null
+++ b/exampleJsonLiveEdit/src/ofApp.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "ofMain.h"
+#include "ofxAssetManager.h"
+#include "ofxInterface.h"
+#include "ofxIO.h"
+
+class ofApp : public ofBaseApp{
+
+ public:
+ void setup();
+ void update();
+ void draw();
+
+ void keyPressed(int key);
+ void keyReleased(int key);
+ void mouseMoved(int x, int y );
+ void mouseDragged(int x, int y, int button);
+ void mousePressed(int x, int y, int button);
+ void mouseReleased(int x, int y, int button);
+ void mouseEntered(int x, int y);
+ void mouseExited(int x, int y);
+ void windowResized(int w, int h);
+ void dragEvent(ofDragInfo dragInfo);
+ void gotMessage(ofMessage msg);
+
+ void onGuiJsonChanged(const ofxIO::DirectoryWatcherManager::DirectoryEvent& evt);
+
+ shared_ptr assets;
+ ofxInterface::Node* scene;
+
+ bool isDebug = false;
+
+ ofxIO::DirectoryWatcherManager watcher;
+ ofxIO::HiddenFileFilter fileFilter;
+};
diff --git a/src/AnimatableNode.cpp b/src/AnimatableNode.cpp
index dad683c..721d4c0 100644
--- a/src/AnimatableNode.cpp
+++ b/src/AnimatableNode.cpp
@@ -66,8 +66,7 @@ void AnimatableNode::update(float dt)
setPosition(nodePositionAnimation.source.getInterpolated(nodePositionAnimation.target, prog));
if (nodePositionAnimation.progress >= nodePositionAnimation.delay+nodePositionAnimation.duration) {
nodePositionAnimation.bActive = false;
- function f = nodePositionAnimation.onEnd;
- f();
+ nodePositionAnimation.onEnd();
}
}
@@ -80,8 +79,7 @@ void AnimatableNode::update(float dt)
*anim.second.color = anim.second.source.getLerped(anim.second.target, prog);
if (anim.second.progress >= anim.second.delay+anim.second.duration) {
rmcolor.push_back(anim.first);
- function f = anim.second.onEnd;
- f();
+ anim.second.onEnd();
}
}
// go over again and remove completed animations
@@ -100,8 +98,7 @@ void AnimatableNode::update(float dt)
*anim.second.value = ofMap(prog, 0, 1, anim.second.source, anim.second.target, true);
if (anim.second.progress >= anim.second.delay+anim.second.duration) {
rmfloat.push_back(anim.first);
- function f = anim.second.onEnd;
- f();
+ anim.second.onEnd();
}
}
// go over again and remove completed animations
diff --git a/src/GuiFactory.cpp b/src/GuiFactory.cpp
new file mode 100644
index 0000000..61d2631
--- /dev/null
+++ b/src/GuiFactory.cpp
@@ -0,0 +1,769 @@
+#include "GuiFactory.h"
+
+namespace ofxInterface
+{
+ GuiFactory::GuiFactory() {
+ }
+
+
+ GuiFactory::~GuiFactory() {
+ }
+
+ void GuiFactory::setup(ofJson config, shared_ptr assets_) {
+ assets = assets_;
+
+ // init colors
+ if (!config["colors"].is_null()) {
+ for (auto& color : config["colors"]) {
+ colors.insert(pair(color["id"].get(), colorFromJson(color["color"],colors)));
+
+
+ }
+ }else {
+ ofLogError("GuiFactory::setup", "no font colors defined, gui elements will use predefined ones");
+ }
+
+ // init font styles
+
+ // disable logging each font that is loaded
+ auto currentLogLevel = ofGetLogLevel();
+ ofSetLogLevel(OF_LOG_ERROR);
+
+ float fontScale = 1.5;
+ if (!config["fontScale"].is_null()) {
+ fontScale = config["fontScale"].get();
+ }
+ if (!config["fontStyles"].is_null()) {
+ for (auto& font : config["fontStyles"]) {
+ ofxFontStash2::Style style;
+ style.fontID = font["fontId"].get();
+ style.fontSize = font["fontSize"].get()*fontScale;
+ style.color = colors[font["color"].get()];
+ if (!font["lineHeight"].is_null()) style.lineHeightMult = font["lineHeight"].get();
+ if (!font["spacing"].is_null()) style.spacing = font["spacing"].get();
+
+ assets->getFonts()->addStyle(font["name"].get(), style);
+ }
+ } else {
+ ofLogError("GuiFactory::setup", "no font styles defined, gui elements using fonts won't be available" );
+ }
+ ofSetLogLevel(currentLogLevel);
+
+ // init elements
+ if (!config["elements"].is_null()) {
+ styles = config["elements"];
+ }else {
+ ofLogVerbose("GuiFactory::setup", "no elements defined");
+ }
+
+
+ // register creation functions
+ registerCreationFunction("node", [this](ofJson config, ofJson style) {return this->getNode(config, style); });
+ registerCreationFunction("canvas", [this](ofJson config, ofJson style) {return this->getNode(config, style); });
+ registerCreationFunction("label", [this](ofJson config, ofJson style) {return this->getLabel(config, style); });
+ registerCreationFunction("textField", [this](ofJson config, ofJson style) {return this->getTextField(config, style); });
+ registerCreationFunction("colorPanel", [this](ofJson config, ofJson style) {return this->getColorPanel(config, style); });
+ registerCreationFunction("scrollableContainer", [this](ofJson config, ofJson style) {return this->getScrollableContainer(config, style); });
+ registerCreationFunction("texture", [this](ofJson config, ofJson style) {return this->getTextureNode(config, style); });
+ registerCreationFunction("modalElement", [this](ofJson config, ofJson style) {return this->getModal(config, style); });
+ registerCreationFunction("simpleChecker", [this](ofJson config, ofJson style) {return this->getSimpleChecker(config, style); });
+ registerCreationFunction("softKeyboard", [this](ofJson config, ofJson style) {return this->getSoftKeyboard(config, style); });
+ registerCreationFunction("textInput", [this](ofJson config, ofJson style) {return this->getTextInput(config, style); });
+ registerCreationFunction("slider", [this](ofJson config, ofJson style) {return this->getSlider(config, style); });
+ }
+
+ ///\brief create all GUI elements from json and add them to parent node
+ void GuiFactory::createElements(Node * parent, ofJson config) {
+ for (auto& e : config["elements"]) {
+ auto elem = getElement(e);
+ if (elem != nullptr) {
+ parent->addChild(elem);
+ // create child elements
+ if (!e["elements"].is_null() && e["elements"].is_array()) {
+ createElements(elem, e);
+ }
+ }
+ }
+ }
+
+ Node * GuiFactory::getElement(ofJson config) {
+
+ // usinf "node" if no class specified
+ string className;
+ if (config["class"].is_null()) {
+ className = "node";
+ }
+ else {
+ className = config["class"].get();
+ }
+ ofJson styleConfig = getStyle(className);
+
+ if (styleConfig["id"] != "#error") {
+ string elemClass = styleConfig["class"].get();
+ ofLogVerbose() <<"create element : " << config["id"].get() << " ( " << elemClass << " -> " << styleConfig["id"].get() << " )";
+ Node* ret = nullptr;
+ if (creationTable.find(elemClass) != creationTable.end()) {
+ ret = (this->creationTable[elemClass])(config, styleConfig);
+ } else {
+ ofLogError("GuiFactory", "Can't create element \"" + config["id"].get() + "\" : no definition for class \"" + elemClass + "\" available.");
+ return nullptr;
+ }
+ return ret;
+ }
+ return nullptr;
+ }
+ Node * GuiFactory::getNode(ofJson config, ofJson style) {
+ NodeSettings s;
+ readNodeSettings(s,config,style);
+ Node* n = new Node();
+ n->setup(s);
+ return n;
+ }
+ // --------------------------------------------------------------------------------------------------------------
+ Node * GuiFactory::getLabel(ofJson config, ofJson style) {
+
+ LabelSettings s;
+ readNodeSettings(s, config, style);
+
+ s.font = assets->getFonts();
+ if (hasValue("font", config, style)) {
+ s.style = assets->getFonts()->getStyle(getValue("font", config, style));
+ }
+
+ if (hasValue("text", config, style)) {
+ s.text = getValue("text", config, style);
+ }
+
+ if (hasValue("alignment", config, style)) {
+ if (getValue("alignment", config, style) == "center") {
+ s.horzAlignment = ofAlignHorz::OF_ALIGN_HORZ_CENTER;
+ } else if (getValue("alignment", config, style) == "right") {
+ s.horzAlignment = ofAlignHorz::OF_ALIGN_HORZ_RIGHT;
+ }
+ }
+
+ if (hasValue("shadow", config, style)) {
+ s.isDropshadow = true;
+ if (hasValue("size", config["shadow"], style["shadow"])) {
+ s.shadowSize = getValue("size", config["shadow"], style["shadow"]);
+ }
+ if (hasValue("x", config["shadow"], style["shadow"])) {
+ s.shadowPos.x = getValue("x", config["shadow"], style["shadow"]);
+ }
+ if (hasValue("y", config["shadow"], style["shadow"])) {
+ s.shadowPos.y = getValue("y", config["shadow"], style["shadow"]);
+ }
+ if (hasValue("color", config["shadow"], style["shadow"])) {
+
+ s.shadowColor = colors[getValue("color", config["shadow"], style["shadow"])];
+ }
+ }
+
+ Label* label = new Label();
+ label->setup(s);
+
+ return label;
+ }
+
+ Node * GuiFactory::getTextField(ofJson config, ofJson style) {
+ TextFieldSettings s;
+ readNodeSettings(s, config, style);
+
+ s.font = assets->getFonts();
+
+ if (!config["text"].is_null()) {
+ if (config["text"].is_array()) {
+ for (auto elem : config["text"]) {
+ s.text += elem.get();
+ }
+ } else {
+ s.text = config["text"].get();
+ }
+ }
+ if (hasValue("alignment", config, style)) {
+ string al = getValue("alignment", config, style);
+ if (al == "left") {
+ s.horzAlignment = ofAlignHorz::OF_ALIGN_HORZ_LEFT;
+ } else if (al == "right") {
+ s.horzAlignment = ofAlignHorz::OF_ALIGN_HORZ_RIGHT;
+ } else if (al == "center") {
+ s.horzAlignment = ofAlignHorz::OF_ALIGN_HORZ_CENTER;
+ }
+ }
+
+
+ TextField* textField = new TextField();
+ textField->setup(s);
+ return textField;
+ }
+
+ Node * GuiFactory::getColorPanel(ofJson config, ofJson style) {
+ ColorPanelSettings s;
+ readColorpanelSettings(s, config, style);
+
+ ColorPanel* p = new ColorPanel();
+ p->setup(s);
+ return p;
+
+ }
+
+
+
+ Node * GuiFactory::getScrollableContainer(ofJson config, ofJson style) {
+ ScrollableContainerSettings s;
+ readScrollableContainerSettings(s, config, style);
+ ScrollableContainer * p = new ScrollableContainer();
+ p->setup(s);
+ return p;
+ }
+
+ Node * GuiFactory::getTextureNode(ofJson config, ofJson style) {
+ TextureNodeSettings s;
+ readTextureSettings(s,config,style);
+ TextureNode* tex = new TextureNode();
+ tex->setup(s);
+ return tex;
+ }
+
+ Node * GuiFactory::getModal(ofJson config, ofJson style) {
+ ModalElementSettings s;
+ readModalElementSettings(s, config, style);
+ ModalElement* ret = new ModalElement();
+ ret->setup(s);
+ return ret;
+ }
+
+ Node * GuiFactory::getSimpleChecker(ofJson config, ofJson style) {
+ ModalElementSettings s;
+ readModalElementSettings(s, config, style);
+
+ SimpleChecker* ret = new SimpleChecker();
+ ret->setup(s);
+ return ret;
+ }
+
+ Node * GuiFactory::getSoftKeyboard(ofJson config, ofJson style)
+ {
+ SoftKeyboardSettings settings;
+ readNodeSettings(settings, config, style);
+
+ SoftKeyboard* ret = new SoftKeyboard();
+
+ settings.font = assets->getFonts();
+ if (hasValue("font", config, style)) {
+ settings.style = assets->getFonts()->getStyle(getValue("font", config, style));
+ }
+ if (hasValue("bgColor", config, style)) {
+ settings.bgColor = colors[getValue("bgColor", config, style)];
+ }
+ if (hasValue("colorActive", config, style)) {
+ settings.colorActive = colors[getValue("colorActive", config, style)];
+ }
+ if (hasValue("colorInactive", config, style)) {
+ settings.colorInactive = colors[getValue("colorInactive", config, style)];
+ }
+ if (hasValue("colorSelected", config, style)) {
+ settings.colorSelected = colors[getValue("colorSelected", config, style)];
+ }
+
+ if (hasValue("borderRadius", config, style)) {
+ settings.borderRadius = getValue("borderRadius", config, style);
+ }
+ if (hasValue("borderWidth", config, style)) {
+ settings.borderWidth = getValue("borderWidth", config, style);
+ }
+ if (hasValue("margin", config, style)) {
+ settings.margin = getValue("margin", config, style);
+ }
+ if (hasValue("padding", config, style)) {
+ settings.padding = getValue("padding", config, style);
+ }
+ if (hasValue("layout", config, style)) {
+ settings.layout = getValue("layout", config, style);
+ }
+ if (hasValue("textureKeys", config, style)) {
+ ofJson mapping;
+ if (config["textureKeys"] != nullptr) {
+ mapping = config["textureKeys"];
+ }
+ else if (style["textureKeys"] != nullptr) {
+ mapping = style["textureKeys"];
+ }
+
+ for (auto& entry:mapping.items())
+ {
+ settings.textureKeys.insert(make_pair(ofToInt(entry.key()), assets->getTexture(entry.value().get())));
+ }
+ }
+
+ ret->setup(settings);
+ return ret;
+ }
+
+ Node * GuiFactory::getTextInput(ofJson config, ofJson style)
+ {
+ TextInputSettings settings;
+ readTextInputSettings(settings, config, style);
+
+
+ TextInput* t = new TextInput();
+ t->setup(settings);
+ return t;
+ }
+
+ Node * GuiFactory::getSlider(ofJson config, ofJson style)
+ {
+ SliderSettings settings;
+ readNodeSettings(settings, config, style);
+
+ if (hasValue("direction", config, style)) {
+ string val = ofToUpper(getValue("direction", config, style));
+ if (val == "HORIZONTAL") {
+ settings.direction = HORIZONTAL;
+ }
+ else if (val == "VERTICAL") {
+ settings.direction = VERTICAL;
+ }
+ }
+ if (hasValue("colorActive", config, style)) {
+ settings.colorActive = colors[getValue("colorActive", config, style)];
+ }
+ if (hasValue("colorInactive", config, style)) {
+ settings.colorInactive = colors[getValue("colorInactive", config, style)];
+ }
+ if (hasValue("colorSelected", config, style)) {
+ settings.colorSelected = colors[getValue("colorSelected", config, style)];
+ }
+
+ if (hasValue("lineWidth", config, style)) {
+ settings.lineWidth = getValue("lineWidth", config, style);
+ }
+
+ Slider* ret = new Slider();
+ ret->setup(settings);
+ return ret;
+ }
+
+ ofJson GuiFactory::getTemplate(string templateId)
+ {
+ for (auto& elem:styles){
+ if (elem["id"].get() == templateId) {
+ return elem;
+ }
+ }
+ return ofJson{ {"error","style not found"} };
+ }
+
+ ofJson GuiFactory::getStyle(string id) {
+ for (auto& s : styles) {
+ if (s["id"] == id) {
+ return s;
+ }
+ }
+ if (id != "canvas") {
+ ofLogError("GuiFactory::getStyle", "style <" + id + "> not found");
+ }
+
+ return ofJson({ {"id","#error"} });
+ }
+ void GuiFactory::readNodeSettings(NodeSettings & settings, ofJson config, ofJson style) {
+ readEffectSettings(settings, config, style);
+ settings.name = config["id"].get();
+ if (hasValue("position", config, style)) {
+ settings.position = vec2fFromJson(getValue("position", config, style));
+ }
+ if (hasValue("size", config, style)) {
+ settings.size = vec2fFromJson(getValue("size", config, style));
+ }
+ if (hasValue("plane", config, style)) {
+ settings.plane = getValue("plane", config, style);
+ }
+ if (hasValue("renderClip", config, style)) {
+ settings.renderClip = getValue("plane", config, style);
+ }
+ if (hasValue("active", config, style)) {
+ settings.isActive = getValue("active", config, style);
+ }
+ }
+
+ void GuiFactory::readModalElementSettings(ModalElementSettings & settings, ofJson config, ofJson style) {
+ readNodeSettings(settings, config, style);
+ if (hasValue("type", config, style)) {
+ string val = getValue("type", config, style);
+ if (val == "button") {
+ settings.type = BUTTON;
+ }else if (val == "checker") {
+ settings.type = CHECKER;
+ } else if (val == "radio") {
+ settings.type = RADIO;
+ }
+ }
+ if (hasValue("colorActive", config, style)) {
+ settings.colorActive = colors[getValue("colorActive", config, style)];
+ }
+ if (hasValue("colorInactive", config, style)) {
+ settings.colorInactive = colors[getValue("colorInactive", config, style)];
+ }
+ if (hasValue("colorSelected", config, style)) {
+ settings.colorSelected = colors[getValue("colorSelected", config, style)];
+ }
+ }
+
+ void GuiFactory::readScrollableContainerSettings(ScrollableContainerSettings & settings, ofJson config, ofJson style) {
+ readNodeSettings(settings, config, style);
+ if (hasValue("sizeScrollableArea", config, style)) {
+ settings.sizeScrollableArea = vec2fFromJson(getValue("sizeScrollableArea", config, style));
+ } else {
+ settings.sizeScrollableArea = settings.size;
+ }
+ if (hasValue("bgColor", config, style)) {
+ settings.bgColor = colors[getValue("bgColor", config, style)];
+ }
+ if (hasValue("scrollActiveColor", config, style)) {
+ settings.scrollActiveColor = colors[getValue("scrollActiveColor", config, style)];
+ }
+ if (hasValue("scrollInactiveColor", config, style)) {
+ settings.scrollInactiveColor = colors[getValue("scrollInactiveColor", config, style)];
+ }
+ }
+
+ void GuiFactory::readColorpanelSettings(ColorPanelSettings & settings, ofJson config, ofJson style) {
+ readNodeSettings(settings, config, style);
+
+ if (hasValue("strokeWidth", config, style)) {
+ settings.strokeWidth = getValue("strokeWidth", config, style);
+ }
+ if (hasValue("drawBackground", config, style)) {
+ settings.bDrawBackground = getValue("drawBackground", config, style);
+ }
+ if (hasValue("borderRadius", config, style)) {
+ settings.borderRadius = getValue("borderRadius", config, style);
+ }
+ if (hasValue("color", config, style)) {
+ if (getValueType("color", config, style) == "array") {
+ auto cs = config["color"] == nullptr ? style["color"] : config["color"];
+ settings.colors.clear();
+ for (auto& c : cs) {
+ settings.colors.push_back(colors[c.get()]);
+ }
+ }
+ else {
+ settings.colors.front() = colors[getValue("color", config, style)];
+ }
+ }
+ if (hasValue("strokeColor", config, style)) {
+ settings.strokeColor = colors[getValue("strokeColor", config, style)];
+ }
+ if (hasValue("gradientDirection", config, style)) {
+ settings.gradientDirection = getValue("gradientDirection", config, style);
+ }
+ if (hasValue("gradientColorPositions", config, style)) {
+ auto gs = config["gradientColorPositions"] == nullptr ? style["gradientColorPositions"] : config["gradientColorPositions"];
+ settings.gradientColorPositions.clear();
+ for (auto& g : gs) {
+ settings.colors.push_back(g.get());
+ }
+ }
+
+
+ vector gradientColorPositions;
+ }
+
+ void GuiFactory::readTextureSettings(TextureNodeSettings & settings, ofJson config, ofJson style)
+ {
+ readNodeSettings(settings, config, style);
+
+ if (hasValue("texture", config, style)) {
+ settings.texture = assets->getTexture(getValue("texture", config, style));
+ if (!hasValue("size", config, style)) {
+ settings.size = ofVec2f(assets->getTexture(getValue("texture", config, style)).getWidth(),
+ assets->getTexture(getValue("texture", config, style)).getHeight());
+ }
+ }
+ if (hasValue("tint", config, style)) {
+ settings.tinting = colors[getValue("tint", config, style)];
+ }
+ if (hasValue("scaleMode", config, style)) {
+ string al = getValue("scaleMode", config, style);
+ if (al == "fill") {
+ settings.scaleMode = OF_SCALEMODE_FILL;
+ }
+ else if (al == "fit") {
+ settings.scaleMode = OF_SCALEMODE_FIT;
+ }
+ else if (al == "center") {
+ settings.scaleMode = OF_SCALEMODE_CENTER;
+ }
+ else if (al == "stretch") {
+ settings.scaleMode = OF_SCALEMODE_STRETCH_TO_FILL;
+ }
+ }
+
+ if (hasValue("verticalAlign", config, style)) {
+ string al = getValue("verticalAlign", config, style);
+ if (al == "top") {
+ settings.verticalAlign = OF_ALIGN_VERT_TOP;
+ }
+ else if (al == "bottom") {
+ settings.verticalAlign = OF_ALIGN_VERT_BOTTOM;
+ }
+ else if (al == "center") {
+ settings.verticalAlign = OF_ALIGN_VERT_CENTER;
+ }
+ }
+
+ if (hasValue("horizontalAlign", config, style)) {
+ string al = getValue("horizontalAlign", config, style);
+ if (al == "left") {
+ settings.horizontalAlign = OF_ALIGN_HORZ_LEFT;
+ }
+ else if (al == "right") {
+ settings.horizontalAlign = OF_ALIGN_HORZ_RIGHT;
+ }
+ else if (al == "center") {
+ settings.horizontalAlign = OF_ALIGN_HORZ_CENTER;
+ }
+ }
+
+ if (hasValue("blendmode", config, style)) {
+ string al = getValue("blendmode", config, style);
+ if (al == "alpha") {
+ settings.blendmode = OF_BLENDMODE_ALPHA;
+ }
+ else if (al == "none") {
+ settings.blendmode = OF_BLENDMODE_DISABLED;
+ }
+ else if (al == "add") {
+ settings.blendmode = OF_BLENDMODE_ADD;
+ }
+ else if (al == "multiply") {
+ settings.blendmode = OF_BLENDMODE_MULTIPLY;
+ }
+ else if (al == "screen") {
+ settings.blendmode = OF_BLENDMODE_SCREEN;
+ }
+ else if (al == "subtract") {
+ settings.blendmode = OF_BLENDMODE_SUBTRACT;
+ }
+ }
+ }
+
+ void GuiFactory::readTextInputSettings(TextInputSettings& settings, ofJson config, ofJson style)
+ {
+ readModalElementSettings(settings, config, style);
+
+ settings.font = assets->getFonts();
+ if (hasValue("font", config, style)) {
+ settings.style = assets->getFonts()->getStyle(getValue("font", config, style));
+ }
+
+ if (hasValue("maxChars", config, style)) {
+ settings.maxChars = getValue("maxChars", config, style);
+ }
+ if (hasValue("description", config, style)) {
+ settings.descriptionText = getValue("description", config, style);
+ }
+ if (hasValue("enableNewline", config, style)) {
+ settings.enableNewline = getValue("enableNewline", config, style);
+ }
+
+ if (hasValue("alignment", config, style)) {
+ if (getValue("alignment", config, style) == "center") {
+ settings.horzAlignment = ofAlignHorz::OF_ALIGN_HORZ_CENTER;
+ }
+ else if (getValue("alignment", config, style) == "right") {
+ settings.horzAlignment = ofAlignHorz::OF_ALIGN_HORZ_RIGHT;
+ }
+ }
+
+ if (hasValue("autoResize", config, style)) {
+ settings.autoResize = getValue("autoResize", config, style);
+ }
+ }
+
+ void GuiFactory::readEffectSettings(NodeSettings& settings, ofJson config, ofJson style)
+ {
+ if (hasValue("nodeEffects", config, style)) {
+ if (hasValue("shadow", config["nodeEffects"], style["nodeEffects"])) {
+ if (hasValue("color", config["nodeEffects"]["shadow"], style["nodeEffects"]["shadow"])) {
+ settings.effects.dropShadow.color = colors[getValue("color", config["nodeEffects"]["shadow"], style["nodeEffects"]["shadow"])];
+ }
+ if (hasValue("size", config["nodeEffects"]["shadow"], style["nodeEffects"]["shadow"])) {
+ settings.effects.dropShadow.size = getValue("size", config["nodeEffects"]["shadow"], style["nodeEffects"]["shadow"]);
+ }
+ if (hasValue("position", config["nodeEffects"]["shadow"], style["nodeEffects"]["shadow"])) {
+ settings.effects.dropShadow.position = vec2fFromJson(getValue("position", config["nodeEffects"]["shadow"], style["nodeEffects"]["shadow"]));
+ }
+
+ }
+ }
+
+ }
+
+ ofColor GuiFactory::colorFromJson(ofJson val, map colors) {
+ if (val.is_string()) { // read ofParam
+ if (ofIsStringInString(val.get(), ".")) {
+ ofParameter pTemp;
+ pTemp.fromString(val);
+ return pTemp.get();
+ }
+ else {
+ ofParameter pTemp;
+ pTemp.fromString(val);
+ return pTemp.get();
+ }
+ }
+ else if (val.is_array()) { // read as rgba array
+ switch (val.size()) {
+ case 1:
+ return ofColor(val[0].get());
+ break;
+ case 2:
+ return ofColor(val[0], val[0], val[0], val[1]);
+ break;
+ case 3:
+ return ofColor(val[0], val[1], val[2]);
+ break;
+ case 4:
+ return ofColor(val[0], val[1], val[2], val[3]);
+ break;
+ default:
+ return ofColor();
+ break;
+ }
+ }
+ else if(val.is_object())
+ {
+ ofColor base;
+ if (val["base"].is_null()) {
+ ofLogError("no base Color defined");
+ }
+ else {
+ if (colors.find(val["base"].get()) != colors.end()) {
+ base = colors[val["base"].get()];
+ }
+ if (!val["r"].is_null()) {
+ base.r = val["r"].get();
+ }
+ if (!val["g"].is_null()) {
+ base.g = val["g"].get();
+ }
+ if (!val["b"].is_null()) {
+ base.b = val["b"].get();
+ }
+ if (!val["a"].is_null()) {
+ base.a = val["a"].get();
+ }
+
+ }
+
+ return base;
+ }
+ return ofColor();
+ }
+
+ ofVec2f GuiFactory::vec2fFromJson(ofJson val) {
+ switch (val.size()) {
+ case 1:
+ // interprete string
+ if (val.is_string()) {
+ ofParameter pTemp;
+ pTemp.fromString(val);
+ return pTemp.get();
+ }// interprete array
+ else
+ return ofVec2f(val[0].get());
+
+ break;
+ case 2:
+ return ofVec2f(val[0], val[1]);
+ break;
+ default:
+ return ofVec2f();
+ break;
+ }
+ }
+
+ ofVec3f GuiFactory::vec3fFromJson(ofJson val) {
+ switch (val.size()) {
+ case 1:
+ // interprete string
+ if (val.is_string()) {
+ ofParameter pTemp;
+ pTemp.fromString(val);
+ return pTemp.get();
+ }// interprete array
+ else
+ return ofVec3f(val[0].get());
+
+ break;
+ case 2:
+ return ofVec3f(val[0], val[1]);
+ break;
+ case 3:
+ return ofVec3f(val[0], val[1], val[2]);
+ break;
+ default:
+ return ofVec2f();
+ break;
+ }
+ }
+
+ void GuiFactory::addStyle(ofJson style, ofJson& styleConfig, bool overwrite)
+ {
+ vector styleList;
+ bool hasStyle = false;
+ for (auto& elem : styleConfig) {
+ if (elem["id"] == style["id"]) {
+ hasStyle = true;
+ if (overwrite) {
+ styleList.push_back(style);
+ } else {
+ styleList.push_back(elem);
+ }
+ } else {
+ styleList.push_back(elem);
+ }
+ }
+ if (!hasStyle) {
+ styleList.push_back(style);
+ }
+ styleConfig = styleList;
+ }
+
+ bool GuiFactory::hasValue(string valueName, ofJson config, ofJson style) {
+ if (!config[valueName].is_null()) {
+ return true;
+ }
+ if (!style[valueName].is_null()) {
+ return true;
+ }
+ return false;
+ }
+ string GuiFactory::getValueType(string valueName, ofJson config, ofJson style)
+ {
+ string ret = "null";
+ if (config[valueName] != nullptr) {
+ ret = config[valueName].type_name();
+ }
+ else if (style[valueName] != nullptr) {
+ ret = style[valueName].type_name();
+ }
+ return ret;
+ }
+ ///\brief register a creation function
+ ///
+ /// since it is not self-explanatory, here you have an example (using a lamda function)
+ /// registerCreationFunction("a functionname", [this](ofJson config, ofJson style) {return this->myFunction(config, style); })
+ ///
+ /// if someone finds a way to achive this easier, please edit ;)
+ void GuiFactory::registerCreationFunction(string name, creationFunction fct) {
+ // register function in table
+ creationTable[name] = fct;
+
+ // create base style for factory
+ addStyle(ofJson{ {"id",name },{"class",name} }, styles);
+ }
+
+
+}
diff --git a/src/GuiFactory.h b/src/GuiFactory.h
new file mode 100644
index 0000000..5f9f2be
--- /dev/null
+++ b/src/GuiFactory.h
@@ -0,0 +1,108 @@
+#pragma once
+#include "ofMain.h"
+#include "ofxAssetManager.h"
+
+#include "Label.h"
+#include "TextField.h"
+#include "ColorPanel.h"
+#include "ScrollableContainer.h"
+#include "TextureNode.h"
+#include "SimpleChecker.h"
+#include "SoftKeyboard.h"
+#include "TextInput.h"
+#include "Slider.h"
+
+
+///\brief defines a function for creating gui elements
+///
+/// since it looks a little overcomplicated (maybe it is) i try to explain it
+/// the GuiFactory should be subclassed and therefore you need to register creation functions
+/// for your own gui elements. Every function is set in the form of:
+/// ofxInterface::Node*(ofJson, ofJson)
+/// to register function from subclasses you need to use a std::function template
+///
+/// typedef does not work with std::function thats why we used a struct
+/// https://stackoverflow.com/questions/27002263/c11-typedef-stdfunction-and-argument-on-itself
+struct creationFunction : std::function < ofxInterface::Node*(ofJson, ofJson)> {
+ using std::function < ofxInterface::Node*(ofJson, ofJson)>::function;
+};
+
+namespace ofxInterface
+{
+ class GuiFactory
+ {
+ public:
+ GuiFactory();
+ ~GuiFactory();
+
+ virtual void setup(ofJson config, shared_ptr assets);
+
+ void createElements(Node* parent, ofJson config);
+ Node* getElement(ofJson config);
+
+ Node* getNode(ofJson config, ofJson style);
+ Node* getLabel(ofJson config, ofJson style);
+ Node* getTextField(ofJson config, ofJson style);
+ Node* getColorPanel(ofJson config, ofJson style);
+ Node* getScrollableContainer(ofJson config, ofJson style);
+ Node* getTextureNode(ofJson config, ofJson style);
+ Node* getModal(ofJson config, ofJson style);
+ Node* getSimpleChecker(ofJson config, ofJson style);
+ Node* getSoftKeyboard(ofJson config, ofJson style);
+ Node* getTextInput(ofJson config, ofJson style);
+ Node* getSlider(ofJson config, ofJson style);
+
+ ofJson getTemplate(string templateId);
+
+ // -- Helper functions -- //
+ ofJson getStyle(string id);
+
+ bool hasValue(string valueName, ofJson config, ofJson style);
+ static ofColor colorFromJson(ofJson val, map colors = map());
+ static ofVec2f vec2fFromJson(ofJson val);
+ static ofVec3f vec3fFromJson(ofJson val);
+
+ void addStyle(ofJson style, ofJson& styleConfig, bool overwrite = false);
+
+ template
+ ValueType getValue(string valueName, ofJson config, ofJson style);
+ string getValueType(string valueName, ofJson config, ofJson style);
+
+ // -- function registration -- //
+ void registerCreationFunction(string name, creationFunction fct);
+
+ // -- class functions --//
+ void readNodeSettings(NodeSettings& settings, ofJson config, ofJson style);
+ void readModalElementSettings(ModalElementSettings& settings, ofJson config, ofJson style);
+ void readScrollableContainerSettings(ScrollableContainerSettings& settings, ofJson config, ofJson style);
+ void readColorpanelSettings(ColorPanelSettings& settings, ofJson config, ofJson style);
+ void readTextureSettings(TextureNodeSettings& settings, ofJson config, ofJson style);
+ void readTextInputSettings(TextInputSettings& settings, ofJson config, ofJson style);
+
+ void readEffectSettings(NodeSettings& settings, ofJson config, ofJson style);
+
+ // -- variables -- //
+ shared_ptr assets;
+ map colors;
+ bool isDebug = false;
+
+
+
+ private:
+
+ ofJson styles;
+ std::map creationTable;
+
+ };
+
+ template
+ inline ValueType GuiFactory::getValue(string valueName, ofJson config, ofJson style) {
+ ValueType ret;
+ if (config[valueName] != nullptr) {
+ ret = config[valueName].get();
+ }else if (style[valueName] != nullptr) {
+ ret = style[valueName].get();
+ }
+ return ret;
+ }
+}
diff --git a/src/Node.cpp b/src/Node.cpp
index 1b48880..ad32414 100644
--- a/src/Node.cpp
+++ b/src/Node.cpp
@@ -20,11 +20,10 @@ ofColor Node::touchEnterNodeColor = ofColor(255, 0, 128);
static int numInterfaceNodes = 0;
-
Node::~Node()
{
numInterfaceNodes--;
- if (parent != NULL) {
+ if (parent != nullptr) {
((Node*)parent)->removeChild(this);
}
@@ -48,13 +47,15 @@ Node::Node()
bClipTouch = false;
bClipRender = false;
bReceivingTouch = true;
- data = NULL;
+ data = nullptr;
bSendDestroy = true;
bNodeAllowOneTouch = false;
bNodeUpdateWhenHidden = false;
bNodeTouched = false;
nodeCurrentTouchId = -1; // not relevant
+ data = shared_ptr(new ofJson());
+ dataIsJson = true;
#ifdef OLDSORT
sameDepthOffset = ofRandom(0, 1);
#endif
@@ -64,8 +65,13 @@ Node::Node()
#endif
#ifdef USE_OFX_HISTORY_PLOT
- historyPlot = NULL;
+ historyPlot = nullptr;
#endif
+
+ ofAddListener(eventTouchDown, this, &Node::onTouchDown);
+ ofAddListener(eventTouchMove, this, &Node::onTouchMove);
+ ofAddListener(eventTouchUp, this, &Node::onTouchUp);
+ ofAddListener(eventClick, this, &Node::onClick);
}
Node::Node(const Node& mom) : ofNode(mom)
@@ -84,6 +90,8 @@ Node::Node()
bNodeUpdateWhenHidden = mom.bNodeUpdateWhenHidden;
bNodeTouched = mom.bNodeTouched;
nodeCurrentTouchId = -1;
+ data = mom.data;
+ dataIsJson = mom.dataIsJson;
#ifdef OLDSORT
sameDepthOffset = ofRandom(0, 1);
#endif
@@ -93,21 +101,48 @@ Node::Node()
#endif
#ifdef USE_OFX_HISTORY_PLOT
- historyPlot = NULL;
+ historyPlot = nullptr;
#endif
// copy children
for (auto& c: mom.getChildren()) {
addChild(c->clone());
}
-
+ ofAddListener(eventTouchDown, this, &Node::onTouchDown);
+ ofAddListener(eventTouchMove, this, &Node::onTouchMove);
+ ofAddListener(eventTouchUp, this, &Node::onTouchUp);
}
Node* Node::clone()
{
- return new Node(*this);
-}
+ auto ret = new Node(*this);
+ /*for (auto& child : getChildren())
+ {
+ ret->addChild(child->clone());
+ }*/
+ return ret;
+}
+
+void Node::setup(NodeSettings settings) {
+ setName(settings.name);
+ setPosition(settings.position);
+ setSize(settings.size);
+ setPlane(settings.plane);
+ setRenderClip(settings.renderClip);
+ if (settings.isActive) {
+ activate();
+ }
+ else {
+ deactivate();
+ }
+
+ if (settings.effects.dropShadow.size > 0) {
+ effectDropShadow.setup(settings.effects.dropShadow);
+ }
+ effectSettings = settings.effects;
+}
+
int Node::getNumNodesAlive(){
return numInterfaceNodes;
}
@@ -119,18 +154,41 @@ Node* Node::getChildWithName(const std::string &searchName, int searchDepth) con
}
if (searchDepth==0) {
- return NULL;
+ return nullptr;
}
for (int i=0; igetChildWithName(searchName, searchDepth-1);
- if (node != NULL) {
+ if (node != nullptr) {
return node;
}
}
- return NULL;
+ return nullptr;
+}
+
+vector Node::getChildrenWithName(const std::string& searchName, int searchDepth) const
+{
+ vector ret;
+
+ if (searchName == name) {
+ ret.push_back((Node*)this);
+ }
+
+ if (searchDepth == 0) {
+ return ret;
+ }
+
+ for (int i = 0; i < childNodes.size(); i++)
+ {
+ vector retC = childNodes[i]->getChildrenWithName(searchName, searchDepth - 1);
+ if (retC.size() > 0) {
+ ret.insert(ret.end(), retC.begin(), retC.end());
+ }
+ }
+
+ return ret;
}
Node* Node::getParentWithName(const std::string &searchName, int searchDepth) const
@@ -140,11 +198,11 @@ Node* Node::getParentWithName(const std::string &searchName, int searchDepth) co
}
if (searchDepth==0) {
- return NULL;
+ return nullptr;
}
- if (parent == NULL) {
- return NULL;
+ if (parent == nullptr) {
+ return nullptr;
}
return ((Node*)parent)->getParentWithName(searchName, searchDepth-1);
@@ -167,7 +225,7 @@ void Node::drawDebug()
ofDrawBitmapString(ss.str(), 0, -3);
#ifdef USE_OFX_HISTORY_PLOT
- if (historyPlot != NULL) {
+ if (historyPlot != nullptr) {
ofFill();
historyPlot->draw(0, getHeight(), 100, 50);
}
@@ -181,6 +239,9 @@ void Node::drawBounds()
void Node::render(bool forceAll)
{
+ //not yet finally tested
+ renderDynamic(forceAll);
+ /*
std::list sortedNodes;
std::list::iterator it;
@@ -216,6 +277,7 @@ void Node::render(bool forceAll)
ofPopMatrix();
ofPopStyle();
}
+ */
}
void Node::renderDebug(bool forceAll)
@@ -250,6 +312,81 @@ void Node::renderDebug(bool forceAll)
}
}
+void Node::renderDynamic(bool forceAll)
+{
+ std::list sortedNodes;
+ std::list::iterator it;
+
+ if (forceAll) {
+ // get all nodes (visible and invisible)
+ getSubTreeList(sortedNodes);
+ }
+ else {
+ // get only visible nodes
+ getVisibleSubTreeList(sortedNodes);
+ }
+
+ // sort scene by z (+z goes outside of the screen), plane is z
+ sortedNodes.sort(Node::bottomPlaneFirst);
+
+ for (it = sortedNodes.begin(); it != sortedNodes.end(); it++)
+ {
+ ofPushStyle();
+ ofPushMatrix();
+ ofMultMatrix((*it)->getGlobalTransformMatrix());
+ // use anchor
+
+ if ((*it)->getGlobalRenderClip()) {
+ (*it)->enableScissor((*it)->getRenderClipRect());
+ }
+ if ((*it)->getRenderChildrenInGroup()) {
+ (*it)->renderGroups();
+ }
+ else {
+
+ (*it)->drawDropShadow();
+ (*it)->draw();
+ }
+
+ if ((*it)->getGlobalRenderClip()) {
+ (*it)->disableScissor();
+ }
+
+ ofPopMatrix();
+ ofPopStyle();
+ }
+}
+
+void Node::renderGroups(bool forceAll)
+{
+ for (auto& child : childNodes) {
+ if (forceAll || child->getVisible()) {
+
+ //if (child->getName() != "") cout << child->getName() << " predraw " <preDraw();
+ //ofPushStyle();
+ ofPushMatrix();
+ ofMultMatrix(child->getGlobalTransformMatrix());
+ // use anchor
+
+ if (child->getGlobalRenderClip()) {
+ child->enableScissor(child->getRenderClipRect());
+ }
+ //if (child->getName() != "") cout << child->getName() << " draw " << endl;
+ child->draw();
+ if (child->getGlobalRenderClip()) {
+ child->disableScissor();
+ }
+
+ ofPopMatrix();
+ //ofPopStyle();
+ child->renderGroups();
+ //if (child->getName() != "") cout << child->getName() << " postDraw" << endl;
+ child->postDraw();
+ }
+ }
+}
+
void Node::updateSubtree(float dt, bool forceAll)
{
update(dt);
@@ -290,7 +427,7 @@ void Node::updateSubtreePostOrder(float dt, bool forceAll)
bool Node::getGlobalRenderClip()
{
- if (getParent() != NULL) {
+ if (getParent() != nullptr) {
return bClipRender || ((Node*)getParent())->getGlobalRenderClip();
}
else {
@@ -301,12 +438,13 @@ bool Node::getGlobalRenderClip()
ofRectangle Node::getRenderClipRect()
{
ofRectangle parentRect;
- if (getParent() != NULL) {
+ if (getParent() != nullptr) {
parentRect = ((Node*)getParent())->getRenderClipRect();
}
ofRectangle rect;
if (bClipRender) {
ofVec2f pos = getGlobalPosition();
+
rect = ofRectangle(pos.x, pos.y, getGlobalWidth(), getGlobalHeight());
}
@@ -333,7 +471,11 @@ void Node::enableScissor(float x, float y, float w, float h)
ofVec2f local = toLocal(ofVec2f(x, y));
ofVec2f localScale = ofVec2f(w, h)*getGlobalScale();
glEnable(GL_SCISSOR_TEST);
- glScissor(local.x, local.y, localScale.x, localScale.y);
+ //glScissor(local.x, local.y, localScale.x, localScale.y);
+ // openGL uses an inverted y - axis, so we need to invert the y value
+ float dy = ofGetWindowHeight()-h -y;
+
+ glScissor(x, dy, localScale.x, localScale.y);
}
void Node::disableScissor()
@@ -343,7 +485,7 @@ void Node::disableScissor()
void Node::setCenteredH()
{
- if (parent == NULL) {
+ if (parent == nullptr) {
return;
}
@@ -352,7 +494,7 @@ void Node::setCenteredH()
void Node::setCenteredV()
{
- if (parent == NULL) {
+ if (parent == nullptr) {
return;
}
@@ -361,7 +503,7 @@ void Node::setCenteredV()
void Node::setCentered()
{
- if (parent == NULL) {
+ if (parent == nullptr) {
return;
}
@@ -369,6 +511,62 @@ void Node::setCentered()
(((Node*)parent)->getLocalHeight() - getLocalHeight())/2);
}
+void Node::drawDropShadow()
+{
+ if (effectSettings.dropShadow.size > 0) {
+ ofRectangle drawingRect = ofRectangle(
+ -effectSettings.dropShadow.size + effectSettings.dropShadow.position.x,
+ -effectSettings.dropShadow.size + effectSettings.dropShadow.position.y,
+ getWidth() + effectSettings.dropShadow.size * 2,
+ getHeight() + effectSettings.dropShadow.size * 2
+ );
+
+ if (!fboDropShadow.isAllocated()) {
+ fboDropShadow.allocate(
+ getWidth() ,//+ effectSettings.dropShadow.size * 2,
+ getHeight() //+ effectSettings.dropShadow.size * 2
+ );
+
+ fboDropShadow.begin();
+ ofClear(0, 0);
+
+ draw();
+ fboDropShadow.end();
+ effectDropShadow.update(fboDropShadow);
+
+ }
+
+ //ofEnableBlendMode(ofBlendMode::OF_BLENDMODE_MULTIPLY);
+ effectDropShadow.update(fboDropShadow);
+ effectDropShadow.getShadow().draw(drawingRect);
+ ofEnableAlphaBlending();
+ }
+}
+
+void Node::onTouchDown(TouchEvent & event) {
+ if (touchDownFunc) {
+ touchDownFunc(event);
+ }
+}
+
+void Node::onTouchMove(TouchEvent & event) {
+ if (touchMoveFunc) {
+ touchMoveFunc(event);
+ }
+}
+
+void Node::onTouchUp(TouchEvent & event) {
+ if (touchUpFunc) {
+ touchUpFunc(event);
+ }
+}
+
+void Node::onClick(TouchEvent & event) {
+ if (clickFunc) {
+ clickFunc(event);
+ }
+}
+
void Node::touchDown(int id, TouchEvent* event)
{
if (bNodeAllowOneTouch && bNodeTouched && nodeCurrentTouchId!=id) {
@@ -398,6 +596,10 @@ void Node::touchMove(int id, TouchEvent* event)
if (bNodeAllowOneTouch && id != nodeCurrentTouchId) {
return;
}
+
+ if (event->velocitySmoothed.length() > 200) {
+ isClickAllowed = false;
+ }
#ifdef OFXUINODE_DEBUG
debugBorderColor.a -= 10;
@@ -419,16 +621,17 @@ void Node::touchUp(int id, TouchEvent* event)
#ifdef USE_OFX_HISTORY_PLOT
if (historyPlot) {
delete historyPlot;
- historyPlot = NULL;
+ historyPlot = nullptr;
}
#endif
ofNotifyEvent(eventTouchUp, *event);
- if (contains(event->position)) {
+ if (contains(event->position) && isClickAllowed) {
ofNotifyEvent(eventClick, *event);
}
bNodeTouched = false;
+ isClickAllowed = true;
}
void Node::touchExit(int id, TouchEvent *event)
@@ -455,12 +658,40 @@ void Node::touchEnter(int id, TouchEvent *event)
ofNotifyEvent(eventTouchEnter, *event);
}
+const ofJson Node::toJson(glm::vec3 val)
+{
+ ofJson ret;
+ ret.push_back(val.x);
+ ret.push_back(val.y);
+ ret.push_back(val.z);
+ return ret;
+}
+
+const ofJson Node::toJson(glm::vec2 val)
+{
+ ofJson ret;
+ ret.push_back(val.x);
+ ret.push_back(val.y);
+ return ret;
+}
+
+const ofJson Node::toJson(glm::quat val)
+{
+ ofJson ret;
+ ret.push_back(val.w);
+ ret.push_back(val.x);
+ ret.push_back(val.y);
+ ret.push_back(val.z);
+ return ofJson();
+}
+
ofVec3f Node::toLocal(const ofVec3f &screenPoint)
{
#ifdef GLM_SWIZZLE //this version of OF is using GLM for math ops
return (ofVec3f)screenPoint * glm::inverse(ofNode::getGlobalTransformMatrix());
#else
- return (ofVec3f)screenPoint*ofNode::getGlobalTransformMatrix().getInverse();
+ //return (ofVec3f)screenPoint*ofNode::getGlobalTransformMatrix().getInverse();
+ return (ofVec3f)screenPoint * glm::inverse(ofNode::getGlobalTransformMatrix());
#endif
}
@@ -502,7 +733,7 @@ void Node::setVisible(bool visible)
bool Node::getVisibleGlobally() const
{
- if (parent == NULL) {
+ if (parent == nullptr) {
return bVisible;
}
else {
@@ -543,7 +774,7 @@ void Node::setEnabled(bool enable)
bool Node::getEnabledGlobally() const
{
- if (parent == NULL) {
+ if (parent == nullptr) {
return bEnabled;
}
else {
@@ -565,6 +796,22 @@ bool Node::contains(const ofVec3f &globalPoint)
return true;
}
+void Node::setTouchDownFunction(std::function _func) {
+ touchDownFunc = _func;
+}
+
+void Node::setTouchMoveFunction(std::function _func) {
+ touchMoveFunc = _func;
+}
+
+void Node::setTouchUpFunction(std::function _func) {
+ touchUpFunc = _func;
+}
+
+void Node::setClickFunction(std::function _func) {
+ clickFunc = _func;
+}
+
void Node::addChild(Node *child, int insertAt, bool bMaintainChildGlobalTransform)
{
child->setParent(*this, bMaintainChildGlobalTransform);
@@ -590,14 +837,14 @@ Node* Node::removeChild(Node *child, bool bMaintainChildGlobalTransform)
}
ofLogWarning("ofxInterface::Node::removeChild", "are you trying to remove a child that does not exist?");
- return NULL;
+ return nullptr;
}
Node* Node::removeChild(int index, bool bMaintainChildGlobalTransform)
{
if (index >= childNodes.size()) {
ofLogWarning("ofxInterface::Node::removeChild", "are you trying to remove a child that does not exist?");
- return NULL;
+ return nullptr;
}
Node *child = childNodes[index];
@@ -617,17 +864,20 @@ void Node::sortChildren(const function& comp
std::sort(childNodes.begin(), childNodes.end(), compareFunction);
}
-void Node::getSubTreeList(std::list& list)
+void Node::getSubTreeList(std::list& list, bool returnAllGroupElements)
{
list.push_back(this);
- for (int i=0; igetSubTreeList(list);
+ if (!bRenderChildrenInGroup) {
+ for (int i = 0; i < childNodes.size(); i++)
+ {
+ childNodes[i]->getSubTreeList(list);
+ }
}
+
}
-void Node::getVisibleSubTreeList(std::list& list)
+void Node::getVisibleSubTreeList(std::list& list, bool returnAllGroupElements)
{
if (!getVisible()) {
return;
@@ -635,8 +885,10 @@ void Node::getVisibleSubTreeList(std::list& list)
list.push_back(this);
- for (int i=0; igetVisibleSubTreeList(list);
+ if (!bRenderChildrenInGroup) {
+ for (int i = 0; i < childNodes.size(); i++) {
+ childNodes[i]->getVisibleSubTreeList(list);
+ }
}
}
@@ -683,7 +935,7 @@ void Node::placeNextTo(Node &comp, Node::Side side, float margin)
}
}
-string Node::print(int depth) const
+string Node::printItem(int depth) const
{
stringstream ss;
@@ -693,7 +945,7 @@ string Node::print(int depth) const
ss << depth<<"- "<print(depth+1);
+ ss << node->printItem(depth+1);
}
return ss.str();
@@ -710,5 +962,117 @@ float Node::getAngleTo(ofxInterface::Node *node)
return atan2(translate.y, translate.x)*180/PI;
}
+ofJson Node::getSceneDescription()
+{
+ ofJson ret;
+ ret = getNodeJson();
+ ofJson jChild;
+ for (auto& child:getChildren()){
+ jChild.push_back(child->getSceneDescription());
+ }
+ ret["children"] = jChild;
+ return ret;
+}
+
+ofJson Node::getSceneDescription(vector attributes,bool onlyActiveNodes)
+{
+ return filterSceneDescription(getSceneDescription(), attributes , onlyActiveNodes);
+}
+
+ofJson Node::getNodeJson()
+{
+ ofJson ret;
+ ret["nodeType"] = "Node";
+ ret["name"] = getName();
+ ret["size"] = toJson(getSize());
+ ret["position"] = toJson(getPosition());
+ ret["orientation"] = toJson(getOrientationQuat());
+ ret["scale"] = toJson(getScale());
+ ret["isVisible"] = getVisible();
+ ret["isEnabled"] = getEnabled();
+ ret["isReceivingTouch"] = getReceivingTouch();
+ ret["plane"] = getPlane();
+ if (dataIsJson) {
+ auto t = static_pointer_cast(data);
+ ret["customData"] = *t;
+ }
+ if (childNodes.size() > 0) {
+ ret["children"] = ofJson::array();
+ for (auto& child : childNodes) {
+ ret["children"].push_back(child->getNodeJson());
+ }
+ }
+
+ return ret;
+}
+
+ofJson Node::getNodeJsonFiltered(set keys)
+{
+ return filterJsonDescription(getNodeJson(),keys);
+}
+
+
+string Node::listActiveNodes(int depth)
+{
+ string ret;
+ if (getEnabled()) {
+ for (size_t i = 0; i < depth; i++){
+ ret += " ";
+ }
+ ret += name + "\n";
+ for (auto n : getChildren()) {
+ ret += n->listActiveNodes(depth+1);
+ }
+ }
+ return ret;
+}
+
+EffectSettings Node::getEffectSettings()
+{
+ return effectSettings;
+}
+
+ofJson Node::filterSceneDescription(ofJson desc, vector attributes, bool onlyActiveNodes)
+{
+ ofJson ret;
+ if (onlyActiveNodes == false || (onlyActiveNodes == true && getEnabled()) ) {
+ for (auto& attr : attributes) {
+ if (desc[attr] != nullptr) ret[attr] = desc[attr];
+ }
+
+ if (desc["children"] != nullptr && desc["children"].size() > 0) {
+ for (auto& child : desc["children"]) {
+ ret["children"].push_back(filterSceneDescription(child, attributes, onlyActiveNodes));
+ }
+ }
+ }
+ return ret;
+}
+
+ofJson Node::filterJsonDescription(ofJson desc, set keys)
+{
+ ofJson ret = ofJson();
+ bool hasChild = false;
+ for (ofJson::iterator it = desc.begin(); it != desc.end(); ++it) {
+ if (keys.find(it.key()) != keys.end()) {
+ if (it.key() == "children") {
+ hasChild = true;
+ }
+ else {
+ ret[it.key()] = it.value();
+ }
+ }
+ }
+
+ if (hasChild) {
+ ret["children"] = ofJson::array();
+ for (auto& child : desc["children"]) {
+ ret["children"].push_back(filterJsonDescription(child, keys));
+ }
+ }
+
+ return ret;
+}
+
} // namespace
diff --git a/src/Node.h b/src/Node.h
index 069b81c..9bb19c6 100644
--- a/src/Node.h
+++ b/src/Node.h
@@ -12,6 +12,7 @@
#include
#include "ofMain.h"
#include "TouchEvent.h"
+#include "EffectDropShadow.h"
// you can comment this to save a little (very little)
#define OFXUINODE_DEBUG
@@ -22,6 +23,21 @@
namespace ofxInterface
{
+
+
+ struct EffectSettings {
+ EffectDropShadowSettings dropShadow;
+ };
+
+ struct NodeSettings {
+ string name;
+ ofVec2f position;
+ ofVec2f size;
+ bool renderClip = false;
+ bool isActive = true;
+ float plane = 10;
+ EffectSettings effects;
+};
class Node : public ofNode
{
@@ -34,6 +50,8 @@ class Node : public ofNode
Node(const Node& mom);
virtual Node* clone();
+ void setup(NodeSettings settings);
+
/******
* Node Names:
*
@@ -43,6 +61,7 @@ class Node : public ofNode
const std::string& getName() const { return name; }
// search the tree for a node with a specific name, searchDepth of -1 means search all the way down
Node* getChildWithName(const std::string& searchName, int searchDepth = -1) const;
+ vector getChildrenWithName(const std::string& searchName, int searchDepth = -1) const;
Node* getParentWithName(const std::string& searchName, int searchDepth = -1) const;
/******
@@ -65,8 +84,10 @@ class Node : public ofNode
// functions to override
//
- virtual void update(float dt) {} // please override with update code
- virtual void draw() {}; // please override! draw your object in local space
+ virtual void update(float dt) {}; // please override with update code
+ virtual void draw() {} ; // please override! draw your object in local space
+ virtual void preDraw() {}; // affects group rendering, executed before the draw function
+ virtual void postDraw() {}; // affects group rendering, executed after the draw function
// for debugging
virtual void drawDebug(); // debug debugging stuff (will be called by renderDebug)
@@ -89,6 +110,13 @@ class Node : public ofNode
ofEvent eventTouchEnter;
ofEvent eventClick;
+ // interaction functions (register lambda functions here)
+ void setTouchDownFunction(std::function _func);
+ void setTouchMoveFunction(std::function _func);
+ void setTouchUpFunction(std::function _func);
+ void setClickFunction(std::function _func);
+
+
// use this to enforce only one touch at a time (single touch)
void setAllowOnlyOneTouch(bool set) { bNodeAllowOneTouch = set; }
bool isTouched() { return bNodeTouched; }
@@ -107,6 +135,10 @@ class Node : public ofNode
*/
void render(bool forceAll = false);
void renderDebug(bool forceAll = false); // same as render but calls drawDebug instead of draw.
+
+ //TODO : description
+ void renderDynamic(bool forceAll = false);
+ void renderGroups(bool forceAll = false);
/******
* call the 'update' function of !!visible!! children
@@ -125,6 +157,10 @@ class Node : public ofNode
bool getRenderClip() { return bClipRender; }
bool getGlobalRenderClip();
ofRectangle getRenderClipRect();
+ // if group is activated then all children are rendered afterwards and using the plane of this node (enables group masking)
+ void setRenderChildrenInGroup(bool set) {bRenderChildrenInGroup = set;};
+ bool getRenderChildrenInGroup() { return bRenderChildrenInGroup; };
+
// override these functions if you use some other renderer such as NanoVG
virtual void enableScissor(const ofRectangle& rect) { enableScissor(rect.x, rect.y, rect.width, rect.height); }
virtual void enableScissor(float x, float y, float w, float h);
@@ -251,9 +287,9 @@ class Node : public ofNode
}
}
// returns a list representation of all childNodes in sub graph
- void getSubTreeList(std::list& list);
+ void getSubTreeList(std::list& list, bool returnAllGroupElements = true);
// returns a list of visible elements in subtree
- void getVisibleSubTreeList(std::list& list);
+ void getVisibleSubTreeList(std::list& list, bool returnAllGroupElements = true);
// returns a list of enabled elements in subtree
void getEnabledSubTreeList(std::list& list);
@@ -339,7 +375,8 @@ class Node : public ofNode
// use this to hold any data
- void setData(shared_ptr _data) { data = _data; }
+ template
+ void setData(shared_ptr _data);
shared_ptr getData() { return data; }
//check for leaks
@@ -357,7 +394,7 @@ class Node : public ofNode
ofEvent eventDestroy; // send this event in the destructor
- string print(int depth=0) const;
+ string printItem(int depth=0) const;
template
void addEventListener(const string& eventName, ListenerClass * listener, void (ListenerClass::*listenerMethod)(ArgType&), bool recursive=false)
@@ -408,8 +445,26 @@ class Node : public ofNode
ofVec3f getTranslationTo(Node* node);
float getAngleTo(Node* node);
+ /******
+ * Exporting
+ *
+ * export the node as json
+ *
+ */
+
+ ofJson getSceneDescription();
+ ofJson getSceneDescription(vector attributes, bool onlyActiveNodes = false);
+ virtual ofJson getNodeJson();
+ virtual ofJson getNodeJsonFiltered(set keys);
+ string listActiveNodes(int depth = 0);
+
+ // effect settings
+ EffectSettings getEffectSettings();
protected:
+ ofJson filterSceneDescription(ofJson desc, vector attributes,bool onlyActiveNodes);
+ ofJson filterJsonDescription(ofJson desc, set keys);
+
std::string name;
ofVec2f size;
@@ -418,6 +473,7 @@ class Node : public ofNode
bool bReceivingTouch;
bool bClipRender;
bool bClipTouch;
+ bool bRenderChildrenInGroup = false;
vector childNodes;
@@ -427,6 +483,7 @@ class Node : public ofNode
// hold custom data
shared_ptr data;
+ bool dataIsJson = false;
// debug stuff
@@ -443,6 +500,16 @@ class Node : public ofNode
static ofColor touchEnterNodeColor;
#endif
+
+
+ // effects
+ EffectSettings effectSettings;
+
+ // drop shadow
+ ofFbo fboDropShadow;
+ EffectDropShadow effectDropShadow;
+ void drawDropShadow();
+
private:
template
ofEvent* getEventForName(const string& eventName)
@@ -461,12 +528,27 @@ class Node : public ofNode
}
}
+ // lambda functions here
+ std::function touchDownFunc;
+ std::function touchMoveFunc;
+ std::function touchUpFunc;
+ std::function clickFunc;
+
+ void onTouchDown(TouchEvent& event);
+ void onTouchMove(TouchEvent& event);
+ void onTouchUp(TouchEvent& event);
+ void onClick(TouchEvent& event);
+
bool bSendDestroy;
bool bNodeAllowOneTouch;
bool bNodeUpdateWhenHidden;
bool bNodeTouched;
int nodeCurrentTouchId;
+ bool isClickAllowed = true;
+
+
+
#ifdef OLD_SORT
/******
* sameDepthOffset:
@@ -485,8 +567,27 @@ class Node : public ofNode
void touchUp(int id, TouchEvent* event);
void touchExit(int id, TouchEvent* event);
void touchEnter(int id, TouchEvent* event);
+
+
+ //helper functions for export json
+ const ofJson toJson(glm::vec3 val);
+ const ofJson toJson(glm::vec2 val);
+ const ofJson toJson(glm::quat val);
};
+template
+inline void Node::setData(shared_ptr _data)
+{
+ if (ofIsStringInString(typeid(_data).name(), "class std::shared_ptr{ style.color.r,style.color.g,style.color.b,style.color.a };
+ ret["fontId"] = style.fontID;
+ ret["fontSize"] = style.fontSize;
+ ret["lineHeightMult"] = style.lineHeightMult;
+ ret["spacing"] = style.spacing;
+ return ret;
+ }
+ ofJson compositionToLog(ofJson sceneDescription, ofJson metaData)
+ {
+ ofJson ret = ofJson();
+ if (!metaData["background"].is_null()) {
+ auto path = ofSplitString(metaData["background"].get(), "../").back();
+ ret["background"] = path;
+ }
+ if (!metaData["overlay"].is_null()) {
+ auto path = ofSplitString(metaData["overlay"].get(), "../").back();
+ ret["overlay"] = path;
+ }
+ if (!metaData["isPrint"].is_null()) {
+ ret["isPrint"] = metaData["isPrint"].get();
+ }
+ if (!metaData["sharing"].is_null()) {
+ ret["sharing"] = metaData["sharing"].get();
+ }
+ if (!metaData["itemId"].is_null()) {
+ ret["itemId"] = metaData["itemId"].get();
+ }
+ if (!metaData["furtherUse"].is_null()) {
+ ret["furtherUse"] = metaData["furtherUse"].get();
+ }
+
+ ofJson icons = ofJson();
+ ofJson texts = ofJson();
+
+ for (auto& objects : sceneDescription["children"][0]["children"]) {
+ if (objects["name"] == "objects" && !objects["children"].is_null()) {
+ for (auto& elem : objects["children"]) {
+ if (elem["nodeType"] == "TextureNode") {
+ auto path = ofSplitString(elem["customData"]["file"].get(), "../").back();
+ icons.push_back(path);
+ }
+ else if (elem["nodeType"] == "TextInputOfTTF") {
+ ofJson text = ofJson();
+ text["fontId"] = ofSplitString(elem["style"].get(), "/").back();
+ text["text"] = elem["text"];
+ texts.push_back(text);
+ }
+
+ }
+ }
+ }
+ if (!icons.is_null())
+ {
+ ret["icons"] = icons;
+ }
+ if (!texts.is_null()) {
+ ret["texts"] = texts;
+ }
+ return ret;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/NodeToJsonHelper.h b/src/NodeToJsonHelper.h
new file mode 100644
index 0000000..a6e3ae0
--- /dev/null
+++ b/src/NodeToJsonHelper.h
@@ -0,0 +1,11 @@
+#pragma once
+#include "ofMain.h"
+#include "ofxFontStash2.h"
+
+namespace reddo{
+ namespace NodeToJson {
+ ofJson styleToJson(ofxFontStash2::Style style);
+ ofJson compositionToLog(ofJson sceneDescription, ofJson metaData);
+ }
+}
+
diff --git a/src/components/BitmapTextButton.cpp b/src/components/BitmapTextButton.cpp
deleted file mode 100644
index 8b726ee..0000000
--- a/src/components/BitmapTextButton.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-//
-// BitmapTextButton.cpp
-// ofxInterface
-//
-// Created by Gal Sasson on 1/19/15.
-//
-//
-
-#include "BitmapTextButton.h"
-
-namespace ofxInterface
-{
-
-void BitmapTextButton::setup(const string &_label)
-{
- label = _label;
- setSize(label.length()*8 + 10, 20);
- setName(label);
-
- bDrawBorder = true;
- bDrawBackground = true;
-
- borderColor = ofColor(0);
- bgColor = ofColor(255);
- labelColor = ofColor(0);
-}
-
-void BitmapTextButton::draw()
-{
- if (bDrawBackground) {
- ofSetColor(bgColor * (isTouched()?0.5:1));
- ofFill();
- ofDrawRectangle(0, 0, getWidth(), getHeight());
- }
-
- ofSetColor(labelColor);
- ofDrawBitmapString(label, 5, getHeight()-5);
-
- if (bDrawBorder) {
- ofSetColor(borderColor);
- ofNoFill();
- ofSetLineWidth(1);
- ofDrawRectangle(0, 0, getWidth(), getHeight());
- }
-}
-
-
-} // namespace
diff --git a/src/components/BitmapTextButton.h b/src/components/BitmapTextButton.h
deleted file mode 100644
index 7b15cec..0000000
--- a/src/components/BitmapTextButton.h
+++ /dev/null
@@ -1,49 +0,0 @@
-//
-// BitmapTextButton.h
-// AnimationBreaker
-//
-// Created by Gal Sasson on 1/19/15.
-//
-//
-
-#ifndef __ofxInterface__BitmapTextButton__
-#define __ofxInterface__BitmapTextButton__
-
-#include
-#include "ofMain.h"
-
-#include "ofxInterface.h"
-
-namespace ofxInterface
-{
-
-class BitmapTextButton : public Node
-{
-public:
-
- void setup(const string& label);
-
- void setBackground(bool set) { bDrawBackground = set; }
- void setBorder(bool set) { bDrawBorder = set; }
-
- void setLabelColor(const ofColor& c) { labelColor = c; }
- void setBGColor(const ofColor& c) { bgColor = c; }
- void setBorderColor(const ofColor& c) { borderColor = c; }
-
- void draw();
-
-private:
-
- bool bDrawBackground;
- bool bDrawBorder;
-
- ofColor borderColor;
- ofColor bgColor;
- ofColor labelColor;
-
- string label;
-};
-
-} // namespace
-
-#endif /* defined(__ofxUINode__BitmapTextButton__) */
diff --git a/src/components/ColorPanel.cpp b/src/components/ColorPanel.cpp
new file mode 100644
index 0000000..47c6fa4
--- /dev/null
+++ b/src/components/ColorPanel.cpp
@@ -0,0 +1,221 @@
+#include "ColorPanel.h"
+
+namespace ofxInterface {
+
+
+void ColorPanel::setup(ofColor color, float w, float h)
+{
+ ColorPanelSettings s;
+ s.size = ofVec2f(w, h);
+ s.colors.front() = color;
+ s.bDrawBackground = true;
+ setup(s);
+}
+
+void ColorPanel::setup(ColorPanelSettings settings) {
+ Node::setup(settings);
+ borderWidth = settings.strokeWidth;
+ bDrawBackground = settings.bDrawBackground;
+ borderRadius = settings.borderRadius;
+ strokeColor = settings.strokeColor;
+
+ if (settings.colors.size() > 0) {
+ colors.clear();
+ }
+ for (size_t i = 0; i < settings.colors.size();++i) {
+ colors.push_back(settings.colors[i]);
+ if (i < settings.gradientColorPositions.size()) {
+ gradientColorPositions.push_back(settings.gradientColorPositions[i]);
+ }
+ else {
+ if (i == 0) {
+ gradientColorPositions.push_back(0.0);
+ }
+ else if (i == 1) {
+ gradientColorPositions.push_back(1.0);
+ }
+
+ }
+ }
+ gradientDirection = settings.gradientDirection;
+
+ shader.push_back(getGradientShader2Colors());
+ shader.push_back(getGradientShader3Colors());
+
+
+ initShape();
+
+ ofAddListener(eventNodeSizeChanged,this, &ColorPanel::onNodeSizeChanged);
+
+}
+
+Node* ColorPanel::clone()
+{
+ auto ret = new ColorPanel(*this);
+ return ret;
+}
+
+ofColor ColorPanel::getColor() {
+ return colors.front();
+}
+
+ofColor ColorPanel::getStrokeColor() {
+ return strokeColor;
+}
+
+void ColorPanel::setStrokeColor(const ofColor & c) {
+ strokeColor = c;
+}
+
+bool ColorPanel::isDrawBackground() {
+ return bDrawBackground;
+}
+
+void ColorPanel::setDrawBackground(bool set) {
+ bDrawBackground = set;
+}
+
+float ColorPanel::getBorderWidth() {
+ return borderWidth;
+}
+
+void ColorPanel::setBorderWidth(float set) {
+ borderWidth = set;
+}
+
+float ColorPanel::getRoundedEdge() {
+ return borderRadius;
+}
+
+void ColorPanel::setRoundedEdge(float ang) {
+ borderRadius = ang;
+}
+
+void ColorPanel::onNodeSizeChanged(Node& n)
+{
+ initShape();
+}
+
+void ColorPanel::initShape()
+{
+ shape.clear();
+ outline.clear();
+ shape.setFilled(colors.front().a > 0);
+ outline.setStrokeWidth(borderWidth);
+ shape.setFillColor(colors.front());
+ outline.setStrokeColor(strokeColor);
+ shape.setUseShapeColor(true);
+ outline.setUseShapeColor(true);
+
+ if (borderRadius > 0) {
+ shape.rectRounded(borderWidth, borderWidth, getWidth() - getWidth() * borderWidth, getHeight() - 2 * borderWidth, borderRadius);
+ outline.rectRounded(0, 0, getWidth(), getHeight(), borderRadius);
+ outline.rectRounded(borderWidth, borderWidth, getWidth() - getWidth() * borderWidth, getHeight() - 2 * borderWidth, borderRadius);
+ }
+ else {
+ shape.rectangle(borderWidth, borderWidth, getWidth() - 2 * borderWidth, getHeight() - 2 * borderWidth);
+ outline.rectangle(0, 0, getWidth(), getHeight());
+ outline.rectangle(borderWidth, borderWidth, getWidth() - 2 * borderWidth, getHeight() - 2 * borderWidth);
+ }
+
+ // shader
+ if (colors.size() > 1) {
+ fbo.allocate(getWidth(), getHeight());
+
+ ofEnableBlendMode(OF_BLENDMODE_DISABLED);
+ fbo.begin();
+
+ ofClear(0, 0);
+ int nShader = 0;
+ if (colors.size() == 3) {
+ nShader = 1;
+ }
+ shader[nShader].begin();
+
+
+
+ ofVec2f pEnd = angleToBorderPoint(gradientDirection);
+ ofVec2f pStart = angleToBorderPoint(fmod(gradientDirection+180,360));
+ ofVec2f dir = pEnd - pStart;
+ pStart += dir * gradientColorPositions.front();
+ pEnd = pStart + dir * gradientColorPositions.back();
+
+
+
+
+ shader[nShader].setUniform2f("begin", pStart);
+ shader[nShader].setUniform2f("end", pEnd);
+
+
+
+ shader[nShader].setUniform2f("size", getSize());
+ shader[nShader].setUniform4f("color1", colorToVec4(colors[0]));
+ shader[nShader].setUniform4f("color2", colorToVec4(colors[1]));
+ if (nShader == 2) {
+ shader[nShader].setUniform1f("mid", 0.66f);
+ shader[nShader].setUniform4f("color3", colorToVec4(colors[2]));
+ }
+
+
+ /*if (roundAngle > 0) {
+ ofDrawRectRounded(0, 0, getWidth(), getHeight(), roundAngle);
+ }
+ else {*/
+ ofDrawRectangle(0, 0, getWidth(), getHeight());
+ //}
+
+ shader[nShader].end();
+
+ fbo.end();
+ ofEnableBlendMode(OF_BLENDMODE_ALPHA);
+ ofSetColor(255);
+ fbo.draw(0, 0);
+ }
+
+}
+
+ofVec2f ColorPanel::angleToBorderPoint(float angle)
+{
+ ofVec2f d = angleToDirVector(angle);
+ ofVec2f p = ofVec2f(0.5, 0.5);
+
+ if (angle < 45 || angle >315) {
+ float r = (1.0 - p.x) / d.x;
+ return ofVec2f(1.0, p.y + r * d.y);
+ }
+ else if (angle < 135) {
+ float r = (1.0 - p.y) / d.y;
+ return ofVec2f(p.x + r * d.x,1.0);
+ }
+ else if (angle < 225) {
+ float r = (0 - p.x) / d.x;
+ return ofVec2f(0, p.y + r * d.y);
+ }
+ else {
+ float r = (0 - p.y) / d.y;
+ return ofVec2f(p.x + r * d.x, 0);
+ }
+}
+
+ofVec2f ColorPanel::angleToDirVector(float angle)
+{
+ ofVec2f vDirE = ofVec2f(1, 0);
+ vDirE.rotate(angle);
+ return vDirE;
+}
+
+void ColorPanel::draw()
+{
+ outline.draw();
+ if (colors.size() == 1) {
+ shape.draw();
+ }
+ else {
+ fbo.draw(borderWidth, borderWidth, getWidth() - getWidth() * borderWidth, getHeight() - 2 * borderWidth);
+ }
+
+}
+
+
+
+} // namespace
\ No newline at end of file
diff --git a/src/components/ColorPanel.h b/src/components/ColorPanel.h
new file mode 100644
index 0000000..3bf39e8
--- /dev/null
+++ b/src/components/ColorPanel.h
@@ -0,0 +1,68 @@
+#pragma once
+
+#include "ofMain.h"
+
+#include "Node.h"
+#include "GradientShader.h"
+
+namespace ofxInterface {
+ struct ColorPanelSettings : NodeSettings{
+ float strokeWidth = 0;
+ bool bDrawBackground = true;
+ float borderRadius = 0;
+
+ vector colors = {ofColor(0)};
+ ofColor strokeColor = ofColor(0);
+ float gradientDirection = 0;
+ vector gradientColorPositions;
+ };
+
+ class ColorPanel : public ofxInterface::Node
+ {
+ public:
+ void setup(ofColor color, float w, float h);
+ void setup(ColorPanelSettings settings);
+ virtual Node* clone() override;
+
+ void draw();
+
+ ofColor getColor();
+ void setColor(const ofColor& c) { colors[0] = c; shape.setFillColor(c); }
+
+ ofColor getStrokeColor();
+ void setStrokeColor(const ofColor& c);
+
+ bool isDrawBackground();
+ void setDrawBackground(bool set);
+
+ float getBorderWidth();
+ void setBorderWidth(float set);
+
+ float getRoundedEdge();
+ void setRoundedEdge(float ang);
+
+ void onNodeSizeChanged(Node& n);
+
+ protected:
+ void initShape();
+ ofVec2f angleToBorderPoint(float angle);
+ ofVec2f angleToDirVector(float angle);
+
+ float borderWidth;
+ bool bDrawBackground = true;
+ float borderRadius;
+
+ vector colors;
+ ofColor strokeColor;
+ float gradientDirection;
+ vector gradientColorPositions;
+
+ ofPath shape;
+ ofPath outline;
+
+ vector shader;
+ ofFbo fbo;
+ };
+
+}
+
diff --git a/src/components/Label.cpp b/src/components/Label.cpp
new file mode 100644
index 0000000..038d8cc
--- /dev/null
+++ b/src/components/Label.cpp
@@ -0,0 +1,115 @@
+#include "Label.h"
+
+
+namespace ofxInterface {
+ Label::Label() :Node()
+ {
+
+ }
+
+ Label::Label(const Label & mom):Node(mom)
+ {
+ font = mom.font;
+ style = mom.style;
+ text = mom.text;
+ horzAlignment = mom.horzAlignment;
+ isDropshadow = mom.isDropshadow;
+ shadow = mom.shadow;
+ shadowPos = mom.shadowPos;
+ }
+
+
+ Label::~Label()
+ {
+ }
+
+ Node * Label::clone()
+ {
+ return new Label(*this);
+ }
+
+ void Label::draw()
+ {
+ auto bounds = font->getTextBounds(text, style, 0, 0);
+ float dy = bounds.height*0.5;
+
+ bounds.y += dy;
+
+ // since fontstash uses its own clip functions, we need to manually disable the draw
+ // masks out the text completely, when out of clip
+ // need to find a more appropriate function
+ //ofRectangle boundsGlobal( toGlobal(ofVec3f(bounds.x,bounds.y)),toGlobal(ofVec3f(bounds.width,bounds.height)) );
+ ofRectangle boundsGlobal( toGlobal(ofVec3f(bounds.x,bounds.y)),bounds.width,bounds.height );
+ boundsGlobal.y -= getHeight() * 0.5;
+ //boundsGlobal.width -= boundsGlobal.x;
+ //boundsGlobal.height -= boundsGlobal.y;
+
+ auto cl = getGlobalRenderClip();
+ auto in = getRenderClipRect().intersects(boundsGlobal);
+ if (!getGlobalRenderClip() || getRenderClipRect().intersects(boundsGlobal)) {
+ if (isDropshadow) {
+ font->drawColumn(text, shadow, shadowPos.x, shadowPos.y + dy, getWidth(), horzAlignment);
+ }
+ font->drawColumn(text, style, 0, dy, getWidth(), horzAlignment);
+ }
+
+
+ }
+
+ void ofxInterface::Label::setup(LabelSettings s)
+ {
+ Node::setup(s);
+ font = s.font;
+ style = s.style;
+ text = s.text;
+ horzAlignment = s.horzAlignment;
+ setShadow(s.isDropshadow, s.shadowSize, s.shadowPos.x, s.shadowPos.y, s.shadowColor);
+
+ }
+
+ void Label::setText(string text_)
+ {
+ text = text_;
+ }
+
+ string Label::getText()
+ {
+ return text;
+ }
+
+ void Label::setAlignment(ofAlignHorz alignment_)
+ {
+ horzAlignment = alignment_;
+ }
+ void Label::setShadowEnabled(bool isEnabled)
+ {
+ isDropshadow = isEnabled;
+ }
+ void Label::setShadow(bool isEnabled, float w, float x, float y, ofColor color) {
+ isDropshadow = isEnabled;
+ shadow = style;
+ shadow.blur = w;
+ shadow.color = color;
+ shadowPos = ofVec2f(x, y);
+ }
+ void Label::setFontSize(float size) {
+ style.fontSize = size;
+ shadow.fontSize = size;
+ }
+ float Label::getFontSize() {
+ return style.fontSize;
+ }
+ ofxFontStash2::Style Label::getFontStyle()
+ {
+ return style;
+ }
+
+ void Label::setColor(ofColor color)
+ {
+ style.color = color;
+ }
+ ofColor Label::getColor()
+ {
+ return style.color;
+ }
+}
\ No newline at end of file
diff --git a/src/components/Label.h b/src/components/Label.h
new file mode 100644
index 0000000..dfd12f9
--- /dev/null
+++ b/src/components/Label.h
@@ -0,0 +1,54 @@
+#pragma once
+#include "Node.h"
+#include "ofxFontStash2.h"
+
+namespace ofxInterface {
+ struct LabelSettings :NodeSettings{
+ shared_ptr font;
+ ofxFontStash2::Style style;
+ string text;
+ ofAlignHorz horzAlignment = OF_ALIGN_HORZ_LEFT;
+
+ bool isDropshadow = false;
+ ofxFontStash2::Style shadow;
+ ofVec2f shadowPos;
+ float shadowSize;
+ ofColor shadowColor;
+ };
+
+ class Label :
+ public Node
+ {
+ public:
+ Label();
+ Label(const Label& mom);
+ ~Label();
+
+ virtual Node* clone() override;
+
+ virtual void draw();
+ virtual void setup(LabelSettings s);
+ void setText(string text);
+ string getText();
+ void setAlignment(ofAlignHorz horzAlignment);
+ void setShadowEnabled(bool isEnabled);
+ void setShadow(bool isEnabled, float w = 0, float x= 0, float y = 0, ofColor color = ofColor(0));
+ void setFontSize(float size);
+ float getFontSize();
+ ofxFontStash2::Style getFontStyle();
+ void setColor(ofColor color);
+ ofColor getColor();
+
+
+ private:
+ shared_ptr font;
+ ofxFontStash2::Style style;
+ string text;
+ ofAlignHorz horzAlignment = OF_ALIGN_HORZ_LEFT;
+
+ bool isDropshadow = false;
+ ofxFontStash2::Style shadow;
+ ofVec2f shadowPos;
+ };
+}
+
diff --git a/src/components/LambdaView.cpp b/src/components/LambdaView.cpp
index 209de57..280751e 100644
--- a/src/components/LambdaView.cpp
+++ b/src/components/LambdaView.cpp
@@ -14,9 +14,6 @@ namespace ofxInterface
LambdaView::LambdaView(const string& name)
{
setName(name);
- ofAddListener(eventTouchDown, this, &LambdaView::onTouchDown);
- ofAddListener(eventTouchMove, this, &LambdaView::onTouchMove);
- ofAddListener(eventTouchUp, this, &LambdaView::onTouchUp);
}
void LambdaView::setDrawFunction(std::function _func)
@@ -34,21 +31,6 @@ void LambdaView::setUpdateFunction(std::function _func)
updateFunc = _func;
}
-void LambdaView::setTouchDownFunction(std::function _func)
-{
- touchDownFunc = _func;
-}
-
-void LambdaView::setTouchMoveFunction(std::function _func)
-{
- touchMoveFunc = _func;
-}
-
-void LambdaView::setTouchUpFunction(std::function _func)
-{
- touchUpFunc = _func;
-}
-
void LambdaView::setContainsFunction(std::function _func)
{
containsFunc = _func;
@@ -85,25 +67,4 @@ bool LambdaView::contains(const ofVec3f& global)
}
}
-void LambdaView::onTouchDown(TouchEvent& event)
-{
- if (touchDownFunc) {
- touchDownFunc(event);
- }
-}
-
-void LambdaView::onTouchMove(TouchEvent& event)
-{
- if (touchMoveFunc) {
- touchMoveFunc(event);
- }
-}
-
-void LambdaView::onTouchUp(TouchEvent& event)
-{
- if (touchUpFunc) {
- touchUpFunc(event);
- }
-}
-
}
\ No newline at end of file
diff --git a/src/components/LambdaView.h b/src/components/LambdaView.h
index 5e147bb..e15c7a6 100644
--- a/src/components/LambdaView.h
+++ b/src/components/LambdaView.h
@@ -22,9 +22,6 @@ class LambdaView : public Node
void setDrawFunction(std::function _func);
void setDrawDebugFunction(std::function _func);
void setUpdateFunction(std::function _func);
- void setTouchDownFunction(std::function _func);
- void setTouchMoveFunction(std::function _func);
- void setTouchUpFunction(std::function _func);
void setContainsFunction(std::function _func);
void update(float dt);
void draw();
@@ -35,14 +32,7 @@ class LambdaView : public Node
std::function drawFunc;
std::function drawDebugFunc = [&](){Node::drawDebug();};
std::function updateFunc;
- std::function touchDownFunc;
- std::function touchMoveFunc;
- std::function touchUpFunc;
std::function containsFunc;
-
- void onTouchDown(TouchEvent& event);
- void onTouchMove(TouchEvent& event);
- void onTouchUp(TouchEvent& event);
};
}
diff --git a/src/components/ModalElement.cpp b/src/components/ModalElement.cpp
new file mode 100644
index 0000000..9e81d1e
--- /dev/null
+++ b/src/components/ModalElement.cpp
@@ -0,0 +1,129 @@
+#include "ModalElement.h"
+
+
+namespace ofxInterface {
+ ModalElement::ModalElement()
+ {
+ ofAddListener(eventClick, this, &ModalElement::onClicked, 100);
+ ofAddListener(eventTouchDown, this, &ModalElement::onTouchDown, 100);
+ ofAddListener(eventTouchUp, this, &ModalElement::onTouchUp, 100);
+ isSelected.addListener(this, &ModalElement::onStateChanged);
+ }
+
+ ModalElement::ModalElement(const ModalElement & mom) : Node(mom)
+ {
+ colorActive = mom.colorActive;
+ colorSelected = mom.colorSelected;
+ colorInactive = mom.colorInactive;
+ type = mom.type;
+ isSelected = mom.isSelected.get();
+ isSelected.addListener(this, &ModalElement::onStateChanged);
+ ofAddListener(eventClick, this, &ModalElement::onClicked, 100);
+ ofAddListener(eventTouchDown, this, &ModalElement::onTouchDown, 100);
+ ofAddListener(eventTouchUp, this, &ModalElement::onTouchUp, 100);
+ }
+
+
+ ModalElement::~ModalElement()
+ {
+ }
+
+ Node * ModalElement::clone()
+ {
+ return new ModalElement(*this);
+ }
+
+ void ModalElement::setup(ModalElementSettings s)
+ {
+ Node::setup(s);
+ type = s.type;
+ colorActive = s.colorActive;
+ colorInactive = s.colorInactive;
+ colorSelected = s.colorSelected;
+
+
+ }
+
+ void ModalElement::draw()
+ {
+ //if (type == ofxInterface::BUTTON && !isTouched() && isSelected) {
+ // isSelected = false;
+ //}
+
+ if (getEnabled()) {
+ ofSetColor(colorActive);
+ ofDrawRectangle(0, 0, getWidth(), getHeight());
+ if (isSelected) {
+ ofSetColor(colorSelected);
+ ofDrawRectangle(2, 2, getWidth()-4, getHeight()-4);
+ }
+ } else {
+ ofSetColor(colorInactive);
+ ofDrawRectangle(0, 0, getWidth(), getHeight());
+ }
+
+
+
+ }
+ void ModalElement::onClicked(TouchEvent & e)
+ {
+ switch (type) {
+ case ofxInterface::BUTTON:
+ break;
+ case ofxInterface::CHECKER:
+ isSelected = !isSelected;
+ break;
+ case ofxInterface::RADIO:
+ if (!isSelected) {
+ isSelected = true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ void ModalElement::onTouchDown(TouchEvent & e) {
+ if (type == BUTTON) {
+ isSelected = true;
+ }
+ }
+
+ void ModalElement::onTouchUp(TouchEvent & e) {
+ if (type == BUTTON) {
+ isSelected = false;
+ }
+ }
+
+ void ModalElement::setModalType(ModalType type_) {
+ type = type_;
+ }
+
+ ModalType ModalElement::getModalType() {
+ return type;
+ }
+
+ ofJson ModalElement::getNodeJson()
+ {
+ auto ret = Node::getNodeJson();
+ ret["nodeType"] = "ModalElement";
+
+ switch (type) {
+ case BUTTON:
+ ret["modalType"] = "Button";
+ break;
+ case CHECKER:
+ ret["modalType"] = "Checker";
+ break;
+ case RADIO:
+ ret["modalType"] = "Radio";
+ break;
+ }
+ ret["colorActive"] = vector{ colorActive.r,colorActive.g,colorActive.b,colorActive.a };
+ ret["colorSelected"] = vector{ colorSelected.r,colorSelected.g,colorSelected.b,colorSelected.a };
+ ret["colorInactive"] = vector{ colorInactive.r,colorInactive.g,colorInactive.b,colorInactive.a };
+
+ return ret;
+ }
+
+}
\ No newline at end of file
diff --git a/src/components/ModalElement.h b/src/components/ModalElement.h
new file mode 100644
index 0000000..8279588
--- /dev/null
+++ b/src/components/ModalElement.h
@@ -0,0 +1,49 @@
+#pragma once
+#include "Node.h"
+
+namespace ofxInterface {
+ enum ModalType {
+ BUTTON,
+ CHECKER,
+ RADIO
+ };
+
+ struct ModalElementSettings : NodeSettings{
+ ModalType type = BUTTON;
+ ofColor colorActive = ofColor(200);
+ ofColor colorSelected = ofColor(100);
+ ofColor colorInactive = ofColor(50);
+ };
+
+ class ModalElement : public Node
+ {
+ public:
+ ModalElement();
+ ModalElement(const ModalElement& mom);
+ ~ModalElement();
+
+ virtual Node* clone() override;
+
+ void setup(ModalElementSettings s);
+ virtual void draw();
+
+ void onClicked(TouchEvent& e);
+ void onTouchDown(TouchEvent& e);
+ void onTouchUp(TouchEvent& e);
+ virtual void onStateChanged(bool& isActive) {};
+ ofParameter isSelected = false;
+
+ void setModalType(ModalType type);
+ ModalType getModalType();
+
+ ofColor colorActive;
+ ofColor colorSelected;
+ ofColor colorInactive;
+
+ ofJson getNodeJson() override;
+
+ private:
+ ModalType type;
+
+ };
+}
diff --git a/src/components/ScrollableContainer.cpp b/src/components/ScrollableContainer.cpp
new file mode 100644
index 0000000..abb5859
--- /dev/null
+++ b/src/components/ScrollableContainer.cpp
@@ -0,0 +1,330 @@
+#include "ScrollableContainer.h"
+
+
+namespace ofxInterface {
+ ScrollableContainer::ScrollableContainer():Node()
+ {
+ }
+
+
+ ScrollableContainer::~ScrollableContainer()
+ {
+ }
+
+ void ScrollableContainer::setup(ScrollableContainerSettings s) {
+ Node::setup(s);
+ setRenderClip(true);
+ setTouchClip(true);
+
+ scrollableArea = new Node();
+ scrollableArea->setName("scrollableArea");
+ scrollableArea->setSize(s.sizeScrollableArea);
+ addChild(scrollableArea);
+
+ bgColor = s.bgColor;
+ scrollActiveColor = s.scrollActiveColor;
+ scrollInactiveColor = s.scrollInactiveColor;
+
+ ColorPanelSettings cs;
+ cs.colors.front() = s.scrollActiveColor;
+ cs.plane = 10;
+
+ slider.insert(make_pair("w", new ColorPanel()));
+ slider["w"]->setup(cs);
+ slider.insert(make_pair("h", new ColorPanel()));
+ slider["h"]->setup(cs);
+
+ cs.colors.front() = s.scrollInactiveColor;
+ slider.insert(make_pair("wInactive", new ColorPanel()));
+ slider["wInactive"]->setup(cs);
+ slider.insert(make_pair("hInactive", new ColorPanel()));
+ slider["hInactive"]->setup(cs);
+
+ baseSize = getSize();
+
+ addChild(slider["wInactive"]);
+ addChild(slider["hInactive"]);
+ addChild(slider["w"]);
+ addChild(slider["h"]);
+ for (auto& s : slider) {
+ s.second->setEnabled(false);
+ }
+
+ ofAddListener(eventChildAdded, this, &ScrollableContainer::onChildAdded);
+ ofAddListener(scrollableArea->eventChildRemoved, this, &ScrollableContainer::onChildRemoved);
+ ofAddListener(scrollableArea->eventTouchMove, this, &ScrollableContainer::onTouchMove);
+ ofAddListener(scrollableArea->eventTouchUp, this, &ScrollableContainer::onTouchUp);
+ ofAddListener(scrollableArea->eventTouchDown, this, &ScrollableContainer::onTouchDown);
+
+ edgeAnimation.setCurve(AnimCurve::SWIFT_GOOGLE);
+ edgeAnimation.setAnimFinishedLambda([this]() {
+ if (animationReady) {
+ edgeAnimation.animateTo(finalPoint);
+ animationReady = false;
+ }
+ });
+
+
+
+ }
+
+
+ void ScrollableContainer::draw()
+ {
+
+ // animate elements if scrolled on edge
+ if (edgeAnimation.isAnimating()) {
+ edgeAnimation.update(ofGetLastFrameTime());
+ scrollableArea->setPosition(edgeAnimation.getCurrentPosition());
+
+
+ // animate elements right after touchUp
+ }
+ if (isAutoScroll) {
+ currentVelocity *= friction;
+
+ ofVec2f newPos = ofVec2f(scrollableArea->getPosition()) + currentVelocity* ofGetLastFrameTime();
+
+ newPos.x = ofClamp(newPos.x, getWidth() - scrollableArea->getWidth(), 0);
+ newPos.y = ofClamp(newPos.y, getHeight() - scrollableArea->getHeight(), 0);
+
+ scrollableArea->setPosition(newPos);
+
+ if (abs(currentVelocity.x) < 1 && abs(currentVelocity.y) < 1) {
+ isAutoScroll = false;
+ }
+ else if( ((newPos.x == 0 || newPos.x == getWidth() - scrollableArea->getWidth()) &&
+ (newPos.y == 0 || newPos.y == getHeight() - scrollableArea->getHeight()))) {
+ isAutoScroll = false;
+
+ edgeAnimation.setPosition(scrollableArea->getPosition());
+ edgeAnimation.setDuration(0.15);
+
+ ofVec2f d;
+ d.x = currentVelocity.x < 0 ? -50 : 50;
+ d.y = currentVelocity.y < 0 ? -50 : 50;
+ if (getWidth() == scrollableArea->getWidth()) {
+ d.x = 0;
+ }
+ if (getHeight() == scrollableArea->getHeight()) {
+ d.y = 0;
+ }
+ ofVec2f pos = ofVec2f(scrollableArea->getPosition()) + d;
+ pos.x = ofClamp(pos.x,-1*(scrollableArea->getWidth() - getWidth()), 0);
+ pos.y = ofClamp(pos.y,-1*(scrollableArea->getHeight() - getHeight()), 0);
+
+ edgeAnimation.animateTo(pos);
+ finalPoint = scrollableArea->getPosition();
+ animationReady = false;
+ }
+ }
+
+ ofSetColor(bgColor);
+ ofDrawRectangle(0, 0, getWidth(), getHeight());
+
+ updateSlider();
+
+ }
+
+
+ Node * ScrollableContainer::getScrollableArea() {
+ return scrollableArea;
+ }
+
+ void ScrollableContainer::onTouchDown(TouchEvent & event) {
+ edgeAnimation.reset();
+ }
+
+ void ScrollableContainer::onTouchUp(TouchEvent & event) {
+ // use the event velocity to simulate an impulse
+ float dMaxImpulse = 1000;
+ ofVec3f velocityModifier;
+
+ velocityModifier.x = ofxeasing::map_clamp(abs(event.velocitySmoothed.x), 100, maxVelocity, 0, dMaxImpulse, &ofxeasing::exp::easeOut);
+ if (event.velocitySmoothed.x < 0) {
+ velocityModifier.x *= -1;
+ }
+ velocityModifier.y = ofxeasing::map_clamp(abs(event.velocitySmoothed.y), 100, maxVelocity, 0, dMaxImpulse, &ofxeasing::exp::easeOut);
+ if (event.velocitySmoothed.y < 0) {
+ velocityModifier.y *= -1;
+ }
+
+ ofVec2f newPos = scrollableArea->getPosition() + velocityModifier;
+
+ newPos.x = ofClamp(newPos.x, getWidth() - scrollableArea->getWidth(), 0);
+ newPos.y = ofClamp(newPos.y, getHeight() - scrollableArea->getHeight(), 0);
+
+ currentVelocity = velocityModifier;
+ isAutoScroll = true;
+
+ }
+
+ void ScrollableContainer::onTouchMove(TouchEvent & event) {
+
+ // calculate movement frome last event
+ ofVec3f d = event.position - event.prevPosition;
+ ofVec3f newPos = scrollableArea->getPosition() + d;
+
+ // clamp the position
+ newPos.x = ofClamp(newPos.x, getWidth() - scrollableArea->getWidth(),0);
+ newPos.y = ofClamp(newPos.y, getHeight() - scrollableArea->getHeight(),0);
+
+ scrollableArea->setPosition(newPos);
+ }
+
+ void ScrollableContainer::onScrollAreaSizeChanged(ofxInterface::Node & n) {
+ dragArea->setSize(n.getSize());
+ }
+
+ ///\brief moves child to scrollable Area
+ void ScrollableContainer::onChildAdded(ofxInterface::Node & n) {
+
+
+ // move child to scroll and resize continer
+ containerRemoveChild(&n);
+ scrollableArea->addChild(&n);
+ fitSizeToItems();
+
+ // clip render
+ n.setRenderClip(true);
+
+ // add listeners
+ updateChildListeners(&n);
+
+
+ }
+
+ void ScrollableContainer::onChildRemoved(ofxInterface::Node& n)
+ {
+ fitSizeToItems();
+ }
+
+ void ScrollableContainer::fitSizeToItems()
+ {
+ int w = baseSize.x;
+ int h = baseSize.y;
+
+ for (auto& c : getChildWithName("scrollableArea")->getChildren())
+ {
+ updateChildItemSize(c, w, h);
+ }
+ getChildWithName("scrollableArea")->setSize(w, h);
+
+ }
+
+ Node * ScrollableContainer::containerRemoveChild(Node * child, bool bMaintainChildGlobalTransform)
+ {
+ for (int i = 0; i < childNodes.size(); i++)
+ {
+ if (childNodes[i] == child) {
+ return containerRemoveChild(i, bMaintainChildGlobalTransform);
+ }
+ }
+
+ ofLogWarning("ofxInterface::Node::removeChild", "are you trying to remove a child that does not exist?");
+ return NULL;
+ }
+
+ Node * ScrollableContainer::containerRemoveChild(int index, bool bMaintainChildGlobalTransform)
+ {
+ if (index >= childNodes.size()) {
+ ofLogWarning("ofxInterface::Node::removeChild", "are you trying to remove a child that does not exist?");
+ return NULL;
+ }
+
+ Node *child = childNodes[index];
+ childNodes.erase(childNodes.begin() + index);
+ child->clearParent(bMaintainChildGlobalTransform);
+ //ofNotifyEvent(eventChildRemoved, *child, this);
+ return child;
+ }
+
+ void ScrollableContainer::updateChildItemSize(Node* child, int& w, int& h)
+ {
+ for (auto& c : child->getChildren())
+ {
+ updateChildItemSize(c, w, h);
+ }
+
+ if (child->getPosition().x + child->getWidth() > w) {
+ w = child->getPosition().x + child->getWidth();
+ }
+ if (child->getPosition().y + child->getHeight() > h) {
+ h = child->getPosition().y + child->getHeight();
+ }
+ }
+
+ void ScrollableContainer::updateChildListeners(Node* child)
+ {
+ for (auto& c : child->getChildren()) {
+ updateChildListeners(c);
+ }
+
+ ofAddListener(child->eventTouchMove, this, &ScrollableContainer::onTouchMove);
+ ofAddListener(child->eventTouchUp, this, &ScrollableContainer::onTouchUp);
+ ofAddListener(child->eventTouchDown, this, &ScrollableContainer::onTouchDown);
+ }
+
+ void ScrollableContainer::updateSlider()
+ {
+ // draw slider
+ float wSlider = 6;
+ float marginSlider = 20;
+
+ if (scrollableArea->getWidth() > getWidth()) {
+ slider["wInactive"]->setVisible(true);
+ slider["w"]->setVisible(true);
+
+ ofVec2f d0 = ofVec2f(marginSlider, getHeight() - marginSlider - wSlider);
+ //ofSetColor(scrollInactiveColor);
+ float width = getWidth() - 3 * marginSlider;
+
+ slider["wInactive"]->setPosition(d0);
+ slider["wInactive"]->setSize(width, wSlider);
+ //ofDrawRectangle(d0, width, wSlider);
+
+ //ofSetColor(scrollActiveColor);
+ float wActive = width * getWidth() / scrollableArea->getWidth();
+ if (edgeAnimation.isAnimating()) {
+ wActive -= (1.0 - edgeAnimation.getPercentDone()) * 25;
+ }
+ float pos = (width - wActive) - ofMap(scrollableArea->getPosition().x, getWidth() - scrollableArea->getWidth(), 0, 0, width - wActive, true);
+ //ofDrawRectangle(d0 + ofVec2f(pos,0), wActive, wSlider);
+ slider["w"]->setPosition(d0 + ofVec2f(pos, 0));
+ slider["w"]->setSize(wActive, wSlider);
+ }
+ else {
+ slider["wInactive"]->setVisible(false);
+ slider["w"]->setVisible(false);
+ }
+
+
+ if (scrollableArea->getHeight() > getHeight()) {
+ slider["hInactive"]->setVisible(true);
+ slider["h"]->setVisible(true);
+
+ ofVec2f d0 = ofVec2f(getWidth() - marginSlider - wSlider, marginSlider);
+ //ofSetColor(scrollInactiveColor);
+ float height = getHeight() - 2 * marginSlider;
+ //ofDrawRectangle(d0, wSlider, height);
+ slider["hInactive"]->setPosition(d0);
+ slider["hInactive"]->setSize(wSlider, height);
+
+ //ofSetColor(scrollActiveColor);
+ float hActive = height * getHeight() / scrollableArea->getHeight();
+ if (edgeAnimation.isAnimating()) {
+ hActive -= (1.0 - edgeAnimation.getPercentDone()) * 25;
+ }
+ float pos = (height - hActive) - ofMap(scrollableArea->getPosition().y, getHeight() - scrollableArea->getHeight(), 0, 0, height - hActive, true);
+ //ofDrawRectangle(d0 +ofVec2f(0,pos), wSlider, hActive);
+ slider["h"]->setPosition(d0 + ofVec2f(0, pos));
+ slider["h"]->setSize(wSlider, hActive);
+ }
+ else {
+ slider["hInactive"]->setVisible(false);
+ slider["h"]->setVisible(false);
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/components/ScrollableContainer.h b/src/components/ScrollableContainer.h
new file mode 100644
index 0000000..45e36ac
--- /dev/null
+++ b/src/components/ScrollableContainer.h
@@ -0,0 +1,82 @@
+#pragma once
+#include "Node.h"
+#include "ofxFontStash2.h"
+#include "ofxAnimatableOfPoint.h"
+#include "ofxEasing.h"
+#include "ColorPanel.h"
+
+namespace ofxInterface {
+
+ struct ScrollableContainerSettings : NodeSettings{
+ ofVec2f sizeScrollableArea;
+ float plane = 0; // option to set scroll option above elements
+
+ ofColor bgColor = ofColor(0, 0);
+ ofColor scrollActiveColor = ofColor(255);
+ ofColor scrollInactiveColor = ofColor(128);
+
+ };
+
+ class ScrollableContainer :
+ public Node
+ {
+ public:
+ ScrollableContainer();
+ ~ScrollableContainer();
+
+ virtual void setup(ScrollableContainerSettings s);
+
+ virtual void draw() override;
+
+ Node* getScrollableArea();
+ //void addChildToScrollableArea(Node* child);
+
+ virtual void onTouchDown(TouchEvent & event);
+ virtual void onTouchUp(TouchEvent & event);
+ virtual void onTouchMove(TouchEvent & event);
+
+ void onScrollAreaSizeChanged(ofxInterface::Node & n);
+
+ void onChildAdded(ofxInterface::Node& n);
+ void onChildRemoved(ofxInterface::Node& n);
+ void fitSizeToItems();
+
+ protected:
+ Node* containerRemoveChild(Node *child, bool bMaintainChildGlobalTransform = false);
+ Node* containerRemoveChild(int index, bool bMaintainChildGlobalTransform = false);
+ void updateChildItemSize(Node* child, int& w, int& h);
+ void updateChildListeners(Node* child);
+
+ void updateSlider();
+
+ private:
+ Node* scrollableArea;
+ Node* dragArea;
+ map slider;
+ ofVec2f pStartDragCursor;
+ ofVec2f pStartDrag;
+ ofVec2f baseSize;
+
+ ofColor bgColor;
+ ofColor scrollActiveColor;
+ ofColor scrollInactiveColor;
+
+ /// \brief rollout animation on touch up
+ ofxAnimatableOfPoint edgeAnimation;
+ ofVec2f currentVelocity;
+ ofVec2f finalPoint;
+ bool animationReady = false;
+ const float friction = 0.94;
+ bool isAutoScroll = false;
+
+ /// \brief clamp of velocity animation sliding
+ const int maxVelocity = 1000;
+
+ /// \brief in s
+ const float maxAnimationLength = 2.3f;
+
+
+
+ };
+}
+
diff --git a/src/components/SimpleChecker.cpp b/src/components/SimpleChecker.cpp
new file mode 100644
index 0000000..71fac9a
--- /dev/null
+++ b/src/components/SimpleChecker.cpp
@@ -0,0 +1,60 @@
+#include "SimpleChecker.h"
+
+
+namespace ofxInterface {
+ SimpleChecker::SimpleChecker()
+ {
+ }
+
+
+ SimpleChecker::~SimpleChecker()
+ {
+ }
+
+ void SimpleChecker::setup(ModalElementSettings s)
+ {
+ s.type = CHECKER;
+ ModalElement::setup(s);
+
+ colorFade.setColor(colorInactive);
+ colorFade.setDuration(0.10);
+
+ // init shape
+ float multX = float(getWidth()) / 32;
+ float multY = float(getHeight()) / 32;
+ checkerShape.moveTo(2*multX, 6 * multY);
+ checkerShape.lineTo(0 * multX, 8 * multY);
+ checkerShape.lineTo(7 * multX, 15 * multY);
+ checkerShape.lineTo(20 * multX, 2 * multY);
+ checkerShape.lineTo(18 * multX, 0 * multY);
+ checkerShape.lineTo(7 * multX, 11 * multY);
+ checkerShape.lineTo(2 * multX,6 * multY);
+
+ checkerShape.close();
+ checkerShape.translate(ofVec2f(6 * multX, 8 * multY));
+ checkerShape.setColor(colorSelected);
+ }
+ void SimpleChecker::draw()
+ {
+ colorFade.update(ofGetLastFrameTime());
+ ofSetColor(colorFade.getCurrentColor());
+
+ ofDrawRectangle(0, 0, getWidth(), getHeight());
+
+ if (isSelected) {
+ checkerShape.draw();
+ }
+
+ }
+
+ void SimpleChecker::onStateChanged(bool & isActive)
+ {
+ if (isActive) {
+ colorFade.animateTo(colorActive);
+ }
+ else {
+ colorFade.animateTo(colorInactive);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/components/SimpleChecker.h b/src/components/SimpleChecker.h
new file mode 100644
index 0000000..7732a01
--- /dev/null
+++ b/src/components/SimpleChecker.h
@@ -0,0 +1,26 @@
+#pragma once
+#include "ModalElement.h"
+#include "ofxAnimatable.h"
+#include "ofxAnimatableOfColor.h"
+
+namespace ofxInterface {
+
+ class SimpleChecker : public ModalElement
+ {
+ public:
+ SimpleChecker();
+ ~SimpleChecker();
+
+ void setup(ModalElementSettings s);
+ virtual void draw();
+
+ void onStateChanged(bool& isActive);
+
+ protected:
+ ofxAnimatableOfColor colorFade;
+ int currentAni = 0;
+
+ ofPath checkerShape;
+
+ };
+}
diff --git a/src/components/Slider.cpp b/src/components/Slider.cpp
new file mode 100644
index 0000000..da63750
--- /dev/null
+++ b/src/components/Slider.cpp
@@ -0,0 +1,110 @@
+#include "Slider.h"
+
+
+namespace ofxInterface {
+ Slider::Slider()
+ {
+ ofAddListener(eventTouchMove, this, &Slider::onTouchMove, 100);
+ ofAddListener(eventTouchDown, this, &Slider::onTouchDown, 100);
+ //ofAddListener(eventTouchUp, this, &Slider::onTouchUp, 100);
+ value.set("value", 0, 0, 1);
+ }
+
+ Slider::Slider(const Slider & mom) : Node(mom)
+ {
+ colorActive = mom.colorActive;
+ colorSelected = mom.colorSelected;
+ lineWidth = mom.lineWidth;
+ colorInactive = mom.colorInactive;
+ direction = mom.direction;
+ value = mom.value;
+ ofAddListener(eventTouchMove, this, &Slider::onTouchMove, 100);
+ ofAddListener(eventTouchDown, this, &Slider::onTouchDown, 100);
+ }
+
+
+ Slider::~Slider()
+ {
+ }
+
+ Node * Slider::clone()
+ {
+ return new Slider(*this);
+ }
+
+ void Slider::setup(SliderSettings s)
+ {
+ Node::setup(s);
+ colorActive = s.colorActive;
+ colorSelected = s.colorSelected;
+ lineWidth = s.lineWidth;
+ colorInactive = s.colorInactive;
+ direction = s.direction;
+ }
+
+ void Slider::draw()
+ {
+ float lActive;
+ float lInactive;
+
+ ofRectangle rActive;
+ ofRectangle rInactive;
+
+ if (direction == HORIZONTAL) {
+ lActive = ofMap(value, value.getMin(), value.getMax(), 0, getWidth(), true);
+ lInactive = getWidth() - lActive;
+ rActive = ofRectangle(0, 0.5*(getHeight() - lineWidth), lActive, lineWidth);
+ rInactive = ofRectangle(lActive, 0.5*(getHeight() - lineWidth), lInactive, lineWidth);
+ }else {
+ lActive = ofMap(value, value.getMin(), value.getMax(), 0, getHeight(), true);
+ lInactive = getHeight() - lActive;
+ rActive = ofRectangle(0.5*(getWidth() - lineWidth),0, lineWidth, lActive );
+ rInactive = ofRectangle(0.5*(getWidth() - lineWidth),lActive, lineWidth,lInactive);
+ }
+
+ ofSetColor(colorSelected);
+ ofDrawRectangle(rActive);
+ ofSetColor(colorInactive);
+ ofDrawRectangle(rInactive);
+ ofSetColor(colorActive);
+ ofDrawCircle(rInactive.x, rInactive.y, 0.5*getHeight());
+ }
+
+ void Slider::onTouchDown(TouchEvent & e) {
+ if (direction == HORIZONTAL) {
+ value = ofMap(toLocal(e.position).x, 0, getWidth(), value.getMin(), value.getMax(), true);
+ }
+ else {
+ value = ofMap(toLocal(e.position).y, 0, getHeight(), value.getMin(), value.getMax(), true);
+ }
+
+ }
+
+ void Slider::onTouchMove(TouchEvent & e)
+ {
+ if (direction == HORIZONTAL) {
+ value = ofMap(toLocal(e.position).x, 0, getWidth(), value.getMin(), value.getMax(), true);
+ }
+ else {
+ value = ofMap(toLocal(e.position).y, 0, getHeight(), value.getMin(), value.getMax(), true);
+ }
+ }
+
+ void Slider::setDirection(Direction type)
+ {
+ direction = type;
+ }
+
+ Direction Slider::getDirection()
+ {
+ return direction;
+ }
+
+ /*void Slider::onTouchUp(TouchEvent & e) {
+ if (type == BUTTON) {
+ isSelected = false;
+ }
+ }*/
+
+
+}
\ No newline at end of file
diff --git a/src/components/Slider.h b/src/components/Slider.h
new file mode 100644
index 0000000..aba5e59
--- /dev/null
+++ b/src/components/Slider.h
@@ -0,0 +1,47 @@
+#pragma once
+#include "Node.h"
+
+namespace ofxInterface {
+ enum Direction {
+ HORIZONTAL,
+ VERTICAL
+ };
+
+ struct SliderSettings : NodeSettings{
+ Direction direction = HORIZONTAL;
+ ofColor colorActive = ofColor(200);
+ ofColor colorSelected = ofColor(100);
+ ofColor colorInactive = ofColor(50);
+ int lineWidth = 6;
+ };
+
+ class Slider : public Node
+ {
+ public:
+ Slider();
+ Slider(const Slider& mom);
+ ~Slider();
+
+ virtual Node* clone() override;
+
+ void setup(SliderSettings s);
+ virtual void draw();
+
+ void onTouchDown(TouchEvent& e);
+ //void onTouchUp(TouchEvent& e);
+ void onTouchMove(TouchEvent& e);
+
+ void setDirection(Direction type);
+ Direction getDirection();
+
+ ofColor colorActive;
+ ofColor colorSelected;
+ ofColor colorInactive;
+
+ ofParameter value;
+
+ private:
+ Direction direction;
+ int lineWidth;
+ };
+}
diff --git a/src/components/SoftKeyboard.cpp b/src/components/SoftKeyboard.cpp
new file mode 100644
index 0000000..58321b0
--- /dev/null
+++ b/src/components/SoftKeyboard.cpp
@@ -0,0 +1,305 @@
+/*
+ * SoftKeyboard.cpp
+ * emptyExample
+ *
+ * Created by Brian Eschrich on 23.02.16
+ * Copyright 2017 reddo UG. All rights reserved.
+ *
+ */
+
+#include "SoftKeyboard.h"
+
+namespace ofxInterface {
+ void SoftKeyboard::setup(SoftKeyboardSettings s)
+ {
+ Node::setup(s);
+ font = s.font;
+ style = s.style;
+
+ bgColor = s.bgColor;
+ colorActive = s.colorActive;
+ colorInactive = s.colorInactive;
+ colorSelected = s.colorSelected;
+ borderRadius = s.borderRadius;
+ borderWidth = s.borderWidth;
+ padding = s.padding;
+ margin = s.margin;
+ textureKeys = s.textureKeys;
+
+
+
+ // fixed ratio
+ if (getHeight() == 0) {
+ setHeight(getWidth() * 19.4 / 51);
+ }
+
+
+ // create keys, different layout for code and language input
+ if (s.layout == "code") {
+ wKey = (getWidth() - 2 * (margin + borderWidth) - 9 * padding) / 10;
+ }
+ else {
+ wKey = (getWidth() - 2 * (margin + borderWidth) - 10 * padding) / 11;
+ }
+ hKey = (getHeight() - 2 * (margin + borderWidth) - 3 * padding) / 4;
+
+ auto keyLayout = getKeyLayout(s.layout);
+ for (auto& l:keyLayout)
+ {
+ addSet(l.first, l.second);
+ }
+
+ setActiveKeyset( "alpha");
+
+
+ // draw border
+ if (borderWidth > 0) {
+ borderPath.setColor(style.color);
+ borderPath.setArcResolution(200);
+ // other path
+ borderPath.moveTo(borderRadius, 0);
+ borderPath.lineTo(getWidth() - borderRadius, 0);
+ borderPath.arc(ofVec2f(getWidth() - borderRadius, borderRadius), borderRadius, borderRadius, 270, 0);
+ borderPath.lineTo(getWidth(), getHeight() - borderRadius);
+ borderPath.arc(ofVec2f(getWidth() - borderRadius, getHeight() - borderRadius), borderRadius, borderRadius, 0, 90);
+ borderPath.lineTo(borderRadius, getHeight());
+ borderPath.arc(ofVec2f(borderRadius, getHeight() - borderRadius), borderRadius, borderRadius, 90, 180);
+ borderPath.lineTo(0, borderRadius);
+ borderPath.arc(ofVec2f(borderRadius, borderRadius), borderRadius, borderRadius, 180, 270);
+ // inner path
+ borderPath.moveTo(borderRadius, borderWidth);
+ borderPath.lineTo(getWidth() - borderRadius, borderWidth);
+ borderPath.arc(ofVec2f(getWidth() - borderRadius, borderRadius), borderRadius - borderWidth, borderRadius - borderWidth, 270, 0);
+ borderPath.lineTo(getWidth() - borderWidth, getHeight() - borderRadius);
+ borderPath.arc(ofVec2f(getWidth() - borderRadius, getHeight() - borderRadius), borderRadius - borderWidth, borderRadius - borderWidth, 0, 90);
+ borderPath.lineTo(borderRadius, getHeight() - borderWidth);
+ borderPath.arc(ofVec2f(borderRadius, getHeight() - borderRadius), borderRadius - borderWidth, borderRadius - borderWidth, 90, 180);
+ borderPath.lineTo(borderWidth, borderRadius);
+ borderPath.arc(ofVec2f(borderRadius, borderRadius), borderRadius - borderWidth, borderRadius - borderWidth, 180, 270);
+ borderPath.close();
+ }
+ }
+
+ //------------------------------------------------------------------
+ void SoftKeyboard::draw() {
+ borderPath.draw();
+
+ }
+
+ void SoftKeyboard::setActiveKeyset(string id)
+ {
+ for (auto& keySet : keySets) {
+ if (keySet.first == id) {
+ keySet.second->activate();
+ activeKeySet = keySet.second;
+ }
+ else {
+ keySet.second->deactivate();
+ }
+ }
+ }
+
+ void SoftKeyboard::addSet(string keySet, vector> keys)
+ {
+ keySets.insert(make_pair(keySet, new Node()));
+ addChild(keySets[keySet]);
+ keySets[keySet]->setName(keySet);
+
+ int py = borderWidth + margin;
+ for (auto& line : keys) {
+ addLine(keySets[keySet], line, py);
+ py += hKey + padding;
+ }
+ }
+
+ void SoftKeyboard::addLine(Node* keySet, vector keys, vector widthKeys, int y)
+ {
+ int wLine = margin* (widthKeys.size()-1);
+ for (auto& w : widthKeys) {
+ wLine += wKey * w;
+ }
+
+ float x = margin + borderWidth;
+ int i = 0;
+ for (auto& key : keys) {
+ float w = widthKeys[i];
+ if (key != 10000) {
+ addKey(keySet, key, x, y, w, hKey);
+ }
+ x += w + padding;
+ ++i;
+ }
+ }
+
+ void SoftKeyboard::addLine(Node* keySet, vector keys, int y){
+ map keyWidths;
+ keyWidths[' '] = wKey * 5 + padding * 4;
+ keyWidths['@'] = (3 * wKey + padding) *0.5;
+ keyWidths[9997] = (3 * wKey + padding) *0.5; // abc
+ keyWidths[9996] = (3 * wKey + padding) *0.5; // 123
+ keyWidths[3680] = wKey * 2 + padding; // shift
+ keyWidths[8] = wKey * 2 + padding; // backspace
+ keyWidths[13] = wKey * 2 + padding; // return
+ keyWidths[10000] = (wKey ) * 0.5; // half key empty space
+
+ vector widthKeys;
+
+ for (auto key : keys) {
+ if (keyWidths.find(key) != keyWidths.end()) {
+ widthKeys.push_back(keyWidths[key]);
+ }else {
+ widthKeys.push_back(wKey);
+ }
+ }
+ addLine(keySet, keys, widthKeys, y);
+ }
+
+ void SoftKeyboard::addKey(Node* keySet, int keyValue, int x, int y, int w, int h){
+
+ SoftKeyboardKey* key = new SoftKeyboardKey();
+ SoftKeyboardKeySettings s;
+
+ s.name = "key_" + ofToString(keyValue);
+ s.position = ofVec2f(x, y);
+ s.size = ofVec2f(w, h);
+ s.borderWidth = borderWidth * 0.25;
+ s.borderRadius = borderRadius * 0.5;
+ s.colorActive = colorActive;
+ s.colorInactive = colorInactive;
+ s.colorSelected = colorSelected;
+ s.font = font;
+ s.key = keyValue;
+ s.style = style;
+
+ if (textureKeys.find(keyValue) != textureKeys.end()) {
+ s.isTextureKey = true;
+ s.keyTexture = textureKeys[keyValue];
+ }
+
+ key->setup(s);
+
+ keySet->addChild(key);
+
+ ofAddListener(key->keyPressed, this, &SoftKeyboard::onKeyPressed);
+ ofAddListener(key->keyReleased, this, &SoftKeyboard::onKeyReleased);
+ }
+
+ map>> SoftKeyboard::getKeyLayout(string layout)
+ {
+ map>> ret;
+ vector alphaKeys;
+ vector alphaBigKeys;
+ vector numbers;
+
+ if (layout == "de") {
+ vector< vector> alphaKeys = {
+ {'q','w','e','r','t','z','u','i','o','p',252},
+ {'a','s','d','f','g','h','j','k','l',246,228},
+ { 3680,'y','x','c','v','b','n','m',8},
+ { 9997,'@',' ','.',13}
+ };
+
+ ret.insert(make_pair("alpha", alphaKeys));
+
+ vector> alphaBigKeys = {
+ {'Q','W','E','R','T','Z','U','I','O','P',220},
+ {'A','S','D','F','G','H','J','K','L',214,196},
+ { 3680,'Y','X','C','V','B','N','M',8},
+ { 9997,'@',' ','.',13}
+ };
+
+ ret.insert(make_pair("alphaBig", alphaBigKeys));
+
+ vector< vector> numbers = {
+ {'1','2','3','4','5','6','7','8','9','0','='},
+ {'!','#','$','%','&','\'','*','+','-','/','?'},
+ {'_',223,'|','{','}','[',']','(',')',',',':'},
+ { 9996,'@',' ',';',13}
+ };
+
+ ret.insert(make_pair("numbers", numbers));
+ }
+ else if(layout == "en") {
+ vector< vector> alphaKeys = {
+ {'q','w','e','r','t','y','u','i','o','p',252},
+ {'a','s','d','f','g','h','j','k','l',246,228},
+ { 3680,'z','x','c','v','b','n','m',8},
+ { 9997,'@',' ','.',13}
+ };
+
+ ret.insert(make_pair("alpha", alphaKeys));
+
+ vector> alphaBigKeys = {
+ {'Q','W','E','R','T','Y','U','I','O','P',220},
+ {'A','S','D','F','G','H','J','K','L',214,196},
+ { 3680,'Z','X','C','V','B','N','M',8},
+ { 9997,'@',' ','.',13}
+ };
+
+ ret.insert(make_pair("alphaBig", alphaBigKeys));
+
+ vector< vector> numbers = {
+ {'1','2','3','4','5','6','7','8','9','0','='},
+ {'!','#','$','%','&','\'','*','+','-','/','?'},
+ {'_',223,'|','{','}','[',']','(',')',',',':'},
+ { 9996,'@',' ',';',13}
+ };
+
+ ret.insert(make_pair("numbers", numbers));
+ }
+ else if (layout == "code") {
+ vector< vector> alphaKeys = {
+ {'1','2','3','4','5','6','7','8','9','0'},
+ {'Q','W','E','R','T','Y','U','I','O','P'},
+ {10000,'A','S','D','F','G','H','J','K','L'},
+ {10000,'Z','X','C','V','B','N','M',8}
+ };
+
+ ret.insert(make_pair("alpha", alphaKeys));
+
+ vector> alphaBigKeys = {
+ {'1','2','3','4','5','6','7','8','9','0'},
+ {'Q','W','E','R','T','Y','U','I','O','P'},
+ {10000,'A','S','D','F','G','H','J','K','L'},
+ {10000,'Z','X','C','V','B','N','M',8}
+ };
+
+ ret.insert(make_pair("alphaBig", alphaBigKeys));
+
+ vector< vector> numbers = {
+ {'1','2','3','4','5','6','7','8','9','0'},
+ {'Q','W','E','R','T','Y','U','I','O','P'},
+ {10000,'A','S','D','F','G','H','J','K','L'},
+ {10000,'Z','X','C','V','B','N','M',8}
+ };
+
+ ret.insert(make_pair("numbers", numbers));
+ }
+
+ return ret;
+
+ }
+
+ void SoftKeyboard::onKeyPressed(ofKeyEventArgs &event)
+ {
+ if (activeKeySet->getName() == "alphaBig") {
+ setActiveKeyset("alpha");
+ }
+ else if(activeKeySet->getName() == "alpha" && (event.key == 3680 || event.key == 3681)){
+ setActiveKeyset("alphaBig");
+ }
+ else if (event.key == 9996) {
+ setActiveKeyset("alpha");
+ }
+ else if (event.key == 9997) {
+ setActiveKeyset("numbers");
+ }
+ ofNotifyEvent(keyPressed, event);
+ }
+
+ void SoftKeyboard::onKeyReleased(ofKeyEventArgs &event)
+ {
+ ofNotifyEvent(keyReleased, event);
+ }
+
+}
\ No newline at end of file
diff --git a/src/components/SoftKeyboard.h b/src/components/SoftKeyboard.h
new file mode 100644
index 0000000..da323e7
--- /dev/null
+++ b/src/components/SoftKeyboard.h
@@ -0,0 +1,88 @@
+/*
+ * SoftKeyboard.h
+ * emptyExample
+ *
+ * Created by Brian Eschrich on 23.02.16
+ * Copyright 2017 reddo UG. All rights reserved.
+ *
+ */
+
+#ifndef _SoftKeyboard
+#define _SoftKeyboard
+
+#include "ofMain.h"
+#include "SoftKeyboardKey.h"
+
+namespace ofxInterface {
+ struct SoftKeyboardSettings : NodeSettings {
+ shared_ptr font;
+ ofxFontStash2::Style style;
+
+ ofColor bgColor = ofColor(0, 0);
+ ofColor colorActive = ofColor(255);
+ ofColor colorInactive = ofColor(128);
+ ofColor colorSelected = ofColor(128);
+
+ int borderRadius = 8;
+ int borderWidth = 4;
+ int margin = 12;
+ int padding = 8;
+
+ string layout = "de";
+
+ map textureKeys;
+
+ };
+
+
+class SoftKeyboard : public Node{
+
+public:
+ virtual void setup(SoftKeyboardSettings s);
+ virtual void draw();
+
+ void setActiveKeyset(string id);
+
+ void onKeyPressed(ofKeyEventArgs& event);
+ void onKeyReleased(ofKeyEventArgs& event);
+
+ ofEvent keyPressed;
+ ofEvent keyReleased;
+
+protected:
+ void addSet(string keySet, vector> keys);
+ void addLine(Node* keySet, vector keys, vector widthKeys, int y);
+ void addLine(Node* keySet, vector keys, int y);
+ void addKey(Node* keySet, int keyValue, int x, int y, int w, int h);
+
+ map> > getKeyLayout(string layout);
+
+ map