The Shape tool, implied rectangles, and the palette floating again
A Shapes button right of Text draws a rectangle, an ellipse, a line of taps or an arrow in the pen's colour and width, with a fill colour of its own; a hold, a secondary press or two quick clicks open its menu. Text and Hide rectangles draw nothing on the page and show blue corners while the pointer hovers over them; free text's red corner mark is gone. Arrange moves and resizes shapes. The colour palette floats below "+" again instead of reflowing the toolbar. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
46a82bf3b7
commit
03907da8c9
12 files changed
+1502
-297
No files matched your search
+26
-7
@@ -14,7 +14,7 @@ const ERASER_BUTTON = 32;
|
||||
* Pressure recorded for a mouse or a finger, which report none. It is the
|
||||
* pressure at which a stroke is drawn at its nominal width.
|
||||
*/
|
||||
const NOMINAL_PRESSURE = 0.4;
|
||||
export const NOMINAL_PRESSURE = 0.4;
|
||||
|
||||
/**
|
||||
* A press this soon after the window gained focus is taken to be the
|
||||
@@ -41,11 +41,16 @@ export const FOCUS_SETTLE_MS = 250;
|
||||
*/
|
||||
const focusSettleOn = !new URLSearchParams(location.search).has('no_focus_settle');
|
||||
|
||||
/**
|
||||
* The tools of the toolbar that act on the page.
|
||||
* @typedef {'pen' | 'eraser' | 'text' | 'hide' | 'arrange' | 'shape'} Tool
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PenTarget
|
||||
* @property {(clientX: number, clientY: number) => [number, number]} toPage
|
||||
* Convert a window position to page units.
|
||||
* @property {() => 'pen' | 'eraser' | 'text' | 'hide' | 'arrange'} tool The tool selected in the toolbar.
|
||||
* @property {() => Tool} tool The tool selected in the toolbar.
|
||||
* @property {() => boolean} canWrite False while there is no page to write on.
|
||||
* @property {() => void} beginStroke
|
||||
* @property {(points: number[]) => void} addPoints
|
||||
@@ -56,10 +61,16 @@ const focusSettleOn = !new URLSearchParams(location.search).has('no_focus_settle
|
||||
* The eraser went down at (x, y) and has come up again, having moved
|
||||
* no farther than `reach` from there; Infinity if the press was cancelled.
|
||||
* @property {(x0: number, y0: number, x1: number, y1: number) => void} placeRect
|
||||
* With the text or Hide tool, the pointer went down at (x0, y0) and up
|
||||
* at (x1, y1): a tap, or a rectangle dragged open.
|
||||
* With the Text, Hide or Shape tool, the pointer went down at (x0, y0)
|
||||
* and up at (x1, y1): a tap, or a rectangle (a shape) dragged open.
|
||||
* @property {(x0: number, y0: number, x1: number, y1: number) => void} [previewRect]
|
||||
* With the text or Hide tool, the pointer is down at (x0, y0) and now at (x1, y1).
|
||||
* With the Text, Hide or Shape tool, the pointer is down at (x0, y0) and now at (x1, y1).
|
||||
* @property {(clientX: number, clientY: number) => void} [hover]
|
||||
* The pointer moves over the page with no button down and nothing in
|
||||
* progress (a mouse, or a pen in the air above a tablet that reports
|
||||
* it; a finger never hovers).
|
||||
* @property {() => void} [hoverEnded]
|
||||
* The pointer left the page, or pressed.
|
||||
* @property {(clientX: number, clientY: number) => boolean} [beginHold]
|
||||
* The pointer has rested on the page for HOLD_MS, with any tool: a
|
||||
* hold. True if something under it took the hold (an image to move
|
||||
@@ -240,8 +251,11 @@ export function attachPenInput(surface, target) {
|
||||
return;
|
||||
}
|
||||
const tool = target.tool();
|
||||
target.hoverEnded?.();
|
||||
erasing = tool === 'eraser' || (event.buttons & ERASER_BUTTON) !== 0;
|
||||
placingText = (tool === 'text' || tool === 'hide') && !erasing;
|
||||
// The Text, Hide and Shape tools place by a tap or a drag, reported
|
||||
// whole when the pointer lifts, with a preview meanwhile.
|
||||
placingText = (tool === 'text' || tool === 'hide' || tool === 'shape') && !erasing;
|
||||
lastX = NaN;
|
||||
lastY = NaN;
|
||||
strokeStart = performance.now();
|
||||
@@ -289,7 +303,11 @@ export function attachPenInput(surface, target) {
|
||||
});
|
||||
|
||||
surface.addEventListener('pointermove', (event) => {
|
||||
if (event.pointerId !== activePointer) return;
|
||||
if (event.pointerId !== activePointer) {
|
||||
// A pointer in the air: hovering, if nothing is pressed.
|
||||
if (activePointer === null && event.buttons === 0) target.hover?.(event.clientX, event.clientY);
|
||||
return;
|
||||
}
|
||||
if (holding) {
|
||||
target.holdMove?.(event.clientX, event.clientY);
|
||||
return;
|
||||
@@ -349,6 +367,7 @@ export function attachPenInput(surface, target) {
|
||||
}
|
||||
surface.addEventListener('pointerup', finish);
|
||||
surface.addEventListener('pointercancel', finish);
|
||||
surface.addEventListener('pointerleave', () => { if (activePointer === null) target.hoverEnded?.(); });
|
||||
surface.addEventListener('lostpointercapture', (event) => {
|
||||
if (event.pointerId === activePointer) trace(`capture of pointer ${event.pointerId} lost while active`);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user