Merge "Stop using ImageActor in PageTurnView" 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/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::OnInitialize()
131 {
132   Button::OnInitialize();
133
134   // Push button requires the Leave event.
135   Actor self = Self();
136   self.SetLeaveRequired( true );
137
138   // Set resize policy to natural size so that buttons will resize to background images
139   self.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
140
141   SetUnselectedImage( UNSELECTED_BUTTON_IMAGE_DIR );
142   SetSelectedImage( SELECTED_BUTTON_IMAGE_DIR );
143   SetDisabledImage( DISABLED_UNSELECTED_BUTTON_IMAGE_DIR );
144   SetDisabledSelectedImage( DISABLED_SELECTED_BUTTON_IMAGE_DIR );
145 }
146
147 void PushButton::SetIcon( DecorationState state, const std::string iconFilename )
148 {
149   mIconName[ state ] = iconFilename;
150   SetDecoration( state, Toolkit::ImageView::New( iconFilename  ) );
151   ConfigureSizeNegotiation();
152 }
153
154 std::string& PushButton::GetIcon( DecorationState state )
155 {
156   return mIconName[ state ];
157 }
158
159 void PushButton::SetIconAlignment( const PushButton::IconAlignment iconAlignment )
160 {
161   mIconAlignment = iconAlignment;
162   ConfigureSizeNegotiation();
163 }
164
165 const PushButton::IconAlignment PushButton::GetIconAlignment() const
166 {
167   return mIconAlignment;
168 }
169
170 void PushButton::SetLabelPadding( const Vector4& padding )
171 {
172   mLabelPadding = Padding( padding.x, padding.y, padding.z, padding.w );
173   ConfigureSizeNegotiation();
174 }
175
176 Vector4 PushButton::GetLabelPadding()
177 {
178   return Vector4( mLabelPadding.left, mLabelPadding.right, mLabelPadding.top, mLabelPadding.bottom );
179 }
180
181 void PushButton::SetIconPadding( const Vector4& padding )
182 {
183   mIconPadding = Padding( padding.x, padding.y, padding.z, padding.w );
184   ConfigureSizeNegotiation();
185 }
186
187 Vector4 PushButton::GetIconPadding()
188 {
189   return Vector4( mIconPadding.left, mIconPadding.right, mIconPadding.top, mIconPadding.bottom );
190 }
191
192 void PushButton::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
193 {
194   Toolkit::PushButton pushButton = Toolkit::PushButton::DownCast( Dali::BaseHandle( object ) );
195
196   if ( pushButton )
197   {
198     PushButton& pushButtonImpl( GetImplementation( pushButton ) );
199
200     switch ( propertyIndex )
201     {
202       case Toolkit::PushButton::Property::UNSELECTED_ICON:
203       {
204         pushButtonImpl.SetIcon( UNSELECTED_DECORATION, value.Get< std::string >() );
205         break;
206       }
207       case Toolkit::PushButton::Property::SELECTED_ICON:
208       {
209         pushButtonImpl.SetIcon( SELECTED_DECORATION, value.Get< std::string >() );
210         break;
211       }
212       case Toolkit::PushButton::Property::ICON_ALIGNMENT:
213       {
214         IconAlignment iconAlignment;
215         if( Scripting::GetEnumeration< IconAlignment >( value.Get< std::string >().c_str(), IconAlignmentTable, IconAlignmentTableCount, iconAlignment ) )
216         {
217           pushButtonImpl.SetIconAlignment( iconAlignment );
218         }
219         break;
220       }
221       case Toolkit::PushButton::Property::LABEL_PADDING:
222       {
223         pushButtonImpl.SetLabelPadding( value.Get< Vector4 >() );
224         break;
225       }
226       case Toolkit::PushButton::Property::ICON_PADDING:
227       {
228         pushButtonImpl.SetIconPadding( value.Get< Vector4 >() );
229         break;
230       }
231     }
232   }
233 }
234
235 Property::Value PushButton::GetProperty( BaseObject* object, Property::Index propertyIndex )
236 {
237   Property::Value value;
238
239   Toolkit::PushButton pushButton = Toolkit::PushButton::DownCast( Dali::BaseHandle( object ) );
240
241   if ( pushButton )
242   {
243     PushButton& pushButtonImpl( GetImplementation( pushButton ) );
244
245     switch ( propertyIndex )
246     {
247       case Toolkit::PushButton::Property::UNSELECTED_ICON:
248       {
249         value = pushButtonImpl.GetIcon( UNSELECTED_DECORATION );
250         break;
251       }
252       case Toolkit::PushButton::Property::SELECTED_ICON:
253       {
254         value = pushButtonImpl.GetIcon( UNSELECTED_DECORATION );
255         break;
256       }
257       case Toolkit::PushButton::Property::ICON_ALIGNMENT:
258       {
259         value = Scripting::GetLinearEnumerationName< IconAlignment >( pushButtonImpl.GetIconAlignment(), IconAlignmentTable, IconAlignmentTableCount );
260         break;
261       }
262       case Toolkit::PushButton::Property::LABEL_PADDING:
263       {
264         value = pushButtonImpl.GetLabelPadding();
265         break;
266       }
267       case Toolkit::PushButton::Property::ICON_PADDING:
268       {
269         value = pushButtonImpl.GetIconPadding();
270         break;
271       }
272     }
273   }
274
275   return value;
276 }
277
278 void PushButton::OnLabelSet( bool noPadding )
279 {
280   Actor& label = GetLabelActor();
281
282   if( label )
283   {
284     if( noPadding )
285     {
286       mLabelPadding = Padding( 0.0f, 0.0f, 0.0f, 0.0f );
287     }
288
289     Toolkit::TextLabel textLabel = Toolkit::TextLabel::DownCast( label );
290     if( textLabel )
291     {
292       textLabel.SetProperty( Toolkit::TextLabel::Property::MULTI_LINE, false );
293     }
294   }
295   ConfigureSizeNegotiation();
296 }
297
298 void PushButton::OnButtonImageSet()
299 {
300   ConfigureSizeNegotiation();
301 }
302
303 void PushButton::OnSelectedImageSet()
304 {
305   ConfigureSizeNegotiation();
306 }
307
308 void PushButton::OnBackgroundImageSet()
309 {
310   ConfigureSizeNegotiation();
311 }
312
313 void PushButton::OnSelectedBackgroundImageSet()
314 {
315   ConfigureSizeNegotiation();
316 }
317
318 void PushButton::OnDisabledImageSet()
319 {
320   ConfigureSizeNegotiation();
321 }
322
323 void PushButton::OnDisabledSelectedImageSet()
324 {
325   ConfigureSizeNegotiation();
326 }
327
328 void PushButton::OnDisabledBackgroundImageSet()
329 {
330   ConfigureSizeNegotiation();
331 }
332
333 void PushButton::OnSizeSet( const Vector3& targetSize )
334 {
335   if( targetSize != mSize )
336   {
337     mSize = targetSize;
338
339     Actor& label = GetLabelActor();
340
341     if( label )
342     {
343       label.SetSize( mSize );
344     }
345   }
346 }
347
348 void PushButton::PrepareForTranstionIn( Actor actor )
349 {
350   actor.SetOpacity( 0.0f );
351 }
352
353 void PushButton::PrepareForTranstionOut( Actor actor )
354 {
355   actor.SetOpacity( 1.0f );
356 }
357
358 void PushButton::OnTransitionIn( Actor actor )
359 {
360   FadeImageTo( actor, 1.f );
361 }
362
363 void PushButton::OnTransitionOut( Actor actor )
364 {
365   FadeImageTo( actor, 0.0f );
366 }
367
368 void PushButton::FadeImageTo( Actor actor, float opacity )
369 {
370   if( actor )
371   {
372     Dali::Animation transitionAnimation = GetTransitionAnimation();
373     DALI_ASSERT_DEBUG( transitionAnimation );
374
375     if( transitionAnimation )
376     {
377       transitionAnimation.AnimateTo( Property( actor, Actor::Property::COLOR_ALPHA ), opacity );
378     }
379   }
380 }
381
382 Vector3 PushButton::GetNaturalSize()
383 {
384   Vector3 size;
385
386   // If label, test against it's size
387   Toolkit::TextLabel label = Toolkit::TextLabel::DownCast( GetLabelActor() );
388
389   Actor icon = GetDecoration( UNSELECTED_DECORATION );
390   if( label || icon )
391   {
392     Vector3 labelSize( Vector3::ZERO );
393     Vector3 iconSize( Vector3::ZERO );
394
395     if( label )
396     {
397       Vector3 labelNaturalSize = label.GetNaturalSize();
398       labelSize.width = labelNaturalSize.width + mLabelPadding.left + mLabelPadding.right;
399       labelSize.height = labelNaturalSize.height + mLabelPadding.top + mLabelPadding.bottom;
400     }
401
402     if( icon )
403     {
404       Vector3 iconNaturalSize = icon.GetNaturalSize();
405       iconSize.width = iconNaturalSize.width + mIconPadding.left + mIconPadding.right;
406       iconSize.height = iconNaturalSize.height + mIconPadding.top + mIconPadding.bottom;
407
408       switch( mIconAlignment )
409       {
410         case LEFT:
411         case RIGHT:
412         {
413           size.width = labelSize.width + iconSize.width;
414           size.height = std::max( labelSize.height, iconSize.height );
415           break;
416         }
417         case TOP:
418         case BOTTOM:
419         {
420           size.width = std::max( labelSize.width, iconSize.width );
421           size.height = labelSize.height + iconSize.height;
422           break;
423         }
424       }
425     }
426     else
427     {
428       // No icon, so size is the same as label size.
429       // (If there is no label this is zero).
430       size = labelSize;
431     }
432   }
433   else
434   {
435     // Check Image and Background image and use the largest size as the control's Natural size.
436     SizeOfActorIfLarger( GetUnselectedImage(), size );
437     SizeOfActorIfLarger( GetBackgroundImage(), size );
438   }
439
440   return size;
441 }
442
443 void PushButton::OnSetResizePolicy( ResizePolicy::Type policy, Dimension::Type dimension )
444 {
445   ConfigureSizeNegotiation();
446 }
447
448 void PushButton::ConfigureSizeNegotiation()
449 {
450   std::vector< Actor > images;
451   images.reserve( 7 );
452
453   images.push_back( GetUnselectedImage() );
454   images.push_back( GetSelectedImage() );
455   images.push_back( GetSelectedBackgroundImage() );
456   images.push_back( GetBackgroundImage() );
457   images.push_back( GetDisabledImage() );
458   images.push_back( GetDisabledSelectedImage() );
459   images.push_back( GetDisabledBackgroundImage() );
460
461   Actor label = GetLabelActor();
462
463   for( unsigned int i = 0; i < Dimension::DIMENSION_COUNT; ++i )
464   {
465     ConfigureSizeNegotiationDimension( static_cast< Dimension::Type >( 1 << i ), images, label );
466   }
467
468   // Add any vertical padding directly to the actors.
469   Actor icon = GetDecoration( UNSELECTED_DECORATION );
470   Actor selectedIcon = GetDecoration( SELECTED_DECORATION );
471   bool iconExists = icon || selectedIcon;
472
473   if( label )
474   {
475     label.SetPadding( mLabelPadding );
476   }
477   if( icon )
478   {
479     icon.SetPadding( mIconPadding );
480   }
481   if( selectedIcon )
482   {
483     selectedIcon.SetPadding( mIconPadding );
484   }
485
486   // Calculate and apply horizontal alignments and offsets
487   // to text and icon (depending on existence).
488   Vector3 iconPosition( Vector3::ZERO );
489   Vector3 labelPosition( Vector3::ZERO );
490   Vector3 iconAnchoring( AnchorPoint::CENTER );
491   Vector3 labelAnchoring( AnchorPoint::CENTER );
492   std::string horizontalLabelAlignment = "CENTER";
493   std::string verticalLabelAlignment = "CENTER";
494
495   if( iconExists && label )
496   {
497     // There is an icon and a label to lay out.
498     switch( mIconAlignment )
499     {
500       case LEFT:
501       {
502         iconPosition.x = mIconPadding.left;
503         labelPosition.x = -mLabelPadding.right;
504         iconAnchoring = AnchorPoint::CENTER_LEFT;
505         labelAnchoring = AnchorPoint::CENTER_RIGHT;
506         horizontalLabelAlignment = "END";
507         break;
508       }
509       case RIGHT:
510       {
511         iconPosition.x = -mIconPadding.right;
512         labelPosition.x = mLabelPadding.left;
513         iconAnchoring = AnchorPoint::CENTER_RIGHT;
514         labelAnchoring = AnchorPoint::CENTER_LEFT;
515         horizontalLabelAlignment = "BEGIN";
516         break;
517       }
518       case TOP:
519       {
520         iconPosition.y = mIconPadding.top;
521         labelPosition.y = -mLabelPadding.bottom;
522         iconAnchoring = AnchorPoint::TOP_CENTER;
523         labelAnchoring = AnchorPoint::BOTTOM_CENTER;
524         verticalLabelAlignment = "BOTTOM";
525         break;
526       }
527       case BOTTOM:
528       {
529         iconPosition.y = -mIconPadding.bottom;
530         labelPosition.y = mLabelPadding.top;
531         iconAnchoring = AnchorPoint::BOTTOM_CENTER;
532         labelAnchoring = AnchorPoint::TOP_CENTER;
533         verticalLabelAlignment = "TOP";
534         break;
535       }
536     }
537   }
538
539   // Note: If there is only an icon, or only a label, the default values are now correct.
540   // Setup the icon(s) with the precalculated values.
541   if( icon )
542   {
543     icon.SetPosition( iconPosition );
544     icon.SetParentOrigin( iconAnchoring );
545     icon.SetAnchorPoint( iconAnchoring );
546   }
547   if( selectedIcon )
548   {
549     selectedIcon.SetPosition( iconPosition );
550     selectedIcon.SetParentOrigin( iconAnchoring );
551     selectedIcon.SetAnchorPoint( iconAnchoring );
552   }
553
554   // Setup the label.
555   if( label )
556   {
557     label.SetPosition( labelPosition );
558     label.SetParentOrigin( labelAnchoring );
559     label.SetAnchorPoint( labelAnchoring );
560     label.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, horizontalLabelAlignment );
561     label.SetProperty( Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, verticalLabelAlignment );
562   }
563
564   RelayoutRequest();
565 }
566
567
568 void PushButton::ConfigureSizeNegotiationDimension( Dimension::Type dimension, const std::vector< Actor >& images, Actor& label )
569 {
570   ResizePolicy::Type imageResizePolicy = ResizePolicy::FILL_TO_PARENT;
571   ResizePolicy::Type labelResizePolicy = ResizePolicy::FILL_TO_PARENT;
572
573   ResizePolicy::Type resizePolicy = Self().GetResizePolicy( dimension );
574
575   if( resizePolicy == ResizePolicy::FIT_TO_CHILDREN || resizePolicy == ResizePolicy::USE_NATURAL_SIZE )
576   {
577     if( label )
578     {
579       labelResizePolicy = ResizePolicy::USE_NATURAL_SIZE;
580     }
581     else
582     {
583       imageResizePolicy = ResizePolicy::USE_NATURAL_SIZE;
584     }
585   }
586
587   if( label )
588   {
589     label.SetResizePolicy( labelResizePolicy, dimension );
590   }
591
592   for( std::vector< Actor >::const_iterator it = images.begin(), itEnd = images.end(); it != itEnd; ++it )
593   {
594     Actor actor = *it;
595     if( actor )
596     {
597       actor.SetResizePolicy( imageResizePolicy, dimension );
598     }
599   }
600 }
601
602
603 } // namespace Internal
604
605 } // namespace Toolkit
606
607 } // namespace Dali