WidgetsNew Initial

This commit is contained in:
IlyaShurupov 2024-07-17 09:44:18 +03:00
parent 450a816b2b
commit 49cfe70c0d
15 changed files with 741 additions and 0 deletions

View file

@ -0,0 +1,20 @@
#pragma once
#include "Widget.hpp"
namespace tp {
class AnimationTestWidget : public Widget {
public:
AnimationTestWidget() = default;
~AnimationTestWidget() override = default;
void draw(Canvas& canvas) override;
void process(const EventHandler& events) override;
[[nodiscard]] bool needUpdate() const override;
private:
SpringRect mTestSpring;
};
}

View file

@ -0,0 +1,22 @@
#pragma once
#include "Widget.hpp"
namespace tp {
class FloatingWidget : public Widget {
public:
FloatingWidget() = default;
void process(const EventHandler& events) override;
void updateArea(RectF& area) const override;
void draw(Canvas& canvas) override;
private:
bool mIsFloating = false;
Vec2F mPointerStart;
Vec2F mPointerCurrent;
};
}

View file

@ -0,0 +1,47 @@
#pragma once
#include "Widget.hpp"
namespace tp {
class RootWidget {
// path from leaf to root of widgets that need to be updated
struct ActivePath {
struct ActiveNode {
Widget* widget = nullptr;
Vec2F globalPos;
};
Buffer<ActiveNode> path;
};
public:
RootWidget() = default;
void setRootWidget(Widget* widget);
void processFrame(EventHandler* events, const RectF& screenArea);
void drawFrame(Canvas& canvas);
[[nodiscard]] bool needsUpdate() const;
static void setWidgetArea(Widget& widget, const RectF& rect);
private:
void updateActivePaths(Buffer<ActivePath>* activePaths, const Vec2F& pointer);
void drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos);
void processPaths(const Buffer<ActivePath>& path, EventHandler& events);
void clearProcessedFlag(const Buffer<ActivePath>& paths);
private:
Widget* mRoot = nullptr;
Buffer<Widget*> mActiveWidgets;
Buffer<ActivePath> mActivePaths[2]; // current and prev
bool mFlipFlop = false;
bool mNeedUpdate = false;
};
}

View file

@ -0,0 +1,51 @@
#pragma once
#include "Widget.hpp"
#include <functional>
namespace tp {
class LabelWidget : public Widget {
public:
LabelWidget() = default;
void setText(const std::string& text);
[[nodiscard]] const std::string& getText() const;
void draw(Canvas& canvas) override;
[[nodiscard]] bool isPassThroughEvents() const override { return true; }
private:
std::string mText = "Text";
halnf mPadding = 5;
RGBA mColor = 1.f;
halnf mSize = 17.f;
};
class ButtonWidget : public LabelWidget {
public:
ButtonWidget();
void setAction(const std::function<void()>& action);
void process(const EventHandler& eventHandler) override;
void draw(Canvas& canvas) override;
[[nodiscard]] bool isPassThroughEvents() const override { return false; }
[[nodiscard]] bool needUpdate() const override;
void finishAnimations() override;
private:
std::function<void()> mAction;
SpringRect mColorAnimated;
halnf mRounding = 5;
RGBA mColor = { 1.0f, 0.0f, 0.0f, 1.f };
RGBA mColorHovered = { 0.0f, 0.0f, 0.0f, 1.f };
};
}

View file

@ -0,0 +1,155 @@
#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 checkAnimationShouldEnd() const {
return mStartPos.checkAnimationShouldEnd() && mEndPos.checkAnimationShouldEnd();
}
void endAnimation() {
mStartPos.endAnimation();
mEndPos.endAnimation();
}
private:
SpringVec mStartPos;
SpringVec mEndPos;
};
}

View file

@ -0,0 +1,53 @@
#pragma once
#include "SpringAnimations.hpp"
#include "EventHandler.hpp"
#include "Graphics.hpp"
namespace tp {
class Widget {
friend class RootWidget;
public:
Widget();
virtual ~Widget() = default;
virtual void process(const EventHandler& events) {}
virtual void updateArea(RectF& area) const {}
virtual void draw(Canvas& canvas);
virtual void drawOverlay(Canvas& canvas) {}
virtual bool isPassThroughEvents() const { return false; }
[[nodiscard]] virtual bool needUpdate() const;
virtual void finishAnimations();
public:
[[nodiscard]] RectF getRelativeArea() const;
void setActive(bool active);
void addChild(Widget* widget);
private:
[[nodiscard]] RectF getArea() const;
void setArea(const RectF& area);
void removeChild(Widget* widget);
private:
// relative to the parent
SpringRect mArea;
// tree structure of widgets
List<Widget*> mChildWidgets;
// if needed updates even though pointer is not in area
bool mIsActive = false;
Widget* mParent = nullptr;
bool mProcessedFlag = false;
};
}