TextLabel property 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/logical-model.h>
25 #include <dali-toolkit/internal/text/multi-language-support.h>
26 #include <dali-toolkit/internal/text/script-run.h>
27 #include <dali-toolkit/internal/text/segmentation.h>
28 #include <dali-toolkit/internal/text/shaper.h>
29 #include <dali-toolkit/internal/text/text-view.h>
30 #include <dali-toolkit/internal/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::GetText( std::string& text )
311 {
312   if( !mImpl->mNewText.empty() )
313   {
314     text = mImpl->mNewText;
315   }
316   else
317   {
318     // TODO - Convert from UTF-32
319   }
320 }
321
322 void Controller::EnableTextInput( DecoratorPtr decorator )
323 {
324   if( !mImpl->mTextInput )
325   {
326     mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
327   }
328 }
329
330 bool Controller::Relayout( const Vector2& size )
331 {
332   if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
333   {
334     // Not worth to relayout if width or height is equal to zero.
335     return false;
336   }
337
338   bool updated = false;
339
340   if( size != mImpl->mControlSize )
341   {
342     updated = DoRelayout( size, mImpl->mOperations );
343
344     // Do not re-do any operation until something changes.
345     mImpl->mOperations = NO_OPERATION;
346
347     mImpl->mControlSize = size;
348   }
349
350   if( mImpl->mTextInput )
351   {
352     // Move the cursor, grab handle etc.
353     updated = mImpl->mTextInput->ProcessTouchEvents() || updated;
354   }
355
356   return updated;
357 }
358
359 bool Controller::DoRelayout( const Vector2& size, OperationsMask operations )
360 {
361   bool viewUpdated( false );
362
363   Vector<Character> utf32Characters;
364   Length characterCount = 0u;
365   if( CONVERT_TO_UTF32 & operations )
366   {
367     std::string& text = mImpl->mNewText;
368
369     //  Convert text into UTF-32
370     utf32Characters.Resize( text.size() );
371
372     // This is a bit horrible but std::string returns a (signed) char*
373     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
374
375     // Transform a text array encoded in utf8 into an array encoded in utf32.
376     // It returns the actual number of characters.
377     characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
378     utf32Characters.Resize( characterCount );
379
380     // Sets the text into the model.
381     mImpl->mLogicalModel->SetText( utf32Characters.Begin(), characterCount );
382
383     // Discard temporary text
384     text.clear();
385   }
386
387   Vector<LineBreakInfo> lineBreakInfo;
388   if( GET_LINE_BREAKS & operations )
389   {
390     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
391     // calculate the bidirectional info for each 'paragraph'.
392     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
393     // is not shaped together).
394     lineBreakInfo.Resize( characterCount, TextAbstraction::LINE_NO_BREAK );
395
396     SetLineBreakInfo( utf32Characters,
397                       lineBreakInfo );
398
399     mImpl->mLogicalModel->SetLineBreakInfo( lineBreakInfo.Begin(), characterCount );
400   }
401
402   const bool getScripts = GET_SCRIPTS & operations;
403   const bool validateFonts = VALIDATE_FONTS & operations;
404
405   Vector<ScriptRun> scripts;
406   Vector<FontRun> fonts;
407   if( getScripts || validateFonts )
408   {
409     // Validates the fonts assigned by the application or assigns default ones.
410     // It makes sure all the characters are going to be rendered by the correct font.
411     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
412
413     if( getScripts )
414     {
415       // Retrieves the scripts used in the text.
416       multilanguageSupport.SetScripts( utf32Characters,
417                                        lineBreakInfo,
418                                        scripts );
419
420       // Sets the scripts into the model.
421       mImpl->mLogicalModel->SetScripts( scripts.Begin(), scripts.Count() );
422     }
423
424     if( validateFonts )
425     {
426       // Validates the fonts. If there is a character with no assigned font it sets a default one.
427       // After this call, fonts are validated.
428       multilanguageSupport.ValidateFonts( utf32Characters,
429                                           scripts,
430                                           fonts );
431
432       // Sets the fonts into the model.
433       mImpl->mLogicalModel->SetFonts( fonts.Begin(), fonts.Count() );
434     }
435   }
436
437   Vector<GlyphInfo> glyphs;
438   Vector<CharacterIndex> characterIndices;
439   Vector<Length> charactersPerGlyph;
440   if( SHAPE_TEXT & operations )
441   {
442     // Shapes the text.
443     ShapeText( utf32Characters,
444                lineBreakInfo,
445                scripts,
446                fonts,
447                glyphs,
448                characterIndices,
449                charactersPerGlyph );
450   }
451
452   if( GET_GLYPH_METRICS & operations )
453   {
454     mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), glyphs.Count() );
455   }
456
457   if( LAYOUT & operations )
458   {
459     if( 0u == glyphs.Count() )
460     {
461       const Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
462
463       glyphs.Resize( numberOfGlyphs );
464       characterIndices.Resize( numberOfGlyphs );
465       charactersPerGlyph.Resize( numberOfGlyphs );
466
467       mImpl->mVisualModel->GetGlyphs( glyphs.Begin(),
468                                       0u,
469                                       numberOfGlyphs );
470
471       mImpl->mVisualModel->GetGlyphToCharacterMap( characterIndices.Begin(),
472                                                    0u,
473                                                    numberOfGlyphs );
474
475       mImpl->mVisualModel->GetCharactersPerGlyphMap( charactersPerGlyph.Begin(),
476                                                      0u,
477                                                      numberOfGlyphs );
478     }
479
480     // Update the visual model
481     mImpl->mLayoutEngine.UpdateVisualModel( size,
482                                             glyphs,
483                                             characterIndices,
484                                             charactersPerGlyph,
485                                             *mImpl->mVisualModel );
486
487     viewUpdated = true;
488   }
489
490   return viewUpdated;
491 }
492
493 Vector3 Controller::GetNaturalSize()
494 {
495   // TODO - Finish implementing
496   return Vector3::ZERO;
497
498   // Operations that can be done only once until the text changes.
499   const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
500                                                                          GET_SCRIPTS      |
501                                                                          VALIDATE_FONTS   |
502                                                                          GET_LINE_BREAKS  |
503                                                                          GET_WORD_BREAKS  |
504                                                                          SHAPE_TEXT       |
505                                                                          GET_GLYPH_METRICS );
506
507   // Operations that need to be done if the size or the text changes.
508   const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
509                                                                       REORDER );
510
511   const float maxFloat = std::numeric_limits<float>::max();
512   DoRelayout( Vector2( maxFloat, maxFloat ),
513               static_cast<OperationsMask>( onlyOnceOperations |
514                                            sizeOperations ) );
515
516   // Do not do again the only once operations.
517   mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations & ~onlyOnceOperations );
518
519   // Do the size related operations again.
520   mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations | sizeOperations );
521
522   return Vector3( mImpl->mVisualModel->GetNaturalSize() );
523 }
524
525 float Controller::GetHeightForWidth( float width )
526 {
527   // Operations that can be done only once until the text changes.
528   const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
529                                                                          GET_SCRIPTS      |
530                                                                          VALIDATE_FONTS   |
531                                                                          GET_LINE_BREAKS  |
532                                                                          GET_WORD_BREAKS  |
533                                                                          SHAPE_TEXT       |
534                                                                          GET_GLYPH_METRICS );
535
536   // Operations that need to be done if the size or the text changes.
537   const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
538                                                                       REORDER );
539
540   DoRelayout( Size( width, 0.f ),
541               static_cast<OperationsMask>( onlyOnceOperations |
542                                            sizeOperations ) );
543
544   // Do not do again the only once operations.
545   mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations & ~onlyOnceOperations );
546
547   // Do the size related operations again.
548   mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations | sizeOperations );
549
550   return mImpl->mVisualModel->GetActualSize().height;
551 }
552
553 View& Controller::GetView()
554 {
555   return mImpl->mView;
556 }
557
558 LayoutEngine& Controller::GetLayoutEngine()
559 {
560   return mImpl->mLayoutEngine;
561 }
562
563 void Controller::RequestRelayout()
564 {
565   mImpl->mControlInterface.RequestTextRelayout();
566 }
567
568 void Controller::KeyboardFocusGainEvent()
569 {
570   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
571
572   if( mImpl->mTextInput )
573   {
574     TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
575     mImpl->mTextInput->mEventQueue.push_back( event );
576
577     RequestRelayout();
578   }
579 }
580
581 void Controller::KeyboardFocusLostEvent()
582 {
583   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
584
585   if( mImpl->mTextInput )
586   {
587     TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
588     mImpl->mTextInput->mEventQueue.push_back( event );
589
590     RequestRelayout();
591   }
592 }
593
594 void Controller::TapEvent( unsigned int tapCount, float x, float y )
595 {
596   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
597
598   if( mImpl->mTextInput )
599   {
600     TextInput::Event event( TextInput::TAP_EVENT );
601     event.p1.mUint = tapCount;
602     event.p2.mFloat = x;
603     event.p3.mFloat = y;
604     mImpl->mTextInput->mEventQueue.push_back( event );
605
606     RequestRelayout();
607   }
608 }
609
610 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
611 {
612   DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
613
614   if( mImpl->mTextInput )
615   {
616     TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
617     event.p1.mUint  = state;
618     event.p2.mFloat = x;
619     event.p3.mFloat = y;
620     mImpl->mTextInput->mEventQueue.push_back( event );
621
622     RequestRelayout();
623   }
624 }
625
626 Controller::~Controller()
627 {
628   delete mImpl;
629 }
630
631 Controller::Controller( ControlInterface& controlInterface )
632 : mImpl( NULL )
633 {
634   mImpl = new Controller::Impl( controlInterface );
635 }
636
637 } // namespace Text
638
639 } // namespace Toolkit
640
641 } // namespace Dali