From 4c99ba2b298854a4cb58bbf52dc6ee45b35d085b Mon Sep 17 00:00:00 2001 From: lnd3 Date: Fri, 23 Aug 2024 18:44:26 +0200 Subject: [PATCH] Add ui links for connecting container input and output. --- .../include/rendering/ui/UIContainer.h | 41 ++- .../include/rendering/ui/UIVisitors.h | 18 +- .../source/common/ui/UIContainer.cpp | 243 ++++++++++-------- .../rendering/source/common/ui/UIVisitors.cpp | 233 +++++++++-------- 4 files changed, 311 insertions(+), 224 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 3787d111..fe2681cb 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -108,10 +108,10 @@ namespace l::ui { } // Used in visitors only and parent scale is already premultiplied - ImVec2 Transform(const ImVec2& p, ImVec2 rootPos = ImVec2()) const { + ImVec2 Transform(const ImVec2& p, ImVec2 screenRootPos = ImVec2()) const { ImVec2 transformed; - transformed.x = rootPos.x + mPosition.x + p.x * mScale; - transformed.y = rootPos.y + mPosition.y + p.y * mScale; + transformed.x = screenRootPos.x + mPosition.x + p.x * mScale; + transformed.y = screenRootPos.y + mPosition.y + p.y * mScale; return transformed; } @@ -180,6 +180,8 @@ namespace l::ui { bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax); bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax, const ContainerArea& parent); bool OverlapScreenRect(const ImVec2& p, const ImVec2& pCenter, const ImVec2& offset, const ContainerArea& parent); + bool OverlapScreenCircle(const ImVec2& p, const ImVec2& pCenter, float radii, const ContainerArea& parent); + bool OverlapCircle(const ImVec2& p, const ImVec2& pCenter, float radii); class UIContainer; @@ -187,7 +189,7 @@ namespace l::ui { public: virtual ~UIVisitor() = default; - virtual bool Active(const InputState&) { + virtual bool Active(UIContainer&, const InputState&) { return true; } virtual bool Visit(UIContainer&, const InputState&, const ContainerArea&) { @@ -196,6 +198,9 @@ namespace l::ui { virtual void Debug(bool on = true) { mDebug = on; } + virtual bool ShouldUpdateContainer() { + return false; + } protected: bool mDebug = false; @@ -240,8 +245,8 @@ namespace l::ui { mContainer = nullptr; } - T* get() { - return reinterpret_cast(mContainer); + UIContainer* get() { + return mContainer; } T* operator->() { @@ -266,7 +271,7 @@ namespace l::ui { virtual ~UIContainer() = default; bool Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode = UITraversalMode::AllBFS); - virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode = UITraversalMode::AllBFS); + virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& contentArea, UITraversalMode mode = UITraversalMode::AllBFS); virtual void Add(UIContainer* container, int32_t i = -1); template @@ -276,6 +281,17 @@ namespace l::ui { virtual void Remove(int32_t i); + template + void Remove(UIHandle& handle) { + for (auto it = mContent.begin(); it != mContent.end();it++) { + auto containerPtr = *it; + if (containerPtr == handle.get()) { + mContent.erase(it); + break; + } + } + } + void Move(ImVec2 localChange); void Resize(ImVec2 localChange); void Rescale(float localChange); @@ -289,6 +305,7 @@ namespace l::ui { void SetDisplayName(std::string_view id); void SetId(std::string_view id); void SetContainerArea(const ContainerArea& area); + void SetLayoutArea(const ContainerArea& area); void SetParent(UIContainer* input); void SetCoParent(UIContainer* input); UIContainer* GetParent(); @@ -299,6 +316,7 @@ namespace l::ui { ImVec2 GetSize(bool untransformed = false); float GetScale(); ContainerArea& GetContainerArea(); + const ContainerArea& GetLayoutArea() const; void DebugLog(); @@ -325,9 +343,12 @@ namespace l::ui { uint32_t mConfigFlags = 0; // Active visitor flags uint32_t mNotificationFlags = 0; // Notification flags for ux feedback (resizing box animation etc) - UIContainer* mParent; - UIContainer* mCoParent; // when a container is influenced by two parent in a specific way defined by the type of container and the visitor + ContainerArea mAreaT; + + UIContainer* mParent = nullptr; + UIContainer* mCoParent = nullptr; // when a container is influenced by two parent in a specific way defined by the type of container and the visitor std::vector mContent; + std::vector mContentAreas; }; enum class UISplitMode { @@ -349,7 +370,7 @@ namespace l::ui { } ~UISplit() = default; - virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode); + virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& contentArea, UITraversalMode mode); protected: UISplitMode mSplitMode; }; diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h index 6b4ae59b..36c175b8 100644 --- a/packages/rendering/include/rendering/ui/UIVisitors.h +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -14,15 +14,23 @@ namespace l::ui { + class UIUpdate : public UIVisitor { + public: + UIUpdate() {} + ~UIUpdate() = default; + + virtual bool ShouldUpdateContainer(); + }; + class UIZoom : public UIVisitor { public: - virtual bool Active(const InputState& input); + virtual bool Active(UIContainer& container, const InputState& input); virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); }; class UIDrag : public UIVisitor { public: - virtual bool Active(const InputState& input); + virtual bool Active(UIContainer& container, const InputState& input); virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); protected: bool mDragging = false; @@ -31,7 +39,7 @@ namespace l::ui { class UIMove : public UIVisitor { public: - virtual bool Active(const InputState& input); + virtual bool Active(UIContainer& container, const InputState& input); virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); protected: bool mMoving = false; @@ -59,15 +67,17 @@ namespace l::ui { class UILinkIO : public UIVisitor { public: + virtual bool Active(UIContainer& container, const InputState& input); + UILinkIO(UICreator* creator = nullptr) : mCreator(creator) {} ~UILinkIO() = default; virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); protected: bool mDragging = false; + bool mPossibleLinkImminent = false; UIHandle mLinkContainer; UICreator* mCreator = nullptr; - float mResizeAreaSize = 8.0f; }; } diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 20e3c575..7cbfa13e 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -37,37 +37,43 @@ namespace l::ui { return pMin.x < p.x && pMin.y < p.y && pMax.x > p.x && pMax.y > p.y; } - bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax, const ContainerArea& parent) { - ImVec2 pMinT = parent.Transform(pMin); - ImVec2 pMaxT = parent.Transform(pMax); + bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax, const ContainerArea& contentArea) { + ImVec2 pMinT = contentArea.Transform(pMin); + ImVec2 pMaxT = contentArea.Transform(pMax); return pMinT.x < p.x && pMinT.y < p.y && pMaxT.x > p.x && pMaxT.y > p.y; } - bool OverlapScreenRect(const ImVec2& p, const ImVec2& pCenter, const ImVec2& offset, const ContainerArea& parent) { - ImVec2 pMinT = parent.Transform(pCenter, ImVec2(-offset.x, -offset.y)); - ImVec2 pMaxT = parent.Transform(pCenter, ImVec2(offset.x, offset.y)); + bool OverlapScreenRect(const ImVec2& p, const ImVec2& pCenter, const ImVec2& screenOffset, const ContainerArea& contentArea) { + ImVec2 pMinT = contentArea.Transform(pCenter, ImVec2(-screenOffset.x, -screenOffset.y)); + ImVec2 pMaxT = contentArea.Transform(pCenter, ImVec2(screenOffset.x, screenOffset.y)); return pMinT.x < p.x && pMinT.y < p.y && pMaxT.x > p.x && pMaxT.y > p.y; } - bool OverlapScreenCircle(const ImVec2& p, const ImVec2& pCenter, float radii, const ContainerArea& parent) { - ImVec2 pT = parent.Transform(pCenter); + bool OverlapScreenCircle(const ImVec2& p, const ImVec2& pCenter, float radii, const ContainerArea& contentArea) { + ImVec2 pT = contentArea.Transform(pCenter); ImVec2 d = ImVec2(pT.x - p.x, pT.y - p.y); - return d.x*d.x + d.y*d.y < radii * radii; + return d.x * d.x + d.y * d.y < radii * radii; + } + + bool OverlapCircle(const ImVec2& p, const ImVec2& pCenter, float radii) { + ImVec2 d = ImVec2(pCenter.x - p.x, pCenter.y - p.y); + return d.x * d.x + d.y * d.y < radii * radii; } void UIContainer::Add(UIContainer* container, int32_t i) { if (i < 0) { mContent.push_back(container); - container->SetParent(this); } else { ASSERT(static_cast(i) < mContent.size()); mContent.insert(mContent.begin() + i, container); } + container->SetParent(this); } void UIContainer::Remove(int32_t i) { ASSERT(i >= 0 && static_cast(i) < mContent.size()); + mContent.at(i)->SetParent(nullptr); mContent.erase(mContent.begin() + i); } @@ -125,11 +131,14 @@ namespace l::ui { mId = id; } - void UIContainer::SetContainerArea(const ContainerArea& area) { mArea = area; } + void UIContainer::SetLayoutArea(const ContainerArea& transformedLayoutArea) { + mAreaT = transformedLayoutArea; + } + UIContainer* UIContainer::GetParent() { return mParent; } @@ -182,140 +191,121 @@ namespace l::ui { return mArea; } + const ContainerArea& UIContainer::GetLayoutArea() const { + return mAreaT; + } + void UIContainer::DebugLog() { LOG(LogDebug) << "UIContainer: " << mDisplayName << ", [" << mArea.mScale << "][" << mArea.mPosition.x << ", " << mArea.mPosition.y << "][" << mArea.mSize.x << ", " << mArea.mSize.y << "]"; } bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode) { ContainerArea current; - if (visitor.Active(input)) { + if (visitor.Active(*this, input)) { return Accept(visitor, input, current, mode); } return false; } - bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode) { - //ContainerArea current; - //current.mScale = mArea.GetWorldScale(parent.mScale); - //current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); - //current.mSize = mArea.GetWorldSize(parent.mScale); - - auto& layout = GetContainerArea().mLayout; - switch (layout.mLayoutH) { - case UILayoutH::Fixed: - break; - case UILayoutH::Scaled: - break; - case UILayoutH::Parent: - mArea.mSize.x = parent.GetLocalSize().x; - break; - } - switch (layout.mLayoutV) { - case UILayoutV::Fixed: - break; - case UILayoutV::Scaled: - break; - case UILayoutV::Parent: - mArea.mSize.y = parent.GetLocalSize().y; - break; + bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& contentArea, UITraversalMode mode) { + if (visitor.ShouldUpdateContainer()) { + auto& layout = GetContainerArea().mLayout; + switch (layout.mLayoutH) { + case UILayoutH::Fixed: + break; + case UILayoutH::Scaled: + break; + case UILayoutH::Parent: + mArea.mSize.x = contentArea.GetLocalSize().x; + break; + } + switch (layout.mLayoutV) { + case UILayoutV::Fixed: + break; + case UILayoutV::Scaled: + break; + case UILayoutV::Parent: + mArea.mSize.y = contentArea.GetLocalSize().y; + break; + } } - if (mode == UITraversalMode::AllBFS && visitor.Active(input)) { - visitor.Visit(*this, input, parent); + if (mode == UITraversalMode::AllBFS && visitor.Active(*this, input)) { + visitor.Visit(*this, input, contentArea); } + size_t i = 0; for (auto& content : mContent) { - auto contentSize = content->GetSize(); - auto& contentLayout = content->GetContainerArea().mLayout; - ContainerArea current; - current.mScale = mArea.GetWorldScale(parent.mScale); - current.mSize = mArea.GetWorldSizeLayout(parent.mScale); - current.mPosition = mArea.GetWorldPosLayout(parent.mScale, parent.mPosition, contentSize, contentLayout.mAlignH, contentLayout.mAlignV); - - if (content->Accept(visitor, input, current, mode)) { + if (visitor.ShouldUpdateContainer()) { + auto contentSize = content->GetSize(); + auto& contentLayout = content->GetContainerArea().mLayout; + ContainerArea current; + current.mScale = mArea.GetWorldScale(contentArea.mScale); + current.mSize = mArea.GetWorldSizeLayout(contentArea.mScale); + current.mPosition = mArea.GetWorldPosLayout(contentArea.mScale, contentArea.mPosition, contentSize, contentLayout.mAlignH, contentLayout.mAlignV); + + mContentAreas.resize(mContent.size()); + mContentAreas.at(i) = current; + + content->SetLayoutArea(current); + } + + if (content->Accept(visitor, input, mContentAreas.at(i), mode)) { if (mode == UITraversalMode::Once) { return true; } } + i++; } - if ((mode == UITraversalMode::AllDFS || mode == UITraversalMode::Once || mode == UITraversalMode::Twice) && visitor.Active(input)) { - return visitor.Visit(*this, input, parent); + if ((mode == UITraversalMode::AllDFS || mode == UITraversalMode::Once || mode == UITraversalMode::Twice) && visitor.Active(*this, input)) { + return visitor.Visit(*this, input, contentArea); } return false; } - bool UISplit::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode) { + bool UISplit::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& contentArea, UITraversalMode mode) { // Since we can have multiple layouts in a container for different content, it will act as // an anchor rather than a container, therefore it has to align within it and size - auto& layout = GetContainerArea().mLayout; - switch (layout.mLayoutH) { - case UILayoutH::Fixed: - break; - case UILayoutH::Scaled: - break; - case UILayoutH::Parent: - mArea.mSize.x = parent.GetLocalSize().x; - break; - } - switch (layout.mLayoutV) { - case UILayoutV::Fixed: - break; - case UILayoutV::Scaled: - break; - case UILayoutV::Parent: - mArea.mSize.y = parent.GetLocalSize().y; - break; - } - - float contentCount = static_cast(mContent.size()); - ContainerArea current; - current.mScale = mArea.GetWorldScale(parent.mScale); - current.mSize = mArea.GetWorldSize(parent.mScale); - current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); - - switch (mSplitMode) { - case UISplitMode::EqualSplitH: - current.mSize.x /= contentCount; - break; - case UISplitMode::EqualSplitV: - current.mSize.y /= contentCount; - break; - case UISplitMode::AppendH: - break; - case UISplitMode::AppendV: - break; - case UISplitMode::EqualResizeH: - break; - case UISplitMode::EqualResizeV: - break; - } - if (mode == UITraversalMode::AllBFS && visitor.Active(input)) { - visitor.Visit(*this, input, parent); - } - - for (auto& content : mContent) { - if (content->Accept(visitor, input, current, mode)) { - if (mode == UITraversalMode::Once) { - return true; - } + if (visitor.ShouldUpdateContainer()) { + auto& layout = GetContainerArea().mLayout; + switch (layout.mLayoutH) { + case UILayoutH::Fixed: + break; + case UILayoutH::Scaled: + break; + case UILayoutH::Parent: + mArea.mSize.x = contentArea.GetLocalSize().x; + break; + } + switch (layout.mLayoutV) { + case UILayoutV::Fixed: + break; + case UILayoutV::Scaled: + break; + case UILayoutV::Parent: + mArea.mSize.y = contentArea.GetLocalSize().y; + break; } + float contentCount = static_cast(mContent.size()); + current.mScale = mArea.GetWorldScale(contentArea.mScale); + current.mSize = mArea.GetWorldSize(contentArea.mScale); + current.mPosition = mArea.GetWorldPos(contentArea.mScale, contentArea.mPosition); + switch (mSplitMode) { case UISplitMode::EqualSplitH: - current.mPosition.x += current.mSize.x; + current.mSize.x /= contentCount; break; case UISplitMode::EqualSplitV: - current.mPosition.y += current.mSize.y; + current.mSize.y /= contentCount; break; case UISplitMode::AppendH: - current.mPosition.x += content->GetSize().x * parent.mScale; break; case UISplitMode::AppendV: - current.mPosition.y += content->GetSize().y * parent.mScale; break; case UISplitMode::EqualResizeH: break; @@ -324,8 +314,49 @@ namespace l::ui { } } - if ((mode == UITraversalMode::AllDFS || mode == UITraversalMode::Once || mode == UITraversalMode::Twice) && visitor.Active(input)) { - return visitor.Visit(*this, input, parent); + if (mode == UITraversalMode::AllBFS && visitor.Active(*this, input)) { + visitor.Visit(*this, input, contentArea); + } + + size_t i = 0; + for (auto& content : mContent) { + if (visitor.ShouldUpdateContainer()) { + mContentAreas.resize(mContent.size()); + mContentAreas.at(i) = current; + content->SetLayoutArea(current); + } + + if (content->Accept(visitor, input, mContentAreas.at(i), mode)) { + if (mode == UITraversalMode::Once) { + return true; + } + } + + if (visitor.ShouldUpdateContainer()) { + switch (mSplitMode) { + case UISplitMode::EqualSplitH: + current.mPosition.x += current.mSize.x; + break; + case UISplitMode::EqualSplitV: + current.mPosition.y += current.mSize.y; + break; + case UISplitMode::AppendH: + current.mPosition.x += content->GetSize().x * contentArea.mScale; + break; + case UISplitMode::AppendV: + current.mPosition.y += content->GetSize().y * contentArea.mScale; + break; + case UISplitMode::EqualResizeH: + break; + case UISplitMode::EqualResizeV: + break; + } + } + i++; + } + + if ((mode == UITraversalMode::AllDFS || mode == UITraversalMode::Once || mode == UITraversalMode::Twice) && visitor.Active(*this, input)) { + return visitor.Visit(*this, input, contentArea); } return false; } diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index decb8d30..030e551a 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -2,81 +2,85 @@ namespace l::ui { -bool UIZoom::Active(const InputState& input) { - return input.mScroll != 0; -} + bool UIUpdate::ShouldUpdateContainer() { + return true; + } -bool UIZoom::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { - if (!container.HasConfigFlag(UIContainer_ZoomFlag)) { - return false; + bool UIZoom::Active(UIContainer&, const InputState& input) { + return input.mScroll != 0; } - if (input.mScroll != 0.0f) { - float scaleChange = 1.0f; - float scaleDelta = 0.1f; - scaleChange = 1.0f + scaleDelta * input.mScroll; - if (input.mScroll < 0.0f) { - scaleChange = 1.0f / (1.0f - scaleDelta * input.mScroll); + + bool UIZoom::Visit(UIContainer& container, const InputState& input, const ContainerArea& contentArea) { + if (!container.HasConfigFlag(UIContainer_ZoomFlag)) { + return false; } - if ((container.GetScale() > 100.0f && scaleChange > 1.0f) || (container.GetScale() < 0.01f && scaleChange < 1.0f)) { + if (input.mScroll != 0.0f) { + float scaleChange = 1.0f; + float scaleDelta = 0.1f; + scaleChange = 1.0f + scaleDelta * input.mScroll; + if (input.mScroll < 0.0f) { + scaleChange = 1.0f / (1.0f - scaleDelta * input.mScroll); + } + if ((container.GetScale() > 100.0f && scaleChange > 1.0f) || (container.GetScale() < 0.01f && scaleChange < 1.0f)) { + return true; + } + + ImVec2 mousePos = input.GetLocalPos(); + ImVec2 localMousePos = ImVec2(contentArea.mPosition.x - mousePos.x, contentArea.mPosition.y - mousePos.y); + ImVec2 p = container.GetPosition(); + container.Rescale(scaleChange); + p.x = ((p.x + localMousePos.x) * scaleChange - localMousePos.x) / container.GetScale(); + p.y = ((p.y + localMousePos.y) * scaleChange - localMousePos.y) / container.GetScale(); + container.SetPosition(p); return true; } - - ImVec2 mousePos = input.GetLocalPos(); - ImVec2 localMousePos = ImVec2(parent.mPosition.x - mousePos.x, parent.mPosition.y - mousePos.y); - ImVec2 p = container.GetPosition(); - container.Rescale(scaleChange); - p.x = ((p.x + localMousePos.x) * scaleChange - localMousePos.x) / container.GetScale(); - p.y = ((p.y + localMousePos.y) * scaleChange - localMousePos.y) / container.GetScale(); - container.SetPosition(p); - return true; + return false; } - return false; -} - -bool UIDrag::Active(const InputState& input) { - return (input.mStarted && !mDragging) || mDragging; -} -bool UIDrag::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { - if (!container.HasConfigFlag(UIContainer_DragFlag)) { - return false; + bool UIDrag::Active(UIContainer&, const InputState& input) { + return (input.mStarted && !mDragging) || mDragging; } - if (input.mStarted && !mDragging) { - if (input.GetLocalPos().x >= 0.0f && input.GetLocalPos().y >= 0.0f) { - mDragging = true; - mSourceContainer = &container; + + bool UIDrag::Visit(UIContainer& container, const InputState& input, const ContainerArea& contentArea) { + if (!container.HasConfigFlag(UIContainer_DragFlag)) { + return false; } - } - if (mDragging && mSourceContainer == &container) { - ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale * container.GetScale()); - container.Move(move); - container.Notification(UIContainer_DragFlag); + if (input.mStarted && !mDragging) { + if (input.GetLocalPos().x >= 0.0f && input.GetLocalPos().y >= 0.0f) { + mDragging = true; + mSourceContainer = &container; + } + } + if (mDragging && mSourceContainer == &container) { + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, contentArea.mScale * container.GetScale()); + container.Move(move); + container.Notification(UIContainer_DragFlag); - if (input.mStopped) { - mDragging = false; - mSourceContainer = nullptr; + if (input.mStopped) { + mDragging = false; + mSourceContainer = nullptr; + } + return mDragging; } - return mDragging; + return false; } - return false; -} -bool UIMove::Active(const InputState& input) { - return (input.mStarted && !mMoving) || mMoving; -} + bool UIMove::Active(UIContainer&, const InputState& input) { + return (input.mStarted && !mMoving) || mMoving; + } -bool UIMove::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + bool UIMove::Visit(UIContainer& container, const InputState& input, const ContainerArea& contentArea) { if(!container.HasConfigFlag(UIContainer_MoveFlag)){ return false; } if (input.mStarted && !mMoving) { - if (Overlap(input.GetLocalPos(), container.GetPosition(), container.GetPositionAtSize(), parent)) { + if (Overlap(input.GetLocalPos(), container.GetPosition(), container.GetPositionAtSize(), contentArea)) { mMoving = true; mSourceContainer = &container; } } if (mMoving && mSourceContainer == &container) { - ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale); + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, contentArea.mScale); container.Move(move); container.Notification(UIContainer_MoveFlag); @@ -89,14 +93,14 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai return false; } - bool UIResize::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + bool UIResize::Visit(UIContainer& container, const InputState& input, const ContainerArea& contentArea) { if (!container.HasConfigFlag(UIContainer_ResizeFlag)) { return false; } if (!mResizing) { const float radii = mResizeAreaSize * 0.5f; ImVec2 p = container.GetPositionAtSize(); - if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(radii, radii), parent)) { + if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(radii, radii), contentArea)) { mSourceContainer = &container; container.Notification(UIContainer_ResizeFlag); @@ -113,7 +117,7 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai } } if (mResizing && mSourceContainer == &container) { - ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale); + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, contentArea.mScale); container.Resize(move); if (input.mStopped) { @@ -126,27 +130,31 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai return false; } - bool UIDraw::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + bool UIDraw::Visit(UIContainer& container, const InputState& input, const ContainerArea& contentArea) { if (!mDebug && !container.HasConfigFlag(UIContainer_DrawFlag)) { return false; } + float splineThickness = 2.0f; ImU32 color = container.GetRenderData().mColor; ImVec2 pTopLeft = container.GetPosition(); ImVec2 pCenter = container.GetPositionAtCenter(); ImVec2 pLowRight = container.GetPositionAtSize(); ImVec2 pSize = container.GetSize(); - pSize.x *= parent.mScale; - pSize.y *= parent.mScale; - ImVec2 p1 = parent.Transform(pTopLeft, input.mRootPos); - ImVec2 p12 = parent.Transform(pCenter, input.mRootPos); - ImVec2 p2 = parent.Transform(pLowRight, input.mRootPos); + pSize.x *= contentArea.mScale; + pSize.y *= contentArea.mScale; + ImVec2 p1 = contentArea.Transform(pTopLeft, input.mRootPos); + ImVec2 p12 = contentArea.Transform(pCenter, input.mRootPos); + ImVec2 p2 = contentArea.Transform(pLowRight, input.mRootPos); + ImVec2 p11; + ImVec2 p22; + const char* nameStart; const char* nameEnd; switch (container.GetRenderData().mType) { case l::ui::UIRenderType::Rect: - mDrawList->AddRect(p1, p2, color, 5.0f, ImDrawFlags_RoundCornersAll, 1.0f * container.GetScale() * parent.mScale); + mDrawList->AddRect(p1, p2, color, 5.0f, ImDrawFlags_RoundCornersAll, 1.0f * container.GetScale() * contentArea.mScale); break; case l::ui::UIRenderType::RectFilled: mDrawList->AddRectFilled(p1, p2, color, 5.0f, ImDrawFlags_RoundCornersAll); @@ -156,7 +164,7 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai case l::ui::UIRenderType::TriangleFilled: break; case l::ui::UIRenderType::Circle: - mDrawList->AddCircle(p12, pSize.x, color, 15, 2.0f * container.GetScale() * parent.mScale); + mDrawList->AddCircle(p12, pSize.x, color, 15, 2.0f * container.GetScale() * contentArea.mScale); break; case l::ui::UIRenderType::CircleFilled: mDrawList->AddCircleFilled(p12, pSize.x, color, 15); @@ -167,15 +175,27 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai break; case l::ui::UIRenderType::Spline: if (container.HasConfigFlag(UIContainer_LinkFlag)) { - ImVec2 pLinkInput = container.GetParent()->GetPositionAtCenter(); - ImVec2 pLinkOutput = container.GetCoParent()->GetPositionAtCenter(); + splineThickness = container.HasNotification(UIContainer_LinkFlag) ? 2.0f * splineThickness : splineThickness; + ImVec2 pLinkInput = container.GetParent()->GetPosition(); + p1 = contentArea.Transform(ImVec2(), input.mRootPos); + p11 = contentArea.Transform(ImVec2(pLinkInput.x + 120.0f, pLinkInput.y), input.mRootPos); + if (container.GetCoParent() != nullptr) { + ImVec2 pLinkOutput = container.GetCoParent()->GetPosition(); + auto& coContentArea = container.GetCoParent()->GetLayoutArea(); + p2 = coContentArea.Transform(pLinkOutput, input.mRootPos); + p22 = coContentArea.Transform(ImVec2(pLinkOutput.x - 120.0f, pLinkOutput.y), input.mRootPos); + } + else { + p2 = input.mCurPos; + p22 = ImVec2(p2.x - 120.0f, p2.y); + } } else { - ImVec2 p11 = parent.Transform(ImVec2(pTopLeft.x + 120.0f, pTopLeft.y), input.mRootPos); - ImVec2 p22 = parent.Transform(ImVec2(pLowRight.x - 120.0f, pLowRight.y), input.mRootPos); - mDrawList->AddBezierCubic(p1, p11, p22, p2, color, 2.0f, 15); + p11 = contentArea.Transform(ImVec2(pTopLeft.x + 120.0f, pTopLeft.y), input.mRootPos); + p22 = contentArea.Transform(ImVec2(pLowRight.x - 120.0f, pLowRight.y), input.mRootPos); } + mDrawList->AddBezierCubic(p1, p11, p22, p2, color, splineThickness, 30); break; case l::ui::UIRenderType::Text: @@ -188,7 +208,7 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai nameStart = container.GetDisplayName().data(); nameEnd = container.GetDisplayName().data() + container.GetDisplayName().size(); container.SetSize(ImGui::CalcTextSize(nameStart, nameEnd)); - mDrawList->AddText(ImGui::GetDefaultFont(), 13.0f * container.GetScale() * parent.mScale, p1, color, nameStart, nameEnd); + mDrawList->AddText(ImGui::GetDefaultFont(), 13.0f * container.GetScale() * contentArea.mScale, p1, color, nameStart, nameEnd); } break; case l::ui::UIRenderType::Texture: @@ -208,11 +228,11 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai case l::ui::UIRenderType::Texture: case l::ui::UIRenderType::Spline: if (container.HasConfigFlag(ui::UIContainer_ResizeFlag)) { - ImVec2 p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 3.0f, input.mRootPos.y - 3.0f)); - ImVec2 p4 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x + 3.0f, input.mRootPos.y + 3.0f)); + ImVec2 p3 = contentArea.Transform(pLowRight, ImVec2(input.mRootPos.x - 3.0f, input.mRootPos.y - 3.0f)); + ImVec2 p4 = contentArea.Transform(pLowRight, ImVec2(input.mRootPos.x + 3.0f, input.mRootPos.y + 3.0f)); if (container.HasNotification(ui::UIContainer_ResizeFlag)) { - p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 5.0f, input.mRootPos.y - 5.0f)); - p4 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x + 5.0f, input.mRootPos.y + 5.0f)); + p3 = contentArea.Transform(pLowRight, ImVec2(input.mRootPos.x - 5.0f, input.mRootPos.y - 5.0f)); + p4 = contentArea.Transform(pLowRight, ImVec2(input.mRootPos.x + 5.0f, input.mRootPos.y + 5.0f)); } mDrawList->AddRectFilled(p3, p4, color); } @@ -224,12 +244,17 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai return false; } - bool UILinkIO::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + bool UILinkIO::Active(UIContainer& container, const InputState&) { + return container.HasConfigFlag(UIContainer_InputFlag) || container.HasConfigFlag(UIContainer_OutputFlag) || container.HasConfigFlag(UIContainer_LinkFlag); + } + + bool UILinkIO::Visit(UIContainer& container, const InputState& input, const ContainerArea& contentArea) { // Create link at from a clicked output container - const float radii = mResizeAreaSize * 0.5f; - if (!mDragging && input.mStarted && container.HasConfigFlag(UIContainer_OutputFlag)) { - ImVec2 p = container.GetPosition(); - if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(radii, radii), parent)) { + if (container.HasConfigFlag(UIContainer_OutputFlag) && !mDragging && input.mStarted && mLinkContainer.get() == nullptr) { + ImVec2 pCenter = container.GetPosition(); + ImVec2 size = container.GetSize(); + ImVec2 pT = contentArea.Transform(pCenter, input.mRootPos); + if (OverlapCircle(input.mCurPos, pT, size.x * contentArea.mScale)) { mDragging = true; mLinkContainer = mCreator->CreateContainer(UIContainer_LinkFlag | UIContainer_DrawFlag, UIRenderType::Spline); container.Add(mLinkContainer); @@ -237,36 +262,36 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai } } - if (mDragging) { - if (mLinkContainer.get() == &container) { - // On the newly create link container, drag the end point along the mouse movement - ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale * container.GetScale()); - mLinkContainer->Move(move); + if (mDragging && mLinkContainer.get() != nullptr && container.HasConfigFlag(UIContainer_LinkFlag) && mLinkContainer.get() == &container) { + // On the newly created link container, drag the end point along the mouse movement + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, contentArea.mScale * container.GetScale()); + mLinkContainer->Move(move); + } + + if (mDragging && mLinkContainer.get() != nullptr && container.HasConfigFlag(UIContainer_InputFlag)) { + ImVec2 pCenter = container.GetPosition(); + ImVec2 size = container.GetSize(); + ImVec2 pT = contentArea.Transform(pCenter, input.mRootPos); + + if (OverlapCircle(input.mCurPos, pT, size.x * contentArea.mScale)) { + mLinkContainer->Notification(UIContainer_LinkFlag); + mLinkContainer->SetCoParent(&container); + } + else if (mLinkContainer->GetCoParent() == &container){ + mLinkContainer->SetCoParent(nullptr); + mLinkContainer->ClearNotifications(); } - else if (mLinkContainer->GetParent() != &container) { - // When checking for an input container we check all but the output and link containers - if (container.HasConfigFlag(UIContainer_InputFlag)) { - ImVec2 p = container.GetPosition(); - if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(radii, radii), parent)) { - container.Notification(UIContainer_LinkFlag); - if (input.mStopped) { - mLinkContainer->SetCoParent(&container); - mLinkContainer.reset(); - mDragging = false; - container.ClearNotifications(); - return true; - } - } - else { - container.ClearNotifications(); - } - } - if (input.mStopped) { + if (input.mStopped) { + mLinkContainer->ClearNotifications(); + if (mLinkContainer->GetCoParent() != nullptr) { + mDragging = false; mLinkContainer.reset(); + } + else { + mLinkContainer->GetParent()->Remove(mLinkContainer); mDragging = false; - container.ClearNotifications(); - return true; + mLinkContainer.reset(); } } }