2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller.h>
24 #include <dali/public-api/adaptor-framework/key.h>
25 #include <dali/public-api/text-abstraction/font-client.h>
28 #include <dali-toolkit/internal/text/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/character-set-conversion.h>
30 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
31 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
32 #include <dali-toolkit/internal/text/logical-model-impl.h>
33 #include <dali-toolkit/internal/text/multi-language-support.h>
34 #include <dali-toolkit/internal/text/script-run.h>
35 #include <dali-toolkit/internal/text/segmentation.h>
36 #include <dali-toolkit/internal/text/shaper.h>
37 #include <dali-toolkit/internal/text/text-io.h>
38 #include <dali-toolkit/internal/text/text-view.h>
39 #include <dali-toolkit/internal/text/visual-model-impl.h>
46 const float MAX_FLOAT = std::numeric_limits<float>::max();
47 const std::string EMPTY_STRING;
51 REPLACE_TEXT, ///< Replace the entire text
52 INSERT_TEXT, ///< Insert characters at the current cursor position
53 DELETE_TEXT ///< Delete a character at the current cursor position
73 struct Controller::TextInput
75 // Used to queue input events until DoRelayout()
78 KEYBOARD_FOCUS_GAIN_EVENT,
79 KEYBOARD_FOCUS_LOST_EVENT,
94 Event( EventType eventType )
114 TextInput( LogicalModelPtr logicalModel,
115 VisualModelPtr visualModel,
116 DecoratorPtr decorator )
117 : mLogicalModel( logicalModel ),
118 mVisualModel( visualModel ),
119 mDecorator( decorator ),
121 mPrimaryCursorPosition( 0u ),
122 mSecondaryCursorPosition( 0u ),
123 mDecoratorUpdated( false ),
124 mCursorBlinkEnabled( true )
129 * @brief Helper to move the cursor, grab handle etc.
131 bool ProcessInputEvents()
133 mDecoratorUpdated = false;
137 for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
141 case KEYBOARD_FOCUS_GAIN_EVENT:
143 OnKeyboardFocus( true );
146 case KEYBOARD_FOCUS_LOST_EVENT:
148 OnKeyboardFocus( false );
151 case CURSOR_KEY_EVENT:
153 OnCursorKeyEvent( *iter );
161 case GRAB_HANDLE_EVENT:
163 OnGrabHandleEvent( *iter );
172 return mDecoratorUpdated;
175 void OnKeyboardFocus( bool hasFocus )
179 ChangeState( INACTIVE );
183 ChangeState( EDITING );
187 void OnCursorKeyEvent( const Event& event )
189 int keyCode = event.p1.mInt;
191 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
195 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
199 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
203 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
209 void HandleBackspaceKey()
214 void HandleCursorKey( int keyCode )
219 void HandleKeyString( const char* keyString )
224 void OnTapEvent( const Event& event )
226 unsigned int tapCount = event.p1.mUint;
230 ChangeState( EDITING );
232 float xPosition = event.p2.mFloat;
233 float yPosition = event.p3.mFloat;
235 GetClosestCursorPosition( xPosition, yPosition, height );
236 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
238 mDecoratorUpdated = true;
240 else if( 2u == tapCount )
242 ChangeState( SELECTING );
246 void OnGrabHandleEvent( const Event& event )
248 unsigned int state = event.p1.mUint;
250 if( GRAB_HANDLE_PRESSED == state )
252 float xPosition = event.p2.mFloat;
253 float yPosition = event.p3.mFloat;
256 GetClosestCursorPosition( xPosition, yPosition, height );
258 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
259 mDecorator->HidePopup();
260 mDecoratorUpdated = true;
262 else if ( GRAB_HANDLE_RELEASED == state )
264 mDecorator->ShowPopup();
269 void ChangeState( State newState )
271 if( mState != newState )
275 if( INACTIVE == mState )
277 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
278 mDecorator->StopCursorBlink();
279 mDecorator->SetGrabHandleActive( false );
280 mDecorator->SetSelectionActive( false );
281 mDecorator->HidePopup();
282 mDecoratorUpdated = true;
284 else if ( SELECTING == mState )
286 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
287 mDecorator->StopCursorBlink();
288 mDecorator->SetGrabHandleActive( false );
289 mDecorator->SetSelectionActive( true );
290 mDecoratorUpdated = true;
292 else if( EDITING == mState )
294 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
295 if( mCursorBlinkEnabled )
297 mDecorator->StartCursorBlink();
299 mDecorator->SetGrabHandleActive( true );
300 mDecorator->SetSelectionActive( false );
301 mDecoratorUpdated = true;
306 void GetClosestCursorPosition( float& x, float& y, float& height )
308 // TODO - Look at LineRuns first
310 Text::Length numberOfGlyphs = mVisualModel->GetNumberOfGlyphs();
311 if( 0 == numberOfGlyphs )
316 Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
317 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
319 Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
320 const Vector2* const positionsBuffer = positions.Begin();
322 unsigned int closestGlyph = 0;
323 float closestDistance = MAX_FLOAT;
325 for( unsigned int i = 0, numberOfGLyphs = glyphs.Count(); i < numberOfGLyphs; ++i )
327 const GlyphInfo& glyphInfo = *( glyphsBuffer + i );
328 const Vector2& position = *( positionsBuffer + i );
329 float glyphX = position.x + glyphInfo.width*0.5f;
330 float glyphY = position.y + glyphInfo.height*0.5f;
332 float distanceToGlyph = fabsf( glyphX - x ) + fabsf( glyphY - y );
334 if( distanceToGlyph < closestDistance )
336 closestDistance = distanceToGlyph;
341 // TODO - Consider RTL languages
342 x = positions[closestGlyph].x + glyphs[closestGlyph].width;
346 TextAbstraction::FontClient::Get().GetFontMetrics( glyphs[closestGlyph].fontId, metrics );
347 height = metrics.height; // TODO - Fix for multi-line
350 LogicalModelPtr mLogicalModel;
351 VisualModelPtr mVisualModel;
352 DecoratorPtr mDecorator;
354 std::string mPlaceholderText;
357 * This is used to delay handling events until after the model has been updated.
358 * The number of updates to the model is minimized to improve performance.
360 vector<Event> mEventQueue; ///< The queue of touch events etc.
362 State mState; ///< Selection mode, edit mode etc.
364 CharacterIndex mPrimaryCursorPosition; ///< Index into logical model for primary cursor
365 CharacterIndex mSecondaryCursorPosition; ///< Index into logical model for secondary cursor
367 bool mDecoratorUpdated : 1; ///< True if the decorator was updated during event processing
368 bool mCursorBlinkEnabled : 1; ///< True if cursor should blink when active
371 struct Controller::FontDefaults
374 : mDefaultPointSize(0.0f),
379 FontId GetFontId( TextAbstraction::FontClient& fontClient )
383 Dali::TextAbstraction::PointSize26Dot6 pointSize = mDefaultPointSize*64;
384 mFontId = fontClient.GetFontId( mDefaultFontFamily, mDefaultFontStyle, pointSize );
390 std::string mDefaultFontFamily;
391 std::string mDefaultFontStyle;
392 float mDefaultPointSize;
396 struct Controller::Impl
398 Impl( ControlInterface& controlInterface )
399 : mControlInterface( controlInterface ),
402 mFontDefaults( NULL ),
409 mOperationsPending( NO_OPERATION ),
410 mRecalculateNaturalSize( true )
412 mLogicalModel = LogicalModel::New();
413 mVisualModel = VisualModel::New();
415 mFontClient = TextAbstraction::FontClient::Get();
417 mView.SetVisualModel( mVisualModel );
425 ControlInterface& mControlInterface; ///< Reference to the text controller.
426 LogicalModelPtr mLogicalModel; ///< Pointer to the logical model.
427 VisualModelPtr mVisualModel; ///< Pointer to the visual model.
428 FontDefaults* mFontDefaults; ///< Avoid allocating this when the user does not specify a font.
429 Controller::TextInput* mTextInput; ///< Avoid allocating everything for text input until EnableTextInput().
430 TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
431 View mView; ///< The view interface to the rendering back-end.
432 LayoutEngine mLayoutEngine; ///< The layout engine.
433 std::vector<ModifyEvent> mModifyEvents; ///< Temporary stores the text set until the next relayout.
434 Size mControlSize; ///< The size of the control.
435 OperationsMask mOperationsPending; ///< Operations pending to be done to layout the text.
436 bool mRecalculateNaturalSize:1; ///< Whether the natural size needs to be recalculated.
439 ControllerPtr Controller::New( ControlInterface& controlInterface )
441 return ControllerPtr( new Controller( controlInterface ) );
444 void Controller::SetText( const std::string& text )
446 // Cancel previously queued inserts etc.
447 mImpl->mModifyEvents.clear();
449 // Keep until size negotiation
451 event.type = REPLACE_TEXT;
453 mImpl->mModifyEvents.push_back( event );
455 if( mImpl->mTextInput )
457 // Cancel previously queued events
458 mImpl->mTextInput->mEventQueue.clear();
460 // TODO - Hide selection decorations
464 void Controller::GetText( std::string& text ) const
466 if( !mImpl->mModifyEvents.empty() &&
467 REPLACE_TEXT == mImpl->mModifyEvents[0].type )
469 text = mImpl->mModifyEvents[0].text;
473 // TODO - Convert from UTF-32
477 void Controller::SetPlaceholderText( const std::string& text )
479 if( !mImpl->mTextInput )
481 mImpl->mTextInput->mPlaceholderText = text;
485 void Controller::GetPlaceholderText( std::string& text ) const
487 if( !mImpl->mTextInput )
489 text = mImpl->mTextInput->mPlaceholderText;
493 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
495 if( !mImpl->mFontDefaults )
497 mImpl->mFontDefaults = new Controller::FontDefaults();
500 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
501 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
502 mImpl->mOperationsPending = ALL_OPERATIONS;
503 mImpl->mRecalculateNaturalSize = true;
506 const std::string& Controller::GetDefaultFontFamily() const
508 if( mImpl->mFontDefaults )
510 return mImpl->mFontDefaults->mDefaultFontFamily;
516 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
518 if( !mImpl->mFontDefaults )
520 mImpl->mFontDefaults = new Controller::FontDefaults();
523 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
524 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
525 mImpl->mOperationsPending = ALL_OPERATIONS;
526 mImpl->mRecalculateNaturalSize = true;
529 const std::string& Controller::GetDefaultFontStyle() const
531 if( mImpl->mFontDefaults )
533 return mImpl->mFontDefaults->mDefaultFontStyle;
539 void Controller::SetDefaultPointSize( float pointSize )
541 if( !mImpl->mFontDefaults )
543 mImpl->mFontDefaults = new Controller::FontDefaults();
546 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
547 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
548 mImpl->mOperationsPending = ALL_OPERATIONS;
549 mImpl->mRecalculateNaturalSize = true;
552 float Controller::GetDefaultPointSize() const
554 if( mImpl->mFontDefaults )
556 return mImpl->mFontDefaults->mDefaultPointSize;
562 void Controller::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
564 if( mImpl->mFontDefaults )
567 fontRun.characterRun.characterIndex = 0;
568 fontRun.characterRun.numberOfCharacters = numberOfCharacters;
569 fontRun.fontId = mImpl->mFontDefaults->GetFontId( mImpl->mFontClient );
570 fontRun.isDefault = true;
572 fonts.PushBack( fontRun );
576 void Controller::EnableTextInput( DecoratorPtr decorator )
578 if( !mImpl->mTextInput )
580 mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
584 void Controller::SetEnableCursorBlink( bool enable )
586 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "TextInput disabled" );
588 if( mImpl->mTextInput )
590 mImpl->mTextInput->mCursorBlinkEnabled = enable;
593 mImpl->mTextInput->mDecorator )
595 mImpl->mTextInput->mDecorator->StopCursorBlink();
600 bool Controller::GetEnableCursorBlink() const
602 if( mImpl->mTextInput )
604 return mImpl->mTextInput->mCursorBlinkEnabled;
610 Vector3 Controller::GetNaturalSize()
614 // Make sure the model is up-to-date before layouting
615 ProcessModifyEvents();
617 if( mImpl->mRecalculateNaturalSize )
619 // Operations that can be done only once until the text changes.
620 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
628 // Make sure the model is up-to-date before layouting
629 UpdateModel( onlyOnceOperations );
631 // Operations that need to be done if the size changes.
632 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
636 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
637 static_cast<OperationsMask>( onlyOnceOperations |
639 naturalSize.GetVectorXY() );
641 // Do not do again the only once operations.
642 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
644 // Do the size related operations again.
645 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
647 // Stores the natural size to avoid recalculate it again
648 // unless the text/style changes.
649 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
651 mImpl->mRecalculateNaturalSize = false;
655 naturalSize = mImpl->mVisualModel->GetNaturalSize();
661 float Controller::GetHeightForWidth( float width )
663 // Make sure the model is up-to-date before layouting
664 ProcessModifyEvents();
667 if( width != mImpl->mControlSize.width )
669 // Operations that can be done only once until the text changes.
670 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
678 // Make sure the model is up-to-date before layouting
679 UpdateModel( onlyOnceOperations );
681 // Operations that need to be done if the size changes.
682 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
686 DoRelayout( Size( width, MAX_FLOAT ),
687 static_cast<OperationsMask>( onlyOnceOperations |
691 // Do not do again the only once operations.
692 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
694 // Do the size related operations again.
695 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
699 layoutSize = mImpl->mVisualModel->GetActualSize();
702 return layoutSize.height;
705 bool Controller::Relayout( const Vector2& size )
707 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
709 bool glyphsRemoved( false );
710 if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
712 mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
713 glyphsRemoved = true;
716 // Not worth to relayout if width or height is equal to zero.
717 return glyphsRemoved;
720 if( size != mImpl->mControlSize )
722 // Operations that need to be done if the size changes.
723 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
729 mImpl->mControlSize = size;
732 // Make sure the model is up-to-date before layouting
733 ProcessModifyEvents();
734 UpdateModel( mImpl->mOperationsPending );
737 bool updated = DoRelayout( mImpl->mControlSize,
738 mImpl->mOperationsPending,
741 // Do not re-do any operation until something changes.
742 mImpl->mOperationsPending = NO_OPERATION;
744 if( mImpl->mTextInput )
746 // Move the cursor, grab handle etc.
747 updated = mImpl->mTextInput->ProcessInputEvents() || updated;
753 void Controller::ProcessModifyEvents()
755 std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
757 for( unsigned int i=0; i<events.size(); ++i )
759 if( REPLACE_TEXT == events[0].type )
761 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
762 DALI_ASSERT_DEBUG( 0 == i && "Unexpected REPLACE event" );
764 ReplaceTextEvent( events[0].text );
766 else if( INSERT_TEXT == events[0].type )
768 InsertTextEvent( events[0].text );
770 else if( DELETE_TEXT == events[0].type )
776 // Discard temporary text
780 void Controller::ReplaceTextEvent( const std::string& text )
783 mImpl->mLogicalModel->mText.Clear();
784 mImpl->mLogicalModel->mScriptRuns.Clear();
785 mImpl->mLogicalModel->mFontRuns.Clear();
786 mImpl->mLogicalModel->mLineBreakInfo.Clear();
787 mImpl->mLogicalModel->mWordBreakInfo.Clear();
788 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
789 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
790 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
791 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
792 mImpl->mVisualModel->mGlyphs.Clear();
793 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
794 mImpl->mVisualModel->mCharactersToGlyph.Clear();
795 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
796 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
797 mImpl->mVisualModel->mGlyphPositions.Clear();
798 mImpl->mVisualModel->mLines.Clear();
800 // Convert text into UTF-32
801 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
802 utf32Characters.Resize( text.size() );
804 // This is a bit horrible but std::string returns a (signed) char*
805 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
807 // Transform a text array encoded in utf8 into an array encoded in utf32.
808 // It returns the actual number of characters.
809 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
810 utf32Characters.Resize( characterCount );
812 // Reset the cursor position
813 if( mImpl->mTextInput )
815 mImpl->mTextInput->mPrimaryCursorPosition = characterCount;
816 // TODO - handle secondary cursor
819 // The natural size needs to be re-calculated.
820 mImpl->mRecalculateNaturalSize = true;
822 // Apply modifications to the model
823 mImpl->mOperationsPending = ALL_OPERATIONS;
824 UpdateModel( ALL_OPERATIONS );
825 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
831 void Controller::InsertTextEvent( const std::string& text )
833 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
835 // TODO - Optimize this
836 mImpl->mLogicalModel->mScriptRuns.Clear();
837 mImpl->mLogicalModel->mFontRuns.Clear();
838 mImpl->mLogicalModel->mLineBreakInfo.Clear();
839 mImpl->mLogicalModel->mWordBreakInfo.Clear();
840 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
841 mImpl->mVisualModel->mGlyphs.Clear();
843 // Convert text into UTF-32
844 Vector<Character> utf32Characters;
845 utf32Characters.Resize( text.size() );
847 // This is a bit horrible but std::string returns a (signed) char*
848 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
850 // Transform a text array encoded in utf8 into an array encoded in utf32.
851 // It returns the actual number of characters.
852 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
853 utf32Characters.Resize( characterCount );
855 // Insert at current cursor position
856 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
857 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
859 if( cursorIndex < modifyText.Count() )
861 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.End() );
865 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.End() );
868 // Advance the cursor position
871 // The natural size needs to be re-calculated.
872 mImpl->mRecalculateNaturalSize = true;
874 // Apply modifications to the model; TODO - Optimize this
875 mImpl->mOperationsPending = ALL_OPERATIONS;
876 UpdateModel( ALL_OPERATIONS );
877 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
883 void Controller::DeleteTextEvent()
885 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
887 // TODO - Optimize this
888 mImpl->mLogicalModel->mScriptRuns.Clear();
889 mImpl->mLogicalModel->mFontRuns.Clear();
890 mImpl->mLogicalModel->mLineBreakInfo.Clear();
891 mImpl->mLogicalModel->mWordBreakInfo.Clear();
892 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
893 mImpl->mVisualModel->mGlyphs.Clear();
895 // Delte at current cursor position
896 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
897 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
899 if( cursorIndex > 0 &&
900 cursorIndex-1 < modifyText.Count() )
902 modifyText.Remove( modifyText.Begin() + cursorIndex - 1 );
904 // Cursor position retreat
908 // The natural size needs to be re-calculated.
909 mImpl->mRecalculateNaturalSize = true;
911 // Apply modifications to the model; TODO - Optimize this
912 mImpl->mOperationsPending = ALL_OPERATIONS;
913 UpdateModel( ALL_OPERATIONS );
914 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
920 void Controller::UpdateModel( OperationsMask operationsRequired )
922 // Calculate the operations to be done.
923 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
925 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
927 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
929 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
930 if( GET_LINE_BREAKS & operations )
932 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
933 // calculate the bidirectional info for each 'paragraph'.
934 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
935 // is not shaped together).
936 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
938 SetLineBreakInfo( utf32Characters,
942 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
943 if( GET_WORD_BREAKS & operations )
945 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
946 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
948 SetWordBreakInfo( utf32Characters,
952 const bool getScripts = GET_SCRIPTS & operations;
953 const bool validateFonts = VALIDATE_FONTS & operations;
955 Vector<ScriptRun>& scripts = mImpl->mLogicalModel->mScriptRuns;
956 Vector<FontRun>& validFonts = mImpl->mLogicalModel->mFontRuns;
958 if( getScripts || validateFonts )
960 // Validates the fonts assigned by the application or assigns default ones.
961 // It makes sure all the characters are going to be rendered by the correct font.
962 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
966 // Retrieves the scripts used in the text.
967 multilanguageSupport.SetScripts( utf32Characters,
974 if( 0u == validFonts.Count() )
976 // Copy the requested font defaults received via the property system.
977 // These may not be valid i.e. may not contain glyphs for the necessary scripts.
978 GetDefaultFonts( validFonts, numberOfCharacters );
981 // Validates the fonts. If there is a character with no assigned font it sets a default one.
982 // After this call, fonts are validated.
983 multilanguageSupport.ValidateFonts( utf32Characters,
989 Vector<Character> mirroredUtf32Characters;
990 bool textMirrored = false;
991 if( BIDI_INFO & operations )
993 // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
994 // bidirectional info.
996 Length numberOfParagraphs = 0u;
998 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
999 for( Length index = 0u; index < numberOfCharacters; ++index )
1001 if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
1003 ++numberOfParagraphs;
1007 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1008 bidirectionalInfo.Reserve( numberOfParagraphs );
1010 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1011 SetBidirectionalInfo( utf32Characters,
1014 bidirectionalInfo );
1016 if( 0u != bidirectionalInfo.Count() )
1018 // This paragraph has right to left text. Some characters may need to be mirrored.
1019 // TODO: consider if the mirrored string can be stored as well.
1021 textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
1025 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1026 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1027 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1028 if( SHAPE_TEXT & operations )
1030 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1032 ShapeText( textToShape,
1037 glyphsToCharactersMap,
1038 charactersPerGlyph );
1041 const Length numberOfGlyphs = glyphs.Count();
1043 if( GET_GLYPH_METRICS & operations )
1045 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
1048 if( 0u != numberOfGlyphs )
1050 // Create the glyph to character conversion table and the 'number of glyphs' per character.
1051 mImpl->mVisualModel->CreateCharacterToGlyphTable(numberOfCharacters );
1052 mImpl->mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
1056 bool Controller::DoRelayout( const Vector2& size,
1057 OperationsMask operationsRequired,
1060 bool viewUpdated( false );
1062 // Calculate the operations to be done.
1063 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1065 if( LAYOUT & operations )
1067 // Some vectors with data needed to layout and reorder may be void
1068 // after the first time the text has been laid out.
1069 // Fill the vectors again.
1071 Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
1073 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1074 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1075 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1076 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1077 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1079 // Set the layout parameters.
1080 LayoutParameters layoutParameters( size,
1081 mImpl->mLogicalModel->mText.Begin(),
1082 lineBreakInfo.Begin(),
1083 wordBreakInfo.Begin(),
1086 glyphsToCharactersMap.Begin(),
1087 charactersPerGlyph.Begin() );
1089 // The laid-out lines.
1090 // It's not possible to know in how many lines the text is going to be laid-out,
1091 // but it can be resized at least with the number of 'paragraphs' to avoid
1092 // some re-allocations.
1093 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1095 // Delete any previous laid out lines before setting the new ones.
1098 // The capacity of the bidirectional paragraph info is the number of paragraphs.
1099 lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
1101 // Resize the vector of positions to have the same size than the vector of glyphs.
1102 Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
1103 glyphPositions.Resize( numberOfGlyphs );
1105 // Update the visual model.
1106 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
1113 // Reorder the lines
1114 if( REORDER & operations )
1116 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1118 // Check first if there are paragraphs with bidirectional info.
1119 if( 0u != bidirectionalInfo.Count() )
1122 const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
1124 // Reorder the lines.
1125 Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
1126 lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
1127 ReorderLines( bidirectionalInfo,
1129 lineBidirectionalInfoRuns );
1131 // Set the bidirectional info into the model.
1132 const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
1133 mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
1134 numberOfBidirectionalInfoRuns );
1136 // Set the bidirectional info per line into the layout parameters.
1137 layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
1138 layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
1140 // Get the character to glyph conversion table and set into the layout.
1141 layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
1143 // Get the glyphs per character table and set into the layout.
1144 layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
1146 // Re-layout the text. Reorder those lines with right to left characters.
1147 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1150 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
1151 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
1152 endIt = lineBidirectionalInfoRuns.End();
1156 BidirectionalLineInfoRun& bidiLineInfo = *it;
1158 free( bidiLineInfo.visualToLogicalMap );
1163 if( ALIGN & operations )
1165 mImpl->mLayoutEngine.Align( layoutParameters,
1170 // Sets the actual size.
1171 if( UPDATE_ACTUAL_SIZE & operations )
1173 mImpl->mVisualModel->SetActualSize( layoutSize );
1179 layoutSize = mImpl->mVisualModel->GetActualSize();
1185 View& Controller::GetView()
1187 return mImpl->mView;
1190 LayoutEngine& Controller::GetLayoutEngine()
1192 return mImpl->mLayoutEngine;
1195 void Controller::RequestRelayout()
1197 mImpl->mControlInterface.RequestTextRelayout();
1200 void Controller::KeyboardFocusGainEvent()
1202 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
1204 if( mImpl->mTextInput )
1206 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
1207 mImpl->mTextInput->mEventQueue.push_back( event );
1213 void Controller::KeyboardFocusLostEvent()
1215 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
1217 if( mImpl->mTextInput )
1219 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
1220 mImpl->mTextInput->mEventQueue.push_back( event );
1226 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1228 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" );
1230 if( mImpl->mTextInput &&
1231 keyEvent.state == KeyEvent::Down )
1233 int keyCode = keyEvent.keyCode;
1234 const std::string& keyString = keyEvent.keyPressed;
1236 // Pre-process to separate modifying events from non-modifying input events.
1237 if( Dali::DALI_KEY_ESCAPE == keyCode )
1239 // Escape key is a special case which causes focus loss
1240 KeyboardFocusLostEvent();
1242 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
1243 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1244 Dali::DALI_KEY_CURSOR_UP == keyCode ||
1245 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1247 TextInput::Event event( TextInput::CURSOR_KEY_EVENT );
1248 event.p1.mInt = keyCode;
1249 mImpl->mTextInput->mEventQueue.push_back( event );
1251 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1253 // Queue a delete event
1255 event.type = DELETE_TEXT;
1256 mImpl->mModifyEvents.push_back( event );
1258 else if( !keyString.empty() )
1260 // Queue an insert event
1262 event.type = INSERT_TEXT;
1263 event.text = keyString;
1264 mImpl->mModifyEvents.push_back( event );
1273 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1275 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
1277 if( mImpl->mTextInput )
1279 TextInput::Event event( TextInput::TAP_EVENT );
1280 event.p1.mUint = tapCount;
1281 event.p2.mFloat = x;
1282 event.p3.mFloat = y;
1283 mImpl->mTextInput->mEventQueue.push_back( event );
1289 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
1291 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
1293 if( mImpl->mTextInput )
1295 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
1296 event.p1.mUint = state;
1297 event.p2.mFloat = x;
1298 event.p3.mFloat = y;
1299 mImpl->mTextInput->mEventQueue.push_back( event );
1305 Controller::~Controller()
1310 Controller::Controller( ControlInterface& controlInterface )
1313 mImpl = new Controller::Impl( controlInterface );
1318 } // namespace Toolkit