@@ -384,16 +384,22 @@ export function createResttyApp(options: ResttyAppOptions): ResttyApp {
384384 const touchSelectionState : {
385385 pendingPointerId : number | null ;
386386 activePointerId : number | null ;
387+ panPointerId : number | null ;
387388 pendingCell : { row : number ; col : number } | null ;
389+ pendingStartedAt : number ;
388390 pendingStartX : number ;
389391 pendingStartY : number ;
392+ panLastY : number ;
390393 pendingTimer : number ;
391394 } = {
392395 pendingPointerId : null ,
393396 activePointerId : null ,
397+ panPointerId : null ,
394398 pendingCell : null ,
399+ pendingStartedAt : 0 ,
395400 pendingStartX : 0 ,
396401 pendingStartY : 0 ,
402+ panLastY : 0 ,
397403 pendingTimer : 0 ,
398404 } ;
399405
@@ -431,6 +437,21 @@ export function createResttyApp(options: ResttyAppOptions): ResttyApp {
431437 }
432438 touchSelectionState . pendingPointerId = null ;
433439 touchSelectionState . pendingCell = null ;
440+ touchSelectionState . pendingStartedAt = 0 ;
441+ }
442+
443+ function tryActivatePendingTouchSelection ( pointerId : number ) {
444+ if ( touchSelectionMode !== "long-press" ) return false ;
445+ if ( touchSelectionState . pendingPointerId !== pointerId || ! touchSelectionState . pendingCell ) {
446+ return false ;
447+ }
448+ if ( performance . now ( ) - touchSelectionState . pendingStartedAt < touchSelectionLongPressMs ) {
449+ return false ;
450+ }
451+ const pendingCell = touchSelectionState . pendingCell ;
452+ clearPendingTouchSelection ( ) ;
453+ beginSelectionDrag ( pendingCell , pointerId ) ;
454+ return true ;
434455 }
435456
436457 function beginSelectionDrag ( cell : { row : number ; col : number } , pointerId : number ) {
@@ -439,6 +460,7 @@ export function createResttyApp(options: ResttyAppOptions): ResttyApp {
439460 selectionState . anchor = cell ;
440461 selectionState . focus = cell ;
441462 touchSelectionState . activePointerId = pointerId ;
463+ touchSelectionState . panPointerId = null ;
442464 canvas . setPointerCapture ?.( pointerId ) ;
443465 updateCanvasCursor ( ) ;
444466 needsRender = true ;
@@ -448,6 +470,18 @@ export function createResttyApp(options: ResttyAppOptions): ResttyApp {
448470 scrollbarState . lastInputAt = performance . now ( ) ;
449471 }
450472
473+ function scrollViewportByLines ( lines : number ) {
474+ if ( ! wasmReady || ! wasmHandle || ! gridState . cellH ) return ;
475+ scrollRemainder += lines ;
476+ const delta = Math . trunc ( scrollRemainder ) ;
477+ scrollRemainder -= delta ;
478+ if ( ! delta ) return ;
479+ wasm . scrollViewport ( wasmHandle , delta ) ;
480+ wasm . renderUpdate ( wasmHandle ) ;
481+ needsRender = true ;
482+ noteScrollActivity ( ) ;
483+ }
484+
451485 function releaseKittyImage ( entry : KittyDecodedImage | undefined ) {
452486 const source = entry ?. source as ImageBitmap | undefined ;
453487 if ( source && typeof source . close === "function" ) {
@@ -1337,7 +1371,10 @@ export function createResttyApp(options: ResttyAppOptions): ResttyApp {
13371371
13381372 function bindCanvasEvents ( ) {
13391373 if ( ! attachCanvasEvents ) return ;
1340- canvas . style . touchAction = touchSelectionMode === "drag" ? "none" : "pan-y pinch-zoom" ;
1374+ canvas . style . touchAction =
1375+ touchSelectionMode === "long-press" || touchSelectionMode === "drag"
1376+ ? "none"
1377+ : "pan-y pinch-zoom" ;
13411378 const onPointerDown = ( event : PointerEvent ) => {
13421379 if ( inputHandler . sendMouseEvent ( "down" , event ) ) {
13431380 event . preventDefault ( ) ;
@@ -1348,6 +1385,7 @@ export function createResttyApp(options: ResttyAppOptions): ResttyApp {
13481385 if ( event . button !== 0 ) return ;
13491386 const cell = normalizeSelectionCell ( positionToCell ( event ) ) ;
13501387 touchSelectionState . activePointerId = null ;
1388+ touchSelectionState . panPointerId = null ;
13511389
13521390 if ( touchSelectionMode === "off" ) return ;
13531391 if ( touchSelectionMode === "drag" ) {
@@ -1359,18 +1397,13 @@ export function createResttyApp(options: ResttyAppOptions): ResttyApp {
13591397 clearPendingTouchSelection ( ) ;
13601398 touchSelectionState . pendingPointerId = event . pointerId ;
13611399 touchSelectionState . pendingCell = cell ;
1400+ touchSelectionState . pendingStartedAt = performance . now ( ) ;
13621401 touchSelectionState . pendingStartX = event . clientX ;
13631402 touchSelectionState . pendingStartY = event . clientY ;
1403+ touchSelectionState . panPointerId = event . pointerId ;
1404+ touchSelectionState . panLastY = event . clientY ;
13641405 touchSelectionState . pendingTimer = setTimeout ( ( ) => {
1365- if (
1366- touchSelectionState . pendingPointerId !== event . pointerId ||
1367- ! touchSelectionState . pendingCell
1368- ) {
1369- return ;
1370- }
1371- const pendingCell = touchSelectionState . pendingCell ;
1372- clearPendingTouchSelection ( ) ;
1373- beginSelectionDrag ( pendingCell , event . pointerId ) ;
1406+ tryActivatePendingTouchSelection ( event . pointerId ) ;
13741407 } , touchSelectionLongPressMs ) ;
13751408 return ;
13761409 }
@@ -1387,12 +1420,22 @@ export function createResttyApp(options: ResttyAppOptions): ResttyApp {
13871420 return ;
13881421 }
13891422 if ( isTouchPointer ( event ) ) {
1423+ tryActivatePendingTouchSelection ( event . pointerId ) ;
13901424 if ( touchSelectionState . pendingPointerId === event . pointerId ) {
13911425 const dx = event . clientX - touchSelectionState . pendingStartX ;
13921426 const dy = event . clientY - touchSelectionState . pendingStartY ;
13931427 if ( dx * dx + dy * dy >= touchSelectionMoveThresholdPx * touchSelectionMoveThresholdPx ) {
13941428 clearPendingTouchSelection ( ) ;
13951429 }
1430+ if (
1431+ touchSelectionMode === "long-press" &&
1432+ touchSelectionState . panPointerId === event . pointerId
1433+ ) {
1434+ const deltaPx = touchSelectionState . panLastY - event . clientY ;
1435+ touchSelectionState . panLastY = event . clientY ;
1436+ scrollViewportByLines ( ( deltaPx / Math . max ( 1 , gridState . cellH ) ) * 1.5 ) ;
1437+ event . preventDefault ( ) ;
1438+ }
13961439 return ;
13971440 }
13981441 if ( selectionState . dragging && touchSelectionState . activePointerId === event . pointerId ) {
@@ -1402,6 +1445,16 @@ export function createResttyApp(options: ResttyAppOptions): ResttyApp {
14021445 updateLinkHover ( null ) ;
14031446 updateCanvasCursor ( ) ;
14041447 needsRender = true ;
1448+ return ;
1449+ }
1450+ if (
1451+ touchSelectionMode === "long-press" &&
1452+ touchSelectionState . panPointerId === event . pointerId
1453+ ) {
1454+ const deltaPx = touchSelectionState . panLastY - event . clientY ;
1455+ touchSelectionState . panLastY = event . clientY ;
1456+ scrollViewportByLines ( ( deltaPx / Math . max ( 1 , gridState . cellH ) ) * 1.5 ) ;
1457+ event . preventDefault ( ) ;
14051458 }
14061459 return ;
14071460 }
@@ -1426,6 +1479,7 @@ export function createResttyApp(options: ResttyAppOptions): ResttyApp {
14261479 if ( touchSelectionState . pendingPointerId === event . pointerId ) {
14271480 clearPendingTouchSelection ( ) ;
14281481 touchSelectionState . activePointerId = null ;
1482+ touchSelectionState . panPointerId = null ;
14291483 return ;
14301484 }
14311485 if ( selectionState . dragging && touchSelectionState . activePointerId === event . pointerId ) {
@@ -1445,6 +1499,10 @@ export function createResttyApp(options: ResttyAppOptions): ResttyApp {
14451499 updateCanvasCursor ( ) ;
14461500 needsRender = true ;
14471501 }
1502+ return ;
1503+ }
1504+ if ( touchSelectionState . panPointerId === event . pointerId ) {
1505+ touchSelectionState . panPointerId = null ;
14481506 }
14491507 return ;
14501508 }
@@ -1482,6 +1540,9 @@ export function createResttyApp(options: ResttyAppOptions): ResttyApp {
14821540 if ( touchSelectionState . pendingPointerId === event . pointerId ) {
14831541 clearPendingTouchSelection ( ) ;
14841542 }
1543+ if ( touchSelectionState . panPointerId === event . pointerId ) {
1544+ touchSelectionState . panPointerId = null ;
1545+ }
14851546 if ( touchSelectionState . activePointerId === event . pointerId ) {
14861547 touchSelectionState . activePointerId = null ;
14871548 if ( selectionState . dragging ) {
@@ -1512,15 +1573,7 @@ export function createResttyApp(options: ResttyAppOptions): ResttyApp {
15121573 } else {
15131574 lines = event . deltaY / gridState . cellH ;
15141575 }
1515- lines *= speed ;
1516- scrollRemainder += lines ;
1517- const delta = Math . trunc ( scrollRemainder ) ;
1518- scrollRemainder -= delta ;
1519- if ( ! delta ) return ;
1520- wasm . scrollViewport ( wasmHandle , delta ) ;
1521- wasm . renderUpdate ( wasmHandle ) ;
1522- needsRender = true ;
1523- noteScrollActivity ( ) ;
1576+ scrollViewportByLines ( lines * speed ) ;
15241577 event . preventDefault ( ) ;
15251578 } ;
15261579
0 commit comments