X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali-toolkit%2Fpublic-api%2Ftext%2Ftext-controller.cpp;h=4582944dbdb4a7a2ec9ff1e393e04e710930a7b8;hb=c9db1395ed95a1ab3f4377b412f9e60c892818f9;hp=6fe0f33832bfd9c0f4d5bc90204112cbf65dba87;hpb=2dd044328238768ae8b27a223cb7d0f5cda53513;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git diff --git a/dali-toolkit/public-api/text/text-controller.cpp b/dali-toolkit/public-api/text/text-controller.cpp index 6fe0f33..4582944 100644 --- a/dali-toolkit/public-api/text/text-controller.cpp +++ b/dali-toolkit/public-api/text/text-controller.cpp @@ -18,18 +18,21 @@ // CLASS HEADER #include -// EXTERNAL INCLUDES -#include - // INTERNAL INCLUDES #include #include #include #include #include +#include +#include #include #include +// EXTERNAL INCLUDES +#include +#include + namespace Dali { @@ -39,10 +42,55 @@ 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; + float mFloat; + }; + + struct Event + { + Event( EventType eventType ) + : type( eventType ) + { + p1.mInt = 0; + p2.mInt = 0; + } + + EventType type; + Param p1; + Param p2; + }; + + TextInput( DecoratorPtr decorator ) + : mDecorator( decorator ) + { + } + + DecoratorPtr mDecorator; + + std::vector mEventQueue; +}; + struct Controller::Impl { - Impl() - : mNewTextArrived( false ) + Impl( ControlInterface& controlInterface ) + : mControlInterface( controlInterface ), + mNewText(), + mOperations( NO_OPERATION ), + mControlSize(), + mTextInput( NULL ) { mLogicalModel = LogicalModel::New(); mVisualModel = VisualModel::New(); @@ -52,8 +100,14 @@ struct Controller::Impl mFontClient = TextAbstraction::FontClient::Get(); } + ~Impl() + { + delete mTextInput; + } + + ControlInterface& mControlInterface; + std::string mNewText; - bool mNewTextArrived; LogicalModelPtr mLogicalModel; VisualModelPtr mVisualModel; @@ -63,67 +117,259 @@ struct Controller::Impl LayoutEngine mLayoutEngine; 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 ) { // Keep until size negotiation mImpl->mNewText = text; - mImpl->mNewTextArrived = true; + 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( decorator ); + } } bool Controller::Relayout( const Vector2& size ) { + if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) ) + { + // Not worth to relayout if width or height is equal to zero. + return false; + } + + bool viewUpdated = false; + + if( size != mImpl->mControlSize ) + { + viewUpdated = DoRelayout( size, mImpl->mOperations ); + + // Do not re-do any operation until something changes. + mImpl->mOperations = NO_OPERATION; + + mImpl->mControlSize = size; + } + + return viewUpdated; +} + +bool Controller::DoRelayout( const Vector2& size, OperationsMask operations ) +{ bool viewUpdated( false ); - if( mImpl->mNewTextArrived ) + Vector utf32Characters; + Length characterCount = 0u; + if( CONVERT_TO_UTF32 & operations ) { std::string& text = mImpl->mNewText; // Convert text into UTF-32 - Vector utf32Characters; utf32Characters.Resize( text.size() ); // This is a bit horrible but std::string returns a (signed) char* const uint8_t* utf8 = reinterpret_cast( text.c_str() ); - Length characterCount = Utf8ToUtf32( utf8, text.size(), &utf32Characters[0] ); + // Transform a text array encoded in utf8 into an array encoded in utf32. + // It returns the actual number of characters. + characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() ); utf32Characters.Resize( characterCount ); - Vector scripts; + // Sets the text into the model. + mImpl->mLogicalModel->SetText( utf32Characters.Begin(), characterCount ); + + // Discard temporary text + 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; + + Vector scripts; + Vector fonts; + if( getScripts || validateFonts ) + { + // Validates the fonts assigned by the application or assigns default ones. + // It makes sure all the characters are going to be rendered by the correct font. MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get(); - multilanguageSupport.SetScripts( utf32Characters, - scripts ); + if( getScripts ) + { + // Retrieves the scripts used in the text. + multilanguageSupport.SetScripts( utf32Characters, + lineBreakInfo, + scripts ); + + // Sets the scripts into the model. + mImpl->mLogicalModel->SetScripts( scripts.Begin(), scripts.Count() ); + } + + if( validateFonts ) + { + // Validates the fonts. If there is a character with no assigned font it sets a default one. + // After this call, fonts are validated. + multilanguageSupport.ValidateFonts( utf32Characters, + scripts, + fonts ); + + // Sets the fonts into the model. + mImpl->mLogicalModel->SetFonts( fonts.Begin(), fonts.Count() ); + } + } + + Vector glyphs; + Vector characterIndices; + Vector charactersPerGlyph; + if( SHAPE_TEXT & operations ) + { + // Shapes the text. + ShapeText( utf32Characters, + lineBreakInfo, + scripts, + fonts, + glyphs, + characterIndices, + charactersPerGlyph ); + } + + if( GET_GLYPH_METRICS & operations ) + { + TextAbstraction::FontClient::Get().GetGlyphMetrics( glyphs.Begin(), glyphs.Count() ); + } - Vector fonts; - multilanguageSupport.ValidateFonts( utf32Characters, - scripts, - fonts ); + if( LAYOUT & operations ) + { + if( 0u == glyphs.Count() ) + { + const Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs(); - // Manipulate the logical model - mImpl->mLogicalModel->SetText( &utf32Characters[0], characterCount ); - mImpl->mLogicalModel->SetScripts( &scripts[0], scripts.Count() ); - mImpl->mLogicalModel->SetFonts( &fonts[0], fonts.Count() ); + glyphs.Resize( numberOfGlyphs ); + characterIndices.Resize( numberOfGlyphs ); + charactersPerGlyph.Resize( numberOfGlyphs ); - // Update the visual model - mImpl->mLayoutEngine.UpdateVisualModel( size, *mImpl->mLogicalModel, *mImpl->mVisualModel ); + mImpl->mVisualModel->GetGlyphs( glyphs.Begin(), + 0u, + numberOfGlyphs ); - // Discard temporary text - mImpl->mNewTextArrived = false; - text.clear(); + mImpl->mVisualModel->GetGlyphToCharacterMap( characterIndices.Begin(), + 0u, + numberOfGlyphs ); + + mImpl->mVisualModel->GetCharactersPerGlyphMap( charactersPerGlyph.Begin(), + 0u, + numberOfGlyphs ); + } + + // Update the visual model + mImpl->mLayoutEngine.UpdateVisualModel( size, + glyphs, + characterIndices, + charactersPerGlyph, + *mImpl->mVisualModel ); viewUpdated = true; } + // TODO - process input events to move grab handle + return viewUpdated; } +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 ); + + 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 ); + + return Vector3( mImpl->mVisualModel->GetNaturalSize() ); +} + +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 ); + + return mImpl->mVisualModel->GetActualSize().height; +} + View& Controller::GetView() { return mImpl->mView; @@ -134,15 +380,74 @@ 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( float x, float y) +{ + DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" ); + + if( mImpl->mTextInput ) + { + TextInput::Event event( TextInput::TAP_EVENT ); + event.p1.mFloat = x; + event.p2.mFloat = y; + + RequestRelayout(); + } +} + +void Controller::GrabHandleEvent( GrabHandleState state, float x ) +{ + DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" ); + + if( mImpl->mTextInput ) + { + TextInput::Event event( TextInput::GRAB_HANDLE_EVENT ); + event.p1.mInt = state; + event.p2.mFloat = x; + + RequestRelayout(); + } +} + Controller::~Controller() { delete mImpl; } -Controller::Controller() +Controller::Controller( ControlInterface& controlInterface ) : mImpl( NULL ) { - mImpl = new Controller::Impl(); + mImpl = new Controller::Impl( controlInterface ); } } // namespace Text