2 * Copyright (c) 2016 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/public-api/images/resource-image.h>
29 #include <dali/integration-api/debug.h>
32 #include <dali-toolkit/public-api/controls/control.h>
33 #include <dali-toolkit/public-api/controls/control-impl.h>
34 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
45 namespace // unnamed namespace
50 const char* const SIGNAL_FOCUS_CHANGED = "focusChanged";
51 const char* const SIGNAL_FOCUS_OVERSHOT = "focusOvershot";
52 const char* const SIGNAL_FOCUSED_ACTOR_ACTIVATED = "focusedActorActivated";
54 #if defined(DEBUG_ENABLED)
55 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_FOCUS_MANAGER");
58 const char* const ACTOR_FOCUSABLE("focusable");
59 const char* const IS_FOCUS_GROUP("isFocusGroup");
61 const char* FOCUS_BORDER_IMAGE_PATH = DALI_IMAGE_DIR "B16-8_TTS_focus.9.png";
63 const char* FOCUS_SOUND_FILE = DALI_SOUND_DIR "Focus.ogg";
64 const char* FOCUS_CHAIN_END_SOUND_FILE = DALI_SOUND_DIR "End_of_List.ogg";
67 * The function to be used in the hit-test algorithm to check whether the actor is hittable.
69 bool IsActorFocusableFunction(Actor actor, Dali::HitTestAlgorithm::TraverseType type)
71 bool hittable = false;
75 case Dali::HitTestAlgorithm::CHECK_ACTOR:
77 // Check whether the actor is visible and not fully transparent.
79 && actor.GetCurrentWorldColor().a > 0.01f) // not FULLY_TRANSPARENT
81 // Check whether the actor is focusable
82 Property::Index propertyActorFocusable = actor.GetPropertyIndex(ACTOR_FOCUSABLE);
83 if(propertyActorFocusable != Property::INVALID_INDEX)
85 hittable = actor.GetProperty<bool>(propertyActorFocusable);
90 case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
92 if( actor.IsVisible() ) // Actor is visible, if not visible then none of its children are visible.
109 AccessibilityManager::AccessibilityManager()
110 : mCurrentFocusActor(FocusIDPair(0, 0)),
111 mFocusIndicatorActor(),
112 mRecursiveFocusMoveCounter(0),
114 mIsFocusWithinGroup(false),
115 mIsEndcapFeedbackEnabled(false),
116 mIsEndcapFeedbackPlayed(false),
117 mIsAccessibilityTtsEnabled(false),
119 mIsFocusIndicatorEnabled(false),
120 mContinuousPlayMode(false)
124 AccessibilityManager::~AccessibilityManager()
128 void AccessibilityManager::Initialise()
130 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
131 adaptor.SetActionHandler(*this);
132 adaptor.SetGestureHandler(*this);
134 ChangeAccessibilityStatus();
137 AccessibilityManager::ActorAdditionalInfo AccessibilityManager::GetActorAdditionalInfo(const unsigned int actorID) const
139 ActorAdditionalInfo data;
140 IDAdditionalInfoConstIter iter = mIDAdditionalInfoContainer.find(actorID);
141 if(iter != mIDAdditionalInfoContainer.end())
143 data = (*iter).second;
149 void AccessibilityManager::SynchronizeActorAdditionalInfo(const unsigned int actorID, const unsigned int order)
151 ActorAdditionalInfo actorInfo = GetActorAdditionalInfo(actorID);
152 actorInfo.mFocusOrder = order;
153 mIDAdditionalInfoContainer.erase(actorID);
154 mIDAdditionalInfoContainer.insert(IDAdditionalInfoPair(actorID, actorInfo));
157 void AccessibilityManager::SetAccessibilityAttribute(Actor actor, Toolkit::AccessibilityManager::AccessibilityAttribute type, const std::string& text)
161 unsigned int actorID = actor.GetId();
163 ActorAdditionalInfo info = GetActorAdditionalInfo(actorID);
164 info.mAccessibilityAttributes[type] = text;
166 mIDAdditionalInfoContainer.erase(actorID);
167 mIDAdditionalInfoContainer.insert(IDAdditionalInfoPair(actorID, info));
171 std::string AccessibilityManager::GetAccessibilityAttribute(Actor actor, Toolkit::AccessibilityManager::AccessibilityAttribute type) const
177 ActorAdditionalInfo data = GetActorAdditionalInfo(actor.GetId());
178 text = data.mAccessibilityAttributes[type];
184 void AccessibilityManager::SetFocusOrder(Actor actor, const unsigned int order)
186 // Do nothing if the focus order of the actor is not changed.
187 if(actor && GetFocusOrder(actor) != order)
189 // Firstly delete the actor from the focus chain if it's already there with a different focus order.
190 mFocusIDContainer.erase(GetFocusOrder(actor));
192 // Create/retrieve actor focusable property
193 Property::Index propertyActorFocusable = actor.RegisterProperty( ACTOR_FOCUSABLE, true, Property::READ_WRITE );
197 // The actor is not focusable without a defined focus order.
198 actor.SetProperty(propertyActorFocusable, false);
200 // If the actor is currently being focused, it should clear the focus
201 if(actor == GetCurrentFocusActor())
206 else // Insert the actor to the focus chain
208 // Check whether there is another actor in the focus chain with the same focus order already.
209 FocusIDIter focusIDIter = mFocusIDContainer.find(order);
210 if(focusIDIter != mFocusIDContainer.end())
212 // We need to increase the focus order of that actor and all the actors followed it
213 // in the focus chain.
214 FocusIDIter lastIter = mFocusIDContainer.end();
215 --lastIter;//We want forward iterator to the last element here
216 mFocusIDContainer.insert(FocusIDPair((*lastIter).first + 1, (*lastIter).second));
218 // Update the actor's focus order in its additional data
219 SynchronizeActorAdditionalInfo((*lastIter).second, (*lastIter).first + 1);
221 for(FocusIDIter iter = lastIter; iter != focusIDIter; iter--)
223 FocusIDIter previousIter = iter;
224 --previousIter;//We want forward iterator to the previous element here
225 unsigned int actorID = (*previousIter).second;
226 (*iter).second = actorID;
228 // Update the actor's focus order in its additional data
229 SynchronizeActorAdditionalInfo(actorID, (*iter).first);
232 mFocusIDContainer.erase(order);
235 // The actor is focusable
236 actor.SetProperty(propertyActorFocusable, true);
238 // Now we insert the actor into the focus chain with the specified focus order
239 mFocusIDContainer.insert(FocusIDPair(order, actor.GetId()));
242 // Update the actor's focus order in its additional data
243 SynchronizeActorAdditionalInfo(actor.GetId(), order);
247 unsigned int AccessibilityManager::GetFocusOrder(Actor actor) const
249 unsigned int focusOrder = 0;
253 ActorAdditionalInfo data = GetActorAdditionalInfo(actor.GetId());
254 focusOrder = data.mFocusOrder;
260 unsigned int AccessibilityManager::GenerateNewFocusOrder() const
262 unsigned int order = 1;
263 FocusIDContainer::const_reverse_iterator iter = mFocusIDContainer.rbegin();
265 if(iter != mFocusIDContainer.rend())
267 order = (*iter).first + 1;
273 Actor AccessibilityManager::GetActorByFocusOrder(const unsigned int order)
275 Actor actor = Actor();
277 FocusIDIter focusIDIter = mFocusIDContainer.find(order);
278 if(focusIDIter != mFocusIDContainer.end())
280 Actor rootActor = Stage::GetCurrent().GetRootLayer();
281 actor = rootActor.FindChildById(mFocusIDContainer[order]);
287 bool AccessibilityManager::SetCurrentFocusActor(Actor actor)
291 return DoSetCurrentFocusActor(actor.GetId());
297 bool AccessibilityManager::DoSetCurrentFocusActor(const unsigned int actorID)
299 Actor rootActor = Stage::GetCurrent().GetRootLayer();
301 // If the group mode is enabled, check which focus group the current focused actor belongs to
303 if(mIsFocusWithinGroup)
305 focusGroup = GetFocusGroup(GetCurrentFocusActor());
310 focusGroup = rootActor;
313 Actor actor = focusGroup.FindChildById(actorID);
315 // Check whether the actor is in the stage
318 // Check whether the actor is focusable
319 bool actorFocusable = false;
320 Property::Index propertyActorFocusable = actor.GetPropertyIndex(ACTOR_FOCUSABLE);
321 if(propertyActorFocusable != Property::INVALID_INDEX)
323 actorFocusable = actor.GetProperty<bool>(propertyActorFocusable);
326 // Go through the actor's hierarchy to check whether the actor is visible
327 bool actorVisible = actor.IsVisible();
328 Actor parent = actor.GetParent();
329 while (actorVisible && parent && parent != rootActor)
331 actorVisible = parent.IsVisible();
332 parent = parent.GetParent();
335 // Check whether the actor is fully transparent
336 bool actorOpaque = actor.GetCurrentWorldColor().a > 0.01f;
338 // Set the focus only when the actor is focusable and visible and not fully transparent
339 if(actorVisible && actorFocusable && actorOpaque)
341 // Draw the focus indicator upon the focused actor
342 if( mIsFocusIndicatorEnabled )
344 actor.Add( GetFocusIndicatorActor() );
347 // Send notification for the change of focus actor
348 mFocusChangedSignal.Emit( GetCurrentFocusActor(), actor );
350 // Save the current focused actor
351 mCurrentFocusActor = FocusIDPair(GetFocusOrder(actor), actorID);
353 if(mIsAccessibilityTtsEnabled)
355 Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
358 soundPlayer.PlaySound(FOCUS_SOUND_FILE);
361 // Play the accessibility attributes with the TTS player.
362 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
364 // Combine attribute texts to one text
365 std::string informationText;
366 for(int i = 0; i < Toolkit::AccessibilityManager::ACCESSIBILITY_ATTRIBUTE_NUM; i++)
368 if(!GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i].empty())
372 informationText += ", "; // for space time between each information
374 informationText += GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i];
377 player.Play(informationText);
384 DALI_LOG_WARNING("[%s:%d] FAILED\n", __FUNCTION__, __LINE__);
388 Actor AccessibilityManager::GetCurrentFocusActor()
390 Actor rootActor = Stage::GetCurrent().GetRootLayer();
391 return rootActor.FindChildById(mCurrentFocusActor.second);
394 Actor AccessibilityManager::GetCurrentFocusGroup()
396 return GetFocusGroup(GetCurrentFocusActor());
399 unsigned int AccessibilityManager::GetCurrentFocusOrder()
401 return mCurrentFocusActor.first;
404 bool AccessibilityManager::MoveFocusForward()
407 mRecursiveFocusMoveCounter = 0;
409 FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first);
410 if(focusIDIter != mFocusIDContainer.end())
412 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
413 ret = DoMoveFocus(focusIDIter, true, mIsWrapped);
417 // TODO: if there is not focused actor, move first actor
418 if(!mFocusIDContainer.empty())
420 //if there is not focused actor, move 1st actor
421 focusIDIter = mFocusIDContainer.begin(); // TODO: I'm not sure it was sorted.
422 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
423 ret = DoSetCurrentFocusActor((*focusIDIter).second);
427 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!");
432 bool AccessibilityManager::MoveFocusBackward()
435 mRecursiveFocusMoveCounter = 0;
437 FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first);
438 if(focusIDIter != mFocusIDContainer.end())
440 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
441 ret = DoMoveFocus(focusIDIter, false, mIsWrapped);
445 // TODO: if there is not focused actor, move last actor
446 if(!mFocusIDContainer.empty())
448 //if there is not focused actor, move last actor
449 focusIDIter = mFocusIDContainer.end();
450 --focusIDIter;//We want forward iterator to the last element here
451 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
452 ret = DoSetCurrentFocusActor((*focusIDIter).second);
456 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!");
461 void AccessibilityManager::DoActivate(Actor actor)
465 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
468 // Notify the control that it is activated
469 GetImplementation( control ).AccessibilityActivate();
472 // Send notification for the activation of focused actor
473 mFocusedActorActivatedSignal.Emit(actor);
477 void AccessibilityManager::ClearFocus()
479 Actor actor = GetCurrentFocusActor();
480 if( actor && mFocusIndicatorActor )
482 actor.Remove( mFocusIndicatorActor );
485 mCurrentFocusActor = FocusIDPair(0, 0);
487 // Send notification for the change of focus actor
488 mFocusChangedSignal.Emit(actor, Actor());
490 if(mIsAccessibilityTtsEnabled)
492 // Stop the TTS playing if any
493 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
498 void AccessibilityManager::Reset()
501 mFocusIDContainer.clear();
502 mIDAdditionalInfoContainer.clear();
505 void AccessibilityManager::SetFocusGroup(Actor actor, bool isFocusGroup)
509 // Create/Set focus group property.
510 actor.RegisterProperty( IS_FOCUS_GROUP, isFocusGroup, Property::READ_WRITE );
514 bool AccessibilityManager::IsFocusGroup(Actor actor) const
516 // Check whether the actor is a focus group
517 bool isFocusGroup = false;
521 Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(IS_FOCUS_GROUP);
522 if(propertyIsFocusGroup != Property::INVALID_INDEX)
524 isFocusGroup = actor.GetProperty<bool>(propertyIsFocusGroup);
531 Actor AccessibilityManager::GetFocusGroup(Actor actor)
533 // Go through the actor's hierarchy to check which focus group the actor belongs to
534 while (actor && !IsFocusGroup(actor))
536 actor = actor.GetParent();
542 Vector2 AccessibilityManager::GetReadPosition() const
544 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
545 return adaptor.GetReadPosition();
548 void AccessibilityManager::SetGroupMode(bool enabled)
550 mIsFocusWithinGroup = enabled;
553 bool AccessibilityManager::GetGroupMode() const
555 return mIsFocusWithinGroup;
558 void AccessibilityManager::SetWrapMode(bool wrapped)
560 mIsWrapped = wrapped;
563 bool AccessibilityManager::GetWrapMode() const
568 void AccessibilityManager::SetFocusIndicatorActor(Actor indicator)
570 if( mFocusIndicatorActor != indicator )
572 Actor currentFocusActor = GetCurrentFocusActor();
573 if( currentFocusActor )
575 // The new focus indicator should be added to the current focused actor immediately
576 if( mFocusIndicatorActor )
578 currentFocusActor.Remove( mFocusIndicatorActor );
583 currentFocusActor.Add( indicator );
587 mFocusIndicatorActor = indicator;
591 Actor AccessibilityManager::GetFocusIndicatorActor()
593 if( ! mFocusIndicatorActor )
595 // Create the default if it hasn't been set and one that's shared by all the keyboard focusable actors
596 mFocusIndicatorActor = Toolkit::ImageView::New( FOCUS_BORDER_IMAGE_PATH );
597 mFocusIndicatorActor.SetParentOrigin( ParentOrigin::CENTER );
598 mFocusIndicatorActor.SetZ( 1.0f );
600 // Apply size constraint to the focus indicator
601 mFocusIndicatorActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
604 return mFocusIndicatorActor;
607 bool AccessibilityManager::DoMoveFocus(FocusIDIter focusIDIter, bool forward, bool wrapped)
609 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] %d focusable actors\n", __FUNCTION__, __LINE__, mFocusIDContainer.size());
610 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
612 if( (forward && ++focusIDIter == mFocusIDContainer.end())
613 || (!forward && focusIDIter-- == mFocusIDContainer.begin()) )
615 if(mIsEndcapFeedbackEnabled)
617 if(mIsEndcapFeedbackPlayed == false)
619 // play sound & skip moving once
620 Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
623 soundPlayer.PlaySound(FOCUS_CHAIN_END_SOUND_FILE);
626 mIsEndcapFeedbackPlayed = true;
629 mIsEndcapFeedbackPlayed = false;
636 focusIDIter = mFocusIDContainer.begin();
640 focusIDIter = mFocusIDContainer.end();
641 --focusIDIter;//We want forward iterator to the last element here
646 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] Overshot\n", __FUNCTION__, __LINE__);
647 // Send notification for handling overshooted situation
648 mFocusOvershotSignal.Emit(GetCurrentFocusActor(), forward ? Toolkit::AccessibilityManager::OVERSHOT_NEXT : Toolkit::AccessibilityManager::OVERSHOT_PREVIOUS);
650 return false; // Try to move the focus out of the scope
655 if( focusIDIter == mFocusIDContainer.end() )
660 // Note: This function performs the focus change.
661 if( !DoSetCurrentFocusActor( (*focusIDIter).second ) )
663 mRecursiveFocusMoveCounter++;
664 if(mRecursiveFocusMoveCounter > mFocusIDContainer.size())
666 // We've attempted to focus all the actors in the whole focus chain and no actor
667 // can be focused successfully.
668 DALI_LOG_WARNING("[%s] There is no more focusable actor in %d focus chains\n", __FUNCTION__, mRecursiveFocusMoveCounter);
674 return DoMoveFocus(focusIDIter, forward, wrapped);
681 void AccessibilityManager::SetFocusable(Actor actor, bool focusable)
685 // Create/Set actor focusable property.
686 actor.RegisterProperty( ACTOR_FOCUSABLE, focusable, Property::READ_WRITE );
690 bool AccessibilityManager::ChangeAccessibilityStatus()
692 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
693 mIsAccessibilityTtsEnabled = adaptor.IsEnabled();
694 Dali::Toolkit::AccessibilityManager handle( this );
696 if(mIsAccessibilityTtsEnabled)
698 // Show indicator when tts turned on if there is focused actor.
699 Actor actor = GetCurrentFocusActor();
702 actor.Add( GetFocusIndicatorActor() );
704 mIsFocusIndicatorEnabled = true;
706 // Connect a signal to the TTS player to implement continuous reading mode.
707 Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
708 player.StateChangedSignal().Connect( this, &AccessibilityManager::TtsStateChanged );
713 // Hide indicator when tts turned off
714 Actor actor = GetCurrentFocusActor();
715 if( actor && mFocusIndicatorActor )
717 actor.Remove( mFocusIndicatorActor );
719 mIsFocusIndicatorEnabled = false;
723 // Disconnect the TTS state change signal.
724 Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
725 player.StateChangedSignal().Disconnect( this, &AccessibilityManager::TtsStateChanged );
730 mStatusChangedSignal.Emit( handle );
735 bool AccessibilityManager::AccessibilityActionNext(bool allowEndFeedback)
737 Dali::Toolkit::AccessibilityManager handle( this );
738 if( !mActionNextSignal.Empty() )
740 mActionNextSignal.Emit( handle );
743 if(mIsAccessibilityTtsEnabled)
745 mIsEndcapFeedbackEnabled = allowEndFeedback;
746 return MoveFocusForward();
754 bool AccessibilityManager::AccessibilityActionPrevious(bool allowEndFeedback)
756 Dali::Toolkit::AccessibilityManager handle( this );
757 if( !mActionPreviousSignal.Empty() )
759 mActionPreviousSignal.Emit( handle );
762 if(mIsAccessibilityTtsEnabled)
764 mIsEndcapFeedbackEnabled = allowEndFeedback;
765 return MoveFocusBackward();
773 bool AccessibilityManager::AccessibilityActionActivate()
775 Dali::Toolkit::AccessibilityManager handle( this );
776 if( !mActionActivateSignal.Empty() )
778 mActionActivateSignal.Emit( handle );
783 Actor actor = GetCurrentFocusActor();
793 bool AccessibilityManager::AccessibilityActionRead(bool allowReadAgain)
795 Dali::Toolkit::AccessibilityManager handle( this );
799 if ( !mActionReadSignal.Empty() )
801 mActionReadSignal.Emit( handle );
806 if ( !mActionOverSignal.Empty() )
808 mActionOverSignal.Emit( handle );
814 if(mIsAccessibilityTtsEnabled)
816 // Find the focusable actor at the read position
817 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
818 Dali::HitTestAlgorithm::Results results;
819 Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), adaptor.GetReadPosition(), results, IsActorFocusableFunction );
821 FocusIDIter focusIDIter = mFocusIDContainer.find(GetFocusOrder(results.actor));
822 if(focusIDIter != mFocusIDContainer.end())
824 if( allowReadAgain || (results.actor != GetCurrentFocusActor()) )
826 // Move the focus to the actor
827 ret = SetCurrentFocusActor(results.actor);
828 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] SetCurrentFocusActor returns %s\n", __FUNCTION__, __LINE__, ret?"TRUE":"FALSE");
836 bool AccessibilityManager::AccessibilityActionReadNext(bool allowEndFeedback)
838 Dali::Toolkit::AccessibilityManager handle( this );
839 if( !mActionReadNextSignal.Empty() )
841 mActionReadNextSignal.Emit( handle );
844 if(mIsAccessibilityTtsEnabled)
846 return MoveFocusForward();
854 bool AccessibilityManager::AccessibilityActionReadPrevious(bool allowEndFeedback)
856 Dali::Toolkit::AccessibilityManager handle( this );
857 if( !mActionReadPreviousSignal.Empty() )
859 mActionReadPreviousSignal.Emit( handle );
862 if(mIsAccessibilityTtsEnabled)
864 return MoveFocusBackward();
872 bool AccessibilityManager::AccessibilityActionUp()
874 Dali::Toolkit::AccessibilityManager handle( this );
875 if( !mActionUpSignal.Empty() )
877 mActionUpSignal.Emit( handle );
882 if(mIsAccessibilityTtsEnabled)
884 Actor actor = GetCurrentFocusActor();
887 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
890 // Notify the control that it is activated
891 ret = GetImplementation( control ).OnAccessibilityValueChange(true);
899 bool AccessibilityManager::AccessibilityActionDown()
901 Dali::Toolkit::AccessibilityManager handle( this );
902 if( !mActionDownSignal.Empty() )
904 mActionDownSignal.Emit( handle );
909 if(mIsAccessibilityTtsEnabled)
911 Actor actor = GetCurrentFocusActor();
914 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
917 // Notify the control that it is activated
918 ret = GetImplementation( control ).OnAccessibilityValueChange(false);
926 bool AccessibilityManager::ClearAccessibilityFocus()
928 Dali::Toolkit::AccessibilityManager handle( this );
929 if( !mActionClearFocusSignal.Empty() )
931 mActionClearFocusSignal.Emit( handle );
934 if(mIsAccessibilityTtsEnabled)
945 bool AccessibilityManager::AccessibilityActionScroll( Dali::TouchEvent& touchEvent )
947 Dali::Toolkit::AccessibilityManager handle( this );
948 if( !mActionScrollSignal.Empty() )
950 mActionScrollSignal.Emit( handle, touchEvent );
956 bool AccessibilityManager::AccessibilityActionBack()
958 Dali::Toolkit::AccessibilityManager handle( this );
959 if( !mActionBackSignal.Empty() )
961 mActionBackSignal.Emit( handle );
964 // TODO: Back to previous view
966 return mIsAccessibilityTtsEnabled;
969 bool AccessibilityManager::AccessibilityActionScrollUp()
971 Dali::Toolkit::AccessibilityManager handle( this );
972 if( !mActionScrollUpSignal.Empty() )
974 mActionScrollUpSignal.Emit( handle );
979 if(mIsAccessibilityTtsEnabled)
981 Actor actor = GetCurrentFocusActor();
984 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
987 // TODO: Notify the control to scroll up. Should control handle this?
988 // ret = GetImplementation( control ).OnAccessibilityScroll(Direction::UP);
996 bool AccessibilityManager::AccessibilityActionScrollDown()
998 Dali::Toolkit::AccessibilityManager handle( this );
999 if( !mActionScrollDownSignal.Empty() )
1001 mActionScrollDownSignal.Emit( handle );
1006 if(mIsAccessibilityTtsEnabled)
1008 Actor actor = GetCurrentFocusActor();
1011 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1014 // TODO: Notify the control to scroll down. Should control handle this?
1015 // ret = GetImplementation( control ).OnAccessibilityScrollDown(Direction::DOWN);
1023 bool AccessibilityManager::AccessibilityActionPageLeft()
1025 Dali::Toolkit::AccessibilityManager handle( this );
1026 if( !mActionPageLeftSignal.Empty() )
1028 mActionPageLeftSignal.Emit( handle );
1033 if(mIsAccessibilityTtsEnabled)
1035 Actor actor = GetCurrentFocusActor();
1038 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1041 // TODO: Notify the control to scroll left to the previous page. Should control handle this?
1042 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::LEFT);
1050 bool AccessibilityManager::AccessibilityActionPageRight()
1052 Dali::Toolkit::AccessibilityManager handle( this );
1053 if( !mActionPageRightSignal.Empty() )
1055 mActionPageRightSignal.Emit( handle );
1060 if(mIsAccessibilityTtsEnabled)
1062 Actor actor = GetCurrentFocusActor();
1065 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1068 // TODO: Notify the control to scroll right to the next page. Should control handle this?
1069 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::RIGHT);
1077 bool AccessibilityManager::AccessibilityActionPageUp()
1079 Dali::Toolkit::AccessibilityManager handle( this );
1080 if( !mActionPageUpSignal.Empty() )
1082 mActionPageUpSignal.Emit( handle );
1087 if(mIsAccessibilityTtsEnabled)
1089 Actor actor = GetCurrentFocusActor();
1092 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1095 // TODO: Notify the control to scroll up to the previous page. Should control handle this?
1096 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::UP);
1104 bool AccessibilityManager::AccessibilityActionPageDown()
1106 Dali::Toolkit::AccessibilityManager handle( this );
1107 if( !mActionPageDownSignal.Empty() )
1109 mActionPageDownSignal.Emit( handle );
1114 if(mIsAccessibilityTtsEnabled)
1116 Actor actor = GetCurrentFocusActor();
1119 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1122 // TODO: Notify the control to scroll down to the next page. Should control handle this?
1123 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::DOWN);
1131 bool AccessibilityManager::AccessibilityActionMoveToFirst()
1133 Dali::Toolkit::AccessibilityManager handle( this );
1134 if( !mActionMoveToFirstSignal.Empty() )
1136 mActionMoveToFirstSignal.Emit( handle );
1139 // TODO: Move to the first item on screen
1141 return mIsAccessibilityTtsEnabled;
1144 bool AccessibilityManager::AccessibilityActionMoveToLast()
1146 Dali::Toolkit::AccessibilityManager handle( this );
1147 if( !mActionMoveToLastSignal.Empty() )
1149 mActionMoveToLastSignal.Emit( handle );
1152 // TODO: Move to the last item on screen
1154 return mIsAccessibilityTtsEnabled;
1157 bool AccessibilityManager::AccessibilityActionReadFromTop()
1159 Dali::Toolkit::AccessibilityManager handle( this );
1160 if( !mActionReadFromTopSignal.Empty() )
1162 mActionReadFromTopSignal.Emit( handle );
1165 // TODO: Move to the top item on screen and read from the item continuously
1167 return mIsAccessibilityTtsEnabled;
1170 bool AccessibilityManager::AccessibilityActionReadFromNext()
1172 Dali::Toolkit::AccessibilityManager handle( this );
1174 if( !mActionReadFromNextSignal.Empty() )
1176 mActionReadFromNextSignal.Emit( handle );
1179 if( mIsAccessibilityTtsEnabled )
1181 // Mark that we are in continuous play mode, so TTS signals can move focus.
1182 mContinuousPlayMode = true;
1184 // Attempt to move to the next item and read from the item continuously.
1188 return mIsAccessibilityTtsEnabled;
1191 void AccessibilityManager::TtsStateChanged( const Dali::TtsPlayer::State previousState, const Dali::TtsPlayer::State currentState )
1193 if( mContinuousPlayMode )
1195 // If we were playing and now we have stopped, attempt to play the next item.
1196 if( ( previousState == Dali::TtsPlayer::PLAYING ) && ( currentState == Dali::TtsPlayer::READY ) )
1198 // Attempt to move the focus forward and play.
1199 // If we can't cancel continuous play mode.
1200 if( !MoveFocusForward() )
1202 // We are done, exit continuous play mode.
1203 mContinuousPlayMode = false;
1208 // Unexpected play state change, exit continuous play mode.
1209 mContinuousPlayMode = false;
1214 bool AccessibilityManager::AccessibilityActionZoom()
1216 Dali::Toolkit::AccessibilityManager handle( this );
1217 if( !mActionZoomSignal.Empty() )
1219 mActionZoomSignal.Emit( handle );
1224 if(mIsAccessibilityTtsEnabled)
1226 Actor actor = GetCurrentFocusActor();
1229 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1232 // Notify the control to zoom
1233 ret = GetImplementation( control ).OnAccessibilityZoom();
1241 bool AccessibilityManager::AccessibilityActionReadIndicatorInformation()
1243 Dali::Toolkit::AccessibilityManager handle( this );
1244 if( !mActionReadIndicatorInformationSignal.Empty() )
1246 mActionReadIndicatorInformationSignal.Emit( handle );
1249 // TODO: Read the information in the indicator
1251 return mIsAccessibilityTtsEnabled;
1254 bool AccessibilityManager::AccessibilityActionReadPauseResume()
1256 Dali::Toolkit::AccessibilityManager handle( this );
1257 if( !mActionReadPauseResumeSignal.Empty() )
1259 mActionReadPauseResumeSignal.Emit( handle );
1264 if(mIsAccessibilityTtsEnabled)
1266 // Pause or resume the TTS player
1267 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
1268 Dali::TtsPlayer::State state = player.GetState();
1269 if(state == Dali::TtsPlayer::PLAYING)
1274 else if(state == Dali::TtsPlayer::PAUSED)
1284 bool AccessibilityManager::AccessibilityActionStartStop()
1286 Dali::Toolkit::AccessibilityManager handle( this );
1287 if( !mActionStartStopSignal.Empty() )
1289 mActionStartStopSignal.Emit( handle );
1292 // TODO: Start/stop the current action
1294 return mIsAccessibilityTtsEnabled;
1297 bool AccessibilityManager::AccessibilityActionTouch(const TouchEvent& touchEvent)
1299 bool handled = false;
1301 // TODO: Need to convert the touchevent for the focused actor?
1303 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(GetCurrentFocusActor());
1306 handled = GetImplementation( control ).OnAccessibilityTouch(touchEvent);
1312 bool AccessibilityManager::HandlePanGesture(const Integration::PanGestureEvent& panEvent)
1314 bool handled = false;
1316 if( panEvent.state == Gesture::Started )
1318 // Find the focusable actor at the event position
1319 Dali::HitTestAlgorithm::Results results;
1320 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
1322 Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), panEvent.currentPosition, results, IsActorFocusableFunction );
1323 mCurrentGesturedActor = results.actor;
1325 if(!mCurrentGesturedActor)
1327 DALI_LOG_ERROR("Gesture detected, but no hit actor\n");
1331 // Gesture::Finished (Up) events are delivered with previous (Motion) event position
1332 // Use the real previous position; otherwise we may incorrectly get a ZERO velocity
1333 if ( Gesture::Finished != panEvent.state )
1335 // Store the previous position for next Gesture::Finished iteration.
1336 mPreviousPosition = panEvent.previousPosition;
1339 Actor rootActor = Stage::GetCurrent().GetRootLayer();
1341 Dali::PanGesture pan(panEvent.state);
1342 pan.time = panEvent.time;
1343 pan.numberOfTouches = panEvent.numberOfTouches;
1344 pan.screenPosition = panEvent.currentPosition;
1345 pan.screenDisplacement = mPreviousPosition - panEvent.currentPosition;
1346 pan.screenVelocity.x = pan.screenDisplacement.x / panEvent.timeDelta;
1347 pan.screenVelocity.y = pan.screenDisplacement.y / panEvent.timeDelta;
1349 // Only handle the pan gesture when the current focused actor is scrollable or within a scrollable actor
1350 while(mCurrentGesturedActor && mCurrentGesturedActor != rootActor && !handled)
1352 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(mCurrentGesturedActor);
1355 Vector2 localCurrent;
1356 control.ScreenToLocal( localCurrent.x, localCurrent.y, panEvent.currentPosition.x, panEvent.currentPosition.y );
1357 pan.position = localCurrent;
1359 Vector2 localPrevious;
1360 control.ScreenToLocal( localPrevious.x, localPrevious.y, mPreviousPosition.x, mPreviousPosition.y );
1362 pan.displacement = localCurrent - localPrevious;
1363 pan.velocity.x = pan.displacement.x / panEvent.timeDelta;
1364 pan.velocity.y = pan.displacement.y / panEvent.timeDelta;
1366 handled = GetImplementation( control ).OnAccessibilityPan(pan);
1369 // If the gesture is not handled by the control, check its parent
1372 mCurrentGesturedActor = mCurrentGesturedActor.GetParent();
1374 if(!mCurrentGesturedActor)
1376 DALI_LOG_ERROR("no more gestured actor\n");
1381 // If handled, then update the pan gesture properties
1382 PanGestureDetector::SetPanGestureProperties( pan );
1389 Toolkit::AccessibilityManager::FocusChangedSignalType& AccessibilityManager::FocusChangedSignal()
1391 return mFocusChangedSignal;
1394 Toolkit::AccessibilityManager::FocusOvershotSignalType& AccessibilityManager::FocusOvershotSignal()
1396 return mFocusOvershotSignal;
1399 Toolkit::AccessibilityManager::FocusedActorActivatedSignalType& AccessibilityManager::FocusedActorActivatedSignal()
1401 return mFocusedActorActivatedSignal;
1404 } // namespace Internal
1406 } // namespace Toolkit