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=dba8386212a74407948668394815d107d7c46b7d;hb=a314b4d8d02b983f50b5faafb225f0f3e4593a45;hpb=beacebbb139c15b44535e3d28c835fc31b412c7c diff --git a/dali-toolkit/internal/text/text-controller.cpp b/dali-toolkit/internal/text/text-controller.cpp index dba8386..3433301 100644 --- a/dali-toolkit/internal/text/text-controller.cpp +++ b/dali-toolkit/internal/text/text-controller.cpp @@ -33,10 +33,16 @@ // EXTERNAL INCLUDES #include #include +#include #include using std::vector; +namespace +{ +const float MAX_FLOAT = std::numeric_limits::max(); +} // namespace + namespace Dali { @@ -53,6 +59,7 @@ struct Controller::TextInput { KEYBOARD_FOCUS_GAIN_EVENT, KEYBOARD_FOCUS_LOST_EVENT, + KEY_EVENT, TAP_EVENT, GRAB_HANDLE_EVENT }; @@ -62,6 +69,7 @@ struct Controller::TextInput int mInt; unsigned int mUint; float mFloat; + char* mString; }; struct Event @@ -99,7 +107,7 @@ struct Controller::TextInput /** * @brief Helper to move the cursor, grab handle etc. */ - bool ProcessTouchEvents() + bool ProcessInputEvents() { mDecoratorUpdated = false; @@ -119,6 +127,11 @@ struct Controller::TextInput OnKeyboardFocus( false ); break; } + case KEY_EVENT: + { + OnKeyEvent( *iter ); + break; + } case TAP_EVENT: { OnTapEvent( *iter ); @@ -140,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; @@ -160,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 ); } } @@ -186,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 @@ -198,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; }; @@ -274,18 +371,24 @@ struct Controller::Impl { Impl( ControlInterface& controlInterface ) : mControlInterface( controlInterface ), + mLogicalModel(), + mVisualModel(), + mFontDefaults( NULL ), + mTextInput( NULL ), + mFontClient(), + mView(), + mLayoutEngine(), mNewText(), - mOperations( NO_OPERATION ), mControlSize(), - mFontDefaults( NULL ), - mTextInput( NULL ) + mOperationsPending( NO_OPERATION ), + mRecalculateNaturalSize( true ) { mLogicalModel = LogicalModel::New(); mVisualModel = VisualModel::New(); - mView.SetVisualModel( mVisualModel ); - mFontClient = TextAbstraction::FontClient::Get(); + + mView.SetVisualModel( mVisualModel ); } ~Impl() @@ -293,28 +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 this when the user does not specify a font - FontDefaults* mFontDefaults; - - // 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 ) @@ -326,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 ) { @@ -349,6 +447,22 @@ void Controller::GetText( std::string& text ) const } } +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 ) @@ -358,7 +472,8 @@ void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily ) mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily; mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID - mImpl->mOperations = ALL_OPERATIONS; + mImpl->mOperationsPending = ALL_OPERATIONS; + mImpl->mRecalculateNaturalSize = true; } const std::string& Controller::GetDefaultFontFamily() const @@ -380,7 +495,8 @@ void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle ) mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle; mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID - mImpl->mOperations = ALL_OPERATIONS; + mImpl->mOperationsPending = ALL_OPERATIONS; + mImpl->mRecalculateNaturalSize = true; } const std::string& Controller::GetDefaultFontStyle() const @@ -402,7 +518,8 @@ void Controller::SetDefaultPointSize( float pointSize ) mImpl->mFontDefaults->mDefaultPointSize = pointSize; mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID - mImpl->mOperations = ALL_OPERATIONS; + mImpl->mOperationsPending = ALL_OPERATIONS; + mImpl->mRecalculateNaturalSize = true; } float Controller::GetDefaultPointSize() const @@ -431,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 ) @@ -477,7 +607,7 @@ bool Controller::DoRelayout( const Vector2& size, OperationsMask operations ) mImpl->mLogicalModel->SetText( utf32Characters.Begin(), characterCount ); // Discard temporary text - //text.clear(); temporary keep the text. will be fixed in the next patch. + text.clear(); } Vector lineBreakInfo; @@ -625,19 +755,27 @@ bool Controller::DoRelayout( const Vector2& size, OperationsMask operations ) Vector glyphPositions; glyphPositions.Resize( numberOfGlyphs ); - Size layoutSize; - // Update the visual model viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters, glyphPositions, layoutSize ); // Sets the positions into the model. - mImpl->mVisualModel->SetGlyphPositions( glyphPositions.Begin(), - numberOfGlyphs ); + if( UPDATE_POSITIONS & operations ) + { + mImpl->mVisualModel->SetGlyphPositions( glyphPositions.Begin(), + numberOfGlyphs ); + } // Sets the actual size. - mImpl->mVisualModel->SetActualSize( layoutSize ); + if( UPDATE_ACTUAL_SIZE & operations ) + { + mImpl->mVisualModel->SetActualSize( layoutSize ); + } + } + else + { + layoutSize = mImpl->mVisualModel->GetActualSize(); } return viewUpdated; @@ -645,62 +783,83 @@ bool Controller::DoRelayout( const Vector2& size, OperationsMask operations ) Vector3 Controller::GetNaturalSize() { - // TODO - Finish implementing - return Vector3::ZERO; + Vector3 naturalSize; + + 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 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 ); - // Operations that need to be done if the size or the text changes. - const OperationsMask sizeOperations = static_cast( LAYOUT | - REORDER ); + DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ), + static_cast( onlyOnceOperations | + sizeOperations ), + naturalSize.GetVectorXY() ); - const float maxFloat = std::numeric_limits::max(); - DoRelayout( Vector2( maxFloat, maxFloat ), - static_cast( onlyOnceOperations | - sizeOperations ) ); + // Do not do again the only once operations. + mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending & ~onlyOnceOperations ); - // Do not do again the only once operations. - mImpl->mOperations = static_cast( mImpl->mOperations & ~onlyOnceOperations ); + // Do the size related operations again. + mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending | sizeOperations ); - // Do the size related operations again. - mImpl->mOperations = static_cast( mImpl->mOperations | sizeOperations ); + // Stores the natural size to avoid recalculate it again + // unless the text/style changes. + mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() ); - return Vector3( mImpl->mVisualModel->GetNaturalSize() ); + mImpl->mRecalculateNaturalSize = false; + } + else + { + naturalSize = 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 ); + 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 or the text changes. - const OperationsMask sizeOperations = static_cast( LAYOUT | - REORDER ); + // Operations that need to be done if the size changes. + const OperationsMask sizeOperations = static_cast( LAYOUT | + REORDER ); - DoRelayout( Size( width, 0.f ), - static_cast( onlyOnceOperations | - sizeOperations ) ); + DoRelayout( Size( width, MAX_FLOAT ), + static_cast( onlyOnceOperations | + sizeOperations ), + layoutSize ); - // Do not do again the only once operations. - mImpl->mOperations = static_cast( mImpl->mOperations & ~onlyOnceOperations ); + // Do not do again the only once operations. + mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending & ~onlyOnceOperations ); - // Do the size related operations again. - mImpl->mOperations = static_cast( mImpl->mOperations | sizeOperations ); + // 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() @@ -744,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" );