#include "FloatingPanelPlacement.h" #include namespace { // The part every placement shares, whatever put the panel where it wants to be: it is // pushed inside band rather than hanging off it, shortened to what its own column has // free, and lifted by however much of it hangs below that. wantedLeftPx and wantedTopPx // are where the panel would stand if nothing were in the way. QRect fitInBand(const QRect& band, int wantedLeftPx, int wantedTopPx, QSize wantedSize, const std::vector& occupiedRects, int marginPx) { const int widthPx = std::min(wantedSize.width(), band.width()); int leftPx = std::min(wantedLeftPx, band.right() - widthPx + 1); leftPx = std::max(leftPx, band.left()); // Only the widgets its own column meets can shorten it. const int bottomPx = getAvailableBottomPx(band, occupiedRects, leftPx, leftPx + widthPx - 1, marginPx); const int heightPx = std::min(wantedSize.height(), std::max(0, bottomPx - band.top() + 1)); // Never above the band: a panel taller than the space left is capped instead, and // scrolls. int topPx = std::max(wantedTopPx, band.top()); topPx = std::min(topPx, bottomPx - heightPx + 1); topPx = std::max(topPx, band.top()); return QRect(leftPx, topPx, widthPx, heightPx); } } // namespace int getAvailableBottomPx(const QRect& band, const std::vector& occupiedRects, int leftPx, int rightPx, int marginPx) { int bottomPx = band.bottom(); for (const QRect& occupied : occupiedRects) { if (occupied.isEmpty()) { continue; } // Only what is actually in the way counts: a widget entirely to one side of this // span is not below it, however tall it is. if (occupied.right() < leftPx || occupied.left() > rightPx) { continue; } bottomPx = std::min(bottomPx, occupied.top() - marginPx - 1); } return bottomPx; } PanelSide chooseSide(const QRect& band, const QRect& anchorRect, int widthPx, int selectionGapPx) { // What each side offers: the room between the anchor and that edge of the band, less // the gap the panel keeps from the anchor. The band's own inset from the view has // already taken the edge margin off. const int roomRightPx = band.right() - anchorRect.right() - selectionGapPx; const int roomLeftPx = anchorRect.left() - band.left() - selectionGapPx; if (roomRightPx >= widthPx) { return PanelSide::Right; } if (roomLeftPx >= widthPx) { return PanelSide::Left; } // Neither side can hold it without covering the selection, so it goes where it // covers the least of it. return (roomRightPx >= roomLeftPx) ? PanelSide::Right : PanelSide::Left; } QRect placeBesideAnchor(const QRect& band, const QRect& anchorRect, PanelSide side, QSize wantedSize, const std::vector& occupiedRects, int selectionGapPx, int marginPx) { const int widthPx = std::min(wantedSize.width(), band.width()); // A gap from the anchor on the chosen side, growing away from it: the edge facing the // selection is the one that stays put as the panel's content resizes. A panel that // does not fit there is pushed back inside the view rather than hanging off it, which // is what puts it over the selection when neither side had room. const int wantedLeftPx = (side == PanelSide::Right) ? anchorRect.right() + selectionGapPx + 1 : anchorRect.left() - selectionGapPx - widthPx; // Top-aligned with the anchor -- the gap separates the two horizontally and plays no // part here -- then lifted by however much of the panel hangs below what is free. return fitInBand(band, wantedLeftPx, anchorRect.top(), wantedSize, occupiedRects, marginPx); } QRect placeAtDesiredTopLeft(const QRect& band, const QPoint& desiredTopLeftPx, QSize wantedSize, const std::vector& occupiedRects, int marginPx) { // Where the player dropped it, resolved by the same rules as any other placement // (REQ-UI-SELECTION-PANEL-DRAG). Nothing here is written back to the desired point: // a panel lifted above the build button bar returns to where it was dropped as soon // as the bar stops meeting its column. return fitInBand(band, desiredTopLeftPx.x(), desiredTopLeftPx.y(), wantedSize, occupiedRects, marginPx); }