From 4ad40058f9563636ea57d70d9b998a152051b657 Mon Sep 17 00:00:00 2001 From: IlyaShurupov Date: Wed, 9 Oct 2024 14:31:23 +0300 Subject: [PATCH] Refactor in a good state --- 3DEditor/public/EditorWidget.hpp | 4 +- Graphics/private/EventHandler.cpp | 5 + LibraryViewer/public/GUI.hpp | 2 +- Math/public/Color.hpp | 9 + Widgets/examples/ChatGUI.hpp | 4 +- Widgets/examples/SimpleGUI.hpp | 16 +- Widgets/private/GridLayoutWidget.cpp | 46 ++-- Widgets/private/WorkspaceWidget.cpp | 10 +- Widgets/public/GridLayoutWidget.hpp | 6 +- Widgets/public/WorkspaceWidget.hpp | 2 +- WidgetsNew/examples/Example.cpp | 32 ++- WidgetsNew/private/DockLayoutWidget.cpp | 324 ++++++++++++++++++++++++ WidgetsNew/private/FloatingWidget.cpp | 2 + WidgetsNew/private/RootWidget.cpp | 94 ++++--- WidgetsNew/private/SimpleWidgets.cpp | 6 + WidgetsNew/private/Widget.cpp | 31 ++- WidgetsNew/public/DockLayoutWidget.hpp | 94 +++++++ WidgetsNew/public/FloatingWidget.hpp | 4 +- WidgetsNew/public/RootWidget.hpp | 4 +- WidgetsNew/public/SimpleWidgets.hpp | 2 + WidgetsNew/public/Widget.hpp | 14 +- 21 files changed, 606 insertions(+), 105 deletions(-) create mode 100644 WidgetsNew/private/DockLayoutWidget.cpp create mode 100644 WidgetsNew/public/DockLayoutWidget.hpp diff --git a/3DEditor/public/EditorWidget.hpp b/3DEditor/public/EditorWidget.hpp index ebaf9f6..2c22d51 100644 --- a/3DEditor/public/EditorWidget.hpp +++ b/3DEditor/public/EditorWidget.hpp @@ -36,8 +36,8 @@ namespace tp { mViewport(canvas, editor) { mEditor = editor; - mDockSpace.addSideWidget(&mNavigationMenu, GridLayoutWidget::RIGHT); - mDockSpace.addSideWidget(&mRenderMenu, GridLayoutWidget::LEFT); + mDockSpace.addSideWidget(&mNavigationMenu, DockLayoutWidget::RIGHT); + mDockSpace.addSideWidget(&mRenderMenu, DockLayoutWidget::LEFT); mDockSpace.setCenterWidget(&mViewport); // Render diff --git a/Graphics/private/EventHandler.cpp b/Graphics/private/EventHandler.cpp index 4bac101..1f49e0d 100644 --- a/Graphics/private/EventHandler.cpp +++ b/Graphics/private/EventHandler.cpp @@ -44,6 +44,9 @@ bool transitionsReduce[4][4] = { void EventHandler::processAllEvent() { mMutex.lock(); + + mPointerPrev = mPointer; + while (mEventQueue.size()) { processEventUnguarded(); } @@ -53,6 +56,8 @@ void EventHandler::processAllEvent() { void EventHandler::processEvent() { mMutex.lock(); + mPointerPrev = mPointer; + if (!mEventQueue.size()) { mMutex.unlock(); return; diff --git a/LibraryViewer/public/GUI.hpp b/LibraryViewer/public/GUI.hpp index 50730f6..a6362aa 100644 --- a/LibraryViewer/public/GUI.hpp +++ b/LibraryViewer/public/GUI.hpp @@ -195,7 +195,7 @@ namespace tp { mCurrentTrackInfo.mPlayer = mPlayer; mDockSpace.setCenterWidget(&mSongList); - mDockSpace.addSideWidget(&mCurrentTrackInfo, GridLayoutWidget::RIGHT); + mDockSpace.addSideWidget(&mCurrentTrackInfo, DockLayoutWidget::RIGHT); // this->mChildWidgets.pushBack(&mCurrentTrackInfo); // this->mChildWidgets.pushBack(&mCurrentTrack); diff --git a/Math/public/Color.hpp b/Math/public/Color.hpp index 2c3dce7..7cebe3d 100644 --- a/Math/public/Color.hpp +++ b/Math/public/Color.hpp @@ -79,6 +79,15 @@ namespace tp { bool operator==(const RGBA& in) const { return r == in.r && g == in.g && b == in.b && a == in.a; } + static RGBA random() { + return RGBA{ + (halnf) randomFloat(), + (halnf) randomFloat(), + (halnf) randomFloat(), + 1.f, + }; + } + public: union { RGB rgbs; diff --git a/Widgets/examples/ChatGUI.hpp b/Widgets/examples/ChatGUI.hpp index cba4243..7fcad0d 100644 --- a/Widgets/examples/ChatGUI.hpp +++ b/Widgets/examples/ChatGUI.hpp @@ -177,7 +177,7 @@ namespace tp { halnf mPadding = 0; }; - class ChattingWidget : public GridLayoutWidget { + class ChattingWidget : public DockLayoutWidget { public: ChattingWidget() { // todo : fetch code @@ -205,7 +205,7 @@ namespace tp { mActive.mHistoryView.addWidget(&message.data()); } - addSideWidget(&mSideView, GridLayoutWidget::RIGHT); + addSideWidget(&mSideView, DockLayoutWidget::RIGHT); setCenterWidget(&mActive); } diff --git a/Widgets/examples/SimpleGUI.hpp b/Widgets/examples/SimpleGUI.hpp index dc9ebc8..b924838 100644 --- a/Widgets/examples/SimpleGUI.hpp +++ b/Widgets/examples/SimpleGUI.hpp @@ -33,19 +33,19 @@ namespace tp { public: SimpleWidget3() { - mDockSpace.addSideWidget(&mButtons[0], GridLayoutWidget::BOTTOM); - mDockSpace.addSideWidget(&mButtons[1], GridLayoutWidget::RIGHT); + mDockSpace.addSideWidget(&mButtons[0], DockLayoutWidget::BOTTOM); + mDockSpace.addSideWidget(&mButtons[1], DockLayoutWidget::RIGHT); - mDockSpace.removeSideWidget(GridLayoutWidget::BOTTOM); + mDockSpace.removeSideWidget(DockLayoutWidget::BOTTOM); - mDockSpace.addSideWidget(&mButtons[0], GridLayoutWidget::TOP); - mDockSpace.addSideWidget(&mButtons[2], GridLayoutWidget::LEFT); + mDockSpace.addSideWidget(&mButtons[0], DockLayoutWidget::TOP); + mDockSpace.addSideWidget(&mButtons[2], DockLayoutWidget::LEFT); - mDockSpace.removeSideWidget(GridLayoutWidget::TOP); + mDockSpace.removeSideWidget(DockLayoutWidget::TOP); - mDockSpace.addSideWidget(&mButtons[0], GridLayoutWidget::BOTTOM); + mDockSpace.addSideWidget(&mButtons[0], DockLayoutWidget::BOTTOM); - mDockSpace.addSideWidget(&mButtons[3], GridLayoutWidget::TOP); + mDockSpace.addSideWidget(&mButtons[3], DockLayoutWidget::TOP); mDockSpace.setCenterWidget(&mButtons[4]); /* diff --git a/Widgets/private/GridLayoutWidget.cpp b/Widgets/private/GridLayoutWidget.cpp index 35df2a1..c1a4857 100644 --- a/Widgets/private/GridLayoutWidget.cpp +++ b/Widgets/private/GridLayoutWidget.cpp @@ -2,15 +2,15 @@ using namespace tp; -GridLayoutWidget::GridLayoutWidget() { +DockLayoutWidget::DockLayoutWidget() { mSideWidgets[0].side = LEFT; mSideWidgets[1].side = TOP; mSideWidgets[2].side = RIGHT; mSideWidgets[3].side = BOTTOM; } -void GridLayoutWidget::addSideWidget(Widget* widget, Side side) { - if (isSideExists(side)) return; +void DockLayoutWidget::addSideWidget(Widget* widget, Side side) { + if (sideExists(side)) return; auto& sideWidget = mSideWidgets[side]; sideWidget.widget = widget; @@ -27,8 +27,8 @@ void GridLayoutWidget::addSideWidget(Widget* widget, Side side) { widget->mIsDocked = true; } -void GridLayoutWidget::removeSideWidget(Side side) { - if (!isSideExists(side)) return; +void DockLayoutWidget::removeSideWidget(Side side) { + if (!sideExists(side)) return; bool removed = false; for (ualni i = 0; i < 3; i++) { @@ -48,18 +48,18 @@ void GridLayoutWidget::removeSideWidget(Side side) { mSideWidgets[side].widget = nullptr; } -void GridLayoutWidget::setCenterWidget(Widget* widget) { +void DockLayoutWidget::setCenterWidget(Widget* widget) { mChildWidgets.removeNode(mChildWidgets.find(mCenterWidget)); mCenterWidget = widget; mChildWidgets.pushBack(mCenterWidget); } -void GridLayoutWidget::toggleHiddenState(GridLayoutWidget::Side side) { - if (!isSideExists(side)) return; +void DockLayoutWidget::toggleHiddenState(DockLayoutWidget::Side side) { + if (!sideExists(side)) return; mSideWidgets[side].hidden = !mSideWidgets[side].hidden; } -void GridLayoutWidget::eventProcess(const tp::Events& events) { +void DockLayoutWidget::eventProcess(const tp::Events& events) { calculateSideAreas(); calculateResizeHandles(); // calculateHeaderAreas(); @@ -70,7 +70,7 @@ void GridLayoutWidget::eventProcess(const tp::Events& events) { updateChildSideWidgets(); } -void GridLayoutWidget::eventDraw(Canvas& canvas) { +void DockLayoutWidget::eventDraw(Canvas& canvas) { canvas.rect(this->mArea, mBackgroundColor, 0); for (auto& sideWidget : mSideWidgets) { @@ -89,7 +89,7 @@ void GridLayoutWidget::eventDraw(Canvas& canvas) { } } -void GridLayoutWidget::eventDrawOver(Canvas& canvas) { +void DockLayoutWidget::eventDrawOver(Canvas& canvas) { if (!mPreview) return; if (mPreviewSide != NONE) canvas.rect(mPreviewArea.shrink(mPadding * 2), mPreviewColor, mRounding); @@ -100,7 +100,7 @@ void GridLayoutWidget::eventDrawOver(Canvas& canvas) { } } -void GridLayoutWidget::calculateSideAreas() { +void DockLayoutWidget::calculateSideAreas() { auto startArea = this->mArea; for (auto& sideWidget : mSideWidgets) { const auto side = sideWidget.order; @@ -135,7 +135,7 @@ void GridLayoutWidget::calculateSideAreas() { mCenterArea = startArea.shrink(mPadding); } -void GridLayoutWidget::calculateResizeHandles() { +void DockLayoutWidget::calculateResizeHandles() { RectF rec; if (isSideVisible(LEFT)) { @@ -162,7 +162,7 @@ void GridLayoutWidget::calculateResizeHandles() { } } -void GridLayoutWidget::handleResizeEvents(const Events& events) { +void DockLayoutWidget::handleResizeEvents(const Events& events) { for (auto& sideWidget : mSideWidgets) { auto& sideSize = sideWidget.absoluteSize; auto& resizeHandle = sideWidget.resizeHandle; @@ -225,11 +225,11 @@ void GridLayoutWidget::handleResizeEvents(const Events& events) { } } -void GridLayoutWidget::updateChildSideWidgets() { +void DockLayoutWidget::updateChildSideWidgets() { // Update Child Widgets { for (ualni i = 0; i < 4; i++) { - if (!isSideExists(Side(i))) continue; + if (!sideExists(Side(i))) continue; auto widget = mSideWidgets[i].widget; if (!isSideVisible(Side(i))) { @@ -261,7 +261,7 @@ void GridLayoutWidget::updateChildSideWidgets() { */ } -void GridLayoutWidget::calculateHeaderAreas() { +void DockLayoutWidget::calculateHeaderAreas() { for (ualni i = 0; i < 4; i++) { if (!isSideVisible(Side(i))) continue; auto& area = mSideWidgets[i].area; @@ -273,13 +273,13 @@ void GridLayoutWidget::calculateHeaderAreas() { } } -bool GridLayoutWidget::isSideVisible(GridLayoutWidget::Side side) { - return isSideExists(side) && !mSideWidgets[side].hidden; +bool DockLayoutWidget::isSideVisible(DockLayoutWidget::Side side) { + return sideExists(side) && !mSideWidgets[side].hidden; } -bool GridLayoutWidget::isSideExists(GridLayoutWidget::Side side) { return mSideWidgets[side].widget; } +bool DockLayoutWidget::sideExists(DockLayoutWidget::Side side) { return mSideWidgets[side].widget; } -ualni GridLayoutWidget::getVisibleSidesSize() { +ualni DockLayoutWidget::getVisibleSidesSize() { ualni out = 0; for (ualni i = 0; i < 4; i++) { if (isSideVisible(Side(i))) out++; @@ -287,9 +287,9 @@ ualni GridLayoutWidget::getVisibleSidesSize() { return out; } -GridLayoutWidget::Side GridLayoutWidget::getPreviewSide() { return mPreviewSide; } +DockLayoutWidget::Side DockLayoutWidget::getPreviewSide() { return mPreviewSide; } -void GridLayoutWidget::handlePreview(const Events& events) { +void DockLayoutWidget::handlePreview(const Events& events) { if (!mPreview) { mPreviewSide = NONE; return; diff --git a/Widgets/private/WorkspaceWidget.cpp b/Widgets/private/WorkspaceWidget.cpp index 45aecaf..3d38668 100644 --- a/Widgets/private/WorkspaceWidget.cpp +++ b/Widgets/private/WorkspaceWidget.cpp @@ -20,7 +20,7 @@ void WorkspaceWidget::eventProcess(const Events& events) { if (widget->mDropped) { auto side = mDockSpace.getPreviewSide(); - if (side != GridLayoutWidget::NONE) { + if (side != DockLayoutWidget::NONE) { mFloatingLayer.mChildWidgets.removeNode(mFloatingLayer.mChildWidgets.find(widget)); widget->setCollapsed(false); widget->stopFloating(); @@ -52,13 +52,13 @@ void WorkspaceWidget::eventProcess(const Events& events) { // toggle hide state if (events.isDown(InputID::LEFT_ALT)) { if (events.isPressed(InputID::N1)) { - mDockSpace.toggleHiddenState(GridLayoutWidget::LEFT); + mDockSpace.toggleHiddenState(DockLayoutWidget::LEFT); } else if (events.isPressed(InputID::N2)) { - mDockSpace.toggleHiddenState(GridLayoutWidget::TOP); + mDockSpace.toggleHiddenState(DockLayoutWidget::TOP); } else if (events.isPressed(InputID::N3)) { - mDockSpace.toggleHiddenState(GridLayoutWidget::RIGHT); + mDockSpace.toggleHiddenState(DockLayoutWidget::RIGHT); } else if (events.isPressed(InputID::N4)) { - mDockSpace.toggleHiddenState(GridLayoutWidget::BOTTOM); + mDockSpace.toggleHiddenState(DockLayoutWidget::BOTTOM); } } diff --git a/Widgets/public/GridLayoutWidget.hpp b/Widgets/public/GridLayoutWidget.hpp index 69b867a..65179b0 100644 --- a/Widgets/public/GridLayoutWidget.hpp +++ b/Widgets/public/GridLayoutWidget.hpp @@ -1,7 +1,7 @@ #include "WidgetBase.hpp" namespace tp { - class GridLayoutWidget : public Widget { + class DockLayoutWidget : public Widget { public: enum Side { LEFT, TOP, RIGHT, BOTTOM, NONE }; @@ -30,7 +30,7 @@ namespace tp { }; public: - GridLayoutWidget(); + DockLayoutWidget(); void eventProcess(const Events& events) override; void eventDraw(Canvas& canvas) override; @@ -54,7 +54,7 @@ namespace tp { void calculateHeaderAreas(); bool isSideVisible(Side side); - bool isSideExists(Side side); + bool sideExists(DockLayoutWidget::Side side); ualni getVisibleSidesSize(); void handlePreview(const Events& events); diff --git a/Widgets/public/WorkspaceWidget.hpp b/Widgets/public/WorkspaceWidget.hpp index b4aa8d7..f8363cc 100644 --- a/Widgets/public/WorkspaceWidget.hpp +++ b/Widgets/public/WorkspaceWidget.hpp @@ -10,7 +10,7 @@ namespace tp { void eventProcess(const Events& events) override; protected: - GridLayoutWidget mDockSpace; + DockLayoutWidget mDockSpace; FloatingLayoutWidget mFloatingLayer; // Parameters diff --git a/WidgetsNew/examples/Example.cpp b/WidgetsNew/examples/Example.cpp index e2817d5..20c8005 100644 --- a/WidgetsNew/examples/Example.cpp +++ b/WidgetsNew/examples/Example.cpp @@ -4,17 +4,27 @@ #include "RootWidget.hpp" #include "AnimationTestWidget.hpp" #include "FloatingWidget.hpp" -#include "SimpleWidgets.hpp" -#include "LayoutWidget.hpp" +#include "DockLayoutWidget.hpp" using namespace tp; class WidgetApplication : public Application { public: WidgetApplication() { - mRootWidget.setRootWidget(&mLayoutWidget); + mRootWidget.setRootWidget(&mDockLayout); + + mDockLayout.addChild(&mFloatingMenu); + mDockLayout.addChild(&mFloatingMenu2); + + mDockLayout.setCenterWidget(&mButton3); + mDockLayout.dockWidget(&mButton4, DockLayoutWidget::RIGHT); + + mFloatingMenu.addToMenu(&mButton); + mFloatingMenu2.addToMenu(&mButton2); + + mButton.setAction([this]() { mButton2.setColor(RGBA::random()); }); + mButton2.setAction([this]() { mButton.setColor(RGBA::random()); }); - mLayoutWidget.addChild(&mFloatingMenu); // mLayoutWidget.addChild(&mButton2); // mWidgetFloating.addChild(&mButton); @@ -24,8 +34,7 @@ public: // mAnimationTestWidget.addChild(&mWidgetFloating); // mAnimationTestWidget.addChild(&mLayoutWidget); - // RootWidget::setWidgetArea(mButton, { 100, 100, 150, 200 }); - + RootWidget::setWidgetArea(mFloatingMenu2, { 300, 100, 150, 200 }); RootWidget::setWidgetArea(mFloatingMenu, { 100, 100, 150, 200 }); // mWidgetFloating.addChild(&mLayoutWidget); @@ -50,13 +59,20 @@ public: } private: - LabelWidget mLabel; ButtonWidget mButton; ButtonWidget mButton2; + ButtonWidget mButton3; + ButtonWidget mButton4; + + FloatingMenu mFloatingMenu; + FloatingMenu mFloatingMenu2; + + LabelWidget mLabel; FloatingWidget mWidgetFloating; AnimationTestWidget mAnimationTestWidget; DesktopLayout mLayoutWidget; - FloatingMenu mFloatingMenu; + + DockLayoutWidget mDockLayout; RootWidget mRootWidget; }; diff --git a/WidgetsNew/private/DockLayoutWidget.cpp b/WidgetsNew/private/DockLayoutWidget.cpp new file mode 100644 index 0000000..5b39cd4 --- /dev/null +++ b/WidgetsNew/private/DockLayoutWidget.cpp @@ -0,0 +1,324 @@ +#include "DockLayoutWidget.hpp" + +using namespace tp; + +DockLayoutWidget::DockLayoutWidget() { + mSideWidgets[0].side = LEFT; + mSideWidgets[1].side = TOP; + mSideWidgets[2].side = RIGHT; + mSideWidgets[3].side = BOTTOM; +} + +void DockLayoutWidget::process(const EventHandler& events) { + // calculateHeaderAreas(); + // handlePreview(events); + + handleResizeEvents(events); + calculateSideAreas(); + calculateResizeHandles(); + updateChildSideWidgets(); +} + +void DockLayoutWidget::draw(Canvas& canvas) { + canvas.rect(getRelativeArea(), mBackgroundColor, 0); +} + +void DockLayoutWidget::drawOverlay(Canvas& canvas) { + for (auto& sideWidget : mSideWidgets) { + if (!isSideVisible(sideWidget.side)) continue; + auto& handle = sideWidget.resizeHandle; + + if (handle.active) { + canvas.rect(handle.area.shrink(mPadding / 1.5f), mResizeHandleColorActive, 0); + } else if (handle.hover) { + canvas.rect(handle.area.shrink(mPadding / 1.5f), mResizeHandleColorHovered, 0); + } + } + + for (auto& sideWidget : mSideWidgets) { + if (!isSideVisible(sideWidget.side)) continue; + canvas.rect(sideWidget.headerArea, mResizeHandleColorActive, 0); + } + + if (mPreview) { + if (mPreviewSide != NONE) canvas.rect(mPreviewArea.shrink(mPadding * 2), mPreviewColor, mRounding); + + for (auto& sideWidget : mSideWidgets) { + if (sideWidget.widget) continue; + canvas.rect(sideWidget.previewHandleArea, mPreviewColor, mRounding); + } + } +} + +bool DockLayoutWidget::propagateEventsToChildren() const { + return !mResizing; +} + +bool DockLayoutWidget::needsNextFrame() const { + return Widget::needsNextFrame() || mResizing; +} + +void DockLayoutWidget::setCenterWidget(Widget* widget) { + mCenterWidget = widget; + addChild(widget); +} + +void DockLayoutWidget::dockWidget(Widget* widget, Side side) { + DEBUG_ASSERT(!sideExists(side)) + + addChild(widget); + + auto& sideWidget = mSideWidgets[side]; + sideWidget.widget = widget; + + for (auto& order : mSideWidgets) { + if (order.order == -1) { + order.order = side; + break; + } + } + + sideWidget.hidden = false; +} + +void DockLayoutWidget::undockWidget(Side side) { + DEBUG_ASSERT(sideExists(side)) + + bool removed = false; + for (ualni i = 0; i < 3; i++) { + if (mSideWidgets[i].order == side) { + removed = true; + } + if (removed) { + swap(mSideWidgets[i].order, mSideWidgets[i + 1].order); + } + } + mSideWidgets[3].order = -1; + + mSideWidgets[side].widget = nullptr; +} + +void DockLayoutWidget::toggleWidgetVisibility(Side side) { + DEBUG_ASSERT(sideExists(side)) + mSideWidgets[side].hidden = !mSideWidgets[side].hidden; +} + +void DockLayoutWidget::calculateSideAreas() { + auto startArea = getRelativeArea(); + + for (auto& sideWidget : mSideWidgets) { + const auto side = sideWidget.order; + + if (side == -1) break; + if (!isSideVisible(Side(side))) continue; + + bool vertical = side == TOP || side == BOTTOM; + bool opposite = side == BOTTOM || side == RIGHT; + + auto& sideSize = mSideWidgets[side].absoluteSize; + + auto factor = sideSize / startArea.size[vertical]; + if (opposite) factor = factor * -1 + 1; + auto& area = mSideWidgets[side].area; + + if (!vertical) { + const auto first = startArea.splitByFactorHL(factor); + const auto second = startArea.splitByFactorHR(factor); + area = side == LEFT ? first : second; + startArea = side == LEFT ? second : first; + } else { + const auto first = startArea.splitByFactorVT(factor); + const auto second = startArea.splitByFactorVB(factor); + area = side == TOP ? first : second; + startArea = side == TOP ? second : first; + } + + area = area.shrink(mPadding); + } + + mCenterArea = startArea.shrink(mPadding); +} + +void DockLayoutWidget::calculateResizeHandles() { + RectF rec; + RectF area = getRelativeArea(); + + for (auto& sideWidget : mSideWidgets) { + auto& sideSize = sideWidget.absoluteSize; + auto& resizeHandle = sideWidget.resizeHandle; + + if (resizeHandle.end < mSideSizePadding * 2) { + sideSize = resizeHandle.end / 2.f; + } else { + sideSize = clamp(sideSize, resizeHandle.start + mSideSizePadding, resizeHandle.end - mSideSizePadding); + } + } + + if (isSideVisible(LEFT)) { + auto& side = mSideWidgets[LEFT]; + rec = { side.area.p4(), { mPadding * 2, side.area.size.y } }; + side.resizeHandle.area = rec; + side.resizeHandle.start = 0; + side.resizeHandle.end = mCenterArea.p3().x; + } + if (isSideVisible(RIGHT)) { + auto& side = mSideWidgets[RIGHT]; + rec = { side.area.p1(), { mPadding * 2, side.area.size.y } }; + rec.x -= mPadding * 2; + side.resizeHandle.area = rec; + side.resizeHandle.start = 0; + side.resizeHandle.end = (area.p3() - mCenterArea.p1()).x; + } + if (isSideVisible(TOP)) { + auto& side = mSideWidgets[TOP]; + rec = { side.area.p2(), { side.area.size.x, mPadding * 2 } }; + side.resizeHandle.area = rec; + side.resizeHandle.start = 0; + side.resizeHandle.end = mCenterArea.p2().y; + } + if (isSideVisible(BOTTOM)) { + auto& side = mSideWidgets[BOTTOM]; + rec = { side.area.p1(), { side.area.size.x, mPadding * 2 } }; + rec.y -= mPadding * 2; + side.resizeHandle.area = rec; + side.resizeHandle.start = 0; + side.resizeHandle.end = (area.p3() - mCenterArea.p1()).y; + } +} + +void DockLayoutWidget::handleResizeEvents(const EventHandler& events) { + auto pointer = events.getPointer(); + auto pointerDelta = events.getPointerDelta(); + + // check triggers + if (events.isReleased(InputID::MOUSE1)) { + for (auto& sideWidget : mSideWidgets) { + sideWidget.resizeHandle.active = false; + } + + mResizing = false; + } else { + for (auto& sideWidget : mSideWidgets) { + sideWidget.resizeHandle.hover = sideWidget.resizeHandle.area.isInside(pointer); + if (sideWidget.resizeHandle.hover && events.isPressed(InputID::MOUSE1)) { + sideWidget.resizeHandle.active = true; + + mResizing = true; + triggerWidgetUpdate(); + } + } + } + + // do the resizing + for (auto& sideWidget : mSideWidgets) { + if (!sideWidget.resizeHandle.active) continue; + + halnf delta = pointerDelta[(sideWidget.side == TOP || sideWidget.side == BOTTOM)]; + + if (sideWidget.side == BOTTOM || sideWidget.side == RIGHT) { + delta *= -1; + } + + sideWidget.absoluteSize += delta; + } +} + +void DockLayoutWidget::updateChildSideWidgets() { + // Update Child Widgets + { + for (ualni i = 0; i < 4; i++) { + if (!sideExists(Side(i))) continue; + auto widget = mSideWidgets[i].widget; + + if (!isSideVisible(Side(i))) { + // widget->mEnable = false; + } else { + widget->setArea(mSideWidgets[i].area); + // widget->mEnable = true; + } + } + + if (mCenterWidget) mCenterWidget->setArea(mCenterArea); + } + + // update depth order + /* + if (mChildWidgets.size() > 1) { + for (auto sideChild : mSideWidgets) { + if (!sideChild) continue; + + for (auto childNode = mChildWidgets.firstNode(); childNode; childNode = childNode->next) { + if (childNode->data == sideChild) { + mChildWidgets.detach(childNode); + mChildWidgets.pushBack(childNode); + break; + } + } + } + } + */ +} + +void DockLayoutWidget::calculateHeaderAreas() { + for (ualni i = 0; i < 4; i++) { + if (!isSideVisible(Side(i))) continue; + auto& area = mSideWidgets[i].area; + const auto factor = mHeaderSize / area.size.y; + mSideWidgets[i].headerArea = area.splitByFactorVT(factor); + area = area.splitByFactorVB(factor); + + mSideWidgets[i].headerArea.size.y -= mPadding; + } +} + +bool DockLayoutWidget::isSideVisible(DockLayoutWidget::Side side) { + return sideExists(side) && !mSideWidgets[side].hidden; +} + +bool DockLayoutWidget::sideExists(DockLayoutWidget::Side side) { return mSideWidgets[side].widget; } + +ualni DockLayoutWidget::getVisibleSidesSize() { + ualni out = 0; + for (ualni i = 0; i < 4; i++) { + if (isSideVisible(Side(i))) out++; + } + return out; +} + +void DockLayoutWidget::handlePreview(const EventHandler& events) { + if (!mPreview) { + mPreviewSide = NONE; + return; + } + + const halnf factor = 0.3; + const halnf handleFactor = 0.1; + + const auto handleSize = min(mCenterArea.size.x, mCenterArea.size.y) * handleFactor; + + for (auto& sideWidget : mSideWidgets) { + if (sideWidget.widget) continue; + + switch (sideWidget.side) { + case TOP: sideWidget.area = mCenterArea.splitByFactorVT(factor); break; + case BOTTOM: sideWidget.area = mCenterArea.splitByFactorVB(1 - factor); break; + case LEFT: sideWidget.area = mCenterArea.splitByFactorHL(factor); break; + case RIGHT: sideWidget.area = mCenterArea.splitByFactorHR(1 - factor); break; + default: break; + } + + sideWidget.area = sideWidget.area.shrink(mPadding * 2); + sideWidget.previewHandleArea = sideWidget.area.getSizedFromCenter({ handleSize, handleSize }); + } + + mPreviewArea = {}; + mPreviewSide = NONE; + for (auto& sideWidget : mSideWidgets) { + if (sideWidget.widget) continue; + + if (sideWidget.previewHandleArea.isInside(events.getPointer())) { + mPreviewArea = sideWidget.area; + mPreviewSide = sideWidget.side; + } + } +} diff --git a/WidgetsNew/private/FloatingWidget.cpp b/WidgetsNew/private/FloatingWidget.cpp index 74d8c4f..c65ba27 100644 --- a/WidgetsNew/private/FloatingWidget.cpp +++ b/WidgetsNew/private/FloatingWidget.cpp @@ -10,6 +10,8 @@ void FloatingWidget::process(const EventHandler& events) { if (inside && events.isPressed(InputID::MOUSE1)) { mPointerStart = relativePointer; mIsFloating = true; + + bringToFront(); } if (mIsFloating && resizeHandleRect().isInside(relativePointer)) { diff --git a/WidgetsNew/private/RootWidget.cpp b/WidgetsNew/private/RootWidget.cpp index de880f9..ecb714f 100644 --- a/WidgetsNew/private/RootWidget.cpp +++ b/WidgetsNew/private/RootWidget.cpp @@ -19,23 +19,26 @@ void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) { // construct hierarchy tree of widgets to process updateTreeToProcess(); + auto root = mWidgetsToProcess.find(mRoot) == mWidgetsToProcess.end() ? nullptr : &mWidgetsToProcess[mRoot]; + // update active tree animations - updateAnimations(&mWidgetsToProcess[mRoot]); + updateAnimations(root); // update all events and call all event processing callbacks EventHandler dummyEvents; processFocusItems(*events); - processActiveTree(&mWidgetsToProcess[mRoot], dummyEvents); + processActiveTree(root, dummyEvents); // update widget sizes base on individual size policies - adjustSizes(&mWidgetsToProcess[mRoot]); + adjustSizes(root); // trigger some widgets by moise pointer handleFocusChanges(*events); - // check triggered widgets for removal - erase_if(mTriggeredWidgets, [](auto widget) { + // check triggered widgets for removal + erase_if(mTriggeredWidgets, [this](auto widget) { + if (mWidgetsToProcess.find(widget) == mWidgetsToProcess.end()) return false; auto end = !widget->needsNextFrame(); if (end) { widget->endAnimations(); @@ -53,8 +56,10 @@ void RootWidget::drawFrame(Canvas& canvas) { // draw all tree from top to bottom drawRecursion(canvas, mRoot, { 0, 0 }); + ImGui::Checkbox("Draw debug", &mDebug); + if (mDebug) { - drawDebug(canvas, mRoot, { 0, 0 }); + drawDebug(canvas, mRoot, { 0, 0 }, 0); ImGui::Text("Triggered Widgets: %i", (int) mTriggeredWidgets.size()); ImGui::Text("Widgets To Process: %i", (int) mWidgetsToProcess.size()); } @@ -80,8 +85,8 @@ void RootWidget::drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos) active->draw(canvas); - for (auto child : active->mChildren) { - drawRecursion(canvas, child, pos + child->getArea().pos); + for (auto child = active->mDepthOrder.lastNode(); child; child = child->prev) { + drawRecursion(canvas, child->data, pos + child->data->getArea().pos); } canvas.setOrigin(pos); @@ -90,14 +95,13 @@ void RootWidget::drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos) active->drawOverlay(canvas); } -void RootWidget::drawDebug(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 }; - if (mWidgetsToProcess.find(active) != mWidgetsToProcess.end()) { RGBA color = { 1, 0, 0, 1}; canvas.frame(area, color); - canvas.text(active->mName.c_str(), area, 12, Canvas::Align::LC, 2, color); + canvas.text((active->mName + ":" + std::to_string(depthOrder)).c_str(), area, 22, Canvas::Align::LC, 2, color); } if (active->mInFocus) { @@ -107,8 +111,9 @@ void RootWidget::drawDebug(Canvas& canvas, Widget* active, const Vec2F& pos) { canvas.circle(active->mGlobalPoint, 10, active->mDebugColor); } - for (auto child : active->mChildren) { - drawDebug(canvas, child, pos + child->getArea().pos); + int orderIdx = 0; + for (auto child = active->mDepthOrder.lastNode(); child; child = child->prev) { + drawDebug(canvas, child->data, pos + child->data->getArea().pos, orderIdx++); } } @@ -126,40 +131,61 @@ void RootWidget::updateAnimations(ActiveTreeNode* iter) { } void RootWidget::getWidgetPath(Widget* widget, std::vector& out) { + if (!widget) return; + for (auto iter = widget; iter && iter != this; iter = iter->mParent) { out.push_back(iter); } + + for (auto i = 0; i < out.size() / 2; i++) { + swap(out[i], out[out.size() - i - 1]); + } } void RootWidget::handleFocusChanges(EventHandler& events) { auto prevFocus = mInFocusWidget; + findFocusWidget(mRoot, &mInFocusWidget, events.getPointer()); - if (prevFocus) { - std::vector path1; - std::vector path2; + if (mInFocusWidget) updateWidget(mInFocusWidget); - getWidgetPath(prevFocus, path1); - getWidgetPath(mInFocusWidget, path2); - - size_t iter = 0; - while (path1[iter] == path2[iter]) { - iter++; + std::vector path2; + getWidgetPath(mInFocusWidget, path2); + size_t propLen2 = path2.size(); + for (auto i = 0; i < path2.size(); i++) { + if (!path2[i]->propagateEventsToChildren()) { + propLen2 = i + 1; + break; } + } - for (auto i = iter; i < path1.size(); i++) { - path1[i]->mouseLeave(); + std::vector path1; + getWidgetPath(prevFocus, path1); + size_t propLen1 = path1.size(); + for (auto i = 0; i < path1.size(); i++) { + if (!path1[i]->propagateEventsToChildren()) { + propLen1 = i + 1; + break; } + } - for (auto i = iter; i < path2.size(); i++) { - path2[i]->mouseEnter(); - } + size_t mostCommonIdx = 0; - } else { - for (auto iter = mInFocusWidget; iter; iter = iter->mParent) { - iter->mouseEnter(); + if (!(path1.empty() || path2.empty())) { + while (path1[mostCommonIdx] == path2[mostCommonIdx]) { + mostCommonIdx++; } } + + for (auto i = 0; i < path1.size(); i++) { + path1[i]->mInFocus = false; + 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(); + } } void RootWidget::findFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer) { @@ -167,8 +193,8 @@ void RootWidget::findFocusWidget(Widget* iter, Widget** focus, const Vec2F& poin *focus = iter; - for (auto child : iter->mChildren) { - findFocusWidget(child, focus, pointer - iter->mArea.getTargetRect().pos); + for (auto child = iter->mDepthOrder.lastNode(); child; child = child->prev) { + findFocusWidget(child->data, focus, pointer - iter->mArea.getTargetRect().pos); } } @@ -208,10 +234,6 @@ void RootWidget::processFocusItems(EventHandler& events) { std::vector path; getWidgetPath(mInFocusWidget, path); - for (auto i = 0; i < path.size() / 2; i++) { - swap(path[i], path[path.size() - i - 1]); - } - size_t len = path.size(); for (auto i = 0; i < len; i++) { if (!path[i]->propagateEventsToChildren()) { diff --git a/WidgetsNew/private/SimpleWidgets.cpp b/WidgetsNew/private/SimpleWidgets.cpp index 3d5c4d3..d957a31 100644 --- a/WidgetsNew/private/SimpleWidgets.cpp +++ b/WidgetsNew/private/SimpleWidgets.cpp @@ -30,6 +30,12 @@ void ButtonWidget::setAction(const std::function& action) { mAction = action; } +void ButtonWidget::setColor(const RGBA& in) { + mColor = in; + mColorAnimated.setTargetColor(mColor); + triggerWidgetUpdate(); +} + void ButtonWidget::process(const EventHandler& eventHandler) { if (getArea().isInside(eventHandler.getPointer())) { if (eventHandler.isPressed(InputID::MOUSE1)) { diff --git a/WidgetsNew/private/Widget.cpp b/WidgetsNew/private/Widget.cpp index e5ed9de..2da4239 100644 --- a/WidgetsNew/private/Widget.cpp +++ b/WidgetsNew/private/Widget.cpp @@ -62,6 +62,24 @@ void Widget::adjustRect() { } } +void Widget::bringToFront() { + if (!mParent) return; + auto& order = mParent->mDepthOrder; + auto node = order.find(this); + DEBUG_ASSERT(node); + order.detach(node); + order.pushFront(node); +} + +void Widget::bringToBack() { + if (!mParent) return; + auto& order = mParent->mDepthOrder; + auto node = order.find(this); + DEBUG_ASSERT(node); + order.detach(node); + order.pushBack(node); +} + void Widget::adjustChildrenRect() { auto area = RectF({}, getArea().size); for (auto widget : mChildren) { @@ -71,7 +89,6 @@ void Widget::adjustChildrenRect() { void Widget::mouseEnter() { mInFocus = true; - triggerWidgetUpdate(); } void Widget::mouseLeave() { @@ -80,4 +97,14 @@ void Widget::mouseLeave() { bool Widget::propagateEventsToChildren() const { return true; -} \ No newline at end of file +} + +void Widget::addChild(Widget* child) { + mChildren.push_back(child); + mDepthOrder.pushBack(child); + + child->mParent = this; + + triggerWidgetUpdate(); + child->triggerWidgetUpdate(); +} diff --git a/WidgetsNew/public/DockLayoutWidget.hpp b/WidgetsNew/public/DockLayoutWidget.hpp new file mode 100644 index 0000000..2e6173d --- /dev/null +++ b/WidgetsNew/public/DockLayoutWidget.hpp @@ -0,0 +1,94 @@ +#include "Widget.hpp" + +namespace tp { + class DockLayoutWidget : public Widget { + public: + enum Side { LEFT, TOP, RIGHT, BOTTOM, NONE }; + + private: + struct ResizeHandle { + RectF area{ 0, 0, 0, 0 }; + halnf start{ 0 }; + halnf end{ 0 }; + bool active = false; + bool hover = false; + }; + + struct SideWidgetData { + Widget* widget = nullptr; + bool hidden = false; + halnf absoluteSize = 200; + alni order = -1; + + Side side = { TOP }; + + RectF area = {}; + RectF headerArea = {}; + RectF previewHandleArea = {}; + + ResizeHandle resizeHandle; + }; + + public: + DockLayoutWidget(); + + public: + void adjustRect() override {} + void adjustChildrenRect() override {} + + void process(const EventHandler& events) override; + void draw(Canvas& canvas) override; + void drawOverlay(Canvas& canvas) override; + + [[nodiscard]] bool propagateEventsToChildren() const override; + [[nodiscard]] bool needsNextFrame() const override; + + public: + void setCenterWidget(Widget* widget); + + void dockWidget(Widget* widget, Side side); + void undockWidget(Side side); + void toggleWidgetVisibility(Side side); + + private: + void calculateSideAreas(); + void calculateResizeHandles(); + void handleResizeEvents(const EventHandler& events); + void updateChildSideWidgets(); + + void calculateHeaderAreas(); + + bool isSideVisible(Side side); + bool sideExists(DockLayoutWidget::Side side); + ualni getVisibleSidesSize(); + void handlePreview(const EventHandler& events); + + private: + RectF mPreviewArea = {}; + Side mPreviewSide = NONE; + int resizeType[2] = { 0, 0 }; + + SideWidgetData mSideWidgets[4]; + + private: + bool mResizing = false; + + Widget* mCenterWidget = nullptr; + RectF mCenterArea {}; + + // Parameters + halnf mSideSizePadding = 150.f; + halnf mPadding = 4; + halnf mHeaderSize = 27; + halnf mRounding = 10; + Vec2F mPreviewHandleSize = { 50, 50 }; + + RGBA mResizeHandleColorHovered = RGBA(0.3, 0.3, 0.3, 1); + RGBA mResizeHandleColorActive = RGBA(0.6, 0.6, 0.6, 1); + RGBA mBackgroundColor = RGBA(0, 0, 0, 1); + RGBA mPreviewColor = RGBA(0.6, 0.6, 0.6, 1); + + public: + bool mPreview = false; + }; +} \ No newline at end of file diff --git a/WidgetsNew/public/FloatingWidget.hpp b/WidgetsNew/public/FloatingWidget.hpp index e501dc5..a701244 100644 --- a/WidgetsNew/public/FloatingWidget.hpp +++ b/WidgetsNew/public/FloatingWidget.hpp @@ -18,7 +18,7 @@ namespace tp { RectF resizeHandleRect(); - bool propagateEventsToChildren() const override; + [[nodiscard]] bool propagateEventsToChildren() const override; private: bool mIsFloating = false; @@ -41,8 +41,6 @@ namespace tp { mMenuLayout.addChild(&mHeader); mMenuLayout.addChild(&mBodyLayout); - addToMenu(&mTestButton); - mHeader.setText("Menu"); } diff --git a/WidgetsNew/public/RootWidget.hpp b/WidgetsNew/public/RootWidget.hpp index b96dc94..6f9e55e 100644 --- a/WidgetsNew/public/RootWidget.hpp +++ b/WidgetsNew/public/RootWidget.hpp @@ -33,7 +33,7 @@ namespace tp { void updateTreeToProcess(); void updateAnimations(ActiveTreeNode* iter); void drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos); - void drawDebug(Canvas& canvas, Widget* active, const Vec2F& pos); + void drawDebug(Canvas& canvas, Widget* active, const Vec2F& pos, int depthOrder); void findFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer); void handleFocusChanges(EventHandler& events); void getWidgetPath(Widget* widget, std::vector& out); @@ -50,6 +50,6 @@ namespace tp { std::map mWidgetsToProcess; Widget* mInFocusWidget = nullptr; - bool mDebug = false; + bool mDebug = true; }; } \ No newline at end of file diff --git a/WidgetsNew/public/SimpleWidgets.hpp b/WidgetsNew/public/SimpleWidgets.hpp index 03e3a1b..e767a0a 100644 --- a/WidgetsNew/public/SimpleWidgets.hpp +++ b/WidgetsNew/public/SimpleWidgets.hpp @@ -45,6 +45,8 @@ namespace tp { void endAnimations() override; void updateAnimations() override; + void setColor(const RGBA& in); + private: std::function mAction; diff --git a/WidgetsNew/public/Widget.hpp b/WidgetsNew/public/Widget.hpp index a7e5807..5c8507f 100644 --- a/WidgetsNew/public/Widget.hpp +++ b/WidgetsNew/public/Widget.hpp @@ -22,18 +22,15 @@ namespace tp { mDebugColor = col; } - void addChild(Widget* child) { - mChildren.push_back(child); - child->mParent = this; - - triggerWidgetUpdate(); - child->triggerWidgetUpdate(); - } + void addChild(Widget* child); WidgetManagerInterface* getRoot(); void triggerWidgetUpdate(); + void bringToFront(); + void bringToBack(); + public: virtual void process(const EventHandler& events) {} virtual void draw(Canvas& canvas) { canvas.rect(mArea.getCurrentRect(), RGBA(1.f), 10); } @@ -63,12 +60,11 @@ namespace tp { Widget* mParent = nullptr; std::vector mChildren; + List mDepthOrder; // relative to the parent SpringRect mArea; - ualni mZValue = 0; - bool mInFocus = false; // debug