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,
95 Event( EventType eventType )
115 TextInput( LogicalModelPtr logicalModel,
116 VisualModelPtr visualModel,
117 DecoratorPtr decorator )
118 : mLogicalModel( logicalModel ),
119 mVisualModel( visualModel ),
120 mDecorator( decorator ),
122 mPrimaryCursorPosition( 0u ),
123 mSecondaryCursorPosition( 0u ),
124 mDecoratorUpdated( false ),
125 mCursorBlinkEnabled( true ),
126 mGrabHandleEnabled( false ),
127 mGrabHandlePopupEnabled( false ),
128 mSelectionEnabled( false ),
129 mHorizontalScrollingEnabled( true ),
130 mVerticalScrollingEnabled( false ),
131 mUpdateCursorPosition( false )
136 * @brief Helper to move the cursor, grab handle etc.
138 bool ProcessInputEvents( const Vector2& controlSize )
140 mDecoratorUpdated = false;
144 for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
148 case KEYBOARD_FOCUS_GAIN_EVENT:
150 OnKeyboardFocus( true );
153 case KEYBOARD_FOCUS_LOST_EVENT:
155 OnKeyboardFocus( false );
158 case CURSOR_KEY_EVENT:
160 OnCursorKeyEvent( *iter );
170 OnPanEvent( *iter, controlSize );
173 case GRAB_HANDLE_EVENT:
175 OnGrabHandleEvent( *iter );
182 // The cursor must also be repositioned after inserts into the model
183 if( mUpdateCursorPosition )
185 UpdateCursorPosition();
186 mUpdateCursorPosition = false;
191 return mDecoratorUpdated;
194 void OnKeyboardFocus( bool hasFocus )
198 ChangeState( INACTIVE );
202 ChangeState( EDITING );
206 void OnCursorKeyEvent( const Event& event )
208 int keyCode = event.p1.mInt;
210 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
214 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
218 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
222 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
228 void HandleCursorKey( int keyCode )
233 void OnTapEvent( const Event& event )
235 unsigned int tapCount = event.p1.mUint;
239 ChangeState( EDITING );
241 float xPosition = event.p2.mFloat;
242 float yPosition = event.p3.mFloat;
244 GetClosestCursorPosition( mPrimaryCursorPosition, xPosition, yPosition, height );
245 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
246 mUpdateCursorPosition = false;
248 mDecoratorUpdated = true;
250 else if( mSelectionEnabled &&
253 ChangeState( SELECTING );
257 void OnPanEvent( const Event& event, const Vector2& controlSize )
259 int state = event.p1.mInt;
261 if( Gesture::Started == state ||
262 Gesture::Continuing == state )
264 const Vector2& actualSize = mVisualModel->GetActualSize();
266 if( mHorizontalScrollingEnabled )
268 float displacementX = event.p2.mFloat;
269 mScrollPosition.x += displacementX;
271 // Clamp between -space & 0
272 float contentWidth = actualSize.width;
273 float space = (contentWidth > controlSize.width) ? contentWidth - controlSize.width : 0.0f;
274 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
275 mScrollPosition.x = ( mScrollPosition.x > 0 ) ? 0 : mScrollPosition.x;
277 mDecoratorUpdated = true;
279 if( mVerticalScrollingEnabled )
281 float displacementY = event.p3.mFloat;
282 mScrollPosition.y += displacementY;
284 // Clamp between -space & 0
285 float space = (actualSize.height > controlSize.height) ? actualSize.height - controlSize.height : 0.0f;
286 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
287 mScrollPosition.y = ( mScrollPosition.y > 0 ) ? 0 : mScrollPosition.y;
289 mDecoratorUpdated = true;
294 void OnGrabHandleEvent( const Event& event )
296 unsigned int state = event.p1.mUint;
298 if( GRAB_HANDLE_PRESSED == state )
300 float xPosition = event.p2.mFloat;
301 float yPosition = event.p3.mFloat;
304 GetClosestCursorPosition( mPrimaryCursorPosition, xPosition, yPosition, height );
306 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
307 mDecorator->HidePopup();
308 mDecoratorUpdated = true;
310 else if ( mGrabHandlePopupEnabled &&
311 GRAB_HANDLE_RELEASED == state )
313 mDecorator->ShowPopup();
317 void ChangeState( State newState )
319 if( mState != newState )
323 if( INACTIVE == mState )
325 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
326 mDecorator->StopCursorBlink();
327 mDecorator->SetGrabHandleActive( false );
328 mDecorator->SetSelectionActive( false );
329 mDecorator->HidePopup();
330 mDecoratorUpdated = true;
332 else if ( SELECTING == mState )
334 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
335 mDecorator->StopCursorBlink();
336 mDecorator->SetGrabHandleActive( false );
337 mDecorator->SetSelectionActive( true );
338 mDecoratorUpdated = true;
340 else if( EDITING == mState )
342 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
343 if( mCursorBlinkEnabled )
345 mDecorator->StartCursorBlink();
347 if( mGrabHandleEnabled )
349 mDecorator->SetGrabHandleActive( true );
351 mDecorator->SetSelectionActive( false );
352 mDecoratorUpdated = true;
357 LineIndex GetClosestLine( float y )
359 LineIndex lineIndex( 0u );
361 const Vector<LineRun>& lines = mVisualModel->mLines;
362 for( float totalHeight = 0; lineIndex < lines.Count(); ++lineIndex )
364 totalHeight += lines[lineIndex].lineSize.height;
365 if( y < totalHeight )
374 void GetClosestCursorPosition( CharacterIndex& logical, float& visualX, float& visualY, float& height )
376 Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
377 Length numberOfLines = mVisualModel->mLines.Count();
378 if( 0 == numberOfGlyphs ||
384 // Transform to visual model coords
385 visualX -= mScrollPosition.x;
386 visualY -= mScrollPosition.y;
388 // Find which line is closest
389 LineIndex lineIndex( GetClosestLine( visualY ) );
391 const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
392 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
394 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
395 const Vector2* const positionsBuffer = positions.Begin();
397 unsigned int closestGlyph = 0;
398 bool leftOfGlyph( false ); // which side of the glyph?
399 float closestDistance = MAX_FLOAT;
401 const LineRun& line = mVisualModel->mLines[lineIndex];
402 GlyphIndex startGlyph = line.glyphIndex;
403 GlyphIndex endGlyph = line.glyphIndex + line.numberOfGlyphs;
404 DALI_ASSERT_DEBUG( endGlyph <= glyphs.Count() && "Invalid line info" );
406 for( GlyphIndex i = startGlyph; i < endGlyph; ++i )
408 const GlyphInfo& glyphInfo = *( glyphsBuffer + i );
409 const Vector2& position = *( positionsBuffer + i );
410 float glyphX = position.x + glyphInfo.width*0.5f;
411 float glyphY = position.y + glyphInfo.height*0.5f;
413 float distanceToGlyph = fabsf( glyphX - visualX ) + fabsf( glyphY - visualY );
415 if( distanceToGlyph < closestDistance )
417 closestDistance = distanceToGlyph;
419 leftOfGlyph = ( visualX < glyphX );
423 // Calculate the logical position
424 logical = mVisualModel->GetCharacterIndex( closestGlyph );
426 // Returns the visual position of the glyph
427 visualX = positions[closestGlyph].x;
430 visualX += glyphs[closestGlyph].width;
435 else// if ( RTL ) TODO
441 height = line.lineSize.height;
444 void UpdateCursorPosition()
446 if( 0 == mVisualModel->mGlyphs.Count() )
451 // FIXME GetGlyphIndex() is behaving strangely
453 GlyphIndex cursorGlyph = mVisualModel->GetGlyphIndex( mPrimaryCursorPosition );
455 GlyphIndex cursorGlyph( 0u );
456 for( cursorGlyph = 0; cursorGlyph < mVisualModel->mGlyphs.Count(); ++cursorGlyph )
458 if( mPrimaryCursorPosition == mVisualModel->GetCharacterIndex( cursorGlyph ) )
465 float visualX( 0.0f );
466 float visualY( 0.0f );
467 LineIndex lineIndex( 0u );
468 const Vector<LineRun>& lineRuns = mVisualModel->mLines;
470 if( cursorGlyph > 0 )
474 visualX = mVisualModel->mGlyphPositions[ cursorGlyph ].x;
476 visualX += mVisualModel->mGlyphs[ cursorGlyph ].width;
478 // Find the line height
479 for( GlyphIndex lastGlyph = 0; lineIndex < lineRuns.Count(); ++lineIndex )
481 lastGlyph = (lineRuns[lineIndex].glyphIndex + lineRuns[lineIndex].numberOfGlyphs);
482 if( cursorGlyph < lastGlyph )
489 mDecorator->SetPosition( PRIMARY_CURSOR, visualX, visualY, lineRuns[lineIndex].lineSize.height );
490 mDecoratorUpdated = true;
493 LogicalModelPtr mLogicalModel;
494 VisualModelPtr mVisualModel;
495 DecoratorPtr mDecorator;
497 std::string mPlaceholderText;
500 * This is used to delay handling events until after the model has been updated.
501 * The number of updates to the model is minimized to improve performance.
503 vector<Event> mEventQueue; ///< The queue of touch events etc.
505 State mState; ///< Selection mode, edit mode etc.
507 CharacterIndex mPrimaryCursorPosition; ///< Index into logical model for primary cursor
508 CharacterIndex mSecondaryCursorPosition; ///< Index into logical model for secondary cursor
511 * 0,0 means that the top-left corner of the layout matches the top-left corner of the UI control.
512 * Typically this will have a negative value with scrolling occurs.
514 Vector2 mScrollPosition; ///< The text is offset by this position when scrolling.
516 bool mDecoratorUpdated : 1; ///< True if the decorator was updated during event processing
517 bool mCursorBlinkEnabled : 1; ///< True if cursor should blink when active
518 bool mGrabHandleEnabled : 1; ///< True if grab handle is enabled
519 bool mGrabHandlePopupEnabled : 1; ///< True if the grab handle popu-up should be shown
520 bool mSelectionEnabled : 1; ///< True if selection handles, highlight etc. are enabled
521 bool mHorizontalScrollingEnabled : 1; ///< True if horizontal scrolling is enabled
522 bool mVerticalScrollingEnabled : 1; ///< True if vertical scrolling is enabled
523 bool mUpdateCursorPosition : 1; ///< True if the visual position of the cursor must be recalculated
526 struct Controller::FontDefaults
529 : mDefaultPointSize(0.0f),
534 FontId GetFontId( TextAbstraction::FontClient& fontClient )
538 Dali::TextAbstraction::PointSize26Dot6 pointSize = mDefaultPointSize*64;
539 mFontId = fontClient.GetFontId( mDefaultFontFamily, mDefaultFontStyle, pointSize );
545 std::string mDefaultFontFamily;
546 std::string mDefaultFontStyle;
547 float mDefaultPointSize;
551 struct Controller::Impl
553 Impl( ControlInterface& controlInterface )
554 : mControlInterface( controlInterface ),
557 mFontDefaults( NULL ),
564 mOperationsPending( NO_OPERATION ),
565 mRecalculateNaturalSize( true )
567 mLogicalModel = LogicalModel::New();
568 mVisualModel = VisualModel::New();
570 mFontClient = TextAbstraction::FontClient::Get();
572 mView.SetVisualModel( mVisualModel );
580 ControlInterface& mControlInterface; ///< Reference to the text controller.
581 LogicalModelPtr mLogicalModel; ///< Pointer to the logical model.
582 VisualModelPtr mVisualModel; ///< Pointer to the visual model.
583 FontDefaults* mFontDefaults; ///< Avoid allocating this when the user does not specify a font.
584 Controller::TextInput* mTextInput; ///< Avoid allocating everything for text input until EnableTextInput().
585 TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
586 View mView; ///< The view interface to the rendering back-end.
587 LayoutEngine mLayoutEngine; ///< The layout engine.
588 std::vector<ModifyEvent> mModifyEvents; ///< Temporary stores the text set until the next relayout.
589 Size mControlSize; ///< The size of the control.
590 OperationsMask mOperationsPending; ///< Operations pending to be done to layout the text.
591 bool mRecalculateNaturalSize:1; ///< Whether the natural size needs to be recalculated.
594 ControllerPtr Controller::New( ControlInterface& controlInterface )
596 return ControllerPtr( new Controller( controlInterface ) );
599 void Controller::SetText( const std::string& text )
601 // Cancel previously queued inserts etc.
602 mImpl->mModifyEvents.clear();
604 // Keep until size negotiation
606 event.type = REPLACE_TEXT;
608 mImpl->mModifyEvents.push_back( event );
610 if( mImpl->mTextInput )
612 // Cancel previously queued events
613 mImpl->mTextInput->mEventQueue.clear();
615 // TODO - Hide selection decorations
619 void Controller::GetText( std::string& text ) const
621 if( !mImpl->mModifyEvents.empty() &&
622 REPLACE_TEXT == mImpl->mModifyEvents[0].type )
624 text = mImpl->mModifyEvents[0].text;
628 // TODO - Convert from UTF-32
632 void Controller::SetPlaceholderText( const std::string& text )
634 if( !mImpl->mTextInput )
636 mImpl->mTextInput->mPlaceholderText = text;
640 void Controller::GetPlaceholderText( std::string& text ) const
642 if( !mImpl->mTextInput )
644 text = mImpl->mTextInput->mPlaceholderText;
648 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
650 if( !mImpl->mFontDefaults )
652 mImpl->mFontDefaults = new Controller::FontDefaults();
655 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
656 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
657 mImpl->mOperationsPending = ALL_OPERATIONS;
658 mImpl->mRecalculateNaturalSize = true;
661 const std::string& Controller::GetDefaultFontFamily() const
663 if( mImpl->mFontDefaults )
665 return mImpl->mFontDefaults->mDefaultFontFamily;
671 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
673 if( !mImpl->mFontDefaults )
675 mImpl->mFontDefaults = new Controller::FontDefaults();
678 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
679 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
680 mImpl->mOperationsPending = ALL_OPERATIONS;
681 mImpl->mRecalculateNaturalSize = true;
684 const std::string& Controller::GetDefaultFontStyle() const
686 if( mImpl->mFontDefaults )
688 return mImpl->mFontDefaults->mDefaultFontStyle;
694 void Controller::SetDefaultPointSize( float pointSize )
696 if( !mImpl->mFontDefaults )
698 mImpl->mFontDefaults = new Controller::FontDefaults();
701 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
702 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
703 mImpl->mOperationsPending = ALL_OPERATIONS;
704 mImpl->mRecalculateNaturalSize = true;
707 float Controller::GetDefaultPointSize() const
709 if( mImpl->mFontDefaults )
711 return mImpl->mFontDefaults->mDefaultPointSize;
717 void Controller::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
719 if( mImpl->mFontDefaults )
722 fontRun.characterRun.characterIndex = 0;
723 fontRun.characterRun.numberOfCharacters = numberOfCharacters;
724 fontRun.fontId = mImpl->mFontDefaults->GetFontId( mImpl->mFontClient );
725 fontRun.isDefault = true;
727 fonts.PushBack( fontRun );
731 void Controller::EnableTextInput( DecoratorPtr decorator )
733 if( !mImpl->mTextInput )
735 mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
739 void Controller::SetEnableCursorBlink( bool enable )
741 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "TextInput disabled" );
743 if( mImpl->mTextInput )
745 mImpl->mTextInput->mCursorBlinkEnabled = enable;
748 mImpl->mTextInput->mDecorator )
750 mImpl->mTextInput->mDecorator->StopCursorBlink();
755 bool Controller::GetEnableCursorBlink() const
757 if( mImpl->mTextInput )
759 return mImpl->mTextInput->mCursorBlinkEnabled;
765 const Vector2& Controller::GetScrollPosition() const
767 if( mImpl->mTextInput )
769 return mImpl->mTextInput->mScrollPosition;
772 return Vector2::ZERO;
775 Vector3 Controller::GetNaturalSize()
779 // Make sure the model is up-to-date before layouting
780 ProcessModifyEvents();
782 if( mImpl->mRecalculateNaturalSize )
784 // Operations that can be done only once until the text changes.
785 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
793 // Make sure the model is up-to-date before layouting
794 UpdateModel( onlyOnceOperations );
796 // Operations that need to be done if the size changes.
797 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
801 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
802 static_cast<OperationsMask>( onlyOnceOperations |
804 naturalSize.GetVectorXY() );
806 // Do not do again the only once operations.
807 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
809 // Do the size related operations again.
810 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
812 // Stores the natural size to avoid recalculate it again
813 // unless the text/style changes.
814 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
816 mImpl->mRecalculateNaturalSize = false;
820 naturalSize = mImpl->mVisualModel->GetNaturalSize();
826 float Controller::GetHeightForWidth( float width )
828 // Make sure the model is up-to-date before layouting
829 ProcessModifyEvents();
832 if( width != mImpl->mControlSize.width )
834 // Operations that can be done only once until the text changes.
835 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
843 // Make sure the model is up-to-date before layouting
844 UpdateModel( onlyOnceOperations );
846 // Operations that need to be done if the size changes.
847 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
851 DoRelayout( Size( width, MAX_FLOAT ),
852 static_cast<OperationsMask>( onlyOnceOperations |
856 // Do not do again the only once operations.
857 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
859 // Do the size related operations again.
860 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
864 layoutSize = mImpl->mVisualModel->GetActualSize();
867 return layoutSize.height;
870 bool Controller::Relayout( const Vector2& size )
872 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
874 bool glyphsRemoved( false );
875 if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
877 mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
878 glyphsRemoved = true;
881 // Not worth to relayout if width or height is equal to zero.
882 return glyphsRemoved;
885 if( size != mImpl->mControlSize )
887 // Operations that need to be done if the size changes.
888 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
894 mImpl->mControlSize = size;
897 // Make sure the model is up-to-date before layouting
898 ProcessModifyEvents();
899 UpdateModel( mImpl->mOperationsPending );
902 bool updated = DoRelayout( mImpl->mControlSize,
903 mImpl->mOperationsPending,
906 // Do not re-do any operation until something changes.
907 mImpl->mOperationsPending = NO_OPERATION;
909 if( mImpl->mTextInput )
911 // Move the cursor, grab handle etc.
912 updated = mImpl->mTextInput->ProcessInputEvents( mImpl->mControlSize ) || updated;
918 void Controller::ProcessModifyEvents()
920 std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
922 for( unsigned int i=0; i<events.size(); ++i )
924 if( REPLACE_TEXT == events[0].type )
926 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
927 DALI_ASSERT_DEBUG( 0 == i && "Unexpected REPLACE event" );
929 ReplaceTextEvent( events[0].text );
931 else if( INSERT_TEXT == events[0].type )
933 InsertTextEvent( events[0].text );
935 else if( DELETE_TEXT == events[0].type )
941 // Discard temporary text
945 void Controller::ReplaceTextEvent( const std::string& text )
948 mImpl->mLogicalModel->mText.Clear();
949 mImpl->mLogicalModel->mScriptRuns.Clear();
950 mImpl->mLogicalModel->mFontRuns.Clear();
951 mImpl->mLogicalModel->mLineBreakInfo.Clear();
952 mImpl->mLogicalModel->mWordBreakInfo.Clear();
953 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
954 mImpl->mLogicalModel->mCharacterDirections.Clear();
955 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
956 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
957 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
958 mImpl->mVisualModel->mGlyphs.Clear();
959 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
960 mImpl->mVisualModel->mCharactersToGlyph.Clear();
961 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
962 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
963 mImpl->mVisualModel->mGlyphPositions.Clear();
964 mImpl->mVisualModel->mLines.Clear();
966 // Convert text into UTF-32
967 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
968 utf32Characters.Resize( text.size() );
970 // This is a bit horrible but std::string returns a (signed) char*
971 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
973 // Transform a text array encoded in utf8 into an array encoded in utf32.
974 // It returns the actual number of characters.
975 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
976 utf32Characters.Resize( characterCount );
978 // Reset the cursor position
979 if( mImpl->mTextInput )
981 mImpl->mTextInput->mPrimaryCursorPosition = characterCount;
982 // TODO - handle secondary cursor
985 // The natural size needs to be re-calculated.
986 mImpl->mRecalculateNaturalSize = true;
988 // Apply modifications to the model
989 mImpl->mOperationsPending = ALL_OPERATIONS;
990 UpdateModel( ALL_OPERATIONS );
991 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
997 void Controller::InsertTextEvent( const std::string& text )
999 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1001 // TODO - Optimize this
1002 mImpl->mLogicalModel->mScriptRuns.Clear();
1003 mImpl->mLogicalModel->mFontRuns.Clear();
1004 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1005 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1006 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1007 mImpl->mLogicalModel->mCharacterDirections.Clear();
1008 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1009 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1010 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1011 mImpl->mVisualModel->mGlyphs.Clear();
1012 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1013 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1014 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1015 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1016 mImpl->mVisualModel->mGlyphPositions.Clear();
1017 mImpl->mVisualModel->mLines.Clear();
1019 // Convert text into UTF-32
1020 Vector<Character> utf32Characters;
1021 utf32Characters.Resize( text.size() );
1023 // This is a bit horrible but std::string returns a (signed) char*
1024 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1026 // Transform a text array encoded in utf8 into an array encoded in utf32.
1027 // It returns the actual number of characters.
1028 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1029 utf32Characters.Resize( characterCount );
1031 // Insert at current cursor position
1032 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1033 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1035 if( cursorIndex < modifyText.Count() )
1037 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.End() );
1041 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.End() );
1044 // Advance the cursor position
1047 // The natural size needs to be re-calculated.
1048 mImpl->mRecalculateNaturalSize = true;
1050 // Apply modifications to the model; TODO - Optimize this
1051 mImpl->mOperationsPending = ALL_OPERATIONS;
1052 UpdateModel( ALL_OPERATIONS );
1053 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1055 UPDATE_ACTUAL_SIZE |
1058 // Queue a cursor reposition event; this must wait until after DoRelayout()
1059 mImpl->mTextInput->mUpdateCursorPosition = true;
1062 void Controller::DeleteTextEvent()
1064 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1066 // TODO - Optimize this
1067 mImpl->mLogicalModel->mScriptRuns.Clear();
1068 mImpl->mLogicalModel->mFontRuns.Clear();
1069 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1070 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1071 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1072 mImpl->mLogicalModel->mCharacterDirections.Clear();
1073 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1074 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1075 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1076 mImpl->mVisualModel->mGlyphs.Clear();
1077 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1078 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1079 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1080 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1081 mImpl->mVisualModel->mGlyphPositions.Clear();
1082 mImpl->mVisualModel->mLines.Clear();
1084 // Delte at current cursor position
1085 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1086 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1088 if( cursorIndex > 0 &&
1089 cursorIndex-1 < modifyText.Count() )
1091 modifyText.Remove( modifyText.Begin() + cursorIndex - 1 );
1093 // Cursor position retreat
1097 // The natural size needs to be re-calculated.
1098 mImpl->mRecalculateNaturalSize = true;
1100 // Apply modifications to the model; TODO - Optimize this
1101 mImpl->mOperationsPending = ALL_OPERATIONS;
1102 UpdateModel( ALL_OPERATIONS );
1103 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1105 UPDATE_ACTUAL_SIZE |
1108 // Queue a cursor reposition event; this must wait until after DoRelayout()
1109 mImpl->mTextInput->mUpdateCursorPosition = true;
1112 void Controller::UpdateModel( OperationsMask operationsRequired )
1114 // Calculate the operations to be done.
1115 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1117 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1119 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
1121 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1122 if( GET_LINE_BREAKS & operations )
1124 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
1125 // calculate the bidirectional info for each 'paragraph'.
1126 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
1127 // is not shaped together).
1128 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
1130 SetLineBreakInfo( utf32Characters,
1134 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1135 if( GET_WORD_BREAKS & operations )
1137 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
1138 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
1140 SetWordBreakInfo( utf32Characters,
1144 const bool getScripts = GET_SCRIPTS & operations;
1145 const bool validateFonts = VALIDATE_FONTS & operations;
1147 Vector<ScriptRun>& scripts = mImpl->mLogicalModel->mScriptRuns;
1148 Vector<FontRun>& validFonts = mImpl->mLogicalModel->mFontRuns;
1150 if( getScripts || validateFonts )
1152 // Validates the fonts assigned by the application or assigns default ones.
1153 // It makes sure all the characters are going to be rendered by the correct font.
1154 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
1158 // Retrieves the scripts used in the text.
1159 multilanguageSupport.SetScripts( utf32Characters,
1166 if( 0u == validFonts.Count() )
1168 // Copy the requested font defaults received via the property system.
1169 // These may not be valid i.e. may not contain glyphs for the necessary scripts.
1170 GetDefaultFonts( validFonts, numberOfCharacters );
1173 // Validates the fonts. If there is a character with no assigned font it sets a default one.
1174 // After this call, fonts are validated.
1175 multilanguageSupport.ValidateFonts( utf32Characters,
1181 Vector<Character> mirroredUtf32Characters;
1182 bool textMirrored = false;
1183 if( BIDI_INFO & operations )
1185 // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
1186 // bidirectional info.
1188 Length numberOfParagraphs = 0u;
1190 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
1191 for( Length index = 0u; index < numberOfCharacters; ++index )
1193 if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
1195 ++numberOfParagraphs;
1199 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1200 bidirectionalInfo.Reserve( numberOfParagraphs );
1202 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1203 SetBidirectionalInfo( utf32Characters,
1206 bidirectionalInfo );
1208 if( 0u != bidirectionalInfo.Count() )
1210 // This paragraph has right to left text. Some characters may need to be mirrored.
1211 // TODO: consider if the mirrored string can be stored as well.
1213 textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
1215 // Only set the character directions if there is right to left characters.
1216 Vector<CharacterDirection>& directions = mImpl->mLogicalModel->mCharacterDirections;
1217 directions.Resize( numberOfCharacters );
1219 GetCharactersDirection( bidirectionalInfo,
1224 // There is no right to left characters. Clear the directions vector.
1225 mImpl->mLogicalModel->mCharacterDirections.Clear();
1230 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1231 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1232 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1233 if( SHAPE_TEXT & operations )
1235 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1237 ShapeText( textToShape,
1242 glyphsToCharactersMap,
1243 charactersPerGlyph );
1246 const Length numberOfGlyphs = glyphs.Count();
1248 if( GET_GLYPH_METRICS & operations )
1250 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
1253 if( 0u != numberOfGlyphs )
1255 // Create the glyph to character conversion table and the 'number of glyphs' per character.
1256 mImpl->mVisualModel->CreateCharacterToGlyphTable(numberOfCharacters );
1257 mImpl->mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
1261 bool Controller::DoRelayout( const Vector2& size,
1262 OperationsMask operationsRequired,
1265 bool viewUpdated( false );
1267 // Calculate the operations to be done.
1268 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1270 if( LAYOUT & operations )
1272 // Some vectors with data needed to layout and reorder may be void
1273 // after the first time the text has been laid out.
1274 // Fill the vectors again.
1276 Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
1278 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1279 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1280 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1281 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1282 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1284 // Set the layout parameters.
1285 LayoutParameters layoutParameters( size,
1286 mImpl->mLogicalModel->mText.Begin(),
1287 lineBreakInfo.Begin(),
1288 wordBreakInfo.Begin(),
1291 glyphsToCharactersMap.Begin(),
1292 charactersPerGlyph.Begin() );
1294 // The laid-out lines.
1295 // It's not possible to know in how many lines the text is going to be laid-out,
1296 // but it can be resized at least with the number of 'paragraphs' to avoid
1297 // some re-allocations.
1298 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1300 // Delete any previous laid out lines before setting the new ones.
1303 // The capacity of the bidirectional paragraph info is the number of paragraphs.
1304 lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
1306 // Resize the vector of positions to have the same size than the vector of glyphs.
1307 Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
1308 glyphPositions.Resize( numberOfGlyphs );
1310 // Update the visual model.
1311 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
1318 // Reorder the lines
1319 if( REORDER & operations )
1321 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1323 // Check first if there are paragraphs with bidirectional info.
1324 if( 0u != bidirectionalInfo.Count() )
1327 const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
1329 // Reorder the lines.
1330 Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
1331 lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
1332 ReorderLines( bidirectionalInfo,
1334 lineBidirectionalInfoRuns );
1336 // Set the bidirectional info into the model.
1337 const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
1338 mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
1339 numberOfBidirectionalInfoRuns );
1341 // Set the bidirectional info per line into the layout parameters.
1342 layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
1343 layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
1345 // Get the character to glyph conversion table and set into the layout.
1346 layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
1348 // Get the glyphs per character table and set into the layout.
1349 layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
1351 // Re-layout the text. Reorder those lines with right to left characters.
1352 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1355 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
1356 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
1357 endIt = lineBidirectionalInfoRuns.End();
1361 BidirectionalLineInfoRun& bidiLineInfo = *it;
1363 free( bidiLineInfo.visualToLogicalMap );
1368 if( ALIGN & operations )
1370 mImpl->mLayoutEngine.Align( layoutParameters,
1375 // Sets the actual size.
1376 if( UPDATE_ACTUAL_SIZE & operations )
1378 mImpl->mVisualModel->SetActualSize( layoutSize );
1384 layoutSize = mImpl->mVisualModel->GetActualSize();
1390 View& Controller::GetView()
1392 return mImpl->mView;
1395 LayoutEngine& Controller::GetLayoutEngine()
1397 return mImpl->mLayoutEngine;
1400 void Controller::RequestRelayout()
1402 mImpl->mControlInterface.RequestTextRelayout();
1405 void Controller::KeyboardFocusGainEvent()
1407 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
1409 if( mImpl->mTextInput )
1411 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
1412 mImpl->mTextInput->mEventQueue.push_back( event );
1418 void Controller::KeyboardFocusLostEvent()
1420 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
1422 if( mImpl->mTextInput )
1424 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
1425 mImpl->mTextInput->mEventQueue.push_back( event );
1431 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1433 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" );
1435 if( mImpl->mTextInput &&
1436 keyEvent.state == KeyEvent::Down )
1438 int keyCode = keyEvent.keyCode;
1439 const std::string& keyString = keyEvent.keyPressed;
1441 // Pre-process to separate modifying events from non-modifying input events.
1442 if( Dali::DALI_KEY_ESCAPE == keyCode )
1444 // Escape key is a special case which causes focus loss
1445 KeyboardFocusLostEvent();
1447 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
1448 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1449 Dali::DALI_KEY_CURSOR_UP == keyCode ||
1450 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1452 TextInput::Event event( TextInput::CURSOR_KEY_EVENT );
1453 event.p1.mInt = keyCode;
1454 mImpl->mTextInput->mEventQueue.push_back( event );
1456 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1458 // Queue a delete event
1460 event.type = DELETE_TEXT;
1461 mImpl->mModifyEvents.push_back( event );
1463 else if( !keyString.empty() )
1465 // Queue an insert event
1467 event.type = INSERT_TEXT;
1468 event.text = keyString;
1469 mImpl->mModifyEvents.push_back( event );
1478 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1480 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
1482 if( mImpl->mTextInput )
1484 TextInput::Event event( TextInput::TAP_EVENT );
1485 event.p1.mUint = tapCount;
1486 event.p2.mFloat = x;
1487 event.p3.mFloat = y;
1488 mImpl->mTextInput->mEventQueue.push_back( event );
1494 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1496 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected PanEvent" );
1498 if( mImpl->mTextInput )
1500 TextInput::Event event( TextInput::PAN_EVENT );
1501 event.p1.mInt = state;
1502 event.p2.mFloat = displacement.x;
1503 event.p3.mFloat = displacement.y;
1504 mImpl->mTextInput->mEventQueue.push_back( event );
1510 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
1512 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
1514 if( mImpl->mTextInput )
1516 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
1517 event.p1.mUint = state;
1518 event.p2.mFloat = x;
1519 event.p3.mFloat = y;
1520 mImpl->mTextInput->mEventQueue.push_back( event );
1526 Controller::~Controller()
1531 Controller::Controller( ControlInterface& controlInterface )
1534 mImpl = new Controller::Impl( controlInterface );
1539 } // namespace Toolkit