Replace rectangle button with rounded-rect Graphics to match pill styling

The old flat `Rectangle` background couldn't have rounded corners, so buttons visually clashed with the pill UI elements. Switching to a `Graphics` object lets us draw a rounded rect (using the shared `PILL_RADIUS`) while preserving the same fill/stroke colors and hover behavior.

Key details:
- Added `drawBg()` helper that clears and redraws the rounded rect + outline, reused for both normal and hover states.
- Replaced `setFillStyle` hover handlers with re-draw calls since Graphics has no fill style API.
- Defined an explicit hit area matching the drawn rounded rect; without it, `setInteractive()` on a Graphics uses the full 512x512 texture frame, causing clicks near other buttons to land on the wrong one.
This commit is contained in:
Brian Fertig 2026-08-22 11:11:50 -06:00
parent ba3385ee19
commit 2e4b67db89
1 changed files with 32 additions and 4 deletions

View File

@ -1,20 +1,48 @@
import Phaser from 'phaser';
import { WORLD_SCALE } from '../config.js';
import { PILL_RADIUS } from './pill.js';
// Rounded-rect blue button - same look as the old flat Rectangle button,
// just with rounded corners matching the pills (PILL_RADIUS). Graphics
// instead of a Rectangle because only Graphics can round corners; the
// hover highlight re-draws the fill, keeping the dark outline.
const FILL = 0x2255aa;
const HOVER_FILL = 0x2f6ac0;
const STROKE = 0x1a1f29;
const STROKE_WIDTH = 2 * WORLD_SCALE;
function drawBg(g, width, height, fill) {
g.clear();
g.fillStyle(fill, 1);
g.fillRoundedRect(-width / 2, -height / 2, width, height, PILL_RADIUS);
g.lineStyle(STROKE_WIDTH, STROKE, 1);
g.strokeRoundedRect(-width / 2, -height / 2, width, height, PILL_RADIUS);
}
export function createButton(scene, x, y, label, onClick, options = {}) {
const width = options.width || 220 * WORLD_SCALE;
const height = options.height || 56 * WORLD_SCALE;
const fontSize = options.fontSize || `${20 * WORLD_SCALE}px`;
const bg = scene.add.rectangle(x, y, width, height, 0x2255aa).setStrokeStyle(2 * WORLD_SCALE, 0x1a1f29);
const bg = scene.add.graphics().setPosition(x, y);
drawBg(bg, width, height, FILL);
const text = scene.add.text(x, y, label, {
fontFamily: 'monospace',
fontSize,
color: '#ffffff',
}).setOrigin(0.5);
bg.setInteractive({ useHandCursor: true });
bg.on('pointerover', () => bg.setFillStyle(0x2f6ac0));
bg.on('pointerout', () => bg.setFillStyle(0x2255aa));
// Explicit hit rect - a default setInteractive() on a Graphics would use
// its full 512x512 texture frame, making clicks near other buttons land
// on the wrong one. Graphics hit areas live in its local drawing space
// (displayOrigin 0,0), so the rect mirrors the drawn rounded rect.
bg.setInteractive({
hitArea: new Phaser.Geom.Rectangle(-width / 2, -height / 2, width, height),
hitAreaCallback: Phaser.Geom.Rectangle.Contains,
useHandCursor: true,
});
bg.on('pointerover', () => drawBg(bg, width, height, HOVER_FILL));
bg.on('pointerout', () => drawBg(bg, width, height, FILL));
bg.on('pointerdown', onClick);
return { bg, text };