Move Re-Capture and Insert Conform from the panel top bar to the overflow menu; re-pin action_bar width test to the new 4-button top bar.

This commit is contained in:
2026-08-03 17:44:00 -04:00
parent 4b19556513
commit d22446ef7d
3 changed files with 47 additions and 58 deletions
+33 -47
View File
@@ -30,15 +30,13 @@ static int g_fail = 0;
#define CHECK(cond) do { if(!(cond)) { \
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
// The panel's real inventory shape: 2 capture, 1 maintenance (Re-capture), 3 placement
// (Insert / Insert Conform / Insert as FX) = 6 buttons. Cluster order:
// Capture -> Maintenance -> Placement (Re-capture sits between the two capture verbs and the
// placement verbs).
// The panel's real inventory shape: 2 capture, 2 placement (Insert / Insert as FX) = 4
// buttons. Re-capture and Insert Conform moved to the overflow menu, so the top bar's
// Maintenance cluster is now empty. Cluster order: Capture -> Placement.
static std::vector<ClusterSpec> inventory() {
return {
{ActionCluster::Capture, 2},
{ActionCluster::Maintenance, 1},
{ActionCluster::Placement, 3},
{ActionCluster::Placement, 2},
};
}
@@ -55,28 +53,26 @@ static ActionBarSpec roundSpec() {
// --- Layout: all fit, correct rects + gaps ------------------------------------
// A wide bar fits all 6 buttons. Verify the first few rects, the intra-cluster gap, and the
// (wider) inter-cluster gap between button 1 (last capture) and button 2 (maintenance).
// A wide bar fits all 4 buttons. Verify the first few rects, the intra-cluster gap, and the
// (wider) inter-cluster gap between button 1 (last capture) and button 2 (first placement).
//
// Pixel walk with roundSpec and bar at (0,40):
// btn0 (Capture): x=8, right=108
// btn1 (Capture): x=112, right=212 (intraGap = 4 after btn0's right)
// btn2 (Maintenance): x=228, right=328 (clusterGap = 16 after btn1's right)
// btn3 (Placement): x=344, right=444 (clusterGap = 16 after btn2's right)
// btn4 (Placement): x=448, right=548 (intraGap = 4 after btn3's right)
// btn5 (Placement): x=552, right=652 (intraGap = 4 after btn4's right)
// Minimum bar width: 652 + sidePad(8) = 660. Give it 700.
// btn0 (Capture): x=8, right=108
// btn1 (Capture): x=112, right=212 (intraGap = 4 after btn0's right)
// btn2 (Placement): x=228, right=328 (clusterGap = 16 after btn1's right)
// btn3 (Placement): x=332, right=432 (intraGap = 4 after btn2's right)
// Minimum bar width: 432 + sidePad(8) = 440. Give it 500.
static void testAllFitRectsAndGaps() {
const auto clusters = inventory();
const ActionBarSpec spec = roundSpec();
ActionBarRect bar{0, 40, 700, 34};
ActionBarRect bar{0, 40, 500, 34};
BarFit fit = computeBarFit(bar, clusters, spec);
CHECK(fit.visibleCount == 6);
CHECK(fit.visibleCount == 4);
CHECK(fit.hiddenCount == 0);
auto slots = computeBarSlots(bar, clusters, spec);
CHECK(slots.size() == 6);
CHECK(slots.size() == 4);
// Button 0: at sidePad, top = y + verticalInset, height = barH - 2*inset.
CHECK(slots[0].x == 8);
@@ -90,27 +86,17 @@ static void testAllFitRectsAndGaps() {
CHECK(slots[1].x == 112);
CHECK(slots[1].cluster == ActionCluster::Capture);
// Button 2 (maintenance / Re-capture): cluster gap of 16 after 212 -> 228.
// Button 2 (first placement): cluster gap of 16 after 212 -> 228.
CHECK(slots[2].x == 228);
CHECK(slots[2].cluster == ActionCluster::Maintenance);
CHECK(slots[2].cluster == ActionCluster::Placement);
CHECK(slots[2].index == 2);
// Button 3 (first placement): cluster gap of 16 after 328 -> 344.
CHECK(slots[3].x == 344);
// Button 3 (Insert as FX — second placement): intra-cluster gap of 4 after 328 -> 332.
// It joins the Placement cluster, so no cluster gap opens before it.
CHECK(slots[3].x == 332);
CHECK(slots[3].cluster == ActionCluster::Placement);
CHECK(slots[3].index == 3);
// Button 4 (second placement): intra-cluster gap of 4 after 444 -> 448.
CHECK(slots[4].x == 448);
CHECK(slots[4].cluster == ActionCluster::Placement);
CHECK(slots[4].index == 4);
// Button 5 (Insert as FX — third placement): intra-cluster gap of 4 after 548 -> 552.
// It joins the Placement cluster, so no cluster gap opens before it.
CHECK(slots[5].x == 552);
CHECK(slots[5].cluster == ActionCluster::Placement);
CHECK(slots[5].index == 5);
CHECK(slots[5].x - (slots[4].x + slots[4].width) == spec.buttonGap);
CHECK(slots[3].x - (slots[2].x + slots[2].width) == spec.buttonGap);
// Inter-cluster gap (slot[2].x - slot[1].right = 228 - 212 = 16) is wider than the
// intra-cluster gap (slot[1].x - slot[0].right = 112 - 108 = 4) — task grouping is
@@ -159,14 +145,14 @@ static void testLabelFullHeightWhenShort() {
// keep their full width (never clipped), and the frequent capture cluster survives.
//
// Exact fit for 2 capture buttons: sidePad(8) + 2*100 + 1*4 + sidePad(8) = 220.
// A 3rd button (Maintenance) needs clusterGap(16)+100 = 116 more -> 336. So 220 fits exactly 2.
// A 3rd button (Placement) needs clusterGap(16)+100 = 116 more -> 336. So 220 fits exactly 2.
static void testOverflowDropsTrailingWhole() {
const auto clusters = inventory();
const ActionBarSpec spec = roundSpec();
ActionBarRect bar{0, 0, 220, 34};
BarFit fit = computeBarFit(bar, clusters, spec);
CHECK(fit.visibleCount == 2);
CHECK(fit.hiddenCount == 4);
CHECK(fit.hiddenCount == 2);
auto slots = computeBarSlots(bar, clusters, spec);
CHECK(slots.size() == 2);
@@ -186,7 +172,7 @@ static void testTooNarrowForAny() {
ActionBarRect bar{0, 0, 60, 34}; // sidePad*2 + one 100-wide button won't fit
BarFit fit = computeBarFit(bar, clusters, spec);
CHECK(fit.visibleCount == 0);
CHECK(fit.hiddenCount == 6);
CHECK(fit.hiddenCount == 4);
CHECK(computeBarSlots(bar, clusters, spec).empty());
}
@@ -246,9 +232,9 @@ static ActionBarSpec realBarSpec() {
}
// At the real spec plus the real 40 px reserve (28 + 2*6, panel_state.h's kMenuBtnSpec --
// all default-constructed here since its defaults already match), 764 is the narrowest
// band that still shows all six buttons; one pixel narrower drops Insert as FX (flat index
// 5, the trailing Placement button) alone into overflow, leaving its five neighbours intact.
// all default-constructed here since its defaults already match), 520 is the narrowest
// band that still shows all four buttons; one pixel narrower drops Insert as FX (flat index
// 3, the trailing Placement button) alone into overflow, leaving its three neighbours intact.
static void testReserveDropsInsertAsFxAtRealWidth() {
const auto clusters = inventory();
const ActionBarSpec spec = realBarSpec();
@@ -256,18 +242,18 @@ static void testReserveDropsInsertAsFxAtRealWidth() {
const int reserve = menuButtonReserve(MenuBarRect{0, 0, 900, 34}, menuSpec);
CHECK(reserve == 40);
const int allSixWidth = 764;
const int allFourWidth = 520;
{
ActionBarRect action{0, 0, allSixWidth - reserve, 34};
ActionBarRect action{0, 0, allFourWidth - reserve, 34};
const auto slots = computeBarSlots(action, clusters, spec);
CHECK(slots.size() == 6);
CHECK(slots.back().index == 5); // Insert as FX still visible
CHECK(slots.size() == 4);
CHECK(slots.back().index == 3); // Insert as FX still visible
}
{
ActionBarRect action{0, 0, allSixWidth - 1 - reserve, 34};
ActionBarRect action{0, 0, allFourWidth - 1 - reserve, 34};
const auto slots = computeBarSlots(action, clusters, spec);
CHECK(slots.size() == 5); // Insert as FX alone dropped
CHECK(slots.back().index == 4); // Insert Conform is now the trailing visible button
CHECK(slots.size() == 3); // Insert as FX alone dropped
CHECK(slots.back().index == 2); // Insert is now the trailing visible button
}
}