71a4c5f78aabe6196cbcdaabc7a3e1b56bd72314
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / text-controls / text-field-impl.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/controls/text-controls/text-field-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <string>
23 #include <dali/public-api/adaptor-framework/key.h>
24 #include <dali/public-api/common/stage.h>
25 #include <dali/public-api/images/resource-image.h>
26 #include <dali/public-api/object/type-registry.h>
27 #include <dali/public-api/object/type-registry-helper.h>
28 #include <dali/public-api/scripting/scripting.h>
29 #include <dali/integration-api/debug.h>
30 #include <dali/public-api/adaptor-framework/virtual-keyboard.h>
31
32 // INTERNAL INCLUDES
33 #include <dali-toolkit/public-api/text/rendering-backend.h>
34 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
35 #include <dali-toolkit/internal/text/rendering/text-backend.h>
36 #include <dali-toolkit/internal/styling/style-manager-impl.h>
37
38 using namespace Dali::Toolkit::Text;
39
40 namespace Dali
41 {
42
43 namespace Toolkit
44 {
45
46 namespace Internal
47 {
48
49 namespace
50 {
51   const unsigned int DEFAULT_RENDERING_BACKEND = Dali::Toolkit::Text::DEFAULT_RENDERING_BACKEND;
52 }
53
54 namespace
55 {
56
57 const Scripting::StringEnum< Toolkit::Text::LayoutEngine::Alignment > ALIGNMENT_STRING_TABLE[] =
58 {
59   { "BEGIN",  Toolkit::Text::LayoutEngine::ALIGN_BEGIN  },
60   { "CENTER", Toolkit::Text::LayoutEngine::ALIGN_CENTER },
61   { "END",    Toolkit::Text::LayoutEngine::ALIGN_END    },
62 };
63 const unsigned int ALIGNMENT_STRING_TABLE_COUNT = sizeof( ALIGNMENT_STRING_TABLE ) / sizeof( ALIGNMENT_STRING_TABLE[0] );
64
65 // Type registration
66 BaseHandle Create()
67 {
68   return Toolkit::TextField::New();
69 }
70
71 // Setup properties, signals and actions using the type-registry.
72 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::TextField, Toolkit::Control, Create );
73
74 DALI_PROPERTY_REGISTRATION( TextField, "rendering-backend",       INTEGER,   RENDERING_BACKEND       )
75 DALI_PROPERTY_REGISTRATION( TextField, "placeholder-text",        STRING,    PLACEHOLDER_TEXT        )
76 DALI_PROPERTY_REGISTRATION( TextField, "text",                    STRING,    TEXT                    )
77 DALI_PROPERTY_REGISTRATION( TextField, "font-family",             STRING,    FONT_FAMILY             )
78 DALI_PROPERTY_REGISTRATION( TextField, "font-style",              STRING,    FONT_STYLE              )
79 DALI_PROPERTY_REGISTRATION( TextField, "point-size",              FLOAT,     POINT_SIZE              )
80 DALI_PROPERTY_REGISTRATION( TextField, "exceed-policy",           INTEGER,   EXCEED_POLICY           )
81 DALI_PROPERTY_REGISTRATION( TextField, "primary-cursor-color",    VECTOR4,   PRIMARY_CURSOR_COLOR    )
82 DALI_PROPERTY_REGISTRATION( TextField, "secondary-cursor-color",  VECTOR4,   SECONDARY_CURSOR_COLOR  )
83 DALI_PROPERTY_REGISTRATION( TextField, "enable-cursor-blink",     BOOLEAN,   ENABLE_CURSOR_BLINK     )
84 DALI_PROPERTY_REGISTRATION( TextField, "cursor-blink-interval",   FLOAT,     CURSOR_BLINK_INTERVAL   )
85 DALI_PROPERTY_REGISTRATION( TextField, "cursor-blink-duration",   FLOAT,     CURSOR_BLINK_DURATION   )
86 DALI_PROPERTY_REGISTRATION( TextField, "grab-handle-image",       STRING,    GRAB_HANDLE_IMAGE       )
87 DALI_PROPERTY_REGISTRATION( TextField, "decoration bounding-box", RECTANGLE, DECORATION_BOUNDING_BOX )
88 DALI_PROPERTY_REGISTRATION( TextField, "alignment",               STRING,    ALIGNMENT               )
89 DALI_TYPE_REGISTRATION_END()
90
91 } // namespace
92
93 Toolkit::TextField TextField::New()
94 {
95   // Create the implementation, temporarily owned by this handle on stack
96   IntrusivePtr< TextField > impl = new TextField();
97
98   // Pass ownership to CustomActor handle
99   Toolkit::TextField handle( *impl );
100
101   // Second-phase init of the implementation
102   // This can only be done after the CustomActor connection has been made...
103   impl->Initialize();
104
105   return handle;
106 }
107
108 void TextField::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
109 {
110   Toolkit::TextField textField = Toolkit::TextField::DownCast( Dali::BaseHandle( object ) );
111
112   if( textField )
113   {
114     TextField& impl( GetImpl( textField ) );
115
116     switch( index )
117     {
118       case Toolkit::TextField::Property::RENDERING_BACKEND:
119       {
120         int backend = value.Get< int >();
121
122         if( impl.mRenderingBackend != backend )
123         {
124           impl.mRenderingBackend = backend;
125           impl.mRenderer.Reset();
126         }
127         break;
128       }
129       case Toolkit::TextField::Property::PLACEHOLDER_TEXT:
130       {
131         if( impl.mController )
132         {
133           //impl.mController->SetPlaceholderText( value.Get< std::string >() ); TODO
134         }
135         break;
136       }
137       case Toolkit::TextField::Property::TEXT:
138       {
139         if( impl.mController )
140         {
141           impl.mController->SetText( value.Get< std::string >() );
142         }
143         break;
144       }
145       case Toolkit::TextField::Property::FONT_FAMILY:
146       {
147         if( impl.mController )
148         {
149           std::string fontFamily = value.Get< std::string >();
150
151           if( impl.mController->GetDefaultFontFamily() != fontFamily )
152           {
153             impl.mController->SetDefaultFontFamily( fontFamily );
154             impl.RequestTextRelayout();
155           }
156         }
157         break;
158       }
159       case Toolkit::TextField::Property::FONT_STYLE:
160       {
161         if( impl.mController )
162         {
163           std::string fontStyle = value.Get< std::string >();
164
165           if( impl.mController->GetDefaultFontStyle() != fontStyle )
166           {
167             impl.mController->SetDefaultFontStyle( fontStyle );
168             impl.RequestTextRelayout();
169           }
170         }
171         break;
172       }
173       case Toolkit::TextField::Property::POINT_SIZE:
174       {
175         if( impl.mController )
176         {
177           float pointSize = value.Get< float >();
178
179           if( impl.mController->GetDefaultPointSize() != pointSize /*TODO - epsilon*/ )
180           {
181             impl.mController->SetDefaultPointSize( pointSize );
182             impl.RequestTextRelayout();
183           }
184         }
185         break;
186       }
187       case Toolkit::TextField::Property::EXCEED_POLICY:
188       {
189         impl.mExceedPolicy = value.Get< int >();
190         break;
191       }
192       case Toolkit::TextField::Property::PRIMARY_CURSOR_COLOR:
193       {
194         if( impl.mDecorator )
195         {
196           impl.mDecorator->SetColor( PRIMARY_CURSOR, value.Get< Vector4 >() );
197         }
198         break;
199       }
200       case Toolkit::TextField::Property::SECONDARY_CURSOR_COLOR:
201       {
202         if( impl.mDecorator )
203         {
204           impl.mDecorator->SetColor( SECONDARY_CURSOR, value.Get< Vector4 >() );
205         }
206         break;
207       }
208       case Toolkit::TextField::Property::ENABLE_CURSOR_BLINK:
209       {
210         if( impl.mController )
211         {
212           impl.mController->SetEnableCursorBlink( value.Get< bool >() );
213         }
214         break;
215       }
216       case Toolkit::TextField::Property::CURSOR_BLINK_INTERVAL:
217       {
218         if( impl.mDecorator )
219         {
220           impl.mDecorator->SetCursorBlinkInterval( value.Get< float >() );
221         }
222         break;
223       }
224       case Toolkit::TextField::Property::CURSOR_BLINK_DURATION:
225       {
226         if( impl.mDecorator )
227         {
228           impl.mDecorator->SetCursorBlinkDuration( value.Get< float >() );
229         }
230         break;
231       }
232       case Toolkit::TextField::Property::GRAB_HANDLE_IMAGE:
233       {
234         ResourceImage image = ResourceImage::New( value.Get< std::string >() );
235
236         if( impl.mDecorator )
237         {
238           impl.mDecorator->SetGrabHandleImage( image );
239         }
240         break;
241       }
242       case Toolkit::TextField::Property::DECORATION_BOUNDING_BOX:
243       {
244         if( impl.mDecorator )
245         {
246           impl.mDecorator->SetBoundingBox( value.Get< Rect<int> >() );
247         }
248         break;
249       }
250       case Toolkit::TextField::Property::ALIGNMENT:
251       {
252         LayoutEngine& engine = impl.mController->GetLayoutEngine();
253         const LayoutEngine::Alignment alignment = Scripting::GetEnumeration< Toolkit::Text::LayoutEngine::Alignment >( value.Get< std::string >().c_str(),
254                                                                                                                        ALIGNMENT_STRING_TABLE,
255                                                                                                                        ALIGNMENT_STRING_TABLE_COUNT );
256
257         if( engine.GetAlignment() != alignment )
258         {
259           engine.SetAlignment( alignment );
260           impl.RequestTextRelayout();
261         }
262         break;
263       }
264     } // switch
265   } // textfield
266 }
267
268 Property::Value TextField::GetProperty( BaseObject* object, Property::Index index )
269 {
270   Property::Value value;
271
272   Toolkit::TextField textField = Toolkit::TextField::DownCast( Dali::BaseHandle( object ) );
273
274   if( textField )
275   {
276     TextField& impl( GetImpl( textField ) );
277
278     switch( index )
279     {
280       case Toolkit::TextField::Property::RENDERING_BACKEND:
281       {
282         value = impl.mRenderingBackend;
283         break;
284       }
285       case Toolkit::TextField::Property::PLACEHOLDER_TEXT:
286       {
287         if( impl.mController )
288         {
289           std::string text;
290           impl.mController->GetPlaceholderText( text );
291           value = text;
292         }
293         break;
294       }
295       case Toolkit::TextField::Property::TEXT:
296       {
297         if( impl.mController )
298         {
299           std::string text;
300           impl.mController->GetText( text );
301           value = text;
302         }
303         break;
304       }
305       case Toolkit::TextField::Property::EXCEED_POLICY:
306       {
307         value = impl.mExceedPolicy;
308         break;
309       }
310       case Toolkit::TextField::Property::PRIMARY_CURSOR_COLOR:
311       {
312         if( impl.mDecorator )
313         {
314           value = impl.mDecorator->GetColor( PRIMARY_CURSOR );
315         }
316         break;
317       }
318       case Toolkit::TextField::Property::SECONDARY_CURSOR_COLOR:
319       {
320         if( impl.mDecorator )
321         {
322           value = impl.mDecorator->GetColor( SECONDARY_CURSOR );
323         }
324         break;
325       }
326       case Toolkit::TextField::Property::ENABLE_CURSOR_BLINK:
327       {
328         value = impl.mController->GetEnableCursorBlink();
329         break;
330       }
331       case Toolkit::TextField::Property::CURSOR_BLINK_INTERVAL:
332       {
333         if( impl.mDecorator )
334         {
335           value = impl.mDecorator->GetCursorBlinkInterval();
336         }
337         break;
338       }
339       case Toolkit::TextField::Property::CURSOR_BLINK_DURATION:
340       {
341         if( impl.mDecorator )
342         {
343           value = impl.mDecorator->GetCursorBlinkDuration();
344         }
345         break;
346       }
347       case Toolkit::TextField::Property::DECORATION_BOUNDING_BOX:
348       {
349         if( impl.mDecorator )
350         {
351           value = impl.mDecorator->GetBoundingBox();
352         }
353         break;
354       }
355       case Toolkit::TextField::Property::ALIGNMENT:
356       {
357         if( impl.mController )
358         {
359           value = std::string( Scripting::GetEnumerationName< Toolkit::Text::LayoutEngine::Alignment >( impl.mController->GetLayoutEngine().GetAlignment(),
360                                                                                                         ALIGNMENT_STRING_TABLE,
361                                                                                                         ALIGNMENT_STRING_TABLE_COUNT ) );
362         }
363         break;
364       }
365     } //switch
366   }
367
368   return value;
369 }
370
371 void TextField::OnInitialize()
372 {
373   mController = Text::Controller::New( *this );
374
375   mDecorator = Text::Decorator::New( *this, *mController );
376
377   mController->GetLayoutEngine().SetLayout( LayoutEngine::SINGLE_LINE_BOX );
378
379   mController->EnableTextInput( mDecorator );
380
381   // Forward input events to controller
382   EnableGestureDetection(Gesture::Tap);
383   GetTapGestureDetector().SetMaximumTapsRequired( 2 );
384   EnableGestureDetection(Gesture::Pan);
385
386   // Set BoundingBox to stage size if not already set.
387   if ( mDecorator->GetBoundingBox().IsEmpty() )
388   {
389     Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
390     mDecorator->SetBoundingBox( Rect<int>( 0.0f, 0.0f, stageSize.width, stageSize.height ) );
391   }
392 }
393
394 void TextField::OnStyleChange( Toolkit::StyleManager styleManager, StyleChange change )
395 {
396   GetImpl( styleManager ).ApplyThemeStyle( Toolkit::Control( GetOwner() ) );
397 }
398
399 Vector3 TextField::GetNaturalSize()
400 {
401   return mController->GetNaturalSize();
402 }
403
404 float TextField::GetHeightForWidth( float width )
405 {
406   return mController->GetHeightForWidth( width );
407 }
408
409 void TextField::OnRelayout( const Vector2& size, ActorSizeContainer& container )
410 {
411   if( mController->Relayout( size ) ||
412       !mRenderer )
413   {
414     const Vector2& scrollPosition = mController->GetScrollPosition();
415     const Vector2& alignmentOffset = mController->GetAlignmentOffset();
416
417     Vector2 offset = scrollPosition + alignmentOffset;
418
419     if( mDecorator )
420     {
421       mDecorator->Relayout( size, offset );
422     }
423
424     if( !mRenderer )
425     {
426       mRenderer = Backend::Get().NewRenderer( mRenderingBackend );
427     }
428
429     RenderableActor renderableActor;
430     if( mRenderer )
431     {
432       renderableActor = mRenderer->Render( mController->GetView() );
433     }
434
435     EnableClipping( (Dali::Toolkit::TextField::EXCEED_POLICY_CLIP == mExceedPolicy), size );
436
437     if( renderableActor != mRenderableActor )
438     {
439       UnparentAndReset( mRenderableActor );
440       mRenderableActor = renderableActor;
441     }
442
443     if( mRenderableActor )
444     {
445       mRenderableActor.SetPosition( offset.x, offset.y );
446
447       // Make sure the actor is parented correctly with/without clipping
448       if( mClipper )
449       {
450         mClipper->GetRootActor().Add( mRenderableActor );
451       }
452       else
453       {
454         Self().Add( mRenderableActor );
455       }
456     }
457   }
458 }
459
460 void TextField::OnKeyInputFocusGained()
461 {
462   VirtualKeyboard::StatusChangedSignal().Connect( this, &TextField::KeyboardStatusChanged );
463
464   ImfManager imfManager = ImfManager::Get();
465
466   if ( imfManager )
467   {
468     imfManager.EventReceivedSignal().Connect( this, &TextField::OnImfEvent );
469
470     // Notify that the text editing start.
471     imfManager.Activate();
472
473     // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
474     imfManager.SetRestoreAfterFocusLost( true );
475   }
476
477   mController->KeyboardFocusGainEvent();
478 }
479
480 void TextField::OnKeyInputFocusLost()
481 {
482   VirtualKeyboard::StatusChangedSignal().Disconnect( this, &TextField::KeyboardStatusChanged );
483
484   ImfManager imfManager = ImfManager::Get();
485   if ( imfManager )
486   {
487     // The text editing is finished. Therefore the imf manager don't have restore activation.
488     imfManager.SetRestoreAfterFocusLost( false );
489
490     // Notify that the text editing finish.
491     imfManager.Deactivate();
492
493     imfManager.EventReceivedSignal().Disconnect( this, &TextField::OnImfEvent );
494   }
495
496   mController->KeyboardFocusLostEvent();
497 }
498
499 void TextField::OnTap( const TapGesture& gesture )
500 {
501   // Show the keyboard if it was hidden.
502   if (!VirtualKeyboard::IsVisible())
503   {
504     VirtualKeyboard::Show();
505   }
506
507   SetKeyInputFocus();
508
509   mController->TapEvent( gesture.numberOfTaps, gesture.localPoint.x, gesture.localPoint.y );
510 }
511
512 void TextField::OnPan( const PanGesture& gesture )
513 {
514   mController->PanEvent( gesture.state, gesture.displacement );
515 }
516
517 bool TextField::OnKeyEvent( const KeyEvent& event )
518 {
519   if( Dali::DALI_KEY_ESCAPE == event.keyCode )
520   {
521     ClearKeyInputFocus();
522   }
523
524   return mController->KeyEvent( event );
525 }
526
527 ImfManager::ImfCallbackData TextField::OnImfEvent( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
528 {
529   switch ( imfEvent.eventName )
530   {
531     case ImfManager::COMMIT:
532     {
533       KeyEvent event( "", imfEvent.predictiveString, 0, 0, 0, KeyEvent::Down );
534       mController->KeyEvent( event );
535       break;
536     }
537     case ImfManager::PREEDIT: // fall through
538     case ImfManager::DELETESURROUNDING:
539     case ImfManager::GETSURROUNDING:
540     case ImfManager::VOID:
541     {
542       // do nothing
543     }
544   } // end switch
545
546   return ImfManager::ImfCallbackData();
547 }
548
549 void TextField::RequestTextRelayout()
550 {
551   RelayoutRequest();
552 }
553
554 void TextField::EnableClipping( bool clipping, const Vector2& size )
555 {
556   if( clipping )
557   {
558     // Not worth to created clip actor if width or height is equal to zero.
559     if( size.width > Math::MACHINE_EPSILON_1000 && size.height > Math::MACHINE_EPSILON_1000 )
560     {
561       if( !mClipper )
562       {
563         Actor self = Self();
564
565         mClipper = Clipper::New( size );
566         self.Add( mClipper->GetRootActor() );
567         self.Add( mClipper->GetImageActor() );
568       }
569       else if ( mClipper )
570       {
571         mClipper->Refresh( size );
572       }
573     }
574   }
575   else
576   {
577     // Note - this will automatically remove the root & image actors
578     mClipper.Reset();
579   }
580 }
581
582 void TextField::KeyboardStatusChanged(bool keyboardShown)
583 {
584   // Just hide the grab handle when keyboard is hidden.
585   if (!keyboardShown )
586   {
587     mController->KeyboardFocusLostEvent();
588   }
589 }
590
591 TextField::TextField()
592 : Control( ControlBehaviour( REQUIRES_STYLE_CHANGE_SIGNALS ) ),
593   mRenderingBackend( DEFAULT_RENDERING_BACKEND ),
594   mExceedPolicy( Dali::Toolkit::TextField::EXCEED_POLICY_CLIP )
595 {
596 }
597
598 TextField::~TextField()
599 {
600   mClipper.Reset();
601 }
602
603 } // namespace Internal
604
605 } // namespace Toolkit
606
607 } // namespace Dali