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>
27 #include <dali-toolkit/internal/text/bidirectional-support.h>
28 #include <dali-toolkit/internal/text/character-set-conversion.h>
29 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
30 #include <dali-toolkit/internal/text/multi-language-support.h>
31 #include <dali-toolkit/internal/text/script-run.h>
32 #include <dali-toolkit/internal/text/segmentation.h>
33 #include <dali-toolkit/internal/text/shaper.h>
34 #include <dali-toolkit/internal/text/text-controller-impl.h>
35 #include <dali-toolkit/internal/text/text-io.h>
36 #include <dali-toolkit/internal/text/text-view.h>
41 const float MAX_FLOAT = std::numeric_limits<float>::max();
43 const std::string EMPTY_STRING("");
56 ControllerPtr Controller::New( ControlInterface& controlInterface )
58 return ControllerPtr( new Controller( controlInterface ) );
61 void Controller::SetText( const std::string& text )
63 // Cancel previously queued inserts etc.
64 mImpl->mModifyEvents.clear();
66 // Remove the previously set text
71 // Convert text into UTF-32
72 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
73 utf32Characters.Resize( text.size() );
75 // This is a bit horrible but std::string returns a (signed) char*
76 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
78 // Transform a text array encoded in utf8 into an array encoded in utf32.
79 // It returns the actual number of characters.
80 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
81 utf32Characters.Resize( characterCount );
83 // Reset the cursor position
84 if( mImpl->mEventData )
86 mImpl->mEventData->mPrimaryCursorPosition = characterCount;
89 // Update the rest of the model during size negotiation
90 mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
94 mImpl->ShowPlaceholderText();
97 if( mImpl->mEventData )
99 // Cancel previously queued events
100 mImpl->mEventData->mEventQueue.clear();
103 // Reset keyboard as text changed
104 mImpl->PreEditReset();
107 void Controller::GetText( std::string& text ) const
109 if( ! mImpl->IsShowingPlaceholderText() )
111 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
113 if( 0u != utf32Characters.Count() )
115 uint32_t numberOfBytes = GetNumberOfUtf8Bytes( &utf32Characters[0], utf32Characters.Count() );
117 text.resize( numberOfBytes );
119 // This is a bit horrible but std::string returns a (signed) char*
120 Utf32ToUtf8( &utf32Characters[0], utf32Characters.Count(), reinterpret_cast<uint8_t*>(&text[0]) );
125 unsigned int Controller::GetLogicalCursorPosition() const
127 if( mImpl->mEventData )
129 return mImpl->mEventData->mPrimaryCursorPosition;
135 void Controller::SetPlaceholderText( PlaceholderType type, const std::string& text )
137 if( mImpl->mEventData )
139 if( PLACEHOLDER_TYPE_INACTIVE == type )
141 mImpl->mEventData->mPlaceholderTextInactive = text;
145 mImpl->mEventData->mPlaceholderTextActive = text;
148 mImpl->ShowPlaceholderText();
152 void Controller::GetPlaceholderText( PlaceholderType type, std::string& text ) const
154 if( mImpl->mEventData )
156 if( PLACEHOLDER_TYPE_INACTIVE == type )
158 text = mImpl->mEventData->mPlaceholderTextInactive;
162 text = mImpl->mEventData->mPlaceholderTextActive;
167 void Controller::SetMaximumNumberOfCharacters( int maxCharacters )
169 if ( maxCharacters >= 0 )
171 mImpl->mMaximumNumberOfCharacters = maxCharacters;
175 int Controller::GetMaximumNumberOfCharacters()
177 return mImpl->mMaximumNumberOfCharacters;
180 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
182 if( !mImpl->mFontDefaults )
184 mImpl->mFontDefaults = new FontDefaults();
187 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
188 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
189 mImpl->mOperationsPending = ALL_OPERATIONS;
190 mImpl->mRecalculateNaturalSize = true;
192 // Clear the font-specific data
193 mImpl->mLogicalModel->mFontRuns.Clear();
194 mImpl->mVisualModel->mGlyphs.Clear();
195 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
196 mImpl->mVisualModel->mCharactersToGlyph.Clear();
197 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
198 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
199 mImpl->mVisualModel->mGlyphPositions.Clear();
200 mImpl->mVisualModel->mLines.Clear();
201 mImpl->mVisualModel->ClearCaches();
203 mImpl->RequestRelayout();
206 const std::string& Controller::GetDefaultFontFamily() const
208 if( mImpl->mFontDefaults )
210 return mImpl->mFontDefaults->mDefaultFontFamily;
216 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
218 if( !mImpl->mFontDefaults )
220 mImpl->mFontDefaults = new FontDefaults();
223 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
224 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
225 mImpl->mOperationsPending = ALL_OPERATIONS;
226 mImpl->mRecalculateNaturalSize = true;
228 // Clear the font-specific data
229 mImpl->mLogicalModel->mFontRuns.Clear();
230 mImpl->mVisualModel->mGlyphs.Clear();
231 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
232 mImpl->mVisualModel->mCharactersToGlyph.Clear();
233 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
234 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
235 mImpl->mVisualModel->mGlyphPositions.Clear();
236 mImpl->mVisualModel->mLines.Clear();
237 mImpl->mVisualModel->ClearCaches();
239 mImpl->RequestRelayout();
242 const std::string& Controller::GetDefaultFontStyle() const
244 if( mImpl->mFontDefaults )
246 return mImpl->mFontDefaults->mDefaultFontStyle;
252 void Controller::SetDefaultPointSize( float pointSize )
254 if( !mImpl->mFontDefaults )
256 mImpl->mFontDefaults = new FontDefaults();
259 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
260 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
261 mImpl->mOperationsPending = ALL_OPERATIONS;
262 mImpl->mRecalculateNaturalSize = true;
264 // Clear the font-specific data
265 mImpl->mLogicalModel->mFontRuns.Clear();
266 mImpl->mVisualModel->mGlyphs.Clear();
267 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
268 mImpl->mVisualModel->mCharactersToGlyph.Clear();
269 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
270 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
271 mImpl->mVisualModel->mGlyphPositions.Clear();
272 mImpl->mVisualModel->mLines.Clear();
273 mImpl->mVisualModel->ClearCaches();
275 mImpl->RequestRelayout();
278 float Controller::GetDefaultPointSize() const
280 if( mImpl->mFontDefaults )
282 return mImpl->mFontDefaults->mDefaultPointSize;
288 void Controller::SetTextColor( const Vector4& textColor )
290 mImpl->mTextColor = textColor;
292 if( ! mImpl->IsShowingPlaceholderText() )
294 mImpl->mVisualModel->SetTextColor( textColor );
298 const Vector4& Controller::GetTextColor() const
300 return mImpl->mTextColor;
303 bool Controller::RemoveText( int cursorOffset, int numberOfChars )
305 bool removed( false );
307 if( ! mImpl->IsShowingPlaceholderText() )
309 // Delete at current cursor position
310 Vector<Character>& currentText = mImpl->mLogicalModel->mText;
311 CharacterIndex& oldCursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
313 CharacterIndex cursorIndex = oldCursorIndex;
315 // Validate the cursor position & number of characters
316 if( static_cast< CharacterIndex >( std::abs( cursorOffset ) ) <= cursorIndex )
318 cursorIndex = oldCursorIndex + cursorOffset;
321 if( (cursorIndex + numberOfChars) > currentText.Count() )
323 numberOfChars = currentText.Count() - cursorIndex;
326 if( cursorIndex >= 0 &&
327 (cursorIndex + numberOfChars) <= currentText.Count() )
329 Vector<Character>::Iterator first = currentText.Begin() + cursorIndex;
330 Vector<Character>::Iterator last = first + numberOfChars;
332 currentText.Erase( first, last );
334 // Cursor position retreat
335 oldCursorIndex = cursorIndex;
344 void Controller::SetPlaceholderTextColor( const Vector4& textColor )
346 if( mImpl->mEventData )
348 mImpl->mEventData->mPlaceholderTextColor = textColor;
351 if( mImpl->IsShowingPlaceholderText() )
353 mImpl->mVisualModel->SetTextColor( textColor );
357 const Vector4& Controller::GetPlaceholderTextColor() const
359 if( mImpl->mEventData )
361 return mImpl->mEventData->mPlaceholderTextColor;
367 void Controller::SetShadowOffset( const Vector2& shadowOffset )
369 mImpl->mVisualModel->SetShadowOffset( shadowOffset );
372 const Vector2& Controller::GetShadowOffset() const
374 return mImpl->mVisualModel->GetShadowOffset();
377 void Controller::SetShadowColor( const Vector4& shadowColor )
379 mImpl->mVisualModel->SetShadowColor( shadowColor );
382 const Vector4& Controller::GetShadowColor() const
384 return mImpl->mVisualModel->GetShadowColor();
387 void Controller::SetUnderlineColor( const Vector4& color )
389 mImpl->mVisualModel->SetUnderlineColor( color );
392 const Vector4& Controller::GetUnderlineColor() const
394 return mImpl->mVisualModel->GetUnderlineColor();
397 void Controller::SetUnderlineEnabled( bool enabled )
399 mImpl->mVisualModel->SetUnderlineEnabled( enabled );
402 bool Controller::IsUnderlineEnabled() const
404 return mImpl->mVisualModel->IsUnderlineEnabled();
407 void Controller::SetUnderlineHeight( float height )
409 mImpl->mVisualModel->SetUnderlineHeight( height );
412 float Controller::GetUnderlineHeight() const
414 return mImpl->mVisualModel->GetUnderlineHeight();
417 void Controller::EnableTextInput( DecoratorPtr decorator )
419 if( !mImpl->mEventData )
421 mImpl->mEventData = new EventData( decorator );
425 void Controller::SetEnableCursorBlink( bool enable )
427 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "TextInput disabled" );
429 if( mImpl->mEventData )
431 mImpl->mEventData->mCursorBlinkEnabled = enable;
434 mImpl->mEventData->mDecorator )
436 mImpl->mEventData->mDecorator->StopCursorBlink();
441 bool Controller::GetEnableCursorBlink() const
443 if( mImpl->mEventData )
445 return mImpl->mEventData->mCursorBlinkEnabled;
451 const Vector2& Controller::GetScrollPosition() const
453 if( mImpl->mEventData )
455 return mImpl->mEventData->mScrollPosition;
458 return Vector2::ZERO;
461 const Vector2& Controller::GetAlignmentOffset() const
463 return mImpl->mAlignmentOffset;
466 Vector3 Controller::GetNaturalSize()
470 // Make sure the model is up-to-date before layouting
471 ProcessModifyEvents();
473 if( mImpl->mRecalculateNaturalSize )
475 // Operations that can be done only once until the text changes.
476 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
484 // Make sure the model is up-to-date before layouting
485 mImpl->UpdateModel( onlyOnceOperations );
487 // Operations that need to be done if the size changes.
488 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
492 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
493 static_cast<OperationsMask>( onlyOnceOperations |
495 naturalSize.GetVectorXY() );
497 // Do not do again the only once operations.
498 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
500 // Do the size related operations again.
501 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
503 // Stores the natural size to avoid recalculate it again
504 // unless the text/style changes.
505 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
507 mImpl->mRecalculateNaturalSize = false;
511 naturalSize = mImpl->mVisualModel->GetNaturalSize();
517 float Controller::GetHeightForWidth( float width )
519 // Make sure the model is up-to-date before layouting
520 ProcessModifyEvents();
523 if( width != mImpl->mControlSize.width )
525 // Operations that can be done only once until the text changes.
526 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
534 // Make sure the model is up-to-date before layouting
535 mImpl->UpdateModel( onlyOnceOperations );
537 // Operations that need to be done if the size changes.
538 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
542 DoRelayout( Size( width, MAX_FLOAT ),
543 static_cast<OperationsMask>( onlyOnceOperations |
547 // Do not do again the only once operations.
548 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
550 // Do the size related operations again.
551 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
555 layoutSize = mImpl->mVisualModel->GetActualSize();
558 return layoutSize.height;
561 bool Controller::Relayout( const Size& size )
563 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
565 bool glyphsRemoved( false );
566 if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
568 mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
569 glyphsRemoved = true;
571 // Not worth to relayout if width or height is equal to zero.
572 return glyphsRemoved;
575 if( size != mImpl->mControlSize )
577 // Operations that need to be done if the size changes.
578 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
584 mImpl->mControlSize = size;
587 // Make sure the model is up-to-date before layouting
588 ProcessModifyEvents();
589 mImpl->UpdateModel( mImpl->mOperationsPending );
592 bool updated = DoRelayout( mImpl->mControlSize,
593 mImpl->mOperationsPending,
596 // Do not re-do any operation until something changes.
597 mImpl->mOperationsPending = NO_OPERATION;
599 // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
600 CalculateTextAlignment( size );
602 if( mImpl->mEventData )
604 // Move the cursor, grab handle etc.
605 updated = mImpl->ProcessInputEvents() || updated;
611 void Controller::ProcessModifyEvents()
613 std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
615 for( unsigned int i=0; i<events.size(); ++i )
617 if( ModifyEvent::PLACEHOLDER_TEXT == events[0].type )
619 // Use placeholder if text is empty
620 if( 0u == mImpl->mLogicalModel->mText.Count() &&
621 mImpl->IsShowingPlaceholderText() )
623 mImpl->ReplaceTextWithPlaceholder();
626 else if( ModifyEvent::TEXT_REPLACED == events[0].type )
628 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
629 DALI_ASSERT_DEBUG( 0 == i && "Unexpected TEXT_REPLACED event" );
633 else if( ModifyEvent::TEXT_INSERTED == events[0].type )
637 else if( ModifyEvent::TEXT_DELETED == events[0].type )
639 // Placeholder-text cannot be deleted
640 if( !mImpl->IsShowingPlaceholderText() )
647 // Discard temporary text
651 void Controller::ResetText()
654 mImpl->mLogicalModel->mText.Clear();
655 mImpl->mLogicalModel->mScriptRuns.Clear();
656 mImpl->mLogicalModel->mFontRuns.Clear();
657 mImpl->mLogicalModel->mLineBreakInfo.Clear();
658 mImpl->mLogicalModel->mWordBreakInfo.Clear();
659 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
660 mImpl->mLogicalModel->mCharacterDirections.Clear();
661 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
662 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
663 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
664 mImpl->mVisualModel->mGlyphs.Clear();
665 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
666 mImpl->mVisualModel->mCharactersToGlyph.Clear();
667 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
668 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
669 mImpl->mVisualModel->mGlyphPositions.Clear();
670 mImpl->mVisualModel->mLines.Clear();
671 mImpl->mVisualModel->ClearCaches();
673 // Reset the cursor position
674 if( mImpl->mEventData )
676 mImpl->mEventData->mPrimaryCursorPosition = 0;
679 // We have cleared everything including the placeholder-text
680 mImpl->PlaceholderCleared();
682 // The natural size needs to be re-calculated.
683 mImpl->mRecalculateNaturalSize = true;
685 // Apply modifications to the model
686 mImpl->mOperationsPending = ALL_OPERATIONS;
689 void Controller::TextReplacedEvent()
692 mImpl->mLogicalModel->mScriptRuns.Clear();
693 mImpl->mLogicalModel->mFontRuns.Clear();
694 mImpl->mLogicalModel->mLineBreakInfo.Clear();
695 mImpl->mLogicalModel->mWordBreakInfo.Clear();
696 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
697 mImpl->mLogicalModel->mCharacterDirections.Clear();
698 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
699 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
700 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
701 mImpl->mVisualModel->mGlyphs.Clear();
702 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
703 mImpl->mVisualModel->mCharactersToGlyph.Clear();
704 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
705 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
706 mImpl->mVisualModel->mGlyphPositions.Clear();
707 mImpl->mVisualModel->mLines.Clear();
708 mImpl->mVisualModel->ClearCaches();
710 // The natural size needs to be re-calculated.
711 mImpl->mRecalculateNaturalSize = true;
713 // Apply modifications to the model
714 mImpl->mOperationsPending = ALL_OPERATIONS;
715 mImpl->UpdateModel( ALL_OPERATIONS );
716 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
722 void Controller::TextInsertedEvent()
724 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextInsertedEvent" );
726 // TODO - Optimize this
727 mImpl->mLogicalModel->mScriptRuns.Clear();
728 mImpl->mLogicalModel->mFontRuns.Clear();
729 mImpl->mLogicalModel->mLineBreakInfo.Clear();
730 mImpl->mLogicalModel->mWordBreakInfo.Clear();
731 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
732 mImpl->mLogicalModel->mCharacterDirections.Clear();
733 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
734 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
735 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
736 mImpl->mVisualModel->mGlyphs.Clear();
737 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
738 mImpl->mVisualModel->mCharactersToGlyph.Clear();
739 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
740 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
741 mImpl->mVisualModel->mGlyphPositions.Clear();
742 mImpl->mVisualModel->mLines.Clear();
743 mImpl->mVisualModel->ClearCaches();
745 // The natural size needs to be re-calculated.
746 mImpl->mRecalculateNaturalSize = true;
748 // Apply modifications to the model; TODO - Optimize this
749 mImpl->mOperationsPending = ALL_OPERATIONS;
750 mImpl->UpdateModel( ALL_OPERATIONS );
751 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
756 // Queue a cursor reposition event; this must wait until after DoRelayout()
757 mImpl->mEventData->mUpdateCursorPosition = true;
758 mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
761 void Controller::TextDeletedEvent()
763 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextDeletedEvent" );
765 // TODO - Optimize this
766 mImpl->mLogicalModel->mScriptRuns.Clear();
767 mImpl->mLogicalModel->mFontRuns.Clear();
768 mImpl->mLogicalModel->mLineBreakInfo.Clear();
769 mImpl->mLogicalModel->mWordBreakInfo.Clear();
770 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
771 mImpl->mLogicalModel->mCharacterDirections.Clear();
772 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
773 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
774 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
775 mImpl->mVisualModel->mGlyphs.Clear();
776 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
777 mImpl->mVisualModel->mCharactersToGlyph.Clear();
778 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
779 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
780 mImpl->mVisualModel->mGlyphPositions.Clear();
781 mImpl->mVisualModel->mLines.Clear();
782 mImpl->mVisualModel->ClearCaches();
784 // The natural size needs to be re-calculated.
785 mImpl->mRecalculateNaturalSize = true;
787 // Apply modifications to the model; TODO - Optimize this
788 mImpl->mOperationsPending = ALL_OPERATIONS;
789 mImpl->UpdateModel( ALL_OPERATIONS );
790 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
795 // Queue a cursor reposition event; this must wait until after DoRelayout()
796 mImpl->mEventData->mUpdateCursorPosition = true;
797 mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
800 bool Controller::DoRelayout( const Size& size,
801 OperationsMask operationsRequired,
804 bool viewUpdated( false );
806 // Calculate the operations to be done.
807 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
809 if( LAYOUT & operations )
811 // Some vectors with data needed to layout and reorder may be void
812 // after the first time the text has been laid out.
813 // Fill the vectors again.
815 Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
817 if( 0u == numberOfGlyphs )
819 // Nothing else to do if there is no glyphs.
823 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
824 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
825 Vector<CharacterDirection>& characterDirection = mImpl->mLogicalModel->mCharacterDirections;
826 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
827 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
828 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
830 // Set the layout parameters.
831 LayoutParameters layoutParameters( size,
832 mImpl->mLogicalModel->mText.Begin(),
833 lineBreakInfo.Begin(),
834 wordBreakInfo.Begin(),
835 ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
838 glyphsToCharactersMap.Begin(),
839 charactersPerGlyph.Begin() );
841 // The laid-out lines.
842 // It's not possible to know in how many lines the text is going to be laid-out,
843 // but it can be resized at least with the number of 'paragraphs' to avoid
844 // some re-allocations.
845 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
847 // Delete any previous laid out lines before setting the new ones.
850 // The capacity of the bidirectional paragraph info is the number of paragraphs.
851 lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
853 // Resize the vector of positions to have the same size than the vector of glyphs.
854 Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
855 glyphPositions.Resize( numberOfGlyphs );
857 // Update the visual model.
858 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
866 if( REORDER & operations )
868 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
870 // Check first if there are paragraphs with bidirectional info.
871 if( 0u != bidirectionalInfo.Count() )
874 const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
876 // Reorder the lines.
877 Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
878 lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
879 ReorderLines( bidirectionalInfo,
881 lineBidirectionalInfoRuns );
883 // Set the bidirectional info into the model.
884 const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
885 mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
886 numberOfBidirectionalInfoRuns );
888 // Set the bidirectional info per line into the layout parameters.
889 layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
890 layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
892 // Get the character to glyph conversion table and set into the layout.
893 layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
895 // Get the glyphs per character table and set into the layout.
896 layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
898 // Re-layout the text. Reorder those lines with right to left characters.
899 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
902 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
903 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
904 endIt = lineBidirectionalInfoRuns.End();
908 BidirectionalLineInfoRun& bidiLineInfo = *it;
910 free( bidiLineInfo.visualToLogicalMap );
915 if( ALIGN & operations )
917 mImpl->mLayoutEngine.Align( layoutParameters,
923 // Sets the actual size.
924 if( UPDATE_ACTUAL_SIZE & operations )
926 mImpl->mVisualModel->SetActualSize( layoutSize );
932 layoutSize = mImpl->mVisualModel->GetActualSize();
938 void Controller::CalculateTextAlignment( const Size& size )
940 // Get the direction of the first character.
941 const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
943 const Size& actualSize = mImpl->mVisualModel->GetActualSize();
945 // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
946 LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
947 if( firstParagraphDirection &&
948 ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
950 if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
952 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
956 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
960 switch( horizontalAlignment )
962 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
964 mImpl->mAlignmentOffset.x = 0.f;
967 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
969 const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
970 mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
973 case LayoutEngine::HORIZONTAL_ALIGN_END:
975 mImpl->mAlignmentOffset.x = size.width - actualSize.width;
980 const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
981 switch( verticalAlignment )
983 case LayoutEngine::VERTICAL_ALIGN_TOP:
985 mImpl->mAlignmentOffset.y = 0.f;
988 case LayoutEngine::VERTICAL_ALIGN_CENTER:
990 const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
991 mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
994 case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
996 mImpl->mAlignmentOffset.y = size.height - actualSize.height;
1002 LayoutEngine& Controller::GetLayoutEngine()
1004 return mImpl->mLayoutEngine;
1007 View& Controller::GetView()
1009 return mImpl->mView;
1012 void Controller::KeyboardFocusGainEvent()
1014 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
1016 if( mImpl->mEventData )
1018 Event event( Event::KEYBOARD_FOCUS_GAIN_EVENT );
1019 mImpl->mEventData->mEventQueue.push_back( event );
1021 mImpl->RequestRelayout();
1025 void Controller::KeyboardFocusLostEvent()
1027 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
1029 if( mImpl->mEventData )
1031 Event event( Event::KEYBOARD_FOCUS_LOST_EVENT );
1032 mImpl->mEventData->mEventQueue.push_back( event );
1034 mImpl->RequestRelayout();
1038 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1040 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
1042 if( mImpl->mEventData &&
1043 keyEvent.state == KeyEvent::Down )
1045 int keyCode = keyEvent.keyCode;
1046 const std::string& keyString = keyEvent.keyPressed;
1048 // Pre-process to separate modifying events from non-modifying input events.
1049 if( Dali::DALI_KEY_ESCAPE == keyCode )
1051 // Escape key is a special case which causes focus loss
1052 KeyboardFocusLostEvent();
1054 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
1055 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1056 Dali::DALI_KEY_CURSOR_UP == keyCode ||
1057 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1059 Event event( Event::CURSOR_KEY_EVENT );
1060 event.p1.mInt = keyCode;
1061 mImpl->mEventData->mEventQueue.push_back( event );
1063 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1065 // Remove the character before the current cursor position
1066 bool removed = RemoveText( -1, 1 );
1070 if( 0u == mImpl->mLogicalModel->mText.Count() )
1072 mImpl->ShowPlaceholderText();
1076 mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
1082 InsertText( keyString, COMMIT );
1085 mImpl->ChangeState( EventData::EDITING ); // todo Confirm this is the best place to change the state of
1087 mImpl->RequestRelayout();
1093 void Controller::InsertText( const std::string& text, Controller::InsertType type )
1095 bool removedPreEdit( false );
1096 bool maxLengthReached( false );
1098 if( ! text.empty() )
1100 if( mImpl->IsShowingPlaceholderText() )
1106 if( mImpl->mEventData )
1108 if( COMMIT == type )
1110 mImpl->mEventData->mPreEditFlag = false;
1114 if( mImpl->mEventData->mPreEditFlag &&
1115 0 != mImpl->mEventData->mPreEditLength )
1117 // Remove previous pre-edit text
1118 mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
1119 removedPreEdit = RemoveText( -1, mImpl->mEventData->mPreEditLength );
1123 // Record the start of the pre-edit text
1124 mImpl->mEventData->mPreEditStartPosition = mImpl->mEventData->mPrimaryCursorPosition;
1125 mImpl->mEventData->mPreEditLength = text.size();
1128 mImpl->mEventData->mPreEditFlag = true;
1132 if( ! text.empty() )
1134 // Convert text into UTF-32
1135 Vector<Character> utf32Characters;
1136 utf32Characters.Resize( text.size() );
1138 // This is a bit horrible but std::string returns a (signed) char*
1139 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1141 // Transform a text array encoded in utf8 into an array encoded in utf32.
1142 // It returns the actual number of characters.
1143 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1144 utf32Characters.Resize( characterCount );
1146 const Length numberOfCharactersInModel = mImpl->mLogicalModel->GetNumberOfCharacters();
1148 // Restrict new text to fit within Maximum characters setting
1149 Length maxSizeOfNewText = std::min ( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
1150 maxLengthReached = ( characterCount > maxSizeOfNewText );
1152 // Insert at current cursor position
1153 CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
1155 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1157 if( cursorIndex < numberOfCharactersInModel )
1159 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1163 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1166 cursorIndex += maxSizeOfNewText;
1169 if( removedPreEdit || !text.empty() )
1171 // Queue an inserted event
1172 mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
1175 if( maxLengthReached )
1177 mImpl->mControlInterface.MaxLengthReached();
1179 mImpl->PreEditReset();
1183 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1185 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
1187 if( mImpl->mEventData )
1189 Event event( Event::TAP_EVENT );
1190 event.p1.mUint = tapCount;
1191 event.p2.mFloat = x;
1192 event.p3.mFloat = y;
1193 mImpl->mEventData->mEventQueue.push_back( event );
1195 mImpl->RequestRelayout();
1198 // Reset keyboard as tap event has occurred.
1199 mImpl->PreEditReset();
1202 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1204 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
1206 if( mImpl->mEventData )
1208 Event event( Event::PAN_EVENT );
1209 event.p1.mInt = state;
1210 event.p2.mFloat = displacement.x;
1211 event.p3.mFloat = displacement.y;
1212 mImpl->mEventData->mEventQueue.push_back( event );
1214 mImpl->RequestRelayout();
1218 void Controller::HandleEvent( HandleType handleType, HandleState state, float x, float y )
1220 DALI_ASSERT_DEBUG( mImpl->mEventData && "Controller::HandleEvent. Unexpected HandleEvent" );
1222 if( mImpl->mEventData )
1224 switch( handleType )
1228 Event event( Event::GRAB_HANDLE_EVENT );
1229 event.p1.mUint = state;
1230 event.p2.mFloat = x;
1231 event.p3.mFloat = y;
1233 mImpl->mEventData->mEventQueue.push_back( event );
1236 case LEFT_SELECTION_HANDLE:
1238 Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
1239 event.p1.mUint = state;
1240 event.p2.mFloat = x;
1241 event.p3.mFloat = y;
1243 mImpl->mEventData->mEventQueue.push_back( event );
1246 case RIGHT_SELECTION_HANDLE:
1248 Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
1249 event.p1.mUint = state;
1250 event.p2.mFloat = x;
1251 event.p3.mFloat = y;
1253 mImpl->mEventData->mEventQueue.push_back( event );
1256 case HANDLE_TYPE_COUNT:
1258 DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
1262 mImpl->RequestRelayout();
1266 Controller::~Controller()
1271 Controller::Controller( ControlInterface& controlInterface )
1274 mImpl = new Controller::Impl( controlInterface );
1279 } // namespace Toolkit