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::FontDefaults
76 : mDefaultPointSize(0.0f),
81 FontId GetFontId( TextAbstraction::FontClient& fontClient )
85 Dali::TextAbstraction::PointSize26Dot6 pointSize = mDefaultPointSize*64;
86 mFontId = fontClient.GetFontId( mDefaultFontFamily, mDefaultFontStyle, pointSize );
92 std::string mDefaultFontFamily;
93 std::string mDefaultFontStyle;
94 float mDefaultPointSize;
98 struct Controller::TextInput
100 // Used to queue input events until DoRelayout()
103 KEYBOARD_FOCUS_GAIN_EVENT,
104 KEYBOARD_FOCUS_LOST_EVENT,
120 Event( EventType eventType )
141 TextInput( LogicalModelPtr logicalModel,
142 VisualModelPtr visualModel,
143 DecoratorPtr decorator )
144 : mLogicalModel( logicalModel ),
145 mVisualModel( visualModel ),
146 mDecorator( decorator ),
148 mPrimaryCursorPosition( 0u ),
149 mSecondaryCursorPosition( 0u ),
150 mDecoratorUpdated( false ),
151 mCursorBlinkEnabled( true ),
152 mGrabHandleEnabled( true ),
153 mGrabHandlePopupEnabled( true ),
154 mSelectionEnabled( true ),
155 mHorizontalScrollingEnabled( true ),
156 mVerticalScrollingEnabled( false ),
157 mUpdateCursorPosition( false )
162 * @brief Helper to move the cursor, grab handle etc.
164 bool ProcessInputEvents( const Vector2& controlSize,
165 const Vector2& alignmentOffset )
167 mDecoratorUpdated = false;
171 for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
175 case KEYBOARD_FOCUS_GAIN_EVENT:
177 OnKeyboardFocus( true );
180 case KEYBOARD_FOCUS_LOST_EVENT:
182 OnKeyboardFocus( false );
185 case CURSOR_KEY_EVENT:
187 OnCursorKeyEvent( *iter );
192 OnTapEvent( *iter, alignmentOffset );
197 OnPanEvent( *iter, controlSize, alignmentOffset );
200 case GRAB_HANDLE_EVENT:
202 OnGrabHandleEvent( *iter );
209 // The cursor must also be repositioned after inserts into the model
210 if( mUpdateCursorPosition )
212 UpdateCursorPosition();
213 mUpdateCursorPosition = false;
218 return mDecoratorUpdated;
221 void OnKeyboardFocus( bool hasFocus )
225 ChangeState( INACTIVE );
229 ChangeState( EDITING );
233 void OnCursorKeyEvent( const Event& event )
235 int keyCode = event.p1.mInt;
237 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
241 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
245 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
249 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
255 void HandleCursorKey( int keyCode )
260 void OnTapEvent( const Event& event,
261 const Vector2& alignmentOffset )
263 unsigned int tapCount = event.p1.mUint;
267 ChangeState( EDITING );
269 float xPosition = event.p2.mFloat - alignmentOffset.x;
270 float yPosition = event.p3.mFloat - alignmentOffset.y;
272 GetClosestCursorPosition( mPrimaryCursorPosition, xPosition, yPosition, height );
273 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height, height ); // TODO: To be fixed in the next patch.
274 mUpdateCursorPosition = false;
276 mDecoratorUpdated = true;
278 else if( mSelectionEnabled &&
281 ChangeState( SELECTING );
283 RepositionSelectionHandles( event.p2.mFloat, event.p3.mFloat );
287 void OnPanEvent( const Event& event,
288 const Vector2& controlSize,
289 const Vector2& alignmentOffset )
291 int state = event.p1.mInt;
293 if( Gesture::Started == state ||
294 Gesture::Continuing == state )
296 const Vector2& actualSize = mVisualModel->GetActualSize();
298 if( mHorizontalScrollingEnabled )
300 const float displacementX = event.p2.mFloat;
301 mScrollPosition.x += displacementX;
303 // Clamp between -space & 0 (and the text alignment).
304 const float contentWidth = actualSize.width;
305 if( contentWidth > controlSize.width )
307 const float space = ( contentWidth - controlSize.width ) + alignmentOffset.x;
308 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
309 mScrollPosition.x = ( mScrollPosition.x > -alignmentOffset.x ) ? -alignmentOffset.x : mScrollPosition.x;
311 mDecoratorUpdated = true;
315 mScrollPosition.x = 0.f;
319 if( mVerticalScrollingEnabled )
321 const float displacementY = event.p3.mFloat;
322 mScrollPosition.y += displacementY;
324 // Clamp between -space & 0 (and the text alignment).
325 if( actualSize.height > controlSize.height )
327 const float space = ( actualSize.height - controlSize.height ) + alignmentOffset.y;
328 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
329 mScrollPosition.y = ( mScrollPosition.y > -alignmentOffset.y ) ? -alignmentOffset.y : mScrollPosition.y;
331 mDecoratorUpdated = true;
335 mScrollPosition.y = 0.f;
341 void OnGrabHandleEvent( const Event& event )
343 unsigned int state = event.p1.mUint;
345 if( GRAB_HANDLE_PRESSED == state )
347 float xPosition = event.p2.mFloat;
348 float yPosition = event.p3.mFloat;
351 GetClosestCursorPosition( mPrimaryCursorPosition, xPosition, yPosition, height );
353 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height, height ); // TODO: To be fixed in the next patch.
355 //mDecorator->HidePopup();
356 ChangeState ( EDITING );
357 mDecoratorUpdated = true;
359 else if ( mGrabHandlePopupEnabled &&
360 GRAB_HANDLE_RELEASED == state )
362 //mDecorator->ShowPopup();
363 ChangeState ( EDITING_WITH_POPUP );
364 mDecoratorUpdated = true;
368 void RepositionSelectionHandles( float visualX, float visualY )
370 // TODO - Find which word was selected
372 const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
373 const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
375 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
376 const Vector<Vector2>::SizeType positionCount = positions.Count();
378 // Guard against glyphs which did not fit inside the layout
379 const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
383 float primaryX = positions[0].x;
384 float secondaryX = positions[count-1].x + glyphs[count-1].width;
386 // TODO - multi-line selection
387 const Vector<LineRun>& lines = mVisualModel->mLines;
388 float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
390 mDecorator->SetPosition( PRIMARY_SELECTION_HANDLE, primaryX, 0.0f, height );
391 mDecorator->SetPosition( SECONDARY_SELECTION_HANDLE, secondaryX, 0.0f, height );
393 mDecorator->ClearHighlights();
394 mDecorator->AddHighlight( primaryX, 0.0f, secondaryX, height );
398 void ChangeState( State newState )
400 if( mState != newState )
404 if( INACTIVE == mState )
406 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
407 mDecorator->StopCursorBlink();
408 mDecorator->SetGrabHandleActive( false );
409 mDecorator->SetSelectionActive( false );
410 mDecorator->SetPopupActive( false );
411 mDecoratorUpdated = true;
413 else if ( SELECTING == mState )
415 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
416 mDecorator->StopCursorBlink();
417 mDecorator->SetGrabHandleActive( false );
418 mDecorator->SetSelectionActive( true );
419 mDecoratorUpdated = true;
421 else if( EDITING == mState )
423 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
424 if( mCursorBlinkEnabled )
426 mDecorator->StartCursorBlink();
428 if( mGrabHandleEnabled )
430 mDecorator->SetGrabHandleActive( true );
432 if( mGrabHandlePopupEnabled )
434 mDecorator->SetPopupActive( false );
436 mDecorator->SetSelectionActive( false );
437 mDecoratorUpdated = true;
439 else if( EDITING_WITH_POPUP == mState )
441 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
442 if( mCursorBlinkEnabled )
444 mDecorator->StartCursorBlink();
446 if( mGrabHandleEnabled )
448 mDecorator->SetGrabHandleActive( true );
450 if( mGrabHandlePopupEnabled )
452 mDecorator->SetPopupActive( true );
454 mDecorator->SetSelectionActive( false );
455 mDecoratorUpdated = true;
460 LineIndex GetClosestLine( float y )
462 LineIndex lineIndex( 0u );
464 const Vector<LineRun>& lines = mVisualModel->mLines;
465 for( float totalHeight = 0; lineIndex < lines.Count(); ++lineIndex )
467 const LineRun& lineRun = lines[lineIndex];
468 totalHeight += lineRun.ascender + -lineRun.descender;
469 if( y < totalHeight )
478 void GetClosestCursorPosition( CharacterIndex& logical, float& visualX, float& visualY, float& height )
480 Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
481 Length numberOfLines = mVisualModel->mLines.Count();
482 if( 0 == numberOfGlyphs ||
488 // Transform to visual model coords
489 visualX -= mScrollPosition.x;
490 visualY -= mScrollPosition.y;
492 // Find which line is closest
493 LineIndex lineIndex( GetClosestLine( visualY ) );
495 const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
496 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
498 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
499 const Vector2* const positionsBuffer = positions.Begin();
501 unsigned int closestGlyph = 0;
502 bool leftOfGlyph( false ); // which side of the glyph?
503 float closestDistance = MAX_FLOAT;
505 const LineRun& line = mVisualModel->mLines[lineIndex];
506 GlyphIndex startGlyph = line.glyphIndex;
507 GlyphIndex endGlyph = line.glyphIndex + line.numberOfGlyphs;
508 DALI_ASSERT_DEBUG( endGlyph <= glyphs.Count() && "Invalid line info" );
510 for( GlyphIndex i = startGlyph; i < endGlyph; ++i )
512 const GlyphInfo& glyphInfo = *( glyphsBuffer + i );
513 const Vector2& position = *( positionsBuffer + i );
514 float glyphX = position.x + glyphInfo.width*0.5f;
515 float glyphY = position.y + glyphInfo.height*0.5f;
517 float distanceToGlyph = fabsf( glyphX - visualX ) + fabsf( glyphY - visualY );
519 if( distanceToGlyph < closestDistance )
521 closestDistance = distanceToGlyph;
523 leftOfGlyph = ( visualX < glyphX );
527 // Calculate the logical position
528 logical = mVisualModel->GetCharacterIndex( closestGlyph );
530 // Returns the visual position of the glyph
531 visualX = positions[closestGlyph].x;
534 visualX += glyphs[closestGlyph].width;
539 else// if ( RTL ) TODO
545 height = line.ascender + -line.descender;
548 void UpdateCursorPosition()
550 if( 0 == mVisualModel->mGlyphs.Count() )
555 // FIXME GetGlyphIndex() is behaving strangely
557 GlyphIndex cursorGlyph = mVisualModel->GetGlyphIndex( mPrimaryCursorPosition );
559 GlyphIndex cursorGlyph( 0u );
560 for( cursorGlyph = 0; cursorGlyph < mVisualModel->mGlyphs.Count(); ++cursorGlyph )
562 if( mPrimaryCursorPosition == mVisualModel->GetCharacterIndex( cursorGlyph ) )
569 float visualX( 0.0f );
570 float visualY( 0.0f );
571 float height( 0.0f );
572 const Vector<LineRun>& lineRuns = mVisualModel->mLines;
574 if( cursorGlyph > 0 )
578 visualX = mVisualModel->mGlyphPositions[ cursorGlyph ].x;
580 visualX += mVisualModel->mGlyphs[ cursorGlyph ].width;
582 // Find the line height
583 GlyphIndex lastGlyph( 0 );
584 for( LineIndex lineIndex = 0u; lineIndex < lineRuns.Count(); ++lineIndex )
586 lastGlyph = (lineRuns[lineIndex].glyphIndex + lineRuns[lineIndex].numberOfGlyphs);
587 if( cursorGlyph < lastGlyph )
589 const LineRun& lineRun = lineRuns[lineIndex];
590 height = lineRun.ascender + -lineRun.descender;
596 mDecorator->SetPosition( PRIMARY_CURSOR, visualX, visualY, height, height ); // TODO: To be fixed in the next patch.
597 mDecoratorUpdated = true;
600 LogicalModelPtr mLogicalModel;
601 VisualModelPtr mVisualModel;
602 DecoratorPtr mDecorator;
604 std::string mPlaceholderText;
607 * This is used to delay handling events until after the model has been updated.
608 * The number of updates to the model is minimized to improve performance.
610 vector<Event> mEventQueue; ///< The queue of touch events etc.
612 State mState; ///< Selection mode, edit mode etc.
614 CharacterIndex mPrimaryCursorPosition; ///< Index into logical model for primary cursor
615 CharacterIndex mSecondaryCursorPosition; ///< Index into logical model for secondary cursor
618 * 0,0 means that the top-left corner of the layout matches the top-left corner of the UI control.
619 * Typically this will have a negative value with scrolling occurs.
621 Vector2 mScrollPosition; ///< The text is offset by this position when scrolling.
623 bool mDecoratorUpdated : 1; ///< True if the decorator was updated during event processing
624 bool mCursorBlinkEnabled : 1; ///< True if cursor should blink when active
625 bool mGrabHandleEnabled : 1; ///< True if grab handle is enabled
626 bool mGrabHandlePopupEnabled : 1; ///< True if the grab handle popu-up should be shown
627 bool mSelectionEnabled : 1; ///< True if selection handles, highlight etc. are enabled
628 bool mHorizontalScrollingEnabled : 1; ///< True if horizontal scrolling is enabled
629 bool mVerticalScrollingEnabled : 1; ///< True if vertical scrolling is enabled
630 bool mUpdateCursorPosition : 1; ///< True if the visual position of the cursor must be recalculated
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