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>
22 #include <dali-toolkit/internal/text/character-set-conversion.h>
23 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
24 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
25 #include <dali-toolkit/internal/text/logical-model.h>
26 #include <dali-toolkit/internal/text/multi-language-support.h>
27 #include <dali-toolkit/internal/text/script-run.h>
28 #include <dali-toolkit/internal/text/segmentation.h>
29 #include <dali-toolkit/internal/text/shaper.h>
30 #include <dali-toolkit/internal/text/text-view.h>
31 #include <dali-toolkit/internal/text/visual-model.h>
36 #include <dali/public-api/adaptor-framework/key.h>
37 #include <dali/public-api/text-abstraction/font-client.h>
43 const float MAX_FLOAT = std::numeric_limits<float>::max();
44 const std::string EMPTY_STRING;
56 struct Controller::TextInput
58 // Used to queue input events until DoRelayout()
61 KEYBOARD_FOCUS_GAIN_EVENT,
62 KEYBOARD_FOCUS_LOST_EVENT,
78 Event( EventType eventType )
98 TextInput( LogicalModelPtr logicalModel,
99 VisualModelPtr visualModel,
100 DecoratorPtr decorator )
101 : mLogicalModel( logicalModel ),
102 mVisualModel( visualModel ),
103 mDecorator( decorator ),
109 * @brief Helper to move the cursor, grab handle etc.
111 bool ProcessInputEvents()
113 mDecoratorUpdated = false;
117 for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
121 case KEYBOARD_FOCUS_GAIN_EVENT:
123 OnKeyboardFocus( true );
126 case KEYBOARD_FOCUS_LOST_EVENT:
128 OnKeyboardFocus( false );
141 case GRAB_HANDLE_EVENT:
143 OnGrabHandleEvent( *iter );
152 return mDecoratorUpdated;
155 void OnKeyboardFocus( bool hasFocus )
159 void OnKeyEvent( const Event& event )
161 int keyCode = event.p1.mInt;
163 // Handle state changes
164 if( Dali::DALI_KEY_ESCAPE == keyCode )
166 ChangeState( INACTIVE ); // Escape key ends edit mode
168 else if ( event.p2.mString )
170 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
171 ChangeState( EDITING );
174 // Handle the actual key event
175 if( Dali::DALI_KEY_BACKSPACE == keyCode )
177 HandleBackspaceKey();
179 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
180 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
181 Dali::DALI_KEY_CURSOR_UP == keyCode ||
182 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
184 HandleCursorKey( keyCode );
186 else if ( event.p2.mString )
188 HandleKeyString( event.p2.mString );
190 delete [] event.p2.mString;
194 void HandleBackspaceKey()
199 void HandleCursorKey( int keyCode )
204 void HandleKeyString( const char* keyString )
209 void OnTapEvent( const Event& event )
211 unsigned int tapCount = event.p1.mUint;
215 ChangeState( EDITING );
217 float xPosition = event.p2.mFloat;
218 float yPosition = event.p3.mFloat;
220 GetClosestCursorPosition( xPosition, yPosition, height );
221 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
223 mDecoratorUpdated = true;
225 else if( 2u == tapCount )
227 ChangeState( SELECTING );
231 void OnGrabHandleEvent( const Event& event )
233 unsigned int state = event.p1.mUint;
235 if( GRAB_HANDLE_PRESSED == state )
237 float xPosition = event.p2.mFloat;
238 float yPosition = event.p3.mFloat;
241 GetClosestCursorPosition( xPosition, yPosition, height );
243 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
244 mDecorator->HidePopup();
245 mDecoratorUpdated = true;
247 else if ( GRAB_HANDLE_RELEASED == state )
249 mDecorator->ShowPopup();
254 void ChangeState( State newState )
256 if( mState != newState )
260 if( INACTIVE == mState )
262 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
263 mDecorator->StopCursorBlink();
264 mDecorator->SetGrabHandleActive( false );
265 mDecorator->SetSelectionActive( false );
266 mDecorator->HidePopup();
267 mDecoratorUpdated = true;
269 else if ( SELECTING == mState )
271 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
272 mDecorator->StopCursorBlink();
273 mDecorator->SetGrabHandleActive( false );
274 mDecorator->SetSelectionActive( true );
275 mDecoratorUpdated = true;
277 else if( EDITING == mState )
279 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
280 mDecorator->StartCursorBlink();
281 mDecorator->SetGrabHandleActive( true );
282 mDecorator->SetSelectionActive( false );
283 mDecoratorUpdated = true;
288 void GetClosestCursorPosition( float& x, float& y, float& height )
290 // TODO - Look at LineRuns first
292 Text::Length numberOfGlyphs = mVisualModel->GetNumberOfGlyphs();
293 if( 0 == numberOfGlyphs )
298 Vector<GlyphInfo> glyphs;
299 glyphs.Resize( numberOfGlyphs );
300 mVisualModel->GetGlyphs( glyphs.Begin(), 0, numberOfGlyphs );
301 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
303 Vector<Vector2> positions;
304 positions.Resize( numberOfGlyphs );
305 mVisualModel->GetGlyphPositions( positions.Begin(), 0, numberOfGlyphs );
306 const Vector2* const positionsBuffer = positions.Begin();
308 unsigned int closestGlyph = 0;
309 float closestDistance = MAX_FLOAT;
311 for( unsigned int i = 0, numberOfGLyphs = glyphs.Count(); i < numberOfGLyphs; ++i )
313 const GlyphInfo& glyphInfo = *( glyphsBuffer + i );
314 const Vector2& position = *( positionsBuffer + i );
315 float glyphX = position.x + glyphInfo.width*0.5f;
316 float glyphY = position.y + glyphInfo.height*0.5f;
318 float distanceToGlyph = fabsf( glyphX - x ) + fabsf( glyphY - y );
320 if( distanceToGlyph < closestDistance )
322 closestDistance = distanceToGlyph;
327 // TODO - Consider RTL languages
328 x = positions[closestGlyph].x + glyphs[closestGlyph].width;
332 TextAbstraction::FontClient::Get().GetFontMetrics( glyphs[closestGlyph].fontId, metrics );
333 height = metrics.height; // TODO - Fix for multi-line
336 LogicalModelPtr mLogicalModel;
337 VisualModelPtr mVisualModel;
338 DecoratorPtr mDecorator;
340 std::string mPlaceholderText;
343 * This is used to delay handling events until after the model has been updated.
344 * The number of updates to the model is minimized to improve performance.
346 vector<Event> mEventQueue; ///< The queue of touch events etc.
350 bool mDecoratorUpdated;
353 struct Controller::FontDefaults
356 : mDefaultPointSize(0.0f),
361 FontId GetFontId( TextAbstraction::FontClient& fontClient )
365 Dali::TextAbstraction::PointSize26Dot6 pointSize = mDefaultPointSize*64;
366 mFontId = fontClient.GetFontId( mDefaultFontFamily, mDefaultFontStyle, pointSize );
372 std::string mDefaultFontFamily;
373 std::string mDefaultFontStyle;
374 float mDefaultPointSize;
378 struct Controller::Impl
380 Impl( ControlInterface& controlInterface )
381 : mControlInterface( controlInterface ),
384 mFontDefaults( NULL ),
391 mOperationsPending( NO_OPERATION ),
392 mRecalculateNaturalSize( true )
394 mLogicalModel = LogicalModel::New();
395 mVisualModel = VisualModel::New();
397 mFontClient = TextAbstraction::FontClient::Get();
399 mView.SetVisualModel( mVisualModel );
407 ControlInterface& mControlInterface; ///< Reference to the text controller.
408 LogicalModelPtr mLogicalModel; ///< Pointer to the logical model.
409 VisualModelPtr mVisualModel; ///< Pointer to the visual model.
410 FontDefaults* mFontDefaults; ///< Avoid allocating this when the user does not specify a font.
411 Controller::TextInput* mTextInput; ///< Avoid allocating everything for text input until EnableTextInput().
412 TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
413 View mView; ///< The view interface to the rendering back-end.
414 LayoutEngine mLayoutEngine; ///< The layout engine.
415 std::string mNewText; ///< Temporary stores the text set until the next relayout.
416 Size mControlSize; ///< The size of the control.
417 OperationsMask mOperationsPending; ///< Operations pending to be done to layout the text.
418 bool mRecalculateNaturalSize:1; ///< Whether the natural size needs to be recalculated.
421 ControllerPtr Controller::New( ControlInterface& controlInterface )
423 return ControllerPtr( new Controller( controlInterface ) );
426 void Controller::SetText( const std::string& text )
428 // Keep until size negotiation
429 mImpl->mNewText = text;
431 // All operations need to be done. (convert to utf32, get break info, ..., layout, ...)
432 mImpl->mOperationsPending = ALL_OPERATIONS;
434 // The natural size needs to be re-calculated.
435 mImpl->mRecalculateNaturalSize = true;
437 if( mImpl->mTextInput )
439 // Cancel previously queued events
440 mImpl->mTextInput->mEventQueue.clear();
442 // TODO - Hide selection decorations
446 void Controller::GetText( std::string& text ) const
448 if( !mImpl->mNewText.empty() )
450 text = mImpl->mNewText;
454 // TODO - Convert from UTF-32
458 void Controller::SetPlaceholderText( const std::string& text )
460 if( !mImpl->mTextInput )
462 mImpl->mTextInput->mPlaceholderText = text;
466 void Controller::GetPlaceholderText( std::string& text ) const
468 if( !mImpl->mTextInput )
470 text = mImpl->mTextInput->mPlaceholderText;
474 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
476 if( !mImpl->mFontDefaults )
478 mImpl->mFontDefaults = new Controller::FontDefaults();
481 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
482 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
483 mImpl->mOperationsPending = ALL_OPERATIONS;
484 mImpl->mRecalculateNaturalSize = true;
487 const std::string& Controller::GetDefaultFontFamily() const
489 if( mImpl->mFontDefaults )
491 return mImpl->mFontDefaults->mDefaultFontFamily;
497 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
499 if( !mImpl->mFontDefaults )
501 mImpl->mFontDefaults = new Controller::FontDefaults();
504 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
505 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
506 mImpl->mOperationsPending = ALL_OPERATIONS;
507 mImpl->mRecalculateNaturalSize = true;
510 const std::string& Controller::GetDefaultFontStyle() const
512 if( mImpl->mFontDefaults )
514 return mImpl->mFontDefaults->mDefaultFontStyle;
520 void Controller::SetDefaultPointSize( float pointSize )
522 if( !mImpl->mFontDefaults )
524 mImpl->mFontDefaults = new Controller::FontDefaults();
527 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
528 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
529 mImpl->mOperationsPending = ALL_OPERATIONS;
530 mImpl->mRecalculateNaturalSize = true;
533 float Controller::GetDefaultPointSize() const
535 if( mImpl->mFontDefaults )
537 return mImpl->mFontDefaults->mDefaultPointSize;
543 void Controller::EnableTextInput( DecoratorPtr decorator )
545 if( !mImpl->mTextInput )
547 mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
551 bool Controller::Relayout( const Vector2& size )
553 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
555 // Not worth to relayout if width or height is equal to zero.
559 if( size != mImpl->mControlSize )
561 // Operations that need to be done if the size changes.
562 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
569 mImpl->mControlSize = size;
573 bool updated = DoRelayout( mImpl->mControlSize,
574 mImpl->mOperationsPending,
577 // Do not re-do any operation until something changes.
578 mImpl->mOperationsPending = NO_OPERATION;
580 if( mImpl->mTextInput )
582 // Move the cursor, grab handle etc.
583 updated = mImpl->mTextInput->ProcessInputEvents() || updated;
589 bool Controller::DoRelayout( const Vector2& size,
590 OperationsMask operationsRequired,
593 bool viewUpdated( false );
595 // Calculate the operations to be done.
596 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
598 Vector<Character> utf32Characters;
599 Length characterCount = 0u;
600 if( CONVERT_TO_UTF32 & operations )
602 std::string& text = mImpl->mNewText;
604 // Convert text into UTF-32
605 utf32Characters.Resize( text.size() );
607 // This is a bit horrible but std::string returns a (signed) char*
608 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
610 // Transform a text array encoded in utf8 into an array encoded in utf32.
611 // It returns the actual number of characters.
612 characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
613 utf32Characters.Resize( characterCount );
615 // Sets the text into the model.
616 mImpl->mLogicalModel->SetText( utf32Characters.Begin(), characterCount );
618 // Discard temporary text
622 Vector<LineBreakInfo> lineBreakInfo;
623 if( GET_LINE_BREAKS & operations )
625 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
626 // calculate the bidirectional info for each 'paragraph'.
627 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
628 // is not shaped together).
629 lineBreakInfo.Resize( characterCount, TextAbstraction::LINE_NO_BREAK );
631 SetLineBreakInfo( utf32Characters,
634 mImpl->mLogicalModel->SetLineBreakInfo( lineBreakInfo.Begin(), characterCount );
637 Vector<WordBreakInfo> wordBreakInfo;
638 if( GET_WORD_BREAKS & operations )
640 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
641 wordBreakInfo.Resize( characterCount, TextAbstraction::WORD_NO_BREAK );
643 SetWordBreakInfo( utf32Characters,
646 mImpl->mLogicalModel->SetWordBreakInfo( wordBreakInfo.Begin(), characterCount );
649 const bool getScripts = GET_SCRIPTS & operations;
650 const bool validateFonts = VALIDATE_FONTS & operations;
652 Vector<ScriptRun> scripts;
653 Vector<FontRun> fonts;
655 if( mImpl->mFontDefaults )
657 // TODO - pass into ValidateFonts
660 if( getScripts || validateFonts )
662 // Validates the fonts assigned by the application or assigns default ones.
663 // It makes sure all the characters are going to be rendered by the correct font.
664 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
668 // Retrieves the scripts used in the text.
669 multilanguageSupport.SetScripts( utf32Characters,
673 // Sets the scripts into the model.
674 mImpl->mLogicalModel->SetScripts( scripts.Begin(), scripts.Count() );
679 // Validates the fonts. If there is a character with no assigned font it sets a default one.
680 // After this call, fonts are validated.
681 multilanguageSupport.ValidateFonts( utf32Characters,
685 // Sets the fonts into the model.
686 mImpl->mLogicalModel->SetFonts( fonts.Begin(), fonts.Count() );
690 Vector<GlyphInfo> glyphs;
691 Vector<CharacterIndex> glyphsToCharactersMap;
692 Vector<Length> charactersPerGlyph;
693 if( SHAPE_TEXT & operations )
696 ShapeText( utf32Characters,
701 glyphsToCharactersMap,
702 charactersPerGlyph );
705 if( GET_GLYPH_METRICS & operations )
707 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), glyphs.Count() );
710 Length numberOfGlyphs = glyphs.Count();
711 if( 0u != numberOfGlyphs )
713 // Sets the glyphs into the model.
714 mImpl->mVisualModel->SetGlyphs( glyphs.Begin(),
715 glyphsToCharactersMap.Begin(),
716 charactersPerGlyph.Begin(),
720 if( LAYOUT & operations )
722 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
724 if( 0u == numberOfGlyphs )
726 numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
728 lineBreakInfo.Resize( numberOfCharacters );
729 wordBreakInfo.Resize( numberOfCharacters );
730 glyphs.Resize( numberOfGlyphs );
731 glyphsToCharactersMap.Resize( numberOfGlyphs );
732 charactersPerGlyph.Resize( numberOfGlyphs );
734 mImpl->mLogicalModel->GetLineBreakInfo( lineBreakInfo.Begin(),
736 numberOfCharacters );
738 mImpl->mLogicalModel->GetWordBreakInfo( wordBreakInfo.Begin(),
740 numberOfCharacters );
742 mImpl->mVisualModel->GetGlyphs( glyphs.Begin(),
746 mImpl->mVisualModel->GetGlyphToCharacterMap( glyphsToCharactersMap.Begin(),
750 mImpl->mVisualModel->GetCharactersPerGlyphMap( charactersPerGlyph.Begin(),
755 // Set the layout parameters.
756 LayoutParameters layoutParameters( size,
757 lineBreakInfo.Begin(),
758 wordBreakInfo.Begin(),
761 glyphsToCharactersMap.Begin(),
762 charactersPerGlyph.Begin() );
764 // Reserve space to set the positions of the glyphs.
765 Vector<Vector2> glyphPositions;
766 glyphPositions.Resize( numberOfGlyphs );
768 // The laid-out lines.
769 // It's not possible to know in how many lines the text is going to be laid-out,
770 // but it can be resized at least with the number of 'paragraphs' to avoid
771 // some re-allocations.
772 Vector<LineRun> lines;
773 lines.Reserve( mImpl->mLogicalModel->GetNumberOfBidirectionalInfoRuns( 0u, numberOfCharacters ) );
775 // Update the visual model.
776 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
783 // Sets the positions into the model.
784 if( UPDATE_POSITIONS & operations )
786 mImpl->mVisualModel->SetGlyphPositions( glyphPositions.Begin(),
790 // Sets the lines into the model.
791 if( UPDATE_LINES & operations )
793 mImpl->mVisualModel->SetLines( lines.Begin(),
797 // Sets the actual size.
798 if( UPDATE_ACTUAL_SIZE & operations )
800 mImpl->mVisualModel->SetActualSize( layoutSize );
806 layoutSize = mImpl->mVisualModel->GetActualSize();
812 Vector3 Controller::GetNaturalSize()
816 if( mImpl->mRecalculateNaturalSize )
818 // Operations that can be done only once until the text changes.
819 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
827 // Operations that need to be done if the size changes.
828 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
831 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
832 static_cast<OperationsMask>( onlyOnceOperations |
834 naturalSize.GetVectorXY() );
836 // Do not do again the only once operations.
837 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
839 // Do the size related operations again.
840 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
842 // Stores the natural size to avoid recalculate it again
843 // unless the text/style changes.
844 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
846 mImpl->mRecalculateNaturalSize = false;
850 naturalSize = mImpl->mVisualModel->GetNaturalSize();
856 float Controller::GetHeightForWidth( float width )
859 if( width != mImpl->mControlSize.width )
861 // Operations that can be done only once until the text changes.
862 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
870 // Operations that need to be done if the size changes.
871 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
874 DoRelayout( Size( width, MAX_FLOAT ),
875 static_cast<OperationsMask>( onlyOnceOperations |
879 // Do not do again the only once operations.
880 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
882 // Do the size related operations again.
883 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
887 layoutSize = mImpl->mVisualModel->GetActualSize();
890 return layoutSize.height;
893 View& Controller::GetView()
898 LayoutEngine& Controller::GetLayoutEngine()
900 return mImpl->mLayoutEngine;
903 void Controller::RequestRelayout()
905 mImpl->mControlInterface.RequestTextRelayout();
908 void Controller::KeyboardFocusGainEvent()
910 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
912 if( mImpl->mTextInput )
914 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
915 mImpl->mTextInput->mEventQueue.push_back( event );
921 void Controller::KeyboardFocusLostEvent()
923 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
925 if( mImpl->mTextInput )
927 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
928 mImpl->mTextInput->mEventQueue.push_back( event );
934 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
936 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" );
938 if( mImpl->mTextInput )
940 TextInput::Event event( TextInput::KEY_EVENT );
941 event.p1.mInt = keyEvent.keyCode;
942 event.p2.mString = NULL;
944 const std::string& keyString = keyEvent.keyPressed;
945 if ( !keyString.empty() )
947 event.p2.mString = new char[keyString.size() + 1];
948 std::copy(keyString.begin(), keyString.end(), event.p2.mString);
949 event.p2.mString[keyString.size()] = '\0';
952 mImpl->mTextInput->mEventQueue.push_back( event );
960 void Controller::TapEvent( unsigned int tapCount, float x, float y )
962 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
964 if( mImpl->mTextInput )
966 TextInput::Event event( TextInput::TAP_EVENT );
967 event.p1.mUint = tapCount;
970 mImpl->mTextInput->mEventQueue.push_back( event );
976 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
978 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
980 if( mImpl->mTextInput )
982 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
983 event.p1.mUint = state;
986 mImpl->mTextInput->mEventQueue.push_back( event );
992 Controller::~Controller()
997 Controller::Controller( ControlInterface& controlInterface )
1000 mImpl = new Controller::Impl( controlInterface );
1005 } // namespace Toolkit