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>
24 #include <dali/integration-api/debug.h>
25 #include <dali/devel-api/adaptor-framework/clipboard-event-notifier.h>
28 #include <dali-toolkit/internal/text/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/character-set-conversion.h>
30 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
31 #include <dali-toolkit/internal/text/text-controller-impl.h>
36 #if defined(DEBUG_ENABLED)
37 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
40 const float MAX_FLOAT = std::numeric_limits<float>::max();
41 const unsigned int POINTS_PER_INCH = 72;
43 const std::string EMPTY_STRING("");
45 float ConvertToEven( float value )
47 int intValue(static_cast<int>( value ));
48 return static_cast<float>(intValue % 2 == 0) ? intValue : (intValue + 1);
62 ControllerPtr Controller::New( ControlInterface& controlInterface )
64 return ControllerPtr( new Controller( controlInterface ) );
67 void Controller::EnableTextInput( DecoratorPtr decorator )
69 if( !mImpl->mEventData )
71 mImpl->mEventData = new EventData( decorator );
75 void Controller::SetText( const std::string& text )
77 // Remove the previously set text
80 CharacterIndex lastCursorIndex = 0u;
82 if( mImpl->mEventData )
84 // If popup shown then hide it by switching to Editing state
85 if( ( EventData::SELECTING == mImpl->mEventData->mState ) ||
86 ( EventData::SELECTION_CHANGED == mImpl->mEventData->mState ) ||
87 ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) ||
88 ( EventData::EDITING_WITH_GRAB_HANDLE == mImpl->mEventData->mState ) )
90 mImpl->ChangeState( EventData::EDITING );
96 // Convert text into UTF-32
97 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
98 utf32Characters.Resize( text.size() );
100 // This is a bit horrible but std::string returns a (signed) char*
101 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
103 // Transform a text array encoded in utf8 into an array encoded in utf32.
104 // It returns the actual number of characters.
105 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
106 utf32Characters.Resize( characterCount );
108 DALI_ASSERT_DEBUG( text.size() >= characterCount && "Invalid UTF32 conversion length" );
109 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText %p UTF8 size %d, UTF32 size %d\n", this, text.size(), mImpl->mLogicalModel->mText.Count() );
111 // To reset the cursor position
112 lastCursorIndex = characterCount;
114 // Update the rest of the model during size negotiation
115 mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
117 // The natural size needs to be re-calculated.
118 mImpl->mRecalculateNaturalSize = true;
120 // Apply modifications to the model
121 mImpl->mOperationsPending = ALL_OPERATIONS;
125 ShowPlaceholderText();
128 // Resets the cursor position.
129 ResetCursorPosition( lastCursorIndex );
131 // Scrolls the text to make the cursor visible.
132 ResetScrollPosition();
134 mImpl->RequestRelayout();
136 if( mImpl->mEventData )
138 // Cancel previously queued events
139 mImpl->mEventData->mEventQueue.clear();
142 // Reset keyboard as text changed
143 mImpl->ResetImfManager();
145 // Do this last since it provides callbacks into application code
146 mImpl->mControlInterface.TextChanged();
149 void Controller::GetText( std::string& text ) const
151 if( ! mImpl->IsShowingPlaceholderText() )
153 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
155 if( 0u != utf32Characters.Count() )
157 Utf32ToUtf8( &utf32Characters[0], utf32Characters.Count(), text );
162 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::GetText %p empty (but showing placeholder)\n", this );
166 unsigned int Controller::GetLogicalCursorPosition() const
168 if( mImpl->mEventData )
170 return mImpl->mEventData->mPrimaryCursorPosition;
176 void Controller::SetPlaceholderText( PlaceholderType type, const std::string& text )
178 if( mImpl->mEventData )
180 if( PLACEHOLDER_TYPE_INACTIVE == type )
182 mImpl->mEventData->mPlaceholderTextInactive = text;
186 mImpl->mEventData->mPlaceholderTextActive = text;
189 // Update placeholder if there is no text
190 if( mImpl->IsShowingPlaceholderText() ||
191 0u == mImpl->mLogicalModel->mText.Count() )
193 ShowPlaceholderText();
198 void Controller::GetPlaceholderText( PlaceholderType type, std::string& text ) const
200 if( mImpl->mEventData )
202 if( PLACEHOLDER_TYPE_INACTIVE == type )
204 text = mImpl->mEventData->mPlaceholderTextInactive;
208 text = mImpl->mEventData->mPlaceholderTextActive;
213 void Controller::SetMaximumNumberOfCharacters( int maxCharacters )
215 if ( maxCharacters >= 0 )
217 mImpl->mMaximumNumberOfCharacters = maxCharacters;
221 int Controller::GetMaximumNumberOfCharacters()
223 return mImpl->mMaximumNumberOfCharacters;
226 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
228 if( !mImpl->mFontDefaults )
230 mImpl->mFontDefaults = new FontDefaults();
233 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
235 // Clear the font-specific data
238 mImpl->mOperationsPending = ALL_OPERATIONS;
239 mImpl->mRecalculateNaturalSize = true;
241 mImpl->RequestRelayout();
244 const std::string& Controller::GetDefaultFontFamily() const
246 if( mImpl->mFontDefaults )
248 return mImpl->mFontDefaults->mDefaultFontFamily;
254 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
256 if( !mImpl->mFontDefaults )
258 mImpl->mFontDefaults = new FontDefaults();
261 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
263 // Clear the font-specific data
266 mImpl->mOperationsPending = ALL_OPERATIONS;
267 mImpl->mRecalculateNaturalSize = true;
269 mImpl->RequestRelayout();
272 const std::string& Controller::GetDefaultFontStyle() const
274 if( mImpl->mFontDefaults )
276 return mImpl->mFontDefaults->mDefaultFontStyle;
282 void Controller::SetDefaultPointSize( float pointSize )
284 if( !mImpl->mFontDefaults )
286 mImpl->mFontDefaults = new FontDefaults();
289 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
291 unsigned int horizontalDpi( 0u );
292 unsigned int verticalDpi( 0u );
293 mImpl->mFontClient.GetDpi( horizontalDpi, verticalDpi );
295 // Adjust the metrics if the fixed-size font should be down-scaled
296 int maxEmojiSize( pointSize/POINTS_PER_INCH * verticalDpi );
297 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetDefaultPointSize %p setting MaxEmojiSize %d\n", this, maxEmojiSize );
298 mImpl->mMetrics->SetMaxEmojiSize( maxEmojiSize );
300 // Clear the font-specific data
303 mImpl->mOperationsPending = ALL_OPERATIONS;
304 mImpl->mRecalculateNaturalSize = true;
306 mImpl->RequestRelayout();
309 float Controller::GetDefaultPointSize() const
311 if( mImpl->mFontDefaults )
313 return mImpl->mFontDefaults->mDefaultPointSize;
319 void Controller::SetTextColor( const Vector4& textColor )
321 mImpl->mTextColor = textColor;
323 if( !mImpl->IsShowingPlaceholderText() )
325 mImpl->mVisualModel->SetTextColor( textColor );
327 mImpl->RequestRelayout();
331 const Vector4& Controller::GetTextColor() const
333 return mImpl->mTextColor;
336 bool Controller::RemoveText( int cursorOffset, int numberOfChars )
338 bool removed( false );
340 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p mText.Count() %d cursor %d cursorOffset %d numberOfChars %d\n",
341 this, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition, cursorOffset, numberOfChars );
343 if( !mImpl->IsShowingPlaceholderText() )
345 // Delete at current cursor position
346 Vector<Character>& currentText = mImpl->mLogicalModel->mText;
347 CharacterIndex& oldCursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
349 CharacterIndex cursorIndex = oldCursorIndex;
351 // Validate the cursor position & number of characters
352 if( static_cast< CharacterIndex >( std::abs( cursorOffset ) ) <= cursorIndex )
354 cursorIndex = oldCursorIndex + cursorOffset;
357 if( (cursorIndex + numberOfChars) > currentText.Count() )
359 numberOfChars = currentText.Count() - cursorIndex;
362 if( (cursorIndex + numberOfChars) <= currentText.Count() )
364 Vector<Character>::Iterator first = currentText.Begin() + cursorIndex;
365 Vector<Character>::Iterator last = first + numberOfChars;
367 currentText.Erase( first, last );
369 // Cursor position retreat
370 oldCursorIndex = cursorIndex;
372 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p removed %d\n", this, numberOfChars );
380 void Controller::SetPlaceholderTextColor( const Vector4& textColor )
382 if( mImpl->mEventData )
384 mImpl->mEventData->mPlaceholderTextColor = textColor;
387 if( mImpl->IsShowingPlaceholderText() )
389 mImpl->mVisualModel->SetTextColor( textColor );
390 mImpl->RequestRelayout();
394 const Vector4& Controller::GetPlaceholderTextColor() const
396 if( mImpl->mEventData )
398 return mImpl->mEventData->mPlaceholderTextColor;
404 void Controller::SetShadowOffset( const Vector2& shadowOffset )
406 mImpl->mVisualModel->SetShadowOffset( shadowOffset );
408 mImpl->RequestRelayout();
411 const Vector2& Controller::GetShadowOffset() const
413 return mImpl->mVisualModel->GetShadowOffset();
416 void Controller::SetShadowColor( const Vector4& shadowColor )
418 mImpl->mVisualModel->SetShadowColor( shadowColor );
420 mImpl->RequestRelayout();
423 const Vector4& Controller::GetShadowColor() const
425 return mImpl->mVisualModel->GetShadowColor();
428 void Controller::SetUnderlineColor( const Vector4& color )
430 mImpl->mVisualModel->SetUnderlineColor( color );
432 mImpl->RequestRelayout();
435 const Vector4& Controller::GetUnderlineColor() const
437 return mImpl->mVisualModel->GetUnderlineColor();
440 void Controller::SetUnderlineEnabled( bool enabled )
442 mImpl->mVisualModel->SetUnderlineEnabled( enabled );
444 mImpl->RequestRelayout();
447 bool Controller::IsUnderlineEnabled() const
449 return mImpl->mVisualModel->IsUnderlineEnabled();
452 void Controller::SetUnderlineHeight( float height )
454 mImpl->mVisualModel->SetUnderlineHeight( height );
456 mImpl->RequestRelayout();
459 float Controller::GetUnderlineHeight() const
461 return mImpl->mVisualModel->GetUnderlineHeight();
464 void Controller::SetEnableCursorBlink( bool enable )
466 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "TextInput disabled" );
468 if( mImpl->mEventData )
470 mImpl->mEventData->mCursorBlinkEnabled = enable;
473 mImpl->mEventData->mDecorator )
475 mImpl->mEventData->mDecorator->StopCursorBlink();
480 bool Controller::GetEnableCursorBlink() const
482 if( mImpl->mEventData )
484 return mImpl->mEventData->mCursorBlinkEnabled;
490 const Vector2& Controller::GetScrollPosition() const
492 if( mImpl->mEventData )
494 return mImpl->mEventData->mScrollPosition;
497 return Vector2::ZERO;
500 const Vector2& Controller::GetAlignmentOffset() const
502 return mImpl->mAlignmentOffset;
505 Vector3 Controller::GetNaturalSize()
507 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetNaturalSize\n" );
510 // Make sure the model is up-to-date before layouting
511 ProcessModifyEvents();
513 if( mImpl->mRecalculateNaturalSize )
515 // Operations that can be done only once until the text changes.
516 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
524 // Make sure the model is up-to-date before layouting
525 mImpl->UpdateModel( onlyOnceOperations );
527 // Operations that need to be done if the size changes.
528 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
532 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
533 static_cast<OperationsMask>( onlyOnceOperations |
535 naturalSize.GetVectorXY() );
537 // Do not do again the only once operations.
538 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
540 // Do the size related operations again.
541 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
543 // Stores the natural size to avoid recalculate it again
544 // unless the text/style changes.
545 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
547 mImpl->mRecalculateNaturalSize = false;
549 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize calculated %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
553 naturalSize = mImpl->mVisualModel->GetNaturalSize();
555 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize cached %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
558 naturalSize.x = ConvertToEven( naturalSize.x );
559 naturalSize.y = ConvertToEven( naturalSize.y );
564 float Controller::GetHeightForWidth( float width )
566 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetHeightForWidth %p width %f\n", this, width );
567 // Make sure the model is up-to-date before layouting
568 ProcessModifyEvents();
571 if( width != mImpl->mVisualModel->mControlSize.width )
573 // Operations that can be done only once until the text changes.
574 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
582 // Make sure the model is up-to-date before layouting
583 mImpl->UpdateModel( onlyOnceOperations );
585 // Operations that need to be done if the size changes.
586 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
590 DoRelayout( Size( width, MAX_FLOAT ),
591 static_cast<OperationsMask>( onlyOnceOperations |
595 // Do not do again the only once operations.
596 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
598 // Do the size related operations again.
599 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
600 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth calculated %f\n", layoutSize.height );
604 layoutSize = mImpl->mVisualModel->GetActualSize();
605 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth cached %f\n", layoutSize.height );
608 return layoutSize.height;
611 bool Controller::Relayout( const Size& size )
613 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::Relayout %p size %f,%f\n", this, size.width, size.height );
615 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
617 bool glyphsRemoved( false );
618 if( 0u != mImpl->mVisualModel->mGlyphPositions.Count() )
620 mImpl->mVisualModel->mGlyphPositions.Clear();
621 glyphsRemoved = true;
623 // Not worth to relayout if width or height is equal to zero.
624 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout (skipped)\n" );
625 return glyphsRemoved;
628 if( size != mImpl->mVisualModel->mControlSize )
630 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "new size (previous size %f,%f)\n", mImpl->mVisualModel->mControlSize.width, mImpl->mVisualModel->mControlSize.height );
632 // Operations that need to be done if the size changes.
633 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
639 mImpl->mVisualModel->mControlSize = size;
642 // Make sure the model is up-to-date before layouting
643 ProcessModifyEvents();
644 mImpl->UpdateModel( mImpl->mOperationsPending );
647 bool updated = DoRelayout( mImpl->mVisualModel->mControlSize,
648 mImpl->mOperationsPending,
651 // Do not re-do any operation until something changes.
652 mImpl->mOperationsPending = NO_OPERATION;
654 // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
655 CalculateTextAlignment( size );
657 if( mImpl->mEventData )
659 // Move the cursor, grab handle etc.
660 updated = mImpl->ProcessInputEvents() || updated;
663 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout\n" );
667 void Controller::ProcessModifyEvents()
669 std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
671 for( unsigned int i=0; i<events.size(); ++i )
673 if( ModifyEvent::TEXT_REPLACED == events[0].type )
675 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
676 DALI_ASSERT_DEBUG( 0 == i && "Unexpected TEXT_REPLACED event" );
680 else if( ModifyEvent::TEXT_INSERTED == events[0].type )
684 else if( ModifyEvent::TEXT_DELETED == events[0].type )
686 // Placeholder-text cannot be deleted
687 if( !mImpl->IsShowingPlaceholderText() )
694 if( mImpl->mEventData &&
697 // When the text is being modified, delay cursor blinking
698 mImpl->mEventData->mDecorator->DelayCursorBlink();
701 // Discard temporary text
705 void Controller::ResetText()
708 mImpl->mLogicalModel->mText.Clear();
711 // We have cleared everything including the placeholder-text
712 mImpl->PlaceholderCleared();
714 // The natural size needs to be re-calculated.
715 mImpl->mRecalculateNaturalSize = true;
717 // Apply modifications to the model
718 mImpl->mOperationsPending = ALL_OPERATIONS;
721 void Controller::ResetCursorPosition( CharacterIndex cursorIndex )
723 // Reset the cursor position
724 if( NULL != mImpl->mEventData )
726 mImpl->mEventData->mPrimaryCursorPosition = cursorIndex;
728 // Update the cursor if it's in editing mode.
729 if( ( EventData::EDITING == mImpl->mEventData->mState ) ||
730 ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) ||
731 ( EventData::EDITING_WITH_GRAB_HANDLE == mImpl->mEventData->mState ) )
733 mImpl->mEventData->mUpdateCursorPosition = true;
738 void Controller::ResetScrollPosition()
740 if( NULL != mImpl->mEventData )
742 // Reset the scroll position.
743 mImpl->mEventData->mScrollPosition = Vector2::ZERO;
744 mImpl->mEventData->mScrollAfterUpdatePosition = true;
748 void Controller::TextReplacedEvent()
753 // The natural size needs to be re-calculated.
754 mImpl->mRecalculateNaturalSize = true;
756 // Apply modifications to the model
757 mImpl->mOperationsPending = ALL_OPERATIONS;
758 mImpl->UpdateModel( ALL_OPERATIONS );
759 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
765 void Controller::TextInsertedEvent()
767 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextInsertedEvent" );
769 // TODO - Optimize this
772 // The natural size needs to be re-calculated.
773 mImpl->mRecalculateNaturalSize = true;
775 // Apply modifications to the model; TODO - Optimize this
776 mImpl->mOperationsPending = ALL_OPERATIONS;
777 mImpl->UpdateModel( ALL_OPERATIONS );
778 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
783 // Queue a cursor reposition event; this must wait until after DoRelayout()
784 if( ( EventData::EDITING == mImpl->mEventData->mState ) ||
785 ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) ||
786 ( EventData::EDITING_WITH_GRAB_HANDLE == mImpl->mEventData->mState ) )
788 mImpl->mEventData->mUpdateCursorPosition = true;
789 mImpl->mEventData->mScrollAfterUpdatePosition = true;
793 void Controller::TextDeletedEvent()
795 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextDeletedEvent" );
797 // TODO - Optimize this
800 // The natural size needs to be re-calculated.
801 mImpl->mRecalculateNaturalSize = true;
803 // Apply modifications to the model; TODO - Optimize this
804 mImpl->mOperationsPending = ALL_OPERATIONS;
805 mImpl->UpdateModel( ALL_OPERATIONS );
806 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
811 // Queue a cursor reposition event; this must wait until after DoRelayout()
812 if( 0u == mImpl->mLogicalModel->mText.Count() )
814 mImpl->mEventData->mUpdateCursorPosition = true;
818 mImpl->mEventData->mScrollAfterDelete = true;
822 bool Controller::DoRelayout( const Size& size,
823 OperationsMask operationsRequired,
826 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout %p size %f,%f\n", this, size.width, size.height );
827 bool viewUpdated( false );
829 // Calculate the operations to be done.
830 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
832 if( LAYOUT & operations )
834 // Some vectors with data needed to layout and reorder may be void
835 // after the first time the text has been laid out.
836 // Fill the vectors again.
838 const Length numberOfGlyphs = mImpl->mVisualModel->mGlyphs.Count();
840 if( 0u == numberOfGlyphs )
842 // Nothing else to do if there is no glyphs.
843 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout no glyphs, view updated true\n" );
847 const Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
848 const Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
849 const Vector<CharacterDirection>& characterDirection = mImpl->mLogicalModel->mCharacterDirections;
850 const Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
851 const Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
852 const Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
853 const Character* const textBuffer = mImpl->mLogicalModel->mText.Begin();
855 // Set the layout parameters.
856 LayoutParameters layoutParameters( size,
858 lineBreakInfo.Begin(),
859 wordBreakInfo.Begin(),
860 ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
863 glyphsToCharactersMap.Begin(),
864 charactersPerGlyph.Begin() );
866 // The laid-out lines.
867 // It's not possible to know in how many lines the text is going to be laid-out,
868 // but it can be resized at least with the number of 'paragraphs' to avoid
869 // some re-allocations.
870 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
872 // Delete any previous laid out lines before setting the new ones.
875 // The capacity of the bidirectional paragraph info is the number of paragraphs.
876 lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
878 // Resize the vector of positions to have the same size than the vector of glyphs.
879 Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
880 glyphPositions.Resize( numberOfGlyphs );
882 // Whether the last character is a new paragraph character.
883 layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph( *( textBuffer + ( mImpl->mLogicalModel->mText.Count() - 1u ) ) );
885 // Update the visual model.
886 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
894 if( REORDER & operations )
896 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
898 // Check first if there are paragraphs with bidirectional info.
899 if( 0u != bidirectionalInfo.Count() )
902 const Length numberOfLines = mImpl->mVisualModel->mLines.Count();
904 // Reorder the lines.
905 Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
906 lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
907 ReorderLines( bidirectionalInfo,
909 lineBidirectionalInfoRuns );
911 // Set the bidirectional info into the model.
912 const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
913 mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
914 numberOfBidirectionalInfoRuns );
916 // Set the bidirectional info per line into the layout parameters.
917 layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
918 layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
920 // Get the character to glyph conversion table and set into the layout.
921 layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
923 // Get the glyphs per character table and set into the layout.
924 layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
926 // Re-layout the text. Reorder those lines with right to left characters.
927 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
930 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
931 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
932 endIt = lineBidirectionalInfoRuns.End();
936 BidirectionalLineInfoRun& bidiLineInfo = *it;
938 free( bidiLineInfo.visualToLogicalMap );
943 // Sets the actual size.
944 if( UPDATE_ACTUAL_SIZE & operations )
946 mImpl->mVisualModel->SetActualSize( layoutSize );
952 layoutSize = mImpl->mVisualModel->GetActualSize();
955 if( ALIGN & operations )
957 // The laid-out lines.
958 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
960 mImpl->mLayoutEngine.Align( layoutSize,
966 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout, view updated %s\n", ( viewUpdated ? "true" : "false" ) );
970 void Controller::SetMultiLineEnabled( bool enable )
972 const LayoutEngine::Layout layout = enable ? LayoutEngine::MULTI_LINE_BOX : LayoutEngine::SINGLE_LINE_BOX;
974 if( layout != mImpl->mLayoutEngine.GetLayout() )
976 // Set the layout type.
977 mImpl->mLayoutEngine.SetLayout( layout );
979 // Set the flags to redo the layout operations
980 const OperationsMask layoutOperations = static_cast<OperationsMask>( LAYOUT |
985 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | layoutOperations );
987 mImpl->RequestRelayout();
991 bool Controller::IsMultiLineEnabled() const
993 return LayoutEngine::MULTI_LINE_BOX == mImpl->mLayoutEngine.GetLayout();
996 void Controller::SetHorizontalAlignment( LayoutEngine::HorizontalAlignment alignment )
998 if( alignment != mImpl->mLayoutEngine.GetHorizontalAlignment() )
1000 // Set the alignment.
1001 mImpl->mLayoutEngine.SetHorizontalAlignment( alignment );
1003 // Set the flag to redo the alignment operation.
1004 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
1006 mImpl->RequestRelayout();
1010 LayoutEngine::HorizontalAlignment Controller::GetHorizontalAlignment() const
1012 return mImpl->mLayoutEngine.GetHorizontalAlignment();
1015 void Controller::SetVerticalAlignment( LayoutEngine::VerticalAlignment alignment )
1017 if( alignment != mImpl->mLayoutEngine.GetVerticalAlignment() )
1019 // Set the alignment.
1020 mImpl->mLayoutEngine.SetVerticalAlignment( alignment );
1022 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
1024 mImpl->RequestRelayout();
1028 LayoutEngine::VerticalAlignment Controller::GetVerticalAlignment() const
1030 return mImpl->mLayoutEngine.GetVerticalAlignment();
1033 void Controller::CalculateTextAlignment( const Size& size )
1035 // Get the direction of the first character.
1036 const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
1038 Size actualSize = mImpl->mVisualModel->GetActualSize();
1039 if( fabsf( actualSize.height ) < Math::MACHINE_EPSILON_1000 )
1041 // Get the line height of the default font.
1042 actualSize.height = mImpl->GetDefaultFontLineHeight();
1045 // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1046 LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
1047 if( firstParagraphDirection &&
1048 ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
1050 if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
1052 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
1056 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
1060 switch( horizontalAlignment )
1062 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1064 mImpl->mAlignmentOffset.x = 0.f;
1067 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1069 const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1070 mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1073 case LayoutEngine::HORIZONTAL_ALIGN_END:
1075 mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1080 const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1081 switch( verticalAlignment )
1083 case LayoutEngine::VERTICAL_ALIGN_TOP:
1085 mImpl->mAlignmentOffset.y = 0.f;
1088 case LayoutEngine::VERTICAL_ALIGN_CENTER:
1090 const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
1091 mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
1094 case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1096 mImpl->mAlignmentOffset.y = size.height - actualSize.height;
1102 LayoutEngine& Controller::GetLayoutEngine()
1104 return mImpl->mLayoutEngine;
1107 View& Controller::GetView()
1109 return mImpl->mView;
1112 void Controller::KeyboardFocusGainEvent()
1114 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
1116 if( mImpl->mEventData )
1118 if( ( EventData::INACTIVE == mImpl->mEventData->mState ) ||
1119 ( EventData::INTERRUPTED == mImpl->mEventData->mState ) )
1121 mImpl->ChangeState( EventData::EDITING );
1122 mImpl->mEventData->mUpdateCursorPosition = true; //If editing started without tap event, cursor update must be triggered.
1125 if( mImpl->IsShowingPlaceholderText() )
1127 // Show alternative placeholder-text when editing
1128 ShowPlaceholderText();
1131 mImpl->RequestRelayout();
1135 void Controller::KeyboardFocusLostEvent()
1137 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
1139 if( mImpl->mEventData )
1141 if ( EventData::INTERRUPTED != mImpl->mEventData->mState )
1143 mImpl->ChangeState( EventData::INACTIVE );
1145 if( mImpl->IsShowingPlaceholderText() )
1147 // Revert to regular placeholder-text when not editing
1148 ShowPlaceholderText();
1152 mImpl->RequestRelayout();
1155 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1157 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
1159 bool textChanged( false );
1161 if( mImpl->mEventData &&
1162 keyEvent.state == KeyEvent::Down )
1164 int keyCode = keyEvent.keyCode;
1165 const std::string& keyString = keyEvent.keyPressed;
1167 // Pre-process to separate modifying events from non-modifying input events.
1168 if( Dali::DALI_KEY_ESCAPE == keyCode )
1170 // Escape key is a special case which causes focus loss
1171 KeyboardFocusLostEvent();
1173 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
1174 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1175 Dali::DALI_KEY_CURSOR_UP == keyCode ||
1176 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1178 Event event( Event::CURSOR_KEY_EVENT );
1179 event.p1.mInt = keyCode;
1180 mImpl->mEventData->mEventQueue.push_back( event );
1182 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1184 textChanged = BackspaceKeyEvent();
1186 else if ( IsKey( keyEvent, Dali::DALI_KEY_POWER ) )
1188 mImpl->ChangeState( EventData::INTERRUPTED ); // State is not INACTIVE as expect to return to edit mode.
1189 // Avoids calling the InsertText() method which can delete selected text
1191 else if ( IsKey( keyEvent, Dali::DALI_KEY_MENU ) ||
1192 IsKey( keyEvent, Dali::DALI_KEY_HOME ) )
1194 mImpl->ChangeState( EventData::INACTIVE );
1195 // Menu/Home key behaviour does not allow edit mode to resume like Power key
1196 // Avoids calling the InsertText() method which can delete selected text
1198 else if( Dali::DALI_KEY_SHIFT_LEFT == keyCode )
1200 // DALI_KEY_SHIFT_LEFT is the key code for the Left Shift. It's sent (by the imf?) when the predictive text is enabled
1201 // and a character is typed after the type of a upper case latin character.
1207 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", this, keyString.c_str() );
1209 // IMF manager is no longer handling key-events
1210 mImpl->ClearPreEditFlag();
1212 InsertText( keyString, COMMIT );
1216 if ( ( mImpl->mEventData->mState != EventData::INTERRUPTED ) &&
1217 ( mImpl->mEventData->mState != EventData::INACTIVE ) )
1219 mImpl->ChangeState( EventData::EDITING );
1222 mImpl->RequestRelayout();
1227 // Do this last since it provides callbacks into application code
1228 mImpl->mControlInterface.TextChanged();
1234 void Controller::InsertText( const std::string& text, Controller::InsertType type )
1236 bool removedPrevious( false );
1237 bool maxLengthReached( false );
1239 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
1240 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPrimaryCursorPosition %d mPreEditFlag %d mPreEditStartPosition %d mPreEditLength %d\n",
1241 this, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"),
1242 mImpl->mEventData->mPrimaryCursorPosition, mImpl->mEventData->mPreEditFlag, mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1244 // TODO: At the moment the underline runs are only for pre-edit.
1245 mImpl->mVisualModel->mUnderlineRuns.Clear();
1247 Vector<Character> utf32Characters;
1248 Length characterCount( 0u );
1250 // Remove the previous IMF pre-edit (predicitive text)
1251 if( mImpl->mEventData &&
1252 mImpl->mEventData->mPreEditFlag &&
1253 0 != mImpl->mEventData->mPreEditLength )
1255 CharacterIndex offset = mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition;
1257 removedPrevious = RemoveText( -static_cast<int>(offset), mImpl->mEventData->mPreEditLength );
1259 mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
1260 mImpl->mEventData->mPreEditLength = 0;
1264 // Remove the previous Selection
1265 removedPrevious = RemoveSelectedText();
1270 // Convert text into UTF-32
1271 utf32Characters.Resize( text.size() );
1273 // This is a bit horrible but std::string returns a (signed) char*
1274 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1276 // Transform a text array encoded in utf8 into an array encoded in utf32.
1277 // It returns the actual number of characters.
1278 characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1279 utf32Characters.Resize( characterCount );
1281 DALI_ASSERT_DEBUG( text.size() >= utf32Characters.Count() && "Invalid UTF32 conversion length" );
1282 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "UTF8 size %d, UTF32 size %d\n", text.size(), utf32Characters.Count() );
1285 if( 0u != utf32Characters.Count() ) // Check if Utf8ToUtf32 conversion succeeded
1287 // The placeholder text is no longer needed
1288 if( mImpl->IsShowingPlaceholderText() )
1293 mImpl->ChangeState( EventData::EDITING );
1295 // Handle the IMF (predicitive text) state changes
1296 if( mImpl->mEventData )
1298 if( COMMIT == type )
1300 // IMF manager is no longer handling key-events
1301 mImpl->ClearPreEditFlag();
1305 if( !mImpl->mEventData->mPreEditFlag )
1307 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Entered PreEdit state" );
1309 // Record the start of the pre-edit text
1310 mImpl->mEventData->mPreEditStartPosition = mImpl->mEventData->mPrimaryCursorPosition;
1313 mImpl->mEventData->mPreEditLength = utf32Characters.Count();
1314 mImpl->mEventData->mPreEditFlag = true;
1316 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1320 const Length numberOfCharactersInModel = mImpl->mLogicalModel->mText.Count();
1322 // Restrict new text to fit within Maximum characters setting
1323 Length maxSizeOfNewText = std::min ( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
1324 maxLengthReached = ( characterCount > maxSizeOfNewText );
1326 // Insert at current cursor position
1327 CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
1329 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1331 if( cursorIndex < numberOfCharactersInModel )
1333 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1337 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1340 cursorIndex += maxSizeOfNewText;
1342 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Inserted %d characters, new size %d new cursor %d\n", maxSizeOfNewText, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition );
1345 if( 0u == mImpl->mLogicalModel->mText.Count() &&
1346 mImpl->IsPlaceholderAvailable() )
1348 // Show place-holder if empty after removing the pre-edit text
1349 ShowPlaceholderText();
1350 mImpl->mEventData->mUpdateCursorPosition = true;
1351 mImpl->ClearPreEditFlag();
1353 else if( removedPrevious ||
1354 0 != utf32Characters.Count() )
1356 // Queue an inserted event
1357 mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
1360 if( maxLengthReached )
1362 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", mImpl->mLogicalModel->mText.Count() );
1364 mImpl->ResetImfManager();
1366 // Do this last since it provides callbacks into application code
1367 mImpl->mControlInterface.MaxLengthReached();
1371 bool Controller::RemoveSelectedText()
1373 bool textRemoved( false );
1375 if ( EventData::SELECTING == mImpl->mEventData->mState ||
1376 EventData::SELECTION_CHANGED == mImpl->mEventData->mState )
1378 std::string removedString;
1379 mImpl->RetrieveSelection( removedString, true );
1381 if( !removedString.empty() )
1384 mImpl->ChangeState( EventData::EDITING );
1391 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1393 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
1395 if( NULL != mImpl->mEventData )
1397 if( 1u == tapCount )
1399 // This is to avoid unnecessary relayouts when tapping an empty text-field
1400 bool relayoutNeeded( false );
1402 if( mImpl->IsShowingRealText() &&
1403 EventData::EDITING == mImpl->mEventData->mState )
1405 // Show grab handle on second tap
1406 mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
1407 relayoutNeeded = true;
1409 else if( EventData::EDITING != mImpl->mEventData->mState &&
1410 EventData::EDITING_WITH_GRAB_HANDLE != mImpl->mEventData->mState )
1412 // Show cursor on first tap
1413 mImpl->ChangeState( EventData::EDITING );
1414 relayoutNeeded = true;
1416 else if( mImpl->IsShowingRealText() )
1419 relayoutNeeded = true;
1422 // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
1423 if( relayoutNeeded )
1425 Event event( Event::TAP_EVENT );
1426 event.p1.mUint = tapCount;
1427 event.p2.mFloat = x;
1428 event.p3.mFloat = y;
1429 mImpl->mEventData->mEventQueue.push_back( event );
1431 mImpl->RequestRelayout();
1434 else if( 2u == tapCount )
1436 if( mImpl->mEventData->mSelectionEnabled &&
1437 mImpl->IsShowingRealText() )
1439 SelectEvent( x, y, false );
1444 // Reset keyboard as tap event has occurred.
1445 mImpl->ResetImfManager();
1448 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1450 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
1452 if( mImpl->mEventData )
1454 Event event( Event::PAN_EVENT );
1455 event.p1.mInt = state;
1456 event.p2.mFloat = displacement.x;
1457 event.p3.mFloat = displacement.y;
1458 mImpl->mEventData->mEventQueue.push_back( event );
1460 mImpl->RequestRelayout();
1464 void Controller::LongPressEvent( Gesture::State state, float x, float y )
1466 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
1468 if ( mImpl->IsShowingPlaceholderText() || mImpl->mLogicalModel->mText.Count() == 0u )
1470 if ( mImpl->mEventData )
1472 Event event( Event::LONG_PRESS_EVENT );
1473 event.p1.mInt = state;
1474 mImpl->mEventData->mEventQueue.push_back( event );
1475 mImpl->RequestRelayout();
1478 else if( mImpl->mEventData )
1480 SelectEvent( x, y, false );
1484 void Controller::SelectEvent( float x, float y, bool selectAll )
1486 if( mImpl->mEventData )
1488 if ( mImpl->mEventData->mState == EventData::SELECTING )
1490 mImpl->ChangeState( EventData::SELECTION_CHANGED );
1494 mImpl->ChangeState( EventData::SELECTING );
1499 Event event( Event::SELECT_ALL );
1500 mImpl->mEventData->mEventQueue.push_back( event );
1504 Event event( Event::SELECT );
1505 event.p2.mFloat = x;
1506 event.p3.mFloat = y;
1507 mImpl->mEventData->mEventQueue.push_back( event );
1510 mImpl->RequestRelayout();
1514 void Controller::GetTargetSize( Vector2& targetSize )
1516 targetSize = mImpl->mVisualModel->mControlSize;
1519 void Controller::AddDecoration( Actor& actor, bool needsClipping )
1521 mImpl->mControlInterface.AddDecoration( actor, needsClipping );
1524 void Controller::DecorationEvent( HandleType handleType, HandleState state, float x, float y )
1526 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected DecorationEvent" );
1528 if( mImpl->mEventData )
1530 switch( handleType )
1534 Event event( Event::GRAB_HANDLE_EVENT );
1535 event.p1.mUint = state;
1536 event.p2.mFloat = x;
1537 event.p3.mFloat = y;
1539 mImpl->mEventData->mEventQueue.push_back( event );
1542 case LEFT_SELECTION_HANDLE:
1544 Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
1545 event.p1.mUint = state;
1546 event.p2.mFloat = x;
1547 event.p3.mFloat = y;
1549 mImpl->mEventData->mEventQueue.push_back( event );
1552 case RIGHT_SELECTION_HANDLE:
1554 Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
1555 event.p1.mUint = state;
1556 event.p2.mFloat = x;
1557 event.p3.mFloat = y;
1559 mImpl->mEventData->mEventQueue.push_back( event );
1562 case LEFT_SELECTION_HANDLE_MARKER:
1563 case RIGHT_SELECTION_HANDLE_MARKER:
1565 // Markers do not move the handles.
1568 case HANDLE_TYPE_COUNT:
1570 DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
1574 mImpl->RequestRelayout();
1578 void Controller::PasteText( const std::string& stringToPaste )
1580 InsertText( stringToPaste, Text::Controller::COMMIT );
1581 mImpl->ChangeState( EventData::EDITING );
1582 mImpl->RequestRelayout();
1585 void Controller::PasteClipboardItemEvent()
1587 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1588 std::string stringToPaste( notifier.GetContent() );
1589 PasteText( stringToPaste );
1592 void Controller::TextPopupButtonTouched( Dali::Toolkit::TextSelectionPopup::Buttons button )
1594 if( NULL == mImpl->mEventData )
1601 case Toolkit::TextSelectionPopup::CUT:
1603 mImpl->SendSelectionToClipboard( true ); // Synchronous call to modify text
1604 mImpl->mOperationsPending = ALL_OPERATIONS;
1605 if( 0u != mImpl->mLogicalModel->mText.Count() ||
1606 !mImpl->IsPlaceholderAvailable() )
1608 mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
1612 ShowPlaceholderText();
1613 mImpl->mEventData->mUpdateCursorPosition = true;
1615 mImpl->RequestRelayout();
1616 mImpl->mControlInterface.TextChanged();
1619 case Toolkit::TextSelectionPopup::COPY:
1621 mImpl->SendSelectionToClipboard( false ); // Text not modified
1622 mImpl->RequestRelayout(); // Handles, Selection Highlight, Popup
1625 case Toolkit::TextSelectionPopup::PASTE:
1627 std::string stringToPaste("");
1628 mImpl->GetTextFromClipboard( 0, stringToPaste ); // Paste latest item from system clipboard
1629 PasteText( stringToPaste );
1632 case Toolkit::TextSelectionPopup::SELECT:
1634 const Vector2& currentCursorPosition = mImpl->mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1636 if( mImpl->mEventData->mSelectionEnabled )
1638 // Creates a SELECT event.
1639 SelectEvent( currentCursorPosition.x, currentCursorPosition.y, false );
1643 case Toolkit::TextSelectionPopup::SELECT_ALL:
1645 // Creates a SELECT_ALL event
1646 SelectEvent( 0.f, 0.f, true );
1649 case Toolkit::TextSelectionPopup::CLIPBOARD:
1651 mImpl->ShowClipboard();
1654 case Toolkit::TextSelectionPopup::NONE:
1662 ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
1664 bool update( false );
1665 bool requestRelayout = false;
1668 unsigned int cursorPosition( 0 );
1670 switch ( imfEvent.eventName )
1672 case ImfManager::COMMIT:
1674 InsertText( imfEvent.predictiveString, Text::Controller::COMMIT );
1675 requestRelayout = true;
1678 case ImfManager::PREEDIT:
1680 InsertText( imfEvent.predictiveString, Text::Controller::PRE_EDIT );
1682 requestRelayout = true;
1685 case ImfManager::DELETESURROUNDING:
1687 update = RemoveText( imfEvent.cursorOffset, imfEvent.numberOfChars );
1691 if( 0u != mImpl->mLogicalModel->mText.Count() ||
1692 !mImpl->IsPlaceholderAvailable() )
1694 mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
1698 ShowPlaceholderText();
1699 mImpl->mEventData->mUpdateCursorPosition = true;
1702 requestRelayout = true;
1705 case ImfManager::GETSURROUNDING:
1708 cursorPosition = GetLogicalCursorPosition();
1710 imfManager.SetSurroundingText( text );
1711 imfManager.SetCursorPosition( cursorPosition );
1714 case ImfManager::VOID:
1721 if( ImfManager::GETSURROUNDING != imfEvent.eventName )
1724 cursorPosition = GetLogicalCursorPosition();
1727 if( requestRelayout )
1729 mImpl->mOperationsPending = ALL_OPERATIONS;
1730 mImpl->RequestRelayout();
1732 // Do this last since it provides callbacks into application code
1733 mImpl->mControlInterface.TextChanged();
1736 ImfManager::ImfCallbackData callbackData( update, cursorPosition, text, false );
1738 return callbackData;
1741 Controller::~Controller()
1746 bool Controller::BackspaceKeyEvent()
1748 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE\n", this );
1750 // IMF manager is no longer handling key-events
1751 mImpl->ClearPreEditFlag();
1753 bool removed( false );
1755 if ( EventData::SELECTING == mImpl->mEventData->mState ||
1756 EventData::SELECTION_CHANGED == mImpl->mEventData->mState )
1758 removed = RemoveSelectedText();
1760 else if( mImpl->mEventData->mPrimaryCursorPosition > 0 )
1762 // Remove the character before the current cursor position
1763 removed = RemoveText( -1, 1 );
1768 if( 0u != mImpl->mLogicalModel->mText.Count() ||
1769 !mImpl->IsPlaceholderAvailable() )
1771 mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
1775 ShowPlaceholderText();
1776 mImpl->mEventData->mUpdateCursorPosition = true;
1783 void Controller::ShowPlaceholderText()
1785 if( mImpl->IsPlaceholderAvailable() )
1787 DALI_ASSERT_DEBUG( mImpl->mEventData && "No placeholder text available" );
1789 mImpl->mEventData->mIsShowingPlaceholderText = true;
1791 // Disable handles when showing place-holder text
1792 mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1793 mImpl->mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1794 mImpl->mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1796 const char* text( NULL );
1799 // TODO - Switch placeholder text styles when changing state
1800 if( EventData::INACTIVE != mImpl->mEventData->mState &&
1801 0u != mImpl->mEventData->mPlaceholderTextActive.c_str() )
1803 text = mImpl->mEventData->mPlaceholderTextActive.c_str();
1804 size = mImpl->mEventData->mPlaceholderTextActive.size();
1808 text = mImpl->mEventData->mPlaceholderTextInactive.c_str();
1809 size = mImpl->mEventData->mPlaceholderTextInactive.size();
1812 // Reset model for showing placeholder.
1813 mImpl->mLogicalModel->mText.Clear();
1815 mImpl->mVisualModel->SetTextColor( mImpl->mEventData->mPlaceholderTextColor );
1817 // Convert text into UTF-32
1818 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1819 utf32Characters.Resize( size );
1821 // This is a bit horrible but std::string returns a (signed) char*
1822 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text );
1824 // Transform a text array encoded in utf8 into an array encoded in utf32.
1825 // It returns the actual number of characters.
1826 Length characterCount = Utf8ToUtf32( utf8, size, utf32Characters.Begin() );
1827 utf32Characters.Resize( characterCount );
1829 // Reset the cursor position
1830 mImpl->mEventData->mPrimaryCursorPosition = 0;
1832 // The natural size needs to be re-calculated.
1833 mImpl->mRecalculateNaturalSize = true;
1835 // Apply modifications to the model
1836 mImpl->mOperationsPending = ALL_OPERATIONS;
1838 // Update the rest of the model during size negotiation
1839 mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
1843 void Controller::ClearModelData()
1845 // n.b. This does not Clear the mText from mLogicalModel
1846 mImpl->mLogicalModel->mScriptRuns.Clear();
1847 mImpl->mLogicalModel->mFontRuns.Clear();
1848 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1849 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1850 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1851 mImpl->mLogicalModel->mCharacterDirections.Clear();
1852 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1853 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1854 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1855 mImpl->mVisualModel->mGlyphs.Clear();
1856 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1857 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1858 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1859 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1860 mImpl->mVisualModel->mGlyphPositions.Clear();
1861 mImpl->mVisualModel->mLines.Clear();
1862 mImpl->mVisualModel->ClearCaches();
1865 void Controller::ClearFontData()
1867 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1868 mImpl->mLogicalModel->mFontRuns.Clear();
1869 mImpl->mVisualModel->mGlyphs.Clear();
1870 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1871 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1872 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1873 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1874 mImpl->mVisualModel->mGlyphPositions.Clear();
1875 mImpl->mVisualModel->mLines.Clear();
1876 mImpl->mVisualModel->ClearCaches();
1879 Controller::Controller( ControlInterface& controlInterface )
1882 mImpl = new Controller::Impl( controlInterface );
1887 } // namespace Toolkit