2 * Copyright (c) 2020 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-event.h>
26 #include <dali/public-api/object/type-registry.h>
27 #include <dali/public-api/object/type-registry-helper.h>
28 #include <dali/public-api/size-negotiation/relayout-container.h>
29 #include <dali/devel-api/object/property-helper-devel.h>
30 #include <dali/devel-api/scripting/scripting.h>
33 #include <dali-toolkit/public-api/controls/text-controls/text-label.h>
34 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
35 #include <dali-toolkit/public-api/visuals/color-visual-properties.h>
36 #include <dali-toolkit/public-api/visuals/image-visual-properties.h>
37 #include <dali-toolkit/public-api/align-enumerations.h>
38 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
39 #include <dali-toolkit/devel-api/controls/control-devel.h>
40 #include <dali-toolkit/devel-api/controls/buttons/button-devel.h>
41 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
42 #include <dali-toolkit/internal/visuals/text/text-visual.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");
64 // empty handle as we cannot create button (but type registered for clicked signal)
68 // Setup properties, signals and actions using the type-registry.
69 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::Button, Toolkit::Control, Create )
71 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "disabled", BOOLEAN, DISABLED )
72 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "autoRepeating", BOOLEAN, AUTO_REPEATING )
73 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "initialAutoRepeatingDelay", FLOAT, INITIAL_AUTO_REPEATING_DELAY )
74 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "nextAutoRepeatingDelay", FLOAT, NEXT_AUTO_REPEATING_DELAY )
75 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "togglable", BOOLEAN, TOGGLABLE )
76 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "selected", BOOLEAN, SELECTED )
77 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "unselectedVisual", MAP, UNSELECTED_VISUAL )
78 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "selectedVisual", MAP, SELECTED_VISUAL )
79 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "disabledSelectedVisual", MAP, DISABLED_SELECTED_VISUAL )
80 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "disabledUnselectedVisual", MAP, DISABLED_UNSELECTED_VISUAL )
81 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "unselectedBackgroundVisual", MAP, UNSELECTED_BACKGROUND_VISUAL )
82 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "label", MAP, LABEL )
83 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "selectedBackgroundVisual", MAP, SELECTED_BACKGROUND_VISUAL )
84 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "disabledUnselectedBackgroundVisual", MAP, DISABLED_UNSELECTED_BACKGROUND_VISUAL )
85 DALI_PROPERTY_REGISTRATION( Toolkit, Button, "disabledSelectedBackgroundVisual", MAP, DISABLED_SELECTED_BACKGROUND_VISUAL )
86 DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, Button, "labelRelativeAlignment", STRING, LABEL_RELATIVE_ALIGNMENT )
87 DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, Button, "labelPadding", VECTOR4, LABEL_PADDING )
88 DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, Button, "visualPadding", VECTOR4, VISUAL_PADDING )
91 DALI_SIGNAL_REGISTRATION( Toolkit, Button, "pressed", SIGNAL_PRESSED )
92 DALI_SIGNAL_REGISTRATION( Toolkit, Button, "released", SIGNAL_RELEASED )
93 DALI_SIGNAL_REGISTRATION( Toolkit, Button, "clicked", SIGNAL_CLICKED )
94 DALI_SIGNAL_REGISTRATION( Toolkit, Button, "stateChanged", SIGNAL_STATE_CHANGED )
97 DALI_ACTION_REGISTRATION( Toolkit, Button, "buttonClick", ACTION_BUTTON_CLICK )
99 DALI_TYPE_REGISTRATION_END()
101 DALI_ENUM_TO_STRING_TABLE_BEGIN( ALIGNMENT )
102 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::Internal::Button, BEGIN )
103 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::Internal::Button, END )
104 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::Internal::Button, TOP )
105 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::Internal::Button, BOTTOM )
106 DALI_ENUM_TO_STRING_TABLE_END( ALIGNMENT )
108 const Scripting::StringEnum ALIGNMENT_STRING_TABLE[] =
110 { "BEGIN", Button::BEGIN },
111 { "END", Button::END },
112 { "TOP", Button::TOP },
113 { "BOTTOM", Button::BOTTOM },
116 const unsigned int ALIGNMENT_STRING_TABLE_COUNT = sizeof( ALIGNMENT_STRING_TABLE ) / sizeof( ALIGNMENT_STRING_TABLE[0] );
118 const Property::Index VISUAL_INDEX_FOR_STATE[][Button::STATE_COUNT] =
120 { Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, Toolkit::Button::Property::UNSELECTED_VISUAL },
121 { Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, Toolkit::Button::Property::SELECTED_VISUAL },
122 { Toolkit::Button::Property::DISABLED_UNSELECTED_BACKGROUND_VISUAL, Toolkit::Button::Property::DISABLED_UNSELECTED_VISUAL },
123 { Toolkit::Button::Property::DISABLED_SELECTED_BACKGROUND_VISUAL, Toolkit::Button::Property::DISABLED_SELECTED_VISUAL }
127 * Checks if given map contains a text string
129 bool MapContainsTextString( Property::Map& map )
132 Property::Value* value = map.Find( Toolkit::TextVisual::Property::TEXT );
135 std::string textString;
136 value->Get( textString );
137 if ( !textString.empty() )
145 } // unnamed namespace
148 : Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
149 mAutoRepeatingTimer(),
150 mTextLabelAlignment( END ),
151 mAutoRepeating( false ),
152 mTogglableButton( false ),
153 mTextStringSetFlag( false ),
154 mInitialAutoRepeatingDelay( 0.0f ),
155 mNextAutoRepeatingDelay( 0.0f ),
156 mAnimationTime( 0.0f ),
157 mButtonPressedState( UNPRESSED ),
158 mButtonState( UNSELECTED_STATE ),
159 mPreviousButtonState( mButtonState ),
160 mClickActionPerforming( false )
168 void Button::SetAutoRepeating( bool autoRepeating )
170 mAutoRepeating = autoRepeating;
172 // An autorepeating button can't be a toggle button.
177 SetSelected( false ); // UnSelect before switching off Toggle feature.
179 mTogglableButton = false;
183 void Button::SetInitialAutoRepeatingDelay( float initialAutoRepeatingDelay )
185 DALI_ASSERT_DEBUG( initialAutoRepeatingDelay > 0.f );
186 mInitialAutoRepeatingDelay = initialAutoRepeatingDelay;
189 void Button::SetNextAutoRepeatingDelay( float nextAutoRepeatingDelay )
191 DALI_ASSERT_DEBUG( nextAutoRepeatingDelay > 0.f );
192 mNextAutoRepeatingDelay = nextAutoRepeatingDelay;
195 void Button::SetTogglableButton( bool togglable )
197 mTogglableButton = togglable;
199 // A toggle button can't be an autorepeating button.
202 mAutoRepeating = false;
206 void Button::SetSelected( bool selected )
208 if( mTogglableButton )
210 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::SetSelected (%s)\n", (selected?"true":"false") );
212 if ( selected && ( mButtonState != SELECTED_STATE ) )
214 ChangeState( SELECTED_STATE );
216 else if ( !selected && ( mButtonState != UNSELECTED_STATE ) )
218 ChangeState( UNSELECTED_STATE );
223 void Button::SetDisabled( bool disabled )
225 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::SetDisabled(%s) state(%d)\n", (disabled)?"disabled":"active", mButtonState );
229 if ( mButtonState == SELECTED_STATE )
231 ChangeState( DISABLED_SELECTED_STATE );
233 else if ( mButtonState == UNSELECTED_STATE )
235 ChangeState( DISABLED_UNSELECTED_STATE );
240 if ( mButtonState == DISABLED_SELECTED_STATE )
242 ChangeState( SELECTED_STATE );
244 else if ( mButtonState == DISABLED_UNSELECTED_STATE )
246 ChangeState( UNSELECTED_STATE );
251 bool Button::IsDisabled() const
253 return ( mButtonState == DISABLED_SELECTED_STATE || mButtonState == DISABLED_UNSELECTED_STATE ) ;
256 bool Button::ValidateState( State requestedState )
258 /* Below tables shows allowed state transitions
259 * Match rows in first column to following columns, if true then transition allowed.
260 * eg UNSELECTED_STATE to DISABLED_UNSELECTED_STATE is true so state transition allowed.
262 to| UNSELECTED_STATE | SELECTED_STATE | DISABLED_UNSELECTED_STATE | DISABLED_SELECTED_STATE |*/
264 bool transitionTable[4][4] = { /* UNSELECTED_STATE*/ { false, true, true, false },
265 /* SELECTED_STATE*/ { true, false, false, true },
266 /* DISABLED_UNSELECTED_STATE*/{ true, true, false, false },
267 /* DISABLED_SELECTED_STATE*/ { false, true, false, false }
270 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::ValidateState ReuestedState:%d, CurrentState:%d, result:%s\n",
271 requestedState, mButtonState, (transitionTable[mButtonState][requestedState])?"change-accepted":"change-denied");
273 return transitionTable[mButtonState][requestedState];
276 void Button::ChangeState( State requestedState )
278 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::ChangeState ReuestedState(%d)\n", requestedState );
280 // Validate State before changing
281 if ( !ValidateState( requestedState ))
283 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::ChangeState ReuestedState(%d) not validated\n", requestedState );
287 // If not on stage the button could have still been set to selected so update state
288 mPreviousButtonState = mButtonState; // Store previous state for visual removal (used when animations ended)
289 mButtonState = requestedState; // Update current state
291 if ( Self().GetProperty< bool >( Actor::Property::CONNECTED_TO_SCENE ) )
293 OnStateChange( mButtonState ); // Notify derived buttons
294 SelectRequiredVisual( VISUAL_INDEX_FOR_STATE[ mButtonState ][ BACKGROUND ] );
295 SelectRequiredVisual( VISUAL_INDEX_FOR_STATE[ mButtonState ][ FOREGROUND ] );
296 // If animation supported then visual removal should be performed after any transition animation has completed.
297 // If Required Visual is not loaded before current visual is removed then a flickering will be evident.
298 // Derived button can override OnButtonVisualRemoval
299 OnButtonVisualRemoval( VISUAL_INDEX_FOR_STATE[ mPreviousButtonState ][ BACKGROUND ] );
300 OnButtonVisualRemoval( VISUAL_INDEX_FOR_STATE[ mPreviousButtonState ][ FOREGROUND ] );
304 Toolkit::Button handle( GetOwner() );
306 mStateChangedSignal.Emit( handle );
309 bool Button::IsSelected() const
311 bool selected = ( mButtonState == SELECTED_STATE ) || ( mButtonState == DISABLED_SELECTED_STATE );
312 return mTogglableButton && selected;
315 void Button::MergeWithExistingLabelProperties( const Property::Map& inMap, Property::Map& outMap )
317 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "MergeLabelProperties with %d properties\n", inMap.Count() );
320 * Properties for the Label visual could be from a style sheet but after being set the "TEXT" property could be set.
321 * Hence would need to create the Text Visual with the complete merged set of properties.
323 * 1) Find Label Visual
324 * 2) Retrieve current properties ( settings )
325 * 3) Merge with new properties ( settings )
326 * 4) Return new merged map
328 Toolkit::Visual::Base visual = DevelControl::GetVisual( *this, Toolkit::Button::Property::LABEL );
331 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "MergeLabelProperties Visual already exists, retrieving existing map\n");
332 visual.CreatePropertyMap( outMap );
333 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "MergeLabelProperties retrieved %d properties\n", outMap.Count() );
336 outMap.Merge( inMap );
338 // Store if a text string has been supplied.
340 mTextStringSetFlag = MapContainsTextString( outMap );
342 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "MergeLabelProperties now has %d properties\n", outMap.Count() );
345 void Button::SetLabelAlignment( Button::Align labelAlignment)
347 mTextLabelAlignment = labelAlignment;
351 Button::Align Button::GetLabelAlignment()
353 return mTextLabelAlignment;
357 * Create Visual for given index from a property map or url.
358 * 1) Check if value passed in is a url and create visual
359 * 2) Create visual from map if step (1) is false
360 * 3) Register visual with control with false for enable flag. Button will later enable visual when needed ( Button::SelectRequiredVisual )
361 * 4) Unregister visual if empty map was provided. This is the method to remove a visual
363 void Button::CreateVisualsForComponent( Property::Index index, const Property::Value& value, const int visualDepth )
365 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "CreateVisualsForComponent index(%d)\n", index );
366 Toolkit::VisualFactory visualFactory = Toolkit::VisualFactory::Get();
367 Toolkit::Visual::Base buttonVisual;
369 std::string imageUrl;
370 if( value.Get( imageUrl ) )
372 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "CreateVisualsForComponent Using image URL(%d)\n", index );
373 if ( !imageUrl.empty() )
375 DALI_ASSERT_DEBUG( index != Toolkit::Button::Property::LABEL && "Creating a Image Visual instead of Text Visual " );
376 buttonVisual = visualFactory.CreateVisual( imageUrl, ImageDimensions() );
381 // if its not a string then get a Property::Map from the property if possible.
382 Property::Map *map = value.GetMap();
383 if( map && !map->Empty() ) // Empty map results in current visual removal.
385 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "CreateVisualsForComponent Using Map(%d)\n", index );
386 buttonVisual = visualFactory.CreateVisual( *map );
392 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "CreateVisualsForComponent RegisterVisual index(%d) enabled(%s)\n",
393 index, DevelControl::IsVisualEnabled( *this, index )?"true":"false" );
394 // enable the visual if needed for current state
395 const bool enabled = ( ( index == VISUAL_INDEX_FOR_STATE[ mButtonState ][ BACKGROUND ] )||
396 ( index == VISUAL_INDEX_FOR_STATE[ mButtonState ][ FOREGROUND ] )||
397 ( index == Toolkit::Button::Property::LABEL ) );
398 DevelControl::RegisterVisual( *this, index, buttonVisual, enabled, visualDepth );
402 DevelControl::UnregisterVisual( *this, index );
403 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "CreateVisualsForComponent Visual not created or empty map (clearing visual).(%d)\n", index);
408 bool Button::GetPropertyMapForVisual( Property::Index visualIndex, Property::Map& retreivedMap ) const
410 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "GetPropertyMapForVisual visual(%d)\n", visualIndex);
411 bool success = false;
412 Toolkit::Visual::Base visual = DevelControl::GetVisual( *this, visualIndex );
415 visual.CreatePropertyMap( retreivedMap );
418 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "GetPropertyMapForVisual %s\n", success?"Success":"Failure");
422 bool Button::DoAction( BaseObject* object, const std::string& actionName, const Property::Map& attributes )
426 Dali::BaseHandle handle( object );
428 Toolkit::Button button = Toolkit::Button::DownCast( handle );
430 DALI_ASSERT_DEBUG( button );
432 if( 0 == strcmp( actionName.c_str(), ACTION_BUTTON_CLICK ) )
434 ret = GetImplementation( button ).DoClickAction( attributes );
440 bool Button::DoClickAction( const Property::Map& attributes )
442 // Prevents the button signals from doing a recursive loop by sending an action
443 // and re-emitting the signals.
444 if( !mClickActionPerforming )
446 mClickActionPerforming = true;
448 if ( !mTogglableButton )
450 mButtonPressedState = DEPRESSED;
453 mClickActionPerforming = false;
461 void Button::ButtonDown()
463 if( mTogglableButton )
465 if ( mButtonState != SELECTED_STATE )
468 mButtonPressedState = TOGGLE_DEPRESSED;
472 mButtonPressedState = DEPRESSED;
478 mButtonPressedState = DEPRESSED;
481 SetUpTimer( mInitialAutoRepeatingDelay );
485 // The pressed signal should be emitted regardless of toggle mode.
486 Toolkit::Button handle( GetOwner() );
487 mPressedSignal.Emit( handle );
490 void Button::ButtonUp()
492 bool emitSignalsForPressAndReleaseAction = false;
494 if( DEPRESSED == mButtonPressedState )
496 if( mTogglableButton ) // Button up will change state
498 emitSignalsForPressAndReleaseAction = OnToggleReleased(); // Derived toggle buttons can override this to provide custom behaviour
502 Released(); // Button up will result in unselected state
505 mAutoRepeatingTimer.Reset();
507 emitSignalsForPressAndReleaseAction = true;
510 else if ( TOGGLE_DEPRESSED == mButtonPressedState )
512 emitSignalsForPressAndReleaseAction = true; // toggle released after being pressed, a click
515 if ( emitSignalsForPressAndReleaseAction )
517 // The clicked and released signals should be emitted regardless of toggle mode.
518 Toolkit::Button handle( GetOwner() );
519 mReleasedSignal.Emit( handle );
520 mClickedSignal.Emit( handle );
524 bool Button::OnToggleReleased()
526 SetSelected( !IsSelected() );
527 mButtonPressedState = UNPRESSED;
532 void Button::OnTouchPointLeave()
534 if( DEPRESSED == mButtonPressedState )
536 if( !mTogglableButton )
542 mAutoRepeatingTimer.Reset();
546 mButtonPressedState = UNPRESSED;
548 // The released signal should be emitted regardless of toggle mode.
549 Toolkit::Button handle( GetOwner() );
550 mReleasedSignal.Emit( handle );
554 void Button::OnTouchPointInterrupted()
559 Toolkit::Button::ButtonSignalType& Button::PressedSignal()
561 return mPressedSignal;
564 Toolkit::Button::ButtonSignalType& Button::ReleasedSignal()
566 return mReleasedSignal;
569 Toolkit::Button::ButtonSignalType& Button::ClickedSignal()
571 return mClickedSignal;
574 Toolkit::Button::ButtonSignalType& Button::StateChangedSignal()
576 return mStateChangedSignal;
579 bool Button::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
581 Dali::BaseHandle handle( object );
583 bool connected( true );
584 Toolkit::Button button = Toolkit::Button::DownCast( handle );
586 if( 0 == strcmp( signalName.c_str(), SIGNAL_PRESSED ) )
588 button.PressedSignal().Connect( tracker, functor );
590 else if( 0 == strcmp( signalName.c_str(), SIGNAL_RELEASED ) )
592 button.ReleasedSignal().Connect( tracker, functor );
594 else if( 0 == strcmp( signalName.c_str(), SIGNAL_CLICKED ) )
596 button.ClickedSignal().Connect( tracker, functor );
598 else if( 0 == strcmp( signalName.c_str(), SIGNAL_STATE_CHANGED ) )
600 button.StateChangedSignal().Connect( tracker, functor );
604 // signalName does not match any signal
611 void Button::OnInitialize()
613 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::OnInitialize\n" );
617 mTapDetector = TapGestureDetector::New();
618 mTapDetector.Attach( self );
619 mTapDetector.DetectedSignal().Connect(this, &Button::OnTap);
621 self.SetProperty( Actor::Property::KEYBOARD_FOCUSABLE, true );
623 self.TouchedSignal().Connect( this, &Button::OnTouch );
626 bool Button::OnAccessibilityActivated()
628 return OnKeyboardEnter();
631 bool Button::OnTouch( Actor actor, const TouchEvent& touch )
633 if( !IsDisabled() && (actor == touch.GetHitActor(0)) )
635 if ( 1 == touch.GetPointCount() )
637 switch( touch.GetState( 0 ) )
639 case PointState::DOWN:
649 case PointState::INTERRUPTED:
651 OnTouchPointInterrupted();
654 case PointState::LEAVE:
659 case PointState::MOTION:
660 case PointState::STATIONARY: // FALLTHROUGH
667 else if( 1 < touch.GetPointCount() )
669 OnTouchPointLeave(); // Notification for derived classes.
671 // Sets the button state to the default
672 mButtonPressedState = UNPRESSED;
678 bool Button::OnKeyboardEnter()
680 // When the enter key is pressed, or button is activated, the click action is performed.
681 Property::Map attributes;
682 bool ret = DoClickAction( attributes );
687 void Button::OnSceneDisconnection()
689 if( DEPRESSED == mButtonPressedState )
691 if( !mTogglableButton )
697 mAutoRepeatingTimer.Reset();
702 mButtonPressedState = UNPRESSED;
704 Control::OnSceneDisconnection(); // Visuals will be set off stage
707 void Button::OnSceneConnection( int depth )
709 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::OnSceneConnection ptr(%p) \n", this );
710 OnButtonVisualRemoval( VISUAL_INDEX_FOR_STATE[ mPreviousButtonState ][ BACKGROUND ] );
711 OnButtonVisualRemoval( VISUAL_INDEX_FOR_STATE[ mPreviousButtonState ][ FOREGROUND ] );
712 SelectRequiredVisual( Toolkit::Button::Property::LABEL );
713 SelectRequiredVisual( VISUAL_INDEX_FOR_STATE[ mButtonState ][ BACKGROUND ] );
714 SelectRequiredVisual( VISUAL_INDEX_FOR_STATE[ mButtonState ][ FOREGROUND ] );
715 Control::OnSceneConnection( depth ); // Enabled visuals will be put on stage
719 Vector3 Button::GetNaturalSize()
721 Vector3 size = Vector3::ZERO;
723 bool horizontalAlignment = mTextLabelAlignment == BEGIN || mTextLabelAlignment == END; // label and visual side by side
725 // Get natural size of foreground ( largest of the possible visuals )
726 Size largestProvidedVisual;
727 Size labelSize = Size::ZERO;
729 bool foreGroundVisualUsed = false;
731 for ( int state = Button::UNSELECTED_STATE; state < Button::STATE_COUNT; state++ )
733 Toolkit::Visual::Base visual = DevelControl::GetVisual( *this, VISUAL_INDEX_FOR_STATE[state][FOREGROUND] );
737 visual.GetNaturalSize( visualSize );
738 largestProvidedVisual.width = std::max(largestProvidedVisual.width, visualSize.width );
739 largestProvidedVisual.height = std::max(largestProvidedVisual.height, visualSize.height );
740 foreGroundVisualUsed = true;
744 if ( !foreGroundVisualUsed ) // If foreground visual not supplied then use the background visual to calculate Natural size
746 for ( int state = Button::UNSELECTED_STATE; state < Button::STATE_COUNT; state++ )
748 Toolkit::Visual::Base visual = DevelControl::GetVisual( *this, VISUAL_INDEX_FOR_STATE[state][BACKGROUND] );
752 visual.GetNaturalSize( visualSize );
753 largestProvidedVisual.width = std::max(largestProvidedVisual.width, visualSize.width );
754 largestProvidedVisual.height = std::max(largestProvidedVisual.height, visualSize.height );
759 // Get horizontal padding total
760 if ( largestProvidedVisual.width > 0 ) // if visual exists
762 size.width += largestProvidedVisual.width + mForegroundPadding.left + mForegroundPadding.right;
764 // Get vertical padding total
765 if ( largestProvidedVisual.height > 0 )
767 size.height += largestProvidedVisual.height + mForegroundPadding.top + mForegroundPadding.bottom;
770 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "GetNaturalSize visual Size(%f,%f)\n",
771 largestProvidedVisual.width, largestProvidedVisual.height );
773 // Get natural size of label if text has been set
774 if ( mTextStringSetFlag )
776 Toolkit::Visual::Base visual = DevelControl::GetVisual( *this, Toolkit::Button::Property::LABEL );
780 visual.GetNaturalSize( labelSize );
782 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "GetNaturalSize labelSize(%f,%f) padding(%f,%f)\n",
783 labelSize.width, labelSize.height, mLabelPadding.left + mLabelPadding.right, mLabelPadding.top + mLabelPadding.bottom);
785 labelSize.width += mLabelPadding.left + mLabelPadding.right;
786 labelSize.height += mLabelPadding.top + mLabelPadding.bottom;
788 // Add label size to height or width depending on alignment position
789 if ( horizontalAlignment )
791 size.width += labelSize.width;
792 size.height = std::max(size.height, labelSize.height );
796 size.height += labelSize.height;
797 size.width = std::max(size.width, labelSize.width );
802 if( size.width < 1 && size.height < 1 )
804 // if no image or label then use Control's natural size
805 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "GetNaturalSize Using control natural size\n");
806 size = Control::GetNaturalSize();
809 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "Button GetNaturalSize (%f,%f)\n", size.width, size.height );
814 void Button::OnSetResizePolicy( ResizePolicy::Type policy, Dimension::Type dimension )
816 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnSetResizePolicy\n");
821 * Visuals are sized and positioned in this function.
822 * Whilst the control has it's size negotiated it has to size it's visuals explicitly here.
825 void Button::OnRelayout( const Vector2& size, RelayoutContainer& container )
827 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout targetSize(%f,%f) ptr(%p) state[%d]\n", size.width, size.height, this, mButtonState );
829 Toolkit::Visual::Base currentVisual = DevelControl::GetVisual( *this, VISUAL_INDEX_FOR_STATE[mButtonState][FOREGROUND] );
830 Toolkit::Visual::Base currentBackGroundVisual = DevelControl::GetVisual( *this, VISUAL_INDEX_FOR_STATE[mButtonState][BACKGROUND] );
832 // Sizes and padding set to zero, if not present then values will no effect calculations.
833 Vector2 visualPosition = Vector2::ZERO;
834 Vector2 labelPosition = Vector2::ZERO;
835 Size visualSize = Size::ZERO;
836 Padding foregroundVisualPadding = Padding(0.0f, 0.0f, 0.0f, 0.0f );
837 Padding labelVisualPadding = Padding(0.0f, 0.0f, 0.0f, 0.0f );
839 if ( mTextStringSetFlag )
841 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout Label padding setting padding:%f,%f,%f,%f\n", mLabelPadding.y, mLabelPadding.x, mLabelPadding.width,mLabelPadding.height );
842 labelVisualPadding = mLabelPadding;
847 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout Foreground Visual setting padding:%f,%f,%f,%f\n", mForegroundPadding.y, mForegroundPadding.x, mForegroundPadding.width,mForegroundPadding.height );
848 currentVisual.GetNaturalSize( visualSize );
849 foregroundVisualPadding = mForegroundPadding;
852 Toolkit::Align::Type visualAnchorPoint = Toolkit::Align::TOP_BEGIN;
854 Vector2 visualAndPaddingSize = Vector2( ( foregroundVisualPadding.x + visualSize.width + foregroundVisualPadding.y ),
855 ( foregroundVisualPadding.width + visualSize.height + foregroundVisualPadding.height ));
857 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout visualAndPaddingSize(%f,%f)\n", visualAndPaddingSize.width, visualAndPaddingSize.height);
859 // Text Visual should take all space available after foreground visual size and all padding is considered.
860 // Remaining Space priority, Foreground padding, foreground visual, Text padding then Text visual.
861 Size remainingSpaceForText = Size::ZERO;
863 switch ( mTextLabelAlignment )
867 visualAnchorPoint = Toolkit::Align::TOP_END;
868 visualPosition.x = foregroundVisualPadding.right;
869 visualPosition.y = foregroundVisualPadding.top;
871 labelPosition.x = labelVisualPadding.x;
872 labelPosition.y = labelVisualPadding.top;
874 remainingSpaceForText.width = size.width - visualAndPaddingSize.width - labelVisualPadding.x - labelVisualPadding.y;
875 remainingSpaceForText.height = size.height - labelVisualPadding.top - labelVisualPadding.bottom;
880 visualAnchorPoint = Toolkit::Align::TOP_BEGIN;
881 visualPosition.x = foregroundVisualPadding.left;
882 visualPosition.y = foregroundVisualPadding.top;
884 labelPosition.x = visualAndPaddingSize.width + labelVisualPadding.x;
885 labelPosition.y = labelVisualPadding.top;
887 remainingSpaceForText.width = size.width - visualAndPaddingSize.width - labelVisualPadding.x - labelVisualPadding.y;
888 remainingSpaceForText.height = size.height - labelVisualPadding.top - labelVisualPadding.bottom;
893 visualAnchorPoint = Toolkit::Align::BOTTOM_END;
894 visualPosition.x = foregroundVisualPadding.left;
895 visualPosition.y = foregroundVisualPadding.bottom;
897 labelPosition.x = labelVisualPadding.left;
898 labelPosition.y = labelVisualPadding.top;
900 remainingSpaceForText.width = size.width - labelVisualPadding.x - labelVisualPadding.y;
901 remainingSpaceForText.height = size.height - visualAndPaddingSize.height - labelVisualPadding.top - labelVisualPadding.bottom;
907 visualAnchorPoint = Toolkit::Align::TOP_END;
908 visualPosition.x = foregroundVisualPadding.left;
909 visualPosition.y = foregroundVisualPadding.top;
911 labelPosition.x = labelVisualPadding.left;
912 labelPosition.y = visualAndPaddingSize.height + labelVisualPadding.top;
914 remainingSpaceForText.width = size.width - labelVisualPadding.x - labelVisualPadding.y;
915 remainingSpaceForText.height = size.height - visualAndPaddingSize.height - labelVisualPadding.top - labelVisualPadding.bottom;
921 if ( currentBackGroundVisual )
923 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout Setting visual background size to(%f,%f)\n", size.width, size.height);
925 Property::Map visualTransform;
927 visualTransform.Add( Toolkit::Visual::Transform::Property::SIZE, size )
928 .Add( Toolkit::Visual::Transform::Property::SIZE_POLICY, Vector2( Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE ) );
930 currentBackGroundVisual.SetTransformAndSize( visualTransform, size );
935 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout Setting visual size to(%f,%f)\n", visualSize.width, visualSize.height);
937 Property::Map visualTransform;
939 visualTransform.Add( Toolkit::Visual::Transform::Property::SIZE, visualSize )
940 .Add( Toolkit::Visual::Transform::Property::OFFSET, visualPosition )
941 .Add( Toolkit::Visual::Transform::Property::OFFSET_POLICY, Vector2( Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE ) )
942 .Add( Toolkit::Visual::Transform::Property::SIZE_POLICY, Vector2( Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE ) )
943 .Add( Toolkit::Visual::Transform::Property::ORIGIN, Toolkit::Align::TOP_BEGIN )
944 .Add( Toolkit::Visual::Transform::Property::ANCHOR_POINT, visualAnchorPoint );
946 currentVisual.SetTransformAndSize( visualTransform, size );
949 if ( mTextStringSetFlag )
951 Toolkit::Visual::Base textVisual = DevelControl::GetVisual( *this, Toolkit::Button::Property::LABEL ); // No need to search for Label visual if no text set.
955 if ( !currentVisual )
957 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout Only Text\n");
958 labelPosition.x = labelVisualPadding.left;
959 labelPosition.y = labelVisualPadding.height;
962 Vector2 preSize = Vector2( static_cast< int >( remainingSpaceForText.x ), static_cast< int >( remainingSpaceForText.y ));
964 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout text Size(%f,%f) text Position(%f,%f) \n", remainingSpaceForText.width, remainingSpaceForText.height, labelPosition.x, labelPosition.y);
966 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout text Size -- (%f,%f) text Position(%f,%f) \n", preSize.width, preSize.height, labelPosition.x, labelPosition.y);
969 Property::Map textVisualTransform;
970 textVisualTransform.Add( Toolkit::Visual::Transform::Property::SIZE, preSize )
971 .Add( Toolkit::Visual::Transform::Property::OFFSET, labelPosition )
972 .Add( Toolkit::Visual::Transform::Property::OFFSET_POLICY, Vector2( Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE ) )
973 .Add( Toolkit::Visual::Transform::Property::SIZE_POLICY, Vector2( Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE ) )
974 .Add( Toolkit::Visual::Transform::Property::ORIGIN, Toolkit::Align::TOP_BEGIN )
975 .Add( Toolkit::Visual::Transform::Property::ANCHOR_POINT, visualAnchorPoint );
977 textVisual.SetTransformAndSize( textVisualTransform, size );
981 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout selected (%s) \n", IsSelected()?"yes":"no" );
983 DALI_LOG_INFO( gLogButtonFilter, Debug::General, "OnRelayout << \n");
986 void Button::OnTap(Actor actor, const TapGesture& tap)
988 // Prevents Parent getting a tap event
991 void Button::SetUpTimer( float delay )
993 mAutoRepeatingTimer = Dali::Timer::New( static_cast<unsigned int>( 1000.f * delay ) );
994 mAutoRepeatingTimer.TickSignal().Connect( this, &Button::AutoRepeatingSlot );
995 mAutoRepeatingTimer.Start();
998 bool Button::AutoRepeatingSlot()
1000 bool consumed = false;
1003 // Restart the autorepeat timer.
1004 SetUpTimer( mNextAutoRepeatingDelay );
1008 Toolkit::Button handle( GetOwner() );
1011 consumed = mReleasedSignal.Emit( handle );
1012 consumed = mClickedSignal.Emit( handle );
1013 consumed |= mPressedSignal.Emit( handle );
1019 void Button::Pressed()
1021 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::Pressed\n" );
1023 if( mButtonState == UNSELECTED_STATE )
1025 ChangeState( SELECTED_STATE );
1026 OnPressed(); // Notifies the derived class the button has been pressed.
1030 void Button::Released()
1032 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::Released\n" );
1034 if( mButtonState == SELECTED_STATE && !mTogglableButton )
1036 ChangeState( UNSELECTED_STATE );
1037 OnReleased(); // // Notifies the derived class the button has been released.
1039 mButtonPressedState = UNPRESSED;
1042 void Button::SelectRequiredVisual( Property::Index visualIndex )
1044 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::SelectRequiredVisual index(%d) state(%d)\n", visualIndex, mButtonState );
1045 // only enable visuals that exist
1046 if( DevelControl::GetVisual( *this, visualIndex ) )
1048 DevelControl::EnableVisual( *this, visualIndex, true );
1052 void Button::RemoveVisual( Property::Index visualIndex )
1054 // Use OnButtonVisualRemoval if want button developer to have the option to override removal.
1055 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::RemoveVisual index(%d) state(%d)\n", visualIndex, mButtonState );
1057 Toolkit::Visual::Base visual = DevelControl::GetVisual( *this, visualIndex );
1061 DevelControl::EnableVisual( *this, visualIndex, false );
1065 void Button::OnButtonVisualRemoval( Property::Index visualIndex )
1067 // Derived Buttons can over ride this to prevent default removal.
1068 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::OnButtonVisualRemoval index(%d)\n", visualIndex );
1069 RemoveVisual( visualIndex );
1072 void Button::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
1074 Toolkit::Button button = Toolkit::Button::DownCast( Dali::BaseHandle( object ) );
1076 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::SetProperty index[%d]\n", index );
1082 case Toolkit::Button::Property::DISABLED:
1084 GetImplementation( button ).SetDisabled( value.Get< bool >() );
1088 case Toolkit::Button::Property::AUTO_REPEATING:
1090 GetImplementation( button ).SetAutoRepeating( value.Get< bool >() );
1094 case Toolkit::Button::Property::INITIAL_AUTO_REPEATING_DELAY:
1096 GetImplementation( button ).SetInitialAutoRepeatingDelay( value.Get< float >() );
1100 case Toolkit::Button::Property::NEXT_AUTO_REPEATING_DELAY:
1102 GetImplementation( button ).SetNextAutoRepeatingDelay( value.Get< float >() );
1106 case Toolkit::Button::Property::TOGGLABLE:
1108 GetImplementation( button ).SetTogglableButton( value.Get< bool >() );
1112 case Toolkit::Button::Property::SELECTED:
1114 GetImplementation( button ).SetSelected( value.Get< bool >() );
1118 case Toolkit::Button::Property::UNSELECTED_VISUAL:
1119 case Toolkit::Button::Property::SELECTED_VISUAL:
1120 case Toolkit::Button::Property::DISABLED_SELECTED_VISUAL:
1121 case Toolkit::Button::Property::DISABLED_UNSELECTED_VISUAL:
1123 GetImplementation( button ).CreateVisualsForComponent( index, value, DepthIndex::CONTENT );
1127 case Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL:
1128 case Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL:
1129 case Toolkit::Button::Property::DISABLED_SELECTED_BACKGROUND_VISUAL:
1130 case Toolkit::Button::Property::DISABLED_UNSELECTED_BACKGROUND_VISUAL:
1132 GetImplementation( button ).CreateVisualsForComponent( index , value, DepthIndex::BACKGROUND);
1136 case Toolkit::Button::Property::LABEL:
1138 Property::Map outTextVisualProperties;
1139 std::string textString;
1141 if ( value.Get( textString ) )
1143 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::SetProperty Setting TextVisual with string[%s]\n", textString.c_str() );
1145 Property::Map setPropertyMap;
1146 setPropertyMap.Add( Toolkit::Visual::Property::TYPE, Toolkit::Visual::TEXT )
1147 .Add( Toolkit::TextVisual::Property::TEXT, textString );
1149 GetImplementation( button ).MergeWithExistingLabelProperties( setPropertyMap, outTextVisualProperties );
1153 // Get a Property::Map from the property if possible.
1154 Property::Map* setPropertyMap = value.GetMap();
1155 if( setPropertyMap )
1157 TextVisual::ConvertStringKeysToIndexKeys( *setPropertyMap );
1158 GetImplementation( button ).MergeWithExistingLabelProperties( *setPropertyMap, outTextVisualProperties );
1162 if( !outTextVisualProperties.Empty() )
1164 GetImplementation( button ).CreateVisualsForComponent( index, outTextVisualProperties, DepthIndex::CONTENT );
1169 case Toolkit::DevelButton::Property::LABEL_RELATIVE_ALIGNMENT:
1171 Button::Align labelAlignment(END);
1172 Scripting::GetEnumeration< Button::Align> ( value.Get< std::string >().c_str(),
1173 ALIGNMENT_TABLE, ALIGNMENT_TABLE_COUNT,
1176 GetImplementation( button ).SetLabelAlignment( labelAlignment );
1180 case Toolkit::DevelButton::Property::LABEL_PADDING:
1182 Vector4 padding ( value.Get< Vector4 >() );
1183 GetImplementation( button ).SetLabelPadding( Padding( padding.x, padding.y, padding.z, padding.w ) );
1187 case Toolkit::DevelButton::Property::VISUAL_PADDING:
1189 Vector4 padding ( value.Get< Vector4 >() );
1190 GetImplementation( button ).SetForegroundPadding( Padding( padding.x, padding.y, padding.z, padding.w ) );
1197 Property::Value Button::GetProperty( BaseObject* object, Property::Index propertyIndex )
1199 Property::Value value;
1201 Toolkit::Button button = Toolkit::Button::DownCast( Dali::BaseHandle( object ) );
1205 switch ( propertyIndex )
1207 case Toolkit::Button::Property::DISABLED:
1209 value = GetImplementation( button ).IsDisabled();
1213 case Toolkit::Button::Property::AUTO_REPEATING:
1215 value = GetImplementation( button ).mAutoRepeating;
1219 case Toolkit::Button::Property::INITIAL_AUTO_REPEATING_DELAY:
1221 value = GetImplementation( button ).mInitialAutoRepeatingDelay;
1225 case Toolkit::Button::Property::NEXT_AUTO_REPEATING_DELAY:
1227 value = GetImplementation( button ).mNextAutoRepeatingDelay;
1231 case Toolkit::Button::Property::TOGGLABLE:
1233 value = GetImplementation( button ).mTogglableButton;
1237 case Toolkit::Button::Property::SELECTED:
1239 value = GetImplementation( button ).IsSelected();
1243 case Toolkit::Button::Property::UNSELECTED_VISUAL:
1244 case Toolkit::Button::Property::SELECTED_VISUAL:
1245 case Toolkit::Button::Property::DISABLED_SELECTED_VISUAL:
1246 case Toolkit::Button::Property::DISABLED_UNSELECTED_VISUAL:
1247 case Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL:
1248 case Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL:
1249 case Toolkit::Button::Property::DISABLED_SELECTED_BACKGROUND_VISUAL:
1250 case Toolkit::Button::Property::DISABLED_UNSELECTED_BACKGROUND_VISUAL:
1251 case Toolkit::Button::Property::LABEL:
1253 Property::Map visualProperty;
1254 if ( GetImplementation( button ).GetPropertyMapForVisual( propertyIndex, visualProperty ) )
1256 value = visualProperty;
1261 case Toolkit::DevelButton::Property::LABEL_RELATIVE_ALIGNMENT:
1263 const char* alignment = Scripting::GetEnumerationName< Button::Align >( GetImplementation( button ).GetLabelAlignment(),
1264 ALIGNMENT_STRING_TABLE,
1265 ALIGNMENT_STRING_TABLE_COUNT );
1268 value = std::string( alignment );
1274 case Toolkit::DevelButton::Property::LABEL_PADDING:
1276 Padding padding = GetImplementation( button ).GetLabelPadding();
1277 value = Vector4( padding.x, padding.y, padding.top, padding.bottom);
1281 case Toolkit::DevelButton::Property::VISUAL_PADDING:
1283 Padding padding = GetImplementation( button ).GetForegroundPadding();
1284 value = Vector4( padding.x, padding.y, padding.top, padding.bottom);
1292 void Button::SetLabelPadding( const Padding& padding)
1294 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::SetLabelPadding padding(%f,%f,%f,%f)\n", padding.left, padding.right, padding.bottom, padding.top );
1295 mLabelPadding = Padding( padding.left, padding.right, padding.bottom, padding.top );
1299 Padding Button::GetLabelPadding()
1301 return mLabelPadding;
1304 void Button::SetForegroundPadding( const Padding& padding)
1306 DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "Button::SetForegroundPadding padding(%f,%f,%f,%f)\n", padding.left, padding.right, padding.bottom, padding.top );
1307 mForegroundPadding = Padding( padding.left, padding.right, padding.bottom, padding.top );
1311 Padding Button::GetForegroundPadding()
1313 return mForegroundPadding;
1316 } // namespace Internal
1318 } // namespace Toolkit