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();
50 REPLACE_TEXT, ///< Replace the entire text
51 INSERT_TEXT, ///< Insert characters at the current cursor position
52 DELETE_TEXT ///< Delete a character at the current cursor position
61 const std::string EMPTY_STRING("");
74 struct Controller::FontDefaults
77 : mDefaultPointSize(0.0f),
82 FontId GetFontId( TextAbstraction::FontClient& fontClient )
86 Dali::TextAbstraction::PointSize26Dot6 pointSize = mDefaultPointSize*64;
87 mFontId = fontClient.GetFontId( mDefaultFontFamily, mDefaultFontStyle, pointSize );
93 std::string mDefaultFontFamily;
94 std::string mDefaultFontStyle;
95 float mDefaultPointSize;
99 struct Controller::TextInput
101 // Used to queue input events until DoRelayout()
104 KEYBOARD_FOCUS_GAIN_EVENT,
105 KEYBOARD_FOCUS_LOST_EVENT,
121 Event( EventType eventType )
140 primaryCursorHeight( 0.f ),
141 secondaryCursorHeight( 0.f ),
142 isSecondaryCursor( false )
148 Vector2 primaryPosition; ///< The primary cursor's position.
149 Vector2 secondaryPosition; ///< The secondary cursor's position.
150 float lineHeight; ///< The height of the line where the cursor is placed.
151 float primaryCursorHeight; ///< The primary cursor's height.
152 float secondaryCursorHeight; ///< The secondary cursor's height.
153 bool isSecondaryCursor; ///< Whether the secondary cursor is valid.
157 * @brief Some characters can be shaped in more than one glyph.
158 * This struct is used to retrieve metrics from these group of glyphs.
172 float fontHeight; ///< The font's height of that glyphs.
173 float advance; ///< The sum of all the advances of all the glyphs.
174 float ascender; ///< The font's ascender.
175 float xBearing; ///< The x bearing of the first glyph.
186 TextInput( LogicalModelPtr logicalModel,
187 VisualModelPtr visualModel,
188 DecoratorPtr decorator,
189 FontDefaults* fontDefaults,
190 TextAbstraction::FontClient& fontClient )
191 : mLogicalModel( logicalModel ),
192 mVisualModel( visualModel ),
193 mDecorator( decorator ),
194 mFontDefaults( fontDefaults ),
195 mFontClient( fontClient ),
197 mPrimaryCursorPosition( 0u ),
198 mSecondaryCursorPosition( 0u ),
199 mDecoratorUpdated( false ),
200 mCursorBlinkEnabled( true ),
201 mGrabHandleEnabled( true ),
202 mGrabHandlePopupEnabled( true ),
203 mSelectionEnabled( true ),
204 mHorizontalScrollingEnabled( true ),
205 mVerticalScrollingEnabled( false ),
206 mUpdateCursorPosition( false )
210 * @brief Helper to move the cursor, grab handle etc.
212 bool ProcessInputEvents( const Vector2& controlSize,
213 const Vector2& alignmentOffset )
215 mDecoratorUpdated = false;
219 for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
223 case KEYBOARD_FOCUS_GAIN_EVENT:
225 OnKeyboardFocus( true );
228 case KEYBOARD_FOCUS_LOST_EVENT:
230 OnKeyboardFocus( false );
233 case CURSOR_KEY_EVENT:
235 OnCursorKeyEvent( *iter );
240 OnTapEvent( *iter, alignmentOffset );
245 OnPanEvent( *iter, controlSize, alignmentOffset );
248 case GRAB_HANDLE_EVENT:
250 OnGrabHandleEvent( *iter );
257 // The cursor must also be repositioned after inserts into the model
258 if( mUpdateCursorPosition )
260 UpdateCursorPosition();
261 mUpdateCursorPosition = false;
266 return mDecoratorUpdated;
269 void OnKeyboardFocus( bool hasFocus )
273 ChangeState( INACTIVE );
277 ChangeState( EDITING );
281 void OnCursorKeyEvent( const Event& event )
283 int keyCode = event.p1.mInt;
285 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
287 if( mPrimaryCursorPosition > 0u )
289 mPrimaryCursorPosition = CalculateNewCursorIndex( mPrimaryCursorPosition - 1u );
292 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
294 if( mLogicalModel->GetNumberOfCharacters() > mPrimaryCursorPosition )
296 mPrimaryCursorPosition = CalculateNewCursorIndex( mPrimaryCursorPosition );
299 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
303 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
308 UpdateCursorPosition();
311 void HandleCursorKey( int keyCode )
316 void OnTapEvent( const Event& event,
317 const Vector2& alignmentOffset )
319 unsigned int tapCount = event.p1.mUint;
323 ChangeState( EDITING );
325 float xPosition = event.p2.mFloat - alignmentOffset.x;
326 float yPosition = event.p3.mFloat - alignmentOffset.y;
328 mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
331 UpdateCursorPosition();
333 else if( mSelectionEnabled &&
336 ChangeState( SELECTING );
338 RepositionSelectionHandles( event.p2.mFloat, event.p3.mFloat );
342 void OnPanEvent( const Event& event,
343 const Vector2& controlSize,
344 const Vector2& alignmentOffset )
346 int state = event.p1.mInt;
348 if( Gesture::Started == state ||
349 Gesture::Continuing == state )
351 const Vector2& actualSize = mVisualModel->GetActualSize();
353 if( mHorizontalScrollingEnabled )
355 const float displacementX = event.p2.mFloat;
356 mScrollPosition.x += displacementX;
358 // Clamp between -space & 0 (and the text alignment).
359 const float contentWidth = actualSize.width;
360 if( contentWidth > controlSize.width )
362 const float space = ( contentWidth - controlSize.width ) + alignmentOffset.x;
363 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
364 mScrollPosition.x = ( mScrollPosition.x > -alignmentOffset.x ) ? -alignmentOffset.x : mScrollPosition.x;
366 mDecoratorUpdated = true;
370 mScrollPosition.x = 0.f;
374 if( mVerticalScrollingEnabled )
376 const float displacementY = event.p3.mFloat;
377 mScrollPosition.y += displacementY;
379 // Clamp between -space & 0 (and the text alignment).
380 if( actualSize.height > controlSize.height )
382 const float space = ( actualSize.height - controlSize.height ) + alignmentOffset.y;
383 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
384 mScrollPosition.y = ( mScrollPosition.y > -alignmentOffset.y ) ? -alignmentOffset.y : mScrollPosition.y;
386 mDecoratorUpdated = true;
390 mScrollPosition.y = 0.f;
396 void OnGrabHandleEvent( const Event& event )
398 unsigned int state = event.p1.mUint;
400 if( GRAB_HANDLE_PRESSED == state )
402 float xPosition = event.p2.mFloat + mScrollPosition.x;
403 float yPosition = event.p3.mFloat + mScrollPosition.y;
405 mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
408 UpdateCursorPosition();
410 //mDecorator->HidePopup();
411 ChangeState ( EDITING );
413 else if ( mGrabHandlePopupEnabled &&
414 GRAB_HANDLE_RELEASED == state )
416 //mDecorator->ShowPopup();
417 ChangeState ( EDITING_WITH_POPUP );
418 mDecoratorUpdated = true;
422 void RepositionSelectionHandles( float visualX, float visualY )
424 // TODO - Find which word was selected
426 const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
427 const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
429 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
430 const Vector<Vector2>::SizeType positionCount = positions.Count();
432 // Guard against glyphs which did not fit inside the layout
433 const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
437 float primaryX = positions[0].x;
438 float secondaryX = positions[count-1].x + glyphs[count-1].width;
440 // TODO - multi-line selection
441 const Vector<LineRun>& lines = mVisualModel->mLines;
442 float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
444 mDecorator->SetPosition( PRIMARY_SELECTION_HANDLE, primaryX, 0.0f, height );
445 mDecorator->SetPosition( SECONDARY_SELECTION_HANDLE, secondaryX, 0.0f, height );
447 mDecorator->ClearHighlights();
448 mDecorator->AddHighlight( primaryX, 0.0f, secondaryX, height );
452 void ChangeState( State newState )
454 if( mState != newState )
458 if( INACTIVE == mState )
460 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
461 mDecorator->StopCursorBlink();
462 mDecorator->SetGrabHandleActive( false );
463 mDecorator->SetSelectionActive( false );
464 mDecorator->SetPopupActive( false );
465 mDecoratorUpdated = true;
467 else if ( SELECTING == mState )
469 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
470 mDecorator->StopCursorBlink();
471 mDecorator->SetGrabHandleActive( false );
472 mDecorator->SetSelectionActive( true );
473 mDecoratorUpdated = true;
475 else if( EDITING == mState )
477 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
478 if( mCursorBlinkEnabled )
480 mDecorator->StartCursorBlink();
482 if( mGrabHandleEnabled )
484 mDecorator->SetGrabHandleActive( true );
486 if( mGrabHandlePopupEnabled )
488 mDecorator->SetPopupActive( false );
490 mDecorator->SetSelectionActive( false );
491 mDecoratorUpdated = true;
493 else if( EDITING_WITH_POPUP == mState )
495 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
496 if( mCursorBlinkEnabled )
498 mDecorator->StartCursorBlink();
500 if( mGrabHandleEnabled )
502 mDecorator->SetGrabHandleActive( true );
504 if( mGrabHandlePopupEnabled )
506 mDecorator->SetPopupActive( true );
508 mDecorator->SetSelectionActive( false );
509 mDecoratorUpdated = true;
514 LineIndex GetClosestLine( float y ) const
516 float totalHeight = 0.f;
517 LineIndex lineIndex = 0u;
519 const Vector<LineRun>& lines = mVisualModel->mLines;
520 for( LineIndex endLine = lines.Count();
524 const LineRun& lineRun = lines[lineIndex];
525 totalHeight += lineRun.ascender + -lineRun.descender;
526 if( y < totalHeight )
536 * @brief Retrieves the cursor's logical position for a given touch point x,y
538 * @param[in] visualX The touch point x.
539 * @param[in] visualY The touch point y.
541 * @return The logical cursor position (in characters). 0 is just before the first character, a value equal to the number of characters is just after the last character.
543 CharacterIndex GetClosestCursorIndex( float visualX,
544 float visualY ) const
546 CharacterIndex logicalIndex = 0u;
548 const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
549 const Length numberOfLines = mVisualModel->mLines.Count();
550 if( 0 == numberOfGlyphs ||
556 // Transform to visual model coords
557 visualX -= mScrollPosition.x;
558 visualY -= mScrollPosition.y;
560 // Find which line is closest
561 const LineIndex lineIndex = GetClosestLine( visualY );
562 const LineRun& line = mVisualModel->mLines[lineIndex];
564 // Get the positions of the glyphs.
565 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
566 const Vector2* const positionsBuffer = positions.Begin();
568 // Get the visual to logical conversion tables.
569 const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
570 const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
572 // Get the character to glyph conversion table.
573 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
575 // Get the glyphs per character table.
576 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
578 // If the vector is void, there is no right to left characters.
579 const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
581 const CharacterIndex startCharacter = line.characterRun.characterIndex;
582 const CharacterIndex endCharacter = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
583 DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
585 // Whether there is a hit on a glyph.
586 bool matched = false;
588 // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
589 CharacterIndex visualIndex = startCharacter;
590 for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
592 // The character in logical order.
593 const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
595 // The first glyph for that character in logical order.
596 const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
598 // The number of glyphs for that character
599 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
601 // Get the metrics for the group of glyphs.
602 GlyphMetrics glyphMetrics;
603 GetGlyphsMetrics( glyphLogicalOrderIndex,
607 const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
609 const float glyphX = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
611 if( visualX < glyphX )
618 // Return the logical position of the cursor in characters.
622 visualIndex = endCharacter;
625 return hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
629 * @brief Calculates the cursor's position for a given character index in the logical order.
631 * It retrieves as well the line's height and the cursor's height and
632 * if there is a valid alternative cursor, its position and height.
634 * @param[in] logical The logical cursor position (in characters). 0 is just before the first character, a value equal to the number of characters is just after the last character.
635 * @param[out] cursorInfo The line's height, the cursor's height, the cursor's position and whether there is an alternative cursor.
637 void GetCursorPosition( CharacterIndex logical,
638 CursorInfo& cursorInfo ) const
640 // TODO: Check for multiline with \n, etc...
642 // Check if the logical position is the first or the last one of the text.
643 const bool isFirstPosition = 0u == logical;
644 const bool isLastPosition = mLogicalModel->GetNumberOfCharacters() == logical;
646 if( isFirstPosition && isLastPosition )
648 // There is zero characters. Get the default font.
650 FontId defaultFontId = 0u;
651 if( NULL == mFontDefaults )
653 defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
658 defaultFontId = mFontDefaults->GetFontId( mFontClient );
661 Text::FontMetrics fontMetrics;
662 mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
664 cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
665 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
667 cursorInfo.primaryPosition.x = 0.f;
668 cursorInfo.primaryPosition.y = 0.f;
670 // Nothing else to do.
674 // Get the previous logical index.
675 const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
677 // Decrease the logical index if it's the last one.
683 // Get the direction of the character and the previous one.
684 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
686 CharacterDirection isCurrentRightToLeft = false;
687 CharacterDirection isPreviousRightToLeft = false;
688 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
690 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
691 isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
694 // Get the line where the character is laid-out.
695 const LineRun* modelLines = mVisualModel->mLines.Begin();
697 const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
698 const LineRun& line = *( modelLines + lineIndex );
700 // Get the paragraph's direction.
701 const CharacterDirection isRightToLeftParagraph = line.direction;
703 // Check whether there is an alternative position:
705 cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
706 ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
708 // Set the line height.
709 cursorInfo.lineHeight = line.ascender + -line.descender;
711 // Convert the cursor position into the glyph position.
712 CharacterIndex characterIndex = logical;
713 if( cursorInfo.isSecondaryCursor &&
714 ( isRightToLeftParagraph != isCurrentRightToLeft ) )
716 characterIndex = previousLogical;
719 const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
720 const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
721 const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
723 // Get the metrics for the group of glyphs.
724 GlyphMetrics glyphMetrics;
725 GetGlyphsMetrics( currentGlyphIndex,
729 float interGlyphAdvance = 0.f;
730 if( !isLastPosition &&
731 ( numberOfCharacters > 1u ) )
733 const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
734 interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
737 // Get the glyph position and x bearing.
738 const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
740 // Set the cursor's height.
741 cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
744 cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
745 cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
749 // The position of the cursor after the last character needs special
750 // care depending on its direction and the direction of the paragraph.
752 if( cursorInfo.isSecondaryCursor )
754 // Need to find the first character after the last character with the paragraph's direction.
755 // i.e l0 l1 l2 r0 r1 should find r0.
757 // TODO: check for more than one line!
758 characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
759 characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
761 const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
762 const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
764 const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
766 // Get the metrics for the group of glyphs.
767 GlyphMetrics glyphMetrics;
768 GetGlyphsMetrics( glyphIndex,
772 cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
774 cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
778 if( !isCurrentRightToLeft )
780 cursorInfo.primaryPosition.x += glyphMetrics.advance;
784 cursorInfo.primaryPosition.x -= glyphMetrics.advance;
789 // Set the alternative cursor position.
790 if( cursorInfo.isSecondaryCursor )
792 // Convert the cursor position into the glyph position.
793 const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
794 const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
795 const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
797 // Get the glyph position.
798 const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
800 // Get the metrics for the group of glyphs.
801 GlyphMetrics glyphMetrics;
802 GetGlyphsMetrics( previousGlyphIndex,
806 // Set the cursor position and height.
807 cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
808 ( !isLastPosition && isCurrentRightToLeft ) ) ? glyphMetrics.advance : 0.f );
810 cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
812 cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
814 // Update the primary cursor height as well.
815 cursorInfo.primaryCursorHeight *= 0.5f;
820 * @brief Get some glyph's metrics of a group of glyphs formed as a result of shaping one character.
822 * @param[in] glyphIndex The index to the first glyph.
823 * @param[in] numberOfGlyphs The number of glyphs.
824 * @param[out] glyphMetrics Some glyph metrics (font height, advance, ascender and x bearing).
826 void GetGlyphsMetrics( GlyphIndex glyphIndex,
827 Length numberOfGlyphs,
828 GlyphMetrics& glyphMetrics ) const
830 const GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin();
832 const GlyphInfo& firstGlyph = *( glyphsBuffer + glyphIndex );
834 Text::FontMetrics fontMetrics;
835 mFontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
837 glyphMetrics.fontHeight = fontMetrics.height;
838 glyphMetrics.advance = firstGlyph.advance;
839 glyphMetrics.ascender = fontMetrics.ascender;
840 glyphMetrics.xBearing = firstGlyph.xBearing;
842 for( unsigned int i = 1u; i < numberOfGlyphs; ++i )
844 const GlyphInfo& glyphInfo = *( glyphsBuffer + glyphIndex + i );
846 glyphMetrics.advance += glyphInfo.advance;
851 * @brief Calculates the new cursor index.
853 * It takes into account that in some scripts multiple characters can form a glyph and all of them
854 * need to be jumped with one key event.
856 * @param[in] index The initial new index.
858 * @return The new cursor index.
860 CharacterIndex CalculateNewCursorIndex( CharacterIndex index ) const
862 CharacterIndex cursorIndex = mPrimaryCursorPosition;
864 const Script script = mLogicalModel->GetScript( index );
865 const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
866 const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
868 Length numberOfCharacters = 0u;
869 if( TextAbstraction::LATIN == script )
871 // Prevents to jump the whole Latin ligatures like fi, ff, ...
872 numberOfCharacters = 1u;
876 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
877 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
879 while( 0u == numberOfCharacters )
881 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
886 if( index < mPrimaryCursorPosition )
888 cursorIndex -= numberOfCharacters;
892 cursorIndex += numberOfCharacters;
898 void UpdateCursorPosition()
900 CursorInfo cursorInfo;
902 GetCursorPosition( mPrimaryCursorPosition,
905 mDecorator->SetPosition( PRIMARY_CURSOR,
906 cursorInfo.primaryPosition.x,
907 cursorInfo.primaryPosition.y,
908 cursorInfo.primaryCursorHeight,
909 cursorInfo.lineHeight );
911 if( cursorInfo.isSecondaryCursor )
913 mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
914 mDecorator->SetPosition( SECONDARY_CURSOR,
915 cursorInfo.secondaryPosition.x,
916 cursorInfo.secondaryPosition.y,
917 cursorInfo.secondaryCursorHeight,
918 cursorInfo.lineHeight );
922 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
925 mUpdateCursorPosition = false;
926 mDecoratorUpdated = true;
929 LogicalModelPtr mLogicalModel;
930 VisualModelPtr mVisualModel;
931 DecoratorPtr mDecorator;
932 FontDefaults* mFontDefaults;
933 TextAbstraction::FontClient& mFontClient;
934 std::string mPlaceholderText;
937 * This is used to delay handling events until after the model has been updated.
938 * The number of updates to the model is minimized to improve performance.
940 vector<Event> mEventQueue; ///< The queue of touch events etc.
942 State mState; ///< Selection mode, edit mode etc.
944 CharacterIndex mPrimaryCursorPosition; ///< Index into logical model for primary cursor
945 CharacterIndex mSecondaryCursorPosition; ///< Index into logical model for secondary cursor
948 * 0,0 means that the top-left corner of the layout matches the top-left corner of the UI control.
949 * Typically this will have a negative value with scrolling occurs.
951 Vector2 mScrollPosition; ///< The text is offset by this position when scrolling.
953 bool mDecoratorUpdated : 1; ///< True if the decorator was updated during event processing
954 bool mCursorBlinkEnabled : 1; ///< True if cursor should blink when active
955 bool mGrabHandleEnabled : 1; ///< True if grab handle is enabled
956 bool mGrabHandlePopupEnabled : 1; ///< True if the grab handle popu-up should be shown
957 bool mSelectionEnabled : 1; ///< True if selection handles, highlight etc. are enabled
958 bool mHorizontalScrollingEnabled : 1; ///< True if horizontal scrolling is enabled
959 bool mVerticalScrollingEnabled : 1; ///< True if vertical scrolling is enabled
960 bool mUpdateCursorPosition : 1; ///< True if the visual position of the cursor must be recalculated
963 struct Controller::Impl
965 Impl( ControlInterface& controlInterface )
966 : mControlInterface( controlInterface ),
969 mFontDefaults( NULL ),
977 mOperationsPending( NO_OPERATION ),
978 mRecalculateNaturalSize( true )
980 mLogicalModel = LogicalModel::New();
981 mVisualModel = VisualModel::New();
983 mFontClient = TextAbstraction::FontClient::Get();
985 mView.SetVisualModel( mVisualModel );
987 // Set the text properties to default
988 mVisualModel->SetTextColor( Color::WHITE );
989 mVisualModel->SetShadowOffset( Vector2::ZERO );
990 mVisualModel->SetShadowColor( Vector4::ZERO );
991 mVisualModel->SetUnderlineEnabled( false );
999 ControlInterface& mControlInterface; ///< Reference to the text controller.
1000 LogicalModelPtr mLogicalModel; ///< Pointer to the logical model.
1001 VisualModelPtr mVisualModel; ///< Pointer to the visual model.
1002 FontDefaults* mFontDefaults; ///< Avoid allocating this when the user does not specify a font.
1003 Controller::TextInput* mTextInput; ///< Avoid allocating everything for text input until EnableTextInput().
1004 TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
1005 View mView; ///< The view interface to the rendering back-end.
1006 LayoutEngine mLayoutEngine; ///< The layout engine.
1007 std::vector<ModifyEvent> mModifyEvents; ///< Temporary stores the text set until the next relayout.
1008 Size mControlSize; ///< The size of the control.
1009 Vector2 mAlignmentOffset; ///< Vertical and horizontal offset of the whole text inside the control due to alignment.
1010 OperationsMask mOperationsPending; ///< Operations pending to be done to layout the text.
1011 bool mRecalculateNaturalSize:1; ///< Whether the natural size needs to be recalculated.
1014 ControllerPtr Controller::New( ControlInterface& controlInterface )
1016 return ControllerPtr( new Controller( controlInterface ) );
1019 void Controller::SetText( const std::string& text )
1021 // Cancel previously queued inserts etc.
1022 mImpl->mModifyEvents.clear();
1024 // Keep until size negotiation
1026 event.type = REPLACE_TEXT;
1028 mImpl->mModifyEvents.push_back( event );
1030 if( mImpl->mTextInput )
1032 // Cancel previously queued events
1033 mImpl->mTextInput->mEventQueue.clear();
1035 // TODO - Hide selection decorations
1039 void Controller::GetText( std::string& text ) const
1041 if( !mImpl->mModifyEvents.empty() &&
1042 REPLACE_TEXT == mImpl->mModifyEvents[0].type )
1044 text = mImpl->mModifyEvents[0].text;
1048 // TODO - Convert from UTF-32
1052 void Controller::SetPlaceholderText( const std::string& text )
1054 if( !mImpl->mTextInput )
1056 mImpl->mTextInput->mPlaceholderText = text;
1060 void Controller::GetPlaceholderText( std::string& text ) const
1062 if( !mImpl->mTextInput )
1064 text = mImpl->mTextInput->mPlaceholderText;
1068 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
1070 if( !mImpl->mFontDefaults )
1072 mImpl->mFontDefaults = new Controller::FontDefaults();
1075 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
1076 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1077 mImpl->mOperationsPending = ALL_OPERATIONS;
1078 mImpl->mRecalculateNaturalSize = true;
1080 // Clear the font-specific data
1081 mImpl->mLogicalModel->mFontRuns.Clear();
1082 mImpl->mVisualModel->mGlyphs.Clear();
1083 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1084 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1085 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1086 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1087 mImpl->mVisualModel->mGlyphPositions.Clear();
1088 mImpl->mVisualModel->mLines.Clear();
1089 mImpl->mVisualModel->ClearCaches();
1094 const std::string& Controller::GetDefaultFontFamily() const
1096 if( mImpl->mFontDefaults )
1098 return mImpl->mFontDefaults->mDefaultFontFamily;
1101 return EMPTY_STRING;
1104 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
1106 if( !mImpl->mFontDefaults )
1108 mImpl->mFontDefaults = new Controller::FontDefaults();
1111 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
1112 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1113 mImpl->mOperationsPending = ALL_OPERATIONS;
1114 mImpl->mRecalculateNaturalSize = true;
1116 // Clear the font-specific data
1117 mImpl->mLogicalModel->mFontRuns.Clear();
1118 mImpl->mVisualModel->mGlyphs.Clear();
1119 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1120 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1121 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1122 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1123 mImpl->mVisualModel->mGlyphPositions.Clear();
1124 mImpl->mVisualModel->mLines.Clear();
1125 mImpl->mVisualModel->ClearCaches();
1130 const std::string& Controller::GetDefaultFontStyle() const
1132 if( mImpl->mFontDefaults )
1134 return mImpl->mFontDefaults->mDefaultFontStyle;
1137 return EMPTY_STRING;
1140 void Controller::SetDefaultPointSize( float pointSize )
1142 if( !mImpl->mFontDefaults )
1144 mImpl->mFontDefaults = new Controller::FontDefaults();
1147 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
1148 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1149 mImpl->mOperationsPending = ALL_OPERATIONS;
1150 mImpl->mRecalculateNaturalSize = true;
1152 // Clear the font-specific data
1153 mImpl->mLogicalModel->mFontRuns.Clear();
1154 mImpl->mVisualModel->mGlyphs.Clear();
1155 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1156 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1157 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1158 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1159 mImpl->mVisualModel->mGlyphPositions.Clear();
1160 mImpl->mVisualModel->mLines.Clear();
1161 mImpl->mVisualModel->ClearCaches();
1166 float Controller::GetDefaultPointSize() const
1168 if( mImpl->mFontDefaults )
1170 return mImpl->mFontDefaults->mDefaultPointSize;
1176 void Controller::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters ) const
1178 if( mImpl->mFontDefaults )
1181 fontRun.characterRun.characterIndex = 0;
1182 fontRun.characterRun.numberOfCharacters = numberOfCharacters;
1183 fontRun.fontId = mImpl->mFontDefaults->GetFontId( mImpl->mFontClient );
1184 fontRun.isDefault = true;
1186 fonts.PushBack( fontRun );
1190 const Vector4& Controller::GetTextColor() const
1192 return mImpl->mVisualModel->GetTextColor();
1195 const Vector2& Controller::GetShadowOffset() const
1197 return mImpl->mVisualModel->GetShadowOffset();
1200 const Vector4& Controller::GetShadowColor() const
1202 return mImpl->mVisualModel->GetShadowColor();
1205 const Vector4& Controller::GetUnderlineColor() const
1207 return mImpl->mVisualModel->GetUnderlineColor();
1210 bool Controller::IsUnderlineEnabled() const
1212 return mImpl->mVisualModel->IsUnderlineEnabled();
1215 void Controller::SetTextColor( const Vector4& textColor )
1217 mImpl->mVisualModel->SetTextColor( textColor );
1220 void Controller::SetShadowOffset( const Vector2& shadowOffset )
1222 mImpl->mVisualModel->SetShadowOffset( shadowOffset );
1225 void Controller::SetShadowColor( const Vector4& shadowColor )
1227 mImpl->mVisualModel->SetShadowColor( shadowColor );
1230 void Controller::SetUnderlineColor( const Vector4& color )
1232 mImpl->mVisualModel->SetUnderlineColor( color );
1235 void Controller::SetUnderlineEnabled( bool enabled )
1237 mImpl->mVisualModel->SetUnderlineEnabled( enabled );
1240 void Controller::EnableTextInput( DecoratorPtr decorator )
1242 if( !mImpl->mTextInput )
1244 mImpl->mTextInput = new TextInput( mImpl->mLogicalModel,
1245 mImpl->mVisualModel,
1247 mImpl->mFontDefaults,
1248 mImpl->mFontClient );
1252 void Controller::SetEnableCursorBlink( bool enable )
1254 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "TextInput disabled" );
1256 if( mImpl->mTextInput )
1258 mImpl->mTextInput->mCursorBlinkEnabled = enable;
1261 mImpl->mTextInput->mDecorator )
1263 mImpl->mTextInput->mDecorator->StopCursorBlink();
1268 bool Controller::GetEnableCursorBlink() const
1270 if( mImpl->mTextInput )
1272 return mImpl->mTextInput->mCursorBlinkEnabled;
1278 const Vector2& Controller::GetScrollPosition() const
1280 if( mImpl->mTextInput )
1282 return mImpl->mTextInput->mScrollPosition;
1285 return Vector2::ZERO;
1288 const Vector2& Controller::GetAlignmentOffset() const
1290 return mImpl->mAlignmentOffset;
1293 Vector3 Controller::GetNaturalSize()
1295 Vector3 naturalSize;
1297 // Make sure the model is up-to-date before layouting
1298 ProcessModifyEvents();
1300 if( mImpl->mRecalculateNaturalSize )
1302 // Operations that can be done only once until the text changes.
1303 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
1310 GET_GLYPH_METRICS );
1311 // Make sure the model is up-to-date before layouting
1312 UpdateModel( onlyOnceOperations );
1314 // Operations that need to be done if the size changes.
1315 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
1319 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
1320 static_cast<OperationsMask>( onlyOnceOperations |
1322 naturalSize.GetVectorXY() );
1324 // Do not do again the only once operations.
1325 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1327 // Do the size related operations again.
1328 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1330 // Stores the natural size to avoid recalculate it again
1331 // unless the text/style changes.
1332 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
1334 mImpl->mRecalculateNaturalSize = false;
1338 naturalSize = mImpl->mVisualModel->GetNaturalSize();
1344 float Controller::GetHeightForWidth( float width )
1346 // Make sure the model is up-to-date before layouting
1347 ProcessModifyEvents();
1350 if( width != mImpl->mControlSize.width )
1352 // Operations that can be done only once until the text changes.
1353 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
1360 GET_GLYPH_METRICS );
1361 // Make sure the model is up-to-date before layouting
1362 UpdateModel( onlyOnceOperations );
1364 // Operations that need to be done if the size changes.
1365 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
1369 DoRelayout( Size( width, MAX_FLOAT ),
1370 static_cast<OperationsMask>( onlyOnceOperations |
1374 // Do not do again the only once operations.
1375 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1377 // Do the size related operations again.
1378 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1382 layoutSize = mImpl->mVisualModel->GetActualSize();
1385 return layoutSize.height;
1388 bool Controller::Relayout( const Size& size )
1390 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
1392 bool glyphsRemoved( false );
1393 if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
1395 mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
1396 glyphsRemoved = true;
1399 // Not worth to relayout if width or height is equal to zero.
1400 return glyphsRemoved;
1403 if( size != mImpl->mControlSize )
1405 // Operations that need to be done if the size changes.
1406 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1409 UPDATE_ACTUAL_SIZE |
1412 mImpl->mControlSize = size;
1415 // Make sure the model is up-to-date before layouting
1416 ProcessModifyEvents();
1417 UpdateModel( mImpl->mOperationsPending );
1420 bool updated = DoRelayout( mImpl->mControlSize,
1421 mImpl->mOperationsPending,
1424 // Do not re-do any operation until something changes.
1425 mImpl->mOperationsPending = NO_OPERATION;
1427 // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
1428 CalculateTextAlignment( size );
1430 if( mImpl->mTextInput )
1432 // Move the cursor, grab handle etc.
1433 updated = mImpl->mTextInput->ProcessInputEvents( mImpl->mControlSize, mImpl->mAlignmentOffset ) || updated;
1439 void Controller::ProcessModifyEvents()
1441 std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
1443 for( unsigned int i=0; i<events.size(); ++i )
1445 if( REPLACE_TEXT == events[0].type )
1447 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
1448 DALI_ASSERT_DEBUG( 0 == i && "Unexpected REPLACE event" );
1450 ReplaceTextEvent( events[0].text );
1452 else if( INSERT_TEXT == events[0].type )
1454 InsertTextEvent( events[0].text );
1456 else if( DELETE_TEXT == events[0].type )
1462 // Discard temporary text
1466 void Controller::ReplaceTextEvent( const std::string& text )
1469 mImpl->mLogicalModel->mText.Clear();
1470 mImpl->mLogicalModel->mScriptRuns.Clear();
1471 mImpl->mLogicalModel->mFontRuns.Clear();
1472 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1473 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1474 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1475 mImpl->mLogicalModel->mCharacterDirections.Clear();
1476 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1477 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1478 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1479 mImpl->mVisualModel->mGlyphs.Clear();
1480 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1481 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1482 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1483 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1484 mImpl->mVisualModel->mGlyphPositions.Clear();
1485 mImpl->mVisualModel->mLines.Clear();
1486 mImpl->mVisualModel->ClearCaches();
1488 // Convert text into UTF-32
1489 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1490 utf32Characters.Resize( text.size() );
1492 // This is a bit horrible but std::string returns a (signed) char*
1493 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1495 // Transform a text array encoded in utf8 into an array encoded in utf32.
1496 // It returns the actual number of characters.
1497 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1498 utf32Characters.Resize( characterCount );
1500 // Reset the cursor position
1501 if( mImpl->mTextInput )
1503 mImpl->mTextInput->mPrimaryCursorPosition = characterCount;
1504 // TODO - handle secondary cursor
1507 // The natural size needs to be re-calculated.
1508 mImpl->mRecalculateNaturalSize = true;
1510 // Apply modifications to the model
1511 mImpl->mOperationsPending = ALL_OPERATIONS;
1512 UpdateModel( ALL_OPERATIONS );
1513 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1515 UPDATE_ACTUAL_SIZE |
1519 void Controller::InsertTextEvent( const std::string& text )
1521 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1523 // TODO - Optimize this
1524 mImpl->mLogicalModel->mScriptRuns.Clear();
1525 mImpl->mLogicalModel->mFontRuns.Clear();
1526 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1527 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1528 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1529 mImpl->mLogicalModel->mCharacterDirections.Clear();
1530 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1531 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1532 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1533 mImpl->mVisualModel->mGlyphs.Clear();
1534 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1535 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1536 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1537 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1538 mImpl->mVisualModel->mGlyphPositions.Clear();
1539 mImpl->mVisualModel->mLines.Clear();
1540 mImpl->mVisualModel->ClearCaches();
1542 // Convert text into UTF-32
1543 Vector<Character> utf32Characters;
1544 utf32Characters.Resize( text.size() );
1546 // This is a bit horrible but std::string returns a (signed) char*
1547 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1549 // Transform a text array encoded in utf8 into an array encoded in utf32.
1550 // It returns the actual number of characters.
1551 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1552 utf32Characters.Resize( characterCount );
1554 // Insert at current cursor position
1555 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1556 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1558 if( cursorIndex < modifyText.Count() )
1560 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.End() );
1564 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.End() );
1567 // Advance the cursor position
1570 // The natural size needs to be re-calculated.
1571 mImpl->mRecalculateNaturalSize = true;
1573 // Apply modifications to the model; TODO - Optimize this
1574 mImpl->mOperationsPending = ALL_OPERATIONS;
1575 UpdateModel( ALL_OPERATIONS );
1576 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1578 UPDATE_ACTUAL_SIZE |
1581 // Queue a cursor reposition event; this must wait until after DoRelayout()
1582 mImpl->mTextInput->mUpdateCursorPosition = true;
1585 void Controller::DeleteTextEvent()
1587 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1589 // TODO - Optimize this
1590 mImpl->mLogicalModel->mScriptRuns.Clear();
1591 mImpl->mLogicalModel->mFontRuns.Clear();
1592 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1593 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1594 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1595 mImpl->mLogicalModel->mCharacterDirections.Clear();
1596 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1597 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1598 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1599 mImpl->mVisualModel->mGlyphs.Clear();
1600 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1601 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1602 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1603 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1604 mImpl->mVisualModel->mGlyphPositions.Clear();
1605 mImpl->mVisualModel->mLines.Clear();
1606 mImpl->mVisualModel->ClearCaches();
1608 // Delte at current cursor position
1609 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1610 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1612 if( cursorIndex > 0 &&
1613 cursorIndex-1 < modifyText.Count() )
1615 modifyText.Remove( modifyText.Begin() + cursorIndex - 1 );
1617 // Cursor position retreat
1621 // The natural size needs to be re-calculated.
1622 mImpl->mRecalculateNaturalSize = true;
1624 // Apply modifications to the model; TODO - Optimize this
1625 mImpl->mOperationsPending = ALL_OPERATIONS;
1626 UpdateModel( ALL_OPERATIONS );
1627 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1629 UPDATE_ACTUAL_SIZE |
1632 // Queue a cursor reposition event; this must wait until after DoRelayout()
1633 mImpl->mTextInput->mUpdateCursorPosition = true;
1636 void Controller::UpdateModel( OperationsMask operationsRequired )
1638 // Calculate the operations to be done.
1639 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1641 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1643 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
1645 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1646 if( GET_LINE_BREAKS & operations )
1648 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
1649 // calculate the bidirectional info for each 'paragraph'.
1650 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
1651 // is not shaped together).
1652 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
1654 SetLineBreakInfo( utf32Characters,
1658 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1659 if( GET_WORD_BREAKS & operations )
1661 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
1662 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
1664 SetWordBreakInfo( utf32Characters,
1668 const bool getScripts = GET_SCRIPTS & operations;
1669 const bool validateFonts = VALIDATE_FONTS & operations;
1671 Vector<ScriptRun>& scripts = mImpl->mLogicalModel->mScriptRuns;
1672 Vector<FontRun>& validFonts = mImpl->mLogicalModel->mFontRuns;
1674 if( getScripts || validateFonts )
1676 // Validates the fonts assigned by the application or assigns default ones.
1677 // It makes sure all the characters are going to be rendered by the correct font.
1678 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
1682 // Retrieves the scripts used in the text.
1683 multilanguageSupport.SetScripts( utf32Characters,
1690 if( 0u == validFonts.Count() )
1692 // Copy the requested font defaults received via the property system.
1693 // These may not be valid i.e. may not contain glyphs for the necessary scripts.
1694 GetDefaultFonts( validFonts, numberOfCharacters );
1697 // Validates the fonts. If there is a character with no assigned font it sets a default one.
1698 // After this call, fonts are validated.
1699 multilanguageSupport.ValidateFonts( utf32Characters,
1705 Vector<Character> mirroredUtf32Characters;
1706 bool textMirrored = false;
1707 if( BIDI_INFO & operations )
1709 // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
1710 // bidirectional info.
1712 Length numberOfParagraphs = 0u;
1714 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
1715 for( Length index = 0u; index < numberOfCharacters; ++index )
1717 if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
1719 ++numberOfParagraphs;
1723 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1724 bidirectionalInfo.Reserve( numberOfParagraphs );
1726 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1727 SetBidirectionalInfo( utf32Characters,
1730 bidirectionalInfo );
1732 if( 0u != bidirectionalInfo.Count() )
1734 // This paragraph has right to left text. Some characters may need to be mirrored.
1735 // TODO: consider if the mirrored string can be stored as well.
1737 textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
1739 // Only set the character directions if there is right to left characters.
1740 Vector<CharacterDirection>& directions = mImpl->mLogicalModel->mCharacterDirections;
1741 directions.Resize( numberOfCharacters );
1743 GetCharactersDirection( bidirectionalInfo,
1748 // There is no right to left characters. Clear the directions vector.
1749 mImpl->mLogicalModel->mCharacterDirections.Clear();
1754 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1755 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1756 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1757 if( SHAPE_TEXT & operations )
1759 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1761 ShapeText( textToShape,
1766 glyphsToCharactersMap,
1767 charactersPerGlyph );
1769 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1770 mImpl->mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
1771 mImpl->mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
1774 const Length numberOfGlyphs = glyphs.Count();
1776 if( GET_GLYPH_METRICS & operations )
1778 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
1782 bool Controller::DoRelayout( const Size& size,
1783 OperationsMask operationsRequired,
1786 bool viewUpdated( false );
1788 // Calculate the operations to be done.
1789 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1791 if( LAYOUT & operations )
1793 // Some vectors with data needed to layout and reorder may be void
1794 // after the first time the text has been laid out.
1795 // Fill the vectors again.
1797 Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
1799 if( 0u == numberOfGlyphs )
1801 // Nothing else to do if there is no glyphs.
1805 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1806 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1807 Vector<CharacterDirection>& characterDirection = mImpl->mLogicalModel->mCharacterDirections;
1808 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1809 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1810 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1812 // Set the layout parameters.
1813 LayoutParameters layoutParameters( size,
1814 mImpl->mLogicalModel->mText.Begin(),
1815 lineBreakInfo.Begin(),
1816 wordBreakInfo.Begin(),
1817 ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
1820 glyphsToCharactersMap.Begin(),
1821 charactersPerGlyph.Begin() );
1823 // The laid-out lines.
1824 // It's not possible to know in how many lines the text is going to be laid-out,
1825 // but it can be resized at least with the number of 'paragraphs' to avoid
1826 // some re-allocations.
1827 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1829 // Delete any previous laid out lines before setting the new ones.
1832 // The capacity of the bidirectional paragraph info is the number of paragraphs.
1833 lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
1835 // Resize the vector of positions to have the same size than the vector of glyphs.
1836 Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
1837 glyphPositions.Resize( numberOfGlyphs );
1839 // Update the visual model.
1840 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
1847 // Reorder the lines
1848 if( REORDER & operations )
1850 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1852 // Check first if there are paragraphs with bidirectional info.
1853 if( 0u != bidirectionalInfo.Count() )
1856 const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
1858 // Reorder the lines.
1859 Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
1860 lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
1861 ReorderLines( bidirectionalInfo,
1863 lineBidirectionalInfoRuns );
1865 // Set the bidirectional info into the model.
1866 const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
1867 mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
1868 numberOfBidirectionalInfoRuns );
1870 // Set the bidirectional info per line into the layout parameters.
1871 layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
1872 layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
1874 // Get the character to glyph conversion table and set into the layout.
1875 layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
1877 // Get the glyphs per character table and set into the layout.
1878 layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
1880 // Re-layout the text. Reorder those lines with right to left characters.
1881 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1884 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
1885 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
1886 endIt = lineBidirectionalInfoRuns.End();
1890 BidirectionalLineInfoRun& bidiLineInfo = *it;
1892 free( bidiLineInfo.visualToLogicalMap );
1897 if( ALIGN & operations )
1899 mImpl->mLayoutEngine.Align( layoutParameters,
1905 // Sets the actual size.
1906 if( UPDATE_ACTUAL_SIZE & operations )
1908 mImpl->mVisualModel->SetActualSize( layoutSize );
1914 layoutSize = mImpl->mVisualModel->GetActualSize();
1920 void Controller::CalculateTextAlignment( const Size& size )
1922 // Get the direction of the first character.
1923 const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
1925 const Size& actualSize = mImpl->mVisualModel->GetActualSize();
1927 // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1928 LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
1929 if( firstParagraphDirection &&
1930 ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
1932 if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
1934 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
1938 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
1942 switch( horizontalAlignment )
1944 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1946 mImpl->mAlignmentOffset.x = 0.f;
1949 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1951 const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1952 mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1955 case LayoutEngine::HORIZONTAL_ALIGN_END:
1957 mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1962 const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1963 switch( verticalAlignment )
1965 case LayoutEngine::VERTICAL_ALIGN_TOP:
1967 mImpl->mAlignmentOffset.y = 0.f;
1970 case LayoutEngine::VERTICAL_ALIGN_CENTER:
1972 const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
1973 mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
1976 case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1978 mImpl->mAlignmentOffset.y = size.height - actualSize.height;
1984 View& Controller::GetView()
1986 return mImpl->mView;
1989 LayoutEngine& Controller::GetLayoutEngine()
1991 return mImpl->mLayoutEngine;
1994 void Controller::RequestRelayout()
1996 mImpl->mControlInterface.RequestTextRelayout();
1999 void Controller::KeyboardFocusGainEvent()
2001 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
2003 if( mImpl->mTextInput )
2005 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
2006 mImpl->mTextInput->mEventQueue.push_back( event );
2012 void Controller::KeyboardFocusLostEvent()
2014 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
2016 if( mImpl->mTextInput )
2018 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
2019 mImpl->mTextInput->mEventQueue.push_back( event );
2025 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
2027 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" );
2029 if( mImpl->mTextInput &&
2030 keyEvent.state == KeyEvent::Down )
2032 int keyCode = keyEvent.keyCode;
2033 const std::string& keyString = keyEvent.keyPressed;
2035 // Pre-process to separate modifying events from non-modifying input events.
2036 if( Dali::DALI_KEY_ESCAPE == keyCode )
2038 // Escape key is a special case which causes focus loss
2039 KeyboardFocusLostEvent();
2041 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
2042 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
2043 Dali::DALI_KEY_CURSOR_UP == keyCode ||
2044 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
2046 TextInput::Event event( TextInput::CURSOR_KEY_EVENT );
2047 event.p1.mInt = keyCode;
2048 mImpl->mTextInput->mEventQueue.push_back( event );
2050 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
2052 // Queue a delete event
2054 event.type = DELETE_TEXT;
2055 mImpl->mModifyEvents.push_back( event );
2057 else if( !keyString.empty() )
2059 // Queue an insert event
2061 event.type = INSERT_TEXT;
2062 event.text = keyString;
2063 mImpl->mModifyEvents.push_back( event );
2066 mImpl->mTextInput->ChangeState( TextInput::EDITING ); // todo Confirm this is the best place to change the state of
2074 void Controller::TapEvent( unsigned int tapCount, float x, float y )
2076 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
2078 if( mImpl->mTextInput )
2080 TextInput::Event event( TextInput::TAP_EVENT );
2081 event.p1.mUint = tapCount;
2082 event.p2.mFloat = x;
2083 event.p3.mFloat = y;
2084 mImpl->mTextInput->mEventQueue.push_back( event );
2090 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
2092 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected PanEvent" );
2094 if( mImpl->mTextInput )
2096 TextInput::Event event( TextInput::PAN_EVENT );
2097 event.p1.mInt = state;
2098 event.p2.mFloat = displacement.x;
2099 event.p3.mFloat = displacement.y;
2100 mImpl->mTextInput->mEventQueue.push_back( event );
2106 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
2108 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
2110 if( mImpl->mTextInput )
2112 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
2113 event.p1.mUint = state;
2114 event.p2.mFloat = x;
2115 event.p3.mFloat = y;
2116 mImpl->mTextInput->mEventQueue.push_back( event );
2122 Controller::~Controller()
2127 Controller::Controller( ControlInterface& controlInterface )
2130 mImpl = new Controller::Impl( controlInterface );
2135 } // namespace Toolkit