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>
23 #include <dali/public-api/adaptor-framework/key.h>
26 #include <dali-toolkit/internal/text/bidirectional-support.h>
27 #include <dali-toolkit/internal/text/character-set-conversion.h>
28 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
29 #include <dali-toolkit/internal/text/multi-language-support.h>
30 #include <dali-toolkit/internal/text/script-run.h>
31 #include <dali-toolkit/internal/text/segmentation.h>
32 #include <dali-toolkit/internal/text/shaper.h>
33 #include <dali-toolkit/internal/text/text-controller-impl.h>
34 #include <dali-toolkit/internal/text/text-io.h>
35 #include <dali-toolkit/internal/text/text-view.h>
40 const float MAX_FLOAT = std::numeric_limits<float>::max();
42 const std::string EMPTY_STRING("");
55 ControllerPtr Controller::New( ControlInterface& controlInterface )
57 return ControllerPtr( new Controller( controlInterface ) );
60 void Controller::SetText( const std::string& text )
62 // Cancel previously queued inserts etc.
63 mImpl->mModifyEvents.clear();
65 // Keep until size negotiation
67 event.type = ModifyEvent::REPLACE_TEXT;
69 mImpl->mModifyEvents.push_back( event );
71 if( mImpl->mEventData )
73 // Cancel previously queued events
74 mImpl->mEventData->mEventQueue.clear();
76 // TODO - Hide selection decorations
80 void Controller::GetText( std::string& text ) const
82 if( !mImpl->mModifyEvents.empty() &&
83 ModifyEvent::REPLACE_TEXT == mImpl->mModifyEvents[0].type )
85 text = mImpl->mModifyEvents[0].text;
89 // TODO - Convert from UTF-32
93 void Controller::SetPlaceholderText( const std::string& text )
95 if( !mImpl->mEventData )
97 mImpl->mEventData->mPlaceholderText = text;
101 void Controller::GetPlaceholderText( std::string& text ) const
103 if( !mImpl->mEventData )
105 text = mImpl->mEventData->mPlaceholderText;
109 void Controller::SetMaximumNumberOfCharacters( int maxCharacters )
111 if ( maxCharacters >= 0 )
113 mImpl->mMaximumNumberOfCharacters = maxCharacters;
117 int Controller::GetMaximumNumberOfCharacters()
119 return mImpl->mMaximumNumberOfCharacters;
122 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
124 if( !mImpl->mFontDefaults )
126 mImpl->mFontDefaults = new FontDefaults();
129 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
130 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
131 mImpl->mOperationsPending = ALL_OPERATIONS;
132 mImpl->mRecalculateNaturalSize = true;
134 // Clear the font-specific data
135 mImpl->mLogicalModel->mFontRuns.Clear();
136 mImpl->mVisualModel->mGlyphs.Clear();
137 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
138 mImpl->mVisualModel->mCharactersToGlyph.Clear();
139 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
140 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
141 mImpl->mVisualModel->mGlyphPositions.Clear();
142 mImpl->mVisualModel->mLines.Clear();
143 mImpl->mVisualModel->ClearCaches();
145 mImpl->RequestRelayout();
148 const std::string& Controller::GetDefaultFontFamily() const
150 if( mImpl->mFontDefaults )
152 return mImpl->mFontDefaults->mDefaultFontFamily;
158 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
160 if( !mImpl->mFontDefaults )
162 mImpl->mFontDefaults = new FontDefaults();
165 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
166 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
167 mImpl->mOperationsPending = ALL_OPERATIONS;
168 mImpl->mRecalculateNaturalSize = true;
170 // Clear the font-specific data
171 mImpl->mLogicalModel->mFontRuns.Clear();
172 mImpl->mVisualModel->mGlyphs.Clear();
173 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
174 mImpl->mVisualModel->mCharactersToGlyph.Clear();
175 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
176 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
177 mImpl->mVisualModel->mGlyphPositions.Clear();
178 mImpl->mVisualModel->mLines.Clear();
179 mImpl->mVisualModel->ClearCaches();
181 mImpl->RequestRelayout();
184 const std::string& Controller::GetDefaultFontStyle() const
186 if( mImpl->mFontDefaults )
188 return mImpl->mFontDefaults->mDefaultFontStyle;
194 void Controller::SetDefaultPointSize( float pointSize )
196 if( !mImpl->mFontDefaults )
198 mImpl->mFontDefaults = new FontDefaults();
201 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
202 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
203 mImpl->mOperationsPending = ALL_OPERATIONS;
204 mImpl->mRecalculateNaturalSize = true;
206 // Clear the font-specific data
207 mImpl->mLogicalModel->mFontRuns.Clear();
208 mImpl->mVisualModel->mGlyphs.Clear();
209 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
210 mImpl->mVisualModel->mCharactersToGlyph.Clear();
211 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
212 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
213 mImpl->mVisualModel->mGlyphPositions.Clear();
214 mImpl->mVisualModel->mLines.Clear();
215 mImpl->mVisualModel->ClearCaches();
217 mImpl->RequestRelayout();
220 float Controller::GetDefaultPointSize() const
222 if( mImpl->mFontDefaults )
224 return mImpl->mFontDefaults->mDefaultPointSize;
230 void Controller::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters ) const
232 if( mImpl->mFontDefaults )
235 fontRun.characterRun.characterIndex = 0;
236 fontRun.characterRun.numberOfCharacters = numberOfCharacters;
237 fontRun.fontId = mImpl->mFontDefaults->GetFontId( mImpl->mFontClient );
238 fontRun.isDefault = true;
240 fonts.PushBack( fontRun );
244 void Controller::SetTextColor( const Vector4& textColor )
246 mImpl->mVisualModel->SetTextColor( textColor );
249 const Vector4& Controller::GetTextColor() const
251 return mImpl->mVisualModel->GetTextColor();
254 void Controller::SetShadowOffset( const Vector2& shadowOffset )
256 mImpl->mVisualModel->SetShadowOffset( shadowOffset );
259 const Vector2& Controller::GetShadowOffset() const
261 return mImpl->mVisualModel->GetShadowOffset();
264 void Controller::SetShadowColor( const Vector4& shadowColor )
266 mImpl->mVisualModel->SetShadowColor( shadowColor );
269 const Vector4& Controller::GetShadowColor() const
271 return mImpl->mVisualModel->GetShadowColor();
274 void Controller::SetUnderlineColor( const Vector4& color )
276 mImpl->mVisualModel->SetUnderlineColor( color );
279 const Vector4& Controller::GetUnderlineColor() const
281 return mImpl->mVisualModel->GetUnderlineColor();
284 void Controller::SetUnderlineEnabled( bool enabled )
286 mImpl->mVisualModel->SetUnderlineEnabled( enabled );
289 bool Controller::IsUnderlineEnabled() const
291 return mImpl->mVisualModel->IsUnderlineEnabled();
294 void Controller::SetUnderlineHeight( float height )
296 mImpl->mVisualModel->SetUnderlineHeight( height );
299 float Controller::GetUnderlineHeight() const
301 return mImpl->mVisualModel->GetUnderlineHeight();
304 void Controller::EnableTextInput( DecoratorPtr decorator )
306 if( !mImpl->mEventData )
308 mImpl->mEventData = new EventData( decorator );
312 void Controller::SetEnableCursorBlink( bool enable )
314 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "TextInput disabled" );
316 if( mImpl->mEventData )
318 mImpl->mEventData->mCursorBlinkEnabled = enable;
321 mImpl->mEventData->mDecorator )
323 mImpl->mEventData->mDecorator->StopCursorBlink();
328 bool Controller::GetEnableCursorBlink() const
330 if( mImpl->mEventData )
332 return mImpl->mEventData->mCursorBlinkEnabled;
338 const Vector2& Controller::GetScrollPosition() const
340 if( mImpl->mEventData )
342 return mImpl->mEventData->mScrollPosition;
345 return Vector2::ZERO;
348 const Vector2& Controller::GetAlignmentOffset() const
350 return mImpl->mAlignmentOffset;
353 Vector3 Controller::GetNaturalSize()
357 // Make sure the model is up-to-date before layouting
358 ProcessModifyEvents();
360 if( mImpl->mRecalculateNaturalSize )
362 // Operations that can be done only once until the text changes.
363 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
371 // Make sure the model is up-to-date before layouting
372 UpdateModel( onlyOnceOperations );
374 // Operations that need to be done if the size changes.
375 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
379 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
380 static_cast<OperationsMask>( onlyOnceOperations |
382 naturalSize.GetVectorXY() );
384 // Do not do again the only once operations.
385 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
387 // Do the size related operations again.
388 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
390 // Stores the natural size to avoid recalculate it again
391 // unless the text/style changes.
392 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
394 mImpl->mRecalculateNaturalSize = false;
398 naturalSize = mImpl->mVisualModel->GetNaturalSize();
404 float Controller::GetHeightForWidth( float width )
406 // Make sure the model is up-to-date before layouting
407 ProcessModifyEvents();
410 if( width != mImpl->mControlSize.width )
412 // Operations that can be done only once until the text changes.
413 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
421 // Make sure the model is up-to-date before layouting
422 UpdateModel( onlyOnceOperations );
424 // Operations that need to be done if the size changes.
425 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
429 DoRelayout( Size( width, MAX_FLOAT ),
430 static_cast<OperationsMask>( onlyOnceOperations |
434 // Do not do again the only once operations.
435 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
437 // Do the size related operations again.
438 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
442 layoutSize = mImpl->mVisualModel->GetActualSize();
445 return layoutSize.height;
448 bool Controller::Relayout( const Size& size )
450 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
452 bool glyphsRemoved( false );
453 if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
455 mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
456 glyphsRemoved = true;
458 // Not worth to relayout if width or height is equal to zero.
459 return glyphsRemoved;
462 if( size != mImpl->mControlSize )
464 // Operations that need to be done if the size changes.
465 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
471 mImpl->mControlSize = size;
474 // Make sure the model is up-to-date before layouting
475 ProcessModifyEvents();
476 UpdateModel( mImpl->mOperationsPending );
479 bool updated = DoRelayout( mImpl->mControlSize,
480 mImpl->mOperationsPending,
483 // Do not re-do any operation until something changes.
484 mImpl->mOperationsPending = NO_OPERATION;
486 // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
487 CalculateTextAlignment( size );
489 if( mImpl->mEventData )
491 // Move the cursor, grab handle etc.
492 updated = mImpl->ProcessInputEvents() || updated;
498 void Controller::ProcessModifyEvents()
500 std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
502 for( unsigned int i=0; i<events.size(); ++i )
504 if( ModifyEvent::REPLACE_TEXT == events[0].type )
506 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
507 DALI_ASSERT_DEBUG( 0 == i && "Unexpected REPLACE event" );
509 ReplaceTextEvent( events[0].text );
511 else if( ModifyEvent::INSERT_TEXT == events[0].type )
513 InsertTextEvent( events[0].text );
515 else if( ModifyEvent::DELETE_TEXT == events[0].type )
521 // Discard temporary text
525 void Controller::ReplaceTextEvent( const std::string& text )
528 mImpl->mLogicalModel->mText.Clear();
529 mImpl->mLogicalModel->mScriptRuns.Clear();
530 mImpl->mLogicalModel->mFontRuns.Clear();
531 mImpl->mLogicalModel->mLineBreakInfo.Clear();
532 mImpl->mLogicalModel->mWordBreakInfo.Clear();
533 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
534 mImpl->mLogicalModel->mCharacterDirections.Clear();
535 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
536 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
537 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
538 mImpl->mVisualModel->mGlyphs.Clear();
539 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
540 mImpl->mVisualModel->mCharactersToGlyph.Clear();
541 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
542 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
543 mImpl->mVisualModel->mGlyphPositions.Clear();
544 mImpl->mVisualModel->mLines.Clear();
545 mImpl->mVisualModel->ClearCaches();
547 // Convert text into UTF-32
548 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
549 utf32Characters.Resize( text.size() );
551 // This is a bit horrible but std::string returns a (signed) char*
552 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
554 // Transform a text array encoded in utf8 into an array encoded in utf32.
555 // It returns the actual number of characters.
556 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
557 utf32Characters.Resize( characterCount );
559 // Reset the cursor position
560 if( mImpl->mEventData )
562 mImpl->mEventData->mPrimaryCursorPosition = characterCount;
563 // TODO - handle secondary cursor
566 // The natural size needs to be re-calculated.
567 mImpl->mRecalculateNaturalSize = true;
569 // Apply modifications to the model
570 mImpl->mOperationsPending = ALL_OPERATIONS;
571 UpdateModel( ALL_OPERATIONS );
572 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
578 void Controller::InsertTextEvent( const std::string& text )
580 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertTextEvent" );
582 // TODO - Optimize this
583 mImpl->mLogicalModel->mScriptRuns.Clear();
584 mImpl->mLogicalModel->mFontRuns.Clear();
585 mImpl->mLogicalModel->mLineBreakInfo.Clear();
586 mImpl->mLogicalModel->mWordBreakInfo.Clear();
587 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
588 mImpl->mLogicalModel->mCharacterDirections.Clear();
589 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
590 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
591 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
592 mImpl->mVisualModel->mGlyphs.Clear();
593 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
594 mImpl->mVisualModel->mCharactersToGlyph.Clear();
595 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
596 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
597 mImpl->mVisualModel->mGlyphPositions.Clear();
598 mImpl->mVisualModel->mLines.Clear();
599 mImpl->mVisualModel->ClearCaches();
601 // Convert text into UTF-32
602 Vector<Character> utf32Characters;
603 utf32Characters.Resize( text.size() );
605 // This is a bit horrible but std::string returns a (signed) char*
606 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
608 // Transform a text array encoded in utf8 into an array encoded in utf32.
609 // It returns the actual number of characters.
610 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
611 utf32Characters.Resize( characterCount );
613 const Length numberOfCharactersInModel = mImpl->mLogicalModel->GetNumberOfCharacters();
615 // Restrict new text to fit within Maximum characters setting
616 Length maxSizeOfNewText = std::min ( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
618 // Insert at current cursor position
619 CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
621 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
623 if( cursorIndex < numberOfCharactersInModel )
625 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin()+ maxSizeOfNewText );
629 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
632 cursorIndex += maxSizeOfNewText;
634 // The natural size needs to be re-calculated.
635 mImpl->mRecalculateNaturalSize = true;
637 // Apply modifications to the model; TODO - Optimize this
638 mImpl->mOperationsPending = ALL_OPERATIONS;
639 UpdateModel( ALL_OPERATIONS );
640 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
645 // Queue a cursor reposition event; this must wait until after DoRelayout()
646 mImpl->mEventData->mUpdateCursorPosition = true;
647 mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
649 if ( characterCount > maxSizeOfNewText )
651 mImpl->mControlInterface.MaxLengthReached();
655 void Controller::DeleteTextEvent()
657 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertTextEvent" );
659 // TODO - Optimize this
660 mImpl->mLogicalModel->mScriptRuns.Clear();
661 mImpl->mLogicalModel->mFontRuns.Clear();
662 mImpl->mLogicalModel->mLineBreakInfo.Clear();
663 mImpl->mLogicalModel->mWordBreakInfo.Clear();
664 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
665 mImpl->mLogicalModel->mCharacterDirections.Clear();
666 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
667 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
668 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
669 mImpl->mVisualModel->mGlyphs.Clear();
670 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
671 mImpl->mVisualModel->mCharactersToGlyph.Clear();
672 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
673 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
674 mImpl->mVisualModel->mGlyphPositions.Clear();
675 mImpl->mVisualModel->mLines.Clear();
676 mImpl->mVisualModel->ClearCaches();
678 // Delte at current cursor position
679 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
680 CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
682 if( cursorIndex > 0 &&
683 cursorIndex-1 < modifyText.Count() )
685 modifyText.Remove( modifyText.Begin() + cursorIndex - 1 );
687 // Cursor position retreat
691 // The natural size needs to be re-calculated.
692 mImpl->mRecalculateNaturalSize = true;
694 // Apply modifications to the model; TODO - Optimize this
695 mImpl->mOperationsPending = ALL_OPERATIONS;
696 UpdateModel( ALL_OPERATIONS );
697 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
702 // Queue a cursor reposition event; this must wait until after DoRelayout()
703 mImpl->mEventData->mUpdateCursorPosition = true;
704 mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
707 void Controller::UpdateModel( OperationsMask operationsRequired )
709 // Calculate the operations to be done.
710 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
712 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
714 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
716 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
717 if( GET_LINE_BREAKS & operations )
719 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
720 // calculate the bidirectional info for each 'paragraph'.
721 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
722 // is not shaped together).
723 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
725 SetLineBreakInfo( utf32Characters,
729 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
730 if( GET_WORD_BREAKS & operations )
732 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
733 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
735 SetWordBreakInfo( utf32Characters,
739 const bool getScripts = GET_SCRIPTS & operations;
740 const bool validateFonts = VALIDATE_FONTS & operations;
742 Vector<ScriptRun>& scripts = mImpl->mLogicalModel->mScriptRuns;
743 Vector<FontRun>& validFonts = mImpl->mLogicalModel->mFontRuns;
745 if( getScripts || validateFonts )
747 // Validates the fonts assigned by the application or assigns default ones.
748 // It makes sure all the characters are going to be rendered by the correct font.
749 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
753 // Retrieves the scripts used in the text.
754 multilanguageSupport.SetScripts( utf32Characters,
761 if( 0u == validFonts.Count() )
763 // Copy the requested font defaults received via the property system.
764 // These may not be valid i.e. may not contain glyphs for the necessary scripts.
765 GetDefaultFonts( validFonts, numberOfCharacters );
768 // Validates the fonts. If there is a character with no assigned font it sets a default one.
769 // After this call, fonts are validated.
770 multilanguageSupport.ValidateFonts( utf32Characters,
776 Vector<Character> mirroredUtf32Characters;
777 bool textMirrored = false;
778 if( BIDI_INFO & operations )
780 // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
781 // bidirectional info.
783 Length numberOfParagraphs = 0u;
785 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
786 for( Length index = 0u; index < numberOfCharacters; ++index )
788 if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
790 ++numberOfParagraphs;
794 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
795 bidirectionalInfo.Reserve( numberOfParagraphs );
797 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
798 SetBidirectionalInfo( utf32Characters,
803 if( 0u != bidirectionalInfo.Count() )
805 // This paragraph has right to left text. Some characters may need to be mirrored.
806 // TODO: consider if the mirrored string can be stored as well.
808 textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
810 // Only set the character directions if there is right to left characters.
811 Vector<CharacterDirection>& directions = mImpl->mLogicalModel->mCharacterDirections;
812 directions.Resize( numberOfCharacters );
814 GetCharactersDirection( bidirectionalInfo,
819 // There is no right to left characters. Clear the directions vector.
820 mImpl->mLogicalModel->mCharacterDirections.Clear();
825 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
826 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
827 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
828 if( SHAPE_TEXT & operations )
830 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
832 ShapeText( textToShape,
837 glyphsToCharactersMap,
838 charactersPerGlyph );
840 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
841 mImpl->mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
842 mImpl->mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
845 const Length numberOfGlyphs = glyphs.Count();
847 if( GET_GLYPH_METRICS & operations )
849 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
853 bool Controller::DoRelayout( const Size& size,
854 OperationsMask operationsRequired,
857 bool viewUpdated( false );
859 // Calculate the operations to be done.
860 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
862 if( LAYOUT & operations )
864 // Some vectors with data needed to layout and reorder may be void
865 // after the first time the text has been laid out.
866 // Fill the vectors again.
868 Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
870 if( 0u == numberOfGlyphs )
872 // Nothing else to do if there is no glyphs.
876 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
877 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
878 Vector<CharacterDirection>& characterDirection = mImpl->mLogicalModel->mCharacterDirections;
879 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
880 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
881 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
883 // Set the layout parameters.
884 LayoutParameters layoutParameters( size,
885 mImpl->mLogicalModel->mText.Begin(),
886 lineBreakInfo.Begin(),
887 wordBreakInfo.Begin(),
888 ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
891 glyphsToCharactersMap.Begin(),
892 charactersPerGlyph.Begin() );
894 // The laid-out lines.
895 // It's not possible to know in how many lines the text is going to be laid-out,
896 // but it can be resized at least with the number of 'paragraphs' to avoid
897 // some re-allocations.
898 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
900 // Delete any previous laid out lines before setting the new ones.
903 // The capacity of the bidirectional paragraph info is the number of paragraphs.
904 lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
906 // Resize the vector of positions to have the same size than the vector of glyphs.
907 Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
908 glyphPositions.Resize( numberOfGlyphs );
910 // Update the visual model.
911 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
919 if( REORDER & operations )
921 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
923 // Check first if there are paragraphs with bidirectional info.
924 if( 0u != bidirectionalInfo.Count() )
927 const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
929 // Reorder the lines.
930 Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
931 lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
932 ReorderLines( bidirectionalInfo,
934 lineBidirectionalInfoRuns );
936 // Set the bidirectional info into the model.
937 const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
938 mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
939 numberOfBidirectionalInfoRuns );
941 // Set the bidirectional info per line into the layout parameters.
942 layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
943 layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
945 // Get the character to glyph conversion table and set into the layout.
946 layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
948 // Get the glyphs per character table and set into the layout.
949 layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
951 // Re-layout the text. Reorder those lines with right to left characters.
952 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
955 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
956 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
957 endIt = lineBidirectionalInfoRuns.End();
961 BidirectionalLineInfoRun& bidiLineInfo = *it;
963 free( bidiLineInfo.visualToLogicalMap );
968 if( ALIGN & operations )
970 mImpl->mLayoutEngine.Align( layoutParameters,
976 // Sets the actual size.
977 if( UPDATE_ACTUAL_SIZE & operations )
979 mImpl->mVisualModel->SetActualSize( layoutSize );
985 layoutSize = mImpl->mVisualModel->GetActualSize();
991 void Controller::CalculateTextAlignment( const Size& size )
993 // Get the direction of the first character.
994 const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
996 const Size& actualSize = mImpl->mVisualModel->GetActualSize();
998 // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
999 LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
1000 if( firstParagraphDirection &&
1001 ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
1003 if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
1005 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
1009 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
1013 switch( horizontalAlignment )
1015 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1017 mImpl->mAlignmentOffset.x = 0.f;
1020 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1022 const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1023 mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1026 case LayoutEngine::HORIZONTAL_ALIGN_END:
1028 mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1033 const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1034 switch( verticalAlignment )
1036 case LayoutEngine::VERTICAL_ALIGN_TOP:
1038 mImpl->mAlignmentOffset.y = 0.f;
1041 case LayoutEngine::VERTICAL_ALIGN_CENTER:
1043 const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
1044 mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
1047 case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1049 mImpl->mAlignmentOffset.y = size.height - actualSize.height;
1055 LayoutEngine& Controller::GetLayoutEngine()
1057 return mImpl->mLayoutEngine;
1060 View& Controller::GetView()
1062 return mImpl->mView;
1065 void Controller::KeyboardFocusGainEvent()
1067 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
1069 if( mImpl->mEventData )
1071 Event event( Event::KEYBOARD_FOCUS_GAIN_EVENT );
1072 mImpl->mEventData->mEventQueue.push_back( event );
1074 mImpl->RequestRelayout();
1078 void Controller::KeyboardFocusLostEvent()
1080 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
1082 if( mImpl->mEventData )
1084 Event event( Event::KEYBOARD_FOCUS_LOST_EVENT );
1085 mImpl->mEventData->mEventQueue.push_back( event );
1087 mImpl->RequestRelayout();
1091 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1093 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
1095 if( mImpl->mEventData &&
1096 keyEvent.state == KeyEvent::Down )
1098 int keyCode = keyEvent.keyCode;
1099 const std::string& keyString = keyEvent.keyPressed;
1101 // Pre-process to separate modifying events from non-modifying input events.
1102 if( Dali::DALI_KEY_ESCAPE == keyCode )
1104 // Escape key is a special case which causes focus loss
1105 KeyboardFocusLostEvent();
1107 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
1108 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1109 Dali::DALI_KEY_CURSOR_UP == keyCode ||
1110 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1112 Event event( Event::CURSOR_KEY_EVENT );
1113 event.p1.mInt = keyCode;
1114 mImpl->mEventData->mEventQueue.push_back( event );
1116 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1118 // Queue a delete event
1120 event.type = ModifyEvent::DELETE_TEXT;
1121 mImpl->mModifyEvents.push_back( event );
1123 else if( !keyString.empty() )
1125 // Queue an insert event
1127 event.type = ModifyEvent::INSERT_TEXT;
1128 event.text = keyString;
1129 mImpl->mModifyEvents.push_back( event );
1132 mImpl->ChangeState( EventData::EDITING ); // todo Confirm this is the best place to change the state of
1134 mImpl->RequestRelayout();
1140 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1142 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
1144 if( mImpl->mEventData )
1146 Event event( Event::TAP_EVENT );
1147 event.p1.mUint = tapCount;
1148 event.p2.mFloat = x;
1149 event.p3.mFloat = y;
1150 mImpl->mEventData->mEventQueue.push_back( event );
1152 mImpl->RequestRelayout();
1156 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1158 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
1160 if( mImpl->mEventData )
1162 Event event( Event::PAN_EVENT );
1163 event.p1.mInt = state;
1164 event.p2.mFloat = displacement.x;
1165 event.p3.mFloat = displacement.y;
1166 mImpl->mEventData->mEventQueue.push_back( event );
1168 mImpl->RequestRelayout();
1172 void Controller::HandleEvent( HandleType handleType, HandleState state, float x, float y )
1174 DALI_ASSERT_DEBUG( mImpl->mEventData && "Controller::HandleEvent. Unexpected HandleEvent" );
1176 if( mImpl->mEventData )
1178 switch( handleType )
1182 Event event( Event::GRAB_HANDLE_EVENT );
1183 event.p1.mUint = state;
1184 event.p2.mFloat = x;
1185 event.p3.mFloat = y;
1187 mImpl->mEventData->mEventQueue.push_back( event );
1190 case LEFT_SELECTION_HANDLE:
1192 Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
1193 event.p1.mUint = state;
1194 event.p2.mFloat = x;
1195 event.p3.mFloat = y;
1197 mImpl->mEventData->mEventQueue.push_back( event );
1200 case RIGHT_SELECTION_HANDLE:
1202 Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
1203 event.p1.mUint = state;
1204 event.p2.mFloat = x;
1205 event.p3.mFloat = y;
1207 mImpl->mEventData->mEventQueue.push_back( event );
1210 case HANDLE_TYPE_COUNT:
1212 DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
1216 mImpl->RequestRelayout();
1220 Controller::~Controller()
1225 Controller::Controller( ControlInterface& controlInterface )
1228 mImpl = new Controller::Impl( controlInterface );
1233 } // namespace Toolkit