Merge "Set focusable to true when touched in default." into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / buttons / button-impl.cpp
1 /*
2  * Copyright (c) 2021 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 "button-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/object/property-helper-devel.h>
23 #include <dali/devel-api/scripting/enum-helper.h>
24 #include <dali/devel-api/scripting/scripting.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/public-api/events/touch-event.h>
27 #include <dali/public-api/object/type-registry-helper.h>
28 #include <dali/public-api/object/type-registry.h>
29 #include <dali/public-api/size-negotiation/relayout-container.h>
30 #include <cstring> // for strcmp
31
32 // INTERNAL INCLUDES
33 #include <dali-toolkit/devel-api/controls/buttons/button-devel.h>
34 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
35 #include <dali-toolkit/devel-api/controls/control-devel.h>
36 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
37 #include <dali-toolkit/internal/visuals/text/text-visual.h>
38 #include <dali-toolkit/public-api/align-enumerations.h>
39 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
40 #include <dali-toolkit/public-api/controls/text-controls/text-label.h>
41 #include <dali-toolkit/public-api/visuals/color-visual-properties.h>
42 #include <dali-toolkit/public-api/visuals/image-visual-properties.h>
43 #include <dali-toolkit/public-api/visuals/text-visual-properties.h>
44 #include <dali-toolkit/public-api/visuals/visual-properties.h>
45
46 #if defined(DEBUG_ENABLED)
47 Debug::Filter* gLogButtonFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_BUTTON_CONTROL");
48 #endif
49
50 namespace Dali
51 {
52 namespace Toolkit
53 {
54 namespace Internal
55 {
56 namespace
57 {
58 BaseHandle Create()
59 {
60   // empty handle as we cannot create button (but type registered for clicked signal)
61   return BaseHandle();
62 }
63
64 // Setup properties, signals and actions using the type-registry.
65 DALI_TYPE_REGISTRATION_BEGIN(Toolkit::Button, Toolkit::Control, Create)
66
67 DALI_PROPERTY_REGISTRATION(Toolkit, Button, "disabled", BOOLEAN, DISABLED)
68 DALI_PROPERTY_REGISTRATION(Toolkit, Button, "autoRepeating", BOOLEAN, AUTO_REPEATING)
69 DALI_PROPERTY_REGISTRATION(Toolkit, Button, "initialAutoRepeatingDelay", FLOAT, INITIAL_AUTO_REPEATING_DELAY)
70 DALI_PROPERTY_REGISTRATION(Toolkit, Button, "nextAutoRepeatingDelay", FLOAT, NEXT_AUTO_REPEATING_DELAY)
71 DALI_PROPERTY_REGISTRATION(Toolkit, Button, "togglable", BOOLEAN, TOGGLABLE)
72 DALI_PROPERTY_REGISTRATION(Toolkit, Button, "selected", BOOLEAN, SELECTED)
73 DALI_PROPERTY_REGISTRATION(Toolkit, Button, "unselectedVisual", MAP, UNSELECTED_VISUAL)
74 DALI_PROPERTY_REGISTRATION(Toolkit, Button, "selectedVisual", MAP, SELECTED_VISUAL)
75 DALI_PROPERTY_REGISTRATION(Toolkit, Button, "disabledSelectedVisual", MAP, DISABLED_SELECTED_VISUAL)
76 DALI_PROPERTY_REGISTRATION(Toolkit, Button, "disabledUnselectedVisual", MAP, DISABLED_UNSELECTED_VISUAL)
77 DALI_PROPERTY_REGISTRATION(Toolkit, Button, "unselectedBackgroundVisual", MAP, UNSELECTED_BACKGROUND_VISUAL)
78 DALI_PROPERTY_REGISTRATION(Toolkit, Button, "label", MAP, LABEL)
79 DALI_PROPERTY_REGISTRATION(Toolkit, Button, "selectedBackgroundVisual", MAP, SELECTED_BACKGROUND_VISUAL)
80 DALI_PROPERTY_REGISTRATION(Toolkit, Button, "disabledUnselectedBackgroundVisual", MAP, DISABLED_UNSELECTED_BACKGROUND_VISUAL)
81 DALI_PROPERTY_REGISTRATION(Toolkit, Button, "disabledSelectedBackgroundVisual", MAP, DISABLED_SELECTED_BACKGROUND_VISUAL)
82 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit, Button, "labelRelativeAlignment", STRING, LABEL_RELATIVE_ALIGNMENT)
83 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit, Button, "labelPadding", VECTOR4, LABEL_PADDING)
84 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit, Button, "visualPadding", VECTOR4, VISUAL_PADDING)
85
86 // Signals:
87 DALI_SIGNAL_REGISTRATION(Toolkit, Button, "pressed", SIGNAL_PRESSED)
88 DALI_SIGNAL_REGISTRATION(Toolkit, Button, "released", SIGNAL_RELEASED)
89 DALI_SIGNAL_REGISTRATION(Toolkit, Button, "clicked", SIGNAL_CLICKED)
90 DALI_SIGNAL_REGISTRATION(Toolkit, Button, "stateChanged", SIGNAL_STATE_CHANGED)
91
92 // Actions:
93 DALI_ACTION_REGISTRATION(Toolkit, Button, "buttonClick", ACTION_BUTTON_CLICK)
94
95 DALI_TYPE_REGISTRATION_END()
96
97 DALI_ENUM_TO_STRING_TABLE_BEGIN(ALIGNMENT)
98   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::Button, BEGIN)
99   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::Button, END)
100   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::Button, TOP)
101   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::Button, BOTTOM)
102 DALI_ENUM_TO_STRING_TABLE_END(ALIGNMENT)
103
104 const Scripting::StringEnum ALIGNMENT_STRING_TABLE[] =
105   {
106     {"BEGIN", Button::BEGIN},
107     {"END", Button::END},
108     {"TOP", Button::TOP},
109     {"BOTTOM", Button::BOTTOM},
110 };
111
112 const unsigned int ALIGNMENT_STRING_TABLE_COUNT = sizeof(ALIGNMENT_STRING_TABLE) / sizeof(ALIGNMENT_STRING_TABLE[0]);
113
114 const Property::Index VISUAL_INDEX_FOR_STATE[Button::STATE_COUNT][Button::VISUAL_STATE_COUNT] =
115   {
116     {Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, Toolkit::Button::Property::UNSELECTED_VISUAL},
117     {Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, Toolkit::Button::Property::SELECTED_VISUAL},
118     {Toolkit::Button::Property::DISABLED_UNSELECTED_BACKGROUND_VISUAL, Toolkit::Button::Property::DISABLED_UNSELECTED_VISUAL},
119     {Toolkit::Button::Property::DISABLED_SELECTED_BACKGROUND_VISUAL, Toolkit::Button::Property::DISABLED_SELECTED_VISUAL}};
120
121 /**
122  * Checks if given map contains a text string
123  */
124 bool MapContainsTextString(Property::Map& map)
125 {
126   bool             result = false;
127   Property::Value* value  = map.Find(Toolkit::TextVisual::Property::TEXT);
128   if(value)
129   {
130     std::string textString;
131     value->Get(textString);
132     if(!textString.empty())
133     {
134       result = true;
135     }
136   }
137   return result;
138 }
139
140 } // unnamed namespace
141
142 Button::Button()
143 : Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT)),
144   mAutoRepeatingTimer(),
145   mTextLabelAlignment(END),
146   mAutoRepeating(false),
147   mTogglableButton(false),
148   mTextStringSetFlag(false),
149   mInitialAutoRepeatingDelay(0.0f),
150   mNextAutoRepeatingDelay(0.0f),
151   mAnimationTime(0.0f),
152   mButtonPressedState(UNPRESSED),
153   mButtonState(UNSELECTED_STATE),
154   mPreviousButtonState(mButtonState),
155   mClickActionPerforming(false)
156 {
157 }
158
159 Button::~Button()
160 {
161 }
162
163 void Button::SetAutoRepeating(bool autoRepeating)
164 {
165   mAutoRepeating = autoRepeating;
166
167   // An autorepeating button can't be a toggle button.
168   if(autoRepeating)
169   {
170     if(IsSelected())
171     {
172       SetSelected(false); // UnSelect before switching off Toggle feature.
173     }
174     mTogglableButton = false;
175   }
176 }
177
178 void Button::SetInitialAutoRepeatingDelay(float initialAutoRepeatingDelay)
179 {
180   DALI_ASSERT_DEBUG(initialAutoRepeatingDelay > 0.f);
181   mInitialAutoRepeatingDelay = initialAutoRepeatingDelay;
182 }
183
184 void Button::SetNextAutoRepeatingDelay(float nextAutoRepeatingDelay)
185 {
186   DALI_ASSERT_DEBUG(nextAutoRepeatingDelay > 0.f);
187   mNextAutoRepeatingDelay = nextAutoRepeatingDelay;
188 }
189
190 void Button::SetTogglableButton(bool togglable)
191 {
192   mTogglableButton = togglable;
193
194   // A toggle button can't be an autorepeating button.
195   if(togglable)
196   {
197     mAutoRepeating = false;
198   }
199 }
200
201 void Button::SetSelected(bool selected)
202 {
203   if(mTogglableButton)
204   {
205     DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::SetSelected (%s)\n", (selected ? "true" : "false"));
206
207     if(selected && (mButtonState != SELECTED_STATE))
208     {
209       ChangeState(SELECTED_STATE);
210     }
211     else if(!selected && (mButtonState != UNSELECTED_STATE))
212     {
213       ChangeState(UNSELECTED_STATE);
214     }
215   }
216 }
217
218 void Button::SetDisabled(bool disabled)
219 {
220   DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::SetDisabled(%s) state(%d)\n", (disabled) ? "disabled" : "active", mButtonState);
221
222   if(disabled)
223   {
224     if(mButtonState == SELECTED_STATE)
225     {
226       ChangeState(DISABLED_SELECTED_STATE);
227     }
228     else if(mButtonState == UNSELECTED_STATE)
229     {
230       ChangeState(DISABLED_UNSELECTED_STATE);
231     }
232   }
233   else
234   {
235     if(mButtonState == DISABLED_SELECTED_STATE)
236     {
237       ChangeState(SELECTED_STATE);
238     }
239     else if(mButtonState == DISABLED_UNSELECTED_STATE)
240     {
241       ChangeState(UNSELECTED_STATE);
242     }
243   }
244 }
245
246 bool Button::IsDisabled() const
247 {
248   return (mButtonState == DISABLED_SELECTED_STATE || mButtonState == DISABLED_UNSELECTED_STATE);
249 }
250
251 bool Button::ValidateState(State requestedState)
252 {
253   /*  Below tables shows allowed state transitions
254    *  Match rows in first column to following columns, if true then transition allowed.
255    *  eg UNSELECTED_STATE to DISABLED_UNSELECTED_STATE is true so state transition allowed.
256    *
257                                                              to| UNSELECTED_STATE | SELECTED_STATE | DISABLED_UNSELECTED_STATE | DISABLED_SELECTED_STATE |*/
258   /* from*/
259   bool transitionTable[4][4] = {/* UNSELECTED_STATE*/ {false, true, true, false},
260                                 /* SELECTED_STATE*/ {true, false, false, true},
261                                 /* DISABLED_UNSELECTED_STATE*/ {true, true, false, false},
262                                 /* DISABLED_SELECTED_STATE*/ {false, true, false, false}};
263
264   DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::ValidateState ReuestedState:%d, CurrentState:%d, result:%s\n", requestedState, mButtonState, (transitionTable[mButtonState][requestedState]) ? "change-accepted" : "change-denied");
265
266   return transitionTable[mButtonState][requestedState];
267 }
268
269 void Button::ChangeState(State requestedState)
270 {
271   DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::ChangeState ReuestedState(%d)\n", requestedState);
272
273   // Validate State before changing
274   if(!ValidateState(requestedState))
275   {
276     DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::ChangeState ReuestedState(%d) not validated\n", requestedState);
277     return;
278   }
279
280   // If not on stage the button could have still been set to selected so update state
281   mPreviousButtonState = mButtonState;   // Store previous state for visual removal (used when animations ended)
282   mButtonState         = requestedState; // Update current state
283
284   if(Self().GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
285   {
286     OnStateChange(mButtonState); // Notify derived buttons
287     SelectRequiredVisual(VISUAL_INDEX_FOR_STATE[mButtonState][BACKGROUND]);
288     SelectRequiredVisual(VISUAL_INDEX_FOR_STATE[mButtonState][FOREGROUND]);
289     // If animation supported then visual removal should be performed after any transition animation has completed.
290     // If Required Visual is not loaded before current visual is removed then a flickering will be evident.
291     // Derived button can override OnButtonVisualRemoval
292     OnButtonVisualRemoval(VISUAL_INDEX_FOR_STATE[mPreviousButtonState][BACKGROUND]);
293     OnButtonVisualRemoval(VISUAL_INDEX_FOR_STATE[mPreviousButtonState][FOREGROUND]);
294     RelayoutRequest();
295   }
296
297   Toolkit::Button handle(GetOwner());
298   // Emit signal.
299   mStateChangedSignal.Emit(handle);
300 }
301
302 bool Button::IsSelected() const
303 {
304   bool selected = (mButtonState == SELECTED_STATE) || (mButtonState == DISABLED_SELECTED_STATE);
305   return mTogglableButton && selected;
306 }
307
308 void Button::MergeWithExistingLabelProperties(const Property::Map& inMap, Property::Map& outMap)
309 {
310   DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "MergeLabelProperties with %d properties\n", inMap.Count());
311
312   /**
313    * Properties for the Label visual could be from a style sheet but after being set the "TEXT" property could be set.
314    * Hence would need to create the Text Visual with the complete merged set of properties.
315    *
316    * 1) Find Label Visual
317    * 2) Retrieve current properties ( settings )
318    * 3) Merge with new properties ( settings )
319    * 4) Return new merged map
320    */
321   Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, Toolkit::Button::Property::LABEL);
322   if(visual)
323   {
324     DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "MergeLabelProperties Visual already exists, retrieving existing map\n");
325     visual.CreatePropertyMap(outMap);
326     DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "MergeLabelProperties retrieved %d properties\n", outMap.Count());
327   }
328
329   outMap.Merge(inMap);
330
331   // Store if a text string has been supplied.
332
333   mTextStringSetFlag = MapContainsTextString(outMap);
334
335   DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "MergeLabelProperties now has %d properties\n", outMap.Count());
336 }
337
338 void Button::SetLabelAlignment(Button::Align labelAlignment)
339 {
340   mTextLabelAlignment = labelAlignment;
341   RelayoutRequest();
342 }
343
344 Button::Align Button::GetLabelAlignment()
345 {
346   return mTextLabelAlignment;
347 }
348
349 /**
350  * Create Visual for given index from a property map or url.
351  * 1) Check if value passed in is a url and create visual
352  * 2) Create visual from map if step (1) is false
353  * 3) Register visual with control with false for enable flag. Button will later enable visual when needed ( Button::SelectRequiredVisual )
354  * 4) Unregister visual if empty map was provided. This is the method to remove a visual
355  */
356 void Button::CreateVisualsForComponent(Property::Index index, const Property::Value& value, const int visualDepth)
357 {
358   DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "CreateVisualsForComponent index(%d)\n", index);
359   Toolkit::VisualFactory visualFactory = Toolkit::VisualFactory::Get();
360   Toolkit::Visual::Base  buttonVisual;
361
362   std::string imageUrl;
363   if(value.Get(imageUrl))
364   {
365     DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "CreateVisualsForComponent Using image URL(%d)\n", index);
366     if(!imageUrl.empty())
367     {
368       DALI_ASSERT_DEBUG(index != Toolkit::Button::Property::LABEL && "Creating a Image Visual instead of Text Visual ");
369       buttonVisual = visualFactory.CreateVisual(imageUrl, ImageDimensions());
370     }
371   }
372   else
373   {
374     // if its not a string then get a Property::Map from the property if possible.
375     const Property::Map* map = value.GetMap();
376     if(map && !map->Empty()) // Empty map results in current visual removal.
377     {
378       DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "CreateVisualsForComponent Using Map(%d)\n", index);
379       buttonVisual = visualFactory.CreateVisual(*map);
380     }
381   }
382
383   if(buttonVisual)
384   {
385     DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "CreateVisualsForComponent RegisterVisual index(%d) enabled(%s)\n", index, DevelControl::IsVisualEnabled(*this, index) ? "true" : "false");
386     // enable the visual if needed for current state
387     const bool enabled = ((index == VISUAL_INDEX_FOR_STATE[mButtonState][BACKGROUND]) ||
388                           (index == VISUAL_INDEX_FOR_STATE[mButtonState][FOREGROUND]) ||
389                           (index == Toolkit::Button::Property::LABEL));
390     DevelControl::RegisterVisual(*this, index, buttonVisual, enabled, visualDepth);
391   }
392   else
393   {
394     DevelControl::UnregisterVisual(*this, index);
395     DALI_LOG_INFO(gLogButtonFilter, Debug::General, "CreateVisualsForComponent Visual not created or empty map (clearing visual).(%d)\n", index);
396   }
397   RelayoutRequest();
398 }
399
400 bool Button::GetPropertyMapForVisual(Property::Index visualIndex, Property::Map& retreivedMap) const
401 {
402   DALI_LOG_INFO(gLogButtonFilter, Debug::General, "GetPropertyMapForVisual visual(%d)\n", visualIndex);
403   bool                  success = false;
404   Toolkit::Visual::Base visual  = DevelControl::GetVisual(*this, visualIndex);
405   if(visual)
406   {
407     visual.CreatePropertyMap(retreivedMap);
408     success = true;
409   }
410   DALI_LOG_INFO(gLogButtonFilter, Debug::General, "GetPropertyMapForVisual %s\n", success ? "Success" : "Failure");
411   return success;
412 }
413
414 bool Button::DoAction(BaseObject* object, const std::string& actionName, const Property::Map& attributes)
415 {
416   bool ret = false;
417
418   Dali::BaseHandle handle(object);
419
420   Toolkit::Button button = Toolkit::Button::DownCast(handle);
421
422   DALI_ASSERT_DEBUG(button);
423
424   if(0 == strcmp(actionName.c_str(), ACTION_BUTTON_CLICK))
425   {
426     ret = GetImplementation(button).DoClickAction(attributes);
427   }
428
429   return ret;
430 }
431
432 bool Button::DoClickAction(const Property::Map& attributes)
433 {
434   // Prevents the button signals from doing a recursive loop by sending an action
435   // and re-emitting the signals.
436   if(!mClickActionPerforming)
437   {
438     mClickActionPerforming = true;
439     ButtonDown();
440     if(!mTogglableButton)
441     {
442       mButtonPressedState = DEPRESSED;
443     }
444     ButtonUp();
445     mClickActionPerforming = false;
446
447     return true;
448   }
449
450   return false;
451 }
452
453 void Button::ButtonDown()
454 {
455   if(mTogglableButton)
456   {
457     if(mButtonState != SELECTED_STATE)
458     {
459       SetSelected(true);
460       mButtonPressedState = TOGGLE_DEPRESSED;
461     }
462     else
463     {
464       mButtonPressedState = DEPRESSED;
465     }
466   }
467   else
468   {
469     Pressed();
470     mButtonPressedState = DEPRESSED;
471     if(mAutoRepeating)
472     {
473       SetUpTimer(mInitialAutoRepeatingDelay);
474     }
475   }
476
477   // The pressed signal should be emitted regardless of toggle mode.
478   Toolkit::Button handle(GetOwner());
479   mPressedSignal.Emit(handle);
480 }
481
482 void Button::ButtonUp()
483 {
484   bool emitSignalsForPressAndReleaseAction = false;
485
486   if(DEPRESSED == mButtonPressedState)
487   {
488     if(mTogglableButton) // Button up will change state
489     {
490       emitSignalsForPressAndReleaseAction = OnToggleReleased(); // Derived toggle buttons can override this to provide custom behaviour
491     }
492     else
493     {
494       Released(); // Button up will result in unselected state
495       if(mAutoRepeating)
496       {
497         mAutoRepeatingTimer.Reset();
498       }
499       emitSignalsForPressAndReleaseAction = true;
500     }
501   }
502   else if(TOGGLE_DEPRESSED == mButtonPressedState)
503   {
504     emitSignalsForPressAndReleaseAction = true; // toggle released after being pressed, a click
505   }
506
507   if(emitSignalsForPressAndReleaseAction)
508   {
509     // The clicked and released signals should be emitted regardless of toggle mode.
510     Toolkit::Button handle(GetOwner());
511     mReleasedSignal.Emit(handle);
512     mClickedSignal.Emit(handle);
513   }
514 }
515
516 bool Button::OnToggleReleased()
517 {
518   SetSelected(!IsSelected());
519   mButtonPressedState = UNPRESSED;
520   return true;
521 }
522
523 void Button::OnTouchPointLeave()
524 {
525   if(DEPRESSED == mButtonPressedState)
526   {
527     if(!mTogglableButton)
528     {
529       Released();
530
531       if(mAutoRepeating)
532       {
533         mAutoRepeatingTimer.Reset();
534       }
535     }
536
537     mButtonPressedState = UNPRESSED;
538
539     // The released signal should be emitted regardless of toggle mode.
540     Toolkit::Button handle(GetOwner());
541     mReleasedSignal.Emit(handle);
542   }
543 }
544
545 void Button::OnTouchPointInterrupted()
546 {
547   OnTouchPointLeave();
548 }
549
550 Toolkit::Button::ButtonSignalType& Button::PressedSignal()
551 {
552   return mPressedSignal;
553 }
554
555 Toolkit::Button::ButtonSignalType& Button::ReleasedSignal()
556 {
557   return mReleasedSignal;
558 }
559
560 Toolkit::Button::ButtonSignalType& Button::ClickedSignal()
561 {
562   return mClickedSignal;
563 }
564
565 Toolkit::Button::ButtonSignalType& Button::StateChangedSignal()
566 {
567   return mStateChangedSignal;
568 }
569
570 bool Button::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor)
571 {
572   Dali::BaseHandle handle(object);
573
574   bool            connected(true);
575   Toolkit::Button button = Toolkit::Button::DownCast(handle);
576
577   if(0 == strcmp(signalName.c_str(), SIGNAL_PRESSED))
578   {
579     button.PressedSignal().Connect(tracker, functor);
580   }
581   else if(0 == strcmp(signalName.c_str(), SIGNAL_RELEASED))
582   {
583     button.ReleasedSignal().Connect(tracker, functor);
584   }
585   else if(0 == strcmp(signalName.c_str(), SIGNAL_CLICKED))
586   {
587     button.ClickedSignal().Connect(tracker, functor);
588   }
589   else if(0 == strcmp(signalName.c_str(), SIGNAL_STATE_CHANGED))
590   {
591     button.StateChangedSignal().Connect(tracker, functor);
592   }
593   else
594   {
595     // signalName does not match any signal
596     connected = false;
597   }
598
599   return connected;
600 }
601
602 void Button::OnInitialize()
603 {
604   DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::OnInitialize\n");
605
606   Actor self = Self();
607
608   mTapDetector = TapGestureDetector::New();
609   mTapDetector.Attach(self);
610   mTapDetector.DetectedSignal().Connect(this, &Button::OnTap);
611
612   self.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);
613   self.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_HIGHLIGHTABLE, true);
614
615   self.TouchedSignal().Connect(this, &Button::OnTouch);
616 }
617
618 bool Button::OnAccessibilityActivated()
619 {
620   return OnKeyboardEnter();
621 }
622
623 bool Button::OnTouch(Actor actor, const TouchEvent& touch)
624 {
625   if(!IsDisabled() && (actor == touch.GetHitActor(0)))
626   {
627     if(1 == touch.GetPointCount())
628     {
629       switch(touch.GetState(0))
630       {
631         case PointState::DOWN:
632         {
633           ButtonDown();
634           break;
635         }
636         case PointState::UP:
637         {
638           ButtonUp();
639           break;
640         }
641         case PointState::INTERRUPTED:
642         {
643           OnTouchPointInterrupted();
644           break;
645         }
646         case PointState::LEAVE:
647         {
648           OnTouchPointLeave();
649           break;
650         }
651         case PointState::MOTION:
652         case PointState::STATIONARY: // FALLTHROUGH
653         {
654           // Nothing to do
655           break;
656         }
657       }
658     }
659     else if(1 < touch.GetPointCount())
660     {
661       OnTouchPointLeave(); // Notification for derived classes.
662
663       // Sets the button state to the default
664       mButtonPressedState = UNPRESSED;
665     }
666   }
667   return false;
668 }
669
670 bool Button::OnKeyboardEnter()
671 {
672   // When the enter key is pressed, or button is activated, the click action is performed.
673   Property::Map attributes;
674   bool          ret = DoClickAction(attributes);
675
676   return ret;
677 }
678
679 void Button::OnSceneDisconnection()
680 {
681   if(DEPRESSED == mButtonPressedState)
682   {
683     if(!mTogglableButton)
684     {
685       Released();
686
687       if(mAutoRepeating)
688       {
689         mAutoRepeatingTimer.Reset();
690       }
691     }
692   }
693
694   mButtonPressedState = UNPRESSED;
695
696   Control::OnSceneDisconnection(); // Visuals will be set off stage
697 }
698
699 void Button::OnSceneConnection(int depth)
700 {
701   DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::OnSceneConnection ptr(%p) \n", this);
702   OnButtonVisualRemoval(VISUAL_INDEX_FOR_STATE[mPreviousButtonState][BACKGROUND]);
703   OnButtonVisualRemoval(VISUAL_INDEX_FOR_STATE[mPreviousButtonState][FOREGROUND]);
704   SelectRequiredVisual(Toolkit::Button::Property::LABEL);
705   SelectRequiredVisual(VISUAL_INDEX_FOR_STATE[mButtonState][BACKGROUND]);
706   SelectRequiredVisual(VISUAL_INDEX_FOR_STATE[mButtonState][FOREGROUND]);
707   Control::OnSceneConnection(depth); // Enabled visuals will be put on stage
708   RelayoutRequest();
709 }
710
711 Vector3 Button::GetNaturalSize()
712 {
713   Vector3 size = Vector3::ZERO;
714
715   bool horizontalAlignment = mTextLabelAlignment == BEGIN || mTextLabelAlignment == END; // label and visual side by side
716
717   // Get natural size of foreground ( largest of the possible visuals )
718   Size largestProvidedVisual;
719   Size labelSize = Size::ZERO;
720
721   bool foreGroundVisualUsed = false;
722
723   for(int state = Button::UNSELECTED_STATE; state < Button::STATE_COUNT; state++)
724   {
725     Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, VISUAL_INDEX_FOR_STATE[state][FOREGROUND]);
726     Size                  visualSize;
727     if(visual)
728     {
729       visual.GetNaturalSize(visualSize);
730       largestProvidedVisual.width  = std::max(largestProvidedVisual.width, visualSize.width);
731       largestProvidedVisual.height = std::max(largestProvidedVisual.height, visualSize.height);
732       foreGroundVisualUsed         = true;
733     }
734   }
735
736   if(!foreGroundVisualUsed) // If foreground visual not supplied then use the background visual to calculate Natural size
737   {
738     for(int state = Button::UNSELECTED_STATE; state < Button::STATE_COUNT; state++)
739     {
740       Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, VISUAL_INDEX_FOR_STATE[state][BACKGROUND]);
741       Size                  visualSize;
742       if(visual)
743       {
744         visual.GetNaturalSize(visualSize);
745         largestProvidedVisual.width  = std::max(largestProvidedVisual.width, visualSize.width);
746         largestProvidedVisual.height = std::max(largestProvidedVisual.height, visualSize.height);
747       }
748     }
749   }
750
751   // Get horizontal padding total
752   if(largestProvidedVisual.width > 0) // if visual exists
753   {
754     size.width += largestProvidedVisual.width + mForegroundPadding.left + mForegroundPadding.right;
755   }
756   // Get vertical padding total
757   if(largestProvidedVisual.height > 0)
758   {
759     size.height += largestProvidedVisual.height + mForegroundPadding.top + mForegroundPadding.bottom;
760   }
761
762   DALI_LOG_INFO(gLogButtonFilter, Debug::General, "GetNaturalSize visual Size(%f,%f)\n", largestProvidedVisual.width, largestProvidedVisual.height);
763
764   // Get natural size of label if text has been set
765   if(mTextStringSetFlag)
766   {
767     Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, Toolkit::Button::Property::LABEL);
768
769     if(visual)
770     {
771       visual.GetNaturalSize(labelSize);
772
773       DALI_LOG_INFO(gLogButtonFilter, Debug::General, "GetNaturalSize labelSize(%f,%f) padding(%f,%f)\n", labelSize.width, labelSize.height, mLabelPadding.left + mLabelPadding.right, mLabelPadding.top + mLabelPadding.bottom);
774
775       labelSize.width += mLabelPadding.left + mLabelPadding.right;
776       labelSize.height += mLabelPadding.top + mLabelPadding.bottom;
777
778       // Add label size to height or width depending on alignment position
779       if(horizontalAlignment)
780       {
781         size.width += labelSize.width;
782         size.height = std::max(size.height, labelSize.height);
783       }
784       else
785       {
786         size.height += labelSize.height;
787         size.width = std::max(size.width, labelSize.width);
788       }
789     }
790   }
791
792   if(size.width < 1 && size.height < 1)
793   {
794     // if no image or label then use Control's natural size
795     DALI_LOG_INFO(gLogButtonFilter, Debug::General, "GetNaturalSize Using control natural size\n");
796     size = Control::GetNaturalSize();
797   }
798
799   DALI_LOG_INFO(gLogButtonFilter, Debug::General, "Button GetNaturalSize (%f,%f)\n", size.width, size.height);
800
801   return size;
802 }
803
804 void Button::OnSetResizePolicy(ResizePolicy::Type policy, Dimension::Type dimension)
805 {
806   DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnSetResizePolicy\n");
807   RelayoutRequest();
808 }
809
810 /**
811  * Visuals are sized and positioned in this function.
812  * Whilst the control has it's size negotiated it has to size it's visuals explicitly here.
813  */
814
815 void Button::OnRelayout(const Vector2& size, RelayoutContainer& container)
816 {
817   DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout targetSize(%f,%f) ptr(%p) state[%d]\n", size.width, size.height, this, mButtonState);
818
819   Toolkit::Visual::Base currentVisual           = DevelControl::GetVisual(*this, VISUAL_INDEX_FOR_STATE[mButtonState][FOREGROUND]);
820   Toolkit::Visual::Base currentBackGroundVisual = DevelControl::GetVisual(*this, VISUAL_INDEX_FOR_STATE[mButtonState][BACKGROUND]);
821
822   // Sizes and padding set to zero, if not present then values will no effect calculations.
823   Vector2 visualPosition          = Vector2::ZERO;
824   Vector2 labelPosition           = Vector2::ZERO;
825   Size    visualSize              = Size::ZERO;
826   Padding foregroundVisualPadding = Padding(0.0f, 0.0f, 0.0f, 0.0f);
827   Padding labelVisualPadding      = Padding(0.0f, 0.0f, 0.0f, 0.0f);
828
829   if(mTextStringSetFlag)
830   {
831     DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout Label padding setting padding:%f,%f,%f,%f\n", mLabelPadding.y, mLabelPadding.x, mLabelPadding.width, mLabelPadding.height);
832     labelVisualPadding = mLabelPadding;
833   }
834
835   if(currentVisual)
836   {
837     DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout Foreground Visual setting padding:%f,%f,%f,%f\n", mForegroundPadding.y, mForegroundPadding.x, mForegroundPadding.width, mForegroundPadding.height);
838     currentVisual.GetNaturalSize(visualSize);
839     foregroundVisualPadding = mForegroundPadding;
840   }
841
842   Toolkit::Align::Type visualAnchorPoint = Toolkit::Align::TOP_BEGIN;
843
844   Vector2 visualAndPaddingSize = Vector2((foregroundVisualPadding.x + visualSize.width + foregroundVisualPadding.y),
845                                          (foregroundVisualPadding.width + visualSize.height + foregroundVisualPadding.height));
846
847   DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout visualAndPaddingSize(%f,%f)\n", visualAndPaddingSize.width, visualAndPaddingSize.height);
848
849   // Text Visual should take all space available after foreground visual size and all padding is considered.
850   // Remaining Space priority, Foreground padding, foreground visual, Text padding then Text visual.
851   Size remainingSpaceForText = Size::ZERO;
852
853   switch(mTextLabelAlignment)
854   {
855     case BEGIN:
856     {
857       visualAnchorPoint = Toolkit::Align::TOP_END;
858       visualPosition.x  = foregroundVisualPadding.right;
859       visualPosition.y  = foregroundVisualPadding.top;
860
861       labelPosition.x = labelVisualPadding.x;
862       labelPosition.y = labelVisualPadding.top;
863
864       remainingSpaceForText.width  = size.width - visualAndPaddingSize.width - labelVisualPadding.x - labelVisualPadding.y;
865       remainingSpaceForText.height = size.height - labelVisualPadding.top - labelVisualPadding.bottom;
866       break;
867     }
868     case END:
869     {
870       visualAnchorPoint = Toolkit::Align::TOP_BEGIN;
871       visualPosition.x  = foregroundVisualPadding.left;
872       visualPosition.y  = foregroundVisualPadding.top;
873
874       labelPosition.x = visualAndPaddingSize.width + labelVisualPadding.x;
875       labelPosition.y = labelVisualPadding.top;
876
877       remainingSpaceForText.width  = size.width - visualAndPaddingSize.width - labelVisualPadding.x - labelVisualPadding.y;
878       remainingSpaceForText.height = size.height - labelVisualPadding.top - labelVisualPadding.bottom;
879       break;
880     }
881     case TOP:
882     {
883       visualAnchorPoint = Toolkit::Align::BOTTOM_END;
884       visualPosition.x  = foregroundVisualPadding.left;
885       visualPosition.y  = foregroundVisualPadding.bottom;
886
887       labelPosition.x = labelVisualPadding.left;
888       labelPosition.y = labelVisualPadding.top;
889
890       remainingSpaceForText.width  = size.width - labelVisualPadding.x - labelVisualPadding.y;
891       remainingSpaceForText.height = size.height - visualAndPaddingSize.height - labelVisualPadding.top - labelVisualPadding.bottom;
892
893       break;
894     }
895     case BOTTOM:
896     {
897       visualAnchorPoint = Toolkit::Align::TOP_END;
898       visualPosition.x  = foregroundVisualPadding.left;
899       visualPosition.y  = foregroundVisualPadding.top;
900
901       labelPosition.x = labelVisualPadding.left;
902       labelPosition.y = visualAndPaddingSize.height + labelVisualPadding.top;
903
904       remainingSpaceForText.width  = size.width - labelVisualPadding.x - labelVisualPadding.y;
905       remainingSpaceForText.height = size.height - visualAndPaddingSize.height - labelVisualPadding.top - labelVisualPadding.bottom;
906
907       break;
908     }
909   }
910
911   if(currentBackGroundVisual)
912   {
913     DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout Setting visual background size to(%f,%f)\n", size.width, size.height);
914
915     Property::Map visualTransform;
916
917     visualTransform.Add(Toolkit::Visual::Transform::Property::SIZE, size)
918       .Add(Toolkit::Visual::Transform::Property::SIZE_POLICY, Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE));
919
920     currentBackGroundVisual.SetTransformAndSize(visualTransform, size);
921   }
922
923   if(currentVisual)
924   {
925     DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout Setting visual size to(%f,%f)\n", visualSize.width, visualSize.height);
926
927     Property::Map visualTransform;
928
929     visualTransform.Add(Toolkit::Visual::Transform::Property::SIZE, visualSize)
930       .Add(Toolkit::Visual::Transform::Property::OFFSET, visualPosition)
931       .Add(Toolkit::Visual::Transform::Property::OFFSET_POLICY, Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE))
932       .Add(Toolkit::Visual::Transform::Property::SIZE_POLICY, Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE))
933       .Add(Toolkit::Visual::Transform::Property::ORIGIN, Toolkit::Align::TOP_BEGIN)
934       .Add(Toolkit::Visual::Transform::Property::ANCHOR_POINT, visualAnchorPoint);
935
936     currentVisual.SetTransformAndSize(visualTransform, size);
937   }
938
939   if(mTextStringSetFlag)
940   {
941     Toolkit::Visual::Base textVisual = DevelControl::GetVisual(*this, Toolkit::Button::Property::LABEL); // No need to search for Label visual if no text set.
942
943     if(textVisual)
944     {
945       if(!currentVisual)
946       {
947         DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout Only Text\n");
948         labelPosition.x = labelVisualPadding.left;
949         labelPosition.y = labelVisualPadding.height;
950       }
951
952       Vector2 preSize = Vector2(static_cast<int>(remainingSpaceForText.x), static_cast<int>(remainingSpaceForText.y));
953
954       DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout text Size(%f,%f) text Position(%f,%f) \n", remainingSpaceForText.width, remainingSpaceForText.height, labelPosition.x, labelPosition.y);
955
956       DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout text Size -- (%f,%f) text Position(%f,%f) \n", preSize.width, preSize.height, labelPosition.x, labelPosition.y);
957
958       Property::Map textVisualTransform;
959       textVisualTransform.Add(Toolkit::Visual::Transform::Property::SIZE, preSize)
960         .Add(Toolkit::Visual::Transform::Property::OFFSET, labelPosition)
961         .Add(Toolkit::Visual::Transform::Property::OFFSET_POLICY, Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE))
962         .Add(Toolkit::Visual::Transform::Property::SIZE_POLICY, Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE))
963         .Add(Toolkit::Visual::Transform::Property::ORIGIN, Toolkit::Align::TOP_BEGIN)
964         .Add(Toolkit::Visual::Transform::Property::ANCHOR_POINT, visualAnchorPoint);
965
966       textVisual.SetTransformAndSize(textVisualTransform, size);
967     }
968   }
969
970   DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout selected (%s) \n", IsSelected() ? "yes" : "no");
971
972   DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout << \n");
973 }
974
975 void Button::OnTap(Actor actor, const TapGesture& tap)
976 {
977   // Prevents Parent getting a tap event
978 }
979
980 void Button::SetUpTimer(float delay)
981 {
982   mAutoRepeatingTimer = Dali::Timer::New(static_cast<unsigned int>(1000.f * delay));
983   mAutoRepeatingTimer.TickSignal().Connect(this, &Button::AutoRepeatingSlot);
984   mAutoRepeatingTimer.Start();
985 }
986
987 bool Button::AutoRepeatingSlot()
988 {
989   bool consumed = false;
990   if(!IsDisabled())
991   {
992     // Restart the autorepeat timer.
993     SetUpTimer(mNextAutoRepeatingDelay);
994
995     Pressed();
996
997     Toolkit::Button handle(GetOwner());
998
999     //Emit signal.
1000     consumed = mReleasedSignal.Emit(handle);
1001     consumed = mClickedSignal.Emit(handle);
1002     consumed |= mPressedSignal.Emit(handle);
1003   }
1004
1005   return consumed;
1006 }
1007
1008 void Button::Pressed()
1009 {
1010   DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::Pressed\n");
1011
1012   if(mButtonState == UNSELECTED_STATE)
1013   {
1014     ChangeState(SELECTED_STATE);
1015     OnPressed(); // Notifies the derived class the button has been pressed.
1016   }
1017 }
1018
1019 void Button::Released()
1020 {
1021   DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::Released\n");
1022
1023   if(mButtonState == SELECTED_STATE && !mTogglableButton)
1024   {
1025     ChangeState(UNSELECTED_STATE);
1026     OnReleased(); //    // Notifies the derived class the button has been released.
1027   }
1028   mButtonPressedState = UNPRESSED;
1029 }
1030
1031 void Button::SelectRequiredVisual(Property::Index visualIndex)
1032 {
1033   DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::SelectRequiredVisual index(%d) state(%d)\n", visualIndex, mButtonState);
1034   // only enable visuals that exist
1035   if(DevelControl::GetVisual(*this, visualIndex))
1036   {
1037     DevelControl::EnableVisual(*this, visualIndex, true);
1038   }
1039 }
1040
1041 void Button::RemoveVisual(Property::Index visualIndex)
1042 {
1043   // Use OnButtonVisualRemoval if want button developer to have the option to override removal.
1044   DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::RemoveVisual index(%d) state(%d)\n", visualIndex, mButtonState);
1045
1046   Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, visualIndex);
1047
1048   if(visual)
1049   {
1050     DevelControl::EnableVisual(*this, visualIndex, false);
1051   }
1052 }
1053
1054 void Button::OnButtonVisualRemoval(Property::Index visualIndex)
1055 {
1056   // Derived Buttons can over ride this to prevent default removal.
1057   DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::OnButtonVisualRemoval index(%d)\n", visualIndex);
1058   RemoveVisual(visualIndex);
1059 }
1060
1061 void Button::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value)
1062 {
1063   Toolkit::Button button = Toolkit::Button::DownCast(Dali::BaseHandle(object));
1064
1065   DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::SetProperty index[%d]\n", index);
1066
1067   if(button)
1068   {
1069     switch(index)
1070     {
1071       case Toolkit::Button::Property::DISABLED:
1072       {
1073         GetImplementation(button).SetDisabled(value.Get<bool>());
1074         break;
1075       }
1076
1077       case Toolkit::Button::Property::AUTO_REPEATING:
1078       {
1079         GetImplementation(button).SetAutoRepeating(value.Get<bool>());
1080         break;
1081       }
1082
1083       case Toolkit::Button::Property::INITIAL_AUTO_REPEATING_DELAY:
1084       {
1085         GetImplementation(button).SetInitialAutoRepeatingDelay(value.Get<float>());
1086         break;
1087       }
1088
1089       case Toolkit::Button::Property::NEXT_AUTO_REPEATING_DELAY:
1090       {
1091         GetImplementation(button).SetNextAutoRepeatingDelay(value.Get<float>());
1092         break;
1093       }
1094
1095       case Toolkit::Button::Property::TOGGLABLE:
1096       {
1097         GetImplementation(button).SetTogglableButton(value.Get<bool>());
1098         break;
1099       }
1100
1101       case Toolkit::Button::Property::SELECTED:
1102       {
1103         GetImplementation(button).SetSelected(value.Get<bool>());
1104         break;
1105       }
1106
1107       case Toolkit::Button::Property::UNSELECTED_VISUAL:
1108       case Toolkit::Button::Property::SELECTED_VISUAL:
1109       case Toolkit::Button::Property::DISABLED_SELECTED_VISUAL:
1110       case Toolkit::Button::Property::DISABLED_UNSELECTED_VISUAL:
1111       {
1112         GetImplementation(button).CreateVisualsForComponent(index, value, DepthIndex::CONTENT);
1113         break;
1114       }
1115
1116       case Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL:
1117       case Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL:
1118       case Toolkit::Button::Property::DISABLED_SELECTED_BACKGROUND_VISUAL:
1119       case Toolkit::Button::Property::DISABLED_UNSELECTED_BACKGROUND_VISUAL:
1120       {
1121         GetImplementation(button).CreateVisualsForComponent(index, value, DepthIndex::BACKGROUND);
1122         break;
1123       }
1124
1125       case Toolkit::Button::Property::LABEL:
1126       {
1127         Property::Map outTextVisualProperties;
1128         std::string   textString;
1129
1130         if(value.Get(textString))
1131         {
1132           DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::SetProperty Setting TextVisual with string[%s]\n", textString.c_str());
1133
1134           Property::Map setPropertyMap;
1135           setPropertyMap.Add(Toolkit::Visual::Property::TYPE, Toolkit::Visual::TEXT)
1136             .Add(Toolkit::TextVisual::Property::TEXT, textString);
1137
1138           GetImplementation(button).MergeWithExistingLabelProperties(setPropertyMap, outTextVisualProperties);
1139         }
1140         else
1141         {
1142           // Get a Property::Map from the property if possible.
1143           const Property::Map* setPropertyMap = value.GetMap();
1144           if(setPropertyMap)
1145           {
1146             Property::Map indexKeys = TextVisual::ConvertStringKeysToIndexKeys(*setPropertyMap);
1147             GetImplementation(button).MergeWithExistingLabelProperties(indexKeys, outTextVisualProperties);
1148           }
1149         }
1150
1151         if(!outTextVisualProperties.Empty())
1152         {
1153           GetImplementation(button).CreateVisualsForComponent(index, outTextVisualProperties, DepthIndex::CONTENT);
1154         }
1155         break;
1156       }
1157
1158       case Toolkit::DevelButton::Property::LABEL_RELATIVE_ALIGNMENT:
1159       {
1160         Button::Align labelAlignment(END);
1161         Scripting::GetEnumeration<Button::Align>(value.Get<std::string>().c_str(),
1162                                                  ALIGNMENT_TABLE,
1163                                                  ALIGNMENT_TABLE_COUNT,
1164                                                  labelAlignment);
1165
1166         GetImplementation(button).SetLabelAlignment(labelAlignment);
1167         break;
1168       }
1169
1170       case Toolkit::DevelButton::Property::LABEL_PADDING:
1171       {
1172         Vector4 padding(value.Get<Vector4>());
1173         GetImplementation(button).SetLabelPadding(Padding(padding.x, padding.y, padding.z, padding.w));
1174         break;
1175       }
1176
1177       case Toolkit::DevelButton::Property::VISUAL_PADDING:
1178       {
1179         Vector4 padding(value.Get<Vector4>());
1180         GetImplementation(button).SetForegroundPadding(Padding(padding.x, padding.y, padding.z, padding.w));
1181         break;
1182       }
1183     }
1184   }
1185 }
1186
1187 Property::Value Button::GetProperty(BaseObject* object, Property::Index propertyIndex)
1188 {
1189   Property::Value value;
1190
1191   Toolkit::Button button = Toolkit::Button::DownCast(Dali::BaseHandle(object));
1192
1193   if(button)
1194   {
1195     switch(propertyIndex)
1196     {
1197       case Toolkit::Button::Property::DISABLED:
1198       {
1199         value = GetImplementation(button).IsDisabled();
1200         break;
1201       }
1202
1203       case Toolkit::Button::Property::AUTO_REPEATING:
1204       {
1205         value = GetImplementation(button).mAutoRepeating;
1206         break;
1207       }
1208
1209       case Toolkit::Button::Property::INITIAL_AUTO_REPEATING_DELAY:
1210       {
1211         value = GetImplementation(button).mInitialAutoRepeatingDelay;
1212         break;
1213       }
1214
1215       case Toolkit::Button::Property::NEXT_AUTO_REPEATING_DELAY:
1216       {
1217         value = GetImplementation(button).mNextAutoRepeatingDelay;
1218         break;
1219       }
1220
1221       case Toolkit::Button::Property::TOGGLABLE:
1222       {
1223         value = GetImplementation(button).mTogglableButton;
1224         break;
1225       }
1226
1227       case Toolkit::Button::Property::SELECTED:
1228       {
1229         value = GetImplementation(button).IsSelected();
1230         break;
1231       }
1232
1233       case Toolkit::Button::Property::UNSELECTED_VISUAL:
1234       case Toolkit::Button::Property::SELECTED_VISUAL:
1235       case Toolkit::Button::Property::DISABLED_SELECTED_VISUAL:
1236       case Toolkit::Button::Property::DISABLED_UNSELECTED_VISUAL:
1237       case Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL:
1238       case Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL:
1239       case Toolkit::Button::Property::DISABLED_SELECTED_BACKGROUND_VISUAL:
1240       case Toolkit::Button::Property::DISABLED_UNSELECTED_BACKGROUND_VISUAL:
1241       case Toolkit::Button::Property::LABEL:
1242       {
1243         Property::Map visualProperty;
1244         if(GetImplementation(button).GetPropertyMapForVisual(propertyIndex, visualProperty))
1245         {
1246           value = visualProperty;
1247         }
1248         break;
1249       }
1250
1251       case Toolkit::DevelButton::Property::LABEL_RELATIVE_ALIGNMENT:
1252       {
1253         const char* alignment = Scripting::GetEnumerationName<Button::Align>(GetImplementation(button).GetLabelAlignment(),
1254                                                                              ALIGNMENT_STRING_TABLE,
1255                                                                              ALIGNMENT_STRING_TABLE_COUNT);
1256         if(alignment)
1257         {
1258           value = std::string(alignment);
1259         }
1260
1261         break;
1262       }
1263
1264       case Toolkit::DevelButton::Property::LABEL_PADDING:
1265       {
1266         Padding padding = GetImplementation(button).GetLabelPadding();
1267         value           = Vector4(padding.x, padding.y, padding.top, padding.bottom);
1268         break;
1269       }
1270
1271       case Toolkit::DevelButton::Property::VISUAL_PADDING:
1272       {
1273         Padding padding = GetImplementation(button).GetForegroundPadding();
1274         value           = Vector4(padding.x, padding.y, padding.top, padding.bottom);
1275       }
1276     }
1277   }
1278
1279   return value;
1280 }
1281
1282 void Button::SetLabelPadding(const Padding& padding)
1283 {
1284   DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::SetLabelPadding padding(%f,%f,%f,%f)\n", padding.left, padding.right, padding.bottom, padding.top);
1285   mLabelPadding = Padding(padding.left, padding.right, padding.bottom, padding.top);
1286   RelayoutRequest();
1287 }
1288
1289 Padding Button::GetLabelPadding()
1290 {
1291   return mLabelPadding;
1292 }
1293
1294 void Button::SetForegroundPadding(const Padding& padding)
1295 {
1296   DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::SetForegroundPadding padding(%f,%f,%f,%f)\n", padding.left, padding.right, padding.bottom, padding.top);
1297   mForegroundPadding = Padding(padding.left, padding.right, padding.bottom, padding.top);
1298   RelayoutRequest();
1299 }
1300
1301 Padding Button::GetForegroundPadding()
1302 {
1303   return mForegroundPadding;
1304 }
1305
1306 std::string Button::AccessibleImpl::GetNameRaw() const
1307 {
1308   std::string   labelText;
1309   auto          slf      = Toolkit::Button::DownCast(Self());
1310   Property::Map labelMap = slf.GetProperty<Property::Map>(Toolkit::Button::Property::LABEL);
1311
1312   Property::Value* textPropertyPtr = labelMap.Find(Toolkit::TextVisual::Property::TEXT);
1313   if(textPropertyPtr)
1314   {
1315     textPropertyPtr->Get(labelText);
1316   }
1317
1318   return labelText;
1319 }
1320
1321 Property::Index Button::AccessibleImpl::GetNamePropertyIndex()
1322 {
1323   Property::Index label    = Toolkit::Button::Property::LABEL;
1324   Property::Map   labelMap = Self().GetProperty<Property::Map>(label);
1325
1326   if(MapContainsTextString(labelMap))
1327   {
1328     return label;
1329   }
1330   else
1331   {
1332     return Property::INVALID_INDEX;
1333   }
1334 }
1335
1336 Dali::Accessibility::States Button::AccessibleImpl::CalculateStates()
1337 {
1338   auto tmp                                    = DevelControl::ControlAccessible::CalculateStates();
1339   tmp[Dali::Accessibility::State::SELECTABLE] = true;
1340   auto slf                                    = Toolkit::Button::DownCast(Self());
1341   tmp[Dali::Accessibility::State::ENABLED]    = !slf.GetProperty<bool>(Toolkit::Button::Property::DISABLED);
1342   tmp[Dali::Accessibility::State::CHECKED]    = slf.GetProperty<bool>(Toolkit::Button::Property::SELECTED);
1343   return tmp;
1344 }
1345
1346 } // namespace Internal
1347
1348 } // namespace Toolkit
1349
1350 } // namespace Dali