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 mDecoratorUpdated = true;
248 void ChangeState( State newState )
250 if( mState != newState )
254 if( INACTIVE == mState )
256 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
257 mDecorator->StopCursorBlink();
258 mDecorator->SetGrabHandleActive( false );
259 mDecorator->SetSelectionActive( false );
260 mDecoratorUpdated = true;
262 else if ( SELECTING == mState )
264 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
265 mDecorator->StopCursorBlink();
266 mDecorator->SetGrabHandleActive( false );
267 mDecorator->SetSelectionActive( true );
268 mDecoratorUpdated = true;
270 else if( EDITING == mState )
272 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
273 mDecorator->StartCursorBlink();
274 mDecorator->SetGrabHandleActive( true );
275 mDecorator->SetSelectionActive( false );
276 mDecoratorUpdated = true;
281 void GetClosestCursorPosition( float& x, float& y, float& height )
283 // TODO - Look at LineRuns first
285 Text::Length numberOfGlyphs = mVisualModel->GetNumberOfGlyphs();
286 if( 0 == numberOfGlyphs )
291 Vector<GlyphInfo> glyphs;
292 glyphs.Resize( numberOfGlyphs );
293 mVisualModel->GetGlyphs( glyphs.Begin(), 0, numberOfGlyphs );
294 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
296 Vector<Vector2> positions;
297 positions.Resize( numberOfGlyphs );
298 mVisualModel->GetGlyphPositions( positions.Begin(), 0, numberOfGlyphs );
299 const Vector2* const positionsBuffer = positions.Begin();
301 unsigned int closestGlyph = 0;
302 float closestDistance = MAX_FLOAT;
304 for( unsigned int i = 0, numberOfGLyphs = glyphs.Count(); i < numberOfGLyphs; ++i )
306 const GlyphInfo& glyphInfo = *( glyphsBuffer + i );
307 const Vector2& position = *( positionsBuffer + i );
308 float glyphX = position.x + glyphInfo.width*0.5f;
309 float glyphY = position.y + glyphInfo.height*0.5f;
311 float distanceToGlyph = fabsf( glyphX - x ) + fabsf( glyphY - y );
313 if( distanceToGlyph < closestDistance )
315 closestDistance = distanceToGlyph;
320 // TODO - Consider RTL languages
321 x = positions[closestGlyph].x + glyphs[closestGlyph].width;
325 TextAbstraction::FontClient::Get().GetFontMetrics( glyphs[closestGlyph].fontId, metrics );
326 height = metrics.height; // TODO - Fix for multi-line
329 LogicalModelPtr mLogicalModel;
330 VisualModelPtr mVisualModel;
331 DecoratorPtr mDecorator;
333 std::string mPlaceholderText;
336 * This is used to delay handling events until after the model has been updated.
337 * The number of updates to the model is minimized to improve performance.
339 vector<Event> mEventQueue; ///< The queue of touch events etc.
343 bool mDecoratorUpdated;
346 struct Controller::FontDefaults
349 : mDefaultPointSize(0.0f),
354 FontId GetFontId( TextAbstraction::FontClient& fontClient )
358 Dali::TextAbstraction::PointSize26Dot6 pointSize = mDefaultPointSize*64;
359 mFontId = fontClient.GetFontId( mDefaultFontFamily, mDefaultFontStyle, pointSize );
365 std::string mDefaultFontFamily;
366 std::string mDefaultFontStyle;
367 float mDefaultPointSize;
371 struct Controller::Impl
373 Impl( ControlInterface& controlInterface )
374 : mControlInterface( controlInterface ),
377 mFontDefaults( NULL ),
384 mOperationsPending( NO_OPERATION ),
385 mRecalculateNaturalSize( true )
387 mLogicalModel = LogicalModel::New();
388 mVisualModel = VisualModel::New();
390 mFontClient = TextAbstraction::FontClient::Get();
392 mView.SetVisualModel( mVisualModel );
400 ControlInterface& mControlInterface; ///< Reference to the text controller.
401 LogicalModelPtr mLogicalModel; ///< Pointer to the logical model.
402 VisualModelPtr mVisualModel; ///< Pointer to the visual model.
403 FontDefaults* mFontDefaults; ///< Avoid allocating this when the user does not specify a font.
404 Controller::TextInput* mTextInput; ///< Avoid allocating everything for text input until EnableTextInput().
405 TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
406 View mView; ///< The view interface to the rendering back-end.
407 LayoutEngine mLayoutEngine; ///< The layout engine.
408 std::string mNewText; ///< Temporary stores the text set until the next relayout.
409 Size mControlSize; ///< The size of the control.
410 OperationsMask mOperationsPending; ///< Operations pending to be done to layout the text.
411 bool mRecalculateNaturalSize:1; ///< Whether the natural size needs to be recalculated.
414 ControllerPtr Controller::New( ControlInterface& controlInterface )
416 return ControllerPtr( new Controller( controlInterface ) );
419 void Controller::SetText( const std::string& text )
421 // Keep until size negotiation
422 mImpl->mNewText = text;
424 // All operations need to be done. (convert to utf32, get break info, ..., layout, ...)
425 mImpl->mOperationsPending = ALL_OPERATIONS;
427 // The natural size needs to be re-calculated.
428 mImpl->mRecalculateNaturalSize = true;
430 if( mImpl->mTextInput )
432 // Cancel previously queued events
433 mImpl->mTextInput->mEventQueue.clear();
435 // TODO - Hide selection decorations
439 void Controller::GetText( std::string& text ) const
441 if( !mImpl->mNewText.empty() )
443 text = mImpl->mNewText;
447 // TODO - Convert from UTF-32
451 void Controller::SetPlaceholderText( const std::string& text )
453 if( !mImpl->mTextInput )
455 mImpl->mTextInput->mPlaceholderText = text;
459 void Controller::GetPlaceholderText( std::string& text ) const
461 if( !mImpl->mTextInput )
463 text = mImpl->mTextInput->mPlaceholderText;
467 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
469 if( !mImpl->mFontDefaults )
471 mImpl->mFontDefaults = new Controller::FontDefaults();
474 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
475 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
476 mImpl->mOperationsPending = ALL_OPERATIONS;
477 mImpl->mRecalculateNaturalSize = true;
480 const std::string& Controller::GetDefaultFontFamily() const
482 if( mImpl->mFontDefaults )
484 return mImpl->mFontDefaults->mDefaultFontFamily;
490 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
492 if( !mImpl->mFontDefaults )
494 mImpl->mFontDefaults = new Controller::FontDefaults();
497 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
498 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
499 mImpl->mOperationsPending = ALL_OPERATIONS;
500 mImpl->mRecalculateNaturalSize = true;
503 const std::string& Controller::GetDefaultFontStyle() const
505 if( mImpl->mFontDefaults )
507 return mImpl->mFontDefaults->mDefaultFontStyle;
513 void Controller::SetDefaultPointSize( float pointSize )
515 if( !mImpl->mFontDefaults )
517 mImpl->mFontDefaults = new Controller::FontDefaults();
520 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
521 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
522 mImpl->mOperationsPending = ALL_OPERATIONS;
523 mImpl->mRecalculateNaturalSize = true;
526 float Controller::GetDefaultPointSize() const
528 if( mImpl->mFontDefaults )
530 return mImpl->mFontDefaults->mDefaultPointSize;
536 void Controller::EnableTextInput( DecoratorPtr decorator )
538 if( !mImpl->mTextInput )
540 mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
544 bool Controller::Relayout( const Vector2& size )
546 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
548 // Not worth to relayout if width or height is equal to zero.
552 if( size != mImpl->mControlSize )
554 // Operations that need to be done if the size changes.
555 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
562 mImpl->mControlSize = size;
566 bool updated = DoRelayout( mImpl->mControlSize,
567 mImpl->mOperationsPending,
570 // Do not re-do any operation until something changes.
571 mImpl->mOperationsPending = NO_OPERATION;
573 if( mImpl->mTextInput )
575 // Move the cursor, grab handle etc.
576 updated = mImpl->mTextInput->ProcessInputEvents() || updated;
582 bool Controller::DoRelayout( const Vector2& size,
583 OperationsMask operationsRequired,
586 bool viewUpdated( false );
588 // Calculate the operations to be done.
589 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
591 Vector<Character> utf32Characters;
592 Length characterCount = 0u;
593 if( CONVERT_TO_UTF32 & operations )
595 std::string& text = mImpl->mNewText;
597 // Convert text into UTF-32
598 utf32Characters.Resize( text.size() );
600 // This is a bit horrible but std::string returns a (signed) char*
601 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
603 // Transform a text array encoded in utf8 into an array encoded in utf32.
604 // It returns the actual number of characters.
605 characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
606 utf32Characters.Resize( characterCount );
608 // Sets the text into the model.
609 mImpl->mLogicalModel->SetText( utf32Characters.Begin(), characterCount );
611 // Discard temporary text
615 Vector<LineBreakInfo> lineBreakInfo;
616 if( GET_LINE_BREAKS & operations )
618 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
619 // calculate the bidirectional info for each 'paragraph'.
620 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
621 // is not shaped together).
622 lineBreakInfo.Resize( characterCount, TextAbstraction::LINE_NO_BREAK );
624 SetLineBreakInfo( utf32Characters,
627 mImpl->mLogicalModel->SetLineBreakInfo( lineBreakInfo.Begin(), characterCount );
630 Vector<WordBreakInfo> wordBreakInfo;
631 if( GET_WORD_BREAKS & operations )
633 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
634 wordBreakInfo.Resize( characterCount, TextAbstraction::WORD_NO_BREAK );
636 SetWordBreakInfo( utf32Characters,
639 mImpl->mLogicalModel->SetWordBreakInfo( wordBreakInfo.Begin(), characterCount );
642 const bool getScripts = GET_SCRIPTS & operations;
643 const bool validateFonts = VALIDATE_FONTS & operations;
645 Vector<ScriptRun> scripts;
646 Vector<FontRun> fonts;
648 if( mImpl->mFontDefaults )
650 // TODO - pass into ValidateFonts
653 if( getScripts || validateFonts )
655 // Validates the fonts assigned by the application or assigns default ones.
656 // It makes sure all the characters are going to be rendered by the correct font.
657 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
661 // Retrieves the scripts used in the text.
662 multilanguageSupport.SetScripts( utf32Characters,
666 // Sets the scripts into the model.
667 mImpl->mLogicalModel->SetScripts( scripts.Begin(), scripts.Count() );
672 // Validates the fonts. If there is a character with no assigned font it sets a default one.
673 // After this call, fonts are validated.
674 multilanguageSupport.ValidateFonts( utf32Characters,
678 // Sets the fonts into the model.
679 mImpl->mLogicalModel->SetFonts( fonts.Begin(), fonts.Count() );
683 Vector<GlyphInfo> glyphs;
684 Vector<CharacterIndex> glyphsToCharactersMap;
685 Vector<Length> charactersPerGlyph;
686 if( SHAPE_TEXT & operations )
689 ShapeText( utf32Characters,
694 glyphsToCharactersMap,
695 charactersPerGlyph );
698 if( GET_GLYPH_METRICS & operations )
700 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), glyphs.Count() );
703 Length numberOfGlyphs = glyphs.Count();
704 if( 0u != numberOfGlyphs )
706 // Sets the glyphs into the model.
707 mImpl->mVisualModel->SetGlyphs( glyphs.Begin(),
708 glyphsToCharactersMap.Begin(),
709 charactersPerGlyph.Begin(),
713 if( LAYOUT & operations )
715 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
717 if( 0u == numberOfGlyphs )
719 numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
721 lineBreakInfo.Resize( numberOfCharacters );
722 wordBreakInfo.Resize( numberOfCharacters );
723 glyphs.Resize( numberOfGlyphs );
724 glyphsToCharactersMap.Resize( numberOfGlyphs );
725 charactersPerGlyph.Resize( numberOfGlyphs );
727 mImpl->mLogicalModel->GetLineBreakInfo( lineBreakInfo.Begin(),
729 numberOfCharacters );
731 mImpl->mLogicalModel->GetWordBreakInfo( wordBreakInfo.Begin(),
733 numberOfCharacters );
735 mImpl->mVisualModel->GetGlyphs( glyphs.Begin(),
739 mImpl->mVisualModel->GetGlyphToCharacterMap( glyphsToCharactersMap.Begin(),
743 mImpl->mVisualModel->GetCharactersPerGlyphMap( charactersPerGlyph.Begin(),
748 // Set the layout parameters.
749 LayoutParameters layoutParameters( size,
750 lineBreakInfo.Begin(),
751 wordBreakInfo.Begin(),
754 glyphsToCharactersMap.Begin(),
755 charactersPerGlyph.Begin() );
757 // Reserve space to set the positions of the glyphs.
758 Vector<Vector2> glyphPositions;
759 glyphPositions.Resize( numberOfGlyphs );
761 // The laid-out lines.
762 // It's not possible to know in how many lines the text is going to be laid-out,
763 // but it can be resized at least with the number of 'paragraphs' to avoid
764 // some re-allocations.
765 Vector<LineRun> lines;
766 lines.Reserve( mImpl->mLogicalModel->GetNumberOfBidirectionalInfoRuns( 0u, numberOfCharacters ) );
768 // Update the visual model.
769 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
776 // Sets the positions into the model.
777 if( UPDATE_POSITIONS & operations )
779 mImpl->mVisualModel->SetGlyphPositions( glyphPositions.Begin(),
783 // Sets the lines into the model.
784 if( UPDATE_LINES & operations )
786 mImpl->mVisualModel->SetLines( lines.Begin(),
790 // Sets the actual size.
791 if( UPDATE_ACTUAL_SIZE & operations )
793 mImpl->mVisualModel->SetActualSize( layoutSize );
799 layoutSize = mImpl->mVisualModel->GetActualSize();
805 Vector3 Controller::GetNaturalSize()
809 if( mImpl->mRecalculateNaturalSize )
811 // Operations that can be done only once until the text changes.
812 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
820 // Operations that need to be done if the size changes.
821 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
824 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
825 static_cast<OperationsMask>( onlyOnceOperations |
827 naturalSize.GetVectorXY() );
829 // Do not do again the only once operations.
830 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
832 // Do the size related operations again.
833 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
835 // Stores the natural size to avoid recalculate it again
836 // unless the text/style changes.
837 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
839 mImpl->mRecalculateNaturalSize = false;
843 naturalSize = mImpl->mVisualModel->GetNaturalSize();
849 float Controller::GetHeightForWidth( float width )
852 if( width != mImpl->mControlSize.width )
854 // Operations that can be done only once until the text changes.
855 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
863 // Operations that need to be done if the size changes.
864 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
867 DoRelayout( Size( width, MAX_FLOAT ),
868 static_cast<OperationsMask>( onlyOnceOperations |
872 // Do not do again the only once operations.
873 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
875 // Do the size related operations again.
876 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
880 layoutSize = mImpl->mVisualModel->GetActualSize();
883 return layoutSize.height;
886 View& Controller::GetView()
891 LayoutEngine& Controller::GetLayoutEngine()
893 return mImpl->mLayoutEngine;
896 void Controller::RequestRelayout()
898 mImpl->mControlInterface.RequestTextRelayout();
901 void Controller::KeyboardFocusGainEvent()
903 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
905 if( mImpl->mTextInput )
907 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
908 mImpl->mTextInput->mEventQueue.push_back( event );
914 void Controller::KeyboardFocusLostEvent()
916 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
918 if( mImpl->mTextInput )
920 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
921 mImpl->mTextInput->mEventQueue.push_back( event );
927 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
929 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" );
931 if( mImpl->mTextInput )
933 TextInput::Event event( TextInput::KEY_EVENT );
934 event.p1.mInt = keyEvent.keyCode;
935 event.p2.mString = NULL;
937 const std::string& keyString = keyEvent.keyPressed;
938 if ( !keyString.empty() )
940 event.p2.mString = new char[keyString.size() + 1];
941 std::copy(keyString.begin(), keyString.end(), event.p2.mString);
942 event.p2.mString[keyString.size()] = '\0';
945 mImpl->mTextInput->mEventQueue.push_back( event );
953 void Controller::TapEvent( unsigned int tapCount, float x, float y )
955 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
957 if( mImpl->mTextInput )
959 TextInput::Event event( TextInput::TAP_EVENT );
960 event.p1.mUint = tapCount;
963 mImpl->mTextInput->mEventQueue.push_back( event );
969 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
971 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
973 if( mImpl->mTextInput )
975 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
976 event.p1.mUint = state;
979 mImpl->mTextInput->mEventQueue.push_back( event );
985 Controller::~Controller()
990 Controller::Controller( ControlInterface& controlInterface )
993 mImpl = new Controller::Impl( controlInterface );
998 } // namespace Toolkit