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