Merge "Adding TextField Popup behaviour to (Programming) Guide" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / buttons / push-button-impl.cpp
1 /*
2  * Copyright (c) 2014 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 "push-button-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/actors/image-actor.h>
23 #include <dali/public-api/object/type-registry.h>
24 #include <dali/devel-api/object/type-registry-helper.h>
25 #include <dali/public-api/images/resource-image.h>
26 #include <dali/devel-api/scripting/scripting.h>
27
28 // INTERNAL INCLUDES
29 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
30 #include <dali-toolkit/public-api/controls/text-controls/text-label.h>
31
32 namespace Dali
33 {
34
35 namespace Toolkit
36 {
37
38 namespace Internal
39 {
40
41 namespace
42 {
43
44 const float   ANIMATION_TIME( 0.2f );
45 const Padding DEFAULT_LABEL_PADDING( 12.0f, 12.0f, 12.0f, 12.0f );
46 const Padding DEFAULT_ICON_PADDING( 12.0f, 12.0f, 12.0f, 12.0f );
47
48 BaseHandle Create()
49 {
50   return Toolkit::PushButton::New();
51 }
52
53 // Properties
54
55 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::PushButton, Toolkit::Button, Create )
56
57 DALI_PROPERTY_REGISTRATION( Toolkit, PushButton, "unselectedIcon",  STRING, UNSELECTED_ICON )
58 DALI_PROPERTY_REGISTRATION( Toolkit, PushButton, "selectedIcon",  STRING, SELECTED_ICON )
59 DALI_PROPERTY_REGISTRATION( Toolkit, PushButton, "iconAlignment",  STRING, ICON_ALIGNMENT )
60 DALI_PROPERTY_REGISTRATION( Toolkit, PushButton, "labelPadding",  STRING, LABEL_PADDING )
61 DALI_PROPERTY_REGISTRATION( Toolkit, PushButton, "iconPadding",  STRING, ICON_PADDING )
62
63 DALI_TYPE_REGISTRATION_END()
64
65 /*
66  * Table to define Text-to-enum conversions for IconAlignment.
67  */
68 const Dali::Scripting::StringEnum IconAlignmentTable[] = {
69   { "LEFT",   Toolkit::Internal::PushButton::LEFT },
70   { "RIGHT",  Toolkit::Internal::PushButton::RIGHT },
71   { "TOP",    Toolkit::Internal::PushButton::TOP },
72   { "BOTTOM", Toolkit::Internal::PushButton::BOTTOM },
73 }; const unsigned int IconAlignmentTableCount = sizeof( IconAlignmentTable ) / sizeof( IconAlignmentTable[0] );
74
75 const char* const UNSELECTED_BUTTON_IMAGE_DIR = DALI_IMAGE_DIR "button-up.9.png";
76 const char* const SELECTED_BUTTON_IMAGE_DIR = DALI_IMAGE_DIR "button-down.9.png";
77 const char* const DISABLED_UNSELECTED_BUTTON_IMAGE_DIR = DALI_IMAGE_DIR "button-disabled.9.png";
78 const char* const DISABLED_SELECTED_BUTTON_IMAGE_DIR = DALI_IMAGE_DIR "button-down-disabled.9.png";
79
80 } // unnamed namespace
81
82 namespace
83 {
84
85 /**
86  * Get size of Actor if larger than given size
87  * @param[in] root the actor to get the size of
88  * @param[out] size the greater of the given size or the size of the Actor
89  */
90 void SizeOfActorIfLarger( Actor root, Vector3& size )
91 {
92   if ( root )
93   {
94     // RelayoutSize retreived for Actor to use any padding set to it.
95     size.width = std::max( root.GetRelayoutSize( Dimension::WIDTH ), size.width );
96     size.height = std::max( root.GetRelayoutSize( Dimension::HEIGHT ), size.height );
97   }
98 }
99
100 } // unnamed namespace
101
102 Dali::Toolkit::PushButton PushButton::New()
103 {
104   // Create the implementation, temporarily owned on stack
105   IntrusivePtr< PushButton > internalPushButton = new PushButton();
106
107   // Pass ownership to CustomActor
108   Dali::Toolkit::PushButton pushButton( *internalPushButton );
109
110   // Second-phase init of the implementation
111   // This can only be done after the CustomActor connection has been made...
112   internalPushButton->Initialize();
113
114   return pushButton;
115 }
116
117 PushButton::PushButton()
118 : Button(),
119   mLabelPadding( DEFAULT_LABEL_PADDING ),
120   mIconPadding( DEFAULT_ICON_PADDING ),
121   mIconAlignment( RIGHT ),
122   mSize()
123 {
124   SetAnimationTime( ANIMATION_TIME );
125 }
126
127 PushButton::~PushButton()
128 {
129 }
130
131 void PushButton::OnButtonInitialize()
132 {
133   // Push button requires the Leave event.
134   Actor self = Self();
135   self.SetLeaveRequired( true );
136
137   // Set resize policy to natural size so that buttons will resize to background images
138   self.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
139
140   SetUnselectedImage( UNSELECTED_BUTTON_IMAGE_DIR );
141   SetSelectedImage( SELECTED_BUTTON_IMAGE_DIR );
142   SetDisabledImage( DISABLED_UNSELECTED_BUTTON_IMAGE_DIR );
143   SetDisabledSelectedImage( DISABLED_SELECTED_BUTTON_IMAGE_DIR );
144 }
145
146 void PushButton::SetIcon( DecorationState state, const std::string iconFilename )
147 {
148   mIconName[ state ] = iconFilename;
149   SetDecoration( state, Toolkit::ImageView::New( iconFilename  ) );
150   ConfigureSizeNegotiation();
151 }
152
153 std::string& PushButton::GetIcon( DecorationState state )
154 {
155   return mIconName[ state ];
156 }
157
158 void PushButton::SetIconAlignment( const PushButton::IconAlignment iconAlignment )
159 {
160   mIconAlignment = iconAlignment;
161   ConfigureSizeNegotiation();
162 }
163
164 const PushButton::IconAlignment PushButton::GetIconAlignment() const
165 {
166   return mIconAlignment;
167 }
168
169 void PushButton::SetLabelPadding( const Vector4& padding )
170 {
171   mLabelPadding = Padding( padding.x, padding.y, padding.z, padding.w );
172   ConfigureSizeNegotiation();
173 }
174
175 Vector4 PushButton::GetLabelPadding()
176 {
177   return Vector4( mLabelPadding.left, mLabelPadding.right, mLabelPadding.top, mLabelPadding.bottom );
178 }
179
180 void PushButton::SetIconPadding( const Vector4& padding )
181 {
182   mIconPadding = Padding( padding.x, padding.y, padding.z, padding.w );
183   ConfigureSizeNegotiation();
184 }
185
186 Vector4 PushButton::GetIconPadding()
187 {
188   return Vector4( mIconPadding.left, mIconPadding.right, mIconPadding.top, mIconPadding.bottom );
189 }
190
191 void PushButton::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
192 {
193   Toolkit::PushButton pushButton = Toolkit::PushButton::DownCast( Dali::BaseHandle( object ) );
194
195   if ( pushButton )
196   {
197     PushButton& pushButtonImpl( GetImplementation( pushButton ) );
198
199     switch ( propertyIndex )
200     {
201       case Toolkit::PushButton::Property::UNSELECTED_ICON:
202       {
203         pushButtonImpl.SetIcon( UNSELECTED_DECORATION, value.Get< std::string >() );
204         break;
205       }
206       case Toolkit::PushButton::Property::SELECTED_ICON:
207       {
208         pushButtonImpl.SetIcon( SELECTED_DECORATION, value.Get< std::string >() );
209         break;
210       }
211       case Toolkit::PushButton::Property::ICON_ALIGNMENT:
212       {
213         IconAlignment iconAlignment;
214         if( Scripting::GetEnumeration< IconAlignment >( value.Get< std::string >().c_str(), IconAlignmentTable, IconAlignmentTableCount, iconAlignment ) )
215         {
216           pushButtonImpl.SetIconAlignment( iconAlignment );
217         }
218         break;
219       }
220       case Toolkit::PushButton::Property::LABEL_PADDING:
221       {
222         pushButtonImpl.SetLabelPadding( value.Get< Vector4 >() );
223         break;
224       }
225       case Toolkit::PushButton::Property::ICON_PADDING:
226       {
227         pushButtonImpl.SetIconPadding( value.Get< Vector4 >() );
228         break;
229       }
230     }
231   }
232 }
233
234 Property::Value PushButton::GetProperty( BaseObject* object, Property::Index propertyIndex )
235 {
236   Property::Value value;
237
238   Toolkit::PushButton pushButton = Toolkit::PushButton::DownCast( Dali::BaseHandle( object ) );
239
240   if ( pushButton )
241   {
242     PushButton& pushButtonImpl( GetImplementation( pushButton ) );
243
244     switch ( propertyIndex )
245     {
246       case Toolkit::PushButton::Property::UNSELECTED_ICON:
247       {
248         value = pushButtonImpl.GetIcon( UNSELECTED_DECORATION );
249         break;
250       }
251       case Toolkit::PushButton::Property::SELECTED_ICON:
252       {
253         value = pushButtonImpl.GetIcon( UNSELECTED_DECORATION );
254         break;
255       }
256       case Toolkit::PushButton::Property::ICON_ALIGNMENT:
257       {
258         value = Scripting::GetLinearEnumerationName< IconAlignment >( pushButtonImpl.GetIconAlignment(), IconAlignmentTable, IconAlignmentTableCount );
259         break;
260       }
261       case Toolkit::PushButton::Property::LABEL_PADDING:
262       {
263         value = pushButtonImpl.GetLabelPadding();
264         break;
265       }
266       case Toolkit::PushButton::Property::ICON_PADDING:
267       {
268         value = pushButtonImpl.GetIconPadding();
269         break;
270       }
271     }
272   }
273
274   return value;
275 }
276
277 void PushButton::OnLabelSet( bool noPadding )
278 {
279   Actor& label = GetLabelActor();
280
281   if( label )
282   {
283     if( noPadding )
284     {
285       mLabelPadding = Padding( 0.0f, 0.0f, 0.0f, 0.0f );
286     }
287
288     Toolkit::TextLabel textLabel = Toolkit::TextLabel::DownCast( label );
289     if( textLabel )
290     {
291       textLabel.SetProperty( Toolkit::TextLabel::Property::MULTI_LINE, false );
292     }
293   }
294   ConfigureSizeNegotiation();
295 }
296
297 void PushButton::OnButtonImageSet()
298 {
299   ConfigureSizeNegotiation();
300 }
301
302 void PushButton::OnSelectedImageSet()
303 {
304   ConfigureSizeNegotiation();
305 }
306
307 void PushButton::OnBackgroundImageSet()
308 {
309   ConfigureSizeNegotiation();
310 }
311
312 void PushButton::OnSelectedBackgroundImageSet()
313 {
314   ConfigureSizeNegotiation();
315 }
316
317 void PushButton::OnDisabledImageSet()
318 {
319   ConfigureSizeNegotiation();
320 }
321
322 void PushButton::OnDisabledSelectedImageSet()
323 {
324   ConfigureSizeNegotiation();
325 }
326
327 void PushButton::OnDisabledBackgroundImageSet()
328 {
329   ConfigureSizeNegotiation();
330 }
331
332 void PushButton::OnSizeSet( const Vector3& targetSize )
333 {
334   if( targetSize != mSize )
335   {
336     mSize = targetSize;
337
338     Actor& label = GetLabelActor();
339
340     if( label )
341     {
342       label.SetSize( mSize );
343     }
344   }
345 }
346
347 void PushButton::PrepareForTranstionIn( Actor actor )
348 {
349   actor.SetOpacity( 0.0f );
350 }
351
352 void PushButton::PrepareForTranstionOut( Actor actor )
353 {
354   actor.SetOpacity( 1.0f );
355 }
356
357 void PushButton::OnTransitionIn( Actor actor )
358 {
359   FadeImageTo( actor, 1.f );
360 }
361
362 void PushButton::OnTransitionOut( Actor actor )
363 {
364   FadeImageTo( actor, 0.0f );
365 }
366
367 void PushButton::FadeImageTo( Actor actor, float opacity )
368 {
369   if( actor )
370   {
371     Dali::Animation transitionAnimation = GetTransitionAnimation();
372     DALI_ASSERT_DEBUG( transitionAnimation );
373
374     if( transitionAnimation )
375     {
376       transitionAnimation.AnimateTo( Property( actor, Actor::Property::COLOR_ALPHA ), opacity );
377     }
378   }
379 }
380
381 Vector3 PushButton::GetNaturalSize()
382 {
383   Vector3 size;
384
385   // If label, test against it's size
386   Toolkit::TextLabel label = Toolkit::TextLabel::DownCast( GetLabelActor() );
387
388   Actor icon = GetDecoration( UNSELECTED_DECORATION );
389   if( label || icon )
390   {
391     Vector3 labelSize( Vector3::ZERO );
392     Vector3 iconSize( Vector3::ZERO );
393
394     if( label )
395     {
396       Vector3 labelNaturalSize = label.GetNaturalSize();
397       labelSize.width = labelNaturalSize.width + mLabelPadding.left + mLabelPadding.right;
398       labelSize.height = labelNaturalSize.height + mLabelPadding.top + mLabelPadding.bottom;
399     }
400
401     if( icon )
402     {
403       Vector3 iconNaturalSize = icon.GetNaturalSize();
404       iconSize.width = iconNaturalSize.width + mIconPadding.left + mIconPadding.right;
405       iconSize.height = iconNaturalSize.height + mIconPadding.top + mIconPadding.bottom;
406
407       switch( mIconAlignment )
408       {
409         case LEFT:
410         case RIGHT:
411         {
412           size.width = labelSize.width + iconSize.width;
413           size.height = std::max( labelSize.height, iconSize.height );
414           break;
415         }
416         case TOP:
417         case BOTTOM:
418         {
419           size.width = std::max( labelSize.width, iconSize.width );
420           size.height = labelSize.height + iconSize.height;
421           break;
422         }
423       }
424     }
425     else
426     {
427       // No icon, so size is the same as label size.
428       // (If there is no label this is zero).
429       size = labelSize;
430     }
431   }
432   else
433   {
434     // Check Image and Background image and use the largest size as the control's Natural size.
435     SizeOfActorIfLarger( GetUnselectedImage(), size );
436     SizeOfActorIfLarger( GetBackgroundImage(), size );
437   }
438
439   return size;
440 }
441
442 void PushButton::OnSetResizePolicy( ResizePolicy::Type policy, Dimension::Type dimension )
443 {
444   ConfigureSizeNegotiation();
445 }
446
447 void PushButton::ConfigureSizeNegotiation()
448 {
449   std::vector< Actor > images;
450   images.reserve( 7 );
451
452   images.push_back( GetUnselectedImage() );
453   images.push_back( GetSelectedImage() );
454   images.push_back( GetSelectedBackgroundImage() );
455   images.push_back( GetBackgroundImage() );
456   images.push_back( GetDisabledImage() );
457   images.push_back( GetDisabledSelectedImage() );
458   images.push_back( GetDisabledBackgroundImage() );
459
460   Actor label = GetLabelActor();
461
462   for( unsigned int i = 0; i < Dimension::DIMENSION_COUNT; ++i )
463   {
464     ConfigureSizeNegotiationDimension( static_cast< Dimension::Type >( 1 << i ), images, label );
465   }
466
467   // Add any vertical padding directly to the actors.
468   Actor icon = GetDecoration( UNSELECTED_DECORATION );
469   Actor selectedIcon = GetDecoration( SELECTED_DECORATION );
470   bool iconExists = icon || selectedIcon;
471
472   if( label )
473   {
474     label.SetPadding( mLabelPadding );
475   }
476   if( icon )
477   {
478     icon.SetPadding( mIconPadding );
479   }
480   if( selectedIcon )
481   {
482     selectedIcon.SetPadding( mIconPadding );
483   }
484
485   // Calculate and apply horizontal alignments and offsets
486   // to text and icon (depending on existence).
487   Vector3 iconPosition( Vector3::ZERO );
488   Vector3 labelPosition( Vector3::ZERO );
489   Vector3 iconAnchoring( AnchorPoint::CENTER );
490   Vector3 labelAnchoring( AnchorPoint::CENTER );
491   std::string horizontalLabelAlignment = "CENTER";
492   std::string verticalLabelAlignment = "CENTER";
493
494   if( iconExists && label )
495   {
496     // There is an icon and a label to lay out.
497     switch( mIconAlignment )
498     {
499       case LEFT:
500       {
501         iconPosition.x = mIconPadding.left;
502         labelPosition.x = -mLabelPadding.right;
503         iconAnchoring = AnchorPoint::CENTER_LEFT;
504         labelAnchoring = AnchorPoint::CENTER_RIGHT;
505         horizontalLabelAlignment = "END";
506         break;
507       }
508       case RIGHT:
509       {
510         iconPosition.x = -mIconPadding.right;
511         labelPosition.x = mLabelPadding.left;
512         iconAnchoring = AnchorPoint::CENTER_RIGHT;
513         labelAnchoring = AnchorPoint::CENTER_LEFT;
514         horizontalLabelAlignment = "BEGIN";
515         break;
516       }
517       case TOP:
518       {
519         iconPosition.y = mIconPadding.top;
520         labelPosition.y = -mLabelPadding.bottom;
521         iconAnchoring = AnchorPoint::TOP_CENTER;
522         labelAnchoring = AnchorPoint::BOTTOM_CENTER;
523         verticalLabelAlignment = "BOTTOM";
524         break;
525       }
526       case BOTTOM:
527       {
528         iconPosition.y = -mIconPadding.bottom;
529         labelPosition.y = mLabelPadding.top;
530         iconAnchoring = AnchorPoint::BOTTOM_CENTER;
531         labelAnchoring = AnchorPoint::TOP_CENTER;
532         verticalLabelAlignment = "TOP";
533         break;
534       }
535     }
536   }
537
538   // Note: If there is only an icon, or only a label, the default values are now correct.
539   // Setup the icon(s) with the precalculated values.
540   if( icon )
541   {
542     icon.SetPosition( iconPosition );
543     icon.SetParentOrigin( iconAnchoring );
544     icon.SetAnchorPoint( iconAnchoring );
545   }
546   if( selectedIcon )
547   {
548     selectedIcon.SetPosition( iconPosition );
549     selectedIcon.SetParentOrigin( iconAnchoring );
550     selectedIcon.SetAnchorPoint( iconAnchoring );
551   }
552
553   // Setup the label.
554   if( label )
555   {
556     label.SetPosition( labelPosition );
557     label.SetParentOrigin( labelAnchoring );
558     label.SetAnchorPoint( labelAnchoring );
559     label.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, horizontalLabelAlignment );
560     label.SetProperty( Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, verticalLabelAlignment );
561   }
562
563   RelayoutRequest();
564 }
565
566
567 void PushButton::ConfigureSizeNegotiationDimension( Dimension::Type dimension, const std::vector< Actor >& images, Actor& label )
568 {
569   ResizePolicy::Type imageResizePolicy = ResizePolicy::FILL_TO_PARENT;
570   ResizePolicy::Type labelResizePolicy = ResizePolicy::FILL_TO_PARENT;
571
572   ResizePolicy::Type resizePolicy = Self().GetResizePolicy( dimension );
573
574   if( resizePolicy == ResizePolicy::FIT_TO_CHILDREN || resizePolicy == ResizePolicy::USE_NATURAL_SIZE )
575   {
576     if( label )
577     {
578       labelResizePolicy = ResizePolicy::USE_NATURAL_SIZE;
579     }
580     else
581     {
582       imageResizePolicy = ResizePolicy::USE_NATURAL_SIZE;
583     }
584   }
585
586   if( label )
587   {
588     label.SetResizePolicy( labelResizePolicy, dimension );
589   }
590
591   for( std::vector< Actor >::const_iterator it = images.begin(), itEnd = images.end(); it != itEnd; ++it )
592   {
593     Actor actor = *it;
594     if( actor )
595     {
596       actor.SetResizePolicy( imageResizePolicy, dimension );
597     }
598   }
599 }
600
601
602 } // namespace Internal
603
604 } // namespace Toolkit
605
606 } // namespace Dali