A press that only brings the window to the front draws nothing
From the development repository at 62e0967. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
2de843143d
commit
51e66db9a1
5 files changed
+78
-10
No files matched your search
+32
-1
@@ -16,6 +16,31 @@ const ERASER_BUTTON = 32;
|
||||
*/
|
||||
const NOMINAL_PRESSURE = 0.4;
|
||||
|
||||
/**
|
||||
* A press this soon after the window gained focus is taken to be the
|
||||
* click that brought the window to the front, and draws nothing (Andy,
|
||||
* 2026-10-11): clicking the page to return from the terminal, with the
|
||||
* Pen chosen, left a stray dot. Measured on Hollis (Firefox, Linux):
|
||||
* the window's focus event fires about 4 ms before such a press, the
|
||||
* document already reporting focus, so neither document.hasFocus() nor
|
||||
* the pointer's movement (a pen hovers either way) can tell it from an
|
||||
* ordinary press; only the time since the focus can. A quarter second
|
||||
* is far longer than that press and far shorter than any deliberate
|
||||
* stroke made as a separate act after focusing the window.
|
||||
*/
|
||||
export const FOCUS_SETTLE_MS = 250;
|
||||
|
||||
/**
|
||||
* The end-to-end tests turn the focusing-click rule off with
|
||||
* `?no_focus_settle` in the address: a browser driven by WebDriver gains
|
||||
* window focus on the first pointer action after every load or tab
|
||||
* switch, so it cannot tell such a switch from a click that raised the
|
||||
* window, and the rule would refuse legitimate test strokes. The rule
|
||||
* itself is tested with a dispatched focus event in one tab. A real
|
||||
* user never sets this, and it disables nothing but this nicety.
|
||||
*/
|
||||
const focusSettleOn = !new URLSearchParams(location.search).has('no_focus_settle');
|
||||
|
||||
/**
|
||||
* @typedef {object} PenTarget
|
||||
* @property {(clientX: number, clientY: number) => [number, number]} toPage
|
||||
@@ -100,6 +125,8 @@ export function attachPenInput(surface, target) {
|
||||
let pressY = 0;
|
||||
/** @type {PointerEvent | null} The eraser's press, not yet erased with. */
|
||||
let eraseWaiting = null;
|
||||
/** When the window last gained focus, for the focusing-click rule. */
|
||||
let lastFocusAt = -Infinity;
|
||||
|
||||
/** The eraser's press erases now, if it was waiting. */
|
||||
function eraseFromPress() {
|
||||
@@ -188,6 +215,10 @@ export function attachPenInput(surface, target) {
|
||||
if (activePointer !== null) return refuse(`pointer ${activePointer} still active`);
|
||||
if (event.target !== surface) return refuse('not on the page');
|
||||
if (event.pointerType === 'pen') penSeen = true;
|
||||
// A press that only brought the window to the front draws nothing.
|
||||
if (focusSettleOn && performance.now() - lastFocusAt < FOCUS_SETTLE_MS) {
|
||||
return refuse('it brought the window to the front');
|
||||
}
|
||||
// Once a pen has been used, a touch is taken to be the resting hand.
|
||||
if (event.pointerType === 'touch' && penSeen) return refuse('a touch after a pen: the resting hand');
|
||||
if (event.pointerType === 'mouse' && event.button !== 0) return refuse('not the main button');
|
||||
@@ -325,7 +356,7 @@ export function attachPenInput(surface, target) {
|
||||
// learned on his machine before the rule is written (a window focus
|
||||
// is faked by the test harness on every tab switch, so it cannot be
|
||||
// learned here). The press trace already carries "in front"/"behind".
|
||||
window.addEventListener('focus', () => trace('window: focus'));
|
||||
window.addEventListener('focus', () => { lastFocusAt = performance.now(); trace('window: focus'); });
|
||||
window.addEventListener('blur', () => trace('window: blur'));
|
||||
document.addEventListener('visibilitychange', () => trace(`visibility: ${document.visibilityState}`));
|
||||
|
||||
|
||||
Reference in new issue
Block a user