keep the selection panel half a tile off what it describes
The panel had one distance for the view edges, the widgets it steps around, and the selection alike, so it stood eight pixels from a building and touched it outright along the top. The gap from the selection is now its own value, half a tile, and it is horizontal only -- the top edges stay level. It is sampled where the tile size is known, in the same moment as the anchor rectangle, and travels with it: a rectangle frozen in one moment has no meaningful distance to a tile size measured in another. chooseSide now takes the gap in place of the margin, the band having already taken the margin off. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ne3mejABZoLWKLh8fgpM3x
This commit is contained in:
@@ -12,7 +12,11 @@ static QRect makeBand()
|
||||
return QRect(0, 0, 1000, 600);
|
||||
}
|
||||
|
||||
static const int kMarginPx = 8;
|
||||
// The two distances the rules run on, deliberately different (REQ-UI-SELECTION-PANEL):
|
||||
// the edge margin the panel keeps from the view and from the widgets it steps around, and
|
||||
// the wider gap it keeps from the selection -- half a tile, so 20 px at a 40 px tile.
|
||||
static const int kMarginPx = 8;
|
||||
static const int kSelectionGapPx = 20;
|
||||
|
||||
TEST_CASE("With nothing in the way a widget may use the whole band", "[layout]")
|
||||
{
|
||||
@@ -74,25 +78,34 @@ TEST_CASE("A widget filling the column leaves nothing", "[layout]")
|
||||
TEST_CASE("The panel stands to the right of the selection where it fits", "[layout]")
|
||||
{
|
||||
// REQ-UI-SELECTION-PANEL: right of the anchor is the first choice.
|
||||
REQUIRE(chooseSide(makeBand(), QRect(100, 100, 60, 60), 300, kMarginPx)
|
||||
REQUIRE(chooseSide(makeBand(), QRect(100, 100, 60, 60), 300, kSelectionGapPx)
|
||||
== PanelSide::Right);
|
||||
}
|
||||
|
||||
TEST_CASE("The panel goes left when the right cannot hold it", "[layout]")
|
||||
{
|
||||
// A selection near the right edge leaves 100 px there, not enough for a 300 px
|
||||
// panel, and the left is wide open.
|
||||
REQUIRE(chooseSide(makeBand(), QRect(880, 100, 20, 60), 300, kMarginPx)
|
||||
// A selection near the right edge leaves 80 px there once the gap it keeps from the
|
||||
// selection is taken off, not enough for a 300 px panel, and the left is wide open.
|
||||
REQUIRE(chooseSide(makeBand(), QRect(880, 100, 20, 60), 300, kSelectionGapPx)
|
||||
== PanelSide::Left);
|
||||
}
|
||||
|
||||
TEST_CASE("The room a side offers is measured less the gap", "[layout]")
|
||||
{
|
||||
// A 200 px panel beside a selection whose right edge leaves 214 px to the band's: it
|
||||
// fits there on the margin alone, but not once the wider gap is taken off.
|
||||
const QRect anchorRect(766, 100, 20, 60);
|
||||
REQUIRE(chooseSide(makeBand(), anchorRect, 200, kMarginPx) == PanelSide::Right);
|
||||
REQUIRE(chooseSide(makeBand(), anchorRect, 200, kSelectionGapPx) == PanelSide::Left);
|
||||
}
|
||||
|
||||
TEST_CASE("Fitting on neither side, the panel takes the roomier one", "[layout]")
|
||||
{
|
||||
// A bounding box spanning most of the view: 192 px free on the left, 92 on the
|
||||
// A bounding box spanning most of the view: 180 px free on the left, 80 on the
|
||||
// right, and a 300 px panel fits in neither. It covers as little as it can.
|
||||
REQUIRE(chooseSide(makeBand(), QRect(200, 100, 700, 200), 300, kMarginPx)
|
||||
REQUIRE(chooseSide(makeBand(), QRect(200, 100, 700, 200), 300, kSelectionGapPx)
|
||||
== PanelSide::Left);
|
||||
REQUIRE(chooseSide(makeBand(), QRect(100, 100, 700, 200), 300, kMarginPx)
|
||||
REQUIRE(chooseSide(makeBand(), QRect(100, 100, 700, 200), 300, kSelectionGapPx)
|
||||
== PanelSide::Right);
|
||||
}
|
||||
|
||||
@@ -102,17 +115,30 @@ TEST_CASE("Fitting on neither side, the panel takes the roomier one", "[layout]"
|
||||
|
||||
TEST_CASE("The panel sits beside the anchor with its top edges aligned", "[layout]")
|
||||
{
|
||||
// REQ-UI-SELECTION-PANEL: separated by the margin, growing away from the selection,
|
||||
// top edge on the anchor's top edge.
|
||||
// REQ-UI-SELECTION-PANEL: separated by the gap it keeps from the selection, growing
|
||||
// away from it, top edge on the anchor's top edge.
|
||||
const QRect placed = placeBesideAnchor(makeBand(), QRect(100, 120, 60, 60),
|
||||
PanelSide::Right, QSize(300, 200), {},
|
||||
kMarginPx);
|
||||
REQUIRE(placed == QRect(168, 120, 300, 200));
|
||||
kSelectionGapPx, kMarginPx);
|
||||
REQUIRE(placed == QRect(180, 120, 300, 200));
|
||||
|
||||
const QRect placedLeft = placeBesideAnchor(makeBand(), QRect(500, 120, 60, 60),
|
||||
const QRect placedLeft = placeBesideAnchor(makeBand(), QRect(520, 120, 60, 60),
|
||||
PanelSide::Left, QSize(300, 200), {},
|
||||
kMarginPx);
|
||||
REQUIRE(placedLeft == QRect(192, 120, 300, 200));
|
||||
kSelectionGapPx, kMarginPx);
|
||||
REQUIRE(placedLeft == QRect(200, 120, 300, 200));
|
||||
}
|
||||
|
||||
TEST_CASE("The gap separates the panel horizontally only", "[layout]")
|
||||
{
|
||||
// REQ-UI-SELECTION-PANEL: widening the gap moves the panel further from the selection
|
||||
// sideways and nowhere else -- its top stays level with the top of what it describes.
|
||||
const QRect anchorRect(100, 120, 60, 60);
|
||||
REQUIRE(placeBesideAnchor(makeBand(), anchorRect, PanelSide::Right, QSize(300, 200),
|
||||
{}, 0, kMarginPx)
|
||||
== QRect(160, 120, 300, 200));
|
||||
REQUIRE(placeBesideAnchor(makeBand(), anchorRect, PanelSide::Right, QSize(300, 200),
|
||||
{}, kSelectionGapPx, kMarginPx)
|
||||
== QRect(180, 120, 300, 200));
|
||||
}
|
||||
|
||||
TEST_CASE("A panel that would hang below the view is lifted", "[layout]")
|
||||
@@ -121,19 +147,20 @@ TEST_CASE("A panel that would hang below the view is lifted", "[layout]")
|
||||
// the band, so it rises until it fits rather than overrunning it.
|
||||
const QRect placed = placeBesideAnchor(makeBand(), QRect(100, 500, 60, 60),
|
||||
PanelSide::Right, QSize(300, 200), {},
|
||||
kMarginPx);
|
||||
REQUIRE(placed == QRect(168, 400, 300, 200));
|
||||
kSelectionGapPx, kMarginPx);
|
||||
REQUIRE(placed == QRect(180, 400, 300, 200));
|
||||
}
|
||||
|
||||
TEST_CASE("A panel standing over another widget rises above it", "[layout]")
|
||||
{
|
||||
// The controls panel in the bottom-left is in the way of a panel placed to the left
|
||||
// of a selection: it clears the top of it by the margin (REQ-UI-CONTROLS-PANEL).
|
||||
// of a selection: it clears the top of it by the edge margin, the gap from the
|
||||
// selection having settled its left edge (REQ-UI-CONTROLS-PANEL).
|
||||
const std::vector<QRect> occupied = { QRect(0, 300, 260, 300) };
|
||||
const QRect placed = placeBesideAnchor(makeBand(), QRect(500, 250, 60, 60),
|
||||
PanelSide::Left, QSize(300, 200), occupied,
|
||||
kMarginPx);
|
||||
REQUIRE(placed == QRect(192, 92, 300, 200));
|
||||
kSelectionGapPx, kMarginPx);
|
||||
REQUIRE(placed == QRect(180, 92, 300, 200));
|
||||
}
|
||||
|
||||
TEST_CASE("A panel taller than the space left is capped", "[layout]")
|
||||
@@ -141,8 +168,8 @@ TEST_CASE("A panel taller than the space left is capped", "[layout]")
|
||||
// Capping is the caller's cue to scroll: it asked for 700 and got what there was.
|
||||
const QRect placed = placeBesideAnchor(makeBand(), QRect(100, 100, 60, 60),
|
||||
PanelSide::Right, QSize(300, 700), {},
|
||||
kMarginPx);
|
||||
REQUIRE(placed == QRect(168, 0, 300, 600));
|
||||
kSelectionGapPx, kMarginPx);
|
||||
REQUIRE(placed == QRect(180, 0, 300, 600));
|
||||
}
|
||||
|
||||
TEST_CASE("A panel that fits on neither side is pushed inside the view", "[layout]")
|
||||
@@ -151,7 +178,7 @@ TEST_CASE("A panel that fits on neither side is pushed inside the view", "[layou
|
||||
// stands as far from the anchor as the band allows, not off the edge of it.
|
||||
const QRect placed = placeBesideAnchor(makeBand(), QRect(100, 100, 700, 200),
|
||||
PanelSide::Right, QSize(300, 200), {},
|
||||
kMarginPx);
|
||||
kSelectionGapPx, kMarginPx);
|
||||
REQUIRE(placed == QRect(700, 100, 300, 200));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user