From 88db0b74e98565034534f8b27598399737be6d8b Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 15 Feb 2022 22:46:54 -0500 Subject: [PATCH] Improve RackWidget::setModulePosForce() with a few intuitive special cases. --- src/app/RackWidget.cpp | 104 ++++++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 47 deletions(-) diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index 61251949..17bfeb99 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -699,20 +699,52 @@ void RackWidget::setModulePosForce(ModuleWidget* mw, math::Vec pos) { math::Rect newBox = mw->box; newBox.pos = (pos / RACK_GRID_SIZE).round() * RACK_GRID_SIZE; - // Comparison of X coordinates + // Reset modules to their old positions, including this module + for (auto& pair : internal->moduleOldPositions) { + widget::Widget* w2 = pair.first; + w2->box.pos = pair.second; + } + + // Comparator of X coordinates auto cmp = [&](const widget::Widget* a, const widget::Widget* b) { - return a->box.pos.x < b->box.pos.x; + return a->box.getLeft() < b->box.getLeft(); }; + // Collect modules to the right of old pos + std::set rightOldModules(cmp); + for (widget::Widget* w2 : internal->moduleContainer->children) { + // Skip this module + if (w2 == mw) + continue; + // Modules must be on the same row as old pos + if (w2->box.getTop() != mw->box.getTop()) + continue; + // Insert into rightOldModules + if (w2->box.getCenter().x >= mw->box.getCenter().x) + rightOldModules.insert(w2); + } + + // Shove old right modules to the left + float xDelta = newBox.pos.x - mw->box.pos.x; + if (newBox.pos.y != mw->box.pos.y || xDelta > 0 || xDelta < -mw->box.size.x) { + xDelta = -mw->box.size.x; + } + + float xRight = mw->box.getRight(); + for (auto it = rightOldModules.begin(); it != rightOldModules.end(); it++) { + widget::Widget* w2 = *it; + // Break when module is no longer touching + if (xRight < w2->box.getLeft()) + break; + xRight = w2->box.getRight(); + // Move module to the left by the width of mw, so it replaces its position + w2->box.pos.x += xDelta; + } + // Collect modules to the left and right of pos std::set leftModules(cmp); std::set rightModules(cmp); for (widget::Widget* w2 : internal->moduleContainer->children) { - // Reset position to old position, including this module - auto it = internal->moduleOldPositions.find(w2); - if (it != internal->moduleOldPositions.end()) { - w2->box.pos = it->second; - } // Skip this module if (w2 == mw) continue; @@ -729,49 +761,27 @@ void RackWidget::setModulePosForce(ModuleWidget* mw, math::Vec pos) { widget::Widget* leftModule = leftModules.empty() ? NULL : *leftModules.rbegin(); widget::Widget* rightModule = rightModules.empty() ? NULL : *rightModules.begin(); - if (leftModule) { - // Place right of leftModule - if (leftModule->box.getRight() > newBox.getLeft()) { - newBox.pos.x = leftModule->box.getRight(); + // If there isn't enough space between the last leftModule and first rightModule, place module to the right of the leftModule and shove right modules. + if (leftModule && rightModule && leftModule->box.getRight() + newBox.getWidth() > rightModule->box.getLeft()) { + newBox.pos.x = leftModule->box.getRight(); + + // Shove right modules + float xRight = newBox.getRight(); + for (auto it = rightModules.begin(); it != rightModules.end(); it++) { + widget::Widget* w2 = *it; + if (w2->box.getLeft() >= xRight) + break; + w2->box.pos.x = std::round(xRight / RACK_GRID_WIDTH) * RACK_GRID_WIDTH; + xRight = w2->box.getRight(); } } - if (rightModule) { - // Place left of rightModule - if (newBox.getRight() > rightModule->box.getLeft()) { - newBox.pos.x = rightModule->box.getLeft() - newBox.size.x; - } + // Place right of leftModule + else if (leftModule && leftModule->box.getRight() > newBox.getLeft()) { + newBox.pos.x = leftModule->box.getRight(); } - if (leftModule && rightModule) { - // If there isn't enough space between the last leftModule and first rightModule, place module to the right of the leftModule. - if (leftModule->box.getRight() + newBox.getWidth() > rightModule->box.getLeft()) { - // If module old position is to the left of the new position, shove other modules to the left instead of the right. - if (mw->box.getTop() == newBox.getTop() && mw->box.getLeft() < newBox.getLeft()) { - newBox.pos.x = rightModule->box.getLeft() - newBox.size.x; - - // Shove left modules - float xLeft = newBox.getLeft(); - for (auto it = leftModules.rbegin(); it != leftModules.rend(); it++) { - widget::Widget* w2 = *it; - if (w2->box.getRight() <= xLeft) - break; - w2->box.pos.x = std::round((xLeft - w2->box.size.x) / RACK_GRID_WIDTH) * RACK_GRID_WIDTH; - xLeft = w2->box.getLeft(); - } - } - else { - newBox.pos.x = leftModule->box.getRight(); - - // Shove right modules - float xRight = newBox.getRight(); - for (auto it = rightModules.begin(); it != rightModules.end(); it++) { - widget::Widget* w2 = *it; - if (w2->box.getLeft() >= xRight) - break; - w2->box.pos.x = std::round(xRight / RACK_GRID_WIDTH) * RACK_GRID_WIDTH; - xRight = w2->box.getRight(); - } - } - } + // Place left of rightModule + else if (rightModule && newBox.getRight() > rightModule->box.getLeft()) { + newBox.pos.x = rightModule->box.getLeft() - newBox.size.x; } mw->box.pos = (newBox.pos / RACK_GRID_SIZE).round() * RACK_GRID_SIZE;