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::SetDefaultFontFamily( const std::string& defaultFontFamily )
111 if( !mImpl->mFontDefaults )
113 mImpl->mFontDefaults = new FontDefaults();
116 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
117 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
118 mImpl->mOperationsPending = ALL_OPERATIONS;
119 mImpl->mRecalculateNaturalSize = true;
121 // Clear the font-specific data
122 mImpl->mLogicalModel->mFontRuns.Clear();
123 mImpl->mVisualModel->mGlyphs.Clear();
124 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
125 mImpl->mVisualModel->mCharactersToGlyph.Clear();
126 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
127 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
128 mImpl->mVisualModel->mGlyphPositions.Clear();
129 mImpl->mVisualModel->mLines.Clear();
130 mImpl->mVisualModel->ClearCaches();
132 mImpl->RequestRelayout();
135 const std::string& Controller::GetDefaultFontFamily() const
137 if( mImpl->mFontDefaults )
139 return mImpl->mFontDefaults->mDefaultFontFamily;
145 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
147 if( !mImpl->mFontDefaults )
149 mImpl->mFontDefaults = new FontDefaults();
152 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
153 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
154 mImpl->mOperationsPending = ALL_OPERATIONS;
155 mImpl->mRecalculateNaturalSize = true;
157 // Clear the font-specific data
158 mImpl->mLogicalModel->mFontRuns.Clear();
159 mImpl->mVisualModel->mGlyphs.Clear();
160 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
161 mImpl->mVisualModel->mCharactersToGlyph.Clear();
162 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
163 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
164 mImpl->mVisualModel->mGlyphPositions.Clear();
165 mImpl->mVisualModel->mLines.Clear();
166 mImpl->mVisualModel->ClearCaches();
168 mImpl->RequestRelayout();
171 const std::string& Controller::GetDefaultFontStyle() const
173 if( mImpl->mFontDefaults )
175 return mImpl->mFontDefaults->mDefaultFontStyle;
181 void Controller::SetDefaultPointSize( float pointSize )
183 if( !mImpl->mFontDefaults )
185 mImpl->mFontDefaults = new FontDefaults();
188 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
189 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
190 mImpl->mOperationsPending = ALL_OPERATIONS;
191 mImpl->mRecalculateNaturalSize = true;
193 // Clear the font-specific data
194 mImpl->mLogicalModel->mFontRuns.Clear();
195 mImpl->mVisualModel->mGlyphs.Clear();
196 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
197 mImpl->mVisualModel->mCharactersToGlyph.Clear();
198 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
199 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
200 mImpl->mVisualModel->mGlyphPositions.Clear();
201 mImpl->mVisualModel->mLines.Clear();
202 mImpl->mVisualModel->ClearCaches();
204 mImpl->RequestRelayout();
207 float Controller::GetDefaultPointSize() const
209 if( mImpl->mFontDefaults )
211 return mImpl->mFontDefaults->mDefaultPointSize;
217 void Controller::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters ) const
219 if( mImpl->mFontDefaults )
222 fontRun.characterRun.characterIndex = 0;
223 fontRun.characterRun.numberOfCharacters = numberOfCharacters;
224 fontRun.fontId = mImpl->mFontDefaults->GetFontId( mImpl->mFontClient );
225 fontRun.isDefault = true;
227 fonts.PushBack( fontRun );
231 void Controller::SetTextColor( const Vector4& textColor )
233 mImpl->mVisualModel->SetTextColor( textColor );
236 const Vector4& Controller::GetTextColor() const
238 return mImpl->mVisualModel->GetTextColor();
241 void Controller::SetShadowOffset( const Vector2& shadowOffset )
243 mImpl->mVisualModel->SetShadowOffset( shadowOffset );
246 const Vector2& Controller::GetShadowOffset() const
248 return mImpl->mVisualModel->GetShadowOffset();
251 void Controller::SetShadowColor( const Vector4& shadowColor )
253 mImpl->mVisualModel->SetShadowColor( shadowColor );
256 const Vector4& Controller::GetShadowColor() const
258 return mImpl->mVisualModel->GetShadowColor();
261 void Controller::SetUnderlineColor( const Vector4& color )
263 mImpl->mVisualModel->SetUnderlineColor( color );
266 const Vector4& Controller::GetUnderlineColor() const
268 return mImpl->mVisualModel->GetUnderlineColor();
271 void Controller::SetUnderlineEnabled( bool enabled )
273 mImpl->mVisualModel->SetUnderlineEnabled( enabled );
276 bool Controller::IsUnderlineEnabled() const
278 return mImpl->mVisualModel->IsUnderlineEnabled();
281 void Controller::SetUnderlineHeight( float height )
283 mImpl->mVisualModel->SetUnderlineHeight( height );
286 float Controller::GetUnderlineHeight() const
288 return mImpl->mVisualModel->GetUnderlineHeight();
291 void Controller::EnableTextInput( DecoratorPtr decorator )
293 if( !mImpl->mEventData )
295 mImpl->mEventData = new EventData( decorator );
299 void Controller::SetEnableCursorBlink( bool enable )
301 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "TextInput disabled" );
303 if( mImpl->mEventData )
305 mImpl->mEventData->mCursorBlinkEnabled = enable;
308 mImpl->mEventData->mDecorator )
310 mImpl->mEventData->mDecorator->StopCursorBlink();
315 bool Controller::GetEnableCursorBlink() const
317 if( mImpl->mEventData )
319 return mImpl->mEventData->mCursorBlinkEnabled;
325 const Vector2& Controller::GetScrollPosition() const
327 if( mImpl->mEventData )
329 return mImpl->mEventData->mScrollPosition;
332 return Vector2::ZERO;
335 const Vector2& Controller::GetAlignmentOffset() const
337 return mImpl->mAlignmentOffset;
340 Vector3 Controller::GetNaturalSize()
344 // Make sure the model is up-to-date before layouting
345 ProcessModifyEvents();
347 if( mImpl->mRecalculateNaturalSize )
349 // Operations that can be done only once until the text changes.
350 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
358 // Make sure the model is up-to-date before layouting
359 UpdateModel( onlyOnceOperations );
361 // Operations that need to be done if the size changes.
362 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
366 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
367 static_cast<OperationsMask>( onlyOnceOperations |
369 naturalSize.GetVectorXY() );
371 // Do not do again the only once operations.
372 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
374 // Do the size related operations again.
375 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
377 // Stores the natural size to avoid recalculate it again
378 // unless the text/style changes.
379 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
381 mImpl->mRecalculateNaturalSize = false;
385 naturalSize = mImpl->mVisualModel->GetNaturalSize();
391 float Controller::GetHeightForWidth( float width )
393 // Make sure the model is up-to-date before layouting
394 ProcessModifyEvents();
397 if( width != mImpl->mControlSize.width )
399 // Operations that can be done only once until the text changes.
400 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
408 // Make sure the model is up-to-date before layouting
409 UpdateModel( onlyOnceOperations );
411 // Operations that need to be done if the size changes.
412 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
416 DoRelayout( Size( width, MAX_FLOAT ),
417 static_cast<OperationsMask>( onlyOnceOperations |
421 // Do not do again the only once operations.
422 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
424 // Do the size related operations again.
425 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
429 layoutSize = mImpl->mVisualModel->GetActualSize();
432 return layoutSize.height;
435 bool Controller::Relayout( const Size& size )
437 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
439 bool glyphsRemoved( false );
440 if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
442 mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
443 glyphsRemoved = true;
445 // Not worth to relayout if width or height is equal to zero.
446 return glyphsRemoved;
449 if( size != mImpl->mControlSize )
451 // Operations that need to be done if the size changes.
452 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
458 mImpl->mControlSize = size;
461 // Make sure the model is up-to-date before layouting
462 ProcessModifyEvents();
463 UpdateModel( mImpl->mOperationsPending );
466 bool updated = DoRelayout( mImpl->mControlSize,
467 mImpl->mOperationsPending,
470 // Do not re-do any operation until something changes.
471 mImpl->mOperationsPending = NO_OPERATION;
473 // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
474 CalculateTextAlignment( size );
476 if( mImpl->mEventData )
478 // Move the cursor, grab handle etc.
479 updated = mImpl->ProcessInputEvents() || updated;
485 void Controller::ProcessModifyEvents()
487 std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
489 for( unsigned int i=0; i<events.size(); ++i )
491 if( ModifyEvent::REPLACE_TEXT == events[0].type )
493 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
494 DALI_ASSERT_DEBUG( 0 == i && "Unexpected REPLACE event" );
496 ReplaceTextEvent( events[0].text );
498 else if( ModifyEvent::INSERT_TEXT == events[0].type )
500 InsertTextEvent( events[0].text );
502 else if( ModifyEvent::DELETE_TEXT == events[0].type )
508 // Discard temporary text
512 void Controller::ReplaceTextEvent( const std::string& text )
515 mImpl->mLogicalModel->mText.Clear();
516 mImpl->mLogicalModel->mScriptRuns.Clear();
517 mImpl->mLogicalModel->mFontRuns.Clear();
518 mImpl->mLogicalModel->mLineBreakInfo.Clear();
519 mImpl->mLogicalModel->mWordBreakInfo.Clear();
520 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
521 mImpl->mLogicalModel->mCharacterDirections.Clear();
522 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
523 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
524 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
525 mImpl->mVisualModel->mGlyphs.Clear();
526 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
527 mImpl->mVisualModel->mCharactersToGlyph.Clear();
528 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
529 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
530 mImpl->mVisualModel->mGlyphPositions.Clear();
531 mImpl->mVisualModel->mLines.Clear();
532 mImpl->mVisualModel->ClearCaches();
534 // Convert text into UTF-32
535 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
536 utf32Characters.Resize( text.size() );
538 // This is a bit horrible but std::string returns a (signed) char*
539 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
541 // Transform a text array encoded in utf8 into an array encoded in utf32.
542 // It returns the actual number of characters.
543 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
544 utf32Characters.Resize( characterCount );
546 // Reset the cursor position
547 if( mImpl->mEventData )
549 mImpl->mEventData->mPrimaryCursorPosition = characterCount;
550 // TODO - handle secondary cursor
553 // The natural size needs to be re-calculated.
554 mImpl->mRecalculateNaturalSize = true;
556 // Apply modifications to the model
557 mImpl->mOperationsPending = ALL_OPERATIONS;
558 UpdateModel( ALL_OPERATIONS );
559 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
565 void Controller::InsertTextEvent( const std::string& text )
567 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertTextEvent" );
569 // TODO - Optimize this
570 mImpl->mLogicalModel->mScriptRuns.Clear();
571 mImpl->mLogicalModel->mFontRuns.Clear();
572 mImpl->mLogicalModel->mLineBreakInfo.Clear();
573 mImpl->mLogicalModel->mWordBreakInfo.Clear();
574 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
575 mImpl->mLogicalModel->mCharacterDirections.Clear();
576 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
577 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
578 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
579 mImpl->mVisualModel->mGlyphs.Clear();
580 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
581 mImpl->mVisualModel->mCharactersToGlyph.Clear();
582 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
583 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
584 mImpl->mVisualModel->mGlyphPositions.Clear();
585 mImpl->mVisualModel->mLines.Clear();
586 mImpl->mVisualModel->ClearCaches();
588 // Convert text into UTF-32
589 Vector<Character> utf32Characters;
590 utf32Characters.Resize( text.size() );
592 // This is a bit horrible but std::string returns a (signed) char*
593 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
595 // Transform a text array encoded in utf8 into an array encoded in utf32.
596 // It returns the actual number of characters.
597 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
598 utf32Characters.Resize( characterCount );
600 // Insert at current cursor position
601 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
602 CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
604 if( cursorIndex < modifyText.Count() )
606 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.End() );
610 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.End() );
613 // Advance the cursor position
616 // The natural size needs to be re-calculated.
617 mImpl->mRecalculateNaturalSize = true;
619 // Apply modifications to the model; TODO - Optimize this
620 mImpl->mOperationsPending = ALL_OPERATIONS;
621 UpdateModel( ALL_OPERATIONS );
622 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
627 // Queue a cursor reposition event; this must wait until after DoRelayout()
628 mImpl->mEventData->mUpdateCursorPosition = true;
629 mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
632 void Controller::DeleteTextEvent()
634 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertTextEvent" );
636 // TODO - Optimize this
637 mImpl->mLogicalModel->mScriptRuns.Clear();
638 mImpl->mLogicalModel->mFontRuns.Clear();
639 mImpl->mLogicalModel->mLineBreakInfo.Clear();
640 mImpl->mLogicalModel->mWordBreakInfo.Clear();
641 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
642 mImpl->mLogicalModel->mCharacterDirections.Clear();
643 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
644 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
645 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
646 mImpl->mVisualModel->mGlyphs.Clear();
647 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
648 mImpl->mVisualModel->mCharactersToGlyph.Clear();
649 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
650 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
651 mImpl->mVisualModel->mGlyphPositions.Clear();
652 mImpl->mVisualModel->mLines.Clear();
653 mImpl->mVisualModel->ClearCaches();
655 // Delte at current cursor position
656 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
657 CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
659 if( cursorIndex > 0 &&
660 cursorIndex-1 < modifyText.Count() )
662 modifyText.Remove( modifyText.Begin() + cursorIndex - 1 );
664 // Cursor position retreat
668 // The natural size needs to be re-calculated.
669 mImpl->mRecalculateNaturalSize = true;
671 // Apply modifications to the model; TODO - Optimize this
672 mImpl->mOperationsPending = ALL_OPERATIONS;
673 UpdateModel( ALL_OPERATIONS );
674 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
679 // Queue a cursor reposition event; this must wait until after DoRelayout()
680 mImpl->mEventData->mUpdateCursorPosition = true;
681 mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
684 void Controller::UpdateModel( OperationsMask operationsRequired )
686 // Calculate the operations to be done.
687 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
689 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
691 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
693 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
694 if( GET_LINE_BREAKS & operations )
696 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
697 // calculate the bidirectional info for each 'paragraph'.
698 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
699 // is not shaped together).
700 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
702 SetLineBreakInfo( utf32Characters,
706 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
707 if( GET_WORD_BREAKS & operations )
709 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
710 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
712 SetWordBreakInfo( utf32Characters,
716 const bool getScripts = GET_SCRIPTS & operations;
717 const bool validateFonts = VALIDATE_FONTS & operations;
719 Vector<ScriptRun>& scripts = mImpl->mLogicalModel->mScriptRuns;
720 Vector<FontRun>& validFonts = mImpl->mLogicalModel->mFontRuns;
722 if( getScripts || validateFonts )
724 // Validates the fonts assigned by the application or assigns default ones.
725 // It makes sure all the characters are going to be rendered by the correct font.
726 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
730 // Retrieves the scripts used in the text.
731 multilanguageSupport.SetScripts( utf32Characters,
738 if( 0u == validFonts.Count() )
740 // Copy the requested font defaults received via the property system.
741 // These may not be valid i.e. may not contain glyphs for the necessary scripts.
742 GetDefaultFonts( validFonts, numberOfCharacters );
745 // Validates the fonts. If there is a character with no assigned font it sets a default one.
746 // After this call, fonts are validated.
747 multilanguageSupport.ValidateFonts( utf32Characters,
753 Vector<Character> mirroredUtf32Characters;
754 bool textMirrored = false;
755 if( BIDI_INFO & operations )
757 // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
758 // bidirectional info.
760 Length numberOfParagraphs = 0u;
762 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
763 for( Length index = 0u; index < numberOfCharacters; ++index )
765 if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
767 ++numberOfParagraphs;
771 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
772 bidirectionalInfo.Reserve( numberOfParagraphs );
774 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
775 SetBidirectionalInfo( utf32Characters,
780 if( 0u != bidirectionalInfo.Count() )
782 // This paragraph has right to left text. Some characters may need to be mirrored.
783 // TODO: consider if the mirrored string can be stored as well.
785 textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
787 // Only set the character directions if there is right to left characters.
788 Vector<CharacterDirection>& directions = mImpl->mLogicalModel->mCharacterDirections;
789 directions.Resize( numberOfCharacters );
791 GetCharactersDirection( bidirectionalInfo,
796 // There is no right to left characters. Clear the directions vector.
797 mImpl->mLogicalModel->mCharacterDirections.Clear();
802 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
803 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
804 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
805 if( SHAPE_TEXT & operations )
807 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
809 ShapeText( textToShape,
814 glyphsToCharactersMap,
815 charactersPerGlyph );
817 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
818 mImpl->mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
819 mImpl->mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
822 const Length numberOfGlyphs = glyphs.Count();
824 if( GET_GLYPH_METRICS & operations )
826 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
830 bool Controller::DoRelayout( const Size& size,
831 OperationsMask operationsRequired,
834 bool viewUpdated( false );
836 // Calculate the operations to be done.
837 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
839 if( LAYOUT & operations )
841 // Some vectors with data needed to layout and reorder may be void
842 // after the first time the text has been laid out.
843 // Fill the vectors again.
845 Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
847 if( 0u == numberOfGlyphs )
849 // Nothing else to do if there is no glyphs.
853 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
854 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
855 Vector<CharacterDirection>& characterDirection = mImpl->mLogicalModel->mCharacterDirections;
856 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
857 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
858 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
860 // Set the layout parameters.
861 LayoutParameters layoutParameters( size,
862 mImpl->mLogicalModel->mText.Begin(),
863 lineBreakInfo.Begin(),
864 wordBreakInfo.Begin(),
865 ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
868 glyphsToCharactersMap.Begin(),
869 charactersPerGlyph.Begin() );
871 // The laid-out lines.
872 // It's not possible to know in how many lines the text is going to be laid-out,
873 // but it can be resized at least with the number of 'paragraphs' to avoid
874 // some re-allocations.
875 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
877 // Delete any previous laid out lines before setting the new ones.
880 // The capacity of the bidirectional paragraph info is the number of paragraphs.
881 lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
883 // Resize the vector of positions to have the same size than the vector of glyphs.
884 Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
885 glyphPositions.Resize( numberOfGlyphs );
887 // Update the visual model.
888 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
896 if( REORDER & operations )
898 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
900 // Check first if there are paragraphs with bidirectional info.
901 if( 0u != bidirectionalInfo.Count() )
904 const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
906 // Reorder the lines.
907 Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
908 lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
909 ReorderLines( bidirectionalInfo,
911 lineBidirectionalInfoRuns );
913 // Set the bidirectional info into the model.
914 const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
915 mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
916 numberOfBidirectionalInfoRuns );
918 // Set the bidirectional info per line into the layout parameters.
919 layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
920 layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
922 // Get the character to glyph conversion table and set into the layout.
923 layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
925 // Get the glyphs per character table and set into the layout.
926 layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
928 // Re-layout the text. Reorder those lines with right to left characters.
929 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
932 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
933 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
934 endIt = lineBidirectionalInfoRuns.End();
938 BidirectionalLineInfoRun& bidiLineInfo = *it;
940 free( bidiLineInfo.visualToLogicalMap );
945 if( ALIGN & operations )
947 mImpl->mLayoutEngine.Align( layoutParameters,
953 // Sets the actual size.
954 if( UPDATE_ACTUAL_SIZE & operations )
956 mImpl->mVisualModel->SetActualSize( layoutSize );
962 layoutSize = mImpl->mVisualModel->GetActualSize();
968 void Controller::CalculateTextAlignment( const Size& size )
970 // Get the direction of the first character.
971 const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
973 const Size& actualSize = mImpl->mVisualModel->GetActualSize();
975 // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
976 LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
977 if( firstParagraphDirection &&
978 ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
980 if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
982 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
986 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
990 switch( horizontalAlignment )
992 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
994 mImpl->mAlignmentOffset.x = 0.f;
997 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
999 const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1000 mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1003 case LayoutEngine::HORIZONTAL_ALIGN_END:
1005 mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1010 const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1011 switch( verticalAlignment )
1013 case LayoutEngine::VERTICAL_ALIGN_TOP:
1015 mImpl->mAlignmentOffset.y = 0.f;
1018 case LayoutEngine::VERTICAL_ALIGN_CENTER:
1020 const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
1021 mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
1024 case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1026 mImpl->mAlignmentOffset.y = size.height - actualSize.height;
1032 LayoutEngine& Controller::GetLayoutEngine()
1034 return mImpl->mLayoutEngine;
1037 View& Controller::GetView()
1039 return mImpl->mView;
1042 void Controller::KeyboardFocusGainEvent()
1044 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
1046 if( mImpl->mEventData )
1048 Event event( Event::KEYBOARD_FOCUS_GAIN_EVENT );
1049 mImpl->mEventData->mEventQueue.push_back( event );
1051 mImpl->RequestRelayout();
1055 void Controller::KeyboardFocusLostEvent()
1057 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
1059 if( mImpl->mEventData )
1061 Event event( Event::KEYBOARD_FOCUS_LOST_EVENT );
1062 mImpl->mEventData->mEventQueue.push_back( event );
1064 mImpl->RequestRelayout();
1068 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1070 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
1072 if( mImpl->mEventData &&
1073 keyEvent.state == KeyEvent::Down )
1075 int keyCode = keyEvent.keyCode;
1076 const std::string& keyString = keyEvent.keyPressed;
1078 // Pre-process to separate modifying events from non-modifying input events.
1079 if( Dali::DALI_KEY_ESCAPE == keyCode )
1081 // Escape key is a special case which causes focus loss
1082 KeyboardFocusLostEvent();
1084 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
1085 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1086 Dali::DALI_KEY_CURSOR_UP == keyCode ||
1087 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1089 Event event( Event::CURSOR_KEY_EVENT );
1090 event.p1.mInt = keyCode;
1091 mImpl->mEventData->mEventQueue.push_back( event );
1093 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1095 // Queue a delete event
1097 event.type = ModifyEvent::DELETE_TEXT;
1098 mImpl->mModifyEvents.push_back( event );
1100 else if( !keyString.empty() )
1102 // Queue an insert event
1104 event.type = ModifyEvent::INSERT_TEXT;
1105 event.text = keyString;
1106 mImpl->mModifyEvents.push_back( event );
1109 mImpl->ChangeState( EventData::EDITING ); // todo Confirm this is the best place to change the state of
1111 mImpl->RequestRelayout();
1117 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1119 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
1121 if( mImpl->mEventData )
1123 Event event( Event::TAP_EVENT );
1124 event.p1.mUint = tapCount;
1125 event.p2.mFloat = x;
1126 event.p3.mFloat = y;
1127 mImpl->mEventData->mEventQueue.push_back( event );
1129 mImpl->RequestRelayout();
1133 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1135 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
1137 if( mImpl->mEventData )
1139 Event event( Event::PAN_EVENT );
1140 event.p1.mInt = state;
1141 event.p2.mFloat = displacement.x;
1142 event.p3.mFloat = displacement.y;
1143 mImpl->mEventData->mEventQueue.push_back( event );
1145 mImpl->RequestRelayout();
1149 void Controller::HandleEvent( HandleType handleType, HandleState state, float x, float y )
1151 DALI_ASSERT_DEBUG( mImpl->mEventData && "Controller::HandleEvent. Unexpected HandleEvent" );
1153 if( mImpl->mEventData )
1155 switch( handleType )
1159 Event event( Event::GRAB_HANDLE_EVENT );
1160 event.p1.mUint = state;
1161 event.p2.mFloat = x;
1162 event.p3.mFloat = y;
1164 mImpl->mEventData->mEventQueue.push_back( event );
1167 case LEFT_SELECTION_HANDLE:
1169 Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
1170 event.p1.mUint = state;
1171 event.p2.mFloat = x;
1172 event.p3.mFloat = y;
1174 mImpl->mEventData->mEventQueue.push_back( event );
1177 case RIGHT_SELECTION_HANDLE:
1179 Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
1180 event.p1.mUint = state;
1181 event.p2.mFloat = x;
1182 event.p3.mFloat = y;
1184 mImpl->mEventData->mEventQueue.push_back( event );
1187 case HANDLE_TYPE_COUNT:
1189 DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
1193 mImpl->RequestRelayout();
1197 Controller::~Controller()
1202 Controller::Controller( ControlInterface& controlInterface )
1205 mImpl = new Controller::Impl( controlInterface );
1210 } // namespace Toolkit