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