Refactor in a good state

This commit is contained in:
IlyaShurupov 2024-10-09 14:31:23 +03:00
parent 72c4740f82
commit 4ad40058f9
21 changed files with 606 additions and 105 deletions

View file

@ -36,8 +36,8 @@ namespace tp {
mViewport(canvas, editor) {
mEditor = editor;
mDockSpace.addSideWidget(&mNavigationMenu, GridLayoutWidget::RIGHT);
mDockSpace.addSideWidget(&mRenderMenu, GridLayoutWidget::LEFT);
mDockSpace.addSideWidget(&mNavigationMenu, DockLayoutWidget::RIGHT);
mDockSpace.addSideWidget(&mRenderMenu, DockLayoutWidget::LEFT);
mDockSpace.setCenterWidget(&mViewport);
// Render

View file

@ -44,6 +44,9 @@ bool transitionsReduce[4][4] = {
void EventHandler::processAllEvent() {
mMutex.lock();
mPointerPrev = mPointer;
while (mEventQueue.size()) {
processEventUnguarded();
}
@ -53,6 +56,8 @@ void EventHandler::processAllEvent() {
void EventHandler::processEvent() {
mMutex.lock();
mPointerPrev = mPointer;
if (!mEventQueue.size()) {
mMutex.unlock();
return;

View file

@ -195,7 +195,7 @@ namespace tp {
mCurrentTrackInfo.mPlayer = mPlayer;
mDockSpace.setCenterWidget(&mSongList);
mDockSpace.addSideWidget(&mCurrentTrackInfo, GridLayoutWidget::RIGHT);
mDockSpace.addSideWidget(&mCurrentTrackInfo, DockLayoutWidget::RIGHT);
// this->mChildWidgets.pushBack(&mCurrentTrackInfo);
// this->mChildWidgets.pushBack(&mCurrentTrack);

View file

@ -79,6 +79,15 @@ namespace tp {
bool operator==(const RGBA& in) const { return r == in.r && g == in.g && b == in.b && a == in.a; }
static RGBA random() {
return RGBA{
(halnf) randomFloat(),
(halnf) randomFloat(),
(halnf) randomFloat(),
1.f,
};
}
public:
union {
RGB rgbs;

View file

@ -177,7 +177,7 @@ namespace tp {
halnf mPadding = 0;
};
class ChattingWidget : public GridLayoutWidget {
class ChattingWidget : public DockLayoutWidget {
public:
ChattingWidget() {
// todo : fetch code
@ -205,7 +205,7 @@ namespace tp {
mActive.mHistoryView.addWidget(&message.data());
}
addSideWidget(&mSideView, GridLayoutWidget::RIGHT);
addSideWidget(&mSideView, DockLayoutWidget::RIGHT);
setCenterWidget(&mActive);
}

View file

@ -33,19 +33,19 @@ namespace tp {
public:
SimpleWidget3() {
mDockSpace.addSideWidget(&mButtons[0], GridLayoutWidget::BOTTOM);
mDockSpace.addSideWidget(&mButtons[1], GridLayoutWidget::RIGHT);
mDockSpace.addSideWidget(&mButtons[0], DockLayoutWidget::BOTTOM);
mDockSpace.addSideWidget(&mButtons[1], DockLayoutWidget::RIGHT);
mDockSpace.removeSideWidget(GridLayoutWidget::BOTTOM);
mDockSpace.removeSideWidget(DockLayoutWidget::BOTTOM);
mDockSpace.addSideWidget(&mButtons[0], GridLayoutWidget::TOP);
mDockSpace.addSideWidget(&mButtons[2], GridLayoutWidget::LEFT);
mDockSpace.addSideWidget(&mButtons[0], DockLayoutWidget::TOP);
mDockSpace.addSideWidget(&mButtons[2], DockLayoutWidget::LEFT);
mDockSpace.removeSideWidget(GridLayoutWidget::TOP);
mDockSpace.removeSideWidget(DockLayoutWidget::TOP);
mDockSpace.addSideWidget(&mButtons[0], GridLayoutWidget::BOTTOM);
mDockSpace.addSideWidget(&mButtons[0], DockLayoutWidget::BOTTOM);
mDockSpace.addSideWidget(&mButtons[3], GridLayoutWidget::TOP);
mDockSpace.addSideWidget(&mButtons[3], DockLayoutWidget::TOP);
mDockSpace.setCenterWidget(&mButtons[4]);
/*

View file

@ -2,15 +2,15 @@
using namespace tp;
GridLayoutWidget::GridLayoutWidget() {
DockLayoutWidget::DockLayoutWidget() {
mSideWidgets[0].side = LEFT;
mSideWidgets[1].side = TOP;
mSideWidgets[2].side = RIGHT;
mSideWidgets[3].side = BOTTOM;
}
void GridLayoutWidget::addSideWidget(Widget* widget, Side side) {
if (isSideExists(side)) return;
void DockLayoutWidget::addSideWidget(Widget* widget, Side side) {
if (sideExists(side)) return;
auto& sideWidget = mSideWidgets[side];
sideWidget.widget = widget;
@ -27,8 +27,8 @@ void GridLayoutWidget::addSideWidget(Widget* widget, Side side) {
widget->mIsDocked = true;
}
void GridLayoutWidget::removeSideWidget(Side side) {
if (!isSideExists(side)) return;
void DockLayoutWidget::removeSideWidget(Side side) {
if (!sideExists(side)) return;
bool removed = false;
for (ualni i = 0; i < 3; i++) {
@ -48,18 +48,18 @@ void GridLayoutWidget::removeSideWidget(Side side) {
mSideWidgets[side].widget = nullptr;
}
void GridLayoutWidget::setCenterWidget(Widget* widget) {
void DockLayoutWidget::setCenterWidget(Widget* widget) {
mChildWidgets.removeNode(mChildWidgets.find(mCenterWidget));
mCenterWidget = widget;
mChildWidgets.pushBack(mCenterWidget);
}
void GridLayoutWidget::toggleHiddenState(GridLayoutWidget::Side side) {
if (!isSideExists(side)) return;
void DockLayoutWidget::toggleHiddenState(DockLayoutWidget::Side side) {
if (!sideExists(side)) return;
mSideWidgets[side].hidden = !mSideWidgets[side].hidden;
}
void GridLayoutWidget::eventProcess(const tp::Events& events) {
void DockLayoutWidget::eventProcess(const tp::Events& events) {
calculateSideAreas();
calculateResizeHandles();
// calculateHeaderAreas();
@ -70,7 +70,7 @@ void GridLayoutWidget::eventProcess(const tp::Events& events) {
updateChildSideWidgets();
}
void GridLayoutWidget::eventDraw(Canvas& canvas) {
void DockLayoutWidget::eventDraw(Canvas& canvas) {
canvas.rect(this->mArea, mBackgroundColor, 0);
for (auto& sideWidget : mSideWidgets) {
@ -89,7 +89,7 @@ void GridLayoutWidget::eventDraw(Canvas& canvas) {
}
}
void GridLayoutWidget::eventDrawOver(Canvas& canvas) {
void DockLayoutWidget::eventDrawOver(Canvas& canvas) {
if (!mPreview) return;
if (mPreviewSide != NONE) canvas.rect(mPreviewArea.shrink(mPadding * 2), mPreviewColor, mRounding);
@ -100,7 +100,7 @@ void GridLayoutWidget::eventDrawOver(Canvas& canvas) {
}
}
void GridLayoutWidget::calculateSideAreas() {
void DockLayoutWidget::calculateSideAreas() {
auto startArea = this->mArea;
for (auto& sideWidget : mSideWidgets) {
const auto side = sideWidget.order;
@ -135,7 +135,7 @@ void GridLayoutWidget::calculateSideAreas() {
mCenterArea = startArea.shrink(mPadding);
}
void GridLayoutWidget::calculateResizeHandles() {
void DockLayoutWidget::calculateResizeHandles() {
RectF rec;
if (isSideVisible(LEFT)) {
@ -162,7 +162,7 @@ void GridLayoutWidget::calculateResizeHandles() {
}
}
void GridLayoutWidget::handleResizeEvents(const Events& events) {
void DockLayoutWidget::handleResizeEvents(const Events& events) {
for (auto& sideWidget : mSideWidgets) {
auto& sideSize = sideWidget.absoluteSize;
auto& resizeHandle = sideWidget.resizeHandle;
@ -225,11 +225,11 @@ void GridLayoutWidget::handleResizeEvents(const Events& events) {
}
}
void GridLayoutWidget::updateChildSideWidgets() {
void DockLayoutWidget::updateChildSideWidgets() {
// Update Child Widgets
{
for (ualni i = 0; i < 4; i++) {
if (!isSideExists(Side(i))) continue;
if (!sideExists(Side(i))) continue;
auto widget = mSideWidgets[i].widget;
if (!isSideVisible(Side(i))) {
@ -261,7 +261,7 @@ void GridLayoutWidget::updateChildSideWidgets() {
*/
}
void GridLayoutWidget::calculateHeaderAreas() {
void DockLayoutWidget::calculateHeaderAreas() {
for (ualni i = 0; i < 4; i++) {
if (!isSideVisible(Side(i))) continue;
auto& area = mSideWidgets[i].area;
@ -273,13 +273,13 @@ void GridLayoutWidget::calculateHeaderAreas() {
}
}
bool GridLayoutWidget::isSideVisible(GridLayoutWidget::Side side) {
return isSideExists(side) && !mSideWidgets[side].hidden;
bool DockLayoutWidget::isSideVisible(DockLayoutWidget::Side side) {
return sideExists(side) && !mSideWidgets[side].hidden;
}
bool GridLayoutWidget::isSideExists(GridLayoutWidget::Side side) { return mSideWidgets[side].widget; }
bool DockLayoutWidget::sideExists(DockLayoutWidget::Side side) { return mSideWidgets[side].widget; }
ualni GridLayoutWidget::getVisibleSidesSize() {
ualni DockLayoutWidget::getVisibleSidesSize() {
ualni out = 0;
for (ualni i = 0; i < 4; i++) {
if (isSideVisible(Side(i))) out++;
@ -287,9 +287,9 @@ ualni GridLayoutWidget::getVisibleSidesSize() {
return out;
}
GridLayoutWidget::Side GridLayoutWidget::getPreviewSide() { return mPreviewSide; }
DockLayoutWidget::Side DockLayoutWidget::getPreviewSide() { return mPreviewSide; }
void GridLayoutWidget::handlePreview(const Events& events) {
void DockLayoutWidget::handlePreview(const Events& events) {
if (!mPreview) {
mPreviewSide = NONE;
return;

View file

@ -20,7 +20,7 @@ void WorkspaceWidget::eventProcess(const Events& events) {
if (widget->mDropped) {
auto side = mDockSpace.getPreviewSide();
if (side != GridLayoutWidget::NONE) {
if (side != DockLayoutWidget::NONE) {
mFloatingLayer.mChildWidgets.removeNode(mFloatingLayer.mChildWidgets.find(widget));
widget->setCollapsed(false);
widget->stopFloating();
@ -52,13 +52,13 @@ void WorkspaceWidget::eventProcess(const Events& events) {
// toggle hide state
if (events.isDown(InputID::LEFT_ALT)) {
if (events.isPressed(InputID::N1)) {
mDockSpace.toggleHiddenState(GridLayoutWidget::LEFT);
mDockSpace.toggleHiddenState(DockLayoutWidget::LEFT);
} else if (events.isPressed(InputID::N2)) {
mDockSpace.toggleHiddenState(GridLayoutWidget::TOP);
mDockSpace.toggleHiddenState(DockLayoutWidget::TOP);
} else if (events.isPressed(InputID::N3)) {
mDockSpace.toggleHiddenState(GridLayoutWidget::RIGHT);
mDockSpace.toggleHiddenState(DockLayoutWidget::RIGHT);
} else if (events.isPressed(InputID::N4)) {
mDockSpace.toggleHiddenState(GridLayoutWidget::BOTTOM);
mDockSpace.toggleHiddenState(DockLayoutWidget::BOTTOM);
}
}

View file

@ -1,7 +1,7 @@
#include "WidgetBase.hpp"
namespace tp {
class GridLayoutWidget : public Widget {
class DockLayoutWidget : public Widget {
public:
enum Side { LEFT, TOP, RIGHT, BOTTOM, NONE };
@ -30,7 +30,7 @@ namespace tp {
};
public:
GridLayoutWidget();
DockLayoutWidget();
void eventProcess(const Events& events) override;
void eventDraw(Canvas& canvas) override;
@ -54,7 +54,7 @@ namespace tp {
void calculateHeaderAreas();
bool isSideVisible(Side side);
bool isSideExists(Side side);
bool sideExists(DockLayoutWidget::Side side);
ualni getVisibleSidesSize();
void handlePreview(const Events& events);

View file

@ -10,7 +10,7 @@ namespace tp {
void eventProcess(const Events& events) override;
protected:
GridLayoutWidget mDockSpace;
DockLayoutWidget mDockSpace;
FloatingLayoutWidget mFloatingLayer;
// Parameters

View file

@ -4,17 +4,27 @@
#include "RootWidget.hpp"
#include "AnimationTestWidget.hpp"
#include "FloatingWidget.hpp"
#include "SimpleWidgets.hpp"
#include "LayoutWidget.hpp"
#include "DockLayoutWidget.hpp"
using namespace tp;
class WidgetApplication : public Application {
public:
WidgetApplication() {
mRootWidget.setRootWidget(&mLayoutWidget);
mRootWidget.setRootWidget(&mDockLayout);
mDockLayout.addChild(&mFloatingMenu);
mDockLayout.addChild(&mFloatingMenu2);
mDockLayout.setCenterWidget(&mButton3);
mDockLayout.dockWidget(&mButton4, DockLayoutWidget::RIGHT);
mFloatingMenu.addToMenu(&mButton);
mFloatingMenu2.addToMenu(&mButton2);
mButton.setAction([this]() { mButton2.setColor(RGBA::random()); });
mButton2.setAction([this]() { mButton.setColor(RGBA::random()); });
mLayoutWidget.addChild(&mFloatingMenu);
// mLayoutWidget.addChild(&mButton2);
// mWidgetFloating.addChild(&mButton);
@ -24,8 +34,7 @@ public:
// mAnimationTestWidget.addChild(&mWidgetFloating);
// mAnimationTestWidget.addChild(&mLayoutWidget);
// RootWidget::setWidgetArea(mButton, { 100, 100, 150, 200 });
RootWidget::setWidgetArea(mFloatingMenu2, { 300, 100, 150, 200 });
RootWidget::setWidgetArea(mFloatingMenu, { 100, 100, 150, 200 });
// mWidgetFloating.addChild(&mLayoutWidget);
@ -50,13 +59,20 @@ public:
}
private:
LabelWidget mLabel;
ButtonWidget mButton;
ButtonWidget mButton2;
ButtonWidget mButton3;
ButtonWidget mButton4;
FloatingMenu mFloatingMenu;
FloatingMenu mFloatingMenu2;
LabelWidget mLabel;
FloatingWidget mWidgetFloating;
AnimationTestWidget mAnimationTestWidget;
DesktopLayout mLayoutWidget;
FloatingMenu mFloatingMenu;
DockLayoutWidget mDockLayout;
RootWidget mRootWidget;
};

View file

@ -0,0 +1,324 @@
#include "DockLayoutWidget.hpp"
using namespace tp;
DockLayoutWidget::DockLayoutWidget() {
mSideWidgets[0].side = LEFT;
mSideWidgets[1].side = TOP;
mSideWidgets[2].side = RIGHT;
mSideWidgets[3].side = BOTTOM;
}
void DockLayoutWidget::process(const EventHandler& events) {
// calculateHeaderAreas();
// handlePreview(events);
handleResizeEvents(events);
calculateSideAreas();
calculateResizeHandles();
updateChildSideWidgets();
}
void DockLayoutWidget::draw(Canvas& canvas) {
canvas.rect(getRelativeArea(), mBackgroundColor, 0);
}
void DockLayoutWidget::drawOverlay(Canvas& canvas) {
for (auto& sideWidget : mSideWidgets) {
if (!isSideVisible(sideWidget.side)) continue;
auto& handle = sideWidget.resizeHandle;
if (handle.active) {
canvas.rect(handle.area.shrink(mPadding / 1.5f), mResizeHandleColorActive, 0);
} else if (handle.hover) {
canvas.rect(handle.area.shrink(mPadding / 1.5f), mResizeHandleColorHovered, 0);
}
}
for (auto& sideWidget : mSideWidgets) {
if (!isSideVisible(sideWidget.side)) continue;
canvas.rect(sideWidget.headerArea, mResizeHandleColorActive, 0);
}
if (mPreview) {
if (mPreviewSide != NONE) canvas.rect(mPreviewArea.shrink(mPadding * 2), mPreviewColor, mRounding);
for (auto& sideWidget : mSideWidgets) {
if (sideWidget.widget) continue;
canvas.rect(sideWidget.previewHandleArea, mPreviewColor, mRounding);
}
}
}
bool DockLayoutWidget::propagateEventsToChildren() const {
return !mResizing;
}
bool DockLayoutWidget::needsNextFrame() const {
return Widget::needsNextFrame() || mResizing;
}
void DockLayoutWidget::setCenterWidget(Widget* widget) {
mCenterWidget = widget;
addChild(widget);
}
void DockLayoutWidget::dockWidget(Widget* widget, Side side) {
DEBUG_ASSERT(!sideExists(side))
addChild(widget);
auto& sideWidget = mSideWidgets[side];
sideWidget.widget = widget;
for (auto& order : mSideWidgets) {
if (order.order == -1) {
order.order = side;
break;
}
}
sideWidget.hidden = false;
}
void DockLayoutWidget::undockWidget(Side side) {
DEBUG_ASSERT(sideExists(side))
bool removed = false;
for (ualni i = 0; i < 3; i++) {
if (mSideWidgets[i].order == side) {
removed = true;
}
if (removed) {
swap(mSideWidgets[i].order, mSideWidgets[i + 1].order);
}
}
mSideWidgets[3].order = -1;
mSideWidgets[side].widget = nullptr;
}
void DockLayoutWidget::toggleWidgetVisibility(Side side) {
DEBUG_ASSERT(sideExists(side))
mSideWidgets[side].hidden = !mSideWidgets[side].hidden;
}
void DockLayoutWidget::calculateSideAreas() {
auto startArea = getRelativeArea();
for (auto& sideWidget : mSideWidgets) {
const auto side = sideWidget.order;
if (side == -1) break;
if (!isSideVisible(Side(side))) continue;
bool vertical = side == TOP || side == BOTTOM;
bool opposite = side == BOTTOM || side == RIGHT;
auto& sideSize = mSideWidgets[side].absoluteSize;
auto factor = sideSize / startArea.size[vertical];
if (opposite) factor = factor * -1 + 1;
auto& area = mSideWidgets[side].area;
if (!vertical) {
const auto first = startArea.splitByFactorHL(factor);
const auto second = startArea.splitByFactorHR(factor);
area = side == LEFT ? first : second;
startArea = side == LEFT ? second : first;
} else {
const auto first = startArea.splitByFactorVT(factor);
const auto second = startArea.splitByFactorVB(factor);
area = side == TOP ? first : second;
startArea = side == TOP ? second : first;
}
area = area.shrink(mPadding);
}
mCenterArea = startArea.shrink(mPadding);
}
void DockLayoutWidget::calculateResizeHandles() {
RectF rec;
RectF area = getRelativeArea();
for (auto& sideWidget : mSideWidgets) {
auto& sideSize = sideWidget.absoluteSize;
auto& resizeHandle = sideWidget.resizeHandle;
if (resizeHandle.end < mSideSizePadding * 2) {
sideSize = resizeHandle.end / 2.f;
} else {
sideSize = clamp(sideSize, resizeHandle.start + mSideSizePadding, resizeHandle.end - mSideSizePadding);
}
}
if (isSideVisible(LEFT)) {
auto& side = mSideWidgets[LEFT];
rec = { side.area.p4(), { mPadding * 2, side.area.size.y } };
side.resizeHandle.area = rec;
side.resizeHandle.start = 0;
side.resizeHandle.end = mCenterArea.p3().x;
}
if (isSideVisible(RIGHT)) {
auto& side = mSideWidgets[RIGHT];
rec = { side.area.p1(), { mPadding * 2, side.area.size.y } };
rec.x -= mPadding * 2;
side.resizeHandle.area = rec;
side.resizeHandle.start = 0;
side.resizeHandle.end = (area.p3() - mCenterArea.p1()).x;
}
if (isSideVisible(TOP)) {
auto& side = mSideWidgets[TOP];
rec = { side.area.p2(), { side.area.size.x, mPadding * 2 } };
side.resizeHandle.area = rec;
side.resizeHandle.start = 0;
side.resizeHandle.end = mCenterArea.p2().y;
}
if (isSideVisible(BOTTOM)) {
auto& side = mSideWidgets[BOTTOM];
rec = { side.area.p1(), { side.area.size.x, mPadding * 2 } };
rec.y -= mPadding * 2;
side.resizeHandle.area = rec;
side.resizeHandle.start = 0;
side.resizeHandle.end = (area.p3() - mCenterArea.p1()).y;
}
}
void DockLayoutWidget::handleResizeEvents(const EventHandler& events) {
auto pointer = events.getPointer();
auto pointerDelta = events.getPointerDelta();
// check triggers
if (events.isReleased(InputID::MOUSE1)) {
for (auto& sideWidget : mSideWidgets) {
sideWidget.resizeHandle.active = false;
}
mResizing = false;
} else {
for (auto& sideWidget : mSideWidgets) {
sideWidget.resizeHandle.hover = sideWidget.resizeHandle.area.isInside(pointer);
if (sideWidget.resizeHandle.hover && events.isPressed(InputID::MOUSE1)) {
sideWidget.resizeHandle.active = true;
mResizing = true;
triggerWidgetUpdate();
}
}
}
// do the resizing
for (auto& sideWidget : mSideWidgets) {
if (!sideWidget.resizeHandle.active) continue;
halnf delta = pointerDelta[(sideWidget.side == TOP || sideWidget.side == BOTTOM)];
if (sideWidget.side == BOTTOM || sideWidget.side == RIGHT) {
delta *= -1;
}
sideWidget.absoluteSize += delta;
}
}
void DockLayoutWidget::updateChildSideWidgets() {
// Update Child Widgets
{
for (ualni i = 0; i < 4; i++) {
if (!sideExists(Side(i))) continue;
auto widget = mSideWidgets[i].widget;
if (!isSideVisible(Side(i))) {
// widget->mEnable = false;
} else {
widget->setArea(mSideWidgets[i].area);
// widget->mEnable = true;
}
}
if (mCenterWidget) mCenterWidget->setArea(mCenterArea);
}
// update depth order
/*
if (mChildWidgets.size() > 1) {
for (auto sideChild : mSideWidgets) {
if (!sideChild) continue;
for (auto childNode = mChildWidgets.firstNode(); childNode; childNode = childNode->next) {
if (childNode->data == sideChild) {
mChildWidgets.detach(childNode);
mChildWidgets.pushBack(childNode);
break;
}
}
}
}
*/
}
void DockLayoutWidget::calculateHeaderAreas() {
for (ualni i = 0; i < 4; i++) {
if (!isSideVisible(Side(i))) continue;
auto& area = mSideWidgets[i].area;
const auto factor = mHeaderSize / area.size.y;
mSideWidgets[i].headerArea = area.splitByFactorVT(factor);
area = area.splitByFactorVB(factor);
mSideWidgets[i].headerArea.size.y -= mPadding;
}
}
bool DockLayoutWidget::isSideVisible(DockLayoutWidget::Side side) {
return sideExists(side) && !mSideWidgets[side].hidden;
}
bool DockLayoutWidget::sideExists(DockLayoutWidget::Side side) { return mSideWidgets[side].widget; }
ualni DockLayoutWidget::getVisibleSidesSize() {
ualni out = 0;
for (ualni i = 0; i < 4; i++) {
if (isSideVisible(Side(i))) out++;
}
return out;
}
void DockLayoutWidget::handlePreview(const EventHandler& events) {
if (!mPreview) {
mPreviewSide = NONE;
return;
}
const halnf factor = 0.3;
const halnf handleFactor = 0.1;
const auto handleSize = min(mCenterArea.size.x, mCenterArea.size.y) * handleFactor;
for (auto& sideWidget : mSideWidgets) {
if (sideWidget.widget) continue;
switch (sideWidget.side) {
case TOP: sideWidget.area = mCenterArea.splitByFactorVT(factor); break;
case BOTTOM: sideWidget.area = mCenterArea.splitByFactorVB(1 - factor); break;
case LEFT: sideWidget.area = mCenterArea.splitByFactorHL(factor); break;
case RIGHT: sideWidget.area = mCenterArea.splitByFactorHR(1 - factor); break;
default: break;
}
sideWidget.area = sideWidget.area.shrink(mPadding * 2);
sideWidget.previewHandleArea = sideWidget.area.getSizedFromCenter({ handleSize, handleSize });
}
mPreviewArea = {};
mPreviewSide = NONE;
for (auto& sideWidget : mSideWidgets) {
if (sideWidget.widget) continue;
if (sideWidget.previewHandleArea.isInside(events.getPointer())) {
mPreviewArea = sideWidget.area;
mPreviewSide = sideWidget.side;
}
}
}

View file

@ -10,6 +10,8 @@ void FloatingWidget::process(const EventHandler& events) {
if (inside && events.isPressed(InputID::MOUSE1)) {
mPointerStart = relativePointer;
mIsFloating = true;
bringToFront();
}
if (mIsFloating && resizeHandleRect().isInside(relativePointer)) {

View file

@ -19,23 +19,26 @@ void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) {
// construct hierarchy tree of widgets to process
updateTreeToProcess();
auto root = mWidgetsToProcess.find(mRoot) == mWidgetsToProcess.end() ? nullptr : &mWidgetsToProcess[mRoot];
// update active tree animations
updateAnimations(&mWidgetsToProcess[mRoot]);
updateAnimations(root);
// update all events and call all event processing callbacks
EventHandler dummyEvents;
processFocusItems(*events);
processActiveTree(&mWidgetsToProcess[mRoot], dummyEvents);
processActiveTree(root, dummyEvents);
// update widget sizes base on individual size policies
adjustSizes(&mWidgetsToProcess[mRoot]);
adjustSizes(root);
// trigger some widgets by moise pointer
handleFocusChanges(*events);
// check triggered widgets for removal
erase_if(mTriggeredWidgets, [](auto widget) {
// check triggered widgets for removal
erase_if(mTriggeredWidgets, [this](auto widget) {
if (mWidgetsToProcess.find(widget) == mWidgetsToProcess.end()) return false;
auto end = !widget->needsNextFrame();
if (end) {
widget->endAnimations();
@ -53,8 +56,10 @@ void RootWidget::drawFrame(Canvas& canvas) {
// draw all tree from top to bottom
drawRecursion(canvas, mRoot, { 0, 0 });
ImGui::Checkbox("Draw debug", &mDebug);
if (mDebug) {
drawDebug(canvas, mRoot, { 0, 0 });
drawDebug(canvas, mRoot, { 0, 0 }, 0);
ImGui::Text("Triggered Widgets: %i", (int) mTriggeredWidgets.size());
ImGui::Text("Widgets To Process: %i", (int) mWidgetsToProcess.size());
}
@ -80,8 +85,8 @@ void RootWidget::drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos)
active->draw(canvas);
for (auto child : active->mChildren) {
drawRecursion(canvas, child, pos + child->getArea().pos);
for (auto child = active->mDepthOrder.lastNode(); child; child = child->prev) {
drawRecursion(canvas, child->data, pos + child->data->getArea().pos);
}
canvas.setOrigin(pos);
@ -90,14 +95,13 @@ void RootWidget::drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos)
active->drawOverlay(canvas);
}
void RootWidget::drawDebug(Canvas& canvas, Widget* active, const Vec2F& pos) {
void RootWidget::drawDebug(Canvas& canvas, Widget* active, const Vec2F& pos, int depthOrder) {
auto area = RectF{ pos, active->getArea().size };
if (mWidgetsToProcess.find(active) != mWidgetsToProcess.end()) {
RGBA color = { 1, 0, 0, 1};
canvas.frame(area, color);
canvas.text(active->mName.c_str(), area, 12, Canvas::Align::LC, 2, color);
canvas.text((active->mName + ":" + std::to_string(depthOrder)).c_str(), area, 22, Canvas::Align::LC, 2, color);
}
if (active->mInFocus) {
@ -107,8 +111,9 @@ void RootWidget::drawDebug(Canvas& canvas, Widget* active, const Vec2F& pos) {
canvas.circle(active->mGlobalPoint, 10, active->mDebugColor);
}
for (auto child : active->mChildren) {
drawDebug(canvas, child, pos + child->getArea().pos);
int orderIdx = 0;
for (auto child = active->mDepthOrder.lastNode(); child; child = child->prev) {
drawDebug(canvas, child->data, pos + child->data->getArea().pos, orderIdx++);
}
}
@ -126,40 +131,61 @@ void RootWidget::updateAnimations(ActiveTreeNode* iter) {
}
void RootWidget::getWidgetPath(Widget* widget, std::vector<Widget*>& out) {
if (!widget) return;
for (auto iter = widget; iter && iter != this; iter = iter->mParent) {
out.push_back(iter);
}
for (auto i = 0; i < out.size() / 2; i++) {
swap(out[i], out[out.size() - i - 1]);
}
}
void RootWidget::handleFocusChanges(EventHandler& events) {
auto prevFocus = mInFocusWidget;
findFocusWidget(mRoot, &mInFocusWidget, events.getPointer());
if (prevFocus) {
std::vector<Widget*> path1;
std::vector<Widget*> path2;
if (mInFocusWidget) updateWidget(mInFocusWidget);
getWidgetPath(prevFocus, path1);
getWidgetPath(mInFocusWidget, path2);
size_t iter = 0;
while (path1[iter] == path2[iter]) {
iter++;
std::vector<Widget*> path2;
getWidgetPath(mInFocusWidget, path2);
size_t propLen2 = path2.size();
for (auto i = 0; i < path2.size(); i++) {
if (!path2[i]->propagateEventsToChildren()) {
propLen2 = i + 1;
break;
}
}
for (auto i = iter; i < path1.size(); i++) {
path1[i]->mouseLeave();
std::vector<Widget*> path1;
getWidgetPath(prevFocus, path1);
size_t propLen1 = path1.size();
for (auto i = 0; i < path1.size(); i++) {
if (!path1[i]->propagateEventsToChildren()) {
propLen1 = i + 1;
break;
}
}
for (auto i = iter; i < path2.size(); i++) {
path2[i]->mouseEnter();
}
size_t mostCommonIdx = 0;
} else {
for (auto iter = mInFocusWidget; iter; iter = iter->mParent) {
iter->mouseEnter();
if (!(path1.empty() || path2.empty())) {
while (path1[mostCommonIdx] == path2[mostCommonIdx]) {
mostCommonIdx++;
}
}
for (auto i = 0; i < path1.size(); i++) {
path1[i]->mInFocus = false;
if (i >= mostCommonIdx && i < propLen1) path1[i]->mouseLeave();
}
for (auto i = 0; i < path2.size(); i++) {
path2[i]->mInFocus = true;
if (i >= mostCommonIdx && i < propLen2) path2[i]->mouseEnter();
}
}
void RootWidget::findFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer) {
@ -167,8 +193,8 @@ void RootWidget::findFocusWidget(Widget* iter, Widget** focus, const Vec2F& poin
*focus = iter;
for (auto child : iter->mChildren) {
findFocusWidget(child, focus, pointer - iter->mArea.getTargetRect().pos);
for (auto child = iter->mDepthOrder.lastNode(); child; child = child->prev) {
findFocusWidget(child->data, focus, pointer - iter->mArea.getTargetRect().pos);
}
}
@ -208,10 +234,6 @@ void RootWidget::processFocusItems(EventHandler& events) {
std::vector<Widget*> path;
getWidgetPath(mInFocusWidget, path);
for (auto i = 0; i < path.size() / 2; i++) {
swap(path[i], path[path.size() - i - 1]);
}
size_t len = path.size();
for (auto i = 0; i < len; i++) {
if (!path[i]->propagateEventsToChildren()) {

View file

@ -30,6 +30,12 @@ void ButtonWidget::setAction(const std::function<void()>& action) {
mAction = action;
}
void ButtonWidget::setColor(const RGBA& in) {
mColor = in;
mColorAnimated.setTargetColor(mColor);
triggerWidgetUpdate();
}
void ButtonWidget::process(const EventHandler& eventHandler) {
if (getArea().isInside(eventHandler.getPointer())) {
if (eventHandler.isPressed(InputID::MOUSE1)) {

View file

@ -62,6 +62,24 @@ void Widget::adjustRect() {
}
}
void Widget::bringToFront() {
if (!mParent) return;
auto& order = mParent->mDepthOrder;
auto node = order.find(this);
DEBUG_ASSERT(node);
order.detach(node);
order.pushFront(node);
}
void Widget::bringToBack() {
if (!mParent) return;
auto& order = mParent->mDepthOrder;
auto node = order.find(this);
DEBUG_ASSERT(node);
order.detach(node);
order.pushBack(node);
}
void Widget::adjustChildrenRect() {
auto area = RectF({}, getArea().size);
for (auto widget : mChildren) {
@ -71,7 +89,6 @@ void Widget::adjustChildrenRect() {
void Widget::mouseEnter() {
mInFocus = true;
triggerWidgetUpdate();
}
void Widget::mouseLeave() {
@ -80,4 +97,14 @@ void Widget::mouseLeave() {
bool Widget::propagateEventsToChildren() const {
return true;
}
}
void Widget::addChild(Widget* child) {
mChildren.push_back(child);
mDepthOrder.pushBack(child);
child->mParent = this;
triggerWidgetUpdate();
child->triggerWidgetUpdate();
}

View file

@ -0,0 +1,94 @@
#include "Widget.hpp"
namespace tp {
class DockLayoutWidget : public Widget {
public:
enum Side { LEFT, TOP, RIGHT, BOTTOM, NONE };
private:
struct ResizeHandle {
RectF area{ 0, 0, 0, 0 };
halnf start{ 0 };
halnf end{ 0 };
bool active = false;
bool hover = false;
};
struct SideWidgetData {
Widget* widget = nullptr;
bool hidden = false;
halnf absoluteSize = 200;
alni order = -1;
Side side = { TOP };
RectF area = {};
RectF headerArea = {};
RectF previewHandleArea = {};
ResizeHandle resizeHandle;
};
public:
DockLayoutWidget();
public:
void adjustRect() override {}
void adjustChildrenRect() override {}
void process(const EventHandler& events) override;
void draw(Canvas& canvas) override;
void drawOverlay(Canvas& canvas) override;
[[nodiscard]] bool propagateEventsToChildren() const override;
[[nodiscard]] bool needsNextFrame() const override;
public:
void setCenterWidget(Widget* widget);
void dockWidget(Widget* widget, Side side);
void undockWidget(Side side);
void toggleWidgetVisibility(Side side);
private:
void calculateSideAreas();
void calculateResizeHandles();
void handleResizeEvents(const EventHandler& events);
void updateChildSideWidgets();
void calculateHeaderAreas();
bool isSideVisible(Side side);
bool sideExists(DockLayoutWidget::Side side);
ualni getVisibleSidesSize();
void handlePreview(const EventHandler& events);
private:
RectF mPreviewArea = {};
Side mPreviewSide = NONE;
int resizeType[2] = { 0, 0 };
SideWidgetData mSideWidgets[4];
private:
bool mResizing = false;
Widget* mCenterWidget = nullptr;
RectF mCenterArea {};
// Parameters
halnf mSideSizePadding = 150.f;
halnf mPadding = 4;
halnf mHeaderSize = 27;
halnf mRounding = 10;
Vec2F mPreviewHandleSize = { 50, 50 };
RGBA mResizeHandleColorHovered = RGBA(0.3, 0.3, 0.3, 1);
RGBA mResizeHandleColorActive = RGBA(0.6, 0.6, 0.6, 1);
RGBA mBackgroundColor = RGBA(0, 0, 0, 1);
RGBA mPreviewColor = RGBA(0.6, 0.6, 0.6, 1);
public:
bool mPreview = false;
};
}

View file

@ -18,7 +18,7 @@ namespace tp {
RectF resizeHandleRect();
bool propagateEventsToChildren() const override;
[[nodiscard]] bool propagateEventsToChildren() const override;
private:
bool mIsFloating = false;
@ -41,8 +41,6 @@ namespace tp {
mMenuLayout.addChild(&mHeader);
mMenuLayout.addChild(&mBodyLayout);
addToMenu(&mTestButton);
mHeader.setText("Menu");
}

View file

@ -33,7 +33,7 @@ namespace tp {
void updateTreeToProcess();
void updateAnimations(ActiveTreeNode* iter);
void drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos);
void drawDebug(Canvas& canvas, Widget* active, const Vec2F& pos);
void drawDebug(Canvas& canvas, Widget* active, const Vec2F& pos, int depthOrder);
void findFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer);
void handleFocusChanges(EventHandler& events);
void getWidgetPath(Widget* widget, std::vector<Widget*>& out);
@ -50,6 +50,6 @@ namespace tp {
std::map<Widget*, ActiveTreeNode> mWidgetsToProcess;
Widget* mInFocusWidget = nullptr;
bool mDebug = false;
bool mDebug = true;
};
}

View file

@ -45,6 +45,8 @@ namespace tp {
void endAnimations() override;
void updateAnimations() override;
void setColor(const RGBA& in);
private:
std::function<void()> mAction;

View file

@ -22,18 +22,15 @@ namespace tp {
mDebugColor = col;
}
void addChild(Widget* child) {
mChildren.push_back(child);
child->mParent = this;
triggerWidgetUpdate();
child->triggerWidgetUpdate();
}
void addChild(Widget* child);
WidgetManagerInterface* getRoot();
void triggerWidgetUpdate();
void bringToFront();
void bringToBack();
public:
virtual void process(const EventHandler& events) {}
virtual void draw(Canvas& canvas) { canvas.rect(mArea.getCurrentRect(), RGBA(1.f), 10); }
@ -63,12 +60,11 @@ namespace tp {
Widget* mParent = nullptr;
std::vector<Widget*> mChildren;
List<Widget*> mDepthOrder;
// relative to the parent
SpringRect mArea;
ualni mZValue = 0;
bool mInFocus = false;
// debug