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:
@@ -5,6 +5,9 @@
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "BuildingIconCache.h"
|
||||
#include "EventManager.h"
|
||||
#include "FloatingLayoutInvalidatedEvent.h"
|
||||
#include "FloatingPanelPlacement.h"
|
||||
#include "ItemIconCache.h"
|
||||
#include "Simulation.h"
|
||||
#include "VisualsConfig.h"
|
||||
@@ -13,8 +16,8 @@
|
||||
namespace
|
||||
{
|
||||
|
||||
// Distance kept between the panel and the edges of the band it is anchored to
|
||||
// (REQ-UI-SELECTION-PANEL).
|
||||
// Distance kept between the panel and the edges of the game world view, and between it
|
||||
// and the widgets it steps around (REQ-UI-SELECTION-PANEL).
|
||||
const int kMarginPx = 8;
|
||||
|
||||
// Upper bound on the card width. The panel is content-sized, but several of the cards'
|
||||
@@ -94,10 +97,10 @@ SelectionPanel::~SelectionPanel()
|
||||
unregisterForEvents();
|
||||
}
|
||||
|
||||
void SelectionPanel::anchorTo(const QRect& bandRect)
|
||||
void SelectionPanel::invalidateLayout()
|
||||
{
|
||||
m_bandRect = bandRect;
|
||||
updateVisibility();
|
||||
EventManager::getInstance()->sendEventImmediately(
|
||||
std::make_shared<FloatingLayoutInvalidatedEvent>());
|
||||
}
|
||||
|
||||
void SelectionPanel::handleEvent(std::shared_ptr<const SelectionChangedEvent> event)
|
||||
@@ -178,7 +181,7 @@ void SelectionPanel::refreshContent()
|
||||
}
|
||||
|
||||
m_content->refresh();
|
||||
updateVisibility();
|
||||
invalidateLayout();
|
||||
}
|
||||
|
||||
void SelectionPanel::rebuildContent()
|
||||
@@ -208,36 +211,28 @@ void SelectionPanel::rebuildContent()
|
||||
m_content->show();
|
||||
m_content->refresh();
|
||||
}
|
||||
updateVisibility();
|
||||
invalidateLayout();
|
||||
}
|
||||
|
||||
void SelectionPanel::updateVisibility()
|
||||
void SelectionPanel::placeIn(const QRect& viewRect, const std::vector<QRect>& occupiedRects)
|
||||
{
|
||||
// Nothing selected in either category means no panel at all rather than an empty one
|
||||
// (REQ-UI-EMPTY-SELECTION), leaving the whole game world view visible. Before the
|
||||
// owner has anchored the panel there is nowhere to put it either, so it stays hidden
|
||||
// until then.
|
||||
const bool shouldShow = (m_content != nullptr) && !m_bandRect.isNull();
|
||||
setVisible(shouldShow);
|
||||
if (shouldShow)
|
||||
// (REQ-UI-EMPTY-SELECTION), leaving the whole game world view visible.
|
||||
setVisible(m_content != nullptr);
|
||||
if (m_content == nullptr || viewRect.isNull())
|
||||
{
|
||||
refit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void SelectionPanel::refit()
|
||||
{
|
||||
const QRect band =
|
||||
m_bandRect.adjusted(kMarginPx, kMarginPx, -kMarginPx, -kMarginPx);
|
||||
const QRect band = viewRect.adjusted(kMarginPx, kMarginPx, -kMarginPx, -kMarginPx);
|
||||
|
||||
// The panel's border is drawn around the scroll area rather than around the card, so
|
||||
// it is added to whatever the card asks for. Spelled out here instead of read back
|
||||
// from contentsMargins() because the stylesheet box is what sets it, and asking the
|
||||
// style for it before the first show is unreliable.
|
||||
const int borderPx = 1;
|
||||
const int maxWidthPx = qMin(kMaxContentWidthPx, band.width() - 2 * borderPx);
|
||||
const int maxHeightPx = band.height() - 2 * borderPx;
|
||||
if (maxWidthPx <= 0 || maxHeightPx <= 0)
|
||||
const int borderPx = 1;
|
||||
const int maxWidthPx = qMin(kMaxContentWidthPx, band.width() - 2 * borderPx);
|
||||
if (maxWidthPx <= 0 || band.height() <= 2 * borderPx)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -273,9 +268,26 @@ void SelectionPanel::refit()
|
||||
// First at the cap, the most room the card can ever get, to learn how wide it
|
||||
// wants to be; then at that width for the height that follows from it.
|
||||
int contentWidthPx = qMin(measureAt(maxWidthPx).width(), maxWidthPx);
|
||||
|
||||
// How much height there is depends on where the panel stands: of the widgets
|
||||
// placed before it, only those whose rectangles meet its own column are in its
|
||||
// way (REQ-UI-SELECTION-PANEL). Its column follows from the width just measured,
|
||||
// so this cannot be settled before it -- and where the scroll bar below widens
|
||||
// the panel, the second pass settles it again against the wider column.
|
||||
const int leftPx =
|
||||
band.right() - (contentWidthPx + 2 * borderPx) + 1;
|
||||
const int bottomPx = getAvailableBottomPx(band, occupiedRects, leftPx,
|
||||
band.right(), kMarginPx);
|
||||
const int freeHeightPx = bottomPx - band.top() + 1;
|
||||
const int maxHeightPx = freeHeightPx - 2 * borderPx;
|
||||
if (maxHeightPx <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int contentHeightPx = measureAt(contentWidthPx).height();
|
||||
|
||||
// A card taller than the band is capped there and scrolls
|
||||
// A card taller than the space left is capped there and scrolls
|
||||
// (REQ-UI-SELECTION-PANEL). The bar is laid out beside the card, so the panel
|
||||
// widens by its width to leave the card the width its height was measured for --
|
||||
// and where the cap does not allow that, the card is measured again at what is
|
||||
@@ -301,11 +313,11 @@ void SelectionPanel::refit()
|
||||
const int panelWidthPx = contentWidthPx + 2 * borderPx;
|
||||
const int panelHeightPx = contentHeightPx + 2 * borderPx;
|
||||
|
||||
// Right-aligned in the band and centered on it vertically. The band excludes the
|
||||
// build button bar's strip, so centering here never puts the panel over the bar
|
||||
// (REQ-UI-SELECTION-PANEL).
|
||||
// Right-aligned in the band and centered vertically on the free part of it, so
|
||||
// the panel sits above whatever the widgets placed before it are occupying rather
|
||||
// than over them (REQ-UI-SELECTION-PANEL).
|
||||
setGeometry(band.right() - panelWidthPx + 1,
|
||||
band.top() + (band.height() - panelHeightPx) / 2,
|
||||
band.top() + (freeHeightPx - panelHeightPx) / 2,
|
||||
panelWidthPx, panelHeightPx);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user