878272c559239c8cfd267a7688c2a2421c61d9df
[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( DecoratorPtr decorator )
89   : mDecorator( decorator ),
90     mState( INACTIVE )
91   {
92   }
93
94   /**
95    * @brief Helper to move the cursor, grab handle etc.
96    */
97   bool ProcessTouchEvents()
98   {
99     mDecoratorUpdated = false;
100
101     if( mDecorator )
102     {
103       for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
104       {
105         switch( iter->type )
106         {
107           case KEYBOARD_FOCUS_GAIN_EVENT:
108           {
109             OnKeyboardFocus( true );
110             break;
111           }
112           case KEYBOARD_FOCUS_LOST_EVENT:
113           {
114             OnKeyboardFocus( false );
115             break;
116           }
117           case TAP_EVENT:
118           {
119             OnTapEvent( *iter );
120             break;
121           }
122           case GRAB_HANDLE_EVENT:
123           {
124             OnGrabHandleEvent( *iter );
125             break;
126           }
127         }
128       }
129     }
130
131     mEventQueue.clear();
132
133     return mDecoratorUpdated;
134   }
135
136   void OnKeyboardFocus( bool hasFocus )
137   {
138     // TODO
139   }
140
141   void OnTapEvent( const Event& event )
142   {
143     if( 1u == event.p1.mUint )
144     {
145       mState = TextInput::EDITING;
146       mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
147       mDecorator->StartCursorBlink();
148       mDecorator->SetGrabHandleActive( true );
149       mDecorator->SetPosition( PRIMARY_CURSOR, 10, 0, 18 );
150       mDecoratorUpdated = true;
151     }
152     else if( 2u == event.p1.mUint )
153     {
154       mState = TextInput::SELECTING;
155       mDecorator->SetGrabHandleActive( false );
156       mDecorator->SetSelectionActive( true );
157       mDecoratorUpdated = true;
158     }
159   }
160
161   void OnGrabHandleEvent( const Event& event )
162   {
163     // TODO
164   }
165
166   DecoratorPtr mDecorator;
167   bool mDecoratorUpdated;
168
169   State mState;
170
171   /**
172    * This is used to delay handling events until after the model has been updated.
173    * The number of updates to the model is minimized to improve performance.
174    */
175   vector<Event> mEventQueue; ///< The queue of touch events etc.
176 };
177
178 struct Controller::Impl
179 {
180   Impl( ControlInterface& controlInterface )
181   : mControlInterface( controlInterface ),
182     mNewText(),
183     mOperations( NO_OPERATION ),
184     mControlSize(),
185     mTextInput( NULL )
186   {
187     mLogicalModel = LogicalModel::New();
188     mVisualModel  = VisualModel::New();
189
190     mView.SetVisualModel( mVisualModel );
191
192     mFontClient = TextAbstraction::FontClient::Get();
193   }
194
195   ~Impl()
196   {
197     delete mTextInput;
198   }
199
200   ControlInterface& mControlInterface;
201
202   std::string mNewText;
203
204   LogicalModelPtr mLogicalModel;
205   VisualModelPtr  mVisualModel;
206
207   View mView;
208
209   LayoutEngine mLayoutEngine;
210
211   TextAbstraction::FontClient mFontClient;
212
213   OperationsMask mOperations;
214
215   Size mControlSize;
216
217   // Avoid allocating everything for text input until EnableTextInput()
218   Controller::TextInput* mTextInput;
219 };
220
221 ControllerPtr Controller::New( ControlInterface& controlInterface )
222 {
223   return ControllerPtr( new Controller( controlInterface ) );
224 }
225
226 void Controller::SetText( const std::string& text )
227 {
228   // Keep until size negotiation
229   mImpl->mNewText = text;
230   mImpl->mOperations = ALL_OPERATIONS;
231
232   if( mImpl->mTextInput )
233   {
234     // Cancel previously queued events
235     mImpl->mTextInput->mEventQueue.clear();
236
237     // TODO - Hide selection decorations
238   }
239 }
240
241 void Controller::EnableTextInput( DecoratorPtr decorator )
242 {
243   if( !mImpl->mTextInput )
244   {
245     mImpl->mTextInput = new TextInput( decorator );
246   }
247 }
248
249 bool Controller::Relayout( const Vector2& size )
250 {
251   if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
252   {
253     // Not worth to relayout if width or height is equal to zero.
254     return false;
255   }
256
257   bool updated = false;
258
259   if( size != mImpl->mControlSize )
260   {
261     updated = DoRelayout( size, mImpl->mOperations );
262
263     // Do not re-do any operation until something changes.
264     mImpl->mOperations = NO_OPERATION;
265
266     mImpl->mControlSize = size;
267   }
268
269   if( mImpl->mTextInput )
270   {
271     // Move the cursor, grab handle etc.
272     updated = mImpl->mTextInput->ProcessTouchEvents() || updated;
273   }
274
275   return updated;
276 }
277
278 bool Controller::DoRelayout( const Vector2& size, OperationsMask operations )
279 {
280   bool viewUpdated( false );
281
282   Vector<Character> utf32Characters;
283   Length characterCount = 0u;
284   if( CONVERT_TO_UTF32 & operations )
285   {
286     std::string& text = mImpl->mNewText;
287
288     //  Convert text into UTF-32
289     utf32Characters.Resize( text.size() );
290
291     // This is a bit horrible but std::string returns a (signed) char*
292     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
293
294     // Transform a text array encoded in utf8 into an array encoded in utf32.
295     // It returns the actual number of characters.
296     characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
297     utf32Characters.Resize( characterCount );
298
299     // Sets the text into the model.
300     mImpl->mLogicalModel->SetText( utf32Characters.Begin(), characterCount );
301
302     // Discard temporary text
303     text.clear();
304   }
305
306   Vector<LineBreakInfo> lineBreakInfo;
307   if( GET_LINE_BREAKS & operations )
308   {
309     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
310     // calculate the bidirectional info for each 'paragraph'.
311     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
312     // is not shaped together).
313     lineBreakInfo.Resize( characterCount, TextAbstraction::LINE_NO_BREAK );
314
315     SetLineBreakInfo( utf32Characters,
316                       lineBreakInfo );
317
318     mImpl->mLogicalModel->SetLineBreakInfo( lineBreakInfo.Begin(), characterCount );
319   }
320
321   const bool getScripts = GET_SCRIPTS & operations;
322   const bool validateFonts = VALIDATE_FONTS & operations;
323
324   Vector<ScriptRun> scripts;
325   Vector<FontRun> fonts;
326   if( getScripts || validateFonts )
327   {
328     // Validates the fonts assigned by the application or assigns default ones.
329     // It makes sure all the characters are going to be rendered by the correct font.
330     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
331
332     if( getScripts )
333     {
334       // Retrieves the scripts used in the text.
335       multilanguageSupport.SetScripts( utf32Characters,
336                                        lineBreakInfo,
337                                        scripts );
338
339       // Sets the scripts into the model.
340       mImpl->mLogicalModel->SetScripts( scripts.Begin(), scripts.Count() );
341     }
342
343     if( validateFonts )
344     {
345       // Validates the fonts. If there is a character with no assigned font it sets a default one.
346       // After this call, fonts are validated.
347       multilanguageSupport.ValidateFonts( utf32Characters,
348                                           scripts,
349                                           fonts );
350
351       // Sets the fonts into the model.
352       mImpl->mLogicalModel->SetFonts( fonts.Begin(), fonts.Count() );
353     }
354   }
355
356   Vector<GlyphInfo> glyphs;
357   Vector<CharacterIndex> characterIndices;
358   Vector<Length> charactersPerGlyph;
359   if( SHAPE_TEXT & operations )
360   {
361     // Shapes the text.
362     ShapeText( utf32Characters,
363                lineBreakInfo,
364                scripts,
365                fonts,
366                glyphs,
367                characterIndices,
368                charactersPerGlyph );
369   }
370
371   if( GET_GLYPH_METRICS & operations )
372   {
373     TextAbstraction::FontClient::Get().GetGlyphMetrics( glyphs.Begin(), glyphs.Count() );
374   }
375
376   if( LAYOUT & operations )
377   {
378     if( 0u == glyphs.Count() )
379     {
380       const Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
381
382       glyphs.Resize( numberOfGlyphs );
383       characterIndices.Resize( numberOfGlyphs );
384       charactersPerGlyph.Resize( numberOfGlyphs );
385
386       mImpl->mVisualModel->GetGlyphs( glyphs.Begin(),
387                                       0u,
388                                       numberOfGlyphs );
389
390       mImpl->mVisualModel->GetGlyphToCharacterMap( characterIndices.Begin(),
391                                                    0u,
392                                                    numberOfGlyphs );
393
394       mImpl->mVisualModel->GetCharactersPerGlyphMap( charactersPerGlyph.Begin(),
395                                                      0u,
396                                                      numberOfGlyphs );
397     }
398
399     // Update the visual model
400     mImpl->mLayoutEngine.UpdateVisualModel( size,
401                                             glyphs,
402                                             characterIndices,
403                                             charactersPerGlyph,
404                                             *mImpl->mVisualModel );
405
406     viewUpdated = true;
407   }
408
409   return viewUpdated;
410 }
411
412 Vector3 Controller::GetNaturalSize()
413 {
414   // Operations that can be done only once until the text changes.
415   const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
416                                                                          GET_SCRIPTS      |
417                                                                          VALIDATE_FONTS   |
418                                                                          GET_LINE_BREAKS  |
419                                                                          GET_WORD_BREAKS  |
420                                                                          SHAPE_TEXT       |
421                                                                          GET_GLYPH_METRICS );
422
423   // Operations that need to be done if the size or the text changes.
424   const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
425                                                                       REORDER );
426
427   const float maxFloat = std::numeric_limits<float>::max();
428   DoRelayout( Vector2( maxFloat, maxFloat ),
429               static_cast<OperationsMask>( onlyOnceOperations |
430                                            sizeOperations ) );
431
432   // Do not do again the only once operations.
433   mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations & ~onlyOnceOperations );
434
435   // Do the size related operations again.
436   mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations | sizeOperations );
437
438   return Vector3( mImpl->mVisualModel->GetNaturalSize() );
439 }
440
441 float Controller::GetHeightForWidth( float width )
442 {
443   // Operations that can be done only once until the text changes.
444   const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
445                                                                          GET_SCRIPTS      |
446                                                                          VALIDATE_FONTS   |
447                                                                          GET_LINE_BREAKS  |
448                                                                          GET_WORD_BREAKS  |
449                                                                          SHAPE_TEXT       |
450                                                                          GET_GLYPH_METRICS );
451
452   // Operations that need to be done if the size or the text changes.
453   const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
454                                                                       REORDER );
455
456   DoRelayout( Size( width, 0.f ),
457               static_cast<OperationsMask>( onlyOnceOperations |
458                                            sizeOperations ) );
459
460   // Do not do again the only once operations.
461   mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations & ~onlyOnceOperations );
462
463   // Do the size related operations again.
464   mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations | sizeOperations );
465
466   return mImpl->mVisualModel->GetActualSize().height;
467 }
468
469 View& Controller::GetView()
470 {
471   return mImpl->mView;
472 }
473
474 LayoutEngine& Controller::GetLayoutEngine()
475 {
476   return mImpl->mLayoutEngine;
477 }
478
479 void Controller::RequestRelayout()
480 {
481   mImpl->mControlInterface.RequestTextRelayout();
482 }
483
484 void Controller::KeyboardFocusGainEvent()
485 {
486   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
487
488   if( mImpl->mTextInput )
489   {
490     TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
491     mImpl->mTextInput->mEventQueue.push_back( event );
492
493     RequestRelayout();
494   }
495 }
496
497 void Controller::KeyboardFocusLostEvent()
498 {
499   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
500
501   if( mImpl->mTextInput )
502   {
503     TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
504     mImpl->mTextInput->mEventQueue.push_back( event );
505
506     RequestRelayout();
507   }
508 }
509
510 void Controller::TapEvent( unsigned int tapCount, float x, float y )
511 {
512   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
513
514   if( mImpl->mTextInput )
515   {
516     TextInput::Event event( TextInput::TAP_EVENT );
517     event.p1.mUint = tapCount;
518     event.p2.mFloat = x;
519     event.p3.mFloat = y;
520     mImpl->mTextInput->mEventQueue.push_back( event );
521
522     RequestRelayout();
523   }
524 }
525
526 void Controller::GrabHandleEvent( GrabHandleState state, float x )
527 {
528   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
529
530   if( mImpl->mTextInput )
531   {
532     TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
533     event.p1.mInt   = state;
534     event.p2.mFloat = x;
535     mImpl->mTextInput->mEventQueue.push_back( event );
536
537     RequestRelayout();
538   }
539 }
540
541 Controller::~Controller()
542 {
543   delete mImpl;
544 }
545
546 Controller::Controller( ControlInterface& controlInterface )
547 : mImpl( NULL )
548 {
549   mImpl = new Controller::Impl( controlInterface );
550 }
551
552 } // namespace Text
553
554 } // namespace Toolkit
555
556 } // namespace Dali