feat(editor): handles for a finger on documentation items, and a grip that moves a text

The focused rectangle or text shows 44 px handles on its focus band: corners,
and the middles of long enough sides; on an item smaller than two targets only
the bottom right one, outside its corner. A text gets a grip above its top
left corner, since its middle is the field typed into. The three-dot button
moves above the top right corner; grip and button step aside at the surface's
edge. A tap on a handle is one on what lies under it, a long press opens the
menu, a second finger zooms. WebKit hands a tap's end to the handle despite
the capture, so the handle passes its events on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-10 22:04:40 +00:00
co-authored by Claude Opus 5.5
parent 12b26f4abe
commit 8c4e5049f6
9 changed files with 588 additions and 12 deletions
+19 -2
View File
@@ -3,7 +3,7 @@
// the sidebar's rectangle dragged onto the surface (its middle under the
// pointer), moved, resized to Java's smallest size, coloured from Java's
// list; the sidebar's text tapped into the view, typed into, made large and
// italic; Java's paint in the documentation mode and in the design; Delete;
// italic; its handles by mouse; Java's paint in the documentation mode and in the design; Delete;
// a simulation started ends the mode; „Exportieren“ (F4.8c): the image
// saved under the project's name, cut and painted as Java's, and Java's
// message when saving fails; and a phone.
@@ -11,7 +11,7 @@
import { readFileSync } from 'node:fs';
import { expect, test, type Page } from '@playwright/test';
import { JAVA_LABEL_FONT } from '../src/canvas/layout.js';
import { documentUpdates, drag, drawing, openEditor, serveEditor } from './editorPage.js';
import { documentUpdates, drag, drawing, openEditor, scaleOf, serveEditor } from './editorPage.js';
let server: { readonly base: string; readonly close: () => Promise<void> };
@@ -122,6 +122,23 @@ test('makes, moves, resizes, colours and types documentation as Java does, and p
})
.toEqual([18, true, false]);
await expect(field).toHaveCSS('font-style', 'italic');
// Its handles take the mouse too, even where one jump of it lands over the field typed into: the corner follows.
const sized = (await docOf(page, text.id))!;
const scale = await scaleOf(page);
const cornerHandle = (await page.locator('[data-handle="bottom-right"]').boundingBox())!;
const from = { x: cornerHandle.x + cornerHandle.width / 2, y: cornerHandle.y + cornerHandle.height / 2 };
const fieldBox = (await field.boundingBox())!;
const into = { x: fieldBox.x + fieldBox.width - 4, y: fieldBox.y + fieldBox.height - 4 };
await page.mouse.move(from.x, from.y);
await page.mouse.down();
await page.mouse.move(into.x, into.y);
await page.mouse.up();
await expect
.poll(async () => {
const item = (await docOf(page, text.id))!;
return [item.width, item.height].map(Math.round);
})
.toEqual([Math.round(Math.max(88, sized.width + (into.x - from.x) / scale)), Math.round(Math.max(20, sized.height + (into.y - from.y) / scale))]);
// Escape ends the focus: the text drawn, with Java's grey border in the documentation mode.
await field.press('Escape');