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 LineIndex lineIndex( 0u );
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 for( GlyphIndex lastGlyph = 0; lineIndex < lineRuns.Count(); ++lineIndex )
526 lastGlyph = (lineRuns[lineIndex].glyphIndex + lineRuns[lineIndex].numberOfGlyphs);
527 if( cursorGlyph < lastGlyph )
534 mDecorator->SetPosition( PRIMARY_CURSOR, visualX, visualY, lineRuns[lineIndex].lineSize.height );
535 mDecoratorUpdated = true;
538 LogicalModelPtr mLogicalModel;
539 VisualModelPtr mVisualModel;
540 DecoratorPtr mDecorator;
542 std::string mPlaceholderText;
545 * This is used to delay handling events until after the model has been updated.
546 * The number of updates to the model is minimized to improve performance.
548 vector<Event> mEventQueue; ///< The queue of touch events etc.
550 State mState; ///< Selection mode, edit mode etc.
552 CharacterIndex mPrimaryCursorPosition; ///< Index into logical model for primary cursor
553 CharacterIndex mSecondaryCursorPosition; ///< Index into logical model for secondary cursor
556 * 0,0 means that the top-left corner of the layout matches the top-left corner of the UI control.
557 * Typically this will have a negative value with scrolling occurs.
559 Vector2 mScrollPosition; ///< The text is offset by this position when scrolling.
561 bool mDecoratorUpdated : 1; ///< True if the decorator was updated during event processing
562 bool mCursorBlinkEnabled : 1; ///< True if cursor should blink when active
563 bool mGrabHandleEnabled : 1; ///< True if grab handle is enabled
564 bool mGrabHandlePopupEnabled : 1; ///< True if the grab handle popu-up should be shown
565 bool mSelectionEnabled : 1; ///< True if selection handles, highlight etc. are enabled
566 bool mHorizontalScrollingEnabled : 1; ///< True if horizontal scrolling is enabled
567 bool mVerticalScrollingEnabled : 1; ///< True if vertical scrolling is enabled
568 bool mUpdateCursorPosition : 1; ///< True if the visual position of the cursor must be recalculated
571 struct Controller::FontDefaults
574 : mDefaultPointSize(0.0f),
579 FontId GetFontId( TextAbstraction::FontClient& fontClient )
583 Dali::TextAbstraction::PointSize26Dot6 pointSize = mDefaultPointSize*64;
584 mFontId = fontClient.GetFontId( mDefaultFontFamily, mDefaultFontStyle, pointSize );
590 std::string mDefaultFontFamily;
591 std::string mDefaultFontStyle;
592 float mDefaultPointSize;
596 struct Controller::Impl
598 Impl( ControlInterface& controlInterface )
599 : mControlInterface( controlInterface ),
602 mFontDefaults( NULL ),
610 mOperationsPending( NO_OPERATION ),
611 mRecalculateNaturalSize( true )
613 mLogicalModel = LogicalModel::New();
614 mVisualModel = VisualModel::New();
616 mFontClient = TextAbstraction::FontClient::Get();
618 mView.SetVisualModel( mVisualModel );
626 ControlInterface& mControlInterface; ///< Reference to the text controller.
627 LogicalModelPtr mLogicalModel; ///< Pointer to the logical model.
628 VisualModelPtr mVisualModel; ///< Pointer to the visual model.
629 FontDefaults* mFontDefaults; ///< Avoid allocating this when the user does not specify a font.
630 Controller::TextInput* mTextInput; ///< Avoid allocating everything for text input until EnableTextInput().
631 TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
632 View mView; ///< The view interface to the rendering back-end.
633 LayoutEngine mLayoutEngine; ///< The layout engine.
634 std::vector<ModifyEvent> mModifyEvents; ///< Temporary stores the text set until the next relayout.
635 Size mControlSize; ///< The size of the control.
636 Vector2 mAlignmentOffset; ///< Vertical and horizontal offset of the whole text inside the control due to alignment.
637 OperationsMask mOperationsPending; ///< Operations pending to be done to layout the text.
638 bool mRecalculateNaturalSize:1; ///< Whether the natural size needs to be recalculated.
641 ControllerPtr Controller::New( ControlInterface& controlInterface )
643 return ControllerPtr( new Controller( controlInterface ) );
646 void Controller::SetText( const std::string& text )
648 // Cancel previously queued inserts etc.
649 mImpl->mModifyEvents.clear();
651 // Keep until size negotiation
653 event.type = REPLACE_TEXT;
655 mImpl->mModifyEvents.push_back( event );
657 if( mImpl->mTextInput )
659 // Cancel previously queued events
660 mImpl->mTextInput->mEventQueue.clear();
662 // TODO - Hide selection decorations
666 void Controller::GetText( std::string& text ) const
668 if( !mImpl->mModifyEvents.empty() &&
669 REPLACE_TEXT == mImpl->mModifyEvents[0].type )
671 text = mImpl->mModifyEvents[0].text;
675 // TODO - Convert from UTF-32
679 void Controller::SetPlaceholderText( const std::string& text )
681 if( !mImpl->mTextInput )
683 mImpl->mTextInput->mPlaceholderText = text;
687 void Controller::GetPlaceholderText( std::string& text ) const
689 if( !mImpl->mTextInput )
691 text = mImpl->mTextInput->mPlaceholderText;
695 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
697 if( !mImpl->mFontDefaults )
699 mImpl->mFontDefaults = new Controller::FontDefaults();
702 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
703 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
704 mImpl->mOperationsPending = ALL_OPERATIONS;
705 mImpl->mRecalculateNaturalSize = true;
707 // Clear the font-specific data
708 mImpl->mLogicalModel->mFontRuns.Clear();
709 mImpl->mVisualModel->mGlyphs.Clear();
710 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
711 mImpl->mVisualModel->mCharactersToGlyph.Clear();
712 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
713 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
714 mImpl->mVisualModel->mGlyphPositions.Clear();
715 mImpl->mVisualModel->mLines.Clear();
720 const std::string& Controller::GetDefaultFontFamily() const
722 if( mImpl->mFontDefaults )
724 return mImpl->mFontDefaults->mDefaultFontFamily;
730 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
732 if( !mImpl->mFontDefaults )
734 mImpl->mFontDefaults = new Controller::FontDefaults();
737 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
738 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
739 mImpl->mOperationsPending = ALL_OPERATIONS;
740 mImpl->mRecalculateNaturalSize = true;
742 // Clear the font-specific data
743 mImpl->mLogicalModel->mFontRuns.Clear();
744 mImpl->mVisualModel->mGlyphs.Clear();
745 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
746 mImpl->mVisualModel->mCharactersToGlyph.Clear();
747 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
748 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
749 mImpl->mVisualModel->mGlyphPositions.Clear();
750 mImpl->mVisualModel->mLines.Clear();
755 const std::string& Controller::GetDefaultFontStyle() const
757 if( mImpl->mFontDefaults )
759 return mImpl->mFontDefaults->mDefaultFontStyle;
765 void Controller::SetDefaultPointSize( float pointSize )
767 if( !mImpl->mFontDefaults )
769 mImpl->mFontDefaults = new Controller::FontDefaults();
772 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
773 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
774 mImpl->mOperationsPending = ALL_OPERATIONS;
775 mImpl->mRecalculateNaturalSize = true;
777 // Clear the font-specific data
778 mImpl->mLogicalModel->mFontRuns.Clear();
779 mImpl->mVisualModel->mGlyphs.Clear();
780 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
781 mImpl->mVisualModel->mCharactersToGlyph.Clear();
782 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
783 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
784 mImpl->mVisualModel->mGlyphPositions.Clear();
785 mImpl->mVisualModel->mLines.Clear();
790 float Controller::GetDefaultPointSize() const
792 if( mImpl->mFontDefaults )
794 return mImpl->mFontDefaults->mDefaultPointSize;
800 void Controller::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
802 if( mImpl->mFontDefaults )
805 fontRun.characterRun.characterIndex = 0;
806 fontRun.characterRun.numberOfCharacters = numberOfCharacters;
807 fontRun.fontId = mImpl->mFontDefaults->GetFontId( mImpl->mFontClient );
808 fontRun.isDefault = true;
810 fonts.PushBack( fontRun );
814 void Controller::EnableTextInput( DecoratorPtr decorator )
816 if( !mImpl->mTextInput )
818 mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
822 void Controller::SetEnableCursorBlink( bool enable )
824 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "TextInput disabled" );
826 if( mImpl->mTextInput )
828 mImpl->mTextInput->mCursorBlinkEnabled = enable;
831 mImpl->mTextInput->mDecorator )
833 mImpl->mTextInput->mDecorator->StopCursorBlink();
838 bool Controller::GetEnableCursorBlink() const
840 if( mImpl->mTextInput )
842 return mImpl->mTextInput->mCursorBlinkEnabled;
848 const Vector2& Controller::GetScrollPosition() const
850 if( mImpl->mTextInput )
852 return mImpl->mTextInput->mScrollPosition;
855 return Vector2::ZERO;
858 const Vector2& Controller::GetAlignmentOffset() const
860 return mImpl->mAlignmentOffset;
863 Vector3 Controller::GetNaturalSize()
867 // Make sure the model is up-to-date before layouting
868 ProcessModifyEvents();
870 if( mImpl->mRecalculateNaturalSize )
872 // Operations that can be done only once until the text changes.
873 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
881 // Make sure the model is up-to-date before layouting
882 UpdateModel( onlyOnceOperations );
884 // Operations that need to be done if the size changes.
885 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
889 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
890 static_cast<OperationsMask>( onlyOnceOperations |
892 naturalSize.GetVectorXY() );
894 // Do not do again the only once operations.
895 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
897 // Do the size related operations again.
898 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
900 // Stores the natural size to avoid recalculate it again
901 // unless the text/style changes.
902 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
904 mImpl->mRecalculateNaturalSize = false;
908 naturalSize = mImpl->mVisualModel->GetNaturalSize();
914 float Controller::GetHeightForWidth( float width )
916 // Make sure the model is up-to-date before layouting
917 ProcessModifyEvents();
920 if( width != mImpl->mControlSize.width )
922 // Operations that can be done only once until the text changes.
923 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
931 // Make sure the model is up-to-date before layouting
932 UpdateModel( onlyOnceOperations );
934 // Operations that need to be done if the size changes.
935 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
939 DoRelayout( Size( width, MAX_FLOAT ),
940 static_cast<OperationsMask>( onlyOnceOperations |
944 // Do not do again the only once operations.
945 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
947 // Do the size related operations again.
948 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
952 layoutSize = mImpl->mVisualModel->GetActualSize();
955 return layoutSize.height;
958 bool Controller::Relayout( const Size& size )
960 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
962 bool glyphsRemoved( false );
963 if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
965 mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
966 glyphsRemoved = true;
969 // Not worth to relayout if width or height is equal to zero.
970 return glyphsRemoved;
973 if( size != mImpl->mControlSize )
975 // Operations that need to be done if the size changes.
976 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
982 mImpl->mControlSize = size;
985 // Make sure the model is up-to-date before layouting
986 ProcessModifyEvents();
987 UpdateModel( mImpl->mOperationsPending );
990 bool updated = DoRelayout( mImpl->mControlSize,
991 mImpl->mOperationsPending,
994 // Do not re-do any operation until something changes.
995 mImpl->mOperationsPending = NO_OPERATION;
997 // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
998 CalculateTextAlignment( size );
1000 if( mImpl->mTextInput )
1002 // Move the cursor, grab handle etc.
1003 updated = mImpl->mTextInput->ProcessInputEvents( mImpl->mControlSize, mImpl->mAlignmentOffset ) || updated;
1009 void Controller::ProcessModifyEvents()
1011 std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
1013 for( unsigned int i=0; i<events.size(); ++i )
1015 if( REPLACE_TEXT == events[0].type )
1017 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
1018 DALI_ASSERT_DEBUG( 0 == i && "Unexpected REPLACE event" );
1020 ReplaceTextEvent( events[0].text );
1022 else if( INSERT_TEXT == events[0].type )
1024 InsertTextEvent( events[0].text );
1026 else if( DELETE_TEXT == events[0].type )
1032 // Discard temporary text
1036 void Controller::ReplaceTextEvent( const std::string& text )
1039 mImpl->mLogicalModel->mText.Clear();
1040 mImpl->mLogicalModel->mScriptRuns.Clear();
1041 mImpl->mLogicalModel->mFontRuns.Clear();
1042 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1043 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1044 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1045 mImpl->mLogicalModel->mCharacterDirections.Clear();
1046 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1047 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1048 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1049 mImpl->mVisualModel->mGlyphs.Clear();
1050 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1051 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1052 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1053 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1054 mImpl->mVisualModel->mGlyphPositions.Clear();
1055 mImpl->mVisualModel->mLines.Clear();
1057 // Convert text into UTF-32
1058 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1059 utf32Characters.Resize( text.size() );
1061 // This is a bit horrible but std::string returns a (signed) char*
1062 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1064 // Transform a text array encoded in utf8 into an array encoded in utf32.
1065 // It returns the actual number of characters.
1066 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1067 utf32Characters.Resize( characterCount );
1069 // Reset the cursor position
1070 if( mImpl->mTextInput )
1072 mImpl->mTextInput->mPrimaryCursorPosition = characterCount;
1073 // TODO - handle secondary cursor
1076 // The natural size needs to be re-calculated.
1077 mImpl->mRecalculateNaturalSize = true;
1079 // Apply modifications to the model
1080 mImpl->mOperationsPending = ALL_OPERATIONS;
1081 UpdateModel( ALL_OPERATIONS );
1082 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1084 UPDATE_ACTUAL_SIZE |
1088 void Controller::InsertTextEvent( const std::string& text )
1090 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1092 // TODO - Optimize this
1093 mImpl->mLogicalModel->mScriptRuns.Clear();
1094 mImpl->mLogicalModel->mFontRuns.Clear();
1095 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1096 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1097 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1098 mImpl->mLogicalModel->mCharacterDirections.Clear();
1099 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1100 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1101 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1102 mImpl->mVisualModel->mGlyphs.Clear();
1103 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1104 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1105 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1106 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1107 mImpl->mVisualModel->mGlyphPositions.Clear();
1108 mImpl->mVisualModel->mLines.Clear();
1110 // Convert text into UTF-32
1111 Vector<Character> utf32Characters;
1112 utf32Characters.Resize( text.size() );
1114 // This is a bit horrible but std::string returns a (signed) char*
1115 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1117 // Transform a text array encoded in utf8 into an array encoded in utf32.
1118 // It returns the actual number of characters.
1119 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1120 utf32Characters.Resize( characterCount );
1122 // Insert at current cursor position
1123 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1124 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1126 if( cursorIndex < modifyText.Count() )
1128 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.End() );
1132 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.End() );
1135 // Advance the cursor position
1138 // The natural size needs to be re-calculated.
1139 mImpl->mRecalculateNaturalSize = true;
1141 // Apply modifications to the model; TODO - Optimize this
1142 mImpl->mOperationsPending = ALL_OPERATIONS;
1143 UpdateModel( ALL_OPERATIONS );
1144 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1146 UPDATE_ACTUAL_SIZE |
1149 // Queue a cursor reposition event; this must wait until after DoRelayout()
1150 mImpl->mTextInput->mUpdateCursorPosition = true;
1153 void Controller::DeleteTextEvent()
1155 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1157 // TODO - Optimize this
1158 mImpl->mLogicalModel->mScriptRuns.Clear();
1159 mImpl->mLogicalModel->mFontRuns.Clear();
1160 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1161 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1162 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1163 mImpl->mLogicalModel->mCharacterDirections.Clear();
1164 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1165 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1166 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1167 mImpl->mVisualModel->mGlyphs.Clear();
1168 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1169 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1170 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1171 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1172 mImpl->mVisualModel->mGlyphPositions.Clear();
1173 mImpl->mVisualModel->mLines.Clear();
1175 // Delte at current cursor position
1176 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1177 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1179 if( cursorIndex > 0 &&
1180 cursorIndex-1 < modifyText.Count() )
1182 modifyText.Remove( modifyText.Begin() + cursorIndex - 1 );
1184 // Cursor position retreat
1188 // The natural size needs to be re-calculated.
1189 mImpl->mRecalculateNaturalSize = true;
1191 // Apply modifications to the model; TODO - Optimize this
1192 mImpl->mOperationsPending = ALL_OPERATIONS;
1193 UpdateModel( ALL_OPERATIONS );
1194 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1196 UPDATE_ACTUAL_SIZE |
1199 // Queue a cursor reposition event; this must wait until after DoRelayout()
1200 mImpl->mTextInput->mUpdateCursorPosition = true;
1203 void Controller::UpdateModel( OperationsMask operationsRequired )
1205 // Calculate the operations to be done.
1206 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1208 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1210 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
1212 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1213 if( GET_LINE_BREAKS & operations )
1215 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
1216 // calculate the bidirectional info for each 'paragraph'.
1217 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
1218 // is not shaped together).
1219 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
1221 SetLineBreakInfo( utf32Characters,
1225 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1226 if( GET_WORD_BREAKS & operations )
1228 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
1229 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
1231 SetWordBreakInfo( utf32Characters,
1235 const bool getScripts = GET_SCRIPTS & operations;
1236 const bool validateFonts = VALIDATE_FONTS & operations;
1238 Vector<ScriptRun>& scripts = mImpl->mLogicalModel->mScriptRuns;
1239 Vector<FontRun>& validFonts = mImpl->mLogicalModel->mFontRuns;
1241 if( getScripts || validateFonts )
1243 // Validates the fonts assigned by the application or assigns default ones.
1244 // It makes sure all the characters are going to be rendered by the correct font.
1245 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
1249 // Retrieves the scripts used in the text.
1250 multilanguageSupport.SetScripts( utf32Characters,
1257 if( 0u == validFonts.Count() )
1259 // Copy the requested font defaults received via the property system.
1260 // These may not be valid i.e. may not contain glyphs for the necessary scripts.
1261 GetDefaultFonts( validFonts, numberOfCharacters );
1264 // Validates the fonts. If there is a character with no assigned font it sets a default one.
1265 // After this call, fonts are validated.
1266 multilanguageSupport.ValidateFonts( utf32Characters,
1272 Vector<Character> mirroredUtf32Characters;
1273 bool textMirrored = false;
1274 if( BIDI_INFO & operations )
1276 // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
1277 // bidirectional info.
1279 Length numberOfParagraphs = 0u;
1281 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
1282 for( Length index = 0u; index < numberOfCharacters; ++index )
1284 if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
1286 ++numberOfParagraphs;
1290 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1291 bidirectionalInfo.Reserve( numberOfParagraphs );
1293 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1294 SetBidirectionalInfo( utf32Characters,
1297 bidirectionalInfo );
1299 if( 0u != bidirectionalInfo.Count() )
1301 // This paragraph has right to left text. Some characters may need to be mirrored.
1302 // TODO: consider if the mirrored string can be stored as well.
1304 textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
1306 // Only set the character directions if there is right to left characters.
1307 Vector<CharacterDirection>& directions = mImpl->mLogicalModel->mCharacterDirections;
1308 directions.Resize( numberOfCharacters );
1310 GetCharactersDirection( bidirectionalInfo,
1315 // There is no right to left characters. Clear the directions vector.
1316 mImpl->mLogicalModel->mCharacterDirections.Clear();
1321 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1322 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1323 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1324 if( SHAPE_TEXT & operations )
1326 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1328 ShapeText( textToShape,
1333 glyphsToCharactersMap,
1334 charactersPerGlyph );
1337 const Length numberOfGlyphs = glyphs.Count();
1339 if( GET_GLYPH_METRICS & operations )
1341 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
1344 if( 0u != numberOfGlyphs )
1346 // Create the glyph to character conversion table and the 'number of glyphs' per character.
1347 mImpl->mVisualModel->CreateCharacterToGlyphTable(numberOfCharacters );
1348 mImpl->mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
1352 bool Controller::DoRelayout( const Size& size,
1353 OperationsMask operationsRequired,
1356 bool viewUpdated( false );
1358 // Calculate the operations to be done.
1359 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1361 if( LAYOUT & operations )
1363 // Some vectors with data needed to layout and reorder may be void
1364 // after the first time the text has been laid out.
1365 // Fill the vectors again.
1367 Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
1369 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1370 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1371 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1372 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1373 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1375 // Set the layout parameters.
1376 LayoutParameters layoutParameters( size,
1377 mImpl->mLogicalModel->mText.Begin(),
1378 lineBreakInfo.Begin(),
1379 wordBreakInfo.Begin(),
1382 glyphsToCharactersMap.Begin(),
1383 charactersPerGlyph.Begin() );
1385 // The laid-out lines.
1386 // It's not possible to know in how many lines the text is going to be laid-out,
1387 // but it can be resized at least with the number of 'paragraphs' to avoid
1388 // some re-allocations.
1389 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1391 // Delete any previous laid out lines before setting the new ones.
1394 // The capacity of the bidirectional paragraph info is the number of paragraphs.
1395 lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
1397 // Resize the vector of positions to have the same size than the vector of glyphs.
1398 Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
1399 glyphPositions.Resize( numberOfGlyphs );
1401 // Update the visual model.
1402 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
1409 // Reorder the lines
1410 if( REORDER & operations )
1412 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1414 // Check first if there are paragraphs with bidirectional info.
1415 if( 0u != bidirectionalInfo.Count() )
1418 const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
1420 // Reorder the lines.
1421 Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
1422 lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
1423 ReorderLines( bidirectionalInfo,
1425 lineBidirectionalInfoRuns );
1427 // Set the bidirectional info into the model.
1428 const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
1429 mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
1430 numberOfBidirectionalInfoRuns );
1432 // Set the bidirectional info per line into the layout parameters.
1433 layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
1434 layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
1436 // Get the character to glyph conversion table and set into the layout.
1437 layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
1439 // Get the glyphs per character table and set into the layout.
1440 layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
1442 // Re-layout the text. Reorder those lines with right to left characters.
1443 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1446 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
1447 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
1448 endIt = lineBidirectionalInfoRuns.End();
1452 BidirectionalLineInfoRun& bidiLineInfo = *it;
1454 free( bidiLineInfo.visualToLogicalMap );
1459 if( ALIGN & operations )
1461 mImpl->mLayoutEngine.Align( layoutParameters,
1467 // Sets the actual size.
1468 if( UPDATE_ACTUAL_SIZE & operations )
1470 mImpl->mVisualModel->SetActualSize( layoutSize );
1476 layoutSize = mImpl->mVisualModel->GetActualSize();
1482 void Controller::CalculateTextAlignment( const Size& size )
1484 // TODO : Calculate the vertical offset.
1486 // Get the direction of the first character.
1487 const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
1489 const Size& actualSize = mImpl->mVisualModel->GetActualSize();
1491 // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1492 LayoutEngine::Alignment alignment = mImpl->mLayoutEngine.GetAlignment();
1493 if( firstParagraphDirection &&
1494 ( LayoutEngine::ALIGN_CENTER != alignment ) )
1496 if( LayoutEngine::ALIGN_BEGIN == alignment )
1498 alignment = LayoutEngine::ALIGN_END;
1502 alignment = LayoutEngine::ALIGN_BEGIN;
1508 case LayoutEngine::ALIGN_BEGIN:
1510 mImpl->mAlignmentOffset = Vector2::ZERO;
1513 case LayoutEngine::ALIGN_CENTER:
1515 mImpl->mAlignmentOffset.y = 0.f;
1516 const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1517 mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1520 case LayoutEngine::ALIGN_END:
1522 mImpl->mAlignmentOffset.y = 0.f;
1523 mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1529 View& Controller::GetView()
1531 return mImpl->mView;
1534 LayoutEngine& Controller::GetLayoutEngine()
1536 return mImpl->mLayoutEngine;
1539 void Controller::RequestRelayout()
1541 mImpl->mControlInterface.RequestTextRelayout();
1544 void Controller::KeyboardFocusGainEvent()
1546 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
1548 if( mImpl->mTextInput )
1550 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
1551 mImpl->mTextInput->mEventQueue.push_back( event );
1557 void Controller::KeyboardFocusLostEvent()
1559 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
1561 if( mImpl->mTextInput )
1563 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
1564 mImpl->mTextInput->mEventQueue.push_back( event );
1570 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1572 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" );
1574 if( mImpl->mTextInput &&
1575 keyEvent.state == KeyEvent::Down )
1577 int keyCode = keyEvent.keyCode;
1578 const std::string& keyString = keyEvent.keyPressed;
1580 // Pre-process to separate modifying events from non-modifying input events.
1581 if( Dali::DALI_KEY_ESCAPE == keyCode )
1583 // Escape key is a special case which causes focus loss
1584 KeyboardFocusLostEvent();
1586 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
1587 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1588 Dali::DALI_KEY_CURSOR_UP == keyCode ||
1589 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1591 TextInput::Event event( TextInput::CURSOR_KEY_EVENT );
1592 event.p1.mInt = keyCode;
1593 mImpl->mTextInput->mEventQueue.push_back( event );
1595 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1597 // Queue a delete event
1599 event.type = DELETE_TEXT;
1600 mImpl->mModifyEvents.push_back( event );
1602 else if( !keyString.empty() )
1604 // Queue an insert event
1606 event.type = INSERT_TEXT;
1607 event.text = keyString;
1608 mImpl->mModifyEvents.push_back( event );
1611 mImpl->mTextInput->ChangeState( TextInput::EDITING ); // todo Confirm this is the best place to change the state of
1619 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1621 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
1623 if( mImpl->mTextInput )
1625 TextInput::Event event( TextInput::TAP_EVENT );
1626 event.p1.mUint = tapCount;
1627 event.p2.mFloat = x;
1628 event.p3.mFloat = y;
1629 mImpl->mTextInput->mEventQueue.push_back( event );
1635 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1637 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected PanEvent" );
1639 if( mImpl->mTextInput )
1641 TextInput::Event event( TextInput::PAN_EVENT );
1642 event.p1.mInt = state;
1643 event.p2.mFloat = displacement.x;
1644 event.p3.mFloat = displacement.y;
1645 mImpl->mTextInput->mEventQueue.push_back( event );
1651 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
1653 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
1655 if( mImpl->mTextInput )
1657 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
1658 event.p1.mUint = state;
1659 event.p2.mFloat = x;
1660 event.p3.mFloat = y;
1661 mImpl->mTextInput->mEventQueue.push_back( event );
1667 Controller::~Controller()
1672 Controller::Controller( ControlInterface& controlInterface )
1675 mImpl = new Controller::Impl( controlInterface );
1680 } // namespace Toolkit