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.cpp;h=3433301ec3e8ba7036bc37e7b68180c7dfaa8830;hp=2b90bc342627c7c860a159d1eef58614fb1cb1ec;hb=a314b4d8d02b983f50b5faafb225f0f3e4593a45;hpb=830f03638ec6ecd3b12ba3d9eb6419fdb3a3db09 diff --git a/dali-toolkit/internal/text/text-controller.cpp b/dali-toolkit/internal/text/text-controller.cpp index 2b90bc3..3433301 100644 --- a/dali-toolkit/internal/text/text-controller.cpp +++ b/dali-toolkit/internal/text/text-controller.cpp @@ -21,6 +21,7 @@ // INTERNAL INCLUDES #include #include +#include #include #include #include @@ -32,10 +33,16 @@ // EXTERNAL INCLUDES #include #include +#include #include using std::vector; +namespace +{ +const float MAX_FLOAT = std::numeric_limits::max(); +} // namespace + namespace Dali { @@ -52,6 +59,7 @@ struct Controller::TextInput { KEYBOARD_FOCUS_GAIN_EVENT, KEYBOARD_FOCUS_LOST_EVENT, + KEY_EVENT, TAP_EVENT, GRAB_HANDLE_EVENT }; @@ -61,6 +69,7 @@ struct Controller::TextInput int mInt; unsigned int mUint; float mFloat; + char* mString; }; struct Event @@ -98,7 +107,7 @@ struct Controller::TextInput /** * @brief Helper to move the cursor, grab handle etc. */ - bool ProcessTouchEvents() + bool ProcessInputEvents() { mDecoratorUpdated = false; @@ -118,6 +127,11 @@ struct Controller::TextInput OnKeyboardFocus( false ); break; } + case KEY_EVENT: + { + OnKeyEvent( *iter ); + break; + } case TAP_EVENT: { OnTapEvent( *iter ); @@ -139,17 +153,65 @@ struct Controller::TextInput void OnKeyboardFocus( bool hasFocus ) { + } + + void OnKeyEvent( const Event& event ) + { + int keyCode = event.p1.mInt; + + // Handle state changes + if( Dali::DALI_KEY_ESCAPE == keyCode ) + { + ChangeState( INACTIVE ); // Escape key ends edit mode + } + else if ( event.p2.mString ) + { + // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case + ChangeState( EDITING ); + } + + // Handle the actual key event + if( Dali::DALI_KEY_BACKSPACE == keyCode ) + { + HandleBackspaceKey(); + } + else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode || + Dali::DALI_KEY_CURSOR_RIGHT == keyCode || + Dali::DALI_KEY_CURSOR_UP == keyCode || + Dali::DALI_KEY_CURSOR_DOWN == keyCode ) + { + HandleCursorKey( keyCode ); + } + else if ( event.p2.mString ) + { + HandleKeyString( event.p2.mString ); + + delete [] event.p2.mString; + } + } + + void HandleBackspaceKey() + { + // TODO + } + + void HandleCursorKey( int keyCode ) + { + // TODO + } + + void HandleKeyString( const char* keyString ) + { // TODO } void OnTapEvent( const Event& event ) { - if( 1u == event.p1.mUint ) + unsigned int tapCount = event.p1.mUint; + + if( 1u == tapCount ) { - mState = TextInput::EDITING; - mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY ); - mDecorator->StartCursorBlink(); - mDecorator->SetGrabHandleActive( true ); + ChangeState( EDITING ); float xPosition = event.p2.mFloat; float yPosition = event.p3.mFloat; @@ -159,12 +221,9 @@ struct Controller::TextInput mDecoratorUpdated = true; } - else if( 2u == event.p1.mUint ) + else if( 2u == tapCount ) { - mState = TextInput::SELECTING; - mDecorator->SetGrabHandleActive( false ); - mDecorator->SetSelectionActive( true ); - mDecoratorUpdated = true; + ChangeState( SELECTING ); } } @@ -185,6 +244,39 @@ struct Controller::TextInput } } + void ChangeState( State newState ) + { + if( mState != newState ) + { + mState = newState; + + if( INACTIVE == mState ) + { + mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE ); + mDecorator->StopCursorBlink(); + mDecorator->SetGrabHandleActive( false ); + mDecorator->SetSelectionActive( false ); + mDecoratorUpdated = true; + } + else if ( SELECTING == mState ) + { + mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE ); + mDecorator->StopCursorBlink(); + mDecorator->SetGrabHandleActive( false ); + mDecorator->SetSelectionActive( true ); + mDecoratorUpdated = true; + } + else if( EDITING == mState ) + { + mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY ); + mDecorator->StartCursorBlink(); + mDecorator->SetGrabHandleActive( true ); + mDecorator->SetSelectionActive( false ); + mDecoratorUpdated = true; + } + } + } + void GetClosestCursorPosition( float& x, float& y, float& height ) { // TODO - Look at LineRuns first @@ -197,19 +289,23 @@ struct Controller::TextInput Vector glyphs; glyphs.Resize( numberOfGlyphs ); - mVisualModel->GetGlyphs( &glyphs[0], 0, numberOfGlyphs ); + mVisualModel->GetGlyphs( glyphs.Begin(), 0, numberOfGlyphs ); + const GlyphInfo* const glyphsBuffer = glyphs.Begin(); - std::vector positions; - positions.resize( numberOfGlyphs ); - mVisualModel->GetGlyphPositions( &positions[0], 0, numberOfGlyphs ); + Vector positions; + positions.Resize( numberOfGlyphs ); + mVisualModel->GetGlyphPositions( positions.Begin(), 0, numberOfGlyphs ); + const Vector2* const positionsBuffer = positions.Begin(); unsigned int closestGlyph = 0; - float closestDistance = std::numeric_limits::max(); + float closestDistance = MAX_FLOAT; - for( unsigned int i=0; i mEventQueue; ///< The queue of touch events etc. + State mState; + bool mDecoratorUpdated; }; +struct Controller::FontDefaults +{ + FontDefaults() + : mDefaultPointSize(0.0f), + mFontId(0u) + { + } + + FontId GetFontId( TextAbstraction::FontClient& fontClient ) + { + if( !mFontId ) + { + Dali::TextAbstraction::PointSize26Dot6 pointSize = mDefaultPointSize*64; + mFontId = fontClient.GetFontId( mDefaultFontFamily, mDefaultFontStyle, pointSize ); + } + + return mFontId; + } + + std::string mDefaultFontFamily; + std::string mDefaultFontStyle; + float mDefaultPointSize; + FontId mFontId; +}; + struct Controller::Impl { Impl( ControlInterface& controlInterface ) : mControlInterface( controlInterface ), + mLogicalModel(), + mVisualModel(), + mFontDefaults( NULL ), + mTextInput( NULL ), + mFontClient(), + mView(), + mLayoutEngine(), mNewText(), - mOperations( NO_OPERATION ), mControlSize(), - mTextInput( NULL ) + mOperationsPending( NO_OPERATION ), + mRecalculateNaturalSize( true ) { mLogicalModel = LogicalModel::New(); mVisualModel = VisualModel::New(); - mView.SetVisualModel( mVisualModel ); - mFontClient = TextAbstraction::FontClient::Get(); + + mView.SetVisualModel( mVisualModel ); } ~Impl() @@ -266,25 +396,18 @@ struct Controller::Impl delete mTextInput; } - ControlInterface& mControlInterface; - - std::string mNewText; - - LogicalModelPtr mLogicalModel; - VisualModelPtr mVisualModel; - - View mView; - - LayoutEngine mLayoutEngine; - - TextAbstraction::FontClient mFontClient; - - OperationsMask mOperations; - - Size mControlSize; - - // Avoid allocating everything for text input until EnableTextInput() - Controller::TextInput* mTextInput; + ControlInterface& mControlInterface; ///< Reference to the text controller. + LogicalModelPtr mLogicalModel; ///< Pointer to the logical model. + VisualModelPtr mVisualModel; ///< Pointer to the visual model. + FontDefaults* mFontDefaults; ///< Avoid allocating this when the user does not specify a font. + Controller::TextInput* mTextInput; ///< Avoid allocating everything for text input until EnableTextInput(). + TextAbstraction::FontClient mFontClient; ///< Handle to the font client. + View mView; ///< The view interface to the rendering back-end. + LayoutEngine mLayoutEngine; ///< The layout engine. + std::string mNewText; ///< Temporary stores the text set until the next relayout. + Size mControlSize; ///< The size of the control. + OperationsMask mOperationsPending; ///< Operations pending to be done to layout the text. + bool mRecalculateNaturalSize:1; ///< Whether the natural size needs to be recalculated. }; ControllerPtr Controller::New( ControlInterface& controlInterface ) @@ -296,7 +419,12 @@ void Controller::SetText( const std::string& text ) { // Keep until size negotiation mImpl->mNewText = text; - mImpl->mOperations = ALL_OPERATIONS; + + // All operations need to be done. (convert to utf32, get break info, ..., layout, ...) + mImpl->mOperationsPending = ALL_OPERATIONS; + + // The natural size needs to be re-calculated. + mImpl->mRecalculateNaturalSize = true; if( mImpl->mTextInput ) { @@ -307,6 +435,103 @@ void Controller::SetText( const std::string& text ) } } +void Controller::GetText( std::string& text ) const +{ + if( !mImpl->mNewText.empty() ) + { + text = mImpl->mNewText; + } + else + { + // TODO - Convert from UTF-32 + } +} + +void Controller::SetPlaceholderText( const std::string& text ) +{ + if( !mImpl->mTextInput ) + { + mImpl->mTextInput->mPlaceholderText = text; + } +} + +void Controller::GetPlaceholderText( std::string& text ) const +{ + if( !mImpl->mTextInput ) + { + text = mImpl->mTextInput->mPlaceholderText; + } +} + +void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily ) +{ + if( !mImpl->mFontDefaults ) + { + mImpl->mFontDefaults = new Controller::FontDefaults(); + } + + mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily; + mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID + mImpl->mOperationsPending = ALL_OPERATIONS; + mImpl->mRecalculateNaturalSize = true; +} + +const std::string& Controller::GetDefaultFontFamily() const +{ + if( mImpl->mFontDefaults ) + { + return mImpl->mFontDefaults->mDefaultFontFamily; + } + + return Dali::String::EMPTY; +} + +void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle ) +{ + if( !mImpl->mFontDefaults ) + { + mImpl->mFontDefaults = new Controller::FontDefaults(); + } + + mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle; + mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID + mImpl->mOperationsPending = ALL_OPERATIONS; + mImpl->mRecalculateNaturalSize = true; +} + +const std::string& Controller::GetDefaultFontStyle() const +{ + if( mImpl->mFontDefaults ) + { + return mImpl->mFontDefaults->mDefaultFontStyle; + } + + return Dali::String::EMPTY; +} + +void Controller::SetDefaultPointSize( float pointSize ) +{ + if( !mImpl->mFontDefaults ) + { + mImpl->mFontDefaults = new Controller::FontDefaults(); + } + + mImpl->mFontDefaults->mDefaultPointSize = pointSize; + mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID + mImpl->mOperationsPending = ALL_OPERATIONS; + mImpl->mRecalculateNaturalSize = true; +} + +float Controller::GetDefaultPointSize() const +{ + if( mImpl->mFontDefaults ) + { + return mImpl->mFontDefaults->mDefaultPointSize; + } + + return 0.0f; +} + void Controller::EnableTextInput( DecoratorPtr decorator ) { if( !mImpl->mTextInput ) @@ -323,31 +548,44 @@ bool Controller::Relayout( const Vector2& size ) return false; } - bool updated = false; - if( size != mImpl->mControlSize ) { - updated = DoRelayout( size, mImpl->mOperations ); - - // Do not re-do any operation until something changes. - mImpl->mOperations = NO_OPERATION; + // Operations that need to be done if the size changes. + mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending | + LAYOUT | + UPDATE_ACTUAL_SIZE | + UPDATE_POSITIONS | + REORDER ); mImpl->mControlSize = size; } + Size layoutSize; + bool updated = DoRelayout( mImpl->mControlSize, + mImpl->mOperationsPending, + layoutSize ); + + // Do not re-do any operation until something changes. + mImpl->mOperationsPending = NO_OPERATION; + if( mImpl->mTextInput ) { // Move the cursor, grab handle etc. - updated = mImpl->mTextInput->ProcessTouchEvents() || updated; + updated = mImpl->mTextInput->ProcessInputEvents() || updated; } return updated; } -bool Controller::DoRelayout( const Vector2& size, OperationsMask operations ) +bool Controller::DoRelayout( const Vector2& size, + OperationsMask operationsRequired, + Size& layoutSize ) { bool viewUpdated( false ); + // Calculate the operations to be done. + const OperationsMask operations = static_cast( mImpl->mOperationsPending & operationsRequired ); + Vector utf32Characters; Length characterCount = 0u; if( CONVERT_TO_UTF32 & operations ) @@ -387,11 +625,29 @@ bool Controller::DoRelayout( const Vector2& size, OperationsMask operations ) mImpl->mLogicalModel->SetLineBreakInfo( lineBreakInfo.Begin(), characterCount ); } + Vector wordBreakInfo; + if( GET_WORD_BREAKS & operations ) + { + // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines). + wordBreakInfo.Resize( characterCount, TextAbstraction::WORD_NO_BREAK ); + + SetWordBreakInfo( utf32Characters, + wordBreakInfo ); + + mImpl->mLogicalModel->SetWordBreakInfo( wordBreakInfo.Begin(), characterCount ); + } + const bool getScripts = GET_SCRIPTS & operations; const bool validateFonts = VALIDATE_FONTS & operations; Vector scripts; Vector fonts; + + if( mImpl->mFontDefaults ) + { + // TODO - pass into ValidateFonts + } + if( getScripts || validateFonts ) { // Validates the fonts assigned by the application or assigns default ones. @@ -423,7 +679,7 @@ bool Controller::DoRelayout( const Vector2& size, OperationsMask operations ) } Vector glyphs; - Vector characterIndices; + Vector glyphsToCharactersMap; Vector charactersPerGlyph; if( SHAPE_TEXT & operations ) { @@ -433,7 +689,7 @@ bool Controller::DoRelayout( const Vector2& size, OperationsMask operations ) scripts, fonts, glyphs, - characterIndices, + glyphsToCharactersMap, charactersPerGlyph ); } @@ -442,21 +698,42 @@ bool Controller::DoRelayout( const Vector2& size, OperationsMask operations ) mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), glyphs.Count() ); } + Length numberOfGlyphs = glyphs.Count(); + if( 0u != numberOfGlyphs ) + { + // Sets the glyphs into the model. + mImpl->mVisualModel->SetGlyphs( glyphs.Begin(), + glyphsToCharactersMap.Begin(), + charactersPerGlyph.Begin(), + numberOfGlyphs ); + } + if( LAYOUT & operations ) { - if( 0u == glyphs.Count() ) + if( 0u == numberOfGlyphs ) { - const Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs(); + const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters(); + numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs(); + lineBreakInfo.Resize( numberOfCharacters ); + wordBreakInfo.Resize( numberOfCharacters ); glyphs.Resize( numberOfGlyphs ); - characterIndices.Resize( numberOfGlyphs ); + glyphsToCharactersMap.Resize( numberOfGlyphs ); charactersPerGlyph.Resize( numberOfGlyphs ); + mImpl->mLogicalModel->GetLineBreakInfo( lineBreakInfo.Begin(), + 0u, + numberOfCharacters ); + + mImpl->mLogicalModel->GetWordBreakInfo( wordBreakInfo.Begin(), + 0u, + numberOfCharacters ); + mImpl->mVisualModel->GetGlyphs( glyphs.Begin(), 0u, numberOfGlyphs ); - mImpl->mVisualModel->GetGlyphToCharacterMap( characterIndices.Begin(), + mImpl->mVisualModel->GetGlyphToCharacterMap( glyphsToCharactersMap.Begin(), 0u, numberOfGlyphs ); @@ -465,14 +742,40 @@ bool Controller::DoRelayout( const Vector2& size, OperationsMask operations ) numberOfGlyphs ); } + // Set the layout parameters. + LayoutParameters layoutParameters( size, + lineBreakInfo.Begin(), + wordBreakInfo.Begin(), + numberOfGlyphs, + glyphs.Begin(), + glyphsToCharactersMap.Begin(), + charactersPerGlyph.Begin() ); + + // Reserve space to set the positions of the glyphs. + Vector glyphPositions; + glyphPositions.Resize( numberOfGlyphs ); + // Update the visual model - mImpl->mLayoutEngine.UpdateVisualModel( size, - glyphs, - characterIndices, - charactersPerGlyph, - *mImpl->mVisualModel ); + viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters, + glyphPositions, + layoutSize ); + + // Sets the positions into the model. + if( UPDATE_POSITIONS & operations ) + { + mImpl->mVisualModel->SetGlyphPositions( glyphPositions.Begin(), + numberOfGlyphs ); + } - viewUpdated = true; + // Sets the actual size. + if( UPDATE_ACTUAL_SIZE & operations ) + { + mImpl->mVisualModel->SetActualSize( layoutSize ); + } + } + else + { + layoutSize = mImpl->mVisualModel->GetActualSize(); } return viewUpdated; @@ -480,59 +783,83 @@ bool Controller::DoRelayout( const Vector2& size, OperationsMask operations ) Vector3 Controller::GetNaturalSize() { - // Operations that can be done only once until the text changes. - const OperationsMask onlyOnceOperations = static_cast( CONVERT_TO_UTF32 | - GET_SCRIPTS | - VALIDATE_FONTS | - GET_LINE_BREAKS | - GET_WORD_BREAKS | - SHAPE_TEXT | - GET_GLYPH_METRICS ); - - // Operations that need to be done if the size or the text changes. - const OperationsMask sizeOperations = static_cast( LAYOUT | - REORDER ); + Vector3 naturalSize; - const float maxFloat = std::numeric_limits::max(); - DoRelayout( Vector2( maxFloat, maxFloat ), - static_cast( onlyOnceOperations | - sizeOperations ) ); - - // Do not do again the only once operations. - mImpl->mOperations = static_cast( mImpl->mOperations & ~onlyOnceOperations ); - - // Do the size related operations again. - mImpl->mOperations = static_cast( mImpl->mOperations | sizeOperations ); + if( mImpl->mRecalculateNaturalSize ) + { + // Operations that can be done only once until the text changes. + const OperationsMask onlyOnceOperations = static_cast( CONVERT_TO_UTF32 | + GET_SCRIPTS | + VALIDATE_FONTS | + GET_LINE_BREAKS | + GET_WORD_BREAKS | + SHAPE_TEXT | + GET_GLYPH_METRICS ); + + // Operations that need to be done if the size changes. + const OperationsMask sizeOperations = static_cast( LAYOUT | + REORDER ); + + DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ), + static_cast( onlyOnceOperations | + sizeOperations ), + naturalSize.GetVectorXY() ); + + // Do not do again the only once operations. + mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending & ~onlyOnceOperations ); + + // Do the size related operations again. + mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending | sizeOperations ); + + // Stores the natural size to avoid recalculate it again + // unless the text/style changes. + mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() ); + + mImpl->mRecalculateNaturalSize = false; + } + else + { + naturalSize = mImpl->mVisualModel->GetNaturalSize(); + } - return Vector3( mImpl->mVisualModel->GetNaturalSize() ); + return naturalSize; } float Controller::GetHeightForWidth( float width ) { - // Operations that can be done only once until the text changes. - const OperationsMask onlyOnceOperations = static_cast( CONVERT_TO_UTF32 | - GET_SCRIPTS | - VALIDATE_FONTS | - GET_LINE_BREAKS | - GET_WORD_BREAKS | - SHAPE_TEXT | - GET_GLYPH_METRICS ); - - // Operations that need to be done if the size or the text changes. - const OperationsMask sizeOperations = static_cast( LAYOUT | - REORDER ); - - DoRelayout( Size( width, 0.f ), - static_cast( onlyOnceOperations | - sizeOperations ) ); - - // Do not do again the only once operations. - mImpl->mOperations = static_cast( mImpl->mOperations & ~onlyOnceOperations ); - - // Do the size related operations again. - mImpl->mOperations = static_cast( mImpl->mOperations | sizeOperations ); + Size layoutSize; + if( width != mImpl->mControlSize.width ) + { + // Operations that can be done only once until the text changes. + const OperationsMask onlyOnceOperations = static_cast( CONVERT_TO_UTF32 | + GET_SCRIPTS | + VALIDATE_FONTS | + GET_LINE_BREAKS | + GET_WORD_BREAKS | + SHAPE_TEXT | + GET_GLYPH_METRICS ); + + // Operations that need to be done if the size changes. + const OperationsMask sizeOperations = static_cast( LAYOUT | + REORDER ); + + DoRelayout( Size( width, MAX_FLOAT ), + static_cast( onlyOnceOperations | + sizeOperations ), + layoutSize ); + + // Do not do again the only once operations. + mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending & ~onlyOnceOperations ); + + // Do the size related operations again. + mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending | sizeOperations ); + } + else + { + layoutSize = mImpl->mVisualModel->GetActualSize(); + } - return mImpl->mVisualModel->GetActualSize().height; + return layoutSize.height; } View& Controller::GetView() @@ -576,6 +903,32 @@ void Controller::KeyboardFocusLostEvent() } } +bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent ) +{ + DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" ); + + if( mImpl->mTextInput ) + { + TextInput::Event event( TextInput::KEY_EVENT ); + event.p1.mInt = keyEvent.keyCode; + event.p2.mString = NULL; + + const std::string& keyString = keyEvent.keyPressed; + if ( !keyString.empty() ) + { + event.p2.mString = new char[keyString.size() + 1]; + std::copy(keyString.begin(), keyString.end(), event.p2.mString); + event.p2.mString[keyString.size()] = '\0'; + } + + mImpl->mTextInput->mEventQueue.push_back( event ); + + RequestRelayout(); + } + + return false; +} + void Controller::TapEvent( unsigned int tapCount, float x, float y ) { DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );