Merge remote-tracking branch 'origin/tizen' into new_text
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / public-api / 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/public-api/text/text-controller.h>
20
21 // INTERNAL INCLUDES
22 #include <dali-toolkit/public-api/text/character-set-conversion.h>
23 #include <dali-toolkit/public-api/text/layouts/layout-engine.h>
24 #include <dali-toolkit/public-api/text/logical-model.h>
25 #include <dali-toolkit/public-api/text/multi-language-support.h>
26 #include <dali-toolkit/public-api/text/script-run.h>
27 #include <dali-toolkit/public-api/text/segmentation.h>
28 #include <dali-toolkit/public-api/text/shaper.h>
29 #include <dali-toolkit/public-api/text/text-view.h>
30 #include <dali-toolkit/public-api/text/visual-model.h>
31
32 // EXTERNAL INCLUDES
33 #include <limits>
34 #include <vector>
35 #include <dali/public-api/text-abstraction/font-client.h>
36
37 using std::vector;
38
39 namespace Dali
40 {
41
42 namespace Toolkit
43 {
44
45 namespace Text
46 {
47
48 struct Controller::TextInput
49 {
50   // Used to queue input events until DoRelayout()
51   enum EventType
52   {
53     KEYBOARD_FOCUS_GAIN_EVENT,
54     KEYBOARD_FOCUS_LOST_EVENT,
55     TAP_EVENT,
56     GRAB_HANDLE_EVENT
57   };
58
59   union Param
60   {
61     int mInt;
62     unsigned int mUint;
63     float mFloat;
64   };
65
66   struct Event
67   {
68     Event( EventType eventType )
69     : type( eventType )
70     {
71       p1.mInt = 0;
72       p2.mInt = 0;
73     }
74
75     EventType type;
76     Param p1;
77     Param p2;
78     Param p3;
79   };
80
81   enum State
82   {
83     INACTIVE,
84     SELECTING,
85     EDITING
86   };
87
88   TextInput( LogicalModelPtr logicalModel,
89              VisualModelPtr visualModel,
90              DecoratorPtr decorator )
91   : mLogicalModel( logicalModel ),
92     mVisualModel( visualModel ),
93     mDecorator( decorator ),
94     mState( INACTIVE )
95   {
96   }
97
98   /**
99    * @brief Helper to move the cursor, grab handle etc.
100    */
101   bool ProcessTouchEvents()
102   {
103     mDecoratorUpdated = false;
104
105     if( mDecorator )
106     {
107       for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
108       {
109         switch( iter->type )
110         {
111           case KEYBOARD_FOCUS_GAIN_EVENT:
112           {
113             OnKeyboardFocus( true );
114             break;
115           }
116           case KEYBOARD_FOCUS_LOST_EVENT:
117           {
118             OnKeyboardFocus( false );
119             break;
120           }
121           case TAP_EVENT:
122           {
123             OnTapEvent( *iter );
124             break;
125           }
126           case GRAB_HANDLE_EVENT:
127           {
128             OnGrabHandleEvent( *iter );
129             break;
130           }
131         }
132       }
133     }
134
135     mEventQueue.clear();
136
137     return mDecoratorUpdated;
138   }
139
140   void OnKeyboardFocus( bool hasFocus )
141   {
142     // TODO
143   }
144
145   void OnTapEvent( const Event& event )
146   {
147     if( 1u == event.p1.mUint )
148     {
149       mState = TextInput::EDITING;
150       mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
151       mDecorator->StartCursorBlink();
152       mDecorator->SetGrabHandleActive( true );
153
154       float xPosition = event.p2.mFloat;
155       float yPosition = event.p3.mFloat;
156       float height(0.0f);
157       GetClosestCursorPosition( xPosition, yPosition, height );
158       mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
159
160       mDecoratorUpdated = true;
161     }
162     else if( 2u == event.p1.mUint )
163     {
164       mState = TextInput::SELECTING;
165       mDecorator->SetGrabHandleActive( false );
166       mDecorator->SetSelectionActive( true );
167       mDecoratorUpdated = true;
168     }
169   }
170
171   void OnGrabHandleEvent( const Event& event )
172   {
173     unsigned int state = event.p1.mUint;
174
175     if( GRAB_HANDLE_PRESSED == state )
176     {
177       float xPosition = event.p2.mFloat;
178       float yPosition = event.p3.mFloat;
179       float height(0.0f);
180
181       GetClosestCursorPosition( xPosition, yPosition, height );
182
183       mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
184       mDecoratorUpdated = true;
185     }
186   }
187
188   void GetClosestCursorPosition( float& x, float& y, float& height )
189   {
190     // TODO - Look at LineRuns first
191
192     Text::Length numberOfGlyphs = mVisualModel->GetNumberOfGlyphs();
193     if( 0 == numberOfGlyphs )
194     {
195       return;
196     }
197
198     Vector<GlyphInfo> glyphs;
199     glyphs.Resize( numberOfGlyphs );
200     mVisualModel->GetGlyphs( &glyphs[0], 0, numberOfGlyphs );
201
202     std::vector<Vector2> positions;
203     positions.resize( numberOfGlyphs );
204     mVisualModel->GetGlyphPositions( &positions[0], 0, numberOfGlyphs );
205
206     unsigned int closestGlyph = 0;
207     float closestDistance = std::numeric_limits<float>::max();
208
209     for( unsigned int i=0; i<glyphs.Count(); ++i )
210     {
211       float glyphX = positions[i].x + glyphs[i].width*0.5f;
212       float glyphY = positions[i].y + glyphs[i].height*0.5f;
213
214       float distanceToGlyph = fabsf( glyphX - x ) + fabsf( glyphY - y );
215
216       if( distanceToGlyph < closestDistance )
217       {
218         closestDistance = distanceToGlyph;
219         closestGlyph = i;
220       }
221     }
222
223     // TODO - Consider RTL languages
224     x = positions[closestGlyph].x + glyphs[closestGlyph].width;
225     y = 0.0f;
226
227     FontMetrics metrics;
228     TextAbstraction::FontClient::Get().GetFontMetrics( glyphs[closestGlyph].fontId, metrics );
229     height = metrics.height; // TODO - Fix for multi-line
230   }
231
232   LogicalModelPtr mLogicalModel;
233   VisualModelPtr  mVisualModel;
234   DecoratorPtr    mDecorator;
235
236   State mState;
237
238   /**
239    * This is used to delay handling events until after the model has been updated.
240    * The number of updates to the model is minimized to improve performance.
241    */
242   vector<Event> mEventQueue; ///< The queue of touch events etc.
243
244   bool mDecoratorUpdated;
245 };
246
247 struct Controller::Impl
248 {
249   Impl( ControlInterface& controlInterface )
250   : mControlInterface( controlInterface ),
251     mNewText(),
252     mOperations( NO_OPERATION ),
253     mControlSize(),
254     mTextInput( NULL )
255   {
256     mLogicalModel = LogicalModel::New();
257     mVisualModel  = VisualModel::New();
258
259     mView.SetVisualModel( mVisualModel );
260
261     mFontClient = TextAbstraction::FontClient::Get();
262   }
263
264   ~Impl()
265   {
266     delete mTextInput;
267   }
268
269   ControlInterface& mControlInterface;
270
271   std::string mNewText;
272
273   LogicalModelPtr mLogicalModel;
274   VisualModelPtr  mVisualModel;
275
276   View mView;
277
278   LayoutEngine mLayoutEngine;
279
280   TextAbstraction::FontClient mFontClient;
281
282   OperationsMask mOperations;
283
284   Size mControlSize;
285
286   // Avoid allocating everything for text input until EnableTextInput()
287   Controller::TextInput* mTextInput;
288 };
289
290 ControllerPtr Controller::New( ControlInterface& controlInterface )
291 {
292   return ControllerPtr( new Controller( controlInterface ) );
293 }
294
295 void Controller::SetText( const std::string& text )
296 {
297   // Keep until size negotiation
298   mImpl->mNewText = text;
299   mImpl->mOperations = ALL_OPERATIONS;
300
301   if( mImpl->mTextInput )
302   {
303     // Cancel previously queued events
304     mImpl->mTextInput->mEventQueue.clear();
305
306     // TODO - Hide selection decorations
307   }
308 }
309
310 void Controller::EnableTextInput( DecoratorPtr decorator )
311 {
312   if( !mImpl->mTextInput )
313   {
314     mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
315   }
316 }
317
318 bool Controller::Relayout( const Vector2& size )
319 {
320   if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
321   {
322     // Not worth to relayout if width or height is equal to zero.
323     return false;
324   }
325
326   bool updated = false;
327
328   if( size != mImpl->mControlSize )
329   {
330     updated = DoRelayout( size, mImpl->mOperations );
331
332     // Do not re-do any operation until something changes.
333     mImpl->mOperations = NO_OPERATION;
334
335     mImpl->mControlSize = size;
336   }
337
338   if( mImpl->mTextInput )
339   {
340     // Move the cursor, grab handle etc.
341     updated = mImpl->mTextInput->ProcessTouchEvents() || updated;
342   }
343
344   return updated;
345 }
346
347 bool Controller::DoRelayout( const Vector2& size, OperationsMask operations )
348 {
349   bool viewUpdated( false );
350
351   Vector<Character> utf32Characters;
352   Length characterCount = 0u;
353   if( CONVERT_TO_UTF32 & operations )
354   {
355     std::string& text = mImpl->mNewText;
356
357     //  Convert text into UTF-32
358     utf32Characters.Resize( text.size() );
359
360     // This is a bit horrible but std::string returns a (signed) char*
361     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
362
363     // Transform a text array encoded in utf8 into an array encoded in utf32.
364     // It returns the actual number of characters.
365     characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
366     utf32Characters.Resize( characterCount );
367
368     // Sets the text into the model.
369     mImpl->mLogicalModel->SetText( utf32Characters.Begin(), characterCount );
370
371     // Discard temporary text
372     text.clear();
373   }
374
375   Vector<LineBreakInfo> lineBreakInfo;
376   if( GET_LINE_BREAKS & operations )
377   {
378     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
379     // calculate the bidirectional info for each 'paragraph'.
380     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
381     // is not shaped together).
382     lineBreakInfo.Resize( characterCount, TextAbstraction::LINE_NO_BREAK );
383
384     SetLineBreakInfo( utf32Characters,
385                       lineBreakInfo );
386
387     mImpl->mLogicalModel->SetLineBreakInfo( lineBreakInfo.Begin(), characterCount );
388   }
389
390   const bool getScripts = GET_SCRIPTS & operations;
391   const bool validateFonts = VALIDATE_FONTS & operations;
392
393   Vector<ScriptRun> scripts;
394   Vector<FontRun> fonts;
395   if( getScripts || validateFonts )
396   {
397     // Validates the fonts assigned by the application or assigns default ones.
398     // It makes sure all the characters are going to be rendered by the correct font.
399     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
400
401     if( getScripts )
402     {
403       // Retrieves the scripts used in the text.
404       multilanguageSupport.SetScripts( utf32Characters,
405                                        lineBreakInfo,
406                                        scripts );
407
408       // Sets the scripts into the model.
409       mImpl->mLogicalModel->SetScripts( scripts.Begin(), scripts.Count() );
410     }
411
412     if( validateFonts )
413     {
414       // Validates the fonts. If there is a character with no assigned font it sets a default one.
415       // After this call, fonts are validated.
416       multilanguageSupport.ValidateFonts( utf32Characters,
417                                           scripts,
418                                           fonts );
419
420       // Sets the fonts into the model.
421       mImpl->mLogicalModel->SetFonts( fonts.Begin(), fonts.Count() );
422     }
423   }
424
425   Vector<GlyphInfo> glyphs;
426   Vector<CharacterIndex> characterIndices;
427   Vector<Length> charactersPerGlyph;
428   if( SHAPE_TEXT & operations )
429   {
430     // Shapes the text.
431     ShapeText( utf32Characters,
432                lineBreakInfo,
433                scripts,
434                fonts,
435                glyphs,
436                characterIndices,
437                charactersPerGlyph );
438   }
439
440   if( GET_GLYPH_METRICS & operations )
441   {
442     mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), glyphs.Count() );
443   }
444
445   if( LAYOUT & operations )
446   {
447     if( 0u == glyphs.Count() )
448     {
449       const Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
450
451       glyphs.Resize( numberOfGlyphs );
452       characterIndices.Resize( numberOfGlyphs );
453       charactersPerGlyph.Resize( numberOfGlyphs );
454
455       mImpl->mVisualModel->GetGlyphs( glyphs.Begin(),
456                                       0u,
457                                       numberOfGlyphs );
458
459       mImpl->mVisualModel->GetGlyphToCharacterMap( characterIndices.Begin(),
460                                                    0u,
461                                                    numberOfGlyphs );
462
463       mImpl->mVisualModel->GetCharactersPerGlyphMap( charactersPerGlyph.Begin(),
464                                                      0u,
465                                                      numberOfGlyphs );
466     }
467
468     // Update the visual model
469     mImpl->mLayoutEngine.UpdateVisualModel( size,
470                                             glyphs,
471                                             characterIndices,
472                                             charactersPerGlyph,
473                                             *mImpl->mVisualModel );
474
475     viewUpdated = true;
476   }
477
478   return viewUpdated;
479 }
480
481 Vector3 Controller::GetNaturalSize()
482 {
483   // Operations that can be done only once until the text changes.
484   const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
485                                                                          GET_SCRIPTS      |
486                                                                          VALIDATE_FONTS   |
487                                                                          GET_LINE_BREAKS  |
488                                                                          GET_WORD_BREAKS  |
489                                                                          SHAPE_TEXT       |
490                                                                          GET_GLYPH_METRICS );
491
492   // Operations that need to be done if the size or the text changes.
493   const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
494                                                                       REORDER );
495
496   const float maxFloat = std::numeric_limits<float>::max();
497   DoRelayout( Vector2( maxFloat, maxFloat ),
498               static_cast<OperationsMask>( onlyOnceOperations |
499                                            sizeOperations ) );
500
501   // Do not do again the only once operations.
502   mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations & ~onlyOnceOperations );
503
504   // Do the size related operations again.
505   mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations | sizeOperations );
506
507   return Vector3( mImpl->mVisualModel->GetNaturalSize() );
508 }
509
510 float Controller::GetHeightForWidth( float width )
511 {
512   // Operations that can be done only once until the text changes.
513   const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
514                                                                          GET_SCRIPTS      |
515                                                                          VALIDATE_FONTS   |
516                                                                          GET_LINE_BREAKS  |
517                                                                          GET_WORD_BREAKS  |
518                                                                          SHAPE_TEXT       |
519                                                                          GET_GLYPH_METRICS );
520
521   // Operations that need to be done if the size or the text changes.
522   const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
523                                                                       REORDER );
524
525   DoRelayout( Size( width, 0.f ),
526               static_cast<OperationsMask>( onlyOnceOperations |
527                                            sizeOperations ) );
528
529   // Do not do again the only once operations.
530   mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations & ~onlyOnceOperations );
531
532   // Do the size related operations again.
533   mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations | sizeOperations );
534
535   return mImpl->mVisualModel->GetActualSize().height;
536 }
537
538 View& Controller::GetView()
539 {
540   return mImpl->mView;
541 }
542
543 LayoutEngine& Controller::GetLayoutEngine()
544 {
545   return mImpl->mLayoutEngine;
546 }
547
548 void Controller::RequestRelayout()
549 {
550   mImpl->mControlInterface.RequestTextRelayout();
551 }
552
553 void Controller::KeyboardFocusGainEvent()
554 {
555   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
556
557   if( mImpl->mTextInput )
558   {
559     TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
560     mImpl->mTextInput->mEventQueue.push_back( event );
561
562     RequestRelayout();
563   }
564 }
565
566 void Controller::KeyboardFocusLostEvent()
567 {
568   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
569
570   if( mImpl->mTextInput )
571   {
572     TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
573     mImpl->mTextInput->mEventQueue.push_back( event );
574
575     RequestRelayout();
576   }
577 }
578
579 void Controller::TapEvent( unsigned int tapCount, float x, float y )
580 {
581   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
582
583   if( mImpl->mTextInput )
584   {
585     TextInput::Event event( TextInput::TAP_EVENT );
586     event.p1.mUint = tapCount;
587     event.p2.mFloat = x;
588     event.p3.mFloat = y;
589     mImpl->mTextInput->mEventQueue.push_back( event );
590
591     RequestRelayout();
592   }
593 }
594
595 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
596 {
597   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
598
599   if( mImpl->mTextInput )
600   {
601     TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
602     event.p1.mUint  = state;
603     event.p2.mFloat = x;
604     event.p3.mFloat = y;
605     mImpl->mTextInput->mEventQueue.push_back( event );
606
607     RequestRelayout();
608   }
609 }
610
611 Controller::~Controller()
612 {
613   delete mImpl;
614 }
615
616 Controller::Controller( ControlInterface& controlInterface )
617 : mImpl( NULL )
618 {
619   mImpl = new Controller::Impl( controlInterface );
620 }
621
622 } // namespace Text
623
624 } // namespace Toolkit
625
626 } // namespace Dali