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( false ),
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 );
260 void OnPanEvent( const Event& event,
261 const Vector2& controlSize,
262 const Vector2& alignmentOffset )
264 int state = event.p1.mInt;
266 if( Gesture::Started == state ||
267 Gesture::Continuing == state )
269 const Vector2& actualSize = mVisualModel->GetActualSize();
271 if( mHorizontalScrollingEnabled )
273 const float displacementX = event.p2.mFloat;
274 mScrollPosition.x += displacementX;
276 // Clamp between -space & 0 (and the text alignment).
277 const float contentWidth = actualSize.width;
278 if( contentWidth > controlSize.width )
280 const float space = ( contentWidth - controlSize.width ) + alignmentOffset.x;
281 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
282 mScrollPosition.x = ( mScrollPosition.x > -alignmentOffset.x ) ? -alignmentOffset.x : mScrollPosition.x;
284 mDecoratorUpdated = true;
288 mScrollPosition.x = 0.f;
292 if( mVerticalScrollingEnabled )
294 const float displacementY = event.p3.mFloat;
295 mScrollPosition.y += displacementY;
297 // Clamp between -space & 0 (and the text alignment).
298 if( actualSize.height > controlSize.height )
300 const float space = ( actualSize.height - controlSize.height ) + alignmentOffset.y;
301 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
302 mScrollPosition.y = ( mScrollPosition.y > -alignmentOffset.y ) ? -alignmentOffset.y : mScrollPosition.y;
304 mDecoratorUpdated = true;
308 mScrollPosition.y = 0.f;
314 void OnGrabHandleEvent( const Event& event )
316 unsigned int state = event.p1.mUint;
318 if( GRAB_HANDLE_PRESSED == state )
320 float xPosition = event.p2.mFloat;
321 float yPosition = event.p3.mFloat;
324 GetClosestCursorPosition( mPrimaryCursorPosition, xPosition, yPosition, height );
326 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
327 //mDecorator->HidePopup();
328 ChangeState ( EDITING );
329 mDecoratorUpdated = true;
331 else if ( mGrabHandlePopupEnabled &&
332 GRAB_HANDLE_RELEASED == state )
334 //mDecorator->ShowPopup();
335 ChangeState ( EDITING_WITH_POPUP );
336 mDecoratorUpdated = true;
340 void ChangeState( State newState )
342 if( mState != newState )
346 if( INACTIVE == mState )
348 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
349 mDecorator->StopCursorBlink();
350 mDecorator->SetGrabHandleActive( false );
351 mDecorator->SetSelectionActive( false );
352 mDecorator->SetPopupActive( false );
353 mDecoratorUpdated = true;
355 else if ( SELECTING == mState )
357 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
358 mDecorator->StopCursorBlink();
359 mDecorator->SetGrabHandleActive( false );
360 mDecorator->SetSelectionActive( true );
361 mDecoratorUpdated = true;
363 else if( EDITING == mState )
365 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
366 if( mCursorBlinkEnabled )
368 mDecorator->StartCursorBlink();
370 if( mGrabHandleEnabled )
372 mDecorator->SetGrabHandleActive( true );
374 if( mGrabHandlePopupEnabled )
376 mDecorator->SetPopupActive( false );
378 mDecorator->SetSelectionActive( false );
379 mDecoratorUpdated = true;
381 else if( EDITING_WITH_POPUP == mState )
383 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
384 if( mCursorBlinkEnabled )
386 mDecorator->StartCursorBlink();
388 if( mGrabHandleEnabled )
390 mDecorator->SetGrabHandleActive( true );
392 if( mGrabHandlePopupEnabled )
394 mDecorator->SetPopupActive( true );
396 mDecorator->SetSelectionActive( false );
397 mDecoratorUpdated = true;
402 LineIndex GetClosestLine( float y )
404 LineIndex lineIndex( 0u );
406 const Vector<LineRun>& lines = mVisualModel->mLines;
407 for( float totalHeight = 0; lineIndex < lines.Count(); ++lineIndex )
409 totalHeight += lines[lineIndex].lineSize.height;
410 if( y < totalHeight )
419 void GetClosestCursorPosition( CharacterIndex& logical, float& visualX, float& visualY, float& height )
421 Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
422 Length numberOfLines = mVisualModel->mLines.Count();
423 if( 0 == numberOfGlyphs ||
429 // Transform to visual model coords
430 visualX -= mScrollPosition.x;
431 visualY -= mScrollPosition.y;
433 // Find which line is closest
434 LineIndex lineIndex( GetClosestLine( visualY ) );
436 const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
437 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
439 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
440 const Vector2* const positionsBuffer = positions.Begin();
442 unsigned int closestGlyph = 0;
443 bool leftOfGlyph( false ); // which side of the glyph?
444 float closestDistance = MAX_FLOAT;
446 const LineRun& line = mVisualModel->mLines[lineIndex];
447 GlyphIndex startGlyph = line.glyphIndex;
448 GlyphIndex endGlyph = line.glyphIndex + line.numberOfGlyphs;
449 DALI_ASSERT_DEBUG( endGlyph <= glyphs.Count() && "Invalid line info" );
451 for( GlyphIndex i = startGlyph; i < endGlyph; ++i )
453 const GlyphInfo& glyphInfo = *( glyphsBuffer + i );
454 const Vector2& position = *( positionsBuffer + i );
455 float glyphX = position.x + glyphInfo.width*0.5f;
456 float glyphY = position.y + glyphInfo.height*0.5f;
458 float distanceToGlyph = fabsf( glyphX - visualX ) + fabsf( glyphY - visualY );
460 if( distanceToGlyph < closestDistance )
462 closestDistance = distanceToGlyph;
464 leftOfGlyph = ( visualX < glyphX );
468 // Calculate the logical position
469 logical = mVisualModel->GetCharacterIndex( closestGlyph );
471 // Returns the visual position of the glyph
472 visualX = positions[closestGlyph].x;
475 visualX += glyphs[closestGlyph].width;
480 else// if ( RTL ) TODO
486 height = line.lineSize.height;
489 void UpdateCursorPosition()
491 if( 0 == mVisualModel->mGlyphs.Count() )
496 // FIXME GetGlyphIndex() is behaving strangely
498 GlyphIndex cursorGlyph = mVisualModel->GetGlyphIndex( mPrimaryCursorPosition );
500 GlyphIndex cursorGlyph( 0u );
501 for( cursorGlyph = 0; cursorGlyph < mVisualModel->mGlyphs.Count(); ++cursorGlyph )
503 if( mPrimaryCursorPosition == mVisualModel->GetCharacterIndex( cursorGlyph ) )
510 float visualX( 0.0f );
511 float visualY( 0.0f );
512 float height( 0.0f );
513 const Vector<LineRun>& lineRuns = mVisualModel->mLines;
515 if( cursorGlyph > 0 )
519 visualX = mVisualModel->mGlyphPositions[ cursorGlyph ].x;
521 visualX += mVisualModel->mGlyphs[ cursorGlyph ].width;
523 // Find the line height
524 GlyphIndex lastGlyph( 0 );
525 for( LineIndex lineIndex = 0u; lineIndex < lineRuns.Count(); ++lineIndex )
527 lastGlyph = (lineRuns[lineIndex].glyphIndex + lineRuns[lineIndex].numberOfGlyphs);
528 if( cursorGlyph < lastGlyph )
530 height = lineRuns[lineIndex].lineSize.height;
536 mDecorator->SetPosition( PRIMARY_CURSOR, visualX, visualY, height );
537 mDecoratorUpdated = true;
540 LogicalModelPtr mLogicalModel;
541 VisualModelPtr mVisualModel;
542 DecoratorPtr mDecorator;
544 std::string mPlaceholderText;
547 * This is used to delay handling events until after the model has been updated.
548 * The number of updates to the model is minimized to improve performance.
550 vector<Event> mEventQueue; ///< The queue of touch events etc.
552 State mState; ///< Selection mode, edit mode etc.
554 CharacterIndex mPrimaryCursorPosition; ///< Index into logical model for primary cursor
555 CharacterIndex mSecondaryCursorPosition; ///< Index into logical model for secondary cursor
558 * 0,0 means that the top-left corner of the layout matches the top-left corner of the UI control.
559 * Typically this will have a negative value with scrolling occurs.
561 Vector2 mScrollPosition; ///< The text is offset by this position when scrolling.
563 bool mDecoratorUpdated : 1; ///< True if the decorator was updated during event processing
564 bool mCursorBlinkEnabled : 1; ///< True if cursor should blink when active
565 bool mGrabHandleEnabled : 1; ///< True if grab handle is enabled
566 bool mGrabHandlePopupEnabled : 1; ///< True if the grab handle popu-up should be shown
567 bool mSelectionEnabled : 1; ///< True if selection handles, highlight etc. are enabled
568 bool mHorizontalScrollingEnabled : 1; ///< True if horizontal scrolling is enabled
569 bool mVerticalScrollingEnabled : 1; ///< True if vertical scrolling is enabled
570 bool mUpdateCursorPosition : 1; ///< True if the visual position of the cursor must be recalculated
573 struct Controller::FontDefaults
576 : mDefaultPointSize(0.0f),
581 FontId GetFontId( TextAbstraction::FontClient& fontClient )
585 Dali::TextAbstraction::PointSize26Dot6 pointSize = mDefaultPointSize*64;
586 mFontId = fontClient.GetFontId( mDefaultFontFamily, mDefaultFontStyle, pointSize );
592 std::string mDefaultFontFamily;
593 std::string mDefaultFontStyle;
594 float mDefaultPointSize;
598 struct Controller::Impl
600 Impl( ControlInterface& controlInterface )
601 : mControlInterface( controlInterface ),
604 mFontDefaults( NULL ),
612 mOperationsPending( NO_OPERATION ),
613 mRecalculateNaturalSize( true )
615 mLogicalModel = LogicalModel::New();
616 mVisualModel = VisualModel::New();
618 mFontClient = TextAbstraction::FontClient::Get();
620 mView.SetVisualModel( mVisualModel );
628 ControlInterface& mControlInterface; ///< Reference to the text controller.
629 LogicalModelPtr mLogicalModel; ///< Pointer to the logical model.
630 VisualModelPtr mVisualModel; ///< Pointer to the visual model.
631 FontDefaults* mFontDefaults; ///< Avoid allocating this when the user does not specify a font.
632 Controller::TextInput* mTextInput; ///< Avoid allocating everything for text input until EnableTextInput().
633 TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
634 View mView; ///< The view interface to the rendering back-end.
635 LayoutEngine mLayoutEngine; ///< The layout engine.
636 std::vector<ModifyEvent> mModifyEvents; ///< Temporary stores the text set until the next relayout.
637 Size mControlSize; ///< The size of the control.
638 Vector2 mAlignmentOffset; ///< Vertical and horizontal offset of the whole text inside the control due to alignment.
639 OperationsMask mOperationsPending; ///< Operations pending to be done to layout the text.
640 bool mRecalculateNaturalSize:1; ///< Whether the natural size needs to be recalculated.
643 ControllerPtr Controller::New( ControlInterface& controlInterface )
645 return ControllerPtr( new Controller( controlInterface ) );
648 void Controller::SetText( const std::string& text )
650 // Cancel previously queued inserts etc.
651 mImpl->mModifyEvents.clear();
653 // Keep until size negotiation
655 event.type = REPLACE_TEXT;
657 mImpl->mModifyEvents.push_back( event );
659 if( mImpl->mTextInput )
661 // Cancel previously queued events
662 mImpl->mTextInput->mEventQueue.clear();
664 // TODO - Hide selection decorations
668 void Controller::GetText( std::string& text ) const
670 if( !mImpl->mModifyEvents.empty() &&
671 REPLACE_TEXT == mImpl->mModifyEvents[0].type )
673 text = mImpl->mModifyEvents[0].text;
677 // TODO - Convert from UTF-32
681 void Controller::SetPlaceholderText( const std::string& text )
683 if( !mImpl->mTextInput )
685 mImpl->mTextInput->mPlaceholderText = text;
689 void Controller::GetPlaceholderText( std::string& text ) const
691 if( !mImpl->mTextInput )
693 text = mImpl->mTextInput->mPlaceholderText;
697 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
699 if( !mImpl->mFontDefaults )
701 mImpl->mFontDefaults = new Controller::FontDefaults();
704 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
705 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
706 mImpl->mOperationsPending = ALL_OPERATIONS;
707 mImpl->mRecalculateNaturalSize = true;
709 // Clear the font-specific data
710 mImpl->mLogicalModel->mFontRuns.Clear();
711 mImpl->mVisualModel->mGlyphs.Clear();
712 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
713 mImpl->mVisualModel->mCharactersToGlyph.Clear();
714 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
715 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
716 mImpl->mVisualModel->mGlyphPositions.Clear();
717 mImpl->mVisualModel->mLines.Clear();
722 const std::string& Controller::GetDefaultFontFamily() const
724 if( mImpl->mFontDefaults )
726 return mImpl->mFontDefaults->mDefaultFontFamily;
732 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
734 if( !mImpl->mFontDefaults )
736 mImpl->mFontDefaults = new Controller::FontDefaults();
739 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
740 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
741 mImpl->mOperationsPending = ALL_OPERATIONS;
742 mImpl->mRecalculateNaturalSize = true;
744 // Clear the font-specific data
745 mImpl->mLogicalModel->mFontRuns.Clear();
746 mImpl->mVisualModel->mGlyphs.Clear();
747 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
748 mImpl->mVisualModel->mCharactersToGlyph.Clear();
749 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
750 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
751 mImpl->mVisualModel->mGlyphPositions.Clear();
752 mImpl->mVisualModel->mLines.Clear();
757 const std::string& Controller::GetDefaultFontStyle() const
759 if( mImpl->mFontDefaults )
761 return mImpl->mFontDefaults->mDefaultFontStyle;
767 void Controller::SetDefaultPointSize( float pointSize )
769 if( !mImpl->mFontDefaults )
771 mImpl->mFontDefaults = new Controller::FontDefaults();
774 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
775 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
776 mImpl->mOperationsPending = ALL_OPERATIONS;
777 mImpl->mRecalculateNaturalSize = true;
779 // Clear the font-specific data
780 mImpl->mLogicalModel->mFontRuns.Clear();
781 mImpl->mVisualModel->mGlyphs.Clear();
782 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
783 mImpl->mVisualModel->mCharactersToGlyph.Clear();
784 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
785 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
786 mImpl->mVisualModel->mGlyphPositions.Clear();
787 mImpl->mVisualModel->mLines.Clear();
792 float Controller::GetDefaultPointSize() const
794 if( mImpl->mFontDefaults )
796 return mImpl->mFontDefaults->mDefaultPointSize;
802 void Controller::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
804 if( mImpl->mFontDefaults )
807 fontRun.characterRun.characterIndex = 0;
808 fontRun.characterRun.numberOfCharacters = numberOfCharacters;
809 fontRun.fontId = mImpl->mFontDefaults->GetFontId( mImpl->mFontClient );
810 fontRun.isDefault = true;
812 fonts.PushBack( fontRun );
816 void Controller::EnableTextInput( DecoratorPtr decorator )
818 if( !mImpl->mTextInput )
820 mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
824 void Controller::SetEnableCursorBlink( bool enable )
826 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "TextInput disabled" );
828 if( mImpl->mTextInput )
830 mImpl->mTextInput->mCursorBlinkEnabled = enable;
833 mImpl->mTextInput->mDecorator )
835 mImpl->mTextInput->mDecorator->StopCursorBlink();
840 bool Controller::GetEnableCursorBlink() const
842 if( mImpl->mTextInput )
844 return mImpl->mTextInput->mCursorBlinkEnabled;
850 const Vector2& Controller::GetScrollPosition() const
852 if( mImpl->mTextInput )
854 return mImpl->mTextInput->mScrollPosition;
857 return Vector2::ZERO;
860 const Vector2& Controller::GetAlignmentOffset() const
862 return mImpl->mAlignmentOffset;
865 Vector3 Controller::GetNaturalSize()
869 // Make sure the model is up-to-date before layouting
870 ProcessModifyEvents();
872 if( mImpl->mRecalculateNaturalSize )
874 // Operations that can be done only once until the text changes.
875 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
883 // Make sure the model is up-to-date before layouting
884 UpdateModel( onlyOnceOperations );
886 // Operations that need to be done if the size changes.
887 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
891 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
892 static_cast<OperationsMask>( onlyOnceOperations |
894 naturalSize.GetVectorXY() );
896 // Do not do again the only once operations.
897 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
899 // Do the size related operations again.
900 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
902 // Stores the natural size to avoid recalculate it again
903 // unless the text/style changes.
904 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
906 mImpl->mRecalculateNaturalSize = false;
910 naturalSize = mImpl->mVisualModel->GetNaturalSize();
916 float Controller::GetHeightForWidth( float width )
918 // Make sure the model is up-to-date before layouting
919 ProcessModifyEvents();
922 if( width != mImpl->mControlSize.width )
924 // Operations that can be done only once until the text changes.
925 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
933 // Make sure the model is up-to-date before layouting
934 UpdateModel( onlyOnceOperations );
936 // Operations that need to be done if the size changes.
937 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
941 DoRelayout( Size( width, MAX_FLOAT ),
942 static_cast<OperationsMask>( onlyOnceOperations |
946 // Do not do again the only once operations.
947 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
949 // Do the size related operations again.
950 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
954 layoutSize = mImpl->mVisualModel->GetActualSize();
957 return layoutSize.height;
960 bool Controller::Relayout( const Size& size )
962 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
964 bool glyphsRemoved( false );
965 if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
967 mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
968 glyphsRemoved = true;
971 // Not worth to relayout if width or height is equal to zero.
972 return glyphsRemoved;
975 if( size != mImpl->mControlSize )
977 // Operations that need to be done if the size changes.
978 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
984 mImpl->mControlSize = size;
987 // Make sure the model is up-to-date before layouting
988 ProcessModifyEvents();
989 UpdateModel( mImpl->mOperationsPending );
992 bool updated = DoRelayout( mImpl->mControlSize,
993 mImpl->mOperationsPending,
996 // Do not re-do any operation until something changes.
997 mImpl->mOperationsPending = NO_OPERATION;
999 // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
1000 CalculateTextAlignment( size );
1002 if( mImpl->mTextInput )
1004 // Move the cursor, grab handle etc.
1005 updated = mImpl->mTextInput->ProcessInputEvents( mImpl->mControlSize, mImpl->mAlignmentOffset ) || updated;
1011 void Controller::ProcessModifyEvents()
1013 std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
1015 for( unsigned int i=0; i<events.size(); ++i )
1017 if( REPLACE_TEXT == events[0].type )
1019 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
1020 DALI_ASSERT_DEBUG( 0 == i && "Unexpected REPLACE event" );
1022 ReplaceTextEvent( events[0].text );
1024 else if( INSERT_TEXT == events[0].type )
1026 InsertTextEvent( events[0].text );
1028 else if( DELETE_TEXT == events[0].type )
1034 // Discard temporary text
1038 void Controller::ReplaceTextEvent( const std::string& text )
1041 mImpl->mLogicalModel->mText.Clear();
1042 mImpl->mLogicalModel->mScriptRuns.Clear();
1043 mImpl->mLogicalModel->mFontRuns.Clear();
1044 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1045 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1046 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1047 mImpl->mLogicalModel->mCharacterDirections.Clear();
1048 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1049 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1050 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1051 mImpl->mVisualModel->mGlyphs.Clear();
1052 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1053 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1054 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1055 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1056 mImpl->mVisualModel->mGlyphPositions.Clear();
1057 mImpl->mVisualModel->mLines.Clear();
1059 // Convert text into UTF-32
1060 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1061 utf32Characters.Resize( text.size() );
1063 // This is a bit horrible but std::string returns a (signed) char*
1064 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1066 // Transform a text array encoded in utf8 into an array encoded in utf32.
1067 // It returns the actual number of characters.
1068 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1069 utf32Characters.Resize( characterCount );
1071 // Reset the cursor position
1072 if( mImpl->mTextInput )
1074 mImpl->mTextInput->mPrimaryCursorPosition = characterCount;
1075 // TODO - handle secondary cursor
1078 // The natural size needs to be re-calculated.
1079 mImpl->mRecalculateNaturalSize = true;
1081 // Apply modifications to the model
1082 mImpl->mOperationsPending = ALL_OPERATIONS;
1083 UpdateModel( ALL_OPERATIONS );
1084 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1086 UPDATE_ACTUAL_SIZE |
1090 void Controller::InsertTextEvent( const std::string& text )
1092 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1094 // TODO - Optimize this
1095 mImpl->mLogicalModel->mScriptRuns.Clear();
1096 mImpl->mLogicalModel->mFontRuns.Clear();
1097 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1098 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1099 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1100 mImpl->mLogicalModel->mCharacterDirections.Clear();
1101 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1102 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1103 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1104 mImpl->mVisualModel->mGlyphs.Clear();
1105 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1106 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1107 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1108 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1109 mImpl->mVisualModel->mGlyphPositions.Clear();
1110 mImpl->mVisualModel->mLines.Clear();
1112 // Convert text into UTF-32
1113 Vector<Character> utf32Characters;
1114 utf32Characters.Resize( text.size() );
1116 // This is a bit horrible but std::string returns a (signed) char*
1117 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1119 // Transform a text array encoded in utf8 into an array encoded in utf32.
1120 // It returns the actual number of characters.
1121 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1122 utf32Characters.Resize( characterCount );
1124 // Insert at current cursor position
1125 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1126 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1128 if( cursorIndex < modifyText.Count() )
1130 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.End() );
1134 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.End() );
1137 // Advance the cursor position
1140 // The natural size needs to be re-calculated.
1141 mImpl->mRecalculateNaturalSize = true;
1143 // Apply modifications to the model; TODO - Optimize this
1144 mImpl->mOperationsPending = ALL_OPERATIONS;
1145 UpdateModel( ALL_OPERATIONS );
1146 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1148 UPDATE_ACTUAL_SIZE |
1151 // Queue a cursor reposition event; this must wait until after DoRelayout()
1152 mImpl->mTextInput->mUpdateCursorPosition = true;
1155 void Controller::DeleteTextEvent()
1157 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1159 // TODO - Optimize this
1160 mImpl->mLogicalModel->mScriptRuns.Clear();
1161 mImpl->mLogicalModel->mFontRuns.Clear();
1162 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1163 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1164 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1165 mImpl->mLogicalModel->mCharacterDirections.Clear();
1166 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1167 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1168 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1169 mImpl->mVisualModel->mGlyphs.Clear();
1170 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1171 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1172 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1173 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1174 mImpl->mVisualModel->mGlyphPositions.Clear();
1175 mImpl->mVisualModel->mLines.Clear();
1177 // Delte at current cursor position
1178 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1179 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1181 if( cursorIndex > 0 &&
1182 cursorIndex-1 < modifyText.Count() )
1184 modifyText.Remove( modifyText.Begin() + cursorIndex - 1 );
1186 // Cursor position retreat
1190 // The natural size needs to be re-calculated.
1191 mImpl->mRecalculateNaturalSize = true;
1193 // Apply modifications to the model; TODO - Optimize this
1194 mImpl->mOperationsPending = ALL_OPERATIONS;
1195 UpdateModel( ALL_OPERATIONS );
1196 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1198 UPDATE_ACTUAL_SIZE |
1201 // Queue a cursor reposition event; this must wait until after DoRelayout()
1202 mImpl->mTextInput->mUpdateCursorPosition = true;
1205 void Controller::UpdateModel( OperationsMask operationsRequired )
1207 // Calculate the operations to be done.
1208 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1210 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1212 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
1214 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1215 if( GET_LINE_BREAKS & operations )
1217 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
1218 // calculate the bidirectional info for each 'paragraph'.
1219 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
1220 // is not shaped together).
1221 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
1223 SetLineBreakInfo( utf32Characters,
1227 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1228 if( GET_WORD_BREAKS & operations )
1230 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
1231 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
1233 SetWordBreakInfo( utf32Characters,
1237 const bool getScripts = GET_SCRIPTS & operations;
1238 const bool validateFonts = VALIDATE_FONTS & operations;
1240 Vector<ScriptRun>& scripts = mImpl->mLogicalModel->mScriptRuns;
1241 Vector<FontRun>& validFonts = mImpl->mLogicalModel->mFontRuns;
1243 if( getScripts || validateFonts )
1245 // Validates the fonts assigned by the application or assigns default ones.
1246 // It makes sure all the characters are going to be rendered by the correct font.
1247 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
1251 // Retrieves the scripts used in the text.
1252 multilanguageSupport.SetScripts( utf32Characters,
1259 if( 0u == validFonts.Count() )
1261 // Copy the requested font defaults received via the property system.
1262 // These may not be valid i.e. may not contain glyphs for the necessary scripts.
1263 GetDefaultFonts( validFonts, numberOfCharacters );
1266 // Validates the fonts. If there is a character with no assigned font it sets a default one.
1267 // After this call, fonts are validated.
1268 multilanguageSupport.ValidateFonts( utf32Characters,
1274 Vector<Character> mirroredUtf32Characters;
1275 bool textMirrored = false;
1276 if( BIDI_INFO & operations )
1278 // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
1279 // bidirectional info.
1281 Length numberOfParagraphs = 0u;
1283 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
1284 for( Length index = 0u; index < numberOfCharacters; ++index )
1286 if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
1288 ++numberOfParagraphs;
1292 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1293 bidirectionalInfo.Reserve( numberOfParagraphs );
1295 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1296 SetBidirectionalInfo( utf32Characters,
1299 bidirectionalInfo );
1301 if( 0u != bidirectionalInfo.Count() )
1303 // This paragraph has right to left text. Some characters may need to be mirrored.
1304 // TODO: consider if the mirrored string can be stored as well.
1306 textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
1308 // Only set the character directions if there is right to left characters.
1309 Vector<CharacterDirection>& directions = mImpl->mLogicalModel->mCharacterDirections;
1310 directions.Resize( numberOfCharacters );
1312 GetCharactersDirection( bidirectionalInfo,
1317 // There is no right to left characters. Clear the directions vector.
1318 mImpl->mLogicalModel->mCharacterDirections.Clear();
1323 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1324 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1325 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1326 if( SHAPE_TEXT & operations )
1328 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1330 ShapeText( textToShape,
1335 glyphsToCharactersMap,
1336 charactersPerGlyph );
1339 const Length numberOfGlyphs = glyphs.Count();
1341 if( GET_GLYPH_METRICS & operations )
1343 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
1346 if( 0u != numberOfGlyphs )
1348 // Create the glyph to character conversion table and the 'number of glyphs' per character.
1349 mImpl->mVisualModel->CreateCharacterToGlyphTable(numberOfCharacters );
1350 mImpl->mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
1354 bool Controller::DoRelayout( const Size& size,
1355 OperationsMask operationsRequired,
1358 bool viewUpdated( false );
1360 // Calculate the operations to be done.
1361 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1363 if( LAYOUT & operations )
1365 // Some vectors with data needed to layout and reorder may be void
1366 // after the first time the text has been laid out.
1367 // Fill the vectors again.
1369 Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
1371 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1372 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1373 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1374 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1375 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1377 // Set the layout parameters.
1378 LayoutParameters layoutParameters( size,
1379 mImpl->mLogicalModel->mText.Begin(),
1380 lineBreakInfo.Begin(),
1381 wordBreakInfo.Begin(),
1384 glyphsToCharactersMap.Begin(),
1385 charactersPerGlyph.Begin() );
1387 // The laid-out lines.
1388 // It's not possible to know in how many lines the text is going to be laid-out,
1389 // but it can be resized at least with the number of 'paragraphs' to avoid
1390 // some re-allocations.
1391 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1393 // Delete any previous laid out lines before setting the new ones.
1396 // The capacity of the bidirectional paragraph info is the number of paragraphs.
1397 lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
1399 // Resize the vector of positions to have the same size than the vector of glyphs.
1400 Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
1401 glyphPositions.Resize( numberOfGlyphs );
1403 // Update the visual model.
1404 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
1411 // Reorder the lines
1412 if( REORDER & operations )
1414 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1416 // Check first if there are paragraphs with bidirectional info.
1417 if( 0u != bidirectionalInfo.Count() )
1420 const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
1422 // Reorder the lines.
1423 Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
1424 lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
1425 ReorderLines( bidirectionalInfo,
1427 lineBidirectionalInfoRuns );
1429 // Set the bidirectional info into the model.
1430 const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
1431 mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
1432 numberOfBidirectionalInfoRuns );
1434 // Set the bidirectional info per line into the layout parameters.
1435 layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
1436 layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
1438 // Get the character to glyph conversion table and set into the layout.
1439 layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
1441 // Get the glyphs per character table and set into the layout.
1442 layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
1444 // Re-layout the text. Reorder those lines with right to left characters.
1445 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1448 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
1449 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
1450 endIt = lineBidirectionalInfoRuns.End();
1454 BidirectionalLineInfoRun& bidiLineInfo = *it;
1456 free( bidiLineInfo.visualToLogicalMap );
1461 if( ALIGN & operations )
1463 mImpl->mLayoutEngine.Align( layoutParameters,
1469 // Sets the actual size.
1470 if( UPDATE_ACTUAL_SIZE & operations )
1472 mImpl->mVisualModel->SetActualSize( layoutSize );
1478 layoutSize = mImpl->mVisualModel->GetActualSize();
1484 void Controller::CalculateTextAlignment( const Size& size )
1486 // TODO : Calculate the vertical offset.
1488 // Get the direction of the first character.
1489 const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
1491 const Size& actualSize = mImpl->mVisualModel->GetActualSize();
1493 // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1494 LayoutEngine::Alignment alignment = mImpl->mLayoutEngine.GetAlignment();
1495 if( firstParagraphDirection &&
1496 ( LayoutEngine::ALIGN_CENTER != alignment ) )
1498 if( LayoutEngine::ALIGN_BEGIN == alignment )
1500 alignment = LayoutEngine::ALIGN_END;
1504 alignment = LayoutEngine::ALIGN_BEGIN;
1510 case LayoutEngine::ALIGN_BEGIN:
1512 mImpl->mAlignmentOffset = Vector2::ZERO;
1515 case LayoutEngine::ALIGN_CENTER:
1517 mImpl->mAlignmentOffset.y = 0.f;
1518 const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1519 mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1522 case LayoutEngine::ALIGN_END:
1524 mImpl->mAlignmentOffset.y = 0.f;
1525 mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1531 View& Controller::GetView()
1533 return mImpl->mView;
1536 LayoutEngine& Controller::GetLayoutEngine()
1538 return mImpl->mLayoutEngine;
1541 void Controller::RequestRelayout()
1543 mImpl->mControlInterface.RequestTextRelayout();
1546 void Controller::KeyboardFocusGainEvent()
1548 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
1550 if( mImpl->mTextInput )
1552 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
1553 mImpl->mTextInput->mEventQueue.push_back( event );
1559 void Controller::KeyboardFocusLostEvent()
1561 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
1563 if( mImpl->mTextInput )
1565 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
1566 mImpl->mTextInput->mEventQueue.push_back( event );
1572 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1574 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" );
1576 if( mImpl->mTextInput &&
1577 keyEvent.state == KeyEvent::Down )
1579 int keyCode = keyEvent.keyCode;
1580 const std::string& keyString = keyEvent.keyPressed;
1582 // Pre-process to separate modifying events from non-modifying input events.
1583 if( Dali::DALI_KEY_ESCAPE == keyCode )
1585 // Escape key is a special case which causes focus loss
1586 KeyboardFocusLostEvent();
1588 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
1589 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1590 Dali::DALI_KEY_CURSOR_UP == keyCode ||
1591 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1593 TextInput::Event event( TextInput::CURSOR_KEY_EVENT );
1594 event.p1.mInt = keyCode;
1595 mImpl->mTextInput->mEventQueue.push_back( event );
1597 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1599 // Queue a delete event
1601 event.type = DELETE_TEXT;
1602 mImpl->mModifyEvents.push_back( event );
1604 else if( !keyString.empty() )
1606 // Queue an insert event
1608 event.type = INSERT_TEXT;
1609 event.text = keyString;
1610 mImpl->mModifyEvents.push_back( event );
1613 mImpl->mTextInput->ChangeState( TextInput::EDITING ); // todo Confirm this is the best place to change the state of
1621 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1623 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
1625 if( mImpl->mTextInput )
1627 TextInput::Event event( TextInput::TAP_EVENT );
1628 event.p1.mUint = tapCount;
1629 event.p2.mFloat = x;
1630 event.p3.mFloat = y;
1631 mImpl->mTextInput->mEventQueue.push_back( event );
1637 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1639 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected PanEvent" );
1641 if( mImpl->mTextInput )
1643 TextInput::Event event( TextInput::PAN_EVENT );
1644 event.p1.mInt = state;
1645 event.p2.mFloat = displacement.x;
1646 event.p3.mFloat = displacement.y;
1647 mImpl->mTextInput->mEventQueue.push_back( event );
1653 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
1655 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
1657 if( mImpl->mTextInput )
1659 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
1660 event.p1.mUint = state;
1661 event.p2.mFloat = x;
1662 event.p3.mFloat = y;
1663 mImpl->mTextInput->mEventQueue.push_back( event );
1669 Controller::~Controller()
1674 Controller::Controller( ControlInterface& controlInterface )
1677 mImpl = new Controller::Impl( controlInterface );
1682 } // namespace Toolkit