From 15127c5122afea3b64f83ffa59888f984ce9bd84 Mon Sep 17 00:00:00 2001 From: IlyaShurupov Date: Wed, 26 Jun 2024 11:30:02 +0300 Subject: [PATCH] Cleanup widgets --- 3DEditor/public/EditorWidget.hpp | 71 +++----- Graphics/private/Window.cpp | 10 +- Math/public/Rect.hpp | 6 + Sketch3D/public/Sketch3DWidget.hpp | 13 +- TODO | 3 +- Widgets/examples/SimpleGUI.cpp | 28 +-- Widgets/examples/SimpleGUI.hpp | 45 +++-- Widgets/private/DockspaceWidget.cpp | 257 +++++++++++++++++---------- Widgets/private/FloatSpaceWidget.cpp | 47 +++++ Widgets/private/FloatingWidget.cpp | 6 + Widgets/private/ScrollableWidget.cpp | 2 +- Widgets/private/WidgetBase.cpp | 3 + Widgets/private/WorkspaceWidget.cpp | 66 +++++++ Widgets/public/DockspaceWidget.hpp | 71 +++++--- Widgets/public/FloatSpaceWidget.hpp | 18 ++ Widgets/public/FloatingWidget.hpp | 3 + Widgets/public/WidgetBase.hpp | 6 + Widgets/public/Widgets.hpp | 2 + Widgets/public/WorkspaceWidget.hpp | 19 ++ 19 files changed, 455 insertions(+), 221 deletions(-) create mode 100644 Widgets/private/FloatSpaceWidget.cpp create mode 100644 Widgets/private/WorkspaceWidget.cpp create mode 100644 Widgets/public/FloatSpaceWidget.hpp create mode 100644 Widgets/public/WorkspaceWidget.hpp diff --git a/3DEditor/public/EditorWidget.hpp b/3DEditor/public/EditorWidget.hpp index 22a93cc..093cc63 100644 --- a/3DEditor/public/EditorWidget.hpp +++ b/3DEditor/public/EditorWidget.hpp @@ -30,15 +30,15 @@ namespace tp { Canvas::ImageHandle mImage; }; - class EditorWidget : public Widget { + class EditorWidget : public WorkspaceWidget { public: EditorWidget(Canvas* canvas, Editor* editor) : mViewport(canvas, editor) { mEditor = editor; - this->mChildWidgets.pushBack(&mViewport); - this->mChildWidgets.pushBack(&mSplitView); - this->mChildWidgets.pushBack(&mSettingsWidget); + mDockSpace.addSideWidget(&mNavigationMenu, DockSpaceWidget::RIGHT); + mDockSpace.addSideWidget(&mRenderMenu, DockSpaceWidget::LEFT); + mDockSpace.setCenterWidget(&mViewport); // Render { @@ -68,53 +68,22 @@ namespace tp { mNavigationMenu.setLabel("Navigation"); } - mSettingsWidget.addWidget(&mRenderMenu); - mSettingsWidget.addWidget(&mNavigationMenu); + mRenderPathTracer.mCallback = [this]() { + mEditor->renderPathFrame(); + mEditor->setRenderType(Editor::RenderType::PATH_TRACER); + }; + + mRenderRaster.mCallback = [this]() { mEditor->setRenderType(Editor::RenderType::RASTER); }; + mRenderDeNoise.mCallback = [this]() { mEditor->denoisePathRenderBuffers(); }; + mNavigationOrbit.mCallback = [this]() { mNavigationType = ORBIT; }; + mNavigationPan.mCallback = [this]() { mNavigationType = PAN; }; + mNavigationZoom.mCallback = [this](){ mNavigationType = ZOOM; }; + mNavigationReset.mCallback = [this]() { mEditor->navigationReset(); }; } void eventProcess(const Events& events) override { - mSplitView.setArea(this->mArea); - - mViewport.setArea(mSplitView.getFirst()); - mViewport.mEnable = mSplitView.getFirstEnabled(); - - mSettingsWidget.setArea(mSplitView.getSecond()); - mSettingsWidget.mEnable = mSplitView.getSecondEnabled(); - - // render settings - { - if (mRenderPathTracer.isFired()) { - mEditor->renderPathFrame(); - mEditor->setRenderType(Editor::RenderType::PATH_TRACER); - } - - if (mRenderRaster.isFired()) { - mEditor->setRenderType(Editor::RenderType::RASTER); - } - - if (mRenderDeNoise.isFired()) { - mEditor->denoisePathRenderBuffers(); - } - } - // navigation { - if (mNavigationOrbit.isFired()) { - mNavigationType = ORBIT; - } - - if (mNavigationPan.isFired()) { - mNavigationType = PAN; - } - - if (mNavigationZoom.isFired()) { - mNavigationType = ZOOM; - } - - if (mNavigationReset.isFired()) { - mEditor->navigationReset(); - } - const auto& activeArea = mViewport.mArea; if (this->isHolding() && activeArea.isInside(events.getPointer())) { switch (mNavigationType) { @@ -132,6 +101,8 @@ namespace tp { } } } + + WorkspaceWidget::eventProcess(events); } void eventDraw(Canvas& canvas) override { canvas.rect(this->mArea, mBaseColor); } @@ -144,13 +115,13 @@ namespace tp { public: Editor* mEditor = nullptr; - SplitView mSplitView; + // SplitView mSplitView; ViewportWidget mViewport; - ScrollableWindow mSettingsWidget; + // ScrollableWindow mSettingsWidget; // Controls - CollapsableMenu mRenderMenu; + FloatingWidget mRenderMenu; ButtonWidget mRenderPathTracer; ButtonWidget mRenderRaster; ButtonWidget mRenderDeNoise; @@ -158,7 +129,7 @@ namespace tp { // Navigation enum NavigationType { ORBIT, PAN, ZOOM } mNavigationType = ORBIT; - CollapsableMenu mNavigationMenu; + FloatingWidget mNavigationMenu; ButtonWidget mNavigationPan; ButtonWidget mNavigationOrbit; ButtonWidget mNavigationZoom; diff --git a/Graphics/private/Window.cpp b/Graphics/private/Window.cpp index 4798551..bcd01e5 100644 --- a/Graphics/private/Window.cpp +++ b/Graphics/private/Window.cpp @@ -26,7 +26,8 @@ static void scrollCallback(GLFWwindow* window, double xOffset, double yOffset); Window::Window(Vec2F size, const char* title) { mContext = new Context(); - glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, 1); + // glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, 1); + // glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Create a window and OpenGL context mContext->window = glfwCreateWindow((int) size.x, (int) size.y, title, nullptr, nullptr); @@ -63,7 +64,8 @@ Window* Window::createWindow(Vec2F size, const char* title) { } count--; - glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11); + // glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11); + // glfwInitHint(GLFW_WAYLAND_LIBDECOR, GLFW_WAYLAND_DISABLE_LIBDECOR); // Initialize GLFW if (!glfwInit()) { @@ -89,8 +91,8 @@ void Window::destroyWindow(Window* window) { bool Window::shouldClose() const { return glfwWindowShouldClose(mContext->window); } void Window::processEvents() { - glfwPollEvents(); - // glfwWaitEvents(); + // glfwPollEvents(); + glfwWaitEvents(); checkAxisUpdates(); } diff --git a/Math/public/Rect.hpp b/Math/public/Rect.hpp index 4705736..ac4496b 100644 --- a/Math/public/Rect.hpp +++ b/Math/public/Rect.hpp @@ -245,6 +245,12 @@ namespace tp { return { x, y + abs, size.x, size.y - abs }; } + Rect getSizedFromCenter(Vec2 size) { + const auto pivot = center(); + const auto sizeHalf = size / 2; + return { pivot - sizeHalf, size }; + } + public: union { Vec2 v1; diff --git a/Sketch3D/public/Sketch3DWidget.hpp b/Sketch3D/public/Sketch3DWidget.hpp index 7229ef8..c25c3a8 100644 --- a/Sketch3D/public/Sketch3DWidget.hpp +++ b/Sketch3D/public/Sketch3DWidget.hpp @@ -130,15 +130,10 @@ namespace tp { mViewport.setArea(mSplitView.getFirst()); mOptions.setArea(mSplitView.getSecond()); - if (mDrawButton->isFired()) { - mViewport.mMode = Sketch3DWidget::Mode::DRAW; - } else if (mMoveButton->isFired()) { - mViewport.mMode = Sketch3DWidget::Mode::MOVE; - } else if (mRotateButton->isFired()) { - mViewport.mMode = Sketch3DWidget::Mode::ROTATE; - } else if (mZoomButton->isFired()) { - mViewport.mMode = Sketch3DWidget::Mode::ZOOM; - } + mDrawButton->mCallback = [this]() { mViewport.mMode = Sketch3DWidget::Mode::DRAW; }; + mMoveButton->mCallback = [this]() { mViewport.mMode = Sketch3DWidget::Mode::MOVE; }; + mRotateButton->mCallback = [this]() { mViewport.mMode = Sketch3DWidget::Mode::ROTATE; }; + mZoomButton->mCallback = [this]() { mViewport.mMode = Sketch3DWidget::Mode::ZOOM; }; mViewport.setColor(RGBA(mRed->mSlider.mFactor, mGreen->mSlider.mFactor, mBlue->mSlider.mFactor, 1.f)); } diff --git a/TODO b/TODO index 07a8f33..d2f0156 100644 --- a/TODO +++ b/TODO @@ -67,9 +67,10 @@ Language: Make automations itself a universal tool - NFA DFA conversions Widgets: - Add tree structure interface to simplify code Add shortcuts Config cache may be static + Cleanup + Add animations LibraryViewer: artworks diff --git a/Widgets/examples/SimpleGUI.cpp b/Widgets/examples/SimpleGUI.cpp index 1eb39de..900ec77 100644 --- a/Widgets/examples/SimpleGUI.cpp +++ b/Widgets/examples/SimpleGUI.cpp @@ -9,28 +9,7 @@ using namespace tp; class SimpleGUI : public Application { public: SimpleGUI() { - - // debug - - mGui.addSideWidget(&mButtons[0], DockSpaceWidget::BOTTOM); - mGui.addSideWidget(&mButtons[1], DockSpaceWidget::RIGHT); - - mGui.removeSideWidget(DockSpaceWidget::BOTTOM); - - mGui.addSideWidget(&mButtons[0], DockSpaceWidget::TOP); - mGui.addSideWidget(&mButtons[2], DockSpaceWidget::LEFT); - - mGui.removeSideWidget(DockSpaceWidget::TOP); - - mGui.addSideWidget(&mButtons[0], DockSpaceWidget::BOTTOM); - - mGui.addSideWidget(&mButtons[3], DockSpaceWidget::TOP); - mGui.setCenterWidget(&mButtons[4]); - - mButtons[4].mCallback = [&]() { mGui.toggleHiddenState(DockSpaceWidget::BOTTOM); }; - mButtons[0].mCallback = [&]() { mGui.toggleHiddenState(DockSpaceWidget::TOP); }; - mButtons[2].mCallback = [&]() { mGui.toggleHiddenState(DockSpaceWidget::RIGHT); }; - mButtons[3].mCallback = [&]() { mGui.toggleHiddenState(DockSpaceWidget::LEFT); }; + // mGui.mPreview = true; } void processFrame(EventHandler* eventHandler) override { @@ -49,9 +28,8 @@ public: private: WidgetManager mWidgetManager; - DockSpaceWidget mGui; - - ButtonWidget mButtons[5]; + // DockSpaceWidget mGui; + SimpleWidget3 mGui; }; int main() { diff --git a/Widgets/examples/SimpleGUI.hpp b/Widgets/examples/SimpleGUI.hpp index 636a53a..157043b 100644 --- a/Widgets/examples/SimpleGUI.hpp +++ b/Widgets/examples/SimpleGUI.hpp @@ -12,6 +12,11 @@ namespace tp { this->addWidgetToMenu(&mSlider); mInMenuButton1.mLabel.mLabel = "Button1"; + + mInMenuButton1.mCallback = []() { + printf("asd\n"); + }; + mInMenuButton2.mLabel.mLabel = "Button2"; } @@ -23,31 +28,41 @@ namespace tp { NamedSliderWidget mSlider; }; - class SimpleWidget2 : public DockSpaceWidget { + class SimpleWidget3 : public WorkspaceWidget { public: - SimpleWidget2() { - this->mChildWidgets.pushBack(&mFloating); - mFloating.addWidgetToMenu(&mWidget); + SimpleWidget3() { - this->mChildWidgets.pushBack(&mFloating2); - mFloating2.addWidgetToMenu(&mWidget2); + mDockSpace.addSideWidget(&mButtons[0], DockSpaceWidget::BOTTOM); + mDockSpace.addSideWidget(&mButtons[1], DockSpaceWidget::RIGHT); - mFloating2.mArea = { 200, 100, 100, 100 }; + mDockSpace.removeSideWidget(DockSpaceWidget::BOTTOM); - this->mChildWidgets.pushBack(&mFloating3); - mFloating3.addWidgetToMenu(&mWidget3); + mDockSpace.addSideWidget(&mButtons[0], DockSpaceWidget::TOP); + mDockSpace.addSideWidget(&mButtons[2], DockSpaceWidget::LEFT); - mFloating3.mArea = { 400, 100, 100, 100 }; + mDockSpace.removeSideWidget(DockSpaceWidget::TOP); + + mDockSpace.addSideWidget(&mButtons[0], DockSpaceWidget::BOTTOM); + + mDockSpace.addSideWidget(&mButtons[3], DockSpaceWidget::TOP); + mDockSpace.setCenterWidget(&mButtons[4]); + + /* + mButtons[4].mCallback = [&]() { mDockSpace.toggleHiddenState(DockSpaceWidget::BOTTOM); }; + mButtons[0].mCallback = [&]() { mDockSpace.toggleHiddenState(DockSpaceWidget::TOP); }; + mButtons[2].mCallback = [&]() { mDockSpace.toggleHiddenState(DockSpaceWidget::RIGHT); }; + mButtons[3].mCallback = [&]() { mDockSpace.toggleHiddenState(DockSpaceWidget::LEFT); }; + */ + + mButtons[0].addWidgetToMenu(&mWidget); + mButtons[1].addWidgetToMenu(&mWidget2); } private: - FloatingWidget mFloating; + FloatingWidget mButtons[5]; + SimpleWidget mWidget; - - FloatingWidget mFloating2; SimpleWidget mWidget2; - - FloatingWidget mFloating3; SimpleWidget mWidget3; }; } \ No newline at end of file diff --git a/Widgets/private/DockspaceWidget.cpp b/Widgets/private/DockspaceWidget.cpp index 4518606..7c058bf 100644 --- a/Widgets/private/DockspaceWidget.cpp +++ b/Widgets/private/DockspaceWidget.cpp @@ -2,41 +2,50 @@ using namespace tp; -DockSpaceWidget::DockSpaceWidget() = default; +DockSpaceWidget::DockSpaceWidget() { + mSideWidgets[0].side = LEFT; + mSideWidgets[1].side = TOP; + mSideWidgets[2].side = RIGHT; + mSideWidgets[3].side = BOTTOM; +} void DockSpaceWidget::addSideWidget(Widget* widget, Side side) { - if (mSideWidgets[side]) return; - mSideWidgets[side] = widget; - for (auto& order : mSideSplitOrder) { - if (order == -1) { - order = side; + if (isSideExists(side)) return; + + auto& sideWidget = mSideWidgets[side]; + sideWidget.widget = widget; + for (auto& order : mSideWidgets) { + if (order.order == -1) { + order.order = side; break; } } + + sideWidget.hidden = false; + mChildWidgets.pushBack(widget); widget->mIsDocked = true; - mSideHidden[side] = false; } void DockSpaceWidget::removeSideWidget(Side side) { - if (!mSideWidgets[side]) return; + if (!isSideExists(side)) return; bool removed = false; for (ualni i = 0; i < 3; i++) { - if (mSideSplitOrder[i] == side) { + if (mSideWidgets[i].order == side) { removed = true; } if (removed) { - mSideSplitOrder[i] = mSideSplitOrder[i + 1]; + swap(mSideWidgets[i].order, mSideWidgets[i + 1].order); } } - mSideSplitOrder[3] = -1; + mSideWidgets[3].order = -1; - auto widget = mSideWidgets[side]; - mSideWidgets[side] = nullptr; + auto widget = mSideWidgets[side].widget; widget->mIsDocked = false; - mChildWidgets.removeNode(mChildWidgets.find(widget)); + + mSideWidgets[side].widget = nullptr; } void DockSpaceWidget::setCenterWidget(Widget* widget) { @@ -47,15 +56,15 @@ void DockSpaceWidget::setCenterWidget(Widget* widget) { void DockSpaceWidget::toggleHiddenState(DockSpaceWidget::Side side) { if (!isSideExists(side)) return; - mSideHidden[side] = !mSideHidden[side]; + mSideWidgets[side].hidden = !mSideWidgets[side].hidden; } void DockSpaceWidget::eventProcess(const tp::Events& events) { - updateActiveWindow(events); - calculateSideAreas(); calculateResizeHandles(); // calculateHeaderAreas(); + + handlePreview(events); handleResizeEvents(events); updateChildSideWidgets(); @@ -64,129 +73,156 @@ void DockSpaceWidget::eventProcess(const tp::Events& events) { void DockSpaceWidget::eventDraw(Canvas& canvas) { canvas.rect(this->mArea, mBackgroundColor, 0); - for (auto sizeHandle : mResizeHandles) { - if (sizeHandle->active) { - canvas.rect(sizeHandle->area.shrink(mPadding / 1.5f), mResizeHandleColorActive, 0); - } else if (sizeHandle->hover) { - canvas.rect(sizeHandle->area.shrink(mPadding / 1.5f), mResizeHandleColorHovered, 0); + 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& header : mSideHeaderAreas) { - canvas.rect(header, mResizeHandleColorActive, 0); + for (auto& sideWidget : mSideWidgets) { + if (!isSideVisible(sideWidget.side)) continue; + canvas.rect(sideWidget.headerArea, mResizeHandleColorActive, 0); } } -void DockSpaceWidget::updateActiveWindow(const tp::Events& events) { - if (this->mChildWidgets.size() > 1 && events.isPressed(InputID::MOUSE1)) { +void DockSpaceWidget::eventDrawOver(Canvas& canvas) { + if (!mPreview) return; - Widget* activeChild = nullptr; + if (mPreviewSide != NONE) canvas.rect(mPreviewArea.shrink(mPadding * 2), mPreviewColor, mRounding); - for (auto childNode = this->mChildWidgets.firstNode(); childNode; childNode = childNode->next) { - auto child = childNode->data; - - if (child->mIsDocked) continue; - if (child->mArea.isInside(events.getPointer())) { - - mChildWidgets.detach(childNode); - mChildWidgets.pushFront(childNode); - - child->mHandlesEvents = true; - activeChild = child; - break; - } - } - - if (activeChild) { - for (auto child : this->mChildWidgets) { - if (activeChild != child.data() && !child->mIsDocked) child->mHandlesEvents = false; - } - } + for (auto& sideWidget : mSideWidgets) { + if (sideWidget.widget) continue; + canvas.rect(sideWidget.previewHandleArea, mPreviewColor, mRounding); } } void DockSpaceWidget::calculateSideAreas() { auto startArea = this->mArea; - for (ualni i = 0; i < 4; i++) { - const auto side = mSideSplitOrder[i]; + for (auto& sideWidget : mSideWidgets) { + const auto side = sideWidget.order; if (side == -1) break; - if (mSideHidden[side]) continue; + if (!isSideVisible(Side(side))) continue; bool vertical = side == TOP || side == BOTTOM; bool opposite = side == BOTTOM || side == RIGHT; - const auto maxAbsoluteSize = startArea.size[vertical]; - const auto paddingFactor = mSideSizePadding / maxAbsoluteSize; + auto& sideSize = mSideWidgets[side].absoluteSize; - mSideAbsoluteSize[side] = - clamp(mSideAbsoluteSize[side], maxAbsoluteSize * paddingFactor, maxAbsoluteSize * (1.f - paddingFactor)); - - auto factor = mSideAbsoluteSize[side] / startArea.size[vertical]; + 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); - mSideAreas[side] = side == LEFT ? first : second; + area = side == LEFT ? first : second; startArea = side == LEFT ? second : first; } else { const auto first = startArea.splitByFactorVT(factor); const auto second = startArea.splitByFactorVB(factor); - mSideAreas[side] = side == TOP ? first : second; + area = side == TOP ? first : second; startArea = side == TOP ? second : first; } - mSideAreas[side] = mSideAreas[side].shrink(mPadding); + area = area.shrink(mPadding); } mCenterArea = startArea.shrink(mPadding); } void DockSpaceWidget::calculateResizeHandles() { - mResizeHandles.clear(); RectF rec; if (isSideVisible(LEFT)) { - rec = { mSideAreas[LEFT].p4(), { mPadding * 2, mSideAreas[LEFT].size.y } }; - mResizeHandles.append({ rec, LEFT, 0, mCenterArea.p3().x }); + auto& side = mSideWidgets[LEFT]; + rec = { side.area.p4(), { mPadding * 2, side.area.size.y } }; + side.resizeHandle = { rec, 0, mCenterArea.p3().x }; } if (isSideVisible(RIGHT)) { - rec = { mSideAreas[RIGHT].p1(), { mPadding * 2, mSideAreas[RIGHT].size.y } }; + auto& side = mSideWidgets[RIGHT]; + rec = { side.area.p1(), { mPadding * 2, side.area.size.y } }; rec.x -= mPadding * 2; - mResizeHandles.append({ rec, RIGHT, mCenterArea.p1().x, rec.p3().x }); + side.resizeHandle = { rec, 0, (this->mArea.p3() - mCenterArea.p1()).x }; } if (isSideVisible(TOP)) { - rec = { mSideAreas[TOP].p2(), { mSideAreas[TOP].size.x, mPadding * 2 } }; - mResizeHandles.append({ rec, TOP, 0, mCenterArea.p2().y }); + auto& side = mSideWidgets[TOP]; + rec = { side.area.p2(), { side.area.size.x, mPadding * 2 } }; + side.resizeHandle = { rec, 0, mCenterArea.p2().y }; } if (isSideVisible(BOTTOM)) { - rec = { mSideAreas[BOTTOM].p1(), { mSideAreas[BOTTOM].size.x, mPadding * 2 } }; + auto& side = mSideWidgets[BOTTOM]; + rec = { side.area.p1(), { side.area.size.x, mPadding * 2 } }; rec.y -= mPadding * 2; - mResizeHandles.append({ rec, BOTTOM, mCenterArea.p1().y, rec.p2().y }); + side.resizeHandle = { rec, 0, (this->mArea.p3() - mCenterArea.p1()).y }; } } void DockSpaceWidget::handleResizeEvents(const Events& events) { - for (auto resizeHandle : mResizeHandles) { - resizeHandle->hover = false; - if (resizeHandle->area.isInside(events.getPointerPrev())) { - resizeHandle->hover = true; + 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); } } + for (auto& sideWidget : mSideWidgets) { + sideWidget.resizeHandle.hover = false; + if (sideWidget.resizeHandle.area.isInside(events.getPointerPrev())) { + sideWidget.resizeHandle.hover = true; + } + } + + for (auto& sideWidget : mSideWidgets) { + sideWidget.resizeHandle.active = false; + } + + if (mPreview) return; + + auto resizeSideWidget = [&events](SideWidgetData& sideWidget, Vec2F deltaVec) { + halnf delta = deltaVec[(sideWidget.side == TOP || sideWidget.side == BOTTOM)]; + if (sideWidget.side == BOTTOM || sideWidget.side == RIGHT) delta *= -1; + sideWidget.absoluteSize += delta; + sideWidget.resizeHandle.active = true; + }; + if (events.isDown(InputID::MOUSE1)) { - for (auto resizeHandle : mResizeHandles) { - resizeHandle->active = false; - if (resizeHandle->area.isInside(events.getPointerPrev())) { - auto factorDeltaVec = events.getPointerDelta(); - halnf factorDelta = factorDeltaVec[(resizeHandle->side == TOP || resizeHandle->side == BOTTOM)]; - if (resizeHandle->side == BOTTOM || resizeHandle->side == RIGHT) factorDelta *= -1; - mSideAbsoluteSize[resizeHandle->side] += factorDelta; - resizeHandle->active = true; + for (auto& sideWidget : mSideWidgets) { + if (sideWidget.resizeHandle.area.isInside(events.getPointerPrev())) { + resizeSideWidget(sideWidget, events.getPointerDelta()); } } } + + if (events.isDown(InputID::LEFT_ALT)) { + const auto pointer = events.getPointerPrev(); + if (!mCenterArea.isInside(pointer)) return; + + if (events.isPressed(InputID::MOUSE1)) { + for (auto i : { 0, 1 }) { + const auto step = mCenterArea.size[i] / 3.f; + const auto rec1 = mCenterArea.pos[i]; + const auto pos = pointer[i]; + if (pos > rec1 && pos < rec1 + step) resizeType[i] = 0; + if (pos > rec1 + step && pos < rec1 + step * 2) resizeType[i] = 1; + if (pos > rec1 + step * 2) resizeType[i] = 2; + } + } else if (events.isDown(InputID::MOUSE1)) { + const auto deltaVec = events.getPointerDelta(); + + if (resizeType[0] == 0) resizeSideWidget(mSideWidgets[LEFT], deltaVec); + if (resizeType[1] == 0) resizeSideWidget(mSideWidgets[TOP], deltaVec); + if (resizeType[0] == 2) resizeSideWidget(mSideWidgets[RIGHT], deltaVec); + if (resizeType[1] == 2) resizeSideWidget(mSideWidgets[BOTTOM], deltaVec); + } + } } void DockSpaceWidget::updateChildSideWidgets() { @@ -194,12 +230,13 @@ void DockSpaceWidget::updateChildSideWidgets() { { for (ualni i = 0; i < 4; i++) { if (!isSideExists(Side(i))) continue; + auto widget = mSideWidgets[i].widget; if (!isSideVisible(Side(i))) { - mSideWidgets[i]->mEnable = false; + widget->mEnable = false; } else { - mSideWidgets[i]->setArea(mSideAreas[i]); - mSideWidgets[i]->mEnable = true; + widget->setArea(mSideWidgets[i].area); + widget->mEnable = true; } } @@ -227,18 +264,20 @@ void DockSpaceWidget::updateChildSideWidgets() { void DockSpaceWidget::calculateHeaderAreas() { for (ualni i = 0; i < 4; i++) { if (!isSideVisible(Side(i))) continue; - const auto factor = mHeaderSize / mSideAreas[i].size.y; - mSideHeaderAreas[i] = mSideAreas[i].splitByFactorVT(factor); - mSideAreas[i] = mSideAreas[i].splitByFactorVB(factor); + auto& area = mSideWidgets[i].area; + const auto factor = mHeaderSize / area.size.y; + mSideWidgets[i].headerArea = area.splitByFactorVT(factor); + area = area.splitByFactorVB(factor); - mSideHeaderAreas[i].size.y -= mPadding; + mSideWidgets[i].headerArea.size.y -= mPadding; } } -bool DockSpaceWidget::isSideVisible(DockSpaceWidget::Side side) { return mSideWidgets[side] && !mSideHidden[side]; } - -bool DockSpaceWidget::isSideExists(DockSpaceWidget::Side side) { return mSideWidgets[side]; } +bool DockSpaceWidget::isSideVisible(DockSpaceWidget::Side side) { + return isSideExists(side) && !mSideWidgets[side].hidden; +} +bool DockSpaceWidget::isSideExists(DockSpaceWidget::Side side) { return mSideWidgets[side].widget; } ualni DockSpaceWidget::getVisibleSidesSize() { ualni out = 0; @@ -247,3 +286,41 @@ ualni DockSpaceWidget::getVisibleSidesSize() { } return out; } + +DockSpaceWidget::Side DockSpaceWidget::getPreviewSide() { return mPreviewSide; } + +void DockSpaceWidget::handlePreview(const Events& 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.previewHandleArea.isInside(events.getPointer())) { + mPreviewArea = sideWidget.area; + mPreviewSide = sideWidget.side; + } + } +} \ No newline at end of file diff --git a/Widgets/private/FloatSpaceWidget.cpp b/Widgets/private/FloatSpaceWidget.cpp new file mode 100644 index 0000000..69f3799 --- /dev/null +++ b/Widgets/private/FloatSpaceWidget.cpp @@ -0,0 +1,47 @@ +#include "FloatSpaceWidget.hpp" + +using namespace tp; + +FloatSpaceWidget::FloatSpaceWidget() = default; + +void FloatSpaceWidget::eventProcess(const tp::Events& events) { + updateActiveWindow(events); +} + +void FloatSpaceWidget::updateActiveWindow(const tp::Events& events) { + mIsPassThrough = true; + for (auto childNode = this->mChildWidgets.firstNode(); childNode; childNode = childNode->next) { + auto child = childNode->data; + if (child->mArea.isInside(events.getPointer())) { + mIsPassThrough = false; + } + } + + if (events.isPressed(InputID::MOUSE1)) { + + Widget* activeChild = nullptr; + + for (auto childNode = this->mChildWidgets.firstNode(); childNode; childNode = childNode->next) { + auto child = childNode->data; + + if (child->mArea.isInside(events.getPointer())) { + + mChildWidgets.detach(childNode); + mChildWidgets.pushFront(childNode); + + child->mHandlesEvents = true; + activeChild = child; + break; + } + } + + if (activeChild) { + for (auto child : this->mChildWidgets) { + if (activeChild != child.data() && !child->mIsDocked) child->mHandlesEvents = false; + } + } + + } +} + +bool FloatSpaceWidget::handlesEvent() const { return !mIsPassThrough; } diff --git a/Widgets/private/FloatingWidget.cpp b/Widgets/private/FloatingWidget.cpp index 8adc986..d7c600d 100644 --- a/Widgets/private/FloatingWidget.cpp +++ b/Widgets/private/FloatingWidget.cpp @@ -78,3 +78,9 @@ RectF FloatingWidget::getResizeHandle() { auto pos = this->mArea.pos + this->mArea.size - size; return { pos, size }; } + +bool FloatingWidget::isFloating() const { return mFloating; } + +void FloatingWidget::stopFloating() { + mFloating = false; +} diff --git a/Widgets/private/ScrollableWidget.cpp b/Widgets/private/ScrollableWidget.cpp index 8578473..0a2b211 100644 --- a/Widgets/private/ScrollableWidget.cpp +++ b/Widgets/private/ScrollableWidget.cpp @@ -13,7 +13,7 @@ void ScrollBarWidget::eventProcess(const Events& events) { return; } - if (events.getScrollY() != 0) { + if (events.getScrollY() != 0 && mArea.isInside(events.getPointer())) { auto offset = events.getScrollY() < 0 ? 1.0f : -1.0f; mPositionFraction += mSizeFraction * offset * 0.3f; mPositionFraction = tp::clamp(mPositionFraction, 0.f, 1.f - mSizeFraction); diff --git a/Widgets/private/WidgetBase.cpp b/Widgets/private/WidgetBase.cpp index 59db281..ca1da3e 100644 --- a/Widgets/private/WidgetBase.cpp +++ b/Widgets/private/WidgetBase.cpp @@ -38,6 +38,8 @@ void Widget::drawWrapper(Canvas& canvas) { child->data->drawWrapper(canvas); } canvas.popClamp(); + + eventDrawOver(canvas); } void Widget::updateConfigWrapper(WidgetManager& wm) { @@ -52,6 +54,7 @@ void Widget::updateConfigWrapper(WidgetManager& wm) { void Widget::eventProcess(const Events& events) {} void Widget::eventDraw(Canvas& canvas) {} +void Widget::eventDrawOver(Canvas& canvas) {} void Widget::eventUpdateConfiguration(WidgetManager& wm) {} void Widget::eventVisible(const Events& events) {} diff --git a/Widgets/private/WorkspaceWidget.cpp b/Widgets/private/WorkspaceWidget.cpp new file mode 100644 index 0000000..82ce0f6 --- /dev/null +++ b/Widgets/private/WorkspaceWidget.cpp @@ -0,0 +1,66 @@ +#include "WorkspaceWidget.hpp" + +using namespace tp; + +WorkspaceWidget::WorkspaceWidget() { + this->mChildWidgets.pushBack(&mFloatingLayer); + this->mChildWidgets.pushBack(&mDockSpace); +} + +void WorkspaceWidget::eventProcess(const Events& events) { + mDockSpace.setArea(this->mArea); + mFloatingLayer.setArea(this->mArea); + + mDockSpace.mPreview = false; + mDockSpace.mHandlesEvents = !mFloatingLayer.handlesEvent(); + + for (auto floatingChild : mFloatingLayer.mChildWidgets) { + auto widget = dynamic_cast(floatingChild.data()); + if (!widget) continue; + if (widget->isFloating()) { + if (widget->isReleased()) { + auto side = mDockSpace.getPreviewSide(); + if (side != DockSpaceWidget::NONE) { + mFloatingLayer.mChildWidgets.removeNode(mFloatingLayer.mChildWidgets.find(widget)); + widget->setCollapsed(true); + widget->stopFloating(); + mDockSpace.addSideWidget(widget, side); + } + } else { + mDockSpace.mHandlesEvents = true; + mDockSpace.mPreview = true; + } + } + } + + for (auto& dockedChild : mDockSpace.mSideWidgets) { + auto widget = dynamic_cast(dockedChild.widget); + if (!widget) continue; + + if (widget->isFloating()) { + mDockSpace.removeSideWidget(dockedChild.side); + mFloatingLayer.mChildWidgets.pushBack(widget); + widget->mArea.pos = events.getPointer() - 20; + widget->mArea.size = mDefaultFloatSize; + } + } + + // toggle hide state + if (events.isDown(InputID::LEFT_ALT)) { + if (events.isPressed(InputID::N1)) { + mDockSpace.toggleHiddenState(DockSpaceWidget::LEFT); + } else if (events.isPressed(InputID::N2)) { + mDockSpace.toggleHiddenState(DockSpaceWidget::TOP); + } else if (events.isPressed(InputID::N3)) { + mDockSpace.toggleHiddenState(DockSpaceWidget::RIGHT); + } else if (events.isPressed(InputID::N4)) { + mDockSpace.toggleHiddenState(DockSpaceWidget::BOTTOM); + } + } + + for (auto& dockedChild : mDockSpace.mSideWidgets) { + auto widget = dynamic_cast(dockedChild.widget); + if (!widget) continue; + widget->setCollapsed(false); + } +} \ No newline at end of file diff --git a/Widgets/public/DockspaceWidget.hpp b/Widgets/public/DockspaceWidget.hpp index 5eba3d0..452a495 100644 --- a/Widgets/public/DockspaceWidget.hpp +++ b/Widgets/public/DockspaceWidget.hpp @@ -3,23 +3,49 @@ namespace tp { class DockSpaceWidget : public Widget { public: - enum Side { LEFT, TOP, RIGHT, BOTTOM }; + 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: DockSpaceWidget(); void eventProcess(const Events& events) override; void eventDraw(Canvas& canvas) override; + void eventDrawOver(Canvas& canvas) override; void addSideWidget(Widget* widget, Side side); void removeSideWidget(Side side); - void toggleHiddenState(DockSpaceWidget::Side side); + void toggleHiddenState(Side side); void setCenterWidget(Widget* widget); + Side getPreviewSide(); + private: - void updateActiveWindow(const tp::Events& events); void calculateSideAreas(); void calculateResizeHandles(); void handleResizeEvents(const Events& events); @@ -27,43 +53,36 @@ namespace tp { void calculateHeaderAreas(); - bool isSideVisible(Side side); bool isSideExists(Side side); ualni getVisibleSidesSize(); + void handlePreview(const Events& events); private: - // todo : make better grouping into structs + RectF mPreviewArea = {}; + Side mPreviewSide = NONE; + int resizeType[2] = { 0, 0 }; - Widget* mSideWidgets[4] { nullptr, nullptr, nullptr, nullptr }; + public: + SideWidgetData mSideWidgets[4]; + + private: Widget* mCenterWidget = nullptr; - - alni mSideSplitOrder[4] = { -1, -1, -1, -1 }; - halnf mSideAbsoluteSize[4] { 200, 100, 200, 100 }; - bool mSideHidden[4] { true, false, false, false }; - halnf mSideSizePadding = 50.f; - - // Geometry cache - struct ResizeHandle { - RectF area{ 0, 0, 0, 0 }; - Side side{ TOP }; - halnf start{ 0 }; - halnf end{ 0 }; - bool active = false; - bool hover = false; - }; - - Buffer mResizeHandles; - RectF mSideAreas[4]; - RectF mSideHeaderAreas[4]; RectF mCenterArea {}; - // View Parameters + // 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/Widgets/public/FloatSpaceWidget.hpp b/Widgets/public/FloatSpaceWidget.hpp new file mode 100644 index 0000000..617ae15 --- /dev/null +++ b/Widgets/public/FloatSpaceWidget.hpp @@ -0,0 +1,18 @@ +#include "FloatingWidget.hpp" + +namespace tp { + class FloatSpaceWidget : public Widget { + public: + FloatSpaceWidget(); + + void eventProcess(const Events& events) override; + + [[nodiscard]] bool handlesEvent() const; + + private: + void updateActiveWindow(const tp::Events& events); + + private: + bool mIsPassThrough = false; + }; +} \ No newline at end of file diff --git a/Widgets/public/FloatingWidget.hpp b/Widgets/public/FloatingWidget.hpp index a529db5..808b85a 100644 --- a/Widgets/public/FloatingWidget.hpp +++ b/Widgets/public/FloatingWidget.hpp @@ -11,6 +11,9 @@ namespace tp { void eventDraw(Canvas& canvas) override; void eventUpdateConfiguration(WidgetManager& wm) override; + [[nodiscard]] bool isFloating() const; + void stopFloating(); + private: void checkFloating(const Events& events); void checkResizing(const Events& events); diff --git a/Widgets/public/WidgetBase.hpp b/Widgets/public/WidgetBase.hpp index 950bd10..cb3c33e 100644 --- a/Widgets/public/WidgetBase.hpp +++ b/Widgets/public/WidgetBase.hpp @@ -20,7 +20,13 @@ namespace tp { void updateConfigWrapper(WidgetManager& wm); virtual void eventProcess(const Events& events); + + // draws before child widgets virtual void eventDraw(Canvas& canvas); + + // draws overlay after child widgets + virtual void eventDrawOver(Canvas& canvas); + virtual void eventUpdateConfiguration(WidgetManager& wm); virtual void eventVisible(const Events& events); diff --git a/Widgets/public/Widgets.hpp b/Widgets/public/Widgets.hpp index 4de32ca..766a2fb 100644 --- a/Widgets/public/Widgets.hpp +++ b/Widgets/public/Widgets.hpp @@ -9,4 +9,6 @@ #include "CollapsableMenu.hpp" #include "FloatingWidget.hpp" #include "Animations.hpp" +#include "FloatSpaceWidget.hpp" #include "DockspaceWidget.hpp" +#include "WorkspaceWidget.hpp" diff --git a/Widgets/public/WorkspaceWidget.hpp b/Widgets/public/WorkspaceWidget.hpp new file mode 100644 index 0000000..3115af8 --- /dev/null +++ b/Widgets/public/WorkspaceWidget.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "Widgets.hpp" + +namespace tp { + class WorkspaceWidget : public Widget { + public: + WorkspaceWidget(); + + void eventProcess(const Events& events) override; + + protected: + DockSpaceWidget mDockSpace; + FloatSpaceWidget mFloatingLayer; + + // Parameters + Vec2F mDefaultFloatSize = { 200, 200 }; + }; +}