diff --git a/Graphics/private/Canvas.cpp b/Graphics/private/Canvas.cpp index 2346b9d..07b1f00 100644 --- a/Graphics/private/Canvas.cpp +++ b/Graphics/private/Canvas.cpp @@ -50,13 +50,21 @@ void Canvas::rect(const RectF& rec, const RGBA& col, halnf round) { nvgFill(mContext->vg); } +void Canvas::circle(const Vec2F& pos, halnf size, const RGBA& col) { + nvgBeginPath(mContext->vg); + nvgCircle(mContext->vg, pos.x, pos.y, size); + + nvgFillColor(mContext->vg, { col.r, col.g, col.b, col.a }); + nvgFill(mContext->vg); +} + void Canvas::pushClamp(const RectF& rec) { RectF intersection = rec; if (mScissors.size()) { mScissors.last().calcIntersection(rec, intersection); } nvgScissor(mContext->vg, intersection.x, intersection.y, intersection.z, intersection.w); - mScissors.append(rec); + mScissors.append(intersection); } void Canvas::popClamp() { diff --git a/Graphics/public/Graphics.hpp b/Graphics/public/Graphics.hpp index dfa1650..e13dc09 100644 --- a/Graphics/public/Graphics.hpp +++ b/Graphics/public/Graphics.hpp @@ -58,6 +58,7 @@ namespace tp { void pushClamp(const RectF& rec); void popClamp(); void rect(const RectF& rec, const RGBA& col, halnf round = 0); + void circle(const Vec2F& pos, halnf size, const RGBA& col); void text(const char*, const RectF&, halnf size, Align, halnf padding, const RGBA&); ImageHandle createImageFromTextId(ualni id, Vec2F size); diff --git a/Math/public/Vec.hpp b/Math/public/Vec.hpp index 27d502d..7a29349 100644 --- a/Math/public/Vec.hpp +++ b/Math/public/Vec.hpp @@ -374,8 +374,8 @@ namespace tp { } void clamp(const Vec& min, const Vec& max) { - tp::clamp(x, min.x, max.x); - tp::clamp(y, min.y, max.y); + x = tp::clamp(x, min.x, max.x); + y = tp::clamp(y, min.y, max.y); } }; diff --git a/Widgets/examples/SimpleGUI.hpp b/Widgets/examples/SimpleGUI.hpp index 5ced83e..e1cc85b 100644 --- a/Widgets/examples/SimpleGUI.hpp +++ b/Widgets/examples/SimpleGUI.hpp @@ -3,29 +3,23 @@ namespace tp { template - class SimpleWidget : public ScrollableWindow { + class SimpleWidget : public CollapsableMenu { public: SimpleWidget() { - this->addWidget(&mLabel); - this->addWidget(&mButton); - this->addWidget(&mSlider); - this->addWidget(&mCollapsableMenu); + this->addWidgetToMenu(&mInMenuButton1); + this->addWidgetToMenu(&mInMenuButton2); - mCollapsableMenu.addWidgetToMenu(&mInMenuButton1); - mCollapsableMenu.addWidgetToMenu(&mInMenuButton2); + this->addWidgetToMenu(&mLabel); + this->addWidgetToMenu(&mSlider); mInMenuButton1.mLabel.mLabel = "Button1"; mInMenuButton2.mLabel.mLabel = "Button2"; - - this->mArea.size = { 500, 500 }; } private: - CollapsableMenu mCollapsableMenu; ButtonWidget mInMenuButton1; ButtonWidget mInMenuButton2; - ButtonWidget mButton; LabelWidget mLabel; NamedSliderWidget mSlider; }; @@ -36,10 +30,17 @@ namespace tp { SimpleWidget2() { this->mChildWidgets.pushBack(&mFloating); mFloating.addWidgetToMenu(&mWidget); + mFloating.addWidgetToMenu(&mWidget2); + + // this->mChildWidgets.pushBack(&mFloating2); + // mFloating2.addWidgetToMenu(&mWidget2); } private: FloatingWidget mFloating; SimpleWidget mWidget; + + FloatingWidget mFloating2; + SimpleWidget mWidget2; }; } \ No newline at end of file diff --git a/Widgets/public/CollapsableMenu.hpp b/Widgets/public/CollapsableMenu.hpp index 3ba76f5..cbefa16 100644 --- a/Widgets/public/CollapsableMenu.hpp +++ b/Widgets/public/CollapsableMenu.hpp @@ -15,18 +15,10 @@ namespace tp { void eventProcess(const Events&) override { if (mHeader.isReleased()) { - mCollapsed = !mCollapsed; + toggleCollapsed(); } - mHeader.setArea(getHeaderRect()); - - mBody.mEnable = !mCollapsed; - if (mCollapsed) { - this->mArea.size.y = headerHeight; - } else { - this->mArea.size.y = headerHeight + getBodyRect().size.y + mPadding * 2; - mBody.setArea(getBodyRect()); - } + updateGeometry(); } void eventDraw(Canvas& canvas) override { @@ -43,6 +35,36 @@ namespace tp { mHeader.mLabel = string; } + void toggleCollapsed() { + setCollapsed(!getCollapsed()); + } + + void setCollapsed(bool collapsed) { + if (mLocked) return; + if (collapsed && !mCollapsed) mPrevHeight = this->mArea.size.y; + if (!collapsed && mCollapsed) this->mArea.size.y = mPrevHeight; + mCollapsed = collapsed; + } + + [[nodiscard]] bool getCollapsed() const{ + return mCollapsed; + } + + void updateGeometry() { + mHeader.setArea(getHeaderRect()); + + mBody.mEnable = !mCollapsed; + if (mCollapsed) { + this->mArea.size.y = headerHeight; + } else { + if (mAdjustHeight) { + this->mArea.size.y = headerHeight + getBodyRect().size.y + mPadding * 2; + } + + mBody.setArea(getBodyRect()); + } + } + private: RectF getHeaderRect() { RectF out = { this->mArea.pos, { this->mArea.size.x, headerHeight } }; @@ -51,9 +73,13 @@ namespace tp { RectF getBodyRect() { RectF out = { { this->mArea.pos.x, this->mArea.pos.y + headerHeight }, - { this->mArea.size.x, mBody.getContentSize() } + { this->mArea.size.x, this->mArea.size.y - headerHeight - mPadding } }; + out.size.y -= mBorderSize * 2; + + if (mAdjustHeight) out.size.y = mBody.getContentSize(); + if (mBody.getContentSize()) { out = out.shrink(mPadding); out.size.y += mPadding * 2 + 1; @@ -86,6 +112,10 @@ namespace tp { halnf mBorderSize = 0; halnf mPadding = 0; + halnf mPrevHeight = 200; + bool mCollapsed = true; + bool mLocked = false; + bool mAdjustHeight = true; }; } \ No newline at end of file diff --git a/Widgets/public/FloatingWidget.hpp b/Widgets/public/FloatingWidget.hpp index 5c7065f..b81d116 100644 --- a/Widgets/public/FloatingWidget.hpp +++ b/Widgets/public/FloatingWidget.hpp @@ -8,39 +8,90 @@ namespace tp { public: FloatingWidget() { this->mArea = { 0, 0, 300, 300 }; - } - - virtual void procWrapper(const Events& events, const RectF& parentArea) override { - if (!this->mEnable) return; - - this->checkVisibility(events, parentArea); - - if (!this->mVisible) return; - - this->checkFocus(events); - - this->checkClicked(events); - - mParentArea = parentArea; - this->eventProcess(events); - - for (auto child : this->mChildWidgets) { - child->procWrapper(events, this->getArea()); - } + this->mAdjustHeight = false; } void eventProcess(const Events& events) override { - CollapsableMenu::eventProcess(events); + checkFloating(events); + checkResizing(events); - if (this->mHeader.isHolding()) { - this->mArea.pos += events.getPointerDelta(); - if (!this->mArea.isEnclosedIn(mParentArea)) { - this->mArea.pos -= events.getPointerDelta(); - } + mActionStartRelativePos = events.getPointer() - this->mArea.pos; + + CollapsableMenu::eventProcess(events); + } + + void eventDraw(Canvas& canvas) override { + CollapsableMenu::eventDraw(canvas); + + if (!this->getCollapsed()) { + auto rect = getResizeHandle(); + canvas.rect(rect, mResizeHandleColor, 0); + canvas.circle(rect.pos - this->mBorderSize, rect.w, this->mMenuColor); } } - RectF mParentArea; + void eventUpdateConfiguration(WidgetManager& wm) override { + CollapsableMenu::eventUpdateConfiguration(wm); + wm.setActiveId("FloatingWidget"); + + mResizeHandleSize = wm.getNumber("ResizeHandleSize", 15); + mResizeHandleColor = wm.getColor("ResizeHandleColor", RGBA(0.16, 0.16, 0.16, 1.f)); + } + + private: + void checkFloating(const Events& events) { + if (this->mHeader.isHolding() && events.getPointerDelta().length2() > 4 ) { + mFloating = true; + } + + if (mFloating && this->mHeader.isReleased()) { + mFloating = false; + this->mHeader.clearEvents(); + } + + if (mFloating) { + auto relativePos = events.getPointer() - this->mArea.pos; + this->mArea.pos += relativePos - mActionStartRelativePos; + } + } + + void checkResizing(const Events& events) { + if (this->getCollapsed()) return; + + if (events.isPressed(InputID::MOUSE1) && getResizeHandle().isInside(events.getPointer())) { + mResizing = true; + } + + if (events.isReleased(InputID::MOUSE1)) { + mResizing = false; + } + + if (mResizing) { + auto relativePos = events.getPointer() - this->mArea.pos; + this->mArea.size += relativePos - mActionStartRelativePos; + } + + if (!this->mCollapsed) { + this->mArea.size.clamp(mMinSize, { FLT_MAX, FLT_MAX }); + } + } + + RectF getResizeHandle() { + auto size = Vec2F(mResizeHandleSize); + auto pos = this->mArea.pos + this->mArea.size - size; + return { pos, size }; + } + + private: + Vec2F mMinSize = { 70, 70 }; + + halnf mResizeHandleSize = 10; + RGBA mResizeHandleColor = {}; + + bool mFloating = false; + bool mResizing = false; + + Vec2F mActionStartRelativePos = {}; }; } \ No newline at end of file diff --git a/Widgets/public/WidgetBase.hpp b/Widgets/public/WidgetBase.hpp index 1656b88..64b2f18 100644 --- a/Widgets/public/WidgetBase.hpp +++ b/Widgets/public/WidgetBase.hpp @@ -134,8 +134,15 @@ namespace tp { bool isReleased() const { return mReleased; } bool isHolding() const { return mHolding; } + void clearEvents() { + mReleased = false; + mPressed = false; + mHolding = false; + } + public: RectF mArea; + List*> mChildWidgets; bool mVisible = false; @@ -145,5 +152,7 @@ namespace tp { bool mHolding = false; bool mPressed = false; bool mReleased = false; + + // Vec2F mPressedRelativeLocation; }; } \ No newline at end of file