place the floating widgets in one ordered pass
The three widgets over the game world view each cached a rect handed to them by MainWindow's resize, then re-placed themselves from it. But the build button bar re-centers on a building unlock and the controls panel re-fits on a 50 ms timer, neither of which goes through MainWindow, so the rects the others held went stale -- and each widget re-implemented its own avoidance against them. They now implement FloatingPanel and are placed in one ordered pass: the bar takes what it wants, the controls panel steps around the bar, and the selection panel keeps clear of both. A widget that changed size or visibility publishes FloatingLayoutInvalidatedEvent instead of moving itself, because what it may take depends on the widgets placed before it. The rule they step around each other by is one function in lib, where it can be tested without a display -- the only way any of this geometry gets automated cover, screen capture of the world view being blank here. The selection panel keeps its right edge and its vertical centering, but the space it centers in is now what its own column has left free rather than the full-width strip the bar used to reserve. It therefore sits lower than before where the centered bar does not reach it, and it now clears the controls panel, which it previously ignored. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
This commit is contained in:
@@ -11,6 +11,9 @@
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "ControlActionText.h"
|
||||
#include "EventManager.h"
|
||||
#include "FloatingLayoutInvalidatedEvent.h"
|
||||
#include "FloatingPanelPlacement.h"
|
||||
#include "GameWorldView.h"
|
||||
#include "selection/SelectionNames.h"
|
||||
|
||||
@@ -159,11 +162,10 @@ ControlsPanel::ControlsPanel(const GameWorldView* view, QWidget* parent)
|
||||
refresh();
|
||||
}
|
||||
|
||||
void ControlsPanel::anchorTo(const QRect& worldRect, const QRect& buildBarRect)
|
||||
void ControlsPanel::invalidateLayout()
|
||||
{
|
||||
m_worldRect = worldRect;
|
||||
m_buildBarRect = buildBarRect;
|
||||
refit();
|
||||
EventManager::getInstance()->sendEventImmediately(
|
||||
std::make_shared<FloatingLayoutInvalidatedEvent>());
|
||||
}
|
||||
|
||||
void ControlsPanel::mousePressEvent(QMouseEvent* event)
|
||||
@@ -174,7 +176,7 @@ void ControlsPanel::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
m_collapsed = !m_collapsed;
|
||||
m_scrollArea->setVisible(!m_collapsed);
|
||||
refit();
|
||||
invalidateLayout();
|
||||
}
|
||||
event->accept();
|
||||
}
|
||||
@@ -241,7 +243,7 @@ void ControlsPanel::rebuild(const ControlContext& context)
|
||||
}
|
||||
|
||||
m_scrollArea->setVisible(!m_collapsed);
|
||||
refit();
|
||||
invalidateLayout();
|
||||
}
|
||||
|
||||
QString ControlsPanel::getHeadingText(const ControlContext& context) const
|
||||
@@ -277,9 +279,9 @@ QString ControlsPanel::getHeadingText(const ControlContext& context) const
|
||||
return name + QStringLiteral(" ") + kHeadingSeparator + QStringLiteral(" ") + detail;
|
||||
}
|
||||
|
||||
void ControlsPanel::refit()
|
||||
void ControlsPanel::placeIn(const QRect& viewRect, const std::vector<QRect>& occupiedRects)
|
||||
{
|
||||
if (m_worldRect.isNull()) { return; }
|
||||
if (viewRect.isNull()) { return; }
|
||||
|
||||
// Rows are torn down and rebuilt wholesale, and a widget added to a layout is only
|
||||
// shown once that layout runs -- without this the new rows count for nothing and
|
||||
@@ -290,7 +292,7 @@ void ControlsPanel::refit()
|
||||
m_rowsLayout->invalidate();
|
||||
m_rowsLayout->activate();
|
||||
|
||||
const QRect band = m_worldRect.adjusted(kMarginPx, kMarginPx, -kMarginPx, -kMarginPx);
|
||||
const QRect band = viewRect.adjusted(kMarginPx, kMarginPx, -kMarginPx, -kMarginPx);
|
||||
if (band.width() <= 0 || band.height() <= 0) { return; }
|
||||
|
||||
// Measured from the heading and the rows directly rather than from the panel's own
|
||||
@@ -314,21 +316,17 @@ void ControlsPanel::refit()
|
||||
|
||||
// The bottom-left corner of the view, growing upward as rows are added
|
||||
// (REQ-UI-CONTROLS-PANEL).
|
||||
int widthPx = qMin(contentWidthPx + chromePx, band.width());
|
||||
int heightPx = qMin(wantedHeightPx, band.height());
|
||||
int topPx = band.bottom() - heightPx + 1;
|
||||
int widthPx = qMin(contentWidthPx + chromePx, band.width());
|
||||
|
||||
// The build button bar is centered and sized to its buttons, so it usually leaves
|
||||
// this corner free and the panel can share the bottom edge with it. Only when the
|
||||
// two would actually overlap does the panel rise, clearing the bar's top by the
|
||||
// same margin it keeps from the view's edges (REQ-UI-BUILD-BAR).
|
||||
const QRect wanted(band.left(), topPx, widthPx, heightPx);
|
||||
if (!m_buildBarRect.isNull() && wanted.intersects(m_buildBarRect))
|
||||
{
|
||||
const int availablePx = m_buildBarRect.top() - kMarginPx - band.top();
|
||||
heightPx = qMin(heightPx, qMax(0, availablePx));
|
||||
topPx = m_buildBarRect.top() - kMarginPx - heightPx;
|
||||
}
|
||||
// this corner free and the panel can share the bottom edge with it. Only where the
|
||||
// two would actually meet does the panel rise, clearing the bar's top by the same
|
||||
// margin it keeps from the view's edges (REQ-UI-BUILD-BAR). The bar is the only
|
||||
// widget placed before this one, so it is the only rect that can be in the way.
|
||||
const int bottomPx = getAvailableBottomPx(band, occupiedRects, band.left(),
|
||||
band.left() + widthPx - 1, kMarginPx);
|
||||
int heightPx = qMin(wantedHeightPx, qMax(0, bottomPx - band.top() + 1));
|
||||
int topPx = bottomPx - heightPx + 1;
|
||||
|
||||
// Whatever the rows lost to either cap, they scroll for. The scrollbar needs its
|
||||
// own width, or it would appear over the labels.
|
||||
|
||||
Reference in New Issue
Block a user