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][Button::VISUAL_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 DevelControl::ControlAccessible* Button::CreateAccessibleObject()
625 return new ButtonAccessible(Self());
628 bool Button::OnTouch(Actor actor, const TouchEvent& touch)
630 if(!IsDisabled() && (actor == touch.GetHitActor(0)))
632 if(1 == touch.GetPointCount())
634 switch(touch.GetState(0))
636 case PointState::DOWN:
646 case PointState::INTERRUPTED:
648 OnTouchPointInterrupted();
651 case PointState::LEAVE:
656 case PointState::MOTION:
657 case PointState::STATIONARY: // FALLTHROUGH
664 else if(1 < touch.GetPointCount())
666 OnTouchPointLeave(); // Notification for derived classes.
668 // Sets the button state to the default
669 mButtonPressedState = UNPRESSED;
675 bool Button::OnKeyboardEnter()
677 // When the enter key is pressed, or button is activated, the click action is performed.
678 Property::Map attributes;
679 bool ret = DoClickAction(attributes);
684 void Button::OnSceneDisconnection()
686 if(DEPRESSED == mButtonPressedState)
688 if(!mTogglableButton)
694 mAutoRepeatingTimer.Reset();
699 mButtonPressedState = UNPRESSED;
701 Control::OnSceneDisconnection(); // Visuals will be set off stage
704 void Button::OnSceneConnection(int depth)
706 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::OnSceneConnection ptr(%p) \n", this);
707 OnButtonVisualRemoval(VISUAL_INDEX_FOR_STATE[mPreviousButtonState][BACKGROUND]);
708 OnButtonVisualRemoval(VISUAL_INDEX_FOR_STATE[mPreviousButtonState][FOREGROUND]);
709 SelectRequiredVisual(Toolkit::Button::Property::LABEL);
710 SelectRequiredVisual(VISUAL_INDEX_FOR_STATE[mButtonState][BACKGROUND]);
711 SelectRequiredVisual(VISUAL_INDEX_FOR_STATE[mButtonState][FOREGROUND]);
712 Control::OnSceneConnection(depth); // Enabled visuals will be put on stage
716 Vector3 Button::GetNaturalSize()
718 Vector3 size = Vector3::ZERO;
720 bool horizontalAlignment = mTextLabelAlignment == BEGIN || mTextLabelAlignment == END; // label and visual side by side
722 // Get natural size of foreground ( largest of the possible visuals )
723 Size largestProvidedVisual;
724 Size labelSize = Size::ZERO;
726 bool foreGroundVisualUsed = false;
728 for(int state = Button::UNSELECTED_STATE; state < Button::STATE_COUNT; state++)
730 Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, VISUAL_INDEX_FOR_STATE[state][FOREGROUND]);
734 visual.GetNaturalSize(visualSize);
735 largestProvidedVisual.width = std::max(largestProvidedVisual.width, visualSize.width);
736 largestProvidedVisual.height = std::max(largestProvidedVisual.height, visualSize.height);
737 foreGroundVisualUsed = true;
741 if(!foreGroundVisualUsed) // If foreground visual not supplied then use the background visual to calculate Natural size
743 for(int state = Button::UNSELECTED_STATE; state < Button::STATE_COUNT; state++)
745 Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, VISUAL_INDEX_FOR_STATE[state][BACKGROUND]);
749 visual.GetNaturalSize(visualSize);
750 largestProvidedVisual.width = std::max(largestProvidedVisual.width, visualSize.width);
751 largestProvidedVisual.height = std::max(largestProvidedVisual.height, visualSize.height);
756 // Get horizontal padding total
757 if(largestProvidedVisual.width > 0) // if visual exists
759 size.width += largestProvidedVisual.width + mForegroundPadding.left + mForegroundPadding.right;
761 // Get vertical padding total
762 if(largestProvidedVisual.height > 0)
764 size.height += largestProvidedVisual.height + mForegroundPadding.top + mForegroundPadding.bottom;
767 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "GetNaturalSize visual Size(%f,%f)\n", largestProvidedVisual.width, largestProvidedVisual.height);
769 // Get natural size of label if text has been set
770 if(mTextStringSetFlag)
772 Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, Toolkit::Button::Property::LABEL);
776 visual.GetNaturalSize(labelSize);
778 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);
780 labelSize.width += mLabelPadding.left + mLabelPadding.right;
781 labelSize.height += mLabelPadding.top + mLabelPadding.bottom;
783 // Add label size to height or width depending on alignment position
784 if(horizontalAlignment)
786 size.width += labelSize.width;
787 size.height = std::max(size.height, labelSize.height);
791 size.height += labelSize.height;
792 size.width = std::max(size.width, labelSize.width);
797 if(size.width < 1 && size.height < 1)
799 // if no image or label then use Control's natural size
800 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "GetNaturalSize Using control natural size\n");
801 size = Control::GetNaturalSize();
804 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "Button GetNaturalSize (%f,%f)\n", size.width, size.height);
809 void Button::OnSetResizePolicy(ResizePolicy::Type policy, Dimension::Type dimension)
811 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnSetResizePolicy\n");
816 * Visuals are sized and positioned in this function.
817 * Whilst the control has it's size negotiated it has to size it's visuals explicitly here.
820 void Button::OnRelayout(const Vector2& size, RelayoutContainer& container)
822 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout targetSize(%f,%f) ptr(%p) state[%d]\n", size.width, size.height, this, mButtonState);
824 Toolkit::Visual::Base currentVisual = DevelControl::GetVisual(*this, VISUAL_INDEX_FOR_STATE[mButtonState][FOREGROUND]);
825 Toolkit::Visual::Base currentBackGroundVisual = DevelControl::GetVisual(*this, VISUAL_INDEX_FOR_STATE[mButtonState][BACKGROUND]);
827 // Sizes and padding set to zero, if not present then values will no effect calculations.
828 Vector2 visualPosition = Vector2::ZERO;
829 Vector2 labelPosition = Vector2::ZERO;
830 Size visualSize = Size::ZERO;
831 Padding foregroundVisualPadding = Padding(0.0f, 0.0f, 0.0f, 0.0f);
832 Padding labelVisualPadding = Padding(0.0f, 0.0f, 0.0f, 0.0f);
834 if(mTextStringSetFlag)
836 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout Label padding setting padding:%f,%f,%f,%f\n", mLabelPadding.y, mLabelPadding.x, mLabelPadding.width, mLabelPadding.height);
837 labelVisualPadding = mLabelPadding;
842 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout Foreground Visual setting padding:%f,%f,%f,%f\n", mForegroundPadding.y, mForegroundPadding.x, mForegroundPadding.width, mForegroundPadding.height);
843 currentVisual.GetNaturalSize(visualSize);
844 foregroundVisualPadding = mForegroundPadding;
847 Toolkit::Align::Type visualAnchorPoint = Toolkit::Align::TOP_BEGIN;
849 Vector2 visualAndPaddingSize = Vector2((foregroundVisualPadding.x + visualSize.width + foregroundVisualPadding.y),
850 (foregroundVisualPadding.width + visualSize.height + foregroundVisualPadding.height));
852 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout visualAndPaddingSize(%f,%f)\n", visualAndPaddingSize.width, visualAndPaddingSize.height);
854 // Text Visual should take all space available after foreground visual size and all padding is considered.
855 // Remaining Space priority, Foreground padding, foreground visual, Text padding then Text visual.
856 Size remainingSpaceForText = Size::ZERO;
858 switch(mTextLabelAlignment)
862 visualAnchorPoint = Toolkit::Align::TOP_END;
863 visualPosition.x = foregroundVisualPadding.right;
864 visualPosition.y = foregroundVisualPadding.top;
866 labelPosition.x = labelVisualPadding.x;
867 labelPosition.y = labelVisualPadding.top;
869 remainingSpaceForText.width = size.width - visualAndPaddingSize.width - labelVisualPadding.x - labelVisualPadding.y;
870 remainingSpaceForText.height = size.height - labelVisualPadding.top - labelVisualPadding.bottom;
875 visualAnchorPoint = Toolkit::Align::TOP_BEGIN;
876 visualPosition.x = foregroundVisualPadding.left;
877 visualPosition.y = foregroundVisualPadding.top;
879 labelPosition.x = visualAndPaddingSize.width + labelVisualPadding.x;
880 labelPosition.y = labelVisualPadding.top;
882 remainingSpaceForText.width = size.width - visualAndPaddingSize.width - labelVisualPadding.x - labelVisualPadding.y;
883 remainingSpaceForText.height = size.height - labelVisualPadding.top - labelVisualPadding.bottom;
888 visualAnchorPoint = Toolkit::Align::BOTTOM_END;
889 visualPosition.x = foregroundVisualPadding.left;
890 visualPosition.y = foregroundVisualPadding.bottom;
892 labelPosition.x = labelVisualPadding.left;
893 labelPosition.y = labelVisualPadding.top;
895 remainingSpaceForText.width = size.width - labelVisualPadding.x - labelVisualPadding.y;
896 remainingSpaceForText.height = size.height - visualAndPaddingSize.height - labelVisualPadding.top - labelVisualPadding.bottom;
902 visualAnchorPoint = Toolkit::Align::TOP_END;
903 visualPosition.x = foregroundVisualPadding.left;
904 visualPosition.y = foregroundVisualPadding.top;
906 labelPosition.x = labelVisualPadding.left;
907 labelPosition.y = visualAndPaddingSize.height + labelVisualPadding.top;
909 remainingSpaceForText.width = size.width - labelVisualPadding.x - labelVisualPadding.y;
910 remainingSpaceForText.height = size.height - visualAndPaddingSize.height - labelVisualPadding.top - labelVisualPadding.bottom;
916 if(currentBackGroundVisual)
918 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout Setting visual background size to(%f,%f)\n", size.width, size.height);
920 Property::Map visualTransform;
922 visualTransform.Add(Toolkit::Visual::Transform::Property::SIZE, size)
923 .Add(Toolkit::Visual::Transform::Property::SIZE_POLICY, Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE));
925 currentBackGroundVisual.SetTransformAndSize(visualTransform, size);
930 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout Setting visual size to(%f,%f)\n", visualSize.width, visualSize.height);
932 Property::Map visualTransform;
934 visualTransform.Add(Toolkit::Visual::Transform::Property::SIZE, visualSize)
935 .Add(Toolkit::Visual::Transform::Property::OFFSET, visualPosition)
936 .Add(Toolkit::Visual::Transform::Property::OFFSET_POLICY, Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE))
937 .Add(Toolkit::Visual::Transform::Property::SIZE_POLICY, Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE))
938 .Add(Toolkit::Visual::Transform::Property::ORIGIN, Toolkit::Align::TOP_BEGIN)
939 .Add(Toolkit::Visual::Transform::Property::ANCHOR_POINT, visualAnchorPoint);
941 currentVisual.SetTransformAndSize(visualTransform, size);
944 if(mTextStringSetFlag)
946 Toolkit::Visual::Base textVisual = DevelControl::GetVisual(*this, Toolkit::Button::Property::LABEL); // No need to search for Label visual if no text set.
952 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout Only Text\n");
953 labelPosition.x = labelVisualPadding.left;
954 labelPosition.y = labelVisualPadding.height;
957 Vector2 preSize = Vector2(static_cast<int>(remainingSpaceForText.x), static_cast<int>(remainingSpaceForText.y));
959 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout text Size(%f,%f) text Position(%f,%f) \n", remainingSpaceForText.width, remainingSpaceForText.height, labelPosition.x, labelPosition.y);
961 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout text Size -- (%f,%f) text Position(%f,%f) \n", preSize.width, preSize.height, labelPosition.x, labelPosition.y);
963 Property::Map textVisualTransform;
964 textVisualTransform.Add(Toolkit::Visual::Transform::Property::SIZE, preSize)
965 .Add(Toolkit::Visual::Transform::Property::OFFSET, labelPosition)
966 .Add(Toolkit::Visual::Transform::Property::OFFSET_POLICY, Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE))
967 .Add(Toolkit::Visual::Transform::Property::SIZE_POLICY, Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE))
968 .Add(Toolkit::Visual::Transform::Property::ORIGIN, Toolkit::Align::TOP_BEGIN)
969 .Add(Toolkit::Visual::Transform::Property::ANCHOR_POINT, visualAnchorPoint);
971 textVisual.SetTransformAndSize(textVisualTransform, size);
975 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout selected (%s) \n", IsSelected() ? "yes" : "no");
977 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "OnRelayout << \n");
980 void Button::OnTap(Actor actor, const TapGesture& tap)
982 // Prevents Parent getting a tap event
985 void Button::SetUpTimer(float delay)
987 mAutoRepeatingTimer = Dali::Timer::New(static_cast<unsigned int>(1000.f * delay));
988 mAutoRepeatingTimer.TickSignal().Connect(this, &Button::AutoRepeatingSlot);
989 mAutoRepeatingTimer.Start();
992 bool Button::AutoRepeatingSlot()
994 bool consumed = false;
997 // Restart the autorepeat timer.
998 SetUpTimer(mNextAutoRepeatingDelay);
1002 Toolkit::Button handle(GetOwner());
1005 consumed = mReleasedSignal.Emit(handle);
1006 consumed = mClickedSignal.Emit(handle);
1007 consumed |= mPressedSignal.Emit(handle);
1013 void Button::Pressed()
1015 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::Pressed\n");
1017 if(mButtonState == UNSELECTED_STATE)
1019 ChangeState(SELECTED_STATE);
1020 OnPressed(); // Notifies the derived class the button has been pressed.
1024 void Button::Released()
1026 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::Released\n");
1028 if(mButtonState == SELECTED_STATE && !mTogglableButton)
1030 ChangeState(UNSELECTED_STATE);
1031 OnReleased(); // // Notifies the derived class the button has been released.
1033 mButtonPressedState = UNPRESSED;
1036 void Button::SelectRequiredVisual(Property::Index visualIndex)
1038 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::SelectRequiredVisual index(%d) state(%d)\n", visualIndex, mButtonState);
1039 // only enable visuals that exist
1040 if(DevelControl::GetVisual(*this, visualIndex))
1042 DevelControl::EnableVisual(*this, visualIndex, true);
1046 void Button::RemoveVisual(Property::Index visualIndex)
1048 // Use OnButtonVisualRemoval if want button developer to have the option to override removal.
1049 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::RemoveVisual index(%d) state(%d)\n", visualIndex, mButtonState);
1051 Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, visualIndex);
1055 DevelControl::EnableVisual(*this, visualIndex, false);
1059 void Button::OnButtonVisualRemoval(Property::Index visualIndex)
1061 // Derived Buttons can over ride this to prevent default removal.
1062 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::OnButtonVisualRemoval index(%d)\n", visualIndex);
1063 RemoveVisual(visualIndex);
1066 void Button::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value)
1068 Toolkit::Button button = Toolkit::Button::DownCast(Dali::BaseHandle(object));
1070 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::SetProperty index[%d]\n", index);
1076 case Toolkit::Button::Property::DISABLED:
1078 GetImplementation(button).SetDisabled(value.Get<bool>());
1082 case Toolkit::Button::Property::AUTO_REPEATING:
1084 GetImplementation(button).SetAutoRepeating(value.Get<bool>());
1088 case Toolkit::Button::Property::INITIAL_AUTO_REPEATING_DELAY:
1090 GetImplementation(button).SetInitialAutoRepeatingDelay(value.Get<float>());
1094 case Toolkit::Button::Property::NEXT_AUTO_REPEATING_DELAY:
1096 GetImplementation(button).SetNextAutoRepeatingDelay(value.Get<float>());
1100 case Toolkit::Button::Property::TOGGLABLE:
1102 GetImplementation(button).SetTogglableButton(value.Get<bool>());
1106 case Toolkit::Button::Property::SELECTED:
1108 GetImplementation(button).SetSelected(value.Get<bool>());
1112 case Toolkit::Button::Property::UNSELECTED_VISUAL:
1113 case Toolkit::Button::Property::SELECTED_VISUAL:
1114 case Toolkit::Button::Property::DISABLED_SELECTED_VISUAL:
1115 case Toolkit::Button::Property::DISABLED_UNSELECTED_VISUAL:
1117 GetImplementation(button).CreateVisualsForComponent(index, value, DepthIndex::CONTENT);
1121 case Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL:
1122 case Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL:
1123 case Toolkit::Button::Property::DISABLED_SELECTED_BACKGROUND_VISUAL:
1124 case Toolkit::Button::Property::DISABLED_UNSELECTED_BACKGROUND_VISUAL:
1126 GetImplementation(button).CreateVisualsForComponent(index, value, DepthIndex::BACKGROUND);
1130 case Toolkit::Button::Property::LABEL:
1132 Property::Map outTextVisualProperties;
1133 std::string textString;
1135 if(value.Get(textString))
1137 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::SetProperty Setting TextVisual with string[%s]\n", textString.c_str());
1139 Property::Map setPropertyMap;
1140 setPropertyMap.Add(Toolkit::Visual::Property::TYPE, Toolkit::Visual::TEXT)
1141 .Add(Toolkit::TextVisual::Property::TEXT, textString);
1143 GetImplementation(button).MergeWithExistingLabelProperties(setPropertyMap, outTextVisualProperties);
1147 // Get a Property::Map from the property if possible.
1148 const Property::Map* setPropertyMap = value.GetMap();
1151 Property::Map indexKeys = TextVisual::ConvertStringKeysToIndexKeys(*setPropertyMap);
1152 GetImplementation(button).MergeWithExistingLabelProperties(indexKeys, outTextVisualProperties);
1156 if(!outTextVisualProperties.Empty())
1158 GetImplementation(button).CreateVisualsForComponent(index, outTextVisualProperties, DepthIndex::CONTENT);
1163 case Toolkit::DevelButton::Property::LABEL_RELATIVE_ALIGNMENT:
1165 Button::Align labelAlignment(END);
1166 Scripting::GetEnumeration<Button::Align>(value.Get<std::string>().c_str(),
1168 ALIGNMENT_TABLE_COUNT,
1171 GetImplementation(button).SetLabelAlignment(labelAlignment);
1175 case Toolkit::DevelButton::Property::LABEL_PADDING:
1177 Vector4 padding(value.Get<Vector4>());
1178 GetImplementation(button).SetLabelPadding(Padding(padding.x, padding.y, padding.z, padding.w));
1182 case Toolkit::DevelButton::Property::VISUAL_PADDING:
1184 Vector4 padding(value.Get<Vector4>());
1185 GetImplementation(button).SetForegroundPadding(Padding(padding.x, padding.y, padding.z, padding.w));
1192 Property::Value Button::GetProperty(BaseObject* object, Property::Index propertyIndex)
1194 Property::Value value;
1196 Toolkit::Button button = Toolkit::Button::DownCast(Dali::BaseHandle(object));
1200 switch(propertyIndex)
1202 case Toolkit::Button::Property::DISABLED:
1204 value = GetImplementation(button).IsDisabled();
1208 case Toolkit::Button::Property::AUTO_REPEATING:
1210 value = GetImplementation(button).mAutoRepeating;
1214 case Toolkit::Button::Property::INITIAL_AUTO_REPEATING_DELAY:
1216 value = GetImplementation(button).mInitialAutoRepeatingDelay;
1220 case Toolkit::Button::Property::NEXT_AUTO_REPEATING_DELAY:
1222 value = GetImplementation(button).mNextAutoRepeatingDelay;
1226 case Toolkit::Button::Property::TOGGLABLE:
1228 value = GetImplementation(button).mTogglableButton;
1232 case Toolkit::Button::Property::SELECTED:
1234 value = GetImplementation(button).IsSelected();
1238 case Toolkit::Button::Property::UNSELECTED_VISUAL:
1239 case Toolkit::Button::Property::SELECTED_VISUAL:
1240 case Toolkit::Button::Property::DISABLED_SELECTED_VISUAL:
1241 case Toolkit::Button::Property::DISABLED_UNSELECTED_VISUAL:
1242 case Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL:
1243 case Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL:
1244 case Toolkit::Button::Property::DISABLED_SELECTED_BACKGROUND_VISUAL:
1245 case Toolkit::Button::Property::DISABLED_UNSELECTED_BACKGROUND_VISUAL:
1246 case Toolkit::Button::Property::LABEL:
1248 Property::Map visualProperty;
1249 if(GetImplementation(button).GetPropertyMapForVisual(propertyIndex, visualProperty))
1251 value = visualProperty;
1256 case Toolkit::DevelButton::Property::LABEL_RELATIVE_ALIGNMENT:
1258 const char* alignment = Scripting::GetEnumerationName<Button::Align>(GetImplementation(button).GetLabelAlignment(),
1259 ALIGNMENT_STRING_TABLE,
1260 ALIGNMENT_STRING_TABLE_COUNT);
1263 value = std::string(alignment);
1269 case Toolkit::DevelButton::Property::LABEL_PADDING:
1271 Padding padding = GetImplementation(button).GetLabelPadding();
1272 value = Vector4(padding.x, padding.y, padding.top, padding.bottom);
1276 case Toolkit::DevelButton::Property::VISUAL_PADDING:
1278 Padding padding = GetImplementation(button).GetForegroundPadding();
1279 value = Vector4(padding.x, padding.y, padding.top, padding.bottom);
1287 void Button::SetLabelPadding(const Padding& padding)
1289 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::SetLabelPadding padding(%f,%f,%f,%f)\n", padding.left, padding.right, padding.bottom, padding.top);
1290 mLabelPadding = Padding(padding.left, padding.right, padding.bottom, padding.top);
1294 Padding Button::GetLabelPadding()
1296 return mLabelPadding;
1299 void Button::SetForegroundPadding(const Padding& padding)
1301 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "Button::SetForegroundPadding padding(%f,%f,%f,%f)\n", padding.left, padding.right, padding.bottom, padding.top);
1302 mForegroundPadding = Padding(padding.left, padding.right, padding.bottom, padding.top);
1306 Padding Button::GetForegroundPadding()
1308 return mForegroundPadding;
1311 std::string Button::ButtonAccessible::GetNameRaw() const
1313 std::string labelText;
1314 auto slf = Toolkit::Button::DownCast(Self());
1315 Property::Map labelMap = slf.GetProperty<Property::Map>(Toolkit::Button::Property::LABEL);
1317 Property::Value* textPropertyPtr = labelMap.Find(Toolkit::TextVisual::Property::TEXT);
1320 textPropertyPtr->Get(labelText);
1326 Property::Index Button::ButtonAccessible::GetNamePropertyIndex()
1328 Property::Index label = Toolkit::Button::Property::LABEL;
1329 Property::Map labelMap = Self().GetProperty<Property::Map>(label);
1331 if(MapContainsTextString(labelMap))
1337 return Property::INVALID_INDEX;
1341 Dali::Accessibility::States Button::ButtonAccessible::CalculateStates()
1343 auto tmp = DevelControl::ControlAccessible::CalculateStates();
1344 tmp[Dali::Accessibility::State::SELECTABLE] = true;
1345 auto slf = Toolkit::Button::DownCast(Self());
1346 tmp[Dali::Accessibility::State::ENABLED] = !slf.GetProperty<bool>(Toolkit::Button::Property::DISABLED);
1347 tmp[Dali::Accessibility::State::CHECKED] = slf.GetProperty<bool>(Toolkit::Button::Property::SELECTED);
1351 } // namespace Internal
1353 } // namespace Toolkit