X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Ftext%2Ftext-controller-impl.cpp;h=6554a2c62fcd290045d8cdbc44c740e2a979974b;hp=0a8fe4bc72a10fcffbd5a8ff9e5f2e8aee15f68f;hb=1bc80454daa9e30655e4c4dec1784f6e9e5f496a;hpb=a57d543cd372560a9065a31b32184a6a02d49e77 diff --git a/dali-toolkit/internal/text/text-controller-impl.cpp b/dali-toolkit/internal/text/text-controller-impl.cpp index 0a8fe4b..6554a2c 100644 --- a/dali-toolkit/internal/text/text-controller-impl.cpp +++ b/dali-toolkit/internal/text/text-controller-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * Copyright (c) 2020 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,10 +19,8 @@ #include // EXTERNAL INCLUDES -#include #include #include -#include // INTERNAL INCLUDES #include @@ -34,32 +32,19 @@ #include #include #include +#include #include +#include using namespace Dali; namespace { -/** - * @brief Struct used to calculate the selection box. - */ -struct SelectionBoxInfo -{ - float lineOffset; - float lineHeight; - float minX; - float maxX; -}; - #if defined(DEBUG_ENABLED) - Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS"); +Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS"); #endif -const float MAX_FLOAT = std::numeric_limits::max(); -const float MIN_FLOAT = std::numeric_limits::min(); -const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction - #define MAKE_SHADER(A)#A const char* VERTEX_SHADER_BACKGROUND = MAKE_SHADER( @@ -159,227 +144,14 @@ EventData::EventData( DecoratorPtr decorator, InputMethodContext& inputMethodCon mIsPlaceholderElideEnabled( false ), mPlaceholderEllipsisFlag( false ), mShiftSelectionFlag( true ), - mUpdateAlignment( false ) + mUpdateAlignment( false ), + mEditingEnabled( true ) { } -EventData::~EventData() -{} - bool Controller::Impl::ProcessInputEvents() { - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" ); - if( NULL == mEventData ) - { - // Nothing to do if there is no text input. - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" ); - return false; - } - - if( mEventData->mDecorator ) - { - for( std::vector::iterator iter = mEventData->mEventQueue.begin(); - iter != mEventData->mEventQueue.end(); - ++iter ) - { - switch( iter->type ) - { - case Event::CURSOR_KEY_EVENT: - { - OnCursorKeyEvent( *iter ); - break; - } - case Event::TAP_EVENT: - { - OnTapEvent( *iter ); - break; - } - case Event::LONG_PRESS_EVENT: - { - OnLongPressEvent( *iter ); - break; - } - case Event::PAN_EVENT: - { - OnPanEvent( *iter ); - break; - } - case Event::GRAB_HANDLE_EVENT: - case Event::LEFT_SELECTION_HANDLE_EVENT: - case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through - { - OnHandleEvent( *iter ); - break; - } - case Event::SELECT: - { - OnSelectEvent( *iter ); - break; - } - case Event::SELECT_ALL: - { - OnSelectAllEvent(); - break; - } - case Event::SELECT_NONE: - { - OnSelectNoneEvent(); - break; - } - } - } - } - - if( mEventData->mUpdateCursorPosition || - mEventData->mUpdateHighlightBox ) - { - NotifyInputMethodContext(); - } - - // The cursor must also be repositioned after inserts into the model - if( mEventData->mUpdateCursorPosition ) - { - // Updates the cursor position and scrolls the text to make it visible. - CursorInfo cursorInfo; - // Calculate the cursor position from the new cursor index. - GetCursorPosition( mEventData->mPrimaryCursorPosition, - cursorInfo ); - - if( mEventData->mUpdateCursorHookPosition ) - { - // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'. - mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x; - mEventData->mUpdateCursorHookPosition = false; - } - - // Scroll first the text after delete ... - if( mEventData->mScrollAfterDelete ) - { - ScrollTextToMatchCursor( cursorInfo ); - } - - // ... then, text can be scrolled to make the cursor visible. - if( mEventData->mScrollAfterUpdatePosition ) - { - const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset ); - ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight ); - } - mEventData->mScrollAfterUpdatePosition = false; - mEventData->mScrollAfterDelete = false; - - UpdateCursorPosition( cursorInfo ); - - mEventData->mDecoratorUpdated = true; - mEventData->mUpdateCursorPosition = false; - mEventData->mUpdateGrabHandlePosition = false; - } - else - { - CursorInfo leftHandleInfo; - CursorInfo rightHandleInfo; - - if( mEventData->mUpdateHighlightBox ) - { - GetCursorPosition( mEventData->mLeftSelectionPosition, - leftHandleInfo ); - - GetCursorPosition( mEventData->mRightSelectionPosition, - rightHandleInfo ); - - if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) ) - { - if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected ) - { - CursorInfo& infoLeft = leftHandleInfo; - - const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset ); - ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight ); - - CursorInfo& infoRight = rightHandleInfo; - - const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset ); - ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight ); - } - else - { - CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo; - - const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset ); - ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight ); - } - } - } - - if( mEventData->mUpdateLeftSelectionPosition ) - { - UpdateSelectionHandle( LEFT_SELECTION_HANDLE, - leftHandleInfo ); - - SetPopupButtons(); - mEventData->mDecoratorUpdated = true; - mEventData->mUpdateLeftSelectionPosition = false; - } - - if( mEventData->mUpdateRightSelectionPosition ) - { - UpdateSelectionHandle( RIGHT_SELECTION_HANDLE, - rightHandleInfo ); - - SetPopupButtons(); - mEventData->mDecoratorUpdated = true; - mEventData->mUpdateRightSelectionPosition = false; - } - - if( mEventData->mUpdateHighlightBox ) - { - RepositionSelectionHandles(); - - mEventData->mUpdateLeftSelectionPosition = false; - mEventData->mUpdateRightSelectionPosition = false; - mEventData->mUpdateHighlightBox = false; - mEventData->mIsLeftHandleSelected = false; - mEventData->mIsRightHandleSelected = false; - } - - mEventData->mScrollAfterUpdatePosition = false; - } - - if( mEventData->mUpdateInputStyle ) - { - // Keep a copy of the current input style. - InputStyle currentInputStyle; - currentInputStyle.Copy( mEventData->mInputStyle ); - - // Set the default style first. - RetrieveDefaultInputStyle( mEventData->mInputStyle ); - - // Get the character index from the cursor index. - const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u; - - // Retrieve the style from the style runs stored in the logical model. - mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle ); - - // Compare if the input style has changed. - const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle ); - - if( hasInputStyleChanged ) - { - const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle ); - // Queue the input style changed signal. - mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask ); - } - - mEventData->mUpdateInputStyle = false; - } - - mEventData->mEventQueue.clear(); - - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" ); - - const bool decoratorUpdated = mEventData->mDecoratorUpdated; - mEventData->mDecoratorUpdated = false; - - return decoratorUpdated; + return ControllerImplEventHandler::ProcessInputEvents(*this); } void Controller::Impl::NotifyInputMethodContext() @@ -952,7 +724,7 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) // Get the default font's description. TextAbstraction::FontDescription defaultFontDescription; - TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE; + TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale; if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) ) { @@ -960,7 +732,7 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription; if( mEventData->mPlaceholderFont->sizeDefined ) { - defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u; + defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * mFontSizeScale * 64u; } } else if( NULL != mFontDefaults ) @@ -974,7 +746,7 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) } else { - defaultPointSize = mFontDefaults->mDefaultPointSize * 64u; + defaultPointSize = mFontDefaults->mDefaultPointSize * mFontSizeScale * 64u; } } @@ -1325,11 +1097,11 @@ float Controller::Impl::GetDefaultFontLineHeight() if( NULL == mFontDefaults ) { TextAbstraction::FontDescription fontDescription; - defaultFontId = mFontClient.GetFontId( fontDescription ); + defaultFontId = mFontClient.GetFontId( fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale ); } else { - defaultFontId = mFontDefaults->GetFontId( mFontClient ); + defaultFontId = mFontDefaults->GetFontId( mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale ); } Text::FontMetrics fontMetrics; @@ -1338,737 +1110,130 @@ float Controller::Impl::GetDefaultFontLineHeight() return( fontMetrics.ascender - fontMetrics.descender ); } -void Controller::Impl::OnCursorKeyEvent( const Event& event ) +void Controller::Impl::SetTextSelectionRange(const uint32_t *pStart, const uint32_t *pEnd) { - if( NULL == mEventData || !IsShowingRealText() ) + if( nullptr == mEventData ) { - // Nothing to do if there is no text input. + // Nothing to do if there is no text. return; } - int keyCode = event.p1.mInt; - bool isShiftModifier = event.p2.mBool; - - CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition; - - if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ) - { - if( mEventData->mPrimaryCursorPosition > 0u ) - { - if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() ) - { - mEventData->mPrimaryCursorPosition = std::min(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition); - } - else - { - mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u ); - } - } - } - else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) - { - if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition ) - { - if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() ) - { - mEventData->mPrimaryCursorPosition = std::max(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition); - } - else - { - mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition ); - } - } - } - else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier ) - { - // Ignore Shift-Up for text selection for now. - - // Get first the line index of the current cursor position index. - CharacterIndex characterIndex = 0u; - - if( mEventData->mPrimaryCursorPosition > 0u ) - { - characterIndex = mEventData->mPrimaryCursorPosition - 1u; - } - - const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex ); - const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex ); - - // Retrieve the cursor position info. - CursorInfo cursorInfo; - GetCursorPosition( mEventData->mPrimaryCursorPosition, - cursorInfo ); - - // Get the line above. - const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex ); - - // Get the next hit 'y' point. - const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender ); - - // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index. - bool matchedCharacter = false; - mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - mEventData->mCursorHookPositionX, - hitPointY, - CharacterHitTest::TAP, - matchedCharacter ); - } - else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier ) + if( mEventData->mSelectionEnabled && (pStart || pEnd)) { - // Ignore Shift-Down for text selection for now. + uint32_t length = static_cast(mModel->mLogicalModel->mText.Count()); - // Get first the line index of the current cursor position index. - CharacterIndex characterIndex = 0u; - - if( mEventData->mPrimaryCursorPosition > 0u ) + if (pStart) { - characterIndex = mEventData->mPrimaryCursorPosition - 1u; + mEventData->mLeftSelectionPosition = std::min(*pStart, length); } - - const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex ); - - if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() ) + if (pEnd) { - // Retrieve the cursor position info. - CursorInfo cursorInfo; - GetCursorPosition( mEventData->mPrimaryCursorPosition, - cursorInfo ); - - // Get the line below. - const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u ); - - // Get the next hit 'y' point. - const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender ); - - // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index. - bool matchedCharacter = false; - mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - mEventData->mCursorHookPositionX, - hitPointY, - CharacterHitTest::TAP, - matchedCharacter ); + mEventData->mRightSelectionPosition = std::min(*pEnd, length); } - } - if ( !isShiftModifier && mEventData->mState != EventData::SELECTING ) - { - // Update selection position after moving the cursor - mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition; - mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition; - } - - if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag ) - { - // Handle text selection - bool selecting = false; - - if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) - { - // Shift-Left/Right to select the text - int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition; - if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary - { - mEventData->mRightSelectionPosition += cursorPositionDelta; - } - selecting = true; - } - else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition ) + if (mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition) { - // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text - selecting = true; + ChangeState( EventData::EDITING ); + mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition; + mEventData->mUpdateCursorPosition = true; } - - if ( selecting ) + else { - // Notify the cursor position to the InputMethodContext. - if( mEventData->mInputMethodContext ) - { - mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition ); - mEventData->mInputMethodContext.NotifyCursorPosition(); - } - ChangeState( EventData::SELECTING ); - + mEventData->mUpdateHighlightBox = true; mEventData->mUpdateLeftSelectionPosition = true; mEventData->mUpdateRightSelectionPosition = true; - mEventData->mUpdateGrabHandlePosition = true; - mEventData->mUpdateHighlightBox = true; - - // Hide the text selection popup if select the text using keyboard instead of moving grab handles - if( mEventData->mGrabHandlePopupEnabled ) - { - mEventData->mDecorator->SetPopupActive( false ); - } } } - else - { - // Handle normal cursor move - ChangeState( EventData::EDITING ); - mEventData->mUpdateCursorPosition = true; - } - - mEventData->mUpdateInputStyle = true; - mEventData->mScrollAfterUpdatePosition = true; } -void Controller::Impl::OnTapEvent( const Event& event ) +CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const { - if( NULL != mEventData ) + if( nullptr == mEventData ) { - const unsigned int tapCount = event.p1.mUint; - - if( 1u == tapCount ) - { - if( IsShowingRealText() ) - { - // Convert from control's coords to text's coords. - const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x; - const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; - - // Keep the tap 'x' position. Used to move the cursor. - mEventData->mCursorHookPositionX = xPosition; - - // Whether to touch point hits on a glyph. - bool matchedCharacter = false; - mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - xPosition, - yPosition, - CharacterHitTest::TAP, - matchedCharacter ); - - // When the cursor position is changing, delay cursor blinking - mEventData->mDecorator->DelayCursorBlink(); - } - else - { - mEventData->mPrimaryCursorPosition = 0u; - } - - // Update selection position after tapping - mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition; - mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition; - - mEventData->mUpdateCursorPosition = true; - mEventData->mUpdateGrabHandlePosition = true; - mEventData->mScrollAfterUpdatePosition = true; - mEventData->mUpdateInputStyle = true; - - // Notify the cursor position to the InputMethodContext. - if( mEventData->mInputMethodContext ) - { - mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition ); - mEventData->mInputMethodContext.NotifyCursorPosition(); - } - } - else if( 2u == tapCount ) - { - if( mEventData->mSelectionEnabled ) - { - // Convert from control's coords to text's coords. - const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x; - const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; - - // Calculates the logical position from the x,y coords. - RepositionSelectionHandles( xPosition, - yPosition, - mEventData->mDoubleTapAction ); - } - } + return 0; } + return mEventData->mPrimaryCursorPosition; } -void Controller::Impl::OnPanEvent( const Event& event ) +bool Controller::Impl::SetPrimaryCursorPosition( CharacterIndex index ) { - if( NULL == mEventData ) + if( nullptr == mEventData ) { - // Nothing to do if there is no text input. - return; + // Nothing to do if there is no text. + return false; } - const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled(); - const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled(); - - if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled ) + if( mEventData->mPrimaryCursorPosition == index ) { - // Nothing to do if scrolling is not enabled. - return; + // Nothing for same cursor position. + return false; } - const GestureState state = static_cast( event.p1.mInt ); - switch( state ) - { - case GestureState::STARTED: - { - // Will remove the cursor, handles or text's popup, ... - ChangeState( EventData::TEXT_PANNING ); - break; - } - case GestureState::CONTINUING: - { - const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize(); - const Vector2 currentScroll = mModel->mScrollPosition; - - if( isHorizontalScrollEnabled ) - { - const float displacementX = event.p2.mFloat; - mModel->mScrollPosition.x += displacementX; - - ClampHorizontalScroll( layoutSize ); - } - - if( isVerticalScrollEnabled ) - { - const float displacementY = event.p3.mFloat; - mModel->mScrollPosition.y += displacementY; + uint32_t length = static_cast(mModel->mLogicalModel->mText.Count()); + mEventData->mPrimaryCursorPosition = std::min(index, length); + ChangeState( EventData::EDITING ); + mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition; + mEventData->mUpdateCursorPosition = true; + ScrollTextToMatchCursor(); + return true; +} - ClampVerticalScroll( layoutSize ); - } +Uint32Pair Controller::Impl::GetTextSelectionRange() const +{ + Uint32Pair range; - mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll ); - break; - } - case GestureState::FINISHED: - case GestureState::CANCELLED: // FALLTHROUGH - { - // Will go back to the previous state to show the cursor, handles, the text's popup, ... - ChangeState( mEventData->mPreviousState ); - break; - } - default: - break; + if( mEventData ) + { + range.first = mEventData->mLeftSelectionPosition; + range.second = mEventData->mRightSelectionPosition; } + + return range; } -void Controller::Impl::OnLongPressEvent( const Event& event ) +bool Controller::Impl::IsEditable() const { - DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" ); + return mEventData && mEventData->mEditingEnabled; +} - if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) ) - { - ChangeState( EventData::EDITING_WITH_POPUP ); - mEventData->mDecoratorUpdated = true; - mEventData->mUpdateInputStyle = true; - } - else +void Controller::Impl::SetEditable( bool editable ) +{ + if( mEventData) { - if( mEventData->mSelectionEnabled ) - { - // Convert from control's coords to text's coords. - const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x; - const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; - - // Calculates the logical position from the x,y coords. - RepositionSelectionHandles( xPosition, - yPosition, - mEventData->mLongPressAction ); - } + mEventData->mEditingEnabled = editable; } } -void Controller::Impl::OnHandleEvent( const Event& event ) +void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval ) { - if( NULL == mEventData ) + if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition ) { - // Nothing to do if there is no text input. + // Nothing to select if handles are in the same place. + selectedText.clear(); return; } - const unsigned int state = event.p1.mUint; - const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state ); - const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled(); - - if( HANDLE_PRESSED == state ) - { - // Convert from decorator's coords to text's coords. - const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x; - const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; + const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition; - // Need to calculate the handle's new position. - bool matchedCharacter = false; - const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - xPosition, - yPosition, - CharacterHitTest::SCROLL, - matchedCharacter ); + //Get start and end position of selection + const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition; + const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText; - if( Event::GRAB_HANDLE_EVENT == event.type ) - { - ChangeState ( EventData::GRAB_HANDLE_PANNING ); + Vector& utf32Characters = mModel->mLogicalModel->mText; + const Length numberOfCharacters = utf32Characters.Count(); - if( handleNewPosition != mEventData->mPrimaryCursorPosition ) - { - // Updates the cursor position if the handle's new position is different than the current one. - mEventData->mUpdateCursorPosition = true; - // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth). - mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled; - mEventData->mPrimaryCursorPosition = handleNewPosition; - } + // Validate the start and end selection points + if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters ) + { + //Get text as a UTF8 string + Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText ); - // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set. - mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled; - } - else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type ) + if( deleteAfterRetrieval ) // Only delete text if copied successfully { - ChangeState ( EventData::SELECTION_HANDLE_PANNING ); - - if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) && - ( handleNewPosition != mEventData->mRightSelectionPosition ) ) - { - // Updates the highlight box if the handle's new position is different than the current one. - mEventData->mUpdateHighlightBox = true; - // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth). - mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled; - mEventData->mLeftSelectionPosition = handleNewPosition; - } - - // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set. - mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled; - - // Will define the order to scroll the text to match the handle position. - mEventData->mIsLeftHandleSelected = true; - mEventData->mIsRightHandleSelected = false; - } - else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type ) - { - ChangeState ( EventData::SELECTION_HANDLE_PANNING ); - - if( ( handleNewPosition != mEventData->mRightSelectionPosition ) && - ( handleNewPosition != mEventData->mLeftSelectionPosition ) ) - { - // Updates the highlight box if the handle's new position is different than the current one. - mEventData->mUpdateHighlightBox = true; - // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth). - mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled; - mEventData->mRightSelectionPosition = handleNewPosition; - } - - // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set. - mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled; - - // Will define the order to scroll the text to match the handle position. - mEventData->mIsLeftHandleSelected = false; - mEventData->mIsRightHandleSelected = true; - } - } // end ( HANDLE_PRESSED == state ) - else if( ( HANDLE_RELEASED == state ) || - handleStopScrolling ) - { - CharacterIndex handlePosition = 0u; - if( handleStopScrolling || isSmoothHandlePanEnabled ) - { - // Convert from decorator's coords to text's coords. - const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x; - const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; - - bool matchedCharacter = false; - handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - xPosition, - yPosition, - CharacterHitTest::SCROLL, - matchedCharacter ); - } - - if( Event::GRAB_HANDLE_EVENT == event.type ) - { - mEventData->mUpdateCursorPosition = true; - mEventData->mUpdateGrabHandlePosition = true; - mEventData->mUpdateInputStyle = true; - - if( !IsClipboardEmpty() ) - { - ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup - } - - if( handleStopScrolling || isSmoothHandlePanEnabled ) - { - mEventData->mScrollAfterUpdatePosition = true; - mEventData->mPrimaryCursorPosition = handlePosition; - } - } - else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type ) - { - ChangeState( EventData::SELECTING ); - - mEventData->mUpdateHighlightBox = true; - mEventData->mUpdateLeftSelectionPosition = true; - mEventData->mUpdateRightSelectionPosition = true; - - if( handleStopScrolling || isSmoothHandlePanEnabled ) - { - mEventData->mScrollAfterUpdatePosition = true; - - if( ( handlePosition != mEventData->mRightSelectionPosition ) && - ( handlePosition != mEventData->mLeftSelectionPosition ) ) - { - mEventData->mLeftSelectionPosition = handlePosition; - } - } - } - else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type ) - { - ChangeState( EventData::SELECTING ); - - mEventData->mUpdateHighlightBox = true; - mEventData->mUpdateRightSelectionPosition = true; - mEventData->mUpdateLeftSelectionPosition = true; - - if( handleStopScrolling || isSmoothHandlePanEnabled ) - { - mEventData->mScrollAfterUpdatePosition = true; - if( ( handlePosition != mEventData->mRightSelectionPosition ) && - ( handlePosition != mEventData->mLeftSelectionPosition ) ) - { - mEventData->mRightSelectionPosition = handlePosition; - } - } - } - - mEventData->mDecoratorUpdated = true; - } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) ) - else if( HANDLE_SCROLLING == state ) - { - const float xSpeed = event.p2.mFloat; - const float ySpeed = event.p3.mFloat; - const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize(); - const Vector2 currentScrollPosition = mModel->mScrollPosition; - - mModel->mScrollPosition.x += xSpeed; - mModel->mScrollPosition.y += ySpeed; - - ClampHorizontalScroll( layoutSize ); - ClampVerticalScroll( layoutSize ); - - bool endOfScroll = false; - if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) ) - { - // Notify the decorator there is no more text to scroll. - // The decorator won't send more scroll events. - mEventData->mDecorator->NotifyEndOfScroll(); - // Still need to set the position of the handle. - endOfScroll = true; - } - - // Set the position of the handle. - const bool scrollRightDirection = xSpeed > 0.f; - const bool scrollBottomDirection = ySpeed > 0.f; - const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type; - const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type; - - if( Event::GRAB_HANDLE_EVENT == event.type ) - { - ChangeState( EventData::GRAB_HANDLE_PANNING ); - - // Get the grab handle position in decorator coords. - Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE ); - - if( mEventData->mDecorator->IsHorizontalScrollEnabled() ) - { - // Position the grag handle close to either the left or right edge. - position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width; - } - - if( mEventData->mDecorator->IsVerticalScrollEnabled() ) - { - position.x = mEventData->mCursorHookPositionX; - - // Position the grag handle close to either the top or bottom edge. - position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height; - } - - // Get the new handle position. - // The grab handle's position is in decorator's coords. Need to transforms to text's coords. - bool matchedCharacter = false; - const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - position.x - mModel->mScrollPosition.x, - position.y - mModel->mScrollPosition.y, - CharacterHitTest::SCROLL, - matchedCharacter ); - - if( mEventData->mPrimaryCursorPosition != handlePosition ) - { - mEventData->mUpdateCursorPosition = true; - mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled; - mEventData->mScrollAfterUpdatePosition = true; - mEventData->mPrimaryCursorPosition = handlePosition; - } - mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition; - - // Updates the decorator if the soft handle panning is enabled. - mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled; - } - else if( leftSelectionHandleEvent || rightSelectionHandleEvent ) - { - ChangeState( EventData::SELECTION_HANDLE_PANNING ); - - // Get the selection handle position in decorator coords. - Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE ); - - if( mEventData->mDecorator->IsHorizontalScrollEnabled() ) - { - // Position the selection handle close to either the left or right edge. - position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width; - } - - if( mEventData->mDecorator->IsVerticalScrollEnabled() ) - { - position.x = mEventData->mCursorHookPositionX; - - // Position the grag handle close to either the top or bottom edge. - position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height; - } - - // Get the new handle position. - // The selection handle's position is in decorator's coords. Need to transform to text's coords. - bool matchedCharacter = false; - const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - position.x - mModel->mScrollPosition.x, - position.y - mModel->mScrollPosition.y, - CharacterHitTest::SCROLL, - matchedCharacter ); - - if( leftSelectionHandleEvent ) - { - const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition ); - - if( differentHandles || endOfScroll ) - { - mEventData->mUpdateHighlightBox = true; - mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled; - mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled; - mEventData->mLeftSelectionPosition = handlePosition; - } - } - else - { - const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition ); - if( differentHandles || endOfScroll ) - { - mEventData->mUpdateHighlightBox = true; - mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled; - mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled; - mEventData->mRightSelectionPosition = handlePosition; - } - } - - if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition ) - { - RepositionSelectionHandles(); - - mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled; - } - } - mEventData->mDecoratorUpdated = true; - } // end ( HANDLE_SCROLLING == state ) -} - -void Controller::Impl::OnSelectEvent( const Event& event ) -{ - if( NULL == mEventData ) - { - // Nothing to do if there is no text. - return; - } - - if( mEventData->mSelectionEnabled ) - { - // Convert from control's coords to text's coords. - const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x; - const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; - - // Calculates the logical position from the x,y coords. - RepositionSelectionHandles( xPosition, - yPosition, - Controller::NoTextTap::HIGHLIGHT ); - } -} - -void Controller::Impl::OnSelectAllEvent() -{ - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false"); - - if( NULL == mEventData ) - { - // Nothing to do if there is no text. - return; - } - - if( mEventData->mSelectionEnabled ) - { - // Calculates the logical position from the start. - RepositionSelectionHandles( 0.f - mModel->mScrollPosition.x, - 0.f - mModel->mScrollPosition.y, - Controller::NoTextTap::HIGHLIGHT ); - - mEventData->mLeftSelectionPosition = 0u; - mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count(); - } -} - -void Controller::Impl::OnSelectNoneEvent() -{ - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false"); - - if( NULL == mEventData ) - { - // Nothing to do if there is no text. - return; - } - - if( mEventData->mSelectionEnabled && mEventData->mState == EventData::SELECTING) - { - mEventData->mPrimaryCursorPosition = 0u; - mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition; - ChangeState( EventData::INACTIVE ); - mEventData->mUpdateCursorPosition = true; - mEventData->mUpdateInputStyle = true; - mEventData->mScrollAfterUpdatePosition = true; - } -} - -void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval ) -{ - if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition ) - { - // Nothing to select if handles are in the same place. - selectedText.clear(); - return; - } - - const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition; - - //Get start and end position of selection - const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition; - const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText; - - Vector& utf32Characters = mModel->mLogicalModel->mText; - const Length numberOfCharacters = utf32Characters.Count(); - - // Validate the start and end selection points - if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters ) - { - //Get text as a UTF8 string - Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText ); - - if( deleteAfterRetrieval ) // Only delete text if copied successfully - { - // Keep a copy of the current input style. - InputStyle currentInputStyle; - currentInputStyle.Copy( mEventData->mInputStyle ); + // Keep a copy of the current input style. + InputStyle currentInputStyle; + currentInputStyle.Copy( mEventData->mInputStyle ); // Set as input style the style of the first deleted character. mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle ); @@ -2117,6 +1282,18 @@ void Controller::Impl::RetrieveSelection( std::string& selectedText, bool delete } } +void Controller::Impl::SetSelection( int start, int end ) +{ + mEventData->mLeftSelectionPosition = start; + mEventData->mRightSelectionPosition = end; + mEventData->mUpdateCursorPosition = true; +} + +std::pair< int, int > Controller::Impl::GetSelectionIndexes() const +{ + return { mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition }; +} + void Controller::Impl::ShowClipboard() { if( mClipboard ) @@ -2138,7 +1315,7 @@ void Controller::Impl::SetClipboardHideEnable(bool enable) mClipboardHideEnabled = enable; } -bool Controller::Impl::CopyStringToClipboard( std::string& source ) +bool Controller::Impl::CopyStringToClipboard( const std::string& source ) { //Send string to clipboard return ( mClipboard && mClipboard.SetItem( source ) ); @@ -2162,477 +1339,11 @@ void Controller::Impl::RequestGetTextFromClipboard() void Controller::Impl::RepositionSelectionHandles() { - CharacterIndex selectionStart = mEventData->mLeftSelectionPosition; - CharacterIndex selectionEnd = mEventData->mRightSelectionPosition; - - if( selectionStart == selectionEnd ) - { - // Nothing to select if handles are in the same place. - // So, deactive Highlight box. - mEventData->mDecorator->SetHighlightActive( false ); - return; - } - - mEventData->mDecorator->ClearHighlights(); - - const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin(); - const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin(); - const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin(); - const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin(); - const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin(); - const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin(); - const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL; - - const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count(); - const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) ); - const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) ); - - // Swap the indices if the start is greater than the end. - const bool indicesSwapped = selectionStart > selectionEnd; - - // Tell the decorator to flip the selection handles if needed. - mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection ); - - if( indicesSwapped ) - { - std::swap( selectionStart, selectionEnd ); - } - - // Get the indices to the first and last selected glyphs. - const CharacterIndex selectionEndMinusOne = selectionEnd - 1u; - const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart ); - const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne ); - const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u ); - - // Get the lines where the glyphs are laid-out. - const LineRun* lineRun = mModel->mVisualModel->mLines.Begin(); - - LineIndex lineIndex = 0u; - Length numberOfLines = 0u; - mModel->mVisualModel->GetNumberOfLines( glyphStart, - 1u + glyphEnd - glyphStart, - lineIndex, - numberOfLines ); - const LineIndex firstLineIndex = lineIndex; - - // Create the structure to store some selection box info. - Vector selectionBoxLinesInfo; - selectionBoxLinesInfo.Resize( numberOfLines ); - - SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin(); - selectionBoxInfo->minX = MAX_FLOAT; - selectionBoxInfo->maxX = MIN_FLOAT; - - // Keep the min and max 'x' position to calculate the size and position of the highlighed text. - float minHighlightX = std::numeric_limits::max(); - float maxHighlightX = std::numeric_limits::min(); - Size highLightSize; - Vector2 highLightPosition; // The highlight position in decorator's coords. - - // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph. - - // The line's vertical offset of all the lines before the line where the first glyph is laid-out. - selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines, - firstLineIndex ); - - // Transform to decorator's (control) coords. - selectionBoxInfo->lineOffset += mModel->mScrollPosition.y; - - lineRun += firstLineIndex; - - // The line height is the addition of the line ascender and the line descender. - // However, the line descender has a negative value, hence the subtraction. - selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender; - - GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u; - - // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code. - const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart ); - bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) ); - - // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code. - const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd ); - bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) ); - - // The number of quads of the selection box. - const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u ); - mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads ); - - // Count the actual number of quads. - unsigned int actualNumberOfQuads = 0u; - Vector4 quad; - - // Traverse the glyphs. - for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index ) - { - const GlyphInfo& glyph = *( glyphsBuffer + index ); - const Vector2& position = *( positionsBuffer + index ); - - if( splitStartGlyph ) - { - // If the first glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box. - - const float glyphAdvance = glyph.advance / static_cast( numberOfCharactersStart ); - const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart ); - // Get the direction of the character. - CharacterDirection isCurrentRightToLeft = false; - if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right. - { - isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart ); - } - - // The end point could be in the middle of the ligature. - // Calculate the number of characters selected. - const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex ); - - quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex ); - quad.y = selectionBoxInfo->lineOffset; - quad.z = quad.x + static_cast( numberOfCharacters ) * glyphAdvance; - quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight; - - // Store the min and max 'x' for each line. - selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x ); - selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z ); - - mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad ); - ++actualNumberOfQuads; - - splitStartGlyph = false; - continue; - } - - if( splitEndGlyph && ( index == glyphEnd ) ) - { - // Equally, if the last glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box. - - const float glyphAdvance = glyph.advance / static_cast( numberOfCharactersEnd ); - const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd ); - // Get the direction of the character. - CharacterDirection isCurrentRightToLeft = false; - if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right. - { - isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd ); - } - - const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex; - - quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast( numberOfCharacters ) ) : 0.f ); - quad.y = selectionBoxInfo->lineOffset; - quad.z = quad.x + static_cast( interGlyphIndex ) * glyphAdvance; - quad.w = quad.y + selectionBoxInfo->lineHeight; - - // Store the min and max 'x' for each line. - selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x ); - selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z ); - - mEventData->mDecorator->AddHighlight( actualNumberOfQuads, - quad ); - ++actualNumberOfQuads; - - splitEndGlyph = false; - continue; - } - - quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x; - quad.y = selectionBoxInfo->lineOffset; - quad.z = quad.x + glyph.advance; - quad.w = quad.y + selectionBoxInfo->lineHeight; - - // Store the min and max 'x' for each line. - selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x ); - selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z ); - - mEventData->mDecorator->AddHighlight( actualNumberOfQuads, - quad ); - ++actualNumberOfQuads; - - // Whether to retrieve the next line. - if( index == lastGlyphOfLine ) - { - ++lineIndex; - if( lineIndex < firstLineIndex + numberOfLines ) - { - // Retrieve the next line. - ++lineRun; - - // Get the last glyph of the new line. - lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u; - - // Keep the offset and height of the current selection box. - const float currentLineOffset = selectionBoxInfo->lineOffset; - const float currentLineHeight = selectionBoxInfo->lineHeight; - - // Get the selection box info for the next line. - ++selectionBoxInfo; - - selectionBoxInfo->minX = MAX_FLOAT; - selectionBoxInfo->maxX = MIN_FLOAT; - - // Update the line's vertical offset. - selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight; - - // The line height is the addition of the line ascender and the line descender. - // However, the line descender has a negative value, hence the subtraction. - selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender; - } - } - } - - // Traverses all the lines and updates the min and max 'x' positions and the total height. - // The final width is calculated after 'boxifying' the selection. - for( Vector::ConstIterator it = selectionBoxLinesInfo.Begin(), - endIt = selectionBoxLinesInfo.End(); - it != endIt; - ++it ) - { - const SelectionBoxInfo& info = *it; - - // Update the size of the highlighted text. - highLightSize.height += info.lineHeight; - minHighlightX = std::min( minHighlightX, info.minX ); - maxHighlightX = std::max( maxHighlightX, info.maxX ); - } - - // Add extra geometry to 'boxify' the selection. - - if( 1u < numberOfLines ) - { - // Boxify the first line. - lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex; - const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() ); - - bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection ); - bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection ); - - if( boxifyBegin ) - { - quad.x = 0.f; - quad.y = firstSelectionBoxLineInfo.lineOffset; - quad.z = firstSelectionBoxLineInfo.minX; - quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight; - - // Boxify at the beginning of the line. - mEventData->mDecorator->AddHighlight( actualNumberOfQuads, - quad ); - ++actualNumberOfQuads; - - // Update the size of the highlighted text. - minHighlightX = 0.f; - } - - if( boxifyEnd ) - { - quad.x = firstSelectionBoxLineInfo.maxX; - quad.y = firstSelectionBoxLineInfo.lineOffset; - quad.z = mModel->mVisualModel->mControlSize.width; - quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight; - - // Boxify at the end of the line. - mEventData->mDecorator->AddHighlight( actualNumberOfQuads, - quad ); - ++actualNumberOfQuads; - - // Update the size of the highlighted text. - maxHighlightX = mModel->mVisualModel->mControlSize.width; - } - - // Boxify the central lines. - if( 2u < numberOfLines ) - { - for( Vector::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u, - endIt = selectionBoxLinesInfo.End() - 1u; - it != endIt; - ++it ) - { - const SelectionBoxInfo& info = *it; - - quad.x = 0.f; - quad.y = info.lineOffset; - quad.z = info.minX; - quad.w = info.lineOffset + info.lineHeight; - - mEventData->mDecorator->AddHighlight( actualNumberOfQuads, - quad ); - ++actualNumberOfQuads; - - quad.x = info.maxX; - quad.y = info.lineOffset; - quad.z = mModel->mVisualModel->mControlSize.width; - quad.w = info.lineOffset + info.lineHeight; - - mEventData->mDecorator->AddHighlight( actualNumberOfQuads, - quad ); - ++actualNumberOfQuads; - } - - // Update the size of the highlighted text. - minHighlightX = 0.f; - maxHighlightX = mModel->mVisualModel->mControlSize.width; - } - - // Boxify the last line. - lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u; - const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u ); - - boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection ); - boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection ); - - if( boxifyBegin ) - { - quad.x = 0.f; - quad.y = lastSelectionBoxLineInfo.lineOffset; - quad.z = lastSelectionBoxLineInfo.minX; - quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight; - - // Boxify at the beginning of the line. - mEventData->mDecorator->AddHighlight( actualNumberOfQuads, - quad ); - ++actualNumberOfQuads; - - // Update the size of the highlighted text. - minHighlightX = 0.f; - } - - if( boxifyEnd ) - { - quad.x = lastSelectionBoxLineInfo.maxX; - quad.y = lastSelectionBoxLineInfo.lineOffset; - quad.z = mModel->mVisualModel->mControlSize.width; - quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight; - - // Boxify at the end of the line. - mEventData->mDecorator->AddHighlight( actualNumberOfQuads, - quad ); - ++actualNumberOfQuads; - - // Update the size of the highlighted text. - maxHighlightX = mModel->mVisualModel->mControlSize.width; - } - } - - // Set the actual number of quads. - mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads ); - - // Sets the highlight's size and position. In decorator's coords. - // The highlight's height has been calculated above (before 'boxifying' the highlight). - highLightSize.width = maxHighlightX - minHighlightX; - - highLightPosition.x = minHighlightX; - const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() ); - highLightPosition.y = firstSelectionBoxLineInfo.lineOffset; - - mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast( mModel->GetOutlineWidth() ) ); - - if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() ) - { - CursorInfo primaryCursorInfo; - GetCursorPosition( mEventData->mLeftSelectionPosition, - primaryCursorInfo ); - - const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition; - - mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE, - primaryPosition.x, - primaryCursorInfo.lineOffset + mModel->mScrollPosition.y, - primaryCursorInfo.lineHeight ); - - CursorInfo secondaryCursorInfo; - GetCursorPosition( mEventData->mRightSelectionPosition, - secondaryCursorInfo ); - - const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition; - - mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, - secondaryPosition.x, - secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y, - secondaryCursorInfo.lineHeight ); - } - - // Set the flag to update the decorator. - mEventData->mDecoratorUpdated = true; + SelectionHandleController::Reposition(*this); } - void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action ) { - if( NULL == mEventData ) - { - // Nothing to do if there is no text input. - return; - } - - if( IsShowingPlaceholderText() ) - { - // Nothing to do if there is the place-holder text. - return; - } - - const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count(); - const Length numberOfLines = mModel->mVisualModel->mLines.Count(); - if( ( 0 == numberOfGlyphs ) || - ( 0 == numberOfLines ) ) - { - // Nothing to do if there is no text. - return; - } - - // Find which word was selected - CharacterIndex selectionStart( 0 ); - CharacterIndex selectionEnd( 0 ); - CharacterIndex noTextHitIndex( 0 ); - const bool characterHit = FindSelectionIndices( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - visualX, - visualY, - selectionStart, - selectionEnd, - noTextHitIndex ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd ); - - if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) ) - { - ChangeState( EventData::SELECTING ); - - mEventData->mLeftSelectionPosition = selectionStart; - mEventData->mRightSelectionPosition = selectionEnd; - - mEventData->mUpdateLeftSelectionPosition = true; - mEventData->mUpdateRightSelectionPosition = true; - mEventData->mUpdateHighlightBox = true; - - // It may happen an InputMethodContext commit event arrives before the selection event - // if the InputMethodContext is in pre-edit state. The commit event will set the - // mEventData->mUpdateCursorPosition flag to true. If it's not set back - // to false, the highlight box won't be updated. - mEventData->mUpdateCursorPosition = false; - - mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition ); - - // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection - mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition ); - } - else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action ) - { - // Nothing to select. i.e. a white space, out of bounds - ChangeState( EventData::EDITING_WITH_POPUP ); - - mEventData->mPrimaryCursorPosition = noTextHitIndex; - - mEventData->mUpdateCursorPosition = true; - mEventData->mUpdateGrabHandlePosition = true; - mEventData->mScrollAfterUpdatePosition = true; - mEventData->mUpdateInputStyle = true; - } - else if( Controller::NoTextTap::NO_ACTION == action ) - { - // Nothing to select. i.e. a white space, out of bounds - mEventData->mPrimaryCursorPosition = noTextHitIndex; - - mEventData->mUpdateCursorPosition = true; - mEventData->mUpdateGrabHandlePosition = true; - mEventData->mScrollAfterUpdatePosition = true; - mEventData->mUpdateInputStyle = true; - } + SelectionHandleController::Reposition(*this, visualX, visualY, action); } void Controller::Impl::SetPopupButtons() @@ -2645,15 +1356,23 @@ void Controller::Impl::SetPopupButtons() * If EDITING_WITH_POPUP : SELECT & SELECT_ALL */ + bool isEditable = IsEditable(); TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE; if( EventData::SELECTING == mEventData->mState ) { - buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY ); + buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::COPY ); + if(isEditable) + { + buttonsToShow = TextSelectionPopup::Buttons( buttonsToShow | TextSelectionPopup::CUT ); + } if( !IsClipboardEmpty() ) { - buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) ); + if(isEditable) + { + buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) ); + } buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) ); } @@ -2671,7 +1390,10 @@ void Controller::Impl::SetPopupButtons() if( !IsClipboardEmpty() ) { - buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) ); + if(isEditable) + { + buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) ); + } buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) ); } } @@ -2679,7 +1401,10 @@ void Controller::Impl::SetPopupButtons() { if ( !IsClipboardEmpty() ) { - buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) ); + if(isEditable) + { + buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) ); + } buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) ); } } @@ -3116,24 +1841,7 @@ void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo ) void Controller::Impl::UpdateSelectionHandle( HandleType handleType, const CursorInfo& cursorInfo ) { - if( ( LEFT_SELECTION_HANDLE != handleType ) && - ( RIGHT_SELECTION_HANDLE != handleType ) ) - { - return; - } - - const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition; - - // Sets the handle's position. - mEventData->mDecorator->SetPosition( handleType, - cursorPosition.x, - cursorInfo.lineOffset + mModel->mScrollPosition.y, - cursorInfo.lineHeight ); - - // If selection handle at start of the text and other at end of the text then all text is selected. - const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition ); - const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition ); - mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() ); + SelectionHandleController::Update(*this, handleType, cursorInfo); } void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize ) @@ -3241,6 +1949,13 @@ void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo ) ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight ); } +void Controller::Impl::ScrollTextToMatchCursor() +{ + CursorInfo cursorInfo; + GetCursorPosition( mEventData->mPrimaryCursorPosition, cursorInfo ); + ScrollTextToMatchCursor(cursorInfo); +} + void Controller::Impl::RequestRelayout() { if( NULL != mControlInterface )