2 * Copyright (c) 2015 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>
44 namespace // unnamed namespace
49 const char* const SIGNAL_FOCUS_CHANGED = "focusChanged";
50 const char* const SIGNAL_FOCUS_OVERSHOT = "focusOvershot";
51 const char* const SIGNAL_FOCUSED_ACTOR_ACTIVATED = "focusedActorActivated";
53 #if defined(DEBUG_ENABLED)
54 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_FOCUS_MANAGER");
57 const char* const ACTOR_FOCUSABLE("focusable");
58 const char* const IS_FOCUS_GROUP("isFocusGroup");
60 const char* FOCUS_BORDER_IMAGE_PATH = DALI_IMAGE_DIR "B16-8_TTS_focus.png";
61 const Vector4 FOCUS_BORDER_IMAGE_BORDER = Vector4(7.0f, 7.0f, 7.0f, 7.0f);
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(Actor()),
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 CreateDefaultFocusIndicatorActor();
132 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
133 adaptor.SetActionHandler(*this);
134 adaptor.SetGestureHandler(*this);
136 ChangeAccessibilityStatus();
139 AccessibilityManager::ActorAdditionalInfo AccessibilityManager::GetActorAdditionalInfo(const unsigned int actorID) const
141 ActorAdditionalInfo data;
142 IDAdditionalInfoConstIter iter = mIDAdditionalInfoContainer.find(actorID);
143 if(iter != mIDAdditionalInfoContainer.end())
145 data = (*iter).second;
151 void AccessibilityManager::SynchronizeActorAdditionalInfo(const unsigned int actorID, const unsigned int order)
153 ActorAdditionalInfo actorInfo = GetActorAdditionalInfo(actorID);
154 actorInfo.mFocusOrder = order;
155 mIDAdditionalInfoContainer.erase(actorID);
156 mIDAdditionalInfoContainer.insert(IDAdditionalInfoPair(actorID, actorInfo));
159 void AccessibilityManager::SetAccessibilityAttribute(Actor actor, Toolkit::AccessibilityManager::AccessibilityAttribute type, const std::string& text)
163 unsigned int actorID = actor.GetId();
165 ActorAdditionalInfo info = GetActorAdditionalInfo(actorID);
166 info.mAccessibilityAttributes[type] = text;
168 mIDAdditionalInfoContainer.erase(actorID);
169 mIDAdditionalInfoContainer.insert(IDAdditionalInfoPair(actorID, info));
173 std::string AccessibilityManager::GetAccessibilityAttribute(Actor actor, Toolkit::AccessibilityManager::AccessibilityAttribute type) const
179 ActorAdditionalInfo data = GetActorAdditionalInfo(actor.GetId());
180 text = data.mAccessibilityAttributes[type];
186 void AccessibilityManager::SetFocusOrder(Actor actor, const unsigned int order)
188 // Do nothing if the focus order of the actor is not changed.
189 if(actor && GetFocusOrder(actor) != order)
191 // Firstly delete the actor from the focus chain if it's already there with a different focus order.
192 mFocusIDContainer.erase(GetFocusOrder(actor));
194 // Create actor focusable property if not already created.
195 Property::Index propertyActorFocusable = actor.GetPropertyIndex(ACTOR_FOCUSABLE);
196 if(propertyActorFocusable == Property::INVALID_INDEX)
198 propertyActorFocusable = actor.RegisterProperty( ACTOR_FOCUSABLE, true, Property::READ_WRITE );
203 // The actor is not focusable without a defined focus order.
204 actor.SetProperty(propertyActorFocusable, false);
206 // If the actor is currently being focused, it should clear the focus
207 if(actor == GetCurrentFocusActor())
212 else // Insert the actor to the focus chain
214 // Check whether there is another actor in the focus chain with the same focus order already.
215 FocusIDIter focusIDIter = mFocusIDContainer.find(order);
216 if(focusIDIter != mFocusIDContainer.end())
218 // We need to increase the focus order of that actor and all the actors followed it
219 // in the focus chain.
220 FocusIDIter lastIter = mFocusIDContainer.end();
221 --lastIter;//We want forward iterator to the last element here
222 mFocusIDContainer.insert(FocusIDPair((*lastIter).first + 1, (*lastIter).second));
224 // Update the actor's focus order in its additional data
225 SynchronizeActorAdditionalInfo((*lastIter).second, (*lastIter).first + 1);
227 for(FocusIDIter iter = lastIter; iter != focusIDIter; iter--)
229 FocusIDIter previousIter = iter;
230 --previousIter;//We want forward iterator to the previous element here
231 unsigned int actorID = (*previousIter).second;
232 (*iter).second = actorID;
234 // Update the actor's focus order in its additional data
235 SynchronizeActorAdditionalInfo(actorID, (*iter).first);
238 mFocusIDContainer.erase(order);
241 // The actor is focusable
242 actor.SetProperty(propertyActorFocusable, true);
244 // Now we insert the actor into the focus chain with the specified focus order
245 mFocusIDContainer.insert(FocusIDPair(order, actor.GetId()));
248 // Update the actor's focus order in its additional data
249 SynchronizeActorAdditionalInfo(actor.GetId(), order);
253 unsigned int AccessibilityManager::GetFocusOrder(Actor actor) const
255 unsigned int focusOrder = 0;
259 ActorAdditionalInfo data = GetActorAdditionalInfo(actor.GetId());
260 focusOrder = data.mFocusOrder;
266 unsigned int AccessibilityManager::GenerateNewFocusOrder() const
268 unsigned int order = 1;
269 FocusIDContainer::const_reverse_iterator iter = mFocusIDContainer.rbegin();
271 if(iter != mFocusIDContainer.rend())
273 order = (*iter).first + 1;
279 Actor AccessibilityManager::GetActorByFocusOrder(const unsigned int order)
281 Actor actor = Actor();
283 FocusIDIter focusIDIter = mFocusIDContainer.find(order);
284 if(focusIDIter != mFocusIDContainer.end())
286 Actor rootActor = Stage::GetCurrent().GetRootLayer();
287 actor = rootActor.FindChildById(mFocusIDContainer[order]);
293 bool AccessibilityManager::SetCurrentFocusActor(Actor actor)
297 return DoSetCurrentFocusActor(actor.GetId());
303 bool AccessibilityManager::DoSetCurrentFocusActor(const unsigned int actorID)
305 Actor rootActor = Stage::GetCurrent().GetRootLayer();
307 // If the group mode is enabled, check which focus group the current focused actor belongs to
309 if(mIsFocusWithinGroup)
311 focusGroup = GetFocusGroup(GetCurrentFocusActor());
316 focusGroup = rootActor;
319 Actor actor = focusGroup.FindChildById(actorID);
321 // Check whether the actor is in the stage
324 // Check whether the actor is focusable
325 bool actorFocusable = false;
326 Property::Index propertyActorFocusable = actor.GetPropertyIndex(ACTOR_FOCUSABLE);
327 if(propertyActorFocusable != Property::INVALID_INDEX)
329 actorFocusable = actor.GetProperty<bool>(propertyActorFocusable);
332 // Go through the actor's hierarchy to check whether the actor is visible
333 bool actorVisible = actor.IsVisible();
334 Actor parent = actor.GetParent();
335 while (actorVisible && parent && parent != rootActor)
337 actorVisible = parent.IsVisible();
338 parent = parent.GetParent();
341 // Check whether the actor is fully transparent
342 bool actorOpaque = actor.GetCurrentWorldColor().a > 0.01f;
344 // Set the focus only when the actor is focusable and visible and not fully transparent
345 if(actorVisible && actorFocusable && actorOpaque)
347 // Draw the focus indicator upon the focused actor
348 if(mIsFocusIndicatorEnabled && mFocusIndicatorActor)
350 actor.Add(mFocusIndicatorActor);
353 // Send notification for the change of focus actor
354 mFocusChangedSignal.Emit( GetCurrentFocusActor(), actor );
356 // Save the current focused actor
357 mCurrentFocusActor = FocusIDPair(GetFocusOrder(actor), actorID);
359 if(mIsAccessibilityTtsEnabled)
361 Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
364 soundPlayer.PlaySound(FOCUS_SOUND_FILE);
367 // Play the accessibility attributes with the TTS player.
368 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
370 // Combine attribute texts to one text
371 std::string informationText;
372 for(int i = 0; i < Toolkit::AccessibilityManager::ACCESSIBILITY_ATTRIBUTE_NUM; i++)
374 if(!GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i].empty())
378 informationText += ", "; // for space time between each information
380 informationText += GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i];
383 player.Play(informationText);
390 DALI_LOG_WARNING("[%s:%d] FAILED\n", __FUNCTION__, __LINE__);
394 Actor AccessibilityManager::GetCurrentFocusActor()
396 Actor rootActor = Stage::GetCurrent().GetRootLayer();
397 return rootActor.FindChildById(mCurrentFocusActor.second);
400 Actor AccessibilityManager::GetCurrentFocusGroup()
402 return GetFocusGroup(GetCurrentFocusActor());
405 unsigned int AccessibilityManager::GetCurrentFocusOrder()
407 return mCurrentFocusActor.first;
410 bool AccessibilityManager::MoveFocusForward()
413 mRecursiveFocusMoveCounter = 0;
415 FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first);
416 if(focusIDIter != mFocusIDContainer.end())
418 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
419 ret = DoMoveFocus(focusIDIter, true, mIsWrapped);
423 // TODO: if there is not focused actor, move first actor
424 if(!mFocusIDContainer.empty())
426 //if there is not focused actor, move 1st actor
427 focusIDIter = mFocusIDContainer.begin(); // TODO: I'm not sure it was sorted.
428 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
429 ret = DoSetCurrentFocusActor((*focusIDIter).second);
433 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!");
438 bool AccessibilityManager::MoveFocusBackward()
441 mRecursiveFocusMoveCounter = 0;
443 FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first);
444 if(focusIDIter != mFocusIDContainer.end())
446 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
447 ret = DoMoveFocus(focusIDIter, false, mIsWrapped);
451 // TODO: if there is not focused actor, move last actor
452 if(!mFocusIDContainer.empty())
454 //if there is not focused actor, move last actor
455 focusIDIter = mFocusIDContainer.end();
456 --focusIDIter;//We want forward iterator to the last element here
457 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
458 ret = DoSetCurrentFocusActor((*focusIDIter).second);
462 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!");
467 void AccessibilityManager::DoActivate(Actor actor)
471 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
474 // Notify the control that it is activated
475 GetImplementation( control ).AccessibilityActivate();
478 // Send notification for the activation of focused actor
479 mFocusedActorActivatedSignal.Emit(actor);
483 void AccessibilityManager::ClearFocus()
485 Actor actor = GetCurrentFocusActor();
488 actor.Remove(mFocusIndicatorActor);
491 mCurrentFocusActor = FocusIDPair(0, 0);
493 // Send notification for the change of focus actor
494 mFocusChangedSignal.Emit(actor, Actor());
496 if(mIsAccessibilityTtsEnabled)
498 // Stop the TTS playing if any
499 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
504 void AccessibilityManager::Reset()
507 mFocusIDContainer.clear();
508 mIDAdditionalInfoContainer.clear();
511 void AccessibilityManager::SetFocusGroup(Actor actor, bool isFocusGroup)
515 // Create focus group property if not already created.
516 Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(IS_FOCUS_GROUP);
517 if(propertyIsFocusGroup == Property::INVALID_INDEX)
519 actor.RegisterProperty( IS_FOCUS_GROUP, isFocusGroup, Property::READ_WRITE );
523 actor.SetProperty(propertyIsFocusGroup, isFocusGroup);
528 bool AccessibilityManager::IsFocusGroup(Actor actor) const
530 // Check whether the actor is a focus group
531 bool isFocusGroup = false;
535 Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(IS_FOCUS_GROUP);
536 if(propertyIsFocusGroup != Property::INVALID_INDEX)
538 isFocusGroup = actor.GetProperty<bool>(propertyIsFocusGroup);
545 Actor AccessibilityManager::GetFocusGroup(Actor actor)
547 // Go through the actor's hierarchy to check which focus group the actor belongs to
548 while (actor && !IsFocusGroup(actor))
550 actor = actor.GetParent();
556 Vector2 AccessibilityManager::GetReadPosition() const
558 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
559 return adaptor.GetReadPosition();
562 void AccessibilityManager::SetGroupMode(bool enabled)
564 mIsFocusWithinGroup = enabled;
567 bool AccessibilityManager::GetGroupMode() const
569 return mIsFocusWithinGroup;
572 void AccessibilityManager::SetWrapMode(bool wrapped)
574 mIsWrapped = wrapped;
577 bool AccessibilityManager::GetWrapMode() const
582 void AccessibilityManager::SetFocusIndicatorActor(Actor indicator)
584 mFocusIndicatorActor = indicator;
587 Actor AccessibilityManager::GetFocusIndicatorActor()
589 return mFocusIndicatorActor;
592 bool AccessibilityManager::DoMoveFocus(FocusIDIter focusIDIter, bool forward, bool wrapped)
594 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] %d focusable actors\n", __FUNCTION__, __LINE__, mFocusIDContainer.size());
595 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
597 if( (forward && ++focusIDIter == mFocusIDContainer.end())
598 || (!forward && focusIDIter-- == mFocusIDContainer.begin()) )
600 if(mIsEndcapFeedbackEnabled)
602 if(mIsEndcapFeedbackPlayed == false)
604 // play sound & skip moving once
605 Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
608 soundPlayer.PlaySound(FOCUS_CHAIN_END_SOUND_FILE);
611 mIsEndcapFeedbackPlayed = true;
614 mIsEndcapFeedbackPlayed = false;
621 focusIDIter = mFocusIDContainer.begin();
625 focusIDIter = mFocusIDContainer.end();
626 --focusIDIter;//We want forward iterator to the last element here
631 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] Overshot\n", __FUNCTION__, __LINE__);
632 // Send notification for handling overshooted situation
633 mFocusOvershotSignal.Emit(GetCurrentFocusActor(), forward ? Toolkit::AccessibilityManager::OVERSHOT_NEXT : Toolkit::AccessibilityManager::OVERSHOT_PREVIOUS);
635 return false; // Try to move the focus out of the scope
640 if( focusIDIter == mFocusIDContainer.end() )
645 // Note: This function performs the focus change.
646 if( !DoSetCurrentFocusActor( (*focusIDIter).second ) )
648 mRecursiveFocusMoveCounter++;
649 if(mRecursiveFocusMoveCounter > mFocusIDContainer.size())
651 // We've attempted to focus all the actors in the whole focus chain and no actor
652 // can be focused successfully.
653 DALI_LOG_WARNING("[%s] There is no more focusable actor in %d focus chains\n", __FUNCTION__, mRecursiveFocusMoveCounter);
659 return DoMoveFocus(focusIDIter, forward, wrapped);
666 void AccessibilityManager::SetFocusable(Actor actor, bool focusable)
670 // Create actor focusable property if not already created.
671 Property::Index propertyActorFocusable = actor.GetPropertyIndex(ACTOR_FOCUSABLE);
672 if(propertyActorFocusable == Property::INVALID_INDEX)
674 actor.RegisterProperty( ACTOR_FOCUSABLE, focusable, Property::READ_WRITE );
678 actor.SetProperty(propertyActorFocusable, focusable);
683 void AccessibilityManager::CreateDefaultFocusIndicatorActor()
685 // Create a focus indicator actor shared by all the focusable actors
686 Image borderImage = ResourceImage::New(FOCUS_BORDER_IMAGE_PATH);
688 ImageActor focusIndicator = ImageActor::New(borderImage);
689 focusIndicator.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
690 focusIndicator.SetStyle( ImageActor::STYLE_NINE_PATCH );
691 focusIndicator.SetNinePatchBorder(FOCUS_BORDER_IMAGE_BORDER);
692 focusIndicator.SetPosition(Vector3(0.0f, 0.0f, 1.0f));
694 // Apply size constraint to the focus indicator
695 focusIndicator.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
697 SetFocusIndicatorActor(focusIndicator);
700 bool AccessibilityManager::ChangeAccessibilityStatus()
702 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
703 mIsAccessibilityTtsEnabled = adaptor.IsEnabled();
704 Dali::Toolkit::AccessibilityManager handle( this );
706 if(mIsAccessibilityTtsEnabled)
708 // Show indicator when tts turned on if there is focused actor.
709 Actor actor = GetCurrentFocusActor();
712 if(mFocusIndicatorActor)
714 actor.Add(mFocusIndicatorActor);
717 mIsFocusIndicatorEnabled = true;
719 // Connect a signal to the TTS player to implement continuous reading mode.
720 Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
721 player.StateChangedSignal().Connect( this, &AccessibilityManager::TtsStateChanged );
726 // Hide indicator when tts turned off
727 Actor actor = GetCurrentFocusActor();
730 actor.Remove(mFocusIndicatorActor);
732 mIsFocusIndicatorEnabled = false;
736 // Disconnect the TTS state change signal.
737 Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
738 player.StateChangedSignal().Disconnect( this, &AccessibilityManager::TtsStateChanged );
743 mStatusChangedSignal.Emit( handle );
748 bool AccessibilityManager::AccessibilityActionNext(bool allowEndFeedback)
750 Dali::Toolkit::AccessibilityManager handle( this );
751 if( !mActionNextSignal.Empty() )
753 mActionNextSignal.Emit( handle );
756 if(mIsAccessibilityTtsEnabled)
758 mIsEndcapFeedbackEnabled = allowEndFeedback;
759 return MoveFocusForward();
767 bool AccessibilityManager::AccessibilityActionPrevious(bool allowEndFeedback)
769 Dali::Toolkit::AccessibilityManager handle( this );
770 if( !mActionPreviousSignal.Empty() )
772 mActionPreviousSignal.Emit( handle );
775 if(mIsAccessibilityTtsEnabled)
777 mIsEndcapFeedbackEnabled = allowEndFeedback;
778 return MoveFocusBackward();
786 bool AccessibilityManager::AccessibilityActionActivate()
788 Dali::Toolkit::AccessibilityManager handle( this );
789 if( !mActionActivateSignal.Empty() )
791 mActionActivateSignal.Emit( handle );
796 Actor actor = GetCurrentFocusActor();
806 bool AccessibilityManager::AccessibilityActionRead(bool allowReadAgain)
808 Dali::Toolkit::AccessibilityManager handle( this );
812 if ( !mActionReadSignal.Empty() )
814 mActionReadSignal.Emit( handle );
819 if ( !mActionOverSignal.Empty() )
821 mActionOverSignal.Emit( handle );
827 if(mIsAccessibilityTtsEnabled)
829 // Find the focusable actor at the read position
830 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
831 Dali::HitTestAlgorithm::Results results;
832 Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), adaptor.GetReadPosition(), results, IsActorFocusableFunction );
834 FocusIDIter focusIDIter = mFocusIDContainer.find(GetFocusOrder(results.actor));
835 if(focusIDIter != mFocusIDContainer.end())
837 if( allowReadAgain || (results.actor != GetCurrentFocusActor()) )
839 // Move the focus to the actor
840 ret = SetCurrentFocusActor(results.actor);
841 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] SetCurrentFocusActor returns %s\n", __FUNCTION__, __LINE__, ret?"TRUE":"FALSE");
849 bool AccessibilityManager::AccessibilityActionReadNext(bool allowEndFeedback)
851 Dali::Toolkit::AccessibilityManager handle( this );
852 if( !mActionReadNextSignal.Empty() )
854 mActionReadNextSignal.Emit( handle );
857 if(mIsAccessibilityTtsEnabled)
859 return MoveFocusForward();
867 bool AccessibilityManager::AccessibilityActionReadPrevious(bool allowEndFeedback)
869 Dali::Toolkit::AccessibilityManager handle( this );
870 if( !mActionReadPreviousSignal.Empty() )
872 mActionReadPreviousSignal.Emit( handle );
875 if(mIsAccessibilityTtsEnabled)
877 return MoveFocusBackward();
885 bool AccessibilityManager::AccessibilityActionUp()
887 Dali::Toolkit::AccessibilityManager handle( this );
888 if( !mActionUpSignal.Empty() )
890 mActionUpSignal.Emit( handle );
895 if(mIsAccessibilityTtsEnabled)
897 Actor actor = GetCurrentFocusActor();
900 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
903 // Notify the control that it is activated
904 ret = GetImplementation( control ).OnAccessibilityValueChange(true);
912 bool AccessibilityManager::AccessibilityActionDown()
914 Dali::Toolkit::AccessibilityManager handle( this );
915 if( !mActionDownSignal.Empty() )
917 mActionDownSignal.Emit( handle );
922 if(mIsAccessibilityTtsEnabled)
924 Actor actor = GetCurrentFocusActor();
927 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
930 // Notify the control that it is activated
931 ret = GetImplementation( control ).OnAccessibilityValueChange(false);
939 bool AccessibilityManager::ClearAccessibilityFocus()
941 Dali::Toolkit::AccessibilityManager handle( this );
942 if( !mActionClearFocusSignal.Empty() )
944 mActionClearFocusSignal.Emit( handle );
947 if(mIsAccessibilityTtsEnabled)
958 bool AccessibilityManager::AccessibilityActionScroll( Dali::TouchEvent& touchEvent )
960 Dali::Toolkit::AccessibilityManager handle( this );
961 if( !mActionScrollSignal.Empty() )
963 mActionScrollSignal.Emit( handle, touchEvent );
969 bool AccessibilityManager::AccessibilityActionBack()
971 Dali::Toolkit::AccessibilityManager handle( this );
972 if( !mActionBackSignal.Empty() )
974 mActionBackSignal.Emit( handle );
977 // TODO: Back to previous view
979 return mIsAccessibilityTtsEnabled;
982 bool AccessibilityManager::AccessibilityActionScrollUp()
984 Dali::Toolkit::AccessibilityManager handle( this );
985 if( !mActionScrollUpSignal.Empty() )
987 mActionScrollUpSignal.Emit( handle );
992 if(mIsAccessibilityTtsEnabled)
994 Actor actor = GetCurrentFocusActor();
997 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1000 // TODO: Notify the control to scroll up. Should control handle this?
1001 // ret = GetImplementation( control ).OnAccessibilityScroll(Direction::UP);
1009 bool AccessibilityManager::AccessibilityActionScrollDown()
1011 Dali::Toolkit::AccessibilityManager handle( this );
1012 if( !mActionScrollDownSignal.Empty() )
1014 mActionScrollDownSignal.Emit( handle );
1019 if(mIsAccessibilityTtsEnabled)
1021 Actor actor = GetCurrentFocusActor();
1024 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1027 // TODO: Notify the control to scroll down. Should control handle this?
1028 // ret = GetImplementation( control ).OnAccessibilityScrollDown(Direction::DOWN);
1036 bool AccessibilityManager::AccessibilityActionPageLeft()
1038 Dali::Toolkit::AccessibilityManager handle( this );
1039 if( !mActionPageLeftSignal.Empty() )
1041 mActionPageLeftSignal.Emit( handle );
1046 if(mIsAccessibilityTtsEnabled)
1048 Actor actor = GetCurrentFocusActor();
1051 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1054 // TODO: Notify the control to scroll left to the previous page. Should control handle this?
1055 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::LEFT);
1063 bool AccessibilityManager::AccessibilityActionPageRight()
1065 Dali::Toolkit::AccessibilityManager handle( this );
1066 if( !mActionPageRightSignal.Empty() )
1068 mActionPageRightSignal.Emit( handle );
1073 if(mIsAccessibilityTtsEnabled)
1075 Actor actor = GetCurrentFocusActor();
1078 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1081 // TODO: Notify the control to scroll right to the next page. Should control handle this?
1082 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::RIGHT);
1090 bool AccessibilityManager::AccessibilityActionPageUp()
1092 Dali::Toolkit::AccessibilityManager handle( this );
1093 if( !mActionPageUpSignal.Empty() )
1095 mActionPageUpSignal.Emit( handle );
1100 if(mIsAccessibilityTtsEnabled)
1102 Actor actor = GetCurrentFocusActor();
1105 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1108 // TODO: Notify the control to scroll up to the previous page. Should control handle this?
1109 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::UP);
1117 bool AccessibilityManager::AccessibilityActionPageDown()
1119 Dali::Toolkit::AccessibilityManager handle( this );
1120 if( !mActionPageDownSignal.Empty() )
1122 mActionPageDownSignal.Emit( handle );
1127 if(mIsAccessibilityTtsEnabled)
1129 Actor actor = GetCurrentFocusActor();
1132 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1135 // TODO: Notify the control to scroll down to the next page. Should control handle this?
1136 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::DOWN);
1144 bool AccessibilityManager::AccessibilityActionMoveToFirst()
1146 Dali::Toolkit::AccessibilityManager handle( this );
1147 if( !mActionMoveToFirstSignal.Empty() )
1149 mActionMoveToFirstSignal.Emit( handle );
1152 // TODO: Move to the first item on screen
1154 return mIsAccessibilityTtsEnabled;
1157 bool AccessibilityManager::AccessibilityActionMoveToLast()
1159 Dali::Toolkit::AccessibilityManager handle( this );
1160 if( !mActionMoveToLastSignal.Empty() )
1162 mActionMoveToLastSignal.Emit( handle );
1165 // TODO: Move to the last item on screen
1167 return mIsAccessibilityTtsEnabled;
1170 bool AccessibilityManager::AccessibilityActionReadFromTop()
1172 Dali::Toolkit::AccessibilityManager handle( this );
1173 if( !mActionReadFromTopSignal.Empty() )
1175 mActionReadFromTopSignal.Emit( handle );
1178 // TODO: Move to the top item on screen and read from the item continuously
1180 return mIsAccessibilityTtsEnabled;
1183 bool AccessibilityManager::AccessibilityActionReadFromNext()
1185 Dali::Toolkit::AccessibilityManager handle( this );
1187 if( !mActionReadFromNextSignal.Empty() )
1189 mActionReadFromNextSignal.Emit( handle );
1192 if( mIsAccessibilityTtsEnabled )
1194 // Mark that we are in continuous play mode, so TTS signals can move focus.
1195 mContinuousPlayMode = true;
1197 // Attempt to move to the next item and read from the item continuously.
1201 return mIsAccessibilityTtsEnabled;
1204 void AccessibilityManager::TtsStateChanged( const Dali::TtsPlayer::State previousState, const Dali::TtsPlayer::State currentState )
1206 if( mContinuousPlayMode )
1208 // If we were playing and now we have stopped, attempt to play the next item.
1209 if( ( previousState == Dali::TtsPlayer::PLAYING ) && ( currentState == Dali::TtsPlayer::READY ) )
1211 // Attempt to move the focus forward and play.
1212 // If we can't cancel continuous play mode.
1213 if( !MoveFocusForward() )
1215 // We are done, exit continuous play mode.
1216 mContinuousPlayMode = false;
1221 // Unexpected play state change, exit continuous play mode.
1222 mContinuousPlayMode = false;
1227 bool AccessibilityManager::AccessibilityActionZoom()
1229 Dali::Toolkit::AccessibilityManager handle( this );
1230 if( !mActionZoomSignal.Empty() )
1232 mActionZoomSignal.Emit( handle );
1237 if(mIsAccessibilityTtsEnabled)
1239 Actor actor = GetCurrentFocusActor();
1242 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1245 // Notify the control to zoom
1246 ret = GetImplementation( control ).OnAccessibilityZoom();
1254 bool AccessibilityManager::AccessibilityActionReadIndicatorInformation()
1256 Dali::Toolkit::AccessibilityManager handle( this );
1257 if( !mActionReadIndicatorInformationSignal.Empty() )
1259 mActionReadIndicatorInformationSignal.Emit( handle );
1262 // TODO: Read the information in the indicator
1264 return mIsAccessibilityTtsEnabled;
1267 bool AccessibilityManager::AccessibilityActionReadPauseResume()
1269 Dali::Toolkit::AccessibilityManager handle( this );
1270 if( !mActionReadPauseResumeSignal.Empty() )
1272 mActionReadPauseResumeSignal.Emit( handle );
1277 if(mIsAccessibilityTtsEnabled)
1279 // Pause or resume the TTS player
1280 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
1281 Dali::TtsPlayer::State state = player.GetState();
1282 if(state == Dali::TtsPlayer::PLAYING)
1287 else if(state == Dali::TtsPlayer::PAUSED)
1297 bool AccessibilityManager::AccessibilityActionStartStop()
1299 Dali::Toolkit::AccessibilityManager handle( this );
1300 if( !mActionStartStopSignal.Empty() )
1302 mActionStartStopSignal.Emit( handle );
1305 // TODO: Start/stop the current action
1307 return mIsAccessibilityTtsEnabled;
1310 bool AccessibilityManager::AccessibilityActionTouch(const TouchEvent& touchEvent)
1312 bool handled = false;
1314 // TODO: Need to convert the touchevent for the focused actor?
1316 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(GetCurrentFocusActor());
1319 handled = GetImplementation( control ).OnAccessibilityTouch(touchEvent);
1325 bool AccessibilityManager::HandlePanGesture(const Integration::PanGestureEvent& panEvent)
1327 bool handled = false;
1329 if( panEvent.state == Gesture::Started )
1331 // Find the focusable actor at the event position
1332 Dali::HitTestAlgorithm::Results results;
1333 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
1335 Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), panEvent.currentPosition, results, IsActorFocusableFunction );
1336 mCurrentGesturedActor = results.actor;
1338 if(!mCurrentGesturedActor)
1340 DALI_LOG_ERROR("Gesture detected, but no hit actor");
1344 // Gesture::Finished (Up) events are delivered with previous (Motion) event position
1345 // Use the real previous position; otherwise we may incorrectly get a ZERO velocity
1346 if ( Gesture::Finished != panEvent.state )
1348 // Store the previous position for next Gesture::Finished iteration.
1349 mPreviousPosition = panEvent.previousPosition;
1352 Actor rootActor = Stage::GetCurrent().GetRootLayer();
1354 Dali::PanGesture pan(panEvent.state);
1355 pan.time = panEvent.time;
1356 pan.numberOfTouches = panEvent.numberOfTouches;
1357 pan.screenPosition = panEvent.currentPosition;
1358 pan.screenDisplacement = mPreviousPosition - panEvent.currentPosition;
1359 pan.screenVelocity.x = pan.screenDisplacement.x / panEvent.timeDelta;
1360 pan.screenVelocity.y = pan.screenDisplacement.y / panEvent.timeDelta;
1362 // Only handle the pan gesture when the current focused actor is scrollable or within a scrollable actor
1363 while(mCurrentGesturedActor && mCurrentGesturedActor != rootActor && !handled)
1365 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(mCurrentGesturedActor);
1368 Vector2 localCurrent;
1369 control.ScreenToLocal( localCurrent.x, localCurrent.y, panEvent.currentPosition.x, panEvent.currentPosition.y );
1370 pan.position = localCurrent;
1372 Vector2 localPrevious;
1373 control.ScreenToLocal( localPrevious.x, localPrevious.y, mPreviousPosition.x, mPreviousPosition.y );
1375 pan.displacement = localCurrent - localPrevious;
1376 pan.velocity.x = pan.displacement.x / panEvent.timeDelta;
1377 pan.velocity.y = pan.displacement.y / panEvent.timeDelta;
1379 handled = GetImplementation( control ).OnAccessibilityPan(pan);
1382 // If the gesture is not handled by the control, check its parent
1385 mCurrentGesturedActor = mCurrentGesturedActor.GetParent();
1387 if(!mCurrentGesturedActor)
1389 DALI_LOG_ERROR("no more gestured actor");
1394 // If handled, then update the pan gesture properties
1395 PanGestureDetector::SetPanGestureProperties( pan );
1402 Toolkit::AccessibilityManager::FocusChangedSignalType& AccessibilityManager::FocusChangedSignal()
1404 return mFocusChangedSignal;
1407 Toolkit::AccessibilityManager::FocusOvershotSignalType& AccessibilityManager::FocusOvershotSignal()
1409 return mFocusOvershotSignal;
1412 Toolkit::AccessibilityManager::FocusedActorActivatedSignalType& AccessibilityManager::FocusedActorActivatedSignal()
1414 return mFocusedActorActivatedSignal;
1417 bool AccessibilityManager::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
1419 Dali::BaseHandle handle( object );
1421 bool connected( true );
1422 AccessibilityManager* manager = dynamic_cast<AccessibilityManager*>( object );
1424 if( 0 == strcmp( signalName.c_str(), SIGNAL_FOCUS_CHANGED ) )
1426 manager->FocusChangedSignal().Connect( tracker, functor );
1428 else if( 0 == strcmp( signalName.c_str(), SIGNAL_FOCUS_OVERSHOT ) )
1430 manager->FocusOvershotSignal().Connect( tracker, functor );
1432 else if( 0 == strcmp( signalName.c_str(), SIGNAL_FOCUSED_ACTOR_ACTIVATED ) )
1434 manager->FocusedActorActivatedSignal().Connect( tracker, functor );
1438 // signalName does not match any signal
1445 } // namespace Internal
1447 } // namespace Toolkit