Reorganize code in widgets

This commit is contained in:
IlyaShurupov 2024-10-14 16:41:13 +03:00 committed by Ilya Shurupov
parent 837d8dc507
commit e00dec67ba
31 changed files with 652 additions and 481 deletions

View file

@ -26,18 +26,18 @@ namespace tp {
virtual void pickRect() = 0;
virtual void clampRect() = 0;
virtual void adjustChildrenRect() = 0;
virtual RectF getAvailableChildArea() const;
[[nodiscard]] virtual RectF getAvailableChildArea() const;
public:
const Vec2F& getMinSize();
void setMinSize(const Vec2F& size);
const Vec2<SizePolicy>& getSizePolicy() const;
[[nodiscard]] const Vec2<SizePolicy>& getSizePolicy() const;
void setSizePolicy(SizePolicy x, SizePolicy y);
public:
[[nodiscard]] const RectF& getArea() const;
RectF getAnimatedArea() const;
[[nodiscard]] RectF getAnimatedArea() const;
void setArea(const RectF& area);
[[nodiscard]] Widget* parent() const;
@ -60,30 +60,4 @@ namespace tp {
Vec2F mMinSize = { 50, 50 };
Vec2F mMaxSize = { FLT_MAX / 2, FLT_MAX / 2 };
};
class BasicLayout : public WidgetLayout {
public:
explicit BasicLayout(Widget* widget) : WidgetLayout(widget) {}
void pickRect() override;
void clampRect() override;
void adjustChildrenRect() override;
RectF getAvailableChildArea() const override;
private:
void adjustLayout(bool vertical);
static halnf changeChildSize(Widget*, halnf diff, bool vertical);
private:
LayoutPolicy mLayoutPolicy = LayoutPolicy::Vertical;
halnf mLayoutGap = 5;
halnf mLayoutMargin = 10;
};
class LayoutManager {
public:
LayoutManager() = default;
void adjust(Widget* root);
};
}

View file

@ -2,54 +2,41 @@
#include "Widget.hpp"
#include <map>
#include <vector>
#include <set>
#include "UpdateManager.hpp"
#include "LayoutManager.hpp"
#include "DebugManager.hpp"
namespace tp {
class RootWidget : public WidgetManagerInterface {
friend DebugManager;
public:
RootWidget() { setDebug("root", RGBA(1)); }
RootWidget();
// User Interface
public:
void setRootWidget(Widget* widget);
void updateWidget(Widget*, const char* reason = nullptr) override;
static void setWidgetArea(Widget& widget, const RectF& rect);
// Graphic Application Interface
public:
void processFrame(EventHandler* events, const RectF& screenArea);
void drawFrame(Canvas& canvas);
[[nodiscard]] bool needsUpdate() const;
// Internals
private:
void updateTreeToProcess();
void updateAnimations();
void processLayout();
void drawRecursion(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);
void processActiveTree(Widget* iter, EventHandler& events, Vec2F pos);
void processFocusItems(EventHandler& events);
void updateWidget(Widget*, const char* reason = nullptr) override;
void updateAnimations();
void updateAreaCache(Widget* iter, bool read);
static void debugDrawWidget(Widget* widget);
private:
LayoutManager mLayoutManager;
RectF mScreenArea;
Widget* mRoot = nullptr;
// frame to frame changes
std::map<Widget*, bool> mTriggeredWidgets;
Widget* mInFocusWidget = nullptr;
bool mDebug = true;
bool mDebugStopProcessing = false;
bool mDebugRedrawAlways = false;
int mDebugWidgetsToProcess = 0;
LayoutManager mLayoutManager;
UpdateManager mUpdateManager;
DebugManager mDebugManager;
};
}

View file

@ -1,155 +0,0 @@
#pragma once
#include "Color.hpp"
#include "Rect.hpp"
#include "Timing.hpp"
namespace tp {
class SpringValue {
public:
SpringValue() = default;
void updateCurrentPosition() {
const auto deltaTime = halnf(gCurrentTime - mPrevTime);
const auto deltaPos = mTargetPosition - mCurrentPosition;
auto deltaVelocity = deltaPos * mSpringStiffness;
deltaVelocity -= mVelocity * (mVelocityDamping);
mVelocity += deltaVelocity;
mCurrentPosition += mVelocity * deltaTime;
mPrevTime = gCurrentTime;
}
void setTargetPosition(halnf pos) {
mTargetPosition = pos;
if (mVelocity == 0) mPrevTime = gCurrentTime;
}
[[nodiscard]] halnf getCurrentPos() const { return mCurrentPosition; }
[[nodiscard]] halnf getTargetPos() const { return mTargetPosition; }
[[nodiscard]] halnf getVelocity() const { return mVelocity; }
[[nodiscard]] halnf getDeltaPos() const { return mTargetPosition - mCurrentPosition; }
void endAnimation() {
mCurrentPosition = mTargetPosition;
mVelocity = 0;
}
private:
time_ms mPrevTime = 0;
halnf mTargetPosition = 0;
halnf mCurrentPosition = 0;
halnf mVelocity = 0;
halnf mSpringStiffness = 0.0041;
halnf mVelocityDamping = 0.25f;
};
class SpringVec {
public:
SpringVec() = default;
void updateCurrentPosition() {
mPosX.updateCurrentPosition();
mPosY.updateCurrentPosition();
}
void setTargetPosition(Vec2F pos) {
mPosX.setTargetPosition(pos.x);
mPosY.setTargetPosition(pos.y);
}
[[nodiscard]] Vec2F getCurrentPos() const {
return { mPosX.getCurrentPos(), mPosY.getCurrentPos() };
}
[[nodiscard]] Vec2F getTargetPos() const {
return { mPosX.getTargetPos(), mPosY.getTargetPos() };
}
[[nodiscard]] Vec2F getVelocity() const {
return { mPosX.getVelocity(), mPosY.getVelocity() };
}
[[nodiscard]] bool checkAnimationShouldEnd() const {
halnf velocityEpsilon = 0.0001f;
halnf positionEpsilon = 1;
const auto vel = abs(mPosY.getVelocity()) > velocityEpsilon || abs(mPosX.getVelocity()) > velocityEpsilon;
const auto pos = abs(mPosY.getDeltaPos()) > positionEpsilon || abs(mPosX.getDeltaPos()) > positionEpsilon;
return !(pos || vel);
}
void endAnimation() {
mPosX.endAnimation();
mPosY.endAnimation();
}
private:
SpringValue mPosY;
SpringValue mPosX;
};
class SpringRect {
public:
SpringRect() = default;
[[nodiscard]] RectF getTargetRect() const {
const auto start = mStartPos.getTargetPos();
const auto end = mEndPos.getTargetPos();
return { start, end - start };
}
[[nodiscard]] RectF getCurrentRect() const {
const auto start = mStartPos.getCurrentPos();
const auto end = mEndPos.getCurrentPos();
return { start, end - start };
}
[[nodiscard]] RGBA getCurrentColor() const {
const auto start = mStartPos.getCurrentPos();
const auto end = mEndPos.getCurrentPos();
return { start.x, start.y, end.x, end.y };
}
void setTargetRect(const RectF& rect) {
mStartPos.setTargetPosition(rect.p1());
mEndPos.setTargetPosition(rect.p3());
}
void setTargetColor(const RGBA& color) {
mStartPos.setTargetPosition({ color.r, color.g });
mEndPos.setTargetPosition({ color.b, color.a });
}
SpringVec& getStart() {
return mStartPos;
}
SpringVec& getEnd() {
return mEndPos;
}
void updateCurrentRect() {
mStartPos.updateCurrentPosition();
mEndPos.updateCurrentPosition();
}
[[nodiscard]] bool shouldEndTransition() const {
return mStartPos.checkAnimationShouldEnd() && mEndPos.checkAnimationShouldEnd();
}
void endAnimation() {
mStartPos.endAnimation();
mEndPos.endAnimation();
}
private:
SpringVec mStartPos;
SpringVec mEndPos;
};
}

View file

@ -1,7 +1,8 @@
#pragma once
#include "SpringAnimations.hpp"
#include "LayoutManager.hpp"
#include "Layout.hpp"
#include "EventHandler.hpp"
#include "Graphics.hpp"
@ -10,14 +11,32 @@
#include <functional>
namespace tp {
class WidgetManagerInterface;
class WidgetLayout;
class LayoutManager;
class UpdateManager;
class DebugManager;
class WidgetManagerInterface;
class RootWidget;
class Widget {
friend RootWidget;
friend LayoutManager;
friend UpdateManager;
friend DebugManager;
using BitField = Bits<ualni>;
using DFSAction = std::function<void(Widget*)>;
enum Flags : int1 {
ENABLED = 0,
NEEDS_UPDATE,
IN_FOCUS,
TRIGGERED,
};
public:
Widget();
@ -30,10 +49,12 @@ namespace tp {
void bringToBack();
void setLayout(WidgetLayout* layout);
void setSizePolicy(SizePolicy x, SizePolicy y) { getLayout()->setSizePolicy(x, y); }
void setSizePolicy(SizePolicy x, SizePolicy y);
void setEnabled(bool val) { mFlags.set(ENABLED, val); }
WidgetLayout* getLayout();
const WidgetLayout* getLayout() const;
[[nodiscard]] const WidgetLayout* getLayout() const;
void triggerWidgetUpdate(const char* reason = nullptr);
@ -58,20 +79,19 @@ namespace tp {
public:
[[nodiscard]] RectF getArea() const;
[[nodiscard]] RectF getAreaT() const;
// [[nodiscard]] const RectF& getAreaCache() const;
[[nodiscard]] RectF getRelativeArea() const;
[[nodiscard]] RectF getRelativeAreaT() const;
// [[nodiscard]] RectF getRelativeAreaCache() const;
void setArea(const RectF& area);
void setAreaCache(const RectF& area);
private:
using DFSAction = std::function<void(Widget*)>;
[[nodiscard]] bool isUpdate() const { return mFlags.get(ENABLED) && mFlags.get(NEEDS_UPDATE); }
[[nodiscard]] bool isDraw() const { return mFlags.get(ENABLED); }
static void dfs(Widget* iter, const DFSAction& before, const DFSAction& after = [](auto){}) {
if (!iter->mNeedsProcessing) return;
if (!iter->isUpdate()) return;
before(iter);
@ -96,10 +116,7 @@ namespace tp {
WidgetLayout* mLayout = nullptr;
// TODO : make bitfield processing flags
bool mInFocus = false;
bool mNeedsProcessing = false;
bool mTriggered = false;
BitField mFlags;
// debug
struct {
@ -113,5 +130,6 @@ namespace tp {
struct WidgetManagerInterface : public Widget {
virtual void updateWidget(Widget*, const char* reason) = 0;
static WidgetLayout* defaultLayout(Widget* widget);
};
}

View file

@ -0,0 +1,25 @@
#pragma once
#include "Layout.hpp"
namespace tp {
class BasicLayout : public WidgetLayout {
public:
explicit BasicLayout(Widget* widget) : WidgetLayout(widget) {}
void pickRect() override;
void clampRect() override;
void adjustChildrenRect() override;
[[nodiscard]] RectF getAvailableChildArea() const override;
private:
void adjustLayout(bool vertical);
static halnf changeChildSize(Widget*, halnf diff, bool vertical);
private:
LayoutPolicy mLayoutPolicy = LayoutPolicy::Vertical;
halnf mLayoutGap = 5;
halnf mLayoutMargin = 10;
};
}

View file

@ -1,11 +1,11 @@
#pragma once
#include "LayoutManager.hpp"
#include "BasicLayout.hpp"
namespace tp {
class FloatingLayout : public BasicLayout {
public:
FloatingLayout(Widget* widget) :
explicit FloatingLayout(Widget* widget) :
BasicLayout(widget) {}
public:

View file

@ -0,0 +1,25 @@
#pragma once
#include "Widget.hpp"
namespace tp {
class DebugManager {
public:
DebugManager() = default;
bool isRedrawAlways() const { return mDebugRedrawAlways; }
bool update(RootWidget* rootWidget, EventHandler& events);
void drawDebug(RootWidget* rootWidget, Canvas& canvas);
private:
void recursiveDraw(Canvas& canvas, Widget* active, const Vec2F& pos, int depthOrder);
static void widgetMenu(Widget*);
private:
// debug
bool mDebug = true;
bool mDebugStopProcessing = false;
bool mDebugRedrawAlways = false;
};
}

View file

@ -0,0 +1,11 @@
#pragma once
#include "Layout.hpp"
namespace tp {
class LayoutManager {
public:
LayoutManager() = default;
void adjust(Widget* root);
};
}

View file

@ -0,0 +1,38 @@
#pragma once
#include "Widget.hpp"
#include <map>
namespace tp {
class UpdateManager {
friend DebugManager;
public:
UpdateManager() = default;
void scheduleUpdate(Widget* widget, const char* reason);
[[nodiscard]] bool isPendingUpdates() const {
return !mTriggeredWidgets.empty();
}
void processWidgets(Widget* root, EventHandler& eventHandler);
void clean();
void updateTreeToProcess(Widget* root);
void handleFocusChanges(Widget* root, EventHandler& events);
void findFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer);
static void getWidgetPath(Widget* widget, std::vector<Widget*>& out);
void processActiveTree(Widget* iter, EventHandler& events, Vec2F pos);
void processFocusItems(EventHandler& events);
private:
std::map<Widget*, bool> mTriggeredWidgets;
Widget* mInFocusWidget = nullptr;
private:
int mDebugWidgetsToProcess = 0;
};
}

View file

@ -6,7 +6,7 @@
namespace tp {
class FloatingWidget : public Widget {
public:
FloatingWidget() {
FloatingWidget() : Widget() {
setDebug("float", { 0.0, 0.9, 0.1, 1 });
setLayout(new FloatingLayout(this));
}
@ -30,7 +30,7 @@ namespace tp {
class FloatingMenu : public FloatingWidget {
public:
FloatingMenu() {
FloatingMenu() : FloatingWidget() {
setDebug("float menu", { 0.0, 0.9, 0.1, 0.7 });
// addChild(&mMenuLayout);

View file

@ -7,7 +7,7 @@
namespace tp {
class LabelWidget : public Widget {
public:
LabelWidget() {
LabelWidget() : Widget() {
setDebug("label", { 0.1, 0.1, 0.1, 0.1 });
}