Mechanical sweep of all .sx sources, plan docs, and tests/expected snapshots for the sx language rename (s8/s16/s32/s64 -> i8/i16/i32/i64). Verified: tools/run_tests.sh 23/23. Note: the ios-sim build has 2 pre-existing 'restart' dot-call errors from the sx opt-in UFCS change (sx a47ea14) — independent of this rename (present pre-sweep); migrated in the follow-up commit.
96 lines
3.9 KiB
Plaintext
96 lines
3.9 KiB
Plaintext
// Pure geometry of the on-screen board: where the centered 8×8 grid sits inside
|
||
// a frame, and the two-way mapping between cells and screen points. Owns no
|
||
// rendering and pulls in NO GL/stb imports, so the touch→cell mapping is
|
||
// unit-testable headless. BoardView composes this for layout + hit-testing, and
|
||
// P5's swap input reuses `point_to_cell` to resolve a tap to a swap endpoint.
|
||
#import "modules/std.sx";
|
||
#import "modules/math";
|
||
#import "modules/ui/types.sx";
|
||
#import "board.sx";
|
||
|
||
BoardLayout :: struct {
|
||
cell_size: f32;
|
||
origin: Point;
|
||
|
||
// Center a square 8×8 grid inside the safe-area-inset region of `frame`.
|
||
compute :: (self: *BoardLayout, frame: Frame, safe: EdgeInsets) {
|
||
avail := frame.inset(safe);
|
||
cols : f32 = xx BOARD_COLS;
|
||
board_dim := min(avail.size.width, avail.size.height);
|
||
self.cell_size = board_dim / cols;
|
||
total := self.cell_size * cols;
|
||
self.origin = Point.{
|
||
x = avail.origin.x + (avail.size.width - total) * 0.5,
|
||
y = avail.origin.y + (avail.size.height - total) * 0.5
|
||
};
|
||
}
|
||
|
||
cell_frame :: (self: *BoardLayout, col: i64, row: i64) -> Frame {
|
||
Frame.make(
|
||
self.origin.x + xx col * self.cell_size,
|
||
self.origin.y + xx row * self.cell_size,
|
||
self.cell_size,
|
||
self.cell_size
|
||
)
|
||
}
|
||
|
||
// Inverse of `cell_frame`: map a view-local point to the grid cell under it,
|
||
// or null when the point falls outside the 8×8 grid. The `< 0.0` guards run
|
||
// BEFORE the truncating cast, since casting a small negative float rounds
|
||
// toward zero into a valid index. Uses the SAME origin / cell_size `compute`
|
||
// produced, so a tap resolves to exactly the cell drawn under the finger.
|
||
point_to_cell :: (self: *BoardLayout, p: Point) -> ?Cell {
|
||
if self.cell_size <= 0.0 { return null; }
|
||
fx := (p.x - self.origin.x) / self.cell_size;
|
||
fy := (p.y - self.origin.y) / self.cell_size;
|
||
if fx < 0.0 or fy < 0.0 { return null; }
|
||
col : i64 = xx fx;
|
||
row : i64 = xx fy;
|
||
if col >= BOARD_COLS or row >= BOARD_ROWS { return null; }
|
||
Cell.{ col = col, row = row }
|
||
}
|
||
|
||
// Frame of the whole 8×8 grid (origin + cols/rows × cell_size). The banner
|
||
// and its dimming overlay are sized off this so they cover exactly the board.
|
||
grid_frame :: (self: *BoardLayout) -> Frame {
|
||
Frame.make(
|
||
self.origin.x, self.origin.y,
|
||
self.cell_size * cast(f32) BOARD_COLS,
|
||
self.cell_size * cast(f32) BOARD_ROWS
|
||
)
|
||
}
|
||
|
||
// Win/lose banner geometry (P7.2): an overlay panel centered over the board
|
||
// grid, with the title band and the restart button inside it. Derived purely
|
||
// from the SAME grid layout the gems use, so the restart hit-test in
|
||
// BoardView.handle_event lands on exactly the button BoardView draws. The
|
||
// headless banner_layout test locks the button-rect ↔ hit-test round-trip.
|
||
banner :: (self: *BoardLayout) -> BannerLayout {
|
||
grid := self.grid_frame();
|
||
cx := grid.mid_x();
|
||
cy := grid.mid_y();
|
||
|
||
panel_w := grid.size.width * 0.84;
|
||
panel_h := grid.size.height * 0.44;
|
||
panel := Frame.make(cx - panel_w * 0.5, cy - panel_h * 0.5, panel_w, panel_h);
|
||
|
||
title := Frame.make(panel.origin.x, panel.origin.y + panel_h * 0.18, panel_w, panel_h * 0.30);
|
||
|
||
btn_w := panel_w * 0.60;
|
||
btn_h := panel_h * 0.24;
|
||
btn_y := panel.origin.y + panel_h - btn_h - panel_h * 0.16;
|
||
button := Frame.make(cx - btn_w * 0.5, btn_y, btn_w, btn_h);
|
||
|
||
BannerLayout.{ panel = panel, title = title, button = button }
|
||
}
|
||
}
|
||
|
||
// Resolved rectangles of the win/lose banner: the centered `panel`, the `title`
|
||
// band where the win/lose headline is centered, and the restart `button` rect
|
||
// (also the hit-test target). All in the same view-local space as BoardLayout.
|
||
BannerLayout :: struct {
|
||
panel: Frame;
|
||
title: Frame;
|
||
button: Frame;
|
||
}
|