Refactor
This commit is contained in:
parent
49cfe70c0d
commit
72c4740f82
18 changed files with 592 additions and 203 deletions
|
|
@ -5,19 +5,33 @@
|
|||
#include "AnimationTestWidget.hpp"
|
||||
#include "FloatingWidget.hpp"
|
||||
#include "SimpleWidgets.hpp"
|
||||
#include "LayoutWidget.hpp"
|
||||
|
||||
using namespace tp;
|
||||
|
||||
class WidgetApplication : public Application {
|
||||
public:
|
||||
WidgetApplication() {
|
||||
mRootWidget.setRootWidget(&mWidget);
|
||||
mRootWidget.setRootWidget(&mLayoutWidget);
|
||||
|
||||
mWidget.addChild(&mWidgetFloating);
|
||||
mLayoutWidget.addChild(&mFloatingMenu);
|
||||
// mLayoutWidget.addChild(&mButton2);
|
||||
|
||||
RootWidget::setWidgetArea(mWidgetFloating, { 100, 100, 150, 200 });
|
||||
// mWidgetFloating.addChild(&mButton);
|
||||
|
||||
mWidgetFloating.addChild(&mButton);
|
||||
// mRootWidget.setRootWidget(&mAnimationTestWidget);
|
||||
|
||||
// mAnimationTestWidget.addChild(&mWidgetFloating);
|
||||
// mAnimationTestWidget.addChild(&mLayoutWidget);
|
||||
|
||||
// RootWidget::setWidgetArea(mButton, { 100, 100, 150, 200 });
|
||||
|
||||
RootWidget::setWidgetArea(mFloatingMenu, { 100, 100, 150, 200 });
|
||||
|
||||
// mWidgetFloating.addChild(&mLayoutWidget);
|
||||
|
||||
// mLayoutWidget.addChild(&mButton);
|
||||
// mLayoutWidget.addChild(&mLabel);
|
||||
}
|
||||
|
||||
void processFrame(EventHandler* eventHandler, halnf deltaTime) override {
|
||||
|
|
@ -38,10 +52,12 @@ public:
|
|||
private:
|
||||
LabelWidget mLabel;
|
||||
ButtonWidget mButton;
|
||||
|
||||
ButtonWidget mButton2;
|
||||
FloatingWidget mWidgetFloating;
|
||||
AnimationTestWidget mAnimationTestWidget;
|
||||
DesktopLayout mLayoutWidget;
|
||||
FloatingMenu mFloatingMenu;
|
||||
|
||||
AnimationTestWidget mWidget;
|
||||
RootWidget mRootWidget;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ void AnimationTestWidget::process(const EventHandler& events) {
|
|||
|
||||
mTestSpring.updateCurrentRect();
|
||||
|
||||
if (mTestSpring.checkAnimationShouldEnd()) {
|
||||
if (mTestSpring.shouldEndTransition()) {
|
||||
mTestSpring.endAnimation();
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,6 @@ void AnimationTestWidget::draw(Canvas& canvas) {
|
|||
canvas.rect(mTestSpring.getCurrentRect(), RGBA(1.f), 10);
|
||||
}
|
||||
|
||||
bool AnimationTestWidget::needUpdate() const {
|
||||
if (Widget::needUpdate()) return true;
|
||||
return !mTestSpring.checkAnimationShouldEnd();
|
||||
bool AnimationTestWidget::needsNextFrame() const {
|
||||
return Widget::needsNextFrame() || !mTestSpring.shouldEndTransition();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,21 +12,45 @@ void FloatingWidget::process(const EventHandler& events) {
|
|||
mIsFloating = true;
|
||||
}
|
||||
|
||||
if (mIsFloating && resizeHandleRect().isInside(relativePointer)) {
|
||||
mIsResizing = true;
|
||||
}
|
||||
|
||||
if (mIsFloating && events.isReleased(InputID::MOUSE1)) {
|
||||
mIsFloating = false;
|
||||
mIsResizing = false;
|
||||
}
|
||||
|
||||
mPointerCurrent = relativePointer;
|
||||
|
||||
setActive(mIsFloating || needUpdate());
|
||||
// triggerWidgetUpdate();
|
||||
}
|
||||
|
||||
void FloatingWidget::updateArea(RectF& area) const {
|
||||
if (mIsFloating) {
|
||||
void FloatingWidget::adjustRect() {
|
||||
auto area = getArea();
|
||||
|
||||
if (mIsResizing) {
|
||||
area.size = mPointerCurrent + mHandleSize / 2.f;
|
||||
} else if (mIsFloating) {
|
||||
area.pos += mPointerCurrent - mPointerStart;
|
||||
}
|
||||
|
||||
setArea(area);
|
||||
}
|
||||
|
||||
void FloatingWidget::draw(Canvas& canvas) {
|
||||
canvas.rect(resizeHandleRect(), RGBA(0.7f), 2);
|
||||
canvas.rect(getRelativeArea(), RGBA(0.5f), 10);
|
||||
}
|
||||
|
||||
RectF FloatingWidget::resizeHandleRect() {
|
||||
auto area = getRelativeArea();
|
||||
area.pos = area.p3() - mHandleSize;
|
||||
area.size = mHandleSize;
|
||||
area.shrink(mHandlePadding);
|
||||
return area;
|
||||
}
|
||||
|
||||
bool FloatingWidget::propagateEventsToChildren() const {
|
||||
return !mIsFloating;
|
||||
}
|
||||
|
|
@ -1,35 +1,77 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include "RootWidget.hpp"
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
using namespace tp;
|
||||
|
||||
void RootWidget::setRootWidget(Widget* widget) { mRoot = widget; }
|
||||
void RootWidget::setRootWidget(Widget* widget) {
|
||||
mRoot = widget;
|
||||
widget->mParent = this;
|
||||
}
|
||||
|
||||
void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) {
|
||||
mNeedUpdate = false;
|
||||
|
||||
auto currentPaths = &mActivePaths[mFlipFlop];
|
||||
auto prevPaths = &mActivePaths[!mFlipFlop];
|
||||
|
||||
updateActivePaths(currentPaths, events->getPointer());
|
||||
mScreenArea = screenArea;
|
||||
|
||||
mRoot->setArea(screenArea);
|
||||
|
||||
clearProcessedFlag(*currentPaths);
|
||||
clearProcessedFlag(*prevPaths);
|
||||
// construct hierarchy tree of widgets to process
|
||||
updateTreeToProcess();
|
||||
|
||||
processPaths(*currentPaths, *events);
|
||||
processPaths(*prevPaths, *events);
|
||||
// update active tree animations
|
||||
updateAnimations(&mWidgetsToProcess[mRoot]);
|
||||
|
||||
mActiveWidgets.erase_if([](auto widget) { return !widget->mIsActive; });
|
||||
// update all events and call all event processing callbacks
|
||||
EventHandler dummyEvents;
|
||||
|
||||
mFlipFlop = !mFlipFlop;
|
||||
processFocusItems(*events);
|
||||
processActiveTree(&mWidgetsToProcess[mRoot], dummyEvents);
|
||||
|
||||
// update widget sizes base on individual size policies
|
||||
adjustSizes(&mWidgetsToProcess[mRoot]);
|
||||
|
||||
// trigger some widgets by moise pointer
|
||||
handleFocusChanges(*events);
|
||||
|
||||
// check triggered widgets for removal
|
||||
erase_if(mTriggeredWidgets, [](auto widget) {
|
||||
auto end = !widget->needsNextFrame();
|
||||
if (end) {
|
||||
widget->endAnimations();
|
||||
}
|
||||
return end;
|
||||
});
|
||||
}
|
||||
|
||||
bool RootWidget::needsUpdate() const { return mNeedUpdate; }
|
||||
bool RootWidget::needsUpdate() const { return !mTriggeredWidgets.empty(); }
|
||||
|
||||
void RootWidget::drawFrame(Canvas& canvas) {
|
||||
// draw from top to bottom
|
||||
canvas.rect(mScreenArea, RGBA(0, 0, 0, 1));
|
||||
|
||||
// draw all tree from top to bottom
|
||||
drawRecursion(canvas, mRoot, { 0, 0 });
|
||||
|
||||
if (mDebug) {
|
||||
drawDebug(canvas, mRoot, { 0, 0 });
|
||||
ImGui::Text("Triggered Widgets: %i", (int) mTriggeredWidgets.size());
|
||||
ImGui::Text("Widgets To Process: %i", (int) mWidgetsToProcess.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RootWidget::updateTreeToProcess() {
|
||||
mWidgetsToProcess.clear();
|
||||
for (auto widget : mTriggeredWidgets) {
|
||||
for (auto iter = widget; iter; iter = iter->mParent) {
|
||||
if (iter->mParent) {
|
||||
mWidgetsToProcess[iter->mParent].children.insert(&mWidgetsToProcess[iter]);
|
||||
mWidgetsToProcess[iter->mParent].widget= iter->mParent;
|
||||
mWidgetsToProcess[iter].widget= iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RootWidget::drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos) {
|
||||
|
|
@ -37,8 +79,9 @@ void RootWidget::drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos)
|
|||
canvas.pushClamp({ pos, active->getArea().size });
|
||||
|
||||
active->draw(canvas);
|
||||
for (auto child : active->mChildWidgets) {
|
||||
drawRecursion(canvas, child.data(), pos + child->getArea().pos);
|
||||
|
||||
for (auto child : active->mChildren) {
|
||||
drawRecursion(canvas, child, pos + child->getArea().pos);
|
||||
}
|
||||
|
||||
canvas.setOrigin(pos);
|
||||
|
|
@ -47,57 +90,25 @@ void RootWidget::drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos)
|
|||
active->drawOverlay(canvas);
|
||||
}
|
||||
|
||||
void RootWidget::updateActivePaths(Buffer<ActivePath>* activePaths, const Vec2F& pointer) {
|
||||
activePaths->clear();
|
||||
void RootWidget::drawDebug(Canvas& canvas, Widget* active, const Vec2F& pos) {
|
||||
auto area = RectF{ pos, active->getArea().size };
|
||||
|
||||
// based on pointer position
|
||||
Widget* pointedWidget = nullptr;
|
||||
|
||||
{
|
||||
ActivePath& activePath = activePaths->append(ActivePath{});
|
||||
activePath.path.append({ mRoot, {} });
|
||||
|
||||
auto pointerIter = pointer;
|
||||
|
||||
bool newActive = true;
|
||||
while (newActive) {
|
||||
newActive = false;
|
||||
for (auto child : activePath.path.last().widget->mChildWidgets) {
|
||||
const auto area = child->mArea.getCurrentRect();
|
||||
if (area.isInside(pointerIter)) {
|
||||
activePath.path.append({ child.data(), {} });
|
||||
newActive = true;
|
||||
pointerIter -= child->getArea().pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pointedWidget = activePath.path.last().widget;
|
||||
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);
|
||||
}
|
||||
|
||||
// based on individual widget requirements
|
||||
{
|
||||
for (auto widget : mActiveWidgets) {
|
||||
if (widget.data() == pointedWidget) continue;
|
||||
|
||||
ActivePath& activePath = activePaths->append(ActivePath{});
|
||||
|
||||
for (Widget* iter = widget.data(); iter; iter = iter->mParent) {
|
||||
activePath.path.append({ iter, {} });
|
||||
}
|
||||
|
||||
activePath.path.reverse();
|
||||
}
|
||||
if (active->mInFocus) {
|
||||
RGBA color = { 1, 0, 0, 0.3f};
|
||||
canvas.debugCross(area, color);
|
||||
canvas.circle(pos + active->mLocalPoint, 5, active->mDebugColor);
|
||||
canvas.circle(active->mGlobalPoint, 10, active->mDebugColor);
|
||||
}
|
||||
|
||||
// update paths global pos
|
||||
for (auto activePath : *activePaths) {
|
||||
Vec2F iterPos = { 0, 0 };
|
||||
for (auto activeWidget : activePath->path) {
|
||||
iterPos += activeWidget->widget->getArea().pos;
|
||||
activeWidget->globalPos = iterPos;
|
||||
}
|
||||
for (auto child : active->mChildren) {
|
||||
drawDebug(canvas, child, pos + child->getArea().pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -106,50 +117,135 @@ void RootWidget::setWidgetArea(Widget& widget, const RectF& rect) {
|
|||
widget.mArea.endAnimation();
|
||||
}
|
||||
|
||||
void RootWidget::processPaths(const Buffer<ActivePath>& paths, EventHandler& events) {
|
||||
EventHandler dummyEvents;
|
||||
bool eventsHandled = false;
|
||||
void RootWidget::updateAnimations(ActiveTreeNode* iter) {
|
||||
if (!iter) return;
|
||||
iter->widget->updateAnimations();
|
||||
for (auto child : iter->children) {
|
||||
updateAnimations(child);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto path : paths) {
|
||||
void RootWidget::getWidgetPath(Widget* widget, std::vector<Widget*>& out) {
|
||||
for (auto iter = widget; iter && iter != this; iter = iter->mParent) {
|
||||
out.push_back(iter);
|
||||
}
|
||||
}
|
||||
|
||||
for (alni widgetIdx = (alni) path->path.size() - 1; widgetIdx >= 0; widgetIdx--) {
|
||||
auto activeWidgetNode = path->path[widgetIdx];
|
||||
auto activeWidget = activeWidgetNode.widget;
|
||||
void RootWidget::handleFocusChanges(EventHandler& events) {
|
||||
auto prevFocus = mInFocusWidget;
|
||||
findFocusWidget(mRoot, &mInFocusWidget, events.getPointer());
|
||||
|
||||
activeWidget->mArea.updateCurrentRect();
|
||||
if (prevFocus) {
|
||||
std::vector<Widget*> path1;
|
||||
std::vector<Widget*> path2;
|
||||
|
||||
events.setCursorOrigin(activeWidgetNode.globalPos);
|
||||
getWidgetPath(prevFocus, path1);
|
||||
getWidgetPath(mInFocusWidget, path2);
|
||||
|
||||
bool useEvents = !eventsHandled || activeWidget->mIsActive;
|
||||
size_t iter = 0;
|
||||
while (path1[iter] == path2[iter]) {
|
||||
iter++;
|
||||
}
|
||||
|
||||
if (!activeWidget->mProcessedFlag) {
|
||||
activeWidget->process(useEvents ? events : dummyEvents);
|
||||
activeWidget->mProcessedFlag = true;
|
||||
}
|
||||
for (auto i = iter; i < path1.size(); i++) {
|
||||
path1[i]->mouseLeave();
|
||||
}
|
||||
|
||||
eventsHandled = !activeWidget->isPassThroughEvents();
|
||||
for (auto i = iter; i < path2.size(); i++) {
|
||||
path2[i]->mouseEnter();
|
||||
}
|
||||
|
||||
auto area = activeWidget->mArea.getCurrentRect();
|
||||
activeWidget->updateArea(area);
|
||||
activeWidget->setArea(area);
|
||||
|
||||
mNeedUpdate |= activeWidget->needUpdate();
|
||||
|
||||
if (!activeWidget->needUpdate()) activeWidget->finishAnimations();
|
||||
|
||||
if (activeWidget->mIsActive) {
|
||||
if (mActiveWidgets.find(activeWidget) == -1) {
|
||||
mActiveWidgets.append(activeWidget);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto iter = mInFocusWidget; iter; iter = iter->mParent) {
|
||||
iter->mouseEnter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RootWidget::clearProcessedFlag(const Buffer<ActivePath>& paths) {
|
||||
for (auto path : paths) {
|
||||
for (auto widget : path->path) {
|
||||
widget->widget->mProcessedFlag = false;
|
||||
void RootWidget::findFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer) {
|
||||
if (!iter->mArea.getTargetRect().isInside(pointer)) return;
|
||||
|
||||
*focus = iter;
|
||||
|
||||
for (auto child : iter->mChildren) {
|
||||
findFocusWidget(child, focus, pointer - iter->mArea.getTargetRect().pos);
|
||||
}
|
||||
}
|
||||
|
||||
void RootWidget::updateWidget(Widget* widget) {
|
||||
mTriggeredWidgets.insert(widget);
|
||||
}
|
||||
|
||||
void RootWidget::adjustSizes(ActiveTreeNode* iter) {
|
||||
if (!iter) return;
|
||||
|
||||
iter->widget->adjustRect();
|
||||
iter->widget->adjustChildrenRect();
|
||||
|
||||
for (auto child : iter->children) {
|
||||
adjustSizes(child);
|
||||
}
|
||||
}
|
||||
|
||||
void RootWidget::processActiveTree(ActiveTreeNode* iter, EventHandler& events) {
|
||||
if (!iter) return;
|
||||
|
||||
if (!iter->widget->mInFocus) {
|
||||
iter->widget->updateAnimations();
|
||||
iter->widget->process(events);
|
||||
}
|
||||
|
||||
for (auto child : iter->children) {
|
||||
processActiveTree(child, events);
|
||||
}
|
||||
}
|
||||
|
||||
void RootWidget::processFocusItems(EventHandler& events) {
|
||||
if (!mInFocusWidget) return;
|
||||
|
||||
// FIXME : cringe
|
||||
|
||||
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()) {
|
||||
len = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Vec2F> widgetGlobalPos(path.size());
|
||||
|
||||
widgetGlobalPos[0] = 0;
|
||||
for (auto widget = 0; widget < path.size() - 1; widget++) {
|
||||
widgetGlobalPos[widget + 1] = widgetGlobalPos[widget] + path[widget + 1]->getArea().pos;
|
||||
path[widget]->mGlobalPoint = widgetGlobalPos[widget];
|
||||
}
|
||||
|
||||
bool eventsProcessed = false;
|
||||
|
||||
for (int iter = (int) len - 1; iter >= 0; iter--) {
|
||||
auto widget = path[iter];
|
||||
|
||||
events.setCursorOrigin(widgetGlobalPos[iter]);
|
||||
|
||||
if (!eventsProcessed && widget->processesEvents()) {
|
||||
events.setEnableKeyEvents(true);
|
||||
|
||||
widget->updateAnimations();
|
||||
widget->process(events);
|
||||
|
||||
events.setEnableKeyEvents(false);
|
||||
eventsProcessed = true;
|
||||
} else {
|
||||
widget->updateAnimations();
|
||||
widget->process(events);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,11 +15,15 @@ void LabelWidget::draw(Canvas& canvas) {
|
|||
canvas.text(mText.c_str(), getRelativeArea(), mSize, Canvas::LC, mPadding, mColor);
|
||||
}
|
||||
|
||||
|
||||
ButtonWidget::ButtonWidget() {
|
||||
mAction = []() {
|
||||
printf("Button Pressed!\n");
|
||||
};
|
||||
|
||||
setDebug("button", { 0.1, 0.1, 0.7, 0.7 });
|
||||
|
||||
mColorAnimated.setTargetColor(mColor);
|
||||
mColorAnimated.endAnimation();
|
||||
}
|
||||
|
||||
void ButtonWidget::setAction(const std::function<void()>& action) {
|
||||
|
|
@ -27,19 +31,11 @@ void ButtonWidget::setAction(const std::function<void()>& action) {
|
|||
}
|
||||
|
||||
void ButtonWidget::process(const EventHandler& eventHandler) {
|
||||
if (getRelativeArea().isInside(eventHandler.getPointer())) {
|
||||
if (getArea().isInside(eventHandler.getPointer())) {
|
||||
if (eventHandler.isPressed(InputID::MOUSE1)) {
|
||||
mAction();
|
||||
}
|
||||
|
||||
mColorAnimated.setTargetColor(mColor);
|
||||
} else {
|
||||
mColorAnimated.setTargetColor(mColorHovered);
|
||||
}
|
||||
|
||||
mColorAnimated.updateCurrentRect();
|
||||
|
||||
setActive(needUpdate());
|
||||
}
|
||||
|
||||
void ButtonWidget::draw(Canvas& canvas) {
|
||||
|
|
@ -47,10 +43,31 @@ void ButtonWidget::draw(Canvas& canvas) {
|
|||
LabelWidget::draw(canvas);
|
||||
}
|
||||
|
||||
bool ButtonWidget::needUpdate() const {
|
||||
return !mColorAnimated.checkAnimationShouldEnd();
|
||||
bool ButtonWidget::needsNextFrame() const {
|
||||
return LabelWidget::needsNextFrame() || !mColorAnimated.shouldEndTransition();
|
||||
}
|
||||
|
||||
void ButtonWidget::finishAnimations() {
|
||||
void ButtonWidget::endAnimations() {
|
||||
mColorAnimated.endAnimation();
|
||||
}
|
||||
LabelWidget::endAnimations();
|
||||
}
|
||||
|
||||
void ButtonWidget::updateAnimations() {
|
||||
mColorAnimated.updateCurrentRect();
|
||||
LabelWidget::updateAnimations();
|
||||
}
|
||||
|
||||
void ButtonWidget::mouseEnter() {
|
||||
mColorAnimated.setTargetColor(mColor);
|
||||
mColorAnimated.endAnimation();
|
||||
|
||||
mColorAnimated.setTargetColor(mColorHovered);
|
||||
mColorAnimated.updateCurrentRect();
|
||||
triggerWidgetUpdate();
|
||||
}
|
||||
|
||||
void ButtonWidget::mouseLeave() {
|
||||
mColorAnimated.setTargetColor(mColor);
|
||||
mColorAnimated.updateCurrentRect();
|
||||
triggerWidgetUpdate();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,20 @@
|
|||
|
||||
using namespace tp;
|
||||
|
||||
WidgetManagerInterface* Widget::getRoot() {
|
||||
Widget* iter = mParent;
|
||||
while (iter && iter->mParent) {
|
||||
iter = iter->mParent;
|
||||
}
|
||||
return dynamic_cast<WidgetManagerInterface*>(iter);
|
||||
}
|
||||
|
||||
void Widget::triggerWidgetUpdate() {
|
||||
if (auto root = getRoot()) {
|
||||
root->updateWidget(this);
|
||||
}
|
||||
}
|
||||
|
||||
Widget::Widget() {
|
||||
mArea.setTargetRect({ 10, 10, 100, 40, });
|
||||
mArea.endAnimation();
|
||||
|
|
@ -9,10 +23,10 @@ Widget::Widget() {
|
|||
|
||||
void Widget::setArea(const RectF& area) {
|
||||
mArea.setTargetRect(area);
|
||||
}
|
||||
|
||||
void Widget::draw(Canvas& canvas) {
|
||||
canvas.rect(mArea.getCurrentRect(), RGBA(1.f), 10);
|
||||
if (!mArea.shouldEndTransition()) {
|
||||
triggerWidgetUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
RectF Widget::getArea() const {
|
||||
|
|
@ -23,28 +37,47 @@ RectF Widget::getRelativeArea() const {
|
|||
return { {}, mArea.getCurrentRect().size };
|
||||
}
|
||||
|
||||
void Widget::setActive(bool active) {
|
||||
mIsActive = active;
|
||||
}
|
||||
|
||||
void Widget::addChild(Widget* widget) {
|
||||
if (widget->mParent) {
|
||||
widget->mParent->removeChild(widget);
|
||||
}
|
||||
mChildWidgets.pushBack(widget);
|
||||
widget->mParent = this;
|
||||
}
|
||||
|
||||
void Widget::removeChild(Widget* widget) {
|
||||
auto node = mChildWidgets.find(widget);
|
||||
node->data->mParent = nullptr;
|
||||
mChildWidgets.removeNode(node);
|
||||
}
|
||||
|
||||
bool Widget::needUpdate() const {
|
||||
return !mArea.checkAnimationShouldEnd();
|
||||
}
|
||||
|
||||
void Widget::finishAnimations() {
|
||||
void Widget::endAnimations() {
|
||||
mArea.endAnimation();
|
||||
}
|
||||
|
||||
bool Widget::processesEvents() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Widget::updateAnimations() {
|
||||
mArea.updateCurrentRect();
|
||||
}
|
||||
|
||||
bool Widget::needsNextFrame() const {
|
||||
return !mArea.shouldEndTransition() || mInFocus;
|
||||
}
|
||||
|
||||
void Widget::adjustRect() {
|
||||
if (mChildren.empty()) return;
|
||||
|
||||
auto area = (*mChildren.begin())->getArea();
|
||||
for (auto widget : mChildren) {
|
||||
area.expand(widget->getArea());
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::adjustChildrenRect() {
|
||||
auto area = RectF({}, getArea().size);
|
||||
for (auto widget : mChildren) {
|
||||
widget->setArea(area);
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::mouseEnter() {
|
||||
mInFocus = true;
|
||||
triggerWidgetUpdate();
|
||||
}
|
||||
|
||||
void Widget::mouseLeave() {
|
||||
mInFocus = false;
|
||||
}
|
||||
|
||||
bool Widget::propagateEventsToChildren() const {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -12,9 +12,9 @@ namespace tp {
|
|||
void draw(Canvas& canvas) override;
|
||||
void process(const EventHandler& events) override;
|
||||
|
||||
[[nodiscard]] bool needUpdate() const override;
|
||||
[[nodiscard]] bool needsNextFrame() const override;
|
||||
|
||||
private:
|
||||
SpringRect mTestSpring;
|
||||
mutable SpringRect mTestSpring;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,22 +1,61 @@
|
|||
#pragma once
|
||||
|
||||
#include "Widget.hpp"
|
||||
#include "SimpleWidgets.hpp"
|
||||
#include "LayoutWidget.hpp"
|
||||
|
||||
namespace tp {
|
||||
class FloatingWidget : public Widget {
|
||||
public:
|
||||
FloatingWidget() = default;
|
||||
FloatingWidget() {
|
||||
setDebug("float", { 0.0, 0.9, 0.1, 0.7 });
|
||||
}
|
||||
|
||||
void process(const EventHandler& events) override;
|
||||
|
||||
void updateArea(RectF& area) const override;
|
||||
void adjustRect() override;
|
||||
|
||||
void draw(Canvas& canvas) override;
|
||||
|
||||
RectF resizeHandleRect();
|
||||
|
||||
bool propagateEventsToChildren() const override;
|
||||
|
||||
private:
|
||||
bool mIsFloating = false;
|
||||
bool mIsResizing = false;
|
||||
|
||||
halnf mHandleSize = 10;
|
||||
halnf mHandlePadding = 2;
|
||||
|
||||
Vec2F mPointerStart;
|
||||
Vec2F mPointerCurrent;
|
||||
};
|
||||
|
||||
class FloatingMenu : public FloatingWidget {
|
||||
public:
|
||||
FloatingMenu() {
|
||||
setDebug("float menu", { 0.0, 0.9, 0.1, 0.7 });
|
||||
|
||||
addChild(&mMenuLayout);
|
||||
|
||||
mMenuLayout.addChild(&mHeader);
|
||||
mMenuLayout.addChild(&mBodyLayout);
|
||||
|
||||
addToMenu(&mTestButton);
|
||||
|
||||
mHeader.setText("Menu");
|
||||
}
|
||||
|
||||
public:
|
||||
void addToMenu(Widget* widget) {
|
||||
mBodyLayout.addChild(widget);
|
||||
}
|
||||
|
||||
private:
|
||||
VerticalLayout mMenuLayout;
|
||||
VerticalLayout mBodyLayout;
|
||||
LabelWidget mHeader;
|
||||
|
||||
ButtonWidget mTestButton;
|
||||
};
|
||||
}
|
||||
56
WidgetsNew/public/LayoutWidget.hpp
Normal file
56
WidgetsNew/public/LayoutWidget.hpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include "Widget.hpp"
|
||||
|
||||
namespace tp {
|
||||
class DesktopLayout : public Widget {
|
||||
public:
|
||||
DesktopLayout() { setDebug("desktop layout", { 0, 0, 0.9, 0.9 }); }
|
||||
|
||||
void draw(Canvas& canvas) override {
|
||||
// canvas.rect(getRelativeArea(), RGBA(0, 0, 0, 1));
|
||||
}
|
||||
|
||||
void adjustRect() override {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void adjustChildrenRect() override {
|
||||
// todo : adjust by viewport pos
|
||||
}
|
||||
|
||||
[[nodiscard]] bool processesEvents() const override { return false; }
|
||||
};
|
||||
|
||||
|
||||
class VerticalLayout : public Widget {
|
||||
public:
|
||||
VerticalLayout() {
|
||||
setDebug("vertical", { 0.8, 0.1, 0.1, 0.9 });
|
||||
}
|
||||
|
||||
void draw(Canvas& canvas) override {
|
||||
// canvas.frame(getRelativeArea(), RGBA(1));
|
||||
}
|
||||
|
||||
void adjustChildrenRect() override {
|
||||
auto content = &mChildren;
|
||||
auto area = getRelativeArea();
|
||||
|
||||
auto iterRect = area.shrink(mPadding);
|
||||
iterRect.height = ((iterRect.height + mGap) / (float) content->size()) - mGap;
|
||||
|
||||
for (auto widget :(*content)) {
|
||||
widget->setArea(iterRect);
|
||||
iterRect.y += iterRect.height + mGap;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool processesEvents() const override { return false; }
|
||||
|
||||
private:
|
||||
halnf mGap = 10;
|
||||
halnf mPadding = 10;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -2,46 +2,54 @@
|
|||
|
||||
#include "Widget.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
namespace tp {
|
||||
class RootWidget {
|
||||
class RootWidget : public WidgetManagerInterface {
|
||||
|
||||
// path from leaf to root of widgets that need to be updated
|
||||
struct ActivePath {
|
||||
struct ActiveNode {
|
||||
Widget* widget = nullptr;
|
||||
Vec2F globalPos;
|
||||
};
|
||||
|
||||
Buffer<ActiveNode> path;
|
||||
struct ActiveTreeNode {
|
||||
ActiveTreeNode* parent = nullptr;
|
||||
std::set<ActiveTreeNode*> children;
|
||||
Widget* widget = nullptr;
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
RootWidget() = default;
|
||||
RootWidget() { setDebug("root", RGBA(1)); }
|
||||
|
||||
void setRootWidget(Widget* widget);
|
||||
void updateWidget(Widget*);
|
||||
|
||||
static void setWidgetArea(Widget& widget, const RectF& rect);
|
||||
|
||||
public:
|
||||
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 updateTreeToProcess();
|
||||
void updateAnimations(ActiveTreeNode* iter);
|
||||
void drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos);
|
||||
|
||||
void processPaths(const Buffer<ActivePath>& path, EventHandler& events);
|
||||
void clearProcessedFlag(const Buffer<ActivePath>& paths);
|
||||
void drawDebug(Canvas& canvas, Widget* active, const Vec2F& pos);
|
||||
void findFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer);
|
||||
void handleFocusChanges(EventHandler& events);
|
||||
void getWidgetPath(Widget* widget, std::vector<Widget*>& out);
|
||||
void processActiveTree(ActiveTreeNode* iter, EventHandler& events);
|
||||
void processFocusItems(EventHandler& events);
|
||||
void adjustSizes(ActiveTreeNode* iter);
|
||||
|
||||
private:
|
||||
RectF mScreenArea;
|
||||
Widget* mRoot = nullptr;
|
||||
|
||||
Buffer<Widget*> mActiveWidgets;
|
||||
Buffer<ActivePath> mActivePaths[2]; // current and prev
|
||||
bool mFlipFlop = false;
|
||||
// frame to frame changes
|
||||
std::set<Widget*> mTriggeredWidgets;
|
||||
std::map<Widget*, ActiveTreeNode> mWidgetsToProcess;
|
||||
Widget* mInFocusWidget = nullptr;
|
||||
|
||||
bool mNeedUpdate = false;
|
||||
bool mDebug = false;
|
||||
};
|
||||
}
|
||||
|
|
@ -7,14 +7,17 @@
|
|||
namespace tp {
|
||||
class LabelWidget : public Widget {
|
||||
public:
|
||||
LabelWidget() = default;
|
||||
LabelWidget() {
|
||||
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 isPassThroughEvents() const override { return true; }
|
||||
[[nodiscard]] bool processesEvents() const override { return false; }
|
||||
|
||||
private:
|
||||
std::string mText = "Text";
|
||||
|
|
@ -33,11 +36,14 @@ namespace tp {
|
|||
void process(const EventHandler& eventHandler) override;
|
||||
void draw(Canvas& canvas) override;
|
||||
|
||||
[[nodiscard]] bool isPassThroughEvents() const override { return false; }
|
||||
void mouseEnter() override;
|
||||
void mouseLeave() override;
|
||||
|
||||
[[nodiscard]] bool needUpdate() const override;
|
||||
[[nodiscard]] bool processesEvents() const override { return true; }
|
||||
[[nodiscard]] bool needsNextFrame() const override;
|
||||
|
||||
void finishAnimations() override;
|
||||
void endAnimations() override;
|
||||
void updateAnimations() override;
|
||||
|
||||
private:
|
||||
std::function<void()> mAction;
|
||||
|
|
@ -45,7 +51,7 @@ namespace tp {
|
|||
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 };
|
||||
RGBA mColorHovered = { 0.0f, 0.4f, 0.4f, 1.f };
|
||||
RGBA mColor = { 0.0f, 0.0f, 0.0f, 1.f };
|
||||
};
|
||||
}
|
||||
|
|
@ -139,7 +139,7 @@ namespace tp {
|
|||
mEndPos.updateCurrentPosition();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool checkAnimationShouldEnd() const {
|
||||
[[nodiscard]] bool shouldEndTransition() const {
|
||||
return mStartPos.checkAnimationShouldEnd() && mEndPos.checkAnimationShouldEnd();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,11 @@
|
|||
#include "EventHandler.hpp"
|
||||
#include "Graphics.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace tp {
|
||||
class WidgetManagerInterface;
|
||||
|
||||
class Widget {
|
||||
friend class RootWidget;
|
||||
|
||||
|
|
@ -13,41 +17,68 @@ namespace tp {
|
|||
Widget();
|
||||
virtual ~Widget() = default;
|
||||
|
||||
virtual void process(const EventHandler& events) {}
|
||||
virtual void updateArea(RectF& area) const {}
|
||||
void setDebug(const char* name, RGBA col) {
|
||||
mName = name;
|
||||
mDebugColor = col;
|
||||
}
|
||||
|
||||
virtual void draw(Canvas& canvas);
|
||||
virtual void drawOverlay(Canvas& canvas) {}
|
||||
void addChild(Widget* child) {
|
||||
mChildren.push_back(child);
|
||||
child->mParent = this;
|
||||
|
||||
virtual bool isPassThroughEvents() const { return false; }
|
||||
triggerWidgetUpdate();
|
||||
child->triggerWidgetUpdate();
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual bool needUpdate() const;
|
||||
WidgetManagerInterface* getRoot();
|
||||
|
||||
virtual void finishAnimations();
|
||||
void triggerWidgetUpdate();
|
||||
|
||||
public:
|
||||
[[nodiscard]] RectF getRelativeArea() const;
|
||||
void setActive(bool active);
|
||||
virtual void process(const EventHandler& events) {}
|
||||
virtual void draw(Canvas& canvas) { canvas.rect(mArea.getCurrentRect(), RGBA(1.f), 10); }
|
||||
virtual void drawOverlay(Canvas& canvas) {}
|
||||
|
||||
void addChild(Widget* widget);
|
||||
virtual void mouseEnter();
|
||||
virtual void mouseLeave();
|
||||
|
||||
private:
|
||||
// size policies
|
||||
virtual void adjustRect();
|
||||
virtual void adjustChildrenRect();
|
||||
|
||||
[[nodiscard]] virtual bool processesEvents() const;
|
||||
[[nodiscard]] virtual bool propagateEventsToChildren() const;
|
||||
|
||||
[[nodiscard]] virtual bool needsNextFrame() const;
|
||||
|
||||
virtual void endAnimations();
|
||||
virtual void updateAnimations();
|
||||
|
||||
public:
|
||||
[[nodiscard]] RectF getArea() const;
|
||||
[[nodiscard]] RectF getRelativeArea() const;
|
||||
void setArea(const RectF& area);
|
||||
void removeChild(Widget* widget);
|
||||
|
||||
private:
|
||||
protected:
|
||||
Widget* mParent = nullptr;
|
||||
|
||||
std::vector<Widget*> mChildren;
|
||||
|
||||
// relative to the parent
|
||||
SpringRect mArea;
|
||||
|
||||
// tree structure of widgets
|
||||
List<Widget*> mChildWidgets;
|
||||
ualni mZValue = 0;
|
||||
|
||||
// if needed updates even though pointer is not in area
|
||||
bool mIsActive = false;
|
||||
bool mInFocus = false;
|
||||
|
||||
Widget* mParent = nullptr;
|
||||
// debug
|
||||
std::string mName = "widget base";
|
||||
Vec2F mLocalPoint;
|
||||
Vec2F mGlobalPoint;
|
||||
RGBA mDebugColor = { 1, 1, 1, 0.3 };
|
||||
};
|
||||
|
||||
bool mProcessedFlag = false;
|
||||
struct WidgetManagerInterface : public Widget {
|
||||
virtual void updateWidget(Widget*) = 0;
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue