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 )
115 TextInput( LogicalModelPtr logicalModel,
116 VisualModelPtr visualModel,
117 DecoratorPtr decorator )
118 : mLogicalModel( logicalModel ),
119 mVisualModel( visualModel ),
120 mDecorator( decorator ),
122 mPrimaryCursorPosition( 0u ),
123 mSecondaryCursorPosition( 0u ),
124 mDecoratorUpdated( false ),
125 mCursorBlinkEnabled( true ),
126 mGrabHandleEnabled( false ),
127 mGrabHandlePopupEnabled( false ),
128 mSelectionEnabled( false ),
129 mHorizontalScrollingEnabled( true ),
130 mVerticalScrollingEnabled( false ),
131 mUpdateCursorPosition( false )
136 * @brief Helper to move the cursor, grab handle etc.
138 bool ProcessInputEvents( const Vector2& controlSize,
139 const Vector2& alignmentOffset )
141 mDecoratorUpdated = false;
145 for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
149 case KEYBOARD_FOCUS_GAIN_EVENT:
151 OnKeyboardFocus( true );
154 case KEYBOARD_FOCUS_LOST_EVENT:
156 OnKeyboardFocus( false );
159 case CURSOR_KEY_EVENT:
161 OnCursorKeyEvent( *iter );
166 OnTapEvent( *iter, alignmentOffset );
171 OnPanEvent( *iter, controlSize, alignmentOffset );
174 case GRAB_HANDLE_EVENT:
176 OnGrabHandleEvent( *iter );
183 // The cursor must also be repositioned after inserts into the model
184 if( mUpdateCursorPosition )
186 UpdateCursorPosition();
187 mUpdateCursorPosition = false;
192 return mDecoratorUpdated;
195 void OnKeyboardFocus( bool hasFocus )
199 ChangeState( INACTIVE );
203 ChangeState( EDITING );
207 void OnCursorKeyEvent( const Event& event )
209 int keyCode = event.p1.mInt;
211 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
215 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
219 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
223 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
229 void HandleCursorKey( int keyCode )
234 void OnTapEvent( const Event& event,
235 const Vector2& alignmentOffset )
237 unsigned int tapCount = event.p1.mUint;
241 ChangeState( EDITING );
243 float xPosition = event.p2.mFloat - alignmentOffset.x;
244 float yPosition = event.p3.mFloat - alignmentOffset.y;
246 GetClosestCursorPosition( mPrimaryCursorPosition, xPosition, yPosition, height );
247 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
248 mUpdateCursorPosition = false;
250 mDecoratorUpdated = true;
252 else if( mSelectionEnabled &&
255 ChangeState( SELECTING );
259 void OnPanEvent( const Event& event,
260 const Vector2& controlSize,
261 const Vector2& alignmentOffset )
263 int state = event.p1.mInt;
265 if( Gesture::Started == state ||
266 Gesture::Continuing == state )
268 const Vector2& actualSize = mVisualModel->GetActualSize();
270 if( mHorizontalScrollingEnabled )
272 const float displacementX = event.p2.mFloat;
273 mScrollPosition.x += displacementX;
275 // Clamp between -space & 0 (and the text alignment).
276 const float contentWidth = actualSize.width;
277 if( contentWidth > controlSize.width )
279 const float space = ( contentWidth - controlSize.width ) + alignmentOffset.x;
280 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
281 mScrollPosition.x = ( mScrollPosition.x > -alignmentOffset.x ) ? -alignmentOffset.x : mScrollPosition.x;
283 mDecoratorUpdated = true;
287 mScrollPosition.x = 0.f;
291 if( mVerticalScrollingEnabled )
293 const float displacementY = event.p3.mFloat;
294 mScrollPosition.y += displacementY;
296 // Clamp between -space & 0 (and the text alignment).
297 if( actualSize.height > controlSize.height )
299 const float space = ( actualSize.height - controlSize.height ) + alignmentOffset.y;
300 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
301 mScrollPosition.y = ( mScrollPosition.y > -alignmentOffset.y ) ? -alignmentOffset.y : mScrollPosition.y;
303 mDecoratorUpdated = true;
307 mScrollPosition.y = 0.f;
313 void OnGrabHandleEvent( const Event& event )
315 unsigned int state = event.p1.mUint;
317 if( GRAB_HANDLE_PRESSED == state )
319 float xPosition = event.p2.mFloat;
320 float yPosition = event.p3.mFloat;
323 GetClosestCursorPosition( mPrimaryCursorPosition, xPosition, yPosition, height );
325 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
326 mDecorator->HidePopup();
327 mDecoratorUpdated = true;
329 else if ( mGrabHandlePopupEnabled &&
330 GRAB_HANDLE_RELEASED == state )
332 mDecorator->ShowPopup();
336 void ChangeState( State newState )
338 if( mState != newState )
342 if( INACTIVE == mState )
344 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
345 mDecorator->StopCursorBlink();
346 mDecorator->SetGrabHandleActive( false );
347 mDecorator->SetSelectionActive( false );
348 mDecorator->HidePopup();
349 mDecoratorUpdated = true;
351 else if ( SELECTING == mState )
353 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
354 mDecorator->StopCursorBlink();
355 mDecorator->SetGrabHandleActive( false );
356 mDecorator->SetSelectionActive( true );
357 mDecoratorUpdated = true;
359 else if( EDITING == mState )
361 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
362 if( mCursorBlinkEnabled )
364 mDecorator->StartCursorBlink();
366 if( mGrabHandleEnabled )
368 mDecorator->SetGrabHandleActive( true );
370 mDecorator->SetSelectionActive( false );
371 mDecoratorUpdated = true;
376 LineIndex GetClosestLine( float y )
378 LineIndex lineIndex( 0u );
380 const Vector<LineRun>& lines = mVisualModel->mLines;
381 for( float totalHeight = 0; lineIndex < lines.Count(); ++lineIndex )
383 totalHeight += lines[lineIndex].lineSize.height;
384 if( y < totalHeight )
393 void GetClosestCursorPosition( CharacterIndex& logical, float& visualX, float& visualY, float& height )
395 Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
396 Length numberOfLines = mVisualModel->mLines.Count();
397 if( 0 == numberOfGlyphs ||
403 // Transform to visual model coords
404 visualX -= mScrollPosition.x;
405 visualY -= mScrollPosition.y;
407 // Find which line is closest
408 LineIndex lineIndex( GetClosestLine( visualY ) );
410 const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
411 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
413 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
414 const Vector2* const positionsBuffer = positions.Begin();
416 unsigned int closestGlyph = 0;
417 bool leftOfGlyph( false ); // which side of the glyph?
418 float closestDistance = MAX_FLOAT;
420 const LineRun& line = mVisualModel->mLines[lineIndex];
421 GlyphIndex startGlyph = line.glyphIndex;
422 GlyphIndex endGlyph = line.glyphIndex + line.numberOfGlyphs;
423 DALI_ASSERT_DEBUG( endGlyph <= glyphs.Count() && "Invalid line info" );
425 for( GlyphIndex i = startGlyph; i < endGlyph; ++i )
427 const GlyphInfo& glyphInfo = *( glyphsBuffer + i );
428 const Vector2& position = *( positionsBuffer + i );
429 float glyphX = position.x + glyphInfo.width*0.5f;
430 float glyphY = position.y + glyphInfo.height*0.5f;
432 float distanceToGlyph = fabsf( glyphX - visualX ) + fabsf( glyphY - visualY );
434 if( distanceToGlyph < closestDistance )
436 closestDistance = distanceToGlyph;
438 leftOfGlyph = ( visualX < glyphX );
442 // Calculate the logical position
443 logical = mVisualModel->GetCharacterIndex( closestGlyph );
445 // Returns the visual position of the glyph
446 visualX = positions[closestGlyph].x;
449 visualX += glyphs[closestGlyph].width;
454 else// if ( RTL ) TODO
460 height = line.lineSize.height;
463 void UpdateCursorPosition()
465 if( 0 == mVisualModel->mGlyphs.Count() )
470 // FIXME GetGlyphIndex() is behaving strangely
472 GlyphIndex cursorGlyph = mVisualModel->GetGlyphIndex( mPrimaryCursorPosition );
474 GlyphIndex cursorGlyph( 0u );
475 for( cursorGlyph = 0; cursorGlyph < mVisualModel->mGlyphs.Count(); ++cursorGlyph )
477 if( mPrimaryCursorPosition == mVisualModel->GetCharacterIndex( cursorGlyph ) )
484 float visualX( 0.0f );
485 float visualY( 0.0f );
486 LineIndex lineIndex( 0u );
487 const Vector<LineRun>& lineRuns = mVisualModel->mLines;
489 if( cursorGlyph > 0 )
493 visualX = mVisualModel->mGlyphPositions[ cursorGlyph ].x;
495 visualX += mVisualModel->mGlyphs[ cursorGlyph ].width;
497 // Find the line height
498 for( GlyphIndex lastGlyph = 0; lineIndex < lineRuns.Count(); ++lineIndex )
500 lastGlyph = (lineRuns[lineIndex].glyphIndex + lineRuns[lineIndex].numberOfGlyphs);
501 if( cursorGlyph < lastGlyph )
508 mDecorator->SetPosition( PRIMARY_CURSOR, visualX, visualY, lineRuns[lineIndex].lineSize.height );
509 mDecoratorUpdated = true;
512 LogicalModelPtr mLogicalModel;
513 VisualModelPtr mVisualModel;
514 DecoratorPtr mDecorator;
516 std::string mPlaceholderText;
519 * This is used to delay handling events until after the model has been updated.
520 * The number of updates to the model is minimized to improve performance.
522 vector<Event> mEventQueue; ///< The queue of touch events etc.
524 State mState; ///< Selection mode, edit mode etc.
526 CharacterIndex mPrimaryCursorPosition; ///< Index into logical model for primary cursor
527 CharacterIndex mSecondaryCursorPosition; ///< Index into logical model for secondary cursor
530 * 0,0 means that the top-left corner of the layout matches the top-left corner of the UI control.
531 * Typically this will have a negative value with scrolling occurs.
533 Vector2 mScrollPosition; ///< The text is offset by this position when scrolling.
535 bool mDecoratorUpdated : 1; ///< True if the decorator was updated during event processing
536 bool mCursorBlinkEnabled : 1; ///< True if cursor should blink when active
537 bool mGrabHandleEnabled : 1; ///< True if grab handle is enabled
538 bool mGrabHandlePopupEnabled : 1; ///< True if the grab handle popu-up should be shown
539 bool mSelectionEnabled : 1; ///< True if selection handles, highlight etc. are enabled
540 bool mHorizontalScrollingEnabled : 1; ///< True if horizontal scrolling is enabled
541 bool mVerticalScrollingEnabled : 1; ///< True if vertical scrolling is enabled
542 bool mUpdateCursorPosition : 1; ///< True if the visual position of the cursor must be recalculated
545 struct Controller::FontDefaults
548 : mDefaultPointSize(0.0f),
553 FontId GetFontId( TextAbstraction::FontClient& fontClient )
557 Dali::TextAbstraction::PointSize26Dot6 pointSize = mDefaultPointSize*64;
558 mFontId = fontClient.GetFontId( mDefaultFontFamily, mDefaultFontStyle, pointSize );
564 std::string mDefaultFontFamily;
565 std::string mDefaultFontStyle;
566 float mDefaultPointSize;
570 struct Controller::Impl
572 Impl( ControlInterface& controlInterface )
573 : mControlInterface( controlInterface ),
576 mFontDefaults( NULL ),
584 mOperationsPending( NO_OPERATION ),
585 mRecalculateNaturalSize( true )
587 mLogicalModel = LogicalModel::New();
588 mVisualModel = VisualModel::New();
590 mFontClient = TextAbstraction::FontClient::Get();
592 mView.SetVisualModel( mVisualModel );
600 ControlInterface& mControlInterface; ///< Reference to the text controller.
601 LogicalModelPtr mLogicalModel; ///< Pointer to the logical model.
602 VisualModelPtr mVisualModel; ///< Pointer to the visual model.
603 FontDefaults* mFontDefaults; ///< Avoid allocating this when the user does not specify a font.
604 Controller::TextInput* mTextInput; ///< Avoid allocating everything for text input until EnableTextInput().
605 TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
606 View mView; ///< The view interface to the rendering back-end.
607 LayoutEngine mLayoutEngine; ///< The layout engine.
608 std::vector<ModifyEvent> mModifyEvents; ///< Temporary stores the text set until the next relayout.
609 Size mControlSize; ///< The size of the control.
610 Vector2 mAlignmentOffset; ///< Vertical and horizontal offset of the whole text inside the control due to alignment.
611 OperationsMask mOperationsPending; ///< Operations pending to be done to layout the text.
612 bool mRecalculateNaturalSize:1; ///< Whether the natural size needs to be recalculated.
615 ControllerPtr Controller::New( ControlInterface& controlInterface )
617 return ControllerPtr( new Controller( controlInterface ) );
620 void Controller::SetText( const std::string& text )
622 // Cancel previously queued inserts etc.
623 mImpl->mModifyEvents.clear();
625 // Keep until size negotiation
627 event.type = REPLACE_TEXT;
629 mImpl->mModifyEvents.push_back( event );
631 if( mImpl->mTextInput )
633 // Cancel previously queued events
634 mImpl->mTextInput->mEventQueue.clear();
636 // TODO - Hide selection decorations
640 void Controller::GetText( std::string& text ) const
642 if( !mImpl->mModifyEvents.empty() &&
643 REPLACE_TEXT == mImpl->mModifyEvents[0].type )
645 text = mImpl->mModifyEvents[0].text;
649 // TODO - Convert from UTF-32
653 void Controller::SetPlaceholderText( const std::string& text )
655 if( !mImpl->mTextInput )
657 mImpl->mTextInput->mPlaceholderText = text;
661 void Controller::GetPlaceholderText( std::string& text ) const
663 if( !mImpl->mTextInput )
665 text = mImpl->mTextInput->mPlaceholderText;
669 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
671 if( !mImpl->mFontDefaults )
673 mImpl->mFontDefaults = new Controller::FontDefaults();
676 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
677 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
678 mImpl->mOperationsPending = ALL_OPERATIONS;
679 mImpl->mRecalculateNaturalSize = true;
681 // Clear the font-specific data
682 mImpl->mLogicalModel->mFontRuns.Clear();
683 mImpl->mVisualModel->mGlyphs.Clear();
684 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
685 mImpl->mVisualModel->mCharactersToGlyph.Clear();
686 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
687 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
688 mImpl->mVisualModel->mGlyphPositions.Clear();
689 mImpl->mVisualModel->mLines.Clear();
694 const std::string& Controller::GetDefaultFontFamily() const
696 if( mImpl->mFontDefaults )
698 return mImpl->mFontDefaults->mDefaultFontFamily;
704 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
706 if( !mImpl->mFontDefaults )
708 mImpl->mFontDefaults = new Controller::FontDefaults();
711 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
712 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
713 mImpl->mOperationsPending = ALL_OPERATIONS;
714 mImpl->mRecalculateNaturalSize = true;
716 // Clear the font-specific data
717 mImpl->mLogicalModel->mFontRuns.Clear();
718 mImpl->mVisualModel->mGlyphs.Clear();
719 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
720 mImpl->mVisualModel->mCharactersToGlyph.Clear();
721 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
722 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
723 mImpl->mVisualModel->mGlyphPositions.Clear();
724 mImpl->mVisualModel->mLines.Clear();
729 const std::string& Controller::GetDefaultFontStyle() const
731 if( mImpl->mFontDefaults )
733 return mImpl->mFontDefaults->mDefaultFontStyle;
739 void Controller::SetDefaultPointSize( float pointSize )
741 if( !mImpl->mFontDefaults )
743 mImpl->mFontDefaults = new Controller::FontDefaults();
746 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
747 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
748 mImpl->mOperationsPending = ALL_OPERATIONS;
749 mImpl->mRecalculateNaturalSize = true;
751 // Clear the font-specific data
752 mImpl->mLogicalModel->mFontRuns.Clear();
753 mImpl->mVisualModel->mGlyphs.Clear();
754 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
755 mImpl->mVisualModel->mCharactersToGlyph.Clear();
756 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
757 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
758 mImpl->mVisualModel->mGlyphPositions.Clear();
759 mImpl->mVisualModel->mLines.Clear();
764 float Controller::GetDefaultPointSize() const
766 if( mImpl->mFontDefaults )
768 return mImpl->mFontDefaults->mDefaultPointSize;
774 void Controller::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
776 if( mImpl->mFontDefaults )
779 fontRun.characterRun.characterIndex = 0;
780 fontRun.characterRun.numberOfCharacters = numberOfCharacters;
781 fontRun.fontId = mImpl->mFontDefaults->GetFontId( mImpl->mFontClient );
782 fontRun.isDefault = true;
784 fonts.PushBack( fontRun );
788 void Controller::EnableTextInput( DecoratorPtr decorator )
790 if( !mImpl->mTextInput )
792 mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
796 void Controller::SetEnableCursorBlink( bool enable )
798 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "TextInput disabled" );
800 if( mImpl->mTextInput )
802 mImpl->mTextInput->mCursorBlinkEnabled = enable;
805 mImpl->mTextInput->mDecorator )
807 mImpl->mTextInput->mDecorator->StopCursorBlink();
812 bool Controller::GetEnableCursorBlink() const
814 if( mImpl->mTextInput )
816 return mImpl->mTextInput->mCursorBlinkEnabled;
822 const Vector2& Controller::GetScrollPosition() const
824 if( mImpl->mTextInput )
826 return mImpl->mTextInput->mScrollPosition;
829 return Vector2::ZERO;
832 const Vector2& Controller::GetAlignmentOffset() const
834 return mImpl->mAlignmentOffset;
837 Vector3 Controller::GetNaturalSize()
841 // Make sure the model is up-to-date before layouting
842 ProcessModifyEvents();
844 if( mImpl->mRecalculateNaturalSize )
846 // Operations that can be done only once until the text changes.
847 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
855 // Make sure the model is up-to-date before layouting
856 UpdateModel( onlyOnceOperations );
858 // Operations that need to be done if the size changes.
859 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
863 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
864 static_cast<OperationsMask>( onlyOnceOperations |
866 naturalSize.GetVectorXY() );
868 // Do not do again the only once operations.
869 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
871 // Do the size related operations again.
872 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
874 // Stores the natural size to avoid recalculate it again
875 // unless the text/style changes.
876 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
878 mImpl->mRecalculateNaturalSize = false;
882 naturalSize = mImpl->mVisualModel->GetNaturalSize();
888 float Controller::GetHeightForWidth( float width )
890 // Make sure the model is up-to-date before layouting
891 ProcessModifyEvents();
894 if( width != mImpl->mControlSize.width )
896 // Operations that can be done only once until the text changes.
897 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
905 // Make sure the model is up-to-date before layouting
906 UpdateModel( onlyOnceOperations );
908 // Operations that need to be done if the size changes.
909 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
913 DoRelayout( Size( width, MAX_FLOAT ),
914 static_cast<OperationsMask>( onlyOnceOperations |
918 // Do not do again the only once operations.
919 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
921 // Do the size related operations again.
922 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
926 layoutSize = mImpl->mVisualModel->GetActualSize();
929 return layoutSize.height;
932 bool Controller::Relayout( const Size& size )
934 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
936 bool glyphsRemoved( false );
937 if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
939 mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
940 glyphsRemoved = true;
943 // Not worth to relayout if width or height is equal to zero.
944 return glyphsRemoved;
947 if( size != mImpl->mControlSize )
949 // Operations that need to be done if the size changes.
950 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
956 mImpl->mControlSize = size;
959 // Make sure the model is up-to-date before layouting
960 ProcessModifyEvents();
961 UpdateModel( mImpl->mOperationsPending );
964 bool updated = DoRelayout( mImpl->mControlSize,
965 mImpl->mOperationsPending,
968 // Do not re-do any operation until something changes.
969 mImpl->mOperationsPending = NO_OPERATION;
971 // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
972 CalculateTextAlignment( size );
974 if( mImpl->mTextInput )
976 // Move the cursor, grab handle etc.
977 updated = mImpl->mTextInput->ProcessInputEvents( mImpl->mControlSize, mImpl->mAlignmentOffset ) || updated;
983 void Controller::ProcessModifyEvents()
985 std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
987 for( unsigned int i=0; i<events.size(); ++i )
989 if( REPLACE_TEXT == events[0].type )
991 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
992 DALI_ASSERT_DEBUG( 0 == i && "Unexpected REPLACE event" );
994 ReplaceTextEvent( events[0].text );
996 else if( INSERT_TEXT == events[0].type )
998 InsertTextEvent( events[0].text );
1000 else if( DELETE_TEXT == events[0].type )
1006 // Discard temporary text
1010 void Controller::ReplaceTextEvent( const std::string& text )
1013 mImpl->mLogicalModel->mText.Clear();
1014 mImpl->mLogicalModel->mScriptRuns.Clear();
1015 mImpl->mLogicalModel->mFontRuns.Clear();
1016 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1017 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1018 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1019 mImpl->mLogicalModel->mCharacterDirections.Clear();
1020 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1021 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1022 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1023 mImpl->mVisualModel->mGlyphs.Clear();
1024 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1025 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1026 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1027 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1028 mImpl->mVisualModel->mGlyphPositions.Clear();
1029 mImpl->mVisualModel->mLines.Clear();
1031 // Convert text into UTF-32
1032 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1033 utf32Characters.Resize( text.size() );
1035 // This is a bit horrible but std::string returns a (signed) char*
1036 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1038 // Transform a text array encoded in utf8 into an array encoded in utf32.
1039 // It returns the actual number of characters.
1040 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1041 utf32Characters.Resize( characterCount );
1043 // Reset the cursor position
1044 if( mImpl->mTextInput )
1046 mImpl->mTextInput->mPrimaryCursorPosition = characterCount;
1047 // TODO - handle secondary cursor
1050 // The natural size needs to be re-calculated.
1051 mImpl->mRecalculateNaturalSize = true;
1053 // Apply modifications to the model
1054 mImpl->mOperationsPending = ALL_OPERATIONS;
1055 UpdateModel( ALL_OPERATIONS );
1056 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1058 UPDATE_ACTUAL_SIZE |
1062 void Controller::InsertTextEvent( const std::string& text )
1064 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1066 // TODO - Optimize this
1067 mImpl->mLogicalModel->mScriptRuns.Clear();
1068 mImpl->mLogicalModel->mFontRuns.Clear();
1069 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1070 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1071 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1072 mImpl->mLogicalModel->mCharacterDirections.Clear();
1073 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1074 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1075 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1076 mImpl->mVisualModel->mGlyphs.Clear();
1077 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1078 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1079 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1080 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1081 mImpl->mVisualModel->mGlyphPositions.Clear();
1082 mImpl->mVisualModel->mLines.Clear();
1084 // Convert text into UTF-32
1085 Vector<Character> utf32Characters;
1086 utf32Characters.Resize( text.size() );
1088 // This is a bit horrible but std::string returns a (signed) char*
1089 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1091 // Transform a text array encoded in utf8 into an array encoded in utf32.
1092 // It returns the actual number of characters.
1093 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1094 utf32Characters.Resize( characterCount );
1096 // Insert at current cursor position
1097 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1098 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1100 if( cursorIndex < modifyText.Count() )
1102 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.End() );
1106 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.End() );
1109 // Advance the cursor position
1112 // The natural size needs to be re-calculated.
1113 mImpl->mRecalculateNaturalSize = true;
1115 // Apply modifications to the model; TODO - Optimize this
1116 mImpl->mOperationsPending = ALL_OPERATIONS;
1117 UpdateModel( ALL_OPERATIONS );
1118 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1120 UPDATE_ACTUAL_SIZE |
1123 // Queue a cursor reposition event; this must wait until after DoRelayout()
1124 mImpl->mTextInput->mUpdateCursorPosition = true;
1127 void Controller::DeleteTextEvent()
1129 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1131 // TODO - Optimize this
1132 mImpl->mLogicalModel->mScriptRuns.Clear();
1133 mImpl->mLogicalModel->mFontRuns.Clear();
1134 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1135 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1136 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1137 mImpl->mLogicalModel->mCharacterDirections.Clear();
1138 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1139 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1140 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1141 mImpl->mVisualModel->mGlyphs.Clear();
1142 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1143 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1144 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1145 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1146 mImpl->mVisualModel->mGlyphPositions.Clear();
1147 mImpl->mVisualModel->mLines.Clear();
1149 // Delte at current cursor position
1150 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1151 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1153 if( cursorIndex > 0 &&
1154 cursorIndex-1 < modifyText.Count() )
1156 modifyText.Remove( modifyText.Begin() + cursorIndex - 1 );
1158 // Cursor position retreat
1162 // The natural size needs to be re-calculated.
1163 mImpl->mRecalculateNaturalSize = true;
1165 // Apply modifications to the model; TODO - Optimize this
1166 mImpl->mOperationsPending = ALL_OPERATIONS;
1167 UpdateModel( ALL_OPERATIONS );
1168 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1170 UPDATE_ACTUAL_SIZE |
1173 // Queue a cursor reposition event; this must wait until after DoRelayout()
1174 mImpl->mTextInput->mUpdateCursorPosition = true;
1177 void Controller::UpdateModel( OperationsMask operationsRequired )
1179 // Calculate the operations to be done.
1180 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1182 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1184 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
1186 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1187 if( GET_LINE_BREAKS & operations )
1189 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
1190 // calculate the bidirectional info for each 'paragraph'.
1191 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
1192 // is not shaped together).
1193 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
1195 SetLineBreakInfo( utf32Characters,
1199 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1200 if( GET_WORD_BREAKS & operations )
1202 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
1203 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
1205 SetWordBreakInfo( utf32Characters,
1209 const bool getScripts = GET_SCRIPTS & operations;
1210 const bool validateFonts = VALIDATE_FONTS & operations;
1212 Vector<ScriptRun>& scripts = mImpl->mLogicalModel->mScriptRuns;
1213 Vector<FontRun>& validFonts = mImpl->mLogicalModel->mFontRuns;
1215 if( getScripts || validateFonts )
1217 // Validates the fonts assigned by the application or assigns default ones.
1218 // It makes sure all the characters are going to be rendered by the correct font.
1219 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
1223 // Retrieves the scripts used in the text.
1224 multilanguageSupport.SetScripts( utf32Characters,
1231 if( 0u == validFonts.Count() )
1233 // Copy the requested font defaults received via the property system.
1234 // These may not be valid i.e. may not contain glyphs for the necessary scripts.
1235 GetDefaultFonts( validFonts, numberOfCharacters );
1238 // Validates the fonts. If there is a character with no assigned font it sets a default one.
1239 // After this call, fonts are validated.
1240 multilanguageSupport.ValidateFonts( utf32Characters,
1246 Vector<Character> mirroredUtf32Characters;
1247 bool textMirrored = false;
1248 if( BIDI_INFO & operations )
1250 // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
1251 // bidirectional info.
1253 Length numberOfParagraphs = 0u;
1255 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
1256 for( Length index = 0u; index < numberOfCharacters; ++index )
1258 if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
1260 ++numberOfParagraphs;
1264 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1265 bidirectionalInfo.Reserve( numberOfParagraphs );
1267 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1268 SetBidirectionalInfo( utf32Characters,
1271 bidirectionalInfo );
1273 if( 0u != bidirectionalInfo.Count() )
1275 // This paragraph has right to left text. Some characters may need to be mirrored.
1276 // TODO: consider if the mirrored string can be stored as well.
1278 textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
1280 // Only set the character directions if there is right to left characters.
1281 Vector<CharacterDirection>& directions = mImpl->mLogicalModel->mCharacterDirections;
1282 directions.Resize( numberOfCharacters );
1284 GetCharactersDirection( bidirectionalInfo,
1289 // There is no right to left characters. Clear the directions vector.
1290 mImpl->mLogicalModel->mCharacterDirections.Clear();
1295 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1296 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1297 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1298 if( SHAPE_TEXT & operations )
1300 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1302 ShapeText( textToShape,
1307 glyphsToCharactersMap,
1308 charactersPerGlyph );
1311 const Length numberOfGlyphs = glyphs.Count();
1313 if( GET_GLYPH_METRICS & operations )
1315 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
1318 if( 0u != numberOfGlyphs )
1320 // Create the glyph to character conversion table and the 'number of glyphs' per character.
1321 mImpl->mVisualModel->CreateCharacterToGlyphTable(numberOfCharacters );
1322 mImpl->mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
1326 bool Controller::DoRelayout( const Size& size,
1327 OperationsMask operationsRequired,
1330 bool viewUpdated( false );
1332 // Calculate the operations to be done.
1333 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1335 if( LAYOUT & operations )
1337 // Some vectors with data needed to layout and reorder may be void
1338 // after the first time the text has been laid out.
1339 // Fill the vectors again.
1341 Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
1343 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1344 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1345 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1346 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1347 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1349 // Set the layout parameters.
1350 LayoutParameters layoutParameters( size,
1351 mImpl->mLogicalModel->mText.Begin(),
1352 lineBreakInfo.Begin(),
1353 wordBreakInfo.Begin(),
1356 glyphsToCharactersMap.Begin(),
1357 charactersPerGlyph.Begin() );
1359 // The laid-out lines.
1360 // It's not possible to know in how many lines the text is going to be laid-out,
1361 // but it can be resized at least with the number of 'paragraphs' to avoid
1362 // some re-allocations.
1363 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1365 // Delete any previous laid out lines before setting the new ones.
1368 // The capacity of the bidirectional paragraph info is the number of paragraphs.
1369 lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
1371 // Resize the vector of positions to have the same size than the vector of glyphs.
1372 Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
1373 glyphPositions.Resize( numberOfGlyphs );
1375 // Update the visual model.
1376 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
1383 // Reorder the lines
1384 if( REORDER & operations )
1386 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1388 // Check first if there are paragraphs with bidirectional info.
1389 if( 0u != bidirectionalInfo.Count() )
1392 const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
1394 // Reorder the lines.
1395 Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
1396 lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
1397 ReorderLines( bidirectionalInfo,
1399 lineBidirectionalInfoRuns );
1401 // Set the bidirectional info into the model.
1402 const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
1403 mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
1404 numberOfBidirectionalInfoRuns );
1406 // Set the bidirectional info per line into the layout parameters.
1407 layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
1408 layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
1410 // Get the character to glyph conversion table and set into the layout.
1411 layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
1413 // Get the glyphs per character table and set into the layout.
1414 layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
1416 // Re-layout the text. Reorder those lines with right to left characters.
1417 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1420 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
1421 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
1422 endIt = lineBidirectionalInfoRuns.End();
1426 BidirectionalLineInfoRun& bidiLineInfo = *it;
1428 free( bidiLineInfo.visualToLogicalMap );
1433 if( ALIGN & operations )
1435 mImpl->mLayoutEngine.Align( layoutParameters,
1441 // Sets the actual size.
1442 if( UPDATE_ACTUAL_SIZE & operations )
1444 mImpl->mVisualModel->SetActualSize( layoutSize );
1450 layoutSize = mImpl->mVisualModel->GetActualSize();
1456 void Controller::CalculateTextAlignment( const Size& size )
1458 // TODO : Calculate the vertical offset.
1460 // Get the direction of the first character.
1461 const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
1463 const Size& actualSize = mImpl->mVisualModel->GetActualSize();
1465 // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1466 LayoutEngine::Alignment alignment = mImpl->mLayoutEngine.GetAlignment();
1467 if( firstParagraphDirection &&
1468 ( LayoutEngine::ALIGN_CENTER != alignment ) )
1470 if( LayoutEngine::ALIGN_BEGIN == alignment )
1472 alignment = LayoutEngine::ALIGN_END;
1476 alignment = LayoutEngine::ALIGN_BEGIN;
1482 case LayoutEngine::ALIGN_BEGIN:
1484 mImpl->mAlignmentOffset = Vector2::ZERO;
1487 case LayoutEngine::ALIGN_CENTER:
1489 mImpl->mAlignmentOffset.y = 0.f;
1490 const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1491 mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1494 case LayoutEngine::ALIGN_END:
1496 mImpl->mAlignmentOffset.y = 0.f;
1497 mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1503 View& Controller::GetView()
1505 return mImpl->mView;
1508 LayoutEngine& Controller::GetLayoutEngine()
1510 return mImpl->mLayoutEngine;
1513 void Controller::RequestRelayout()
1515 mImpl->mControlInterface.RequestTextRelayout();
1518 void Controller::KeyboardFocusGainEvent()
1520 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
1522 if( mImpl->mTextInput )
1524 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
1525 mImpl->mTextInput->mEventQueue.push_back( event );
1531 void Controller::KeyboardFocusLostEvent()
1533 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
1535 if( mImpl->mTextInput )
1537 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
1538 mImpl->mTextInput->mEventQueue.push_back( event );
1544 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1546 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" );
1548 if( mImpl->mTextInput &&
1549 keyEvent.state == KeyEvent::Down )
1551 int keyCode = keyEvent.keyCode;
1552 const std::string& keyString = keyEvent.keyPressed;
1554 // Pre-process to separate modifying events from non-modifying input events.
1555 if( Dali::DALI_KEY_ESCAPE == keyCode )
1557 // Escape key is a special case which causes focus loss
1558 KeyboardFocusLostEvent();
1560 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
1561 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1562 Dali::DALI_KEY_CURSOR_UP == keyCode ||
1563 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1565 TextInput::Event event( TextInput::CURSOR_KEY_EVENT );
1566 event.p1.mInt = keyCode;
1567 mImpl->mTextInput->mEventQueue.push_back( event );
1569 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1571 // Queue a delete event
1573 event.type = DELETE_TEXT;
1574 mImpl->mModifyEvents.push_back( event );
1576 else if( !keyString.empty() )
1578 // Queue an insert event
1580 event.type = INSERT_TEXT;
1581 event.text = keyString;
1582 mImpl->mModifyEvents.push_back( event );
1591 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1593 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
1595 if( mImpl->mTextInput )
1597 TextInput::Event event( TextInput::TAP_EVENT );
1598 event.p1.mUint = tapCount;
1599 event.p2.mFloat = x;
1600 event.p3.mFloat = y;
1601 mImpl->mTextInput->mEventQueue.push_back( event );
1607 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1609 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected PanEvent" );
1611 if( mImpl->mTextInput )
1613 TextInput::Event event( TextInput::PAN_EVENT );
1614 event.p1.mInt = state;
1615 event.p2.mFloat = displacement.x;
1616 event.p3.mFloat = displacement.y;
1617 mImpl->mTextInput->mEventQueue.push_back( event );
1623 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
1625 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
1627 if( mImpl->mTextInput )
1629 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
1630 event.p1.mUint = state;
1631 event.p2.mFloat = x;
1632 event.p3.mFloat = y;
1633 mImpl->mTextInput->mEventQueue.push_back( event );
1639 Controller::~Controller()
1644 Controller::Controller( ControlInterface& controlInterface )
1647 mImpl = new Controller::Impl( controlInterface );
1652 } // namespace Toolkit