138 CGestureHelperImpl* CGestureHelperImpl::NewL( MGestureObserver& aObserver ) |
135 CGestureHelperImpl* CGestureHelperImpl::NewL( MGestureObserver& aObserver ) |
139 { |
136 { |
140 CGestureHelperImpl* self = new ( ELeave ) CGestureHelperImpl( aObserver ); |
137 CGestureHelperImpl* self = new ( ELeave ) CGestureHelperImpl( aObserver ); |
141 CleanupStack::PushL( self ); |
138 CleanupStack::PushL( self ); |
142 self->iEventSender = CGestureEventSender::NewL( aObserver ); |
139 self->iEventSender = CGestureEventSender::NewL( aObserver ); |
143 self->iDoubleTapTimer = CCallbackTimer::NewL( *self, EmitFirstTapEventL, |
140 self->iDoubleTapTimer = CCallbackTimer::NewL( *self, EmitFirstTapEvent, |
144 KMaxTapDuration, EFalse ); // double tap is disabled by default |
141 KMaxTapDuration, EFalse ); // double tap is disabled by default |
145 self->iHoldingTimer = CCallbackTimer::NewL( *self, StartHoldingL, |
142 self->iHoldingTimer = CCallbackTimer::NewL( *self, StartHoldingL, |
146 KHoldDuration, EFalse ); // holding is enabled by default |
143 KHoldDuration, EFalse ); // holding is enabled by default |
147 |
144 |
148 self->iLongTouchTimer = CCallbackTimer::NewL( *self, HandleLongTouchL, |
145 self->iLongTouchTimer = CCallbackTimer::NewL( *self, HandleLongTouch, |
149 KLongTapDuration, ETrue ); // holding is enabled by default |
146 KLongTapDuration, ETrue ); // holding is enabled by default |
150 self->iPointerCapturer = CPointerCapturer::NewL(); |
147 |
151 self->iGesture = new ( ELeave ) CGesture(); |
148 self->iGesture = new ( ELeave ) CGesture(); |
152 self->iUnusedGesture = new ( ELeave ) CGesture(); |
149 self->iUnusedGesture = new ( ELeave ) CGesture(); |
153 TInt tapLimit = Mm2Pixels(KFingerSize_mm) / 2; |
150 TInt tapLimit = Mm2Pixels(KFingerSize_mm) / 2; |
154 self->iEventFilter = new (ELeave) CGestureEventFilter(tapLimit); |
151 self->iEventFilter = new (ELeave) CGestureEventFilter(tapLimit); |
155 CleanupStack::Pop( self ); |
152 CleanupStack::Pop( self ); |
273 */ |
259 */ |
274 return EFalse; |
260 return EFalse; |
275 } |
261 } |
276 } |
262 } |
277 |
263 |
278 // ---------------------------------------------------------------------------- |
|
279 // OfferEventL |
|
280 // ---------------------------------------------------------------------------- |
|
281 // |
|
282 TBool CGestureHelperImpl::OfferEventL( const TAlfEvent& aEvent ) |
|
283 { |
|
284 if ( aEvent.IsPointerEvent() ) |
|
285 { |
|
286 return HandlePointerEventL( aEvent.PointerEvent(), aEvent.Visual() ); |
|
287 } |
|
288 return EFalse; |
|
289 } |
|
290 |
|
291 |
|
292 |
|
293 |
|
294 |
|
295 |
|
296 // ---------------------------------------------------------------------------- |
|
297 // Handle a pointer event |
|
298 // ---------------------------------------------------------------------------- |
|
299 // |
|
300 |
|
301 |
264 |
302 TBool CGestureHelperImpl::noneAlf_HandlePointerEventL( const TPointerEvent& aEvent) |
265 TBool CGestureHelperImpl::noneAlf_HandlePointerEventL( const TPointerEvent& aEvent) |
303 { |
266 { |
304 |
|
305 switch ( aEvent.iType ) |
267 switch ( aEvent.iType ) |
306 { |
268 { |
307 case TPointerEvent::EButton1Down: |
269 case TPointerEvent::EButton1Down: |
308 { |
270 { |
309 |
|
310 iPointerCapturer->StartL(); |
|
311 HandleTouchDownL(aEvent); |
271 HandleTouchDownL(aEvent); |
312 break; |
272 break; |
313 } |
273 } |
314 case TPointerEvent::EDrag: |
274 case TPointerEvent::EDrag: |
315 { |
275 { |
316 HandleMoveL(aEvent); |
276 HandleMoveL(aEvent); |
317 break; |
277 break; |
318 } |
278 } |
319 case TPointerEvent::EButton1Up: |
279 case TPointerEvent::EButton1Up: |
320 { |
280 { |
321 CleanupStack::PushL( TCleanupItem( &ResetHelper, this ) ); |
|
322 if (KErrNone == AddPoint( aEvent )) |
281 if (KErrNone == AddPoint( aEvent )) |
323 { |
282 { |
324 HandleTouchUpL(aEvent); |
283 HandleTouchUp(aEvent); |
325 } |
284 } |
326 else |
285 else |
327 { |
286 { |
328 EmitCancelEventL(); |
287 EmitCancelEvent(); |
329 } |
288 } |
330 CleanupStack::PopAndDestroy( this ); |
289 Reset(); |
331 break; |
290 break; |
332 } |
291 } |
333 default: |
292 default: |
334 break; |
293 break; |
335 } |
294 } |
336 return ETrue; |
295 return ETrue; |
337 } |
296 } |
338 |
297 |
339 |
|
340 TBool CGestureHelperImpl::HandlePointerEventL( const TPointerEvent& aEvent, |
|
341 CAlfVisual* aVisual ) |
|
342 { |
|
343 // filter out events that do not start with button down. It is a stray |
|
344 // event from another visual |
|
345 if ( IsIdle() && aEvent.iType != TPointerEvent::EButton1Down ) |
|
346 { |
|
347 return EFalse; // don't consume |
|
348 } |
|
349 |
|
350 switch ( aEvent.iType ) |
|
351 { |
|
352 case TPointerEvent::EButton1Down: |
|
353 // If no up event was received during previous gesture, cancel |
|
354 // previous event and reset state |
|
355 if ( !IsIdle() ) |
|
356 { |
|
357 // ambiguous what is the right thing when "cancel" event leaves |
|
358 // and "start" does not. Leaving for cancel *after* "start" could |
|
359 // be unexpected to client, as client would have handled start |
|
360 // event successfully. Assume that leaving upon cancellation |
|
361 // can be ignored. |
|
362 TRAP_IGNORE( EmitCancelEventL() ); |
|
363 Reset(); |
|
364 } |
|
365 // as long as down event of a double tap comes within the double |
|
366 // tap timeout, it does not matter how long the user keeps the finger |
|
367 // pressed for the gesture to be a double tap. Therefore, cancel |
|
368 // the timeout, as it is no longer relevant. (Of course, this call |
|
369 // will only do something if the timer is actually running, which |
|
370 // is only if received a tap event very recently.) |
|
371 iDoubleTapTimer->Cancel(); |
|
372 // adding the first point implicitly makes the state "not idle" |
|
373 AddPointL( aEvent ); |
|
374 iGesture->SetVisual( aVisual ); |
|
375 // if pointer capturer leaves, the remaining pointer events will |
|
376 // not be captured if stylus is dragged outside the capturing visual |
|
377 // an error note will be shown, so the potential problem is irrelevant, |
|
378 // assuming client does not (incorrectly) block the leave from reaching |
|
379 // the framework |
|
380 iPointerCapturer->StartL(); |
|
381 // Delay emitting a down event _until_ it is known that this beginning |
|
382 // gesture is _not_ the second tap of a double tap event. |
|
383 // iPreviousTapGesture is only non-null if very recently received |
|
384 // a tap event and double tap is enabled. |
|
385 if ( !iPreviousTapGesture ) |
|
386 { |
|
387 EmitEventL( *iGesture ); |
|
388 } |
|
389 // else delay emitting an event, as it might be a double tap |
|
390 // (allow the second tap of a double tap to be anywhere, so don't check |
|
391 // for start pos here) |
|
392 break; |
|
393 |
|
394 case TPointerEvent::EDrag: |
|
395 // While stylus down, the same event is received repeatedly |
|
396 // even if stylus does not move. Filter out by checking if point |
|
397 // is the same as the latest point |
|
398 if ( !iGesture->IsLatestPoint( Position( aEvent ) ) ) |
|
399 { |
|
400 AddPointL( aEvent ); |
|
401 |
|
402 // as long as the starting gesture is seen as a tap, do not emit any |
|
403 // drag events |
|
404 if ( !iGesture->IsTap() ) |
|
405 { |
|
406 // if there is a previous tap gesture, getting drag events means that |
|
407 // the previous gesture is not a double tap. So emit the previous gesture. |
|
408 if ( iPreviousTapGesture ) |
|
409 { |
|
410 // this is a second gesture after a tap (double tap is enabled) |
|
411 EmitFirstTapEventL(); |
|
412 // emit down event for the current gesture (since its down was delayed, until |
|
413 // it was to be known if the event is a tap. That is known now.) |
|
414 EmitStartEventL( *iGesture ); |
|
415 } |
|
416 // restart holding timer every time the current stylus pos changes |
|
417 StartHoldingTimer( aEvent ); |
|
418 // emit the drag event to client |
|
419 EmitEventL( *iGesture ); |
|
420 } |
|
421 // else: do not emit drag events until it is known that the gesture is not a tap |
|
422 // (or the second tap of double tap) |
|
423 } |
|
424 break; |
|
425 |
|
426 case TPointerEvent::EButton1Up: |
|
427 // reset in case the down event for next gesture is not received for a reason |
|
428 // in client, and instead drag or up events are received. |
|
429 // reset via cleanup stack to ensure Reset is run even if |
|
430 // observer leaves |
|
431 CleanupStack::PushL( TCleanupItem( &ResetHelper, this ) ); |
|
432 // if adding of the point fails, notify client with a |
|
433 // cancelled event. It would be wrong to send another |
|
434 // gesture code when the up point is not known |
|
435 if ( KErrNone == AddPoint( aEvent ) ) |
|
436 { |
|
437 |
|
438 // if the gesture is a tap, the gesture is either the first tap of a _potential_ |
|
439 // double tap, or the second tap of a double tap |
|
440 if ( iDoubleTapTimer->IsEnabled() && iGesture->IsTap() ) |
|
441 { |
|
442 __ASSERT_DEBUG( !iGesture->IsHolding(), Panic( EGesturePanicIllegalLogic ) ); |
|
443 if ( !iPreviousTapGesture ) |
|
444 { |
|
445 // First tap. Delay emitting its code evemt and released events until it is known |
|
446 // whether the tap is a double tap |
|
447 iPreviousTapGesture = iGesture; |
|
448 iGesture = NewGesture(); |
|
449 iDoubleTapTimer->Start(); |
|
450 } |
|
451 else |
|
452 { |
|
453 // This is a second tap of a double tap. Do not emit anything for the second |
|
454 // tap. Only down event has been emitted for the first tap. Emit the code |
|
455 // event (double tap) and released for the first tap. |
|
456 iPreviousTapGesture->SetDoubleTap(); |
|
457 EmitFirstTapEventL(); |
|
458 } |
|
459 } |
|
460 |
|
461 else |
|
462 { |
|
463 // modified iGesture to be "released" |
|
464 CompleteAndEmitL( *iGesture ); |
|
465 } |
|
466 } |
|
467 else |
|
468 { // adding a point failed |
|
469 EmitCancelEventL(); |
|
470 } |
|
471 // reset state |
|
472 CleanupStack::PopAndDestroy( this ); |
|
473 break; |
|
474 |
|
475 default: |
|
476 break; |
|
477 } |
|
478 return ETrue; // consume |
|
479 } |
|
480 |
|
481 |
|
482 TBool CGestureHelperImpl::IsMovementGesture(TGestureCode aCode) |
298 TBool CGestureHelperImpl::IsMovementGesture(TGestureCode aCode) |
483 { |
299 { |
484 return (aCode == EGestureDrag || aCode == EGestureFlick || aCode == EGestureSwipeUp || |
300 return (aCode == EGestureDrag || aCode == EGestureFlick || aCode == EGestureSwipeUp || |
485 aCode == EGestureSwipeDown || aCode == EGestureSwipeRight || aCode == EGestureSwipeLeft); |
301 aCode == EGestureSwipeDown || aCode == EGestureSwipeRight || aCode == EGestureSwipeLeft); |
486 } |
302 } |
487 |
303 |
488 void CGestureHelperImpl::HandleLongTouchL() |
304 void CGestureHelperImpl::HandleLongTouch() |
489 { |
305 { |
490 iDoubleTapTimer->Cancel(); |
306 iDoubleTapTimer->Cancel(); |
491 iGesture->SetLongTap(ETrue); |
307 iGesture->SetLongTap(ETrue); |
492 iGesture->SetComplete(); |
308 iGesture->SetComplete(); |
493 TPoint startPos = iGesture->StartPos(); |
309 TPoint startPos = iGesture->StartPos(); |
494 EmitEventL(*iGesture); |
310 EmitEvent(*iGesture); |
495 iGesture->Reset(); |
311 iGesture->Reset(); |
496 iGesture->AddPoint( startPos, GetLastEventTime() ); |
312 iGesture->AddPoint( startPos, GetLastEventTime() ); |
497 } |
313 } |
498 |
314 |
499 void CGestureHelperImpl::HandleTouchDownL(const TPointerEvent& aEvent) |
315 void CGestureHelperImpl::HandleTouchDownL(const TPointerEvent& aEvent) |
500 { |
316 { |
501 TGestureCode prevCode = iGesture->PreviousGestureCode(); |
317 TGestureCode prevCode = iGesture->PreviousGestureCode(); |
502 if (prevCode == EGestureStart) return; |
318 if (prevCode == EGestureStart) return; |
|
319 if (prevCode == EGestureDrag) |
|
320 { |
|
321 iGesture->Reset(); |
|
322 } |
503 AddPointL( aEvent ); |
323 AddPointL( aEvent ); |
504 |
324 |
|
325 if (!iLongTouchTimer->IsActive()) |
|
326 { |
505 iLongTouchTimer->Start(); |
327 iLongTouchTimer->Start(); |
|
328 } |
506 if (!iDoubleTapTimer->IsActive()) |
329 if (!iDoubleTapTimer->IsActive()) |
507 { |
330 { |
508 EmitEventL( *iGesture ); |
331 EmitEvent( *iGesture ); |
509 } |
332 } |
510 } |
333 } |
511 |
334 |
512 void CGestureHelperImpl::HandleMoveL(const TPointerEvent& aEvent) |
335 void CGestureHelperImpl::HandleMoveL(const TPointerEvent& aEvent) |
513 { |
336 { |
514 if (iGesture->IsLatestPoint( iGesture->Visual() ? Position ( aEvent ) : aEvent.iPosition)) return; // I'm not sure we need this |
337 if (iGesture->IsLatestPoint( Position(aEvent))) return; // I'm not sure we need this |
515 //Cancel double tap time - it's neither tap nor double tap |
338 //Cancel double tap time - it's neither tap nor double tap |
516 iDoubleTapTimer->Cancel(); |
339 iDoubleTapTimer->Cancel(); |
517 iLongTouchTimer->Cancel(); |
340 iLongTouchTimer->Cancel(); |
518 |
341 |
519 TBool isFirstPoint = IsIdle(); |
342 TBool isFirstPoint = IsIdle(); |
697 |
520 |
698 // ---------------------------------------------------------------------------- |
521 // ---------------------------------------------------------------------------- |
699 // Emit the remainder of the previous tap event (tap + released) |
522 // Emit the remainder of the previous tap event (tap + released) |
700 // ---------------------------------------------------------------------------- |
523 // ---------------------------------------------------------------------------- |
701 // |
524 // |
702 void CGestureHelperImpl::EmitFirstTapEventL() |
525 void CGestureHelperImpl::EmitFirstTapEvent() |
703 { |
526 { |
704 // when this function is called, a tap has turned out to _not_ be a double tap |
527 // when this function is called, a tap has turned out to _not_ be a double tap |
705 __ASSERT_DEBUG( IsDoubleTapEnabled(), Panic( EGesturePanicIllegalLogic ) ); |
528 __ASSERT_DEBUG( IsDoubleTapEnabled(), Panic( EGesturePanicIllegalLogic ) ); |
706 __ASSERT_DEBUG( iPreviousTapGesture, Panic( EGesturePanicIllegalLogic ) ); |
529 __ASSERT_DEBUG( iPreviousTapGesture, Panic( EGesturePanicIllegalLogic ) ); |
707 |
530 |
708 iDoubleTapTimer->Cancel(); |
531 iDoubleTapTimer->Cancel(); |
709 |
532 CompleteAndEmit( *iPreviousTapGesture ); |
710 // ensure previous tap gesture is reset even if client leaves |
533 RecycleGesture(iPreviousTapGesture); |
711 CleanupStack::PushL( TCleanupItem( &RecyclePreviousTapGesture, this ) ); |
534 |
712 |
|
713 CompleteAndEmitL( *iPreviousTapGesture ); |
|
714 |
|
715 // recycle the emitted gesture |
|
716 CleanupStack::PopAndDestroy( this ); |
|
717 } |
535 } |
718 |
536 |
719 // ---------------------------------------------------------------------------- |
537 // ---------------------------------------------------------------------------- |
720 // EmitStartEventL |
538 // EmitStartEventL |
721 // ---------------------------------------------------------------------------- |
539 // ---------------------------------------------------------------------------- |
722 // |
540 // |
723 void CGestureHelperImpl::EmitStartEventL( const CGesture& aGesture ) |
541 void CGestureHelperImpl::EmitStartEventL( const CGesture& aGesture ) |
724 { |
542 { |
725 CGesture* startGesture = aGesture.AsStartEventLC(); |
543 CGesture* startGesture = aGesture.AsStartEventLC(); |
726 EmitEventL( *startGesture ); |
544 EmitEvent( *startGesture ); |
727 CleanupStack::PopAndDestroy( startGesture ); |
545 CleanupStack::PopAndDestroy( startGesture ); |
728 } |
546 } |
729 |
547 |
730 // ---------------------------------------------------------------------------- |
548 // ---------------------------------------------------------------------------- |
731 // EmitCompletionEventsL |
549 // EmitCompletionEventsL |
732 // ---------------------------------------------------------------------------- |
550 // ---------------------------------------------------------------------------- |
733 // |
551 // |
734 void CGestureHelperImpl::CompleteAndEmitL( CGesture& aGesture ) |
552 void CGestureHelperImpl::CompleteAndEmit( CGesture& aGesture ) |
735 { |
553 { |
736 aGesture.SetComplete(); |
554 aGesture.SetComplete(); |
737 // send gesture code if holding has not been started. If holding has |
555 // send gesture code if holding has not been started. If holding has |
738 // been started, client has already received a "hold swipe left" e.g. event, in which |
556 // been started, client has already received a "hold swipe left" e.g. event, in which |
739 // case don't another "swipe left" event |
557 // case don't another "swipe left" event |
740 if ( !aGesture.IsHolding() ) |
558 if ( !aGesture.IsHolding() ) |
741 { |
559 { |
742 // if client leaves, the state is automatically reset. |
560 // if client leaves, the state is automatically reset. |
743 // In this case the client will not get the released event |
561 // In this case the client will not get the released event |
744 EmitEventL( aGesture ); |
562 EmitEvent( aGesture ); |
745 } |
563 } |
746 |
564 |
747 // send an event that stylus was lifted |
565 // send an event that stylus was lifted |
748 aGesture.SetReleased(); |
566 aGesture.SetReleased(); |
749 EmitEventL( aGesture ); |
567 EmitEvent( aGesture ); |
750 } |
568 } |
751 |
569 |
752 // ---------------------------------------------------------------------------- |
570 // ---------------------------------------------------------------------------- |
753 // EmitCancelEventL |
571 // EmitCancelEventL |
754 // ---------------------------------------------------------------------------- |
572 // ---------------------------------------------------------------------------- |
755 // |
573 // |
756 void CGestureHelperImpl::EmitCancelEventL() |
574 void CGestureHelperImpl::EmitCancelEvent() |
757 { |
575 { |
758 iDoubleTapTimer->Cancel(); |
576 iDoubleTapTimer->Cancel(); |
759 |
577 |
760 // ensure previous tap gesture is reset even if client leaves |
578 |
761 CleanupStack::PushL( TCleanupItem( &RecyclePreviousTapGesture, this ) ); |
|
762 |
|
763 CGesture& gestureToCancel = iPreviousTapGesture ? *iPreviousTapGesture : *iGesture; |
579 CGesture& gestureToCancel = iPreviousTapGesture ? *iPreviousTapGesture : *iGesture; |
764 gestureToCancel.SetCancelled(); |
580 gestureToCancel.SetCancelled(); |
765 EmitEventL( gestureToCancel ); |
581 EmitEvent( gestureToCancel ); |
766 |
582 RecycleGesture(iPreviousTapGesture); |
767 // recycle the emitted gesture |
583 |
768 CleanupStack::PopAndDestroy( this ); |
|
769 } |
584 } |
770 |
585 |
771 // ---------------------------------------------------------------------------- |
586 // ---------------------------------------------------------------------------- |
772 // Notify observer |
587 // Notify observer |
773 // ---------------------------------------------------------------------------- |
588 // ---------------------------------------------------------------------------- |
774 // |
589 // |
775 void CGestureHelperImpl::EmitEventL( const CGesture& aGesture ) |
590 void CGestureHelperImpl::EmitEvent( const CGesture& aGesture ) |
776 { |
591 { |
777 // deallocation of the event is happening in CGestureEventSender::RunL() |
592 // deallocation of the event is happening in CGestureEventSender::RunL() |
778 CGestureEvent* event = new(ELeave) CGestureEvent(); |
593 TGestureEvent event; |
779 event->iCode = const_cast<CGesture&>(aGesture).Code(MGestureEvent::EAxisBoth); |
594 event.SetCode(const_cast<CGesture&>(aGesture).Code(EAxisBoth)); |
780 event->iCurrPos = aGesture.CurrentPos(); |
595 event.SetCurrentPos(aGesture.CurrentPos()); |
781 event->iDistance = aGesture.Distance(); |
596 event.SetDistance(aGesture.Distance()); |
782 event->iStartPos = aGesture.StartPos(); |
597 event.SetStartPos(aGesture.StartPos()); |
783 event->iIsHolding = aGesture.IsHolding(); |
598 event.SetIsHolding(aGesture.IsHolding()); |
784 event->iSpeed = aGesture.Speed(); |
599 event.SetSpeed(aGesture.Speed()); |
785 event->iVisual = aGesture.Visual(); |
|
786 iEventSender->AddEvent(event); |
600 iEventSender->AddEvent(event); |
787 } |
601 } |
788 |
602 |
789 // ---------------------------------------------------------------------------- |
603 // ---------------------------------------------------------------------------- |
790 // Return a fresh gesture from the gesture pool (pool of one gesture) |
604 // Return a fresh gesture from the gesture pool (pool of one gesture) |