Focused text fields show their outline on every edge (#202)
The focus outline of a text field was only visible at its corners. The mask that clips the text covered the whole field, border included, so the 1 px stroke was cut away along every straight edge. Only the rounded corners bent inwards far enough to survive. With the larger radius the new theme tried, this became obvious. - TextField puts text, selection and caret into their own container. Only that container is masked. Background and outline are not. - NumberField and the rename field in lists inherit the fix. - The theme radius goes back to 2 px. - New debug hook __gm.focusedRect() returns where the focused widget sits, so a check can read its border. Tests: - ./check.sh: 141 files, 2080 tests, exit 0 - GM_STEPS=2: ALL PASSED, 24 checks (one new) - The new check in 2c reads the middle of all four edges of the focused name field. All four must be the focus blue. - Counter-check with the old full-field mask: the check fails, all four edges read #24272e. - Editor check total is now 1286 (1285 + 1), computed, not measured. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
940a78e8ee
commit
0d207769b9
5 files changed
+29
-12
No files matched your search
@@ -43,7 +43,7 @@ npm run build # writes dist/ and regenerates index.html; the folder is wh
|
||||
The browser suite and the three example games need a machine with a GPU:
|
||||
|
||||
```bash
|
||||
node tools/seed-e2e.mjs && node tools/e2e.mjs # the editor, 1285 checks (GM_STEPS=20 for one step)
|
||||
node tools/seed-e2e.mjs && node tools/e2e.mjs # the editor, 1286 checks (GM_STEPS=20 for one step)
|
||||
# every tool signs in first – see the Accounts page
|
||||
node tools/seed-jumprun.mjs && node tools/smoke-jumprun.mjs
|
||||
node tools/seed-tetris.mjs && node tools/smoke-tetris.mjs
|
||||
|
||||
@@ -210,6 +210,11 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
|
||||
inspectorField: fieldOf,
|
||||
/** The inspector panel on screen – what tells whether a row of it can be clicked at all. */
|
||||
inspectorRect: () => rectOf(inspector),
|
||||
/** Where the focused widget sits on screen, or `null` – for checks on its focus border. */
|
||||
focusedRect: () => {
|
||||
const widget = ui().focus.focused;
|
||||
return widget ? rectOf(widget) : null;
|
||||
},
|
||||
inspectorInfo: () => inspector.debugInfo(),
|
||||
textColor,
|
||||
overlayTexts,
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ export const Theme = {
|
||||
controlHeight: 24,
|
||||
padding: 8,
|
||||
gap: 4,
|
||||
radius: 8,
|
||||
radius: 2,
|
||||
fieldPadding: 4,
|
||||
checkboxSize: 16,
|
||||
scrollbarWidth: 4,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CanvasTextMetrics, type FederatedPointerEvent, Graphics, Text } from 'pixi.js';
|
||||
import { CanvasTextMetrics, Container, type FederatedPointerEvent, Graphics, Text } from 'pixi.js';
|
||||
import { pressCount } from '../clicks';
|
||||
import { drawControlBox } from '../draw';
|
||||
import type { TextEditTarget } from '../KeyboardBridge';
|
||||
@@ -66,15 +66,12 @@ export class TextField extends Widget implements TextEditTarget {
|
||||
this.minWidth = options.width ?? 80;
|
||||
this.textNode = new Text({ text: value, style: textStyle(options.style ?? 'body') });
|
||||
this.placeholderNode = new Text({ text: this.placeholder, style: textStyle('muted') });
|
||||
this.addChild(
|
||||
this.bg,
|
||||
this.selectionShape,
|
||||
this.placeholderNode,
|
||||
this.textNode,
|
||||
this.caret,
|
||||
this.clipShape,
|
||||
);
|
||||
this.mask = this.clipShape;
|
||||
// Only the contents are clipped: a mask on the whole field cut its border away along the
|
||||
// straight edges and left it showing at the rounded corners alone.
|
||||
const content = new Container();
|
||||
content.addChild(this.selectionShape, this.placeholderNode, this.textNode, this.caret);
|
||||
content.mask = this.clipShape;
|
||||
this.addChild(this.bg, content, this.clipShape);
|
||||
|
||||
this.trackHover();
|
||||
this.on('pointerdown', (e: FederatedPointerEvent) => this.handlePointerDown(e));
|
||||
|
||||
@@ -63,6 +63,21 @@ export async function inspector(t) {
|
||||
await press('a', { ctrl: true });
|
||||
await typeText('Renamed crate');
|
||||
await shot('03-typing');
|
||||
// The focus border runs all the way round, not only through the rounded corners: the field's
|
||||
// clip once took in its own border and cut the straight edges away.
|
||||
const field = await json(`__gm.focusedRect()`);
|
||||
const edges = field
|
||||
? await json(
|
||||
`[[${field.x + field.w / 2}, ${field.y}], [${field.x + field.w / 2}, ${field.y + field.h - 1}],` +
|
||||
` [${field.x}, ${field.y + field.h / 2}], [${field.x + field.w - 1}, ${field.y + field.h / 2}]]` +
|
||||
`.map(([x, y]) => __gm.pixelAt(Math.round(x), Math.round(y)))`,
|
||||
)
|
||||
: [];
|
||||
check(
|
||||
'the focused field is outlined along all four edges',
|
||||
edges.length === 4 && edges.every((c) => c === '#4c8dff'),
|
||||
JSON.stringify({ field, edges }),
|
||||
);
|
||||
await press('Enter');
|
||||
check(
|
||||
'typing + Enter renamed the object',
|
||||
|
||||
Reference in new issue
Block a user