[AT-SPI] Squashed implementation
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / buttons / toggle-button-impl.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include "toggle-button-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/object/type-registry.h>
23 #include <dali/public-api/object/type-registry-helper.h>
24 #include <dali/devel-api/scripting/scripting.h>
25 #include <dali/public-api/object/property-array.h>
26 #include <dali/integration-api/debug.h>
27
28 // INTERNAL INCLUDES
29 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
30 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
31 #include <dali-toolkit/devel-api/controls/control-devel.h>
32 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
33 #include <dali-toolkit/public-api/visuals/visual-properties.h>
34 #include <dali-toolkit/public-api/align-enumerations.h>
35 #include <dali-toolkit/devel-api/controls/tooltip/tooltip-properties.h>
36 #include <dali-toolkit/public-api/visuals/text-visual-properties.h>
37 #include <dali-toolkit/devel-api/controls/control-devel.h>
38
39 #if defined(DEBUG_ENABLED)
40   extern Debug::Filter* gLogButtonFilter;
41 #endif
42
43 namespace Dali
44 {
45
46 namespace Toolkit
47 {
48
49 namespace Internal
50 {
51
52 namespace
53 {
54
55 BaseHandle Create()
56 {
57   return Toolkit::ToggleButton::New();
58 }
59
60 // Properties
61 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::ToggleButton, Toolkit::Button, Create )
62
63 DALI_PROPERTY_REGISTRATION( Toolkit, ToggleButton, "stateVisuals",       ARRAY,     STATE_VISUALS        )
64 DALI_PROPERTY_REGISTRATION( Toolkit, ToggleButton, "tooltips",           ARRAY,     TOOLTIPS             )
65 DALI_PROPERTY_REGISTRATION( Toolkit, ToggleButton, "currentStateIndex",  INTEGER,   CURRENT_STATE_INDEX  )
66
67 DALI_TYPE_REGISTRATION_END()
68
69 } // unnamed namespace
70
71 Dali::Toolkit::ToggleButton ToggleButton::New()
72 {
73   DALI_LOG_INFO( gLogButtonFilter, Debug::General, "ToggleButton::New\n" );
74   // Create the implementation, temporarily owned on stack
75   IntrusivePtr< ToggleButton > internalToggleButton = new ToggleButton();
76
77   // Pass ownership to CustomActor
78   Dali::Toolkit::ToggleButton toggleButton( *internalToggleButton );
79
80   // Second-phase init of the implementation
81   // This can only be done after the CustomActor connection has been made...
82   internalToggleButton->Initialize();
83
84   return toggleButton;
85 }
86
87 ToggleButton::ToggleButton()
88 : Button(),
89   mToggleStates(),
90   mToggleVisuals(),
91   mToggleSelectedVisuals(),
92   mToggleDisabledVisuals(),
93   mToggleDisabledSelectedVisuals(),
94   mToggleTooltips(),
95   mCurrentToggleIndex(0)
96 {
97   DALI_LOG_INFO( gLogButtonFilter, Debug::General, "ToggleButton::Constructor\n" );
98   SetTogglableButton( false );
99   DevelControl::SetAccessibilityConstructor( Self(), []( Dali::Actor actor ) {
100     return std::unique_ptr< Dali::Accessibility::Accessible >(
101         new AccessibleImpl( actor, Dali::Accessibility::Role::TOGGLE_BUTTON ) );
102   } );
103 }
104
105 ToggleButton::~ToggleButton()
106 {
107 }
108
109 void ToggleButton::OnInitialize()
110 {
111   DALI_LOG_INFO( gLogButtonFilter, Debug::General, "ToggleButton::OnInitialize\n" );
112   Button::OnInitialize();
113
114   // Toggle button requires the Leave event.
115   Actor self = Self();
116   self.SetProperty( Actor::Property::LEAVE_REQUIRED, true );
117 }
118
119 void ToggleButton::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
120 {
121   Toolkit::ToggleButton toggleButton = Toolkit::ToggleButton::DownCast( Dali::BaseHandle( object ) );
122
123   DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "ToggleButton::SetProperty index[%d]\n", propertyIndex );
124
125   if ( toggleButton )
126   {
127     ToggleButton& toggleButtonImpl( GetImplementation( toggleButton ) );
128
129     switch ( propertyIndex )
130     {
131       case Toolkit::ToggleButton::Property::STATE_VISUALS:
132       {
133         Property::Array stateArray;
134         if( value.Get( stateArray ) )
135         {
136           toggleButtonImpl.SetToggleStates( stateArray );
137         }
138
139         break;
140       }
141       case Toolkit::ToggleButton::Property::TOOLTIPS:
142       {
143         const Property::Array* tipArray = value.GetArray();
144         if( tipArray )
145         {
146           std::vector<std::string> tips;
147           size_t tipsCount = tipArray->Count();
148           tips.resize( tipsCount );
149           for( size_t i = 0; i != tipsCount; ++i )
150           {
151             tipArray->GetElementAt( i ).Get( tips[i] );
152           }
153           toggleButtonImpl.SetToggleTooltips(tips);
154         }
155         break;
156       }
157       default :
158       {
159         break;
160       }
161     } // end of switch
162   }
163 }
164
165 Property::Value ToggleButton::GetProperty( BaseObject* object, Property::Index propertyIndex )
166 {
167   Property::Value value;
168
169   Toolkit::ToggleButton toggleButton = Toolkit::ToggleButton::DownCast( Dali::BaseHandle( object ) );
170
171   DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "ToggleButton::GetProperty index[%d]\n", propertyIndex );
172
173   if ( toggleButton )
174   {
175     ToggleButton& toggleButtonImpl( GetImplementation( toggleButton ) );
176
177     switch ( propertyIndex )
178     {
179       case Toolkit::ToggleButton::Property::STATE_VISUALS:
180       {
181         Property::Array array = toggleButtonImpl.GetToggleStates();
182         value = Property::Value( array );
183         break;
184       }
185       case Toolkit::ToggleButton::Property::TOOLTIPS:
186       {
187         Property::Value value1( Property::ARRAY );
188         Property::Array* tipArray = value1.GetArray();
189
190         if( tipArray )
191         {
192           std::vector<std::string> tips = toggleButtonImpl.GetToggleTooltips();
193           size_t tipsCount( tips.size() );
194           for( size_t i( 0 ); i != tipsCount; ++i )
195           {
196             tipArray->PushBack( tips[i] );
197           }
198         }
199         value = value1;
200         break;
201       }
202       case Toolkit::ToggleButton::Property::CURRENT_STATE_INDEX:
203       {
204         value = static_cast<int>(toggleButtonImpl.mCurrentToggleIndex);
205         break;
206       }
207     } // end of switch
208   }
209
210   return value;
211 }
212
213 void ToggleButton::CreateVisualsForAllStates( const Property::Array& states, std::vector<Toolkit::Visual::Base>& visuals )
214 {
215   DALI_LOG_INFO( gLogButtonFilter, Debug::General, "ToggleButton::CreateVisualsForAllStates\n" );
216
217   visuals.clear();
218
219   for ( unsigned int i = 0; i < states.Count(); i++ )
220   {
221     Property::Value value(  states[i] );
222
223     Toolkit::VisualFactory visualFactory = Toolkit::VisualFactory::Get();
224     Toolkit::Visual::Base stateVisual;
225
226     if ( value.GetType() == Property::MAP )
227     {
228       Property::Map *map = value.GetMap();
229       if( map && !map->Empty() ) // Empty map results in current visual removal.
230       {
231         DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "ToggleButton::CreateVisuals Using Map\n" );
232         stateVisual = visualFactory.CreateVisual( *map );
233       }
234     }
235     else if ( value.GetType() ==  Property::STRING )
236     {
237       std::string imageUrl = value.Get<std::string>();
238       DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "ToggleButton::CreateVisuals Using image URL\n" );
239       if ( !imageUrl.empty() )
240       {
241         stateVisual = visualFactory.CreateVisual( imageUrl, ImageDimensions() );
242       }
243     }
244
245     if ( stateVisual )
246     {
247       stateVisual.SetDepthIndex( DepthIndex::CONTENT );
248       visuals.push_back( stateVisual );
249     }
250   } // end of for
251   DALI_LOG_INFO( gLogButtonFilter, Debug::Verbose, "ToggleButton::CreateVisuals mToggleVisuals:%d\n", mToggleVisuals.size() );
252 }
253
254 void ToggleButton::SetToggleStates( const Property::Array& states )
255 { //this should really be generalized to be either string or maps so that any visual can be created.
256   DALI_LOG_INFO( gLogButtonFilter, Debug::General, "ToggleButton::SetToggleStates\n" );
257   if ( !states.Empty() )
258   {
259     mToggleStates.Clear();
260     mToggleStates = states;
261     /* New toggle button index from 0. */
262     mCurrentToggleIndex = 0;
263
264     // Create all visuals, save to mToggleVisuals.
265     CreateVisualsForAllStates( states, mToggleVisuals );
266     CreateVisualsForAllStates( states, mToggleSelectedVisuals );
267     CreateVisualsForAllStates( states, mToggleDisabledVisuals );
268     CreateVisualsForAllStates( states, mToggleDisabledSelectedVisuals );
269
270     DALI_LOG_INFO( gLogButtonFilter, Debug::General, "ToggleButton::Began to register visual.\n" );
271
272     PrepareVisual( Toolkit::Button::Property::UNSELECTED_VISUAL, mToggleVisuals[mCurrentToggleIndex] );
273     PrepareVisual( Toolkit::Button::Property::SELECTED_VISUAL, mToggleSelectedVisuals[mCurrentToggleIndex] );
274     PrepareVisual( Toolkit::Button::Property::DISABLED_UNSELECTED_VISUAL, mToggleDisabledVisuals[mCurrentToggleIndex] );
275     PrepareVisual( Toolkit::Button::Property::DISABLED_SELECTED_VISUAL, mToggleDisabledSelectedVisuals[mCurrentToggleIndex] );
276
277     RelayoutRequest();
278   }
279 }
280
281 Property::Array ToggleButton::GetToggleStates() const
282 {
283   return mToggleStates;
284 }
285
286 void ToggleButton::SetToggleTooltips( std::vector<std::string>& tips )
287 {
288   DALI_LOG_INFO( gLogButtonFilter, Debug::General, "ToggleButton::SetToggleTooltips\n" );
289   if ( !tips.empty() )
290   {
291     mToggleTooltips.clear();
292     mToggleTooltips.swap( tips );
293   }
294
295   if ( !mToggleTooltips.empty() && ( mCurrentToggleIndex < mToggleTooltips.size() ) )
296   {
297     Self().SetProperty( Toolkit::DevelControl::Property::TOOLTIP, mToggleTooltips[mCurrentToggleIndex] );
298   }
299
300   RelayoutRequest();
301 }
302
303 const std::vector<std::string>& ToggleButton::GetToggleTooltips() const
304 {
305   return mToggleTooltips;
306 }
307
308 void ToggleButton::PrepareVisual(Property::Index index, Toolkit::Visual::Base& visual)
309 {
310   bool enabled = false; // Disabled by default
311
312   // Unregister the visual with the given index if registered previously
313   if( DevelControl::GetVisual( *this, index ) )
314   {
315     // Check whether it was enabled to ensure we do the same with the new visual we're registering
316     enabled = DevelControl::IsVisualEnabled( *this, index );
317     DevelControl::UnregisterVisual( *this, index );
318   }
319
320   DevelControl::RegisterVisual( *this, index, visual, enabled );
321 }
322
323 void ToggleButton::RelayoutVisual( Property::Index index, const Vector2& size )
324 {
325   Toolkit::Visual::Base visual = DevelControl::GetVisual( *this, index );
326   if ( visual )
327   {
328     Size visualSize = Size::ZERO;
329     Vector2 visualPosition = Vector2::ZERO;
330
331     visual.GetNaturalSize( visualSize );
332
333     DALI_LOG_INFO( gLogButtonFilter, Debug::General, "ToggleButton::OnRelayout Setting visual size to(%f,%f)\n", visualSize.width, visualSize.height );
334     DALI_LOG_INFO( gLogButtonFilter, Debug::General, "ToggleButton::OnRelayout Setting visual position to(%f,%f)\n", visualPosition.x, visualPosition.y );
335
336     Property::Map visualTransform;
337     visualTransform.Add( Toolkit::Visual::Transform::Property::SIZE, visualSize )
338                    .Add( Toolkit::Visual::Transform::Property::OFFSET, visualPosition )
339                    .Add( Toolkit::Visual::Transform::Property::OFFSET_POLICY, Vector2( Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE ) )
340                    .Add( Toolkit::Visual::Transform::Property::SIZE_POLICY, Vector2( Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE ) )
341                    .Add( Toolkit::Visual::Transform::Property::ORIGIN, Toolkit::Align::CENTER )
342                    .Add( Toolkit::Visual::Transform::Property::ANCHOR_POINT, Toolkit::Align::CENTER );
343
344     visual.SetTransformAndSize( visualTransform, size );
345   }
346 }
347
348 void ToggleButton::OnRelayout( const Vector2& size, RelayoutContainer& container )
349 {
350   DALI_LOG_INFO( gLogButtonFilter, Debug::General, "ToggleButton::OnRelayout targetSize(%f,%f) ptr(%p)\n", size.width, size.height, this );
351
352   RelayoutVisual( Toolkit::Button::Property::UNSELECTED_VISUAL, size );
353   RelayoutVisual( Toolkit::Button::Property::SELECTED_VISUAL, size );
354   RelayoutVisual( Toolkit::Button::Property::DISABLED_UNSELECTED_VISUAL, size );
355   RelayoutVisual( Toolkit::Button::Property::DISABLED_SELECTED_VISUAL, size );
356 }
357
358 void ToggleButton::OnPressed()
359 {
360   DALI_LOG_INFO( gLogButtonFilter, Debug::General, "ToggleButton::OnPressed\n" );
361   // State index will add 1 only when button is pressed.
362   mCurrentToggleIndex = ( mCurrentToggleIndex + 1 ) % mToggleVisuals.size();
363
364   // Both create SelectedVisual and UnselectedVisual
365   PrepareVisual( Toolkit::Button::Property::UNSELECTED_VISUAL, mToggleVisuals[mCurrentToggleIndex] );
366   PrepareVisual( Toolkit::Button::Property::SELECTED_VISUAL, mToggleSelectedVisuals[mCurrentToggleIndex] );
367   PrepareVisual( Toolkit::Button::Property::DISABLED_UNSELECTED_VISUAL, mToggleDisabledVisuals[mCurrentToggleIndex] );
368   PrepareVisual( Toolkit::Button::Property::DISABLED_SELECTED_VISUAL, mToggleDisabledSelectedVisuals[mCurrentToggleIndex] );
369
370   //Need to check mCurrentToggleIndex, it must less than the size of mToggleTooltips.
371   if ( !mToggleTooltips.empty() && ( mCurrentToggleIndex < mToggleTooltips.size() ) )
372   {
373     Self().SetProperty( Toolkit::DevelControl::Property::TOOLTIP, mToggleTooltips[mCurrentToggleIndex] );
374   }
375
376   RelayoutRequest();
377 }
378
379 void ToggleButton::OnStateChange( State newState )
380 {
381   // TODO: replace it with OnPropertySet hook once Button::Property::SELECTED will be consistently used
382   if (Dali::Accessibility::IsUp() && (newState == SELECTED_STATE || newState == UNSELECTED_STATE))
383   {
384     Dali::Accessibility::Accessible::Get(Self())->EmitStateChanged(
385       Dali::Accessibility::State::CHECKED, newState == SELECTED_STATE ? 1 : 0, 0
386     );
387   }
388 }
389
390 } // namespace Internal
391
392 } // namespace Toolkit
393
394 } // namespace Dali