This is an addon for openFrameworks (oF) called ofxComponent. A convenient tool for creating UIs within oF was lacking, so this addon was created.
The purpose of this tool is to simplify the organization of object hierarchies (parent-child relationships) in UI components, such as using relative mouse coordinates within each object and organizing object position relationships within a relative coordinate system.
The drawing order is also considered, ensuring that parent objects are drawn before child objects, making it easier to create structures such as UI panels with buttons and views. Specifically, you can set relative positions using the setPos() method, obtain your own coordinates relative to the parent using getPos(), and obtain relative mouse coordinates within your coordinate system using getMousePos().
- It doesn't depend on any other addons.
- of0.10.1 or later
To get started, please refer to the examples provided. To use this tool, you need to inherit all objects you create from the ofxComponentBase class and manage them using shared_ptr. Please use shared_ptr with caution, paying attention to its features. Establish parent-child relationships using the setChild() method to link parent and child objects. Parent-child relationships can change during runtime.
There is also an ofxComponentManager class to manage the entire object hierarchy. You need to create only one instance of this class and set it as the top-level parent:
// ofApp.h ofApp
shared_ptr<ofxComponentManager> manager;Define one instance like this, and then:
// ofApp.cpp ofApp::setup()
manager->setChild(yourComponent);
// ...and more children...
manager->setup();When removing an object, call destroy() and it will be removed during the next update. Since it is not removed immediately, you may need to use isDestroyed() to check whether the object has been removed as needed.
The ofxComponentBase class (header file provided) contains various methods and attributes to manage and control components, such as positioning, scaling, rotation, mouse and key events, and drawing constraints. It also includes methods for managing parent-child relationships, converting coordinate positions, and handling component destruction.
For more information on the usage and implementation details of ofxComponent, refer to the ofxComponentBase.h header file provided.
When you override these methods, you can handle events.
onStart();// is Called once before the first onUpdate() after the object is created.onUpdate();onDraw();
onKeyPressed(ofKeyEventArgs&);onKeyReleased(ofKeyEventArgs&);onMouseMoved(ofMouseEventArgs&);onMousePressed(ofMouseEventArgs&);onMouseDragged(ofMouseEventArgs&);onMouseReleased(ofMouseEventArgs&);onDragEvent(ofDragInfo&);
onMousePressed(ofMouseEventArgs&): Called when mouse is pressed anywhere on the component, even if other components are layered on toponMousePressedOverComponent(ofMouseEventArgs&): Called only when the component is the topmost (visible) component at the click position
Use onMousePressedOverComponent for button-like interactions where you want clicks to only register on the visible top layer.
isMouseOver(): Returns true if the mouse cursor is over this component AND this component is the topmost at that position- Useful for implementing hover states in UI elements
- Automatically handles layer checking - no need to manually verify if other components are on top
Components provide built-in ofEvent instances for standard event handling:
// Built-in events you can listen to:
ofEvent<void> mousePressedOverComponentEvents; // Triggered only for topmost component
ofEvent<ofMouseEventArgs> mousePressedEvents; // Triggered for any component under cursor
// ... and more for other mouse eventsExample usage:
// In your ofApp setup:
ofAddListener(myButton->mousePressedOverComponentEvents, this, &ofApp::onButtonClicked);
// Event handler method:
void ofApp::onButtonClicked() {
// Handle button click
}ofxComponent provides a built-in timer system for scheduling delayed function execution. This is particularly useful for animations, UI transitions, auto-hide functionality, and other time-based behaviors.
Schedule a function to execute after a specified delay:
// Basic usage - execute after 2 seconds
auto timer = addTimerFunction([]() {
ofLog() << "Timer executed!";
}, 2.0f);
// With component context
auto timer = addTimerFunction([this]() {
this->hide(); // Hide this component after delay
}, 1.5f);The timer returns a shared_ptr<Timer> that you can use to control the timer later.
Pause or resume ALL timers associated with a component:
setTimerPaused(true); // Pause all timers in this component
setTimerPaused(false); // Resume all timers in this component
// Check if timers are paused
if (isTimerPaused()) {
// Component timers are paused
}This automatically applies to:
- All existing timers in the component
- All child components (hierarchical pausing)
- New timers created while paused will inherit the paused state
Cancel all pending timers in a component:
clearTimerFunctions(); // Safely cancel all timersThis is the recommended way to stop all timer activity, especially when components are being destroyed or reset.
You can control individual timers using the returned timer reference:
// Store timer reference
auto myTimer = addTimerFunction([this]() {
// Timer function
}, 3.0f);
// Later, control the individual timer
myTimer->cancel(); // Cancel this specific timer
myTimer->setPaused(true); // Pause this specific timer
myTimer->setPaused(false); // Resume this specific timer
// Check timer state
if (myTimer->isPaused()) {
// Timer is paused
}
if (myTimer->isDone()) {
// Timer has been executed or cancelled
}- Time units: All timer functions use seconds (not milliseconds)
- Automatic cleanup: Executed and cancelled timers are automatically removed
- Pause preservation: When paused timers resume, they maintain their remaining time
- Hierarchical control: Parent component pause state affects all children
- Memory safe: Uses
shared_ptrandweak_ptrto prevent memory leaks - Thread safe: Designed for single-threaded openFrameworks usage
void showToast(const string& message, float duration = 2.0f) {
isVisible = true;
displayText = message;
// Cancel any existing hide timer
if (hideTimer) hideTimer->cancel();
// Schedule auto-hide
hideTimer = addTimerFunction([this]() {
isVisible = false;
}, duration);
}void startAnimationSequence() {
// Step 1: Fade in
addTimerFunction([this]() {
startFadeIn();
}, 0.0f);
// Step 2: Wait, then move
addTimerFunction([this]() {
startMovement();
}, 1.0f);
// Step 3: Final action
addTimerFunction([this]() {
onAnimationComplete();
}, 3.0f);
}void pauseAllActivity() {
setTimerPaused(true); // Pauses ALL timers in this component and children
}
void resumeAllActivity() {
setTimerPaused(false); // Resumes ALL timers, maintaining remaining time
}
void stopAllActivity() {
clearTimerFunctions(); // Cancels ALL timers permanently
}See exampleTimer for a complete working example that demonstrates:
- Toast messages with auto-hide timers
- Sequential message chains with pause/resume/stop controls
- Timer cancellation and management
- Event-driven timer control using component events
setPos(ofVec2f pos);// To set the relative position of an objectgetPos();// To get an object's position relative to its parentgetMousePos();// To get the mouse position relative to the object's coordinate systemsetChild(shared_ptr<ofxComponentBase> child);// To establish a parent-child relationship between objectsgetChildren();// To get a list of child objectssetActive(bool active);// To set an object as active or inactive
This addon includes several examples to help you get started:
A simple demonstration of basic component usage and hierarchy.
A comprehensive demonstration of the timer system featuring:
- Toast messages with auto-hide functionality
- Sequential message chains with pause/resume/stop controls
- Event-driven interactions using
mousePressedOverComponentEvents - Proper timer management and cancellation
This example is particularly useful for learning time-based UI interactions and component event handling.
#include "ofxComponent.h"
using namespace ofxComponent;
class MyComponent : public ofxComponentBase {
void onStart() override {
// set component size
setWidth(200);
setHeight(100);
// set relative position
setPos(50, 50);
}
void onDraw() override {
// draw background
ofSetColor(ofColor::yellow);
// getWidth(), getHeight() returns component size.
ofDrawRectangle(0, 0, getWidth(), getHeight());
}
void onMousePressedOverComponent(ofMouseEventArgs& mouse) override {
ofLog() << "Clicked";
}
};Include the header and use the namespace:
#include "ofxComponent.h"
using namespace ofxComponent;
class ofApp : public ofBaseApp{
public:
void setup();
void update();
void draw();
// ... other standard ofApp methods ...
private:
// IMPORTANT: Create manager as shared_ptr, NOT using getInstance()
shared_ptr<ofxComponentManager> manager;
// Your components
shared_ptr<MyComponent> myComp;
};CRITICAL: Follow this exact setup pattern for proper component system initialization:
void ofApp::setup(){
// 1. Create the manager as shared_ptr
manager = make_shared<ofxComponentManager>();
// 2. Set the manager's drawing area (typically full window)
manager->setRect(0, 0, ofGetWidth(), ofGetHeight());
// 3. Create your components
myComp = make_shared<MyComponent>();
// 4. Add components to manager (NOT to a separate root component)
manager->addChild(myComp);
// 5. IMPORTANT: Call setup() AFTER adding all components
// This registers the manager's event listeners with openFrameworks
manager->setup();
}
// IMPORTANT: Leave these methods empty!
// The manager automatically handles all events after setup() is called
void ofApp::update() {
// Empty - manager handles update automatically
}
void ofApp::draw() {
// Empty - manager handles drawing automatically
}
void ofApp::mousePressed(int x, int y, int button) {
// Empty - manager handles mouse events automatically
}
// ... all other ofApp event methods can be left empty- Use shared_ptr: Always create as
make_shared<ofxComponentManager>(), never usegetInstance() - Set drawing area: Call
setRect()to define the manager's coordinate space - Add children directly: Add components to the manager, not to a separate root component
- Call setup() last: This registers automatic event listeners with openFrameworks
- Empty ofApp methods: After
manager->setup(), you don't need to forward events manually
// DON'T: Use getInstance() pattern
auto manager = ofxComponentManager::getInstance(); // This doesn't exist
// DON'T: Manually forward events in ofApp methods
void ofApp::update() {
manager->update(); // Unnecessary - manager handles this automatically
}
// DON'T: Create separate root components
auto root = make_shared<ofxComponentBase>();
manager->addChild(root);
root->addChild(myComp); // Instead, add directly to managerThis is in development, so there may be major changes from time to time.
MIT