2 * Copyright (c) 2024 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 "toggle-button-impl.h"
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>
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>
38 #if defined(DEBUG_ENABLED)
39 extern Debug::Filter* gLogButtonFilter;
52 return Toolkit::ToggleButton::New();
56 DALI_TYPE_REGISTRATION_BEGIN(Toolkit::ToggleButton, Toolkit::Button, Create)
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)
62 DALI_TYPE_REGISTRATION_END()
64 } // unnamed namespace
66 Dali::Toolkit::ToggleButton ToggleButton::New()
68 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "ToggleButton::New\n");
69 // Create the implementation, temporarily owned on stack
70 IntrusivePtr<ToggleButton> internalToggleButton = new ToggleButton();
72 // Pass ownership to CustomActor
73 Dali::Toolkit::ToggleButton toggleButton(*internalToggleButton);
75 // Second-phase init of the implementation
76 // This can only be done after the CustomActor connection has been made...
77 internalToggleButton->Initialize();
82 ToggleButton::ToggleButton()
86 mToggleSelectedVisuals(),
87 mToggleDisabledVisuals(),
88 mToggleDisabledSelectedVisuals(),
90 mCurrentToggleIndex(0)
92 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "ToggleButton::Constructor\n");
93 SetTogglableButton(false);
96 ToggleButton::~ToggleButton()
100 void ToggleButton::OnInitialize()
102 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "ToggleButton::OnInitialize\n");
103 Button::OnInitialize();
105 // Toggle button requires the Leave event.
107 self.SetProperty(Actor::Property::LEAVE_REQUIRED, true);
109 self.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, Dali::Accessibility::Role::TOGGLE_BUTTON);
112 DevelControl::ControlAccessible* ToggleButton::CreateAccessibleObject()
114 return new ToggleButtonAccessible(Self());
117 void ToggleButton::SetProperty(BaseObject* object, Property::Index propertyIndex, const Property::Value& value)
119 Toolkit::ToggleButton toggleButton = Toolkit::ToggleButton::DownCast(Dali::BaseHandle(object));
121 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "ToggleButton::SetProperty index[%d]\n", propertyIndex);
125 ToggleButton& toggleButtonImpl(GetImplementation(toggleButton));
127 switch(propertyIndex)
129 case Toolkit::ToggleButton::Property::STATE_VISUALS:
131 Property::Array stateArray;
132 if(value.Get(stateArray))
134 toggleButtonImpl.SetToggleStates(stateArray);
139 case Toolkit::ToggleButton::Property::TOOLTIPS:
141 const Property::Array* tipArray = value.GetArray();
144 std::vector<std::string> tips;
145 size_t tipsCount = tipArray->Count();
146 tips.resize(tipsCount);
149 for(size_t i = 0; i != tipsCount; ++i)
151 if(DALI_UNLIKELY(!tipArray->GetElementAt(i).Get(tips[i])))
153 // Given array is invalid. Fast out.
159 if(DALI_LIKELY(valid))
161 toggleButtonImpl.SetToggleTooltips(tips);
174 Property::Value ToggleButton::GetProperty(BaseObject* object, Property::Index propertyIndex)
176 Property::Value value;
178 Toolkit::ToggleButton toggleButton = Toolkit::ToggleButton::DownCast(Dali::BaseHandle(object));
180 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "ToggleButton::GetProperty index[%d]\n", propertyIndex);
184 ToggleButton& toggleButtonImpl(GetImplementation(toggleButton));
186 switch(propertyIndex)
188 case Toolkit::ToggleButton::Property::STATE_VISUALS:
190 value = toggleButtonImpl.GetToggleStates();
193 case Toolkit::ToggleButton::Property::TOOLTIPS:
195 Property::Value value1(Property::ARRAY);
196 Property::Array* tipArray = value1.GetArray();
200 std::vector<std::string> tips = toggleButtonImpl.GetToggleTooltips();
201 size_t tipsCount(tips.size());
202 for(size_t i(0); i != tipsCount; ++i)
204 tipArray->PushBack(tips[i]);
210 case Toolkit::ToggleButton::Property::CURRENT_STATE_INDEX:
212 value = static_cast<int>(toggleButtonImpl.mCurrentToggleIndex);
221 void ToggleButton::CreateVisualsForAllStates(const Property::Array& states, std::vector<Toolkit::Visual::Base>& visuals)
223 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "ToggleButton::CreateVisualsForAllStates\n");
227 for(unsigned int i = 0; i < states.Count(); i++)
229 Property::Value value(states[i]);
231 Toolkit::VisualFactory visualFactory = Toolkit::VisualFactory::Get();
232 Toolkit::Visual::Base stateVisual;
234 if(value.GetType() == Property::MAP)
236 Property::Map* map = value.GetMap();
237 if(map && !map->Empty()) // Empty map results in current visual removal.
239 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "ToggleButton::CreateVisuals Using Map\n");
240 stateVisual = visualFactory.CreateVisual(*map);
243 else if(value.GetType() == Property::STRING)
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())
249 stateVisual = visualFactory.CreateVisual(imageUrl, ImageDimensions());
255 stateVisual.SetDepthIndex(DepthIndex::CONTENT);
256 visuals.push_back(stateVisual);
259 DALI_LOG_INFO(gLogButtonFilter, Debug::Verbose, "ToggleButton::CreateVisuals mToggleVisuals:%d\n", mToggleVisuals.size());
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");
267 mToggleStates.Clear();
268 mToggleStates = states;
269 /* New toggle button index from 0. */
270 mCurrentToggleIndex = 0;
272 // Create all visuals, save to mToggleVisuals.
273 CreateVisualsForAllStates(states, mToggleVisuals);
274 CreateVisualsForAllStates(states, mToggleSelectedVisuals);
275 CreateVisualsForAllStates(states, mToggleDisabledVisuals);
276 CreateVisualsForAllStates(states, mToggleDisabledSelectedVisuals);
278 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "ToggleButton::Began to register visual.\n");
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]);
289 Property::Array ToggleButton::GetToggleStates() const
291 return mToggleStates;
294 void ToggleButton::SetToggleTooltips(std::vector<std::string>& tips)
296 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "ToggleButton::SetToggleTooltips\n");
299 mToggleTooltips.clear();
300 mToggleTooltips.swap(tips);
303 if(!mToggleTooltips.empty() && (mCurrentToggleIndex < mToggleTooltips.size()))
305 Self().SetProperty(Toolkit::DevelControl::Property::TOOLTIP, mToggleTooltips[mCurrentToggleIndex]);
311 const std::vector<std::string>& ToggleButton::GetToggleTooltips() const
313 return mToggleTooltips;
316 void ToggleButton::PrepareVisual(Property::Index index, Toolkit::Visual::Base& visual)
318 bool enabled = false; // Disabled by default
320 // Unregister the visual with the given index if registered previously
321 if(DevelControl::GetVisual(*this, index))
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);
328 DevelControl::RegisterVisual(*this, index, visual, enabled, DepthIndex::CONTENT);
331 void ToggleButton::RelayoutVisual(Property::Index index, const Vector2& size)
333 Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, index);
336 Size visualSize = Size::ZERO;
337 Vector2 visualPosition = Vector2::ZERO;
339 visual.GetNaturalSize(visualSize);
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);
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);
352 visual.SetTransformAndSize(visualTransform, size);
356 void ToggleButton::OnRelayout(const Vector2& size, RelayoutContainer& container)
358 DALI_LOG_INFO(gLogButtonFilter, Debug::General, "ToggleButton::OnRelayout targetSize(%f,%f) ptr(%p)\n", size.width, size.height, this);
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);
366 void ToggleButton::OnPressed()
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());
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]);
378 //Need to check mCurrentToggleIndex, it must less than the size of mToggleTooltips.
379 if(!mToggleTooltips.empty() && (mCurrentToggleIndex < mToggleTooltips.size()))
381 Self().SetProperty(Toolkit::DevelControl::Property::TOOLTIP, mToggleTooltips[mCurrentToggleIndex]);
387 Dali::Accessibility::States ToggleButton::ToggleButtonAccessible::CalculateStates()
389 auto states = Button::ButtonAccessible::CalculateStates();
390 auto button = Toolkit::ToggleButton::DownCast(Self());
391 if(button.GetProperty<int>(Toolkit::ToggleButton::Property::CURRENT_STATE_INDEX))
393 states[Dali::Accessibility::State::CHECKED] = true;
398 std::string ToggleButton::ToggleButtonAccessible::GetDescriptionRaw() const
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);
404 return tooltips[index].Get<std::string>();
407 Property::Index ToggleButton::ToggleButtonAccessible::GetDescriptionPropertyIndex()
409 return Toolkit::ToggleButton::Property::TOOLTIPS;
412 void ToggleButton::OnStateChange(State newState)
414 // TODO: replace it with OnPropertySet hook once Button::Property::SELECTED will be consistently used
415 if(newState == SELECTED_STATE || newState == UNSELECTED_STATE)
417 auto accessible = GetAccessibleObject();
418 if(DALI_LIKELY(accessible) && accessible->IsHighlighted())
420 accessible->EmitStateChanged(Dali::Accessibility::State::CHECKED, mCurrentToggleIndex ? 1 : 0, 0);
421 accessible->Emit(Dali::Accessibility::ObjectPropertyChangeEvent::DESCRIPTION);
426 } // namespace Internal
428 } // namespace Toolkit