Reorganize code in widgets

This commit is contained in:
IlyaShurupov 2024-10-14 16:41:13 +03:00
parent 1508a51cde
commit 7cf3328a40
31 changed files with 652 additions and 481 deletions

View file

@ -0,0 +1,96 @@
#include <algorithm>
#include "Layout.hpp"
#include "Widget.hpp"
using namespace tp;
const RectF& WidgetLayout::getArea() const {
return mWidget->mAreaCache;
}
RectF WidgetLayout::getAnimatedArea() const {
return mWidget->getArea();
}
void WidgetLayout::setArea(const RectF& area) {
mWidget->setAreaCache(area);
}
Widget* WidgetLayout::parent() const { return mWidget->mParent; }
const std::vector<Widget*>& WidgetLayout::children() const { return mWidget->mChildren; }
const Vec2F& WidgetLayout::getMinSize() { return mMinSize; }
void WidgetLayout::setMinSize(const Vec2F& size) { mMinSize = size; }
const Vec2<SizePolicy>& WidgetLayout::getSizePolicy() const { return mSizePolicy; }
void WidgetLayout::setSizePolicy(SizePolicy x, SizePolicy y) { mSizePolicy = { x, y }; }
RangeF WidgetLayout::pickRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical) const {
RangeF out;
switch (mSizePolicy[vertical]) {
case SizePolicy::Fixed: out = current; break;
case SizePolicy::Expanding: out = parent; break;
case SizePolicy::Minimal: out = children; break;
}
out = clampRange(out, children, parent, vertical);
return out;
}
void WidgetLayout::clampMinMaxSize() {
auto current = getArea();
current.size.clamp(mMinSize, mMaxSize);
setArea(current);
}
RangeF WidgetLayout::clampRange(const RangeF& current, const RangeF& child, const RangeF& parent, bool vertical) const {
auto out = current;
if (!children().empty()) {
auto clampedChild = child;
clampedChild.clamp(parent);
out.clamp(clampedChild, parent);
} else {
out.clamp(parent);
}
// clamp min max sizes
auto len = clamp(current.size(), mMinSize[vertical], mMaxSize[vertical]);
if (len != current.size()) {
out.resizeFromCenter(len);
}
return out;
}
RectF WidgetLayout::getChildrenEnclosure() const {
RectF out;
if (children().empty()) {
out = { getArea().center(), { 0, 0 } };
} else {
out = children().front()->getLayout()->getArea();
for (auto child : children()) {
out.expand(child->getLayout()->getArea());
}
out.pos += getArea().pos;
}
return out;
}
RectF WidgetLayout::getAvailableChildArea() const {
return getArea().relative();
}
RectF WidgetLayout::getParentEnclosure() const {
DEBUG_ASSERT(parent())
if (!parent()) return { { 0, 0 }, mMaxSize };
auto out = parent()->getLayout()->getAvailableChildArea();
return out;
}

View file

@ -1,169 +1,70 @@
#include "RootWidget.hpp"
#include "BasicLayout.hpp"
#include <algorithm>
#include "RootWidget.hpp"
#include "imgui.h"
using namespace tp;
WidgetLayout* WidgetManagerInterface::defaultLayout(Widget* widget) {
return new BasicLayout(widget);
}
RootWidget::RootWidget() {
setDebug("root", RGBA(1));
}
void RootWidget::setRootWidget(Widget* widget) {
mRoot = widget;
widget->mParent = this;
}
void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) {
mScreenArea = screenArea;
mRoot->setArea(screenArea);
if (mDebug) {
mScreenArea.size -= { 400, 0 };
events->setEnableKeyEvents(true);
if (events->isPressed(InputID::K)) mDebugStopProcessing = !mDebugStopProcessing;
if (mDebugStopProcessing) return;
}
mRoot->setArea(mScreenArea);
if (!mDebugManager.update(this, *events)) return;
// construct hierarchy tree of widgets to process
updateTreeToProcess();
mUpdateManager.updateTreeToProcess(mRoot);
updateAnimations();
updateAreaCache(mRoot, true);
// update all events and call all event processing callbacks
events->setEnableKeyEvents(true);
events->setCursorOrigin({ 0, 0 });
processFocusItems(*events);
events->setEnableKeyEvents(false);
events->setCursorOrigin({ 0, 0 });
processActiveTree(mRoot, *events, { 0, 0 });
mUpdateManager.processWidgets(mRoot, *events);
updateAreaCache(mRoot, true);
// update widget sizes base on individual size policies
processLayout();
mLayoutManager.adjust(mRoot);
updateAreaCache(mRoot, false);
// trigger some widgets by moise pointer
handleFocusChanges(*events);
mUpdateManager.handleFocusChanges(mRoot, *events);
// check triggered widgets for removal
erase_if(mTriggeredWidgets, [](auto iter) {
auto widget = iter.first;
auto flag = iter.second;
if (!flag) return false;
// if (mWidgetsToProcess.find(widget) == mWidgetsToProcess.end()) return false;
widget->updateAnimations();
auto end = !widget->needsNextFrame();
if (end) {
widget->endAnimations();
widget->mDebug.triggerReason = "del";
}
return end;
});
mUpdateManager.clean();
}
bool RootWidget::needsUpdate() const { return !mTriggeredWidgets.empty() || mDebugRedrawAlways; }
void RootWidget::debugDrawWidget(Widget* widget) {
ImGui::PushID(widget);
if (ImGui::CollapsingHeader(widget->mDebug.id.c_str())) {
ImGui::Text("trigger reason: %s", widget->mDebug.triggerReason.c_str());
auto area = widget->getAreaT();
if (ImGui::InputFloat4("rect", &area.x)) {
widget->setArea(area);
}
// ImGui::InputFloat2("min size", &widget->mMinSize.x);
// ImGui::InputFloat2("max size", &widget->mMaxSize.x);
// int sizePolicyX = int(widget->mSizePolicy.x);
// int sizePolicyY = int(widget->mSizePolicy.y);
// int layout = int(widget->mLayoutPolicy);
// if (ImGui::Combo("Size Policy X", &sizePolicyX, "Fixed\0Expanding\0Minimal\0")) {
// widget->setSizePolicy(SizePolicy(sizePolicyX), SizePolicy(sizePolicyY));
// }
// if (ImGui::Combo("Size Policy Y", &sizePolicyY, "Fixed\0Expanding\0Minimal\0")) {
// widget->setSizePolicy(SizePolicy(sizePolicyX), SizePolicy(sizePolicyY));
// }
// if (ImGui::Combo("Layout", &layout, "Passive\0Vertical\0Horizontal\0")) {
// widget->setLayoutPolicy(LayoutPolicy(layout));
// }
}
ImGui::PopID();
bool RootWidget::needsUpdate() const {
if (mDebugManager.isRedrawAlways()) return true;
return mUpdateManager.isPendingUpdates();
}
void RootWidget::drawFrame(Canvas& canvas) {
// draw from top to bottom
canvas.rect(mScreenArea, RGBA(0, 0, 0, 1));
canvas.rect(mRoot->getAreaT(), RGBA(0, 0, 0, 1));
// draw all tree from top to bottom
drawRecursion(canvas, mRoot, { 0, 0 });
ImGui::Checkbox("Draw debug", &mDebug);
if (mDebug) {
drawDebug(canvas, mRoot, { 0, 0 }, 0);
ImGui::Text("Triggered Widgets: %i", (int) mTriggeredWidgets.size());
ImGui::Text("Widgets To Process: %i", mDebugWidgetsToProcess);
ImGui::Text("To Toggle processing press k");
ImGui::Checkbox("Stop processing", &mDebugStopProcessing);
ImGui::Checkbox("Force new frames", &mDebugRedrawAlways);
if (ImGui::CollapsingHeader("Triggered")) {
if (ImGui::BeginListBox("##triggered", { -FLT_MIN, 300 })) {
for (auto widget : mTriggeredWidgets) {
debugDrawWidget(widget.first);
}
ImGui::EndListBox();
}
}
if (mInFocusWidget) {
if (ImGui::CollapsingHeader("Under cursor")) {
if (ImGui::BeginListBox("##under_cursor", { -FLT_MIN, 300 })) {
for (auto widget = mInFocusWidget; widget && widget != this; widget = widget->mParent) {
debugDrawWidget(widget);
}
ImGui::EndListBox();
}
}
}
}
mDebugManager.drawDebug(this, canvas);
}
void RootWidget::updateTreeToProcess() {
dfs(mRoot, [](Widget* widget) {
widget->mNeedsProcessing = false;
});
for (auto& [widget, flag] : mTriggeredWidgets) {
flag = true;
for (auto iter = widget; iter && iter != this; iter = iter->mParent) {
iter->mNeedsProcessing = true;
}
}
mDebugWidgetsToProcess = 0;
dfs(mRoot, [this](Widget*) {
mDebugWidgetsToProcess++;
});
}
void RootWidget::drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos) {
if (!active->mFlags.get(ENABLED)) return;
canvas.setOrigin(pos);
canvas.pushClamp({ pos, active->getArea().size });
@ -179,31 +80,6 @@ void RootWidget::drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos)
active->drawOverlay(canvas);
}
void RootWidget::drawDebug(Canvas& canvas, Widget* active, const Vec2F& pos, int depthOrder) {
auto area = RectF{ pos, active->getAreaT().size };
if (active->mNeedsProcessing) {
RGBA color = { 1, 0, 0, 1};
canvas.frame(area, color);
canvas.text((active->mDebug.id + ":" + std::to_string(depthOrder)).c_str(), area, 22, Canvas::Align::LC, 2, color);
}
if (active->mNeedsProcessing || active->mInFocus) {
canvas.circle(pos + active->mDebug.pLocal, 5, active->mDebug.col);
canvas.circle(active->mDebug.pGlobal, 15, active->mDebug.col);
}
if (active->mInFocus) {
RGBA color = { 1, 0, 0, 0.3f};
canvas.debugCross(area, color);
}
int orderIdx = 0;
for (auto child = active->mDepthOrder.lastNode(); child; child = child->prev) {
drawDebug(canvas, child->data, pos + child->data->getAreaT().pos, orderIdx++);
}
}
void RootWidget::setWidgetArea(Widget& widget, const RectF& rect) {
widget.mArea.setTargetRect(rect);
widget.mArea.endAnimation();
@ -215,85 +91,14 @@ void RootWidget::updateAnimations() {
});
}
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;
events.setCursorOrigin({ 0, 0 });
findFocusWidget(mRoot, &mInFocusWidget, events.getPointer());
// if (mInFocusWidget == prevFocus) return;
if (mInFocusWidget) updateWidget(mInFocusWidget, "focus entered");
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;
}
}
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;
}
}
size_t mostCommonIdx = 0;
if (!(path1.empty() || path2.empty())) {
while (path1[mostCommonIdx] == path2[mostCommonIdx] && mostCommonIdx < min(path1.size(), path2.size())) {
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) {
if (!iter->mArea.getTargetRect().isInside(pointer)) return;
*focus = iter;
for (auto child = iter->mDepthOrder.lastNode(); child; child = child->prev) {
findFocusWidget(child->data, focus, pointer - iter->mArea.getTargetRect().pos);
}
}
void RootWidget::updateWidget(Widget* widget, const char* reason) {
DEBUG_ASSERT(reason)
widget->mDebug.triggerReason = reason;
mTriggeredWidgets.insert({ widget, false });
mUpdateManager.scheduleUpdate(widget, reason);
}
void RootWidget::updateAreaCache(Widget* iter, bool read) {
if (!iter || !iter->mNeedsProcessing) return;
if (!iter || !iter->isUpdate()) return;
if (read) {
iter->mAreaCache = iter->getAreaT();
@ -314,70 +119,4 @@ void RootWidget::updateAreaCache(Widget* iter, bool read) {
for (auto child : iter->mDepthOrder) {
updateAreaCache(child.data(), read);
}
}
void RootWidget::processLayout() {
mLayoutManager.adjust(mRoot);
}
void RootWidget::processActiveTree(Widget* iter, EventHandler& events, Vec2F parent) {
if (!iter || !iter->mNeedsProcessing) return;
auto current = parent + iter->getAreaT().pos;
if (!iter->mInFocus) {
iter->mDebug.pGlobal = current;
events.setCursorOrigin(current);
iter->process(events);
}
for (auto child : iter->mDepthOrder) {
processActiveTree(child.data(), events, current);
}
}
void RootWidget::processFocusItems(EventHandler& events) {
if (!mInFocusWidget) return;
// FIXME : cringe
std::vector<Widget*> path;
getWidgetPath(mInFocusWidget, path);
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]->getAreaT().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]);
widget->mDebug.pGlobal = widgetGlobalPos[iter];
if (!eventsProcessed && widget->processesEvents()) {
events.setEnableKeyEvents(true);
widget->process(events);
events.setEnableKeyEvents(false);
eventsProcessed = true;
} else {
widget->process(events);
}
}
}
}

View file

@ -6,8 +6,10 @@ using namespace tp;
Widget::Widget() {
mArea.setTargetRect({ 100, 100, 10, 10 });
setLayout(new BasicLayout(this));
setLayout(WidgetManagerInterface::defaultLayout(this));
mArea.endAnimation();
mFlags.set(ENABLED, true);
}
Widget::~Widget() {
@ -72,11 +74,13 @@ void Widget::bringToBack() {
order.pushBack(node);
}
void Widget::mouseEnter() { mInFocus = true; }
void Widget::mouseLeave() { mInFocus = false; }
void Widget::mouseEnter() { mFlags.set(IN_FOCUS, true); }
void Widget::mouseLeave() { mFlags.set(IN_FOCUS, false); }
bool Widget::propagateEventsToChildren() const { return true; }
void Widget::setLayout(tp::WidgetLayout* layout) {
delete mLayout;
mLayout = layout;
triggerWidgetUpdate("chane layout");
}
@ -112,15 +116,15 @@ RectF Widget::getArea() const { return mArea.getCurrentRect(); }
RectF Widget::getAreaT() const { return mArea.getTargetRect(); }
// const RectF& Widget::getAreaCache() const { return mAreaCache; }
RectF Widget::getRelativeArea() const { return { {}, mArea.getCurrentRect().size }; }
RectF Widget::getRelativeAreaT() const { return { {}, mArea.getTargetRect().size }; }
// RectF Widget::getRelativeAreaCache() const { return { {}, mAreaCache.size }; }
void Widget::setDebug(const char* name, RGBA col) {
mDebug.id = name;
mDebug.col = col;
}
}
void Widget::setSizePolicy(SizePolicy x, SizePolicy y) {
getLayout()->setSizePolicy(x, y);
}

View file

@ -1,99 +1,11 @@
#include <algorithm>
#include "LayoutManager.hpp"
#include "BasicLayout.hpp"
#include "Widget.hpp"
#include <algorithm>
using namespace tp;
const RectF& WidgetLayout::getArea() const {
return mWidget->mAreaCache;
}
RectF WidgetLayout::getAnimatedArea() const {
return mWidget->getArea();
}
void WidgetLayout::setArea(const RectF& area) {
mWidget->setAreaCache(area);
}
Widget* WidgetLayout::parent() const { return mWidget->mParent; }
const std::vector<Widget*>& WidgetLayout::children() const { return mWidget->mChildren; }
const Vec2F& WidgetLayout::getMinSize() { return mMinSize; }
void WidgetLayout::setMinSize(const Vec2F& size) { mMinSize = size; }
const Vec2<SizePolicy>& WidgetLayout::getSizePolicy() const { return mSizePolicy; }
void WidgetLayout::setSizePolicy(SizePolicy x, SizePolicy y) { mSizePolicy = { x, y }; }
RangeF WidgetLayout::pickRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical) const {
RangeF out;
switch (mSizePolicy[vertical]) {
case SizePolicy::Fixed: out = current; break;
case SizePolicy::Expanding: out = parent; break;
case SizePolicy::Minimal: out = children; break;
}
out = clampRange(out, children, parent, vertical);
return out;
}
void WidgetLayout::clampMinMaxSize() {
auto current = getArea();
current.size.clamp(mMinSize, mMaxSize);
setArea(current);
}
RangeF WidgetLayout::clampRange(const RangeF& current, const RangeF& child, const RangeF& parent, bool vertical) const {
auto out = current;
if (!children().empty()) {
auto clampedChild = child;
clampedChild.clamp(parent);
out.clamp(clampedChild, parent);
} else {
out.clamp(parent);
}
// clamp min max sizes
auto len = clamp(current.size(), mMinSize[vertical], mMaxSize[vertical]);
if (len != current.size()) {
out.resizeFromCenter(len);
}
return out;
}
RectF WidgetLayout::getChildrenEnclosure() const {
RectF out;
if (children().empty()) {
out = { getArea().center(), { 0, 0 } };
} else {
out = children().front()->getLayout()->getArea();
for (auto child : children()) {
out.expand(child->getLayout()->getArea());
}
out.pos += getArea().pos;
}
return out;
}
RectF WidgetLayout::getAvailableChildArea() const {
return getArea().relative();
}
RectF WidgetLayout::getParentEnclosure() const {
DEBUG_ASSERT(parent())
if (!parent()) return { { 0, 0 }, mMaxSize };
auto out = parent()->getLayout()->getAvailableChildArea();
return out;
}
void BasicLayout::pickRect() {
auto current = getArea();
@ -209,16 +121,4 @@ void BasicLayout::adjustLayout(bool vertical) {
RectF BasicLayout::getAvailableChildArea() const {
auto out = WidgetLayout::getAvailableChildArea();
return out.scaleFromCenter(mLayoutMargin, true);
}
void LayoutManager::adjust(Widget* root) {
Widget::dfs(
root,
[](Widget* widget) {
widget->getLayout()->pickRect();
},
[](Widget* widget) {
widget->getLayout()->adjustChildrenRect();
}
);
}

View file

@ -0,0 +1,116 @@
#include "DebugManager.hpp"
#include "RootWidget.hpp"
#include "imgui.h"
using namespace tp;
bool DebugManager::update(RootWidget* rootWidget, EventHandler& events) {
if (mDebug) {
auto area = rootWidget->mRoot->getAreaT();
area.size -= { 400, 0 };
rootWidget->mRoot->setArea(area);
events.setEnableKeyEvents(true);
if (events.isPressed(InputID::K)) mDebugStopProcessing = !mDebugStopProcessing;
if (mDebugStopProcessing) return false;
}
return true;
}
void DebugManager::drawDebug(RootWidget* rootWidget, Canvas& canvas) {
ImGui::Checkbox("Draw debug", &mDebug);
auto& upd = rootWidget->mUpdateManager;
if (mDebug) {
recursiveDraw(canvas, rootWidget->mRoot, { 0, 0 }, 0);
ImGui::Text("Triggered Widgets: %i", (int) upd.mTriggeredWidgets.size());
ImGui::Text("Widgets To Process: %i", upd.mDebugWidgetsToProcess);
ImGui::Text("To Toggle processing press k");
ImGui::Checkbox("Stop processing", &mDebugStopProcessing);
ImGui::Checkbox("Force new frames", &mDebugRedrawAlways);
if (ImGui::CollapsingHeader("Triggered")) {
if (ImGui::BeginListBox("##triggered", { -FLT_MIN, 300 })) {
for (auto widget : upd.mTriggeredWidgets) {
widgetMenu(widget.first);
}
ImGui::EndListBox();
}
}
if (upd.mInFocusWidget) {
if (ImGui::CollapsingHeader("Under cursor")) {
if (ImGui::BeginListBox("##under_cursor", { -FLT_MIN, 300 })) {
for (auto widget = upd.mInFocusWidget; widget && widget->mParent; widget = widget->mParent) {
widgetMenu(widget);
}
ImGui::EndListBox();
}
}
}
}
}
void DebugManager::recursiveDraw(Canvas& canvas, Widget* active, const Vec2F& pos, int depthOrder) {
auto area = RectF{ pos, active->getAreaT().size };
if (active->isUpdate()) {
RGBA color = { 1, 0, 0, 1};
canvas.frame(area, color);
canvas.text((active->mDebug.id + ":" + std::to_string(depthOrder)).c_str(), area, 22, Canvas::Align::LC, 2, color);
}
if (active->mFlags.get(Widget::IN_FOCUS)) {
if (active->isUpdate()) {
canvas.circle(pos + active->mDebug.pLocal, 5, active->mDebug.col);
canvas.circle(active->mDebug.pGlobal, 15, active->mDebug.col);
}
RGBA color = { 1, 0, 0, 0.3f};
canvas.debugCross(area, color);
}
int orderIdx = 0;
for (auto child = active->mDepthOrder.lastNode(); child; child = child->prev) {
recursiveDraw(canvas, child->data, pos + child->data->getAreaT().pos, orderIdx++);
}
}
void DebugManager::widgetMenu(Widget* widget) {
ImGui::PushID(widget);
if (ImGui::CollapsingHeader(widget->mDebug.id.c_str())) {
ImGui::Text("trigger reason: %s", widget->mDebug.triggerReason.c_str());
auto area = widget->getAreaT();
if (ImGui::InputFloat4("rect", &area.x)) {
widget->setArea(area);
}
// ImGui::InputFloat2("min size", &widget->mMinSize.x);
// ImGui::InputFloat2("max size", &widget->mMaxSize.x);
// int sizePolicyX = int(widget->mSizePolicy.x);
// int sizePolicyY = int(widget->mSizePolicy.y);
// int layout = int(widget->mLayoutPolicy);
// if (ImGui::Combo("Size Policy X", &sizePolicyX, "Fixed\0Expanding\0Minimal\0")) {
// widget->setSizePolicy(SizePolicy(sizePolicyX), SizePolicy(sizePolicyY));
// }
// if (ImGui::Combo("Size Policy Y", &sizePolicyY, "Fixed\0Expanding\0Minimal\0")) {
// widget->setSizePolicy(SizePolicy(sizePolicyX), SizePolicy(sizePolicyY));
// }
// if (ImGui::Combo("Layout", &layout, "Passive\0Vertical\0Horizontal\0")) {
// widget->setLayoutPolicy(LayoutPolicy(layout));
// }
}
ImGui::PopID();
}

View file

@ -0,0 +1,17 @@
#include "LayoutManager.hpp"
#include "Widget.hpp"
using namespace tp;
void LayoutManager::adjust(Widget* root) {
Widget::dfs(
root,
[](Widget* widget) {
widget->getLayout()->pickRect();
},
[](Widget* widget) {
widget->getLayout()->adjustChildrenRect();
}
);
}

View file

@ -0,0 +1,191 @@
#include "UpdateManager.hpp"
using namespace tp;
void UpdateManager::processWidgets(Widget* root, EventHandler& events) {
events.setEnableKeyEvents(true);
events.setCursorOrigin({ 0, 0 });
processFocusItems(events);
events.setEnableKeyEvents(false);
events.setCursorOrigin({ 0, 0 });
processActiveTree(root, events, { 0, 0 });
}
void UpdateManager::clean() {
erase_if(mTriggeredWidgets, [](auto iter) {
auto widget = iter.first;
auto flag = iter.second;
if (!flag) return false;
// if (mWidgetsToProcess.find(widget) == mWidgetsToProcess.end()) return false;
widget->updateAnimations();
auto end = !widget->needsNextFrame();
if (end) {
widget->endAnimations();
widget->mDebug.triggerReason = "del";
}
return end;
});
}
void UpdateManager::scheduleUpdate(Widget* widget, const char* reason) {
widget->mDebug.triggerReason = reason;
mTriggeredWidgets.insert({ widget, false });
}
void UpdateManager::updateTreeToProcess(Widget* root) {
Widget::dfs(root, [](Widget* widget) {
widget->mFlags.set(Widget::NEEDS_UPDATE, false);
});
for (auto& [widget, flag] : mTriggeredWidgets) {
flag = true;
for (auto iter = widget; iter && iter->mParent; iter = iter->mParent) {
iter->mFlags.set(Widget::NEEDS_UPDATE, true);
}
}
mDebugWidgetsToProcess = 0;
Widget::dfs(root, [this](Widget*) {
mDebugWidgetsToProcess++;
});
}
void UpdateManager::getWidgetPath(Widget* widget, std::vector<Widget*>& out) {
if (!widget) return;
for (auto iter = widget; iter && iter->mParent; 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 UpdateManager::handleFocusChanges(Widget* root, EventHandler& events) {
auto prevFocus = mInFocusWidget;
events.setCursorOrigin({ 0, 0 });
findFocusWidget(root, &mInFocusWidget, events.getPointer());
// if (mInFocusWidget == prevFocus) return;
if (mInFocusWidget) scheduleUpdate(mInFocusWidget, "focus entered");
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;
}
}
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;
}
}
size_t mostCommonIdx = 0;
if (!(path1.empty() || path2.empty())) {
while (path1[mostCommonIdx] == path2[mostCommonIdx] && mostCommonIdx < min(path1.size(), path2.size())) {
mostCommonIdx++;
}
}
mostCommonIdx--;
for (auto i = 0; i < path1.size(); i++) {
path1[i]->mFlags.set(Widget::IN_FOCUS, false);
if (i > mostCommonIdx && i < propLen1) path1[i]->mouseLeave();
}
for (auto i = 0; i < path2.size(); i++) {
path2[i]->mFlags.set(Widget::IN_FOCUS, true);
if (i > mostCommonIdx && i < propLen2) path2[i]->mouseEnter();
}
}
void UpdateManager::findFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer) {
if (!iter->mArea.getTargetRect().isInside(pointer) || !iter->mFlags.get(Widget::ENABLED)) return;
*focus = iter;
for (auto child = iter->mDepthOrder.lastNode(); child; child = child->prev) {
findFocusWidget(child->data, focus, pointer - iter->mArea.getTargetRect().pos);
}
}
void UpdateManager::processActiveTree(Widget* iter, EventHandler& events, Vec2F parent) {
if (!iter || !iter->isUpdate()) return;
auto current = parent + iter->getAreaT().pos;
if (!iter->mFlags.get(Widget::IN_FOCUS)) {
iter->mDebug.pGlobal = current;
events.setCursorOrigin(current);
iter->process(events);
}
for (auto child : iter->mDepthOrder) {
processActiveTree(child.data(), events, current);
}
}
void UpdateManager::processFocusItems(EventHandler& events) {
if (!mInFocusWidget) return;
// FIXME : cringe
std::vector<Widget*> path;
getWidgetPath(mInFocusWidget, path);
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]->getAreaT().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]);
widget->mDebug.pGlobal = widgetGlobalPos[iter];
if (!eventsProcessed && widget->processesEvents()) {
events.setEnableKeyEvents(true);
widget->process(events);
events.setEnableKeyEvents(false);
eventsProcessed = true;
} else {
widget->process(events);
}
}
}

View file

@ -3,7 +3,8 @@
using namespace tp;
DockWidget::DockWidget() {
DockWidget::DockWidget() : Widget() {
setDebug("dock", {});
setLayout(new DockLayout(this));
}
@ -39,7 +40,13 @@ void DockWidget::setCenterWidget(Widget* widget) {
}
void DockWidget::toggleWidgetVisibility(DockLayout::Side side) {
layout()->toggleWidgetVisibility(side);
if (layout()->sideExists(side)) {
auto widget = layout()->getSideWidget(side);
widget->setEnabled(!layout()->isSideVisible(side));
widget->bringToBack();
layout()->toggleWidgetVisibility(side);
}
}
void DockWidget::process(const EventHandler& events) {