2 * Copyright (c) 2018 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller.h>
24 #include <dali/public-api/adaptor-framework/key.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/devel-api/adaptor-framework/clipboard-event-notifier.h>
27 #include <dali/devel-api/text-abstraction/font-client.h>
28 #include <dali/devel-api/adaptor-framework/key-devel.h>
31 #include <dali-toolkit/public-api/controls/text-controls/placeholder-properties.h>
32 #include <dali-toolkit/internal/text/bidirectional-support.h>
33 #include <dali-toolkit/internal/text/character-set-conversion.h>
34 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
35 #include <dali-toolkit/internal/text/markup-processor.h>
36 #include <dali-toolkit/internal/text/multi-language-support.h>
37 #include <dali-toolkit/internal/text/text-controller-impl.h>
38 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
39 #include <dali-toolkit/internal/text/text-font-style.h>
44 #if defined(DEBUG_ENABLED)
45 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
48 const float MAX_FLOAT = std::numeric_limits<float>::max();
50 const std::string EMPTY_STRING("");
52 const std::string KEY_C_NAME = "c";
53 const std::string KEY_V_NAME = "v";
54 const std::string KEY_X_NAME = "x";
56 const char * const PLACEHOLDER_TEXT = "text";
57 const char * const PLACEHOLDER_TEXT_FOCUSED = "textFocused";
58 const char * const PLACEHOLDER_COLOR = "color";
59 const char * const PLACEHOLDER_FONT_FAMILY = "fontFamily";
60 const char * const PLACEHOLDER_FONT_STYLE = "fontStyle";
61 const char * const PLACEHOLDER_POINT_SIZE = "pointSize";
62 const char * const PLACEHOLDER_PIXEL_SIZE = "pixelSize";
63 const char * const PLACEHOLDER_ELLIPSIS = "ellipsis";
65 float ConvertToEven( float value )
67 int intValue(static_cast<int>( value ));
68 return static_cast<float>(intValue % 2 == 0) ? intValue : (intValue + 1);
83 * @brief Adds a new font description run for the selected text.
85 * The new font parameters are added after the call to this method.
87 * @param[in] eventData The event data pointer.
88 * @param[in] logicalModel The logical model where to add the new font description run.
89 * @param[out] startOfSelectedText Index to the first selected character.
90 * @param[out] lengthOfSelectedText Number of selected characters.
92 FontDescriptionRun& UpdateSelectionFontStyleRun( EventData* eventData,
93 LogicalModelPtr logicalModel,
94 CharacterIndex& startOfSelectedText,
95 Length& lengthOfSelectedText )
97 const bool handlesCrossed = eventData->mLeftSelectionPosition > eventData->mRightSelectionPosition;
99 // Get start and end position of selection
100 startOfSelectedText = handlesCrossed ? eventData->mRightSelectionPosition : eventData->mLeftSelectionPosition;
101 lengthOfSelectedText = ( handlesCrossed ? eventData->mLeftSelectionPosition : eventData->mRightSelectionPosition ) - startOfSelectedText;
104 const VectorBase::SizeType numberOfRuns = logicalModel->mFontDescriptionRuns.Count();
105 logicalModel->mFontDescriptionRuns.Resize( numberOfRuns + 1u );
107 FontDescriptionRun& fontDescriptionRun = *( logicalModel->mFontDescriptionRuns.Begin() + numberOfRuns );
109 fontDescriptionRun.characterRun.characterIndex = startOfSelectedText;
110 fontDescriptionRun.characterRun.numberOfCharacters = lengthOfSelectedText;
112 // Recalculate the selection highlight as the metrics may have changed.
113 eventData->mUpdateLeftSelectionPosition = true;
114 eventData->mUpdateRightSelectionPosition = true;
115 eventData->mUpdateHighlightBox = true;
117 return fontDescriptionRun;
120 // public : Constructor.
122 ControllerPtr Controller::New()
124 return ControllerPtr( new Controller() );
127 ControllerPtr Controller::New( ControlInterface* controlInterface )
129 return ControllerPtr( new Controller( controlInterface ) );
132 ControllerPtr Controller::New( ControlInterface* controlInterface,
133 EditableControlInterface* editableControlInterface )
135 return ControllerPtr( new Controller( controlInterface,
136 editableControlInterface ) );
139 // public : Configure the text controller.
141 void Controller::EnableTextInput( DecoratorPtr decorator, InputMethodContext& inputMethodContext )
145 delete mImpl->mEventData;
146 mImpl->mEventData = NULL;
148 // Nothing else to do.
152 if( NULL == mImpl->mEventData )
154 mImpl->mEventData = new EventData( decorator, inputMethodContext );
158 void Controller::SetGlyphType( TextAbstraction::GlyphType glyphType )
160 // Metrics for bitmap & vector based glyphs are different
161 mImpl->mMetrics->SetGlyphType( glyphType );
163 // Clear the font-specific data
166 mImpl->RequestRelayout();
169 void Controller::SetMarkupProcessorEnabled( bool enable )
171 if( enable != mImpl->mMarkupProcessorEnabled )
173 //If Text was already set, call the SetText again for enabling or disabling markup
174 mImpl->mMarkupProcessorEnabled = enable;
181 bool Controller::IsMarkupProcessorEnabled() const
183 return mImpl->mMarkupProcessorEnabled;
186 void Controller::SetAutoScrollEnabled( bool enable )
188 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled[%s] SingleBox[%s]-> [%p]\n", (enable)?"true":"false", ( mImpl->mLayoutEngine.GetLayout() == Layout::Engine::SINGLE_LINE_BOX)?"true":"false", this );
190 if( mImpl->mLayoutEngine.GetLayout() == Layout::Engine::SINGLE_LINE_BOX )
194 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled for SINGLE_LINE_BOX\n" );
195 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
205 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled Disabling autoscroll\n");
206 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
213 mImpl->mIsAutoScrollEnabled = enable;
214 mImpl->RequestRelayout();
218 DALI_LOG_WARNING( "Attempted AutoScrolling on a non SINGLE_LINE_BOX, request ignored\n" );
219 mImpl->mIsAutoScrollEnabled = false;
223 bool Controller::IsAutoScrollEnabled() const
225 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::IsAutoScrollEnabled[%s]\n", mImpl->mIsAutoScrollEnabled?"true":"false" );
227 return mImpl->mIsAutoScrollEnabled;
230 CharacterDirection Controller::GetAutoScrollDirection() const
232 return mImpl->mIsTextDirectionRTL;
235 float Controller::GetAutoScrollLineAlignment() const
239 if( mImpl->mModel->mVisualModel &&
240 ( 0u != mImpl->mModel->mVisualModel->mLines.Count() ) )
242 offset = ( *mImpl->mModel->mVisualModel->mLines.Begin() ).alignmentOffset;
248 void Controller::SetHorizontalScrollEnabled( bool enable )
250 if( ( NULL != mImpl->mEventData ) &&
251 mImpl->mEventData->mDecorator )
253 mImpl->mEventData->mDecorator->SetHorizontalScrollEnabled( enable );
256 bool Controller::IsHorizontalScrollEnabled() const
258 if( ( NULL != mImpl->mEventData ) &&
259 mImpl->mEventData->mDecorator )
261 return mImpl->mEventData->mDecorator->IsHorizontalScrollEnabled();
267 void Controller::SetVerticalScrollEnabled( bool enable )
269 if( ( NULL != mImpl->mEventData ) &&
270 mImpl->mEventData->mDecorator )
272 if( mImpl->mEventData->mDecorator )
274 mImpl->mEventData->mDecorator->SetVerticalScrollEnabled( enable );
279 bool Controller::IsVerticalScrollEnabled() const
281 if( ( NULL != mImpl->mEventData ) &&
282 mImpl->mEventData->mDecorator )
284 return mImpl->mEventData->mDecorator->IsVerticalScrollEnabled();
290 void Controller::SetSmoothHandlePanEnabled( bool enable )
292 if( ( NULL != mImpl->mEventData ) &&
293 mImpl->mEventData->mDecorator )
295 mImpl->mEventData->mDecorator->SetSmoothHandlePanEnabled( enable );
299 bool Controller::IsSmoothHandlePanEnabled() const
301 if( ( NULL != mImpl->mEventData ) &&
302 mImpl->mEventData->mDecorator )
304 return mImpl->mEventData->mDecorator->IsSmoothHandlePanEnabled();
310 void Controller::SetMaximumNumberOfCharacters( Length maxCharacters )
312 mImpl->mMaximumNumberOfCharacters = maxCharacters;
315 int Controller::GetMaximumNumberOfCharacters()
317 return mImpl->mMaximumNumberOfCharacters;
320 void Controller::SetEnableCursorBlink( bool enable )
322 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "TextInput disabled" );
324 if( NULL != mImpl->mEventData )
326 mImpl->mEventData->mCursorBlinkEnabled = enable;
329 mImpl->mEventData->mDecorator )
331 mImpl->mEventData->mDecorator->StopCursorBlink();
336 bool Controller::GetEnableCursorBlink() const
338 if( NULL != mImpl->mEventData )
340 return mImpl->mEventData->mCursorBlinkEnabled;
346 void Controller::SetMultiLineEnabled( bool enable )
348 const Layout::Engine::Type layout = enable ? Layout::Engine::MULTI_LINE_BOX : Layout::Engine::SINGLE_LINE_BOX;
350 if( layout != mImpl->mLayoutEngine.GetLayout() )
352 // Set the layout type.
353 mImpl->mLayoutEngine.SetLayout( layout );
355 // Set the flags to redo the layout operations
356 const OperationsMask layoutOperations = static_cast<OperationsMask>( LAYOUT |
361 mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true;
362 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | layoutOperations );
364 mImpl->RequestRelayout();
368 bool Controller::IsMultiLineEnabled() const
370 return Layout::Engine::MULTI_LINE_BOX == mImpl->mLayoutEngine.GetLayout();
373 void Controller::SetHorizontalAlignment( Text::HorizontalAlignment::Type alignment )
375 if( alignment != mImpl->mModel->mHorizontalAlignment )
377 // Set the alignment.
378 mImpl->mModel->mHorizontalAlignment = alignment;
380 // Set the flag to redo the alignment operation.
381 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
383 mImpl->RequestRelayout();
387 Text::HorizontalAlignment::Type Controller::GetHorizontalAlignment() const
389 return mImpl->mModel->mHorizontalAlignment;
392 void Controller::SetVerticalAlignment( VerticalAlignment::Type alignment )
394 if( alignment != mImpl->mModel->mVerticalAlignment )
396 // Set the alignment.
397 mImpl->mModel->mVerticalAlignment = alignment;
399 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
401 mImpl->RequestRelayout();
405 VerticalAlignment::Type Controller::GetVerticalAlignment() const
407 return mImpl->mModel->mVerticalAlignment;
410 bool Controller::IsIgnoreSpacesAfterText() const
412 return mImpl->mModel->mIgnoreSpacesAfterText;
415 void Controller::SetIgnoreSpacesAfterText( bool ignore )
417 mImpl->mModel->mIgnoreSpacesAfterText = ignore;
420 bool Controller::IsMatchSystemLanguageDirection() const
422 return mImpl->mModel->mMatchSystemLanguageDirection;
425 void Controller::SetMatchSystemLanguageDirection( bool match )
427 mImpl->mModel->mMatchSystemLanguageDirection = match;
430 void Controller::SetLayoutDirection( Dali::LayoutDirection::Type layoutDirection )
432 mImpl->mLayoutDirection = layoutDirection;
436 void Controller::SetLineWrapMode( Text::LineWrap::Mode lineWrapMode )
438 if( lineWrapMode != mImpl->mModel->mLineWrapMode )
440 // Set the text wrap mode.
441 mImpl->mModel->mLineWrapMode = lineWrapMode;
444 // Update Text layout for applying wrap mode
445 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
450 mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
451 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
452 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count();
455 mImpl->RequestRelayout();
459 Text::LineWrap::Mode Controller::GetLineWrapMode() const
461 return mImpl->mModel->mLineWrapMode;
464 void Controller::SetTextElideEnabled( bool enabled )
466 mImpl->mModel->mElideEnabled = enabled;
469 bool Controller::IsTextElideEnabled() const
471 return mImpl->mModel->mElideEnabled;
474 void Controller::SetPlaceholderTextElideEnabled( bool enabled )
476 mImpl->mEventData->mIsPlaceholderElideEnabled = enabled;
477 mImpl->mEventData->mPlaceholderEllipsisFlag = true;
479 // Update placeholder if there is no text
480 if( mImpl->IsShowingPlaceholderText() ||
481 ( 0u == mImpl->mModel->mLogicalModel->mText.Count() ) )
483 ShowPlaceholderText();
487 bool Controller::IsPlaceholderTextElideEnabled() const
489 return mImpl->mEventData->mIsPlaceholderElideEnabled;
492 void Controller::SetSelectionEnabled( bool enabled )
494 mImpl->mEventData->mSelectionEnabled = enabled;
497 bool Controller::IsSelectionEnabled() const
499 return mImpl->mEventData->mSelectionEnabled;
502 void Controller::SetShiftSelectionEnabled( bool enabled )
504 mImpl->mEventData->mShiftSelectionFlag = enabled;
507 bool Controller::IsShiftSelectionEnabled() const
509 return mImpl->mEventData->mShiftSelectionFlag;
512 void Controller::SetGrabHandleEnabled( bool enabled )
514 mImpl->mEventData->mGrabHandleEnabled = enabled;
517 bool Controller::IsGrabHandleEnabled() const
519 return mImpl->mEventData->mGrabHandleEnabled;
524 void Controller::SetText( const std::string& text )
526 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText\n" );
528 // Reset keyboard as text changed
529 mImpl->ResetInputMethodContext();
531 // Remove the previously set text and style.
537 CharacterIndex lastCursorIndex = 0u;
539 if( NULL != mImpl->mEventData )
541 // If popup shown then hide it by switching to Editing state
542 if( ( EventData::SELECTING == mImpl->mEventData->mState ) ||
543 ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) ||
544 ( EventData::EDITING_WITH_GRAB_HANDLE == mImpl->mEventData->mState ) ||
545 ( EventData::EDITING_WITH_PASTE_POPUP == mImpl->mEventData->mState ) )
547 mImpl->ChangeState( EventData::EDITING );
553 mImpl->mModel->mVisualModel->SetTextColor( mImpl->mTextColor );
555 MarkupProcessData markupProcessData( mImpl->mModel->mLogicalModel->mColorRuns,
556 mImpl->mModel->mLogicalModel->mFontDescriptionRuns );
558 Length textSize = 0u;
559 const uint8_t* utf8 = NULL;
560 if( mImpl->mMarkupProcessorEnabled )
562 ProcessMarkupString( text, markupProcessData );
563 textSize = markupProcessData.markupProcessedText.size();
565 // This is a bit horrible but std::string returns a (signed) char*
566 utf8 = reinterpret_cast<const uint8_t*>( markupProcessData.markupProcessedText.c_str() );
570 textSize = text.size();
572 // This is a bit horrible but std::string returns a (signed) char*
573 utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
576 const Length maxTextLength = 32768u; // 1024 * 32;
577 if( textSize > maxTextLength )
579 DALI_LOG_WARNING( "The text size is too large(%d), limit the length to 32,768u\n", textSize );
580 textSize = maxTextLength;
583 // Convert text into UTF-32
584 Vector<Character>& utf32Characters = mImpl->mModel->mLogicalModel->mText;
585 utf32Characters.Resize( textSize );
587 // Transform a text array encoded in utf8 into an array encoded in utf32.
588 // It returns the actual number of characters.
589 Length characterCount = Utf8ToUtf32( utf8, textSize, utf32Characters.Begin() );
590 utf32Characters.Resize( characterCount );
592 DALI_ASSERT_DEBUG( textSize >= characterCount && "Invalid UTF32 conversion length" );
593 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText %p UTF8 size %d, UTF32 size %d\n", this, textSize, mImpl->mModel->mLogicalModel->mText.Count() );
595 // The characters to be added.
596 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count();
598 // To reset the cursor position
599 lastCursorIndex = characterCount;
601 // Update the rest of the model during size negotiation
602 mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
604 // The natural size needs to be re-calculated.
605 mImpl->mRecalculateNaturalSize = true;
607 // The text direction needs to be updated.
608 mImpl->mUpdateTextDirection = true;
610 // Apply modifications to the model
611 mImpl->mOperationsPending = ALL_OPERATIONS;
615 ShowPlaceholderText();
618 // Resets the cursor position.
619 ResetCursorPosition( lastCursorIndex );
621 // Scrolls the text to make the cursor visible.
622 ResetScrollPosition();
624 mImpl->RequestRelayout();
626 if( NULL != mImpl->mEventData )
628 // Cancel previously queued events
629 mImpl->mEventData->mEventQueue.clear();
632 // Do this last since it provides callbacks into application code.
633 if( NULL != mImpl->mEditableControlInterface )
635 mImpl->mEditableControlInterface->TextChanged();
639 void Controller::GetText( std::string& text ) const
641 if( !mImpl->IsShowingPlaceholderText() )
643 // Retrieves the text string.
644 mImpl->GetText( 0u, text );
648 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::GetText %p empty (but showing placeholder)\n", this );
652 void Controller::SetPlaceholderText( PlaceholderType type, const std::string& text )
654 if( NULL != mImpl->mEventData )
656 if( PLACEHOLDER_TYPE_INACTIVE == type )
658 mImpl->mEventData->mPlaceholderTextInactive = text;
662 mImpl->mEventData->mPlaceholderTextActive = text;
665 // Update placeholder if there is no text
666 if( mImpl->IsShowingPlaceholderText() ||
667 ( 0u == mImpl->mModel->mLogicalModel->mText.Count() ) )
669 ShowPlaceholderText();
674 void Controller::GetPlaceholderText( PlaceholderType type, std::string& text ) const
676 if( NULL != mImpl->mEventData )
678 if( PLACEHOLDER_TYPE_INACTIVE == type )
680 text = mImpl->mEventData->mPlaceholderTextInactive;
684 text = mImpl->mEventData->mPlaceholderTextActive;
689 void Controller::UpdateAfterFontChange( const std::string& newDefaultFont )
691 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::UpdateAfterFontChange\n");
693 if( !mImpl->mFontDefaults->familyDefined ) // If user defined font then should not update when system font changes
695 DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::UpdateAfterFontChange newDefaultFont(%s)\n", newDefaultFont.c_str() );
696 mImpl->mFontDefaults->mFontDescription.family = newDefaultFont;
700 mImpl->RequestRelayout();
704 // public : Default style & Input style
706 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
708 if( NULL == mImpl->mFontDefaults )
710 mImpl->mFontDefaults = new FontDefaults();
713 mImpl->mFontDefaults->mFontDescription.family = defaultFontFamily;
714 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetDefaultFontFamily %s\n", defaultFontFamily.c_str());
715 mImpl->mFontDefaults->familyDefined = !defaultFontFamily.empty();
717 // Clear the font-specific data
720 mImpl->RequestRelayout();
723 const std::string& Controller::GetDefaultFontFamily() const
725 if( NULL != mImpl->mFontDefaults )
727 return mImpl->mFontDefaults->mFontDescription.family;
733 void Controller::SetPlaceholderFontFamily( const std::string& placeholderTextFontFamily )
735 if( NULL != mImpl->mEventData )
737 if( NULL == mImpl->mEventData->mPlaceholderFont )
739 mImpl->mEventData->mPlaceholderFont = new FontDefaults();
742 mImpl->mEventData->mPlaceholderFont->mFontDescription.family = placeholderTextFontFamily;
743 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetPlaceholderFontFamily %s\n", placeholderTextFontFamily.c_str());
744 mImpl->mEventData->mPlaceholderFont->familyDefined = !placeholderTextFontFamily.empty();
746 mImpl->RequestRelayout();
750 const std::string& Controller::GetPlaceholderFontFamily() const
752 if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
754 return mImpl->mEventData->mPlaceholderFont->mFontDescription.family;
760 void Controller::SetDefaultFontWeight( FontWeight weight )
762 if( NULL == mImpl->mFontDefaults )
764 mImpl->mFontDefaults = new FontDefaults();
767 mImpl->mFontDefaults->mFontDescription.weight = weight;
768 mImpl->mFontDefaults->weightDefined = true;
770 // Clear the font-specific data
773 mImpl->RequestRelayout();
776 bool Controller::IsDefaultFontWeightDefined() const
778 if( NULL != mImpl->mFontDefaults )
780 return mImpl->mFontDefaults->weightDefined;
786 FontWeight Controller::GetDefaultFontWeight() const
788 if( NULL != mImpl->mFontDefaults )
790 return mImpl->mFontDefaults->mFontDescription.weight;
793 return TextAbstraction::FontWeight::NORMAL;
796 void Controller::SetPlaceholderTextFontWeight( FontWeight weight )
798 if( NULL != mImpl->mEventData )
800 if( NULL == mImpl->mEventData->mPlaceholderFont )
802 mImpl->mEventData->mPlaceholderFont = new FontDefaults();
805 mImpl->mEventData->mPlaceholderFont->mFontDescription.weight = weight;
806 mImpl->mEventData->mPlaceholderFont->weightDefined = true;
808 mImpl->RequestRelayout();
812 bool Controller::IsPlaceholderTextFontWeightDefined() const
814 if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
816 return mImpl->mEventData->mPlaceholderFont->weightDefined;
821 FontWeight Controller::GetPlaceholderTextFontWeight() const
823 if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
825 return mImpl->mEventData->mPlaceholderFont->mFontDescription.weight;
828 return TextAbstraction::FontWeight::NORMAL;
831 void Controller::SetDefaultFontWidth( FontWidth width )
833 if( NULL == mImpl->mFontDefaults )
835 mImpl->mFontDefaults = new FontDefaults();
838 mImpl->mFontDefaults->mFontDescription.width = width;
839 mImpl->mFontDefaults->widthDefined = true;
841 // Clear the font-specific data
844 mImpl->RequestRelayout();
847 bool Controller::IsDefaultFontWidthDefined() const
849 if( NULL != mImpl->mFontDefaults )
851 return mImpl->mFontDefaults->widthDefined;
857 FontWidth Controller::GetDefaultFontWidth() const
859 if( NULL != mImpl->mFontDefaults )
861 return mImpl->mFontDefaults->mFontDescription.width;
864 return TextAbstraction::FontWidth::NORMAL;
867 void Controller::SetPlaceholderTextFontWidth( FontWidth width )
869 if( NULL != mImpl->mEventData )
871 if( NULL == mImpl->mEventData->mPlaceholderFont )
873 mImpl->mEventData->mPlaceholderFont = new FontDefaults();
876 mImpl->mEventData->mPlaceholderFont->mFontDescription.width = width;
877 mImpl->mEventData->mPlaceholderFont->widthDefined = true;
879 mImpl->RequestRelayout();
883 bool Controller::IsPlaceholderTextFontWidthDefined() const
885 if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
887 return mImpl->mEventData->mPlaceholderFont->widthDefined;
892 FontWidth Controller::GetPlaceholderTextFontWidth() const
894 if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
896 return mImpl->mEventData->mPlaceholderFont->mFontDescription.width;
899 return TextAbstraction::FontWidth::NORMAL;
902 void Controller::SetDefaultFontSlant( FontSlant slant )
904 if( NULL == mImpl->mFontDefaults )
906 mImpl->mFontDefaults = new FontDefaults();
909 mImpl->mFontDefaults->mFontDescription.slant = slant;
910 mImpl->mFontDefaults->slantDefined = true;
912 // Clear the font-specific data
915 mImpl->RequestRelayout();
918 bool Controller::IsDefaultFontSlantDefined() const
920 if( NULL != mImpl->mFontDefaults )
922 return mImpl->mFontDefaults->slantDefined;
927 FontSlant Controller::GetDefaultFontSlant() const
929 if( NULL != mImpl->mFontDefaults )
931 return mImpl->mFontDefaults->mFontDescription.slant;
934 return TextAbstraction::FontSlant::NORMAL;
937 void Controller::SetPlaceholderTextFontSlant( FontSlant slant )
939 if( NULL != mImpl->mEventData )
941 if( NULL == mImpl->mEventData->mPlaceholderFont )
943 mImpl->mEventData->mPlaceholderFont = new FontDefaults();
946 mImpl->mEventData->mPlaceholderFont->mFontDescription.slant = slant;
947 mImpl->mEventData->mPlaceholderFont->slantDefined = true;
949 mImpl->RequestRelayout();
953 bool Controller::IsPlaceholderTextFontSlantDefined() const
955 if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
957 return mImpl->mEventData->mPlaceholderFont->slantDefined;
962 FontSlant Controller::GetPlaceholderTextFontSlant() const
964 if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
966 return mImpl->mEventData->mPlaceholderFont->mFontDescription.slant;
969 return TextAbstraction::FontSlant::NORMAL;
972 void Controller::SetDefaultFontSize( float fontSize, FontSizeType type )
974 if( NULL == mImpl->mFontDefaults )
976 mImpl->mFontDefaults = new FontDefaults();
983 mImpl->mFontDefaults->mDefaultPointSize = fontSize;
984 mImpl->mFontDefaults->sizeDefined = true;
989 // Point size = Pixel size * 72.f / DPI
990 unsigned int horizontalDpi = 0u;
991 unsigned int verticalDpi = 0u;
992 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
993 fontClient.GetDpi( horizontalDpi, verticalDpi );
995 mImpl->mFontDefaults->mDefaultPointSize = ( fontSize * 72.f ) / static_cast< float >( horizontalDpi );
996 mImpl->mFontDefaults->sizeDefined = true;
1001 // Clear the font-specific data
1004 mImpl->RequestRelayout();
1007 float Controller::GetDefaultFontSize( FontSizeType type ) const
1010 if( NULL != mImpl->mFontDefaults )
1016 value = mImpl->mFontDefaults->mDefaultPointSize;
1021 // Pixel size = Point size * DPI / 72.f
1022 unsigned int horizontalDpi = 0u;
1023 unsigned int verticalDpi = 0u;
1024 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1025 fontClient.GetDpi( horizontalDpi, verticalDpi );
1027 value = mImpl->mFontDefaults->mDefaultPointSize * static_cast< float >( horizontalDpi ) / 72.f;
1037 void Controller::SetPlaceholderTextFontSize( float fontSize, FontSizeType type )
1039 if( NULL != mImpl->mEventData )
1041 if( NULL == mImpl->mEventData->mPlaceholderFont )
1043 mImpl->mEventData->mPlaceholderFont = new FontDefaults();
1050 mImpl->mEventData->mPlaceholderFont->mDefaultPointSize = fontSize;
1051 mImpl->mEventData->mPlaceholderFont->sizeDefined = true;
1052 mImpl->mEventData->mIsPlaceholderPixelSize = false; // Font size flag
1057 // Point size = Pixel size * 72.f / DPI
1058 unsigned int horizontalDpi = 0u;
1059 unsigned int verticalDpi = 0u;
1060 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1061 fontClient.GetDpi( horizontalDpi, verticalDpi );
1063 mImpl->mEventData->mPlaceholderFont->mDefaultPointSize = ( fontSize * 72.f ) / static_cast< float >( horizontalDpi );
1064 mImpl->mEventData->mPlaceholderFont->sizeDefined = true;
1065 mImpl->mEventData->mIsPlaceholderPixelSize = true; // Font size flag
1070 mImpl->RequestRelayout();
1074 float Controller::GetPlaceholderTextFontSize( FontSizeType type ) const
1077 if( NULL != mImpl->mEventData )
1083 if( NULL != mImpl->mEventData->mPlaceholderFont )
1085 value = mImpl->mEventData->mPlaceholderFont->mDefaultPointSize;
1089 // If the placeholder text font size is not set, then return the default font size.
1090 value = GetDefaultFontSize( POINT_SIZE );
1096 if( NULL != mImpl->mEventData->mPlaceholderFont )
1098 // Pixel size = Point size * DPI / 72.f
1099 unsigned int horizontalDpi = 0u;
1100 unsigned int verticalDpi = 0u;
1101 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1102 fontClient.GetDpi( horizontalDpi, verticalDpi );
1104 value = mImpl->mEventData->mPlaceholderFont->mDefaultPointSize * static_cast< float >( horizontalDpi ) / 72.f;
1108 // If the placeholder text font size is not set, then return the default font size.
1109 value = GetDefaultFontSize( PIXEL_SIZE );
1120 void Controller::SetDefaultColor( const Vector4& color )
1122 mImpl->mTextColor = color;
1124 if( !mImpl->IsShowingPlaceholderText() )
1126 mImpl->mModel->mVisualModel->SetTextColor( color );
1128 mImpl->RequestRelayout();
1132 const Vector4& Controller::GetDefaultColor() const
1134 return mImpl->mTextColor;
1137 void Controller::SetPlaceholderTextColor( const Vector4& textColor )
1139 if( NULL != mImpl->mEventData )
1141 mImpl->mEventData->mPlaceholderTextColor = textColor;
1144 if( mImpl->IsShowingPlaceholderText() )
1146 mImpl->mModel->mVisualModel->SetTextColor( textColor );
1147 mImpl->RequestRelayout();
1151 const Vector4& Controller::GetPlaceholderTextColor() const
1153 if( NULL != mImpl->mEventData )
1155 return mImpl->mEventData->mPlaceholderTextColor;
1158 return Color::BLACK;
1161 void Controller::SetShadowOffset( const Vector2& shadowOffset )
1163 mImpl->mModel->mVisualModel->SetShadowOffset( shadowOffset );
1165 mImpl->RequestRelayout();
1168 const Vector2& Controller::GetShadowOffset() const
1170 return mImpl->mModel->mVisualModel->GetShadowOffset();
1173 void Controller::SetShadowColor( const Vector4& shadowColor )
1175 mImpl->mModel->mVisualModel->SetShadowColor( shadowColor );
1177 mImpl->RequestRelayout();
1180 const Vector4& Controller::GetShadowColor() const
1182 return mImpl->mModel->mVisualModel->GetShadowColor();
1185 void Controller::SetShadowBlurRadius( const float& shadowBlurRadius )
1187 if ( fabsf( GetShadowBlurRadius() - shadowBlurRadius ) > Math::MACHINE_EPSILON_1 )
1189 mImpl->mModel->mVisualModel->SetShadowBlurRadius( shadowBlurRadius );
1191 mImpl->RequestRelayout();
1195 const float& Controller::GetShadowBlurRadius() const
1197 return mImpl->mModel->mVisualModel->GetShadowBlurRadius();
1200 void Controller::SetUnderlineColor( const Vector4& color )
1202 mImpl->mModel->mVisualModel->SetUnderlineColor( color );
1204 mImpl->RequestRelayout();
1207 const Vector4& Controller::GetUnderlineColor() const
1209 return mImpl->mModel->mVisualModel->GetUnderlineColor();
1212 void Controller::SetUnderlineEnabled( bool enabled )
1214 mImpl->mModel->mVisualModel->SetUnderlineEnabled( enabled );
1216 mImpl->RequestRelayout();
1219 bool Controller::IsUnderlineEnabled() const
1221 return mImpl->mModel->mVisualModel->IsUnderlineEnabled();
1224 void Controller::SetUnderlineHeight( float height )
1226 mImpl->mModel->mVisualModel->SetUnderlineHeight( height );
1228 mImpl->RequestRelayout();
1231 float Controller::GetUnderlineHeight() const
1233 return mImpl->mModel->mVisualModel->GetUnderlineHeight();
1236 void Controller::SetOutlineColor( const Vector4& color )
1238 mImpl->mModel->mVisualModel->SetOutlineColor( color );
1240 mImpl->RequestRelayout();
1243 const Vector4& Controller::GetOutlineColor() const
1245 return mImpl->mModel->mVisualModel->GetOutlineColor();
1248 void Controller::SetOutlineWidth( unsigned int width )
1250 mImpl->mModel->mVisualModel->SetOutlineWidth( width );
1252 mImpl->RequestRelayout();
1255 unsigned int Controller::GetOutlineWidth() const
1257 return mImpl->mModel->mVisualModel->GetOutlineWidth();
1260 void Controller::SetBackgroundColor( const Vector4& color )
1262 mImpl->mModel->mVisualModel->SetBackgroundColor( color );
1264 mImpl->RequestRelayout();
1267 const Vector4& Controller::GetBackgroundColor() const
1269 return mImpl->mModel->mVisualModel->GetBackgroundColor();
1272 void Controller::SetBackgroundEnabled( bool enabled )
1274 mImpl->mModel->mVisualModel->SetBackgroundEnabled( enabled );
1276 mImpl->RequestRelayout();
1279 bool Controller::IsBackgroundEnabled() const
1281 return mImpl->mModel->mVisualModel->IsBackgroundEnabled();
1284 void Controller::SetDefaultEmbossProperties( const std::string& embossProperties )
1286 if( NULL == mImpl->mEmbossDefaults )
1288 mImpl->mEmbossDefaults = new EmbossDefaults();
1291 mImpl->mEmbossDefaults->properties = embossProperties;
1294 const std::string& Controller::GetDefaultEmbossProperties() const
1296 if( NULL != mImpl->mEmbossDefaults )
1298 return mImpl->mEmbossDefaults->properties;
1301 return EMPTY_STRING;
1304 void Controller::SetDefaultOutlineProperties( const std::string& outlineProperties )
1306 if( NULL == mImpl->mOutlineDefaults )
1308 mImpl->mOutlineDefaults = new OutlineDefaults();
1311 mImpl->mOutlineDefaults->properties = outlineProperties;
1314 const std::string& Controller::GetDefaultOutlineProperties() const
1316 if( NULL != mImpl->mOutlineDefaults )
1318 return mImpl->mOutlineDefaults->properties;
1321 return EMPTY_STRING;
1324 bool Controller::SetDefaultLineSpacing( float lineSpacing )
1326 if( std::abs(lineSpacing - mImpl->mLayoutEngine.GetDefaultLineSpacing()) > Math::MACHINE_EPSILON_1000 )
1328 mImpl->mLayoutEngine.SetDefaultLineSpacing(lineSpacing);
1329 mImpl->mRecalculateNaturalSize = true;
1335 float Controller::GetDefaultLineSpacing() const
1337 return mImpl->mLayoutEngine.GetDefaultLineSpacing();
1340 void Controller::SetInputColor( const Vector4& color )
1342 if( NULL != mImpl->mEventData )
1344 mImpl->mEventData->mInputStyle.textColor = color;
1345 mImpl->mEventData->mInputStyle.isDefaultColor = false;
1347 if( EventData::SELECTING == mImpl->mEventData->mState )
1349 const bool handlesCrossed = mImpl->mEventData->mLeftSelectionPosition > mImpl->mEventData->mRightSelectionPosition;
1351 // Get start and end position of selection
1352 const CharacterIndex startOfSelectedText = handlesCrossed ? mImpl->mEventData->mRightSelectionPosition : mImpl->mEventData->mLeftSelectionPosition;
1353 const Length lengthOfSelectedText = ( handlesCrossed ? mImpl->mEventData->mLeftSelectionPosition : mImpl->mEventData->mRightSelectionPosition ) - startOfSelectedText;
1355 // Add the color run.
1356 const VectorBase::SizeType numberOfRuns = mImpl->mModel->mLogicalModel->mColorRuns.Count();
1357 mImpl->mModel->mLogicalModel->mColorRuns.Resize( numberOfRuns + 1u );
1359 ColorRun& colorRun = *( mImpl->mModel->mLogicalModel->mColorRuns.Begin() + numberOfRuns );
1360 colorRun.color = color;
1361 colorRun.characterRun.characterIndex = startOfSelectedText;
1362 colorRun.characterRun.numberOfCharacters = lengthOfSelectedText;
1364 // Request to relayout.
1365 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | COLOR );
1366 mImpl->RequestRelayout();
1368 mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1369 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1370 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText;
1375 const Vector4& Controller::GetInputColor() const
1377 if( NULL != mImpl->mEventData )
1379 return mImpl->mEventData->mInputStyle.textColor;
1382 // Return the default text's color if there is no EventData.
1383 return mImpl->mTextColor;
1387 void Controller::SetInputFontFamily( const std::string& fontFamily )
1389 if( NULL != mImpl->mEventData )
1391 mImpl->mEventData->mInputStyle.familyName = fontFamily;
1392 mImpl->mEventData->mInputStyle.isFamilyDefined = true;
1394 if( EventData::SELECTING == mImpl->mEventData->mState )
1396 CharacterIndex startOfSelectedText = 0u;
1397 Length lengthOfSelectedText = 0u;
1398 FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
1399 mImpl->mModel->mLogicalModel,
1400 startOfSelectedText,
1401 lengthOfSelectedText );
1403 fontDescriptionRun.familyLength = fontFamily.size();
1404 fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
1405 memcpy( fontDescriptionRun.familyName, fontFamily.c_str(), fontDescriptionRun.familyLength );
1406 fontDescriptionRun.familyDefined = true;
1408 // The memory allocated for the font family name is freed when the font description is removed from the logical model.
1410 // Request to relayout.
1411 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1416 UPDATE_LAYOUT_SIZE |
1419 mImpl->mRecalculateNaturalSize = true;
1420 mImpl->RequestRelayout();
1422 mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1423 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1424 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText;
1426 // As the font changes, recalculate the handle positions is needed.
1427 mImpl->mEventData->mUpdateLeftSelectionPosition = true;
1428 mImpl->mEventData->mUpdateRightSelectionPosition = true;
1429 mImpl->mEventData->mUpdateHighlightBox = true;
1430 mImpl->mEventData->mScrollAfterUpdatePosition = true;
1435 const std::string& Controller::GetInputFontFamily() const
1437 if( NULL != mImpl->mEventData )
1439 return mImpl->mEventData->mInputStyle.familyName;
1442 // Return the default font's family if there is no EventData.
1443 return GetDefaultFontFamily();
1446 void Controller::SetInputFontWeight( FontWeight weight )
1448 if( NULL != mImpl->mEventData )
1450 mImpl->mEventData->mInputStyle.weight = weight;
1451 mImpl->mEventData->mInputStyle.isWeightDefined = true;
1453 if( EventData::SELECTING == mImpl->mEventData->mState )
1455 CharacterIndex startOfSelectedText = 0u;
1456 Length lengthOfSelectedText = 0u;
1457 FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
1458 mImpl->mModel->mLogicalModel,
1459 startOfSelectedText,
1460 lengthOfSelectedText );
1462 fontDescriptionRun.weight = weight;
1463 fontDescriptionRun.weightDefined = true;
1465 // Request to relayout.
1466 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1471 UPDATE_LAYOUT_SIZE |
1474 mImpl->mRecalculateNaturalSize = true;
1475 mImpl->RequestRelayout();
1477 mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1478 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1479 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText;
1481 // As the font might change, recalculate the handle positions is needed.
1482 mImpl->mEventData->mUpdateLeftSelectionPosition = true;
1483 mImpl->mEventData->mUpdateRightSelectionPosition = true;
1484 mImpl->mEventData->mUpdateHighlightBox = true;
1485 mImpl->mEventData->mScrollAfterUpdatePosition = true;
1490 bool Controller::IsInputFontWeightDefined() const
1492 bool defined = false;
1494 if( NULL != mImpl->mEventData )
1496 defined = mImpl->mEventData->mInputStyle.isWeightDefined;
1502 FontWeight Controller::GetInputFontWeight() const
1504 if( NULL != mImpl->mEventData )
1506 return mImpl->mEventData->mInputStyle.weight;
1509 return GetDefaultFontWeight();
1512 void Controller::SetInputFontWidth( FontWidth width )
1514 if( NULL != mImpl->mEventData )
1516 mImpl->mEventData->mInputStyle.width = width;
1517 mImpl->mEventData->mInputStyle.isWidthDefined = true;
1519 if( EventData::SELECTING == mImpl->mEventData->mState )
1521 CharacterIndex startOfSelectedText = 0u;
1522 Length lengthOfSelectedText = 0u;
1523 FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
1524 mImpl->mModel->mLogicalModel,
1525 startOfSelectedText,
1526 lengthOfSelectedText );
1528 fontDescriptionRun.width = width;
1529 fontDescriptionRun.widthDefined = true;
1531 // Request to relayout.
1532 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1537 UPDATE_LAYOUT_SIZE |
1540 mImpl->mRecalculateNaturalSize = true;
1541 mImpl->RequestRelayout();
1543 mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1544 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1545 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText;
1547 // As the font might change, recalculate the handle positions is needed.
1548 mImpl->mEventData->mUpdateLeftSelectionPosition = true;
1549 mImpl->mEventData->mUpdateRightSelectionPosition = true;
1550 mImpl->mEventData->mUpdateHighlightBox = true;
1551 mImpl->mEventData->mScrollAfterUpdatePosition = true;
1556 bool Controller::IsInputFontWidthDefined() const
1558 bool defined = false;
1560 if( NULL != mImpl->mEventData )
1562 defined = mImpl->mEventData->mInputStyle.isWidthDefined;
1568 FontWidth Controller::GetInputFontWidth() const
1570 if( NULL != mImpl->mEventData )
1572 return mImpl->mEventData->mInputStyle.width;
1575 return GetDefaultFontWidth();
1578 void Controller::SetInputFontSlant( FontSlant slant )
1580 if( NULL != mImpl->mEventData )
1582 mImpl->mEventData->mInputStyle.slant = slant;
1583 mImpl->mEventData->mInputStyle.isSlantDefined = true;
1585 if( EventData::SELECTING == mImpl->mEventData->mState )
1587 CharacterIndex startOfSelectedText = 0u;
1588 Length lengthOfSelectedText = 0u;
1589 FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
1590 mImpl->mModel->mLogicalModel,
1591 startOfSelectedText,
1592 lengthOfSelectedText );
1594 fontDescriptionRun.slant = slant;
1595 fontDescriptionRun.slantDefined = true;
1597 // Request to relayout.
1598 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1603 UPDATE_LAYOUT_SIZE |
1606 mImpl->mRecalculateNaturalSize = true;
1607 mImpl->RequestRelayout();
1609 mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1610 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1611 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText;
1613 // As the font might change, recalculate the handle positions is needed.
1614 mImpl->mEventData->mUpdateLeftSelectionPosition = true;
1615 mImpl->mEventData->mUpdateRightSelectionPosition = true;
1616 mImpl->mEventData->mUpdateHighlightBox = true;
1617 mImpl->mEventData->mScrollAfterUpdatePosition = true;
1622 bool Controller::IsInputFontSlantDefined() const
1624 bool defined = false;
1626 if( NULL != mImpl->mEventData )
1628 defined = mImpl->mEventData->mInputStyle.isSlantDefined;
1634 FontSlant Controller::GetInputFontSlant() const
1636 if( NULL != mImpl->mEventData )
1638 return mImpl->mEventData->mInputStyle.slant;
1641 return GetDefaultFontSlant();
1644 void Controller::SetInputFontPointSize( float size )
1646 if( NULL != mImpl->mEventData )
1648 mImpl->mEventData->mInputStyle.size = size;
1649 mImpl->mEventData->mInputStyle.isSizeDefined = true;
1651 if( EventData::SELECTING == mImpl->mEventData->mState )
1653 CharacterIndex startOfSelectedText = 0u;
1654 Length lengthOfSelectedText = 0u;
1655 FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
1656 mImpl->mModel->mLogicalModel,
1657 startOfSelectedText,
1658 lengthOfSelectedText );
1660 fontDescriptionRun.size = static_cast<PointSize26Dot6>( size * 64.f );
1661 fontDescriptionRun.sizeDefined = true;
1663 // Request to relayout.
1664 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1669 UPDATE_LAYOUT_SIZE |
1672 mImpl->mRecalculateNaturalSize = true;
1673 mImpl->RequestRelayout();
1675 mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1676 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1677 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText;
1679 // As the font might change, recalculate the handle positions is needed.
1680 mImpl->mEventData->mUpdateLeftSelectionPosition = true;
1681 mImpl->mEventData->mUpdateRightSelectionPosition = true;
1682 mImpl->mEventData->mUpdateHighlightBox = true;
1683 mImpl->mEventData->mScrollAfterUpdatePosition = true;
1688 float Controller::GetInputFontPointSize() const
1690 if( NULL != mImpl->mEventData )
1692 return mImpl->mEventData->mInputStyle.size;
1695 // Return the default font's point size if there is no EventData.
1696 return GetDefaultFontSize( Text::Controller::POINT_SIZE );
1699 void Controller::SetInputLineSpacing( float lineSpacing )
1701 if( NULL != mImpl->mEventData )
1703 mImpl->mEventData->mInputStyle.lineSpacing = lineSpacing;
1704 mImpl->mEventData->mInputStyle.isLineSpacingDefined = true;
1708 float Controller::GetInputLineSpacing() const
1710 if( NULL != mImpl->mEventData )
1712 return mImpl->mEventData->mInputStyle.lineSpacing;
1718 void Controller::SetInputShadowProperties( const std::string& shadowProperties )
1720 if( NULL != mImpl->mEventData )
1722 mImpl->mEventData->mInputStyle.shadowProperties = shadowProperties;
1726 const std::string& Controller::GetInputShadowProperties() const
1728 if( NULL != mImpl->mEventData )
1730 return mImpl->mEventData->mInputStyle.shadowProperties;
1733 return EMPTY_STRING;
1736 void Controller::SetInputUnderlineProperties( const std::string& underlineProperties )
1738 if( NULL != mImpl->mEventData )
1740 mImpl->mEventData->mInputStyle.underlineProperties = underlineProperties;
1744 const std::string& Controller::GetInputUnderlineProperties() const
1746 if( NULL != mImpl->mEventData )
1748 return mImpl->mEventData->mInputStyle.underlineProperties;
1751 return EMPTY_STRING;
1754 void Controller::SetInputEmbossProperties( const std::string& embossProperties )
1756 if( NULL != mImpl->mEventData )
1758 mImpl->mEventData->mInputStyle.embossProperties = embossProperties;
1762 const std::string& Controller::GetInputEmbossProperties() const
1764 if( NULL != mImpl->mEventData )
1766 return mImpl->mEventData->mInputStyle.embossProperties;
1769 return GetDefaultEmbossProperties();
1772 void Controller::SetInputOutlineProperties( const std::string& outlineProperties )
1774 if( NULL != mImpl->mEventData )
1776 mImpl->mEventData->mInputStyle.outlineProperties = outlineProperties;
1780 const std::string& Controller::GetInputOutlineProperties() const
1782 if( NULL != mImpl->mEventData )
1784 return mImpl->mEventData->mInputStyle.outlineProperties;
1787 return GetDefaultOutlineProperties();
1790 void Controller::SetInputModePassword( bool passwordInput )
1792 if( NULL != mImpl->mEventData )
1794 mImpl->mEventData->mPasswordInput = passwordInput;
1798 bool Controller::IsInputModePassword()
1800 if( NULL != mImpl->mEventData )
1802 return mImpl->mEventData->mPasswordInput;
1807 void Controller::SetNoTextDoubleTapAction( NoTextTap::Action action )
1809 if( NULL != mImpl->mEventData )
1811 mImpl->mEventData->mDoubleTapAction = action;
1815 Controller::NoTextTap::Action Controller::GetNoTextDoubleTapAction() const
1817 NoTextTap::Action action = NoTextTap::NO_ACTION;
1819 if( NULL != mImpl->mEventData )
1821 action = mImpl->mEventData->mDoubleTapAction;
1827 void Controller::SetNoTextLongPressAction( NoTextTap::Action action )
1829 if( NULL != mImpl->mEventData )
1831 mImpl->mEventData->mLongPressAction = action;
1835 Controller::NoTextTap::Action Controller::GetNoTextLongPressAction() const
1837 NoTextTap::Action action = NoTextTap::NO_ACTION;
1839 if( NULL != mImpl->mEventData )
1841 action = mImpl->mEventData->mLongPressAction;
1847 bool Controller::IsUnderlineSetByString()
1849 return mImpl->mUnderlineSetByString;
1852 void Controller::UnderlineSetByString( bool setByString )
1854 mImpl->mUnderlineSetByString = setByString;
1857 bool Controller::IsShadowSetByString()
1859 return mImpl->mShadowSetByString;
1862 void Controller::ShadowSetByString( bool setByString )
1864 mImpl->mShadowSetByString = setByString;
1867 bool Controller::IsOutlineSetByString()
1869 return mImpl->mOutlineSetByString;
1872 void Controller::OutlineSetByString( bool setByString )
1874 mImpl->mOutlineSetByString = setByString;
1877 bool Controller::IsFontStyleSetByString()
1879 return mImpl->mFontStyleSetByString;
1882 void Controller::FontStyleSetByString( bool setByString )
1884 mImpl->mFontStyleSetByString = setByString;
1887 // public : Queries & retrieves.
1889 Layout::Engine& Controller::GetLayoutEngine()
1891 return mImpl->mLayoutEngine;
1894 View& Controller::GetView()
1896 return mImpl->mView;
1899 Vector3 Controller::GetNaturalSize()
1901 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetNaturalSize\n" );
1902 Vector3 naturalSize;
1904 // Make sure the model is up-to-date before layouting
1905 ProcessModifyEvents();
1907 if( mImpl->mRecalculateNaturalSize )
1909 // Operations that can be done only once until the text changes.
1910 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
1917 GET_GLYPH_METRICS );
1919 // Set the update info to relayout the whole text.
1920 mImpl->mTextUpdateInfo.mParagraphCharacterIndex = 0u;
1921 mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters = mImpl->mModel->mLogicalModel->mText.Count();
1923 // Make sure the model is up-to-date before layouting
1924 mImpl->UpdateModel( onlyOnceOperations );
1926 // Layout the text for the new width.
1927 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | LAYOUT | REORDER );
1929 // Store the actual control's size to restore later.
1930 const Size actualControlSize = mImpl->mModel->mVisualModel->mControlSize;
1932 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
1933 static_cast<OperationsMask>( onlyOnceOperations |
1935 naturalSize.GetVectorXY() );
1937 // Do not do again the only once operations.
1938 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1940 // Do the size related operations again.
1941 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
1944 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1946 // Stores the natural size to avoid recalculate it again
1947 // unless the text/style changes.
1948 mImpl->mModel->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
1950 mImpl->mRecalculateNaturalSize = false;
1952 // Clear the update info. This info will be set the next time the text is updated.
1953 mImpl->mTextUpdateInfo.Clear();
1954 mImpl->mTextUpdateInfo.mClearAll = true;
1956 // Restore the actual control's size.
1957 mImpl->mModel->mVisualModel->mControlSize = actualControlSize;
1959 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize calculated %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
1963 naturalSize = mImpl->mModel->mVisualModel->GetNaturalSize();
1965 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize cached %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
1968 naturalSize.x = ConvertToEven( naturalSize.x );
1969 naturalSize.y = ConvertToEven( naturalSize.y );
1974 float Controller::GetHeightForWidth( float width )
1976 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetHeightForWidth %p width %f\n", this, width );
1977 // Make sure the model is up-to-date before layouting
1978 ProcessModifyEvents();
1981 if( fabsf( width - mImpl->mModel->mVisualModel->mControlSize.width ) > Math::MACHINE_EPSILON_1000 ||
1982 mImpl->mTextUpdateInfo.mFullRelayoutNeeded ||
1983 mImpl->mTextUpdateInfo.mClearAll )
1985 // Operations that can be done only once until the text changes.
1986 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
1993 GET_GLYPH_METRICS );
1995 // Set the update info to relayout the whole text.
1996 mImpl->mTextUpdateInfo.mParagraphCharacterIndex = 0u;
1997 mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters = mImpl->mModel->mLogicalModel->mText.Count();
1999 // Make sure the model is up-to-date before layouting
2000 mImpl->UpdateModel( onlyOnceOperations );
2003 // Layout the text for the new width.
2004 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | LAYOUT );
2006 // Store the actual control's width.
2007 const float actualControlWidth = mImpl->mModel->mVisualModel->mControlSize.width;
2009 DoRelayout( Size( width, MAX_FLOAT ),
2010 static_cast<OperationsMask>( onlyOnceOperations |
2014 // Do not do again the only once operations.
2015 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
2017 // Do the size related operations again.
2018 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
2022 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
2024 // Clear the update info. This info will be set the next time the text is updated.
2025 mImpl->mTextUpdateInfo.Clear();
2026 mImpl->mTextUpdateInfo.mClearAll = true;
2028 // Restore the actual control's width.
2029 mImpl->mModel->mVisualModel->mControlSize.width = actualControlWidth;
2031 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth calculated %f\n", layoutSize.height );
2035 layoutSize = mImpl->mModel->mVisualModel->GetLayoutSize();
2036 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth cached %f\n", layoutSize.height );
2039 return layoutSize.height;
2042 int Controller::GetLineCount( float width )
2044 GetHeightForWidth( width );
2045 int numberofLines = mImpl->mModel->GetNumberOfLines();
2046 return numberofLines;
2049 const ModelInterface* const Controller::GetTextModel() const
2051 return mImpl->mModel.Get();
2054 float Controller::GetScrollAmountByUserInput()
2056 float scrollAmount = 0.0f;
2058 if (NULL != mImpl->mEventData && mImpl->mEventData->mCheckScrollAmount)
2060 scrollAmount = mImpl->mModel->mScrollPosition.y - mImpl->mModel->mScrollPositionLast.y;
2061 mImpl->mEventData->mCheckScrollAmount = false;
2063 return scrollAmount;
2066 bool Controller::GetTextScrollInfo( float& scrollPosition, float& controlHeight, float& layoutHeight )
2068 const Vector2& layout = mImpl->mModel->mVisualModel->GetLayoutSize();
2071 controlHeight = mImpl->mModel->mVisualModel->mControlSize.height;
2072 layoutHeight = layout.height;
2073 scrollPosition = mImpl->mModel->mScrollPosition.y;
2074 isScrolled = !Equals( mImpl->mModel->mScrollPosition.y, mImpl->mModel->mScrollPositionLast.y, Math::MACHINE_EPSILON_1 );
2078 void Controller::SetHiddenInputOption(const Property::Map& options )
2080 if( NULL == mImpl->mHiddenInput )
2082 mImpl->mHiddenInput = new HiddenText( this );
2084 mImpl->mHiddenInput->SetProperties(options);
2087 void Controller::GetHiddenInputOption(Property::Map& options )
2089 if( NULL != mImpl->mHiddenInput )
2091 mImpl->mHiddenInput->GetProperties(options);
2095 void Controller::SetPlaceholderProperty( const Property::Map& map )
2097 const Property::Map::SizeType count = map.Count();
2099 for( Property::Map::SizeType position = 0; position < count; ++position )
2101 KeyValuePair keyValue = map.GetKeyValue( position );
2102 Property::Key& key = keyValue.first;
2103 Property::Value& value = keyValue.second;
2105 if( key == Toolkit::Text::PlaceHolder::Property::TEXT || key == PLACEHOLDER_TEXT )
2107 std::string text = "";
2109 SetPlaceholderText( Controller::PLACEHOLDER_TYPE_INACTIVE, text );
2111 else if( key == Toolkit::Text::PlaceHolder::Property::TEXT_FOCUSED || key == PLACEHOLDER_TEXT_FOCUSED )
2113 std::string text = "";
2115 SetPlaceholderText( Controller::PLACEHOLDER_TYPE_ACTIVE, text );
2117 else if( key == Toolkit::Text::PlaceHolder::Property::COLOR || key == PLACEHOLDER_COLOR )
2120 value.Get( textColor );
2121 if( GetPlaceholderTextColor() != textColor )
2123 SetPlaceholderTextColor( textColor );
2126 else if( key == Toolkit::Text::PlaceHolder::Property::FONT_FAMILY || key == PLACEHOLDER_FONT_FAMILY )
2128 std::string fontFamily = "";
2129 value.Get( fontFamily );
2130 SetPlaceholderFontFamily( fontFamily );
2132 else if( key == Toolkit::Text::PlaceHolder::Property::FONT_STYLE || key == PLACEHOLDER_FONT_STYLE )
2134 SetFontStyleProperty( this, value, Text::FontStyle::PLACEHOLDER );
2136 else if( key == Toolkit::Text::PlaceHolder::Property::POINT_SIZE || key == PLACEHOLDER_POINT_SIZE )
2139 value.Get( pointSize );
2140 if( !Equals( GetPlaceholderTextFontSize( Text::Controller::POINT_SIZE ), pointSize ) )
2142 SetPlaceholderTextFontSize( pointSize, Text::Controller::POINT_SIZE );
2145 else if( key == Toolkit::Text::PlaceHolder::Property::PIXEL_SIZE || key == PLACEHOLDER_PIXEL_SIZE )
2148 value.Get( pixelSize );
2149 if( !Equals( GetPlaceholderTextFontSize( Text::Controller::PIXEL_SIZE ), pixelSize ) )
2151 SetPlaceholderTextFontSize( pixelSize, Text::Controller::PIXEL_SIZE );
2154 else if( key == Toolkit::Text::PlaceHolder::Property::ELLIPSIS || key == PLACEHOLDER_ELLIPSIS )
2157 value.Get( ellipsis );
2158 SetPlaceholderTextElideEnabled( ellipsis );
2163 void Controller::GetPlaceholderProperty( Property::Map& map )
2165 if( NULL != mImpl->mEventData )
2167 if( !mImpl->mEventData->mPlaceholderTextActive.empty() )
2169 map[ Text::PlaceHolder::Property::TEXT_FOCUSED ] = mImpl->mEventData->mPlaceholderTextActive;
2171 if( !mImpl->mEventData->mPlaceholderTextInactive.empty() )
2173 map[ Text::PlaceHolder::Property::TEXT ] = mImpl->mEventData->mPlaceholderTextInactive;
2176 map[ Text::PlaceHolder::Property::COLOR ] = mImpl->mEventData->mPlaceholderTextColor;
2177 map[ Text::PlaceHolder::Property::FONT_FAMILY ] = GetPlaceholderFontFamily();
2179 Property::Value fontStyleMapGet;
2180 GetFontStyleProperty( this, fontStyleMapGet, Text::FontStyle::PLACEHOLDER );
2181 map[ Text::PlaceHolder::Property::FONT_STYLE ] = fontStyleMapGet;
2183 // Choose font size : POINT_SIZE or PIXEL_SIZE
2184 if( !mImpl->mEventData->mIsPlaceholderPixelSize )
2186 map[ Text::PlaceHolder::Property::POINT_SIZE ] = GetPlaceholderTextFontSize( Text::Controller::POINT_SIZE );
2190 map[ Text::PlaceHolder::Property::PIXEL_SIZE ] = GetPlaceholderTextFontSize( Text::Controller::PIXEL_SIZE );
2193 if( mImpl->mEventData->mPlaceholderEllipsisFlag )
2195 map[ Text::PlaceHolder::Property::ELLIPSIS ] = IsPlaceholderTextElideEnabled();
2200 Toolkit::DevelText::TextDirection::Type Controller::GetTextDirection()
2202 // Make sure the model is up-to-date before layouting
2203 ProcessModifyEvents();
2205 if ( mImpl->mUpdateTextDirection )
2207 // Operations that can be done only once until the text changes.
2208 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
2215 GET_GLYPH_METRICS );
2217 // Set the update info to relayout the whole text.
2218 mImpl->mTextUpdateInfo.mParagraphCharacterIndex = 0u;
2219 mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters = mImpl->mModel->mLogicalModel->mText.Count();
2221 // Make sure the model is up-to-date before layouting
2222 mImpl->UpdateModel( onlyOnceOperations );
2224 Vector3 naturalSize;
2225 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
2226 static_cast<OperationsMask>( onlyOnceOperations |
2227 LAYOUT | REORDER | UPDATE_DIRECTION ),
2228 naturalSize.GetVectorXY() );
2230 // Do not do again the only once operations.
2231 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
2233 // Clear the update info. This info will be set the next time the text is updated.
2234 mImpl->mTextUpdateInfo.Clear();
2236 mImpl->mUpdateTextDirection = false;
2239 return mImpl->mIsTextDirectionRTL ? Toolkit::DevelText::TextDirection::RIGHT_TO_LEFT : Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT;
2242 Toolkit::DevelText::VerticalLineAlignment::Type Controller::GetVerticalLineAlignment() const
2244 return mImpl->mModel->GetVerticalLineAlignment();
2247 void Controller::SetVerticalLineAlignment( Toolkit::DevelText::VerticalLineAlignment::Type alignment )
2249 mImpl->mModel->mVerticalLineAlignment = alignment;
2252 // public : Relayout.
2254 Controller::UpdateTextType Controller::Relayout( const Size& size, Dali::LayoutDirection::Type layoutDirection )
2256 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::Relayout %p size %f,%f, autoScroll[%s]\n", this, size.width, size.height, mImpl->mIsAutoScrollEnabled ?"true":"false" );
2258 UpdateTextType updateTextType = NONE_UPDATED;
2261 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
2263 if( 0u != mImpl->mModel->mVisualModel->mGlyphPositions.Count() )
2265 mImpl->mModel->mVisualModel->mGlyphPositions.Clear();
2266 updateTextType = MODEL_UPDATED;
2269 // Clear the update info. This info will be set the next time the text is updated.
2270 mImpl->mTextUpdateInfo.Clear();
2272 // Not worth to relayout if width or height is equal to zero.
2273 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout (skipped)\n" );
2275 return updateTextType;
2278 // Whether a new size has been set.
2279 const bool newSize = ( size != mImpl->mModel->mVisualModel->mControlSize );
2283 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "new size (previous size %f,%f)\n", mImpl->mModel->mVisualModel->mControlSize.width, mImpl->mModel->mVisualModel->mControlSize.height );
2285 if( ( 0 == mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd ) &&
2286 ( 0 == mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters ) &&
2287 ( ( mImpl->mModel->mVisualModel->mControlSize.width < Math::MACHINE_EPSILON_1000 ) || ( mImpl->mModel->mVisualModel->mControlSize.height < Math::MACHINE_EPSILON_1000 ) ) )
2289 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count();
2292 // Layout operations that need to be done if the size changes.
2293 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
2296 UPDATE_LAYOUT_SIZE |
2298 // Set the update info to relayout the whole text.
2299 mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true;
2300 mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
2302 // Store the size used to layout the text.
2303 mImpl->mModel->mVisualModel->mControlSize = size;
2306 // Whether there are modify events.
2307 if( 0u != mImpl->mModifyEvents.Count() )
2309 // Style operations that need to be done if the text is modified.
2310 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
2314 // Set the update info to elide the text.
2315 if( mImpl->mModel->mElideEnabled ||
2316 ( ( NULL != mImpl->mEventData ) && mImpl->mEventData->mIsPlaceholderElideEnabled ) )
2318 // Update Text layout for applying elided
2319 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
2322 UPDATE_LAYOUT_SIZE |
2324 mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true;
2325 mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
2328 if( mImpl->mModel->mMatchSystemLanguageDirection && mImpl->mLayoutDirection != layoutDirection )
2330 // Clear the update info. This info will be set the next time the text is updated.
2331 mImpl->mTextUpdateInfo.mClearAll = true;
2332 // Apply modifications to the model
2333 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
2340 mImpl->mLayoutDirection = layoutDirection;
2343 // Make sure the model is up-to-date before layouting.
2344 ProcessModifyEvents();
2345 bool updated = mImpl->UpdateModel( mImpl->mOperationsPending );
2349 updated = DoRelayout( size,
2350 mImpl->mOperationsPending,
2351 layoutSize ) || updated;
2355 updateTextType = MODEL_UPDATED;
2358 // Do not re-do any operation until something changes.
2359 mImpl->mOperationsPending = NO_OPERATION;
2360 mImpl->mModel->mScrollPositionLast = mImpl->mModel->mScrollPosition;
2362 // Whether the text control is editable
2363 const bool isEditable = NULL != mImpl->mEventData;
2365 // Keep the current offset as it will be used to update the decorator's positions (if the size changes).
2367 if( newSize && isEditable )
2369 offset = mImpl->mModel->mScrollPosition;
2372 if( !isEditable || !IsMultiLineEnabled() )
2374 // After doing the text layout, the vertical offset to place the actor in the desired position can be calculated.
2375 CalculateVerticalOffset( size );
2382 // If there is a new size, the scroll position needs to be clamped.
2383 mImpl->ClampHorizontalScroll( layoutSize );
2385 // Update the decorator's positions is needed if there is a new size.
2386 mImpl->mEventData->mDecorator->UpdatePositions( mImpl->mModel->mScrollPosition - offset );
2389 // Move the cursor, grab handle etc.
2390 if( mImpl->ProcessInputEvents() )
2392 updateTextType = static_cast<UpdateTextType>( updateTextType | DECORATOR_UPDATED );
2396 // Clear the update info. This info will be set the next time the text is updated.
2397 mImpl->mTextUpdateInfo.Clear();
2398 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout\n" );
2400 return updateTextType;
2403 void Controller::RequestRelayout()
2405 mImpl->RequestRelayout();
2408 // public : Input style change signals.
2410 bool Controller::IsInputStyleChangedSignalsQueueEmpty()
2412 return ( NULL == mImpl->mEventData ) || ( 0u == mImpl->mEventData->mInputStyleChangedQueue.Count() );
2415 void Controller::ProcessInputStyleChangedSignals()
2417 if( NULL == mImpl->mEventData )
2423 for( Vector<InputStyle::Mask>::ConstIterator it = mImpl->mEventData->mInputStyleChangedQueue.Begin(),
2424 endIt = mImpl->mEventData->mInputStyleChangedQueue.End();
2428 const InputStyle::Mask mask = *it;
2430 if( NULL != mImpl->mEditableControlInterface )
2432 // Emit the input style changed signal.
2433 mImpl->mEditableControlInterface->InputStyleChanged( mask );
2437 mImpl->mEventData->mInputStyleChangedQueue.Clear();
2440 // public : Text-input Event Queuing.
2442 void Controller::KeyboardFocusGainEvent()
2444 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
2446 if( NULL != mImpl->mEventData )
2448 if( ( EventData::INACTIVE == mImpl->mEventData->mState ) ||
2449 ( EventData::INTERRUPTED == mImpl->mEventData->mState ) )
2451 mImpl->ChangeState( EventData::EDITING );
2452 mImpl->mEventData->mUpdateCursorPosition = true; //If editing started without tap event, cursor update must be triggered.
2453 mImpl->mEventData->mUpdateInputStyle = true;
2454 mImpl->mEventData->mScrollAfterUpdatePosition = true;
2456 mImpl->NotifyInputMethodContextMultiLineStatus();
2457 if( mImpl->IsShowingPlaceholderText() )
2459 // Show alternative placeholder-text when editing
2460 ShowPlaceholderText();
2463 mImpl->RequestRelayout();
2467 void Controller::KeyboardFocusLostEvent()
2469 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
2471 if( NULL != mImpl->mEventData )
2473 if( EventData::INTERRUPTED != mImpl->mEventData->mState )
2475 mImpl->ChangeState( EventData::INACTIVE );
2477 if( !mImpl->IsShowingRealText() )
2479 // Revert to regular placeholder-text when not editing
2480 ShowPlaceholderText();
2484 mImpl->RequestRelayout();
2487 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
2489 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
2491 bool textChanged = false;
2492 bool relayoutNeeded = false;
2494 if( ( NULL != mImpl->mEventData ) &&
2495 ( keyEvent.state == KeyEvent::Down ) )
2497 int keyCode = keyEvent.keyCode;
2498 const std::string& keyString = keyEvent.keyPressed;
2499 const std::string keyName = keyEvent.keyPressedName;
2501 const bool isNullKey = ( 0 == keyCode ) && ( keyString.empty() );
2503 // Pre-process to separate modifying events from non-modifying input events.
2506 // In some platforms arrive key events with no key code.
2510 else if( Dali::DALI_KEY_ESCAPE == keyCode || Dali::DALI_KEY_BACK == keyCode || Dali::DALI_KEY_SEARCH == keyCode )
2515 else if( ( Dali::DALI_KEY_CURSOR_LEFT == keyCode ) ||
2516 ( Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) ||
2517 ( Dali::DALI_KEY_CURSOR_UP == keyCode ) ||
2518 ( Dali::DALI_KEY_CURSOR_DOWN == keyCode ) )
2520 // If don't have any text, do nothing.
2521 if( !mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters )
2526 uint32_t cursorPosition = mImpl->mEventData->mPrimaryCursorPosition;
2527 uint32_t numberOfCharacters = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
2528 uint32_t cursorLine = mImpl->mModel->mVisualModel->GetLineOfCharacter( cursorPosition );
2529 uint32_t numberOfLines = mImpl->mModel->GetNumberOfLines();
2531 // Logic to determine whether this text control will lose focus or not.
2532 if( ( Dali::DALI_KEY_CURSOR_LEFT == keyCode && 0 == cursorPosition && !keyEvent.IsShiftModifier() ) ||
2533 ( Dali::DALI_KEY_CURSOR_RIGHT == keyCode && numberOfCharacters == cursorPosition && !keyEvent.IsShiftModifier() ) ||
2534 ( Dali::DALI_KEY_CURSOR_DOWN == keyCode && cursorLine == numberOfLines -1 ) ||
2535 ( Dali::DALI_KEY_CURSOR_DOWN == keyCode && numberOfCharacters == cursorPosition && cursorLine -1 == numberOfLines -1 ) ||
2536 ( Dali::DALI_KEY_CURSOR_UP == keyCode && cursorLine == 0 ) ||
2537 ( Dali::DALI_KEY_CURSOR_UP == keyCode && numberOfCharacters == cursorPosition && cursorLine == 1 ) )
2539 // Release the active highlight.
2540 if( mImpl->mEventData->mState == EventData::SELECTING )
2542 mImpl->ChangeState( EventData::EDITING );
2544 // Update selection position.
2545 mImpl->mEventData->mLeftSelectionPosition = mImpl->mEventData->mPrimaryCursorPosition;
2546 mImpl->mEventData->mRightSelectionPosition = mImpl->mEventData->mPrimaryCursorPosition;
2547 mImpl->mEventData->mUpdateCursorPosition = true;
2548 mImpl->RequestRelayout();
2553 mImpl->mEventData->mCheckScrollAmount = true;
2554 Event event( Event::CURSOR_KEY_EVENT );
2555 event.p1.mInt = keyCode;
2556 event.p2.mBool = keyEvent.IsShiftModifier();
2557 mImpl->mEventData->mEventQueue.push_back( event );
2559 // Will request for relayout.
2560 relayoutNeeded = true;
2562 else if ( Dali::DevelKey::DALI_KEY_CONTROL_LEFT == keyCode || Dali::DevelKey::DALI_KEY_CONTROL_RIGHT == keyCode )
2564 // Left or Right Control key event is received before Ctrl-C/V/X key event is received
2565 // If not handle it here, any selected text will be deleted
2570 else if ( keyEvent.IsCtrlModifier() )
2572 bool consumed = false;
2573 if (keyName == KEY_C_NAME)
2575 // Ctrl-C to copy the selected text
2576 TextPopupButtonTouched( Toolkit::TextSelectionPopup::COPY );
2579 else if (keyName == KEY_V_NAME)
2581 // Ctrl-V to paste the copied text
2582 TextPopupButtonTouched( Toolkit::TextSelectionPopup::PASTE );
2585 else if (keyName == KEY_X_NAME)
2587 // Ctrl-X to cut the selected text
2588 TextPopupButtonTouched( Toolkit::TextSelectionPopup::CUT );
2593 else if( ( Dali::DALI_KEY_BACKSPACE == keyCode ) ||
2594 ( Dali::DevelKey::DALI_KEY_DELETE == keyCode ) )
2596 textChanged = DeleteEvent( keyCode );
2598 // Will request for relayout.
2599 relayoutNeeded = true;
2601 else if( IsKey( keyEvent, Dali::DALI_KEY_POWER ) ||
2602 IsKey( keyEvent, Dali::DALI_KEY_MENU ) ||
2603 IsKey( keyEvent, Dali::DALI_KEY_HOME ) )
2605 // Power key/Menu/Home key behaviour does not allow edit mode to resume.
2606 mImpl->ChangeState( EventData::INACTIVE );
2608 // Will request for relayout.
2609 relayoutNeeded = true;
2611 // This branch avoids calling the InsertText() method of the 'else' branch which can delete selected text.
2613 else if( ( Dali::DALI_KEY_SHIFT_LEFT == keyCode ) || ( Dali::DALI_KEY_SHIFT_RIGHT == keyCode ) )
2615 // DALI_KEY_SHIFT_LEFT or DALI_KEY_SHIFT_RIGHT is the key code for the Left Shift. It's sent (by the InputMethodContext?) when the predictive text is enabled
2616 // and a character is typed after the type of a upper case latin character.
2621 else if( ( Dali::DALI_KEY_VOLUME_UP == keyCode ) || ( Dali::DALI_KEY_VOLUME_DOWN == keyCode ) )
2623 // This branch avoids calling the InsertText() method of the 'else' branch which can delete selected text.
2629 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", this, keyString.c_str() );
2631 // InputMethodContext is no longer handling key-events
2632 mImpl->ClearPreEditFlag();
2634 InsertText( keyString, COMMIT );
2637 // Will request for relayout.
2638 relayoutNeeded = true;
2641 if ( ( mImpl->mEventData->mState != EventData::INTERRUPTED ) &&
2642 ( mImpl->mEventData->mState != EventData::INACTIVE ) &&
2644 ( Dali::DALI_KEY_SHIFT_LEFT != keyCode ) &&
2645 ( Dali::DALI_KEY_SHIFT_RIGHT != keyCode ) &&
2646 ( Dali::DALI_KEY_VOLUME_UP != keyCode ) &&
2647 ( Dali::DALI_KEY_VOLUME_DOWN != keyCode ) )
2649 // Should not change the state if the key is the shift send by the InputMethodContext.
2650 // Otherwise, when the state is SELECTING the text controller can't send the right
2651 // surrounding info to the InputMethodContext.
2652 mImpl->ChangeState( EventData::EDITING );
2654 // Will request for relayout.
2655 relayoutNeeded = true;
2658 if( relayoutNeeded )
2660 mImpl->RequestRelayout();
2665 ( NULL != mImpl->mEditableControlInterface ) )
2667 // Do this last since it provides callbacks into application code
2668 mImpl->mEditableControlInterface->TextChanged();
2674 void Controller::TapEvent( unsigned int tapCount, float x, float y )
2676 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
2678 if( NULL != mImpl->mEventData )
2680 DALI_LOG_INFO( gLogFilter, Debug::Concise, "TapEvent state:%d \n", mImpl->mEventData->mState );
2681 EventData::State state( mImpl->mEventData->mState );
2682 bool relayoutNeeded( false ); // to avoid unnecessary relayouts when tapping an empty text-field
2684 if( mImpl->IsClipboardVisible() )
2686 if( EventData::INACTIVE == state || EventData::EDITING == state)
2688 mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
2690 relayoutNeeded = true;
2692 else if( 1u == tapCount )
2694 if( EventData::EDITING_WITH_POPUP == state || EventData::EDITING_WITH_PASTE_POPUP == state )
2696 mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE ); // If Popup shown hide it here so can be shown again if required.
2699 if( mImpl->IsShowingRealText() && ( EventData::INACTIVE != state ) )
2701 mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
2702 relayoutNeeded = true;
2706 if( mImpl->IsShowingPlaceholderText() && !mImpl->IsFocusedPlaceholderAvailable() )
2708 // Hide placeholder text
2712 if( EventData::INACTIVE == state )
2714 mImpl->ChangeState( EventData::EDITING );
2716 else if( !mImpl->IsClipboardEmpty() )
2718 mImpl->ChangeState( EventData::EDITING_WITH_POPUP );
2720 relayoutNeeded = true;
2723 else if( 2u == tapCount )
2725 if( mImpl->mEventData->mSelectionEnabled &&
2726 mImpl->IsShowingRealText() )
2728 relayoutNeeded = true;
2729 mImpl->mEventData->mIsLeftHandleSelected = true;
2730 mImpl->mEventData->mIsRightHandleSelected = true;
2734 // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
2735 if( relayoutNeeded )
2737 Event event( Event::TAP_EVENT );
2738 event.p1.mUint = tapCount;
2739 event.p2.mFloat = x;
2740 event.p3.mFloat = y;
2741 mImpl->mEventData->mEventQueue.push_back( event );
2743 mImpl->RequestRelayout();
2747 // Reset keyboard as tap event has occurred.
2748 mImpl->ResetInputMethodContext();
2751 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
2753 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
2755 if( NULL != mImpl->mEventData )
2757 Event event( Event::PAN_EVENT );
2758 event.p1.mInt = state;
2759 event.p2.mFloat = displacement.x;
2760 event.p3.mFloat = displacement.y;
2761 mImpl->mEventData->mEventQueue.push_back( event );
2763 mImpl->RequestRelayout();
2767 void Controller::LongPressEvent( Gesture::State state, float x, float y )
2769 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected LongPressEvent" );
2771 if( ( state == Gesture::Started ) &&
2772 ( NULL != mImpl->mEventData ) )
2774 // The 1st long-press on inactive text-field is treated as tap
2775 if( EventData::INACTIVE == mImpl->mEventData->mState )
2777 mImpl->ChangeState( EventData::EDITING );
2779 Event event( Event::TAP_EVENT );
2781 event.p2.mFloat = x;
2782 event.p3.mFloat = y;
2783 mImpl->mEventData->mEventQueue.push_back( event );
2785 mImpl->RequestRelayout();
2787 else if( !mImpl->IsShowingRealText() )
2789 Event event( Event::LONG_PRESS_EVENT );
2790 event.p1.mInt = state;
2791 event.p2.mFloat = x;
2792 event.p3.mFloat = y;
2793 mImpl->mEventData->mEventQueue.push_back( event );
2794 mImpl->RequestRelayout();
2796 else if( !mImpl->IsClipboardVisible() )
2798 // Reset the InputMethodContext to commit the pre-edit before selecting the text.
2799 mImpl->ResetInputMethodContext();
2801 Event event( Event::LONG_PRESS_EVENT );
2802 event.p1.mInt = state;
2803 event.p2.mFloat = x;
2804 event.p3.mFloat = y;
2805 mImpl->mEventData->mEventQueue.push_back( event );
2806 mImpl->RequestRelayout();
2808 mImpl->mEventData->mIsLeftHandleSelected = true;
2809 mImpl->mEventData->mIsRightHandleSelected = true;
2814 InputMethodContext::CallbackData Controller::OnInputMethodContextEvent( InputMethodContext& inputMethodContext, const InputMethodContext::EventData& inputMethodContextEvent )
2816 // Whether the text needs to be relaid-out.
2817 bool requestRelayout = false;
2819 // Whether to retrieve the text and cursor position to be sent to the InputMethodContext.
2820 bool retrieveText = false;
2821 bool retrieveCursor = false;
2823 switch( inputMethodContextEvent.eventName )
2825 case InputMethodContext::COMMIT:
2827 InsertText( inputMethodContextEvent.predictiveString, Text::Controller::COMMIT );
2828 requestRelayout = true;
2829 retrieveCursor = true;
2832 case InputMethodContext::PRE_EDIT:
2834 InsertText( inputMethodContextEvent.predictiveString, Text::Controller::PRE_EDIT );
2835 requestRelayout = true;
2836 retrieveCursor = true;
2839 case InputMethodContext::DELETE_SURROUNDING:
2841 const bool textDeleted = RemoveText( inputMethodContextEvent.cursorOffset,
2842 inputMethodContextEvent.numberOfChars,
2843 DONT_UPDATE_INPUT_STYLE );
2847 if( ( 0u != mImpl->mModel->mLogicalModel->mText.Count() ) ||
2848 !mImpl->IsPlaceholderAvailable() )
2850 mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
2854 ShowPlaceholderText();
2856 mImpl->mEventData->mUpdateCursorPosition = true;
2857 mImpl->mEventData->mScrollAfterDelete = true;
2859 requestRelayout = true;
2863 case InputMethodContext::GET_SURROUNDING:
2865 retrieveText = true;
2866 retrieveCursor = true;
2869 case InputMethodContext::PRIVATE_COMMAND:
2871 // PRIVATECOMMAND event is just for getting the private command message
2872 retrieveText = true;
2873 retrieveCursor = true;
2876 case InputMethodContext::VOID:
2883 if( requestRelayout )
2885 mImpl->mOperationsPending = ALL_OPERATIONS;
2886 mImpl->RequestRelayout();
2890 CharacterIndex cursorPosition = 0u;
2891 Length numberOfWhiteSpaces = 0u;
2893 if( retrieveCursor )
2895 numberOfWhiteSpaces = mImpl->GetNumberOfWhiteSpaces( 0u );
2897 cursorPosition = mImpl->GetLogicalCursorPosition();
2899 if( cursorPosition < numberOfWhiteSpaces )
2901 cursorPosition = 0u;
2905 cursorPosition -= numberOfWhiteSpaces;
2911 if( !mImpl->IsShowingPlaceholderText() )
2913 // Retrieves the normal text string.
2914 mImpl->GetText( numberOfWhiteSpaces, text );
2918 // When the current text is Placeholder Text, the surrounding text should be empty string.
2919 // It means DALi should send empty string ("") to IME.
2924 InputMethodContext::CallbackData callbackData( ( retrieveText || retrieveCursor ), cursorPosition, text, false );
2926 if( requestRelayout &&
2927 ( NULL != mImpl->mEditableControlInterface ) )
2929 // Do this last since it provides callbacks into application code
2930 mImpl->mEditableControlInterface->TextChanged();
2933 return callbackData;
2936 void Controller::PasteClipboardItemEvent()
2938 // Retrieve the clipboard contents first
2939 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
2940 std::string stringToPaste( notifier.GetContent() );
2942 // Commit the current pre-edit text; the contents of the clipboard should be appended
2943 mImpl->ResetInputMethodContext();
2945 // Temporary disable hiding clipboard
2946 mImpl->SetClipboardHideEnable( false );
2949 PasteText( stringToPaste );
2951 mImpl->SetClipboardHideEnable( true );
2954 // protected : Inherit from Text::Decorator::ControllerInterface.
2956 void Controller::GetTargetSize( Vector2& targetSize )
2958 targetSize = mImpl->mModel->mVisualModel->mControlSize;
2961 void Controller::AddDecoration( Actor& actor, bool needsClipping )
2963 if( NULL != mImpl->mEditableControlInterface )
2965 mImpl->mEditableControlInterface->AddDecoration( actor, needsClipping );
2969 void Controller::DecorationEvent( HandleType handleType, HandleState state, float x, float y )
2971 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected DecorationEvent" );
2973 if( NULL != mImpl->mEventData )
2975 switch( handleType )
2979 Event event( Event::GRAB_HANDLE_EVENT );
2980 event.p1.mUint = state;
2981 event.p2.mFloat = x;
2982 event.p3.mFloat = y;
2984 mImpl->mEventData->mEventQueue.push_back( event );
2987 case LEFT_SELECTION_HANDLE:
2989 Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
2990 event.p1.mUint = state;
2991 event.p2.mFloat = x;
2992 event.p3.mFloat = y;
2994 mImpl->mEventData->mEventQueue.push_back( event );
2997 case RIGHT_SELECTION_HANDLE:
2999 Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
3000 event.p1.mUint = state;
3001 event.p2.mFloat = x;
3002 event.p3.mFloat = y;
3004 mImpl->mEventData->mEventQueue.push_back( event );
3007 case LEFT_SELECTION_HANDLE_MARKER:
3008 case RIGHT_SELECTION_HANDLE_MARKER:
3010 // Markers do not move the handles.
3013 case HANDLE_TYPE_COUNT:
3015 DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
3019 mImpl->RequestRelayout();
3023 // protected : Inherit from TextSelectionPopup::TextPopupButtonCallbackInterface.
3025 void Controller::TextPopupButtonTouched( Dali::Toolkit::TextSelectionPopup::Buttons button )
3027 if( NULL == mImpl->mEventData )
3034 case Toolkit::TextSelectionPopup::CUT:
3036 mImpl->SendSelectionToClipboard( true ); // Synchronous call to modify text
3037 mImpl->mOperationsPending = ALL_OPERATIONS;
3039 if( ( 0u != mImpl->mModel->mLogicalModel->mText.Count() ) ||
3040 !mImpl->IsPlaceholderAvailable() )
3042 mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
3046 ShowPlaceholderText();
3049 mImpl->mEventData->mUpdateCursorPosition = true;
3050 mImpl->mEventData->mScrollAfterDelete = true;
3052 mImpl->RequestRelayout();
3054 if( NULL != mImpl->mEditableControlInterface )
3056 mImpl->mEditableControlInterface->TextChanged();
3060 case Toolkit::TextSelectionPopup::COPY:
3062 mImpl->SendSelectionToClipboard( false ); // Text not modified
3064 mImpl->mEventData->mUpdateCursorPosition = true;
3066 mImpl->RequestRelayout(); // Cursor, Handles, Selection Highlight, Popup
3069 case Toolkit::TextSelectionPopup::PASTE:
3071 mImpl->RequestGetTextFromClipboard(); // Request clipboard service to retrieve an item
3074 case Toolkit::TextSelectionPopup::SELECT:
3076 const Vector2& currentCursorPosition = mImpl->mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
3078 if( mImpl->mEventData->mSelectionEnabled )
3080 // Creates a SELECT event.
3081 SelectEvent( currentCursorPosition.x, currentCursorPosition.y, false );
3085 case Toolkit::TextSelectionPopup::SELECT_ALL:
3087 // Creates a SELECT_ALL event
3088 SelectEvent( 0.f, 0.f, true );
3091 case Toolkit::TextSelectionPopup::CLIPBOARD:
3093 mImpl->ShowClipboard();
3096 case Toolkit::TextSelectionPopup::NONE:
3104 void Controller::DisplayTimeExpired()
3106 mImpl->mEventData->mUpdateCursorPosition = true;
3107 // Apply modifications to the model
3108 mImpl->mOperationsPending = ALL_OPERATIONS;
3110 mImpl->RequestRelayout();
3113 // private : Update.
3115 void Controller::InsertText( const std::string& text, Controller::InsertType type )
3117 bool removedPrevious = false;
3118 bool removedSelected = false;
3119 bool maxLengthReached = false;
3121 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
3123 if( NULL == mImpl->mEventData )
3128 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPrimaryCursorPosition %d mPreEditFlag %d mPreEditStartPosition %d mPreEditLength %d\n",
3129 this, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"),
3130 mImpl->mEventData->mPrimaryCursorPosition, mImpl->mEventData->mPreEditFlag, mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
3132 // TODO: At the moment the underline runs are only for pre-edit.
3133 mImpl->mModel->mVisualModel->mUnderlineRuns.Clear();
3135 // Remove the previous InputMethodContext pre-edit.
3136 if( mImpl->mEventData->mPreEditFlag && ( 0u != mImpl->mEventData->mPreEditLength ) )
3138 removedPrevious = RemoveText( -static_cast<int>( mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition ),
3139 mImpl->mEventData->mPreEditLength,
3140 DONT_UPDATE_INPUT_STYLE );
3142 mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
3143 mImpl->mEventData->mPreEditLength = 0u;
3147 // Remove the previous Selection.
3148 removedSelected = RemoveSelectedText();
3152 Vector<Character> utf32Characters;
3153 Length characterCount = 0u;
3157 // Convert text into UTF-32
3158 utf32Characters.Resize( text.size() );
3160 // This is a bit horrible but std::string returns a (signed) char*
3161 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
3163 // Transform a text array encoded in utf8 into an array encoded in utf32.
3164 // It returns the actual number of characters.
3165 characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
3166 utf32Characters.Resize( characterCount );
3168 DALI_ASSERT_DEBUG( text.size() >= utf32Characters.Count() && "Invalid UTF32 conversion length" );
3169 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "UTF8 size %d, UTF32 size %d\n", text.size(), utf32Characters.Count() );
3172 if( 0u != utf32Characters.Count() ) // Check if Utf8ToUtf32 conversion succeeded
3174 // The placeholder text is no longer needed
3175 if( mImpl->IsShowingPlaceholderText() )
3180 mImpl->ChangeState( EventData::EDITING );
3182 // Handle the InputMethodContext (predicitive text) state changes
3183 if( COMMIT == type )
3185 // InputMethodContext is no longer handling key-events
3186 mImpl->ClearPreEditFlag();
3190 if( !mImpl->mEventData->mPreEditFlag )
3192 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Entered PreEdit state\n" );
3194 // Record the start of the pre-edit text
3195 mImpl->mEventData->mPreEditStartPosition = mImpl->mEventData->mPrimaryCursorPosition;
3198 mImpl->mEventData->mPreEditLength = utf32Characters.Count();
3199 mImpl->mEventData->mPreEditFlag = true;
3201 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
3204 const Length numberOfCharactersInModel = mImpl->mModel->mLogicalModel->mText.Count();
3206 // Restrict new text to fit within Maximum characters setting.
3207 Length maxSizeOfNewText = std::min( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
3208 maxLengthReached = ( characterCount > maxSizeOfNewText );
3210 // The cursor position.
3211 CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
3213 // Update the text's style.
3215 // Updates the text style runs by adding characters.
3216 mImpl->mModel->mLogicalModel->UpdateTextStyleRuns( cursorIndex, maxSizeOfNewText );
3218 // Get the character index from the cursor index.
3219 const CharacterIndex styleIndex = ( cursorIndex > 0u ) ? cursorIndex - 1u : 0u;
3221 // Retrieve the text's style for the given index.
3223 mImpl->RetrieveDefaultInputStyle( style );
3224 mImpl->mModel->mLogicalModel->RetrieveStyle( styleIndex, style );
3226 // Whether to add a new text color run.
3227 const bool addColorRun = ( style.textColor != mImpl->mEventData->mInputStyle.textColor ) && !mImpl->mEventData->mInputStyle.isDefaultColor;
3229 // Whether to add a new font run.
3230 const bool addFontNameRun = ( style.familyName != mImpl->mEventData->mInputStyle.familyName ) && mImpl->mEventData->mInputStyle.isFamilyDefined;
3231 const bool addFontWeightRun = ( style.weight != mImpl->mEventData->mInputStyle.weight ) && mImpl->mEventData->mInputStyle.isWeightDefined;
3232 const bool addFontWidthRun = ( style.width != mImpl->mEventData->mInputStyle.width ) && mImpl->mEventData->mInputStyle.isWidthDefined;
3233 const bool addFontSlantRun = ( style.slant != mImpl->mEventData->mInputStyle.slant ) && mImpl->mEventData->mInputStyle.isSlantDefined;
3234 const bool addFontSizeRun = ( style.size != mImpl->mEventData->mInputStyle.size ) && mImpl->mEventData->mInputStyle.isSizeDefined ;
3239 const VectorBase::SizeType numberOfRuns = mImpl->mModel->mLogicalModel->mColorRuns.Count();
3240 mImpl->mModel->mLogicalModel->mColorRuns.Resize( numberOfRuns + 1u );
3242 ColorRun& colorRun = *( mImpl->mModel->mLogicalModel->mColorRuns.Begin() + numberOfRuns );
3243 colorRun.color = mImpl->mEventData->mInputStyle.textColor;
3244 colorRun.characterRun.characterIndex = cursorIndex;
3245 colorRun.characterRun.numberOfCharacters = maxSizeOfNewText;
3248 if( addFontNameRun ||
3254 const VectorBase::SizeType numberOfRuns = mImpl->mModel->mLogicalModel->mFontDescriptionRuns.Count();
3255 mImpl->mModel->mLogicalModel->mFontDescriptionRuns.Resize( numberOfRuns + 1u );
3257 FontDescriptionRun& fontDescriptionRun = *( mImpl->mModel->mLogicalModel->mFontDescriptionRuns.Begin() + numberOfRuns );
3259 if( addFontNameRun )
3261 fontDescriptionRun.familyLength = mImpl->mEventData->mInputStyle.familyName.size();
3262 fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
3263 memcpy( fontDescriptionRun.familyName, mImpl->mEventData->mInputStyle.familyName.c_str(), fontDescriptionRun.familyLength );
3264 fontDescriptionRun.familyDefined = true;
3266 // The memory allocated for the font family name is freed when the font description is removed from the logical model.
3269 if( addFontWeightRun )
3271 fontDescriptionRun.weight = mImpl->mEventData->mInputStyle.weight;
3272 fontDescriptionRun.weightDefined = true;
3275 if( addFontWidthRun )
3277 fontDescriptionRun.width = mImpl->mEventData->mInputStyle.width;
3278 fontDescriptionRun.widthDefined = true;
3281 if( addFontSlantRun )
3283 fontDescriptionRun.slant = mImpl->mEventData->mInputStyle.slant;
3284 fontDescriptionRun.slantDefined = true;
3287 if( addFontSizeRun )
3289 fontDescriptionRun.size = static_cast<PointSize26Dot6>( mImpl->mEventData->mInputStyle.size * 64.f );
3290 fontDescriptionRun.sizeDefined = true;
3293 fontDescriptionRun.characterRun.characterIndex = cursorIndex;
3294 fontDescriptionRun.characterRun.numberOfCharacters = maxSizeOfNewText;
3297 // Insert at current cursor position.
3298 Vector<Character>& modifyText = mImpl->mModel->mLogicalModel->mText;
3300 if( cursorIndex < numberOfCharactersInModel )
3302 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
3306 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
3309 // Mark the first paragraph to be updated.
3310 if( Layout::Engine::SINGLE_LINE_BOX == mImpl->mLayoutEngine.GetLayout() )
3312 mImpl->mTextUpdateInfo.mCharacterIndex = 0;
3313 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
3314 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = numberOfCharactersInModel + maxSizeOfNewText;
3315 mImpl->mTextUpdateInfo.mClearAll = true;
3319 mImpl->mTextUpdateInfo.mCharacterIndex = std::min( cursorIndex, mImpl->mTextUpdateInfo.mCharacterIndex );
3320 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd += maxSizeOfNewText;
3323 // Update the cursor index.
3324 cursorIndex += maxSizeOfNewText;
3326 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Inserted %d characters, new size %d new cursor %d\n", maxSizeOfNewText, mImpl->mModel->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition );
3329 if( ( 0u == mImpl->mModel->mLogicalModel->mText.Count() ) &&
3330 mImpl->IsPlaceholderAvailable() )
3332 // Show place-holder if empty after removing the pre-edit text
3333 ShowPlaceholderText();
3334 mImpl->mEventData->mUpdateCursorPosition = true;
3335 mImpl->ClearPreEditFlag();
3337 else if( removedPrevious ||
3339 ( 0 != utf32Characters.Count() ) )
3341 // Queue an inserted event
3342 mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
3344 mImpl->mEventData->mUpdateCursorPosition = true;
3345 if( removedSelected )
3347 mImpl->mEventData->mScrollAfterDelete = true;
3351 mImpl->mEventData->mScrollAfterUpdatePosition = true;
3355 if( maxLengthReached )
3357 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", mImpl->mModel->mLogicalModel->mText.Count() );
3359 mImpl->ResetInputMethodContext();
3361 if( NULL != mImpl->mEditableControlInterface )
3363 // Do this last since it provides callbacks into application code
3364 mImpl->mEditableControlInterface->MaxLengthReached();
3369 void Controller::PasteText( const std::string& stringToPaste )
3371 InsertText( stringToPaste, Text::Controller::COMMIT );
3372 mImpl->ChangeState( EventData::EDITING );
3373 mImpl->RequestRelayout();
3375 if( NULL != mImpl->mEditableControlInterface )
3377 // Do this last since it provides callbacks into application code
3378 mImpl->mEditableControlInterface->TextChanged();
3382 bool Controller::RemoveText( int cursorOffset,
3383 int numberOfCharacters,
3384 UpdateInputStyleType type )
3386 bool removed = false;
3388 if( NULL == mImpl->mEventData )
3393 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p mText.Count() %d cursor %d cursorOffset %d numberOfCharacters %d\n",
3394 this, mImpl->mModel->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition, cursorOffset, numberOfCharacters );
3396 if( !mImpl->IsShowingPlaceholderText() )
3398 // Delete at current cursor position
3399 Vector<Character>& currentText = mImpl->mModel->mLogicalModel->mText;
3400 CharacterIndex& oldCursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
3402 CharacterIndex cursorIndex = 0;
3404 // Validate the cursor position & number of characters
3405 if( ( static_cast< int >( mImpl->mEventData->mPrimaryCursorPosition ) + cursorOffset ) >= 0 )
3407 cursorIndex = mImpl->mEventData->mPrimaryCursorPosition + cursorOffset;
3410 if( ( cursorIndex + numberOfCharacters ) > currentText.Count() )
3412 numberOfCharacters = currentText.Count() - cursorIndex;
3415 if( mImpl->mEventData->mPreEditFlag || // If the preedit flag is enabled, it means two (or more) of them came together i.e. when two keys have been pressed at the same time.
3416 ( ( cursorIndex + numberOfCharacters ) <= mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters ) )
3418 // Mark the paragraphs to be updated.
3419 if( Layout::Engine::SINGLE_LINE_BOX == mImpl->mLayoutEngine.GetLayout() )
3421 mImpl->mTextUpdateInfo.mCharacterIndex = 0;
3422 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
3423 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters - numberOfCharacters;
3424 mImpl->mTextUpdateInfo.mClearAll = true;
3428 mImpl->mTextUpdateInfo.mCharacterIndex = std::min( cursorIndex, mImpl->mTextUpdateInfo.mCharacterIndex );
3429 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove += numberOfCharacters;
3432 // Update the input style and remove the text's style before removing the text.
3434 if( UPDATE_INPUT_STYLE == type )
3436 // Keep a copy of the current input style.
3437 InputStyle currentInputStyle;
3438 currentInputStyle.Copy( mImpl->mEventData->mInputStyle );
3440 // Set first the default input style.
3441 mImpl->RetrieveDefaultInputStyle( mImpl->mEventData->mInputStyle );
3443 // Update the input style.
3444 mImpl->mModel->mLogicalModel->RetrieveStyle( cursorIndex, mImpl->mEventData->mInputStyle );
3446 // Compare if the input style has changed.
3447 const bool hasInputStyleChanged = !currentInputStyle.Equal( mImpl->mEventData->mInputStyle );
3449 if( hasInputStyleChanged )
3451 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mImpl->mEventData->mInputStyle );
3452 // Queue the input style changed signal.
3453 mImpl->mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
3457 // Updates the text style runs by removing characters. Runs with no characters are removed.
3458 mImpl->mModel->mLogicalModel->UpdateTextStyleRuns( cursorIndex, -numberOfCharacters );
3460 // Remove the characters.
3461 Vector<Character>::Iterator first = currentText.Begin() + cursorIndex;
3462 Vector<Character>::Iterator last = first + numberOfCharacters;
3464 currentText.Erase( first, last );
3466 // Cursor position retreat
3467 oldCursorIndex = cursorIndex;
3469 mImpl->mEventData->mScrollAfterDelete = true;
3471 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p removed %d\n", this, numberOfCharacters );
3479 bool Controller::RemoveSelectedText()
3481 bool textRemoved( false );
3483 if( EventData::SELECTING == mImpl->mEventData->mState )
3485 std::string removedString;
3486 mImpl->RetrieveSelection( removedString, true );
3488 if( !removedString.empty() )
3491 mImpl->ChangeState( EventData::EDITING );
3498 // private : Relayout.
3500 bool Controller::DoRelayout( const Size& size,
3501 OperationsMask operationsRequired,
3504 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout %p size %f,%f\n", this, size.width, size.height );
3505 bool viewUpdated( false );
3507 // Calculate the operations to be done.
3508 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
3510 const CharacterIndex startIndex = mImpl->mTextUpdateInfo.mParagraphCharacterIndex;
3511 const Length requestedNumberOfCharacters = mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters;
3513 // Get the current layout size.
3514 layoutSize = mImpl->mModel->mVisualModel->GetLayoutSize();
3516 if( NO_OPERATION != ( LAYOUT & operations ) )
3518 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout LAYOUT & operations\n");
3520 // Some vectors with data needed to layout and reorder may be void
3521 // after the first time the text has been laid out.
3522 // Fill the vectors again.
3524 // Calculate the number of glyphs to layout.
3525 const Vector<GlyphIndex>& charactersToGlyph = mImpl->mModel->mVisualModel->mCharactersToGlyph;
3526 const Vector<Length>& glyphsPerCharacter = mImpl->mModel->mVisualModel->mGlyphsPerCharacter;
3527 const GlyphIndex* const charactersToGlyphBuffer = charactersToGlyph.Begin();
3528 const Length* const glyphsPerCharacterBuffer = glyphsPerCharacter.Begin();
3530 const CharacterIndex lastIndex = startIndex + ( ( requestedNumberOfCharacters > 0u ) ? requestedNumberOfCharacters - 1u : 0u );
3531 const GlyphIndex startGlyphIndex = mImpl->mTextUpdateInfo.mStartGlyphIndex;
3533 // Make sure the index is not out of bound
3534 if ( charactersToGlyph.Count() != glyphsPerCharacter.Count() ||
3535 requestedNumberOfCharacters > charactersToGlyph.Count() ||
3536 ( lastIndex >= charactersToGlyph.Count() && charactersToGlyph.Count() > 0u ) )
3538 std::string currentText;
3539 GetText( currentText );
3541 DALI_LOG_ERROR( "Controller::DoRelayout: Attempting to access invalid buffer\n" );
3542 DALI_LOG_ERROR( "Current text is: %s\n", currentText.c_str() );
3543 DALI_LOG_ERROR( "startIndex: %u, lastIndex: %u, requestedNumberOfCharacters: %u, charactersToGlyph.Count = %lu, glyphsPerCharacter.Count = %lu\n", startIndex, lastIndex, requestedNumberOfCharacters, charactersToGlyph.Count(), glyphsPerCharacter.Count());
3548 const Length numberOfGlyphs = ( requestedNumberOfCharacters > 0u ) ? *( charactersToGlyphBuffer + lastIndex ) + *( glyphsPerCharacterBuffer + lastIndex ) - startGlyphIndex : 0u;
3549 const Length totalNumberOfGlyphs = mImpl->mModel->mVisualModel->mGlyphs.Count();
3551 if( 0u == totalNumberOfGlyphs )
3553 if( NO_OPERATION != ( UPDATE_LAYOUT_SIZE & operations ) )
3555 mImpl->mModel->mVisualModel->SetLayoutSize( Size::ZERO );
3558 // Nothing else to do if there is no glyphs.
3559 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout no glyphs, view updated true\n" );
3563 const Vector<LineBreakInfo>& lineBreakInfo = mImpl->mModel->mLogicalModel->mLineBreakInfo;
3564 const Vector<WordBreakInfo>& wordBreakInfo = mImpl->mModel->mLogicalModel->mWordBreakInfo;
3565 const Vector<CharacterDirection>& characterDirection = mImpl->mModel->mLogicalModel->mCharacterDirections;
3566 const Vector<GlyphInfo>& glyphs = mImpl->mModel->mVisualModel->mGlyphs;
3567 const Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mModel->mVisualModel->mGlyphsToCharacters;
3568 const Vector<Length>& charactersPerGlyph = mImpl->mModel->mVisualModel->mCharactersPerGlyph;
3569 const Character* const textBuffer = mImpl->mModel->mLogicalModel->mText.Begin();
3570 const float outlineWidth = static_cast<float>( mImpl->mModel->GetOutlineWidth() );
3572 // Set the layout parameters.
3573 Layout::Parameters layoutParameters( size,
3575 lineBreakInfo.Begin(),
3576 wordBreakInfo.Begin(),
3577 ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
3579 glyphsToCharactersMap.Begin(),
3580 charactersPerGlyph.Begin(),
3581 charactersToGlyphBuffer,
3582 glyphsPerCharacterBuffer,
3583 totalNumberOfGlyphs,
3584 mImpl->mModel->mHorizontalAlignment,
3585 mImpl->mModel->mLineWrapMode,
3587 mImpl->mModel->mIgnoreSpacesAfterText,
3588 mImpl->mModel->mMatchSystemLanguageDirection );
3590 // Resize the vector of positions to have the same size than the vector of glyphs.
3591 Vector<Vector2>& glyphPositions = mImpl->mModel->mVisualModel->mGlyphPositions;
3592 glyphPositions.Resize( totalNumberOfGlyphs );
3594 // Whether the last character is a new paragraph character.
3595 mImpl->mTextUpdateInfo.mIsLastCharacterNewParagraph = TextAbstraction::IsNewParagraph( *( textBuffer + ( mImpl->mModel->mLogicalModel->mText.Count() - 1u ) ) );
3596 layoutParameters.isLastNewParagraph = mImpl->mTextUpdateInfo.mIsLastCharacterNewParagraph;
3598 // The initial glyph and the number of glyphs to layout.
3599 layoutParameters.startGlyphIndex = startGlyphIndex;
3600 layoutParameters.numberOfGlyphs = numberOfGlyphs;
3601 layoutParameters.startLineIndex = mImpl->mTextUpdateInfo.mStartLineIndex;
3602 layoutParameters.estimatedNumberOfLines = mImpl->mTextUpdateInfo.mEstimatedNumberOfLines;
3604 // Update the ellipsis
3605 bool elideTextEnabled = mImpl->mModel->mElideEnabled;
3607 if( NULL != mImpl->mEventData )
3609 if( mImpl->mEventData->mPlaceholderEllipsisFlag && mImpl->IsShowingPlaceholderText() )
3611 elideTextEnabled = mImpl->mEventData->mIsPlaceholderElideEnabled;
3613 else if( EventData::INACTIVE != mImpl->mEventData->mState )
3615 // Disable ellipsis when editing
3616 elideTextEnabled = false;
3619 // Reset the scroll position in inactive state
3620 if( elideTextEnabled && ( mImpl->mEventData->mState == EventData::INACTIVE ) )
3622 ResetScrollPosition();
3626 // Update the visual model.
3628 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
3630 mImpl->mModel->mVisualModel->mLines,
3634 viewUpdated = viewUpdated || ( newLayoutSize != layoutSize );
3638 layoutSize = newLayoutSize;
3640 if( NO_OPERATION != ( UPDATE_DIRECTION & operations ) )
3642 mImpl->mIsTextDirectionRTL = false;
3645 // Reorder the lines
3646 if( NO_OPERATION != ( REORDER & operations ) )
3648 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mModel->mLogicalModel->mBidirectionalParagraphInfo;
3649 Vector<BidirectionalLineInfoRun>& bidirectionalLineInfo = mImpl->mModel->mLogicalModel->mBidirectionalLineInfo;
3651 // Check first if there are paragraphs with bidirectional info.
3652 if( 0u != bidirectionalInfo.Count() )
3655 const Length numberOfLines = mImpl->mModel->mVisualModel->mLines.Count();
3657 // Reorder the lines.
3658 bidirectionalLineInfo.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
3659 ReorderLines( bidirectionalInfo,
3661 requestedNumberOfCharacters,
3662 mImpl->mModel->mVisualModel->mLines,
3663 bidirectionalLineInfo );
3665 // Set the bidirectional info per line into the layout parameters.
3666 layoutParameters.lineBidirectionalInfoRunsBuffer = bidirectionalLineInfo.Begin();
3667 layoutParameters.numberOfBidirectionalInfoRuns = bidirectionalLineInfo.Count();
3669 // Re-layout the text. Reorder those lines with right to left characters.
3670 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
3672 requestedNumberOfCharacters,
3675 if ( ( NO_OPERATION != ( UPDATE_DIRECTION & operations ) ) && ( numberOfLines > 0 ) )
3677 const LineRun* const firstline = mImpl->mModel->mVisualModel->mLines.Begin();
3680 mImpl->mIsTextDirectionRTL = firstline->direction;
3686 // Sets the layout size.
3687 if( NO_OPERATION != ( UPDATE_LAYOUT_SIZE & operations ) )
3689 mImpl->mModel->mVisualModel->SetLayoutSize( layoutSize );
3694 if( NO_OPERATION != ( ALIGN & operations ) )
3696 // The laid-out lines.
3697 Vector<LineRun>& lines = mImpl->mModel->mVisualModel->mLines;
3699 // Need to align with the control's size as the text may contain lines
3700 // starting either with left to right text or right to left.
3701 mImpl->mLayoutEngine.Align( size,
3703 requestedNumberOfCharacters,
3704 mImpl->mModel->mHorizontalAlignment,
3706 mImpl->mModel->mAlignmentOffset,
3707 mImpl->mLayoutDirection,
3708 mImpl->mModel->mMatchSystemLanguageDirection );
3712 #if defined(DEBUG_ENABLED)
3713 std::string currentText;
3714 GetText( currentText );
3715 DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::DoRelayout [%p] mImpl->mIsTextDirectionRTL[%s] [%s]\n", this, (mImpl->mIsTextDirectionRTL)?"true":"false", currentText.c_str() );
3717 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout, view updated %s\n", ( viewUpdated ? "true" : "false" ) );
3721 void Controller::CalculateVerticalOffset( const Size& controlSize )
3723 Size layoutSize = mImpl->mModel->mVisualModel->GetLayoutSize();
3725 if( fabsf( layoutSize.height ) < Math::MACHINE_EPSILON_1000 )
3727 // Get the line height of the default font.
3728 layoutSize.height = mImpl->GetDefaultFontLineHeight();
3731 switch( mImpl->mModel->mVerticalAlignment )
3733 case VerticalAlignment::TOP:
3735 mImpl->mModel->mScrollPosition.y = 0.f;
3738 case VerticalAlignment::CENTER:
3740 mImpl->mModel->mScrollPosition.y = floorf( 0.5f * ( controlSize.height - layoutSize.height ) ); // try to avoid pixel alignment.
3743 case VerticalAlignment::BOTTOM:
3745 mImpl->mModel->mScrollPosition.y = controlSize.height - layoutSize.height;
3751 // private : Events.
3753 void Controller::ProcessModifyEvents()
3755 Vector<ModifyEvent>& events = mImpl->mModifyEvents;
3757 if( 0u == events.Count() )
3763 for( Vector<ModifyEvent>::ConstIterator it = events.Begin(),
3764 endIt = events.End();
3768 const ModifyEvent& event = *it;
3770 if( ModifyEvent::TEXT_REPLACED == event.type )
3772 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
3773 DALI_ASSERT_DEBUG( it == events.Begin() && "Unexpected TEXT_REPLACED event" );
3775 TextReplacedEvent();
3777 else if( ModifyEvent::TEXT_INSERTED == event.type )
3779 TextInsertedEvent();
3781 else if( ModifyEvent::TEXT_DELETED == event.type )
3783 // Placeholder-text cannot be deleted
3784 if( !mImpl->IsShowingPlaceholderText() )
3791 if( NULL != mImpl->mEventData )
3793 // When the text is being modified, delay cursor blinking
3794 mImpl->mEventData->mDecorator->DelayCursorBlink();
3796 // Update selection position after modifying the text
3797 mImpl->mEventData->mLeftSelectionPosition = mImpl->mEventData->mPrimaryCursorPosition;
3798 mImpl->mEventData->mRightSelectionPosition = mImpl->mEventData->mPrimaryCursorPosition;
3801 // Discard temporary text
3805 void Controller::TextReplacedEvent()
3807 // The natural size needs to be re-calculated.
3808 mImpl->mRecalculateNaturalSize = true;
3810 // The text direction needs to be updated.
3811 mImpl->mUpdateTextDirection = true;
3813 // Apply modifications to the model
3814 mImpl->mOperationsPending = ALL_OPERATIONS;
3817 void Controller::TextInsertedEvent()
3819 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextInsertedEvent" );
3821 if( NULL == mImpl->mEventData )
3826 mImpl->mEventData->mCheckScrollAmount = true;
3828 // The natural size needs to be re-calculated.
3829 mImpl->mRecalculateNaturalSize = true;
3831 // The text direction needs to be updated.
3832 mImpl->mUpdateTextDirection = true;
3834 // Apply modifications to the model; TODO - Optimize this
3835 mImpl->mOperationsPending = ALL_OPERATIONS;
3838 void Controller::TextDeletedEvent()
3840 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextDeletedEvent" );
3842 if( NULL == mImpl->mEventData )
3847 mImpl->mEventData->mCheckScrollAmount = true;
3849 // The natural size needs to be re-calculated.
3850 mImpl->mRecalculateNaturalSize = true;
3852 // The text direction needs to be updated.
3853 mImpl->mUpdateTextDirection = true;
3855 // Apply modifications to the model; TODO - Optimize this
3856 mImpl->mOperationsPending = ALL_OPERATIONS;
3859 void Controller::SelectEvent( float x, float y, bool selectAll )
3861 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SelectEvent\n" );
3863 if( NULL != mImpl->mEventData )
3867 Event event( Event::SELECT_ALL );
3868 mImpl->mEventData->mEventQueue.push_back( event );
3872 Event event( Event::SELECT );
3873 event.p2.mFloat = x;
3874 event.p3.mFloat = y;
3875 mImpl->mEventData->mEventQueue.push_back( event );
3878 mImpl->mEventData->mCheckScrollAmount = true;
3879 mImpl->mEventData->mIsLeftHandleSelected = true;
3880 mImpl->mEventData->mIsRightHandleSelected = true;
3881 mImpl->RequestRelayout();
3885 bool Controller::DeleteEvent( int keyCode )
3887 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p KeyCode : %d \n", this, keyCode );
3889 bool removed = false;
3891 if( NULL == mImpl->mEventData )
3896 // InputMethodContext is no longer handling key-events
3897 mImpl->ClearPreEditFlag();
3899 if( EventData::SELECTING == mImpl->mEventData->mState )
3901 removed = RemoveSelectedText();
3903 else if( ( mImpl->mEventData->mPrimaryCursorPosition > 0 ) && ( keyCode == Dali::DALI_KEY_BACKSPACE) )
3905 // Remove the character before the current cursor position
3906 removed = RemoveText( -1,
3908 UPDATE_INPUT_STYLE );
3910 else if( keyCode == Dali::DevelKey::DALI_KEY_DELETE )
3912 // Remove the character after the current cursor position
3913 removed = RemoveText( 0,
3915 UPDATE_INPUT_STYLE );
3920 if( ( 0u != mImpl->mModel->mLogicalModel->mText.Count() ) ||
3921 !mImpl->IsPlaceholderAvailable() )
3923 mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
3927 ShowPlaceholderText();
3929 mImpl->mEventData->mUpdateCursorPosition = true;
3930 mImpl->mEventData->mScrollAfterDelete = true;
3936 // private : Helpers.
3938 void Controller::ResetText()
3941 mImpl->mModel->mLogicalModel->mText.Clear();
3943 // We have cleared everything including the placeholder-text
3944 mImpl->PlaceholderCleared();
3946 mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
3947 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
3948 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = 0u;
3950 // Clear any previous text.
3951 mImpl->mTextUpdateInfo.mClearAll = true;
3953 // The natural size needs to be re-calculated.
3954 mImpl->mRecalculateNaturalSize = true;
3956 // The text direction needs to be updated.
3957 mImpl->mUpdateTextDirection = true;
3959 // Apply modifications to the model
3960 mImpl->mOperationsPending = ALL_OPERATIONS;
3963 void Controller::ShowPlaceholderText()
3965 if( mImpl->IsPlaceholderAvailable() )
3967 DALI_ASSERT_DEBUG( mImpl->mEventData && "No placeholder text available" );
3969 if( NULL == mImpl->mEventData )
3974 mImpl->mEventData->mIsShowingPlaceholderText = true;
3976 // Disable handles when showing place-holder text
3977 mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
3978 mImpl->mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
3979 mImpl->mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
3981 const char* text( NULL );
3984 // TODO - Switch Placeholder text when changing state
3985 if( ( EventData::INACTIVE != mImpl->mEventData->mState ) &&
3986 ( 0u != mImpl->mEventData->mPlaceholderTextActive.c_str() ) )
3988 text = mImpl->mEventData->mPlaceholderTextActive.c_str();
3989 size = mImpl->mEventData->mPlaceholderTextActive.size();
3993 text = mImpl->mEventData->mPlaceholderTextInactive.c_str();
3994 size = mImpl->mEventData->mPlaceholderTextInactive.size();
3997 mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
3998 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
4000 // Reset model for showing placeholder.
4001 mImpl->mModel->mLogicalModel->mText.Clear();
4002 mImpl->mModel->mVisualModel->SetTextColor( mImpl->mEventData->mPlaceholderTextColor );
4004 // Convert text into UTF-32
4005 Vector<Character>& utf32Characters = mImpl->mModel->mLogicalModel->mText;
4006 utf32Characters.Resize( size );
4008 // This is a bit horrible but std::string returns a (signed) char*
4009 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text );
4011 // Transform a text array encoded in utf8 into an array encoded in utf32.
4012 // It returns the actual number of characters.
4013 const Length characterCount = Utf8ToUtf32( utf8, size, utf32Characters.Begin() );
4014 utf32Characters.Resize( characterCount );
4016 // The characters to be added.
4017 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = characterCount;
4019 // Reset the cursor position
4020 mImpl->mEventData->mPrimaryCursorPosition = 0;
4022 // The natural size needs to be re-calculated.
4023 mImpl->mRecalculateNaturalSize = true;
4025 // The text direction needs to be updated.
4026 mImpl->mUpdateTextDirection = true;
4028 // Apply modifications to the model
4029 mImpl->mOperationsPending = ALL_OPERATIONS;
4031 // Update the rest of the model during size negotiation
4032 mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
4036 void Controller::ClearFontData()
4038 if( mImpl->mFontDefaults )
4040 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
4043 // Set flags to update the model.
4044 mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
4045 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
4046 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count();
4048 mImpl->mTextUpdateInfo.mClearAll = true;
4049 mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true;
4050 mImpl->mRecalculateNaturalSize = true;
4052 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
4058 UPDATE_LAYOUT_SIZE |
4063 void Controller::ClearStyleData()
4065 mImpl->mModel->mLogicalModel->mColorRuns.Clear();
4066 mImpl->mModel->mLogicalModel->ClearFontDescriptionRuns();
4069 void Controller::ResetCursorPosition( CharacterIndex cursorIndex )
4071 // Reset the cursor position
4072 if( NULL != mImpl->mEventData )
4074 mImpl->mEventData->mPrimaryCursorPosition = cursorIndex;
4076 // Update the cursor if it's in editing mode.
4077 if( EventData::IsEditingState( mImpl->mEventData->mState ) )
4079 mImpl->mEventData->mUpdateCursorPosition = true;
4084 void Controller::ResetScrollPosition()
4086 if( NULL != mImpl->mEventData )
4088 // Reset the scroll position.
4089 mImpl->mModel->mScrollPosition = Vector2::ZERO;
4090 mImpl->mEventData->mScrollAfterUpdatePosition = true;
4094 void Controller::SetControlInterface( ControlInterface* controlInterface )
4096 mImpl->mControlInterface = controlInterface;
4099 bool Controller::ShouldClearFocusOnEscape() const
4101 return mImpl->mShouldClearFocusOnEscape;
4104 // private : Private contructors & copy operator.
4106 Controller::Controller()
4109 mImpl = new Controller::Impl( NULL, NULL );
4112 Controller::Controller( ControlInterface* controlInterface )
4114 mImpl = new Controller::Impl( controlInterface, NULL );
4117 Controller::Controller( ControlInterface* controlInterface,
4118 EditableControlInterface* editableControlInterface )
4120 mImpl = new Controller::Impl( controlInterface,
4121 editableControlInterface );
4124 // The copy constructor and operator are left unimplemented.
4126 // protected : Destructor.
4128 Controller::~Controller()
4135 } // namespace Toolkit