##ofxDatGui
ofxDatGui is a simple to use, fully customizable, high-resolution graphical user interface for openFrameworks inspired by the popular JavaScript datgui interface.
##Features
ofxDatGui offers the following features & components:
- Click & Toggle (On/Off) Buttons
- Text Input Fields
- Color Pickers
- Range Sliders
- Dropdown Menus
- Button Matrices
- Coordinate Pads
- Value Plotters
- Waveform Monitors
- Framerate Monitor
- Variable Binding
- Completely Customizable
- Folders to group components together
- An optional header & footer that allow you to title the gui, collapse and drag it around
##Installation
ofxDatGui is built on top of C++11 and requires the new openFrameworks 0.9.0 pre-release which you can download here.
-
Once you've downloaded openFrameworks, clone or download this repository into your openFrameworks/addons directory.
-
Create a new project using the project generator and include ofxDatGui by selecting the
addons
button in the generator. -
Copy the
ofxdatgui_assets
directory in the root of this repository to your project's bin/data directory. This directory contains the fonts & icons used by ofxDatGui. -
Add ofxDatGui to your project by adding
#include "ofxDatGui.h"
to the top of yourofApp.h
file and you're ready to go!
##Getting Started
There are two ways to work with ofxDatGui. You can either instantiate each component on its own or consolidate them into a gui panel that you can drag around. The individual component examples included in this repository demonstrate how to use each on its own.
To create an ofxDatGui panel that groups components together simply pass in the X and Y coordinates where you would like it to live or use one of the convenient pre-defined anchors.
ofxDatGui* gui = new ofxDatGui( 100, 100 );
ofxDatGui* gui = new ofxDatGui( ofxDatGuiAnchor::TOP_LEFT );
ofxDatGui* gui = new ofxDatGui( ofxDatGuiAnchor::TOP_RIGHT );
Adding components to ofxDatGui is as simple as:
gui->addButton("Click!");
This generates a Basic Button with the label "Click!"
ofxDatGui currently offers the following interactive components:
Text Input
gui->addTextInput(string label, string value = "");
Basic Button
gui->addButton(string label);
Toggle Button
gui->addToggle(string label, bool enabled = true);
Range Slider
gui->addSlider(string label, float min, float max);
You can also set the starting value of the slider.
If this is not set it will default to halfway between the min and max values.
gui->addSlider(string label, float min, float max, float value);
Color Picker
gui->addColorPicker(string label, ofColor color = ofColor::black);
Dropdown Menu
vector<string> options = {"ONE", "TWO", "THREE", "FOUR"};
gui->addDropdown(options);
Value Plotter
gui->addValuePlotter(string label, float min, float max);
Waveform Monitor
gui->addWaveMonitor(string label, float frequency, float amplitude);
Value Plotters and Waveform Monitors support four different draw modes:
ofxDatGuiGraph::LINES
ofxDatGuiGraph::FILLED
ofxDatGuiGraph::POINTS
ofxDatGuiGraph::OUTLINE
ofxDatGuiValuePlotter* plotter = new ofxDatGuiValuePlotter("my plotter", 0, 100);
// set the drawmode to "points" //
plotter->setDrawMode(ofxDatGuiGraph::POINTS);
Button Matrix
gui->addMatrix(string label, int numButtons)
You can display numbered labels on the buttons by passing true
as the third argument.
gui->addMatrix(string label, int numButtons, bool showLabels)
2D Coordinate Pad
gui->add2dPad(string label, ofRectangle bounds);
The bounds parameter is optional and will default to the window dimensions if omitted.
##Utility Components
ofxDatGui also provides a few non-interactive utility components to assist with layout and application health monitoring.
Label
gui->addLabel(string label);
Framerate Monitor
gui->addFRM(float refreshFrequency = 1.0f);
Break
gui->addLabel("Above");
gui->addBreak(float height = 10.0f);
gui->addLabel("Stuck in the Middle");
gui->addBreak(float height = 10.0f);
gui->addLabel("Below");
##Headers & Footers
ofxDatGui also provides an optional header and footer that allows you to title your gui, drag it around and conveniently collapse and expand it. The AllComponentsGui
example offers a nice demonstration of their use.
gui->addHeader(":: Drag Me To Reposition ::");
gui->addFooter();
You can change the header's label just like any other component by calling:
gui->getHeader()->setLabel("PANEL 1");
Footers however have two labels that each map to the gui's expanded and collapsed states.
By default these labels are "Collapse Controls" and "Expand Controls" although you can override these defaults via:
gui->getFooter()->setLabelWhenExpanded("CLOSE PANEL 1");
gui->getFooter()->setLabelWhenCollapsed("EXPAND PANEL 1");
##Component Groups (Folders)
You can also group related components into folders. When constructing a folder pass in a label to name the folder and an optional color to help visually group its contents.
ofxDatGuiFolder* folder = gui->addFolder("My White Folder", ofColor::white);
folder->addTextInput("** Input", "A Nested Text Input");
folder->addSlider("** Slider", 0, 100);
folder->addToggle("** Toggle", false);
All components can be nested into folders with the exception of other folders and dropdowns.
Folders and dropdown menus can also be expanded and collapsed programmatically.
folder->expand();
folder->collapse();
Every gui->add*
method returns a pointer to an ofxDatGuiItem object that you can store in a variable for later manipulation.
ofxDatGuiTextInput* myInput;
myInput = gui->addTextInput("Name:", "Stephen");
myInput->setText("Freddy");
Every ofxDatGuiItem has a mutable label that can easily be retrieved or changed via:
item->getLabel();
item->setLabel(string label);
In addition some components have methods (typically getters & setters) that allow for the retrieval and manipulation of its unique properties.
Text Input
ofxDatGuiTextInput* myInput;
string myInput->getText();
myInput->setText(string text);
Toggle Button
ofxDatGuiToggle* myToggle;
myToggle->toggle();
myToggle->setEnabled(bool enable);
bool myToggle->getEnabled();
Range Slider
ofxDatGuiSlider* mySlider;
float mySlider->getScale();
mySlider->setScale(float scale); // a value between 0 & 1 //
float mySlider->getValue();
mySlider->setValue(float value); // a value between min & max //
Color Picker
ofxDatGuiColorPicker* myColorPicker;
ofColor myColorPicker->getColor();
myColorPicker->setColor(int hexValue);
myColorPicker->setColor(int r, int g, int b);
myColorPicker->setColor(ofColor color);
Dropdown Menu
ofxDatGuiDropdown* myDropdown;
int myDropdown->size();
myDropdown->select(int index);
ofxDatGuiDropdownOption* getChildAt(int index);
ofxDatGuiDropdownOption* myDropdown->getSelected();
myDropdown->expand();
myDropdown->collapse();
Value Plotter
ofxDatGuiValuePlotter* myPlotter;
myPlotter->setValue(float value);
myPlotter->setRange(float min, float max);
myPlotter->setSpeed(float speed);
myPlotter->setDrawMode(ofxDatGuiGraph drawMode);
Waveform Monitor
ofxDatGuiWaveMonitor* myWaveMonitor;
myWaveMonitor->setFrequency(float frequency);
myWaveMonitor->setAmplitude(float amplitude);
myWaveMonitor->setFrequencyLimit(float limit);
myWaveMonitor->setDrawMode(ofxDatGuiGraph drawMode);
Button Matrix
ofxDatGuiMatrix* myMatrix;
ofxDatGuiMatrixButton* myMatrix->getChildAt(int index);
vector<int> myMatrix->getSelected();
// returns a vector containing the indicies of the selected buttons //
myMatrix->clear(); // deselects all buttons in the matrix //
2d Coordinate Pad
ofxDatGui2dPad* my2dPad;
my2dPad->setBounds(ofRectangle bounds);
ofPoint my2dPad->getPosition();
// returns the current position within the bounds rectangle you gave it //
Note: All indicies are zero based so the first item in your ofxDatGui instance will have an index of 0, the second item will have an index of 1, the third item an index of 2 etc..
If you're lazy and don't feel like storing your components in variables you can easily retrieve them by name which by default is the same as whatever you set its label to.
ofxDatGuiButton* gui->getButton("My Button"); // button name
ofxDatGuiSlider* gui->getSlider("My Slider"); // slider name
Note: gui->getComponent
performs a case-insensitive lookup against the component's name so the following works fine as well.
ofxDatGuiButton* gui->getButton("my bUTTon"); // button name
ofxDatGuiSlider* gui->getSlider("mY sLiDEr"); // slider name
To change a component's label or name simply:
gui->getButton("My Button")->setName("b1");
gui->getButton("b1")->setLabel("Your Button");
If you have multiple components with the same name nested in separate folders just specify the folder to search.
ofxDatGuiButton* gui->getButton("Reset Button", "Folder 1");
ofxDatGuiButton* gui->getButton("Reset Button", "Folder 2");
Otherwise the function will return the first component whose name & type match the query.
ofxDatGui sliders & coordinate pads can also be bound to object variables. Just pass a reference to the variable to the component and set a range by which to limit its movement.
For example the following snippet binds a circle's position to a range slider and limits its movement to the width & height of the screen.
// draw a circle with a radius of 100px
// and position it in the middle of the screen //
circle = new Circle(100);
circle->x = ofGetWidth()/2;
circle->y = ofGetHeight()/2;
// instantiate a gui and a couple of range sliders //
gui = new ofxDatGui( ofxDatGuiAnchor::TOP_RIGHT );
ofxDatGuiSlider* sx = gui->addSlider("CIRCLE X", 0, 100);
ofxDatGuiSlider* sy = gui->addSlider("CIRCLE Y", 0, 100);
// bind the circle's x & y movement to the sliders //
sx->bind(&circle->x, 0, ofGetWidth());
sy->bind(&circle->y, 0, ofGetHeight());
ofxDatGuiEvents are designed to be as easy as possible to interact with.
To listen for an event simply register a callback to be executed when an event you care about is fired:
gui->onButtonEvent(this, &ofApp::onButtonEvent);
void onButtonEvent(ofxDatGuiButtonEvent e)
{
cout << "A button was clicked!" << endl;
}
Every callback you register will receive an event object that contains a pointer (called target) to the object that created the event.
gui->addButton("My Button");
gui->onButtonEvent(this, &ofApp::onButtonEvent);
void onButtonEvent(ofxDatGuiButtonEvent e)
{
cout << e.target->getLabel() << endl; // prints "My Button"
}
If you saved the pointer returned by gui->add*
in a variable you can compare it to the event target to decide how to handle the event.
ofxDatGuiButton* b1 = gui->addButton("Button 1");
ofxDatGuiButton* b2 = gui->addButton("Button 2");
gui->onButtonEvent(this, &ofApp::onButtonEvent);
void onButtonEvent(ofxDatGuiButtonEvent e)
{
if (e.target == b1){
cout << "Button 1 was clicked" << endl;
} else if (e.target == b2){
// button 2 was clicked, do something else //
}
}
However a more convenient way of determining which component dispatched the event is by using the built in is
operator.
gui->addButton("Button 1");
gui->addButton("Button 2");
void onButtonEvent(ofxDatGuiButtonEvent e)
{
if (e.target->is("button 1")){
cout << "Button 1 was clicked" << endl;
} else if (e.target->is("button 2")){
// button 2 was clicked, do something else //
}
}
This performs a case-insensitive search against the component's name which by default is the same as its label.
All events also contain additonal properties that allow convenient access to the state of the component that dispatched the event.
ofxDatGuiButtonEvent
ofxDatGuiButtonEvent e
bool e.enabled // the enabled state of a toggle button
ofxDatGuiSliderEvent
ofxDatGuiSliderEvent e
float e.value // current value of the slider
float e.scale // current scale of the slider
ofxDatGuiTextInputEvent
ofxDatGuiTextInputEvent e
string e.text // current text in the textfield
ofxDatGuiColorPickerEvent
ofxDatGuiColorPickerEvent e
ofColor e.color // the color of the picker
ofxDatGui2dPadEvent
ofxDatGui2dPadEvent e
float e.x // x coordinate within the pad's bounds rectangle
float e.y // y coordinate within the pad's bounds rectangle
ofxDatGuiDropdownEvent
ofxDatGuiDropdownEvent e
int e.child // the index of the selected option (zero based)
ofxDatGuiMatrixEvent
ofxDatGuiMatrixEvent e
int e.child // the index of the selected button (zero based)
bool e.enabled // enabled state of the selected button
Note: You can also retrieve these properties directly from the event target itself.
ofxDatGuiSliderEvent e
float value = e.target->getValue();
float scale = e.target->getScale();
##Customization
Just about every aspect of ofxDatGui's appearance can be tailored to meet your needs.
Customization is accomplished via extensible templates where you define how you want ofxDatGui to render.
Just create a class that extends ofxDatGuiTemplate
and pass it to ofxDatGui or any component via gui->setTemplate()
class myCustomTemplate : public ofxDatGuiTemplate
{
public:
myCustomTemplate() {
// row characteristics //
row.width = 540;
row.height = 52;
row.padding = 4;
row.spacing = 2;
row.stripeWidth = 4;
// font characteristics //
font.file = "PillGothic-Light.ttf"
font.size = 12;
font.highlightPadding = 4;
// gui / component colors //
row.color.bkgd = ofColor::lightGray;
row.color.label = ofColor::fromHex(0xFFD00B);
row.color.mouseOver = ofColor::fromHex(0x2FA1D6);
row.color.mouseOver = ofColor::fromHex(0x777777);
row.color.inputArea = ofColor::fromHex(0x303030);
// after everything is setup initialize the template //
init();
}
};
// assign this template to a gui instance //
gui = new ofxDatGui();
gui->setTemplate(new myCustomTemplate());
Take a look at the ofxDatGuiTemplate base class for a full list of everything that can be customized.
Aditionally all components and gui instances provide the following instance methods.
setWidth(int width);
setOrigin(int x, int y);
setOpacity(float opacity); // a value between 0 & 1 //
setVisible(bool visible);
setEnabled(bool enabled);
setAlignment(ofxDatGuiAlignment alignment);
##Label Alignment
You can set the label alignment for each component or on ofxDatGui itself via:
gui->setAlignment(ofxDatGuiAlignment alignment);
Valid values are:
ofxDatGuiAlignment::LEFT
ofxDatGuiAlignment::CENTER
ofxDatGuiAlignment::RIGHT
##Automatic Rendering
ofxDatGui automatically updates and draws itself on top of your application so there is no need to call update
or draw
on it. However you can easily disable this if you like via:
gui->setAutoDraw(bool enabled);
##Logging
ofxDatGui will softly warn you if you forget to attach an event listener to a component you've created or if you attempt to perform an action on a component that does not exist.
[ERROR] :: Component Not Found : GHOST BUTTON
[WARNING] :: Event Handler Not Set : MY BUTTON
However you can easily suppress these warnings by calling:
ofxDatGuiLog::quiet();
##Save & Load Settings
Coming Soon
##Additonal Notes
Thanks for reading all this and checking the project out.
I'm actively looking for people to help me test this and provide feedback to help shape ongoing development. If you'd like to see a feature prioritized or have any general questions or feedback please send me a message or open an issue here on Github.
Thanks!