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 "accessibility-manager-impl.h"
22 #include <cstring> // for strcmp
23 #include <dali/public-api/actors/layer.h>
24 #include <dali/devel-api/adaptor-framework/accessibility-adaptor.h>
25 #include <dali/devel-api/adaptor-framework/sound-player.h>
26 #include <dali/public-api/animation/constraints.h>
27 #include <dali/devel-api/events/hit-test-algorithm.h>
28 #include <dali/devel-api/events/pan-gesture-devel.h>
29 #include <dali/integration-api/debug.h>
32 #include <dali-toolkit/devel-api/asset-manager/asset-manager.h>
33 #include <dali-toolkit/public-api/controls/control.h>
34 #include <dali-toolkit/public-api/controls/control-impl.h>
35 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
46 namespace // unnamed namespace
51 const char* const SIGNAL_FOCUS_CHANGED = "focusChanged";
52 const char* const SIGNAL_FOCUS_OVERSHOT = "focusOvershot";
53 const char* const SIGNAL_FOCUSED_ACTOR_ACTIVATED = "focusedActorActivated";
55 #if defined(DEBUG_ENABLED)
56 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_FOCUS_MANAGER");
59 const char* const ACTOR_FOCUSABLE("focusable");
60 const char* const IS_FOCUS_GROUP("isFocusGroup");
62 const char* FOCUS_BORDER_IMAGE_FILE_NAME = "B16-8_TTS_focus.9.png";
64 const char* FOCUS_SOUND_FILE_NAME = "Focus.ogg";
65 const char* FOCUS_CHAIN_END_SOUND_FILE_NAME = "End_of_List.ogg";
68 * The function to be used in the hit-test algorithm to check whether the actor is hittable.
70 bool IsActorFocusableFunction(Actor actor, Dali::HitTestAlgorithm::TraverseType type)
72 bool hittable = false;
76 case Dali::HitTestAlgorithm::CHECK_ACTOR:
78 // Check whether the actor is visible and not fully transparent.
79 if( actor.GetCurrentProperty< bool >( Actor::Property::VISIBLE )
80 && actor.GetCurrentProperty< Vector4 >( Actor::Property::WORLD_COLOR ).a > 0.01f) // not FULLY_TRANSPARENT
82 // Check whether the actor is focusable
83 Property::Index propertyActorFocusable = actor.GetPropertyIndex(ACTOR_FOCUSABLE);
84 if(propertyActorFocusable != Property::INVALID_INDEX)
86 hittable = actor.GetProperty<bool>(propertyActorFocusable);
91 case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
93 if( actor.GetCurrentProperty< bool >( Actor::Property::VISIBLE ) ) // Actor is visible, if not visible then none of its children are visible.
110 AccessibilityManager::AccessibilityManager()
111 : mCurrentFocusActor(FocusIDPair(0, 0)),
112 mCurrentGesturedActor(),
113 mFocusIndicatorActor(),
114 mPreviousPosition( 0.0f, 0.0f ),
115 mRecursiveFocusMoveCounter(0),
116 mFocusSoundFilePath(),
117 mFocusChainEndSoundFilePath(),
119 mIsFocusWithinGroup(false),
120 mIsEndcapFeedbackEnabled(false),
121 mIsEndcapFeedbackPlayed(false),
122 mIsAccessibilityTtsEnabled(false),
124 mIsFocusIndicatorEnabled(false),
125 mContinuousPlayMode(false),
126 mIsFocusSoundFilePathSet(false),
127 mIsFocusChainEndSoundFilePathSet(false)
131 AccessibilityManager::~AccessibilityManager()
135 void AccessibilityManager::Initialise()
137 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
138 adaptor.SetActionHandler(*this);
139 adaptor.SetGestureHandler(*this);
141 ChangeAccessibilityStatus();
144 AccessibilityManager::ActorAdditionalInfo AccessibilityManager::GetActorAdditionalInfo(const unsigned int actorID) const
146 ActorAdditionalInfo data;
147 IDAdditionalInfoConstIter iter = mIDAdditionalInfoContainer.find(actorID);
148 if(iter != mIDAdditionalInfoContainer.end())
150 data = (*iter).second;
156 void AccessibilityManager::SynchronizeActorAdditionalInfo(const unsigned int actorID, const unsigned int order)
158 ActorAdditionalInfo actorInfo = GetActorAdditionalInfo(actorID);
159 actorInfo.mFocusOrder = order;
160 mIDAdditionalInfoContainer.erase(actorID);
161 mIDAdditionalInfoContainer.insert(IDAdditionalInfoPair(actorID, actorInfo));
164 void AccessibilityManager::SetAccessibilityAttribute(Actor actor, Toolkit::AccessibilityManager::AccessibilityAttribute type, const std::string& text)
168 unsigned int actorID = actor.GetProperty< int >( Actor::Property::ID );
170 ActorAdditionalInfo info = GetActorAdditionalInfo(actorID);
171 info.mAccessibilityAttributes[type] = text;
173 mIDAdditionalInfoContainer.erase(actorID);
174 mIDAdditionalInfoContainer.insert(IDAdditionalInfoPair(actorID, info));
178 std::string AccessibilityManager::GetAccessibilityAttribute(Actor actor, Toolkit::AccessibilityManager::AccessibilityAttribute type) const
184 ActorAdditionalInfo data = GetActorAdditionalInfo(actor.GetProperty< int >( Actor::Property::ID ));
185 text = data.mAccessibilityAttributes[type];
191 void AccessibilityManager::SetFocusOrder(Actor actor, const unsigned int order)
193 // Do nothing if the focus order of the actor is not changed.
194 if(actor && GetFocusOrder(actor) != order)
196 // Firstly delete the actor from the focus chain if it's already there with a different focus order.
197 mFocusIDContainer.erase(GetFocusOrder(actor));
199 // Create/retrieve actor focusable property
200 Property::Index propertyActorFocusable = actor.RegisterProperty( ACTOR_FOCUSABLE, true, Property::READ_WRITE );
204 // The actor is not focusable without a defined focus order.
205 actor.SetProperty(propertyActorFocusable, false);
207 // If the actor is currently being focused, it should clear the focus
208 if(actor == GetCurrentFocusActor())
213 else // Insert the actor to the focus chain
215 // Check whether there is another actor in the focus chain with the same focus order already.
216 FocusIDIter focusIDIter = mFocusIDContainer.find(order);
217 if(focusIDIter != mFocusIDContainer.end())
219 // We need to increase the focus order of that actor and all the actors followed it
220 // in the focus chain.
221 FocusIDIter lastIter = mFocusIDContainer.end();
222 --lastIter;//We want forward iterator to the last element here
223 mFocusIDContainer.insert(FocusIDPair((*lastIter).first + 1, (*lastIter).second));
225 // Update the actor's focus order in its additional data
226 SynchronizeActorAdditionalInfo((*lastIter).second, (*lastIter).first + 1);
228 for(FocusIDIter iter = lastIter; iter != focusIDIter; iter--)
230 FocusIDIter previousIter = iter;
231 --previousIter;//We want forward iterator to the previous element here
232 unsigned int actorID = (*previousIter).second;
233 (*iter).second = actorID;
235 // Update the actor's focus order in its additional data
236 SynchronizeActorAdditionalInfo(actorID, (*iter).first);
239 mFocusIDContainer.erase(order);
242 // The actor is focusable
243 actor.SetProperty(propertyActorFocusable, true);
245 // Now we insert the actor into the focus chain with the specified focus order
246 mFocusIDContainer.insert(FocusIDPair(order, actor.GetProperty< int >( Actor::Property::ID )));
249 // Update the actor's focus order in its additional data
250 SynchronizeActorAdditionalInfo(actor.GetProperty< int >( Actor::Property::ID ), order);
254 unsigned int AccessibilityManager::GetFocusOrder(Actor actor) const
256 unsigned int focusOrder = 0;
260 ActorAdditionalInfo data = GetActorAdditionalInfo(actor.GetProperty< int >( Actor::Property::ID ));
261 focusOrder = data.mFocusOrder;
267 unsigned int AccessibilityManager::GenerateNewFocusOrder() const
269 unsigned int order = 1;
270 FocusIDContainer::const_reverse_iterator iter = mFocusIDContainer.rbegin();
272 if(iter != mFocusIDContainer.rend())
274 order = (*iter).first + 1;
280 Actor AccessibilityManager::GetActorByFocusOrder(const unsigned int order)
282 Actor actor = Actor();
284 FocusIDIter focusIDIter = mFocusIDContainer.find(order);
285 if(focusIDIter != mFocusIDContainer.end())
287 Actor rootActor = Stage::GetCurrent().GetRootLayer();
288 actor = rootActor.FindChildById(mFocusIDContainer[order]);
294 bool AccessibilityManager::SetCurrentFocusActor(Actor actor)
298 return DoSetCurrentFocusActor(actor.GetProperty< int >( Actor::Property::ID ));
304 bool AccessibilityManager::DoSetCurrentFocusActor(const unsigned int actorID)
306 Actor rootActor = Stage::GetCurrent().GetRootLayer();
308 // If the group mode is enabled, check which focus group the current focused actor belongs to
310 if(mIsFocusWithinGroup)
312 focusGroup = GetFocusGroup(GetCurrentFocusActor());
317 focusGroup = rootActor;
320 Actor actor = focusGroup.FindChildById(actorID);
322 // Check whether the actor is in the stage
325 // Check whether the actor is focusable
326 bool actorFocusable = false;
327 Property::Index propertyActorFocusable = actor.GetPropertyIndex(ACTOR_FOCUSABLE);
328 if(propertyActorFocusable != Property::INVALID_INDEX)
330 actorFocusable = actor.GetProperty<bool>(propertyActorFocusable);
333 // Go through the actor's hierarchy to check whether the actor is visible
334 bool actorVisible = actor.GetCurrentProperty< bool >( Actor::Property::VISIBLE );
335 Actor parent = actor.GetParent();
336 while (actorVisible && parent && parent != rootActor)
338 actorVisible = parent.GetCurrentProperty< bool >( Actor::Property::VISIBLE );
339 parent = parent.GetParent();
342 // Check whether the actor is fully transparent
343 bool actorOpaque = actor.GetCurrentProperty< Vector4 >( Actor::Property::WORLD_COLOR ).a > 0.01f;
345 // Set the focus only when the actor is focusable and visible and not fully transparent
346 if(actorVisible && actorFocusable && actorOpaque)
348 // Draw the focus indicator upon the focused actor
349 if( mIsFocusIndicatorEnabled )
351 actor.Add( GetFocusIndicatorActor() );
354 // Send notification for the change of focus actor
355 mFocusChangedSignal.Emit( GetCurrentFocusActor(), actor );
357 // Save the current focused actor
358 mCurrentFocusActor = FocusIDPair(GetFocusOrder(actor), actorID);
360 if(mIsAccessibilityTtsEnabled)
362 Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
365 if (!mIsFocusSoundFilePathSet)
367 const std::string soundDirPath = AssetManager::GetDaliSoundPath();
368 mFocusSoundFilePath = soundDirPath + FOCUS_SOUND_FILE_NAME;
369 mIsFocusSoundFilePathSet = true;
371 soundPlayer.PlaySound(mFocusSoundFilePath);
374 // Play the accessibility attributes with the TTS player.
375 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
377 // Combine attribute texts to one text
378 std::string informationText;
379 for(int i = 0; i < Toolkit::AccessibilityManager::ACCESSIBILITY_ATTRIBUTE_NUM; i++)
381 if(!GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i].empty())
385 informationText += ", "; // for space time between each information
387 informationText += GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i];
390 player.Play(informationText);
397 DALI_LOG_WARNING("[%s:%d] FAILED\n", __FUNCTION__, __LINE__);
401 Actor AccessibilityManager::GetCurrentFocusActor()
403 Actor rootActor = Stage::GetCurrent().GetRootLayer();
404 return rootActor.FindChildById(mCurrentFocusActor.second);
407 Actor AccessibilityManager::GetCurrentFocusGroup()
409 return GetFocusGroup(GetCurrentFocusActor());
412 unsigned int AccessibilityManager::GetCurrentFocusOrder()
414 return mCurrentFocusActor.first;
417 bool AccessibilityManager::MoveFocusForward()
420 mRecursiveFocusMoveCounter = 0;
422 FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first);
423 if(focusIDIter != mFocusIDContainer.end())
425 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
426 ret = DoMoveFocus(focusIDIter, true, mIsWrapped);
430 // TODO: if there is not focused actor, move first actor
431 if(!mFocusIDContainer.empty())
433 //if there is not focused actor, move 1st actor
434 focusIDIter = mFocusIDContainer.begin(); // TODO: I'm not sure it was sorted.
435 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
436 ret = DoSetCurrentFocusActor((*focusIDIter).second);
440 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!");
445 bool AccessibilityManager::MoveFocusBackward()
448 mRecursiveFocusMoveCounter = 0;
450 FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first);
451 if(focusIDIter != mFocusIDContainer.end())
453 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
454 ret = DoMoveFocus(focusIDIter, false, mIsWrapped);
458 // TODO: if there is not focused actor, move last actor
459 if(!mFocusIDContainer.empty())
461 //if there is not focused actor, move last actor
462 focusIDIter = mFocusIDContainer.end();
463 --focusIDIter;//We want forward iterator to the last element here
464 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
465 ret = DoSetCurrentFocusActor((*focusIDIter).second);
469 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!");
474 void AccessibilityManager::DoActivate(Actor actor)
478 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
481 // Notify the control that it is activated
482 GetImplementation( control ).AccessibilityActivate();
485 // Send notification for the activation of focused actor
486 mFocusedActorActivatedSignal.Emit(actor);
490 void AccessibilityManager::ClearFocus()
492 Actor actor = GetCurrentFocusActor();
493 if( actor && mFocusIndicatorActor )
495 actor.Remove( mFocusIndicatorActor );
498 mCurrentFocusActor = FocusIDPair(0, 0);
500 // Send notification for the change of focus actor
501 mFocusChangedSignal.Emit(actor, Actor());
503 if(mIsAccessibilityTtsEnabled)
505 // Stop the TTS playing if any
506 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
511 void AccessibilityManager::Reset()
514 mFocusIDContainer.clear();
515 mIDAdditionalInfoContainer.clear();
518 void AccessibilityManager::SetFocusGroup(Actor actor, bool isFocusGroup)
522 // Create/Set focus group property.
523 actor.RegisterProperty( IS_FOCUS_GROUP, isFocusGroup, Property::READ_WRITE );
527 bool AccessibilityManager::IsFocusGroup(Actor actor) const
529 // Check whether the actor is a focus group
530 bool isFocusGroup = false;
534 Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(IS_FOCUS_GROUP);
535 if(propertyIsFocusGroup != Property::INVALID_INDEX)
537 isFocusGroup = actor.GetProperty<bool>(propertyIsFocusGroup);
544 Actor AccessibilityManager::GetFocusGroup(Actor actor)
546 // Go through the actor's hierarchy to check which focus group the actor belongs to
547 while (actor && !IsFocusGroup(actor))
549 actor = actor.GetParent();
555 Vector2 AccessibilityManager::GetReadPosition() const
557 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
558 return adaptor.GetReadPosition();
561 void AccessibilityManager::EnableAccessibility(bool enabled)
563 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
564 adaptor.EnableAccessibility(enabled);
567 bool AccessibilityManager::IsEnabled() const
569 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
570 return adaptor.IsEnabled();
573 void AccessibilityManager::SetGroupMode(bool enabled)
575 mIsFocusWithinGroup = enabled;
578 bool AccessibilityManager::GetGroupMode() const
580 return mIsFocusWithinGroup;
583 void AccessibilityManager::SetWrapMode(bool wrapped)
585 mIsWrapped = wrapped;
588 bool AccessibilityManager::GetWrapMode() const
593 void AccessibilityManager::SetFocusIndicatorActor(Actor indicator)
595 if( mFocusIndicatorActor != indicator )
597 Actor currentFocusActor = GetCurrentFocusActor();
598 if( currentFocusActor )
600 // The new focus indicator should be added to the current focused actor immediately
601 if( mFocusIndicatorActor )
603 currentFocusActor.Remove( mFocusIndicatorActor );
608 currentFocusActor.Add( indicator );
612 mFocusIndicatorActor = indicator;
616 Actor AccessibilityManager::GetFocusIndicatorActor()
618 if( ! mFocusIndicatorActor )
620 // Create the default if it hasn't been set and one that's shared by all the keyboard focusable actors
621 const std::string imageDirPath = AssetManager::GetDaliImagePath();
622 const std::string focusBorderImagePath = imageDirPath + FOCUS_BORDER_IMAGE_FILE_NAME;
624 mFocusIndicatorActor = Toolkit::ImageView::New(focusBorderImagePath);
625 mFocusIndicatorActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
626 mFocusIndicatorActor.SetProperty( Actor::Property::POSITION_Z, 1.0f );
628 // Apply size constraint to the focus indicator
629 mFocusIndicatorActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
632 return mFocusIndicatorActor;
635 bool AccessibilityManager::DoMoveFocus(FocusIDIter focusIDIter, bool forward, bool wrapped)
637 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] %d focusable actors\n", __FUNCTION__, __LINE__, mFocusIDContainer.size());
638 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
640 if( (forward && ++focusIDIter == mFocusIDContainer.end())
641 || (!forward && focusIDIter-- == mFocusIDContainer.begin()) )
643 if(mIsEndcapFeedbackEnabled)
645 if(mIsEndcapFeedbackPlayed == false)
647 // play sound & skip moving once
648 Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
651 if (!mIsFocusChainEndSoundFilePathSet)
653 const std::string soundDirPath = AssetManager::GetDaliSoundPath();
654 mFocusChainEndSoundFilePath = soundDirPath + FOCUS_CHAIN_END_SOUND_FILE_NAME;
655 mIsFocusChainEndSoundFilePathSet = true;
657 soundPlayer.PlaySound(mFocusChainEndSoundFilePath);
660 mIsEndcapFeedbackPlayed = true;
663 mIsEndcapFeedbackPlayed = false;
670 focusIDIter = mFocusIDContainer.begin();
674 focusIDIter = mFocusIDContainer.end();
675 --focusIDIter;//We want forward iterator to the last element here
680 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] Overshot\n", __FUNCTION__, __LINE__);
681 // Send notification for handling overshooted situation
682 mFocusOvershotSignal.Emit(GetCurrentFocusActor(), forward ? Toolkit::AccessibilityManager::OVERSHOT_NEXT : Toolkit::AccessibilityManager::OVERSHOT_PREVIOUS);
684 return false; // Try to move the focus out of the scope
689 if( focusIDIter == mFocusIDContainer.end() )
694 // Note: This function performs the focus change.
695 if( !DoSetCurrentFocusActor( (*focusIDIter).second ) )
697 mRecursiveFocusMoveCounter++;
698 if(mRecursiveFocusMoveCounter > mFocusIDContainer.size())
700 // We've attempted to focus all the actors in the whole focus chain and no actor
701 // can be focused successfully.
702 DALI_LOG_WARNING("[%s] There is no more focusable actor in %d focus chains\n", __FUNCTION__, mRecursiveFocusMoveCounter);
708 return DoMoveFocus(focusIDIter, forward, wrapped);
715 void AccessibilityManager::SetFocusable(Actor actor, bool focusable)
719 // Create/Set actor focusable property.
720 actor.RegisterProperty( ACTOR_FOCUSABLE, focusable, Property::READ_WRITE );
724 bool AccessibilityManager::ChangeAccessibilityStatus()
726 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
727 mIsAccessibilityTtsEnabled = adaptor.IsEnabled();
728 Dali::Toolkit::AccessibilityManager handle( this );
730 if(mIsAccessibilityTtsEnabled)
732 // Show indicator when tts turned on if there is focused actor.
733 Actor actor = GetCurrentFocusActor();
736 actor.Add( GetFocusIndicatorActor() );
738 mIsFocusIndicatorEnabled = true;
740 // Connect a signal to the TTS player to implement continuous reading mode.
741 Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
742 player.StateChangedSignal().Connect( this, &AccessibilityManager::TtsStateChanged );
747 // Hide indicator when tts turned off
748 Actor actor = GetCurrentFocusActor();
749 if( actor && mFocusIndicatorActor )
751 actor.Remove( mFocusIndicatorActor );
753 mIsFocusIndicatorEnabled = false;
757 // Disconnect the TTS state change signal.
758 Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
759 player.StateChangedSignal().Disconnect( this, &AccessibilityManager::TtsStateChanged );
764 mStatusChangedSignal.Emit( handle );
769 bool AccessibilityManager::AccessibilityActionNext(bool allowEndFeedback)
771 Dali::Toolkit::AccessibilityManager handle( this );
772 if( !mActionNextSignal.Empty() )
774 mActionNextSignal.Emit( handle );
777 if(mIsAccessibilityTtsEnabled)
779 mIsEndcapFeedbackEnabled = allowEndFeedback;
780 return MoveFocusForward();
788 bool AccessibilityManager::AccessibilityActionPrevious(bool allowEndFeedback)
790 Dali::Toolkit::AccessibilityManager handle( this );
791 if( !mActionPreviousSignal.Empty() )
793 mActionPreviousSignal.Emit( handle );
796 if(mIsAccessibilityTtsEnabled)
798 mIsEndcapFeedbackEnabled = allowEndFeedback;
799 return MoveFocusBackward();
807 bool AccessibilityManager::AccessibilityActionActivate()
809 Dali::Toolkit::AccessibilityManager handle( this );
810 if( !mActionActivateSignal.Empty() )
812 mActionActivateSignal.Emit( handle );
817 Actor actor = GetCurrentFocusActor();
827 bool AccessibilityManager::AccessibilityActionRead(bool allowReadAgain)
829 Dali::Toolkit::AccessibilityManager handle( this );
833 if ( !mActionReadSignal.Empty() )
835 mActionReadSignal.Emit( handle );
840 if ( !mActionOverSignal.Empty() )
842 mActionOverSignal.Emit( handle );
848 if(mIsAccessibilityTtsEnabled)
850 // Find the focusable actor at the read position
851 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
852 Dali::HitTestAlgorithm::Results results;
853 Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), adaptor.GetReadPosition(), results, IsActorFocusableFunction );
855 FocusIDIter focusIDIter = mFocusIDContainer.find(GetFocusOrder(results.actor));
856 if(focusIDIter != mFocusIDContainer.end())
858 if( allowReadAgain || (results.actor != GetCurrentFocusActor()) )
860 // Move the focus to the actor
861 ret = SetCurrentFocusActor(results.actor);
862 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] SetCurrentFocusActor returns %s\n", __FUNCTION__, __LINE__, ret?"TRUE":"FALSE");
870 bool AccessibilityManager::AccessibilityActionReadNext(bool allowEndFeedback)
872 Dali::Toolkit::AccessibilityManager handle( this );
873 if( !mActionReadNextSignal.Empty() )
875 mActionReadNextSignal.Emit( handle );
878 if(mIsAccessibilityTtsEnabled)
880 return MoveFocusForward();
888 bool AccessibilityManager::AccessibilityActionReadPrevious(bool allowEndFeedback)
890 Dali::Toolkit::AccessibilityManager handle( this );
891 if( !mActionReadPreviousSignal.Empty() )
893 mActionReadPreviousSignal.Emit( handle );
896 if(mIsAccessibilityTtsEnabled)
898 return MoveFocusBackward();
906 bool AccessibilityManager::AccessibilityActionUp()
908 Dali::Toolkit::AccessibilityManager handle( this );
909 if( !mActionUpSignal.Empty() )
911 mActionUpSignal.Emit( handle );
916 if(mIsAccessibilityTtsEnabled)
918 Actor actor = GetCurrentFocusActor();
921 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
924 // Notify the control that it is activated
925 ret = GetImplementation( control ).OnAccessibilityValueChange(true);
933 bool AccessibilityManager::AccessibilityActionDown()
935 Dali::Toolkit::AccessibilityManager handle( this );
936 if( !mActionDownSignal.Empty() )
938 mActionDownSignal.Emit( handle );
943 if(mIsAccessibilityTtsEnabled)
945 Actor actor = GetCurrentFocusActor();
948 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
951 // Notify the control that it is activated
952 ret = GetImplementation( control ).OnAccessibilityValueChange(false);
960 bool AccessibilityManager::ClearAccessibilityFocus()
962 Dali::Toolkit::AccessibilityManager handle( this );
963 if( !mActionClearFocusSignal.Empty() )
965 mActionClearFocusSignal.Emit( handle );
968 if(mIsAccessibilityTtsEnabled)
979 bool AccessibilityManager::AccessibilityActionScroll( Dali::TouchEvent& touch )
981 Dali::Toolkit::AccessibilityManager handle( this );
982 if( !mActionScrollSignal.Empty() )
984 mActionScrollSignal.Emit( handle, touch );
990 bool AccessibilityManager::AccessibilityActionBack()
992 Dali::Toolkit::AccessibilityManager handle( this );
993 if( !mActionBackSignal.Empty() )
995 mActionBackSignal.Emit( handle );
998 // TODO: Back to previous view
1000 return mIsAccessibilityTtsEnabled;
1003 bool AccessibilityManager::AccessibilityActionScrollUp()
1005 Dali::Toolkit::AccessibilityManager handle( this );
1006 if( !mActionScrollUpSignal.Empty() )
1008 mActionScrollUpSignal.Emit( handle );
1013 if(mIsAccessibilityTtsEnabled)
1015 Actor actor = GetCurrentFocusActor();
1018 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1021 // TODO: Notify the control to scroll up. Should control handle this?
1022 // ret = GetImplementation( control ).OnAccessibilityScroll(Direction::UP);
1030 bool AccessibilityManager::AccessibilityActionScrollDown()
1032 Dali::Toolkit::AccessibilityManager handle( this );
1033 if( !mActionScrollDownSignal.Empty() )
1035 mActionScrollDownSignal.Emit( handle );
1040 if(mIsAccessibilityTtsEnabled)
1042 Actor actor = GetCurrentFocusActor();
1045 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1048 // TODO: Notify the control to scroll down. Should control handle this?
1049 // ret = GetImplementation( control ).OnAccessibilityScrollDown(Direction::DOWN);
1057 bool AccessibilityManager::AccessibilityActionPageLeft()
1059 Dali::Toolkit::AccessibilityManager handle( this );
1060 if( !mActionPageLeftSignal.Empty() )
1062 mActionPageLeftSignal.Emit( handle );
1067 if(mIsAccessibilityTtsEnabled)
1069 Actor actor = GetCurrentFocusActor();
1072 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1075 // TODO: Notify the control to scroll left to the previous page. Should control handle this?
1076 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::LEFT);
1084 bool AccessibilityManager::AccessibilityActionPageRight()
1086 Dali::Toolkit::AccessibilityManager handle( this );
1087 if( !mActionPageRightSignal.Empty() )
1089 mActionPageRightSignal.Emit( handle );
1094 if(mIsAccessibilityTtsEnabled)
1096 Actor actor = GetCurrentFocusActor();
1099 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1102 // TODO: Notify the control to scroll right to the next page. Should control handle this?
1103 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::RIGHT);
1111 bool AccessibilityManager::AccessibilityActionPageUp()
1113 Dali::Toolkit::AccessibilityManager handle( this );
1114 if( !mActionPageUpSignal.Empty() )
1116 mActionPageUpSignal.Emit( handle );
1121 if(mIsAccessibilityTtsEnabled)
1123 Actor actor = GetCurrentFocusActor();
1126 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1129 // TODO: Notify the control to scroll up to the previous page. Should control handle this?
1130 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::UP);
1138 bool AccessibilityManager::AccessibilityActionPageDown()
1140 Dali::Toolkit::AccessibilityManager handle( this );
1141 if( !mActionPageDownSignal.Empty() )
1143 mActionPageDownSignal.Emit( handle );
1148 if(mIsAccessibilityTtsEnabled)
1150 Actor actor = GetCurrentFocusActor();
1153 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1156 // TODO: Notify the control to scroll down to the next page. Should control handle this?
1157 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::DOWN);
1165 bool AccessibilityManager::AccessibilityActionMoveToFirst()
1167 Dali::Toolkit::AccessibilityManager handle( this );
1168 if( !mActionMoveToFirstSignal.Empty() )
1170 mActionMoveToFirstSignal.Emit( handle );
1173 // TODO: Move to the first item on screen
1175 return mIsAccessibilityTtsEnabled;
1178 bool AccessibilityManager::AccessibilityActionMoveToLast()
1180 Dali::Toolkit::AccessibilityManager handle( this );
1181 if( !mActionMoveToLastSignal.Empty() )
1183 mActionMoveToLastSignal.Emit( handle );
1186 // TODO: Move to the last item on screen
1188 return mIsAccessibilityTtsEnabled;
1191 bool AccessibilityManager::AccessibilityActionReadFromTop()
1193 Dali::Toolkit::AccessibilityManager handle( this );
1194 if( !mActionReadFromTopSignal.Empty() )
1196 mActionReadFromTopSignal.Emit( handle );
1199 // TODO: Move to the top item on screen and read from the item continuously
1201 return mIsAccessibilityTtsEnabled;
1204 bool AccessibilityManager::AccessibilityActionReadFromNext()
1206 Dali::Toolkit::AccessibilityManager handle( this );
1208 if( !mActionReadFromNextSignal.Empty() )
1210 mActionReadFromNextSignal.Emit( handle );
1213 if( mIsAccessibilityTtsEnabled )
1215 // Mark that we are in continuous play mode, so TTS signals can move focus.
1216 mContinuousPlayMode = true;
1218 // Attempt to move to the next item and read from the item continuously.
1222 return mIsAccessibilityTtsEnabled;
1225 void AccessibilityManager::TtsStateChanged( const Dali::TtsPlayer::State previousState, const Dali::TtsPlayer::State currentState )
1227 if( mContinuousPlayMode )
1229 // If we were playing and now we have stopped, attempt to play the next item.
1230 if( ( previousState == Dali::TtsPlayer::PLAYING ) && ( currentState == Dali::TtsPlayer::READY ) )
1232 // Attempt to move the focus forward and play.
1233 // If we can't cancel continuous play mode.
1234 if( !MoveFocusForward() )
1236 // We are done, exit continuous play mode.
1237 mContinuousPlayMode = false;
1242 // Unexpected play state change, exit continuous play mode.
1243 mContinuousPlayMode = false;
1248 bool AccessibilityManager::AccessibilityActionZoom()
1250 Dali::Toolkit::AccessibilityManager handle( this );
1251 if( !mActionZoomSignal.Empty() )
1253 mActionZoomSignal.Emit( handle );
1258 if(mIsAccessibilityTtsEnabled)
1260 Actor actor = GetCurrentFocusActor();
1263 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1266 // Notify the control to zoom
1267 ret = GetImplementation( control ).OnAccessibilityZoom();
1275 bool AccessibilityManager::AccessibilityActionReadPauseResume()
1277 Dali::Toolkit::AccessibilityManager handle( this );
1278 if( !mActionReadPauseResumeSignal.Empty() )
1280 mActionReadPauseResumeSignal.Emit( handle );
1285 if(mIsAccessibilityTtsEnabled)
1287 // Pause or resume the TTS player
1288 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
1289 Dali::TtsPlayer::State state = player.GetState();
1290 if(state == Dali::TtsPlayer::PLAYING)
1295 else if(state == Dali::TtsPlayer::PAUSED)
1305 bool AccessibilityManager::AccessibilityActionStartStop()
1307 Dali::Toolkit::AccessibilityManager handle( this );
1308 if( !mActionStartStopSignal.Empty() )
1310 mActionStartStopSignal.Emit( handle );
1313 // TODO: Start/stop the current action
1315 return mIsAccessibilityTtsEnabled;
1318 bool AccessibilityManager::HandlePanGesture(const AccessibilityGestureEvent& panEvent)
1320 bool handled = false;
1322 if( panEvent.state == AccessibilityGestureEvent::STARTED )
1324 // Find the focusable actor at the event position
1325 Dali::HitTestAlgorithm::Results results;
1326 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
1328 Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), panEvent.currentPosition, results, IsActorFocusableFunction );
1329 mCurrentGesturedActor = results.actor;
1331 if(!mCurrentGesturedActor)
1333 DALI_LOG_ERROR("Gesture detected, but no hit actor\n");
1337 // GestureState::FINISHED (Up) events are delivered with previous (Motion) event position
1338 // Use the real previous position; otherwise we may incorrectly get a ZERO velocity
1339 if ( AccessibilityGestureEvent::FINISHED != panEvent.state )
1341 // Store the previous position for next GestureState::FINISHED iteration.
1342 mPreviousPosition = panEvent.previousPosition;
1345 Actor rootActor = Stage::GetCurrent().GetRootLayer();
1347 Dali::PanGesture pan = DevelPanGesture::New( static_cast<Dali::GestureState>(panEvent.state) );
1348 DevelPanGesture::SetTime( pan, panEvent.time );
1349 DevelPanGesture::SetNumberOfTouches( pan, panEvent.numberOfTouches );
1350 DevelPanGesture::SetScreenPosition( pan, panEvent.currentPosition );
1351 DevelPanGesture::SetScreenDisplacement( pan, mPreviousPosition - panEvent.currentPosition );
1352 DevelPanGesture::SetScreenVelocity( pan, Vector2( pan.GetScreenDisplacement().x / panEvent.timeDelta, pan.GetScreenDisplacement().y / panEvent.timeDelta ) );
1354 // Only handle the pan gesture when the current focused actor is scrollable or within a scrollable actor
1355 while(mCurrentGesturedActor && mCurrentGesturedActor != rootActor && !handled)
1357 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(mCurrentGesturedActor);
1360 Vector2 localCurrent;
1361 control.ScreenToLocal( localCurrent.x, localCurrent.y, panEvent.currentPosition.x, panEvent.currentPosition.y );
1362 DevelPanGesture::SetPosition( pan, localCurrent );
1364 Vector2 localPrevious;
1365 control.ScreenToLocal( localPrevious.x, localPrevious.y, mPreviousPosition.x, mPreviousPosition.y );
1367 DevelPanGesture::SetDisplacement( pan, localCurrent - localPrevious );
1368 DevelPanGesture::SetVelocity( pan, Vector2( pan.GetDisplacement().x / panEvent.timeDelta, pan.GetDisplacement().y / panEvent.timeDelta ));
1370 handled = GetImplementation( control ).OnAccessibilityPan(pan);
1373 // If the gesture is not handled by the control, check its parent
1376 mCurrentGesturedActor = mCurrentGesturedActor.GetParent();
1378 if(!mCurrentGesturedActor)
1380 DALI_LOG_ERROR("no more gestured actor\n");
1385 // If handled, then update the pan gesture properties
1386 PanGestureDetector::SetPanGestureProperties( pan );
1393 Toolkit::AccessibilityManager::FocusChangedSignalType& AccessibilityManager::FocusChangedSignal()
1395 return mFocusChangedSignal;
1398 Toolkit::AccessibilityManager::FocusOvershotSignalType& AccessibilityManager::FocusOvershotSignal()
1400 return mFocusOvershotSignal;
1403 Toolkit::AccessibilityManager::FocusedActorActivatedSignalType& AccessibilityManager::FocusedActorActivatedSignal()
1405 return mFocusedActorActivatedSignal;
1408 } // namespace Internal
1410 } // namespace Toolkit