diff -r 000000000000 -r f72a12da539e idlehomescreen/xmluirendering/uiengine/src/xngesturehelper.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/idlehomescreen/xmluirendering/uiengine/src/xngesturehelper.cpp Thu Dec 17 08:40:49 2009 +0200 @@ -0,0 +1,420 @@ +/* +* Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Gesture helper implementation +* +*/ + +// System includes +#include +#include + +// User includes +#include "xngesturehelper.h" +#include "xngesture.h" +#include "xngesturedefs.h" +#include "xnnode.h" + +using namespace XnGestureHelper; + +namespace XnGestureHelper + { + NONSHARABLE_CLASS( CHoldingTimer ) : public CTimer + { + public: + /** Two-phase constructor */ + static CHoldingTimer* NewL( CXnGestureHelper& aHelper ) + { + CHoldingTimer* self = new ( ELeave ) CHoldingTimer( aHelper ); + CleanupStack::PushL( self ); + self->ConstructL(); + // "hold event" sending is enabled by default + self->iIsEnabled = ETrue; + CActiveScheduler::Add( self ); + CleanupStack::Pop( self ); + return self; + } + + /** Destructor */ + ~CHoldingTimer() + { + Cancel(); + } + + /** Set whether sending holding events is currently enabled */ + void SetEnabled( TBool aEnabled ) + { + iIsEnabled = aEnabled; + // cancel in case hold timer is already running + Cancel(); + } + + /** @return whether sending holding events is currently enabled */ + TBool IsEnabled() const + { + return iIsEnabled; + } + + /** Start the timer. Calls CXnGestureHelper::StartHoldingL upon + * completion */ + void Start() + { + // if sending hold events is disabled, do not ever start the hold + // timer, and hence hold events will never be triggered + if ( iIsEnabled ) + { + Cancel(); + After( KHoldDuration ); + } + } + + private: + /** Constructor */ + CHoldingTimer( CXnGestureHelper& aHelper ) + : // give higher priority to new pointer events with - 1 + CTimer( EPriorityUserInput - 1 ), + iHelper( aHelper ) + { + } + + void RunL() // From CActive + { + iHelper.StartHoldingL(); + } + + private: + /// helper object that will be called back when timer is triggered + CXnGestureHelper& iHelper; + /// whether sending holding events is currently enabled + TBool iIsEnabled; + }; + } // namespace GestureHelper + +/** +* @return position from event. Use this instead of using aEvent direction to +* avoid accidentally using TPointerEvent::iPosition +*/ +inline TPoint Position( const TPointerEvent& aEvent ) + { + // use parent position, since the capturer is using full screen area, + // and because the (Alfred) drag events are not local to visual even when + // coming from the client + return aEvent.iParentPosition; + } + +// ---------------------------------------------------------------------------- +// Two-phase constructor +// ---------------------------------------------------------------------------- +// +CXnGestureHelper* CXnGestureHelper::NewL( CXnNode& aNode ) + { + CXnGestureHelper* self = new ( ELeave ) CXnGestureHelper( aNode ); + CleanupStack::PushL( self ); + self->iHoldingTimer = CHoldingTimer::NewL( *self ); + self->iGesture = new ( ELeave ) CXnGesture(); + CleanupStack::Pop( self ); + return self; + } + +// ---------------------------------------------------------------------------- +// Constructor +// ---------------------------------------------------------------------------- +// +CXnGestureHelper::CXnGestureHelper( CXnNode& aNode ) + : iOwner( aNode ) + { + } + +// ---------------------------------------------------------------------------- +// Destructor +// ---------------------------------------------------------------------------- +// +CXnGestureHelper::~CXnGestureHelper() + { + delete iHoldingTimer; + delete iGesture; + } + +// ---------------------------------------------------------------------------- +// SetHoldingEnabled +// ---------------------------------------------------------------------------- +// +void CXnGestureHelper::SetHoldingEnabled( TBool aEnabled ) + { + iHoldingTimer->SetEnabled( aEnabled ); + } + +// ---------------------------------------------------------------------------- +// IsHoldingEnabled +// ---------------------------------------------------------------------------- +// +TBool CXnGestureHelper::IsHoldingEnabled() const + { + return iHoldingTimer->IsEnabled(); + } + +// ---------------------------------------------------------------------------- +// Reset state +// ---------------------------------------------------------------------------- +// +void CXnGestureHelper::Reset() + { + iHoldingTimer->Cancel(); + iGesture->Reset(); + } + +/** +* Helper function that calls Reset on the pointer to CXnGestureHelper +*/ +static void ResetHelper( TAny* aHelper ) + { + static_cast< CXnGestureHelper* >( aHelper )->Reset(); + } + +// ---------------------------------------------------------------------------- +// Sets gesture destination +// ---------------------------------------------------------------------------- +// +void CXnGestureHelper::SetDestination( CXnNode* aDestination ) + { + iDestination = aDestination; + } + +// ---------------------------------------------------------------------------- +// Gets gesture destination +// ---------------------------------------------------------------------------- +// +CXnNode* CXnGestureHelper::Destination() const + { + return iDestination; + } + +// ---------------------------------------------------------------------------- +// Gets gesture owner +// ---------------------------------------------------------------------------- +// +CXnNode* CXnGestureHelper::Owner() const + { + return &iOwner; + } + +// ---------------------------------------------------------------------------- +// Handle a pointer event +// ---------------------------------------------------------------------------- +// +TSwipeResult CXnGestureHelper::HandlePointerEventL( const TPointerEvent& aEvent ) + { + TSwipeResult ret = ESwipeNone; + switch ( aEvent.iType ) + { + case TPointerEvent::EButton1Down: + // If no up event was received during previous gesture, cancel + // previous event and reset state + if ( !IsIdle() ) + { + iGesture->SetCancelled(); + // ambiguous what is the right thing when "cancel" event leaves + // and "start" does not. Leaving for cancel *after* "start" could + // be unexpected to client, as client would have handled start + // event successfully. Assume that leaving upon cancellation + // can be ignored. + Reset(); + } + // adding the first point implicitly makes the state "not idle" + AddPointL( aEvent ); + // If AddPointL leaves, IsIdle will return EFalse for other events + // types, hence further pointer events will be ignored. + // Therefore, holding will NOT be started if AddPointL leaves, + // since the callback would trigger a gesture callback, and that + // would access an empty points array. + iHoldingTimer->Start(); + break; + + case TPointerEvent::EDrag: + // ignore the event in case not in "recording" state. this may + // happen if holding was triggered, or client sends up event after + // down event was received in a different *client* state, and + // client did not forward the down event to here. + // Also, while stylus down, the same event is received repeatedly + // even if stylus does not move. Filter out by checking if point + // is the same as the latest point + if ( !IsIdle() && !iGesture->IsLatestPoint( Position( aEvent ) ) ) + { + AddPointL( aEvent ); + if ( !( iGesture->IsHolding() || + iGesture->IsNearHoldingPoint( Position( aEvent ) ) ) ) + { + // restart hold timer, since pointer has moved + iHoldingTimer->Start(); + // Remember the point in which holding was started + iGesture->SetHoldingPoint(); + } + } + break; + + case TPointerEvent::EButton1Up: + // ignore up event if no down event received + if ( !IsIdle() ) + { + // reset in case the down event is not received for a reason + // in client, and instead drag or up events are received. + // reset via cleanup stack to ensure Reset is run even if + // observer leaves + CleanupStack::PushL( TCleanupItem( &ResetHelper, this ) ); + iGesture->SetComplete(); + // if adding of the point fails, notify client with a + // cancelled event. It would be wrong to send another + // gesture code when the up point is not known + if ( AddPoint( aEvent ) != KErrNone ) + { + iGesture->SetCancelled(); + } + else + { + // send gesture code if holding has not been started + if ( !iGesture->IsHolding() ) + { + // if client leaves, the state is automatically reset. + // In this case the client will not get the released event + ret = ValidSwipe(); + } + // send an event that stylus was lifted + iGesture->SetReleased(); + } + // reset state + CleanupStack::PopAndDestroy( this ); + } + break; + + default: + break; + } + return ret; + } + +// ---------------------------------------------------------------------------- +// Is the helper idle? +// inline ok in cpp file for a private member function +// ---------------------------------------------------------------------------- +// +inline TBool CXnGestureHelper::IsIdle() const + { + return iGesture->IsEmpty(); + } + +// ---------------------------------------------------------------------------- +// Add a point to the sequence of points that together make up the gesture +// inline ok in cpp file for a private member function +// ---------------------------------------------------------------------------- +// +inline void CXnGestureHelper::AddPointL( const TPointerEvent& aEvent ) + { + User::LeaveIfError( AddPoint( aEvent ) ); + } + +// ---------------------------------------------------------------------------- +// Add a point to the sequence of points that together make up the gesture +// inline ok in cpp file for a private member function +// ---------------------------------------------------------------------------- +// +inline TInt CXnGestureHelper::AddPoint( const TPointerEvent& aEvent ) + { + return iGesture->AddPoint( Position ( aEvent ) ); + } + +/** +* Helper function that calls ContinueHolding on the pointer to TGesture +*/ +static void ContinueHolding( TAny* aGesture ) + { + static_cast< CXnGesture* >( aGesture )->ContinueHolding(); + } + +// ---------------------------------------------------------------------------- +// Add a point to the sequence of points that together make up the gesture +// ---------------------------------------------------------------------------- +// +void CXnGestureHelper::StartHoldingL() + { + // hold & tap event is specifically filtered out. Use case: in list fast + // scrolling activation (e.g. enhanced coverflow), tap & hold should not + // start fast scroll. In addition, after long tap on start position, + // drag and drag & hold swiping should emit normal swipe and swipe&hold + // events. Therefore, tap & hold is not supported. + if ( !iGesture->IsTap() ) + { + // holding has just started, and gesture code should be provided to client. + // set gesture state so that it produces a gesture code (other than drag) + iGesture->StartHolding(); + + // create an item in the cleanup stack that will set the gesture state + // to holding-was-started-earlier state. NotifyL may leave, but the + // holding-was-started-earlier state must still be successfully set, + // otherwise, the holding gesture code will be sent twice + CleanupStack::PushL( TCleanupItem( &ContinueHolding, iGesture ) ); + + // set holding state to "post holding" + CleanupStack::PopAndDestroy( iGesture ); + } + } + +// ---------------------------------------------------------------------------- +// Check if swipe is valid +// ---------------------------------------------------------------------------- +// +TSwipeResult CXnGestureHelper::ValidSwipe() + { + TSwipeResult ret = ESwipeNone; + TBool validSwipe(ETrue); + + // check if swipe is between defined values + TInt distanceX = Abs( iGesture->Distance().iX ); + TInt speedX = Abs( static_cast< TInt >( iGesture->Speed().iX ) ); + + TInt minLength( iOwner.MarginRect().Width() / 2 ); + + TInt dy( Abs( iGesture->StartPos().iY - iGesture->CurrentPos().iY ) ); + + if ( distanceX < minLength ) + { + validSwipe = EFalse; + } + + if ( speedX < KGestureMinSpeedX ) + { + validSwipe = EFalse; + } + + if ( dy > KGestureMaxDeltaY ) + { + validSwipe = EFalse; + } + + // check the direction of swipe + if ( validSwipe ) + { + switch ( iGesture->Code( CXnGesture::EAxisHorizontal ) ) + { + case EGestureSwipeLeft: + ret = ESwipeLeft; + break; + case EGestureSwipeRight: + ret = ESwipeRight; + break; + default: // fall through + break; + } + } + + return ret; + }