X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Fpublic-api%2Ftext%2Ftext-controller.cpp;h=58fd536176d3b972cec91cee5b0cf03b882c95cf;hp=ceae8ca60503d389a44b44d0b77ff41cf15aea8d;hb=1b2bbfd393cdd9008d15304278bc70e5d0ab5764;hpb=b7b42ddbdd89881b50554c31652fca2675662045 diff --git a/dali-toolkit/public-api/text/text-controller.cpp b/dali-toolkit/public-api/text/text-controller.cpp index ceae8ca..58fd536 100644 --- a/dali-toolkit/public-api/text/text-controller.cpp +++ b/dali-toolkit/public-api/text/text-controller.cpp @@ -24,13 +24,17 @@ #include #include #include +#include #include #include #include // EXTERNAL INCLUDES -#include #include +#include +#include + +using std::vector; namespace Dali { @@ -41,11 +45,213 @@ namespace Toolkit namespace Text { +struct Controller::TextInput +{ + // Used to queue input events until DoRelayout() + enum EventType + { + KEYBOARD_FOCUS_GAIN_EVENT, + KEYBOARD_FOCUS_LOST_EVENT, + TAP_EVENT, + GRAB_HANDLE_EVENT + }; + + union Param + { + int mInt; + unsigned int mUint; + float mFloat; + }; + + struct Event + { + Event( EventType eventType ) + : type( eventType ) + { + p1.mInt = 0; + p2.mInt = 0; + } + + EventType type; + Param p1; + Param p2; + Param p3; + }; + + enum State + { + INACTIVE, + SELECTING, + EDITING + }; + + TextInput( LogicalModelPtr logicalModel, + VisualModelPtr visualModel, + DecoratorPtr decorator ) + : mLogicalModel( logicalModel ), + mVisualModel( visualModel ), + mDecorator( decorator ), + mState( INACTIVE ) + { + } + + /** + * @brief Helper to move the cursor, grab handle etc. + */ + bool ProcessTouchEvents() + { + mDecoratorUpdated = false; + + if( mDecorator ) + { + for( vector::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter ) + { + switch( iter->type ) + { + case KEYBOARD_FOCUS_GAIN_EVENT: + { + OnKeyboardFocus( true ); + break; + } + case KEYBOARD_FOCUS_LOST_EVENT: + { + OnKeyboardFocus( false ); + break; + } + case TAP_EVENT: + { + OnTapEvent( *iter ); + break; + } + case GRAB_HANDLE_EVENT: + { + OnGrabHandleEvent( *iter ); + break; + } + } + } + } + + mEventQueue.clear(); + + return mDecoratorUpdated; + } + + void OnKeyboardFocus( bool hasFocus ) + { + // TODO + } + + void OnTapEvent( const Event& event ) + { + if( 1u == event.p1.mUint ) + { + mState = TextInput::EDITING; + mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY ); + mDecorator->StartCursorBlink(); + mDecorator->SetGrabHandleActive( true ); + + float xPosition = event.p2.mFloat; + float yPosition = event.p3.mFloat; + float height(0.0f); + GetClosestCursorPosition( xPosition, yPosition, height ); + mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height ); + + mDecoratorUpdated = true; + } + else if( 2u == event.p1.mUint ) + { + mState = TextInput::SELECTING; + mDecorator->SetGrabHandleActive( false ); + mDecorator->SetSelectionActive( true ); + mDecoratorUpdated = true; + } + } + + void OnGrabHandleEvent( const Event& event ) + { + unsigned int state = event.p1.mUint; + + if( GRAB_HANDLE_PRESSED == state ) + { + float xPosition = event.p2.mFloat; + float yPosition = event.p3.mFloat; + float height(0.0f); + + GetClosestCursorPosition( xPosition, yPosition, height ); + + mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height ); + mDecoratorUpdated = true; + } + } + + void GetClosestCursorPosition( float& x, float& y, float& height ) + { + // TODO - Look at LineRuns first + + Text::Length numberOfGlyphs = mVisualModel->GetNumberOfGlyphs(); + if( 0 == numberOfGlyphs ) + { + return; + } + + Vector glyphs; + glyphs.Resize( numberOfGlyphs ); + mVisualModel->GetGlyphs( &glyphs[0], 0, numberOfGlyphs ); + + std::vector positions; + positions.resize( numberOfGlyphs ); + mVisualModel->GetGlyphPositions( &positions[0], 0, numberOfGlyphs ); + + unsigned int closestGlyph = 0; + float closestDistance = std::numeric_limits::max(); + + for( unsigned int i=0; i mEventQueue; ///< The queue of touch events etc. + + bool mDecoratorUpdated; +}; + struct Controller::Impl { - Impl() - : mNewText(), - mOperations( NO_OPERATION ) + Impl( ControlInterface& controlInterface ) + : mControlInterface( controlInterface ), + mNewText(), + mOperations( NO_OPERATION ), + mControlSize(), + mTextInput( NULL ) { mLogicalModel = LogicalModel::New(); mVisualModel = VisualModel::New(); @@ -55,6 +261,13 @@ struct Controller::Impl mFontClient = TextAbstraction::FontClient::Get(); } + ~Impl() + { + delete mTextInput; + } + + ControlInterface& mControlInterface; + std::string mNewText; LogicalModelPtr mLogicalModel; @@ -67,11 +280,16 @@ struct Controller::Impl TextAbstraction::FontClient mFontClient; OperationsMask mOperations; + + Size mControlSize; + + // Avoid allocating everything for text input until EnableTextInput() + Controller::TextInput* mTextInput; }; -ControllerPtr Controller::New() +ControllerPtr Controller::New( ControlInterface& controlInterface ) { - return ControllerPtr( new Controller() ); + return ControllerPtr( new Controller( controlInterface ) ); } void Controller::SetText( const std::string& text ) @@ -79,6 +297,22 @@ void Controller::SetText( const std::string& text ) // Keep until size negotiation mImpl->mNewText = text; mImpl->mOperations = ALL_OPERATIONS; + + if( mImpl->mTextInput ) + { + // Cancel previously queued events + mImpl->mTextInput->mEventQueue.clear(); + + // TODO - Hide selection decorations + } +} + +void Controller::EnableTextInput( DecoratorPtr decorator ) +{ + if( !mImpl->mTextInput ) + { + mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator ); + } } bool Controller::Relayout( const Vector2& size ) @@ -89,19 +323,25 @@ bool Controller::Relayout( const Vector2& size ) return false; } - bool viewUpdated = false; + bool updated = false; - if( size != mControlSize ) + if( size != mImpl->mControlSize ) { - viewUpdated = DoRelayout( size, mImpl->mOperations ); + updated = DoRelayout( size, mImpl->mOperations ); // Do not re-do any operation until something changes. mImpl->mOperations = NO_OPERATION; - mControlSize = size; + mImpl->mControlSize = size; } - return viewUpdated; + if( mImpl->mTextInput ) + { + // Move the cursor, grab handle etc. + updated = mImpl->mTextInput->ProcessTouchEvents() || updated; + } + + return updated; } bool Controller::DoRelayout( const Vector2& size, OperationsMask operations ) @@ -132,6 +372,21 @@ bool Controller::DoRelayout( const Vector2& size, OperationsMask operations ) text.clear(); } + Vector lineBreakInfo; + if( GET_LINE_BREAKS & operations ) + { + // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to + // calculate the bidirectional info for each 'paragraph'. + // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines + // is not shaped together). + lineBreakInfo.Resize( characterCount, TextAbstraction::LINE_NO_BREAK ); + + SetLineBreakInfo( utf32Characters, + lineBreakInfo ); + + mImpl->mLogicalModel->SetLineBreakInfo( lineBreakInfo.Begin(), characterCount ); + } + const bool getScripts = GET_SCRIPTS & operations; const bool validateFonts = VALIDATE_FONTS & operations; @@ -147,6 +402,7 @@ bool Controller::DoRelayout( const Vector2& size, OperationsMask operations ) { // Retrieves the scripts used in the text. multilanguageSupport.SetScripts( utf32Characters, + lineBreakInfo, scripts ); // Sets the scripts into the model. @@ -166,17 +422,6 @@ bool Controller::DoRelayout( const Vector2& size, OperationsMask operations ) } } - Vector lineBreakInfo; - if( GET_LINE_BREAKS & operations ) - { - // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to - // calculate the bidirectional info for each 'paragraph'. - // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines - // is not shaped together). - lineBreakInfo.Resize( characterCount, TextAbstraction::LINE_NO_BREAK ); - mImpl->mLogicalModel->SetLineBreakInfo( lineBreakInfo.Begin(), characterCount ); - } - Vector glyphs; Vector characterIndices; Vector charactersPerGlyph; @@ -194,7 +439,7 @@ bool Controller::DoRelayout( const Vector2& size, OperationsMask operations ) if( GET_GLYPH_METRICS & operations ) { - TextAbstraction::FontClient::Get().GetGlyphMetrics( glyphs.Begin(), glyphs.Count() ); + mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), glyphs.Count() ); } if( LAYOUT & operations ) @@ -210,7 +455,7 @@ bool Controller::DoRelayout( const Vector2& size, OperationsMask operations ) mImpl->mVisualModel->GetGlyphs( glyphs.Begin(), 0u, numberOfGlyphs ); - + mImpl->mVisualModel->GetGlyphToCharacterMap( characterIndices.Begin(), 0u, numberOfGlyphs ); @@ -300,16 +545,78 @@ LayoutEngine& Controller::GetLayoutEngine() return mImpl->mLayoutEngine; } +void Controller::RequestRelayout() +{ + mImpl->mControlInterface.RequestTextRelayout(); +} + +void Controller::KeyboardFocusGainEvent() +{ + DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" ); + + if( mImpl->mTextInput ) + { + TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT ); + mImpl->mTextInput->mEventQueue.push_back( event ); + + RequestRelayout(); + } +} + +void Controller::KeyboardFocusLostEvent() +{ + DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" ); + + if( mImpl->mTextInput ) + { + TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT ); + mImpl->mTextInput->mEventQueue.push_back( event ); + + RequestRelayout(); + } +} + +void Controller::TapEvent( unsigned int tapCount, float x, float y ) +{ + DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" ); + + if( mImpl->mTextInput ) + { + TextInput::Event event( TextInput::TAP_EVENT ); + event.p1.mUint = tapCount; + event.p2.mFloat = x; + event.p3.mFloat = y; + mImpl->mTextInput->mEventQueue.push_back( event ); + + RequestRelayout(); + } +} + +void Controller::GrabHandleEvent( GrabHandleState state, float x, float y ) +{ + DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" ); + + if( mImpl->mTextInput ) + { + TextInput::Event event( TextInput::GRAB_HANDLE_EVENT ); + event.p1.mUint = state; + event.p2.mFloat = x; + event.p3.mFloat = y; + mImpl->mTextInput->mEventQueue.push_back( event ); + + RequestRelayout(); + } +} + Controller::~Controller() { delete mImpl; } -Controller::Controller() -: mImpl( NULL ), - mControlSize() +Controller::Controller( ControlInterface& controlInterface ) +: mImpl( NULL ) { - mImpl = new Controller::Impl(); + mImpl = new Controller::Impl( controlInterface ); } } // namespace Text