Clipping support for TextField
[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/integration-api/debug.h>
29
30 // INTERNAL INCLUDES
31 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
32 #include <dali-toolkit/internal/text/rendering/text-backend.h>
33
34 using namespace Dali::Toolkit::Text;
35
36 namespace
37 {
38
39 const unsigned int DEFAULT_RENDERING_BACKEND = 0;
40
41 } // namespace
42
43
44 namespace Dali
45 {
46
47 namespace Toolkit
48 {
49
50 namespace Internal
51 {
52
53 namespace
54 {
55
56 // Type registration
57 BaseHandle Create()
58 {
59   return Toolkit::TextField::New();
60 }
61
62 // Setup properties, signals and actions using the type-registry.
63 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::TextField, Toolkit::Control, Create );
64
65 DALI_PROPERTY_REGISTRATION( TextField, "rendering-backend",       INTEGER,   RENDERING_BACKEND       )
66 DALI_PROPERTY_REGISTRATION( TextField, "placeholder-text",        STRING,    PLACEHOLDER_TEXT        )
67 DALI_PROPERTY_REGISTRATION( TextField, "text",                    STRING,    TEXT                    )
68 DALI_PROPERTY_REGISTRATION( TextField, "font-family",             STRING,    FONT_FAMILY             )
69 DALI_PROPERTY_REGISTRATION( TextField, "font-style",              STRING,    FONT_STYLE              )
70 DALI_PROPERTY_REGISTRATION( TextField, "point-size",              FLOAT,     POINT_SIZE              )
71 DALI_PROPERTY_REGISTRATION( TextField, "exceed-policy",           INTEGER,   EXCEED_POLICY           )
72 DALI_PROPERTY_REGISTRATION( TextField, "cursor-image",            STRING,    CURSOR_IMAGE            )
73 DALI_PROPERTY_REGISTRATION( TextField, "primary-cursor-color",    VECTOR4,   PRIMARY_CURSOR_COLOR    )
74 DALI_PROPERTY_REGISTRATION( TextField, "secondary-cursor-color",  VECTOR4,   SECONDARY_CURSOR_COLOR  )
75 DALI_PROPERTY_REGISTRATION( TextField, "enable-cursor-blink",     BOOLEAN,   ENABLE_CURSOR_BLINK     )
76 DALI_PROPERTY_REGISTRATION( TextField, "cursor-blink-interval",   FLOAT,     CURSOR_BLINK_INTERVAL   )
77 DALI_PROPERTY_REGISTRATION( TextField, "cursor-blink-duration",   FLOAT,     CURSOR_BLINK_DURATION   )
78 DALI_PROPERTY_REGISTRATION( TextField, "grab-handle-image",       STRING,    GRAB_HANDLE_IMAGE       )
79 DALI_PROPERTY_REGISTRATION( TextField, "decoration bounding-box", RECTANGLE, DECORATION_BOUNDING_BOX )
80
81 DALI_TYPE_REGISTRATION_END()
82
83 } // namespace
84
85 Toolkit::TextField TextField::New()
86 {
87   // Create the implementation, temporarily owned by this handle on stack
88   IntrusivePtr< TextField > impl = new TextField();
89
90   // Pass ownership to CustomActor handle
91   Toolkit::TextField handle( *impl );
92
93   // Second-phase init of the implementation
94   // This can only be done after the CustomActor connection has been made...
95   impl->Initialize();
96
97   return handle;
98 }
99
100 void TextField::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
101 {
102   Toolkit::TextField textField = Toolkit::TextField::DownCast( Dali::BaseHandle( object ) );
103
104   if( textField )
105   {
106     TextField& impl( GetImpl( textField ) );
107
108     switch( index )
109     {
110       case Toolkit::TextField::Property::RENDERING_BACKEND:
111       {
112         int backend = value.Get< int >();
113
114         if( impl.mRenderingBackend != backend )
115         {
116           impl.mRenderingBackend = backend;
117           impl.mRenderer.Reset();
118         }
119         break;
120       }
121       case Toolkit::TextField::Property::PLACEHOLDER_TEXT:
122       {
123         if( impl.mController )
124         {
125           //impl.mController->SetPlaceholderText( value.Get< std::string >() ); TODO
126         }
127         break;
128       }
129       case Toolkit::TextField::Property::TEXT:
130       {
131         if( impl.mController )
132         {
133           impl.mController->SetText( value.Get< std::string >() );
134         }
135         break;
136       }
137       case Toolkit::TextField::Property::FONT_FAMILY:
138       {
139         if( impl.mController )
140         {
141           std::string fontFamily = value.Get< std::string >();
142
143           if( impl.mController->GetDefaultFontFamily() != fontFamily )
144           {
145             impl.mController->SetDefaultFontFamily( fontFamily );
146             impl.RequestTextRelayout();
147           }
148         }
149         break;
150       }
151       case Toolkit::TextField::Property::FONT_STYLE:
152       {
153         if( impl.mController )
154         {
155           std::string fontStyle = value.Get< std::string >();
156
157           if( impl.mController->GetDefaultFontStyle() != fontStyle )
158           {
159             impl.mController->SetDefaultFontStyle( fontStyle );
160             impl.RequestTextRelayout();
161           }
162         }
163         break;
164       }
165       case Toolkit::TextField::Property::POINT_SIZE:
166       {
167         if( impl.mController )
168         {
169           float pointSize = value.Get< float >();
170
171           if( impl.mController->GetDefaultPointSize() != pointSize /*TODO - epsilon*/ )
172           {
173             impl.mController->SetDefaultPointSize( pointSize );
174             impl.RequestTextRelayout();
175           }
176         }
177         break;
178       }
179       case Toolkit::TextField::Property::EXCEED_POLICY:
180       {
181         impl.mExceedPolicy = value.Get< int >();
182         break;
183       }
184       case Toolkit::TextField::Property::CURSOR_IMAGE:
185       {
186         ResourceImage image = ResourceImage::New( value.Get< std::string >() );
187
188         if( impl.mDecorator )
189         {
190           impl.mDecorator->SetCursorImage( image );
191         }
192         break;
193       }
194       case Toolkit::TextField::Property::PRIMARY_CURSOR_COLOR:
195       {
196         if( impl.mDecorator )
197         {
198           impl.mDecorator->SetColor( PRIMARY_CURSOR, value.Get< Vector4 >() );
199         }
200         break;
201       }
202       case Toolkit::TextField::Property::SECONDARY_CURSOR_COLOR:
203       {
204         if( impl.mDecorator )
205         {
206           impl.mDecorator->SetColor( SECONDARY_CURSOR, value.Get< Vector4 >() );
207         }
208         break;
209       }
210       case Toolkit::TextField::Property::ENABLE_CURSOR_BLINK:
211       {
212         if( impl.mController )
213         {
214           //impl.mController->SetEnableCursorBlink( value.Get< bool >() ); TODO
215         }
216         break;
217       }
218       case Toolkit::TextField::Property::CURSOR_BLINK_INTERVAL:
219       {
220         if( impl.mDecorator )
221         {
222           impl.mDecorator->SetCursorBlinkInterval( value.Get< float >() );
223         }
224         break;
225       }
226       case Toolkit::TextField::Property::CURSOR_BLINK_DURATION:
227       {
228         if( impl.mDecorator )
229         {
230           impl.mDecorator->SetCursorBlinkDuration( value.Get< float >() );
231         }
232         break;
233       }
234       case Toolkit::TextField::Property::GRAB_HANDLE_IMAGE:
235       {
236         ResourceImage image = ResourceImage::New( value.Get< std::string >() );
237
238         if( impl.mDecorator )
239         {
240           impl.mDecorator->SetGrabHandleImage( image );
241         }
242         break;
243       }
244       case Toolkit::TextField::Property::DECORATION_BOUNDING_BOX:
245       {
246         if( impl.mDecorator )
247         {
248           impl.mDecorator->SetBoundingBox( value.Get< Rect<int> >() );
249         }
250         break;
251       }
252     }
253   }
254 }
255
256 Property::Value TextField::GetProperty( BaseObject* object, Property::Index index )
257 {
258   Property::Value value;
259
260   Toolkit::TextField textField = Toolkit::TextField::DownCast( Dali::BaseHandle( object ) );
261
262   if( textField )
263   {
264     TextField& impl( GetImpl( textField ) );
265
266     switch( index )
267     {
268       case Toolkit::TextField::Property::RENDERING_BACKEND:
269       {
270         value = impl.mRenderingBackend;
271         break;
272       }
273       case Toolkit::TextField::Property::PLACEHOLDER_TEXT:
274       {
275         if( impl.mController )
276         {
277           std::string text;
278           impl.mController->GetPlaceholderText( text );
279           value = text;
280         }
281         break;
282       }
283       case Toolkit::TextField::Property::TEXT:
284       {
285         if( impl.mController )
286         {
287           std::string text;
288           impl.mController->GetText( text );
289           value = text;
290         }
291         break;
292       }
293       case Toolkit::TextField::Property::EXCEED_POLICY:
294       {
295         value = impl.mExceedPolicy;
296         break;
297       }
298       case Toolkit::TextField::Property::CURSOR_IMAGE:
299       {
300         if( impl.mDecorator )
301         {
302           ResourceImage image = ResourceImage::DownCast( impl.mDecorator->GetCursorImage() );
303           if( image )
304           {
305             value = image.GetUrl();
306           }
307         }
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(); TODO
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::GRAB_HANDLE_IMAGE:
348       {
349         if( impl.mDecorator )
350         {
351           ResourceImage image = ResourceImage::DownCast( impl.mDecorator->GetCursorImage() );
352           if( image )
353           {
354             value = image.GetUrl();
355           }
356         }
357         break;
358       }
359       case Toolkit::TextField::Property::DECORATION_BOUNDING_BOX:
360       {
361         if( impl.mDecorator )
362         {
363           value = impl.mDecorator->GetBoundingBox();
364         }
365         break;
366       }
367     }
368   }
369
370   return value;
371 }
372
373 void TextField::OnInitialize()
374 {
375   mController = Text::Controller::New( *this );
376
377   mDecorator = Text::Decorator::New( *this, *mController );
378
379   mController->GetLayoutEngine().SetLayout( LayoutEngine::SINGLE_LINE_BOX );
380
381   mController->EnableTextInput( mDecorator );
382
383   // Forward input events to controller
384   EnableGestureDetection(Gesture::Tap);
385   GetTapGestureDetector().SetMaximumTapsRequired( 2 );
386
387   // Set BoundingBox to stage size if not already set.
388   if ( mDecorator->GetBoundingBox().IsEmpty() )
389   {
390     Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
391     mDecorator->SetBoundingBox( Rect<int>( 0.0f, 0.0f, stageSize.width, stageSize.height ) );
392   }
393 }
394
395 Vector3 TextField::GetNaturalSize()
396 {
397   return mController->GetNaturalSize();
398 }
399
400 float TextField::GetHeightForWidth( float width )
401 {
402   return mController->GetHeightForWidth( width );
403 }
404
405 void TextField::OnRelayout( const Vector2& size, ActorSizeContainer& container )
406 {
407   if( mController->Relayout( size ) ||
408       !mRenderer )
409   {
410     if( mDecorator )
411     {
412       mDecorator->Relayout( size );
413     }
414
415     if( !mRenderer )
416     {
417       mRenderer = Backend::Get().NewRenderer( mRenderingBackend );
418     }
419
420     RenderableActor renderableActor;
421     if( mRenderer )
422     {
423       renderableActor = mRenderer->Render( mController->GetView() );
424     }
425
426     EnableClipping( (Dali::Toolkit::TextField::EXCEED_POLICY_CLIP == mExceedPolicy), size );
427
428     if( renderableActor != mRenderableActor )
429     {
430       UnparentAndReset( mRenderableActor );
431       mRenderableActor = renderableActor;
432     }
433
434     if( mRenderableActor )
435     {
436       // Make sure the actor is parented correctly with/without clipping
437       if( mClipper )
438       {
439         mClipper->GetRootActor().Add( mRenderableActor );
440       }
441       else
442       {
443         Self().Add( mRenderableActor );
444       }
445     }
446   }
447 }
448
449 void TextField::OnTap( const TapGesture& gesture )
450 {
451   SetKeyInputFocus();
452
453   mController->TapEvent( gesture.numberOfTaps, gesture.localPoint.x, gesture.localPoint.y );
454 }
455
456 bool TextField::OnKeyEvent( const KeyEvent& event )
457 {
458   if( Dali::DALI_KEY_ESCAPE == event.keyCode )
459   {
460     ClearKeyInputFocus();
461   }
462
463   return mController->KeyEvent( event );
464 }
465
466 void TextField::RequestTextRelayout()
467 {
468   RelayoutRequest();
469 }
470
471 void TextField::EnableClipping( bool clipping, const Vector2& size )
472 {
473   if( clipping )
474   {
475     // Not worth to created clip actor if width or height is equal to zero.
476     if( size.width > Math::MACHINE_EPSILON_1000 && size.height > Math::MACHINE_EPSILON_1000 )
477     {
478       if( !mClipper )
479       {
480         Actor self = Self();
481
482         mClipper = Clipper::New( size );
483         self.Add( mClipper->GetRootActor() );
484         self.Add( mClipper->GetImageActor() );
485       }
486       else if ( mClipper )
487       {
488         mClipper->Refresh( size );
489       }
490     }
491   }
492   else
493   {
494     // Note - this will automatically remove the root & image actors
495     mClipper.Reset();
496   }
497 }
498
499 TextField::TextField()
500 : Control( ControlBehaviour( CONTROL_BEHAVIOUR_NONE ) ),
501   mRenderingBackend( DEFAULT_RENDERING_BACKEND ),
502   mExceedPolicy( Dali::Toolkit::TextField::EXCEED_POLICY_CLIP )
503 {
504 }
505
506 TextField::~TextField()
507 {
508   mClipper.Reset();
509 }
510
511 } // namespace Internal
512
513 } // namespace Toolkit
514
515 } // namespace Dali