Replace Old Widgets

This commit is contained in:
IlyaShurupov 2024-10-18 10:56:42 +03:00
parent cce8f0e1bc
commit fcd5eb2d2a
81 changed files with 388 additions and 278 deletions

View file

@ -1,57 +0,0 @@
#pragma once
#include "Color.hpp"
#include "Rect.hpp"
#include "Timing.hpp"
namespace tp {
class AnimValue {
halnf mValPrev = 0;
halnf mVal = 0;
time_ms mTimeStart = 0;
halni mTimeAnim = 250;
static bool gInTransition;
private:
[[nodiscard]] halnf interpolate() const;
public:
AnimValue();
explicit AnimValue(halnf val);
[[nodiscard]] halnf prev() const { return mValPrev; }
void set(halnf val);
void setNoTransition(halnf);
void setAnimTime(halni time);
[[nodiscard]] halnf get() const;
[[nodiscard]] halnf getTarget() const;
[[nodiscard]] bool inTransition() const;
explicit operator halnf() const;
void operator=(halnf val);
};
class AnimRect : Rect<AnimValue> {
public:
AnimRect() {
setAnimTime(450);
setNoTransition({ 0, 0, 0, 0 });
}
[[nodiscard]] RectF get() const;
[[nodiscard]] RectF getTarget() const;
void setNoTransition(RectF);
void set(const RectF&);
void setAnimTime(halni time_ms);
};
class AnimColor {
public:
AnimColor();
AnimRect mColor;
[[nodiscard]] RGBA get() const;
void set(const RGBA&);
};
};

View file

@ -1,35 +0,0 @@
#pragma once
#include "LabelWidget.hpp"
#include <functional>
namespace tp {
class ButtonWidget : public Widget {
public:
// enum State { NONE, ANTICIPATION, ACTIVATED, CONFIRMED };
public:
ButtonWidget();
ButtonWidget(const std::string& label, const tp::RectF& aArea);
bool isFired();
void eventProcess(const Events&) override;
void eventDraw(Canvas& canvas) override;
void setLabel(const std::string& string);
public:
void eventUpdateConfiguration(WidgetManager& wm) override;
public:
LabelWidget mLabel;
// State mStat = NONE;
RGBA pressedColor;
RGBA hoveredColor;
RGBA accentColor;
halnf rounding = 0;
std::function<void()> mCallback = [](){};
};
}

View file

@ -1,51 +0,0 @@
#pragma once
#include "ScrollableWidget.hpp"
#include "ButtonWidget.hpp"
namespace tp {
class CollapsableMenu : public Widget {
public:
CollapsableMenu();
void eventProcess(const Events&) override;
void eventDraw(Canvas& canvas) override;
public:
void addWidgetToMenu(Widget* widget);
void setLabel(const std::string& string);
void toggleCollapsed();
void setCollapsed(bool collapsed);
[[nodiscard]] bool getCollapsed() const;
void updateGeometry();
private:
RectF getHeaderRect();
RectF getBodyRect();
public:
void eventUpdateConfiguration(WidgetManager& wm) override;
protected:
ScrollableWindow mBody;
LabelWidget mHeader;
RGBA mMenuColor;
RGBA mBorderColor;
halnf headerHeight = 30;
halnf rounding = 0;
halnf mBorderSize = 0;
halnf mPadding = 0;
halnf mPrevHeight = 200;
bool mCollapsed = true;
bool mLocked = false;
bool mAdjustHeight = true;
public:
bool mBorders = true;
};
}

View file

@ -1,18 +0,0 @@
#include "FloatingWidget.hpp"
namespace tp {
class FloatingLayoutWidget : public Widget {
public:
FloatingLayoutWidget();
void eventProcess(const Events& events) override;
[[nodiscard]] bool handlesEvent() const;
private:
void updateActiveWindow(const tp::Events& events);
private:
bool mIsPassThrough = false;
};
}

View file

@ -1,38 +0,0 @@
#pragma once
#include "CollapsableMenu.hpp"
namespace tp {
class FloatingWidget : public CollapsableMenu {
public:
FloatingWidget();
void eventProcess(const Events& events) override;
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);
RectF getResizeHandle();
private:
Vec2F mMinSize = { 70, 70 };
halnf mResizeHandleSize = 10;
RGBA mResizeHandleColor = {};
bool mFloating = false;
bool mResizing = false;
Vec2F mActionStartRelativePos = {};
public:
bool mResizable = true;
bool mDropped = false;
};
}

View file

@ -1,88 +0,0 @@
#include "WidgetBase.hpp"
namespace tp {
class DockWidget : 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:
DockWidget();
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(Side side);
void setCenterWidget(Widget* widget);
Side getPreviewSide();
private:
void calculateSideAreas();
void calculateResizeHandles();
void handleResizeEvents(const Events& events);
void updateChildSideWidgets();
void calculateHeaderAreas();
bool isSideVisible(Side side);
bool sideExists(DockWidget::Side side);
ualni getVisibleSidesSize();
void handlePreview(const Events& events);
private:
RectF mPreviewArea = {};
Side mPreviewSide = NONE;
int resizeType[2] = { 0, 0 };
public:
SideWidgetData mSideWidgets[4];
private:
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

@ -1,23 +0,0 @@
#pragma once
#include "WidgetBase.hpp"
namespace tp {
class LabelWidget : public Widget {
public:
LabelWidget();
void eventDraw(Canvas& canvas) override;
public:
void eventUpdateConfiguration(WidgetManager& wm) override;
public:
std::string mLabel = "Label";
halnf fontSize = 10;
halnf padding = 0;
RGBA fontColor = { 1, 1, 1, 1 };
};
}

70
Widgets/public/Layout.hpp Normal file
View file

@ -0,0 +1,70 @@
#pragma once
#include "Rect.hpp"
#include <vector>
namespace tp {
class Widget;
enum class SizePolicy {
Fixed,
Expanding,
Minimal,
};
enum class LayoutPolicy {
Passive,
Vertical,
Horizontal,
};
class WidgetLayout {
friend class DebugManager;
friend class LayoutManager;
public:
explicit WidgetLayout(Widget* widget) { mWidget = widget; }
virtual ~WidgetLayout() = default;
virtual void updateLayout(bool vertical) {}
virtual void pickRect(bool vertical) {}
virtual void clampRect() {}
[[nodiscard]] virtual RectF getAvailableChildArea() const;
public:
const Vec2F& getMinSize();
void setMinSize(const Vec2F& size);
[[nodiscard]] const Vec2<SizePolicy>& getSizePolicy() const;
void setSizePolicy(SizePolicy x, SizePolicy y);
public:
[[nodiscard]] const RectF& getArea() const;
[[nodiscard]] RectF getAnimatedArea() const;
void setArea(const RectF& area);
[[nodiscard]] Widget* parent() const;
[[nodiscard]] const std::vector<Widget*>& children() const;
public:
void clampMinMaxSize();
[[nodiscard]] RangeF pickRange(const RangeF& current, const RangeF& child, const RangeF& parent, bool v) const;
[[nodiscard]] RangeF clampRange(const RangeF& current, const RangeF& child, const RangeF& parent, bool v) const;
[[nodiscard]] RectF getChildrenEnclosure() const;
[[nodiscard]] RectF getParentEnclosure() const;
private:
Widget* mWidget = nullptr;
protected:
Vec2<SizePolicy> mSizePolicy = { SizePolicy::Fixed, SizePolicy::Fixed };
Vec2F mMinSize = { 50, 50 };
Vec2F mMaxSize = { FLT_MAX / 2, FLT_MAX / 2 };
private:
// RectF mPrevArea{};
};
}

View file

@ -0,0 +1,53 @@
#pragma once
#include "Widget.hpp"
#include "UpdateManager.hpp"
#include "LayoutManager.hpp"
#include "DebugManager.hpp"
namespace tp {
class RootWidget : public WidgetManagerInterface {
friend DebugManager;
public:
RootWidget();
// User Interface
public:
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);
void drawFrame(Canvas& canvas);
[[nodiscard]] bool needsUpdate() const;
// Internals
private:
void drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos);
void updateWidget(Widget*, const char* reason = nullptr) override;
void openPopup(Widget*) override;
void closePopup(Widget*) override;
void lockFocus(Widget*) override;
void freeFocus(Widget*) override;
void updateAnimations();
void updateAreaCache(Widget* iter, bool read);
private:
Widget mRoot;
Widget mPopups;
Widget* mUserRoot = nullptr;
LayoutManager mLayoutManager;
UpdateManager mUpdateManager;
};
}

View file

@ -1,71 +0,0 @@
#pragma once
#include "WidgetBase.hpp"
#include "Buffer.hpp"
namespace tp {
class ScrollBarWidget : public Widget {
public:
ScrollBarWidget();
// takes whole area
void eventProcess(const Events& events) override;
void eventDraw(Canvas& canvas) override;
RectF getHandleHandle() const;
RectF getViewport() const;
RectF getHandle() const;
public:
void eventUpdateConfiguration(WidgetManager& wm) override;
public:
bool mIsScrolling = false;
halnf mSizeFraction = 1.f;
halnf mPositionFraction = 0.f;
bool mHovered = false;
RGBA mDefaultColor;
RGBA mHandleColor;
RGBA mHoveredColor;
RGBA mScrollingColor;
halnf mPadding = 0;
halnf mHandleSize = 10;
halnf mMinSize = 10;
halnf mRounding = 10;
};
class ScrollableWindow : public Widget {
public:
ScrollableWindow();
virtual ~ScrollableWindow();
// takes whole area
void eventProcess(const Events& events) override;
void addWidget(Widget* widget);
void clearContent();
List<Widget*>& getContent();
void eventUpdateConfiguration(WidgetManager& wm) override;
[[nodiscard]] halnf getContentSize() const;
private:
void updateContents(List<Widget*>& contentWidgets);
// ready content size
void updateContentSize(List<Widget*>& contentWidgets);
void setOffset(List<Widget*>& contentWidgets, const halnf offset);
private:
Widget mContentWidget;
ScrollBarWidget mScroller;
halnf mContentSize = 0;
halnf mPadding = 0;
};
}

View file

@ -1,41 +0,0 @@
#pragma once
#include "LabelWidget.hpp"
namespace tp {
class SliderWidget : public Widget {
public:
SliderWidget();
void eventProcess(const Events& events) override;
void eventDraw(Canvas& canvas) override;
RectF getHandle() const;
public:
void eventUpdateConfiguration(WidgetManager& wm) override;
public:
halnf mFactor = 0.f;
bool mIsSliding = false;
RGBA defaultColor;
RGBA handleColor;
halnf handleSize = 0;
halnf rounding = 0;
halnf borderSize = 2;
};
class NamedSliderWidget : public Widget {
public:
explicit NamedSliderWidget(const char* name = "Value");
void eventProcess(const Events& events) override;
public:
SliderWidget mSlider;
LabelWidget mLabel;
halnf mFactor = 0.5f;
};
}

View file

@ -1,29 +0,0 @@
#pragma once
#include "WidgetBase.hpp"
namespace tp {
class TextInputWidget : public Widget {
public:
TextInputWidget();
void eventDraw(Canvas&) override;
public:
void eventUpdateConfiguration(WidgetManager& wm) override;
public:
enum { mMaxBufferSize = 512 };
char mBuff[mMaxBufferSize] = "";
bool nChanged = false;
std::string mValue;
std::string mId = "id";
bool mMultiline = false;
RGBA mAccentColor;
RGBA mHoveredColor;
RGBA mBaseColor;
halnf mRounding = 0;
halnf mPadding = 0;
};
}

155
Widgets/public/Widget.hpp Normal file
View file

@ -0,0 +1,155 @@
#pragma once
#include "SpringAnimations.hpp"
#include "Layout.hpp"
#include "EventHandler.hpp"
#include "Graphics.hpp"
#include <vector>
#include <functional>
namespace tp {
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(const Widget&&) = delete;
Widget(const Widget&) = delete;
void operator=(const Widget&) = delete;
Widget();
virtual ~Widget();
void addChild(Widget* child, bool front = false);
void removeChild(Widget* child);
void clear();
const List<Widget*>& getChildren() { return mDepthOrder; }
void bringToFront();
void bringToBack();
void setLayout(WidgetLayout* layout);
void setSizePolicy(SizePolicy x, SizePolicy y);
void setEnabled(bool val) { mFlags.set(ENABLED, val); }
WidgetLayout* getLayout();
[[nodiscard]] const WidgetLayout* getLayout() const;
void triggerWidgetUpdate(const char* reason = nullptr);
void openPopup(Widget*);
void closePopup(Widget*);
void lockFocus();
void freeFocus();
protected:
virtual void process(const EventHandler& events) {}
virtual void draw(Canvas& canvas) {}
virtual void drawOverlay(Canvas& canvas) {}
virtual void endAnimations();
virtual void updateAnimations();
[[nodiscard]] virtual bool processesEvents() const;
[[nodiscard]] virtual bool propagateEventsToChildren() const;
[[nodiscard]] virtual bool needsNextFrame() const;
virtual void mouseEnter();
virtual void mouseLeave();
protected:
void setDebug(const char* name, RGBA col);
WidgetManagerInterface* getRoot();
public:
[[nodiscard]] RectF getArea() const;
[[nodiscard]] RectF getAreaT() const;
[[nodiscard]] RectF getRelativeArea() const;
[[nodiscard]] RectF getRelativeAreaT() const;
void setArea(const RectF& area);
void setAreaCache(const RectF& area);
private:
[[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->isUpdate()) return;
before(iter);
for (auto child : iter->mDepthOrder) {
dfs(child.data(), before, after);
}
after(iter);
}
protected:
friend WidgetLayout;
Widget* mParent = nullptr;
std::vector<Widget*> mChildren;
List<Widget*> mDepthOrder;
// relative to the parent
SpringRect mArea;
RectF mAreaCache;
WidgetLayout* mLayout = nullptr;
BitField mFlags;
// debug
struct {
std::string id = "widget base";
RGBA col = { 1, 1, 1, 0.3 };
std::string triggerReason = "none";
Vec2F pLocal;
Vec2F pGlobal;
} mDebug;
};
struct WidgetManagerInterface : public Widget {
virtual void updateWidget(Widget*, const char* reason) = 0;
virtual void openPopup(Widget*) = 0;
virtual void closePopup(Widget*) = 0;
virtual void lockFocus(Widget*) = 0;
virtual void freeFocus(Widget*) = 0;
static WidgetLayout* defaultLayout(Widget* widget);
};
}

View file

@ -0,0 +1,49 @@
#pragma once
#include "GraphicApplication.hpp"
#include "RootWidget.hpp"
#include "Timing.hpp"
namespace tp {
class WidgetApplication : public Application {
public:
WidgetApplication() = default;
void setRoot(Widget* widget) {
mRootWidget.setRootWidget(widget);
}
private:
void processFrame(EventHandler* eventHandler, halnf deltaTime) override {
const auto rec = RectF({ 0, 0 }, mWindow->getSize());
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();
}
private:
void debugUI();
private:
time_ms mProcTime = 0;
time_ms mDrawTime = 0;
RootWidget mRootWidget;
};
}

View file

@ -1,78 +0,0 @@
#pragma once
#include "Graphics.hpp"
#include "EventHandler.hpp"
#include "WidgetManager.hpp"
#include "List.hpp"
namespace tp {
using Events = EventHandler;
class Widget {
public:
Widget();
virtual ~Widget();
void procWrapper(const Events& events, const RectF& parentArea);
void drawWrapper(Canvas& canvas);
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);
virtual void eventNotVisible(const Events& events);
virtual void eventFocusEnter(const Events& events);
virtual void eventFocusLeave(const Events& events);
virtual void eventPressed(const Events& events);
virtual void eventReleased(const Events& events);
public:
void setEnable(bool enable);
void setVisible(bool visible);
void setArea(const RectF& area);
[[nodiscard]] const RectF& getArea() const;
[[nodiscard]] bool isFocus() const;
[[nodiscard]] bool isPressed() const;
[[nodiscard]] bool isReleased() const;
[[nodiscard]] bool isHolding() const;
void clearEvents();
private:
void checkVisibility(const Events& events, const RectF& parentArea);
void checkFocus(const Events& events);
void checkClicked(const Events& events);
public:
RectF mArea;
RectF mVisibleArea;
List<Widget*> mChildWidgets;
bool mVisible = false;
bool mEnable = true;
bool mHandlesEvents = true;
bool mInFocus = false;
bool mHolding = false;
bool mPressed = false;
bool mReleased = false;
// docking
bool mIsDocked = false;
};
}

View file

@ -1,89 +0,0 @@
#pragma once
#include "Animations.hpp"
#include "Map.hpp"
#include "Rect.hpp"
#include "InputCodes.hpp"
#include "Buffer.hpp"
namespace tp {
struct WidgetConfig {
struct Shortcut {
struct Condition {
std::string name;
std::string state;
};
std::string callbackName;
Shortcut() = default;
Shortcut(const InitialierList<Condition>&) {}
};
struct Parameter {
enum Type { NONE, VAL, COL };
halnf value = 0.f;
RGBA color = {};
Type type = NONE;
bool globalReference = false;
std::string globalId;
Parameter() = default;
explicit Parameter(halnf val) {
type = VAL;
value = val;
}
explicit Parameter(const RGBA& val) {
type = COL;
color = val;
}
explicit Parameter(const std::string& id, const Type& referenceType) {
type = referenceType;
globalId = id;
globalReference = true;
}
};
Map<std::string, Parameter> mParameters;
Buffer<Shortcut> mShortcuts;
};
class WidgetManager {
public:
using Parameter = WidgetConfig::Parameter;
public:
WidgetManager();
~WidgetManager();
const RGBA& getColor(const std::string& parameterId, const RGBA& defaultValue);
const RGBA& getColor(const std::string& parameterId, const char* globalRef);
halnf getNumber(const std::string& parameterId, halnf defaultValue);
halnf getNumber(const std::string& parameterId, const char* globalRef);
void setActiveId(const std::string& id);
private:
WidgetConfig& getWidgetConfig(const std::string& id);
Parameter& getParameter(WidgetConfig& config, const std::string& id, const Parameter& defaultValue);
void initGlobalParameters();
private:
Map<std::string, WidgetConfig> mConfigurations;
WidgetConfig mGlobalConfig;
RGBA mErrorColor = { 0, 0, 0, 1 };
halnf mErrorNumber = 0;
std::string mActiveId;
};
}

View file

@ -1,13 +0,0 @@
#pragma once
#include "Animations.hpp"
#include "ButtonWidget.hpp"
#include "LabelWidget.hpp"
#include "ScrollableWidget.hpp"
#include "TextInputWidget.hpp"
#include "SliderWidget.hpp"
#include "CollapsableMenu.hpp"
#include "FloatingWidget.hpp"
#include "FloatingLayoutWidget.hpp"
#include "GridLayoutWidget.hpp"
#include "WorkspaceWidget.hpp"

View file

@ -1,19 +0,0 @@
#pragma once
#include "Widgets.hpp"
namespace tp {
class WorkspaceWidget : public Widget {
public:
WorkspaceWidget();
void eventProcess(const Events& events) override;
protected:
DockWidget mDockSpace;
FloatingLayoutWidget mFloatingLayer;
// Parameters
Vec2F mDefaultFloatSize = { 200, 200 };
};
}

View file

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

View file

@ -0,0 +1,109 @@
#include "Widget.hpp"
namespace tp {
class DockLayout : public WidgetLayout {
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 = 300;
alni order = -1;
Side side = { TOP };
RectF area = {};
RectF headerArea = {};
RectF previewHandleArea = {};
ResizeHandle resizeHandle;
RectF areaBeforeDocking = {};
};
public:
explicit DockLayout(Widget* widget);
void updateResizeHover(const Vec2F& pointer);
bool startResize(const Vec2F& pointer);
void updateResize(const Vec2F& pointerDelta);
void endResize();
void updatePreviewSide(const Vec2F& pointer);
public:
void pickRect(bool vertical) override {}
void clampRect() override {};
void updateLayout(bool vertical) override;
void adjustChildrenRect();
public:
bool setCenterWidget(Widget* widget);
bool dockWidget(Widget* widget, Side side);
bool undockWidget(Side side);
void toggleWidgetVisibility(Side side);
public:
[[nodiscard]] bool isSideVisible(Side side) const;
[[nodiscard]] bool sideExists(Side side) const;
[[nodiscard]] bool isResizing() const { return mResizing; }
[[nodiscard]] bool isResizing(Side side) const { return mSideWidgets[side].resizeHandle.active; }
[[nodiscard]] bool isResizeHandleHover(Side side) const { return mSideWidgets[side].resizeHandle.hover; }
Side getSideFromWidget(Widget*);
Side getPreviewSide();
[[nodiscard]] RectF getRectBeforeDocked(Side side) const { return mSideWidgets[side].areaBeforeDocking; }
[[nodiscard]] RectF getResizeHandleArea(Side side) const { return mSideWidgets[side].resizeHandle.area; }
[[nodiscard]] RectF getHeaderArea(Side side) const { return mSideWidgets[side].headerArea; }
[[nodiscard]] RectF getPreviewHandleArea(Side side) const { return mSideWidgets[side].previewHandleArea; }
[[nodiscard]] RectF getPreviewArea() const { return mPreviewArea; }
Widget* getSideWidget(Side side) { return mSideWidgets[side].widget; }
const std::vector<Widget*>& getDockedWidgets();
private:
ualni getVisibleSidesSize() const;
void calculateSideAreas();
void calculateResizeHandles();
void updateChildSideWidgets();
void calculateHeaderAreas();
private:
RectF mPreviewArea = {};
Side mPreviewSide = NONE;
int resizeType[2] = { 0, 0 };
SideWidgetData mSideWidgets[4];
std::vector<Widget*> mDockedWidgets;
private:
bool mResizing = false;
Widget* mCenterWidget = nullptr;
RectF mCenterArea {};
// Parameters
const halnf mHandleSplitFactor = 0.3;
const halnf mHandleFactor = 0.1;
halnf mSideSizePadding = 150.f;
halnf mPadding = 4;
halnf mHeaderSize = 27;
};
}

View file

@ -0,0 +1,34 @@
#pragma once
#include "BasicLayout.hpp"
namespace tp {
class FloatingLayout : public BasicLayout {
public:
explicit FloatingLayout(Widget* widget) :
BasicLayout(widget) {}
public:
void startAction(const Vec2F& pointer);
void updateAction(const Vec2F& pointer);
void endAction();
bool isFloating() const { return mIsFloating; }
RectF resizeHandleRect();
public:
void pickRect(bool vertical) override;
private:
bool mIsFloating = false;
bool mIsResizing = false;
Vec2F mPointerStart;
Vec2F mPointerCurrent;
// TODO : remove?
halnf mHandleSize = 10;
halnf mHandlePadding = 2;
};
}

View file

@ -0,0 +1,18 @@
#pragma once
#include "Layout.hpp"
namespace tp {
class OverlayLayout : public WidgetLayout {
friend class DebugManager;
public:
explicit OverlayLayout(Widget* widget) : WidgetLayout(widget) {}
void updateLayout(bool vertical) override;
void pickRect(bool vertical) override {}
void clampRect() override {}
};
}

View file

@ -0,0 +1,24 @@
#pragma once
#include "Layout.hpp"
namespace tp {
class ScrollableBarWidget;
class ScrollableLayout : public WidgetLayout {
public:
explicit ScrollableLayout(Widget* widget) :
WidgetLayout(widget) {
setSizePolicy(SizePolicy::Expanding, SizePolicy::Expanding);
}
void updateLayout(bool vertical) override;
[[nodiscard]] RectF getAvailableChildArea() const override;
private:
void updateWidgetRects(const RectF& area, Widget* content, ScrollableBarWidget* scroller) const;
private:
halnf mScrollerSize = 15;
};
}

View file

@ -0,0 +1,52 @@
#pragma once
#include "Widget.hpp"
#include <set>
namespace tp {
class DebugManager {
public:
DebugManager() = default;
bool isRedrawAlways() const { return mDebugRedrawAlways; }
bool update(RootWidget* rootWidget, EventHandler& events);
void drawDebug(RootWidget* rootWidget, Canvas& canvas);
void checkProcBreakPoints(Widget* widget) {
if (mProcBreakpoints.find(widget) != mProcBreakpoints.end()) {
mProcBreakpoints.erase(widget);
DEBUG_BREAK(1)
}
}
void checkLayoutBreakpoints(Widget* widget) {
if (mLayBreakpoints.find(widget) != mLayBreakpoints.end()) {
mLayBreakpoints.erase(widget);
DEBUG_BREAK(1)
}
}
[[nodiscard]] bool isDebug() const { return mDebug; }
private:
void recursiveDraw(Canvas& canvas, Widget* active, const Vec2F& pos, int depthOrder);
static void widgetMenu(Widget*);
void drawLayoutOrder();
private:
RootWidget* mRootWidget = nullptr;
// debug
bool mDebug = false;
bool mDebugStopProcessing = false;
bool mDebugRedrawAlways = false;
bool mSlowMotion = false;
std::set<Widget*> mProcBreakpoints;
std::set<Widget*> mLayBreakpoints;
};
extern DebugManager gDebugWidget;
}

View file

@ -0,0 +1,39 @@
#pragma once
#include "Layout.hpp"
#include "DebugManager.hpp"
#include <map>
#include <vector>
namespace tp {
class LayoutManager {
friend DebugManager;
struct DepNode {
std::vector<Widget*> depends;
int references = 0;
int depth = 0;
};
public:
LayoutManager() = default;
void adjust(Widget* root);
private:
void findDependencies(Widget* root);
void topologicalSort(Widget* root, int depth = 0);
void adjustLayouts();
private:
int getLayoutOrder(WidgetLayout* parent, WidgetLayout* child) const;
private:
std::map<Widget*, DepNode> mDepGraph;
std::vector<Widget*> mRoots;
std::vector<std::pair<Widget*, int>> mLayOrder;
bool mVertical = false;
};
}

View file

@ -0,0 +1,46 @@
#pragma once
#include "Widget.hpp"
#include <map>
namespace tp {
// FIXME : desperately needs refactor
class UpdateManager {
friend DebugManager;
public:
UpdateManager() = default;
void scheduleUpdate(Widget* widget, const char* reason);
void lockFocus(Widget* widget);
void freeFocus(Widget* widget);
[[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:
static void procWidget(Widget* widget, EventHandler& events, bool withEvents = false);
private:
std::map<Widget*, bool> mTriggeredWidgets;
Widget* mInFocusWidget = nullptr;
Widget* mFocusLockWidget = nullptr;
private:
int mDebugWidgetsToProcess = 0;
};
}

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 needsNextFrame() const override;
private:
mutable SpringRect mTestSpring;
};
}

View file

@ -0,0 +1,42 @@
#include "DockLayout.hpp"
namespace tp {
class DockWidget : public Widget {
public:
DockWidget();
public:
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;
[[nodiscard]]bool processesEvents() const override { return true; }
void drawSide(DockLayout::Side side, Canvas& canvas);
public:
void setCenterWidget(Widget* widget);
void dockWidget(Widget* widget, DockLayout::Side side);
void undockWidget(DockLayout::Side side, bool restoreArea = true);
void toggleWidgetVisibility(DockLayout::Side side);
private:
DockLayout* layout() { return dynamic_cast<DockLayout*>(mLayout); }
const DockLayout* layout() const { return dynamic_cast<const DockLayout*>(mLayout); }
private:
Widget* mPreviewWidget = nullptr;
private:
halnf mRounding = 10;
halnf mPadding = 4;
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, 0.2);
};
}

View file

@ -0,0 +1,62 @@
#pragma once
#include "SimpleWidgets.hpp"
#include "FloatingLayout.hpp"
#include "ScrollableWidget.hpp"
namespace tp {
class FloatingWidget : public Widget {
public:
FloatingWidget() : Widget() {
setDebug("float", { 0.0, 0.9, 0.1, 1 });
setLayout(new FloatingLayout(this));
}
void process(const EventHandler& events) override;
void draw(Canvas& canvas) override;
[[nodiscard]] bool needsNextFrame() const override;
[[nodiscard]] bool propagateEventsToChildren() const override;
[[nodiscard]] bool processesEvents() const override;
[[nodiscard]] bool isFloating() const;
private:
FloatingLayout* layout();
[[nodiscard]] const FloatingLayout* layout() const;
};
class FloatingMenu : public FloatingWidget {
public:
FloatingMenu();
public:
void addToMenu(Widget* widget) {
widget->setSizePolicy(SizePolicy::Expanding, SizePolicy::Minimal);
mContentWidget.addChild(widget);
}
const List<Widget*>& getContent() {
return mContentWidget.getChildren();
}
void clearChildren() {
mContentWidget.clear();
}
void setText(const std::string& text);
private:
// VerticalLayout mMenuLayout;
Widget mBodyLayout;
Widget mContentWidget;
ScrollableBarWidget mScrollBar;
LabelWidget mHeader;
// ButtonWidget mTestButton;
};
}

View file

@ -0,0 +1,57 @@
#pragma once
#include "Widget.hpp"
namespace tp {
// just the scrolling bar
class ScrollableBarWidget : public Widget {
public:
ScrollableBarWidget() = default;
[[nodiscard]] bool processesEvents() const override { return true; }
[[nodiscard]] bool propagateEventsToChildren() const override { return false; }
[[nodiscard]] bool needsNextFrame() const override { return mScrolling; }
void process(const EventHandler& events) override;
void draw(Canvas& canvas) override;
void updateSizeFactor(halnf factor); // holder-size divided by content-size
[[nodiscard]] bool getDirection() const { return mVertical; }
[[nodiscard]] halnf getPosFactor() const { return mPosFactor - mSizeFactor / 2; }
private:
[[nodiscard]] const RectF& getHandleRect() const;
void updateHandleRect();
void jumpTo(halnf);
void moveBy(halnf);
void clamp();
private: // state
bool mVertical = true;
bool mScrolling = false;
bool mHandleHovered = false;
Vec2F mStartPos = {};
halnf mPosFactor = 0.2f; // (center-of-holder - content-start) / content-size
halnf mSizeFactor = 0.3f;
private: // TODO : make params static
halnf mRounding = 5;
halnf mHandlePadding = 0;
RGBA mBGColor = { 0.1, 0.1, 0.1, 0.0 };
RGBA mHandleColor = { 0.2, 0.2, 0.2, 1 };
RGBA mHandleHoverColor = { 0.3, 0.3, 0.3, 1 };
RGBA mHandleSlideColor = { 0.5, 0.5, 0.5, 1 };
private: // cache
RectF mHandleRect;
};
class ScrollableWidget : public Widget {
public:
ScrollableWidget();
};
}

View file

@ -0,0 +1,94 @@
#pragma once
#include "Widget.hpp"
#include <functional>
namespace tp {
class LabelWidget : public Widget {
public:
LabelWidget() : Widget() {
setDebug("label", { 0.1, 0.1, 0.1, 0.1 });
}
void setText(const std::string& text);
[[nodiscard]] const std::string& getText() const;
void draw(Canvas& canvas) override;
[[nodiscard]] bool processesEvents() const override { return false; }
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;
void mouseEnter() override;
void mouseLeave() override;
[[nodiscard]] bool processesEvents() const override { return true; }
[[nodiscard]] bool needsNextFrame() const override;
void endAnimations() override;
void updateAnimations() override;
void setColor(const RGBA& in);
private:
std::function<void()> mAction;
SpringRect mColorAnimated;
halnf mRounding = 5;
RGBA mColorHovered = { 0.0f, 0.4f, 0.4f, 1.f };
RGBA mColor = { 0.13f, 0.13f, 0.13f, 1.f };
};
class SliderWidget : public Widget {
enum State {
IDLE,
HOVER,
SLIDING,
};
public:
SliderWidget() = default;
void process(const EventHandler& eventHandler) override;
void draw(Canvas& canvas) override;
[[nodiscard]] bool processesEvents() const override { return true; }
[[nodiscard]] bool needsNextFrame() const override { return mState != SLIDING; }
[[nodiscard]] halnf val() const { return mFactor; }
private:
[[nodiscard]] RectF getHandleArea() const;
private:
halnf mFactor = 0;
State mState = IDLE;
private:
halnf mHandleSize = 20;
halnf mRounding = 5;
RGBA mColorActive = { 0.5f, 0.5f, 0.5f, 1.f };
RGBA mColorHovered = { 0.3f, 0.3f, 0.3f, 1.f };
RGBA mColorIdle = { 0.2f, 0.2f, 0.2f, 1.f };
RGBA mColorBG = { 0.08f, 0.08f, 0.08f, 1.0f };
};
}