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 );
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 );
329 //mDecorator->HidePopup();
330 ChangeState ( EDITING );
331 mDecoratorUpdated = true;
333 else if ( mGrabHandlePopupEnabled &&
334 GRAB_HANDLE_RELEASED == state )
336 //mDecorator->ShowPopup();
337 ChangeState ( EDITING_WITH_POPUP );
338 mDecoratorUpdated = true;
342 void RepositionSelectionHandles( float visualX, float visualY )
344 // TODO - Find which word was selected
346 const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
347 const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
349 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
350 const Vector<Vector2>::SizeType positionCount = positions.Count();
352 // Guard against glyphs which did not fit inside the layout
353 const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
357 float primaryX = positions[0].x;
358 float secondaryX = positions[count-1].x + glyphs[count-1].width;
360 // TODO - multi-line selection
361 const Vector<LineRun>& lines = mVisualModel->mLines;
362 float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
364 mDecorator->SetPosition( PRIMARY_SELECTION_HANDLE, primaryX, 0.0f, height );
365 mDecorator->SetPosition( SECONDARY_SELECTION_HANDLE, secondaryX, 0.0f, height );
367 mDecorator->ClearHighlights();
368 mDecorator->AddHighlight( primaryX, 0.0f, secondaryX, height );
372 void ChangeState( State newState )
374 if( mState != newState )
378 if( INACTIVE == mState )
380 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
381 mDecorator->StopCursorBlink();
382 mDecorator->SetGrabHandleActive( false );
383 mDecorator->SetSelectionActive( false );
384 mDecorator->SetPopupActive( false );
385 mDecoratorUpdated = true;
387 else if ( SELECTING == mState )
389 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
390 mDecorator->StopCursorBlink();
391 mDecorator->SetGrabHandleActive( false );
392 mDecorator->SetSelectionActive( true );
393 mDecoratorUpdated = true;
395 else if( EDITING == mState )
397 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
398 if( mCursorBlinkEnabled )
400 mDecorator->StartCursorBlink();
402 if( mGrabHandleEnabled )
404 mDecorator->SetGrabHandleActive( true );
406 if( mGrabHandlePopupEnabled )
408 mDecorator->SetPopupActive( false );
410 mDecorator->SetSelectionActive( false );
411 mDecoratorUpdated = true;
413 else if( EDITING_WITH_POPUP == mState )
415 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
416 if( mCursorBlinkEnabled )
418 mDecorator->StartCursorBlink();
420 if( mGrabHandleEnabled )
422 mDecorator->SetGrabHandleActive( true );
424 if( mGrabHandlePopupEnabled )
426 mDecorator->SetPopupActive( true );
428 mDecorator->SetSelectionActive( false );
429 mDecoratorUpdated = true;
434 LineIndex GetClosestLine( float y )
436 LineIndex lineIndex( 0u );
438 const Vector<LineRun>& lines = mVisualModel->mLines;
439 for( float totalHeight = 0; lineIndex < lines.Count(); ++lineIndex )
441 const LineRun& lineRun = lines[lineIndex];
442 totalHeight += lineRun.ascender + -lineRun.descender;
443 if( y < totalHeight )
452 void GetClosestCursorPosition( CharacterIndex& logical, float& visualX, float& visualY, float& height )
454 Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
455 Length numberOfLines = mVisualModel->mLines.Count();
456 if( 0 == numberOfGlyphs ||
462 // Transform to visual model coords
463 visualX -= mScrollPosition.x;
464 visualY -= mScrollPosition.y;
466 // Find which line is closest
467 LineIndex lineIndex( GetClosestLine( visualY ) );
469 const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
470 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
472 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
473 const Vector2* const positionsBuffer = positions.Begin();
475 unsigned int closestGlyph = 0;
476 bool leftOfGlyph( false ); // which side of the glyph?
477 float closestDistance = MAX_FLOAT;
479 const LineRun& line = mVisualModel->mLines[lineIndex];
480 GlyphIndex startGlyph = line.glyphIndex;
481 GlyphIndex endGlyph = line.glyphIndex + line.numberOfGlyphs;
482 DALI_ASSERT_DEBUG( endGlyph <= glyphs.Count() && "Invalid line info" );
484 for( GlyphIndex i = startGlyph; i < endGlyph; ++i )
486 const GlyphInfo& glyphInfo = *( glyphsBuffer + i );
487 const Vector2& position = *( positionsBuffer + i );
488 float glyphX = position.x + glyphInfo.width*0.5f;
489 float glyphY = position.y + glyphInfo.height*0.5f;
491 float distanceToGlyph = fabsf( glyphX - visualX ) + fabsf( glyphY - visualY );
493 if( distanceToGlyph < closestDistance )
495 closestDistance = distanceToGlyph;
497 leftOfGlyph = ( visualX < glyphX );
501 // Calculate the logical position
502 logical = mVisualModel->GetCharacterIndex( closestGlyph );
504 // Returns the visual position of the glyph
505 visualX = positions[closestGlyph].x;
508 visualX += glyphs[closestGlyph].width;
513 else// if ( RTL ) TODO
519 height = line.ascender + -line.descender;
522 void UpdateCursorPosition()
524 if( 0 == mVisualModel->mGlyphs.Count() )
529 // FIXME GetGlyphIndex() is behaving strangely
531 GlyphIndex cursorGlyph = mVisualModel->GetGlyphIndex( mPrimaryCursorPosition );
533 GlyphIndex cursorGlyph( 0u );
534 for( cursorGlyph = 0; cursorGlyph < mVisualModel->mGlyphs.Count(); ++cursorGlyph )
536 if( mPrimaryCursorPosition == mVisualModel->GetCharacterIndex( cursorGlyph ) )
543 float visualX( 0.0f );
544 float visualY( 0.0f );
545 float height( 0.0f );
546 const Vector<LineRun>& lineRuns = mVisualModel->mLines;
548 if( cursorGlyph > 0 )
552 visualX = mVisualModel->mGlyphPositions[ cursorGlyph ].x;
554 visualX += mVisualModel->mGlyphs[ cursorGlyph ].width;
556 // Find the line height
557 GlyphIndex lastGlyph( 0 );
558 for( LineIndex lineIndex = 0u; lineIndex < lineRuns.Count(); ++lineIndex )
560 lastGlyph = (lineRuns[lineIndex].glyphIndex + lineRuns[lineIndex].numberOfGlyphs);
561 if( cursorGlyph < lastGlyph )
563 const LineRun& lineRun = lineRuns[lineIndex];
564 height = lineRun.ascender + -lineRun.descender;
570 mDecorator->SetPosition( PRIMARY_CURSOR, visualX, visualY, height );
571 mDecoratorUpdated = true;
574 LogicalModelPtr mLogicalModel;
575 VisualModelPtr mVisualModel;
576 DecoratorPtr mDecorator;
578 std::string mPlaceholderText;
581 * This is used to delay handling events until after the model has been updated.
582 * The number of updates to the model is minimized to improve performance.
584 vector<Event> mEventQueue; ///< The queue of touch events etc.
586 State mState; ///< Selection mode, edit mode etc.
588 CharacterIndex mPrimaryCursorPosition; ///< Index into logical model for primary cursor
589 CharacterIndex mSecondaryCursorPosition; ///< Index into logical model for secondary cursor
592 * 0,0 means that the top-left corner of the layout matches the top-left corner of the UI control.
593 * Typically this will have a negative value with scrolling occurs.
595 Vector2 mScrollPosition; ///< The text is offset by this position when scrolling.
597 bool mDecoratorUpdated : 1; ///< True if the decorator was updated during event processing
598 bool mCursorBlinkEnabled : 1; ///< True if cursor should blink when active
599 bool mGrabHandleEnabled : 1; ///< True if grab handle is enabled
600 bool mGrabHandlePopupEnabled : 1; ///< True if the grab handle popu-up should be shown
601 bool mSelectionEnabled : 1; ///< True if selection handles, highlight etc. are enabled
602 bool mHorizontalScrollingEnabled : 1; ///< True if horizontal scrolling is enabled
603 bool mVerticalScrollingEnabled : 1; ///< True if vertical scrolling is enabled
604 bool mUpdateCursorPosition : 1; ///< True if the visual position of the cursor must be recalculated
607 struct Controller::FontDefaults
610 : mDefaultPointSize(0.0f),
615 FontId GetFontId( TextAbstraction::FontClient& fontClient )
619 Dali::TextAbstraction::PointSize26Dot6 pointSize = mDefaultPointSize*64;
620 mFontId = fontClient.GetFontId( mDefaultFontFamily, mDefaultFontStyle, pointSize );
626 std::string mDefaultFontFamily;
627 std::string mDefaultFontStyle;
628 float mDefaultPointSize;
632 struct Controller::Impl
634 Impl( ControlInterface& controlInterface )
635 : mControlInterface( controlInterface ),
638 mFontDefaults( NULL ),
646 mOperationsPending( NO_OPERATION ),
647 mRecalculateNaturalSize( true )
649 mLogicalModel = LogicalModel::New();
650 mVisualModel = VisualModel::New();
652 mFontClient = TextAbstraction::FontClient::Get();
654 mView.SetVisualModel( mVisualModel );
656 // Set the text properties to default
657 mVisualModel->SetTextColor( Color::WHITE );
658 mVisualModel->SetShadowOffset( Vector2::ZERO );
659 mVisualModel->SetShadowColor( Vector4::ZERO );
660 mVisualModel->SetUnderlineEnabled( false );
668 ControlInterface& mControlInterface; ///< Reference to the text controller.
669 LogicalModelPtr mLogicalModel; ///< Pointer to the logical model.
670 VisualModelPtr mVisualModel; ///< Pointer to the visual model.
671 FontDefaults* mFontDefaults; ///< Avoid allocating this when the user does not specify a font.
672 Controller::TextInput* mTextInput; ///< Avoid allocating everything for text input until EnableTextInput().
673 TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
674 View mView; ///< The view interface to the rendering back-end.
675 LayoutEngine mLayoutEngine; ///< The layout engine.
676 std::vector<ModifyEvent> mModifyEvents; ///< Temporary stores the text set until the next relayout.
677 Size mControlSize; ///< The size of the control.
678 Vector2 mAlignmentOffset; ///< Vertical and horizontal offset of the whole text inside the control due to alignment.
679 OperationsMask mOperationsPending; ///< Operations pending to be done to layout the text.
680 bool mRecalculateNaturalSize:1; ///< Whether the natural size needs to be recalculated.
683 ControllerPtr Controller::New( ControlInterface& controlInterface )
685 return ControllerPtr( new Controller( controlInterface ) );
688 void Controller::SetText( const std::string& text )
690 // Cancel previously queued inserts etc.
691 mImpl->mModifyEvents.clear();
693 // Keep until size negotiation
695 event.type = REPLACE_TEXT;
697 mImpl->mModifyEvents.push_back( event );
699 if( mImpl->mTextInput )
701 // Cancel previously queued events
702 mImpl->mTextInput->mEventQueue.clear();
704 // TODO - Hide selection decorations
708 void Controller::GetText( std::string& text ) const
710 if( !mImpl->mModifyEvents.empty() &&
711 REPLACE_TEXT == mImpl->mModifyEvents[0].type )
713 text = mImpl->mModifyEvents[0].text;
717 // TODO - Convert from UTF-32
721 void Controller::SetPlaceholderText( const std::string& text )
723 if( !mImpl->mTextInput )
725 mImpl->mTextInput->mPlaceholderText = text;
729 void Controller::GetPlaceholderText( std::string& text ) const
731 if( !mImpl->mTextInput )
733 text = mImpl->mTextInput->mPlaceholderText;
737 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
739 if( !mImpl->mFontDefaults )
741 mImpl->mFontDefaults = new Controller::FontDefaults();
744 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
745 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
746 mImpl->mOperationsPending = ALL_OPERATIONS;
747 mImpl->mRecalculateNaturalSize = true;
749 // Clear the font-specific data
750 mImpl->mLogicalModel->mFontRuns.Clear();
751 mImpl->mVisualModel->mGlyphs.Clear();
752 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
753 mImpl->mVisualModel->mCharactersToGlyph.Clear();
754 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
755 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
756 mImpl->mVisualModel->mGlyphPositions.Clear();
757 mImpl->mVisualModel->mLines.Clear();
762 const std::string& Controller::GetDefaultFontFamily() const
764 if( mImpl->mFontDefaults )
766 return mImpl->mFontDefaults->mDefaultFontFamily;
772 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
774 if( !mImpl->mFontDefaults )
776 mImpl->mFontDefaults = new Controller::FontDefaults();
779 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
780 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
781 mImpl->mOperationsPending = ALL_OPERATIONS;
782 mImpl->mRecalculateNaturalSize = true;
784 // Clear the font-specific data
785 mImpl->mLogicalModel->mFontRuns.Clear();
786 mImpl->mVisualModel->mGlyphs.Clear();
787 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
788 mImpl->mVisualModel->mCharactersToGlyph.Clear();
789 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
790 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
791 mImpl->mVisualModel->mGlyphPositions.Clear();
792 mImpl->mVisualModel->mLines.Clear();
797 const std::string& Controller::GetDefaultFontStyle() const
799 if( mImpl->mFontDefaults )
801 return mImpl->mFontDefaults->mDefaultFontStyle;
807 void Controller::SetDefaultPointSize( float pointSize )
809 if( !mImpl->mFontDefaults )
811 mImpl->mFontDefaults = new Controller::FontDefaults();
814 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
815 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
816 mImpl->mOperationsPending = ALL_OPERATIONS;
817 mImpl->mRecalculateNaturalSize = true;
819 // Clear the font-specific data
820 mImpl->mLogicalModel->mFontRuns.Clear();
821 mImpl->mVisualModel->mGlyphs.Clear();
822 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
823 mImpl->mVisualModel->mCharactersToGlyph.Clear();
824 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
825 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
826 mImpl->mVisualModel->mGlyphPositions.Clear();
827 mImpl->mVisualModel->mLines.Clear();
832 float Controller::GetDefaultPointSize() const
834 if( mImpl->mFontDefaults )
836 return mImpl->mFontDefaults->mDefaultPointSize;
842 void Controller::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
844 if( mImpl->mFontDefaults )
847 fontRun.characterRun.characterIndex = 0;
848 fontRun.characterRun.numberOfCharacters = numberOfCharacters;
849 fontRun.fontId = mImpl->mFontDefaults->GetFontId( mImpl->mFontClient );
850 fontRun.isDefault = true;
852 fonts.PushBack( fontRun );
856 const Vector4& Controller::GetTextColor() const
858 return mImpl->mVisualModel->GetTextColor();
861 const Vector2& Controller::GetShadowOffset() const
863 return mImpl->mVisualModel->GetShadowOffset();
866 const Vector4& Controller::GetShadowColor() const
868 return mImpl->mVisualModel->GetShadowColor();
871 const Vector4& Controller::GetUnderlineColor() const
873 return mImpl->mVisualModel->GetUnderlineColor();
876 bool Controller::IsUnderlineEnabled() const
878 return mImpl->mVisualModel->IsUnderlineEnabled();
881 void Controller::SetTextColor( const Vector4& textColor )
883 mImpl->mVisualModel->SetTextColor( textColor );
886 void Controller::SetShadowOffset( const Vector2& shadowOffset )
888 mImpl->mVisualModel->SetShadowOffset( shadowOffset );
891 void Controller::SetShadowColor( const Vector4& shadowColor )
893 mImpl->mVisualModel->SetShadowColor( shadowColor );
896 void Controller::SetUnderlineColor( const Vector4& color )
898 mImpl->mVisualModel->SetUnderlineColor( color );
901 void Controller::SetUnderlineEnabled( bool enabled )
903 mImpl->mVisualModel->SetUnderlineEnabled( enabled );
906 void Controller::EnableTextInput( DecoratorPtr decorator )
908 if( !mImpl->mTextInput )
910 mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
914 void Controller::SetEnableCursorBlink( bool enable )
916 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "TextInput disabled" );
918 if( mImpl->mTextInput )
920 mImpl->mTextInput->mCursorBlinkEnabled = enable;
923 mImpl->mTextInput->mDecorator )
925 mImpl->mTextInput->mDecorator->StopCursorBlink();
930 bool Controller::GetEnableCursorBlink() const
932 if( mImpl->mTextInput )
934 return mImpl->mTextInput->mCursorBlinkEnabled;
940 const Vector2& Controller::GetScrollPosition() const
942 if( mImpl->mTextInput )
944 return mImpl->mTextInput->mScrollPosition;
947 return Vector2::ZERO;
950 const Vector2& Controller::GetAlignmentOffset() const
952 return mImpl->mAlignmentOffset;
955 Vector3 Controller::GetNaturalSize()
959 // Make sure the model is up-to-date before layouting
960 ProcessModifyEvents();
962 if( mImpl->mRecalculateNaturalSize )
964 // Operations that can be done only once until the text changes.
965 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
973 // Make sure the model is up-to-date before layouting
974 UpdateModel( onlyOnceOperations );
976 // Operations that need to be done if the size changes.
977 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
981 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
982 static_cast<OperationsMask>( onlyOnceOperations |
984 naturalSize.GetVectorXY() );
986 // Do not do again the only once operations.
987 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
989 // Do the size related operations again.
990 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
992 // Stores the natural size to avoid recalculate it again
993 // unless the text/style changes.
994 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
996 mImpl->mRecalculateNaturalSize = false;
1000 naturalSize = mImpl->mVisualModel->GetNaturalSize();
1006 float Controller::GetHeightForWidth( float width )
1008 // Make sure the model is up-to-date before layouting
1009 ProcessModifyEvents();
1012 if( width != mImpl->mControlSize.width )
1014 // Operations that can be done only once until the text changes.
1015 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
1022 GET_GLYPH_METRICS );
1023 // Make sure the model is up-to-date before layouting
1024 UpdateModel( onlyOnceOperations );
1026 // Operations that need to be done if the size changes.
1027 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
1031 DoRelayout( Size( width, MAX_FLOAT ),
1032 static_cast<OperationsMask>( onlyOnceOperations |
1036 // Do not do again the only once operations.
1037 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1039 // Do the size related operations again.
1040 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1044 layoutSize = mImpl->mVisualModel->GetActualSize();
1047 return layoutSize.height;
1050 bool Controller::Relayout( const Size& size )
1052 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
1054 bool glyphsRemoved( false );
1055 if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
1057 mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
1058 glyphsRemoved = true;
1061 // Not worth to relayout if width or height is equal to zero.
1062 return glyphsRemoved;
1065 if( size != mImpl->mControlSize )
1067 // Operations that need to be done if the size changes.
1068 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1071 UPDATE_ACTUAL_SIZE |
1074 mImpl->mControlSize = size;
1077 // Make sure the model is up-to-date before layouting
1078 ProcessModifyEvents();
1079 UpdateModel( mImpl->mOperationsPending );
1082 bool updated = DoRelayout( mImpl->mControlSize,
1083 mImpl->mOperationsPending,
1086 // Do not re-do any operation until something changes.
1087 mImpl->mOperationsPending = NO_OPERATION;
1089 // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
1090 CalculateTextAlignment( size );
1092 if( mImpl->mTextInput )
1094 // Move the cursor, grab handle etc.
1095 updated = mImpl->mTextInput->ProcessInputEvents( mImpl->mControlSize, mImpl->mAlignmentOffset ) || updated;
1101 void Controller::ProcessModifyEvents()
1103 std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
1105 for( unsigned int i=0; i<events.size(); ++i )
1107 if( REPLACE_TEXT == events[0].type )
1109 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
1110 DALI_ASSERT_DEBUG( 0 == i && "Unexpected REPLACE event" );
1112 ReplaceTextEvent( events[0].text );
1114 else if( INSERT_TEXT == events[0].type )
1116 InsertTextEvent( events[0].text );
1118 else if( DELETE_TEXT == events[0].type )
1124 // Discard temporary text
1128 void Controller::ReplaceTextEvent( const std::string& text )
1131 mImpl->mLogicalModel->mText.Clear();
1132 mImpl->mLogicalModel->mScriptRuns.Clear();
1133 mImpl->mLogicalModel->mFontRuns.Clear();
1134 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1135 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1136 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1137 mImpl->mLogicalModel->mCharacterDirections.Clear();
1138 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1139 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1140 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1141 mImpl->mVisualModel->mGlyphs.Clear();
1142 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1143 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1144 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1145 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1146 mImpl->mVisualModel->mGlyphPositions.Clear();
1147 mImpl->mVisualModel->mLines.Clear();
1149 // Convert text into UTF-32
1150 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1151 utf32Characters.Resize( text.size() );
1153 // This is a bit horrible but std::string returns a (signed) char*
1154 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1156 // Transform a text array encoded in utf8 into an array encoded in utf32.
1157 // It returns the actual number of characters.
1158 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1159 utf32Characters.Resize( characterCount );
1161 // Reset the cursor position
1162 if( mImpl->mTextInput )
1164 mImpl->mTextInput->mPrimaryCursorPosition = characterCount;
1165 // TODO - handle secondary cursor
1168 // The natural size needs to be re-calculated.
1169 mImpl->mRecalculateNaturalSize = true;
1171 // Apply modifications to the model
1172 mImpl->mOperationsPending = ALL_OPERATIONS;
1173 UpdateModel( ALL_OPERATIONS );
1174 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1176 UPDATE_ACTUAL_SIZE |
1180 void Controller::InsertTextEvent( const std::string& text )
1182 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1184 // TODO - Optimize this
1185 mImpl->mLogicalModel->mScriptRuns.Clear();
1186 mImpl->mLogicalModel->mFontRuns.Clear();
1187 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1188 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1189 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1190 mImpl->mLogicalModel->mCharacterDirections.Clear();
1191 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1192 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1193 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1194 mImpl->mVisualModel->mGlyphs.Clear();
1195 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1196 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1197 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1198 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1199 mImpl->mVisualModel->mGlyphPositions.Clear();
1200 mImpl->mVisualModel->mLines.Clear();
1202 // Convert text into UTF-32
1203 Vector<Character> utf32Characters;
1204 utf32Characters.Resize( text.size() );
1206 // This is a bit horrible but std::string returns a (signed) char*
1207 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1209 // Transform a text array encoded in utf8 into an array encoded in utf32.
1210 // It returns the actual number of characters.
1211 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1212 utf32Characters.Resize( characterCount );
1214 // Insert at current cursor position
1215 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1216 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1218 if( cursorIndex < modifyText.Count() )
1220 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.End() );
1224 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.End() );
1227 // Advance the cursor position
1230 // The natural size needs to be re-calculated.
1231 mImpl->mRecalculateNaturalSize = true;
1233 // Apply modifications to the model; TODO - Optimize this
1234 mImpl->mOperationsPending = ALL_OPERATIONS;
1235 UpdateModel( ALL_OPERATIONS );
1236 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1238 UPDATE_ACTUAL_SIZE |
1241 // Queue a cursor reposition event; this must wait until after DoRelayout()
1242 mImpl->mTextInput->mUpdateCursorPosition = true;
1245 void Controller::DeleteTextEvent()
1247 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1249 // TODO - Optimize this
1250 mImpl->mLogicalModel->mScriptRuns.Clear();
1251 mImpl->mLogicalModel->mFontRuns.Clear();
1252 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1253 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1254 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1255 mImpl->mLogicalModel->mCharacterDirections.Clear();
1256 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1257 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1258 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1259 mImpl->mVisualModel->mGlyphs.Clear();
1260 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1261 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1262 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1263 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1264 mImpl->mVisualModel->mGlyphPositions.Clear();
1265 mImpl->mVisualModel->mLines.Clear();
1267 // Delte at current cursor position
1268 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1269 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1271 if( cursorIndex > 0 &&
1272 cursorIndex-1 < modifyText.Count() )
1274 modifyText.Remove( modifyText.Begin() + cursorIndex - 1 );
1276 // Cursor position retreat
1280 // The natural size needs to be re-calculated.
1281 mImpl->mRecalculateNaturalSize = true;
1283 // Apply modifications to the model; TODO - Optimize this
1284 mImpl->mOperationsPending = ALL_OPERATIONS;
1285 UpdateModel( ALL_OPERATIONS );
1286 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1288 UPDATE_ACTUAL_SIZE |
1291 // Queue a cursor reposition event; this must wait until after DoRelayout()
1292 mImpl->mTextInput->mUpdateCursorPosition = true;
1295 void Controller::UpdateModel( OperationsMask operationsRequired )
1297 // Calculate the operations to be done.
1298 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1300 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1302 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
1304 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1305 if( GET_LINE_BREAKS & operations )
1307 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
1308 // calculate the bidirectional info for each 'paragraph'.
1309 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
1310 // is not shaped together).
1311 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
1313 SetLineBreakInfo( utf32Characters,
1317 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1318 if( GET_WORD_BREAKS & operations )
1320 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
1321 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
1323 SetWordBreakInfo( utf32Characters,
1327 const bool getScripts = GET_SCRIPTS & operations;
1328 const bool validateFonts = VALIDATE_FONTS & operations;
1330 Vector<ScriptRun>& scripts = mImpl->mLogicalModel->mScriptRuns;
1331 Vector<FontRun>& validFonts = mImpl->mLogicalModel->mFontRuns;
1333 if( getScripts || validateFonts )
1335 // Validates the fonts assigned by the application or assigns default ones.
1336 // It makes sure all the characters are going to be rendered by the correct font.
1337 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
1341 // Retrieves the scripts used in the text.
1342 multilanguageSupport.SetScripts( utf32Characters,
1349 if( 0u == validFonts.Count() )
1351 // Copy the requested font defaults received via the property system.
1352 // These may not be valid i.e. may not contain glyphs for the necessary scripts.
1353 GetDefaultFonts( validFonts, numberOfCharacters );
1356 // Validates the fonts. If there is a character with no assigned font it sets a default one.
1357 // After this call, fonts are validated.
1358 multilanguageSupport.ValidateFonts( utf32Characters,
1364 Vector<Character> mirroredUtf32Characters;
1365 bool textMirrored = false;
1366 if( BIDI_INFO & operations )
1368 // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
1369 // bidirectional info.
1371 Length numberOfParagraphs = 0u;
1373 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
1374 for( Length index = 0u; index < numberOfCharacters; ++index )
1376 if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
1378 ++numberOfParagraphs;
1382 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1383 bidirectionalInfo.Reserve( numberOfParagraphs );
1385 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1386 SetBidirectionalInfo( utf32Characters,
1389 bidirectionalInfo );
1391 if( 0u != bidirectionalInfo.Count() )
1393 // This paragraph has right to left text. Some characters may need to be mirrored.
1394 // TODO: consider if the mirrored string can be stored as well.
1396 textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
1398 // Only set the character directions if there is right to left characters.
1399 Vector<CharacterDirection>& directions = mImpl->mLogicalModel->mCharacterDirections;
1400 directions.Resize( numberOfCharacters );
1402 GetCharactersDirection( bidirectionalInfo,
1407 // There is no right to left characters. Clear the directions vector.
1408 mImpl->mLogicalModel->mCharacterDirections.Clear();
1413 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1414 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1415 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1416 if( SHAPE_TEXT & operations )
1418 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1420 ShapeText( textToShape,
1425 glyphsToCharactersMap,
1426 charactersPerGlyph );
1428 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1429 mImpl->mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
1430 mImpl->mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
1433 const Length numberOfGlyphs = glyphs.Count();
1435 if( GET_GLYPH_METRICS & operations )
1437 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
1441 bool Controller::DoRelayout( const Size& size,
1442 OperationsMask operationsRequired,
1445 bool viewUpdated( false );
1447 // Calculate the operations to be done.
1448 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1450 if( LAYOUT & operations )
1452 // Some vectors with data needed to layout and reorder may be void
1453 // after the first time the text has been laid out.
1454 // Fill the vectors again.
1456 Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
1458 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1459 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1460 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1461 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1462 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1464 // Set the layout parameters.
1465 LayoutParameters layoutParameters( size,
1466 mImpl->mLogicalModel->mText.Begin(),
1467 lineBreakInfo.Begin(),
1468 wordBreakInfo.Begin(),
1471 glyphsToCharactersMap.Begin(),
1472 charactersPerGlyph.Begin() );
1474 // The laid-out lines.
1475 // It's not possible to know in how many lines the text is going to be laid-out,
1476 // but it can be resized at least with the number of 'paragraphs' to avoid
1477 // some re-allocations.
1478 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1480 // Delete any previous laid out lines before setting the new ones.
1483 // The capacity of the bidirectional paragraph info is the number of paragraphs.
1484 lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
1486 // Resize the vector of positions to have the same size than the vector of glyphs.
1487 Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
1488 glyphPositions.Resize( numberOfGlyphs );
1490 // Update the visual model.
1491 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
1498 // Reorder the lines
1499 if( REORDER & operations )
1501 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1503 // Check first if there are paragraphs with bidirectional info.
1504 if( 0u != bidirectionalInfo.Count() )
1507 const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
1509 // Reorder the lines.
1510 Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
1511 lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
1512 ReorderLines( bidirectionalInfo,
1514 lineBidirectionalInfoRuns );
1516 // Set the bidirectional info into the model.
1517 const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
1518 mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
1519 numberOfBidirectionalInfoRuns );
1521 // Set the bidirectional info per line into the layout parameters.
1522 layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
1523 layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
1525 // Get the character to glyph conversion table and set into the layout.
1526 layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
1528 // Get the glyphs per character table and set into the layout.
1529 layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
1531 // Re-layout the text. Reorder those lines with right to left characters.
1532 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1535 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
1536 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
1537 endIt = lineBidirectionalInfoRuns.End();
1541 BidirectionalLineInfoRun& bidiLineInfo = *it;
1543 free( bidiLineInfo.visualToLogicalMap );
1548 if( ALIGN & operations )
1550 mImpl->mLayoutEngine.Align( layoutParameters,
1556 // Sets the actual size.
1557 if( UPDATE_ACTUAL_SIZE & operations )
1559 mImpl->mVisualModel->SetActualSize( layoutSize );
1565 layoutSize = mImpl->mVisualModel->GetActualSize();
1571 void Controller::CalculateTextAlignment( const Size& size )
1573 // Get the direction of the first character.
1574 const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
1576 const Size& actualSize = mImpl->mVisualModel->GetActualSize();
1578 // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1579 LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
1580 if( firstParagraphDirection &&
1581 ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
1583 if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
1585 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
1589 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
1593 switch( horizontalAlignment )
1595 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1597 mImpl->mAlignmentOffset.x = 0.f;
1600 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1602 const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1603 mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1606 case LayoutEngine::HORIZONTAL_ALIGN_END:
1608 mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1613 const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1614 switch( verticalAlignment )
1616 case LayoutEngine::VERTICAL_ALIGN_TOP:
1618 mImpl->mAlignmentOffset.y = 0.f;
1621 case LayoutEngine::VERTICAL_ALIGN_CENTER:
1623 const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
1624 mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
1627 case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1629 mImpl->mAlignmentOffset.y = size.height - actualSize.height;
1635 View& Controller::GetView()
1637 return mImpl->mView;
1640 LayoutEngine& Controller::GetLayoutEngine()
1642 return mImpl->mLayoutEngine;
1645 void Controller::RequestRelayout()
1647 mImpl->mControlInterface.RequestTextRelayout();
1650 void Controller::KeyboardFocusGainEvent()
1652 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
1654 if( mImpl->mTextInput )
1656 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
1657 mImpl->mTextInput->mEventQueue.push_back( event );
1663 void Controller::KeyboardFocusLostEvent()
1665 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
1667 if( mImpl->mTextInput )
1669 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
1670 mImpl->mTextInput->mEventQueue.push_back( event );
1676 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1678 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" );
1680 if( mImpl->mTextInput &&
1681 keyEvent.state == KeyEvent::Down )
1683 int keyCode = keyEvent.keyCode;
1684 const std::string& keyString = keyEvent.keyPressed;
1686 // Pre-process to separate modifying events from non-modifying input events.
1687 if( Dali::DALI_KEY_ESCAPE == keyCode )
1689 // Escape key is a special case which causes focus loss
1690 KeyboardFocusLostEvent();
1692 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
1693 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1694 Dali::DALI_KEY_CURSOR_UP == keyCode ||
1695 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1697 TextInput::Event event( TextInput::CURSOR_KEY_EVENT );
1698 event.p1.mInt = keyCode;
1699 mImpl->mTextInput->mEventQueue.push_back( event );
1701 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1703 // Queue a delete event
1705 event.type = DELETE_TEXT;
1706 mImpl->mModifyEvents.push_back( event );
1708 else if( !keyString.empty() )
1710 // Queue an insert event
1712 event.type = INSERT_TEXT;
1713 event.text = keyString;
1714 mImpl->mModifyEvents.push_back( event );
1717 mImpl->mTextInput->ChangeState( TextInput::EDITING ); // todo Confirm this is the best place to change the state of
1725 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1727 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
1729 if( mImpl->mTextInput )
1731 TextInput::Event event( TextInput::TAP_EVENT );
1732 event.p1.mUint = tapCount;
1733 event.p2.mFloat = x;
1734 event.p3.mFloat = y;
1735 mImpl->mTextInput->mEventQueue.push_back( event );
1741 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1743 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected PanEvent" );
1745 if( mImpl->mTextInput )
1747 TextInput::Event event( TextInput::PAN_EVENT );
1748 event.p1.mInt = state;
1749 event.p2.mFloat = displacement.x;
1750 event.p3.mFloat = displacement.y;
1751 mImpl->mTextInput->mEventQueue.push_back( event );
1757 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
1759 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
1761 if( mImpl->mTextInput )
1763 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
1764 event.p1.mUint = state;
1765 event.p2.mFloat = x;
1766 event.p3.mFloat = y;
1767 mImpl->mTextInput->mEventQueue.push_back( event );
1773 Controller::~Controller()
1778 Controller::Controller( ControlInterface& controlInterface )
1781 mImpl = new Controller::Impl( controlInterface );
1786 } // namespace Toolkit