2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "button-impl.h"
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
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>
46 #if defined(DEBUG_ENABLED)
47 Debug::Filter* gLogButtonFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_BUTTON_CONTROL");
60 // empty handle as we cannot create button (but type registered for clicked signal)
64 // Setup properties, signals and actions using the type-registry.
65 DALI_TYPE_REGISTRATION_BEGIN(Toolkit::Button, Toolkit::Control, Create)
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)
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)
93 DALI_ACTION_REGISTRATION(Toolkit, Button, "buttonClick", ACTION_BUTTON_CLICK)
95 DALI_TYPE_REGISTRATION_END()
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)
104 const Scripting::StringEnum ALIGNMENT_STRING_TABLE[] =
106 {"BEGIN", Button::BEGIN},
107 {"END", Button::END},
108 {"TOP", Button::TOP},
109 {"BOTTOM", Button::BOTTOM},
112 const unsigned int ALIGNMENT_STRING_TABLE_COUNT = sizeof(ALIGNMENT_STRING_TABLE) / sizeof(ALIGNMENT_STRING_TABLE[0]);
114 const Property::Index VISUAL_INDEX_FOR_STATE[][Button::STATE_COUNT] =
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}};
122 * Checks if given map contains a text string
124 bool MapContainsTextString(Property::Map& map)
127 Property::Value* value = map.Find(Toolkit::TextVisual::Property::TEXT);
130 std::string textString;
131 value->Get(textString);
132 if(!textString.empty())
140 } // unnamed namespace
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)
163 void Button::SetAutoRepeating(bool autoRepeating)
165 mAutoRepeating = autoRepeating;
167 // An autorepeating button can't be a toggle button.
172 SetSelected(false); // UnSelect before switching off Toggle feature.
174 mTogglableButton = false;
178 void Button::SetInitialAutoRepeatingDelay(float initialAutoRepeatingDelay)
180 DALI_ASSERT_DEBUG(initialAutoRepeatingDelay > 0.f);
181 mInitialAutoRepeatingDelay = initialAutoRepeatingDelay;
184 void Button::SetNextAutoRepeatingDelay(float nextAutoRepeatingDelay)
186 DALI_ASSERT_DEBUG(nextAutoRepeatingDelay > 0.f);
187 mNextAutoRepeatingDelay = nextAutoRepeatingDelay;
190 void Button::SetTogglableButton(bool togglable)
192 mTogglableButton = togglable;
194 // A toggle button can't be an autorepeating button.
197 mAutoRepeating = false;
201 void Button::SetSelected(bool selected)
205 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::SetSelected (%s)\n", (selected ? "true" : "false"));
207 if(selected && (mButtonState != SELECTED_STATE))
209 ChangeState(SELECTED_STATE);
211 else if(!selected && (mButtonState != UNSELECTED_STATE))
213 ChangeState(UNSELECTED_STATE);
218 void Button::SetDisabled(bool disabled)
220 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::SetDisabled(%s) state(%d)\n", (disabled) ? "disabled" : "active", mButtonState);
224 if(mButtonState == SELECTED_STATE)
226 ChangeState(DISABLED_SELECTED_STATE);
228 else if(mButtonState == UNSELECTED_STATE)
230 ChangeState(DISABLED_UNSELECTED_STATE);
235 if(mButtonState == DISABLED_SELECTED_STATE)
237 ChangeState(SELECTED_STATE);
239 else if(mButtonState == DISABLED_UNSELECTED_STATE)
241 ChangeState(UNSELECTED_STATE);
246 bool Button::IsDisabled() const
248 return (mButtonState == DISABLED_SELECTED_STATE || mButtonState == DISABLED_UNSELECTED_STATE);
251 bool Button::ValidateState(State requestedState)
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.
257 to| UNSELECTED_STATE | SELECTED_STATE | DISABLED_UNSELECTED_STATE | DISABLED_SELECTED_STATE |*/
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}};
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");
266 return transitionTable[mButtonState][requestedState];
269 void Button::ChangeState(State requestedState)
271 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::ChangeState ReuestedState(%d)\n", requestedState);
273 // Validate State before changing
274 if(!ValidateState(requestedState))
276 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::ChangeState ReuestedState(%d) not validated\n", requestedState);
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
284 if(Self().GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
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]);
297 Toolkit::Button handle(GetOwner());
299 mStateChangedSignal.Emit(handle);
302 bool Button::IsSelected() const
304 bool selected = (mButtonState == SELECTED_STATE) || (mButtonState == DISABLED_SELECTED_STATE);
305 return mTogglableButton && selected;
308 void Button::MergeWithExistingLabelProperties(const Property::Map& inMap, Property::Map& outMap)
310 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "MergeLabelProperties with %d properties\n", inMap.Count());
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.
316 * 1) Find Label Visual
317 * 2) Retrieve current properties ( settings )
318 * 3) Merge with new properties ( settings )
319 * 4) Return new merged map
321 Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, Toolkit::Button::Property::LABEL);
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());
331 // Store if a text string has been supplied.
333 mTextStringSetFlag = MapContainsTextString(outMap);
335 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "MergeLabelProperties now has %d properties\n", outMap.Count());
338 void Button::SetLabelAlignment(Button::Align labelAlignment)
340 mTextLabelAlignment = labelAlignment;
344 Button::Align Button::GetLabelAlignment()
346 return mTextLabelAlignment;
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
356 void Button::CreateVisualsForComponent(Property::Index index, const Property::Value& value, const int visualDepth)
358 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "CreateVisualsForComponent index(%d)\n", index);
359 Toolkit::VisualFactory visualFactory = Toolkit::VisualFactory::Get();
360 Toolkit::Visual::Base buttonVisual;
362 std::string imageUrl;
363 if(value.Get(imageUrl))
365 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "CreateVisualsForComponent Using image URL(%d)\n", index);
366 if(!imageUrl.empty())
368 DALI_ASSERT_DEBUG(index != Toolkit::Button::Property::LABEL && "Creating a Image Visual instead of Text Visual ");
369 buttonVisual = visualFactory.CreateVisual(imageUrl, ImageDimensions());
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.
378 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "CreateVisualsForComponent Using Map(%d)\n", index);
379 buttonVisual = visualFactory.CreateVisual(*map);
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);
394 DevelControl::UnregisterVisual(*this, index);
395 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "CreateVisualsForComponent Visual not created or empty map (clearing visual).(%d)\n", index);
400 bool Button::GetPropertyMapForVisual(Property::Index visualIndex, Property::Map& retreivedMap) const
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);
407 visual.CreatePropertyMap(retreivedMap);
410 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "GetPropertyMapForVisual %s\n", success ? "Success" : "Failure");
414 bool Button::DoAction(BaseObject* object, const std::string& actionName, const Property::Map& attributes)
418 Dali::BaseHandle handle(object);
420 Toolkit::Button button = Toolkit::Button::DownCast(handle);
422 DALI_ASSERT_DEBUG(button);
424 if(0 == strcmp(actionName.c_str(), ACTION_BUTTON_CLICK))
426 ret = GetImplementation(button).DoClickAction(attributes);
432 bool Button::DoClickAction(const Property::Map& attributes)
434 // Prevents the button signals from doing a recursive loop by sending an action
435 // and re-emitting the signals.
436 if(!mClickActionPerforming)
438 mClickActionPerforming = true;
440 if(!mTogglableButton)
442 mButtonPressedState = DEPRESSED;
445 mClickActionPerforming = false;
453 void Button::ButtonDown()
457 if(mButtonState != SELECTED_STATE)
460 mButtonPressedState = TOGGLE_DEPRESSED;
464 mButtonPressedState = DEPRESSED;
470 mButtonPressedState = DEPRESSED;
473 SetUpTimer(mInitialAutoRepeatingDelay);
477 // The pressed signal should be emitted regardless of toggle mode.
478 Toolkit::Button handle(GetOwner());
479 mPressedSignal.Emit(handle);
482 void Button::ButtonUp()
484 bool emitSignalsForPressAndReleaseAction = false;
486 if(DEPRESSED == mButtonPressedState)
488 if(mTogglableButton) // Button up will change state
490 emitSignalsForPressAndReleaseAction = OnToggleReleased(); // Derived toggle buttons can override this to provide custom behaviour
494 Released(); // Button up will result in unselected state
497 mAutoRepeatingTimer.Reset();
499 emitSignalsForPressAndReleaseAction = true;
502 else if(TOGGLE_DEPRESSED == mButtonPressedState)
504 emitSignalsForPressAndReleaseAction = true; // toggle released after being pressed, a click
507 if(emitSignalsForPressAndReleaseAction)
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);
516 bool Button::OnToggleReleased()
518 SetSelected(!IsSelected());
519 mButtonPressedState = UNPRESSED;
523 void Button::OnTouchPointLeave()
525 if(DEPRESSED == mButtonPressedState)
527 if(!mTogglableButton)
533 mAutoRepeatingTimer.Reset();
537 mButtonPressedState = UNPRESSED;
539 // The released signal should be emitted regardless of toggle mode.
540 Toolkit::Button handle(GetOwner());
541 mReleasedSignal.Emit(handle);
545 void Button::OnTouchPointInterrupted()
550 Toolkit::Button::ButtonSignalType& Button::PressedSignal()
552 return mPressedSignal;
555 Toolkit::Button::ButtonSignalType& Button::ReleasedSignal()
557 return mReleasedSignal;
560 Toolkit::Button::ButtonSignalType& Button::ClickedSignal()
562 return mClickedSignal;
565 Toolkit::Button::ButtonSignalType& Button::StateChangedSignal()
567 return mStateChangedSignal;
570 bool Button::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor)
572 Dali::BaseHandle handle(object);
574 bool connected(true);
575 Toolkit::Button button = Toolkit::Button::DownCast(handle);
577 if(0 == strcmp(signalName.c_str(), SIGNAL_PRESSED))
579 button.PressedSignal().Connect(tracker, functor);
581 else if(0 == strcmp(signalName.c_str(), SIGNAL_RELEASED))
583 button.ReleasedSignal().Connect(tracker, functor);
585 else if(0 == strcmp(signalName.c_str(), SIGNAL_CLICKED))
587 button.ClickedSignal().Connect(tracker, functor);
589 else if(0 == strcmp(signalName.c_str(), SIGNAL_STATE_CHANGED))
591 button.StateChangedSignal().Connect(tracker, functor);
595 // signalName does not match any signal
602 void Button::OnInitialize()
604 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::OnInitialize\n");
608 mTapDetector = TapGestureDetector::New();
609 mTapDetector.Attach(self);
610 mTapDetector.DetectedSignal().Connect(this, &Button::OnTap);
612 self.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);
613 self.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_HIGHLIGHTABLE, true);
615 self.TouchedSignal().Connect(this, &Button::OnTouch);
618 bool Button::OnAccessibilityActivated()
620 return OnKeyboardEnter();
623 bool Button::OnTouch(Actor actor, const TouchEvent& touch)
625 if(!IsDisabled() && (actor == touch.GetHitActor(0)))
627 if(1 == touch.GetPointCount())
629 switch(touch.GetState(0))
631 case PointState::DOWN:
641 case PointState::INTERRUPTED:
643 OnTouchPointInterrupted();
646 case PointState::LEAVE:
651 case PointState::MOTION:
652 case PointState::STATIONARY: // FALLTHROUGH
659 else if(1 < touch.GetPointCount())
661 OnTouchPointLeave(); // Notification for derived classes.
663 // Sets the button state to the default
664 mButtonPressedState = UNPRESSED;
670 bool Button::OnKeyboardEnter()
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);
679 void Button::OnSceneDisconnection()
681 if(DEPRESSED == mButtonPressedState)
683 if(!mTogglableButton)
689 mAutoRepeatingTimer.Reset();
694 mButtonPressedState = UNPRESSED;
696 Control::OnSceneDisconnection(); // Visuals will be set off stage
699 void Button::OnSceneConnection(int depth)
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
711 Vector3 Button::GetNaturalSize()
713 Vector3 size = Vector3::ZERO;
715 bool horizontalAlignment = mTextLabelAlignment == BEGIN || mTextLabelAlignment == END; // label and visual side by side
717 // Get natural size of foreground ( largest of the possible visuals )
718 Size largestProvidedVisual;
719 Size labelSize = Size::ZERO;
721 bool foreGroundVisualUsed = false;
723 for(int state = Button::UNSELECTED_STATE; state < Button::STATE_COUNT; state++)
725 Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, VISUAL_INDEX_FOR_STATE[state][FOREGROUND]);
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;
736 if(!foreGroundVisualUsed) // If foreground visual not supplied then use the background visual to calculate Natural size
738 for(int state = Button::UNSELECTED_STATE; state < Button::STATE_COUNT; state++)
740 Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, VISUAL_INDEX_FOR_STATE[state][BACKGROUND]);
744 visual.GetNaturalSize(visualSize);
745 largestProvidedVisual.width = std::max(largestProvidedVisual.width, visualSize.width);
746 largestProvidedVisual.height = std::max(largestProvidedVisual.height, visualSize.height);
751 // Get horizontal padding total
752 if(largestProvidedVisual.width > 0) // if visual exists
754 size.width += largestProvidedVisual.width + mForegroundPadding.left + mForegroundPadding.right;
756 // Get vertical padding total
757 if(largestProvidedVisual.height > 0)
759 size.height += largestProvidedVisual.height + mForegroundPadding.top + mForegroundPadding.bottom;
762 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "GetNaturalSize visual Size(%f,%f)\n", largestProvidedVisual.width, largestProvidedVisual.height);
764 // Get natural size of label if text has been set
765 if(mTextStringSetFlag)
767 Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, Toolkit::Button::Property::LABEL);
771 visual.GetNaturalSize(labelSize);
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);
775 labelSize.width += mLabelPadding.left + mLabelPadding.right;
776 labelSize.height += mLabelPadding.top + mLabelPadding.bottom;
778 // Add label size to height or width depending on alignment position
779 if(horizontalAlignment)
781 size.width += labelSize.width;
782 size.height = std::max(size.height, labelSize.height);
786 size.height += labelSize.height;
787 size.width = std::max(size.width, labelSize.width);
792 if(size.width < 1 && size.height < 1)
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();
799 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "Button GetNaturalSize (%f,%f)\n", size.width, size.height);
804 void Button::OnSetResizePolicy(ResizePolicy::Type policy, Dimension::Type dimension)
806 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnSetResizePolicy\n");
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.
815 void Button::OnRelayout(const Vector2& size, RelayoutContainer& container)
817 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout targetSize(%f,%f) ptr(%p) state[%d]\n", size.width, size.height, this, mButtonState);
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]);
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);
829 if(mTextStringSetFlag)
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;
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;
842 Toolkit::Align::Type visualAnchorPoint = Toolkit::Align::TOP_BEGIN;
844 Vector2 visualAndPaddingSize = Vector2((foregroundVisualPadding.x + visualSize.width + foregroundVisualPadding.y),
845 (foregroundVisualPadding.width + visualSize.height + foregroundVisualPadding.height));
847 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout visualAndPaddingSize(%f,%f)\n", visualAndPaddingSize.width, visualAndPaddingSize.height);
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;
853 switch(mTextLabelAlignment)
857 visualAnchorPoint = Toolkit::Align::TOP_END;
858 visualPosition.x = foregroundVisualPadding.right;
859 visualPosition.y = foregroundVisualPadding.top;
861 labelPosition.x = labelVisualPadding.x;
862 labelPosition.y = labelVisualPadding.top;
864 remainingSpaceForText.width = size.width - visualAndPaddingSize.width - labelVisualPadding.x - labelVisualPadding.y;
865 remainingSpaceForText.height = size.height - labelVisualPadding.top - labelVisualPadding.bottom;
870 visualAnchorPoint = Toolkit::Align::TOP_BEGIN;
871 visualPosition.x = foregroundVisualPadding.left;
872 visualPosition.y = foregroundVisualPadding.top;
874 labelPosition.x = visualAndPaddingSize.width + labelVisualPadding.x;
875 labelPosition.y = labelVisualPadding.top;
877 remainingSpaceForText.width = size.width - visualAndPaddingSize.width - labelVisualPadding.x - labelVisualPadding.y;
878 remainingSpaceForText.height = size.height - labelVisualPadding.top - labelVisualPadding.bottom;
883 visualAnchorPoint = Toolkit::Align::BOTTOM_END;
884 visualPosition.x = foregroundVisualPadding.left;
885 visualPosition.y = foregroundVisualPadding.bottom;
887 labelPosition.x = labelVisualPadding.left;
888 labelPosition.y = labelVisualPadding.top;
890 remainingSpaceForText.width = size.width - labelVisualPadding.x - labelVisualPadding.y;
891 remainingSpaceForText.height = size.height - visualAndPaddingSize.height - labelVisualPadding.top - labelVisualPadding.bottom;
897 visualAnchorPoint = Toolkit::Align::TOP_END;
898 visualPosition.x = foregroundVisualPadding.left;
899 visualPosition.y = foregroundVisualPadding.top;
901 labelPosition.x = labelVisualPadding.left;
902 labelPosition.y = visualAndPaddingSize.height + labelVisualPadding.top;
904 remainingSpaceForText.width = size.width - labelVisualPadding.x - labelVisualPadding.y;
905 remainingSpaceForText.height = size.height - visualAndPaddingSize.height - labelVisualPadding.top - labelVisualPadding.bottom;
911 if(currentBackGroundVisual)
913 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout Setting visual background size to(%f,%f)\n", size.width, size.height);
915 Property::Map visualTransform;
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));
920 currentBackGroundVisual.SetTransformAndSize(visualTransform, size);
925 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout Setting visual size to(%f,%f)\n", visualSize.width, visualSize.height);
927 Property::Map visualTransform;
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);
936 currentVisual.SetTransformAndSize(visualTransform, size);
939 if(mTextStringSetFlag)
941 Toolkit::Visual::Base textVisual = DevelControl::GetVisual(*this, Toolkit::Button::Property::LABEL); // No need to search for Label visual if no text set.
947 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout Only Text\n");
948 labelPosition.x = labelVisualPadding.left;
949 labelPosition.y = labelVisualPadding.height;
952 Vector2 preSize = Vector2(static_cast<int>(remainingSpaceForText.x), static_cast<int>(remainingSpaceForText.y));
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);
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);
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);
966 textVisual.SetTransformAndSize(textVisualTransform, size);
970 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout selected (%s) \n", IsSelected() ? "yes" : "no");
972 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout << \n");
975 void Button::OnTap(Actor actor, const TapGesture& tap)
977 // Prevents Parent getting a tap event
980 void Button::SetUpTimer(float delay)
982 mAutoRepeatingTimer = Dali::Timer::New(static_cast<unsigned int>(1000.f * delay));
983 mAutoRepeatingTimer.TickSignal().Connect(this, &Button::AutoRepeatingSlot);
984 mAutoRepeatingTimer.Start();
987 bool Button::AutoRepeatingSlot()
989 bool consumed = false;
992 // Restart the autorepeat timer.
993 SetUpTimer(mNextAutoRepeatingDelay);
997 Toolkit::Button handle(GetOwner());
1000 consumed = mReleasedSignal.Emit(handle);
1001 consumed = mClickedSignal.Emit(handle);
1002 consumed |= mPressedSignal.Emit(handle);
1008 void Button::Pressed()
1010 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::Pressed\n");
1012 if(mButtonState == UNSELECTED_STATE)
1014 ChangeState(SELECTED_STATE);
1015 OnPressed(); // Notifies the derived class the button has been pressed.
1019 void Button::Released()
1021 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::Released\n");
1023 if(mButtonState == SELECTED_STATE && !mTogglableButton)
1025 ChangeState(UNSELECTED_STATE);
1026 OnReleased(); // // Notifies the derived class the button has been released.
1028 mButtonPressedState = UNPRESSED;
1031 void Button::SelectRequiredVisual(Property::Index visualIndex)
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))
1037 DevelControl::EnableVisual(*this, visualIndex, true);
1041 void Button::RemoveVisual(Property::Index visualIndex)
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);
1046 Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, visualIndex);
1050 DevelControl::EnableVisual(*this, visualIndex, false);
1054 void Button::OnButtonVisualRemoval(Property::Index visualIndex)
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);
1061 void Button::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value)
1063 Toolkit::Button button = Toolkit::Button::DownCast(Dali::BaseHandle(object));
1065 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::SetProperty index[%d]\n", index);
1071 case Toolkit::Button::Property::DISABLED:
1073 GetImplementation(button).SetDisabled(value.Get<bool>());
1077 case Toolkit::Button::Property::AUTO_REPEATING:
1079 GetImplementation(button).SetAutoRepeating(value.Get<bool>());
1083 case Toolkit::Button::Property::INITIAL_AUTO_REPEATING_DELAY:
1085 GetImplementation(button).SetInitialAutoRepeatingDelay(value.Get<float>());
1089 case Toolkit::Button::Property::NEXT_AUTO_REPEATING_DELAY:
1091 GetImplementation(button).SetNextAutoRepeatingDelay(value.Get<float>());
1095 case Toolkit::Button::Property::TOGGLABLE:
1097 GetImplementation(button).SetTogglableButton(value.Get<bool>());
1101 case Toolkit::Button::Property::SELECTED:
1103 GetImplementation(button).SetSelected(value.Get<bool>());
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:
1112 GetImplementation(button).CreateVisualsForComponent(index, value, DepthIndex::CONTENT);
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:
1121 GetImplementation(button).CreateVisualsForComponent(index, value, DepthIndex::BACKGROUND);
1125 case Toolkit::Button::Property::LABEL:
1127 Property::Map outTextVisualProperties;
1128 std::string textString;
1130 if(value.Get(textString))
1132 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::SetProperty Setting TextVisual with string[%s]\n", textString.c_str());
1134 Property::Map setPropertyMap;
1135 setPropertyMap.Add(Toolkit::Visual::Property::TYPE, Toolkit::Visual::TEXT)
1136 .Add(Toolkit::TextVisual::Property::TEXT, textString);
1138 GetImplementation(button).MergeWithExistingLabelProperties(setPropertyMap, outTextVisualProperties);
1142 // Get a Property::Map from the property if possible.
1143 const Property::Map* setPropertyMap = value.GetMap();
1146 Property::Map indexKeys = TextVisual::ConvertStringKeysToIndexKeys(*setPropertyMap);
1147 GetImplementation(button).MergeWithExistingLabelProperties(indexKeys, outTextVisualProperties);
1151 if(!outTextVisualProperties.Empty())
1153 GetImplementation(button).CreateVisualsForComponent(index, outTextVisualProperties, DepthIndex::CONTENT);
1158 case Toolkit::DevelButton::Property::LABEL_RELATIVE_ALIGNMENT:
1160 Button::Align labelAlignment(END);
1161 Scripting::GetEnumeration<Button::Align>(value.Get<std::string>().c_str(),
1163 ALIGNMENT_TABLE_COUNT,
1166 GetImplementation(button).SetLabelAlignment(labelAlignment);
1170 case Toolkit::DevelButton::Property::LABEL_PADDING:
1172 Vector4 padding(value.Get<Vector4>());
1173 GetImplementation(button).SetLabelPadding(Padding(padding.x, padding.y, padding.z, padding.w));
1177 case Toolkit::DevelButton::Property::VISUAL_PADDING:
1179 Vector4 padding(value.Get<Vector4>());
1180 GetImplementation(button).SetForegroundPadding(Padding(padding.x, padding.y, padding.z, padding.w));
1187 Property::Value Button::GetProperty(BaseObject* object, Property::Index propertyIndex)
1189 Property::Value value;
1191 Toolkit::Button button = Toolkit::Button::DownCast(Dali::BaseHandle(object));
1195 switch(propertyIndex)
1197 case Toolkit::Button::Property::DISABLED:
1199 value = GetImplementation(button).IsDisabled();
1203 case Toolkit::Button::Property::AUTO_REPEATING:
1205 value = GetImplementation(button).mAutoRepeating;
1209 case Toolkit::Button::Property::INITIAL_AUTO_REPEATING_DELAY:
1211 value = GetImplementation(button).mInitialAutoRepeatingDelay;
1215 case Toolkit::Button::Property::NEXT_AUTO_REPEATING_DELAY:
1217 value = GetImplementation(button).mNextAutoRepeatingDelay;
1221 case Toolkit::Button::Property::TOGGLABLE:
1223 value = GetImplementation(button).mTogglableButton;
1227 case Toolkit::Button::Property::SELECTED:
1229 value = GetImplementation(button).IsSelected();
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:
1243 Property::Map visualProperty;
1244 if(GetImplementation(button).GetPropertyMapForVisual(propertyIndex, visualProperty))
1246 value = visualProperty;
1251 case Toolkit::DevelButton::Property::LABEL_RELATIVE_ALIGNMENT:
1253 const char* alignment = Scripting::GetEnumerationName<Button::Align>(GetImplementation(button).GetLabelAlignment(),
1254 ALIGNMENT_STRING_TABLE,
1255 ALIGNMENT_STRING_TABLE_COUNT);
1258 value = std::string(alignment);
1264 case Toolkit::DevelButton::Property::LABEL_PADDING:
1266 Padding padding = GetImplementation(button).GetLabelPadding();
1267 value = Vector4(padding.x, padding.y, padding.top, padding.bottom);
1271 case Toolkit::DevelButton::Property::VISUAL_PADDING:
1273 Padding padding = GetImplementation(button).GetForegroundPadding();
1274 value = Vector4(padding.x, padding.y, padding.top, padding.bottom);
1282 void Button::SetLabelPadding(const Padding& padding)
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);
1289 Padding Button::GetLabelPadding()
1291 return mLabelPadding;
1294 void Button::SetForegroundPadding(const Padding& padding)
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);
1301 Padding Button::GetForegroundPadding()
1303 return mForegroundPadding;
1306 std::string Button::AccessibleImpl::GetNameRaw()
1308 std::string labelText;
1309 auto slf = Toolkit::Button::DownCast(self);
1310 Property::Map labelMap = slf.GetProperty<Property::Map>(Toolkit::Button::Property::LABEL);
1312 Property::Value* textPropertyPtr = labelMap.Find(Toolkit::TextVisual::Property::TEXT);
1315 textPropertyPtr->Get(labelText);
1321 Property::Index Button::AccessibleImpl::GetNamePropertyIndex()
1323 Property::Index label = Toolkit::Button::Property::LABEL;
1324 Property::Map labelMap = self.GetProperty<Property::Map>(label);
1326 if(MapContainsTextString(labelMap))
1329 return Property::INVALID_INDEX;
1332 Dali::Accessibility::States Button::AccessibleImpl::CalculateStates()
1334 auto tmp = Control::Impl::AccessibleImpl::CalculateStates();
1335 tmp[Dali::Accessibility::State::SELECTABLE] = true;
1336 auto slf = Toolkit::Button::DownCast(self);
1337 tmp[Dali::Accessibility::State::ENABLED] = !slf.GetProperty<bool>(Toolkit::Button::Property::DISABLED);
1338 tmp[Dali::Accessibility::State::CHECKED] = slf.GetProperty<bool>(Toolkit::Button::Property::SELECTED);
1342 } // namespace Internal
1344 } // namespace Toolkit