diff --git a/Math/public/Rect.hpp b/Math/public/Rect.hpp index 55ca00e..f200dda 100644 --- a/Math/public/Rect.hpp +++ b/Math/public/Rect.hpp @@ -253,7 +253,7 @@ namespace tp { return out; } - Vec2 center() { return pos + size / 2.f; } + Vec2 center() const { return pos + size / 2.f; } // splits by Factor Horizontally returning Left rect Rect splitByFactorHL(halnf factor) { diff --git a/WidgetsNew/examples/Example.cpp b/WidgetsNew/examples/Example.cpp index effd8de..7d3a430 100644 --- a/WidgetsNew/examples/Example.cpp +++ b/WidgetsNew/examples/Example.cpp @@ -67,34 +67,34 @@ public: } void setup3() { - mRootWidget.setRootWidget(&mDesktopLayout); - mDesktopLayout.addChild(&mFloatingWidget); + mRootWidget.setRootWidget(&mWidget); + mWidget.addChild(&mFloatingWidget); mFloatingWidget.addChild(&mButton); mFloatingWidget.addChild(&mButton2); mFloatingWidget.addChild(&mButton3); - mButton.setSizePolicy(Widget::SizePolicy::Minimal, Widget::SizePolicy::Expanding); - mButton2.setSizePolicy(Widget::SizePolicy::Minimal, Widget::SizePolicy::Expanding); - mButton3.setSizePolicy(Widget::SizePolicy::Expanding, Widget::SizePolicy::Expanding); + mButton.setSizePolicy(SizePolicy::Minimal, SizePolicy::Expanding); + mButton2.setSizePolicy(SizePolicy::Minimal, SizePolicy::Expanding); + mButton3.setSizePolicy(SizePolicy::Expanding, SizePolicy::Expanding); RootWidget::setWidgetArea(mFloatingWidget, { 100, 100, 400, 400 }); } void setup2() { - mRootWidget.setRootWidget(&mDesktopLayout); - mDesktopLayout.addChild(&mWidget); + mRootWidget.setRootWidget(&mWidget); + // mDesktopLayout.addChild(&mWidget); mWidget.addChild(&mButton); mWidget.addChild(&mButton2); mWidget.addChild(&mButton3); - mButton.setSizePolicy(Widget::SizePolicy::Expanding, Widget::SizePolicy::Minimal); - mButton2.setSizePolicy(Widget::SizePolicy::Expanding, Widget::SizePolicy::Minimal); - mButton3.setSizePolicy(Widget::SizePolicy::Expanding, Widget::SizePolicy::Minimal); + mButton.setSizePolicy(SizePolicy::Expanding, SizePolicy::Minimal); + mButton2.setSizePolicy(SizePolicy::Expanding, SizePolicy::Minimal); + mButton3.setSizePolicy(SizePolicy::Expanding, SizePolicy::Minimal); - mWidget.setSizePolicy(Widget::SizePolicy::Minimal, Widget::SizePolicy::Minimal); - mWidget.setLayoutPolicy(Widget::LayoutPolicy::Horizontally); + mWidget.setSizePolicy(SizePolicy::Minimal, SizePolicy::Minimal); + mWidget.setLayoutPolicy(LayoutPolicy::Horizontal); mButton.setAction([this]() { mButton2.setMinSize(mButton2.getMinSize() + 10); }); mButton2.setAction([this]() { mButton.setMinSize(mButton.getMinSize() + 10); } ); @@ -134,7 +134,6 @@ private: LabelWidget mLabel; FloatingWidget mWidgetFloating; AnimationTestWidget mAnimationTestWidget; - DesktopLayout mDesktopLayout; DockLayoutWidget mDockLayout; diff --git a/WidgetsNew/private/LayoutPolicies.cpp b/WidgetsNew/private/LayoutPolicies.cpp new file mode 100644 index 0000000..5507117 --- /dev/null +++ b/WidgetsNew/private/LayoutPolicies.cpp @@ -0,0 +1,190 @@ +#include "LayoutPolicies.hpp" + +#include "Widget.hpp" + +using namespace tp; + +const RectF& WidgetLayout::getArea() { + return mWidget->getAreaCache(); +} + +void WidgetLayout::setArea(const RectF& area) { + mWidget->setAreaCache(area); +} + +void WidgetLayout::pickRect() { + auto current = getArea(); + auto children = getChildrenEnclosure(); + auto parent = getParentEnclosure(); + + auto rangeX = pickRange(current.getRangeX(), children.getRangeX(), parent.getRangeX(), false); + auto rangeY = pickRange(current.getRangeY(), children.getRangeY(), parent.getRangeY(), true); + + auto newArea = RectF(rangeX, rangeY); + + setArea(newArea); +} + +void WidgetLayout::clampRect() { + auto current = getArea(); + auto children = getChildrenEnclosure(); + auto parent = getParentEnclosure(); + + auto rangeX = clampRange(current.getRangeX(), children.getRangeX(), parent.getRangeX(), false); + auto rangeY = clampRange(current.getRangeY(), children.getRangeY(), parent.getRangeY(), true); + + setArea(RectF(rangeX, rangeY)); +} + +void WidgetLayout::adjustChildrenRect() { + if (mWidget->mChildren.empty()) return; + + switch (mWidget->mLayoutPolicy) { + case LayoutPolicy::Passive: break; + case LayoutPolicy::Vertical: adjustLayout(true); break; + case LayoutPolicy::Horizontal: adjustLayout(false); break; + } +} + + +halnf WidgetLayout::changeChildSize(tp::Widget* widget, halnf diff, bool vertical) { + auto prevSize = widget->getAreaCache().size[vertical]; + { + auto area = widget->getAreaCache(); + area.size[vertical] += diff; + widget->setAreaCache(area); + widget->clampMinMaxSize(); + } + auto newSize = widget->getAreaCache().size[vertical]; + + return newSize - prevSize; +} + +void WidgetLayout::adjustLayout(bool vertical) { + std::vector> contributors; + Vec2F contentSize = { 0, 0 }; + Vec2F availableSize = getRelativeAreaT().size; + + for (auto child : mChildren) { + if (child->mSizePolicy[!vertical] != SizePolicy::Minimal) { + child->pickRect(); + } + + if (child->mSizePolicy[vertical] == SizePolicy::Expanding) { + contributors.emplace_back( child, true ); + + auto area = child->getAreaCache(); + area.size[vertical] = 0; + child->setAreaCache(area); + child->clampRect(); + + } + contentSize += child->getAreaCache().size; + } + + availableSize -= mLayoutGap * ((halnf) mChildren.size() - 1) + mLayoutMargin * 2; + + auto diff = availableSize - contentSize; + + // expand or contract as much as possible + while (!contributors.empty() && (diff[vertical] != 0)) { + auto quota = diff / (halnf) contributors.size(); + + for (auto& contributor : contributors) { + if (!contributor.second) continue; + + // contributor.first->endAnimations(); + auto contribution = changeChildSize(contributor.first, quota[vertical], vertical); + + if (contribution == 0) { + contributor.second = false; + } + + diff[vertical] -= contribution; + } + + contributors.erase( + std::remove_if(contributors.begin(), contributors.end(), [](auto node) { return !node.second; }), + contributors.end() + ); + } + + // arrange + halnf iterPos = mLayoutMargin; + for (auto child : mChildren) { + auto area = child->getAreaCache(); + area.pos[vertical] = iterPos; + iterPos += area.size[vertical] + mLayoutGap; + child->setAreaCache(area); + + + // child->updateAnimations(); + // child->triggerWidgetUpdate("layout changed"); + } +} + +RangeF WidgetLayout::pickRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical) { + RangeF out; + + switch (mSizePolicy[vertical]) { + case SizePolicy::Fixed: out = current; break; + case SizePolicy::Expanding: out = parent; break; + case SizePolicy::Minimal: out = children; break; + } + + out = clampRange(out, children, parent, vertical); + return out; +} + +void WidgetLayout::clampMinMaxSize() { + auto current = getAreaCache(); + current.size.clamp(mMinSize, mMaxSize); + setAreaCache(current); +} + +RangeF WidgetLayout::clampRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical) { + auto out = current; + + if (!mChildren.empty()) { + auto clampedChild = children; + clampedChild.clamp(parent); + out.clamp(clampedChild, parent); + } else { + out.clamp(parent); + } + + // clamp min max sizes + auto len = clamp(current.size(), mMinSize[vertical], mMaxSize[vertical]); + if (len != current.size()) { + out.resizeFromCenter(len); + } + + return out; +} + +RectF WidgetLayout::getChildrenEnclosure() const { + RectF out; + + if (mChildren.empty()) { + out = { getAreaCache().center(), { 0, 0 } }; + } else { + out = mChildren.front()->getAreaCache(); + for (auto child : mChildren) { + out.expand(child->getAreaCache()); + } + out.pos += getAreaCache().pos; + } + + return out; +} + +RectF WidgetLayout::getParentEnclosure() { + DEBUG_ASSERT(mParent); + if (!mParent) return { { 0, 0 }, mMaxSize }; + + auto out = mParent->getRelativeAreaT(); + if (mParent->mLayoutPolicy != LayoutPolicy::Passive) { + return out.scaleFromCenter(mParent->mLayoutMargin, true); + } + return out; +} \ No newline at end of file diff --git a/WidgetsNew/private/Widget.cpp b/WidgetsNew/private/Widget.cpp index b63d353..4679b87 100644 --- a/WidgetsNew/private/Widget.cpp +++ b/WidgetsNew/private/Widget.cpp @@ -42,7 +42,7 @@ RectF Widget::getAreaT() const { return mArea.getTargetRect(); } -RectF Widget::getAreaCache() const { +const RectF& Widget::getAreaCache() const { return mAreaCache; } @@ -134,7 +134,7 @@ void Widget::removeChild(Widget* child) { child->triggerWidgetUpdate("parent changed"); } -void Widget::setSizePolicy(tp::Widget::SizePolicy x, tp::Widget::SizePolicy y) { +void Widget::setSizePolicy(SizePolicy x, SizePolicy y) { mSizePolicy = { x, y }; triggerWidgetUpdate("chane size policy"); } @@ -162,7 +162,7 @@ void Widget::pickRect() { auto newArea = RectF(rangeX, rangeY); for (auto child : mChildren) { - child->setAreaCache(child->getAreaCache().move(current.pos - newArea.pos)); + child->setAreaCache(RectF(child->getAreaCache()).move(current.pos - newArea.pos)); } setAreaCache(newArea); @@ -184,8 +184,8 @@ void Widget::adjustChildrenRect() { switch (mLayoutPolicy) { case LayoutPolicy::Passive: break; - case LayoutPolicy::Vertically: adjustLayout(true); break; - case LayoutPolicy::Horizontally: adjustLayout(false); break; + case LayoutPolicy::Vertical: adjustLayout(true); break; + case LayoutPolicy::Horizontal: adjustLayout(false); break; } } @@ -305,7 +305,7 @@ RangeF Widget::clampRange(const RangeF& current, const RangeF& children, const R return out; } -RectF Widget::getChildrenEnclosure() { +RectF Widget::getChildrenEnclosure() const { RectF out; if (mChildren.empty()) { diff --git a/WidgetsNew/public/FloatingWidget.hpp b/WidgetsNew/public/FloatingWidget.hpp index 3de7abc..91de51e 100644 --- a/WidgetsNew/public/FloatingWidget.hpp +++ b/WidgetsNew/public/FloatingWidget.hpp @@ -1,7 +1,6 @@ #pragma once #include "SimpleWidgets.hpp" -#include "LayoutWidget.hpp" namespace tp { class FloatingWidget : public Widget { @@ -10,7 +9,7 @@ namespace tp { setDebug("float", { 0.0, 0.9, 0.1, 1 }); mSizePolicy = { SizePolicy::Fixed, SizePolicy::Fixed }; - mLayoutPolicy = LayoutPolicy::Horizontally; + mLayoutPolicy = LayoutPolicy::Horizontal; } void process(const EventHandler& events) override; @@ -54,8 +53,8 @@ namespace tp { mHeader.setSizePolicy(SizePolicy::Expanding, SizePolicy::Minimal); mBodyLayout.setSizePolicy(SizePolicy::Expanding, SizePolicy::Expanding); - setLayoutPolicy(LayoutPolicy::Vertically); - mBodyLayout.setLayoutPolicy(LayoutPolicy::Vertically); + setLayoutPolicy(LayoutPolicy::Vertical); + mBodyLayout.setLayoutPolicy(LayoutPolicy::Vertical); } public: diff --git a/WidgetsNew/public/LayoutPolicies.hpp b/WidgetsNew/public/LayoutPolicies.hpp new file mode 100644 index 0000000..4a66a57 --- /dev/null +++ b/WidgetsNew/public/LayoutPolicies.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include "Rect.hpp" + +namespace tp { + class Widget; + + enum class SizePolicy { + Fixed, + Expanding, + Minimal, + }; + + enum class LayoutPolicy { + Passive, + Vertical, + Horizontal, + }; + + class WidgetLayout { + public: + explicit WidgetLayout(Widget* widget) { mWidget = widget; } + + virtual void adjustWidget() = 0; + + virtual void pickRect(); + virtual void clampRect(); + virtual void adjustChildrenRect(); + void adjustLayout(bool vertical); + halnf changeChildSize(Widget*, halnf diff, bool vertical); + + RangeF pickRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical); + RangeF clampRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical); + + void clampMinMaxSize(); + + RectF getChildrenEnclosure(); + RectF getParentEnclosure(); + + protected: + const RectF& getArea(); + void setArea(const RectF& area); + + private: + Widget* mWidget = nullptr; + }; + + class BasicLayout : public WidgetLayout { + public: + BasicLayout() = default; + + void adjustWidget() override; + + private: + + + private: + LayoutPolicy mLayoutPolicy = LayoutPolicy::Vertical; + Vec2 mSizePolicy = { SizePolicy::Fixed, SizePolicy::Fixed }; + + Vec2F mMinSize = { 50, 50 }; + Vec2F mMaxSize = { FLT_MAX / 2, FLT_MAX / 2 }; + + halnf mLayoutGap = 5; + halnf mLayoutMargin = 10; + }; +} \ No newline at end of file diff --git a/WidgetsNew/public/LayoutWidget.hpp b/WidgetsNew/public/LayoutWidget.hpp deleted file mode 100644 index ac85986..0000000 --- a/WidgetsNew/public/LayoutWidget.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include "Widget.hpp" - -namespace tp { - class DesktopLayout : public Widget { - public: - DesktopLayout() { setDebug("desktop layout", { 0, 0, 0.9, 0.9 }); } - - void draw(Canvas& canvas) override { - // canvas.rect(getRelativeArea(), RGBA(0, 0, 0, 1)); - } - - void pickRect() override { - // do nothing - } - - void adjustChildrenRect() override { - // todo : adjust by viewport pos - } - - [[nodiscard]] bool processesEvents() const override { return false; } - }; - - - class VerticalLayout : public Widget { - public: - VerticalLayout() { - setDebug("vertical", { 0.8, 0.1, 0.1, 0.9 }); - } - - void draw(Canvas& canvas) override { - // canvas.frame(getRelativeArea(), RGBA(1)); - } - - void adjustChildrenRect() override { - auto content = &mChildren; - auto area = getRelativeArea(); - - auto iterRect = area.shrink(mPadding); - iterRect.height = ((iterRect.height + mGap) / (float) content->size()) - mGap; - - for (auto widget :(*content)) { - widget->setArea(iterRect); - iterRect.y += iterRect.height + mGap; - } - } - - [[nodiscard]] bool processesEvents() const override { return false; } - - private: - halnf mGap = 10; - halnf mPadding = 10; - }; - -} \ No newline at end of file diff --git a/WidgetsNew/public/Widget.hpp b/WidgetsNew/public/Widget.hpp index dc75da9..07a1b60 100644 --- a/WidgetsNew/public/Widget.hpp +++ b/WidgetsNew/public/Widget.hpp @@ -1,6 +1,7 @@ #pragma once #include "SpringAnimations.hpp" +#include "LayoutPolicies.hpp" #include "EventHandler.hpp" #include "Graphics.hpp" @@ -13,19 +14,6 @@ namespace tp { class Widget { friend class RootWidget; - public: - enum class SizePolicy { - Fixed, - Expanding, - Minimal, - }; - - enum class LayoutPolicy { - Passive, - Vertically, - Horizontally, - }; - public: Widget(); virtual ~Widget() = default; @@ -70,7 +58,7 @@ namespace tp { void clampMinMaxSize(); - RectF getChildrenEnclosure(); + RectF getChildrenEnclosure() const; RectF getParentEnclosure(); void adjustLayout(bool vertical); halnf changeChildSize(Widget*, halnf diff, bool vertical); @@ -86,7 +74,7 @@ namespace tp { public: [[nodiscard]] RectF getArea() const; [[nodiscard]] RectF getAreaT() const; - [[nodiscard]] RectF getAreaCache() const; + [[nodiscard]] const RectF& getAreaCache() const; [[nodiscard]] RectF getRelativeArea() const; [[nodiscard]] RectF getRelativeAreaT() const; @@ -96,6 +84,8 @@ namespace tp { void setAreaCache(const RectF& area); protected: + friend class WidgetLayout; + Widget* mParent = nullptr; std::vector mChildren; @@ -119,7 +109,6 @@ namespace tp { RectF mAreaCache; // debug - int inOutCallbacks = 0; std::string mName = "widget base"; Vec2F mLocalPoint; Vec2F mGlobalPoint;