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].lineSize.height : 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 totalHeight += lines[lineIndex].lineSize.height;
442 if( y < totalHeight )
451 void GetClosestCursorPosition( CharacterIndex& logical, float& visualX, float& visualY, float& height )
453 Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
454 Length numberOfLines = mVisualModel->mLines.Count();
455 if( 0 == numberOfGlyphs ||
461 // Transform to visual model coords
462 visualX -= mScrollPosition.x;
463 visualY -= mScrollPosition.y;
465 // Find which line is closest
466 LineIndex lineIndex( GetClosestLine( visualY ) );
468 const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
469 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
471 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
472 const Vector2* const positionsBuffer = positions.Begin();
474 unsigned int closestGlyph = 0;
475 bool leftOfGlyph( false ); // which side of the glyph?
476 float closestDistance = MAX_FLOAT;
478 const LineRun& line = mVisualModel->mLines[lineIndex];
479 GlyphIndex startGlyph = line.glyphIndex;
480 GlyphIndex endGlyph = line.glyphIndex + line.numberOfGlyphs;
481 DALI_ASSERT_DEBUG( endGlyph <= glyphs.Count() && "Invalid line info" );
483 for( GlyphIndex i = startGlyph; i < endGlyph; ++i )
485 const GlyphInfo& glyphInfo = *( glyphsBuffer + i );
486 const Vector2& position = *( positionsBuffer + i );
487 float glyphX = position.x + glyphInfo.width*0.5f;
488 float glyphY = position.y + glyphInfo.height*0.5f;
490 float distanceToGlyph = fabsf( glyphX - visualX ) + fabsf( glyphY - visualY );
492 if( distanceToGlyph < closestDistance )
494 closestDistance = distanceToGlyph;
496 leftOfGlyph = ( visualX < glyphX );
500 // Calculate the logical position
501 logical = mVisualModel->GetCharacterIndex( closestGlyph );
503 // Returns the visual position of the glyph
504 visualX = positions[closestGlyph].x;
507 visualX += glyphs[closestGlyph].width;
512 else// if ( RTL ) TODO
518 height = line.lineSize.height;
521 void UpdateCursorPosition()
523 if( 0 == mVisualModel->mGlyphs.Count() )
528 // FIXME GetGlyphIndex() is behaving strangely
530 GlyphIndex cursorGlyph = mVisualModel->GetGlyphIndex( mPrimaryCursorPosition );
532 GlyphIndex cursorGlyph( 0u );
533 for( cursorGlyph = 0; cursorGlyph < mVisualModel->mGlyphs.Count(); ++cursorGlyph )
535 if( mPrimaryCursorPosition == mVisualModel->GetCharacterIndex( cursorGlyph ) )
542 float visualX( 0.0f );
543 float visualY( 0.0f );
544 float height( 0.0f );
545 const Vector<LineRun>& lineRuns = mVisualModel->mLines;
547 if( cursorGlyph > 0 )
551 visualX = mVisualModel->mGlyphPositions[ cursorGlyph ].x;
553 visualX += mVisualModel->mGlyphs[ cursorGlyph ].width;
555 // Find the line height
556 GlyphIndex lastGlyph( 0 );
557 for( LineIndex lineIndex = 0u; lineIndex < lineRuns.Count(); ++lineIndex )
559 lastGlyph = (lineRuns[lineIndex].glyphIndex + lineRuns[lineIndex].numberOfGlyphs);
560 if( cursorGlyph < lastGlyph )
562 height = lineRuns[lineIndex].lineSize.height;
568 mDecorator->SetPosition( PRIMARY_CURSOR, visualX, visualY, height );
569 mDecoratorUpdated = true;
572 LogicalModelPtr mLogicalModel;
573 VisualModelPtr mVisualModel;
574 DecoratorPtr mDecorator;
576 std::string mPlaceholderText;
579 * This is used to delay handling events until after the model has been updated.
580 * The number of updates to the model is minimized to improve performance.
582 vector<Event> mEventQueue; ///< The queue of touch events etc.
584 State mState; ///< Selection mode, edit mode etc.
586 CharacterIndex mPrimaryCursorPosition; ///< Index into logical model for primary cursor
587 CharacterIndex mSecondaryCursorPosition; ///< Index into logical model for secondary cursor
590 * 0,0 means that the top-left corner of the layout matches the top-left corner of the UI control.
591 * Typically this will have a negative value with scrolling occurs.
593 Vector2 mScrollPosition; ///< The text is offset by this position when scrolling.
595 bool mDecoratorUpdated : 1; ///< True if the decorator was updated during event processing
596 bool mCursorBlinkEnabled : 1; ///< True if cursor should blink when active
597 bool mGrabHandleEnabled : 1; ///< True if grab handle is enabled
598 bool mGrabHandlePopupEnabled : 1; ///< True if the grab handle popu-up should be shown
599 bool mSelectionEnabled : 1; ///< True if selection handles, highlight etc. are enabled
600 bool mHorizontalScrollingEnabled : 1; ///< True if horizontal scrolling is enabled
601 bool mVerticalScrollingEnabled : 1; ///< True if vertical scrolling is enabled
602 bool mUpdateCursorPosition : 1; ///< True if the visual position of the cursor must be recalculated
605 struct Controller::FontDefaults
608 : mDefaultPointSize(0.0f),
613 FontId GetFontId( TextAbstraction::FontClient& fontClient )
617 Dali::TextAbstraction::PointSize26Dot6 pointSize = mDefaultPointSize*64;
618 mFontId = fontClient.GetFontId( mDefaultFontFamily, mDefaultFontStyle, pointSize );
624 std::string mDefaultFontFamily;
625 std::string mDefaultFontStyle;
626 float mDefaultPointSize;
630 struct Controller::Impl
632 Impl( ControlInterface& controlInterface )
633 : mControlInterface( controlInterface ),
636 mFontDefaults( NULL ),
644 mOperationsPending( NO_OPERATION ),
645 mRecalculateNaturalSize( true )
647 mLogicalModel = LogicalModel::New();
648 mVisualModel = VisualModel::New();
650 mFontClient = TextAbstraction::FontClient::Get();
652 mView.SetVisualModel( mVisualModel );
654 // Set the text properties to default
655 mVisualModel->SetTextColor( Color::WHITE );
656 mVisualModel->SetShadowOffset( Vector2::ZERO );
657 mVisualModel->SetShadowColor( Vector4::ZERO );
658 mVisualModel->SetUnderlineEnabled( false );
666 ControlInterface& mControlInterface; ///< Reference to the text controller.
667 LogicalModelPtr mLogicalModel; ///< Pointer to the logical model.
668 VisualModelPtr mVisualModel; ///< Pointer to the visual model.
669 FontDefaults* mFontDefaults; ///< Avoid allocating this when the user does not specify a font.
670 Controller::TextInput* mTextInput; ///< Avoid allocating everything for text input until EnableTextInput().
671 TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
672 View mView; ///< The view interface to the rendering back-end.
673 LayoutEngine mLayoutEngine; ///< The layout engine.
674 std::vector<ModifyEvent> mModifyEvents; ///< Temporary stores the text set until the next relayout.
675 Size mControlSize; ///< The size of the control.
676 Vector2 mAlignmentOffset; ///< Vertical and horizontal offset of the whole text inside the control due to alignment.
677 OperationsMask mOperationsPending; ///< Operations pending to be done to layout the text.
678 bool mRecalculateNaturalSize:1; ///< Whether the natural size needs to be recalculated.
681 ControllerPtr Controller::New( ControlInterface& controlInterface )
683 return ControllerPtr( new Controller( controlInterface ) );
686 void Controller::SetText( const std::string& text )
688 // Cancel previously queued inserts etc.
689 mImpl->mModifyEvents.clear();
691 // Keep until size negotiation
693 event.type = REPLACE_TEXT;
695 mImpl->mModifyEvents.push_back( event );
697 if( mImpl->mTextInput )
699 // Cancel previously queued events
700 mImpl->mTextInput->mEventQueue.clear();
702 // TODO - Hide selection decorations
706 void Controller::GetText( std::string& text ) const
708 if( !mImpl->mModifyEvents.empty() &&
709 REPLACE_TEXT == mImpl->mModifyEvents[0].type )
711 text = mImpl->mModifyEvents[0].text;
715 // TODO - Convert from UTF-32
719 void Controller::SetPlaceholderText( const std::string& text )
721 if( !mImpl->mTextInput )
723 mImpl->mTextInput->mPlaceholderText = text;
727 void Controller::GetPlaceholderText( std::string& text ) const
729 if( !mImpl->mTextInput )
731 text = mImpl->mTextInput->mPlaceholderText;
735 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
737 if( !mImpl->mFontDefaults )
739 mImpl->mFontDefaults = new Controller::FontDefaults();
742 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
743 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
744 mImpl->mOperationsPending = ALL_OPERATIONS;
745 mImpl->mRecalculateNaturalSize = true;
747 // Clear the font-specific data
748 mImpl->mLogicalModel->mFontRuns.Clear();
749 mImpl->mVisualModel->mGlyphs.Clear();
750 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
751 mImpl->mVisualModel->mCharactersToGlyph.Clear();
752 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
753 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
754 mImpl->mVisualModel->mGlyphPositions.Clear();
755 mImpl->mVisualModel->mLines.Clear();
760 const std::string& Controller::GetDefaultFontFamily() const
762 if( mImpl->mFontDefaults )
764 return mImpl->mFontDefaults->mDefaultFontFamily;
770 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
772 if( !mImpl->mFontDefaults )
774 mImpl->mFontDefaults = new Controller::FontDefaults();
777 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
778 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
779 mImpl->mOperationsPending = ALL_OPERATIONS;
780 mImpl->mRecalculateNaturalSize = true;
782 // Clear the font-specific data
783 mImpl->mLogicalModel->mFontRuns.Clear();
784 mImpl->mVisualModel->mGlyphs.Clear();
785 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
786 mImpl->mVisualModel->mCharactersToGlyph.Clear();
787 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
788 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
789 mImpl->mVisualModel->mGlyphPositions.Clear();
790 mImpl->mVisualModel->mLines.Clear();
795 const std::string& Controller::GetDefaultFontStyle() const
797 if( mImpl->mFontDefaults )
799 return mImpl->mFontDefaults->mDefaultFontStyle;
805 void Controller::SetDefaultPointSize( float pointSize )
807 if( !mImpl->mFontDefaults )
809 mImpl->mFontDefaults = new Controller::FontDefaults();
812 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
813 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
814 mImpl->mOperationsPending = ALL_OPERATIONS;
815 mImpl->mRecalculateNaturalSize = true;
817 // Clear the font-specific data
818 mImpl->mLogicalModel->mFontRuns.Clear();
819 mImpl->mVisualModel->mGlyphs.Clear();
820 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
821 mImpl->mVisualModel->mCharactersToGlyph.Clear();
822 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
823 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
824 mImpl->mVisualModel->mGlyphPositions.Clear();
825 mImpl->mVisualModel->mLines.Clear();
830 float Controller::GetDefaultPointSize() const
832 if( mImpl->mFontDefaults )
834 return mImpl->mFontDefaults->mDefaultPointSize;
840 void Controller::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
842 if( mImpl->mFontDefaults )
845 fontRun.characterRun.characterIndex = 0;
846 fontRun.characterRun.numberOfCharacters = numberOfCharacters;
847 fontRun.fontId = mImpl->mFontDefaults->GetFontId( mImpl->mFontClient );
848 fontRun.isDefault = true;
850 fonts.PushBack( fontRun );
854 const Vector4& Controller::GetTextColor() const
856 return mImpl->mVisualModel->GetTextColor();
859 const Vector2& Controller::GetShadowOffset() const
861 return mImpl->mVisualModel->GetShadowOffset();
864 const Vector4& Controller::GetShadowColor() const
866 return mImpl->mVisualModel->GetShadowColor();
869 const Vector4& Controller::GetUnderlineColor() const
871 return mImpl->mVisualModel->GetUnderlineColor();
874 bool Controller::IsUnderlineEnabled() const
876 return mImpl->mVisualModel->IsUnderlineEnabled();
879 void Controller::SetTextColor( const Vector4& textColor )
881 mImpl->mVisualModel->SetTextColor( textColor );
884 void Controller::SetShadowOffset( const Vector2& shadowOffset )
886 mImpl->mVisualModel->SetShadowOffset( shadowOffset );
889 void Controller::SetShadowColor( const Vector4& shadowColor )
891 mImpl->mVisualModel->SetShadowColor( shadowColor );
894 void Controller::SetUnderlineColor( const Vector4& color )
896 mImpl->mVisualModel->SetUnderlineColor( color );
899 void Controller::SetUnderlineEnabled( bool enabled )
901 mImpl->mVisualModel->SetUnderlineEnabled( enabled );
904 void Controller::EnableTextInput( DecoratorPtr decorator )
906 if( !mImpl->mTextInput )
908 mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
912 void Controller::SetEnableCursorBlink( bool enable )
914 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "TextInput disabled" );
916 if( mImpl->mTextInput )
918 mImpl->mTextInput->mCursorBlinkEnabled = enable;
921 mImpl->mTextInput->mDecorator )
923 mImpl->mTextInput->mDecorator->StopCursorBlink();
928 bool Controller::GetEnableCursorBlink() const
930 if( mImpl->mTextInput )
932 return mImpl->mTextInput->mCursorBlinkEnabled;
938 const Vector2& Controller::GetScrollPosition() const
940 if( mImpl->mTextInput )
942 return mImpl->mTextInput->mScrollPosition;
945 return Vector2::ZERO;
948 const Vector2& Controller::GetAlignmentOffset() const
950 return mImpl->mAlignmentOffset;
953 Vector3 Controller::GetNaturalSize()
957 // Make sure the model is up-to-date before layouting
958 ProcessModifyEvents();
960 if( mImpl->mRecalculateNaturalSize )
962 // Operations that can be done only once until the text changes.
963 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
971 // Make sure the model is up-to-date before layouting
972 UpdateModel( onlyOnceOperations );
974 // Operations that need to be done if the size changes.
975 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
979 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
980 static_cast<OperationsMask>( onlyOnceOperations |
982 naturalSize.GetVectorXY() );
984 // Do not do again the only once operations.
985 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
987 // Do the size related operations again.
988 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
990 // Stores the natural size to avoid recalculate it again
991 // unless the text/style changes.
992 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
994 mImpl->mRecalculateNaturalSize = false;
998 naturalSize = mImpl->mVisualModel->GetNaturalSize();
1004 float Controller::GetHeightForWidth( float width )
1006 // Make sure the model is up-to-date before layouting
1007 ProcessModifyEvents();
1010 if( width != mImpl->mControlSize.width )
1012 // Operations that can be done only once until the text changes.
1013 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
1020 GET_GLYPH_METRICS );
1021 // Make sure the model is up-to-date before layouting
1022 UpdateModel( onlyOnceOperations );
1024 // Operations that need to be done if the size changes.
1025 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
1029 DoRelayout( Size( width, MAX_FLOAT ),
1030 static_cast<OperationsMask>( onlyOnceOperations |
1034 // Do not do again the only once operations.
1035 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1037 // Do the size related operations again.
1038 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1042 layoutSize = mImpl->mVisualModel->GetActualSize();
1045 return layoutSize.height;
1048 bool Controller::Relayout( const Size& size )
1050 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
1052 bool glyphsRemoved( false );
1053 if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
1055 mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
1056 glyphsRemoved = true;
1059 // Not worth to relayout if width or height is equal to zero.
1060 return glyphsRemoved;
1063 if( size != mImpl->mControlSize )
1065 // Operations that need to be done if the size changes.
1066 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1069 UPDATE_ACTUAL_SIZE |
1072 mImpl->mControlSize = size;
1075 // Make sure the model is up-to-date before layouting
1076 ProcessModifyEvents();
1077 UpdateModel( mImpl->mOperationsPending );
1080 bool updated = DoRelayout( mImpl->mControlSize,
1081 mImpl->mOperationsPending,
1084 // Do not re-do any operation until something changes.
1085 mImpl->mOperationsPending = NO_OPERATION;
1087 // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
1088 CalculateTextAlignment( size );
1090 if( mImpl->mTextInput )
1092 // Move the cursor, grab handle etc.
1093 updated = mImpl->mTextInput->ProcessInputEvents( mImpl->mControlSize, mImpl->mAlignmentOffset ) || updated;
1099 void Controller::ProcessModifyEvents()
1101 std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
1103 for( unsigned int i=0; i<events.size(); ++i )
1105 if( REPLACE_TEXT == events[0].type )
1107 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
1108 DALI_ASSERT_DEBUG( 0 == i && "Unexpected REPLACE event" );
1110 ReplaceTextEvent( events[0].text );
1112 else if( INSERT_TEXT == events[0].type )
1114 InsertTextEvent( events[0].text );
1116 else if( DELETE_TEXT == events[0].type )
1122 // Discard temporary text
1126 void Controller::ReplaceTextEvent( const std::string& text )
1129 mImpl->mLogicalModel->mText.Clear();
1130 mImpl->mLogicalModel->mScriptRuns.Clear();
1131 mImpl->mLogicalModel->mFontRuns.Clear();
1132 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1133 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1134 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1135 mImpl->mLogicalModel->mCharacterDirections.Clear();
1136 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1137 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1138 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1139 mImpl->mVisualModel->mGlyphs.Clear();
1140 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1141 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1142 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1143 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1144 mImpl->mVisualModel->mGlyphPositions.Clear();
1145 mImpl->mVisualModel->mLines.Clear();
1147 // Convert text into UTF-32
1148 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1149 utf32Characters.Resize( text.size() );
1151 // This is a bit horrible but std::string returns a (signed) char*
1152 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1154 // Transform a text array encoded in utf8 into an array encoded in utf32.
1155 // It returns the actual number of characters.
1156 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1157 utf32Characters.Resize( characterCount );
1159 // Reset the cursor position
1160 if( mImpl->mTextInput )
1162 mImpl->mTextInput->mPrimaryCursorPosition = characterCount;
1163 // TODO - handle secondary cursor
1166 // The natural size needs to be re-calculated.
1167 mImpl->mRecalculateNaturalSize = true;
1169 // Apply modifications to the model
1170 mImpl->mOperationsPending = ALL_OPERATIONS;
1171 UpdateModel( ALL_OPERATIONS );
1172 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1174 UPDATE_ACTUAL_SIZE |
1178 void Controller::InsertTextEvent( const std::string& text )
1180 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1182 // TODO - Optimize this
1183 mImpl->mLogicalModel->mScriptRuns.Clear();
1184 mImpl->mLogicalModel->mFontRuns.Clear();
1185 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1186 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1187 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1188 mImpl->mLogicalModel->mCharacterDirections.Clear();
1189 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1190 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1191 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1192 mImpl->mVisualModel->mGlyphs.Clear();
1193 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1194 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1195 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1196 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1197 mImpl->mVisualModel->mGlyphPositions.Clear();
1198 mImpl->mVisualModel->mLines.Clear();
1200 // Convert text into UTF-32
1201 Vector<Character> utf32Characters;
1202 utf32Characters.Resize( text.size() );
1204 // This is a bit horrible but std::string returns a (signed) char*
1205 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1207 // Transform a text array encoded in utf8 into an array encoded in utf32.
1208 // It returns the actual number of characters.
1209 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1210 utf32Characters.Resize( characterCount );
1212 // Insert at current cursor position
1213 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1214 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1216 if( cursorIndex < modifyText.Count() )
1218 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.End() );
1222 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.End() );
1225 // Advance the cursor position
1228 // The natural size needs to be re-calculated.
1229 mImpl->mRecalculateNaturalSize = true;
1231 // Apply modifications to the model; TODO - Optimize this
1232 mImpl->mOperationsPending = ALL_OPERATIONS;
1233 UpdateModel( ALL_OPERATIONS );
1234 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1236 UPDATE_ACTUAL_SIZE |
1239 // Queue a cursor reposition event; this must wait until after DoRelayout()
1240 mImpl->mTextInput->mUpdateCursorPosition = true;
1243 void Controller::DeleteTextEvent()
1245 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1247 // TODO - Optimize this
1248 mImpl->mLogicalModel->mScriptRuns.Clear();
1249 mImpl->mLogicalModel->mFontRuns.Clear();
1250 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1251 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1252 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1253 mImpl->mLogicalModel->mCharacterDirections.Clear();
1254 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1255 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1256 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1257 mImpl->mVisualModel->mGlyphs.Clear();
1258 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1259 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1260 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1261 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1262 mImpl->mVisualModel->mGlyphPositions.Clear();
1263 mImpl->mVisualModel->mLines.Clear();
1265 // Delte at current cursor position
1266 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1267 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1269 if( cursorIndex > 0 &&
1270 cursorIndex-1 < modifyText.Count() )
1272 modifyText.Remove( modifyText.Begin() + cursorIndex - 1 );
1274 // Cursor position retreat
1278 // The natural size needs to be re-calculated.
1279 mImpl->mRecalculateNaturalSize = true;
1281 // Apply modifications to the model; TODO - Optimize this
1282 mImpl->mOperationsPending = ALL_OPERATIONS;
1283 UpdateModel( ALL_OPERATIONS );
1284 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1286 UPDATE_ACTUAL_SIZE |
1289 // Queue a cursor reposition event; this must wait until after DoRelayout()
1290 mImpl->mTextInput->mUpdateCursorPosition = true;
1293 void Controller::UpdateModel( OperationsMask operationsRequired )
1295 // Calculate the operations to be done.
1296 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1298 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1300 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
1302 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1303 if( GET_LINE_BREAKS & operations )
1305 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
1306 // calculate the bidirectional info for each 'paragraph'.
1307 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
1308 // is not shaped together).
1309 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
1311 SetLineBreakInfo( utf32Characters,
1315 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1316 if( GET_WORD_BREAKS & operations )
1318 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
1319 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
1321 SetWordBreakInfo( utf32Characters,
1325 const bool getScripts = GET_SCRIPTS & operations;
1326 const bool validateFonts = VALIDATE_FONTS & operations;
1328 Vector<ScriptRun>& scripts = mImpl->mLogicalModel->mScriptRuns;
1329 Vector<FontRun>& validFonts = mImpl->mLogicalModel->mFontRuns;
1331 if( getScripts || validateFonts )
1333 // Validates the fonts assigned by the application or assigns default ones.
1334 // It makes sure all the characters are going to be rendered by the correct font.
1335 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
1339 // Retrieves the scripts used in the text.
1340 multilanguageSupport.SetScripts( utf32Characters,
1347 if( 0u == validFonts.Count() )
1349 // Copy the requested font defaults received via the property system.
1350 // These may not be valid i.e. may not contain glyphs for the necessary scripts.
1351 GetDefaultFonts( validFonts, numberOfCharacters );
1354 // Validates the fonts. If there is a character with no assigned font it sets a default one.
1355 // After this call, fonts are validated.
1356 multilanguageSupport.ValidateFonts( utf32Characters,
1362 Vector<Character> mirroredUtf32Characters;
1363 bool textMirrored = false;
1364 if( BIDI_INFO & operations )
1366 // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
1367 // bidirectional info.
1369 Length numberOfParagraphs = 0u;
1371 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
1372 for( Length index = 0u; index < numberOfCharacters; ++index )
1374 if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
1376 ++numberOfParagraphs;
1380 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1381 bidirectionalInfo.Reserve( numberOfParagraphs );
1383 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1384 SetBidirectionalInfo( utf32Characters,
1387 bidirectionalInfo );
1389 if( 0u != bidirectionalInfo.Count() )
1391 // This paragraph has right to left text. Some characters may need to be mirrored.
1392 // TODO: consider if the mirrored string can be stored as well.
1394 textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
1396 // Only set the character directions if there is right to left characters.
1397 Vector<CharacterDirection>& directions = mImpl->mLogicalModel->mCharacterDirections;
1398 directions.Resize( numberOfCharacters );
1400 GetCharactersDirection( bidirectionalInfo,
1405 // There is no right to left characters. Clear the directions vector.
1406 mImpl->mLogicalModel->mCharacterDirections.Clear();
1411 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1412 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1413 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1414 if( SHAPE_TEXT & operations )
1416 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1418 ShapeText( textToShape,
1423 glyphsToCharactersMap,
1424 charactersPerGlyph );
1426 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1427 mImpl->mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
1428 mImpl->mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
1431 const Length numberOfGlyphs = glyphs.Count();
1433 if( GET_GLYPH_METRICS & operations )
1435 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
1439 bool Controller::DoRelayout( const Size& size,
1440 OperationsMask operationsRequired,
1443 bool viewUpdated( false );
1445 // Calculate the operations to be done.
1446 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1448 if( LAYOUT & operations )
1450 // Some vectors with data needed to layout and reorder may be void
1451 // after the first time the text has been laid out.
1452 // Fill the vectors again.
1454 Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
1456 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1457 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1458 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1459 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1460 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1462 // Set the layout parameters.
1463 LayoutParameters layoutParameters( size,
1464 mImpl->mLogicalModel->mText.Begin(),
1465 lineBreakInfo.Begin(),
1466 wordBreakInfo.Begin(),
1469 glyphsToCharactersMap.Begin(),
1470 charactersPerGlyph.Begin() );
1472 // The laid-out lines.
1473 // It's not possible to know in how many lines the text is going to be laid-out,
1474 // but it can be resized at least with the number of 'paragraphs' to avoid
1475 // some re-allocations.
1476 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1478 // Delete any previous laid out lines before setting the new ones.
1481 // The capacity of the bidirectional paragraph info is the number of paragraphs.
1482 lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
1484 // Resize the vector of positions to have the same size than the vector of glyphs.
1485 Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
1486 glyphPositions.Resize( numberOfGlyphs );
1488 // Update the visual model.
1489 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
1496 // Reorder the lines
1497 if( REORDER & operations )
1499 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1501 // Check first if there are paragraphs with bidirectional info.
1502 if( 0u != bidirectionalInfo.Count() )
1505 const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
1507 // Reorder the lines.
1508 Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
1509 lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
1510 ReorderLines( bidirectionalInfo,
1512 lineBidirectionalInfoRuns );
1514 // Set the bidirectional info into the model.
1515 const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
1516 mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
1517 numberOfBidirectionalInfoRuns );
1519 // Set the bidirectional info per line into the layout parameters.
1520 layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
1521 layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
1523 // Get the character to glyph conversion table and set into the layout.
1524 layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
1526 // Get the glyphs per character table and set into the layout.
1527 layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
1529 // Re-layout the text. Reorder those lines with right to left characters.
1530 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1533 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
1534 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
1535 endIt = lineBidirectionalInfoRuns.End();
1539 BidirectionalLineInfoRun& bidiLineInfo = *it;
1541 free( bidiLineInfo.visualToLogicalMap );
1546 if( ALIGN & operations )
1548 mImpl->mLayoutEngine.Align( layoutParameters,
1554 // Sets the actual size.
1555 if( UPDATE_ACTUAL_SIZE & operations )
1557 mImpl->mVisualModel->SetActualSize( layoutSize );
1563 layoutSize = mImpl->mVisualModel->GetActualSize();
1569 void Controller::CalculateTextAlignment( const Size& size )
1571 // Get the direction of the first character.
1572 const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
1574 const Size& actualSize = mImpl->mVisualModel->GetActualSize();
1576 // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1577 LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
1578 if( firstParagraphDirection &&
1579 ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
1581 if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
1583 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
1587 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
1591 switch( horizontalAlignment )
1593 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1595 mImpl->mAlignmentOffset.x = 0.f;
1598 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1600 const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1601 mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1604 case LayoutEngine::HORIZONTAL_ALIGN_END:
1606 mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1611 const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1612 switch( verticalAlignment )
1614 case LayoutEngine::VERTICAL_ALIGN_TOP:
1616 mImpl->mAlignmentOffset.y = 0.f;
1619 case LayoutEngine::VERTICAL_ALIGN_CENTER:
1621 const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
1622 mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
1625 case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1627 mImpl->mAlignmentOffset.y = size.height - actualSize.height;
1633 View& Controller::GetView()
1635 return mImpl->mView;
1638 LayoutEngine& Controller::GetLayoutEngine()
1640 return mImpl->mLayoutEngine;
1643 void Controller::RequestRelayout()
1645 mImpl->mControlInterface.RequestTextRelayout();
1648 void Controller::KeyboardFocusGainEvent()
1650 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
1652 if( mImpl->mTextInput )
1654 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
1655 mImpl->mTextInput->mEventQueue.push_back( event );
1661 void Controller::KeyboardFocusLostEvent()
1663 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
1665 if( mImpl->mTextInput )
1667 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
1668 mImpl->mTextInput->mEventQueue.push_back( event );
1674 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1676 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" );
1678 if( mImpl->mTextInput &&
1679 keyEvent.state == KeyEvent::Down )
1681 int keyCode = keyEvent.keyCode;
1682 const std::string& keyString = keyEvent.keyPressed;
1684 // Pre-process to separate modifying events from non-modifying input events.
1685 if( Dali::DALI_KEY_ESCAPE == keyCode )
1687 // Escape key is a special case which causes focus loss
1688 KeyboardFocusLostEvent();
1690 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
1691 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1692 Dali::DALI_KEY_CURSOR_UP == keyCode ||
1693 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1695 TextInput::Event event( TextInput::CURSOR_KEY_EVENT );
1696 event.p1.mInt = keyCode;
1697 mImpl->mTextInput->mEventQueue.push_back( event );
1699 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1701 // Queue a delete event
1703 event.type = DELETE_TEXT;
1704 mImpl->mModifyEvents.push_back( event );
1706 else if( !keyString.empty() )
1708 // Queue an insert event
1710 event.type = INSERT_TEXT;
1711 event.text = keyString;
1712 mImpl->mModifyEvents.push_back( event );
1715 mImpl->mTextInput->ChangeState( TextInput::EDITING ); // todo Confirm this is the best place to change the state of
1723 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1725 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
1727 if( mImpl->mTextInput )
1729 TextInput::Event event( TextInput::TAP_EVENT );
1730 event.p1.mUint = tapCount;
1731 event.p2.mFloat = x;
1732 event.p3.mFloat = y;
1733 mImpl->mTextInput->mEventQueue.push_back( event );
1739 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1741 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected PanEvent" );
1743 if( mImpl->mTextInput )
1745 TextInput::Event event( TextInput::PAN_EVENT );
1746 event.p1.mInt = state;
1747 event.p2.mFloat = displacement.x;
1748 event.p3.mFloat = displacement.y;
1749 mImpl->mTextInput->mEventQueue.push_back( event );
1755 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
1757 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
1759 if( mImpl->mTextInput )
1761 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
1762 event.p1.mUint = state;
1763 event.p2.mFloat = x;
1764 event.p3.mFloat = y;
1765 mImpl->mTextInput->mEventQueue.push_back( event );
1771 Controller::~Controller()
1776 Controller::Controller( ControlInterface& controlInterface )
1779 mImpl = new Controller::Impl( controlInterface );
1784 } // namespace Toolkit