From cb0cdc68328f44b0c260e289da3f05ab5559c8c1 Mon Sep 17 00:00:00 2001 From: Ephraim Wegner Date: Mon, 9 Jun 2025 14:27:52 +0200 Subject: [PATCH 1/3] Updated ofxDatGui.cpp and header. Enables scrolling in an Fbo --- src/ofxDatGui.cpp | 95 +++++++++++++++++++++++++++++++++++------------ src/ofxDatGui.h | 12 ++++++ 2 files changed, 83 insertions(+), 24 deletions(-) diff --git a/src/ofxDatGui.cpp b/src/ofxDatGui.cpp index b32076c..ae4585d 100644 --- a/src/ofxDatGui.cpp +++ b/src/ofxDatGui.cpp @@ -906,33 +906,80 @@ void ofxDatGui::update() trash.clear(); } -void ofxDatGui::draw() -{ - if (mVisible == false) return; - ofPushStyle(); - ofFill(); - ofSetColor(mGuiBackground, mAlpha * 255); - if (mExpanded == false){ - ofDrawRectangle(mPosition.x, mPosition.y, mWidth, mGuiFooter->getHeight()); - mGuiFooter->draw(); - } else{ - ofDrawRectangle(mPosition.x, mPosition.y, mWidth, mHeight - mRowSpacing); - for (int i=0; idraw(); - // color pickers overlap other components when expanded so they must be drawn last // - for (int i=0; idrawColorPicker(); + void ofxDatGui::draw() + { + if (mVisible == false) return; + ofPushStyle(); + ofPushMatrix(); + ofTranslate(0, -mScrollOffset.y); // apply vertical scroll + ofFill(); + ofSetColor(mGuiBackground, mAlpha * 255); + if (mExpanded == false){ + ofDrawRectangle(mPosition.x, mPosition.y, mWidth, mGuiFooter->getHeight()); + mGuiFooter->draw(); + } else { + ofDrawRectangle(mPosition.x, mPosition.y, mWidth, mHeight - mRowSpacing); + for (int i=0; idraw(); + // color pickers overlap other components when expanded so they must be drawn last // + for (int i=0; idrawColorPicker(); + } + ofPopMatrix(); + ofPopStyle(); + } + + void ofxDatGui::onDraw(ofEventArgs &e) + { + if (!mVisible) return; + + if (mUseFbo) { + mFbo.begin(); + ofClear(0, 0, 0, 0); // transparent + draw(); // draw GUI into FBO + mFbo.end(); } - ofPopStyle(); -} + else + { + draw(); // original behavior + } + } -void ofxDatGui::onDraw(ofEventArgs &e) -{ - draw(); -} + void ofxDatGui::enableFboMode(bool enable, int width, int height) + { + mUseFbo = enable; + if (enable) { + mFboSize.set(width, height); + mFbo.allocate(width, height, GL_RGBA); + mFbo.begin(); + ofClear(0, 0, 0, 0); + mFbo.end(); + } + } -void ofxDatGui::onUpdate(ofEventArgs &e) -{ - update(); -} + ofTexture& ofxDatGui::getFboTexture() + { + return mFbo.getTexture(); + } + + void ofxDatGui::setScrollY(float y) + { + mScrollOffset.y = y; + } + + void ofxDatGui::scroll(float deltaY) + { + float maxScroll = std::max(0.0f, mHeight - mFboSize.y); + mScrollOffset.y = ofClamp(mScrollOffset.y + deltaY, 0.0f, maxScroll); + } + + float ofxDatGui::getScrollY() const + { + return mScrollOffset.y; + } + + void ofxDatGui::onUpdate(ofEventArgs &e) + { + update(); + } void ofxDatGui::onWindowResized(ofResizeEventArgs &e) { diff --git a/src/ofxDatGui.h b/src/ofxDatGui.h index 777a92d..d342bcc 100644 --- a/src/ofxDatGui.h +++ b/src/ofxDatGui.h @@ -96,6 +96,13 @@ class ofxDatGui : public ofxDatGuiInteractiveObject ofxDatGuiValuePlotter* getValuePlotter(string label, string folder = ""); ofxDatGuiFolder* getFolder(string label); ofxDatGuiDropdown* getDropdown(string label); + + void ofxDatGui::enableFboMode(bool enable, int width, int height); + ofTexture& getFboTexture(); + + void setScrollY(float y); + void scroll(float deltaY); + float getScrollY() const; private: @@ -153,4 +160,9 @@ class ofxDatGui : public ofxDatGuiInteractiveObject void onColorPickerEventCallback(ofxDatGuiColorPickerEvent e); void onMatrixEventCallback(ofxDatGuiMatrixEvent e); + ofFbo mFbo; + bool mUseFbo = false; + ofVec2f mFboSize; + + ofVec2f mScrollOffset = ofVec2f(0, 0); }; From 8deccc32f51c46362019349304d92b487b61ad0a Mon Sep 17 00:00:00 2001 From: Ephraim Wegner Date: Mon, 16 Jun 2025 17:40:10 +0200 Subject: [PATCH 2/3] Add scroll offset propagation for mouse interaction in scrollable GUI --- src/components/ofxDatGuiScrollView.h | 3 +- src/core/ofxDatGuiComponent.cpp | 6 ++- src/core/ofxDatGuiComponent.h | 4 ++ src/ofxDatGui.cpp | 70 ++++++++++++++++------------ src/themes/ofxDatGuiTheme.h | 12 ++--- 5 files changed, 56 insertions(+), 39 deletions(-) diff --git a/src/components/ofxDatGuiScrollView.h b/src/components/ofxDatGuiScrollView.h index 9b45619..17aaddd 100644 --- a/src/components/ofxDatGuiScrollView.h +++ b/src/components/ofxDatGuiScrollView.h @@ -48,7 +48,6 @@ class ofxDatGuiScrollViewItem : public ofxDatGuiButton { class ofxDatGuiScrollView : public ofxDatGuiComponent { public: - ofxDatGuiScrollView(string name, int nVisible = 6) : ofxDatGuiComponent(name) { mAutoHeight = true; @@ -226,7 +225,7 @@ class ofxDatGuiScrollView : public ofxDatGuiComponent { { mBackground = color; } - + /* update & draw */ diff --git a/src/core/ofxDatGuiComponent.cpp b/src/core/ofxDatGuiComponent.cpp index 73e8077..e37a158 100644 --- a/src/core/ofxDatGuiComponent.cpp +++ b/src/core/ofxDatGuiComponent.cpp @@ -352,7 +352,7 @@ void ofxDatGuiComponent::update(bool acceptEvents) { if (acceptEvents && mEnabled && mVisible){ bool mp = ofGetMousePressed(); - ofPoint mouse = ofPoint(ofGetMouseX() - mMask.x, ofGetMouseY() - mMask.y); + ofPoint mouse = ofPoint(ofGetMouseX() - mMask.x, ofGetMouseY() - mMask.y + mScrollOffsetY); if (hitTest(mouse)){ if (!mMouseOver){ onMouseEnter(mouse); @@ -502,4 +502,8 @@ void ofxDatGuiComponent::onWindowResized(ofResizeEventArgs &e) onWindowResized(); } +void ofxDatGuiComponent::setScrollOffsetY(float offsetY) +{ + mScrollOffsetY = offsetY; +} diff --git a/src/core/ofxDatGuiComponent.h b/src/core/ofxDatGuiComponent.h index c301e16..c99cec4 100644 --- a/src/core/ofxDatGuiComponent.h +++ b/src/core/ofxDatGuiComponent.h @@ -99,6 +99,8 @@ class ofxDatGuiComponent : public ofxDatGuiInteractiveObject void onWindowResized(ofResizeEventArgs &e); static const ofxDatGuiTheme* getTheme(); + + void setScrollOffsetY(float offsetY); protected: @@ -168,6 +170,8 @@ class ofxDatGuiComponent : public ofxDatGuiInteractiveObject void drawBackground(); void positionLabel(); void setComponentStyle(const ofxDatGuiTheme* t); + + float mScrollOffsetY = 0; private: diff --git a/src/ofxDatGui.cpp b/src/ofxDatGui.cpp index ae4585d..e2c5954 100644 --- a/src/ofxDatGui.cpp +++ b/src/ofxDatGui.cpp @@ -1,4 +1,4 @@ -/* +/* Copyright (C) 2015 Stephen Braitsch [http://braitsch.io] Permission is hereby granted, free of charge, to any person obtaining a copy @@ -306,7 +306,7 @@ ofxDatGuiSlider* ofxDatGui::addSlider(ofParameter& p) ofxDatGuiSlider* ofxDatGui::addSlider(string label, float min, float max) { -// default to halfway between min & max values // + // default to halfway between min & max values // ofxDatGuiSlider* slider = addSlider(label, min, max, (max+min)/2); return slider; } @@ -831,14 +831,18 @@ void ofxDatGui::update() { if (!mVisible) return; - // check if we need to update components // - for (int i=0; isetOpacity(mAlpha); if (mThemeChanged) items[i]->setTheme(mTheme); if (mWidthChanged) items[i]->setWidth(mWidth, mLabelWidth); if (mAlignmentChanged) items[i]->setLabelAlignment(mAlignment); + items[i]->setScrollOffsetY(mScrollOffset.y); // ✅ Inject scroll offset } - + + if (mGuiHeader) mGuiHeader->setScrollOffsetY(mScrollOffset.y); // ✅ optional + if (mGuiFooter) mGuiFooter->setScrollOffsetY(mScrollOffset.y); // ✅ optional + if (mThemeChanged || mWidthChanged) layoutGui(); mTheme = nullptr; @@ -846,63 +850,69 @@ void ofxDatGui::update() mWidthChanged = false; mThemeChanged = false; mAlignmentChanged = false; - - // check for gui focus change // - if (ofGetMousePressed() && mActiveGui->mMoving == false){ + + // Check for gui focus change // + if (ofGetMousePressed() && mActiveGui->mMoving == false) { ofPoint mouse = ofPoint(ofGetMouseX(), ofGetMouseY()); - for (int i=mGuis.size()-1; i>-1; i--){ - // ignore guis that are invisible // - if (mGuis[i]->getVisible() && mGuis[i]->hitTest(mouse)){ + for (int i = mGuis.size() - 1; i > -1; i--) { + if (mGuis[i]->getVisible() && mGuis[i]->hitTest(mouse)) { if (mGuis[i] != mActiveGui) mGuis[i]->focus(); break; } } } - if (!getFocused() || !mEnabled){ - // update children but ignore mouse & keyboard events // - for (int i=0; iupdate(false); - } else { + if (!getFocused() || !mEnabled) { + // Not focused, update but ignore input + for (int i = 0; i < items.size(); i++) { + items[i]->update(false); + } + } + else { mMoving = false; mMouseDown = false; - // this gui has focus so let's see if any of its components were interacted with // - if (mExpanded == false){ + + if (!mExpanded) { mGuiFooter->update(); mMouseDown = mGuiFooter->getMouseDown(); - } else{ + } + else { bool hitComponent = false; - for (int i=0; iupdate(true); if (items[i]->getFocused()) { hitComponent = true; mMouseDown = items[i]->getMouseDown(); - if (mGuiHeader != nullptr && mGuiHeader->getDraggable() && mGuiHeader->getFocused()){ - // track that we're moving to force preserve focus // + + if (mGuiHeader && mGuiHeader->getDraggable() && mGuiHeader->getFocused()) { mMoving = true; ofPoint mouse = ofPoint(ofGetMouseX(), ofGetMouseY()); moveGui(mouse - mGuiHeader->getDragOffset()); } - } else if (items[i]->getIsExpanded()){ - // check if one of its children has focus // - for (int j=0; jchildren.size(); j++) { - if (items[i]->children[j]->getFocused()){ + } + else if (items[i]->getIsExpanded()) { + for (int j = 0; j < items[i]->children.size(); j++) { + items[i]->children[j]->setScrollOffsetY(mScrollOffset.y); // child propagation + items[i]->children[j]->update(true); + if (items[i]->children[j]->getFocused()) { hitComponent = true; mMouseDown = items[i]->children[j]->getMouseDown(); break; } } } - } else{ - // update component but ignore mouse & keyboard events // + } + else { items[i]->update(false); if (items[i]->getFocused()) items[i]->setFocused(false); } } } } -// empty the trash // - for (int i=0; i ptr; } font; From 95a66bc8fafabfa336e1094e46e61622fe26b62e Mon Sep 17 00:00:00 2001 From: Ephraim Wegner Date: Wed, 1 Jul 2026 17:39:20 +0200 Subject: [PATCH 3/3] set namespace for std functions, Linux compatibility --- src/components/ofxDatGuiTextInputField.h | 2 +- src/core/ofxDatGuiComponent.cpp | 2 +- src/core/ofxDatGuiComponent.h | 2 +- src/ofxDatGui.h | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/ofxDatGuiTextInputField.h b/src/components/ofxDatGuiTextInputField.h index bab96f5..320953c 100644 --- a/src/components/ofxDatGuiTextInputField.h +++ b/src/components/ofxDatGuiTextInputField.h @@ -203,7 +203,7 @@ class ofxDatGuiTextInputField : public ofxDatGuiInteractiveObject{ } else if (key == OF_KEY_LEFT) { setCursorIndex(max( (int) mCursorIndex - 1, 0)); } else if (key == OF_KEY_RIGHT) { - setCursorIndex(min( mCursorIndex + 1, (unsigned int) mText.size())); + setCursorIndex(std::min( mCursorIndex + 1, (unsigned int) mText.size())); } else { // insert character at cursor position // setText(mText.substr(0, mCursorIndex) + (char)key + mText.substr(mCursorIndex)); diff --git a/src/core/ofxDatGuiComponent.cpp b/src/core/ofxDatGuiComponent.cpp index e37a158..2e023a7 100644 --- a/src/core/ofxDatGuiComponent.cpp +++ b/src/core/ofxDatGuiComponent.cpp @@ -85,7 +85,7 @@ ofxDatGuiType ofxDatGuiComponent::getType() const ofxDatGuiTheme* ofxDatGuiComponent::getTheme() { - if (theme == nullptr) theme = make_unique(true); + if (theme == nullptr) theme = std::make_unique(true); return theme.get(); } diff --git a/src/core/ofxDatGuiComponent.h b/src/core/ofxDatGuiComponent.h index c99cec4..b1f775b 100644 --- a/src/core/ofxDatGuiComponent.h +++ b/src/core/ofxDatGuiComponent.h @@ -175,7 +175,7 @@ class ofxDatGuiComponent : public ofxDatGuiInteractiveObject private: - static unique_ptr theme; + static std::unique_ptr theme; }; diff --git a/src/ofxDatGui.h b/src/ofxDatGui.h index d342bcc..14cac60 100644 --- a/src/ofxDatGui.h +++ b/src/ofxDatGui.h @@ -97,7 +97,7 @@ class ofxDatGui : public ofxDatGuiInteractiveObject ofxDatGuiFolder* getFolder(string label); ofxDatGuiDropdown* getDropdown(string label); - void ofxDatGui::enableFboMode(bool enable, int width, int height); + void enableFboMode(bool enable, int width, int height); ofTexture& getFboTexture(); void setScrollY(float y); @@ -135,7 +135,7 @@ class ofxDatGui : public ofxDatGuiInteractiveObject vector trash; static ofxDatGui* mActiveGui; static vector mGuis; - static unique_ptr theme; + static std::unique_ptr theme; void init(); void layoutGui();