Property system fixes
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/text-controller.h>
20
21 // INTERNAL INCLUDES
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>
32
33 // EXTERNAL INCLUDES
34 #include <limits>
35 #include <vector>
36 #include <dali/public-api/text-abstraction/font-client.h>
37
38 using std::vector;
39
40 namespace Dali
41 {
42
43 namespace Toolkit
44 {
45
46 namespace Text
47 {
48
49 struct Controller::TextInput
50 {
51   // Used to queue input events until DoRelayout()
52   enum EventType
53   {
54     KEYBOARD_FOCUS_GAIN_EVENT,
55     KEYBOARD_FOCUS_LOST_EVENT,
56     TAP_EVENT,
57     GRAB_HANDLE_EVENT
58   };
59
60   union Param
61   {
62     int mInt;
63     unsigned int mUint;
64     float mFloat;
65   };
66
67   struct Event
68   {
69     Event( EventType eventType )
70     : type( eventType )
71     {
72       p1.mInt = 0;
73       p2.mInt = 0;
74     }
75
76     EventType type;
77     Param p1;
78     Param p2;
79     Param p3;
80   };
81
82   enum State
83   {
84     INACTIVE,
85     SELECTING,
86     EDITING
87   };
88
89   TextInput( LogicalModelPtr logicalModel,
90              VisualModelPtr visualModel,
91              DecoratorPtr decorator )
92   : mLogicalModel( logicalModel ),
93     mVisualModel( visualModel ),
94     mDecorator( decorator ),
95     mState( INACTIVE )
96   {
97   }
98
99   /**
100    * @brief Helper to move the cursor, grab handle etc.
101    */
102   bool ProcessTouchEvents()
103   {
104     mDecoratorUpdated = false;
105
106     if( mDecorator )
107     {
108       for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
109       {
110         switch( iter->type )
111         {
112           case KEYBOARD_FOCUS_GAIN_EVENT:
113           {
114             OnKeyboardFocus( true );
115             break;
116           }
117           case KEYBOARD_FOCUS_LOST_EVENT:
118           {
119             OnKeyboardFocus( false );
120             break;
121           }
122           case TAP_EVENT:
123           {
124             OnTapEvent( *iter );
125             break;
126           }
127           case GRAB_HANDLE_EVENT:
128           {
129             OnGrabHandleEvent( *iter );
130             break;
131           }
132         }
133       }
134     }
135
136     mEventQueue.clear();
137
138     return mDecoratorUpdated;
139   }
140
141   void OnKeyboardFocus( bool hasFocus )
142   {
143     // TODO
144   }
145
146   void OnTapEvent( const Event& event )
147   {
148     if( 1u == event.p1.mUint )
149     {
150       mState = TextInput::EDITING;
151       mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
152       mDecorator->StartCursorBlink();
153       mDecorator->SetGrabHandleActive( true );
154
155       float xPosition = event.p2.mFloat;
156       float yPosition = event.p3.mFloat;
157       float height(0.0f);
158       GetClosestCursorPosition( xPosition, yPosition, height );
159       mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
160
161       mDecoratorUpdated = true;
162     }
163     else if( 2u == event.p1.mUint )
164     {
165       mState = TextInput::SELECTING;
166       mDecorator->SetGrabHandleActive( false );
167       mDecorator->SetSelectionActive( true );
168       mDecoratorUpdated = true;
169     }
170   }
171
172   void OnGrabHandleEvent( const Event& event )
173   {
174     unsigned int state = event.p1.mUint;
175
176     if( GRAB_HANDLE_PRESSED == state )
177     {
178       float xPosition = event.p2.mFloat;
179       float yPosition = event.p3.mFloat;
180       float height(0.0f);
181
182       GetClosestCursorPosition( xPosition, yPosition, height );
183
184       mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
185       mDecoratorUpdated = true;
186     }
187   }
188
189   void GetClosestCursorPosition( float& x, float& y, float& height )
190   {
191     // TODO - Look at LineRuns first
192
193     Text::Length numberOfGlyphs = mVisualModel->GetNumberOfGlyphs();
194     if( 0 == numberOfGlyphs )
195     {
196       return;
197     }
198
199     Vector<GlyphInfo> glyphs;
200     glyphs.Resize( numberOfGlyphs );
201     mVisualModel->GetGlyphs( &glyphs[0], 0, numberOfGlyphs );
202
203     std::vector<Vector2> positions;
204     positions.resize( numberOfGlyphs );
205     mVisualModel->GetGlyphPositions( &positions[0], 0, numberOfGlyphs );
206
207     unsigned int closestGlyph = 0;
208     float closestDistance = std::numeric_limits<float>::max();
209
210     for( unsigned int i=0; i<glyphs.Count(); ++i )
211     {
212       float glyphX = positions[i].x + glyphs[i].width*0.5f;
213       float glyphY = positions[i].y + glyphs[i].height*0.5f;
214
215       float distanceToGlyph = fabsf( glyphX - x ) + fabsf( glyphY - y );
216
217       if( distanceToGlyph < closestDistance )
218       {
219         closestDistance = distanceToGlyph;
220         closestGlyph = i;
221       }
222     }
223
224     // TODO - Consider RTL languages
225     x = positions[closestGlyph].x + glyphs[closestGlyph].width;
226     y = 0.0f;
227
228     FontMetrics metrics;
229     TextAbstraction::FontClient::Get().GetFontMetrics( glyphs[closestGlyph].fontId, metrics );
230     height = metrics.height; // TODO - Fix for multi-line
231   }
232
233   LogicalModelPtr mLogicalModel;
234   VisualModelPtr  mVisualModel;
235   DecoratorPtr    mDecorator;
236
237   State mState;
238
239   /**
240    * This is used to delay handling events until after the model has been updated.
241    * The number of updates to the model is minimized to improve performance.
242    */
243   vector<Event> mEventQueue; ///< The queue of touch events etc.
244
245   bool mDecoratorUpdated;
246 };
247
248 struct Controller::FontDefaults
249 {
250   FontDefaults()
251   : mDefaultPointSize(0.0f),
252     mFontId(0u)
253   {
254   }
255
256   FontId GetFontId( TextAbstraction::FontClient& fontClient )
257   {
258     if( !mFontId )
259     {
260       Dali::TextAbstraction::PointSize26Dot6 pointSize = mDefaultPointSize*64;
261       mFontId = fontClient.GetFontId( mDefaultFontFamily, mDefaultFontStyle, pointSize );
262     }
263
264     return mFontId;
265   }
266
267   std::string mDefaultFontFamily;
268   std::string mDefaultFontStyle;
269   float mDefaultPointSize;
270   FontId mFontId;
271 };
272
273 struct Controller::Impl
274 {
275   Impl( ControlInterface& controlInterface )
276   : mControlInterface( controlInterface ),
277     mNewText(),
278     mOperations( NO_OPERATION ),
279     mControlSize(),
280     mFontDefaults( NULL ),
281     mTextInput( NULL )
282   {
283     mLogicalModel = LogicalModel::New();
284     mVisualModel  = VisualModel::New();
285
286     mView.SetVisualModel( mVisualModel );
287
288     mFontClient = TextAbstraction::FontClient::Get();
289   }
290
291   ~Impl()
292   {
293     delete mTextInput;
294   }
295
296   ControlInterface& mControlInterface;
297
298   std::string mNewText;
299
300   LogicalModelPtr mLogicalModel;
301   VisualModelPtr  mVisualModel;
302
303   View mView;
304
305   LayoutEngine mLayoutEngine;
306
307   TextAbstraction::FontClient mFontClient;
308
309   OperationsMask mOperations;
310
311   Size mControlSize;
312
313   // Avoid allocating this when the user does not specify a font
314   FontDefaults* mFontDefaults;
315
316   // Avoid allocating everything for text input until EnableTextInput()
317   Controller::TextInput* mTextInput;
318 };
319
320 ControllerPtr Controller::New( ControlInterface& controlInterface )
321 {
322   return ControllerPtr( new Controller( controlInterface ) );
323 }
324
325 void Controller::SetText( const std::string& text )
326 {
327   // Keep until size negotiation
328   mImpl->mNewText = text;
329   mImpl->mOperations = ALL_OPERATIONS;
330
331   if( mImpl->mTextInput )
332   {
333     // Cancel previously queued events
334     mImpl->mTextInput->mEventQueue.clear();
335
336     // TODO - Hide selection decorations
337   }
338 }
339
340 void Controller::GetText( std::string& text ) const
341 {
342   if( !mImpl->mNewText.empty() )
343   {
344     text = mImpl->mNewText;
345   }
346   else
347   {
348     // TODO - Convert from UTF-32
349   }
350 }
351
352 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
353 {
354   if( !mImpl->mFontDefaults )
355   {
356     mImpl->mFontDefaults = new Controller::FontDefaults();
357   }
358
359   mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
360   mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
361   mImpl->mOperations = ALL_OPERATIONS;
362 }
363
364 const std::string& Controller::GetDefaultFontFamily() const
365 {
366   if( mImpl->mFontDefaults )
367   {
368     return mImpl->mFontDefaults->mDefaultFontFamily;
369   }
370
371   return Dali::String::EMPTY;
372 }
373
374 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
375 {
376   if( !mImpl->mFontDefaults )
377   {
378     mImpl->mFontDefaults = new Controller::FontDefaults();
379   }
380
381   mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
382   mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
383   mImpl->mOperations = ALL_OPERATIONS;
384 }
385
386 const std::string& Controller::GetDefaultFontStyle() const
387 {
388   if( mImpl->mFontDefaults )
389   {
390     return mImpl->mFontDefaults->mDefaultFontStyle;
391   }
392
393   return Dali::String::EMPTY;
394 }
395
396 void Controller::SetDefaultPointSize( float pointSize )
397 {
398   if( !mImpl->mFontDefaults )
399   {
400     mImpl->mFontDefaults = new Controller::FontDefaults();
401   }
402
403   mImpl->mFontDefaults->mDefaultPointSize = pointSize;
404   mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
405   mImpl->mOperations = ALL_OPERATIONS;
406 }
407
408 float Controller::GetDefaultPointSize() const
409 {
410   if( mImpl->mFontDefaults )
411   {
412     return mImpl->mFontDefaults->mDefaultPointSize;
413   }
414
415   return 0.0f;
416 }
417
418 void Controller::EnableTextInput( DecoratorPtr decorator )
419 {
420   if( !mImpl->mTextInput )
421   {
422     mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
423   }
424 }
425
426 bool Controller::Relayout( const Vector2& size )
427 {
428   if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
429   {
430     // Not worth to relayout if width or height is equal to zero.
431     return false;
432   }
433
434   bool updated = false;
435
436   if( size != mImpl->mControlSize )
437   {
438     updated = DoRelayout( size, mImpl->mOperations );
439
440     // Do not re-do any operation until something changes.
441     mImpl->mOperations = NO_OPERATION;
442
443     mImpl->mControlSize = size;
444   }
445
446   if( mImpl->mTextInput )
447   {
448     // Move the cursor, grab handle etc.
449     updated = mImpl->mTextInput->ProcessTouchEvents() || updated;
450   }
451
452   return updated;
453 }
454
455 bool Controller::DoRelayout( const Vector2& size, OperationsMask operations )
456 {
457   bool viewUpdated( false );
458
459   Vector<Character> utf32Characters;
460   Length characterCount = 0u;
461   if( CONVERT_TO_UTF32 & operations )
462   {
463     std::string& text = mImpl->mNewText;
464
465     //  Convert text into UTF-32
466     utf32Characters.Resize( text.size() );
467
468     // This is a bit horrible but std::string returns a (signed) char*
469     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
470
471     // Transform a text array encoded in utf8 into an array encoded in utf32.
472     // It returns the actual number of characters.
473     characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
474     utf32Characters.Resize( characterCount );
475
476     // Sets the text into the model.
477     mImpl->mLogicalModel->SetText( utf32Characters.Begin(), characterCount );
478
479     // Discard temporary text
480     //text.clear(); temporary keep the text. will be fixed in the next patch.
481   }
482
483   Vector<LineBreakInfo> lineBreakInfo;
484   if( GET_LINE_BREAKS & operations )
485   {
486     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
487     // calculate the bidirectional info for each 'paragraph'.
488     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
489     // is not shaped together).
490     lineBreakInfo.Resize( characterCount, TextAbstraction::LINE_NO_BREAK );
491
492     SetLineBreakInfo( utf32Characters,
493                       lineBreakInfo );
494
495     mImpl->mLogicalModel->SetLineBreakInfo( lineBreakInfo.Begin(), characterCount );
496   }
497
498   Vector<WordBreakInfo> wordBreakInfo;
499   if( GET_WORD_BREAKS & operations )
500   {
501     // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
502     wordBreakInfo.Resize( characterCount, TextAbstraction::WORD_NO_BREAK );
503
504     SetWordBreakInfo( utf32Characters,
505                       wordBreakInfo );
506
507     mImpl->mLogicalModel->SetWordBreakInfo( wordBreakInfo.Begin(), characterCount );
508   }
509
510   const bool getScripts = GET_SCRIPTS & operations;
511   const bool validateFonts = VALIDATE_FONTS & operations;
512
513   Vector<ScriptRun> scripts;
514   Vector<FontRun> fonts;
515
516   if( mImpl->mFontDefaults )
517   {
518     // TODO - pass into ValidateFonts
519   }
520
521   if( getScripts || validateFonts )
522   {
523     // Validates the fonts assigned by the application or assigns default ones.
524     // It makes sure all the characters are going to be rendered by the correct font.
525     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
526
527     if( getScripts )
528     {
529       // Retrieves the scripts used in the text.
530       multilanguageSupport.SetScripts( utf32Characters,
531                                        lineBreakInfo,
532                                        scripts );
533
534       // Sets the scripts into the model.
535       mImpl->mLogicalModel->SetScripts( scripts.Begin(), scripts.Count() );
536     }
537
538     if( validateFonts )
539     {
540       // Validates the fonts. If there is a character with no assigned font it sets a default one.
541       // After this call, fonts are validated.
542       multilanguageSupport.ValidateFonts( utf32Characters,
543                                           scripts,
544                                           fonts );
545
546       // Sets the fonts into the model.
547       mImpl->mLogicalModel->SetFonts( fonts.Begin(), fonts.Count() );
548     }
549   }
550
551   Vector<GlyphInfo> glyphs;
552   Vector<CharacterIndex> glyphsToCharactersMap;
553   Vector<Length> charactersPerGlyph;
554   if( SHAPE_TEXT & operations )
555   {
556     // Shapes the text.
557     ShapeText( utf32Characters,
558                lineBreakInfo,
559                scripts,
560                fonts,
561                glyphs,
562                glyphsToCharactersMap,
563                charactersPerGlyph );
564   }
565
566   if( GET_GLYPH_METRICS & operations )
567   {
568     mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), glyphs.Count() );
569   }
570
571   Length numberOfGlyphs = glyphs.Count();
572   if( 0u != numberOfGlyphs )
573   {
574     // Sets the glyphs into the model.
575     mImpl->mVisualModel->SetGlyphs( glyphs.Begin(),
576                                     glyphsToCharactersMap.Begin(),
577                                     charactersPerGlyph.Begin(),
578                                     numberOfGlyphs );
579   }
580
581   if( LAYOUT & operations )
582   {
583     if( 0u == numberOfGlyphs )
584     {
585       const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
586       numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
587
588       lineBreakInfo.Resize( numberOfCharacters );
589       wordBreakInfo.Resize( numberOfCharacters );
590       glyphs.Resize( numberOfGlyphs );
591       glyphsToCharactersMap.Resize( numberOfGlyphs );
592       charactersPerGlyph.Resize( numberOfGlyphs );
593
594       mImpl->mLogicalModel->GetLineBreakInfo( lineBreakInfo.Begin(),
595                                               0u,
596                                               numberOfCharacters );
597
598       mImpl->mLogicalModel->GetWordBreakInfo( wordBreakInfo.Begin(),
599                                               0u,
600                                               numberOfCharacters );
601
602       mImpl->mVisualModel->GetGlyphs( glyphs.Begin(),
603                                       0u,
604                                       numberOfGlyphs );
605
606       mImpl->mVisualModel->GetGlyphToCharacterMap( glyphsToCharactersMap.Begin(),
607                                                    0u,
608                                                    numberOfGlyphs );
609
610       mImpl->mVisualModel->GetCharactersPerGlyphMap( charactersPerGlyph.Begin(),
611                                                      0u,
612                                                      numberOfGlyphs );
613     }
614
615     // Set the layout parameters.
616     LayoutParameters layoutParameters( size,
617                                        lineBreakInfo.Begin(),
618                                        wordBreakInfo.Begin(),
619                                        numberOfGlyphs,
620                                        glyphs.Begin(),
621                                        glyphsToCharactersMap.Begin(),
622                                        charactersPerGlyph.Begin() );
623
624     // Reserve space to set the positions of the glyphs.
625     Vector<Vector2> glyphPositions;
626     glyphPositions.Resize( numberOfGlyphs );
627
628     Size layoutSize;
629
630     // Update the visual model
631     viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
632                                                    glyphPositions,
633                                                    layoutSize );
634
635     // Sets the positions into the model.
636     mImpl->mVisualModel->SetGlyphPositions( glyphPositions.Begin(),
637                                             numberOfGlyphs );
638
639     // Sets the actual size.
640     mImpl->mVisualModel->SetActualSize( layoutSize );
641   }
642
643   return viewUpdated;
644 }
645
646 Vector3 Controller::GetNaturalSize()
647 {
648   // TODO - Finish implementing
649   return Vector3::ZERO;
650
651   // Operations that can be done only once until the text changes.
652   const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
653                                                                          GET_SCRIPTS      |
654                                                                          VALIDATE_FONTS   |
655                                                                          GET_LINE_BREAKS  |
656                                                                          GET_WORD_BREAKS  |
657                                                                          SHAPE_TEXT       |
658                                                                          GET_GLYPH_METRICS );
659
660   // Operations that need to be done if the size or the text changes.
661   const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
662                                                                       REORDER );
663
664   const float maxFloat = std::numeric_limits<float>::max();
665   DoRelayout( Vector2( maxFloat, maxFloat ),
666               static_cast<OperationsMask>( onlyOnceOperations |
667                                            sizeOperations ) );
668
669   // Do not do again the only once operations.
670   mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations & ~onlyOnceOperations );
671
672   // Do the size related operations again.
673   mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations | sizeOperations );
674
675   return Vector3( mImpl->mVisualModel->GetNaturalSize() );
676 }
677
678 float Controller::GetHeightForWidth( float width )
679 {
680   // Operations that can be done only once until the text changes.
681   const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
682                                                                          GET_SCRIPTS      |
683                                                                          VALIDATE_FONTS   |
684                                                                          GET_LINE_BREAKS  |
685                                                                          GET_WORD_BREAKS  |
686                                                                          SHAPE_TEXT       |
687                                                                          GET_GLYPH_METRICS );
688
689   // Operations that need to be done if the size or the text changes.
690   const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
691                                                                       REORDER );
692
693   DoRelayout( Size( width, 0.f ),
694               static_cast<OperationsMask>( onlyOnceOperations |
695                                            sizeOperations ) );
696
697   // Do not do again the only once operations.
698   mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations & ~onlyOnceOperations );
699
700   // Do the size related operations again.
701   mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations | sizeOperations );
702
703   return mImpl->mVisualModel->GetActualSize().height;
704 }
705
706 View& Controller::GetView()
707 {
708   return mImpl->mView;
709 }
710
711 LayoutEngine& Controller::GetLayoutEngine()
712 {
713   return mImpl->mLayoutEngine;
714 }
715
716 void Controller::RequestRelayout()
717 {
718   mImpl->mControlInterface.RequestTextRelayout();
719 }
720
721 void Controller::KeyboardFocusGainEvent()
722 {
723   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
724
725   if( mImpl->mTextInput )
726   {
727     TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
728     mImpl->mTextInput->mEventQueue.push_back( event );
729
730     RequestRelayout();
731   }
732 }
733
734 void Controller::KeyboardFocusLostEvent()
735 {
736   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
737
738   if( mImpl->mTextInput )
739   {
740     TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
741     mImpl->mTextInput->mEventQueue.push_back( event );
742
743     RequestRelayout();
744   }
745 }
746
747 void Controller::TapEvent( unsigned int tapCount, float x, float y )
748 {
749   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
750
751   if( mImpl->mTextInput )
752   {
753     TextInput::Event event( TextInput::TAP_EVENT );
754     event.p1.mUint = tapCount;
755     event.p2.mFloat = x;
756     event.p3.mFloat = y;
757     mImpl->mTextInput->mEventQueue.push_back( event );
758
759     RequestRelayout();
760   }
761 }
762
763 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
764 {
765   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
766
767   if( mImpl->mTextInput )
768   {
769     TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
770     event.p1.mUint  = state;
771     event.p2.mFloat = x;
772     event.p3.mFloat = y;
773     mImpl->mTextInput->mEventQueue.push_back( event );
774
775     RequestRelayout();
776   }
777 }
778
779 Controller::~Controller()
780 {
781   delete mImpl;
782 }
783
784 Controller::Controller( ControlInterface& controlInterface )
785 : mImpl( NULL )
786 {
787   mImpl = new Controller::Impl( controlInterface );
788 }
789
790 } // namespace Text
791
792 } // namespace Toolkit
793
794 } // namespace Dali