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/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 73e8077..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(); } @@ -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..b1f775b 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,10 +170,12 @@ class ofxDatGuiComponent : public ofxDatGuiInteractiveObject void drawBackground(); void positionLabel(); void setComponentStyle(const ofxDatGuiTheme* t); + + float mScrollOffsetY = 0; private: - static unique_ptr theme; + static std::unique_ptr theme; }; diff --git a/src/ofxDatGui.cpp b/src/ofxDatGui.cpp index b32076c..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,93 +850,146 @@ 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; igetHeight()); - 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..14cac60 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 enableFboMode(bool enable, int width, int height); + ofTexture& getFboTexture(); + + void setScrollY(float y); + void scroll(float deltaY); + float getScrollY() const; private: @@ -128,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(); @@ -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); }; diff --git a/src/themes/ofxDatGuiTheme.h b/src/themes/ofxDatGuiTheme.h index 423ade9..373f5b7 100755 --- a/src/themes/ofxDatGuiTheme.h +++ b/src/themes/ofxDatGuiTheme.h @@ -170,13 +170,13 @@ class ofxDatGuiTheme{ struct { // general rules that are shared by all components // - float width = 270.0f; - float height = 26.0f; + float width = 520.0f; // 270.0f + float height = 32.0f; // 26.0f float padding = 2.0f; float vMargin = 1.0f; // vertical spacing between gui components // - float iconSize = 10.0f; - float labelWidth = 95.0f; - float labelMargin = 12.0f; + float iconSize = 10.0f; // 4.0f + float labelWidth = 200.0f; // 95.0f + float labelMargin = 6.0f; // 12.0f float breakHeight = 3.0f; bool upperCaseLabels = true; @@ -218,7 +218,7 @@ class ofxDatGuiTheme{ static string AssetPath; struct { - int size = 6; + int size = 12; string file = AssetPath + "ofxbraitsch/fonts/Verdana.ttf"; shared_ptr ptr; } font;