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:
@@ -14,6 +14,7 @@ add_files(
|
||||
TunnelCompletionTest.cpp
|
||||
WorldCoordinatesTest.cpp
|
||||
WorldCameraTest.cpp
|
||||
FloatingPanelPlacementTest.cpp
|
||||
SelectionControllerTest.cpp
|
||||
BuildModeControllerTest.cpp
|
||||
ControlActionTest.cpp
|
||||
|
||||
68
src/test/FloatingPanelPlacementTest.cpp
Normal file
68
src/test/FloatingPanelPlacementTest.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include "catch.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QRect>
|
||||
|
||||
#include "FloatingPanelPlacement.h"
|
||||
|
||||
// The band every case below places into: 1000x600, so a bottom edge of 599.
|
||||
static QRect makeBand()
|
||||
{
|
||||
return QRect(0, 0, 1000, 600);
|
||||
}
|
||||
|
||||
static const int kMarginPx = 8;
|
||||
|
||||
TEST_CASE("With nothing in the way a widget may use the whole band", "[layout]")
|
||||
{
|
||||
// REQ-UI-SELECTION-PANEL: the band's own bottom is the limit when no other floating
|
||||
// widget has been placed yet.
|
||||
REQUIRE(getAvailableBottomPx(makeBand(), {}, 0, 999, kMarginPx) == 599);
|
||||
}
|
||||
|
||||
TEST_CASE("A widget in the same column pushes the bottom above it", "[layout]")
|
||||
{
|
||||
// The build button bar sitting at the bottom center leaves the space above it, less
|
||||
// the margin kept between the two (REQ-UI-BUILD-BAR).
|
||||
const std::vector<QRect> occupied = { QRect(400, 520, 200, 72) };
|
||||
REQUIRE(getAvailableBottomPx(makeBand(), occupied, 350, 650, kMarginPx) == 511);
|
||||
}
|
||||
|
||||
TEST_CASE("A widget beside the column does not shorten it", "[layout]")
|
||||
{
|
||||
// The controls panel in the bottom-left corner is not in the way of a panel standing
|
||||
// at the right edge, however tall it is (REQ-UI-CONTROLS-PANEL).
|
||||
const std::vector<QRect> occupied = { QRect(0, 100, 200, 499) };
|
||||
REQUIRE(getAvailableBottomPx(makeBand(), occupied, 700, 999, kMarginPx) == 599);
|
||||
|
||||
// Touching columns do count as meeting: the panel starts exactly where the widget
|
||||
// ends, which is an overlap of one pixel.
|
||||
REQUIRE(getAvailableBottomPx(makeBand(), occupied, 199, 999, kMarginPx) == 91);
|
||||
}
|
||||
|
||||
TEST_CASE("The topmost widget in the column decides", "[layout]")
|
||||
{
|
||||
// Several widgets meet the column: the one that reaches highest is the binding one,
|
||||
// whatever order they are given in.
|
||||
const std::vector<QRect> occupied = { QRect(400, 520, 200, 72),
|
||||
QRect(0, 300, 500, 299),
|
||||
QRect(900, 560, 100, 40) };
|
||||
REQUIRE(getAvailableBottomPx(makeBand(), occupied, 450, 550, kMarginPx) == 291);
|
||||
}
|
||||
|
||||
TEST_CASE("An empty rectangle occupies nothing", "[layout]")
|
||||
{
|
||||
// A floating widget that is hidden contributes a null rect rather than being left
|
||||
// out of the pass.
|
||||
const std::vector<QRect> occupied = { QRect(), QRect(400, 520, 200, 0) };
|
||||
REQUIRE(getAvailableBottomPx(makeBand(), occupied, 0, 999, kMarginPx) == 599);
|
||||
}
|
||||
|
||||
TEST_CASE("A widget filling the column leaves nothing", "[layout]")
|
||||
{
|
||||
// The caller is expected to notice that the space left is not positive rather than
|
||||
// being handed a floor of its own.
|
||||
const std::vector<QRect> occupied = { QRect(0, 0, 1000, 600) };
|
||||
REQUIRE(getAvailableBottomPx(makeBand(), occupied, 0, 999, kMarginPx) == -9);
|
||||
}
|
||||
Reference in New Issue
Block a user