place the selection panel beside what it describes

This commit is contained in:
2026-08-09 13:45:04 +02:00
parent 7ae5f8c4dc
commit c0b009c548
25 changed files with 789 additions and 182 deletions

View File

@@ -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.