From a36386cc201d92f42c937ff601188a39eacd5313 Mon Sep 17 00:00:00 2001 From: IlyaShurupov Date: Fri, 11 Oct 2024 01:48:21 +0300 Subject: [PATCH] Fix sizing issues and others --- Graphics/private/DebugGUI.cpp | 2 + Graphics/private/GraphicApplication.cpp | 2 +- WidgetsNew/examples/Example.cpp | 38 ++++- WidgetsNew/private/DockLayoutWidget.cpp | 18 ++- WidgetsNew/private/FloatingWidget.cpp | 30 ++-- WidgetsNew/private/RootWidget.cpp | 193 ++++++++++++++++-------- WidgetsNew/private/SimpleWidgets.cpp | 12 +- WidgetsNew/private/Widget.cpp | 109 ++++++++----- WidgetsNew/public/DockLayoutWidget.hpp | 2 +- WidgetsNew/public/FloatingWidget.hpp | 27 +++- WidgetsNew/public/RootWidget.hpp | 8 +- WidgetsNew/public/Widget.hpp | 26 +++- 12 files changed, 325 insertions(+), 142 deletions(-) diff --git a/Graphics/private/DebugGUI.cpp b/Graphics/private/DebugGUI.cpp index eefaa0f..d824a88 100644 --- a/Graphics/private/DebugGUI.cpp +++ b/Graphics/private/DebugGUI.cpp @@ -41,6 +41,8 @@ DebugGUI::DebugGUI(Window* window) { // appearance io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + return; + auto& colors = ImGui::GetStyle().Colors; colors[ImGuiCol_WindowBg] = ImVec4{ 0.1f, 0.105f, 0.11f, 1.0f }; diff --git a/Graphics/private/GraphicApplication.cpp b/Graphics/private/GraphicApplication.cpp index d6ef14d..e59ddc7 100644 --- a/Graphics/private/GraphicApplication.cpp +++ b/Graphics/private/GraphicApplication.cpp @@ -6,7 +6,7 @@ using namespace tp; Application::Application() { - mWindow = Window::createWindow(); + mWindow = Window::createWindow({1500, 900}); mGraphics = new Graphics(mWindow); mDrawTimer.setDuration(1000.f / mDrawPerSecond); diff --git a/WidgetsNew/examples/Example.cpp b/WidgetsNew/examples/Example.cpp index 6f8207a..4f806d6 100644 --- a/WidgetsNew/examples/Example.cpp +++ b/WidgetsNew/examples/Example.cpp @@ -8,10 +8,20 @@ using namespace tp; + +/* + * adjust child widgets in the layout view + * make scrollable area + * change Widget::clampMinMax function to include child enclosure + * refactor all sizing in Widget -> move to separate files + * refactor root widget -> reorganize code +*/ + + class WidgetApplication : public Application { public: WidgetApplication() { - setup2(); + setup1(); } void setup1() { @@ -52,6 +62,21 @@ public: // mLayoutWidget.addChild(&mLabel); } + void setup3() { + mRootWidget.setRootWidget(&mDesktopLayout); + mDesktopLayout.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); + + RootWidget::setWidgetArea(mFloatingWidget, { 100, 100, 400, 400 }); + } + void setup2() { mRootWidget.setRootWidget(&mDesktopLayout); mDesktopLayout.addChild(&mWidget); @@ -60,17 +85,17 @@ public: mWidget.addChild(&mButton2); mWidget.addChild(&mButton3); - mButton.setSizePolicy(Widget::SizePolicy::Expand, Widget::SizePolicy::Contract); - mButton2.setSizePolicy(Widget::SizePolicy::Expand, Widget::SizePolicy::Contract); - mButton3.setSizePolicy(Widget::SizePolicy::Expand, Widget::SizePolicy::Contract); + mButton.setSizePolicy(Widget::SizePolicy::Expanding, Widget::SizePolicy::Minimal); + mButton2.setSizePolicy(Widget::SizePolicy::Expanding, Widget::SizePolicy::Minimal); + mButton3.setSizePolicy(Widget::SizePolicy::Expanding, Widget::SizePolicy::Minimal); - mWidget.setSizePolicy(Widget::SizePolicy::Contract, Widget::SizePolicy::Contract); + mWidget.setSizePolicy(Widget::SizePolicy::Minimal, Widget::SizePolicy::Minimal); mWidget.setLayoutPolicy(Widget::LayoutPolicy::Horizontally); mButton.setAction([this]() { mButton2.setMinSize(mButton2.getMinSize() + 10); }); mButton2.setAction([this]() { mButton.setMinSize(mButton.getMinSize() + 10); } ); - RootWidget::setWidgetArea(mWidget, { 300, 100, 150, 200 }); + RootWidget::setWidgetArea(mWidget, { 400, 100, 150, 200 }); } void processFrame(EventHandler* eventHandler, halnf deltaTime) override { @@ -96,6 +121,7 @@ private: ButtonWidget mButton3; ButtonWidget mButton4; + FloatingWidget mFloatingWidget; FloatingMenu mFloatingMenu; FloatingMenu mFloatingMenu2; diff --git a/WidgetsNew/private/DockLayoutWidget.cpp b/WidgetsNew/private/DockLayoutWidget.cpp index 6767483..33e766f 100644 --- a/WidgetsNew/private/DockLayoutWidget.cpp +++ b/WidgetsNew/private/DockLayoutWidget.cpp @@ -38,10 +38,6 @@ void DockLayoutWidget::process(const EventHandler& events) { if (!mPreviewWidget) { handleResizeEvents(events); } - - calculateSideAreas(); - calculateResizeHandles(); - updateChildSideWidgets(); } void DockLayoutWidget::draw(Canvas& canvas) { @@ -146,7 +142,7 @@ void DockLayoutWidget::toggleWidgetVisibility(Side side) { } void DockLayoutWidget::calculateSideAreas() { - auto startArea = getRelativeArea(); + auto startArea = getRelativeAreaCache(); for (auto& sideWidget : mSideWidgets) { const auto side = sideWidget.order; @@ -246,7 +242,7 @@ void DockLayoutWidget::handleResizeEvents(const EventHandler& events) { sideWidget.resizeHandle.active = true; mResizing = true; - triggerWidgetUpdate(); + triggerWidgetUpdate("new docked child"); } } } @@ -275,12 +271,12 @@ void DockLayoutWidget::updateChildSideWidgets() { if (!isSideVisible(Side(i))) { // widget->mEnable = false; } else { - widget->setArea(mSideWidgets[i].area); + widget->setAreaCache(mSideWidgets[i].area); // widget->mEnable = true; } } - if (mCenterWidget) mCenterWidget->setArea(mCenterArea); + if (mCenterWidget) mCenterWidget->setAreaCache(mCenterArea); } // update depth order @@ -369,3 +365,9 @@ auto DockLayoutWidget::getSideFromWidget(Widget* widget) -> Side { } return DockLayoutWidget::NONE; } + +void DockLayoutWidget::adjustChildrenRect() { + calculateSideAreas(); + calculateResizeHandles(); + updateChildSideWidgets(); +} diff --git a/WidgetsNew/private/FloatingWidget.cpp b/WidgetsNew/private/FloatingWidget.cpp index 4702fa7..78bfdd8 100644 --- a/WidgetsNew/private/FloatingWidget.cpp +++ b/WidgetsNew/private/FloatingWidget.cpp @@ -5,18 +5,19 @@ using namespace tp; void FloatingWidget::process(const EventHandler& events) { const auto relativePointer = events.getPointer(); - bool inside = getRelativeArea().isInside(relativePointer); + bool inside = getRelativeAreaT().isInside(relativePointer); if (inside && events.isPressed(InputID::MOUSE1)) { mPointerStart = relativePointer; mIsFloating = true; + if (resizeHandleRect().isInside(relativePointer)) { + mIsResizing = true; + } + bringToFront(); } - if (mIsFloating && resizeHandleRect().isInside(relativePointer)) { - mIsResizing = true; - } if (mIsFloating && events.isReleased(InputID::MOUSE1)) { mIsFloating = false; @@ -30,18 +31,25 @@ void FloatingWidget::process(const EventHandler& events) { void FloatingWidget::pickRect() { if (mIsFloating) { - auto area = getArea(); + auto area = getAreaCache(); if (mIsResizing) { + mPointerCurrent.clamp(mMinSize, mMaxSize); + area.size = mPointerCurrent + mHandleSize / 2.f; + + for (auto child : mChildren) { + child->triggerWidgetUpdate("floating menu resized"); + } + } else if (mIsFloating) { area.pos += mPointerCurrent - mPointerStart; } - setArea(area); - } else { - Widget::pickRect(); + setAreaCache(area); } + + clampMinMaxSize(); } void FloatingWidget::draw(Canvas& canvas) { @@ -63,4 +71,8 @@ bool FloatingWidget::propagateEventsToChildren() const { bool FloatingWidget::isFloating() const { return mIsFloating; -} \ No newline at end of file +} + +bool FloatingWidget::needsNextFrame() const { + return Widget::needsNextFrame() || isFloating(); +} diff --git a/WidgetsNew/private/RootWidget.cpp b/WidgetsNew/private/RootWidget.cpp index 7630e1c..a9c4a2a 100644 --- a/WidgetsNew/private/RootWidget.cpp +++ b/WidgetsNew/private/RootWidget.cpp @@ -12,15 +12,15 @@ void RootWidget::setRootWidget(Widget* widget) { } void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) { + mScreenArea = screenArea; + if (mDebug) { - events->setEnableKeyEvents(true); + mScreenArea.size -= { 400, 0 }; if (events->isPressed(InputID::K)) mDebugStopProcessing = !mDebugStopProcessing; if (mDebugStopProcessing) return; } - mScreenArea = screenArea; - - mRoot->setArea(screenArea); + mRoot->setArea(mScreenArea); // construct hierarchy tree of widgets to process updateTreeToProcess(); @@ -31,24 +31,37 @@ void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) { updateAnimations(root); // update all events and call all event processing callbacks - EventHandler dummyEvents; - + events->setEnableKeyEvents(true); + events->setCursorOrigin({ 0, 0 }); processFocusItems(*events); - processActiveTree(root, dummyEvents); + + events->setEnableKeyEvents(false); + events->setCursorOrigin({ 0, 0 }); + processActiveTree(root, *events, { 0, 0 }); + + updateAreaCache(root, true); // update widget sizes base on individual size policies adjustSizes(root); + updateAreaCache(root, false); + // trigger some widgets by moise pointer handleFocusChanges(*events); - // check triggered widgets for removal - erase_if(mTriggeredWidgets, [this](auto widget) { + // check triggered widgets for removal + erase_if(mTriggeredWidgets, [](auto iter) { + auto widget = iter.first; + auto flag = iter.second; + + if (!flag) return false; + + // if (mWidgetsToProcess.find(widget) == mWidgetsToProcess.end()) return false; widget->updateAnimations(); - if (mWidgetsToProcess.find(widget) == mWidgetsToProcess.end()) return false; auto end = !widget->needsNextFrame(); if (end) { widget->endAnimations(); + widget->mTriggerReason = "del"; } return end; }); @@ -56,6 +69,40 @@ void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) { bool RootWidget::needsUpdate() const { return !mTriggeredWidgets.empty() || mDebugRedrawAlways; } +void RootWidget::debugDrawWidget(Widget* widget) { + + ImGui::PushID(widget); + if (ImGui::CollapsingHeader(widget->mName.c_str())) { + + ImGui::Text("trigger reason: %s", widget->mTriggerReason.c_str()); + + auto area = widget->getAreaT(); + if (ImGui::InputFloat4("rect", &area.x)) { + widget->setArea(area); + } + + ImGui::InputFloat2("min size", &widget->mMinSize.x); + ImGui::InputFloat2("max size", &widget->mMaxSize.x); + + int sizePolicyX = int(widget->mSizePolicy.x); + int sizePolicyY = int(widget->mSizePolicy.y); + int layout = int(widget->mLayoutPolicy); + + if (ImGui::Combo("Size Policy X", &sizePolicyX, "Fixed\0Expanding\0Minimal\0")) { + widget->setSizePolicy(SizePolicy(sizePolicyX), SizePolicy(sizePolicyY)); + } + + if (ImGui::Combo("Size Policy Y", &sizePolicyY, "Fixed\0Expanding\0Minimal\0")) { + widget->setSizePolicy(SizePolicy(sizePolicyX), SizePolicy(sizePolicyY)); + } + + if (ImGui::Combo("Layout", &layout, "Passive\0Vertical\0Horizontal\0")) { + widget->setLayoutPolicy(LayoutPolicy(layout)); + } + } + ImGui::PopID(); +} + void RootWidget::drawFrame(Canvas& canvas) { // draw from top to bottom canvas.rect(mScreenArea, RGBA(0, 0, 0, 1)); @@ -75,43 +122,33 @@ void RootWidget::drawFrame(Canvas& canvas) { ImGui::Checkbox("Stop processing", &mDebugStopProcessing); ImGui::Checkbox("Force new frames", &mDebugRedrawAlways); - - if (mInFocusWidget) { - ImGui::Text("Item under cursor %s", mInFocusWidget->mName.c_str()); - - if (ImGui::BeginListBox("##empty", { -FLT_MIN, 0 })) { - for (auto widget = mInFocusWidget; widget && widget != this; widget = widget->mParent) { - if (ImGui::CollapsingHeader(widget->mName.c_str())) { - ImGui::InputFloat2("min size", &widget->mMinSize.x); - ImGui::InputFloat2("max size", &widget->mMaxSize.x); - - int sizePolicyX = int(widget->mSizePolicy.x); - int sizePolicyY = int(widget->mSizePolicy.y); - int layout = int(widget->mLayoutPolicy); - - if (ImGui::Combo("Size Policy X", &sizePolicyX, "Passive\0Expand\0Contract\0")) { - widget->setSizePolicy(SizePolicy(sizePolicyX), SizePolicy(sizePolicyY)); - } - - if (ImGui::Combo("Size Policy Y", &sizePolicyY, "Passive\0Expand\0Contract\0")) { - widget->setSizePolicy(SizePolicy(sizePolicyX), SizePolicy(sizePolicyY)); - } - - if (ImGui::Combo("Layout", &layout, "Passive\0Vertical\0Horizontal\0")) { - widget->setLayoutPolicy(LayoutPolicy(layout)); - } - } + if (ImGui::CollapsingHeader("Triggered")) { + if (ImGui::BeginListBox("##triggered", { -FLT_MIN, 300 })) { + for (auto widget : mTriggeredWidgets) { + debugDrawWidget(widget.first); } ImGui::EndListBox(); } } + + if (mInFocusWidget) { + if (ImGui::CollapsingHeader("Under cursor")) { + if (ImGui::BeginListBox("##under_cursor", { -FLT_MIN, 300 })) { + for (auto widget = mInFocusWidget; widget && widget != this; widget = widget->mParent) { + debugDrawWidget(widget); + } + ImGui::EndListBox(); + } + } + } } } void RootWidget::updateTreeToProcess() { mWidgetsToProcess.clear(); - for (auto widget : mTriggeredWidgets) { + for (auto& [widget, flag] : mTriggeredWidgets) { + flag = true; for (auto iter = widget; iter; iter = iter->mParent) { if (iter->mParent) { mWidgetsToProcess[iter->mParent].children.insert(&mWidgetsToProcess[iter]); @@ -139,24 +176,29 @@ void RootWidget::drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos) } void RootWidget::drawDebug(Canvas& canvas, Widget* active, const Vec2F& pos, int depthOrder) { - auto area = RectF{ pos, active->getArea().size }; + auto area = RectF{ pos, active->getAreaT().size }; - if (mWidgetsToProcess.find(active) != mWidgetsToProcess.end()) { + auto processed = mWidgetsToProcess.find(active) != mWidgetsToProcess.end(); + + if (processed) { RGBA color = { 1, 0, 0, 1}; canvas.frame(area, color); canvas.text((active->mName + ":" + std::to_string(depthOrder)).c_str(), area, 22, Canvas::Align::LC, 2, color); } + if (processed || active->mInFocus) { + canvas.circle(pos + active->mLocalPoint, 5, active->mDebugColor); + canvas.circle(active->mGlobalPoint, 15, active->mDebugColor); + } + if (active->mInFocus) { RGBA color = { 1, 0, 0, 0.3f}; canvas.debugCross(area, color); - canvas.circle(pos + active->mLocalPoint, 5, active->mDebugColor); - canvas.circle(active->mGlobalPoint, 10, active->mDebugColor); } int orderIdx = 0; for (auto child = active->mDepthOrder.lastNode(); child; child = child->prev) { - drawDebug(canvas, child->data, pos + child->data->getArea().pos, orderIdx++); + drawDebug(canvas, child->data, pos + child->data->getAreaT().pos, orderIdx++); } } @@ -188,9 +230,12 @@ void RootWidget::getWidgetPath(Widget* widget, std::vector& out) { void RootWidget::handleFocusChanges(EventHandler& events) { auto prevFocus = mInFocusWidget; + events.setCursorOrigin({ 0, 0 }); + findFocusWidget(mRoot, &mInFocusWidget, events.getPointer()); - if (mInFocusWidget) updateWidget(mInFocusWidget); + // if (mInFocusWidget == prevFocus) return; + if (mInFocusWidget) updateWidget(mInFocusWidget, "focus entered"); std::vector path2; getWidgetPath(mInFocusWidget, path2); @@ -213,21 +258,21 @@ void RootWidget::handleFocusChanges(EventHandler& events) { } size_t mostCommonIdx = 0; - if (!(path1.empty() || path2.empty())) { - while (path1[mostCommonIdx] == path2[mostCommonIdx]) { + while (path1[mostCommonIdx] == path2[mostCommonIdx] && mostCommonIdx < min(path1.size(), path2.size())) { mostCommonIdx++; } } + mostCommonIdx--; for (auto i = 0; i < path1.size(); i++) { path1[i]->mInFocus = false; - if (i >= mostCommonIdx && i < propLen1) path1[i]->mouseLeave(); + if (i > mostCommonIdx && i < propLen1) path1[i]->mouseLeave(); } for (auto i = 0; i < path2.size(); i++) { path2[i]->mInFocus = true; - if (i >= mostCommonIdx && i < propLen2) path2[i]->mouseEnter(); + if (i > mostCommonIdx && i < propLen2) path2[i]->mouseEnter(); } } @@ -241,32 +286,62 @@ void RootWidget::findFocusWidget(Widget* iter, Widget** focus, const Vec2F& poin } } -void RootWidget::updateWidget(Widget* widget) { - mTriggeredWidgets.insert(widget); +void RootWidget::updateWidget(Widget* widget, const char* reason) { + DEBUG_ASSERT(reason) + widget->mTriggerReason = reason; + mTriggeredWidgets.insert({ widget, false }); +} + +void RootWidget::updateAreaCache(ActiveTreeNode* iter, bool read) { + if (!iter) return; + + if (read) { + iter->widget->mAreaCache = iter->widget->getAreaT(); + } else { + iter->widget->setArea(iter->widget->mAreaCache); + iter->widget->mArea.updateCurrentRect(); + } + + for (auto child : iter->widget->mChildren) { + if (read) { + child->mAreaCache = child->getAreaT(); + } else { + child->setArea(child->mAreaCache); + child->mArea.updateCurrentRect(); + } + } + + for (auto child : iter->children) { + updateAreaCache(child, read); + } } void RootWidget::adjustSizes(ActiveTreeNode* iter) { if (!iter) return; + iter->widget->pickRect(); + for (auto child : iter->children) { adjustSizes(child); } - iter->widget->pickRect(); iter->widget->adjustChildrenRect(); - // iter->widget->pickRect(); } -void RootWidget::processActiveTree(ActiveTreeNode* iter, EventHandler& events) { +void RootWidget::processActiveTree(ActiveTreeNode* iter, EventHandler& events, Vec2F parent) { if (!iter) return; + auto current = parent + iter->widget->getAreaT().pos; + if (!iter->widget->mInFocus) { - iter->widget->updateAnimations(); + iter->widget->mGlobalPoint = current; + + events.setCursorOrigin(current); iter->widget->process(events); } for (auto child : iter->children) { - processActiveTree(child, events); + processActiveTree(child, events, current); } } @@ -290,8 +365,8 @@ void RootWidget::processFocusItems(EventHandler& events) { widgetGlobalPos[0] = 0; for (auto widget = 0; widget < path.size() - 1; widget++) { - widgetGlobalPos[widget + 1] = widgetGlobalPos[widget] + path[widget + 1]->getArea().pos; - path[widget]->mGlobalPoint = widgetGlobalPos[widget]; + widgetGlobalPos[widget + 1] = widgetGlobalPos[widget] + path[widget + 1]->getAreaT().pos; + // path[widget]->mGlobalPoint = widgetGlobalPos[widget]; } bool eventsProcessed = false; @@ -301,19 +376,15 @@ void RootWidget::processFocusItems(EventHandler& events) { events.setCursorOrigin(widgetGlobalPos[iter]); - widget->mLocalPoint = events.getPointer(); + widget->mGlobalPoint = widgetGlobalPos[iter]; if (!eventsProcessed && widget->processesEvents()) { events.setEnableKeyEvents(true); - - widget->updateAnimations(); widget->process(events); events.setEnableKeyEvents(false); eventsProcessed = true; } else { - - widget->updateAnimations(); widget->process(events); } } diff --git a/WidgetsNew/private/SimpleWidgets.cpp b/WidgetsNew/private/SimpleWidgets.cpp index 07cc57c..47206f5 100644 --- a/WidgetsNew/private/SimpleWidgets.cpp +++ b/WidgetsNew/private/SimpleWidgets.cpp @@ -33,7 +33,7 @@ void ButtonWidget::setAction(const std::function& action) { void ButtonWidget::setColor(const RGBA& in) { mColor = in; mColorAnimated.setTargetColor(mColor); - triggerWidgetUpdate(); + triggerWidgetUpdate("color changed"); } void ButtonWidget::process(const EventHandler& eventHandler) { @@ -68,12 +68,14 @@ void ButtonWidget::mouseEnter() { mColorAnimated.endAnimation(); mColorAnimated.setTargetColor(mColorHovered); - mColorAnimated.updateCurrentRect(); - triggerWidgetUpdate(); + + // mColorAnimated.updateCurrentRect(); + triggerWidgetUpdate("button hovered"); } void ButtonWidget::mouseLeave() { mColorAnimated.setTargetColor(mColor); - mColorAnimated.updateCurrentRect(); - triggerWidgetUpdate(); + //mColorAnimated.updateCurrentRect(); + + triggerWidgetUpdate("button out of focus"); } diff --git a/WidgetsNew/private/Widget.cpp b/WidgetsNew/private/Widget.cpp index a565d10..9540e40 100644 --- a/WidgetsNew/private/Widget.cpp +++ b/WidgetsNew/private/Widget.cpp @@ -12,9 +12,9 @@ WidgetManagerInterface* Widget::getRoot() { return dynamic_cast(iter); } -void Widget::triggerWidgetUpdate() { +void Widget::triggerWidgetUpdate(const char* reason) { if (auto root = getRoot()) { - root->updateWidget(this); + root->updateWidget(this, reason); } } @@ -23,11 +23,15 @@ Widget::Widget() { mArea.endAnimation(); } +void Widget::setAreaCache(const tp::RectF& area) { + mAreaCache = area; +} + void Widget::setArea(const RectF& area) { if (mArea.getTargetRect() == area) return; mArea.setTargetRect(area); - triggerWidgetUpdate(); + triggerWidgetUpdate("new area"); } RectF Widget::getArea() const { @@ -38,6 +42,10 @@ RectF Widget::getAreaT() const { return mArea.getTargetRect(); } +RectF Widget::getAreaCache() const { + return mAreaCache; +} + RectF Widget::getRelativeArea() const { return { {}, mArea.getCurrentRect().size }; } @@ -46,12 +54,16 @@ RectF Widget::getRelativeAreaT() const { return { {}, mArea.getTargetRect().size }; } +RectF Widget::getRelativeAreaCache() const { + return { {}, mAreaCache.size }; +} + void Widget::endAnimations() { mArea.endAnimation(); } bool Widget::processesEvents() const { - return true; + return false; } void Widget::updateAnimations() { @@ -59,7 +71,7 @@ void Widget::updateAnimations() { } bool Widget::needsNextFrame() const { - return !mArea.shouldEndTransition(); + return !mArea.shouldEndTransition(); // || mInFocus; } void Widget::bringToFront() { @@ -107,8 +119,8 @@ void Widget::addChild(Widget* child) { child->mParent = this; - triggerWidgetUpdate(); - child->triggerWidgetUpdate(); + triggerWidgetUpdate("add child"); + child->triggerWidgetUpdate("new parent"); } void Widget::removeChild(Widget* child) { @@ -118,29 +130,29 @@ void Widget::removeChild(Widget* child) { mDepthOrder.removeNode(node); mChildren.erase(std::remove(mChildren.begin(), mChildren.end(), child), mChildren.end()); - triggerWidgetUpdate(); - child->triggerWidgetUpdate(); + triggerWidgetUpdate("removed child"); + child->triggerWidgetUpdate("parent changed"); } void Widget::setSizePolicy(tp::Widget::SizePolicy x, tp::Widget::SizePolicy y) { mSizePolicy = { x, y }; - triggerWidgetUpdate(); + triggerWidgetUpdate("chane size policy"); } void Widget::setLayoutPolicy(LayoutPolicy layoutPolicy) { mLayoutPolicy = layoutPolicy; - triggerWidgetUpdate(); + triggerWidgetUpdate("new layout"); } const Vec2F& Widget::getMinSize() { return mMinSize; } void Widget::setMinSize(const Vec2F& size) { mMinSize = size; - triggerWidgetUpdate(); + triggerWidgetUpdate("new min size"); } void Widget::pickRect() { - auto current = getAreaT(); + auto current = getAreaCache(); auto children = getChildrenEnclosure(); auto parent = getParentEnclosure(); @@ -150,21 +162,21 @@ void Widget::pickRect() { auto newArea = RectF(rangeX, rangeY); for (auto child : mChildren) { - child->setArea(child->getAreaT().move(current.pos - newArea.pos)); + child->setAreaCache(child->getAreaCache().move(current.pos - newArea.pos)); } - setArea(newArea); + setAreaCache(newArea); } void Widget::clampRect() { - auto current = getAreaT(); + auto current = getAreaCache(); 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)); + setAreaCache(RectF(rangeX, rangeY)); } void Widget::adjustChildrenRect() { @@ -179,14 +191,14 @@ void Widget::adjustChildrenRect() { halnf Widget::changeChildSize(tp::Widget* widget, halnf diff, bool vertical) { - auto prevSize = widget->getAreaT().size[vertical]; + auto prevSize = widget->getAreaCache().size[vertical]; { - auto area = widget->getAreaT(); + auto area = widget->getAreaCache(); area.size[vertical] += diff; - widget->setArea(area); - widget->clampRect(); + widget->setAreaCache(area); + widget->clampMinMaxSize(); } - auto newSize = widget->getAreaT().size[vertical]; + auto newSize = widget->getAreaCache().size[vertical]; return newSize - prevSize; } @@ -197,12 +209,22 @@ void Widget::adjustLayout(bool vertical) { Vec2F availableSize = getRelativeAreaT().size; for (auto child : mChildren) { - contentSize += child->getAreaT().size; - if (child->mSizePolicy[vertical] == SizePolicy::Expand) { + if (child->mSizePolicy[vertical] == SizePolicy::Expanding) { contributors.emplace_back( child, true ); + + auto area = child->getAreaCache(); + area.size[vertical] = 0; + child->setAreaCache(area); + child->clampRect(); + + } else { + // child->triggerWidgetUpdate("expand child in layout"); } + contentSize += child->getAreaCache().size; } + availableSize -= mLayoutGap * ((halnf) mChildren.size() - 1) + mLayoutMargin * 2; + auto diff = availableSize - contentSize; // expand or contract as much as possible @@ -212,6 +234,7 @@ void Widget::adjustLayout(bool vertical) { for (auto& contributor : contributors) { if (!contributor.second) continue; + // contributor.first->endAnimations(); auto contribution = changeChildSize(contributor.first, quota[vertical], vertical); if (contribution == 0) { @@ -228,12 +251,16 @@ void Widget::adjustLayout(bool vertical) { } // arrange - halnf iterPos = 0; + halnf iterPos = mLayoutMargin; for (auto child : mChildren) { - auto area = child->getAreaT(); + auto area = child->getAreaCache(); area.pos[vertical] = iterPos; - iterPos += area.size[vertical]; - child->setArea(area); + iterPos += area.size[vertical] + mLayoutGap; + child->setAreaCache(area); + + + // child->updateAnimations(); + // child->triggerWidgetUpdate("layout changed"); } } @@ -241,15 +268,21 @@ RangeF Widget::pickRange(const RangeF& current, const RangeF& children, const Ra RangeF out; switch (mSizePolicy[vertical]) { - case SizePolicy::Passive: out = current; break; - case SizePolicy::Expand: out = parent; break; - case SizePolicy::Contract: out = children; break; + 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 Widget::clampMinMaxSize() { + auto current = getAreaCache(); + current.size.clamp(mMinSize, mMaxSize); + setAreaCache(current); +} + RangeF Widget::clampRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical) { auto out = current; @@ -274,12 +307,13 @@ RectF Widget::getChildrenEnclosure() { RectF out; if (mChildren.empty()) { - out = { getAreaT().center(), { 0, 0 } }; + out = { getAreaCache().center(), { 0, 0 } }; } else { + out = mChildren.front()->getAreaCache(); for (auto child : mChildren) { - out.expand(child->getAreaT()); + out.expand(child->getAreaCache()); } - out.pos += getAreaT().pos; + out.pos += getAreaCache().pos; } return out; @@ -288,5 +322,10 @@ RectF Widget::getChildrenEnclosure() { RectF Widget::getParentEnclosure() { DEBUG_ASSERT(mParent); if (!mParent) return { { 0, 0 }, mMaxSize }; - return mParent->getRelativeAreaT(); + + auto out = mParent->getRelativeAreaT(); + if (mParent->mLayoutPolicy != LayoutPolicy::Passive) { + return out.scaleFromCenter(mParent->mLayoutMargin, true); + } + return out; } diff --git a/WidgetsNew/public/DockLayoutWidget.hpp b/WidgetsNew/public/DockLayoutWidget.hpp index 77f3340..d88a63c 100644 --- a/WidgetsNew/public/DockLayoutWidget.hpp +++ b/WidgetsNew/public/DockLayoutWidget.hpp @@ -36,7 +36,7 @@ namespace tp { public: void pickRect() override {} - void adjustChildrenRect() override {} + void adjustChildrenRect() override; void process(const EventHandler& events) override; void draw(Canvas& canvas) override; diff --git a/WidgetsNew/public/FloatingWidget.hpp b/WidgetsNew/public/FloatingWidget.hpp index 6551610..8f9bcb9 100644 --- a/WidgetsNew/public/FloatingWidget.hpp +++ b/WidgetsNew/public/FloatingWidget.hpp @@ -7,7 +7,10 @@ namespace tp { class FloatingWidget : public Widget { public: FloatingWidget() { - setDebug("float", { 0.0, 0.9, 0.1, 0.7 }); + setDebug("float", { 0.0, 0.9, 0.1, 1 }); + + // mSizePolicy = { SizePolicy::Contract, SizePolicy::Contract }; + mLayoutPolicy = LayoutPolicy::Horizontally; } void process(const EventHandler& events) override; @@ -18,7 +21,10 @@ namespace tp { RectF resizeHandleRect(); + [[nodiscard]] bool needsNextFrame() const override; + [[nodiscard]] bool propagateEventsToChildren() const override; + [[nodiscard]] bool processesEvents() const override { return true; } [[nodiscard]] bool isFloating() const; @@ -38,24 +44,31 @@ namespace tp { FloatingMenu() { setDebug("float menu", { 0.0, 0.9, 0.1, 0.7 }); - addChild(&mMenuLayout); + // addChild(&mMenuLayout); - mMenuLayout.addChild(&mHeader); - mMenuLayout.addChild(&mBodyLayout); + addChild(&mHeader); + addChild(&mBodyLayout); mHeader.setText("Menu"); + + mHeader.setSizePolicy(SizePolicy::Expanding, SizePolicy::Minimal); + mBodyLayout.setSizePolicy(SizePolicy::Expanding, SizePolicy::Expanding); + + setLayoutPolicy(LayoutPolicy::Vertically); + mBodyLayout.setLayoutPolicy(LayoutPolicy::Vertically); } public: void addToMenu(Widget* widget) { + widget->setSizePolicy(SizePolicy::Expanding, SizePolicy::Minimal); mBodyLayout.addChild(widget); } private: - VerticalLayout mMenuLayout; - VerticalLayout mBodyLayout; + // VerticalLayout mMenuLayout; + Widget mBodyLayout; LabelWidget mHeader; - ButtonWidget mTestButton; + // ButtonWidget mTestButton; }; } \ No newline at end of file diff --git a/WidgetsNew/public/RootWidget.hpp b/WidgetsNew/public/RootWidget.hpp index 90a01c7..d9f7d8a 100644 --- a/WidgetsNew/public/RootWidget.hpp +++ b/WidgetsNew/public/RootWidget.hpp @@ -19,7 +19,7 @@ namespace tp { RootWidget() { setDebug("root", RGBA(1)); } void setRootWidget(Widget* widget); - void updateWidget(Widget*); + void updateWidget(Widget*, const char* reason = nullptr) override; static void setWidgetArea(Widget& widget, const RectF& rect); @@ -37,16 +37,18 @@ namespace tp { void findFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer); void handleFocusChanges(EventHandler& events); void getWidgetPath(Widget* widget, std::vector& out); - void processActiveTree(ActiveTreeNode* iter, EventHandler& events); + void processActiveTree(ActiveTreeNode* iter, EventHandler& events, Vec2F pos); void processFocusItems(EventHandler& events); void adjustSizes(ActiveTreeNode* iter); + void updateAreaCache(ActiveTreeNode* iter, bool read); + static void debugDrawWidget(Widget* widget); private: RectF mScreenArea; Widget* mRoot = nullptr; // frame to frame changes - std::set mTriggeredWidgets; + std::map mTriggeredWidgets; std::map mWidgetsToProcess; Widget* mInFocusWidget = nullptr; diff --git a/WidgetsNew/public/Widget.hpp b/WidgetsNew/public/Widget.hpp index 30b2c9e..dc75da9 100644 --- a/WidgetsNew/public/Widget.hpp +++ b/WidgetsNew/public/Widget.hpp @@ -15,9 +15,9 @@ namespace tp { public: enum class SizePolicy { - Passive, - Expand, - Contract, + Fixed, + Expanding, + Minimal, }; enum class LayoutPolicy { @@ -40,7 +40,7 @@ namespace tp { WidgetManagerInterface* getRoot(); - void triggerWidgetUpdate(); + void triggerWidgetUpdate(const char* reason = nullptr); void bringToFront(); void bringToBack(); @@ -67,6 +67,9 @@ namespace tp { // resizing 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(); void adjustLayout(bool vertical); @@ -83,11 +86,14 @@ namespace tp { public: [[nodiscard]] RectF getArea() const; [[nodiscard]] RectF getAreaT() const; + [[nodiscard]] RectF getAreaCache() const; [[nodiscard]] RectF getRelativeArea() const; [[nodiscard]] RectF getRelativeAreaT() const; + [[nodiscard]] RectF getRelativeAreaCache() const; void setArea(const RectF& area); + void setAreaCache(const RectF& area); protected: Widget* mParent = nullptr; @@ -100,20 +106,28 @@ namespace tp { // resizing LayoutPolicy mLayoutPolicy = LayoutPolicy::Passive; - Vec2 mSizePolicy = { SizePolicy::Passive, SizePolicy::Passive }; + Vec2 mSizePolicy = { SizePolicy::Fixed, SizePolicy::Fixed }; Vec2F mMinSize = { 50, 50 }; Vec2F mMaxSize = { FLT_MAX / 2, FLT_MAX / 2 }; bool mInFocus = false; + halnf mLayoutGap = 5; + halnf mLayoutMargin = 10; + + // cache + RectF mAreaCache; + // debug + int inOutCallbacks = 0; std::string mName = "widget base"; Vec2F mLocalPoint; Vec2F mGlobalPoint; RGBA mDebugColor = { 1, 1, 1, 0.3 }; + std::string mTriggerReason = "none"; }; struct WidgetManagerInterface : public Widget { - virtual void updateWidget(Widget*) = 0; + virtual void updateWidget(Widget*, const char* reason = nullptr) = 0; }; } \ No newline at end of file