2 * Copyright (c) 2019 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 <cstring> // for strcmp
23 #include <dali/devel-api/scripting/enum-helper.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/public-api/events/touch-data.h>
26 #include <dali/public-api/images/resource-image.h>
27 #include <dali/public-api/object/type-registry.h>
28 #include <dali/public-api/object/type-registry-helper.h>
29 #include <dali/public-api/size-negotiation/relayout-container.h>
30 #include <dali/devel-api/object/property-helper-devel.h>
31 #include <dali/devel-api/scripting/scripting.h>
34 #include <dali-toolkit/public-api/controls/text-controls/text-label.h>
35 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
36 #include <dali-toolkit/public-api/visuals/color-visual-properties.h>
37 #include <dali-toolkit/public-api/visuals/image-visual-properties.h>
38 #include <dali-toolkit/public-api/align-enumerations.h>
39 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
40 #include <dali-toolkit/devel-api/controls/control-devel.h>
41 #include <dali-toolkit/devel-api/controls/buttons/button-devel.h>
42 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
43 #include <dali-toolkit/internal/visuals/text/text-visual.h>
44 #include <dali-toolkit/public-api/visuals/text-visual-properties.h>
45 #include <dali-toolkit/public-api/visuals/visual-properties.h>
47 #if defined(DEBUG_ENABLED)
48 Debug::Filter* gLogButtonFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_BUTTON_CONTROL");
65 // empty handle as we cannot create button (but type registered for clicked signal)
69 // Setup properties, signals and actions using the type-registry.
70 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::Button, Toolkit::Control, Create )
72 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "disabled", BOOLEAN, DISABLED )
73 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "autoRepeating", BOOLEAN, AUTO_REPEATING )
74 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "initialAutoRepeatingDelay", FLOAT, INITIAL_AUTO_REPEATING_DELAY )
75 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "nextAutoRepeatingDelay", FLOAT, NEXT_AUTO_REPEATING_DELAY )
76 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "togglable", BOOLEAN, TOGGLABLE )
77 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "selected", BOOLEAN, SELECTED )
78 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "unselectedVisual", MAP, UNSELECTED_VISUAL )
79 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "selectedVisual", MAP, SELECTED_VISUAL )
80 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "disabledSelectedVisual", MAP, DISABLED_SELECTED_VISUAL )
81 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "disabledUnselectedVisual", MAP, DISABLED_UNSELECTED_VISUAL )
82 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "unselectedBackgroundVisual", MAP, UNSELECTED_BACKGROUND_VISUAL )
83 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "label", MAP, LABEL )
84 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "selectedBackgroundVisual", MAP, SELECTED_BACKGROUND_VISUAL )
85 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "disabledUnselectedBackgroundVisual", MAP, DISABLED_UNSELECTED_BACKGROUND_VISUAL )
86 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "disabledSelectedBackgroundVisual", MAP, DISABLED_SELECTED_BACKGROUND_VISUAL )
87 DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, Button, "labelRelativeAlignment", STRING, LABEL_RELATIVE_ALIGNMENT )
88 DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, Button, "labelPadding", VECTOR4, LABEL_PADDING )
89 DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, Button, "visualPadding", VECTOR4, VISUAL_PADDING )
92 DALI_SIGNAL_REGISTRATION( Toolkit, Button, "pressed", SIGNAL_PRESSED )
93 DALI_SIGNAL_REGISTRATION( Toolkit, Button, "released", SIGNAL_RELEASED )
94 DALI_SIGNAL_REGISTRATION( Toolkit, Button, "clicked", SIGNAL_CLICKED )
95 DALI_SIGNAL_REGISTRATION( Toolkit, Button, "stateChanged", SIGNAL_STATE_CHANGED )
98 DALI_ACTION_REGISTRATION( Toolkit, Button, "buttonClick", ACTION_BUTTON_CLICK )
100 DALI_TYPE_REGISTRATION_END()
102 DALI_ENUM_TO_STRING_TABLE_BEGIN( ALIGNMENT )
103 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::Internal::Button, BEGIN )
104 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::Internal::Button, END )
105 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::Internal::Button, TOP )
106 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::Internal::Button, BOTTOM )
107 DALI_ENUM_TO_STRING_TABLE_END( ALIGNMENT )
109 const Scripting::StringEnum ALIGNMENT_STRING_TABLE[] =
111 { "BEGIN", Button::BEGIN },
112 { "END", Button::END },
113 { "TOP", Button::TOP },
114 { "BOTTOM", Button::BOTTOM },
117 const unsigned int ALIGNMENT_STRING_TABLE_COUNT = sizeof( ALIGNMENT_STRING_TABLE ) / sizeof( ALIGNMENT_STRING_TABLE[0] );
119 const Property::Index VISUAL_INDEX_FOR_STATE[][Button::STATE_COUNT] =
121 { Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, Toolkit::Button::Property::UNSELECTED_VISUAL },
122 { Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, Toolkit::Button::Property::SELECTED_VISUAL },
123 { Toolkit::Button::Property::DISABLED_UNSELECTED_BACKGROUND_VISUAL, Toolkit::Button::Property::DISABLED_UNSELECTED_VISUAL },
124 { Toolkit::Button::Property::DISABLED_SELECTED_BACKGROUND_VISUAL, Toolkit::Button::Property::DISABLED_SELECTED_VISUAL }
128 * Checks if given map contains a text string
130 bool MapContainsTextString( Property::Map& map )
133 Property::Value* value = map.Find( Toolkit::TextVisual::Property::TEXT );
136 std::string textString;
137 value->Get( textString );
138 if ( !textString.empty() )
146 } // unnamed namespace
149 : Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
150 mAutoRepeatingTimer(),
151 mTextLabelAlignment( END ),
152 mAutoRepeating( false ),
153 mTogglableButton( false ),
154 mTextStringSetFlag( false ),
155 mInitialAutoRepeatingDelay( 0.0f ),
156 mNextAutoRepeatingDelay( 0.0f ),
157 mAnimationTime( 0.0f ),
158 mButtonPressedState( UNPRESSED ),
159 mButtonState( UNSELECTED_STATE ),
160 mPreviousButtonState( mButtonState ),
161 mClickActionPerforming( false )
169 void Button::SetAutoRepeating( bool autoRepeating )
171 mAutoRepeating = autoRepeating;
173 // An autorepeating button can't be a toggle button.
178 SetSelected( false ); // UnSelect before switching off Toggle feature.
180 mTogglableButton = false;
184 void Button::SetInitialAutoRepeatingDelay( float initialAutoRepeatingDelay )
186 DALI_ASSERT_DEBUG( initialAutoRepeatingDelay > 0.f );
187 mInitialAutoRepeatingDelay = initialAutoRepeatingDelay;
190 void Button::SetNextAutoRepeatingDelay( float nextAutoRepeatingDelay )
192 DALI_ASSERT_DEBUG( nextAutoRepeatingDelay > 0.f );
193 mNextAutoRepeatingDelay = nextAutoRepeatingDelay;
196 void Button::SetTogglableButton( bool togglable )
198 mTogglableButton = togglable;
200 // A toggle button can't be an autorepeating button.
203 mAutoRepeating = false;
207 void Button::SetSelected( bool selected )
209 if( mTogglableButton )
211 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::SetSelected (%s)\n", (selected?"true":"false") );
213 if ( selected && ( mButtonState != SELECTED_STATE ) )
215 ChangeState( SELECTED_STATE );
217 else if ( !selected && ( mButtonState != UNSELECTED_STATE ) )
219 ChangeState( UNSELECTED_STATE );
224 void Button::SetDisabled( bool disabled )
226 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::SetDisabled(%s) state(%d)\n", (disabled)?"disabled":"active", mButtonState );
230 if ( mButtonState == SELECTED_STATE )
232 ChangeState( DISABLED_SELECTED_STATE );
234 else if ( mButtonState == UNSELECTED_STATE )
236 ChangeState( DISABLED_UNSELECTED_STATE );
241 if ( mButtonState == DISABLED_SELECTED_STATE )
243 ChangeState( SELECTED_STATE );
245 else if ( mButtonState == DISABLED_UNSELECTED_STATE )
247 ChangeState( UNSELECTED_STATE );
252 bool Button::IsDisabled() const
254 return ( mButtonState == DISABLED_SELECTED_STATE || mButtonState == DISABLED_UNSELECTED_STATE ) ;
257 bool Button::ValidateState( State requestedState )
259 /* Below tables shows allowed state transitions
260 * Match rows in first column to following columns, if true then transition allowed.
261 * eg UNSELECTED_STATE to DISABLED_UNSELECTED_STATE is true so state transition allowed.
263 to| UNSELECTED_STATE | SELECTED_STATE | DISABLED_UNSELECTED_STATE | DISABLED_SELECTED_STATE |*/
265 bool transitionTable[4][4] = { /* UNSELECTED_STATE*/ { false, true, true, false },
266 /* SELECTED_STATE*/ { true, false, false, true },
267 /* DISABLED_UNSELECTED_STATE*/{ true, true, false, false },
268 /* DISABLED_SELECTED_STATE*/ { false, true, false, false }
271 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::ValidateState ReuestedState:%d, CurrentState:%d, result:%s\n",
272 requestedState, mButtonState, (transitionTable[mButtonState][requestedState])?"change-accepted":"change-denied");
274 return transitionTable[mButtonState][requestedState];
277 void Button::ChangeState( State requestedState )
279 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::ChangeState ReuestedState(%d)\n", requestedState );
281 // Validate State before changing
282 if ( !ValidateState( requestedState ))
284 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::ChangeState ReuestedState(%d) not validated\n", requestedState );
288 // If not on stage the button could have still been set to selected so update state
289 mPreviousButtonState = mButtonState; // Store previous state for visual removal (used when animations ended)
290 mButtonState = requestedState; // Update current state
292 if ( Self().OnStage() )
294 OnStateChange( mButtonState ); // Notify derived buttons
295 SelectRequiredVisual( VISUAL_INDEX_FOR_STATE[ mButtonState ][ BACKGROUND ] );
296 SelectRequiredVisual( VISUAL_INDEX_FOR_STATE[ mButtonState ][ FOREGROUND ] );
297 // If animation supported then visual removal should be performed after any transition animation has completed.
298 // If Required Visual is not loaded before current visual is removed then a flickering will be evident.
299 // Derived button can override OnButtonVisualRemoval
300 OnButtonVisualRemoval( VISUAL_INDEX_FOR_STATE[ mPreviousButtonState ][ BACKGROUND ] );
301 OnButtonVisualRemoval( VISUAL_INDEX_FOR_STATE[ mPreviousButtonState ][ FOREGROUND ] );
305 Toolkit::Button handle( GetOwner() );
307 mStateChangedSignal.Emit( handle );
310 bool Button::IsSelected() const
312 bool selected = ( mButtonState == SELECTED_STATE ) || ( mButtonState == DISABLED_SELECTED_STATE );
313 return mTogglableButton && selected;
316 void Button::MergeWithExistingLabelProperties( const Property::Map& inMap, Property::Map& outMap )
318 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "MergeLabelProperties with %d properties\n", inMap.Count() );
321 * Properties for the Label visual could be from a style sheet but after being set the "TEXT" property could be set.
322 * Hence would need to create the Text Visual with the complete merged set of properties.
324 * 1) Find Label Visual
325 * 2) Retrieve current properties ( settings )
326 * 3) Merge with new properties ( settings )
327 * 4) Return new merged map
329 Toolkit::Visual::Base visual = DevelControl::GetVisual( *this, Toolkit::Button::Property::LABEL );
332 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "MergeLabelProperties Visual already exists, retrieving existing map\n");
333 visual.CreatePropertyMap( outMap );
334 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "MergeLabelProperties retrieved %d properties\n", outMap.Count() );
337 outMap.Merge( inMap );
339 // Store if a text string has been supplied.
341 mTextStringSetFlag = MapContainsTextString( outMap );
343 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "MergeLabelProperties now has %d properties\n", outMap.Count() );
346 void Button::SetLabelAlignment( Button::Align labelAlignment)
348 mTextLabelAlignment = labelAlignment;
352 Button::Align Button::GetLabelAlignment()
354 return mTextLabelAlignment;
358 * Create Visual for given index from a property map or url.
359 * 1) Check if value passed in is a url and create visual
360 * 2) Create visual from map if step (1) is false
361 * 3) Register visual with control with false for enable flag. Button will later enable visual when needed ( Button::SelectRequiredVisual )
362 * 4) Unregister visual if empty map was provided. This is the method to remove a visual
364 void Button::CreateVisualsForComponent( Property::Index index, const Property::Value& value, const int visualDepth )
366 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "CreateVisualsForComponent index(%d)\n", index );
367 Toolkit::VisualFactory visualFactory = Toolkit::VisualFactory::Get();
368 Toolkit::Visual::Base buttonVisual;
370 std::string imageUrl;
371 if( value.Get( imageUrl ) )
373 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "CreateVisualsForComponent Using image URL(%d)\n", index );
374 if ( !imageUrl.empty() )
376 DALI_ASSERT_DEBUG( index != Toolkit::Button::Property::LABEL && "Creating a Image Visual instead of Text Visual " );
377 buttonVisual = visualFactory.CreateVisual( imageUrl, ImageDimensions() );
382 // if its not a string then get a Property::Map from the property if possible.
383 Property::Map *map = value.GetMap();
384 if( map && !map->Empty() ) // Empty map results in current visual removal.
386 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "CreateVisualsForComponent Using Map(%d)\n", index );
387 buttonVisual = visualFactory.CreateVisual( *map );
393 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "CreateVisualsForComponent RegisterVisual index(%d) enabled(%s)\n",
394 index, DevelControl::IsVisualEnabled( *this, index )?"true":"false" );
395 // enable the visual if needed for current state
396 const bool enabled = ( ( index == VISUAL_INDEX_FOR_STATE[ mButtonState ][ BACKGROUND ] )||
397 ( index == VISUAL_INDEX_FOR_STATE[ mButtonState ][ FOREGROUND ] )||
398 ( index == Toolkit::Button::Property::LABEL ) );
399 DevelControl::RegisterVisual( *this, index, buttonVisual, enabled, visualDepth );
403 DevelControl::UnregisterVisual( *this, index );
404 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "CreateVisualsForComponent Visual not created or empty map (clearing visual).(%d)\n", index);
409 bool Button::GetPropertyMapForVisual( Property::Index visualIndex, Property::Map& retreivedMap ) const
411 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "GetPropertyMapForVisual visual(%d)\n", visualIndex);
412 bool success = false;
413 Toolkit::Visual::Base visual = DevelControl::GetVisual( *this, visualIndex );
416 visual.CreatePropertyMap( retreivedMap );
419 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "GetPropertyMapForVisual %s\n", success?"Success":"Failure");
423 bool Button::DoAction( BaseObject* object, const std::string& actionName, const Property::Map& attributes )
427 Dali::BaseHandle handle( object );
429 Toolkit::Button button = Toolkit::Button::DownCast( handle );
431 DALI_ASSERT_DEBUG( button );
433 if( 0 == strcmp( actionName.c_str(), ACTION_BUTTON_CLICK ) )
435 ret = GetImplementation( button ).DoClickAction( attributes );
441 bool Button::DoClickAction( const Property::Map& attributes )
443 // Prevents the button signals from doing a recursive loop by sending an action
444 // and re-emitting the signals.
445 if( !mClickActionPerforming )
447 mClickActionPerforming = true;
449 if ( !mTogglableButton )
451 mButtonPressedState = DEPRESSED;
454 mClickActionPerforming = false;
462 void Button::ButtonDown()
464 if( mTogglableButton )
466 if ( mButtonState != SELECTED_STATE )
469 mButtonPressedState = TOGGLE_DEPRESSED;
473 mButtonPressedState = DEPRESSED;
479 mButtonPressedState = DEPRESSED;
482 SetUpTimer( mInitialAutoRepeatingDelay );
486 // The pressed signal should be emitted regardless of toggle mode.
487 Toolkit::Button handle( GetOwner() );
488 mPressedSignal.Emit( handle );
491 void Button::ButtonUp()
493 bool emitSignalsForPressAndReleaseAction = false;
495 if( DEPRESSED == mButtonPressedState )
497 if( mTogglableButton ) // Button up will change state
499 emitSignalsForPressAndReleaseAction = OnToggleReleased(); // Derived toggle buttons can override this to provide custom behaviour
503 Released(); // Button up will result in unselected state
506 mAutoRepeatingTimer.Reset();
508 emitSignalsForPressAndReleaseAction = true;
511 else if ( TOGGLE_DEPRESSED == mButtonPressedState )
513 emitSignalsForPressAndReleaseAction = true; // toggle released after being pressed, a click
516 if ( emitSignalsForPressAndReleaseAction )
518 // The clicked and released signals should be emitted regardless of toggle mode.
519 Toolkit::Button handle( GetOwner() );
520 mReleasedSignal.Emit( handle );
521 mClickedSignal.Emit( handle );
525 bool Button::OnToggleReleased()
527 SetSelected( !IsSelected() );
528 mButtonPressedState = UNPRESSED;
533 void Button::OnTouchPointLeave()
535 if( DEPRESSED == mButtonPressedState )
537 if( !mTogglableButton )
543 mAutoRepeatingTimer.Reset();
547 mButtonPressedState = UNPRESSED;
549 // The released signal should be emitted regardless of toggle mode.
550 Toolkit::Button handle( GetOwner() );
551 mReleasedSignal.Emit( handle );
555 void Button::OnTouchPointInterrupted()
560 Toolkit::Button::ButtonSignalType& Button::PressedSignal()
562 return mPressedSignal;
565 Toolkit::Button::ButtonSignalType& Button::ReleasedSignal()
567 return mReleasedSignal;
570 Toolkit::Button::ButtonSignalType& Button::ClickedSignal()
572 return mClickedSignal;
575 Toolkit::Button::ButtonSignalType& Button::StateChangedSignal()
577 return mStateChangedSignal;
580 bool Button::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
582 Dali::BaseHandle handle( object );
584 bool connected( true );
585 Toolkit::Button button = Toolkit::Button::DownCast( handle );
587 if( 0 == strcmp( signalName.c_str(), SIGNAL_PRESSED ) )
589 button.PressedSignal().Connect( tracker, functor );
591 else if( 0 == strcmp( signalName.c_str(), SIGNAL_RELEASED ) )
593 button.ReleasedSignal().Connect( tracker, functor );
595 else if( 0 == strcmp( signalName.c_str(), SIGNAL_CLICKED ) )
597 button.ClickedSignal().Connect( tracker, functor );
599 else if( 0 == strcmp( signalName.c_str(), SIGNAL_STATE_CHANGED ) )
601 button.StateChangedSignal().Connect( tracker, functor );
605 // signalName does not match any signal
612 void Button::OnInitialize()
614 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::OnInitialize\n" );
618 mTapDetector = TapGestureDetector::New();
619 mTapDetector.Attach( self );
620 mTapDetector.DetectedSignal().Connect(this, &Button::OnTap);
622 self.SetKeyboardFocusable( true );
624 self.TouchSignal().Connect( this, &Button::OnTouch );
627 bool Button::OnAccessibilityActivated()
629 return OnKeyboardEnter();
632 bool Button::OnTouch( Actor actor, const TouchData& touch )
635 // Only events are processed when the button is not disabled
636 auto result( false );
640 if ( 1 == touch.GetPointCount() )
642 switch( touch.GetState( 0 ) )
644 case PointState::DOWN:
654 case PointState::INTERRUPTED:
656 OnTouchPointInterrupted();
659 case PointState::LEAVE:
664 case PointState::MOTION:
665 case PointState::STATIONARY: // FALLTHROUGH
672 else if( 1 < touch.GetPointCount() )
674 OnTouchPointLeave(); // Notification for derived classes.
676 // Sets the button state to the default
677 mButtonPressedState = UNPRESSED;
684 bool Button::OnKeyboardEnter()
686 // When the enter key is pressed, or button is activated, the click action is performed.
687 Property::Map attributes;
688 bool ret = DoClickAction( attributes );
693 void Button::OnStageDisconnection()
695 if( DEPRESSED == mButtonPressedState )
697 if( !mTogglableButton )
703 mAutoRepeatingTimer.Reset();
708 mButtonPressedState = UNPRESSED;
710 Control::OnStageDisconnection(); // Visuals will be set off stage
713 void Button::OnStageConnection( int depth )
715 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::OnStageConnection ptr(%p) \n", this );
716 OnButtonVisualRemoval( VISUAL_INDEX_FOR_STATE[ mPreviousButtonState ][ BACKGROUND ] );
717 OnButtonVisualRemoval( VISUAL_INDEX_FOR_STATE[ mPreviousButtonState ][ FOREGROUND ] );
718 SelectRequiredVisual( Toolkit::Button::Property::LABEL );
719 SelectRequiredVisual( VISUAL_INDEX_FOR_STATE[ mButtonState ][ BACKGROUND ] );
720 SelectRequiredVisual( VISUAL_INDEX_FOR_STATE[ mButtonState ][ FOREGROUND ] );
721 Control::OnStageConnection( depth ); // Enabled visuals will be put on stage
725 Vector3 Button::GetNaturalSize()
727 Vector3 size = Vector3::ZERO;
729 bool horizontalAlignment = mTextLabelAlignment == BEGIN || mTextLabelAlignment == END; // label and visual side by side
731 // Get natural size of foreground ( largest of the possible visuals )
732 Size largestProvidedVisual;
733 Size labelSize = Size::ZERO;
735 bool foreGroundVisualUsed = false;
737 for ( int state = Button::UNSELECTED_STATE; state < Button::STATE_COUNT; state++ )
739 Toolkit::Visual::Base visual = DevelControl::GetVisual( *this, VISUAL_INDEX_FOR_STATE[state][FOREGROUND] );
743 visual.GetNaturalSize( visualSize );
744 largestProvidedVisual.width = std::max(largestProvidedVisual.width, visualSize.width );
745 largestProvidedVisual.height = std::max(largestProvidedVisual.height, visualSize.height );
746 foreGroundVisualUsed = true;
750 if ( !foreGroundVisualUsed ) // If foreground visual not supplied then use the background visual to calculate Natural size
752 for ( int state = Button::UNSELECTED_STATE; state < Button::STATE_COUNT; state++ )
754 Toolkit::Visual::Base visual = DevelControl::GetVisual( *this, VISUAL_INDEX_FOR_STATE[state][BACKGROUND] );
758 visual.GetNaturalSize( visualSize );
759 largestProvidedVisual.width = std::max(largestProvidedVisual.width, visualSize.width );
760 largestProvidedVisual.height = std::max(largestProvidedVisual.height, visualSize.height );
765 // Get horizontal padding total
766 if ( largestProvidedVisual.width > 0 ) // if visual exists
768 size.width += largestProvidedVisual.width + mForegroundPadding.left + mForegroundPadding.right;
770 // Get vertical padding total
771 if ( largestProvidedVisual.height > 0 )
773 size.height += largestProvidedVisual.height + mForegroundPadding.top + mForegroundPadding.bottom;
776 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "GetNaturalSize visual Size(%f,%f)\n",
777 largestProvidedVisual.width, largestProvidedVisual.height );
779 // Get natural size of label if text has been set
780 if ( mTextStringSetFlag )
782 Toolkit::Visual::Base visual = DevelControl::GetVisual( *this, Toolkit::Button::Property::LABEL );
786 visual.GetNaturalSize( labelSize );
788 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "GetNaturalSize labelSize(%f,%f) padding(%f,%f)\n",
789 labelSize.width, labelSize.height, mLabelPadding.left + mLabelPadding.right, mLabelPadding.top + mLabelPadding.bottom);
791 labelSize.width += mLabelPadding.left + mLabelPadding.right;
792 labelSize.height += mLabelPadding.top + mLabelPadding.bottom;
794 // Add label size to height or width depending on alignment position
795 if ( horizontalAlignment )
797 size.width += labelSize.width;
798 size.height = std::max(size.height, labelSize.height );
802 size.height += labelSize.height;
803 size.width = std::max(size.width, labelSize.width );
808 if( size.width < 1 && size.height < 1 )
810 // if no image or label then use Control's natural size
811 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "GetNaturalSize Using control natural size\n");
812 size = Control::GetNaturalSize();
815 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "Button GetNaturalSize (%f,%f)\n", size.width, size.height );
820 void Button::OnSetResizePolicy( ResizePolicy::Type policy, Dimension::Type dimension )
822 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnSetResizePolicy\n");
827 * Visuals are sized and positioned in this function.
828 * Whilst the control has it's size negotiated it has to size it's visuals explicitly here.
831 void Button::OnRelayout( const Vector2& size, RelayoutContainer& container )
833 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout targetSize(%f,%f) ptr(%p) state[%d]\n", size.width, size.height, this, mButtonState );
835 Toolkit::Visual::Base currentVisual = DevelControl::GetVisual( *this, VISUAL_INDEX_FOR_STATE[mButtonState][FOREGROUND] );
836 Toolkit::Visual::Base currentBackGroundVisual = DevelControl::GetVisual( *this, VISUAL_INDEX_FOR_STATE[mButtonState][BACKGROUND] );
838 // Sizes and padding set to zero, if not present then values will no effect calculations.
839 Vector2 visualPosition = Vector2::ZERO;
840 Vector2 labelPosition = Vector2::ZERO;
841 Size visualSize = Size::ZERO;
842 Padding foregroundVisualPadding = Padding(0.0f, 0.0f, 0.0f, 0.0f );
843 Padding labelVisualPadding = Padding(0.0f, 0.0f, 0.0f, 0.0f );
845 if ( mTextStringSetFlag )
847 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout Label padding setting padding:%f,%f,%f,%f\n", mLabelPadding.y, mLabelPadding.x, mLabelPadding.width,mLabelPadding.height );
848 labelVisualPadding = mLabelPadding;
853 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout Foreground Visual setting padding:%f,%f,%f,%f\n", mForegroundPadding.y, mForegroundPadding.x, mForegroundPadding.width,mForegroundPadding.height );
854 currentVisual.GetNaturalSize( visualSize );
855 foregroundVisualPadding = mForegroundPadding;
858 Toolkit::Align::Type visualAnchorPoint = Toolkit::Align::TOP_BEGIN;
860 Vector2 visualAndPaddingSize = Vector2( ( foregroundVisualPadding.x + visualSize.width + foregroundVisualPadding.y ),
861 ( foregroundVisualPadding.width + visualSize.height + foregroundVisualPadding.height ));
863 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout visualAndPaddingSize(%f,%f)\n", visualAndPaddingSize.width, visualAndPaddingSize.height);
865 // Text Visual should take all space available after foreground visual size and all padding is considered.
866 // Remaining Space priority, Foreground padding, foreground visual, Text padding then Text visual.
867 Size remainingSpaceForText = Size::ZERO;
869 switch ( mTextLabelAlignment )
873 visualAnchorPoint = Toolkit::Align::TOP_END;
874 visualPosition.x = foregroundVisualPadding.right;
875 visualPosition.y = foregroundVisualPadding.top;
877 labelPosition.x = labelVisualPadding.x;
878 labelPosition.y = labelVisualPadding.top;
880 remainingSpaceForText.width = size.width - visualAndPaddingSize.width - labelVisualPadding.x - labelVisualPadding.y;
881 remainingSpaceForText.height = size.height - labelVisualPadding.top - labelVisualPadding.bottom;
886 visualAnchorPoint = Toolkit::Align::TOP_BEGIN;
887 visualPosition.x = foregroundVisualPadding.left;
888 visualPosition.y = foregroundVisualPadding.top;
890 labelPosition.x = visualAndPaddingSize.width + labelVisualPadding.x;
891 labelPosition.y = labelVisualPadding.top;
893 remainingSpaceForText.width = size.width - visualAndPaddingSize.width - labelVisualPadding.x - labelVisualPadding.y;
894 remainingSpaceForText.height = size.height - labelVisualPadding.top - labelVisualPadding.bottom;
899 visualAnchorPoint = Toolkit::Align::BOTTOM_END;
900 visualPosition.x = foregroundVisualPadding.left;
901 visualPosition.y = foregroundVisualPadding.bottom;
903 labelPosition.x = labelVisualPadding.left;
904 labelPosition.y = labelVisualPadding.top;
906 remainingSpaceForText.width = size.width - labelVisualPadding.x - labelVisualPadding.y;
907 remainingSpaceForText.height = size.height - visualAndPaddingSize.height - labelVisualPadding.top - labelVisualPadding.bottom;
913 visualAnchorPoint = Toolkit::Align::TOP_END;
914 visualPosition.x = foregroundVisualPadding.left;
915 visualPosition.y = foregroundVisualPadding.top;
917 labelPosition.x = labelVisualPadding.left;
918 labelPosition.y = visualAndPaddingSize.height + labelVisualPadding.top;
920 remainingSpaceForText.width = size.width - labelVisualPadding.x - labelVisualPadding.y;
921 remainingSpaceForText.height = size.height - visualAndPaddingSize.height - labelVisualPadding.top - labelVisualPadding.bottom;
927 if ( currentBackGroundVisual )
929 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout Setting visual background size to(%f,%f)\n", size.width, size.height);
931 Property::Map visualTransform;
933 visualTransform.Add( Toolkit::Visual::Transform::Property::SIZE, size )
934 .Add( Toolkit::Visual::Transform::Property::SIZE_POLICY, Vector2( Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE ) );
936 currentBackGroundVisual.SetTransformAndSize( visualTransform, size );
941 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout Setting visual size to(%f,%f)\n", visualSize.width, visualSize.height);
943 Property::Map visualTransform;
945 visualTransform.Add( Toolkit::Visual::Transform::Property::SIZE, visualSize )
946 .Add( Toolkit::Visual::Transform::Property::OFFSET, visualPosition )
947 .Add( Toolkit::Visual::Transform::Property::OFFSET_POLICY, Vector2( Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE ) )
948 .Add( Toolkit::Visual::Transform::Property::SIZE_POLICY, Vector2( Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE ) )
949 .Add( Toolkit::Visual::Transform::Property::ORIGIN, Toolkit::Align::TOP_BEGIN )
950 .Add( Toolkit::Visual::Transform::Property::ANCHOR_POINT, visualAnchorPoint );
952 currentVisual.SetTransformAndSize( visualTransform, size );
955 if ( mTextStringSetFlag )
957 Toolkit::Visual::Base textVisual = DevelControl::GetVisual( *this, Toolkit::Button::Property::LABEL ); // No need to search for Label visual if no text set.
961 if ( !currentVisual )
963 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout Only Text\n");
964 labelPosition.x = labelVisualPadding.left;
965 labelPosition.y = labelVisualPadding.height;
968 Vector2 preSize = Vector2( static_cast< int >( remainingSpaceForText.x ), static_cast< int >( remainingSpaceForText.y ));
970 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout text Size(%f,%f) text Position(%f,%f) \n", remainingSpaceForText.width, remainingSpaceForText.height, labelPosition.x, labelPosition.y);
972 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout text Size -- (%f,%f) text Position(%f,%f) \n", preSize.width, preSize.height, labelPosition.x, labelPosition.y);
975 Property::Map textVisualTransform;
976 textVisualTransform.Add( Toolkit::Visual::Transform::Property::SIZE, preSize )
977 .Add( Toolkit::Visual::Transform::Property::OFFSET, labelPosition )
978 .Add( Toolkit::Visual::Transform::Property::OFFSET_POLICY, Vector2( Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE ) )
979 .Add( Toolkit::Visual::Transform::Property::SIZE_POLICY, Vector2( Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE ) )
980 .Add( Toolkit::Visual::Transform::Property::ORIGIN, Toolkit::Align::TOP_BEGIN )
981 .Add( Toolkit::Visual::Transform::Property::ANCHOR_POINT, visualAnchorPoint );
983 textVisual.SetTransformAndSize( textVisualTransform, size );
987 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout selected (%s) \n", IsSelected()?"yes":"no" );
989 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout << \n");
992 void Button::OnTap(Actor actor, const TapGesture& tap)
994 // Prevents Parent getting a tap event
997 void Button::SetUpTimer( float delay )
999 mAutoRepeatingTimer = Dali::Timer::New( static_cast<unsigned int>( 1000.f * delay ) );
1000 mAutoRepeatingTimer.TickSignal().Connect( this, &Button::AutoRepeatingSlot );
1001 mAutoRepeatingTimer.Start();
1004 bool Button::AutoRepeatingSlot()
1006 bool consumed = false;
1009 // Restart the autorepeat timer.
1010 SetUpTimer( mNextAutoRepeatingDelay );
1014 Toolkit::Button handle( GetOwner() );
1017 consumed = mReleasedSignal.Emit( handle );
1018 consumed = mClickedSignal.Emit( handle );
1019 consumed |= mPressedSignal.Emit( handle );
1025 void Button::Pressed()
1027 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::Pressed\n" );
1029 if( mButtonState == UNSELECTED_STATE )
1031 ChangeState( SELECTED_STATE );
1032 OnPressed(); // Notifies the derived class the button has been pressed.
1036 void Button::Released()
1038 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::Released\n" );
1040 if( mButtonState == SELECTED_STATE && !mTogglableButton )
1042 ChangeState( UNSELECTED_STATE );
1043 OnReleased(); // // Notifies the derived class the button has been released.
1045 mButtonPressedState = UNPRESSED;
1048 void Button::SelectRequiredVisual( Property::Index visualIndex )
1050 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::SelectRequiredVisual index(%d) state(%d)\n", visualIndex, mButtonState );
1051 // only enable visuals that exist
1052 if( DevelControl::GetVisual( *this, visualIndex ) )
1054 DevelControl::EnableVisual( *this, visualIndex, true );
1058 void Button::RemoveVisual( Property::Index visualIndex )
1060 // Use OnButtonVisualRemoval if want button developer to have the option to override removal.
1061 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::RemoveVisual index(%d) state(%d)\n", visualIndex, mButtonState );
1063 Toolkit::Visual::Base visual = DevelControl::GetVisual( *this, visualIndex );
1067 DevelControl::EnableVisual( *this, visualIndex, false );
1071 void Button::OnButtonVisualRemoval( Property::Index visualIndex )
1073 // Derived Buttons can over ride this to prevent default removal.
1074 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::OnButtonVisualRemoval index(%d)\n", visualIndex );
1075 RemoveVisual( visualIndex );
1078 void Button::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
1080 Toolkit::Button button = Toolkit::Button::DownCast( Dali::BaseHandle( object ) );
1082 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::SetProperty index[%d]\n", index );
1088 case Toolkit::Button::Property::DISABLED:
1090 GetImplementation( button ).SetDisabled( value.Get< bool >() );
1094 case Toolkit::Button::Property::AUTO_REPEATING:
1096 GetImplementation( button ).SetAutoRepeating( value.Get< bool >() );
1100 case Toolkit::Button::Property::INITIAL_AUTO_REPEATING_DELAY:
1102 GetImplementation( button ).SetInitialAutoRepeatingDelay( value.Get< float >() );
1106 case Toolkit::Button::Property::NEXT_AUTO_REPEATING_DELAY:
1108 GetImplementation( button ).SetNextAutoRepeatingDelay( value.Get< float >() );
1112 case Toolkit::Button::Property::TOGGLABLE:
1114 GetImplementation( button ).SetTogglableButton( value.Get< bool >() );
1118 case Toolkit::Button::Property::SELECTED:
1120 GetImplementation( button ).SetSelected( value.Get< bool >() );
1124 case Toolkit::Button::Property::UNSELECTED_VISUAL:
1125 case Toolkit::Button::Property::SELECTED_VISUAL:
1126 case Toolkit::Button::Property::DISABLED_SELECTED_VISUAL:
1127 case Toolkit::Button::Property::DISABLED_UNSELECTED_VISUAL:
1129 GetImplementation( button ).CreateVisualsForComponent( index, value, DepthIndex::CONTENT );
1133 case Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL:
1134 case Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL:
1135 case Toolkit::Button::Property::DISABLED_SELECTED_BACKGROUND_VISUAL:
1136 case Toolkit::Button::Property::DISABLED_UNSELECTED_BACKGROUND_VISUAL:
1138 GetImplementation( button ).CreateVisualsForComponent( index , value, DepthIndex::BACKGROUND);
1142 case Toolkit::Button::Property::LABEL:
1144 Property::Map outTextVisualProperties;
1145 std::string textString;
1147 if ( value.Get( textString ) )
1149 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::SetProperty Setting TextVisual with string[%s]\n", textString.c_str() );
1151 Property::Map setPropertyMap;
1152 setPropertyMap.Add( Toolkit::Visual::Property::TYPE, Toolkit::Visual::TEXT )
1153 .Add( Toolkit::TextVisual::Property::TEXT, textString );
1155 GetImplementation( button ).MergeWithExistingLabelProperties( setPropertyMap, outTextVisualProperties );
1159 // Get a Property::Map from the property if possible.
1160 Property::Map* setPropertyMap = value.GetMap();
1161 if( setPropertyMap )
1163 TextVisual::ConvertStringKeysToIndexKeys( *setPropertyMap );
1164 GetImplementation( button ).MergeWithExistingLabelProperties( *setPropertyMap, outTextVisualProperties );
1168 if( !outTextVisualProperties.Empty() )
1170 GetImplementation( button ).CreateVisualsForComponent( index, outTextVisualProperties, DepthIndex::CONTENT );
1175 case Toolkit::DevelButton::Property::LABEL_RELATIVE_ALIGNMENT:
1177 Button::Align labelAlignment(END);
1178 Scripting::GetEnumeration< Button::Align> ( value.Get< std::string >().c_str(),
1179 ALIGNMENT_TABLE, ALIGNMENT_TABLE_COUNT,
1182 GetImplementation( button ).SetLabelAlignment( labelAlignment );
1186 case Toolkit::DevelButton::Property::LABEL_PADDING:
1188 Vector4 padding ( value.Get< Vector4 >() );
1189 GetImplementation( button ).SetLabelPadding( Padding( padding.x, padding.y, padding.z, padding.w ) );
1193 case Toolkit::DevelButton::Property::VISUAL_PADDING:
1195 Vector4 padding ( value.Get< Vector4 >() );
1196 GetImplementation( button ).SetForegroundPadding( Padding( padding.x, padding.y, padding.z, padding.w ) );
1203 Property::Value Button::GetProperty( BaseObject* object, Property::Index propertyIndex )
1205 Property::Value value;
1207 Toolkit::Button button = Toolkit::Button::DownCast( Dali::BaseHandle( object ) );
1211 switch ( propertyIndex )
1213 case Toolkit::Button::Property::DISABLED:
1215 value = GetImplementation( button ).IsDisabled();
1219 case Toolkit::Button::Property::AUTO_REPEATING:
1221 value = GetImplementation( button ).mAutoRepeating;
1225 case Toolkit::Button::Property::INITIAL_AUTO_REPEATING_DELAY:
1227 value = GetImplementation( button ).mInitialAutoRepeatingDelay;
1231 case Toolkit::Button::Property::NEXT_AUTO_REPEATING_DELAY:
1233 value = GetImplementation( button ).mNextAutoRepeatingDelay;
1237 case Toolkit::Button::Property::TOGGLABLE:
1239 value = GetImplementation( button ).mTogglableButton;
1243 case Toolkit::Button::Property::SELECTED:
1245 value = GetImplementation( button ).IsSelected();
1249 case Toolkit::Button::Property::UNSELECTED_VISUAL:
1250 case Toolkit::Button::Property::SELECTED_VISUAL:
1251 case Toolkit::Button::Property::DISABLED_SELECTED_VISUAL:
1252 case Toolkit::Button::Property::DISABLED_UNSELECTED_VISUAL:
1253 case Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL:
1254 case Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL:
1255 case Toolkit::Button::Property::DISABLED_SELECTED_BACKGROUND_VISUAL:
1256 case Toolkit::Button::Property::DISABLED_UNSELECTED_BACKGROUND_VISUAL:
1257 case Toolkit::Button::Property::LABEL:
1259 Property::Map visualProperty;
1260 if ( GetImplementation( button ).GetPropertyMapForVisual( propertyIndex, visualProperty ) )
1262 value = visualProperty;
1267 case Toolkit::DevelButton::Property::LABEL_RELATIVE_ALIGNMENT:
1269 const char* alignment = Scripting::GetEnumerationName< Button::Align >( GetImplementation( button ).GetLabelAlignment(),
1270 ALIGNMENT_STRING_TABLE,
1271 ALIGNMENT_STRING_TABLE_COUNT );
1274 value = std::string( alignment );
1280 case Toolkit::DevelButton::Property::LABEL_PADDING:
1282 Padding padding = GetImplementation( button ).GetLabelPadding();
1283 value = Vector4( padding.x, padding.y, padding.top, padding.bottom);
1287 case Toolkit::DevelButton::Property::VISUAL_PADDING:
1289 Padding padding = GetImplementation( button ).GetForegroundPadding();
1290 value = Vector4( padding.x, padding.y, padding.top, padding.bottom);
1298 void Button::SetLabelPadding( const Padding& padding)
1300 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::SetLabelPadding padding(%f,%f,%f,%f)\n", padding.left, padding.right, padding.bottom, padding.top );
1301 mLabelPadding = Padding( padding.left, padding.right, padding.bottom, padding.top );
1305 Padding Button::GetLabelPadding()
1307 return mLabelPadding;
1310 void Button::SetForegroundPadding( const Padding& padding)
1312 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::SetForegroundPadding padding(%f,%f,%f,%f)\n", padding.left, padding.right, padding.bottom, padding.top );
1313 mForegroundPadding = Padding( padding.left, padding.right, padding.bottom, padding.top );
1317 Padding Button::GetForegroundPadding()
1319 return mForegroundPadding;
1322 } // namespace Internal
1324 } // namespace Toolkit