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->mBidirectionalLineInfo.Clear();
955 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
956 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
957 mImpl->mVisualModel->mGlyphs.Clear();
958 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
959 mImpl->mVisualModel->mCharactersToGlyph.Clear();
960 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
961 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
962 mImpl->mVisualModel->mGlyphPositions.Clear();
963 mImpl->mVisualModel->mLines.Clear();
965 // Convert text into UTF-32
966 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
967 utf32Characters.Resize( text.size() );
969 // This is a bit horrible but std::string returns a (signed) char*
970 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
972 // Transform a text array encoded in utf8 into an array encoded in utf32.
973 // It returns the actual number of characters.
974 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
975 utf32Characters.Resize( characterCount );
977 // Reset the cursor position
978 if( mImpl->mTextInput )
980 mImpl->mTextInput->mPrimaryCursorPosition = characterCount;
981 // TODO - handle secondary cursor
984 // The natural size needs to be re-calculated.
985 mImpl->mRecalculateNaturalSize = true;
987 // Apply modifications to the model
988 mImpl->mOperationsPending = ALL_OPERATIONS;
989 UpdateModel( ALL_OPERATIONS );
990 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
996 void Controller::InsertTextEvent( const std::string& text )
998 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1000 // TODO - Optimize this
1001 mImpl->mLogicalModel->mScriptRuns.Clear();
1002 mImpl->mLogicalModel->mFontRuns.Clear();
1003 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1004 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1005 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1006 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1007 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1008 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1009 mImpl->mVisualModel->mGlyphs.Clear();
1010 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1011 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1012 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1013 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1014 mImpl->mVisualModel->mGlyphPositions.Clear();
1015 mImpl->mVisualModel->mLines.Clear();
1017 // Convert text into UTF-32
1018 Vector<Character> utf32Characters;
1019 utf32Characters.Resize( text.size() );
1021 // This is a bit horrible but std::string returns a (signed) char*
1022 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1024 // Transform a text array encoded in utf8 into an array encoded in utf32.
1025 // It returns the actual number of characters.
1026 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1027 utf32Characters.Resize( characterCount );
1029 // Insert at current cursor position
1030 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1031 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1033 if( cursorIndex < modifyText.Count() )
1035 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.End() );
1039 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.End() );
1042 // Advance the cursor position
1045 // The natural size needs to be re-calculated.
1046 mImpl->mRecalculateNaturalSize = true;
1048 // Apply modifications to the model; TODO - Optimize this
1049 mImpl->mOperationsPending = ALL_OPERATIONS;
1050 UpdateModel( ALL_OPERATIONS );
1051 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1053 UPDATE_ACTUAL_SIZE |
1056 // Queue a cursor reposition event; this must wait until after DoRelayout()
1057 mImpl->mTextInput->mUpdateCursorPosition = true;
1060 void Controller::DeleteTextEvent()
1062 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1064 // TODO - Optimize this
1065 mImpl->mLogicalModel->mScriptRuns.Clear();
1066 mImpl->mLogicalModel->mFontRuns.Clear();
1067 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1068 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1069 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1070 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1071 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1072 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1073 mImpl->mVisualModel->mGlyphs.Clear();
1074 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1075 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1076 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1077 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1078 mImpl->mVisualModel->mGlyphPositions.Clear();
1079 mImpl->mVisualModel->mLines.Clear();
1081 // Delte at current cursor position
1082 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1083 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1085 if( cursorIndex > 0 &&
1086 cursorIndex-1 < modifyText.Count() )
1088 modifyText.Remove( modifyText.Begin() + cursorIndex - 1 );
1090 // Cursor position retreat
1094 // The natural size needs to be re-calculated.
1095 mImpl->mRecalculateNaturalSize = true;
1097 // Apply modifications to the model; TODO - Optimize this
1098 mImpl->mOperationsPending = ALL_OPERATIONS;
1099 UpdateModel( ALL_OPERATIONS );
1100 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1102 UPDATE_ACTUAL_SIZE |
1105 // Queue a cursor reposition event; this must wait until after DoRelayout()
1106 mImpl->mTextInput->mUpdateCursorPosition = true;
1109 void Controller::UpdateModel( OperationsMask operationsRequired )
1111 // Calculate the operations to be done.
1112 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1114 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1116 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
1118 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1119 if( GET_LINE_BREAKS & operations )
1121 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
1122 // calculate the bidirectional info for each 'paragraph'.
1123 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
1124 // is not shaped together).
1125 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
1127 SetLineBreakInfo( utf32Characters,
1131 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1132 if( GET_WORD_BREAKS & operations )
1134 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
1135 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
1137 SetWordBreakInfo( utf32Characters,
1141 const bool getScripts = GET_SCRIPTS & operations;
1142 const bool validateFonts = VALIDATE_FONTS & operations;
1144 Vector<ScriptRun>& scripts = mImpl->mLogicalModel->mScriptRuns;
1145 Vector<FontRun>& validFonts = mImpl->mLogicalModel->mFontRuns;
1147 if( getScripts || validateFonts )
1149 // Validates the fonts assigned by the application or assigns default ones.
1150 // It makes sure all the characters are going to be rendered by the correct font.
1151 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
1155 // Retrieves the scripts used in the text.
1156 multilanguageSupport.SetScripts( utf32Characters,
1163 if( 0u == validFonts.Count() )
1165 // Copy the requested font defaults received via the property system.
1166 // These may not be valid i.e. may not contain glyphs for the necessary scripts.
1167 GetDefaultFonts( validFonts, numberOfCharacters );
1170 // Validates the fonts. If there is a character with no assigned font it sets a default one.
1171 // After this call, fonts are validated.
1172 multilanguageSupport.ValidateFonts( utf32Characters,
1178 Vector<Character> mirroredUtf32Characters;
1179 bool textMirrored = false;
1180 if( BIDI_INFO & operations )
1182 // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
1183 // bidirectional info.
1185 Length numberOfParagraphs = 0u;
1187 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
1188 for( Length index = 0u; index < numberOfCharacters; ++index )
1190 if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
1192 ++numberOfParagraphs;
1196 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1197 bidirectionalInfo.Reserve( numberOfParagraphs );
1199 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1200 SetBidirectionalInfo( utf32Characters,
1203 bidirectionalInfo );
1205 if( 0u != bidirectionalInfo.Count() )
1207 // This paragraph has right to left text. Some characters may need to be mirrored.
1208 // TODO: consider if the mirrored string can be stored as well.
1210 textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
1214 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1215 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1216 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1217 if( SHAPE_TEXT & operations )
1219 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1221 ShapeText( textToShape,
1226 glyphsToCharactersMap,
1227 charactersPerGlyph );
1230 const Length numberOfGlyphs = glyphs.Count();
1232 if( GET_GLYPH_METRICS & operations )
1234 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
1237 if( 0u != numberOfGlyphs )
1239 // Create the glyph to character conversion table and the 'number of glyphs' per character.
1240 mImpl->mVisualModel->CreateCharacterToGlyphTable(numberOfCharacters );
1241 mImpl->mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
1245 bool Controller::DoRelayout( const Vector2& size,
1246 OperationsMask operationsRequired,
1249 bool viewUpdated( false );
1251 // Calculate the operations to be done.
1252 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1254 if( LAYOUT & operations )
1256 // Some vectors with data needed to layout and reorder may be void
1257 // after the first time the text has been laid out.
1258 // Fill the vectors again.
1260 Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
1262 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1263 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1264 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1265 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1266 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1268 // Set the layout parameters.
1269 LayoutParameters layoutParameters( size,
1270 mImpl->mLogicalModel->mText.Begin(),
1271 lineBreakInfo.Begin(),
1272 wordBreakInfo.Begin(),
1275 glyphsToCharactersMap.Begin(),
1276 charactersPerGlyph.Begin() );
1278 // The laid-out lines.
1279 // It's not possible to know in how many lines the text is going to be laid-out,
1280 // but it can be resized at least with the number of 'paragraphs' to avoid
1281 // some re-allocations.
1282 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1284 // Delete any previous laid out lines before setting the new ones.
1287 // The capacity of the bidirectional paragraph info is the number of paragraphs.
1288 lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
1290 // Resize the vector of positions to have the same size than the vector of glyphs.
1291 Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
1292 glyphPositions.Resize( numberOfGlyphs );
1294 // Update the visual model.
1295 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
1302 // Reorder the lines
1303 if( REORDER & operations )
1305 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1307 // Check first if there are paragraphs with bidirectional info.
1308 if( 0u != bidirectionalInfo.Count() )
1311 const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
1313 // Reorder the lines.
1314 Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
1315 lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
1316 ReorderLines( bidirectionalInfo,
1318 lineBidirectionalInfoRuns );
1320 // Set the bidirectional info into the model.
1321 const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
1322 mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
1323 numberOfBidirectionalInfoRuns );
1325 // Set the bidirectional info per line into the layout parameters.
1326 layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
1327 layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
1329 // Get the character to glyph conversion table and set into the layout.
1330 layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
1332 // Get the glyphs per character table and set into the layout.
1333 layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
1335 // Re-layout the text. Reorder those lines with right to left characters.
1336 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1339 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
1340 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
1341 endIt = lineBidirectionalInfoRuns.End();
1345 BidirectionalLineInfoRun& bidiLineInfo = *it;
1347 free( bidiLineInfo.visualToLogicalMap );
1352 if( ALIGN & operations )
1354 mImpl->mLayoutEngine.Align( layoutParameters,
1359 // Sets the actual size.
1360 if( UPDATE_ACTUAL_SIZE & operations )
1362 mImpl->mVisualModel->SetActualSize( layoutSize );
1368 layoutSize = mImpl->mVisualModel->GetActualSize();
1374 View& Controller::GetView()
1376 return mImpl->mView;
1379 LayoutEngine& Controller::GetLayoutEngine()
1381 return mImpl->mLayoutEngine;
1384 void Controller::RequestRelayout()
1386 mImpl->mControlInterface.RequestTextRelayout();
1389 void Controller::KeyboardFocusGainEvent()
1391 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
1393 if( mImpl->mTextInput )
1395 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
1396 mImpl->mTextInput->mEventQueue.push_back( event );
1402 void Controller::KeyboardFocusLostEvent()
1404 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
1406 if( mImpl->mTextInput )
1408 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
1409 mImpl->mTextInput->mEventQueue.push_back( event );
1415 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1417 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" );
1419 if( mImpl->mTextInput &&
1420 keyEvent.state == KeyEvent::Down )
1422 int keyCode = keyEvent.keyCode;
1423 const std::string& keyString = keyEvent.keyPressed;
1425 // Pre-process to separate modifying events from non-modifying input events.
1426 if( Dali::DALI_KEY_ESCAPE == keyCode )
1428 // Escape key is a special case which causes focus loss
1429 KeyboardFocusLostEvent();
1431 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
1432 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1433 Dali::DALI_KEY_CURSOR_UP == keyCode ||
1434 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1436 TextInput::Event event( TextInput::CURSOR_KEY_EVENT );
1437 event.p1.mInt = keyCode;
1438 mImpl->mTextInput->mEventQueue.push_back( event );
1440 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1442 // Queue a delete event
1444 event.type = DELETE_TEXT;
1445 mImpl->mModifyEvents.push_back( event );
1447 else if( !keyString.empty() )
1449 // Queue an insert event
1451 event.type = INSERT_TEXT;
1452 event.text = keyString;
1453 mImpl->mModifyEvents.push_back( event );
1462 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1464 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
1466 if( mImpl->mTextInput )
1468 TextInput::Event event( TextInput::TAP_EVENT );
1469 event.p1.mUint = tapCount;
1470 event.p2.mFloat = x;
1471 event.p3.mFloat = y;
1472 mImpl->mTextInput->mEventQueue.push_back( event );
1478 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1480 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected PanEvent" );
1482 if( mImpl->mTextInput )
1484 TextInput::Event event( TextInput::PAN_EVENT );
1485 event.p1.mInt = state;
1486 event.p2.mFloat = displacement.x;
1487 event.p3.mFloat = displacement.y;
1488 mImpl->mTextInput->mEventQueue.push_back( event );
1494 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
1496 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
1498 if( mImpl->mTextInput )
1500 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
1501 event.p1.mUint = state;
1502 event.p2.mFloat = x;
1503 event.p3.mFloat = y;
1504 mImpl->mTextInput->mEventQueue.push_back( event );
1510 Controller::~Controller()
1515 Controller::Controller( ControlInterface& controlInterface )
1518 mImpl = new Controller::Impl( controlInterface );
1523 } // namespace Toolkit