From 458e56fb4ce558f5c350f62557540e168cbfd11f Mon Sep 17 00:00:00 2001 From: IlyaShurupov Date: Fri, 18 Oct 2024 15:18:50 +0300 Subject: [PATCH] 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; }