2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller.h>
24 #include <dali/public-api/adaptor-framework/key.h>
25 #include <dali/public-api/text-abstraction/font-client.h>
28 #include <dali-toolkit/internal/text/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/character-set-conversion.h>
30 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
31 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
32 #include <dali-toolkit/internal/text/logical-model-impl.h>
33 #include <dali-toolkit/internal/text/multi-language-support.h>
34 #include <dali-toolkit/internal/text/script-run.h>
35 #include <dali-toolkit/internal/text/segmentation.h>
36 #include <dali-toolkit/internal/text/shaper.h>
37 #include <dali-toolkit/internal/text/text-io.h>
38 #include <dali-toolkit/internal/text/text-view.h>
39 #include <dali-toolkit/internal/text/visual-model-impl.h>
46 const float MAX_FLOAT = std::numeric_limits<float>::max();
47 const std::string EMPTY_STRING;
51 REPLACE_TEXT, ///< Replace the entire text
52 INSERT_TEXT, ///< Insert characters at the current cursor position
53 DELETE_TEXT ///< Delete a character at the current cursor position
73 struct Controller::TextInput
75 // Used to queue input events until DoRelayout()
78 KEYBOARD_FOCUS_GAIN_EVENT,
79 KEYBOARD_FOCUS_LOST_EVENT,
95 Event( EventType eventType )
116 TextInput( LogicalModelPtr logicalModel,
117 VisualModelPtr visualModel,
118 DecoratorPtr decorator )
119 : mLogicalModel( logicalModel ),
120 mVisualModel( visualModel ),
121 mDecorator( decorator ),
123 mPrimaryCursorPosition( 0u ),
124 mSecondaryCursorPosition( 0u ),
125 mDecoratorUpdated( false ),
126 mCursorBlinkEnabled( true ),
127 mGrabHandleEnabled( true ),
128 mGrabHandlePopupEnabled( true ),
129 mSelectionEnabled( true ),
130 mHorizontalScrollingEnabled( true ),
131 mVerticalScrollingEnabled( false ),
132 mUpdateCursorPosition( false )
137 * @brief Helper to move the cursor, grab handle etc.
139 bool ProcessInputEvents( const Vector2& controlSize,
140 const Vector2& alignmentOffset )
142 mDecoratorUpdated = false;
146 for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
150 case KEYBOARD_FOCUS_GAIN_EVENT:
152 OnKeyboardFocus( true );
155 case KEYBOARD_FOCUS_LOST_EVENT:
157 OnKeyboardFocus( false );
160 case CURSOR_KEY_EVENT:
162 OnCursorKeyEvent( *iter );
167 OnTapEvent( *iter, alignmentOffset );
172 OnPanEvent( *iter, controlSize, alignmentOffset );
175 case GRAB_HANDLE_EVENT:
177 OnGrabHandleEvent( *iter );
184 // The cursor must also be repositioned after inserts into the model
185 if( mUpdateCursorPosition )
187 UpdateCursorPosition();
188 mUpdateCursorPosition = false;
193 return mDecoratorUpdated;
196 void OnKeyboardFocus( bool hasFocus )
200 ChangeState( INACTIVE );
204 ChangeState( EDITING );
208 void OnCursorKeyEvent( const Event& event )
210 int keyCode = event.p1.mInt;
212 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
216 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
220 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
224 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
230 void HandleCursorKey( int keyCode )
235 void OnTapEvent( const Event& event,
236 const Vector2& alignmentOffset )
238 unsigned int tapCount = event.p1.mUint;
242 ChangeState( EDITING );
244 float xPosition = event.p2.mFloat - alignmentOffset.x;
245 float yPosition = event.p3.mFloat - alignmentOffset.y;
247 GetClosestCursorPosition( mPrimaryCursorPosition, xPosition, yPosition, height );
248 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height, height ); // TODO: To be fixed in the next patch.
249 mUpdateCursorPosition = false;
251 mDecoratorUpdated = true;
253 else if( mSelectionEnabled &&
256 ChangeState( SELECTING );
258 RepositionSelectionHandles( event.p2.mFloat, event.p3.mFloat );
262 void OnPanEvent( const Event& event,
263 const Vector2& controlSize,
264 const Vector2& alignmentOffset )
266 int state = event.p1.mInt;
268 if( Gesture::Started == state ||
269 Gesture::Continuing == state )
271 const Vector2& actualSize = mVisualModel->GetActualSize();
273 if( mHorizontalScrollingEnabled )
275 const float displacementX = event.p2.mFloat;
276 mScrollPosition.x += displacementX;
278 // Clamp between -space & 0 (and the text alignment).
279 const float contentWidth = actualSize.width;
280 if( contentWidth > controlSize.width )
282 const float space = ( contentWidth - controlSize.width ) + alignmentOffset.x;
283 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
284 mScrollPosition.x = ( mScrollPosition.x > -alignmentOffset.x ) ? -alignmentOffset.x : mScrollPosition.x;
286 mDecoratorUpdated = true;
290 mScrollPosition.x = 0.f;
294 if( mVerticalScrollingEnabled )
296 const float displacementY = event.p3.mFloat;
297 mScrollPosition.y += displacementY;
299 // Clamp between -space & 0 (and the text alignment).
300 if( actualSize.height > controlSize.height )
302 const float space = ( actualSize.height - controlSize.height ) + alignmentOffset.y;
303 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
304 mScrollPosition.y = ( mScrollPosition.y > -alignmentOffset.y ) ? -alignmentOffset.y : mScrollPosition.y;
306 mDecoratorUpdated = true;
310 mScrollPosition.y = 0.f;
316 void OnGrabHandleEvent( const Event& event )
318 unsigned int state = event.p1.mUint;
320 if( GRAB_HANDLE_PRESSED == state )
322 float xPosition = event.p2.mFloat;
323 float yPosition = event.p3.mFloat;
326 GetClosestCursorPosition( mPrimaryCursorPosition, xPosition, yPosition, height );
328 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height, height ); // TODO: To be fixed in the next patch.
330 //mDecorator->HidePopup();
331 ChangeState ( EDITING );
332 mDecoratorUpdated = true;
334 else if ( mGrabHandlePopupEnabled &&
335 GRAB_HANDLE_RELEASED == state )
337 //mDecorator->ShowPopup();
338 ChangeState ( EDITING_WITH_POPUP );
339 mDecoratorUpdated = true;
343 void RepositionSelectionHandles( float visualX, float visualY )
345 // TODO - Find which word was selected
347 const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
348 const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
350 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
351 const Vector<Vector2>::SizeType positionCount = positions.Count();
353 // Guard against glyphs which did not fit inside the layout
354 const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
358 float primaryX = positions[0].x;
359 float secondaryX = positions[count-1].x + glyphs[count-1].width;
361 // TODO - multi-line selection
362 const Vector<LineRun>& lines = mVisualModel->mLines;
363 float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
365 mDecorator->SetPosition( PRIMARY_SELECTION_HANDLE, primaryX, 0.0f, height );
366 mDecorator->SetPosition( SECONDARY_SELECTION_HANDLE, secondaryX, 0.0f, height );
368 mDecorator->ClearHighlights();
369 mDecorator->AddHighlight( primaryX, 0.0f, secondaryX, height );
373 void ChangeState( State newState )
375 if( mState != newState )
379 if( INACTIVE == mState )
381 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
382 mDecorator->StopCursorBlink();
383 mDecorator->SetGrabHandleActive( false );
384 mDecorator->SetSelectionActive( false );
385 mDecorator->SetPopupActive( false );
386 mDecoratorUpdated = true;
388 else if ( SELECTING == mState )
390 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
391 mDecorator->StopCursorBlink();
392 mDecorator->SetGrabHandleActive( false );
393 mDecorator->SetSelectionActive( true );
394 mDecoratorUpdated = true;
396 else if( EDITING == mState )
398 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
399 if( mCursorBlinkEnabled )
401 mDecorator->StartCursorBlink();
403 if( mGrabHandleEnabled )
405 mDecorator->SetGrabHandleActive( true );
407 if( mGrabHandlePopupEnabled )
409 mDecorator->SetPopupActive( false );
411 mDecorator->SetSelectionActive( false );
412 mDecoratorUpdated = true;
414 else if( EDITING_WITH_POPUP == mState )
416 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
417 if( mCursorBlinkEnabled )
419 mDecorator->StartCursorBlink();
421 if( mGrabHandleEnabled )
423 mDecorator->SetGrabHandleActive( true );
425 if( mGrabHandlePopupEnabled )
427 mDecorator->SetPopupActive( true );
429 mDecorator->SetSelectionActive( false );
430 mDecoratorUpdated = true;
435 LineIndex GetClosestLine( float y )
437 LineIndex lineIndex( 0u );
439 const Vector<LineRun>& lines = mVisualModel->mLines;
440 for( float totalHeight = 0; lineIndex < lines.Count(); ++lineIndex )
442 const LineRun& lineRun = lines[lineIndex];
443 totalHeight += lineRun.ascender + -lineRun.descender;
444 if( y < totalHeight )
453 void GetClosestCursorPosition( CharacterIndex& logical, float& visualX, float& visualY, float& height )
455 Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
456 Length numberOfLines = mVisualModel->mLines.Count();
457 if( 0 == numberOfGlyphs ||
463 // Transform to visual model coords
464 visualX -= mScrollPosition.x;
465 visualY -= mScrollPosition.y;
467 // Find which line is closest
468 LineIndex lineIndex( GetClosestLine( visualY ) );
470 const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
471 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
473 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
474 const Vector2* const positionsBuffer = positions.Begin();
476 unsigned int closestGlyph = 0;
477 bool leftOfGlyph( false ); // which side of the glyph?
478 float closestDistance = MAX_FLOAT;
480 const LineRun& line = mVisualModel->mLines[lineIndex];
481 GlyphIndex startGlyph = line.glyphIndex;
482 GlyphIndex endGlyph = line.glyphIndex + line.numberOfGlyphs;
483 DALI_ASSERT_DEBUG( endGlyph <= glyphs.Count() && "Invalid line info" );
485 for( GlyphIndex i = startGlyph; i < endGlyph; ++i )
487 const GlyphInfo& glyphInfo = *( glyphsBuffer + i );
488 const Vector2& position = *( positionsBuffer + i );
489 float glyphX = position.x + glyphInfo.width*0.5f;
490 float glyphY = position.y + glyphInfo.height*0.5f;
492 float distanceToGlyph = fabsf( glyphX - visualX ) + fabsf( glyphY - visualY );
494 if( distanceToGlyph < closestDistance )
496 closestDistance = distanceToGlyph;
498 leftOfGlyph = ( visualX < glyphX );
502 // Calculate the logical position
503 logical = mVisualModel->GetCharacterIndex( closestGlyph );
505 // Returns the visual position of the glyph
506 visualX = positions[closestGlyph].x;
509 visualX += glyphs[closestGlyph].width;
514 else// if ( RTL ) TODO
520 height = line.ascender + -line.descender;
523 void UpdateCursorPosition()
525 if( 0 == mVisualModel->mGlyphs.Count() )
530 // FIXME GetGlyphIndex() is behaving strangely
532 GlyphIndex cursorGlyph = mVisualModel->GetGlyphIndex( mPrimaryCursorPosition );
534 GlyphIndex cursorGlyph( 0u );
535 for( cursorGlyph = 0; cursorGlyph < mVisualModel->mGlyphs.Count(); ++cursorGlyph )
537 if( mPrimaryCursorPosition == mVisualModel->GetCharacterIndex( cursorGlyph ) )
544 float visualX( 0.0f );
545 float visualY( 0.0f );
546 float height( 0.0f );
547 const Vector<LineRun>& lineRuns = mVisualModel->mLines;
549 if( cursorGlyph > 0 )
553 visualX = mVisualModel->mGlyphPositions[ cursorGlyph ].x;
555 visualX += mVisualModel->mGlyphs[ cursorGlyph ].width;
557 // Find the line height
558 GlyphIndex lastGlyph( 0 );
559 for( LineIndex lineIndex = 0u; lineIndex < lineRuns.Count(); ++lineIndex )
561 lastGlyph = (lineRuns[lineIndex].glyphIndex + lineRuns[lineIndex].numberOfGlyphs);
562 if( cursorGlyph < lastGlyph )
564 const LineRun& lineRun = lineRuns[lineIndex];
565 height = lineRun.ascender + -lineRun.descender;
571 mDecorator->SetPosition( PRIMARY_CURSOR, visualX, visualY, height, height ); // TODO: To be fixed in the next patch.
572 mDecoratorUpdated = true;
575 LogicalModelPtr mLogicalModel;
576 VisualModelPtr mVisualModel;
577 DecoratorPtr mDecorator;
579 std::string mPlaceholderText;
582 * This is used to delay handling events until after the model has been updated.
583 * The number of updates to the model is minimized to improve performance.
585 vector<Event> mEventQueue; ///< The queue of touch events etc.
587 State mState; ///< Selection mode, edit mode etc.
589 CharacterIndex mPrimaryCursorPosition; ///< Index into logical model for primary cursor
590 CharacterIndex mSecondaryCursorPosition; ///< Index into logical model for secondary cursor
593 * 0,0 means that the top-left corner of the layout matches the top-left corner of the UI control.
594 * Typically this will have a negative value with scrolling occurs.
596 Vector2 mScrollPosition; ///< The text is offset by this position when scrolling.
598 bool mDecoratorUpdated : 1; ///< True if the decorator was updated during event processing
599 bool mCursorBlinkEnabled : 1; ///< True if cursor should blink when active
600 bool mGrabHandleEnabled : 1; ///< True if grab handle is enabled
601 bool mGrabHandlePopupEnabled : 1; ///< True if the grab handle popu-up should be shown
602 bool mSelectionEnabled : 1; ///< True if selection handles, highlight etc. are enabled
603 bool mHorizontalScrollingEnabled : 1; ///< True if horizontal scrolling is enabled
604 bool mVerticalScrollingEnabled : 1; ///< True if vertical scrolling is enabled
605 bool mUpdateCursorPosition : 1; ///< True if the visual position of the cursor must be recalculated
608 struct Controller::FontDefaults
611 : mDefaultPointSize(0.0f),
616 FontId GetFontId( TextAbstraction::FontClient& fontClient )
620 Dali::TextAbstraction::PointSize26Dot6 pointSize = mDefaultPointSize*64;
621 mFontId = fontClient.GetFontId( mDefaultFontFamily, mDefaultFontStyle, pointSize );
627 std::string mDefaultFontFamily;
628 std::string mDefaultFontStyle;
629 float mDefaultPointSize;
633 struct Controller::Impl
635 Impl( ControlInterface& controlInterface )
636 : mControlInterface( controlInterface ),
639 mFontDefaults( NULL ),
647 mOperationsPending( NO_OPERATION ),
648 mRecalculateNaturalSize( true )
650 mLogicalModel = LogicalModel::New();
651 mVisualModel = VisualModel::New();
653 mFontClient = TextAbstraction::FontClient::Get();
655 mView.SetVisualModel( mVisualModel );
657 // Set the text properties to default
658 mVisualModel->SetTextColor( Color::WHITE );
659 mVisualModel->SetShadowOffset( Vector2::ZERO );
660 mVisualModel->SetShadowColor( Vector4::ZERO );
661 mVisualModel->SetUnderlineEnabled( false );
669 ControlInterface& mControlInterface; ///< Reference to the text controller.
670 LogicalModelPtr mLogicalModel; ///< Pointer to the logical model.
671 VisualModelPtr mVisualModel; ///< Pointer to the visual model.
672 FontDefaults* mFontDefaults; ///< Avoid allocating this when the user does not specify a font.
673 Controller::TextInput* mTextInput; ///< Avoid allocating everything for text input until EnableTextInput().
674 TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
675 View mView; ///< The view interface to the rendering back-end.
676 LayoutEngine mLayoutEngine; ///< The layout engine.
677 std::vector<ModifyEvent> mModifyEvents; ///< Temporary stores the text set until the next relayout.
678 Size mControlSize; ///< The size of the control.
679 Vector2 mAlignmentOffset; ///< Vertical and horizontal offset of the whole text inside the control due to alignment.
680 OperationsMask mOperationsPending; ///< Operations pending to be done to layout the text.
681 bool mRecalculateNaturalSize:1; ///< Whether the natural size needs to be recalculated.
684 ControllerPtr Controller::New( ControlInterface& controlInterface )
686 return ControllerPtr( new Controller( controlInterface ) );
689 void Controller::SetText( const std::string& text )
691 // Cancel previously queued inserts etc.
692 mImpl->mModifyEvents.clear();
694 // Keep until size negotiation
696 event.type = REPLACE_TEXT;
698 mImpl->mModifyEvents.push_back( event );
700 if( mImpl->mTextInput )
702 // Cancel previously queued events
703 mImpl->mTextInput->mEventQueue.clear();
705 // TODO - Hide selection decorations
709 void Controller::GetText( std::string& text ) const
711 if( !mImpl->mModifyEvents.empty() &&
712 REPLACE_TEXT == mImpl->mModifyEvents[0].type )
714 text = mImpl->mModifyEvents[0].text;
718 // TODO - Convert from UTF-32
722 void Controller::SetPlaceholderText( const std::string& text )
724 if( !mImpl->mTextInput )
726 mImpl->mTextInput->mPlaceholderText = text;
730 void Controller::GetPlaceholderText( std::string& text ) const
732 if( !mImpl->mTextInput )
734 text = mImpl->mTextInput->mPlaceholderText;
738 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
740 if( !mImpl->mFontDefaults )
742 mImpl->mFontDefaults = new Controller::FontDefaults();
745 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
746 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
747 mImpl->mOperationsPending = ALL_OPERATIONS;
748 mImpl->mRecalculateNaturalSize = true;
750 // Clear the font-specific data
751 mImpl->mLogicalModel->mFontRuns.Clear();
752 mImpl->mVisualModel->mGlyphs.Clear();
753 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
754 mImpl->mVisualModel->mCharactersToGlyph.Clear();
755 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
756 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
757 mImpl->mVisualModel->mGlyphPositions.Clear();
758 mImpl->mVisualModel->mLines.Clear();
763 const std::string& Controller::GetDefaultFontFamily() const
765 if( mImpl->mFontDefaults )
767 return mImpl->mFontDefaults->mDefaultFontFamily;
773 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
775 if( !mImpl->mFontDefaults )
777 mImpl->mFontDefaults = new Controller::FontDefaults();
780 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
781 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
782 mImpl->mOperationsPending = ALL_OPERATIONS;
783 mImpl->mRecalculateNaturalSize = true;
785 // Clear the font-specific data
786 mImpl->mLogicalModel->mFontRuns.Clear();
787 mImpl->mVisualModel->mGlyphs.Clear();
788 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
789 mImpl->mVisualModel->mCharactersToGlyph.Clear();
790 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
791 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
792 mImpl->mVisualModel->mGlyphPositions.Clear();
793 mImpl->mVisualModel->mLines.Clear();
798 const std::string& Controller::GetDefaultFontStyle() const
800 if( mImpl->mFontDefaults )
802 return mImpl->mFontDefaults->mDefaultFontStyle;
808 void Controller::SetDefaultPointSize( float pointSize )
810 if( !mImpl->mFontDefaults )
812 mImpl->mFontDefaults = new Controller::FontDefaults();
815 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
816 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
817 mImpl->mOperationsPending = ALL_OPERATIONS;
818 mImpl->mRecalculateNaturalSize = true;
820 // Clear the font-specific data
821 mImpl->mLogicalModel->mFontRuns.Clear();
822 mImpl->mVisualModel->mGlyphs.Clear();
823 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
824 mImpl->mVisualModel->mCharactersToGlyph.Clear();
825 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
826 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
827 mImpl->mVisualModel->mGlyphPositions.Clear();
828 mImpl->mVisualModel->mLines.Clear();
833 float Controller::GetDefaultPointSize() const
835 if( mImpl->mFontDefaults )
837 return mImpl->mFontDefaults->mDefaultPointSize;
843 void Controller::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
845 if( mImpl->mFontDefaults )
848 fontRun.characterRun.characterIndex = 0;
849 fontRun.characterRun.numberOfCharacters = numberOfCharacters;
850 fontRun.fontId = mImpl->mFontDefaults->GetFontId( mImpl->mFontClient );
851 fontRun.isDefault = true;
853 fonts.PushBack( fontRun );
857 const Vector4& Controller::GetTextColor() const
859 return mImpl->mVisualModel->GetTextColor();
862 const Vector2& Controller::GetShadowOffset() const
864 return mImpl->mVisualModel->GetShadowOffset();
867 const Vector4& Controller::GetShadowColor() const
869 return mImpl->mVisualModel->GetShadowColor();
872 const Vector4& Controller::GetUnderlineColor() const
874 return mImpl->mVisualModel->GetUnderlineColor();
877 bool Controller::IsUnderlineEnabled() const
879 return mImpl->mVisualModel->IsUnderlineEnabled();
882 void Controller::SetTextColor( const Vector4& textColor )
884 mImpl->mVisualModel->SetTextColor( textColor );
887 void Controller::SetShadowOffset( const Vector2& shadowOffset )
889 mImpl->mVisualModel->SetShadowOffset( shadowOffset );
892 void Controller::SetShadowColor( const Vector4& shadowColor )
894 mImpl->mVisualModel->SetShadowColor( shadowColor );
897 void Controller::SetUnderlineColor( const Vector4& color )
899 mImpl->mVisualModel->SetUnderlineColor( color );
902 void Controller::SetUnderlineEnabled( bool enabled )
904 mImpl->mVisualModel->SetUnderlineEnabled( enabled );
907 void Controller::EnableTextInput( DecoratorPtr decorator )
909 if( !mImpl->mTextInput )
911 mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
915 void Controller::SetEnableCursorBlink( bool enable )
917 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "TextInput disabled" );
919 if( mImpl->mTextInput )
921 mImpl->mTextInput->mCursorBlinkEnabled = enable;
924 mImpl->mTextInput->mDecorator )
926 mImpl->mTextInput->mDecorator->StopCursorBlink();
931 bool Controller::GetEnableCursorBlink() const
933 if( mImpl->mTextInput )
935 return mImpl->mTextInput->mCursorBlinkEnabled;
941 const Vector2& Controller::GetScrollPosition() const
943 if( mImpl->mTextInput )
945 return mImpl->mTextInput->mScrollPosition;
948 return Vector2::ZERO;
951 const Vector2& Controller::GetAlignmentOffset() const
953 return mImpl->mAlignmentOffset;
956 Vector3 Controller::GetNaturalSize()
960 // Make sure the model is up-to-date before layouting
961 ProcessModifyEvents();
963 if( mImpl->mRecalculateNaturalSize )
965 // Operations that can be done only once until the text changes.
966 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
974 // Make sure the model is up-to-date before layouting
975 UpdateModel( onlyOnceOperations );
977 // Operations that need to be done if the size changes.
978 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
982 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
983 static_cast<OperationsMask>( onlyOnceOperations |
985 naturalSize.GetVectorXY() );
987 // Do not do again the only once operations.
988 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
990 // Do the size related operations again.
991 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
993 // Stores the natural size to avoid recalculate it again
994 // unless the text/style changes.
995 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
997 mImpl->mRecalculateNaturalSize = false;
1001 naturalSize = mImpl->mVisualModel->GetNaturalSize();
1007 float Controller::GetHeightForWidth( float width )
1009 // Make sure the model is up-to-date before layouting
1010 ProcessModifyEvents();
1013 if( width != mImpl->mControlSize.width )
1015 // Operations that can be done only once until the text changes.
1016 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
1023 GET_GLYPH_METRICS );
1024 // Make sure the model is up-to-date before layouting
1025 UpdateModel( onlyOnceOperations );
1027 // Operations that need to be done if the size changes.
1028 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
1032 DoRelayout( Size( width, MAX_FLOAT ),
1033 static_cast<OperationsMask>( onlyOnceOperations |
1037 // Do not do again the only once operations.
1038 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1040 // Do the size related operations again.
1041 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1045 layoutSize = mImpl->mVisualModel->GetActualSize();
1048 return layoutSize.height;
1051 bool Controller::Relayout( const Size& size )
1053 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
1055 bool glyphsRemoved( false );
1056 if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
1058 mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
1059 glyphsRemoved = true;
1062 // Not worth to relayout if width or height is equal to zero.
1063 return glyphsRemoved;
1066 if( size != mImpl->mControlSize )
1068 // Operations that need to be done if the size changes.
1069 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1072 UPDATE_ACTUAL_SIZE |
1075 mImpl->mControlSize = size;
1078 // Make sure the model is up-to-date before layouting
1079 ProcessModifyEvents();
1080 UpdateModel( mImpl->mOperationsPending );
1083 bool updated = DoRelayout( mImpl->mControlSize,
1084 mImpl->mOperationsPending,
1087 // Do not re-do any operation until something changes.
1088 mImpl->mOperationsPending = NO_OPERATION;
1090 // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
1091 CalculateTextAlignment( size );
1093 if( mImpl->mTextInput )
1095 // Move the cursor, grab handle etc.
1096 updated = mImpl->mTextInput->ProcessInputEvents( mImpl->mControlSize, mImpl->mAlignmentOffset ) || updated;
1102 void Controller::ProcessModifyEvents()
1104 std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
1106 for( unsigned int i=0; i<events.size(); ++i )
1108 if( REPLACE_TEXT == events[0].type )
1110 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
1111 DALI_ASSERT_DEBUG( 0 == i && "Unexpected REPLACE event" );
1113 ReplaceTextEvent( events[0].text );
1115 else if( INSERT_TEXT == events[0].type )
1117 InsertTextEvent( events[0].text );
1119 else if( DELETE_TEXT == events[0].type )
1125 // Discard temporary text
1129 void Controller::ReplaceTextEvent( const std::string& text )
1132 mImpl->mLogicalModel->mText.Clear();
1133 mImpl->mLogicalModel->mScriptRuns.Clear();
1134 mImpl->mLogicalModel->mFontRuns.Clear();
1135 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1136 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1137 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1138 mImpl->mLogicalModel->mCharacterDirections.Clear();
1139 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1140 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1141 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1142 mImpl->mVisualModel->mGlyphs.Clear();
1143 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1144 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1145 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1146 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1147 mImpl->mVisualModel->mGlyphPositions.Clear();
1148 mImpl->mVisualModel->mLines.Clear();
1150 // Convert text into UTF-32
1151 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1152 utf32Characters.Resize( text.size() );
1154 // This is a bit horrible but std::string returns a (signed) char*
1155 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1157 // Transform a text array encoded in utf8 into an array encoded in utf32.
1158 // It returns the actual number of characters.
1159 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1160 utf32Characters.Resize( characterCount );
1162 // Reset the cursor position
1163 if( mImpl->mTextInput )
1165 mImpl->mTextInput->mPrimaryCursorPosition = characterCount;
1166 // TODO - handle secondary cursor
1169 // The natural size needs to be re-calculated.
1170 mImpl->mRecalculateNaturalSize = true;
1172 // Apply modifications to the model
1173 mImpl->mOperationsPending = ALL_OPERATIONS;
1174 UpdateModel( ALL_OPERATIONS );
1175 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1177 UPDATE_ACTUAL_SIZE |
1181 void Controller::InsertTextEvent( const std::string& text )
1183 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1185 // TODO - Optimize this
1186 mImpl->mLogicalModel->mScriptRuns.Clear();
1187 mImpl->mLogicalModel->mFontRuns.Clear();
1188 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1189 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1190 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1191 mImpl->mLogicalModel->mCharacterDirections.Clear();
1192 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1193 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1194 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1195 mImpl->mVisualModel->mGlyphs.Clear();
1196 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1197 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1198 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1199 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1200 mImpl->mVisualModel->mGlyphPositions.Clear();
1201 mImpl->mVisualModel->mLines.Clear();
1203 // Convert text into UTF-32
1204 Vector<Character> utf32Characters;
1205 utf32Characters.Resize( text.size() );
1207 // This is a bit horrible but std::string returns a (signed) char*
1208 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1210 // Transform a text array encoded in utf8 into an array encoded in utf32.
1211 // It returns the actual number of characters.
1212 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1213 utf32Characters.Resize( characterCount );
1215 // Insert at current cursor position
1216 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1217 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1219 if( cursorIndex < modifyText.Count() )
1221 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.End() );
1225 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.End() );
1228 // Advance the cursor position
1231 // The natural size needs to be re-calculated.
1232 mImpl->mRecalculateNaturalSize = true;
1234 // Apply modifications to the model; TODO - Optimize this
1235 mImpl->mOperationsPending = ALL_OPERATIONS;
1236 UpdateModel( ALL_OPERATIONS );
1237 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1239 UPDATE_ACTUAL_SIZE |
1242 // Queue a cursor reposition event; this must wait until after DoRelayout()
1243 mImpl->mTextInput->mUpdateCursorPosition = true;
1246 void Controller::DeleteTextEvent()
1248 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1250 // TODO - Optimize this
1251 mImpl->mLogicalModel->mScriptRuns.Clear();
1252 mImpl->mLogicalModel->mFontRuns.Clear();
1253 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1254 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1255 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1256 mImpl->mLogicalModel->mCharacterDirections.Clear();
1257 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1258 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1259 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1260 mImpl->mVisualModel->mGlyphs.Clear();
1261 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1262 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1263 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1264 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1265 mImpl->mVisualModel->mGlyphPositions.Clear();
1266 mImpl->mVisualModel->mLines.Clear();
1268 // Delte at current cursor position
1269 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1270 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1272 if( cursorIndex > 0 &&
1273 cursorIndex-1 < modifyText.Count() )
1275 modifyText.Remove( modifyText.Begin() + cursorIndex - 1 );
1277 // Cursor position retreat
1281 // The natural size needs to be re-calculated.
1282 mImpl->mRecalculateNaturalSize = true;
1284 // Apply modifications to the model; TODO - Optimize this
1285 mImpl->mOperationsPending = ALL_OPERATIONS;
1286 UpdateModel( ALL_OPERATIONS );
1287 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1289 UPDATE_ACTUAL_SIZE |
1292 // Queue a cursor reposition event; this must wait until after DoRelayout()
1293 mImpl->mTextInput->mUpdateCursorPosition = true;
1296 void Controller::UpdateModel( OperationsMask operationsRequired )
1298 // Calculate the operations to be done.
1299 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1301 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1303 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
1305 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1306 if( GET_LINE_BREAKS & operations )
1308 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
1309 // calculate the bidirectional info for each 'paragraph'.
1310 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
1311 // is not shaped together).
1312 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
1314 SetLineBreakInfo( utf32Characters,
1318 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1319 if( GET_WORD_BREAKS & operations )
1321 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
1322 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
1324 SetWordBreakInfo( utf32Characters,
1328 const bool getScripts = GET_SCRIPTS & operations;
1329 const bool validateFonts = VALIDATE_FONTS & operations;
1331 Vector<ScriptRun>& scripts = mImpl->mLogicalModel->mScriptRuns;
1332 Vector<FontRun>& validFonts = mImpl->mLogicalModel->mFontRuns;
1334 if( getScripts || validateFonts )
1336 // Validates the fonts assigned by the application or assigns default ones.
1337 // It makes sure all the characters are going to be rendered by the correct font.
1338 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
1342 // Retrieves the scripts used in the text.
1343 multilanguageSupport.SetScripts( utf32Characters,
1350 if( 0u == validFonts.Count() )
1352 // Copy the requested font defaults received via the property system.
1353 // These may not be valid i.e. may not contain glyphs for the necessary scripts.
1354 GetDefaultFonts( validFonts, numberOfCharacters );
1357 // Validates the fonts. If there is a character with no assigned font it sets a default one.
1358 // After this call, fonts are validated.
1359 multilanguageSupport.ValidateFonts( utf32Characters,
1365 Vector<Character> mirroredUtf32Characters;
1366 bool textMirrored = false;
1367 if( BIDI_INFO & operations )
1369 // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
1370 // bidirectional info.
1372 Length numberOfParagraphs = 0u;
1374 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
1375 for( Length index = 0u; index < numberOfCharacters; ++index )
1377 if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
1379 ++numberOfParagraphs;
1383 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1384 bidirectionalInfo.Reserve( numberOfParagraphs );
1386 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1387 SetBidirectionalInfo( utf32Characters,
1390 bidirectionalInfo );
1392 if( 0u != bidirectionalInfo.Count() )
1394 // This paragraph has right to left text. Some characters may need to be mirrored.
1395 // TODO: consider if the mirrored string can be stored as well.
1397 textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
1399 // Only set the character directions if there is right to left characters.
1400 Vector<CharacterDirection>& directions = mImpl->mLogicalModel->mCharacterDirections;
1401 directions.Resize( numberOfCharacters );
1403 GetCharactersDirection( bidirectionalInfo,
1408 // There is no right to left characters. Clear the directions vector.
1409 mImpl->mLogicalModel->mCharacterDirections.Clear();
1414 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1415 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1416 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1417 if( SHAPE_TEXT & operations )
1419 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1421 ShapeText( textToShape,
1426 glyphsToCharactersMap,
1427 charactersPerGlyph );
1429 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1430 mImpl->mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
1431 mImpl->mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
1434 const Length numberOfGlyphs = glyphs.Count();
1436 if( GET_GLYPH_METRICS & operations )
1438 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
1442 bool Controller::DoRelayout( const Size& size,
1443 OperationsMask operationsRequired,
1446 bool viewUpdated( false );
1448 // Calculate the operations to be done.
1449 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1451 if( LAYOUT & operations )
1453 // Some vectors with data needed to layout and reorder may be void
1454 // after the first time the text has been laid out.
1455 // Fill the vectors again.
1457 Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
1459 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1460 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1461 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1462 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1463 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1465 // Set the layout parameters.
1466 LayoutParameters layoutParameters( size,
1467 mImpl->mLogicalModel->mText.Begin(),
1468 lineBreakInfo.Begin(),
1469 wordBreakInfo.Begin(),
1472 glyphsToCharactersMap.Begin(),
1473 charactersPerGlyph.Begin() );
1475 // The laid-out lines.
1476 // It's not possible to know in how many lines the text is going to be laid-out,
1477 // but it can be resized at least with the number of 'paragraphs' to avoid
1478 // some re-allocations.
1479 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1481 // Delete any previous laid out lines before setting the new ones.
1484 // The capacity of the bidirectional paragraph info is the number of paragraphs.
1485 lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
1487 // Resize the vector of positions to have the same size than the vector of glyphs.
1488 Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
1489 glyphPositions.Resize( numberOfGlyphs );
1491 // Update the visual model.
1492 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
1499 // Reorder the lines
1500 if( REORDER & operations )
1502 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1504 // Check first if there are paragraphs with bidirectional info.
1505 if( 0u != bidirectionalInfo.Count() )
1508 const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
1510 // Reorder the lines.
1511 Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
1512 lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
1513 ReorderLines( bidirectionalInfo,
1515 lineBidirectionalInfoRuns );
1517 // Set the bidirectional info into the model.
1518 const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
1519 mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
1520 numberOfBidirectionalInfoRuns );
1522 // Set the bidirectional info per line into the layout parameters.
1523 layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
1524 layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
1526 // Get the character to glyph conversion table and set into the layout.
1527 layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
1529 // Get the glyphs per character table and set into the layout.
1530 layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
1532 // Re-layout the text. Reorder those lines with right to left characters.
1533 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1536 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
1537 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
1538 endIt = lineBidirectionalInfoRuns.End();
1542 BidirectionalLineInfoRun& bidiLineInfo = *it;
1544 free( bidiLineInfo.visualToLogicalMap );
1549 if( ALIGN & operations )
1551 mImpl->mLayoutEngine.Align( layoutParameters,
1557 // Sets the actual size.
1558 if( UPDATE_ACTUAL_SIZE & operations )
1560 mImpl->mVisualModel->SetActualSize( layoutSize );
1566 layoutSize = mImpl->mVisualModel->GetActualSize();
1572 void Controller::CalculateTextAlignment( const Size& size )
1574 // Get the direction of the first character.
1575 const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
1577 const Size& actualSize = mImpl->mVisualModel->GetActualSize();
1579 // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1580 LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
1581 if( firstParagraphDirection &&
1582 ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
1584 if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
1586 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
1590 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
1594 switch( horizontalAlignment )
1596 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1598 mImpl->mAlignmentOffset.x = 0.f;
1601 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1603 const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1604 mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1607 case LayoutEngine::HORIZONTAL_ALIGN_END:
1609 mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1614 const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1615 switch( verticalAlignment )
1617 case LayoutEngine::VERTICAL_ALIGN_TOP:
1619 mImpl->mAlignmentOffset.y = 0.f;
1622 case LayoutEngine::VERTICAL_ALIGN_CENTER:
1624 const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
1625 mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
1628 case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1630 mImpl->mAlignmentOffset.y = size.height - actualSize.height;
1636 View& Controller::GetView()
1638 return mImpl->mView;
1641 LayoutEngine& Controller::GetLayoutEngine()
1643 return mImpl->mLayoutEngine;
1646 void Controller::RequestRelayout()
1648 mImpl->mControlInterface.RequestTextRelayout();
1651 void Controller::KeyboardFocusGainEvent()
1653 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
1655 if( mImpl->mTextInput )
1657 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
1658 mImpl->mTextInput->mEventQueue.push_back( event );
1664 void Controller::KeyboardFocusLostEvent()
1666 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
1668 if( mImpl->mTextInput )
1670 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
1671 mImpl->mTextInput->mEventQueue.push_back( event );
1677 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1679 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" );
1681 if( mImpl->mTextInput &&
1682 keyEvent.state == KeyEvent::Down )
1684 int keyCode = keyEvent.keyCode;
1685 const std::string& keyString = keyEvent.keyPressed;
1687 // Pre-process to separate modifying events from non-modifying input events.
1688 if( Dali::DALI_KEY_ESCAPE == keyCode )
1690 // Escape key is a special case which causes focus loss
1691 KeyboardFocusLostEvent();
1693 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
1694 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1695 Dali::DALI_KEY_CURSOR_UP == keyCode ||
1696 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1698 TextInput::Event event( TextInput::CURSOR_KEY_EVENT );
1699 event.p1.mInt = keyCode;
1700 mImpl->mTextInput->mEventQueue.push_back( event );
1702 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1704 // Queue a delete event
1706 event.type = DELETE_TEXT;
1707 mImpl->mModifyEvents.push_back( event );
1709 else if( !keyString.empty() )
1711 // Queue an insert event
1713 event.type = INSERT_TEXT;
1714 event.text = keyString;
1715 mImpl->mModifyEvents.push_back( event );
1718 mImpl->mTextInput->ChangeState( TextInput::EDITING ); // todo Confirm this is the best place to change the state of
1726 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1728 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
1730 if( mImpl->mTextInput )
1732 TextInput::Event event( TextInput::TAP_EVENT );
1733 event.p1.mUint = tapCount;
1734 event.p2.mFloat = x;
1735 event.p3.mFloat = y;
1736 mImpl->mTextInput->mEventQueue.push_back( event );
1742 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1744 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected PanEvent" );
1746 if( mImpl->mTextInput )
1748 TextInput::Event event( TextInput::PAN_EVENT );
1749 event.p1.mInt = state;
1750 event.p2.mFloat = displacement.x;
1751 event.p3.mFloat = displacement.y;
1752 mImpl->mTextInput->mEventQueue.push_back( event );
1758 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
1760 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
1762 if( mImpl->mTextInput )
1764 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
1765 event.p1.mUint = state;
1766 event.p2.mFloat = x;
1767 event.p3.mFloat = y;
1768 mImpl->mTextInput->mEventQueue.push_back( event );
1774 Controller::~Controller()
1779 Controller::Controller( ControlInterface& controlInterface )
1782 mImpl = new Controller::Impl( controlInterface );
1787 } // namespace Toolkit