From f68f91f7d582ed11ca5bf753799dde3f73186890 Mon Sep 17 00:00:00 2001 From: IlyaShurupov Date: Sun, 20 Oct 2024 12:01:42 +0300 Subject: [PATCH 1/4] fix focus locking --- Widgets/examples/Example.cpp | 17 +++++++++++++---- Widgets/private/managers/UpdateManager.cpp | 17 +++++++++++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/Widgets/examples/Example.cpp b/Widgets/examples/Example.cpp index 288d1b1..398298f 100644 --- a/Widgets/examples/Example.cpp +++ b/Widgets/examples/Example.cpp @@ -13,7 +13,7 @@ using namespace tp; class Example : public WidgetApplication { public: Example() { - exampleScrolling(); + examplePopup(); } void exampleAll() { @@ -71,19 +71,28 @@ public: void examplePopup() { static Widget root; + static Widget popup; + static ButtonWidget closeButton; static ButtonWidget openButton; + static ButtonWidget buttons[3]; + + for (auto& button : buttons) { + popup.addChild(&button); + } + + popup.addChild(&closeButton); root.addChild(&openButton); ((BasicLayout*)root.getLayout())->setLayoutPolicy(LayoutPolicy::Passive); openButton.setAction([&](){ - closeButton.setArea({ 50, 50, 100, 100 }); - openButton.openPopup(&closeButton); + popup.setArea({ 50, 50, 300, 300 }); + openButton.openPopup(&popup); }); closeButton.setAction([&](){ - closeButton.closePopup(&closeButton); + closeButton.closePopup(&popup); }); openButton.setText("open"); diff --git a/Widgets/private/managers/UpdateManager.cpp b/Widgets/private/managers/UpdateManager.cpp index 4dcf24c..57e8475 100644 --- a/Widgets/private/managers/UpdateManager.cpp +++ b/Widgets/private/managers/UpdateManager.cpp @@ -76,10 +76,19 @@ void UpdateManager::handleFocusChanges(Widget* root, EventHandler& events) { mInFocusWidget = nullptr; - if (!mFocusLockWidget) { - findFocusWidget(root, &mInFocusWidget, events.getPointer()); - } else { - mInFocusWidget = mFocusLockWidget; + findFocusWidget(root, &mInFocusWidget, events.getPointer()); + + if (mFocusLockWidget) { + bool hasLockedWidget = false; + for (auto iter = mInFocusWidget; iter; iter = iter->mParent) { + if (iter == mFocusLockWidget) { + hasLockedWidget = true; + break; + } + } + if (!hasLockedWidget) { + mInFocusWidget = mFocusLockWidget; + } } // if (mInFocusWidget == prevFocus) return; From 458e56fb4ce558f5c350f62557540e168cbfd11f Mon Sep 17 00:00:00 2001 From: IlyaShurupov Date: Fri, 18 Oct 2024 15:18:50 +0300 Subject: [PATCH 2/4] Debug Tools and Scrollable widget --- .gitmodules | 3 + 3DEditor/private/Editor.cpp | 2 +- 3DEditor/public/EditorWidget.hpp | 2 +- Externals/CMakeLists.txt | 5 +- Externals/implot | 1 + Graphics/private/DebugGUI.cpp | 7 +++ Graphics/private/Window.cpp | 2 + LibraryViewer/public/GUI.hpp | 8 +-- Modules/public/Timing.hpp | 16 ++++++ TODO | 11 +++- Widgets/CMakeLists.txt | 6 +- Widgets/examples/Example.cpp | 15 ++--- Widgets/private/RootWidget.cpp | 35 ++++++------ Widgets/private/WidgetApplication.cpp | 56 +++++++++++++++++- Widgets/private/layouts/DockLayout.cpp | 6 +- Widgets/private/layouts/ScrollableLayout.cpp | 4 +- Widgets/private/managers/DebugManager.cpp | 60 ++++++++++++-------- Widgets/private/managers/LayoutManager.cpp | 4 ++ Widgets/private/widgets/DockWidget.cpp | 2 + Widgets/private/widgets/ScrollableWidget.cpp | 21 ++++++- Widgets/public/Layout.hpp | 2 +- Widgets/public/RootWidget.hpp | 2 - Widgets/public/WidgetApplication.hpp | 35 +++--------- Widgets/public/layouts/DockLayout.hpp | 2 +- Widgets/public/mangers/DebugManager.hpp | 58 ++++++++++++++++++- Widgets/public/widgets/ScrollableWidget.hpp | 10 ++++ Widgets/public/widgets/SimpleWidgets.hpp | 2 +- 27 files changed, 274 insertions(+), 103 deletions(-) create mode 160000 Externals/implot diff --git a/.gitmodules b/.gitmodules index 9d1d1de..7bafd8b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "Externals/asio"] path = Externals/asio url = https://github.com/elushaX/asio.git +[submodule "Externals/implot"] + path = Externals/implot + url = https://github.com/epezent/implot.git diff --git a/3DEditor/private/Editor.cpp b/3DEditor/private/Editor.cpp index 26599a0..59facdf 100644 --- a/3DEditor/private/Editor.cpp +++ b/3DEditor/private/Editor.cpp @@ -57,7 +57,7 @@ void Editor::loadDefaults() { } void Editor::setViewportSize(const Vec2F& size) { - if (size.x < 0 || size.y < 0) return; + if (size.x <= 0 || size.y <= 0) return; // TODO remove // mScene.mCamera.rotate(0.01f, 0.0); diff --git a/3DEditor/public/EditorWidget.hpp b/3DEditor/public/EditorWidget.hpp index 00899df..0914937 100644 --- a/3DEditor/public/EditorWidget.hpp +++ b/3DEditor/public/EditorWidget.hpp @@ -17,7 +17,7 @@ namespace tp { ~ViewportWidget() override { mCanvas->deleteImageHandle(mImage); } void process(const EventHandler& events) override { - mEditor->setViewportSize(getArea().size); + mEditor->setViewportSize(getAreaT().size); } void draw(Canvas& canvas) override { diff --git a/Externals/CMakeLists.txt b/Externals/CMakeLists.txt index ce5f9b4..dcfd02d 100644 --- a/Externals/CMakeLists.txt +++ b/Externals/CMakeLists.txt @@ -49,11 +49,14 @@ set(${PROJECT_NAME}_SOURCES imgui/backends/imgui_impl_glfw.cpp imgui/backends/imgui_impl_opengl3.cpp + + implot/implot.cpp + implot/implot_items.cpp ) add_library(${PROJECT_NAME} STATIC ${${PROJECT_NAME}_SOURCES}) include_directories(${PROJECT_NAME} ./glfw/include) -target_include_directories(${PROJECT_NAME} PUBLIC ./imgui/ ./imgui/backends/) +target_include_directories(${PROJECT_NAME} PUBLIC ./imgui/ ./imgui/backends/ ./implot/) project(Nanovg) set(${PROJECT_NAME}_SOURCES diff --git a/Externals/implot b/Externals/implot new file mode 160000 index 0000000..f156599 --- /dev/null +++ b/Externals/implot @@ -0,0 +1 @@ +Subproject commit f156599faefe316f7dd20fe6c783bf87c8bb6fd9 diff --git a/Graphics/private/DebugGUI.cpp b/Graphics/private/DebugGUI.cpp index d824a88..03f6b1f 100644 --- a/Graphics/private/DebugGUI.cpp +++ b/Graphics/private/DebugGUI.cpp @@ -9,6 +9,8 @@ #include #include +#include "implot.h" + namespace tp { class DebugGUI::Context { public: @@ -41,6 +43,8 @@ DebugGUI::DebugGUI(Window* window) { // appearance io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + ImPlot::CreateContext(); + return; auto& colors = ImGui::GetStyle().Colors; @@ -75,10 +79,13 @@ DebugGUI::DebugGUI(Window* window) { } DebugGUI::~DebugGUI() { + ImPlot::DestroyContext(); + ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); + delete mContext; } diff --git a/Graphics/private/Window.cpp b/Graphics/private/Window.cpp index ac8e958..4f9c756 100644 --- a/Graphics/private/Window.cpp +++ b/Graphics/private/Window.cpp @@ -29,6 +29,8 @@ Window::Window(Vec2F size, const char* title) { // glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, 1); // glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); + mSize = size; + // Create a window and OpenGL context mContext->window = glfwCreateWindow((int) size.x, (int) size.y, title, nullptr, nullptr); if (!mContext->window) { diff --git a/LibraryViewer/public/GUI.hpp b/LibraryViewer/public/GUI.hpp index 9ab1060..10a1ad7 100644 --- a/LibraryViewer/public/GUI.hpp +++ b/LibraryViewer/public/GUI.hpp @@ -248,7 +248,7 @@ namespace tp { void process(const EventHandler& events) override { filter(); - for (auto track : mSongList.getContent()) { + for (auto track : mSongList.getContainer()->getChildren()) { if (auto trackWidget = dynamic_cast(track.data())) { if (trackWidget->mState == TrackWidget::SELECTED) { mCurrentTrackInfo.mTrack = trackWidget->mTrack; @@ -268,7 +268,7 @@ namespace tp { void filter() { if (!mCurrentTrackInfo.isSongFilterChanged) return; - mSongList.clearChildren(); + mSongList.getContainer()->clear(); for (auto track : mTracks) { if (!mCurrentTrackInfo.songFilter.PassFilter(track->mTrack->mName.c_str()) && @@ -290,7 +290,7 @@ namespace tp { break; } - mSongList.addToMenu(track.data()); + mSongList.getContainer()->addChild(track.data()); } mCurrentTrackInfo.isSongFilterChanged = false; @@ -302,7 +302,7 @@ namespace tp { Buffer mTracks; - FloatingMenu mSongList; + ScrollableWidget mSongList; TrackInfoWidget mCurrentTrackInfo; TrackWidget mCurrentTrack; }; diff --git a/Modules/public/Timing.hpp b/Modules/public/Timing.hpp index 52d91c7..505738b 100644 --- a/Modules/public/Timing.hpp +++ b/Modules/public/Timing.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include "Environment.hpp" namespace tp { @@ -56,4 +57,19 @@ namespace tp { } } }; + + struct TimerWrapper { + explicit TimerWrapper(const std::function& arg) { + codeSnippet = arg; + } + + time_ms exec() { + Timer timer; + codeSnippet(); + return timer.timePassed(); + } + + private: + std::function codeSnippet; + }; } \ No newline at end of file diff --git a/TODO b/TODO index e14c5f7..f13e92e 100644 --- a/TODO +++ b/TODO @@ -1,13 +1,18 @@ Widgets: - Replace Old Widgets!! + Basic scrollable widget + Collapsing menus + + Icons + Toolbar widget + Fix focus locking mechanism + Hover PopUps + Color Picker Optimize: Make visual parameters static Make get-areas fetch cache and not animation objects New Widgets: - Collapsing menu - Sliders Check Boxes Drop-Downs Toaster notifications diff --git a/Widgets/CMakeLists.txt b/Widgets/CMakeLists.txt index 126d59f..0589f17 100644 --- a/Widgets/CMakeLists.txt +++ b/Widgets/CMakeLists.txt @@ -10,8 +10,8 @@ target_link_libraries(${PROJECT_NAME} PUBLIC Math Graphics Imgui) ### -------------------------- Applications -------------------------- ### -add_executable(Widgets2Example examples/Example.cpp) -target_link_libraries(Widgets2Example ${PROJECT_NAME} ${GLEW_LIB}) -target_include_directories(Widgets2Example PUBLIC ../Externals/glfw/include ${GLEW_INCLUDE_DIR}) +add_executable(WidgetsExample examples/Example.cpp) +target_link_libraries(WidgetsExample ${PROJECT_NAME} ${GLEW_LIB}) +target_include_directories(WidgetsExample PUBLIC ../Externals/glfw/include ${GLEW_INCLUDE_DIR}) file(COPY "examples/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/") \ No newline at end of file diff --git a/Widgets/examples/Example.cpp b/Widgets/examples/Example.cpp index 398298f..45b5331 100644 --- a/Widgets/examples/Example.cpp +++ b/Widgets/examples/Example.cpp @@ -50,23 +50,18 @@ public: } void exampleScrolling() { - static Widget widget; - static Widget content; + static ScrollableWidget widget; static ButtonWidget buttons[10]; - static ScrollableBarWidget scrollBar; + + // widget.setDirection(false); setRoot(&widget); - widget.addChild(&scrollBar); - widget.addChild(&content); - for (auto& button : buttons) { - content.addChild(&button); + widget.getContainer()->addChild(&button); } - widget.setLayout(new ScrollableLayout(&widget)); - - RootWidget::setWidgetArea(content, { 333, 333, 522, 522 }); + RootWidget::setWidgetArea(*widget.getContainer(), { 333, 333, 522, 522 }); } void examplePopup() { diff --git a/Widgets/private/RootWidget.cpp b/Widgets/private/RootWidget.cpp index 1203b58..2de6ea2 100644 --- a/Widgets/private/RootWidget.cpp +++ b/Widgets/private/RootWidget.cpp @@ -8,6 +8,8 @@ using namespace tp; +#define SAMPLE(label, scope) gDebugWidget.##label.addSample(TimerWrapper([&]() scope ).exec()); + WidgetLayout* WidgetManagerInterface::defaultLayout(Widget* widget) { return new BasicLayout(widget); } @@ -35,21 +37,23 @@ void RootWidget::setRootWidget(Widget* widget) { void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) { mRoot.setArea(screenArea); - if (!gDebugWidget.update(this, *events)) return; + gDebugWidget.mUpdManager.addSample(TimerWrapper([&]() { + // construct hierarchy tree of widgets to process + mUpdateManager.updateTreeToProcess(&mRoot); - // construct hierarchy tree of widgets to process - mUpdateManager.updateTreeToProcess(&mRoot); + updateAnimations(); + updateAreaCache(&mRoot, true); - updateAnimations(); - updateAreaCache(&mRoot, true); + // update all events and call all event processing callbacks + mUpdateManager.processWidgets(&mRoot, *events); - // update all events and call all event processing callbacks - mUpdateManager.processWidgets(&mRoot, *events); + updateAreaCache(&mRoot, true); + }).exec()); - updateAreaCache(&mRoot, true); - - // update widget sizes base on individual size policies - mLayoutManager.adjust(&mRoot); + gDebugWidget.mLayManager.addSample(TimerWrapper([&]() { + // update widget sizes base on individual size policies + mLayoutManager.adjust(&mRoot); + }).exec()); updateAreaCache(&mRoot, false); @@ -67,13 +71,9 @@ bool RootWidget::needsUpdate() const { void RootWidget::drawFrame(Canvas& canvas) { canvas.rect(mRoot.getAreaT(), RGBA(0, 0, 0, 1)); - drawRecursion(canvas, &mRoot, { 0, 0 }); - - gDebugWidget.drawDebug(this, canvas); } - void RootWidget::drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos) { if (!active->mFlags.get(ENABLED)) return; @@ -81,7 +81,9 @@ void RootWidget::drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos) canvas.pushClamp({ pos, active->getArea().size }); if (canvas.getClampedArea().size.length2() > EPSILON) { - active->draw(canvas); + if (active->getArea().sizeVecW() > active->getArea().pos) { + active->draw(canvas); + } for (auto child = active->mDepthOrder.lastNode(); child; child = child->prev) { drawRecursion(canvas, child->data, pos + child->data->getArea().pos); @@ -152,4 +154,3 @@ void RootWidget::closePopup(Widget* widget) { void RootWidget::lockFocus(Widget* widget) { mUpdateManager.lockFocus(widget); } void RootWidget::freeFocus(Widget* widget) { mUpdateManager.freeFocus(widget); } -bool RootWidget::isDebug() const { return gDebugWidget.isDebug(); } diff --git a/Widgets/private/WidgetApplication.cpp b/Widgets/private/WidgetApplication.cpp index 9f0e99d..70e5aa0 100644 --- a/Widgets/private/WidgetApplication.cpp +++ b/Widgets/private/WidgetApplication.cpp @@ -5,6 +5,58 @@ using namespace tp; void WidgetApplication::debugUI() { - ImGui::Text("Proc ms : %i", (int) mProcTime); - ImGui::SameLine(); ImGui::Text("Draw ms : %i", (int) mDrawTime); + // pass } + +void WidgetApplication::setRoot(Widget* widget) { + mRootWidget.setRootWidget(widget); + + tp::RootWidget::setWidgetArea(*widget, {{}, mWindow->getSize()}); +} + +void WidgetApplication::processFrame(EventHandler* eventHandler, halnf deltaTime) { + const auto area = RectF({ 0, 0 }, mWindow->getSize()); + + gDebugWidget.update(&mRootWidget, *eventHandler); + + if (gDebugWidget.isDebug()) { + mGuiArea = area.splitByFactorHL(mDebugSplitFactor); + mDebugArea = area.splitByFactorHR(mDebugSplitFactor); + } else { + mGuiArea = area; + } + + if (gDebugWidget.isFrozen()) return; + + Timer timer; + mRootWidget.processFrame(eventHandler, mGuiArea); + gDebugWidget.mProcTime.addSample(timer.timePassed()); +} + +void WidgetApplication::drawFrame(Canvas* canvas) { + Timer timer; + mRootWidget.drawFrame(*canvas); + gDebugWidget.mDrawTime.addSample(timer.timePassed()); + + if (gDebugWidget.isDebug()) { + ImGui::SetNextWindowPos(ImVec2(mDebugArea.x, mDebugArea.y)); + ImGui::SetNextWindowSize(ImVec2(mDebugArea.z, mDebugArea.w)); + + if (ImGui::Begin( + "Existing Window Name", + nullptr, + ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoDecoration + )) { + + drawDebug(); + debugUI(); + + gDebugWidget.drawDebug(&mRootWidget, *canvas); + } + ImGui::End(); + } +} + +bool WidgetApplication::forceNewFrame() { + return mRootWidget.needsUpdate(); +} \ No newline at end of file diff --git a/Widgets/private/layouts/DockLayout.cpp b/Widgets/private/layouts/DockLayout.cpp index e4c60d7..62f3023 100644 --- a/Widgets/private/layouts/DockLayout.cpp +++ b/Widgets/private/layouts/DockLayout.cpp @@ -61,6 +61,8 @@ void DockLayout::toggleWidgetVisibility(Side side) { void DockLayout::calculateSideAreas() { auto startArea = getArea().relative(); + if (startArea.size.x <= 0 || startArea.size.y <= 0) return; + for (auto& sideWidget : mSideWidgets) { const auto side = sideWidget.order; @@ -99,11 +101,13 @@ void DockLayout::calculateResizeHandles() { RectF area = getArea().relative(); for (auto& sideWidget : mSideWidgets) { + if (!sideWidget.widget) continue; + auto& sideSize = sideWidget.absoluteSize; auto& resizeHandle = sideWidget.resizeHandle; if (resizeHandle.end < mSideSizePadding * 2) { - sideSize = resizeHandle.end / 2.f; + // sideSize = resizeHandle.end / 2.f; } else { sideSize = clamp(sideSize, resizeHandle.start + mSideSizePadding, resizeHandle.end - mSideSizePadding); } diff --git a/Widgets/private/layouts/ScrollableLayout.cpp b/Widgets/private/layouts/ScrollableLayout.cpp index 43f9c39..dd2a1cf 100644 --- a/Widgets/private/layouts/ScrollableLayout.cpp +++ b/Widgets/private/layouts/ScrollableLayout.cpp @@ -30,9 +30,9 @@ void ScrollableLayout::updateWidgetRects(const RectF& area, Widget* content, Scr auto sizeFactor = (area.size[dir] / content->getLayout()->getArea().size[dir]); auto splitFactor = 1 - ((sizeFactor > 1.f) ? 0 : mScrollerSize / area.size[!dir]); - auto holderArea = area.splitByFactorHL(splitFactor); + auto holderArea = dir ? area.splitByFactorHL(splitFactor) : area.splitByFactorVT(splitFactor); auto contentArea = content->getLayout()->getArea(); - auto scrollerArea = area.splitByFactorHR(splitFactor); + auto scrollerArea = dir ? area.splitByFactorHR(splitFactor) : area.splitByFactorVB(splitFactor); scroller->updateSizeFactor(sizeFactor); diff --git a/Widgets/private/managers/DebugManager.cpp b/Widgets/private/managers/DebugManager.cpp index 0e390fe..921f7e1 100644 --- a/Widgets/private/managers/DebugManager.cpp +++ b/Widgets/private/managers/DebugManager.cpp @@ -3,28 +3,26 @@ #include "BasicLayout.hpp" #include "imgui.h" +#include "implot.h" + #include using namespace tp; DebugManager tp::gDebugWidget; -#define LIST_SIZE { -FLT_MIN, 150 } +#define LIST_SIZE \ + { -FLT_MIN, 150 } -bool DebugManager::update(RootWidget* rootWidget, EventHandler& events) { +void DebugManager::update(RootWidget* rootWidget, EventHandler& events) { mRootWidget = rootWidget; events.setEnableKeyEvents(true); if (events.isPressed(InputID::D)) mDebug = !mDebug; if (mDebug) { - auto area = rootWidget->mRoot.getAreaT(); - area.size -= { 400, 0 }; - rootWidget->mRoot.setArea(area); - events.setEnableKeyEvents(true); if (events.isPressed(InputID::K)) mDebugStopProcessing = !mDebugStopProcessing; - if (mDebugStopProcessing) return false; if (auto widget = rootWidget->mUpdateManager.mInFocusWidget) { if (auto lay = dynamic_cast(widget->getLayout())) { @@ -49,8 +47,6 @@ bool DebugManager::update(RootWidget* rootWidget, EventHandler& events) { if (events.isPressed(InputID::L)) mLayBreakpoints.insert(breakWidget); } } - - return true; } void DebugManager::drawDebug(RootWidget* rootWidget, Canvas& canvas) { @@ -60,20 +56,24 @@ void DebugManager::drawDebug(RootWidget* rootWidget, Canvas& canvas) { return; } + drawPerformance(); + // ImGui::Checkbox("Draw debug", &mDebug); - ImGui::SameLine(); ImGui::Text("To Toggle processing press k"); + ImGui::SameLine(); + ImGui::Text("To Toggle processing press k"); auto& upd = rootWidget->mUpdateManager; - recursiveDraw(canvas, &rootWidget->mRoot, { 0, 0 }, 0); - ImGui::Text("Triggered: %i", (int) upd.mTriggeredWidgets.size()); - ImGui::SameLine(); ImGui::Text("Processing: %i", upd.mDebugWidgetsToProcess); + ImGui::SameLine(); + ImGui::Text("Processing: %i", upd.mDebugWidgetsToProcess); ImGui::Checkbox("Stop processing", &mDebugStopProcessing); - ImGui::SameLine(); ImGui::Checkbox("Force new frames", &mDebugRedrawAlways); - ImGui::SameLine(); ImGui::Checkbox("Slow-mo", &mSlowMotion); + ImGui::SameLine(); + ImGui::Checkbox("Force new frames", &mDebugRedrawAlways); + ImGui::SameLine(); + ImGui::Checkbox("Detailed", &mDetailed); if (upd.mInFocusWidget) { ImGui::Text("Under cursor"); @@ -87,17 +87,21 @@ void DebugManager::drawDebug(RootWidget* rootWidget, Canvas& canvas) { } } - ImGui::Text("Triggered"); - { - if (ImGui::BeginListBox("##triggered", LIST_SIZE)) { - for (auto widget : upd.mTriggeredWidgets) { - widgetMenu(widget.first); + if (mDetailed) { + ImGui::Text("Triggered"); + { + if (ImGui::BeginListBox("##triggered", LIST_SIZE)) { + for (auto widget : upd.mTriggeredWidgets) { + widgetMenu(widget.first); + } + ImGui::EndListBox(); } - ImGui::EndListBox(); } - } - drawLayoutOrder(); + drawLayoutOrder(); + + recursiveDraw(canvas, &rootWidget->mRoot, { 0, 0 }, 0); + } } void DebugManager::recursiveDraw(Canvas& canvas, Widget* active, const Vec2F& pos, int depthOrder) { @@ -174,4 +178,14 @@ void DebugManager::drawLayoutOrder() { } ImGui::EndListBox(); } +} + +void DebugManager::drawPerformance() { + if (ImPlot::BeginPlot("Per Frame Time")) { + ImPlot::PlotLine("Proc ms", mProcTime.get(), mProcTime.size()); + ImPlot::PlotLine("Draw ms", mDrawTime.get(), mDrawTime.size()); + ImPlot::PlotLine("UPD ms", mUpdManager.get(), mUpdManager.size()); + ImPlot::PlotLine("LAY ms", mLayManager.get(), mLayManager.size()); + ImPlot::EndPlot(); + } } \ No newline at end of file diff --git a/Widgets/private/managers/LayoutManager.cpp b/Widgets/private/managers/LayoutManager.cpp index 749b7e6..acd8f8f 100644 --- a/Widgets/private/managers/LayoutManager.cpp +++ b/Widgets/private/managers/LayoutManager.cpp @@ -33,7 +33,11 @@ void LayoutManager::findDependencies(Widget* root) { Widget::dfs(root, [this](Widget* widget) { mDepGraph.insert({ widget, {} }); }); for (auto& [widget, deps] : mDepGraph) { + if (!widget->isUpdate()) continue; + for (auto child : widget->mChildren) { + if (!child->isUpdate()) continue; + mDepGraph.insert({ child, {} }); if (auto depOrder = getLayoutOrder(widget->getLayout(), child->getLayout())) { diff --git a/Widgets/private/widgets/DockWidget.cpp b/Widgets/private/widgets/DockWidget.cpp index 565d087..a4a9bc3 100644 --- a/Widgets/private/widgets/DockWidget.cpp +++ b/Widgets/private/widgets/DockWidget.cpp @@ -80,9 +80,11 @@ void DockWidget::process(const EventHandler& events) { if (events.isPressed(InputID::MOUSE1)) { if (layout()->startResize(events.getPointer())) { triggerWidgetUpdate("dock layout resizing"); + lockFocus(); } } else if (events.isReleased(InputID::MOUSE1)) { layout()->endResize(); + freeFocus(); } if (layout()->isResizing()) { diff --git a/Widgets/private/widgets/ScrollableWidget.cpp b/Widgets/private/widgets/ScrollableWidget.cpp index 6497947..e67f0fa 100644 --- a/Widgets/private/widgets/ScrollableWidget.cpp +++ b/Widgets/private/widgets/ScrollableWidget.cpp @@ -1,4 +1,6 @@ #include "ScrollableWidget.hpp" +#include "ScrollableLayout.hpp" +#include "BasicLayout.hpp" using namespace tp; @@ -84,4 +86,21 @@ void ScrollableBarWidget::updateHandleRect() { const RectF& ScrollableBarWidget::getHandleRect() const { return mHandleRect; -} \ No newline at end of file +} + +ScrollableWidget::ScrollableWidget() { + addChild(&mScroller); + addChild(&mContent); + + setLayout(new ScrollableLayout(this)); + + mContent.setSizePolicy(SizePolicy::Minimal, SizePolicy::Minimal); +} + +void ScrollableWidget::setDirection(bool direction) { + if (auto lay = dynamic_cast(mContent.getLayout())) { + lay->setLayoutPolicy(direction ? LayoutPolicy::Vertical : LayoutPolicy::Horizontal); + } + + mScroller.setDirection(direction); +} diff --git a/Widgets/public/Layout.hpp b/Widgets/public/Layout.hpp index 2b437fe..b878d32 100644 --- a/Widgets/public/Layout.hpp +++ b/Widgets/public/Layout.hpp @@ -61,7 +61,7 @@ namespace tp { protected: Vec2 mSizePolicy = { SizePolicy::Fixed, SizePolicy::Fixed }; - Vec2F mMinSize = { 50, 50 }; + Vec2F mMinSize = { 30, 30 }; Vec2F mMaxSize = { FLT_MAX / 2, FLT_MAX / 2 }; private: diff --git a/Widgets/public/RootWidget.hpp b/Widgets/public/RootWidget.hpp index 75f5dbd..d66b1b0 100644 --- a/Widgets/public/RootWidget.hpp +++ b/Widgets/public/RootWidget.hpp @@ -18,8 +18,6 @@ namespace tp { void setRootWidget(Widget* widget); static void setWidgetArea(Widget& widget, const RectF& rect); - [[nodiscard]] bool isDebug() const; - // Graphic Application Interface public: void processFrame(EventHandler* events, const RectF& screenArea); diff --git a/Widgets/public/WidgetApplication.hpp b/Widgets/public/WidgetApplication.hpp index 3548c6e..8f6c697 100644 --- a/Widgets/public/WidgetApplication.hpp +++ b/Widgets/public/WidgetApplication.hpp @@ -9,41 +9,24 @@ namespace tp { public: WidgetApplication() = default; - void setRoot(Widget* widget) { - mRootWidget.setRootWidget(widget); - } + void setRoot(Widget* widget); private: - void processFrame(EventHandler* eventHandler, halnf deltaTime) override { - const auto rec = RectF({ 0, 0 }, mWindow->getSize()); + void processFrame(EventHandler* eventHandler, halnf deltaTime) override; - Timer timer; - mRootWidget.processFrame(eventHandler, rec); - mProcTime = timer.timePassed(); - } - - void drawFrame(Canvas* canvas) override { - Timer timer; - mRootWidget.drawFrame(*canvas); - mDrawTime = timer.timePassed(); - - if (mRootWidget.isDebug()) { - drawDebug(); - debugUI(); - } - } - - bool forceNewFrame() override { - return mRootWidget.needsUpdate(); - } + void drawFrame(Canvas* canvas) override; + bool forceNewFrame() override ; private: void debugUI(); private: - time_ms mProcTime = 0; - time_ms mDrawTime = 0; + halnf mDebugSplitFactor = 0.7; RootWidget mRootWidget; + + private: + RectF mGuiArea; + RectF mDebugArea; }; } \ No newline at end of file diff --git a/Widgets/public/layouts/DockLayout.hpp b/Widgets/public/layouts/DockLayout.hpp index e5b49ab..47de346 100644 --- a/Widgets/public/layouts/DockLayout.hpp +++ b/Widgets/public/layouts/DockLayout.hpp @@ -96,7 +96,7 @@ namespace tp { bool mResizing = false; Widget* mCenterWidget = nullptr; - RectF mCenterArea {}; + RectF mCenterArea { 0, 0, 10, 10 }; // Parameters const halnf mHandleSplitFactor = 0.3; diff --git a/Widgets/public/mangers/DebugManager.hpp b/Widgets/public/mangers/DebugManager.hpp index 938fd6f..0b4ce92 100644 --- a/Widgets/public/mangers/DebugManager.hpp +++ b/Widgets/public/mangers/DebugManager.hpp @@ -5,12 +5,56 @@ #include namespace tp { + class DebugTimeline { + enum { + STEP = 10, + LEN = 200, + }; + + public: + DebugTimeline() = default; + + void addSample(time_ms time) { + samples[end] = time; + start++; + end++; + shift(); + } + + [[nodiscard]] int size() const { + return LEN - STEP; + } + + [[nodiscard]] const time_ms* get() const { + return samples + start; + } + + private: + void shift() { + if (end >= LEN) { + for (ualni i = STEP; i < LEN; i++) { + samples[i - STEP] = samples[i]; + } + start = 0; + end = LEN - STEP; + } + } + + private: + ualni start = 0; + ualni end = LEN - STEP; + + time_ms samples[LEN + 1] {}; + }; + class DebugManager { public: DebugManager() = default; - bool isRedrawAlways() const { return mDebugRedrawAlways; } - bool update(RootWidget* rootWidget, EventHandler& events); + [[nodiscard]] bool isFrozen() const { return mDebugStopProcessing; } + [[nodiscard]] bool isRedrawAlways() const { return mDebugRedrawAlways; } + + void update(RootWidget* rootWidget, EventHandler& events); void drawDebug(RootWidget* rootWidget, Canvas& canvas); void checkProcBreakPoints(Widget* widget) { @@ -32,6 +76,7 @@ namespace tp { private: void recursiveDraw(Canvas& canvas, Widget* active, const Vec2F& pos, int depthOrder); + void drawPerformance(); static void widgetMenu(Widget*); void drawLayoutOrder(); @@ -42,10 +87,17 @@ namespace tp { bool mDebug = false; bool mDebugStopProcessing = false; bool mDebugRedrawAlways = false; - bool mSlowMotion = false; + bool mDetailed = false; std::set mProcBreakpoints; std::set mLayBreakpoints; + + public: + DebugTimeline mProcTime; + DebugTimeline mUpdManager; + DebugTimeline mLayManager; + + DebugTimeline mDrawTime; }; extern DebugManager gDebugWidget; diff --git a/Widgets/public/widgets/ScrollableWidget.hpp b/Widgets/public/widgets/ScrollableWidget.hpp index 13026ca..1903df2 100644 --- a/Widgets/public/widgets/ScrollableWidget.hpp +++ b/Widgets/public/widgets/ScrollableWidget.hpp @@ -20,6 +20,8 @@ namespace tp { [[nodiscard]] bool getDirection() const { return mVertical; } [[nodiscard]] halnf getPosFactor() const { return mPosFactor - mSizeFactor / 2; } + void setDirection(bool direction) { mVertical = direction; } + private: [[nodiscard]] const RectF& getHandleRect() const; void updateHandleRect(); @@ -53,5 +55,13 @@ namespace tp { class ScrollableWidget : public Widget { public: ScrollableWidget(); + + void setDirection(bool direction); + + Widget* getContainer() { return &mContent; } + + private: + Widget mContent; + ScrollableBarWidget mScroller; }; } \ No newline at end of file diff --git a/Widgets/public/widgets/SimpleWidgets.hpp b/Widgets/public/widgets/SimpleWidgets.hpp index b67ba75..a55274e 100644 --- a/Widgets/public/widgets/SimpleWidgets.hpp +++ b/Widgets/public/widgets/SimpleWidgets.hpp @@ -71,7 +71,7 @@ namespace tp { void draw(Canvas& canvas) override; [[nodiscard]] bool processesEvents() const override { return true; } - [[nodiscard]] bool needsNextFrame() const override { return mState != SLIDING; } + [[nodiscard]] bool needsNextFrame() const override { return mState != IDLE; } [[nodiscard]] halnf val() const { return mFactor; } From 23fe07d3d3e09c98ef2e7dd76a6fc032ef203d48 Mon Sep 17 00:00:00 2001 From: IlyaShurupov Date: Tue, 5 Nov 2024 19:30:02 +0300 Subject: [PATCH 3/4] fix focus locking bug --- Graphics/public/EventHandler.hpp | 1 + Widgets/examples/Example.cpp | 2 +- Widgets/private/RootWidget.cpp | 4 ++- Widgets/private/managers/DebugManager.cpp | 7 +++-- Widgets/private/managers/UpdateManager.cpp | 27 ++++++++++---------- Widgets/private/widgets/ScrollableWidget.cpp | 3 ++- Widgets/public/Widget.hpp | 2 ++ Widgets/public/mangers/DebugManager.hpp | 2 +- Widgets/public/mangers/UpdateManager.hpp | 7 +++-- 9 files changed, 34 insertions(+), 21 deletions(-) diff --git a/Graphics/public/EventHandler.hpp b/Graphics/public/EventHandler.hpp index c62bd89..ea35c89 100644 --- a/Graphics/public/EventHandler.hpp +++ b/Graphics/public/EventHandler.hpp @@ -87,6 +87,7 @@ namespace tp { [[nodiscard]] halnf getPointerPressure() const; void setEnableKeyEvents(bool); + [[nodiscard]] bool isKeyEventsEnabled() const { return mEnableKeyEvents; } private: void processEventUnguarded(); diff --git a/Widgets/examples/Example.cpp b/Widgets/examples/Example.cpp index 45b5331..b29d341 100644 --- a/Widgets/examples/Example.cpp +++ b/Widgets/examples/Example.cpp @@ -13,7 +13,7 @@ using namespace tp; class Example : public WidgetApplication { public: Example() { - examplePopup(); + exampleNestedMenus(); } void exampleAll() { diff --git a/Widgets/private/RootWidget.cpp b/Widgets/private/RootWidget.cpp index 2de6ea2..78f2c17 100644 --- a/Widgets/private/RootWidget.cpp +++ b/Widgets/private/RootWidget.cpp @@ -58,7 +58,9 @@ void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) { updateAreaCache(&mRoot, false); // trigger some widgets by moise pointer - mUpdateManager.handleFocusChanges(&mRoot, *events); + auto prevFocus = mUpdateManager.getFocusWidget(); + auto newFocus = mUpdateManager.findFocusWidget(&mRoot, *events); + mUpdateManager.handleFocusChanges(newFocus, prevFocus); // check triggered widgets for removal mUpdateManager.clean(); diff --git a/Widgets/private/managers/DebugManager.cpp b/Widgets/private/managers/DebugManager.cpp index 921f7e1..bd0de0a 100644 --- a/Widgets/private/managers/DebugManager.cpp +++ b/Widgets/private/managers/DebugManager.cpp @@ -113,14 +113,17 @@ void DebugManager::recursiveDraw(Canvas& canvas, Widget* active, const Vec2F& po canvas.text((active->mDebug.id + ":" + std::to_string(depthOrder)).c_str(), area, 22, Canvas::Align::LC, 2, color); } + if (active == mRootWidget->mUpdateManager.mFocusLockWidget) { + canvas.frame(area, { 0, 1, 0, 1 }); + } + if (active->mFlags.get(Widget::IN_FOCUS)) { if (active->isUpdate()) { canvas.circle(pos + active->mDebug.pLocal, 5, active->mDebug.col); canvas.circle(active->mDebug.pGlobal, 15, active->mDebug.col); } - RGBA color = { 1, 0, 0, 0.3f }; - canvas.debugCross(area, color); + canvas.debugCross(area, { 0, 1, 0, 1 }); } int orderIdx = 0; diff --git a/Widgets/private/managers/UpdateManager.cpp b/Widgets/private/managers/UpdateManager.cpp index 57e8475..c02c8e7 100644 --- a/Widgets/private/managers/UpdateManager.cpp +++ b/Widgets/private/managers/UpdateManager.cpp @@ -69,14 +69,11 @@ void UpdateManager::getWidgetPath(Widget* widget, std::vector& out) { } } -void UpdateManager::handleFocusChanges(Widget* root, EventHandler& events) { - auto prevFocus = mInFocusWidget; - +Widget* UpdateManager::findFocusWidget(Widget* root, EventHandler& events) { events.setCursorOrigin({ 0, 0 }); mInFocusWidget = nullptr; - - findFocusWidget(root, &mInFocusWidget, events.getPointer()); + findMouseFocusWidget(root, &mInFocusWidget, events.getPointer()); if (mFocusLockWidget) { bool hasLockedWidget = false; @@ -91,13 +88,17 @@ void UpdateManager::handleFocusChanges(Widget* root, EventHandler& events) { } } - // if (mInFocusWidget == prevFocus) return; - if (mInFocusWidget) scheduleUpdate(mInFocusWidget, "focus entered"); + return mInFocusWidget; +} - if (!mInFocusWidget && !prevFocus) return; +void UpdateManager::handleFocusChanges(Widget* active, Widget* prevActive) { + // if (mInFocusWidget == prevFocus) return; + if (active) scheduleUpdate(active, "focus entered"); + + if (!active && !prevActive) return; std::vector path2; - getWidgetPath(mInFocusWidget, path2); + getWidgetPath(active, path2); size_t propLen2 = path2.size(); for (auto i = 0; i < path2.size(); i++) { if (!path2[i]->propagateEventsToChildren()) { @@ -107,7 +108,7 @@ void UpdateManager::handleFocusChanges(Widget* root, EventHandler& events) { } std::vector path1; - getWidgetPath(prevFocus, path1); + getWidgetPath(prevActive, path1); size_t propLen1 = path1.size(); for (auto i = 0; i < path1.size(); i++) { if (!path1[i]->propagateEventsToChildren()) { @@ -135,7 +136,7 @@ void UpdateManager::handleFocusChanges(Widget* root, EventHandler& events) { } } -void UpdateManager::findFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer) { +void UpdateManager::findMouseFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer) { if (!iter->mArea.getTargetRect().isInside(pointer) || !iter->mFlags.get(Widget::ENABLED)) return; if (iter->processesEvents()) { @@ -143,7 +144,7 @@ void UpdateManager::findFocusWidget(Widget* iter, Widget** focus, const Vec2F& p } for (auto child = iter->mDepthOrder.lastNode(); child; child = child->prev) { - findFocusWidget(child->data, focus, pointer - iter->mArea.getTargetRect().pos); + findMouseFocusWidget(child->data, focus, pointer - iter->mArea.getTargetRect().pos); } } @@ -215,6 +216,6 @@ void UpdateManager::lockFocus(tp::Widget* widget) { } void UpdateManager::freeFocus(tp::Widget* widget) { - // DEBUG_ASSERT(mFocusLockWidget == widget) + DEBUG_ASSERT(mFocusLockWidget == widget) mFocusLockWidget = nullptr; } \ No newline at end of file diff --git a/Widgets/private/widgets/ScrollableWidget.cpp b/Widgets/private/widgets/ScrollableWidget.cpp index e67f0fa..81836c1 100644 --- a/Widgets/private/widgets/ScrollableWidget.cpp +++ b/Widgets/private/widgets/ScrollableWidget.cpp @@ -7,9 +7,10 @@ using namespace tp; void ScrollableBarWidget::process(const EventHandler& events) { // all content is visible no need to process anything if (mSizeFactor >= 1) { + if (mScrolling) freeFocus(); + mScrolling = false; mPosFactor = mSizeFactor / 2.f; - freeFocus(); return; } diff --git a/Widgets/public/Widget.hpp b/Widgets/public/Widget.hpp index 6c3ca28..40f7207 100644 --- a/Widgets/public/Widget.hpp +++ b/Widgets/public/Widget.hpp @@ -31,6 +31,8 @@ namespace tp { using DFSAction = std::function; + protected: + enum Flags : int1 { ENABLED = 0, NEEDS_UPDATE, diff --git a/Widgets/public/mangers/DebugManager.hpp b/Widgets/public/mangers/DebugManager.hpp index 0b4ce92..6c84ef8 100644 --- a/Widgets/public/mangers/DebugManager.hpp +++ b/Widgets/public/mangers/DebugManager.hpp @@ -87,7 +87,7 @@ namespace tp { bool mDebug = false; bool mDebugStopProcessing = false; bool mDebugRedrawAlways = false; - bool mDetailed = false; + bool mDetailed = true; std::set mProcBreakpoints; std::set mLayBreakpoints; diff --git a/Widgets/public/mangers/UpdateManager.hpp b/Widgets/public/mangers/UpdateManager.hpp index 33c5ecf..f97ba62 100644 --- a/Widgets/public/mangers/UpdateManager.hpp +++ b/Widgets/public/mangers/UpdateManager.hpp @@ -25,13 +25,16 @@ namespace tp { void clean(); void updateTreeToProcess(Widget* root); - void handleFocusChanges(Widget* root, EventHandler& events); + void handleFocusChanges(Widget* active, Widget* prevActive); + Widget* findFocusWidget(Widget* root, EventHandler& events); - void findFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer); + void findMouseFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer); static void getWidgetPath(Widget* widget, std::vector& out); void processActiveTree(Widget* iter, EventHandler& events, Vec2F pos); void processFocusItems(EventHandler& events); + Widget* getFocusWidget() { return mInFocusWidget; } + private: static void procWidget(Widget* widget, EventHandler& events, bool withEvents = false); From c57041a48ea9920ebe7a979303c7a999f8756a65 Mon Sep 17 00:00:00 2001 From: IlyaShurupov Date: Sun, 20 Oct 2024 12:07:16 +0300 Subject: [PATCH 4/4] Refactor of layout manager to add support for all menus (dirty unstable) --- 3DEditor/public/EditorWidget.hpp | 16 +-- Sketch3D/public/Sketch3DWidget.hpp | 8 +- Widgets/examples/Example.cpp | 40 +++++-- Widgets/private/Layout.cpp | 20 +++- Widgets/private/Widget.cpp | 3 +- Widgets/private/layouts/BasicLayout.cpp | 115 +++++++++---------- Widgets/private/layouts/DockLayout.cpp | 2 +- Widgets/private/layouts/OverlayLayout.cpp | 2 +- Widgets/private/layouts/ScrollableLayout.cpp | 4 +- Widgets/private/managers/LayoutManager.cpp | 8 +- Widgets/private/widgets/FloatingWidget.cpp | 27 ----- Widgets/private/widgets/MenuWidgets.cpp | 58 ++++++++++ Widgets/private/widgets/SimpleWidgets.cpp | 2 +- Widgets/public/Layout.hpp | 20 +++- Widgets/public/Widget.hpp | 1 + Widgets/public/layouts/BasicLayout.hpp | 12 +- Widgets/public/layouts/DockLayout.hpp | 3 +- Widgets/public/layouts/OverlayLayout.hpp | 3 +- Widgets/public/layouts/ScrollableLayout.hpp | 4 +- Widgets/public/widgets/FloatingWidget.hpp | 33 ------ Widgets/public/widgets/MenuWidgets.hpp | 60 ++++++++++ 21 files changed, 274 insertions(+), 167 deletions(-) create mode 100644 Widgets/private/widgets/MenuWidgets.cpp create mode 100644 Widgets/public/widgets/MenuWidgets.hpp diff --git a/3DEditor/public/EditorWidget.hpp b/3DEditor/public/EditorWidget.hpp index 0914937..2d1f63c 100644 --- a/3DEditor/public/EditorWidget.hpp +++ b/3DEditor/public/EditorWidget.hpp @@ -2,7 +2,7 @@ #include "DockWidget.hpp" #include "Editor.hpp" -#include "FloatingWidget.hpp" +#include "MenuWidgets.hpp" namespace tp { @@ -40,11 +40,11 @@ namespace tp { mViewport(canvas, editor) { mEditor = editor; - mPanel.setText("Controls"); - mPanel.addToMenu(&mNavigationMenu); - mPanel.addToMenu(&mRenderMenu); + // mPanel.setText("Controls"); + //mPanel.addToMenu(&mNavigationMenu); + //mPanel.addToMenu(&mRenderMenu); - dockWidget(&mPanel, DockLayout::RIGHT); + dockWidget(&mNavigationMenu, DockLayout::RIGHT); setCenterWidget(&mViewport); // Render @@ -120,10 +120,10 @@ namespace tp { ViewportWidget mViewport; - FloatingMenu mPanel; + FloatingScrollableMenu mPanel; // Controls - FloatingMenu mRenderMenu; + FloatingScrollableMenu mRenderMenu; ButtonWidget mRenderPathTracer; ButtonWidget mRenderRaster; ButtonWidget mRenderDeNoise; @@ -131,7 +131,7 @@ namespace tp { // Navigation enum NavigationType { ORBIT, PAN, ZOOM } mNavigationType = ORBIT; - FloatingMenu mNavigationMenu; + FloatingScrollableMenu mNavigationMenu; ButtonWidget mNavigationPan; ButtonWidget mNavigationOrbit; ButtonWidget mNavigationZoom; diff --git a/Sketch3D/public/Sketch3DWidget.hpp b/Sketch3D/public/Sketch3DWidget.hpp index 9859525..d59213b 100644 --- a/Sketch3D/public/Sketch3DWidget.hpp +++ b/Sketch3D/public/Sketch3DWidget.hpp @@ -3,7 +3,7 @@ #include "Sketch3D.hpp" #include "Widget.hpp" #include "DockWidget.hpp" -#include "FloatingWidget.hpp" +#include "MenuWidgets.hpp" namespace tp { @@ -150,15 +150,15 @@ namespace tp { private: Sketch3DWidget mViewport; - FloatingMenu mPanel; + FloatingScrollableMenu mPanel; - FloatingMenu mControls; + FloatingScrollableMenu mControls; ButtonWidget mDrawButton; ButtonWidget mMoveButton; ButtonWidget mRotateButton; ButtonWidget mZoomButton; - FloatingMenu mColorPicker; + FloatingScrollableMenu mColorPicker; SliderWidget mRed; SliderWidget mGreen; SliderWidget mBlue; diff --git a/Widgets/examples/Example.cpp b/Widgets/examples/Example.cpp index b29d341..0682651 100644 --- a/Widgets/examples/Example.cpp +++ b/Widgets/examples/Example.cpp @@ -2,7 +2,7 @@ #include "WidgetApplication.hpp" #include "RootWidget.hpp" -#include "FloatingWidget.hpp" +#include "MenuWidgets.hpp" #include "DockWidget.hpp" #include "ScrollableLayout.hpp" @@ -19,9 +19,9 @@ public: void exampleAll() { static DockWidget dock; static LabelWidget centralWidget; - static FloatingMenu menus[4]; - static FloatingMenu nestedMenus[4]; - static FloatingMenu buttons[10]; + static FloatingScrollableMenu menus[4]; + static FloatingScrollableMenu nestedMenus[4]; + static FloatingScrollableMenu buttons[10]; dock.setCenterWidget(¢ralWidget); @@ -36,6 +36,30 @@ public: setRoot(&dock); } + void exampleCollapsingMenus() { + static DockWidget dock; + static CollapsingMenuWidget mainMenu; + static CollapsingMenuWidget menus[3]; + static ButtonWidget buttons[5]; + + for (auto& menu : menus) { + mainMenu.getContainer()->addChild(&menu); + } + + menus[0].getContainer()->addChild(&buttons[0]); + menus[0].getContainer()->addChild(&buttons[1]); + menus[0].getContainer()->addChild(&buttons[2]); + + menus[1].getContainer()->addChild(&buttons[3]); + menus[1].getContainer()->addChild(&buttons[4]); + + // dock.addChild(&menus[0]); + + setRoot(&menus[0]); + + // RootWidget::setWidgetArea(mainMenu, { 333, 333, 522, 522 }); + } + void exampleBasic() { static Widget widget; static LabelWidget label; @@ -100,8 +124,8 @@ public: void exampleNestedMenus() { static DockWidget dock; - static FloatingMenu menu1; - static FloatingMenu menu2; + static FloatingScrollableMenu menu1; + static FloatingScrollableMenu menu2; static ButtonWidget buttons[15]; setRoot(&dock); @@ -173,8 +197,8 @@ public: void exampleDock() { static DockWidget dock; - static FloatingMenu menu1; - static FloatingMenu menu2; + static FloatingScrollableMenu menu1; + static FloatingScrollableMenu menu2; static ButtonWidget buttons[15]; setRoot(&dock); diff --git a/Widgets/private/Layout.cpp b/Widgets/private/Layout.cpp index bd56f95..2eab62e 100644 --- a/Widgets/private/Layout.cpp +++ b/Widgets/private/Layout.cpp @@ -29,7 +29,11 @@ const Vec2& WidgetLayout::getSizePolicy() const { return mSizePolicy void WidgetLayout::setSizePolicy(SizePolicy x, SizePolicy y) { mSizePolicy = { x, y }; } +WidgetLayout* WidgetLayout::lay(Widget* w) { return w->getLayout(); } + RangeF WidgetLayout::pickRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical) const { + return current; + RangeF out; switch (mSizePolicy[vertical]) { @@ -44,10 +48,16 @@ RangeF WidgetLayout::pickRange(const RangeF& current, const RangeF& children, co void WidgetLayout::clampMinMaxSize() { auto current = getArea(); - current.size.clamp(mMinSize, mMaxSize); + auto minContent = minContentArea(); + minContent.size.clamp(mMinSize, mMaxSize); + current.clamp(minContent); setArea(current); } +void WidgetLayout::pickMinimalRect(bool dir) { + setArea(minContentArea()); +} + RangeF WidgetLayout::clampRange(const RangeF& current, const RangeF& child, const RangeF& parent, bool vertical) const { auto out = current; @@ -84,13 +94,17 @@ RectF WidgetLayout::getChildrenEnclosure() const { return out; } -RectF WidgetLayout::getAvailableChildArea() const { +RectF WidgetLayout::availableChildArea() const { return getArea().relative(); } +RectF WidgetLayout::minContentArea() const { + return getChildrenEnclosure(); +} + RectF WidgetLayout::getParentEnclosure() const { DEBUG_ASSERT(parent()) if (!parent()) return { { 0, 0 }, mMaxSize }; - auto out = parent()->getLayout()->getAvailableChildArea(); + auto out = parent()->getLayout()->availableChildArea(); return out; } \ No newline at end of file diff --git a/Widgets/private/Widget.cpp b/Widgets/private/Widget.cpp index 5b7dc2c..3db3244 100644 --- a/Widgets/private/Widget.cpp +++ b/Widgets/private/Widget.cpp @@ -5,7 +5,8 @@ using namespace tp; Widget::Widget() { - mArea.setTargetRect({ 100, 100, 10, 10 }); + mArea.setTargetRect({ 100, 100, 50, 50 }); + setLayout(WidgetManagerInterface::defaultLayout(this)); mArea.endAnimation(); diff --git a/Widgets/private/layouts/BasicLayout.cpp b/Widgets/private/layouts/BasicLayout.cpp index c02b14d..eedf61a 100644 --- a/Widgets/private/layouts/BasicLayout.cpp +++ b/Widgets/private/layouts/BasicLayout.cpp @@ -26,20 +26,7 @@ void BasicLayout::pickRect(bool vertical) { setArea(newArea); } -void BasicLayout::clampRect() { - DEBUG_ASSERT(0) - - auto current = getArea(); - auto children = getChildrenEnclosure(); - auto parent = getParentEnclosure(); - - auto rangeX = clampRange(current.getRangeX(), children.getRangeX(), parent.getRangeX(), false); - auto rangeY = clampRange(current.getRangeY(), children.getRangeY(), parent.getRangeY(), true); - - setArea(RectF(rangeX, rangeY)); -} - -void BasicLayout::updateLayout(bool vertical) { +void BasicLayout::arrangeChildren(bool vertical) { if (children().empty()) return; if (vertical) { @@ -57,61 +44,64 @@ void BasicLayout::updateLayout(bool vertical) { } } +halnf BasicLayout::shrinkLayoutSize(WidgetLayout* widget, halnf diff, bool vertical) { + auto area = widget->getArea(); + auto prevSize = area.size[vertical]; + area.size[vertical] += diff; + widget->setArea(area); + //widget->clampMinMaxSize(); + return widget->getArea().size[vertical] - prevSize; +} -halnf BasicLayout::changeChildSize(tp::Widget* widget, halnf diff, bool vertical) { - auto prevSize = widget->getLayout()->getArea().size[vertical]; - { - auto area = widget->getLayout()->getArea(); - area.size[vertical] += diff; - widget->setAreaCache(area); - - widget->getLayout()->clampMinMaxSize(); - } - auto newSize = widget->getLayout()->getArea().size[vertical]; - return newSize - prevSize; +bool BasicLayout::canChange(Widget* widget, bool vertical) { + return widget->getLayout()->getSizePolicy()[vertical] == SizePolicy::Expanding; } void BasicLayout::adjustLayout(bool vertical) { - std::vector> contributors; - Vec2F contentSize = { 0, 0 }; - Vec2F availableSize = getArea().size; - for (auto child : children()) { - child->getLayout()->pickRect(vertical); + std::vector> contributors; + halnf contentSize = 0; - if (child->getLayout()->getSizePolicy()[vertical] == SizePolicy::Expanding) { - contributors.emplace_back( child, true ); - - auto area = child->getLayout()->getArea(); - area.size[vertical] = 0; - child->setAreaCache(area); - child->getLayout()->clampMinMaxSize(); + // find contributors + for (Widget* child : children()) { + if (canChange(child, vertical)) { + contributors.emplace_back( lay(child), true ); } - - contentSize += child->getLayout()->getArea().size; } - availableSize -= mLayoutGap * ((halnf) children().size() - 1) + mLayoutMargin * 2; + // let each child to chose desired rect + for (Widget* child : children()) { + if (canChange(child, vertical)) { + lay(child)->pickMinimalRect(vertical); + } else { + lay(child)->pickRect(vertical); + } + } - auto diff = availableSize - contentSize; + // calculate content size + for (Widget* child : children()) { + contentSize += lay(child)->getArea().size[vertical]; + } + + halnf availableSize = getArea().size[vertical] - (mLayoutGap * ((halnf) children().size() - 1) + mLayoutMargin * 2); + + if (availableSize < 0) return; + + halnf totalQuota = availableSize - contentSize; // expand or contract as much as possible - while (!contributors.empty() && (diff[vertical] != 0)) { - auto quota = diff / (halnf) contributors.size(); + while (!contributors.empty() && (totalQuota != 0)) { + halnf quotaPerContributor = totalQuota / (halnf) contributors.size(); for (auto& contributor : contributors) { - if (!contributor.second) continue; - - // contributor.first->endAnimations(); - auto contribution = changeChildSize(contributor.first, quota[vertical], vertical); - + halnf contribution = shrinkLayoutSize(contributor.first, quotaPerContributor, vertical); if (contribution == 0) { contributor.second = false; } - - diff[vertical] -= contribution; + totalQuota -= contribution; } + // remove useless contributors contributors.erase( std::remove_if(contributors.begin(), contributors.end(), [](auto node) { return !node.second; }), contributors.end() @@ -120,30 +110,31 @@ void BasicLayout::adjustLayout(bool vertical) { // set opposite direction size for (auto child : children()) { - // if (child->getLayout()->getSizePolicy()[!vertical] != SizePolicy::Minimal) { - auto area = child->getLayout()->getArea(); - area.size[!vertical] = getArea().size[!vertical] - mLayoutMargin * 2; - child->setAreaCache(area); - // } + RectF area = lay(child)->getArea(); + area.size[!vertical] = getArea().size[!vertical] - mLayoutMargin * 2; + child->setAreaCache(area); } // set pos halnf iterPos = mLayoutMargin; for (auto child : children()) { - auto area = child->getLayout()->getArea(); + RectF area = lay(child)->getArea(); area.pos[vertical] = iterPos; area.pos[!vertical] = mLayoutMargin; iterPos += area.size[vertical] + mLayoutGap; - child->setAreaCache(area); - - // child->updateAnimations(); - // child->triggerWidgetUpdate("layout changed"); + lay(child)->setArea(area); } } -RectF BasicLayout::getAvailableChildArea() const { - auto out = WidgetLayout::getAvailableChildArea(); +RectF BasicLayout::availableChildArea() const { + auto out = WidgetLayout::availableChildArea(); return out.shrinkFromCenter(mLayoutMargin, true); +} + +RectF BasicLayout::minContentArea() const { + auto out = WidgetLayout::getChildrenEnclosure(); + out.shrinkFromCenter(-mLayoutMargin, true); + return out; } \ No newline at end of file diff --git a/Widgets/private/layouts/DockLayout.cpp b/Widgets/private/layouts/DockLayout.cpp index 62f3023..20ab1c2 100644 --- a/Widgets/private/layouts/DockLayout.cpp +++ b/Widgets/private/layouts/DockLayout.cpp @@ -188,7 +188,7 @@ auto DockLayout::getSideFromWidget(Widget* widget) -> Side { return DockLayout::NONE; } -void DockLayout::updateLayout(bool vertical) { +void DockLayout::arrangeChildren(bool vertical) { for (auto child : children()) { child->getLayout()->pickRect(vertical); } diff --git a/Widgets/private/layouts/OverlayLayout.cpp b/Widgets/private/layouts/OverlayLayout.cpp index e30ce16..1630117 100644 --- a/Widgets/private/layouts/OverlayLayout.cpp +++ b/Widgets/private/layouts/OverlayLayout.cpp @@ -5,7 +5,7 @@ using namespace tp; -void OverlayLayout::updateLayout(bool vertical) { +void OverlayLayout::arrangeChildren(bool vertical) { if (vertical) return; if (children().empty()) return; diff --git a/Widgets/private/layouts/ScrollableLayout.cpp b/Widgets/private/layouts/ScrollableLayout.cpp index dd2a1cf..100977d 100644 --- a/Widgets/private/layouts/ScrollableLayout.cpp +++ b/Widgets/private/layouts/ScrollableLayout.cpp @@ -5,7 +5,7 @@ using namespace tp; -void ScrollableLayout::updateLayout(bool vertical) { +void ScrollableLayout::arrangeChildren(bool vertical) { if (vertical) return; // TODO : make better interface to get scroller and content widget @@ -44,7 +44,7 @@ void ScrollableLayout::updateWidgetRects(const RectF& area, Widget* content, Scr content->getLayout()->setArea(contentArea); } -RectF ScrollableLayout::getAvailableChildArea() const { +RectF ScrollableLayout::availableChildArea() const { auto out = getArea(); // dont constrain on scroll axis out.pos = -FLT_MAX / 4; diff --git a/Widgets/private/managers/LayoutManager.cpp b/Widgets/private/managers/LayoutManager.cpp index acd8f8f..87ff23a 100644 --- a/Widgets/private/managers/LayoutManager.cpp +++ b/Widgets/private/managers/LayoutManager.cpp @@ -3,6 +3,7 @@ #include "DockLayout.hpp" #include "ScrollableLayout.hpp" +#include "BasicLayout.hpp" #include @@ -78,7 +79,7 @@ void LayoutManager::adjustLayouts() { }); for (auto& [iter, _] : mLayOrder) { - iter->getLayout()->updateLayout(mVertical); + iter->getLayout()->arrangeChildren(mVertical); } } @@ -92,10 +93,13 @@ static int sizePolicyDep[3][3] = { int LayoutManager::getLayoutOrder(WidgetLayout* parent, WidgetLayout* child) const { if (!parent || !child) return 0; + return 1; + auto policyParent = parent->getSizePolicy()[mVertical]; auto policyChild = parent->getSizePolicy()[mVertical]; - if (dynamic_cast(parent)) return -1; + // if (dynamic_cast(parent)) return -1; + // if (dynamic_cast(parent)) return -1; // if (dynamic_cast(parent)) return -1; return sizePolicyDep[int(policyParent)][int(policyChild)]; diff --git a/Widgets/private/widgets/FloatingWidget.cpp b/Widgets/private/widgets/FloatingWidget.cpp index 0a8579a..72eae98 100644 --- a/Widgets/private/widgets/FloatingWidget.cpp +++ b/Widgets/private/widgets/FloatingWidget.cpp @@ -47,30 +47,3 @@ FloatingLayout* FloatingWidget::layout() { return dynamic_cast( const FloatingLayout* FloatingWidget::layout() const { return dynamic_cast(Widget::getLayout()); } - -FloatingMenu::FloatingMenu() : FloatingWidget() { - setDebug("float menu", { 0.0, 0.9, 0.1, 0.7 }); - - // addChild(&mMenuLayout); - - addChild(&mHeader); - addChild(&mBodyLayout); - - mHeader.setText("Menu"); - - mHeader.setSizePolicy(SizePolicy::Expanding, SizePolicy::Minimal); - mBodyLayout.setSizePolicy(SizePolicy::Expanding, SizePolicy::Expanding); - - mBodyLayout.setLayout(new ScrollableLayout(&mBodyLayout)); - mBodyLayout.addChild(&mScrollBar); - mBodyLayout.addChild(&mContentWidget); - - mContentWidget.setSizePolicy(SizePolicy::Minimal, SizePolicy::Minimal); - - // getLayout()->setLayoutPolicy(LayoutPolicy::Vertical); - // mBodyLayout.getLayout()->setLayoutPolicy(LayoutPolicy::Vertical); -} - -void FloatingMenu::setText(const std::string& text) { - mHeader.setText(text); -} diff --git a/Widgets/private/widgets/MenuWidgets.cpp b/Widgets/private/widgets/MenuWidgets.cpp new file mode 100644 index 0000000..56f2dc4 --- /dev/null +++ b/Widgets/private/widgets/MenuWidgets.cpp @@ -0,0 +1,58 @@ +#include "MenuWidgets.hpp" + +#include "ScrollableLayout.hpp" + +using namespace tp; + +CollapsingMenuWidget::CollapsingMenuWidget() { + setDebug("Collapsing Menu", {1}); + + setSizePolicy(SizePolicy::Expanding, SizePolicy::Minimal); + + addChild(&mHeader); + addChild(&mContent); +} + +void CollapsingMenuWidget::process(const EventHandler& events) { + mHover = mHeader.getArea().isInside(events.getPointer()); + + if (events.isPressed(InputID::MOUSE1) && mHover) setCollapsed(!mContent.getEnabled()); +} + +void CollapsingMenuWidget::setCollapsed(bool collapsed) { + mContent.setEnabled(collapsed); +} + +void CollapsingMenuWidget::draw(Canvas& canvas) { + canvas.rect(getArea().relative(), colorBG, rounding); + if (mHover) { + canvas.rect(mHeader.getArea(), colorHover, rounding); + } +} + +FloatingScrollableMenu::FloatingScrollableMenu() : FloatingWidget() { + setDebug("float menu", { 0.0, 0.9, 0.1, 0.7 }); + + // addChild(&mMenuLayout); + + addChild(&mHeader); + addChild(&mBodyLayout); + + mHeader.setText("Menu"); + + mHeader.setSizePolicy(SizePolicy::Expanding, SizePolicy::Minimal); + mBodyLayout.setSizePolicy(SizePolicy::Expanding, SizePolicy::Expanding); + + mBodyLayout.setLayout(new ScrollableLayout(&mBodyLayout)); + mBodyLayout.addChild(&mScrollBar); + mBodyLayout.addChild(&mContentWidget); + + mContentWidget.setSizePolicy(SizePolicy::Minimal, SizePolicy::Minimal); + + // getLayout()->setLayoutPolicy(LayoutPolicy::Vertical); + // mBodyLayout.getLayout()->setLayoutPolicy(LayoutPolicy::Vertical); +} + +void FloatingScrollableMenu::setText(const std::string& text) { + mHeader.setText(text); +} diff --git a/Widgets/private/widgets/SimpleWidgets.cpp b/Widgets/private/widgets/SimpleWidgets.cpp index cdd2c33..ca74076 100644 --- a/Widgets/private/widgets/SimpleWidgets.cpp +++ b/Widgets/private/widgets/SimpleWidgets.cpp @@ -107,7 +107,7 @@ void SliderWidget::process(const EventHandler& events) { } void SliderWidget::draw(Canvas& canvas) { - canvas.rect(getArea().relative(), mColorBG, mRounding); + canvas.rect(getRelativeArea(), mColorBG, mRounding); switch (mState) { case IDLE: canvas.rect(getHandleArea(), mColorIdle, mRounding); break; diff --git a/Widgets/public/Layout.hpp b/Widgets/public/Layout.hpp index b878d32..9c570dc 100644 --- a/Widgets/public/Layout.hpp +++ b/Widgets/public/Layout.hpp @@ -26,11 +26,20 @@ namespace tp { explicit WidgetLayout(Widget* widget) { mWidget = widget; } virtual ~WidgetLayout() = default; - virtual void updateLayout(bool vertical) {} - + public: + // picks own position and size + // modifies only own area virtual void pickRect(bool vertical) {} - virtual void clampRect() {} - [[nodiscard]] virtual RectF getAvailableChildArea() const; + + // arranges all children layouts inside itself + // modifies children areas + virtual void arrangeChildren(bool vertical) {} + + // area that any child can have + [[nodiscard]] virtual RectF availableChildArea() const; + + // minimal size base on the content of the layout + [[nodiscard]] virtual RectF minContentArea() const; public: const Vec2F& getMinSize(); @@ -39,6 +48,8 @@ namespace tp { [[nodiscard]] const Vec2& getSizePolicy() const; void setSizePolicy(SizePolicy x, SizePolicy y); + static WidgetLayout* lay(Widget* w); + public: [[nodiscard]] const RectF& getArea() const; [[nodiscard]] RectF getAnimatedArea() const; @@ -49,6 +60,7 @@ namespace tp { public: void clampMinMaxSize(); + void pickMinimalRect(bool dir); [[nodiscard]] RangeF pickRange(const RangeF& current, const RangeF& child, const RangeF& parent, bool v) const; [[nodiscard]] RangeF clampRange(const RangeF& current, const RangeF& child, const RangeF& parent, bool v) const; diff --git a/Widgets/public/Widget.hpp b/Widgets/public/Widget.hpp index 40f7207..77c1c50 100644 --- a/Widgets/public/Widget.hpp +++ b/Widgets/public/Widget.hpp @@ -61,6 +61,7 @@ namespace tp { void setSizePolicy(SizePolicy x, SizePolicy y); void setEnabled(bool val) { mFlags.set(ENABLED, val); } + [[nodiscard]] bool getEnabled() const { return mFlags.get(ENABLED); } WidgetLayout* getLayout(); [[nodiscard]] const WidgetLayout* getLayout() const; diff --git a/Widgets/public/layouts/BasicLayout.hpp b/Widgets/public/layouts/BasicLayout.hpp index 185e154..9660e1e 100644 --- a/Widgets/public/layouts/BasicLayout.hpp +++ b/Widgets/public/layouts/BasicLayout.hpp @@ -10,17 +10,21 @@ namespace tp { public: explicit BasicLayout(Widget* widget) : WidgetLayout(widget) {} - void updateLayout(bool vertical) override; + void arrangeChildren(bool vertical) override; void pickRect(bool vertical) override; - void clampRect() override; - [[nodiscard]] RectF getAvailableChildArea() const override; + + [[nodiscard]] RectF availableChildArea() const override; + [[nodiscard]] RectF minContentArea() const override; void setLayoutPolicy(LayoutPolicy layout) { mLayoutPolicy = layout; } + private: + static bool canChange(Widget*, bool); + private: void adjustLayout(bool vertical); - static halnf changeChildSize(Widget*, halnf diff, bool vertical); + static halnf shrinkLayoutSize(WidgetLayout* widget, halnf diff, bool vertical); private: LayoutPolicy mLayoutPolicy = LayoutPolicy::Vertical; diff --git a/Widgets/public/layouts/DockLayout.hpp b/Widgets/public/layouts/DockLayout.hpp index 47de346..b999e05 100644 --- a/Widgets/public/layouts/DockLayout.hpp +++ b/Widgets/public/layouts/DockLayout.hpp @@ -43,8 +43,7 @@ namespace tp { public: void pickRect(bool vertical) override {} - void clampRect() override {}; - void updateLayout(bool vertical) override; + void arrangeChildren(bool vertical) override; void adjustChildrenRect(); public: diff --git a/Widgets/public/layouts/OverlayLayout.hpp b/Widgets/public/layouts/OverlayLayout.hpp index e674327..2b1b3bc 100644 --- a/Widgets/public/layouts/OverlayLayout.hpp +++ b/Widgets/public/layouts/OverlayLayout.hpp @@ -10,9 +10,8 @@ namespace tp { public: explicit OverlayLayout(Widget* widget) : WidgetLayout(widget) {} - void updateLayout(bool vertical) override; + void arrangeChildren(bool vertical) override; void pickRect(bool vertical) override {} - void clampRect() override {} }; } \ No newline at end of file diff --git a/Widgets/public/layouts/ScrollableLayout.hpp b/Widgets/public/layouts/ScrollableLayout.hpp index 329131e..d4d0916 100644 --- a/Widgets/public/layouts/ScrollableLayout.hpp +++ b/Widgets/public/layouts/ScrollableLayout.hpp @@ -12,8 +12,8 @@ namespace tp { setSizePolicy(SizePolicy::Expanding, SizePolicy::Expanding); } - void updateLayout(bool vertical) override; - [[nodiscard]] RectF getAvailableChildArea() const override; + void arrangeChildren(bool vertical) override; + [[nodiscard]] RectF availableChildArea() const override; private: void updateWidgetRects(const RectF& area, Widget* content, ScrollableBarWidget* scroller) const; diff --git a/Widgets/public/widgets/FloatingWidget.hpp b/Widgets/public/widgets/FloatingWidget.hpp index 71f1686..30a5139 100644 --- a/Widgets/public/widgets/FloatingWidget.hpp +++ b/Widgets/public/widgets/FloatingWidget.hpp @@ -13,10 +13,8 @@ namespace tp { } void process(const EventHandler& events) override; - void draw(Canvas& canvas) override; - [[nodiscard]] bool needsNextFrame() const override; [[nodiscard]] bool propagateEventsToChildren() const override; @@ -28,35 +26,4 @@ namespace tp { FloatingLayout* layout(); [[nodiscard]] const FloatingLayout* layout() const; }; - - class FloatingMenu : public FloatingWidget { - public: - FloatingMenu(); - - public: - void addToMenu(Widget* widget) { - widget->setSizePolicy(SizePolicy::Expanding, SizePolicy::Minimal); - mContentWidget.addChild(widget); - } - - const List& getContent() { - return mContentWidget.getChildren(); - } - - void clearChildren() { - mContentWidget.clear(); - } - - void setText(const std::string& text); - - private: - // VerticalLayout mMenuLayout; - Widget mBodyLayout; - Widget mContentWidget; - ScrollableBarWidget mScrollBar; - - LabelWidget mHeader; - - // ButtonWidget mTestButton; - }; } \ No newline at end of file diff --git a/Widgets/public/widgets/MenuWidgets.hpp b/Widgets/public/widgets/MenuWidgets.hpp new file mode 100644 index 0000000..36fcbac --- /dev/null +++ b/Widgets/public/widgets/MenuWidgets.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include "FloatingWidget.hpp" + +namespace tp { + class CollapsingMenuWidget : public Widget { + public: + CollapsingMenuWidget(); + + void process(const EventHandler& events) override; + void draw(Canvas& canvas) override; + [[nodiscard]] bool processesEvents() const override { return true; } + + public: + void setCollapsed(bool val); + LabelWidget* getHeader() { return &mHeader; } + Widget* getContainer() { return &mContent; } + + private: + bool mHover = false; + LabelWidget mHeader; + Widget mContent; + + private: + halnf rounding = 5; + RGBA colorHover = RGBA(0.7); + RGBA colorBG = RGBA(0.3); + }; + + class FloatingScrollableMenu : public FloatingWidget { + public: + FloatingScrollableMenu(); + + public: + void addToMenu(Widget* widget) { + widget->setSizePolicy(SizePolicy::Expanding, SizePolicy::Minimal); + mContentWidget.addChild(widget); + } + + const List& getContent() { + return mContentWidget.getChildren(); + } + + void clearChildren() { + mContentWidget.clear(); + } + + void setText(const std::string& text); + + private: + // VerticalLayout mMenuLayout; + Widget mBodyLayout; + Widget mContentWidget; + ScrollableBarWidget mScrollBar; + + LabelWidget mHeader; + + // ButtonWidget mTestButton; + }; +} \ No newline at end of file