Replace Old Widgets
This commit is contained in:
parent
cce8f0e1bc
commit
fcd5eb2d2a
81 changed files with 388 additions and 278 deletions
177
Widgets/private/managers/DebugManager.cpp
Normal file
177
Widgets/private/managers/DebugManager.cpp
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
#include "DebugManager.hpp"
|
||||
#include "RootWidget.hpp"
|
||||
#include "BasicLayout.hpp"
|
||||
|
||||
#include "imgui.h"
|
||||
#include <sstream>
|
||||
|
||||
using namespace tp;
|
||||
|
||||
DebugManager tp::gDebugWidget;
|
||||
|
||||
#define LIST_SIZE { -FLT_MIN, 150 }
|
||||
|
||||
bool DebugManager::update(RootWidget* rootWidget, EventHandler& events) {
|
||||
mRootWidget = rootWidget;
|
||||
|
||||
events.setEnableKeyEvents(true);
|
||||
if (events.isPressed(InputID::D)) mDebug = !mDebug;
|
||||
|
||||
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;
|
||||
|
||||
if (auto widget = rootWidget->mUpdateManager.mInFocusWidget) {
|
||||
if (auto lay = dynamic_cast<BasicLayout*>(widget->getLayout())) {
|
||||
if (events.isPressed(InputID::V)) lay->setLayoutPolicy(LayoutPolicy::Vertical);
|
||||
if (events.isPressed(InputID::H)) lay->setLayoutPolicy(LayoutPolicy::Horizontal);
|
||||
|
||||
auto sizing = lay->getSizePolicy();
|
||||
|
||||
if (events.isDown(InputID::X)) {
|
||||
if (events.isPressed(InputID::S)) lay->setSizePolicy(SizePolicy::Minimal, sizing.y);
|
||||
if (events.isPressed(InputID::E)) lay->setSizePolicy(SizePolicy::Expanding, sizing.y);
|
||||
}
|
||||
if (events.isDown(InputID::Y)) {
|
||||
if (events.isPressed(InputID::S)) lay->setSizePolicy(sizing.x, SizePolicy::Minimal);
|
||||
if (events.isPressed(InputID::E)) lay->setSizePolicy(sizing.x, SizePolicy::Expanding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (auto breakWidget = rootWidget->mUpdateManager.mInFocusWidget) {
|
||||
if (events.isPressed(InputID::P)) mProcBreakpoints.insert(breakWidget);
|
||||
if (events.isPressed(InputID::L)) mLayBreakpoints.insert(breakWidget);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DebugManager::drawDebug(RootWidget* rootWidget, Canvas& canvas) {
|
||||
mRootWidget = rootWidget;
|
||||
|
||||
if (!mDebug) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ImGui::Checkbox("Draw debug", &mDebug);
|
||||
|
||||
ImGui::SameLine(); ImGui::Text("To Toggle processing press k");
|
||||
|
||||
auto& upd = rootWidget->mUpdateManager;
|
||||
|
||||
recursiveDraw(canvas, &rootWidget->mRoot, { 0, 0 }, 0);
|
||||
|
||||
ImGui::Text("Triggered: %i", (int) upd.mTriggeredWidgets.size());
|
||||
ImGui::SameLine(); ImGui::Text("Processing: %i", upd.mDebugWidgetsToProcess);
|
||||
|
||||
ImGui::Checkbox("Stop processing", &mDebugStopProcessing);
|
||||
ImGui::SameLine(); ImGui::Checkbox("Force new frames", &mDebugRedrawAlways);
|
||||
ImGui::SameLine(); ImGui::Checkbox("Slow-mo", &mSlowMotion);
|
||||
|
||||
if (upd.mInFocusWidget) {
|
||||
ImGui::Text("Under cursor");
|
||||
{
|
||||
if (ImGui::BeginListBox("##under_cursor", LIST_SIZE)) {
|
||||
for (auto widget = upd.mInFocusWidget; widget && widget->mParent; widget = widget->mParent) {
|
||||
widgetMenu(widget);
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text("Triggered");
|
||||
{
|
||||
if (ImGui::BeginListBox("##triggered", LIST_SIZE)) {
|
||||
for (auto widget : upd.mTriggeredWidgets) {
|
||||
widgetMenu(widget.first);
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
}
|
||||
|
||||
drawLayoutOrder();
|
||||
}
|
||||
|
||||
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 + std::to_string((long) widget)).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);
|
||||
}
|
||||
|
||||
auto layout = widget->getLayout();
|
||||
|
||||
ImGui::InputFloat2("min size", &layout->mMinSize.x);
|
||||
ImGui::InputFloat2("max size", &layout->mMaxSize.x);
|
||||
|
||||
{
|
||||
int sizePolicyX = int(widget->getLayout()->getSizePolicy().x);
|
||||
int sizePolicyY = int(widget->getLayout()->getSizePolicy().y);
|
||||
|
||||
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 (auto pBasicLayout = dynamic_cast<BasicLayout*>(widget->getLayout())) {
|
||||
int policy = int(pBasicLayout->mLayoutPolicy);
|
||||
if (ImGui::Combo("Layout", &policy, "Passive\0Vertical\0Horizontal\0")) {
|
||||
pBasicLayout->mLayoutPolicy = LayoutPolicy(policy);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void DebugManager::drawLayoutOrder() {
|
||||
ImGui::Text("Layout Processing Order");
|
||||
if (ImGui::BeginListBox("##layout_order", LIST_SIZE)) {
|
||||
for (auto iter : mRootWidget->mLayoutManager.mLayOrder) {
|
||||
ImGui::Text("%i %s (%s)", iter.second, iter.first->mDebug.id.c_str(), std::to_string((long) iter.first).c_str());
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
}
|
||||
98
Widgets/private/managers/LayoutManager.cpp
Normal file
98
Widgets/private/managers/LayoutManager.cpp
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
#include "LayoutManager.hpp"
|
||||
#include "DebugManager.hpp"
|
||||
|
||||
#include "DockLayout.hpp"
|
||||
#include "ScrollableLayout.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace tp;
|
||||
|
||||
void LayoutManager::adjust(Widget* root) {
|
||||
for (auto i : IterRange(2)) {
|
||||
mVertical = bool(i);
|
||||
|
||||
mDepGraph.clear();
|
||||
mLayOrder.clear();
|
||||
mRoots.clear();
|
||||
|
||||
findDependencies(root);
|
||||
|
||||
// TODO : implement better topological-sort
|
||||
for (auto iterRoot : mRoots) {
|
||||
topologicalSort(iterRoot);
|
||||
}
|
||||
|
||||
adjustLayouts();
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutManager::findDependencies(Widget* root) {
|
||||
mDepGraph.insert({ root, {} });
|
||||
|
||||
Widget::dfs(root, [this](Widget* widget) { mDepGraph.insert({ widget, {} }); });
|
||||
|
||||
for (auto& [widget, deps] : mDepGraph) {
|
||||
for (auto child : widget->mChildren) {
|
||||
mDepGraph.insert({ child, {} });
|
||||
|
||||
if (auto depOrder = getLayoutOrder(widget->getLayout(), child->getLayout())) {
|
||||
if (depOrder > 0) {
|
||||
deps.depends.push_back(child);
|
||||
mDepGraph[child].references++;
|
||||
} else {
|
||||
mDepGraph[child].depends.push_back(widget);
|
||||
mDepGraph[widget].references++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// find root
|
||||
for (auto& node : mDepGraph) {
|
||||
if (node.second.references == 0) {
|
||||
mRoots.push_back(node.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutManager::topologicalSort(Widget* root, int depth) {
|
||||
mDepGraph.at(root).depth = max(mDepGraph.at(root).depth, depth);
|
||||
|
||||
for (auto& dep : mDepGraph.at(root).depends) {
|
||||
topologicalSort(dep, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutManager::adjustLayouts() {
|
||||
for (auto& iter : mDepGraph) {
|
||||
mLayOrder.emplace_back(iter.first, iter.second.depth);
|
||||
}
|
||||
|
||||
std::sort(mLayOrder.begin(), mLayOrder.end(), [](auto first, auto second){
|
||||
return first.second > second.second;
|
||||
});
|
||||
|
||||
for (auto& [iter, _] : mLayOrder) {
|
||||
iter->getLayout()->updateLayout(mVertical);
|
||||
}
|
||||
}
|
||||
|
||||
static int sizePolicyDep[3][3] = {
|
||||
// f s e child
|
||||
{ 1, 1, 1 }, // parent fixed
|
||||
{ 1, 1, 1 }, // parent shrink
|
||||
{ 1, 1, 1 }, // parent expand
|
||||
};
|
||||
|
||||
int LayoutManager::getLayoutOrder(WidgetLayout* parent, WidgetLayout* child) const {
|
||||
if (!parent || !child) return 0;
|
||||
|
||||
auto policyParent = parent->getSizePolicy()[mVertical];
|
||||
auto policyChild = parent->getSizePolicy()[mVertical];
|
||||
|
||||
if (dynamic_cast<DockLayout*>(parent)) return -1;
|
||||
// if (dynamic_cast<ScrollableLayout*>(parent)) return -1;
|
||||
|
||||
return sizePolicyDep[int(policyParent)][int(policyChild)];
|
||||
}
|
||||
211
Widgets/private/managers/UpdateManager.cpp
Normal file
211
Widgets/private/managers/UpdateManager.cpp
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
#include "UpdateManager.hpp"
|
||||
#include "DebugManager.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++) {
|
||||
swapV(out[i], out[out.size() - i - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateManager::handleFocusChanges(Widget* root, EventHandler& events) {
|
||||
auto prevFocus = mInFocusWidget;
|
||||
|
||||
events.setCursorOrigin({ 0, 0 });
|
||||
|
||||
mInFocusWidget = nullptr;
|
||||
|
||||
if (!mFocusLockWidget) {
|
||||
findFocusWidget(root, &mInFocusWidget, events.getPointer());
|
||||
} else {
|
||||
mInFocusWidget = mFocusLockWidget;
|
||||
}
|
||||
|
||||
// if (mInFocusWidget == prevFocus) return;
|
||||
if (mInFocusWidget) scheduleUpdate(mInFocusWidget, "focus entered");
|
||||
|
||||
if (!mInFocusWidget && !prevFocus) return;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
int 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;
|
||||
|
||||
if (iter->processesEvents()) {
|
||||
*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);
|
||||
procWidget(iter, events, false);
|
||||
}
|
||||
|
||||
for (auto child : iter->mDepthOrder) {
|
||||
processActiveTree(child.data(), events, current);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateManager::processFocusItems(EventHandler& events) {
|
||||
if (!mInFocusWidget) return;
|
||||
|
||||
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()) {
|
||||
procWidget(widget, events, true);
|
||||
eventsProcessed = true;
|
||||
} else {
|
||||
procWidget(widget, events, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateManager::procWidget(Widget* widget, EventHandler& events, bool withEvents) {
|
||||
events.setEnableKeyEvents(withEvents);
|
||||
gDebugWidget.checkProcBreakPoints(widget);
|
||||
widget->process(events);
|
||||
}
|
||||
|
||||
void UpdateManager::lockFocus(tp::Widget* widget) {
|
||||
mFocusLockWidget = widget;
|
||||
}
|
||||
|
||||
void UpdateManager::freeFocus(tp::Widget* widget) {
|
||||
// DEBUG_ASSERT(mFocusLockWidget == widget)
|
||||
mFocusLockWidget = nullptr;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue