From: Seongjun Yim Date: Thu, 29 Aug 2013 10:09:58 +0000 (+0900) Subject: fix scroll bug on scrollable ui controls X-Git-Tag: submit/tizen_2.2/20131107.062229~46 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6669a2e4436be1394287f7b62bc908a7d40d5ad3;p=framework%2Fosp%2Fweb.git fix scroll bug on scrollable ui controls Change-Id: I76e4cd45ba131aab4c4815ebf23ba7c10c35323e Signed-off-by: Seongjun Yim --- diff --git a/src/controls/FWebCtrl_GestureState.cpp b/src/controls/FWebCtrl_GestureState.cpp index c88cbb0..5bfa592 100755 --- a/src/controls/FWebCtrl_GestureState.cpp +++ b/src/controls/FWebCtrl_GestureState.cpp @@ -160,6 +160,11 @@ _TapGestureState::OnTouchMoved(const _Control& source, const _TouchInfo& touchIn __pWebCore->ChangeGesture(WEB_GESTURE_TYPE_PANNING); + if (!_WebSettingImpl::GetInstance(__pWebCore->GetSetting())->IsScrollEnabled()) + { + return false; + } + return true; } @@ -167,57 +172,55 @@ _TapGestureState::OnTouchMoved(const _Control& source, const _TouchInfo& touchIn bool _TapGestureState::OnTouchReleased(const _Control& source, const _TouchInfo& touchInfo) { - if (__pWebCore->Contains(__pressedPosition)) + if (!__pWebCore->Contains(__pressedPosition)) { - Evas_Object* pView = __pWebCore->GetWebNativeNode(); - Point absPoint(_CoordinateSystemUtils::ConvertToInteger(__pWebCore->GetAbsoluteCoordinate(__pressedPosition))); - - if (__longPressed) - { - Ewk_Hit_Test* pEwkHitTest = ewk_view_hit_test_new(pView, absPoint.x, absPoint.y, EWK_HIT_TEST_MODE_ALL); - SysTryReturn(NID_WEB_CTRL, pEwkHitTest, true, E_SYSTEM, "Failed to get hit test."); - - String tagName(ewk_hit_test_tag_name_get(pEwkHitTest)); - - Eina_Hash* pAttrHash = ewk_hit_test_attribute_hash_get(pEwkHitTest); - char* pValue = reinterpret_cast< char* >(eina_hash_find(pAttrHash, "contenteditable")); - if (tagName.Equals(L"INPUT", false) || tagName.Equals(L"TEXTAREA", false) || pValue) - { - Eina_Rectangle leftHandle; - Eina_Rectangle rightHandle; + return OnTouchCanceled(source, touchInfo); + } + + Evas_Object* pView = __pWebCore->GetWebNativeNode(); + Point absPoint(_CoordinateSystemUtils::ConvertToInteger(__pWebCore->GetAbsoluteCoordinate(__pressedPosition))); - ewk_view_text_selection_range_get(pView, &leftHandle, &rightHandle); - if (((rightHandle.x + rightHandle.w) == 0) && ((rightHandle.y + rightHandle.h) == 0)) - { - Ewk_Event_Gesture gestureEvent; + if (__longPressed) + { + Ewk_Hit_Test* pEwkHitTest = ewk_view_hit_test_new(pView, absPoint.x, absPoint.y, EWK_HIT_TEST_MODE_ALL); + SysTryReturn(NID_WEB_CTRL, pEwkHitTest, true, E_SYSTEM, "Failed to get hit test."); - SetGestureEvent(gestureEvent, EWK_GESTURE_LONG_PRESS, absPoint, Point(0, 0), 0.0, 1); + String tagName(ewk_hit_test_tag_name_get(pEwkHitTest)); - const Ewk_View_Smart_Data* pSmartData = reinterpret_cast< Ewk_View_Smart_Data* >(evas_object_smart_data_get(__pWebCore->GetWebNativeNode())); - SysAssertf(pSmartData, "Failed to get webkit smart data."); - pSmartData->api->gesture_end(const_cast< Ewk_View_Smart_Data* >(pSmartData), &gestureEvent); - } - } - } - else + Eina_Hash* pAttrHash = ewk_hit_test_attribute_hash_get(pEwkHitTest); + char* pValue = reinterpret_cast< char* >(eina_hash_find(pAttrHash, "contenteditable")); + if (tagName.Equals(L"INPUT", false) || tagName.Equals(L"TEXTAREA", false) || pValue) { - Ewk_Event_Gesture gestureEvent; - int touchCount = 1; - if (__doubleTapped) + Eina_Rectangle leftHandle; + Eina_Rectangle rightHandle; + + ewk_view_text_selection_range_get(pView, &leftHandle, &rightHandle); + if (((rightHandle.x + rightHandle.w) == 0) && ((rightHandle.y + rightHandle.h) == 0)) { - touchCount = 2; - } + Ewk_Event_Gesture gestureEvent; - SetGestureEvent(gestureEvent, EWK_GESTURE_TAP, absPoint, Point(0, 0), 0.0, touchCount); + SetGestureEvent(gestureEvent, EWK_GESTURE_LONG_PRESS, absPoint, Point(0, 0), 0.0, 1); - const Ewk_View_Smart_Data* pSmartData = reinterpret_cast< Ewk_View_Smart_Data* >(evas_object_smart_data_get(__pWebCore->GetWebNativeNode())); - SysAssertf(pSmartData, "Failed to get webkit smart data."); - pSmartData->api->gesture_end(const_cast< Ewk_View_Smart_Data* >(pSmartData), &gestureEvent); + const Ewk_View_Smart_Data* pSmartData = reinterpret_cast< Ewk_View_Smart_Data* >(evas_object_smart_data_get(pView)); + SysAssertf(pSmartData, "Failed to get webkit smart data."); + pSmartData->api->gesture_end(const_cast< Ewk_View_Smart_Data* >(pSmartData), &gestureEvent); + } } } else { - OnTouchCanceled(source, touchInfo); + Ewk_Event_Gesture gestureEvent; + int touchCount = 1; + if (__doubleTapped) + { + touchCount = 2; + } + + SetGestureEvent(gestureEvent, EWK_GESTURE_TAP, absPoint, Point(0, 0), 0.0, touchCount); + + const Ewk_View_Smart_Data* pSmartData = reinterpret_cast< Ewk_View_Smart_Data* >(evas_object_smart_data_get(pView)); + SysAssertf(pSmartData, "Failed to get webkit smart data."); + pSmartData->api->gesture_end(const_cast< Ewk_View_Smart_Data* >(pSmartData), &gestureEvent); } return true; @@ -241,9 +244,6 @@ _TapGestureState::OnTouchCanceled(const _Control& source, const _TouchInfo& touc _PanningGestureState::_PanningGestureState(_Web* pWeb) : __pWebCore(pWeb) - , __textSelected(false) - , __textChanged(false) - , __selectedText(L"") { } @@ -253,22 +253,6 @@ _PanningGestureState::~_PanningGestureState(void) } -void -_PanningGestureState::InitializeTextSelectionStatus(void) -{ - __selectedText = String(ewk_view_text_selection_text_get(__pWebCore->GetWebNativeNode())); - if (__selectedText.GetLength() > 0) - { - __textSelected = true; - } - else - { - __textSelected = false; - } - __textChanged = false; -} - - bool _PanningGestureState::OnTouchPressed(const _Control& source, const _TouchInfo& touchInfo) { @@ -279,26 +263,19 @@ _PanningGestureState::OnTouchPressed(const _Control& source, const _TouchInfo& t bool _PanningGestureState::OnTouchMoved(const _Control& source, const _TouchInfo& touchInfo) { - Evas_Object* pView = __pWebCore->GetWebNativeNode(); - if (_WebSettingImpl::GetInstance(__pWebCore->GetSetting())->IsScrollEnabled()) + if (!_WebSettingImpl::GetInstance(__pWebCore->GetSetting())->IsScrollEnabled()) { - Ewk_Event_Gesture gestureEvent; - Point absPoint(_CoordinateSystemUtils::ConvertToInteger(__pWebCore->GetAbsoluteCoordinate(touchInfo.GetCurrentPosition()))); + return false; + } - SetGestureEvent(gestureEvent, EWK_GESTURE_PAN, absPoint, Point(0, 0), 0.0, 0); + Ewk_Event_Gesture gestureEvent; + Point absPoint(_CoordinateSystemUtils::ConvertToInteger(__pWebCore->GetAbsoluteCoordinate(touchInfo.GetCurrentPosition()))); - const Ewk_View_Smart_Data* pSmartData = reinterpret_cast< Ewk_View_Smart_Data* >(evas_object_smart_data_get(pView)); - SysAssertf(pSmartData, "Failed to get webkit smart data."); - pSmartData->api->gesture_move(const_cast< Ewk_View_Smart_Data* >(pSmartData), &gestureEvent); - } + SetGestureEvent(gestureEvent, EWK_GESTURE_PAN, absPoint, Point(0, 0), 0.0, 0); - if (__textSelected && !__textChanged) - { - if (!__selectedText.Equals(String(ewk_view_text_selection_text_get(pView)))) - { - __textChanged = true; - } - } + const Ewk_View_Smart_Data* pSmartData = reinterpret_cast< Ewk_View_Smart_Data* >(evas_object_smart_data_get(__pWebCore->GetWebNativeNode())); + SysAssertf(pSmartData, "Failed to get webkit smart data."); + pSmartData->api->gesture_move(const_cast< Ewk_View_Smart_Data* >(pSmartData), &gestureEvent); return true; } @@ -312,16 +289,10 @@ _PanningGestureState::OnTouchReleased(const _Control& source, const _TouchInfo& SetGestureEvent(gestureEvent, EWK_GESTURE_PAN, absPoint, Point(0, 0), 0.0, 0); - Evas_Object* pView = __pWebCore->GetWebNativeNode(); - const Ewk_View_Smart_Data* pSmartData = reinterpret_cast< Ewk_View_Smart_Data* >(evas_object_smart_data_get(pView)); + const Ewk_View_Smart_Data* pSmartData = reinterpret_cast< Ewk_View_Smart_Data* >(evas_object_smart_data_get(__pWebCore->GetWebNativeNode())); SysAssertf(pSmartData, "Failed to get webkit smart data."); pSmartData->api->gesture_end(const_cast< Ewk_View_Smart_Data* >(pSmartData), &gestureEvent); - if (__textChanged) - { - evas_object_smart_callback_call(pView, "text,selected", NULL); - } - __pWebCore->ChangeGesture(WEB_GESTURE_TYPE_TAP); return true; @@ -352,11 +323,14 @@ _FlickGestureState::OnFlickGestureDetected(_TouchFlickGestureDetector& gesture) { __pWebCore->ChangeGesture(WEB_GESTURE_TYPE_FLICK); - int duration = gesture.GetDuration(); - gesture.GetDistance(__velocity.x, __velocity.y); + if (_WebSettingImpl::GetInstance(__pWebCore->GetSetting())->IsScrollEnabled()) + { + int duration = gesture.GetDuration(); + gesture.GetDistance(__velocity.x, __velocity.y); - __velocity.x = (__velocity.x * FLICK_SCROLL_WEIGHT) / duration; - __velocity.y = (__velocity.y * FLICK_SCROLL_WEIGHT) / duration; + __velocity.x = (__velocity.x * FLICK_SCROLL_WEIGHT) / duration; + __velocity.y = (__velocity.y * FLICK_SCROLL_WEIGHT) / duration; + } return true; } @@ -388,9 +362,12 @@ _FlickGestureState::OnTouchReleased(const _Control& source, const _TouchInfo& to SysAssertf(pSmartData, "Failed to get webkit smart data."); pSmartData->api->gesture_end(const_cast< Ewk_View_Smart_Data* >(pSmartData), &gestureEvent); - SetGestureEvent(gestureEvent, EWK_GESTURE_FLICK, absPoint, __velocity, 0.0, 0); + if (_WebSettingImpl::GetInstance(__pWebCore->GetSetting())->IsScrollEnabled()) + { + SetGestureEvent(gestureEvent, EWK_GESTURE_FLICK, absPoint, __velocity, 0.0, 0); - pSmartData->api->gesture_start(const_cast< Ewk_View_Smart_Data* >(pSmartData), &gestureEvent); + pSmartData->api->gesture_start(const_cast< Ewk_View_Smart_Data* >(pSmartData), &gestureEvent); + } __pWebCore->ChangeGesture(WEB_GESTURE_TYPE_TAP); diff --git a/src/controls/FWebCtrl_GestureState.h b/src/controls/FWebCtrl_GestureState.h index 0758025..9c77f24 100755 --- a/src/controls/FWebCtrl_GestureState.h +++ b/src/controls/FWebCtrl_GestureState.h @@ -85,8 +85,6 @@ public: virtual ~_PanningGestureState(void); - void InitializeTextSelectionStatus(void); - virtual bool OnTouchPressed(const Tizen::Ui::_Control& source, const Tizen::Ui::_TouchInfo& touchinfo); virtual bool OnTouchReleased(const Tizen::Ui::_Control& source, const Tizen::Ui::_TouchInfo& touchinfo); @@ -97,12 +95,6 @@ public: private: _Web* __pWebCore; - - bool __textSelected; - - bool __textChanged; - - Tizen::Base::String __selectedText; }; class _FlickGestureState diff --git a/src/controls/FWebCtrl_Web.cpp b/src/controls/FWebCtrl_Web.cpp index 04cf64e..7e6add7 100755 --- a/src/controls/FWebCtrl_Web.cpp +++ b/src/controls/FWebCtrl_Web.cpp @@ -730,86 +730,182 @@ _Web::OnTouchPressed(const _Control& source, const _TouchInfo& touchInfo) bool -_Web::OnTouchMoved(const _Control& source, const _TouchInfo& touchInfo) +_Web::CanOutOfEdgeLeft(float scrollDistanceX, int pageDistance) { - if (__pEflWebkit.get()) + if (scrollDistanceX < 0.0f) { - if (__gestureType == WEB_GESTURE_TYPE_TAP) - { - __pPanningGestureHandler->InitializeTextSelectionStatus(); - } + __edgeType &= ~WEB_EDGE_RIGHT; - SendTouchEventForJavaScript(touchInfo); + return false; + } + else if (scrollDistanceX > 0.0f && pageDistance != 0) + { + __edgeType &= ~WEB_EDGE_LEFT; + } - __pGestureHandler->OnTouchMoved(source, touchInfo); + return true; +} + + +bool +_Web::CanOutOfEdgeRight(float scrollDistanceX, int pageDistance) +{ + if (scrollDistanceX > 0.0f) + { + __edgeType &= ~WEB_EDGE_LEFT; + + return false; + } + else if (scrollDistanceX < 0.0f && pageDistance != 0) + { + __edgeType &= ~WEB_EDGE_RIGHT; + } + + return true; +} + + +bool +_Web::CanOutOfEdgeTop(float scrollDistanceY, int pageDistance) +{ + if (scrollDistanceY < 0.0f) + { + __edgeType &= ~WEB_EDGE_BOTTOM; + + return false; + } + else if (scrollDistanceY > 0.0f && pageDistance != 0) + { + __edgeType &= ~WEB_EDGE_TOP; + } + + return true; +} - float scrollDistanceX = __previousTouchedPosition.x - touchInfo.GetCurrentPosition().x; - float scrollDistanceY = __previousTouchedPosition.y - touchInfo.GetCurrentPosition().y; - __previousTouchedPosition = touchInfo.GetCurrentPosition(); - if (__edgeType != WEB_EDGE_NONE && __gestureType == WEB_GESTURE_TYPE_PANNING) +bool +_Web::CanOutOfEdgeBottom(float scrollDistanceY, int pageDistance) +{ + if (scrollDistanceY > 0.0f) + { + __edgeType &= ~WEB_EDGE_TOP; + + return false; + } + else if (scrollDistanceY < 0.0f && pageDistance != 0) + { + __edgeType &= ~WEB_EDGE_BOTTOM; + } + + return true; +} + + +bool +_Web::CanConsumeTouchMoved(float scrollDistanceX, float scrollDistanceY) +{ + if (__edgeType == WEB_EDGE_NONE) + { + return true; + } + + bool ret = true; + + int scrollWidth = 0; + int scrollHeight = 0; + ewk_view_scroll_size_get(GetWebNativeNode(), &scrollWidth, &scrollHeight); + + int scrollX = 0; + int scrollY = 0; + ewk_view_scroll_pos_get(GetWebNativeNode(), &scrollX, &scrollY); + + if (scrollWidth == 0) + { + if (__edgeType & WEB_EDGE_TOP) { - if (_Abs(scrollDistanceY) < _Abs(scrollDistanceX)) + if (!CanOutOfEdgeTop(scrollDistanceY, scrollY - 0)) { - if (__edgeType & WEB_EDGE_LEFT) - { - if (scrollDistanceX < 0) - { - __edgeType &= ~WEB_EDGE_RIGHT; - - return false; - } - else - { - __edgeType &= ~WEB_EDGE_LEFT; - } - } - else if (__edgeType & WEB_EDGE_RIGHT) - { - if (scrollDistanceX > 0) - { - __edgeType &= ~WEB_EDGE_LEFT; - - return false; - } - else - { - __edgeType &= ~WEB_EDGE_RIGHT; - } - } + return false; } - else if (_Abs(scrollDistanceY) > _Abs(scrollDistanceX)) + } + else if (__edgeType & WEB_EDGE_BOTTOM) + { + if (!CanOutOfEdgeBottom(scrollDistanceY, scrollY - scrollHeight)) { - if (__edgeType & WEB_EDGE_TOP) - { - if (scrollDistanceY < 0) - { - __edgeType &= ~WEB_EDGE_BOTTOM; - - return false; - } - else - { - __edgeType &= ~WEB_EDGE_TOP; - } - } - else if (__edgeType & WEB_EDGE_BOTTOM) - { - if (scrollDistanceY > 0) - { - __edgeType &= ~WEB_EDGE_TOP; - - return false; - } - else - { - __edgeType &= ~WEB_EDGE_BOTTOM; - } - } + return false; } } + } - return true; + if (scrollHeight == 0) + { + if (__edgeType & WEB_EDGE_LEFT) + { + if (!CanOutOfEdgeLeft(scrollDistanceX, scrollX - 0)) + { + return false; + } + } + else if (__edgeType & WEB_EDGE_RIGHT) + { + if (!CanOutOfEdgeRight(scrollDistanceX, scrollX - scrollWidth)) + { + return false; + } + } + } + + if (scrollWidth > 0 && scrollHeight > 0) + { + if ((__edgeType & WEB_EDGE_LEFT) && (__edgeType & WEB_EDGE_TOP)) + { + if (!CanOutOfEdgeLeft(scrollDistanceX, scrollX - 0) || !CanOutOfEdgeTop(scrollDistanceY, scrollY - 0)) + { + return false; + } + } + else if ((__edgeType & WEB_EDGE_LEFT) && (__edgeType & WEB_EDGE_BOTTOM)) + { + if (!CanOutOfEdgeLeft(scrollDistanceX, scrollX - 0) || !CanOutOfEdgeBottom(scrollDistanceY, scrollY - scrollHeight)) + { + return false; + } + } + else if ((__edgeType & WEB_EDGE_RIGHT) && (__edgeType & WEB_EDGE_TOP)) + { + if (!CanOutOfEdgeRight(scrollDistanceX, scrollX - scrollWidth) || !CanOutOfEdgeTop(scrollDistanceY, scrollY - 0)) + { + return false; + } + } + else if ((__edgeType & WEB_EDGE_RIGHT) && (__edgeType & WEB_EDGE_BOTTOM)) + { + if (!CanOutOfEdgeRight(scrollDistanceX, scrollX - scrollWidth) || !CanOutOfEdgeBottom(scrollDistanceY, scrollY - scrollHeight)) + { + return false; + } + } + } + + return true; +} + + +bool +_Web::OnTouchMoved(const _Control& source, const _TouchInfo& touchInfo) +{ + if (__pEflWebkit.get()) + { + SendTouchEventForJavaScript(touchInfo); + + float scrollDistanceX = __previousTouchedPosition.x - touchInfo.GetCurrentPosition().x; + float scrollDistanceY = __previousTouchedPosition.y - touchInfo.GetCurrentPosition().y; + __previousTouchedPosition = touchInfo.GetCurrentPosition(); + + if (CanConsumeTouchMoved(scrollDistanceX, scrollDistanceY)) + { + return __pGestureHandler->OnTouchMoved(source, touchInfo); + } } return false; @@ -846,30 +942,6 @@ _Web::OnTouchCanceled(const _Control& source, const _TouchInfo& touchInfo) } -void -_Web::OnTouchPressHandled(const _Control& control) -{ -} - - -void -_Web::OnTouchReleaseHandled(const _Control& control) -{ -} - - -void -_Web::OnTouchMoveHandled(const _Control& control) -{ -} - - -void -_Web::OnTouchCancelHandled(const _Control& control) -{ -} - - bool _Web::OnLongPressGestureDetected(_TouchLongPressGestureDetector& gesture) { @@ -878,14 +950,14 @@ _Web::OnLongPressGestureDetected(_TouchLongPressGestureDetector& gesture) return __pTapGestureHandler->OnLongPressGestureDetected(gesture); } - return true; + return false; } bool _Web::OnLongPressGestureCanceled(_TouchLongPressGestureDetector& gesture) { - return true; + return false; } @@ -897,33 +969,33 @@ _Web::OnTapGestureDetected(_TouchTapGestureDetector& gesture) return __pTapGestureHandler->OnTapGestureDetected(gesture); } - return true; + return false; } bool _Web::OnTapGestureCanceled(_TouchTapGestureDetector& gesture) { - return true; + return false; } bool _Web::OnFlickGestureDetected(_TouchFlickGestureDetector& gesture) { - if (__pEflWebkit.get() && _WebSettingImpl::GetInstance(__pWebSetting.get())->IsScrollEnabled()) + if (__pEflWebkit.get()) { return __pFlickGestureHandler->OnFlickGestureDetected(gesture); } - return true; + return false; } bool _Web::OnFlickGestureCanceled(_TouchFlickGestureDetector& gesture) { - return true; + return false; } @@ -935,7 +1007,7 @@ _Web::OnPinchGestureStarted(Tizen::Ui::_TouchPinchGestureDetector& gesture) return __pPinchGestureHandler->OnPinchGestureStarted(gesture); } - return true; + return false; } @@ -947,7 +1019,7 @@ _Web::OnPinchGestureChanged(Tizen::Ui::_TouchPinchGestureDetector& gesture) return __pPinchGestureHandler->OnPinchGestureChanged(gesture); } - return true; + return false; } @@ -959,14 +1031,14 @@ _Web::OnPinchGestureFinished(Tizen::Ui::_TouchPinchGestureDetector& gesture) return __pPinchGestureHandler->OnPinchGestureFinished(gesture); } - return true; + return false; } bool _Web::OnPinchGestureCanceled(Tizen::Ui::_TouchPinchGestureDetector& gesture) { - return true; + return false; } diff --git a/src/controls/FWebCtrl_Web.h b/src/controls/FWebCtrl_Web.h index ca7c1cf..6d86ef8 100755 --- a/src/controls/FWebCtrl_Web.h +++ b/src/controls/FWebCtrl_Web.h @@ -120,6 +120,11 @@ public: void SetFocusEnd(bool focus); bool IsFocusEnd(void); + bool CanOutOfEdgeLeft(float scrollDistanceX, int pageDistance); + bool CanOutOfEdgeRight(float scrollDistanceX, int pageDistance); + bool CanOutOfEdgeTop(float scrollDistanceY, int pageDistance); + bool CanOutOfEdgeBottom(float scrollDistanceY, int pageDistance); + bool CanConsumeTouchMoved(float scrollDistanceX, float scrollDistanceY); virtual result OnAttaching(const _Control* pParent); virtual result OnBoundsChanging(const Tizen::Graphics::Rectangle& bounds); virtual void OnBoundsChanged(void); @@ -130,11 +135,6 @@ public: virtual bool OnTouchMoved(const Tizen::Ui::_Control& source, const Tizen::Ui::_TouchInfo& touchInfo); virtual bool OnTouchCanceled(const Tizen::Ui::_Control& source, const Tizen::Ui::_TouchInfo& touchInfo); - virtual void OnTouchPressHandled(const Tizen::Ui::_Control& control); - virtual void OnTouchReleaseHandled(const Tizen::Ui::_Control& control); - virtual void OnTouchMoveHandled(const Tizen::Ui::_Control& control); - virtual void OnTouchCancelHandled(const Tizen::Ui::_Control& control); - virtual bool OnPinchGestureStarted(Tizen::Ui::_TouchPinchGestureDetector& gesture); virtual bool OnPinchGestureChanged(Tizen::Ui::_TouchPinchGestureDetector& gesture); virtual bool OnPinchGestureFinished(Tizen::Ui::_TouchPinchGestureDetector& gesture);