Fix sizing issues and others

This commit is contained in:
IlyaShurupov 2024-10-11 01:48:21 +03:00
parent b2c393d23a
commit e9a4801d9e
12 changed files with 325 additions and 142 deletions

View file

@ -41,6 +41,8 @@ DebugGUI::DebugGUI(Window* window) {
// appearance // appearance
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
return;
auto& colors = ImGui::GetStyle().Colors; auto& colors = ImGui::GetStyle().Colors;
colors[ImGuiCol_WindowBg] = ImVec4{ 0.1f, 0.105f, 0.11f, 1.0f }; colors[ImGuiCol_WindowBg] = ImVec4{ 0.1f, 0.105f, 0.11f, 1.0f };

View file

@ -6,7 +6,7 @@
using namespace tp; using namespace tp;
Application::Application() { Application::Application() {
mWindow = Window::createWindow(); mWindow = Window::createWindow({1500, 900});
mGraphics = new Graphics(mWindow); mGraphics = new Graphics(mWindow);
mDrawTimer.setDuration(1000.f / mDrawPerSecond); mDrawTimer.setDuration(1000.f / mDrawPerSecond);

View file

@ -8,10 +8,20 @@
using namespace tp; using namespace tp;
/*
* adjust child widgets in the layout view
* make scrollable area
* change Widget::clampMinMax function to include child enclosure
* refactor all sizing in Widget -> move to separate files
* refactor root widget -> reorganize code
*/
class WidgetApplication : public Application { class WidgetApplication : public Application {
public: public:
WidgetApplication() { WidgetApplication() {
setup2(); setup1();
} }
void setup1() { void setup1() {
@ -52,6 +62,21 @@ public:
// mLayoutWidget.addChild(&mLabel); // mLayoutWidget.addChild(&mLabel);
} }
void setup3() {
mRootWidget.setRootWidget(&mDesktopLayout);
mDesktopLayout.addChild(&mFloatingWidget);
mFloatingWidget.addChild(&mButton);
mFloatingWidget.addChild(&mButton2);
mFloatingWidget.addChild(&mButton3);
mButton.setSizePolicy(Widget::SizePolicy::Minimal, Widget::SizePolicy::Expanding);
mButton2.setSizePolicy(Widget::SizePolicy::Minimal, Widget::SizePolicy::Expanding);
mButton3.setSizePolicy(Widget::SizePolicy::Expanding, Widget::SizePolicy::Expanding);
RootWidget::setWidgetArea(mFloatingWidget, { 100, 100, 400, 400 });
}
void setup2() { void setup2() {
mRootWidget.setRootWidget(&mDesktopLayout); mRootWidget.setRootWidget(&mDesktopLayout);
mDesktopLayout.addChild(&mWidget); mDesktopLayout.addChild(&mWidget);
@ -60,17 +85,17 @@ public:
mWidget.addChild(&mButton2); mWidget.addChild(&mButton2);
mWidget.addChild(&mButton3); mWidget.addChild(&mButton3);
mButton.setSizePolicy(Widget::SizePolicy::Expand, Widget::SizePolicy::Contract); mButton.setSizePolicy(Widget::SizePolicy::Expanding, Widget::SizePolicy::Minimal);
mButton2.setSizePolicy(Widget::SizePolicy::Expand, Widget::SizePolicy::Contract); mButton2.setSizePolicy(Widget::SizePolicy::Expanding, Widget::SizePolicy::Minimal);
mButton3.setSizePolicy(Widget::SizePolicy::Expand, Widget::SizePolicy::Contract); mButton3.setSizePolicy(Widget::SizePolicy::Expanding, Widget::SizePolicy::Minimal);
mWidget.setSizePolicy(Widget::SizePolicy::Contract, Widget::SizePolicy::Contract); mWidget.setSizePolicy(Widget::SizePolicy::Minimal, Widget::SizePolicy::Minimal);
mWidget.setLayoutPolicy(Widget::LayoutPolicy::Horizontally); mWidget.setLayoutPolicy(Widget::LayoutPolicy::Horizontally);
mButton.setAction([this]() { mButton2.setMinSize(mButton2.getMinSize() + 10); }); mButton.setAction([this]() { mButton2.setMinSize(mButton2.getMinSize() + 10); });
mButton2.setAction([this]() { mButton.setMinSize(mButton.getMinSize() + 10); } ); mButton2.setAction([this]() { mButton.setMinSize(mButton.getMinSize() + 10); } );
RootWidget::setWidgetArea(mWidget, { 300, 100, 150, 200 }); RootWidget::setWidgetArea(mWidget, { 400, 100, 150, 200 });
} }
void processFrame(EventHandler* eventHandler, halnf deltaTime) override { void processFrame(EventHandler* eventHandler, halnf deltaTime) override {
@ -96,6 +121,7 @@ private:
ButtonWidget mButton3; ButtonWidget mButton3;
ButtonWidget mButton4; ButtonWidget mButton4;
FloatingWidget mFloatingWidget;
FloatingMenu mFloatingMenu; FloatingMenu mFloatingMenu;
FloatingMenu mFloatingMenu2; FloatingMenu mFloatingMenu2;

View file

@ -38,10 +38,6 @@ void DockLayoutWidget::process(const EventHandler& events) {
if (!mPreviewWidget) { if (!mPreviewWidget) {
handleResizeEvents(events); handleResizeEvents(events);
} }
calculateSideAreas();
calculateResizeHandles();
updateChildSideWidgets();
} }
void DockLayoutWidget::draw(Canvas& canvas) { void DockLayoutWidget::draw(Canvas& canvas) {
@ -146,7 +142,7 @@ void DockLayoutWidget::toggleWidgetVisibility(Side side) {
} }
void DockLayoutWidget::calculateSideAreas() { void DockLayoutWidget::calculateSideAreas() {
auto startArea = getRelativeArea(); auto startArea = getRelativeAreaCache();
for (auto& sideWidget : mSideWidgets) { for (auto& sideWidget : mSideWidgets) {
const auto side = sideWidget.order; const auto side = sideWidget.order;
@ -246,7 +242,7 @@ void DockLayoutWidget::handleResizeEvents(const EventHandler& events) {
sideWidget.resizeHandle.active = true; sideWidget.resizeHandle.active = true;
mResizing = true; mResizing = true;
triggerWidgetUpdate(); triggerWidgetUpdate("new docked child");
} }
} }
} }
@ -275,12 +271,12 @@ void DockLayoutWidget::updateChildSideWidgets() {
if (!isSideVisible(Side(i))) { if (!isSideVisible(Side(i))) {
// widget->mEnable = false; // widget->mEnable = false;
} else { } else {
widget->setArea(mSideWidgets[i].area); widget->setAreaCache(mSideWidgets[i].area);
// widget->mEnable = true; // widget->mEnable = true;
} }
} }
if (mCenterWidget) mCenterWidget->setArea(mCenterArea); if (mCenterWidget) mCenterWidget->setAreaCache(mCenterArea);
} }
// update depth order // update depth order
@ -369,3 +365,9 @@ auto DockLayoutWidget::getSideFromWidget(Widget* widget) -> Side {
} }
return DockLayoutWidget::NONE; return DockLayoutWidget::NONE;
} }
void DockLayoutWidget::adjustChildrenRect() {
calculateSideAreas();
calculateResizeHandles();
updateChildSideWidgets();
}

View file

@ -5,18 +5,19 @@ using namespace tp;
void FloatingWidget::process(const EventHandler& events) { void FloatingWidget::process(const EventHandler& events) {
const auto relativePointer = events.getPointer(); const auto relativePointer = events.getPointer();
bool inside = getRelativeArea().isInside(relativePointer); bool inside = getRelativeAreaT().isInside(relativePointer);
if (inside && events.isPressed(InputID::MOUSE1)) { if (inside && events.isPressed(InputID::MOUSE1)) {
mPointerStart = relativePointer; mPointerStart = relativePointer;
mIsFloating = true; mIsFloating = true;
if (resizeHandleRect().isInside(relativePointer)) {
mIsResizing = true;
}
bringToFront(); bringToFront();
} }
if (mIsFloating && resizeHandleRect().isInside(relativePointer)) {
mIsResizing = true;
}
if (mIsFloating && events.isReleased(InputID::MOUSE1)) { if (mIsFloating && events.isReleased(InputID::MOUSE1)) {
mIsFloating = false; mIsFloating = false;
@ -30,18 +31,25 @@ void FloatingWidget::process(const EventHandler& events) {
void FloatingWidget::pickRect() { void FloatingWidget::pickRect() {
if (mIsFloating) { if (mIsFloating) {
auto area = getArea(); auto area = getAreaCache();
if (mIsResizing) { if (mIsResizing) {
mPointerCurrent.clamp(mMinSize, mMaxSize);
area.size = mPointerCurrent + mHandleSize / 2.f; area.size = mPointerCurrent + mHandleSize / 2.f;
for (auto child : mChildren) {
child->triggerWidgetUpdate("floating menu resized");
}
} else if (mIsFloating) { } else if (mIsFloating) {
area.pos += mPointerCurrent - mPointerStart; area.pos += mPointerCurrent - mPointerStart;
} }
setArea(area); setAreaCache(area);
} else {
Widget::pickRect();
} }
clampMinMaxSize();
} }
void FloatingWidget::draw(Canvas& canvas) { void FloatingWidget::draw(Canvas& canvas) {
@ -64,3 +72,7 @@ bool FloatingWidget::propagateEventsToChildren() const {
bool FloatingWidget::isFloating() const { bool FloatingWidget::isFloating() const {
return mIsFloating; return mIsFloating;
} }
bool FloatingWidget::needsNextFrame() const {
return Widget::needsNextFrame() || isFloating();
}

View file

@ -12,15 +12,15 @@ void RootWidget::setRootWidget(Widget* widget) {
} }
void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) { void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) {
mScreenArea = screenArea;
if (mDebug) { if (mDebug) {
events->setEnableKeyEvents(true); mScreenArea.size -= { 400, 0 };
if (events->isPressed(InputID::K)) mDebugStopProcessing = !mDebugStopProcessing; if (events->isPressed(InputID::K)) mDebugStopProcessing = !mDebugStopProcessing;
if (mDebugStopProcessing) return; if (mDebugStopProcessing) return;
} }
mScreenArea = screenArea; mRoot->setArea(mScreenArea);
mRoot->setArea(screenArea);
// construct hierarchy tree of widgets to process // construct hierarchy tree of widgets to process
updateTreeToProcess(); updateTreeToProcess();
@ -31,24 +31,37 @@ void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) {
updateAnimations(root); updateAnimations(root);
// update all events and call all event processing callbacks // update all events and call all event processing callbacks
EventHandler dummyEvents; events->setEnableKeyEvents(true);
events->setCursorOrigin({ 0, 0 });
processFocusItems(*events); processFocusItems(*events);
processActiveTree(root, dummyEvents);
events->setEnableKeyEvents(false);
events->setCursorOrigin({ 0, 0 });
processActiveTree(root, *events, { 0, 0 });
updateAreaCache(root, true);
// update widget sizes base on individual size policies // update widget sizes base on individual size policies
adjustSizes(root); adjustSizes(root);
updateAreaCache(root, false);
// trigger some widgets by moise pointer // trigger some widgets by moise pointer
handleFocusChanges(*events); handleFocusChanges(*events);
// check triggered widgets for removal // check triggered widgets for removal
erase_if(mTriggeredWidgets, [this](auto widget) { 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(); widget->updateAnimations();
if (mWidgetsToProcess.find(widget) == mWidgetsToProcess.end()) return false;
auto end = !widget->needsNextFrame(); auto end = !widget->needsNextFrame();
if (end) { if (end) {
widget->endAnimations(); widget->endAnimations();
widget->mTriggerReason = "del";
} }
return end; return end;
}); });
@ -56,6 +69,40 @@ void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) {
bool RootWidget::needsUpdate() const { return !mTriggeredWidgets.empty() || mDebugRedrawAlways; } bool RootWidget::needsUpdate() const { return !mTriggeredWidgets.empty() || mDebugRedrawAlways; }
void RootWidget::debugDrawWidget(Widget* widget) {
ImGui::PushID(widget);
if (ImGui::CollapsingHeader(widget->mName.c_str())) {
ImGui::Text("trigger reason: %s", widget->mTriggerReason.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();
}
void RootWidget::drawFrame(Canvas& canvas) { void RootWidget::drawFrame(Canvas& canvas) {
// draw from top to bottom // draw from top to bottom
canvas.rect(mScreenArea, RGBA(0, 0, 0, 1)); canvas.rect(mScreenArea, RGBA(0, 0, 0, 1));
@ -75,43 +122,33 @@ void RootWidget::drawFrame(Canvas& canvas) {
ImGui::Checkbox("Stop processing", &mDebugStopProcessing); ImGui::Checkbox("Stop processing", &mDebugStopProcessing);
ImGui::Checkbox("Force new frames", &mDebugRedrawAlways); ImGui::Checkbox("Force new frames", &mDebugRedrawAlways);
if (ImGui::CollapsingHeader("Triggered")) {
if (mInFocusWidget) { if (ImGui::BeginListBox("##triggered", { -FLT_MIN, 300 })) {
ImGui::Text("Item under cursor %s", mInFocusWidget->mName.c_str()); for (auto widget : mTriggeredWidgets) {
debugDrawWidget(widget.first);
if (ImGui::BeginListBox("##empty", { -FLT_MIN, 0 })) {
for (auto widget = mInFocusWidget; widget && widget != this; widget = widget->mParent) {
if (ImGui::CollapsingHeader(widget->mName.c_str())) {
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, "Passive\0Expand\0Contract\0")) {
widget->setSizePolicy(SizePolicy(sizePolicyX), SizePolicy(sizePolicyY));
}
if (ImGui::Combo("Size Policy Y", &sizePolicyY, "Passive\0Expand\0Contract\0")) {
widget->setSizePolicy(SizePolicy(sizePolicyX), SizePolicy(sizePolicyY));
}
if (ImGui::Combo("Layout", &layout, "Passive\0Vertical\0Horizontal\0")) {
widget->setLayoutPolicy(LayoutPolicy(layout));
}
}
} }
ImGui::EndListBox(); 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();
}
}
}
} }
} }
void RootWidget::updateTreeToProcess() { void RootWidget::updateTreeToProcess() {
mWidgetsToProcess.clear(); mWidgetsToProcess.clear();
for (auto widget : mTriggeredWidgets) { for (auto& [widget, flag] : mTriggeredWidgets) {
flag = true;
for (auto iter = widget; iter; iter = iter->mParent) { for (auto iter = widget; iter; iter = iter->mParent) {
if (iter->mParent) { if (iter->mParent) {
mWidgetsToProcess[iter->mParent].children.insert(&mWidgetsToProcess[iter]); mWidgetsToProcess[iter->mParent].children.insert(&mWidgetsToProcess[iter]);
@ -139,24 +176,29 @@ void RootWidget::drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos)
} }
void RootWidget::drawDebug(Canvas& canvas, Widget* active, const Vec2F& pos, int depthOrder) { void RootWidget::drawDebug(Canvas& canvas, Widget* active, const Vec2F& pos, int depthOrder) {
auto area = RectF{ pos, active->getArea().size }; auto area = RectF{ pos, active->getAreaT().size };
if (mWidgetsToProcess.find(active) != mWidgetsToProcess.end()) { auto processed = mWidgetsToProcess.find(active) != mWidgetsToProcess.end();
if (processed) {
RGBA color = { 1, 0, 0, 1}; RGBA color = { 1, 0, 0, 1};
canvas.frame(area, color); canvas.frame(area, color);
canvas.text((active->mName + ":" + std::to_string(depthOrder)).c_str(), area, 22, Canvas::Align::LC, 2, color); canvas.text((active->mName + ":" + std::to_string(depthOrder)).c_str(), area, 22, Canvas::Align::LC, 2, color);
} }
if (processed || active->mInFocus) {
canvas.circle(pos + active->mLocalPoint, 5, active->mDebugColor);
canvas.circle(active->mGlobalPoint, 15, active->mDebugColor);
}
if (active->mInFocus) { if (active->mInFocus) {
RGBA color = { 1, 0, 0, 0.3f}; RGBA color = { 1, 0, 0, 0.3f};
canvas.debugCross(area, color); canvas.debugCross(area, color);
canvas.circle(pos + active->mLocalPoint, 5, active->mDebugColor);
canvas.circle(active->mGlobalPoint, 10, active->mDebugColor);
} }
int orderIdx = 0; int orderIdx = 0;
for (auto child = active->mDepthOrder.lastNode(); child; child = child->prev) { for (auto child = active->mDepthOrder.lastNode(); child; child = child->prev) {
drawDebug(canvas, child->data, pos + child->data->getArea().pos, orderIdx++); drawDebug(canvas, child->data, pos + child->data->getAreaT().pos, orderIdx++);
} }
} }
@ -188,9 +230,12 @@ void RootWidget::getWidgetPath(Widget* widget, std::vector<Widget*>& out) {
void RootWidget::handleFocusChanges(EventHandler& events) { void RootWidget::handleFocusChanges(EventHandler& events) {
auto prevFocus = mInFocusWidget; auto prevFocus = mInFocusWidget;
events.setCursorOrigin({ 0, 0 });
findFocusWidget(mRoot, &mInFocusWidget, events.getPointer()); findFocusWidget(mRoot, &mInFocusWidget, events.getPointer());
if (mInFocusWidget) updateWidget(mInFocusWidget); // if (mInFocusWidget == prevFocus) return;
if (mInFocusWidget) updateWidget(mInFocusWidget, "focus entered");
std::vector<Widget*> path2; std::vector<Widget*> path2;
getWidgetPath(mInFocusWidget, path2); getWidgetPath(mInFocusWidget, path2);
@ -213,21 +258,21 @@ void RootWidget::handleFocusChanges(EventHandler& events) {
} }
size_t mostCommonIdx = 0; size_t mostCommonIdx = 0;
if (!(path1.empty() || path2.empty())) { if (!(path1.empty() || path2.empty())) {
while (path1[mostCommonIdx] == path2[mostCommonIdx]) { while (path1[mostCommonIdx] == path2[mostCommonIdx] && mostCommonIdx < min(path1.size(), path2.size())) {
mostCommonIdx++; mostCommonIdx++;
} }
} }
mostCommonIdx--;
for (auto i = 0; i < path1.size(); i++) { for (auto i = 0; i < path1.size(); i++) {
path1[i]->mInFocus = false; path1[i]->mInFocus = false;
if (i >= mostCommonIdx && i < propLen1) path1[i]->mouseLeave(); if (i > mostCommonIdx && i < propLen1) path1[i]->mouseLeave();
} }
for (auto i = 0; i < path2.size(); i++) { for (auto i = 0; i < path2.size(); i++) {
path2[i]->mInFocus = true; path2[i]->mInFocus = true;
if (i >= mostCommonIdx && i < propLen2) path2[i]->mouseEnter(); if (i > mostCommonIdx && i < propLen2) path2[i]->mouseEnter();
} }
} }
@ -241,32 +286,62 @@ void RootWidget::findFocusWidget(Widget* iter, Widget** focus, const Vec2F& poin
} }
} }
void RootWidget::updateWidget(Widget* widget) { void RootWidget::updateWidget(Widget* widget, const char* reason) {
mTriggeredWidgets.insert(widget); DEBUG_ASSERT(reason)
widget->mTriggerReason = reason;
mTriggeredWidgets.insert({ widget, false });
}
void RootWidget::updateAreaCache(ActiveTreeNode* iter, bool read) {
if (!iter) return;
if (read) {
iter->widget->mAreaCache = iter->widget->getAreaT();
} else {
iter->widget->setArea(iter->widget->mAreaCache);
iter->widget->mArea.updateCurrentRect();
}
for (auto child : iter->widget->mChildren) {
if (read) {
child->mAreaCache = child->getAreaT();
} else {
child->setArea(child->mAreaCache);
child->mArea.updateCurrentRect();
}
}
for (auto child : iter->children) {
updateAreaCache(child, read);
}
} }
void RootWidget::adjustSizes(ActiveTreeNode* iter) { void RootWidget::adjustSizes(ActiveTreeNode* iter) {
if (!iter) return; if (!iter) return;
iter->widget->pickRect();
for (auto child : iter->children) { for (auto child : iter->children) {
adjustSizes(child); adjustSizes(child);
} }
iter->widget->pickRect();
iter->widget->adjustChildrenRect(); iter->widget->adjustChildrenRect();
// iter->widget->pickRect();
} }
void RootWidget::processActiveTree(ActiveTreeNode* iter, EventHandler& events) { void RootWidget::processActiveTree(ActiveTreeNode* iter, EventHandler& events, Vec2F parent) {
if (!iter) return; if (!iter) return;
auto current = parent + iter->widget->getAreaT().pos;
if (!iter->widget->mInFocus) { if (!iter->widget->mInFocus) {
iter->widget->updateAnimations(); iter->widget->mGlobalPoint = current;
events.setCursorOrigin(current);
iter->widget->process(events); iter->widget->process(events);
} }
for (auto child : iter->children) { for (auto child : iter->children) {
processActiveTree(child, events); processActiveTree(child, events, current);
} }
} }
@ -290,8 +365,8 @@ void RootWidget::processFocusItems(EventHandler& events) {
widgetGlobalPos[0] = 0; widgetGlobalPos[0] = 0;
for (auto widget = 0; widget < path.size() - 1; widget++) { for (auto widget = 0; widget < path.size() - 1; widget++) {
widgetGlobalPos[widget + 1] = widgetGlobalPos[widget] + path[widget + 1]->getArea().pos; widgetGlobalPos[widget + 1] = widgetGlobalPos[widget] + path[widget + 1]->getAreaT().pos;
path[widget]->mGlobalPoint = widgetGlobalPos[widget]; // path[widget]->mGlobalPoint = widgetGlobalPos[widget];
} }
bool eventsProcessed = false; bool eventsProcessed = false;
@ -301,19 +376,15 @@ void RootWidget::processFocusItems(EventHandler& events) {
events.setCursorOrigin(widgetGlobalPos[iter]); events.setCursorOrigin(widgetGlobalPos[iter]);
widget->mLocalPoint = events.getPointer(); widget->mGlobalPoint = widgetGlobalPos[iter];
if (!eventsProcessed && widget->processesEvents()) { if (!eventsProcessed && widget->processesEvents()) {
events.setEnableKeyEvents(true); events.setEnableKeyEvents(true);
widget->updateAnimations();
widget->process(events); widget->process(events);
events.setEnableKeyEvents(false); events.setEnableKeyEvents(false);
eventsProcessed = true; eventsProcessed = true;
} else { } else {
widget->updateAnimations();
widget->process(events); widget->process(events);
} }
} }

View file

@ -33,7 +33,7 @@ void ButtonWidget::setAction(const std::function<void()>& action) {
void ButtonWidget::setColor(const RGBA& in) { void ButtonWidget::setColor(const RGBA& in) {
mColor = in; mColor = in;
mColorAnimated.setTargetColor(mColor); mColorAnimated.setTargetColor(mColor);
triggerWidgetUpdate(); triggerWidgetUpdate("color changed");
} }
void ButtonWidget::process(const EventHandler& eventHandler) { void ButtonWidget::process(const EventHandler& eventHandler) {
@ -68,12 +68,14 @@ void ButtonWidget::mouseEnter() {
mColorAnimated.endAnimation(); mColorAnimated.endAnimation();
mColorAnimated.setTargetColor(mColorHovered); mColorAnimated.setTargetColor(mColorHovered);
mColorAnimated.updateCurrentRect();
triggerWidgetUpdate(); // mColorAnimated.updateCurrentRect();
triggerWidgetUpdate("button hovered");
} }
void ButtonWidget::mouseLeave() { void ButtonWidget::mouseLeave() {
mColorAnimated.setTargetColor(mColor); mColorAnimated.setTargetColor(mColor);
mColorAnimated.updateCurrentRect(); //mColorAnimated.updateCurrentRect();
triggerWidgetUpdate();
triggerWidgetUpdate("button out of focus");
} }

View file

@ -12,9 +12,9 @@ WidgetManagerInterface* Widget::getRoot() {
return dynamic_cast<WidgetManagerInterface*>(iter); return dynamic_cast<WidgetManagerInterface*>(iter);
} }
void Widget::triggerWidgetUpdate() { void Widget::triggerWidgetUpdate(const char* reason) {
if (auto root = getRoot()) { if (auto root = getRoot()) {
root->updateWidget(this); root->updateWidget(this, reason);
} }
} }
@ -23,11 +23,15 @@ Widget::Widget() {
mArea.endAnimation(); mArea.endAnimation();
} }
void Widget::setAreaCache(const tp::RectF& area) {
mAreaCache = area;
}
void Widget::setArea(const RectF& area) { void Widget::setArea(const RectF& area) {
if (mArea.getTargetRect() == area) return; if (mArea.getTargetRect() == area) return;
mArea.setTargetRect(area); mArea.setTargetRect(area);
triggerWidgetUpdate(); triggerWidgetUpdate("new area");
} }
RectF Widget::getArea() const { RectF Widget::getArea() const {
@ -38,6 +42,10 @@ RectF Widget::getAreaT() const {
return mArea.getTargetRect(); return mArea.getTargetRect();
} }
RectF Widget::getAreaCache() const {
return mAreaCache;
}
RectF Widget::getRelativeArea() const { RectF Widget::getRelativeArea() const {
return { {}, mArea.getCurrentRect().size }; return { {}, mArea.getCurrentRect().size };
} }
@ -46,12 +54,16 @@ RectF Widget::getRelativeAreaT() const {
return { {}, mArea.getTargetRect().size }; return { {}, mArea.getTargetRect().size };
} }
RectF Widget::getRelativeAreaCache() const {
return { {}, mAreaCache.size };
}
void Widget::endAnimations() { void Widget::endAnimations() {
mArea.endAnimation(); mArea.endAnimation();
} }
bool Widget::processesEvents() const { bool Widget::processesEvents() const {
return true; return false;
} }
void Widget::updateAnimations() { void Widget::updateAnimations() {
@ -59,7 +71,7 @@ void Widget::updateAnimations() {
} }
bool Widget::needsNextFrame() const { bool Widget::needsNextFrame() const {
return !mArea.shouldEndTransition(); return !mArea.shouldEndTransition(); // || mInFocus;
} }
void Widget::bringToFront() { void Widget::bringToFront() {
@ -107,8 +119,8 @@ void Widget::addChild(Widget* child) {
child->mParent = this; child->mParent = this;
triggerWidgetUpdate(); triggerWidgetUpdate("add child");
child->triggerWidgetUpdate(); child->triggerWidgetUpdate("new parent");
} }
void Widget::removeChild(Widget* child) { void Widget::removeChild(Widget* child) {
@ -118,29 +130,29 @@ void Widget::removeChild(Widget* child) {
mDepthOrder.removeNode(node); mDepthOrder.removeNode(node);
mChildren.erase(std::remove(mChildren.begin(), mChildren.end(), child), mChildren.end()); mChildren.erase(std::remove(mChildren.begin(), mChildren.end(), child), mChildren.end());
triggerWidgetUpdate(); triggerWidgetUpdate("removed child");
child->triggerWidgetUpdate(); child->triggerWidgetUpdate("parent changed");
} }
void Widget::setSizePolicy(tp::Widget::SizePolicy x, tp::Widget::SizePolicy y) { void Widget::setSizePolicy(tp::Widget::SizePolicy x, tp::Widget::SizePolicy y) {
mSizePolicy = { x, y }; mSizePolicy = { x, y };
triggerWidgetUpdate(); triggerWidgetUpdate("chane size policy");
} }
void Widget::setLayoutPolicy(LayoutPolicy layoutPolicy) { void Widget::setLayoutPolicy(LayoutPolicy layoutPolicy) {
mLayoutPolicy = layoutPolicy; mLayoutPolicy = layoutPolicy;
triggerWidgetUpdate(); triggerWidgetUpdate("new layout");
} }
const Vec2F& Widget::getMinSize() { return mMinSize; } const Vec2F& Widget::getMinSize() { return mMinSize; }
void Widget::setMinSize(const Vec2F& size) { void Widget::setMinSize(const Vec2F& size) {
mMinSize = size; mMinSize = size;
triggerWidgetUpdate(); triggerWidgetUpdate("new min size");
} }
void Widget::pickRect() { void Widget::pickRect() {
auto current = getAreaT(); auto current = getAreaCache();
auto children = getChildrenEnclosure(); auto children = getChildrenEnclosure();
auto parent = getParentEnclosure(); auto parent = getParentEnclosure();
@ -150,21 +162,21 @@ void Widget::pickRect() {
auto newArea = RectF(rangeX, rangeY); auto newArea = RectF(rangeX, rangeY);
for (auto child : mChildren) { for (auto child : mChildren) {
child->setArea(child->getAreaT().move(current.pos - newArea.pos)); child->setAreaCache(child->getAreaCache().move(current.pos - newArea.pos));
} }
setArea(newArea); setAreaCache(newArea);
} }
void Widget::clampRect() { void Widget::clampRect() {
auto current = getAreaT(); auto current = getAreaCache();
auto children = getChildrenEnclosure(); auto children = getChildrenEnclosure();
auto parent = getParentEnclosure(); auto parent = getParentEnclosure();
auto rangeX = clampRange(current.getRangeX(), children.getRangeX(), parent.getRangeX(), false); auto rangeX = clampRange(current.getRangeX(), children.getRangeX(), parent.getRangeX(), false);
auto rangeY = clampRange(current.getRangeY(), children.getRangeY(), parent.getRangeY(), true); auto rangeY = clampRange(current.getRangeY(), children.getRangeY(), parent.getRangeY(), true);
setArea(RectF(rangeX, rangeY)); setAreaCache(RectF(rangeX, rangeY));
} }
void Widget::adjustChildrenRect() { void Widget::adjustChildrenRect() {
@ -179,14 +191,14 @@ void Widget::adjustChildrenRect() {
halnf Widget::changeChildSize(tp::Widget* widget, halnf diff, bool vertical) { halnf Widget::changeChildSize(tp::Widget* widget, halnf diff, bool vertical) {
auto prevSize = widget->getAreaT().size[vertical]; auto prevSize = widget->getAreaCache().size[vertical];
{ {
auto area = widget->getAreaT(); auto area = widget->getAreaCache();
area.size[vertical] += diff; area.size[vertical] += diff;
widget->setArea(area); widget->setAreaCache(area);
widget->clampRect(); widget->clampMinMaxSize();
} }
auto newSize = widget->getAreaT().size[vertical]; auto newSize = widget->getAreaCache().size[vertical];
return newSize - prevSize; return newSize - prevSize;
} }
@ -197,12 +209,22 @@ void Widget::adjustLayout(bool vertical) {
Vec2F availableSize = getRelativeAreaT().size; Vec2F availableSize = getRelativeAreaT().size;
for (auto child : mChildren) { for (auto child : mChildren) {
contentSize += child->getAreaT().size; if (child->mSizePolicy[vertical] == SizePolicy::Expanding) {
if (child->mSizePolicy[vertical] == SizePolicy::Expand) {
contributors.emplace_back( child, true ); contributors.emplace_back( child, true );
auto area = child->getAreaCache();
area.size[vertical] = 0;
child->setAreaCache(area);
child->clampRect();
} else {
// child->triggerWidgetUpdate("expand child in layout");
} }
contentSize += child->getAreaCache().size;
} }
availableSize -= mLayoutGap * ((halnf) mChildren.size() - 1) + mLayoutMargin * 2;
auto diff = availableSize - contentSize; auto diff = availableSize - contentSize;
// expand or contract as much as possible // expand or contract as much as possible
@ -212,6 +234,7 @@ void Widget::adjustLayout(bool vertical) {
for (auto& contributor : contributors) { for (auto& contributor : contributors) {
if (!contributor.second) continue; if (!contributor.second) continue;
// contributor.first->endAnimations();
auto contribution = changeChildSize(contributor.first, quota[vertical], vertical); auto contribution = changeChildSize(contributor.first, quota[vertical], vertical);
if (contribution == 0) { if (contribution == 0) {
@ -228,12 +251,16 @@ void Widget::adjustLayout(bool vertical) {
} }
// arrange // arrange
halnf iterPos = 0; halnf iterPos = mLayoutMargin;
for (auto child : mChildren) { for (auto child : mChildren) {
auto area = child->getAreaT(); auto area = child->getAreaCache();
area.pos[vertical] = iterPos; area.pos[vertical] = iterPos;
iterPos += area.size[vertical]; iterPos += area.size[vertical] + mLayoutGap;
child->setArea(area); child->setAreaCache(area);
// child->updateAnimations();
// child->triggerWidgetUpdate("layout changed");
} }
} }
@ -241,15 +268,21 @@ RangeF Widget::pickRange(const RangeF& current, const RangeF& children, const Ra
RangeF out; RangeF out;
switch (mSizePolicy[vertical]) { switch (mSizePolicy[vertical]) {
case SizePolicy::Passive: out = current; break; case SizePolicy::Fixed: out = current; break;
case SizePolicy::Expand: out = parent; break; case SizePolicy::Expanding: out = parent; break;
case SizePolicy::Contract: out = children; break; case SizePolicy::Minimal: out = children; break;
} }
out = clampRange(out, children, parent, vertical); out = clampRange(out, children, parent, vertical);
return out; return out;
} }
void Widget::clampMinMaxSize() {
auto current = getAreaCache();
current.size.clamp(mMinSize, mMaxSize);
setAreaCache(current);
}
RangeF Widget::clampRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical) { RangeF Widget::clampRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical) {
auto out = current; auto out = current;
@ -274,12 +307,13 @@ RectF Widget::getChildrenEnclosure() {
RectF out; RectF out;
if (mChildren.empty()) { if (mChildren.empty()) {
out = { getAreaT().center(), { 0, 0 } }; out = { getAreaCache().center(), { 0, 0 } };
} else { } else {
out = mChildren.front()->getAreaCache();
for (auto child : mChildren) { for (auto child : mChildren) {
out.expand(child->getAreaT()); out.expand(child->getAreaCache());
} }
out.pos += getAreaT().pos; out.pos += getAreaCache().pos;
} }
return out; return out;
@ -288,5 +322,10 @@ RectF Widget::getChildrenEnclosure() {
RectF Widget::getParentEnclosure() { RectF Widget::getParentEnclosure() {
DEBUG_ASSERT(mParent); DEBUG_ASSERT(mParent);
if (!mParent) return { { 0, 0 }, mMaxSize }; if (!mParent) return { { 0, 0 }, mMaxSize };
return mParent->getRelativeAreaT();
auto out = mParent->getRelativeAreaT();
if (mParent->mLayoutPolicy != LayoutPolicy::Passive) {
return out.scaleFromCenter(mParent->mLayoutMargin, true);
}
return out;
} }

View file

@ -36,7 +36,7 @@ namespace tp {
public: public:
void pickRect() override {} void pickRect() override {}
void adjustChildrenRect() override {} void adjustChildrenRect() override;
void process(const EventHandler& events) override; void process(const EventHandler& events) override;
void draw(Canvas& canvas) override; void draw(Canvas& canvas) override;

View file

@ -7,7 +7,10 @@ namespace tp {
class FloatingWidget : public Widget { class FloatingWidget : public Widget {
public: public:
FloatingWidget() { FloatingWidget() {
setDebug("float", { 0.0, 0.9, 0.1, 0.7 }); setDebug("float", { 0.0, 0.9, 0.1, 1 });
// mSizePolicy = { SizePolicy::Contract, SizePolicy::Contract };
mLayoutPolicy = LayoutPolicy::Horizontally;
} }
void process(const EventHandler& events) override; void process(const EventHandler& events) override;
@ -18,7 +21,10 @@ namespace tp {
RectF resizeHandleRect(); RectF resizeHandleRect();
[[nodiscard]] bool needsNextFrame() const override;
[[nodiscard]] bool propagateEventsToChildren() const override; [[nodiscard]] bool propagateEventsToChildren() const override;
[[nodiscard]] bool processesEvents() const override { return true; }
[[nodiscard]] bool isFloating() const; [[nodiscard]] bool isFloating() const;
@ -38,24 +44,31 @@ namespace tp {
FloatingMenu() { FloatingMenu() {
setDebug("float menu", { 0.0, 0.9, 0.1, 0.7 }); setDebug("float menu", { 0.0, 0.9, 0.1, 0.7 });
addChild(&mMenuLayout); // addChild(&mMenuLayout);
mMenuLayout.addChild(&mHeader); addChild(&mHeader);
mMenuLayout.addChild(&mBodyLayout); addChild(&mBodyLayout);
mHeader.setText("Menu"); mHeader.setText("Menu");
mHeader.setSizePolicy(SizePolicy::Expanding, SizePolicy::Minimal);
mBodyLayout.setSizePolicy(SizePolicy::Expanding, SizePolicy::Expanding);
setLayoutPolicy(LayoutPolicy::Vertically);
mBodyLayout.setLayoutPolicy(LayoutPolicy::Vertically);
} }
public: public:
void addToMenu(Widget* widget) { void addToMenu(Widget* widget) {
widget->setSizePolicy(SizePolicy::Expanding, SizePolicy::Minimal);
mBodyLayout.addChild(widget); mBodyLayout.addChild(widget);
} }
private: private:
VerticalLayout mMenuLayout; // VerticalLayout mMenuLayout;
VerticalLayout mBodyLayout; Widget mBodyLayout;
LabelWidget mHeader; LabelWidget mHeader;
ButtonWidget mTestButton; // ButtonWidget mTestButton;
}; };
} }

View file

@ -19,7 +19,7 @@ namespace tp {
RootWidget() { setDebug("root", RGBA(1)); } RootWidget() { setDebug("root", RGBA(1)); }
void setRootWidget(Widget* widget); void setRootWidget(Widget* widget);
void updateWidget(Widget*); void updateWidget(Widget*, const char* reason = nullptr) override;
static void setWidgetArea(Widget& widget, const RectF& rect); static void setWidgetArea(Widget& widget, const RectF& rect);
@ -37,16 +37,18 @@ namespace tp {
void findFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer); void findFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer);
void handleFocusChanges(EventHandler& events); void handleFocusChanges(EventHandler& events);
void getWidgetPath(Widget* widget, std::vector<Widget*>& out); void getWidgetPath(Widget* widget, std::vector<Widget*>& out);
void processActiveTree(ActiveTreeNode* iter, EventHandler& events); void processActiveTree(ActiveTreeNode* iter, EventHandler& events, Vec2F pos);
void processFocusItems(EventHandler& events); void processFocusItems(EventHandler& events);
void adjustSizes(ActiveTreeNode* iter); void adjustSizes(ActiveTreeNode* iter);
void updateAreaCache(ActiveTreeNode* iter, bool read);
static void debugDrawWidget(Widget* widget);
private: private:
RectF mScreenArea; RectF mScreenArea;
Widget* mRoot = nullptr; Widget* mRoot = nullptr;
// frame to frame changes // frame to frame changes
std::set<Widget*> mTriggeredWidgets; std::map<Widget*, bool> mTriggeredWidgets;
std::map<Widget*, ActiveTreeNode> mWidgetsToProcess; std::map<Widget*, ActiveTreeNode> mWidgetsToProcess;
Widget* mInFocusWidget = nullptr; Widget* mInFocusWidget = nullptr;

View file

@ -15,9 +15,9 @@ namespace tp {
public: public:
enum class SizePolicy { enum class SizePolicy {
Passive, Fixed,
Expand, Expanding,
Contract, Minimal,
}; };
enum class LayoutPolicy { enum class LayoutPolicy {
@ -40,7 +40,7 @@ namespace tp {
WidgetManagerInterface* getRoot(); WidgetManagerInterface* getRoot();
void triggerWidgetUpdate(); void triggerWidgetUpdate(const char* reason = nullptr);
void bringToFront(); void bringToFront();
void bringToBack(); void bringToBack();
@ -67,6 +67,9 @@ namespace tp {
// resizing // resizing
RangeF pickRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical); RangeF pickRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical);
RangeF clampRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical); RangeF clampRange(const RangeF& current, const RangeF& children, const RangeF& parent, bool vertical);
void clampMinMaxSize();
RectF getChildrenEnclosure(); RectF getChildrenEnclosure();
RectF getParentEnclosure(); RectF getParentEnclosure();
void adjustLayout(bool vertical); void adjustLayout(bool vertical);
@ -83,11 +86,14 @@ namespace tp {
public: public:
[[nodiscard]] RectF getArea() const; [[nodiscard]] RectF getArea() const;
[[nodiscard]] RectF getAreaT() const; [[nodiscard]] RectF getAreaT() const;
[[nodiscard]] RectF getAreaCache() const;
[[nodiscard]] RectF getRelativeArea() const; [[nodiscard]] RectF getRelativeArea() const;
[[nodiscard]] RectF getRelativeAreaT() const; [[nodiscard]] RectF getRelativeAreaT() const;
[[nodiscard]] RectF getRelativeAreaCache() const;
void setArea(const RectF& area); void setArea(const RectF& area);
void setAreaCache(const RectF& area);
protected: protected:
Widget* mParent = nullptr; Widget* mParent = nullptr;
@ -100,20 +106,28 @@ namespace tp {
// resizing // resizing
LayoutPolicy mLayoutPolicy = LayoutPolicy::Passive; LayoutPolicy mLayoutPolicy = LayoutPolicy::Passive;
Vec2<SizePolicy> mSizePolicy = { SizePolicy::Passive, SizePolicy::Passive }; Vec2<SizePolicy> mSizePolicy = { SizePolicy::Fixed, SizePolicy::Fixed };
Vec2F mMinSize = { 50, 50 }; Vec2F mMinSize = { 50, 50 };
Vec2F mMaxSize = { FLT_MAX / 2, FLT_MAX / 2 }; Vec2F mMaxSize = { FLT_MAX / 2, FLT_MAX / 2 };
bool mInFocus = false; bool mInFocus = false;
halnf mLayoutGap = 5;
halnf mLayoutMargin = 10;
// cache
RectF mAreaCache;
// debug // debug
int inOutCallbacks = 0;
std::string mName = "widget base"; std::string mName = "widget base";
Vec2F mLocalPoint; Vec2F mLocalPoint;
Vec2F mGlobalPoint; Vec2F mGlobalPoint;
RGBA mDebugColor = { 1, 1, 1, 0.3 }; RGBA mDebugColor = { 1, 1, 1, 0.3 };
std::string mTriggerReason = "none";
}; };
struct WidgetManagerInterface : public Widget { struct WidgetManagerInterface : public Widget {
virtual void updateWidget(Widget*) = 0; virtual void updateWidget(Widget*, const char* reason = nullptr) = 0;
}; };
} }