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=fef0f70f14232ea1a026dd8be3437e7bb43e53ee;hp=85f48ec7ee65404a3fbe1c1ca92598080d9fabd2;hb=a827febe27e8131e681c24c255472bbbf097905f;hpb=a68dbe03813fd3db34c81f4e6960572e8882930b diff --git a/dali-toolkit/internal/text/text-controller-impl.cpp b/dali-toolkit/internal/text/text-controller-impl.cpp index 85f48ec..fef0f70 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) 2016 Samsung Electronics Co., Ltd. + * Copyright (c) 2017 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. @@ -70,6 +70,7 @@ namespace Text EventData::EventData( DecoratorPtr decorator ) : mDecorator( decorator ), mImfManager(), + mPlaceholderFont( NULL ), mPlaceholderTextActive(), mPlaceholderTextInactive(), mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ), @@ -83,6 +84,8 @@ EventData::EventData( DecoratorPtr decorator ) mPreEditStartPosition( 0u ), mPreEditLength( 0u ), mCursorHookPositionX( 0.f ), + mDoubleTapAction( Controller::NoTextTap::NO_ACTION ), + mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ), mIsShowingPlaceholderText( false ), mPreEditFlag( false ), mDecoratorUpdated( false ), @@ -96,11 +99,14 @@ EventData::EventData( DecoratorPtr decorator ) mUpdateLeftSelectionPosition( false ), mUpdateRightSelectionPosition( false ), mIsLeftHandleSelected( false ), + mIsRightHandleSelected( false ), mUpdateHighlightBox( false ), mScrollAfterUpdatePosition( false ), mScrollAfterDelete( false ), mAllTextSelected( false ), - mUpdateInputStyle( false ) + mUpdateInputStyle( false ), + mPasswordInput( false ), + mIsPlaceholderPixelSize( false ) { mImfManager = ImfManager::Get(); } @@ -225,10 +231,25 @@ bool Controller::Impl::ProcessInputEvents() if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) ) { - CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo; + 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 currentCursorPosition( info.primaryPosition.x, info.lineOffset ); - ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight ); + 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 ); + } } } @@ -259,6 +280,8 @@ bool Controller::Impl::ProcessInputEvents() mEventData->mUpdateLeftSelectionPosition = false; mEventData->mUpdateRightSelectionPosition = false; mEventData->mUpdateHighlightBox = false; + mEventData->mIsLeftHandleSelected = false; + mEventData->mIsRightHandleSelected = false; } mEventData->mScrollAfterUpdatePosition = false; @@ -767,8 +790,16 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) return false; } - Vector& utf32Characters = mModel->mLogicalModel->mText; + Vector& srcCharacters = mModel->mLogicalModel->mText; + Vector displayCharacters; + bool useHiddenText = false; + if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText) + { + mHiddenInput->Substitute( srcCharacters,displayCharacters ); + useHiddenText = true; + } + Vector& utf32Characters = useHiddenText ? displayCharacters : srcCharacters; const Length numberOfCharacters = utf32Characters.Count(); // Index to the first character of the first paragraph to be updated. @@ -854,8 +885,19 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) // Get the default font's description. TextAbstraction::FontDescription defaultFontDescription; TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE; - if( NULL != mFontDefaults ) + + if( IsShowingPlaceholderText() && ( NULL != mEventData->mPlaceholderFont ) ) + { + // If the placeholder font is set specifically, only placeholder font is changed. + defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription; + if( mEventData->mPlaceholderFont->sizeDefined ) + { + defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u; + } + } + else if( NULL != mFontDefaults ) { + // Set the normal font and the placeholder font. defaultFontDescription = mFontDefaults->mFontDescription; defaultPointSize = mFontDefaults->mDefaultPointSize * 64u; } @@ -1133,29 +1175,30 @@ void Controller::Impl::OnCursorKeyEvent( const Event& event ) } const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex ); + const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex ); - if( lineIndex > 0u ) - { - // Retrieve the cursor position info. - CursorInfo cursorInfo; - GetCursorPosition( mEventData->mPrimaryCursorPosition, - cursorInfo ); + // Retrieve the cursor position info. + CursorInfo cursorInfo; + GetCursorPosition( mEventData->mPrimaryCursorPosition, + cursorInfo ); - // Get the line above. - const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + ( lineIndex - 1u ) ); + // 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 ); + // 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. - mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - mEventData->mCursorHookPositionX, - hitPointY ); - } + // 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 ) + else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode ) { // Get first the line index of the current cursor position index. CharacterIndex characterIndex = 0u; @@ -1181,11 +1224,14 @@ void Controller::Impl::OnCursorKeyEvent( const Event& event ) 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 ); + hitPointY, + CharacterHitTest::TAP, + matchedCharacter ); } } @@ -1211,11 +1257,15 @@ void Controller::Impl::OnTapEvent( const Event& event ) // 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 ); + yPosition, + CharacterHitTest::TAP, + matchedCharacter ); // When the cursor position is changing, delay cursor blinking mEventData->mDecorator->DelayCursorBlink(); @@ -1237,6 +1287,20 @@ void Controller::Impl::OnTapEvent( const Event& event ) mEventData->mImfManager.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 ); + } + } } } @@ -1307,12 +1371,26 @@ void Controller::Impl::OnLongPressEvent( const Event& event ) { DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" ); - if( EventData::EDITING == mEventData->mState ) + if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) ) { - ChangeState ( EventData::EDITING_WITH_POPUP ); + ChangeState( EventData::EDITING_WITH_POPUP ); mEventData->mDecoratorUpdated = true; mEventData->mUpdateInputStyle = true; } + else + { + 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 ); + } + } } void Controller::Impl::OnHandleEvent( const Event& event ) @@ -1334,11 +1412,14 @@ void Controller::Impl::OnHandleEvent( const Event& event ) const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; // Need to calculate the handle's new position. + bool matchedCharacter = false; const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel, mModel->mLogicalModel, mMetrics, xPosition, - yPosition ); + yPosition, + CharacterHitTest::SCROLL, + matchedCharacter ); if( Event::GRAB_HANDLE_EVENT == event.type ) { @@ -1375,6 +1456,7 @@ void Controller::Impl::OnHandleEvent( const Event& event ) // 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 ) { @@ -1395,6 +1477,7 @@ void Controller::Impl::OnHandleEvent( const Event& event ) // 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 ) || @@ -1407,11 +1490,14 @@ void Controller::Impl::OnHandleEvent( const Event& event ) 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 ); + yPosition, + CharacterHitTest::SCROLL, + matchedCharacter ); } if( Event::GRAB_HANDLE_EVENT == event.type ) @@ -1523,11 +1609,14 @@ void Controller::Impl::OnHandleEvent( const Event& event ) // 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 ); + position.y - mModel->mScrollPosition.y, + CharacterHitTest::SCROLL, + matchedCharacter ); if( mEventData->mPrimaryCursorPosition != handlePosition ) { @@ -1564,11 +1653,14 @@ void Controller::Impl::OnHandleEvent( const Event& event ) // 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 ); + position.y - mModel->mScrollPosition.y, + CharacterHitTest::SCROLL, + matchedCharacter ); if( leftSelectionHandleEvent ) { @@ -1621,7 +1713,8 @@ void Controller::Impl::OnSelectEvent( const Event& event ) // Calculates the logical position from the x,y coords. RepositionSelectionHandles( xPosition, - yPosition ); + yPosition, + Controller::NoTextTap::HIGHLIGHT ); } } @@ -1695,8 +1788,18 @@ void Controller::Impl::RetrieveSelection( std::string& selectedText, bool delete mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast( lengthOfSelectedText ) ); // Mark the paragraphs to be updated. - mTextUpdateInfo.mCharacterIndex = startOfSelectedText; - mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText; + if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() ) + { + mTextUpdateInfo.mCharacterIndex = 0; + mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters; + mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText; + mTextUpdateInfo.mClearAll = true; + } + else + { + mTextUpdateInfo.mCharacterIndex = startOfSelectedText; + mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText; + } // Delete text between handles Vector::Iterator first = utf32Characters.Begin() + startOfSelectedText; @@ -1946,15 +2049,15 @@ void Controller::Impl::RepositionSelectionHandles() // Whether to retrieve the next line. if( index == lastGlyphOfLine ) { - // Retrieve the next line. - ++lineRun; - - // Get the last glyph of the new line. - lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u; - ++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; @@ -2152,7 +2255,7 @@ void Controller::Impl::RepositionSelectionHandles() mEventData->mDecoratorUpdated = true; } -void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY ) +void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action ) { if( NULL == mEventData ) { @@ -2178,16 +2281,18 @@ void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY // Find which word was selected CharacterIndex selectionStart( 0 ); CharacterIndex selectionEnd( 0 ); - const bool indicesFound = FindSelectionIndices( mModel->mVisualModel, + CharacterIndex noTextHitIndex( 0 ); + const bool characterHit = FindSelectionIndices( mModel->mVisualModel, mModel->mLogicalModel, mMetrics, visualX, visualY, selectionStart, - selectionEnd ); + selectionEnd, + noTextHitIndex ); DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd ); - if( indicesFound ) + if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) ) { ChangeState( EventData::SELECTING ); @@ -2198,14 +2303,30 @@ void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY mEventData->mUpdateRightSelectionPosition = true; mEventData->mUpdateHighlightBox = true; + // It may happen an IMF commit event arrives before the selection event + // if the IMF manager 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 ); } - else + else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action ) { // Nothing to select. i.e. a white space, out of bounds - ChangeState( EventData::EDITING ); + ChangeState( EventData::EDITING_WITH_POPUP ); - mEventData->mPrimaryCursorPosition = selectionEnd; + 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; @@ -2509,13 +2630,18 @@ void Controller::Impl::GetCursorPosition( CharacterIndex logical, return; } - Text::GetCursorPosition( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - logical, + const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() ); + GetCursorPositionParameters parameters; + parameters.visualModel = mModel->mVisualModel; + parameters.logicalModel = mModel->mLogicalModel; + parameters.metrics = mMetrics; + parameters.logical = logical; + parameters.isMultiline = isMultiLine; + + Text::GetCursorPosition( parameters, cursorInfo ); - if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() ) + if( isMultiLine ) { // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control. @@ -2556,7 +2682,7 @@ CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const Script script = mModel->mLogicalModel->GetScript( index ); if( HasLigatureMustBreak( script ) ) { - // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ... + // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ... numberOfCharacters = 1u; } } @@ -2596,6 +2722,8 @@ void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo ) const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition; + mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset ); + // Sets the cursor position. mEventData->mDecorator->SetPosition( PRIMARY_CURSOR, cursorPosition.x, @@ -2668,13 +2796,13 @@ void Controller::Impl::UpdateSelectionHandle( HandleType handleType, void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize ) { - // Clamp between -space & 0. + // Clamp between -space & -alignment offset. if( layoutSize.width > mModel->mVisualModel->mControlSize.width ) { - const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ); + const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset; mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x; - mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > 0.f ) ? 0.f : mModel->mScrollPosition.x; + mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x; mEventData->mDecoratorUpdated = true; } @@ -2731,13 +2859,16 @@ void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, flo mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX; } - if( decoratorPositionBeginY < 0.f ) - { - mModel->mScrollPosition.y = -position.y; - } - else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height ) + if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() ) { - mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY; + if( decoratorPositionBeginY < 0.f ) + { + mModel->mScrollPosition.y = -position.y; + } + else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height ) + { + mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY; + } } } @@ -2746,9 +2877,20 @@ void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo ) // Get the current cursor position in decorator coords. const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR ); + const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition ); + + + // Calculate the offset to match the cursor position before the character was deleted. mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x; - mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset; + + //If text control has more than two lines and current line index is not last, calculate scrollpositionY + if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u ) + { + const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR ); + mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset; + } + ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() ); ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );