2 * Copyright (c) 2017 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>
30 #include <dali-toolkit/internal/text/bidirectional-support.h>
31 #include <dali-toolkit/internal/text/character-set-conversion.h>
32 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
33 #include <dali-toolkit/internal/text/markup-processor.h>
34 #include <dali-toolkit/internal/text/multi-language-support.h>
35 #include <dali-toolkit/internal/text/text-controller-impl.h>
36 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
37 #include <dali-toolkit/internal/text/text-font-style.h>
42 #if defined(DEBUG_ENABLED)
43 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
46 const float MAX_FLOAT = std::numeric_limits<float>::max();
48 const std::string EMPTY_STRING("");
50 const char * const PLACEHOLDER_TEXT = "placeholderText";
51 const char * const PLACEHOLDER_TEXT_FOCUSED = "placeholderTextFocused";
52 const char * const PLACEHOLDER_COLOR = "placeholderColor";
53 const char * const PLACEHOLDER_FONT_FAMILY = "placeholderFontFamily";
54 const char * const PLACEHOLDER_FONT_STYLE = "placeholderFontStyle";
55 const char * const PLACEHOLDER_POINT_SIZE = "placeholderPointSize";
56 const char * const PLACEHOLDER_PIXEL_SIZE = "placeholderPixelSize";
58 float ConvertToEven( float value )
60 int intValue(static_cast<int>( value ));
61 return static_cast<float>(intValue % 2 == 0) ? intValue : (intValue + 1);
76 * @brief Adds a new font description run for the selected text.
78 * The new font parameters are added after the call to this method.
80 * @param[in] eventData The event data pointer.
81 * @param[in] logicalModel The logical model where to add the new font description run.
82 * @param[out] startOfSelectedText Index to the first selected character.
83 * @param[out] lengthOfSelectedText Number of selected characters.
85 FontDescriptionRun& UpdateSelectionFontStyleRun( EventData* eventData,
86 LogicalModelPtr logicalModel,
87 CharacterIndex& startOfSelectedText,
88 Length& lengthOfSelectedText )
90 const bool handlesCrossed = eventData->mLeftSelectionPosition > eventData->mRightSelectionPosition;
92 // Get start and end position of selection
93 startOfSelectedText = handlesCrossed ? eventData->mRightSelectionPosition : eventData->mLeftSelectionPosition;
94 lengthOfSelectedText = ( handlesCrossed ? eventData->mLeftSelectionPosition : eventData->mRightSelectionPosition ) - startOfSelectedText;
97 const VectorBase::SizeType numberOfRuns = logicalModel->mFontDescriptionRuns.Count();
98 logicalModel->mFontDescriptionRuns.Resize( numberOfRuns + 1u );
100 FontDescriptionRun& fontDescriptionRun = *( logicalModel->mFontDescriptionRuns.Begin() + numberOfRuns );
102 fontDescriptionRun.characterRun.characterIndex = startOfSelectedText;
103 fontDescriptionRun.characterRun.numberOfCharacters = lengthOfSelectedText;
105 // Recalculate the selection highlight as the metrics may have changed.
106 eventData->mUpdateLeftSelectionPosition = true;
107 eventData->mUpdateRightSelectionPosition = true;
108 eventData->mUpdateHighlightBox = true;
110 return fontDescriptionRun;
113 // public : Constructor.
115 ControllerPtr Controller::New()
117 return ControllerPtr( new Controller() );
120 ControllerPtr Controller::New( ControlInterface* controlInterface )
122 return ControllerPtr( new Controller( controlInterface ) );
125 ControllerPtr Controller::New( ControlInterface* controlInterface,
126 EditableControlInterface* editableControlInterface )
128 return ControllerPtr( new Controller( controlInterface,
129 editableControlInterface ) );
132 // public : Configure the text controller.
134 void Controller::EnableTextInput( DecoratorPtr decorator )
138 delete mImpl->mEventData;
139 mImpl->mEventData = NULL;
141 // Nothing else to do.
145 if( NULL == mImpl->mEventData )
147 mImpl->mEventData = new EventData( decorator );
151 void Controller::SetGlyphType( TextAbstraction::GlyphType glyphType )
153 // Metrics for bitmap & vector based glyphs are different
154 mImpl->mMetrics->SetGlyphType( glyphType );
156 // Clear the font-specific data
159 mImpl->RequestRelayout();
162 void Controller::SetMarkupProcessorEnabled( bool enable )
164 mImpl->mMarkupProcessorEnabled = enable;
167 bool Controller::IsMarkupProcessorEnabled() const
169 return mImpl->mMarkupProcessorEnabled;
172 void Controller::SetAutoScrollEnabled( bool enable )
174 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 );
176 if( mImpl->mLayoutEngine.GetLayout() == Layout::Engine::SINGLE_LINE_BOX )
180 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled for SINGLE_LINE_BOX\n" );
181 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
191 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled Disabling autoscroll\n");
192 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
199 mImpl->mIsAutoScrollEnabled = enable;
200 mImpl->RequestRelayout();
204 DALI_LOG_WARNING( "Attempted AutoScrolling on a non SINGLE_LINE_BOX, request ignored\n" );
205 mImpl->mIsAutoScrollEnabled = false;
209 bool Controller::IsAutoScrollEnabled() const
211 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::IsAutoScrollEnabled[%s]\n", mImpl->mIsAutoScrollEnabled?"true":"false" );
213 return mImpl->mIsAutoScrollEnabled;
216 CharacterDirection Controller::GetAutoScrollDirection() const
218 return mImpl->mAutoScrollDirectionRTL;
221 float Controller::GetAutoScrollLineAlignment() const
225 if( mImpl->mModel->mVisualModel &&
226 ( 0u != mImpl->mModel->mVisualModel->mLines.Count() ) )
228 offset = ( *mImpl->mModel->mVisualModel->mLines.Begin() ).alignmentOffset;
234 void Controller::SetHorizontalScrollEnabled( bool enable )
236 if( ( NULL != mImpl->mEventData ) &&
237 mImpl->mEventData->mDecorator )
239 mImpl->mEventData->mDecorator->SetHorizontalScrollEnabled( enable );
242 bool Controller::IsHorizontalScrollEnabled() const
244 if( ( NULL != mImpl->mEventData ) &&
245 mImpl->mEventData->mDecorator )
247 return mImpl->mEventData->mDecorator->IsHorizontalScrollEnabled();
253 void Controller::SetVerticalScrollEnabled( bool enable )
255 if( ( NULL != mImpl->mEventData ) &&
256 mImpl->mEventData->mDecorator )
258 if( mImpl->mEventData->mDecorator )
260 mImpl->mEventData->mDecorator->SetVerticalScrollEnabled( enable );
265 bool Controller::IsVerticalScrollEnabled() const
267 if( ( NULL != mImpl->mEventData ) &&
268 mImpl->mEventData->mDecorator )
270 return mImpl->mEventData->mDecorator->IsVerticalScrollEnabled();
276 void Controller::SetSmoothHandlePanEnabled( bool enable )
278 if( ( NULL != mImpl->mEventData ) &&
279 mImpl->mEventData->mDecorator )
281 mImpl->mEventData->mDecorator->SetSmoothHandlePanEnabled( enable );
285 bool Controller::IsSmoothHandlePanEnabled() const
287 if( ( NULL != mImpl->mEventData ) &&
288 mImpl->mEventData->mDecorator )
290 return mImpl->mEventData->mDecorator->IsSmoothHandlePanEnabled();
296 void Controller::SetMaximumNumberOfCharacters( Length maxCharacters )
298 mImpl->mMaximumNumberOfCharacters = maxCharacters;
301 int Controller::GetMaximumNumberOfCharacters()
303 return mImpl->mMaximumNumberOfCharacters;
306 void Controller::SetEnableCursorBlink( bool enable )
308 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "TextInput disabled" );
310 if( NULL != mImpl->mEventData )
312 mImpl->mEventData->mCursorBlinkEnabled = enable;
315 mImpl->mEventData->mDecorator )
317 mImpl->mEventData->mDecorator->StopCursorBlink();
322 bool Controller::GetEnableCursorBlink() const
324 if( NULL != mImpl->mEventData )
326 return mImpl->mEventData->mCursorBlinkEnabled;
332 void Controller::SetMultiLineEnabled( bool enable )
334 const Layout::Engine::Type layout = enable ? Layout::Engine::MULTI_LINE_BOX : Layout::Engine::SINGLE_LINE_BOX;
336 if( layout != mImpl->mLayoutEngine.GetLayout() )
338 // Set the layout type.
339 mImpl->mLayoutEngine.SetLayout( layout );
341 // Set the flags to redo the layout operations
342 const OperationsMask layoutOperations = static_cast<OperationsMask>( LAYOUT |
347 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | layoutOperations );
349 mImpl->RequestRelayout();
353 bool Controller::IsMultiLineEnabled() const
355 return Layout::Engine::MULTI_LINE_BOX == mImpl->mLayoutEngine.GetLayout();
358 void Controller::SetHorizontalAlignment( Layout::HorizontalAlignment alignment )
360 if( alignment != mImpl->mModel->mHorizontalAlignment )
362 // Set the alignment.
363 mImpl->mModel->mHorizontalAlignment = alignment;
365 // Set the flag to redo the alignment operation.
366 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
368 mImpl->RequestRelayout();
372 Layout::HorizontalAlignment Controller::GetHorizontalAlignment() const
374 return mImpl->mModel->mHorizontalAlignment;
377 void Controller::SetVerticalAlignment( Layout::VerticalAlignment alignment )
379 if( alignment != mImpl->mModel->mVerticalAlignment )
381 // Set the alignment.
382 mImpl->mModel->mVerticalAlignment = alignment;
384 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
386 mImpl->RequestRelayout();
390 Layout::VerticalAlignment Controller::GetVerticalAlignment() const
392 return mImpl->mModel->mVerticalAlignment;
395 void Controller::SetLineWrapMode( Layout::LineWrap::Mode lineWrapMode )
397 if( lineWrapMode != mImpl->mModel->mLineWrapMode )
399 // Set the text wrap mode.
400 mImpl->mModel->mLineWrapMode = lineWrapMode;
403 // Update Text layout for applying wrap mode
404 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
409 mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
410 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
411 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count();
414 mImpl->RequestRelayout();
418 Layout::LineWrap::Mode Controller::GetLineWrapMode() const
420 return mImpl->mModel->mLineWrapMode;
423 void Controller::SetTextElideEnabled( bool enabled )
425 mImpl->mModel->mElideEnabled = enabled;
428 bool Controller::IsTextElideEnabled() const
430 return mImpl->mModel->mElideEnabled;
433 void Controller::SetSelectionEnabled( bool enabled )
435 mImpl->mEventData->mSelectionEnabled = enabled;
438 bool Controller::IsSelectionEnabled() const
440 return mImpl->mEventData->mSelectionEnabled;
445 void Controller::SetText( const std::string& text )
447 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText\n" );
449 // Reset keyboard as text changed
450 mImpl->ResetImfManager();
452 // Remove the previously set text and style.
458 CharacterIndex lastCursorIndex = 0u;
460 if( NULL != mImpl->mEventData )
462 // If popup shown then hide it by switching to Editing state
463 if( ( EventData::SELECTING == mImpl->mEventData->mState ) ||
464 ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) ||
465 ( EventData::EDITING_WITH_GRAB_HANDLE == mImpl->mEventData->mState ) ||
466 ( EventData::EDITING_WITH_PASTE_POPUP == mImpl->mEventData->mState ) )
468 mImpl->ChangeState( EventData::EDITING );
474 mImpl->mModel->mVisualModel->SetTextColor( mImpl->mTextColor );
476 MarkupProcessData markupProcessData( mImpl->mModel->mLogicalModel->mColorRuns,
477 mImpl->mModel->mLogicalModel->mFontDescriptionRuns );
479 Length textSize = 0u;
480 const uint8_t* utf8 = NULL;
481 if( mImpl->mMarkupProcessorEnabled )
483 ProcessMarkupString( text, markupProcessData );
484 textSize = markupProcessData.markupProcessedText.size();
486 // This is a bit horrible but std::string returns a (signed) char*
487 utf8 = reinterpret_cast<const uint8_t*>( markupProcessData.markupProcessedText.c_str() );
491 textSize = text.size();
493 // This is a bit horrible but std::string returns a (signed) char*
494 utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
497 // Convert text into UTF-32
498 Vector<Character>& utf32Characters = mImpl->mModel->mLogicalModel->mText;
499 utf32Characters.Resize( textSize );
501 // Transform a text array encoded in utf8 into an array encoded in utf32.
502 // It returns the actual number of characters.
503 Length characterCount = Utf8ToUtf32( utf8, textSize, utf32Characters.Begin() );
504 utf32Characters.Resize( characterCount );
506 DALI_ASSERT_DEBUG( textSize >= characterCount && "Invalid UTF32 conversion length" );
507 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText %p UTF8 size %d, UTF32 size %d\n", this, textSize, mImpl->mModel->mLogicalModel->mText.Count() );
509 // The characters to be added.
510 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count();
512 // To reset the cursor position
513 lastCursorIndex = characterCount;
515 // Update the rest of the model during size negotiation
516 mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
518 // The natural size needs to be re-calculated.
519 mImpl->mRecalculateNaturalSize = true;
521 // Apply modifications to the model
522 mImpl->mOperationsPending = ALL_OPERATIONS;
526 ShowPlaceholderText();
529 // Resets the cursor position.
530 ResetCursorPosition( lastCursorIndex );
532 // Scrolls the text to make the cursor visible.
533 ResetScrollPosition();
535 mImpl->RequestRelayout();
537 if( NULL != mImpl->mEventData )
539 // Cancel previously queued events
540 mImpl->mEventData->mEventQueue.clear();
543 // Do this last since it provides callbacks into application code.
544 if( NULL != mImpl->mEditableControlInterface )
546 mImpl->mEditableControlInterface->TextChanged();
550 void Controller::GetText( std::string& text ) const
552 if( !mImpl->IsShowingPlaceholderText() )
554 // Retrieves the text string.
555 mImpl->GetText( 0u, text );
559 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::GetText %p empty (but showing placeholder)\n", this );
563 void Controller::SetPlaceholderText( PlaceholderType type, const std::string& text )
565 if( NULL != mImpl->mEventData )
567 if( PLACEHOLDER_TYPE_INACTIVE == type )
569 mImpl->mEventData->mPlaceholderTextInactive = text;
573 mImpl->mEventData->mPlaceholderTextActive = text;
576 // Update placeholder if there is no text
577 if( mImpl->IsShowingPlaceholderText() ||
578 ( 0u == mImpl->mModel->mLogicalModel->mText.Count() ) )
580 ShowPlaceholderText();
585 void Controller::GetPlaceholderText( PlaceholderType type, std::string& text ) const
587 if( NULL != mImpl->mEventData )
589 if( PLACEHOLDER_TYPE_INACTIVE == type )
591 text = mImpl->mEventData->mPlaceholderTextInactive;
595 text = mImpl->mEventData->mPlaceholderTextActive;
600 void Controller::UpdateAfterFontChange( const std::string& newDefaultFont )
602 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::UpdateAfterFontChange\n");
604 if( !mImpl->mFontDefaults->familyDefined ) // If user defined font then should not update when system font changes
606 DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::UpdateAfterFontChange newDefaultFont(%s)\n", newDefaultFont.c_str() );
607 mImpl->mFontDefaults->mFontDescription.family = newDefaultFont;
611 mImpl->RequestRelayout();
615 // public : Default style & Input style
617 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
619 if( NULL == mImpl->mFontDefaults )
621 mImpl->mFontDefaults = new FontDefaults();
624 mImpl->mFontDefaults->mFontDescription.family = defaultFontFamily;
625 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetDefaultFontFamily %s\n", defaultFontFamily.c_str());
626 mImpl->mFontDefaults->familyDefined = !defaultFontFamily.empty();
628 // Clear the font-specific data
631 mImpl->RequestRelayout();
634 const std::string& Controller::GetDefaultFontFamily() const
636 if( NULL != mImpl->mFontDefaults )
638 return mImpl->mFontDefaults->mFontDescription.family;
644 void Controller::SetPlaceholderFontFamily( const std::string& placeholderTextFontFamily )
646 if( NULL != mImpl->mEventData )
648 if( NULL == mImpl->mEventData->mPlaceholderFont )
650 mImpl->mEventData->mPlaceholderFont = new FontDefaults();
653 mImpl->mEventData->mPlaceholderFont->mFontDescription.family = placeholderTextFontFamily;
654 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetPlaceholderFontFamily %s\n", placeholderTextFontFamily.c_str());
655 mImpl->mEventData->mPlaceholderFont->familyDefined = !placeholderTextFontFamily.empty();
657 mImpl->RequestRelayout();
661 const std::string& Controller::GetPlaceholderFontFamily() const
663 if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
665 return mImpl->mEventData->mPlaceholderFont->mFontDescription.family;
671 void Controller::SetDefaultFontWeight( FontWeight weight )
673 if( NULL == mImpl->mFontDefaults )
675 mImpl->mFontDefaults = new FontDefaults();
678 mImpl->mFontDefaults->mFontDescription.weight = weight;
679 mImpl->mFontDefaults->weightDefined = true;
681 // Clear the font-specific data
684 mImpl->RequestRelayout();
687 bool Controller::IsDefaultFontWeightDefined() const
689 if( NULL != mImpl->mFontDefaults )
691 return mImpl->mFontDefaults->weightDefined;
697 FontWeight Controller::GetDefaultFontWeight() const
699 if( NULL != mImpl->mFontDefaults )
701 return mImpl->mFontDefaults->mFontDescription.weight;
704 return TextAbstraction::FontWeight::NORMAL;
707 void Controller::SetPlaceholderTextFontWeight( FontWeight weight )
709 if( NULL != mImpl->mEventData )
711 if( NULL == mImpl->mEventData->mPlaceholderFont )
713 mImpl->mEventData->mPlaceholderFont = new FontDefaults();
716 mImpl->mEventData->mPlaceholderFont->mFontDescription.weight = weight;
717 mImpl->mEventData->mPlaceholderFont->weightDefined = true;
719 mImpl->RequestRelayout();
723 bool Controller::IsPlaceholderTextFontWeightDefined() const
725 if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
727 return mImpl->mEventData->mPlaceholderFont->weightDefined;
732 FontWeight Controller::GetPlaceholderTextFontWeight() const
734 if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
736 return mImpl->mEventData->mPlaceholderFont->mFontDescription.weight;
739 return TextAbstraction::FontWeight::NORMAL;
742 void Controller::SetDefaultFontWidth( FontWidth width )
744 if( NULL == mImpl->mFontDefaults )
746 mImpl->mFontDefaults = new FontDefaults();
749 mImpl->mFontDefaults->mFontDescription.width = width;
750 mImpl->mFontDefaults->widthDefined = true;
752 // Clear the font-specific data
755 mImpl->RequestRelayout();
758 bool Controller::IsDefaultFontWidthDefined() const
760 if( NULL != mImpl->mFontDefaults )
762 return mImpl->mFontDefaults->widthDefined;
768 FontWidth Controller::GetDefaultFontWidth() const
770 if( NULL != mImpl->mFontDefaults )
772 return mImpl->mFontDefaults->mFontDescription.width;
775 return TextAbstraction::FontWidth::NORMAL;
778 void Controller::SetPlaceholderTextFontWidth( FontWidth width )
780 if( NULL != mImpl->mEventData )
782 if( NULL == mImpl->mEventData->mPlaceholderFont )
784 mImpl->mEventData->mPlaceholderFont = new FontDefaults();
787 mImpl->mEventData->mPlaceholderFont->mFontDescription.width = width;
788 mImpl->mEventData->mPlaceholderFont->widthDefined = true;
790 mImpl->RequestRelayout();
794 bool Controller::IsPlaceholderTextFontWidthDefined() const
796 if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
798 return mImpl->mEventData->mPlaceholderFont->widthDefined;
803 FontWidth Controller::GetPlaceholderTextFontWidth() const
805 if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
807 return mImpl->mEventData->mPlaceholderFont->mFontDescription.width;
810 return TextAbstraction::FontWidth::NORMAL;
813 void Controller::SetDefaultFontSlant( FontSlant slant )
815 if( NULL == mImpl->mFontDefaults )
817 mImpl->mFontDefaults = new FontDefaults();
820 mImpl->mFontDefaults->mFontDescription.slant = slant;
821 mImpl->mFontDefaults->slantDefined = true;
823 // Clear the font-specific data
826 mImpl->RequestRelayout();
829 bool Controller::IsDefaultFontSlantDefined() const
831 if( NULL != mImpl->mFontDefaults )
833 return mImpl->mFontDefaults->slantDefined;
838 FontSlant Controller::GetDefaultFontSlant() const
840 if( NULL != mImpl->mFontDefaults )
842 return mImpl->mFontDefaults->mFontDescription.slant;
845 return TextAbstraction::FontSlant::NORMAL;
848 void Controller::SetPlaceholderTextFontSlant( FontSlant slant )
850 if( NULL != mImpl->mEventData )
852 if( NULL == mImpl->mEventData->mPlaceholderFont )
854 mImpl->mEventData->mPlaceholderFont = new FontDefaults();
857 mImpl->mEventData->mPlaceholderFont->mFontDescription.slant = slant;
858 mImpl->mEventData->mPlaceholderFont->slantDefined = true;
860 mImpl->RequestRelayout();
864 bool Controller::IsPlaceholderTextFontSlantDefined() const
866 if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
868 return mImpl->mEventData->mPlaceholderFont->slantDefined;
873 FontSlant Controller::GetPlaceholderTextFontSlant() const
875 if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
877 return mImpl->mEventData->mPlaceholderFont->mFontDescription.slant;
880 return TextAbstraction::FontSlant::NORMAL;
883 void Controller::SetDefaultFontSize( float fontSize, FontSizeType type )
885 if( NULL == mImpl->mFontDefaults )
887 mImpl->mFontDefaults = new FontDefaults();
894 mImpl->mFontDefaults->mDefaultPointSize = fontSize;
895 mImpl->mFontDefaults->sizeDefined = true;
900 // Point size = Pixel size * 72.f / DPI
901 unsigned int horizontalDpi = 0u;
902 unsigned int verticalDpi = 0u;
903 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
904 fontClient.GetDpi( horizontalDpi, verticalDpi );
906 mImpl->mFontDefaults->mDefaultPointSize = ( fontSize * 72.f ) / static_cast< float >( horizontalDpi );
907 mImpl->mFontDefaults->sizeDefined = true;
912 // Clear the font-specific data
915 mImpl->RequestRelayout();
918 float Controller::GetDefaultFontSize( FontSizeType type ) const
921 if( NULL != mImpl->mFontDefaults )
927 value = mImpl->mFontDefaults->mDefaultPointSize;
932 // Pixel size = Point size * DPI / 72.f
933 unsigned int horizontalDpi = 0u;
934 unsigned int verticalDpi = 0u;
935 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
936 fontClient.GetDpi( horizontalDpi, verticalDpi );
938 value = mImpl->mFontDefaults->mDefaultPointSize * static_cast< float >( horizontalDpi ) / 72.f;
948 void Controller::SetPlaceholderTextFontSize( float fontSize, FontSizeType type )
950 if( NULL != mImpl->mEventData )
952 if( NULL == mImpl->mEventData->mPlaceholderFont )
954 mImpl->mEventData->mPlaceholderFont = new FontDefaults();
961 mImpl->mEventData->mPlaceholderFont->mDefaultPointSize = fontSize;
962 mImpl->mEventData->mPlaceholderFont->sizeDefined = true;
963 mImpl->mEventData->mIsPlaceholderPixelSize = false; // Font size flag
968 // Point size = Pixel size * 72.f / DPI
969 unsigned int horizontalDpi = 0u;
970 unsigned int verticalDpi = 0u;
971 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
972 fontClient.GetDpi( horizontalDpi, verticalDpi );
974 mImpl->mEventData->mPlaceholderFont->mDefaultPointSize = ( fontSize * 72.f ) / static_cast< float >( horizontalDpi );
975 mImpl->mEventData->mPlaceholderFont->sizeDefined = true;
976 mImpl->mEventData->mIsPlaceholderPixelSize = true; // Font size flag
981 mImpl->RequestRelayout();
985 float Controller::GetPlaceholderTextFontSize( FontSizeType type ) const
988 if( NULL != mImpl->mEventData )
994 if( NULL != mImpl->mEventData->mPlaceholderFont )
996 value = mImpl->mEventData->mPlaceholderFont->mDefaultPointSize;
1000 // If the placeholder text font size is not set, then return the default font size.
1001 value = GetDefaultFontSize( POINT_SIZE );
1007 if( NULL != mImpl->mEventData->mPlaceholderFont )
1009 // Pixel size = Point size * DPI / 72.f
1010 unsigned int horizontalDpi = 0u;
1011 unsigned int verticalDpi = 0u;
1012 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1013 fontClient.GetDpi( horizontalDpi, verticalDpi );
1015 value = mImpl->mEventData->mPlaceholderFont->mDefaultPointSize * static_cast< float >( horizontalDpi ) / 72.f;
1019 // If the placeholder text font size is not set, then return the default font size.
1020 value = GetDefaultFontSize( PIXEL_SIZE );
1031 void Controller::SetDefaultColor( const Vector4& color )
1033 mImpl->mTextColor = color;
1035 if( !mImpl->IsShowingPlaceholderText() )
1037 mImpl->mModel->mVisualModel->SetTextColor( color );
1039 mImpl->RequestRelayout();
1043 const Vector4& Controller::GetDefaultColor() const
1045 return mImpl->mTextColor;
1048 void Controller::SetPlaceholderTextColor( const Vector4& textColor )
1050 if( NULL != mImpl->mEventData )
1052 mImpl->mEventData->mPlaceholderTextColor = textColor;
1055 if( mImpl->IsShowingPlaceholderText() )
1057 mImpl->mModel->mVisualModel->SetTextColor( textColor );
1058 mImpl->RequestRelayout();
1062 const Vector4& Controller::GetPlaceholderTextColor() const
1064 if( NULL != mImpl->mEventData )
1066 return mImpl->mEventData->mPlaceholderTextColor;
1069 return Color::BLACK;
1072 void Controller::SetShadowOffset( const Vector2& shadowOffset )
1074 mImpl->mModel->mVisualModel->SetShadowOffset( shadowOffset );
1076 mImpl->RequestRelayout();
1079 const Vector2& Controller::GetShadowOffset() const
1081 return mImpl->mModel->mVisualModel->GetShadowOffset();
1084 void Controller::SetShadowColor( const Vector4& shadowColor )
1086 mImpl->mModel->mVisualModel->SetShadowColor( shadowColor );
1088 mImpl->RequestRelayout();
1091 const Vector4& Controller::GetShadowColor() const
1093 return mImpl->mModel->mVisualModel->GetShadowColor();
1096 void Controller::SetUnderlineColor( const Vector4& color )
1098 mImpl->mModel->mVisualModel->SetUnderlineColor( color );
1100 mImpl->RequestRelayout();
1103 const Vector4& Controller::GetUnderlineColor() const
1105 return mImpl->mModel->mVisualModel->GetUnderlineColor();
1108 void Controller::SetUnderlineEnabled( bool enabled )
1110 mImpl->mModel->mVisualModel->SetUnderlineEnabled( enabled );
1112 mImpl->RequestRelayout();
1115 bool Controller::IsUnderlineEnabled() const
1117 return mImpl->mModel->mVisualModel->IsUnderlineEnabled();
1120 void Controller::SetUnderlineHeight( float height )
1122 mImpl->mModel->mVisualModel->SetUnderlineHeight( height );
1124 mImpl->RequestRelayout();
1127 float Controller::GetUnderlineHeight() const
1129 return mImpl->mModel->mVisualModel->GetUnderlineHeight();
1132 void Controller::SetOutlineColor( const Vector4& color )
1134 mImpl->mModel->mVisualModel->SetOutlineColor( color );
1136 mImpl->RequestRelayout();
1139 const Vector4& Controller::GetOutlineColor() const
1141 return mImpl->mModel->mVisualModel->GetOutlineColor();
1144 void Controller::SetOutlineWidth( float width )
1146 mImpl->mModel->mVisualModel->SetOutlineWidth( width );
1148 mImpl->RequestRelayout();
1151 float Controller::GetOutlineWidth() const
1153 return mImpl->mModel->mVisualModel->GetOutlineWidth();
1156 void Controller::SetDefaultEmbossProperties( const std::string& embossProperties )
1158 if( NULL == mImpl->mEmbossDefaults )
1160 mImpl->mEmbossDefaults = new EmbossDefaults();
1163 mImpl->mEmbossDefaults->properties = embossProperties;
1166 const std::string& Controller::GetDefaultEmbossProperties() const
1168 if( NULL != mImpl->mEmbossDefaults )
1170 return mImpl->mEmbossDefaults->properties;
1173 return EMPTY_STRING;
1176 void Controller::SetDefaultOutlineProperties( const std::string& outlineProperties )
1178 if( NULL == mImpl->mOutlineDefaults )
1180 mImpl->mOutlineDefaults = new OutlineDefaults();
1183 mImpl->mOutlineDefaults->properties = outlineProperties;
1186 const std::string& Controller::GetDefaultOutlineProperties() const
1188 if( NULL != mImpl->mOutlineDefaults )
1190 return mImpl->mOutlineDefaults->properties;
1193 return EMPTY_STRING;
1196 void Controller::SetDefaultLineSpacing( float lineSpacing )
1198 //TODO finish implementation
1199 mImpl->mLayoutEngine.SetDefaultLineSpacing( lineSpacing );
1202 float Controller::GetDefaultLineSpacing() const
1204 return mImpl->mLayoutEngine.GetDefaultLineSpacing();
1207 void Controller::SetInputColor( const Vector4& color )
1209 if( NULL != mImpl->mEventData )
1211 mImpl->mEventData->mInputStyle.textColor = color;
1212 mImpl->mEventData->mInputStyle.isDefaultColor = false;
1214 if( EventData::SELECTING == mImpl->mEventData->mState )
1216 const bool handlesCrossed = mImpl->mEventData->mLeftSelectionPosition > mImpl->mEventData->mRightSelectionPosition;
1218 // Get start and end position of selection
1219 const CharacterIndex startOfSelectedText = handlesCrossed ? mImpl->mEventData->mRightSelectionPosition : mImpl->mEventData->mLeftSelectionPosition;
1220 const Length lengthOfSelectedText = ( handlesCrossed ? mImpl->mEventData->mLeftSelectionPosition : mImpl->mEventData->mRightSelectionPosition ) - startOfSelectedText;
1222 // Add the color run.
1223 const VectorBase::SizeType numberOfRuns = mImpl->mModel->mLogicalModel->mColorRuns.Count();
1224 mImpl->mModel->mLogicalModel->mColorRuns.Resize( numberOfRuns + 1u );
1226 ColorRun& colorRun = *( mImpl->mModel->mLogicalModel->mColorRuns.Begin() + numberOfRuns );
1227 colorRun.color = color;
1228 colorRun.characterRun.characterIndex = startOfSelectedText;
1229 colorRun.characterRun.numberOfCharacters = lengthOfSelectedText;
1231 // Request to relayout.
1232 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | COLOR );
1233 mImpl->RequestRelayout();
1235 mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1236 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1237 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText;
1242 const Vector4& Controller::GetInputColor() const
1244 if( NULL != mImpl->mEventData )
1246 return mImpl->mEventData->mInputStyle.textColor;
1249 // Return the default text's color if there is no EventData.
1250 return mImpl->mTextColor;
1254 void Controller::SetInputFontFamily( const std::string& fontFamily )
1256 if( NULL != mImpl->mEventData )
1258 mImpl->mEventData->mInputStyle.familyName = fontFamily;
1259 mImpl->mEventData->mInputStyle.isFamilyDefined = true;
1261 if( EventData::SELECTING == mImpl->mEventData->mState )
1263 CharacterIndex startOfSelectedText = 0u;
1264 Length lengthOfSelectedText = 0u;
1265 FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
1266 mImpl->mModel->mLogicalModel,
1267 startOfSelectedText,
1268 lengthOfSelectedText );
1270 fontDescriptionRun.familyLength = fontFamily.size();
1271 fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
1272 memcpy( fontDescriptionRun.familyName, fontFamily.c_str(), fontDescriptionRun.familyLength );
1273 fontDescriptionRun.familyDefined = true;
1275 // The memory allocated for the font family name is freed when the font description is removed from the logical model.
1277 // Request to relayout.
1278 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1283 UPDATE_LAYOUT_SIZE |
1286 mImpl->mRecalculateNaturalSize = true;
1287 mImpl->RequestRelayout();
1289 mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1290 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1291 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText;
1293 // As the font changes, recalculate the handle positions is needed.
1294 mImpl->mEventData->mUpdateLeftSelectionPosition = true;
1295 mImpl->mEventData->mUpdateRightSelectionPosition = true;
1296 mImpl->mEventData->mUpdateHighlightBox = true;
1297 mImpl->mEventData->mScrollAfterUpdatePosition = true;
1302 const std::string& Controller::GetInputFontFamily() const
1304 if( NULL != mImpl->mEventData )
1306 return mImpl->mEventData->mInputStyle.familyName;
1309 // Return the default font's family if there is no EventData.
1310 return GetDefaultFontFamily();
1313 void Controller::SetInputFontWeight( FontWeight weight )
1315 if( NULL != mImpl->mEventData )
1317 mImpl->mEventData->mInputStyle.weight = weight;
1318 mImpl->mEventData->mInputStyle.isWeightDefined = true;
1320 if( EventData::SELECTING == mImpl->mEventData->mState )
1322 CharacterIndex startOfSelectedText = 0u;
1323 Length lengthOfSelectedText = 0u;
1324 FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
1325 mImpl->mModel->mLogicalModel,
1326 startOfSelectedText,
1327 lengthOfSelectedText );
1329 fontDescriptionRun.weight = weight;
1330 fontDescriptionRun.weightDefined = true;
1332 // Request to relayout.
1333 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1338 UPDATE_LAYOUT_SIZE |
1341 mImpl->mRecalculateNaturalSize = true;
1342 mImpl->RequestRelayout();
1344 mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1345 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1346 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText;
1348 // As the font might change, recalculate the handle positions is needed.
1349 mImpl->mEventData->mUpdateLeftSelectionPosition = true;
1350 mImpl->mEventData->mUpdateRightSelectionPosition = true;
1351 mImpl->mEventData->mUpdateHighlightBox = true;
1352 mImpl->mEventData->mScrollAfterUpdatePosition = true;
1357 bool Controller::IsInputFontWeightDefined() const
1359 bool defined = false;
1361 if( NULL != mImpl->mEventData )
1363 defined = mImpl->mEventData->mInputStyle.isWeightDefined;
1369 FontWeight Controller::GetInputFontWeight() const
1371 if( NULL != mImpl->mEventData )
1373 return mImpl->mEventData->mInputStyle.weight;
1376 return GetDefaultFontWeight();
1379 void Controller::SetInputFontWidth( FontWidth width )
1381 if( NULL != mImpl->mEventData )
1383 mImpl->mEventData->mInputStyle.width = width;
1384 mImpl->mEventData->mInputStyle.isWidthDefined = true;
1386 if( EventData::SELECTING == mImpl->mEventData->mState )
1388 CharacterIndex startOfSelectedText = 0u;
1389 Length lengthOfSelectedText = 0u;
1390 FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
1391 mImpl->mModel->mLogicalModel,
1392 startOfSelectedText,
1393 lengthOfSelectedText );
1395 fontDescriptionRun.width = width;
1396 fontDescriptionRun.widthDefined = true;
1398 // Request to relayout.
1399 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1404 UPDATE_LAYOUT_SIZE |
1407 mImpl->mRecalculateNaturalSize = true;
1408 mImpl->RequestRelayout();
1410 mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1411 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1412 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText;
1414 // As the font might change, recalculate the handle positions is needed.
1415 mImpl->mEventData->mUpdateLeftSelectionPosition = true;
1416 mImpl->mEventData->mUpdateRightSelectionPosition = true;
1417 mImpl->mEventData->mUpdateHighlightBox = true;
1418 mImpl->mEventData->mScrollAfterUpdatePosition = true;
1423 bool Controller::IsInputFontWidthDefined() const
1425 bool defined = false;
1427 if( NULL != mImpl->mEventData )
1429 defined = mImpl->mEventData->mInputStyle.isWidthDefined;
1435 FontWidth Controller::GetInputFontWidth() const
1437 if( NULL != mImpl->mEventData )
1439 return mImpl->mEventData->mInputStyle.width;
1442 return GetDefaultFontWidth();
1445 void Controller::SetInputFontSlant( FontSlant slant )
1447 if( NULL != mImpl->mEventData )
1449 mImpl->mEventData->mInputStyle.slant = slant;
1450 mImpl->mEventData->mInputStyle.isSlantDefined = true;
1452 if( EventData::SELECTING == mImpl->mEventData->mState )
1454 CharacterIndex startOfSelectedText = 0u;
1455 Length lengthOfSelectedText = 0u;
1456 FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
1457 mImpl->mModel->mLogicalModel,
1458 startOfSelectedText,
1459 lengthOfSelectedText );
1461 fontDescriptionRun.slant = slant;
1462 fontDescriptionRun.slantDefined = true;
1464 // Request to relayout.
1465 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1470 UPDATE_LAYOUT_SIZE |
1473 mImpl->mRecalculateNaturalSize = true;
1474 mImpl->RequestRelayout();
1476 mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1477 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1478 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText;
1480 // As the font might change, recalculate the handle positions is needed.
1481 mImpl->mEventData->mUpdateLeftSelectionPosition = true;
1482 mImpl->mEventData->mUpdateRightSelectionPosition = true;
1483 mImpl->mEventData->mUpdateHighlightBox = true;
1484 mImpl->mEventData->mScrollAfterUpdatePosition = true;
1489 bool Controller::IsInputFontSlantDefined() const
1491 bool defined = false;
1493 if( NULL != mImpl->mEventData )
1495 defined = mImpl->mEventData->mInputStyle.isSlantDefined;
1501 FontSlant Controller::GetInputFontSlant() const
1503 if( NULL != mImpl->mEventData )
1505 return mImpl->mEventData->mInputStyle.slant;
1508 return GetDefaultFontSlant();
1511 void Controller::SetInputFontPointSize( float size )
1513 if( NULL != mImpl->mEventData )
1515 mImpl->mEventData->mInputStyle.size = size;
1516 mImpl->mEventData->mInputStyle.isSizeDefined = true;
1518 if( EventData::SELECTING == mImpl->mEventData->mState )
1520 CharacterIndex startOfSelectedText = 0u;
1521 Length lengthOfSelectedText = 0u;
1522 FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData,
1523 mImpl->mModel->mLogicalModel,
1524 startOfSelectedText,
1525 lengthOfSelectedText );
1527 fontDescriptionRun.size = static_cast<PointSize26Dot6>( size * 64.f );
1528 fontDescriptionRun.sizeDefined = true;
1530 // Request to relayout.
1531 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1536 UPDATE_LAYOUT_SIZE |
1539 mImpl->mRecalculateNaturalSize = true;
1540 mImpl->RequestRelayout();
1542 mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1543 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1544 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText;
1546 // As the font might change, recalculate the handle positions is needed.
1547 mImpl->mEventData->mUpdateLeftSelectionPosition = true;
1548 mImpl->mEventData->mUpdateRightSelectionPosition = true;
1549 mImpl->mEventData->mUpdateHighlightBox = true;
1550 mImpl->mEventData->mScrollAfterUpdatePosition = true;
1555 float Controller::GetInputFontPointSize() const
1557 if( NULL != mImpl->mEventData )
1559 return mImpl->mEventData->mInputStyle.size;
1562 // Return the default font's point size if there is no EventData.
1563 return GetDefaultFontSize( Text::Controller::POINT_SIZE );
1566 void Controller::SetInputLineSpacing( float lineSpacing )
1568 if( NULL != mImpl->mEventData )
1570 mImpl->mEventData->mInputStyle.lineSpacing = lineSpacing;
1571 mImpl->mEventData->mInputStyle.isLineSpacingDefined = true;
1575 float Controller::GetInputLineSpacing() const
1577 if( NULL != mImpl->mEventData )
1579 return mImpl->mEventData->mInputStyle.lineSpacing;
1585 void Controller::SetInputShadowProperties( const std::string& shadowProperties )
1587 if( NULL != mImpl->mEventData )
1589 mImpl->mEventData->mInputStyle.shadowProperties = shadowProperties;
1593 const std::string& Controller::GetInputShadowProperties() const
1595 if( NULL != mImpl->mEventData )
1597 return mImpl->mEventData->mInputStyle.shadowProperties;
1600 return EMPTY_STRING;
1603 void Controller::SetInputUnderlineProperties( const std::string& underlineProperties )
1605 if( NULL != mImpl->mEventData )
1607 mImpl->mEventData->mInputStyle.underlineProperties = underlineProperties;
1611 const std::string& Controller::GetInputUnderlineProperties() const
1613 if( NULL != mImpl->mEventData )
1615 return mImpl->mEventData->mInputStyle.underlineProperties;
1618 return EMPTY_STRING;
1621 void Controller::SetInputEmbossProperties( const std::string& embossProperties )
1623 if( NULL != mImpl->mEventData )
1625 mImpl->mEventData->mInputStyle.embossProperties = embossProperties;
1629 const std::string& Controller::GetInputEmbossProperties() const
1631 if( NULL != mImpl->mEventData )
1633 return mImpl->mEventData->mInputStyle.embossProperties;
1636 return GetDefaultEmbossProperties();
1639 void Controller::SetInputOutlineProperties( const std::string& outlineProperties )
1641 if( NULL != mImpl->mEventData )
1643 mImpl->mEventData->mInputStyle.outlineProperties = outlineProperties;
1647 const std::string& Controller::GetInputOutlineProperties() const
1649 if( NULL != mImpl->mEventData )
1651 return mImpl->mEventData->mInputStyle.outlineProperties;
1654 return GetDefaultOutlineProperties();
1657 void Controller::SetInputModePassword( bool passwordInput )
1659 if( NULL != mImpl->mEventData )
1661 mImpl->mEventData->mPasswordInput = passwordInput;
1665 bool Controller::IsInputModePassword()
1667 if( NULL != mImpl->mEventData )
1669 return mImpl->mEventData->mPasswordInput;
1674 void Controller::SetNoTextDoubleTapAction( NoTextTap::Action action )
1676 if( NULL != mImpl->mEventData )
1678 mImpl->mEventData->mDoubleTapAction = action;
1682 Controller::NoTextTap::Action Controller::GetNoTextDoubleTapAction() const
1684 NoTextTap::Action action = NoTextTap::NO_ACTION;
1686 if( NULL != mImpl->mEventData )
1688 action = mImpl->mEventData->mDoubleTapAction;
1694 void Controller::SetNoTextLongPressAction( NoTextTap::Action action )
1696 if( NULL != mImpl->mEventData )
1698 mImpl->mEventData->mLongPressAction = action;
1702 Controller::NoTextTap::Action Controller::GetNoTextLongPressAction() const
1704 NoTextTap::Action action = NoTextTap::NO_ACTION;
1706 if( NULL != mImpl->mEventData )
1708 action = mImpl->mEventData->mLongPressAction;
1714 bool Controller::IsUnderlineSetByString()
1716 return mImpl->mUnderlineSetByString;
1719 void Controller::UnderlineSetByString( bool setByString )
1721 mImpl->mUnderlineSetByString = setByString;
1724 bool Controller::IsShadowSetByString()
1726 return mImpl->mShadowSetByString;
1729 void Controller::ShadowSetByString( bool setByString )
1731 mImpl->mShadowSetByString = setByString;
1734 bool Controller::IsFontStyleSetByString()
1736 return mImpl->mFontStyleSetByString;
1739 void Controller::FontStyleSetByString( bool setByString )
1741 mImpl->mFontStyleSetByString = setByString;
1744 // public : Queries & retrieves.
1746 Layout::Engine& Controller::GetLayoutEngine()
1748 return mImpl->mLayoutEngine;
1751 View& Controller::GetView()
1753 return mImpl->mView;
1756 Vector3 Controller::GetNaturalSize()
1758 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetNaturalSize\n" );
1759 Vector3 naturalSize;
1761 // Make sure the model is up-to-date before layouting
1762 ProcessModifyEvents();
1764 if( mImpl->mRecalculateNaturalSize )
1766 // Operations that can be done only once until the text changes.
1767 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
1774 GET_GLYPH_METRICS );
1776 // Set the update info to relayout the whole text.
1777 mImpl->mTextUpdateInfo.mParagraphCharacterIndex = 0u;
1778 mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters = mImpl->mModel->mLogicalModel->mText.Count();
1780 // Make sure the model is up-to-date before layouting
1781 mImpl->UpdateModel( onlyOnceOperations );
1783 // Layout the text for the new width.
1784 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | LAYOUT | REORDER );
1786 // Store the actual control's size to restore later.
1787 const Size actualControlSize = mImpl->mModel->mVisualModel->mControlSize;
1789 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
1790 static_cast<OperationsMask>( onlyOnceOperations |
1792 naturalSize.GetVectorXY() );
1794 // Do not do again the only once operations.
1795 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1797 // Do the size related operations again.
1798 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
1801 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1803 // Stores the natural size to avoid recalculate it again
1804 // unless the text/style changes.
1805 mImpl->mModel->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
1807 mImpl->mRecalculateNaturalSize = false;
1809 // Clear the update info. This info will be set the next time the text is updated.
1810 mImpl->mTextUpdateInfo.Clear();
1812 // Restore the actual control's size.
1813 mImpl->mModel->mVisualModel->mControlSize = actualControlSize;
1815 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize calculated %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
1819 naturalSize = mImpl->mModel->mVisualModel->GetNaturalSize();
1821 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize cached %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
1824 naturalSize.x = ConvertToEven( naturalSize.x );
1825 naturalSize.y = ConvertToEven( naturalSize.y );
1830 float Controller::GetHeightForWidth( float width )
1832 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetHeightForWidth %p width %f\n", this, width );
1833 // Make sure the model is up-to-date before layouting
1834 ProcessModifyEvents();
1837 if( fabsf( width - mImpl->mModel->mVisualModel->mControlSize.width ) > Math::MACHINE_EPSILON_1000 ||
1838 mImpl->mTextUpdateInfo.mFullRelayoutNeeded ||
1839 mImpl->mTextUpdateInfo.mClearAll )
1841 // Operations that can be done only once until the text changes.
1842 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
1849 GET_GLYPH_METRICS );
1851 // Set the update info to relayout the whole text.
1852 mImpl->mTextUpdateInfo.mParagraphCharacterIndex = 0u;
1853 mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters = mImpl->mModel->mLogicalModel->mText.Count();
1855 // Make sure the model is up-to-date before layouting
1856 mImpl->UpdateModel( onlyOnceOperations );
1859 // Layout the text for the new width.
1860 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | LAYOUT );
1862 // Store the actual control's width.
1863 const float actualControlWidth = mImpl->mModel->mVisualModel->mControlSize.width;
1865 DoRelayout( Size( width, MAX_FLOAT ),
1866 static_cast<OperationsMask>( onlyOnceOperations |
1870 // Do not do again the only once operations.
1871 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1873 // Do the size related operations again.
1874 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
1878 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1880 // Clear the update info. This info will be set the next time the text is updated.
1881 mImpl->mTextUpdateInfo.Clear();
1883 // Restore the actual control's width.
1884 mImpl->mModel->mVisualModel->mControlSize.width = actualControlWidth;
1886 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth calculated %f\n", layoutSize.height );
1890 layoutSize = mImpl->mModel->mVisualModel->GetLayoutSize();
1891 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth cached %f\n", layoutSize.height );
1894 return layoutSize.height;
1897 int Controller::GetLineCount( float width )
1899 GetHeightForWidth( width );
1900 int numberofLines = mImpl->mModel->GetNumberOfLines();
1901 return numberofLines;
1904 const ModelInterface* const Controller::GetTextModel() const
1906 return mImpl->mModel.Get();
1909 float Controller::GetScrollAmountByUserInput()
1911 float scrollAmount = 0.0f;
1913 if (NULL != mImpl->mEventData && mImpl->mEventData->mCheckScrollAmount)
1915 scrollAmount = mImpl->mModel->mScrollPosition.y - mImpl->mModel->mScrollPositionLast.y;
1916 mImpl->mEventData->mCheckScrollAmount = false;
1918 return scrollAmount;
1921 bool Controller::GetTextScrollInfo( float& scrollPosition, float& controlHeight, float& layoutHeight )
1923 const Vector2& layout = mImpl->mModel->mVisualModel->GetLayoutSize();
1926 controlHeight = mImpl->mModel->mVisualModel->mControlSize.height;
1927 layoutHeight = layout.height;
1928 scrollPosition = mImpl->mModel->mScrollPosition.y;
1929 isScrolled = !Equals( mImpl->mModel->mScrollPosition.y, mImpl->mModel->mScrollPositionLast.y, Math::MACHINE_EPSILON_1 );
1933 void Controller::SetHiddenInputOption(const Property::Map& options )
1935 if( NULL == mImpl->mHiddenInput )
1937 mImpl->mHiddenInput = new HiddenText( this );
1939 mImpl->mHiddenInput->SetProperties(options);
1942 void Controller::GetHiddenInputOption(Property::Map& options )
1944 if( NULL != mImpl->mHiddenInput )
1946 mImpl->mHiddenInput->GetProperties(options);
1950 void Controller::SetPlaceholderProperty( const Property::Map& map )
1952 const Property::Map::SizeType count = map.Count();
1954 for( Property::Map::SizeType position = 0; position < count; ++position )
1956 KeyValuePair keyValue = map.GetKeyValue( position );
1957 Property::Key& key = keyValue.first;
1958 Property::Value& value = keyValue.second;
1960 if( key == PLACEHOLDER_TEXT )
1962 std::string text = "";
1964 SetPlaceholderText( Controller::PLACEHOLDER_TYPE_INACTIVE, text );
1966 else if( key == PLACEHOLDER_TEXT_FOCUSED )
1968 std::string text = "";
1970 SetPlaceholderText( Controller::PLACEHOLDER_TYPE_ACTIVE, text );
1972 else if( key == PLACEHOLDER_COLOR )
1975 value.Get( textColor );
1976 if( GetPlaceholderTextColor() != textColor )
1978 SetPlaceholderTextColor( textColor );
1981 else if( key == PLACEHOLDER_FONT_FAMILY )
1983 std::string fontFamily = "";
1984 value.Get( fontFamily );
1985 SetPlaceholderFontFamily( fontFamily );
1987 else if( key == PLACEHOLDER_FONT_STYLE )
1989 SetFontStyleProperty( this, value, Text::FontStyle::PLACEHOLDER );
1991 else if( key == PLACEHOLDER_POINT_SIZE )
1994 value.Get( pointSize );
1995 if( !Equals( GetPlaceholderTextFontSize( Text::Controller::POINT_SIZE ), pointSize ) )
1997 SetPlaceholderTextFontSize( pointSize, Text::Controller::POINT_SIZE );
2000 else if( key == PLACEHOLDER_PIXEL_SIZE )
2003 value.Get( pixelSize );
2004 if( !Equals( GetPlaceholderTextFontSize( Text::Controller::PIXEL_SIZE ), pixelSize ) )
2006 SetPlaceholderTextFontSize( pixelSize, Text::Controller::PIXEL_SIZE );
2012 void Controller::GetPlaceholderProperty( Property::Map& map )
2014 if( NULL != mImpl->mEventData )
2016 if( !mImpl->mEventData->mPlaceholderTextActive.empty() )
2018 map[ PLACEHOLDER_TEXT_FOCUSED ] = mImpl->mEventData->mPlaceholderTextActive;
2020 if( !mImpl->mEventData->mPlaceholderTextInactive.empty() )
2022 map[ PLACEHOLDER_TEXT ] = mImpl->mEventData->mPlaceholderTextInactive;
2025 map[ PLACEHOLDER_COLOR ] = mImpl->mEventData->mPlaceholderTextColor;
2026 map[ PLACEHOLDER_FONT_FAMILY ] = GetPlaceholderFontFamily();
2028 Property::Value fontStyleMapGet;
2029 GetFontStyleProperty( this, fontStyleMapGet, Text::FontStyle::PLACEHOLDER );
2030 map[ PLACEHOLDER_FONT_STYLE ] = fontStyleMapGet;
2032 // Choose font size : POINT_SIZE or PIXEL_SIZE
2033 if( !mImpl->mEventData->mIsPlaceholderPixelSize )
2035 map[ PLACEHOLDER_POINT_SIZE ] = GetPlaceholderTextFontSize( Text::Controller::POINT_SIZE );
2039 map[ PLACEHOLDER_PIXEL_SIZE ] = GetPlaceholderTextFontSize( Text::Controller::PIXEL_SIZE );
2044 // public : Relayout.
2046 Controller::UpdateTextType Controller::Relayout( const Size& size )
2048 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::Relayout %p size %f,%f, autoScroll[%s]\n", this, size.width, size.height, mImpl->mIsAutoScrollEnabled ?"true":"false" );
2050 UpdateTextType updateTextType = NONE_UPDATED;
2052 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
2054 if( 0u != mImpl->mModel->mVisualModel->mGlyphPositions.Count() )
2056 mImpl->mModel->mVisualModel->mGlyphPositions.Clear();
2057 updateTextType = MODEL_UPDATED;
2060 // Clear the update info. This info will be set the next time the text is updated.
2061 mImpl->mTextUpdateInfo.Clear();
2063 // Not worth to relayout if width or height is equal to zero.
2064 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout (skipped)\n" );
2066 return updateTextType;
2069 // Whether a new size has been set.
2070 const bool newSize = ( size != mImpl->mModel->mVisualModel->mControlSize );
2074 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "new size (previous size %f,%f)\n", mImpl->mModel->mVisualModel->mControlSize.width, mImpl->mModel->mVisualModel->mControlSize.height );
2076 // Layout operations that need to be done if the size changes.
2077 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
2080 UPDATE_LAYOUT_SIZE |
2082 // Set the update info to relayout the whole text.
2083 mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true;
2084 mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
2086 // Store the size used to layout the text.
2087 mImpl->mModel->mVisualModel->mControlSize = size;
2090 // Whether there are modify events.
2091 if( 0u != mImpl->mModifyEvents.Count() )
2093 // Style operations that need to be done if the text is modified.
2094 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
2098 // Make sure the model is up-to-date before layouting.
2099 ProcessModifyEvents();
2100 bool updated = mImpl->UpdateModel( mImpl->mOperationsPending );
2104 updated = DoRelayout( size,
2105 mImpl->mOperationsPending,
2106 layoutSize ) || updated;
2110 updateTextType = MODEL_UPDATED;
2113 // Do not re-do any operation until something changes.
2114 mImpl->mOperationsPending = NO_OPERATION;
2115 mImpl->mModel->mScrollPositionLast = mImpl->mModel->mScrollPosition;
2117 // Whether the text control is editable
2118 const bool isEditable = NULL != mImpl->mEventData;
2120 // Keep the current offset as it will be used to update the decorator's positions (if the size changes).
2122 if( newSize && isEditable )
2124 offset = mImpl->mModel->mScrollPosition;
2127 if( !isEditable || !IsMultiLineEnabled() )
2129 // After doing the text layout, the vertical offset to place the actor in the desired position can be calculated.
2130 CalculateVerticalOffset( size );
2137 // If there is a new size, the scroll position needs to be clamped.
2138 mImpl->ClampHorizontalScroll( layoutSize );
2140 // Update the decorator's positions is needed if there is a new size.
2141 mImpl->mEventData->mDecorator->UpdatePositions( mImpl->mModel->mScrollPosition - offset );
2144 // Move the cursor, grab handle etc.
2145 if( mImpl->ProcessInputEvents() )
2147 updateTextType = static_cast<UpdateTextType>( updateTextType | DECORATOR_UPDATED );
2151 // Clear the update info. This info will be set the next time the text is updated.
2152 mImpl->mTextUpdateInfo.Clear();
2153 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout\n" );
2155 return updateTextType;
2158 void Controller::RequestRelayout()
2160 mImpl->RequestRelayout();
2163 // public : Input style change signals.
2165 bool Controller::IsInputStyleChangedSignalsQueueEmpty()
2167 return ( NULL == mImpl->mEventData ) || ( 0u == mImpl->mEventData->mInputStyleChangedQueue.Count() );
2170 void Controller::ProcessInputStyleChangedSignals()
2172 if( NULL == mImpl->mEventData )
2178 for( Vector<InputStyle::Mask>::ConstIterator it = mImpl->mEventData->mInputStyleChangedQueue.Begin(),
2179 endIt = mImpl->mEventData->mInputStyleChangedQueue.End();
2183 const InputStyle::Mask mask = *it;
2185 if( NULL != mImpl->mEditableControlInterface )
2187 // Emit the input style changed signal.
2188 mImpl->mEditableControlInterface->InputStyleChanged( mask );
2192 mImpl->mEventData->mInputStyleChangedQueue.Clear();
2195 // public : Text-input Event Queuing.
2197 void Controller::KeyboardFocusGainEvent()
2199 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
2201 if( NULL != mImpl->mEventData )
2203 if( ( EventData::INACTIVE == mImpl->mEventData->mState ) ||
2204 ( EventData::INTERRUPTED == mImpl->mEventData->mState ) )
2206 mImpl->ChangeState( EventData::EDITING );
2207 mImpl->mEventData->mUpdateCursorPosition = true; //If editing started without tap event, cursor update must be triggered.
2208 mImpl->mEventData->mUpdateInputStyle = true;
2210 mImpl->NotifyImfMultiLineStatus();
2211 if( mImpl->IsShowingPlaceholderText() )
2213 // Show alternative placeholder-text when editing
2214 ShowPlaceholderText();
2217 mImpl->RequestRelayout();
2221 void Controller::KeyboardFocusLostEvent()
2223 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
2225 if( NULL != mImpl->mEventData )
2227 if( EventData::INTERRUPTED != mImpl->mEventData->mState )
2229 mImpl->ChangeState( EventData::INACTIVE );
2231 if( !mImpl->IsShowingRealText() )
2233 // Revert to regular placeholder-text when not editing
2234 ShowPlaceholderText();
2238 mImpl->RequestRelayout();
2241 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
2243 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
2245 bool textChanged = false;
2246 bool relayoutNeeded = false;
2248 if( ( NULL != mImpl->mEventData ) &&
2249 ( keyEvent.state == KeyEvent::Down ) )
2251 int keyCode = keyEvent.keyCode;
2252 const std::string& keyString = keyEvent.keyPressed;
2254 const bool isNullKey = ( 0 == keyCode ) && ( keyString.empty() );
2256 // Pre-process to separate modifying events from non-modifying input events.
2259 // In some platforms arrive key events with no key code.
2263 else if( Dali::DALI_KEY_ESCAPE == keyCode || Dali::DALI_KEY_BACK == keyCode )
2268 else if( ( Dali::DALI_KEY_CURSOR_LEFT == keyCode ) ||
2269 ( Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) ||
2270 ( Dali::DALI_KEY_CURSOR_UP == keyCode ) ||
2271 ( Dali::DALI_KEY_CURSOR_DOWN == keyCode ) )
2273 // If don't have any text, do nothing.
2274 if( !mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters )
2279 uint32_t cursorPosition = mImpl->mEventData->mPrimaryCursorPosition;
2280 uint32_t numberOfCharacters = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
2281 uint32_t cursorLine = mImpl->mModel->mVisualModel->GetLineOfCharacter( cursorPosition );
2282 uint32_t numberOfLines = mImpl->mModel->GetNumberOfLines();
2284 // Logic to determine whether this text control will lose focus or not.
2285 if( ( Dali::DALI_KEY_CURSOR_LEFT == keyCode && 0 == cursorPosition ) ||
2286 ( Dali::DALI_KEY_CURSOR_RIGHT == keyCode && numberOfCharacters == cursorPosition) ||
2287 ( Dali::DALI_KEY_CURSOR_DOWN == keyCode && cursorLine == numberOfLines -1 ) ||
2288 ( Dali::DALI_KEY_CURSOR_DOWN == keyCode && numberOfCharacters == cursorPosition && cursorLine -1 == numberOfLines -1 ) ||
2289 ( Dali::DALI_KEY_CURSOR_UP == keyCode && cursorLine == 0 ) ||
2290 ( Dali::DALI_KEY_CURSOR_UP == keyCode && numberOfCharacters == cursorPosition && cursorLine == 1 ) )
2295 mImpl->mEventData->mCheckScrollAmount = true;
2296 Event event( Event::CURSOR_KEY_EVENT );
2297 event.p1.mInt = keyCode;
2298 mImpl->mEventData->mEventQueue.push_back( event );
2300 // Will request for relayout.
2301 relayoutNeeded = true;
2303 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
2305 textChanged = BackspaceKeyEvent();
2307 // Will request for relayout.
2308 relayoutNeeded = true;
2310 else if( IsKey( keyEvent, Dali::DALI_KEY_POWER ) ||
2311 IsKey( keyEvent, Dali::DALI_KEY_MENU ) ||
2312 IsKey( keyEvent, Dali::DALI_KEY_HOME ) )
2314 // Power key/Menu/Home key behaviour does not allow edit mode to resume.
2315 mImpl->ChangeState( EventData::INACTIVE );
2317 // Will request for relayout.
2318 relayoutNeeded = true;
2320 // This branch avoids calling the InsertText() method of the 'else' branch which can delete selected text.
2322 else if( Dali::DALI_KEY_SHIFT_LEFT == keyCode )
2324 // DALI_KEY_SHIFT_LEFT is the key code for the Left Shift. It's sent (by the imf?) when the predictive text is enabled
2325 // and a character is typed after the type of a upper case latin character.
2330 else if( ( Dali::DALI_KEY_VOLUME_UP == keyCode ) || ( Dali::DALI_KEY_VOLUME_DOWN == keyCode ) )
2332 // This branch avoids calling the InsertText() method of the 'else' branch which can delete selected text.
2338 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", this, keyString.c_str() );
2340 // IMF manager is no longer handling key-events
2341 mImpl->ClearPreEditFlag();
2343 InsertText( keyString, COMMIT );
2346 // Will request for relayout.
2347 relayoutNeeded = true;
2350 if ( ( mImpl->mEventData->mState != EventData::INTERRUPTED ) &&
2351 ( mImpl->mEventData->mState != EventData::INACTIVE ) &&
2353 ( Dali::DALI_KEY_SHIFT_LEFT != keyCode ) &&
2354 ( Dali::DALI_KEY_VOLUME_UP != keyCode ) &&
2355 ( Dali::DALI_KEY_VOLUME_DOWN != keyCode ) )
2357 // Should not change the state if the key is the shift send by the imf manager.
2358 // Otherwise, when the state is SELECTING the text controller can't send the right
2359 // surrounding info to the imf.
2360 mImpl->ChangeState( EventData::EDITING );
2362 // Will request for relayout.
2363 relayoutNeeded = true;
2366 if( relayoutNeeded )
2368 mImpl->RequestRelayout();
2373 ( NULL != mImpl->mEditableControlInterface ) )
2375 // Do this last since it provides callbacks into application code
2376 mImpl->mEditableControlInterface->TextChanged();
2382 void Controller::TapEvent( unsigned int tapCount, float x, float y )
2384 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
2386 if( NULL != mImpl->mEventData )
2388 DALI_LOG_INFO( gLogFilter, Debug::Concise, "TapEvent state:%d \n", mImpl->mEventData->mState );
2389 EventData::State state( mImpl->mEventData->mState );
2390 bool relayoutNeeded( false ); // to avoid unnecessary relayouts when tapping an empty text-field
2392 if( mImpl->IsClipboardVisible() )
2394 if( EventData::INACTIVE == state || EventData::EDITING == state)
2396 mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
2398 relayoutNeeded = true;
2400 else if( 1u == tapCount )
2402 if( EventData::EDITING_WITH_POPUP == state || EventData::EDITING_WITH_PASTE_POPUP == state )
2404 mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE ); // If Popup shown hide it here so can be shown again if required.
2407 if( mImpl->IsShowingRealText() && ( EventData::INACTIVE != state ) )
2409 mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
2410 relayoutNeeded = true;
2414 if( mImpl->IsShowingPlaceholderText() && !mImpl->IsFocusedPlaceholderAvailable() )
2416 // Hide placeholder text
2420 if( EventData::INACTIVE == state )
2422 mImpl->ChangeState( EventData::EDITING );
2424 else if( !mImpl->IsClipboardEmpty() )
2426 mImpl->ChangeState( EventData::EDITING_WITH_POPUP );
2428 relayoutNeeded = true;
2431 else if( 2u == tapCount )
2433 if( mImpl->mEventData->mSelectionEnabled &&
2434 mImpl->IsShowingRealText() )
2436 relayoutNeeded = true;
2437 mImpl->mEventData->mIsLeftHandleSelected = true;
2438 mImpl->mEventData->mIsRightHandleSelected = true;
2442 // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
2443 if( relayoutNeeded )
2445 Event event( Event::TAP_EVENT );
2446 event.p1.mUint = tapCount;
2447 event.p2.mFloat = x;
2448 event.p3.mFloat = y;
2449 mImpl->mEventData->mEventQueue.push_back( event );
2451 mImpl->RequestRelayout();
2455 // Reset keyboard as tap event has occurred.
2456 mImpl->ResetImfManager();
2459 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
2461 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
2463 if( NULL != mImpl->mEventData )
2465 Event event( Event::PAN_EVENT );
2466 event.p1.mInt = state;
2467 event.p2.mFloat = displacement.x;
2468 event.p3.mFloat = displacement.y;
2469 mImpl->mEventData->mEventQueue.push_back( event );
2471 mImpl->RequestRelayout();
2475 void Controller::LongPressEvent( Gesture::State state, float x, float y )
2477 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected LongPressEvent" );
2479 if( ( state == Gesture::Started ) &&
2480 ( NULL != mImpl->mEventData ) )
2482 // The 1st long-press on inactive text-field is treated as tap
2483 if( EventData::INACTIVE == mImpl->mEventData->mState )
2485 mImpl->ChangeState( EventData::EDITING );
2487 Event event( Event::TAP_EVENT );
2489 event.p2.mFloat = x;
2490 event.p3.mFloat = y;
2491 mImpl->mEventData->mEventQueue.push_back( event );
2493 mImpl->RequestRelayout();
2495 else if( !mImpl->IsShowingRealText() )
2497 Event event( Event::LONG_PRESS_EVENT );
2498 event.p1.mInt = state;
2499 event.p2.mFloat = x;
2500 event.p3.mFloat = y;
2501 mImpl->mEventData->mEventQueue.push_back( event );
2502 mImpl->RequestRelayout();
2504 else if( !mImpl->IsClipboardVisible() )
2506 // Reset the imf manager to commit the pre-edit before selecting the text.
2507 mImpl->ResetImfManager();
2509 Event event( Event::LONG_PRESS_EVENT );
2510 event.p1.mInt = state;
2511 event.p2.mFloat = x;
2512 event.p3.mFloat = y;
2513 mImpl->mEventData->mEventQueue.push_back( event );
2514 mImpl->RequestRelayout();
2516 mImpl->mEventData->mIsLeftHandleSelected = true;
2517 mImpl->mEventData->mIsRightHandleSelected = true;
2522 ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
2524 // Whether the text needs to be relaid-out.
2525 bool requestRelayout = false;
2527 // Whether to retrieve the text and cursor position to be sent to the IMF manager.
2528 bool retrieveText = false;
2529 bool retrieveCursor = false;
2531 switch( imfEvent.eventName )
2533 case ImfManager::COMMIT:
2535 InsertText( imfEvent.predictiveString, Text::Controller::COMMIT );
2536 requestRelayout = true;
2537 retrieveCursor = true;
2540 case ImfManager::PREEDIT:
2542 InsertText( imfEvent.predictiveString, Text::Controller::PRE_EDIT );
2543 requestRelayout = true;
2544 retrieveCursor = true;
2547 case ImfManager::DELETESURROUNDING:
2549 const bool textDeleted = RemoveText( imfEvent.cursorOffset,
2550 imfEvent.numberOfChars,
2551 DONT_UPDATE_INPUT_STYLE );
2555 if( ( 0u != mImpl->mModel->mLogicalModel->mText.Count() ) ||
2556 !mImpl->IsPlaceholderAvailable() )
2558 mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
2562 ShowPlaceholderText();
2564 mImpl->mEventData->mUpdateCursorPosition = true;
2565 mImpl->mEventData->mScrollAfterDelete = true;
2567 requestRelayout = true;
2571 case ImfManager::GETSURROUNDING:
2573 retrieveText = true;
2574 retrieveCursor = true;
2577 case ImfManager::PRIVATECOMMAND:
2579 // PRIVATECOMMAND event is just for getting the private command message
2580 retrieveText = true;
2581 retrieveCursor = true;
2584 case ImfManager::VOID:
2591 if( requestRelayout )
2593 mImpl->mOperationsPending = ALL_OPERATIONS;
2594 mImpl->RequestRelayout();
2598 CharacterIndex cursorPosition = 0u;
2599 Length numberOfWhiteSpaces = 0u;
2601 if( retrieveCursor )
2603 numberOfWhiteSpaces = mImpl->GetNumberOfWhiteSpaces( 0u );
2605 cursorPosition = mImpl->GetLogicalCursorPosition();
2607 if( cursorPosition < numberOfWhiteSpaces )
2609 cursorPosition = 0u;
2613 cursorPosition -= numberOfWhiteSpaces;
2619 mImpl->GetText( numberOfWhiteSpaces, text );
2622 ImfManager::ImfCallbackData callbackData( ( retrieveText || retrieveCursor ), cursorPosition, text, false );
2624 if( requestRelayout &&
2625 ( NULL != mImpl->mEditableControlInterface ) )
2627 // Do this last since it provides callbacks into application code
2628 mImpl->mEditableControlInterface->TextChanged();
2631 return callbackData;
2634 void Controller::PasteClipboardItemEvent()
2636 // Retrieve the clipboard contents first
2637 ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
2638 std::string stringToPaste( notifier.GetContent() );
2640 // Commit the current pre-edit text; the contents of the clipboard should be appended
2641 mImpl->ResetImfManager();
2643 // Temporary disable hiding clipboard
2644 mImpl->SetClipboardHideEnable( false );
2647 PasteText( stringToPaste );
2649 mImpl->SetClipboardHideEnable( true );
2652 // protected : Inherit from Text::Decorator::ControllerInterface.
2654 void Controller::GetTargetSize( Vector2& targetSize )
2656 targetSize = mImpl->mModel->mVisualModel->mControlSize;
2659 void Controller::AddDecoration( Actor& actor, bool needsClipping )
2661 if( NULL != mImpl->mEditableControlInterface )
2663 mImpl->mEditableControlInterface->AddDecoration( actor, needsClipping );
2667 void Controller::DecorationEvent( HandleType handleType, HandleState state, float x, float y )
2669 DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected DecorationEvent" );
2671 if( NULL != mImpl->mEventData )
2673 switch( handleType )
2677 Event event( Event::GRAB_HANDLE_EVENT );
2678 event.p1.mUint = state;
2679 event.p2.mFloat = x;
2680 event.p3.mFloat = y;
2682 mImpl->mEventData->mEventQueue.push_back( event );
2685 case LEFT_SELECTION_HANDLE:
2687 Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
2688 event.p1.mUint = state;
2689 event.p2.mFloat = x;
2690 event.p3.mFloat = y;
2692 mImpl->mEventData->mEventQueue.push_back( event );
2695 case RIGHT_SELECTION_HANDLE:
2697 Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
2698 event.p1.mUint = state;
2699 event.p2.mFloat = x;
2700 event.p3.mFloat = y;
2702 mImpl->mEventData->mEventQueue.push_back( event );
2705 case LEFT_SELECTION_HANDLE_MARKER:
2706 case RIGHT_SELECTION_HANDLE_MARKER:
2708 // Markers do not move the handles.
2711 case HANDLE_TYPE_COUNT:
2713 DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
2717 mImpl->RequestRelayout();
2721 // protected : Inherit from TextSelectionPopup::TextPopupButtonCallbackInterface.
2723 void Controller::TextPopupButtonTouched( Dali::Toolkit::TextSelectionPopup::Buttons button )
2725 if( NULL == mImpl->mEventData )
2732 case Toolkit::TextSelectionPopup::CUT:
2734 mImpl->SendSelectionToClipboard( true ); // Synchronous call to modify text
2735 mImpl->mOperationsPending = ALL_OPERATIONS;
2737 if( ( 0u != mImpl->mModel->mLogicalModel->mText.Count() ) ||
2738 !mImpl->IsPlaceholderAvailable() )
2740 mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
2744 ShowPlaceholderText();
2747 mImpl->mEventData->mUpdateCursorPosition = true;
2748 mImpl->mEventData->mScrollAfterDelete = true;
2750 mImpl->RequestRelayout();
2752 if( NULL != mImpl->mEditableControlInterface )
2754 mImpl->mEditableControlInterface->TextChanged();
2758 case Toolkit::TextSelectionPopup::COPY:
2760 mImpl->SendSelectionToClipboard( false ); // Text not modified
2762 mImpl->mEventData->mUpdateCursorPosition = true;
2764 mImpl->RequestRelayout(); // Cursor, Handles, Selection Highlight, Popup
2767 case Toolkit::TextSelectionPopup::PASTE:
2769 mImpl->RequestGetTextFromClipboard(); // Request clipboard service to retrieve an item
2772 case Toolkit::TextSelectionPopup::SELECT:
2774 const Vector2& currentCursorPosition = mImpl->mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2776 if( mImpl->mEventData->mSelectionEnabled )
2778 // Creates a SELECT event.
2779 SelectEvent( currentCursorPosition.x, currentCursorPosition.y, false );
2783 case Toolkit::TextSelectionPopup::SELECT_ALL:
2785 // Creates a SELECT_ALL event
2786 SelectEvent( 0.f, 0.f, true );
2789 case Toolkit::TextSelectionPopup::CLIPBOARD:
2791 mImpl->ShowClipboard();
2794 case Toolkit::TextSelectionPopup::NONE:
2802 void Controller::DisplayTimeExpired()
2804 mImpl->mEventData->mUpdateCursorPosition = true;
2805 // Apply modifications to the model
2806 mImpl->mOperationsPending = ALL_OPERATIONS;
2808 mImpl->RequestRelayout();
2811 // private : Update.
2813 void Controller::InsertText( const std::string& text, Controller::InsertType type )
2815 bool removedPrevious = false;
2816 bool removedSelected = false;
2817 bool maxLengthReached = false;
2819 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
2821 if( NULL == mImpl->mEventData )
2826 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPrimaryCursorPosition %d mPreEditFlag %d mPreEditStartPosition %d mPreEditLength %d\n",
2827 this, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"),
2828 mImpl->mEventData->mPrimaryCursorPosition, mImpl->mEventData->mPreEditFlag, mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
2830 // TODO: At the moment the underline runs are only for pre-edit.
2831 mImpl->mModel->mVisualModel->mUnderlineRuns.Clear();
2833 // Remove the previous IMF pre-edit.
2834 if( mImpl->mEventData->mPreEditFlag && ( 0u != mImpl->mEventData->mPreEditLength ) )
2836 removedPrevious = RemoveText( -static_cast<int>( mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition ),
2837 mImpl->mEventData->mPreEditLength,
2838 DONT_UPDATE_INPUT_STYLE );
2840 mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
2841 mImpl->mEventData->mPreEditLength = 0u;
2845 // Remove the previous Selection.
2846 removedSelected = RemoveSelectedText();
2850 Vector<Character> utf32Characters;
2851 Length characterCount = 0u;
2855 // Convert text into UTF-32
2856 utf32Characters.Resize( text.size() );
2858 // This is a bit horrible but std::string returns a (signed) char*
2859 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
2861 // Transform a text array encoded in utf8 into an array encoded in utf32.
2862 // It returns the actual number of characters.
2863 characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
2864 utf32Characters.Resize( characterCount );
2866 DALI_ASSERT_DEBUG( text.size() >= utf32Characters.Count() && "Invalid UTF32 conversion length" );
2867 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "UTF8 size %d, UTF32 size %d\n", text.size(), utf32Characters.Count() );
2870 if( 0u != utf32Characters.Count() ) // Check if Utf8ToUtf32 conversion succeeded
2872 // The placeholder text is no longer needed
2873 if( mImpl->IsShowingPlaceholderText() )
2878 mImpl->ChangeState( EventData::EDITING );
2880 // Handle the IMF (predicitive text) state changes
2881 if( COMMIT == type )
2883 // IMF manager is no longer handling key-events
2884 mImpl->ClearPreEditFlag();
2888 if( !mImpl->mEventData->mPreEditFlag )
2890 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Entered PreEdit state\n" );
2892 // Record the start of the pre-edit text
2893 mImpl->mEventData->mPreEditStartPosition = mImpl->mEventData->mPrimaryCursorPosition;
2896 mImpl->mEventData->mPreEditLength = utf32Characters.Count();
2897 mImpl->mEventData->mPreEditFlag = true;
2899 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
2902 const Length numberOfCharactersInModel = mImpl->mModel->mLogicalModel->mText.Count();
2904 // Restrict new text to fit within Maximum characters setting.
2905 Length maxSizeOfNewText = std::min( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
2906 maxLengthReached = ( characterCount > maxSizeOfNewText );
2908 // The cursor position.
2909 CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
2911 // Update the text's style.
2913 // Updates the text style runs by adding characters.
2914 mImpl->mModel->mLogicalModel->UpdateTextStyleRuns( cursorIndex, maxSizeOfNewText );
2916 // Get the character index from the cursor index.
2917 const CharacterIndex styleIndex = ( cursorIndex > 0u ) ? cursorIndex - 1u : 0u;
2919 // Retrieve the text's style for the given index.
2921 mImpl->RetrieveDefaultInputStyle( style );
2922 mImpl->mModel->mLogicalModel->RetrieveStyle( styleIndex, style );
2924 // Whether to add a new text color run.
2925 const bool addColorRun = ( style.textColor != mImpl->mEventData->mInputStyle.textColor );
2927 // Whether to add a new font run.
2928 const bool addFontNameRun = style.familyName != mImpl->mEventData->mInputStyle.familyName;
2929 const bool addFontWeightRun = style.weight != mImpl->mEventData->mInputStyle.weight;
2930 const bool addFontWidthRun = style.width != mImpl->mEventData->mInputStyle.width;
2931 const bool addFontSlantRun = style.slant != mImpl->mEventData->mInputStyle.slant;
2932 const bool addFontSizeRun = style.size != mImpl->mEventData->mInputStyle.size;
2937 const VectorBase::SizeType numberOfRuns = mImpl->mModel->mLogicalModel->mColorRuns.Count();
2938 mImpl->mModel->mLogicalModel->mColorRuns.Resize( numberOfRuns + 1u );
2940 ColorRun& colorRun = *( mImpl->mModel->mLogicalModel->mColorRuns.Begin() + numberOfRuns );
2941 colorRun.color = mImpl->mEventData->mInputStyle.textColor;
2942 colorRun.characterRun.characterIndex = cursorIndex;
2943 colorRun.characterRun.numberOfCharacters = maxSizeOfNewText;
2946 if( addFontNameRun ||
2952 const VectorBase::SizeType numberOfRuns = mImpl->mModel->mLogicalModel->mFontDescriptionRuns.Count();
2953 mImpl->mModel->mLogicalModel->mFontDescriptionRuns.Resize( numberOfRuns + 1u );
2955 FontDescriptionRun& fontDescriptionRun = *( mImpl->mModel->mLogicalModel->mFontDescriptionRuns.Begin() + numberOfRuns );
2957 if( addFontNameRun )
2959 fontDescriptionRun.familyLength = mImpl->mEventData->mInputStyle.familyName.size();
2960 fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
2961 memcpy( fontDescriptionRun.familyName, mImpl->mEventData->mInputStyle.familyName.c_str(), fontDescriptionRun.familyLength );
2962 fontDescriptionRun.familyDefined = true;
2964 // The memory allocated for the font family name is freed when the font description is removed from the logical model.
2967 if( addFontWeightRun )
2969 fontDescriptionRun.weight = mImpl->mEventData->mInputStyle.weight;
2970 fontDescriptionRun.weightDefined = true;
2973 if( addFontWidthRun )
2975 fontDescriptionRun.width = mImpl->mEventData->mInputStyle.width;
2976 fontDescriptionRun.widthDefined = true;
2979 if( addFontSlantRun )
2981 fontDescriptionRun.slant = mImpl->mEventData->mInputStyle.slant;
2982 fontDescriptionRun.slantDefined = true;
2985 if( addFontSizeRun )
2987 fontDescriptionRun.size = static_cast<PointSize26Dot6>( mImpl->mEventData->mInputStyle.size * 64.f );
2988 fontDescriptionRun.sizeDefined = true;
2991 fontDescriptionRun.characterRun.characterIndex = cursorIndex;
2992 fontDescriptionRun.characterRun.numberOfCharacters = maxSizeOfNewText;
2995 // Insert at current cursor position.
2996 Vector<Character>& modifyText = mImpl->mModel->mLogicalModel->mText;
2998 if( cursorIndex < numberOfCharactersInModel )
3000 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
3004 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
3007 // Mark the first paragraph to be updated.
3008 if( Layout::Engine::SINGLE_LINE_BOX == mImpl->mLayoutEngine.GetLayout() )
3010 mImpl->mTextUpdateInfo.mCharacterIndex = 0;
3011 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
3012 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = numberOfCharactersInModel + maxSizeOfNewText;
3013 mImpl->mTextUpdateInfo.mClearAll = true;
3017 mImpl->mTextUpdateInfo.mCharacterIndex = std::min( cursorIndex, mImpl->mTextUpdateInfo.mCharacterIndex );
3018 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd += maxSizeOfNewText;
3021 // Update the cursor index.
3022 cursorIndex += maxSizeOfNewText;
3024 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 );
3027 if( ( 0u == mImpl->mModel->mLogicalModel->mText.Count() ) &&
3028 mImpl->IsPlaceholderAvailable() )
3030 // Show place-holder if empty after removing the pre-edit text
3031 ShowPlaceholderText();
3032 mImpl->mEventData->mUpdateCursorPosition = true;
3033 mImpl->ClearPreEditFlag();
3035 else if( removedPrevious ||
3037 ( 0 != utf32Characters.Count() ) )
3039 // Queue an inserted event
3040 mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
3042 mImpl->mEventData->mUpdateCursorPosition = true;
3043 if( removedSelected )
3045 mImpl->mEventData->mScrollAfterDelete = true;
3049 mImpl->mEventData->mScrollAfterUpdatePosition = true;
3053 if( maxLengthReached )
3055 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", mImpl->mModel->mLogicalModel->mText.Count() );
3057 mImpl->ResetImfManager();
3059 if( NULL != mImpl->mEditableControlInterface )
3061 // Do this last since it provides callbacks into application code
3062 mImpl->mEditableControlInterface->MaxLengthReached();
3067 void Controller::PasteText( const std::string& stringToPaste )
3069 InsertText( stringToPaste, Text::Controller::COMMIT );
3070 mImpl->ChangeState( EventData::EDITING );
3071 mImpl->RequestRelayout();
3073 if( NULL != mImpl->mEditableControlInterface )
3075 // Do this last since it provides callbacks into application code
3076 mImpl->mEditableControlInterface->TextChanged();
3080 bool Controller::RemoveText( int cursorOffset,
3081 int numberOfCharacters,
3082 UpdateInputStyleType type )
3084 bool removed = false;
3086 if( NULL == mImpl->mEventData )
3091 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p mText.Count() %d cursor %d cursorOffset %d numberOfCharacters %d\n",
3092 this, mImpl->mModel->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition, cursorOffset, numberOfCharacters );
3094 if( !mImpl->IsShowingPlaceholderText() )
3096 // Delete at current cursor position
3097 Vector<Character>& currentText = mImpl->mModel->mLogicalModel->mText;
3098 CharacterIndex& oldCursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
3100 CharacterIndex cursorIndex = 0;
3102 // Validate the cursor position & number of characters
3103 if( ( static_cast< int >( mImpl->mEventData->mPrimaryCursorPosition ) + cursorOffset ) >= 0 )
3105 cursorIndex = mImpl->mEventData->mPrimaryCursorPosition + cursorOffset;
3108 if( ( cursorIndex + numberOfCharacters ) > currentText.Count() )
3110 numberOfCharacters = currentText.Count() - cursorIndex;
3113 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.
3114 ( ( cursorIndex + numberOfCharacters ) <= mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters ) )
3116 // Mark the paragraphs to be updated.
3117 if( Layout::Engine::SINGLE_LINE_BOX == mImpl->mLayoutEngine.GetLayout() )
3119 mImpl->mTextUpdateInfo.mCharacterIndex = 0;
3120 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
3121 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters - numberOfCharacters;
3122 mImpl->mTextUpdateInfo.mClearAll = true;
3126 mImpl->mTextUpdateInfo.mCharacterIndex = std::min( cursorIndex, mImpl->mTextUpdateInfo.mCharacterIndex );
3127 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove += numberOfCharacters;
3130 // Update the input style and remove the text's style before removing the text.
3132 if( UPDATE_INPUT_STYLE == type )
3134 // Keep a copy of the current input style.
3135 InputStyle currentInputStyle;
3136 currentInputStyle.Copy( mImpl->mEventData->mInputStyle );
3138 // Set first the default input style.
3139 mImpl->RetrieveDefaultInputStyle( mImpl->mEventData->mInputStyle );
3141 // Update the input style.
3142 mImpl->mModel->mLogicalModel->RetrieveStyle( cursorIndex, mImpl->mEventData->mInputStyle );
3144 // Compare if the input style has changed.
3145 const bool hasInputStyleChanged = !currentInputStyle.Equal( mImpl->mEventData->mInputStyle );
3147 if( hasInputStyleChanged )
3149 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mImpl->mEventData->mInputStyle );
3150 // Queue the input style changed signal.
3151 mImpl->mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
3155 // Updates the text style runs by removing characters. Runs with no characters are removed.
3156 mImpl->mModel->mLogicalModel->UpdateTextStyleRuns( cursorIndex, -numberOfCharacters );
3158 // Remove the characters.
3159 Vector<Character>::Iterator first = currentText.Begin() + cursorIndex;
3160 Vector<Character>::Iterator last = first + numberOfCharacters;
3162 currentText.Erase( first, last );
3164 // Cursor position retreat
3165 oldCursorIndex = cursorIndex;
3167 mImpl->mEventData->mScrollAfterDelete = true;
3169 DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p removed %d\n", this, numberOfCharacters );
3177 bool Controller::RemoveSelectedText()
3179 bool textRemoved( false );
3181 if( EventData::SELECTING == mImpl->mEventData->mState )
3183 std::string removedString;
3184 mImpl->RetrieveSelection( removedString, true );
3186 if( !removedString.empty() )
3189 mImpl->ChangeState( EventData::EDITING );
3196 // private : Relayout.
3198 bool Controller::DoRelayout( const Size& size,
3199 OperationsMask operationsRequired,
3202 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout %p size %f,%f\n", this, size.width, size.height );
3203 bool viewUpdated( false );
3205 // Calculate the operations to be done.
3206 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
3208 const CharacterIndex startIndex = mImpl->mTextUpdateInfo.mParagraphCharacterIndex;
3209 const Length requestedNumberOfCharacters = mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters;
3211 // Get the current layout size.
3212 layoutSize = mImpl->mModel->mVisualModel->GetLayoutSize();
3214 if( NO_OPERATION != ( LAYOUT & operations ) )
3216 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout LAYOUT & operations\n");
3218 // Some vectors with data needed to layout and reorder may be void
3219 // after the first time the text has been laid out.
3220 // Fill the vectors again.
3222 // Calculate the number of glyphs to layout.
3223 const Vector<GlyphIndex>& charactersToGlyph = mImpl->mModel->mVisualModel->mCharactersToGlyph;
3224 const Vector<Length>& glyphsPerCharacter = mImpl->mModel->mVisualModel->mGlyphsPerCharacter;
3225 const GlyphIndex* const charactersToGlyphBuffer = charactersToGlyph.Begin();
3226 const Length* const glyphsPerCharacterBuffer = glyphsPerCharacter.Begin();
3228 const CharacterIndex lastIndex = startIndex + ( ( requestedNumberOfCharacters > 0u ) ? requestedNumberOfCharacters - 1u : 0u );
3229 const GlyphIndex startGlyphIndex = mImpl->mTextUpdateInfo.mStartGlyphIndex;
3230 const Length numberOfGlyphs = ( requestedNumberOfCharacters > 0u ) ? *( charactersToGlyphBuffer + lastIndex ) + *( glyphsPerCharacterBuffer + lastIndex ) - startGlyphIndex : 0u;
3231 const Length totalNumberOfGlyphs = mImpl->mModel->mVisualModel->mGlyphs.Count();
3233 if( 0u == totalNumberOfGlyphs )
3235 if( NO_OPERATION != ( UPDATE_LAYOUT_SIZE & operations ) )
3237 mImpl->mModel->mVisualModel->SetLayoutSize( Size::ZERO );
3240 // Nothing else to do if there is no glyphs.
3241 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout no glyphs, view updated true\n" );
3245 const Vector<LineBreakInfo>& lineBreakInfo = mImpl->mModel->mLogicalModel->mLineBreakInfo;
3246 const Vector<WordBreakInfo>& wordBreakInfo = mImpl->mModel->mLogicalModel->mWordBreakInfo;
3247 const Vector<CharacterDirection>& characterDirection = mImpl->mModel->mLogicalModel->mCharacterDirections;
3248 const Vector<GlyphInfo>& glyphs = mImpl->mModel->mVisualModel->mGlyphs;
3249 const Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mModel->mVisualModel->mGlyphsToCharacters;
3250 const Vector<Length>& charactersPerGlyph = mImpl->mModel->mVisualModel->mCharactersPerGlyph;
3251 const Character* const textBuffer = mImpl->mModel->mLogicalModel->mText.Begin();
3253 // Set the layout parameters.
3254 Layout::Parameters layoutParameters( size,
3256 lineBreakInfo.Begin(),
3257 wordBreakInfo.Begin(),
3258 ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
3260 glyphsToCharactersMap.Begin(),
3261 charactersPerGlyph.Begin(),
3262 charactersToGlyphBuffer,
3263 glyphsPerCharacterBuffer,
3264 totalNumberOfGlyphs,
3265 mImpl->mModel->mHorizontalAlignment,
3266 mImpl->mModel->mLineWrapMode );
3268 // Resize the vector of positions to have the same size than the vector of glyphs.
3269 Vector<Vector2>& glyphPositions = mImpl->mModel->mVisualModel->mGlyphPositions;
3270 glyphPositions.Resize( totalNumberOfGlyphs );
3272 // Whether the last character is a new paragraph character.
3273 mImpl->mTextUpdateInfo.mIsLastCharacterNewParagraph = TextAbstraction::IsNewParagraph( *( textBuffer + ( mImpl->mModel->mLogicalModel->mText.Count() - 1u ) ) );
3274 layoutParameters.isLastNewParagraph = mImpl->mTextUpdateInfo.mIsLastCharacterNewParagraph;
3276 // The initial glyph and the number of glyphs to layout.
3277 layoutParameters.startGlyphIndex = startGlyphIndex;
3278 layoutParameters.numberOfGlyphs = numberOfGlyphs;
3279 layoutParameters.startLineIndex = mImpl->mTextUpdateInfo.mStartLineIndex;
3280 layoutParameters.estimatedNumberOfLines = mImpl->mTextUpdateInfo.mEstimatedNumberOfLines;
3282 // Update the visual model.
3284 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
3286 mImpl->mModel->mVisualModel->mLines,
3288 mImpl->mModel->mElideEnabled );
3290 viewUpdated = viewUpdated || ( newLayoutSize != layoutSize );
3294 layoutSize = newLayoutSize;
3296 if( NO_OPERATION != ( UPDATE_DIRECTION & operations ) )
3298 mImpl->mAutoScrollDirectionRTL = false;
3301 // Reorder the lines
3302 if( NO_OPERATION != ( REORDER & operations ) )
3304 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mModel->mLogicalModel->mBidirectionalParagraphInfo;
3305 Vector<BidirectionalLineInfoRun>& bidirectionalLineInfo = mImpl->mModel->mLogicalModel->mBidirectionalLineInfo;
3307 // Check first if there are paragraphs with bidirectional info.
3308 if( 0u != bidirectionalInfo.Count() )
3311 const Length numberOfLines = mImpl->mModel->mVisualModel->mLines.Count();
3313 // Reorder the lines.
3314 bidirectionalLineInfo.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
3315 ReorderLines( bidirectionalInfo,
3317 requestedNumberOfCharacters,
3318 mImpl->mModel->mVisualModel->mLines,
3319 bidirectionalLineInfo );
3321 // Set the bidirectional info per line into the layout parameters.
3322 layoutParameters.lineBidirectionalInfoRunsBuffer = bidirectionalLineInfo.Begin();
3323 layoutParameters.numberOfBidirectionalInfoRuns = bidirectionalLineInfo.Count();
3325 // Re-layout the text. Reorder those lines with right to left characters.
3326 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
3328 requestedNumberOfCharacters,
3331 if ( ( NO_OPERATION != ( UPDATE_DIRECTION & operations ) ) && ( numberOfLines > 0 ) )
3333 const LineRun* const firstline = mImpl->mModel->mVisualModel->mLines.Begin();
3336 mImpl->mAutoScrollDirectionRTL = firstline->direction;
3342 // Sets the layout size.
3343 if( NO_OPERATION != ( UPDATE_LAYOUT_SIZE & operations ) )
3345 mImpl->mModel->mVisualModel->SetLayoutSize( layoutSize );
3350 if( NO_OPERATION != ( ALIGN & operations ) )
3352 // The laid-out lines.
3353 Vector<LineRun>& lines = mImpl->mModel->mVisualModel->mLines;
3355 // Need to align with the control's size as the text may contain lines
3356 // starting either with left to right text or right to left.
3357 mImpl->mLayoutEngine.Align( size,
3359 requestedNumberOfCharacters,
3360 mImpl->mModel->mHorizontalAlignment,
3362 mImpl->mModel->mAlignmentOffset );
3366 #if defined(DEBUG_ENABLED)
3367 std::string currentText;
3368 GetText( currentText );
3369 DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::DoRelayout [%p] mImpl->mAutoScrollDirectionRTL[%s] [%s]\n", this, (mImpl->mAutoScrollDirectionRTL)?"true":"false", currentText.c_str() );
3371 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout, view updated %s\n", ( viewUpdated ? "true" : "false" ) );
3375 void Controller::CalculateVerticalOffset( const Size& controlSize )
3377 Size layoutSize = mImpl->mModel->mVisualModel->GetLayoutSize();
3379 if( fabsf( layoutSize.height ) < Math::MACHINE_EPSILON_1000 )
3381 // Get the line height of the default font.
3382 layoutSize.height = mImpl->GetDefaultFontLineHeight();
3385 switch( mImpl->mModel->mVerticalAlignment )
3387 case Layout::VERTICAL_ALIGN_TOP:
3389 mImpl->mModel->mScrollPosition.y = 0.f;
3392 case Layout::VERTICAL_ALIGN_CENTER:
3394 mImpl->mModel->mScrollPosition.y = floorf( 0.5f * ( controlSize.height - layoutSize.height ) ); // try to avoid pixel alignment.
3397 case Layout::VERTICAL_ALIGN_BOTTOM:
3399 mImpl->mModel->mScrollPosition.y = controlSize.height - layoutSize.height;
3405 // private : Events.
3407 void Controller::ProcessModifyEvents()
3409 Vector<ModifyEvent>& events = mImpl->mModifyEvents;
3411 if( 0u == events.Count() )
3417 for( Vector<ModifyEvent>::ConstIterator it = events.Begin(),
3418 endIt = events.End();
3422 const ModifyEvent& event = *it;
3424 if( ModifyEvent::TEXT_REPLACED == event.type )
3426 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
3427 DALI_ASSERT_DEBUG( it == events.Begin() && "Unexpected TEXT_REPLACED event" );
3429 TextReplacedEvent();
3431 else if( ModifyEvent::TEXT_INSERTED == event.type )
3433 TextInsertedEvent();
3435 else if( ModifyEvent::TEXT_DELETED == event.type )
3437 // Placeholder-text cannot be deleted
3438 if( !mImpl->IsShowingPlaceholderText() )
3445 if( NULL != mImpl->mEventData )
3447 // When the text is being modified, delay cursor blinking
3448 mImpl->mEventData->mDecorator->DelayCursorBlink();
3451 // Discard temporary text
3455 void Controller::TextReplacedEvent()
3457 // The natural size needs to be re-calculated.
3458 mImpl->mRecalculateNaturalSize = true;
3460 // Apply modifications to the model
3461 mImpl->mOperationsPending = ALL_OPERATIONS;
3464 void Controller::TextInsertedEvent()
3466 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextInsertedEvent" );
3468 if( NULL == mImpl->mEventData )
3473 mImpl->mEventData->mCheckScrollAmount = true;
3475 // The natural size needs to be re-calculated.
3476 mImpl->mRecalculateNaturalSize = true;
3478 // Apply modifications to the model; TODO - Optimize this
3479 mImpl->mOperationsPending = ALL_OPERATIONS;
3482 void Controller::TextDeletedEvent()
3484 DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextDeletedEvent" );
3486 if( NULL == mImpl->mEventData )
3491 mImpl->mEventData->mCheckScrollAmount = true;
3493 // The natural size needs to be re-calculated.
3494 mImpl->mRecalculateNaturalSize = true;
3496 // Apply modifications to the model; TODO - Optimize this
3497 mImpl->mOperationsPending = ALL_OPERATIONS;
3500 void Controller::SelectEvent( float x, float y, bool selectAll )
3502 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SelectEvent\n" );
3504 if( NULL != mImpl->mEventData )
3508 Event event( Event::SELECT_ALL );
3509 mImpl->mEventData->mEventQueue.push_back( event );
3513 Event event( Event::SELECT );
3514 event.p2.mFloat = x;
3515 event.p3.mFloat = y;
3516 mImpl->mEventData->mEventQueue.push_back( event );
3519 mImpl->mEventData->mCheckScrollAmount = true;
3520 mImpl->mEventData->mIsLeftHandleSelected = true;
3521 mImpl->mEventData->mIsRightHandleSelected = true;
3522 mImpl->RequestRelayout();
3526 bool Controller::BackspaceKeyEvent()
3528 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE\n", this );
3530 bool removed = false;
3532 if( NULL == mImpl->mEventData )
3537 // IMF manager is no longer handling key-events
3538 mImpl->ClearPreEditFlag();
3540 if( EventData::SELECTING == mImpl->mEventData->mState )
3542 removed = RemoveSelectedText();
3544 else if( mImpl->mEventData->mPrimaryCursorPosition > 0 )
3546 // Remove the character before the current cursor position
3547 removed = RemoveText( -1,
3549 UPDATE_INPUT_STYLE );
3554 if( ( 0u != mImpl->mModel->mLogicalModel->mText.Count() ) ||
3555 !mImpl->IsPlaceholderAvailable() )
3557 mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
3561 ShowPlaceholderText();
3563 mImpl->mEventData->mUpdateCursorPosition = true;
3564 mImpl->mEventData->mScrollAfterDelete = true;
3570 // private : Helpers.
3572 void Controller::ResetText()
3575 mImpl->mModel->mLogicalModel->mText.Clear();
3577 // We have cleared everything including the placeholder-text
3578 mImpl->PlaceholderCleared();
3580 mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
3581 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
3582 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = 0u;
3584 // Clear any previous text.
3585 mImpl->mTextUpdateInfo.mClearAll = true;
3587 // The natural size needs to be re-calculated.
3588 mImpl->mRecalculateNaturalSize = true;
3590 // Apply modifications to the model
3591 mImpl->mOperationsPending = ALL_OPERATIONS;
3594 void Controller::ShowPlaceholderText()
3596 if( mImpl->IsPlaceholderAvailable() )
3598 DALI_ASSERT_DEBUG( mImpl->mEventData && "No placeholder text available" );
3600 if( NULL == mImpl->mEventData )
3605 mImpl->mEventData->mIsShowingPlaceholderText = true;
3607 // Disable handles when showing place-holder text
3608 mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
3609 mImpl->mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
3610 mImpl->mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
3612 const char* text( NULL );
3615 // TODO - Switch Placeholder text when changing state
3616 if( ( EventData::INACTIVE != mImpl->mEventData->mState ) &&
3617 ( 0u != mImpl->mEventData->mPlaceholderTextActive.c_str() ) )
3619 text = mImpl->mEventData->mPlaceholderTextActive.c_str();
3620 size = mImpl->mEventData->mPlaceholderTextActive.size();
3624 text = mImpl->mEventData->mPlaceholderTextInactive.c_str();
3625 size = mImpl->mEventData->mPlaceholderTextInactive.size();
3628 mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
3629 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
3631 // Reset model for showing placeholder.
3632 mImpl->mModel->mLogicalModel->mText.Clear();
3633 mImpl->mModel->mVisualModel->SetTextColor( mImpl->mEventData->mPlaceholderTextColor );
3635 // Convert text into UTF-32
3636 Vector<Character>& utf32Characters = mImpl->mModel->mLogicalModel->mText;
3637 utf32Characters.Resize( size );
3639 // This is a bit horrible but std::string returns a (signed) char*
3640 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text );
3642 // Transform a text array encoded in utf8 into an array encoded in utf32.
3643 // It returns the actual number of characters.
3644 const Length characterCount = Utf8ToUtf32( utf8, size, utf32Characters.Begin() );
3645 utf32Characters.Resize( characterCount );
3647 // The characters to be added.
3648 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = characterCount;
3650 // Reset the cursor position
3651 mImpl->mEventData->mPrimaryCursorPosition = 0;
3653 // The natural size needs to be re-calculated.
3654 mImpl->mRecalculateNaturalSize = true;
3656 // Apply modifications to the model
3657 mImpl->mOperationsPending = ALL_OPERATIONS;
3659 // Update the rest of the model during size negotiation
3660 mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
3664 void Controller::ClearFontData()
3666 if( mImpl->mFontDefaults )
3668 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
3671 // Set flags to update the model.
3672 mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
3673 mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
3674 mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count();
3676 mImpl->mTextUpdateInfo.mClearAll = true;
3677 mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true;
3678 mImpl->mRecalculateNaturalSize = true;
3680 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
3685 UPDATE_LAYOUT_SIZE |
3690 void Controller::ClearStyleData()
3692 mImpl->mModel->mLogicalModel->mColorRuns.Clear();
3693 mImpl->mModel->mLogicalModel->ClearFontDescriptionRuns();
3696 void Controller::ResetCursorPosition( CharacterIndex cursorIndex )
3698 // Reset the cursor position
3699 if( NULL != mImpl->mEventData )
3701 mImpl->mEventData->mPrimaryCursorPosition = cursorIndex;
3703 // Update the cursor if it's in editing mode.
3704 if( EventData::IsEditingState( mImpl->mEventData->mState ) )
3706 mImpl->mEventData->mUpdateCursorPosition = true;
3711 void Controller::ResetScrollPosition()
3713 if( NULL != mImpl->mEventData )
3715 // Reset the scroll position.
3716 mImpl->mModel->mScrollPosition = Vector2::ZERO;
3717 mImpl->mEventData->mScrollAfterUpdatePosition = true;
3721 void Controller::SetControlInterface( ControlInterface* controlInterface )
3723 mImpl->mControlInterface = controlInterface;
3726 bool Controller::ShouldClearFocusOnEscape() const
3728 return mImpl->mShouldClearFocusOnEscape;
3731 // private : Private contructors & copy operator.
3733 Controller::Controller()
3736 mImpl = new Controller::Impl( NULL, NULL );
3739 Controller::Controller( ControlInterface* controlInterface )
3741 mImpl = new Controller::Impl( controlInterface, NULL );
3744 Controller::Controller( ControlInterface* controlInterface,
3745 EditableControlInterface* editableControlInterface )
3747 mImpl = new Controller::Impl( controlInterface,
3748 editableControlInterface );
3751 // The copy constructor and operator are left unimplemented.
3753 // protected : Destructor.
3755 Controller::~Controller()
3762 } // namespace Toolkit