2 * Copyright (c) 2014 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 "keyboard-focus-manager-impl.h"
22 #include <dali-toolkit/public-api/controls/control.h>
23 #include <dali-toolkit/public-api/controls/control-impl.h>
24 #include <dali-toolkit/public-api/focus-manager/focus-manager.h>
25 #include <dali-toolkit/public-api/focus-manager/keyinput-focus-manager.h>
26 #include <dali/integration-api/debug.h>
37 namespace // unnamed namespace
40 #if defined(DEBUG_ENABLED)
41 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_KEYBOARD_FOCUS_MANAGER");
44 const std::string IS_FOCUS_GROUP_PROPERTY_NAME("is-keyboard-focus-group"); // This property will be replaced by a flag in Control.
46 const char* FOCUS_BORDER_IMAGE_PATH = DALI_IMAGE_DIR "keyboard_focus.png";
47 const Vector4 FOCUS_BORDER_IMAGE_BORDER = Vector4(7.0f, 7.0f, 7.0f, 7.0f);
51 BaseHandle handle = KeyboardFocusManager::Get();
53 if ( !handle && Adaptor::IsAvailable() )
55 Toolkit::KeyboardFocusManager manager = Toolkit::KeyboardFocusManager( new Internal::KeyboardFocusManager() );
56 Adaptor::Get().RegisterSingleton( typeid( manager ), manager );
62 TypeRegistration KEYBOARD_FOCUS_MANAGER_TYPE( typeid(Dali::Toolkit::KeyboardFocusManager), typeid(Dali::BaseHandle), Create, true /* Create instance at startup */ );
64 } // unnamed namespace
66 Toolkit::KeyboardFocusManager KeyboardFocusManager::Get()
68 Toolkit::KeyboardFocusManager manager;
70 if ( Adaptor::IsAvailable() )
72 // Check whether the keyboard focus manager is already created
73 Dali::BaseHandle handle = Dali::Adaptor::Get().GetSingleton( typeid( Toolkit::KeyboardFocusManager ) );
76 // If so, downcast the handle of singleton to keyboard focus manager
77 manager = Toolkit::KeyboardFocusManager( dynamic_cast< KeyboardFocusManager* >( handle.GetObjectPtr() ) );
84 KeyboardFocusManager::KeyboardFocusManager()
85 : mCurrentFocusActor(0),
86 mFocusIndicatorActor(Actor()),
87 mFocusGroupLoopEnabled(false),
88 mIsKeyboardFocusEnabled(false),
89 mIsFocusIndicatorEnabled(false),
90 mIsWaitingKeyboardFocusChangeCommit(false),
93 CreateDefaultFocusIndicatorActor();
95 OnPhysicalKeyboardStatusChanged(PhysicalKeyboard::Get());
97 Toolkit::KeyInputFocusManager::Get().UnhandledKeyEventSignal().Connect(mSlotDelegate, &KeyboardFocusManager::OnKeyEvent);
98 Stage::GetCurrent().TouchedSignal().Connect(mSlotDelegate, &KeyboardFocusManager::OnTouched);
99 PhysicalKeyboard::Get().StatusChangedSignal().Connect(mSlotDelegate, &KeyboardFocusManager::OnPhysicalKeyboardStatusChanged);
102 KeyboardFocusManager::~KeyboardFocusManager()
106 bool KeyboardFocusManager::SetCurrentFocusActor(Actor actor)
108 DALI_ASSERT_DEBUG( !mIsWaitingKeyboardFocusChangeCommit && "Calling this function in the PreFocusChangeSignal callback?" );
112 return DoSetCurrentFocusActor(actor.GetId());
118 bool KeyboardFocusManager::DoSetCurrentFocusActor(const unsigned int actorID)
120 Actor rootActor = Stage::GetCurrent().GetRootLayer();
121 Actor actor = rootActor.FindChildById(actorID);
123 // Check whether the actor is in the stage
126 // Set the focus only when the actor is keyboard focusable
127 if(actor.IsKeyboardFocusable())
129 // Draw the focus indicator upon the focused actor
130 if(mIsFocusIndicatorEnabled && mFocusIndicatorActor)
132 actor.Add(mFocusIndicatorActor);
135 // Send notification for the change of focus actor
136 if( !mFocusChangedSignalV2.Empty() )
138 mFocusChangedSignalV2.Emit(GetCurrentFocusActor(), actor);
141 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] Focus Changed\n", __FUNCTION__, __LINE__);
143 // Save the current focused actor
144 mCurrentFocusActor = actorID;
146 // Move the accessibility focus to the same actor
147 // Toolkit::FocusManager focusManager = Toolkit::FocusManager::Get();
148 // focusManager.SetCurrentFocusActor(actor);
150 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] SUCCEED\n", __FUNCTION__, __LINE__);
155 DALI_LOG_WARNING("[%s:%d] FAILED\n", __FUNCTION__, __LINE__);
159 Actor KeyboardFocusManager::GetCurrentFocusActor()
161 Actor rootActor = Stage::GetCurrent().GetRootLayer();
162 return rootActor.FindChildById(mCurrentFocusActor);
165 Actor KeyboardFocusManager::GetCurrentFocusGroup()
167 return GetFocusGroup(GetCurrentFocusActor());
170 bool KeyboardFocusManager::IsLayoutControl(Actor actor) const
172 Toolkit::Control control = Toolkit::Control::DownCast(actor);
173 return control && control.GetImplementation().IsKeyboardNavigationSupported();
176 Toolkit::Control KeyboardFocusManager::GetParentLayoutControl(Actor actor) const
178 // Get the actor's parent layout control that supports two dimensional keyboard navigation
179 Actor rootActor = Stage::GetCurrent().GetRootLayer();
183 parent = actor.GetParent();
186 while( parent && !IsLayoutControl(parent) && parent != rootActor )
188 parent = parent.GetParent();
191 return Toolkit::Control::DownCast(parent);
194 bool KeyboardFocusManager::MoveFocus(Toolkit::Control::KeyboardFocusNavigationDirection direction)
196 Actor currentFocusActor = GetCurrentFocusActor();
198 bool succeed = false;
200 // Go through the actor's hierarchy until we find a layout control that knows how to move the focus
201 Toolkit::Control parentLayoutControl = GetParentLayoutControl(currentFocusActor);
202 while(parentLayoutControl && !succeed)
204 succeed = DoMoveFocusWithinLayoutControl(parentLayoutControl, currentFocusActor, direction);
205 parentLayoutControl = GetParentLayoutControl(parentLayoutControl);
208 if(!succeed && !mPreFocusChangeSignalV2.Empty())
210 // Don't know how to move the focus further. The application needs to tell us which actor to move the focus to
211 mIsWaitingKeyboardFocusChangeCommit = true;
212 Actor nextFocusableActor = mPreFocusChangeSignalV2.Emit(currentFocusActor, Actor(), direction);
213 mIsWaitingKeyboardFocusChangeCommit = false;
215 if ( nextFocusableActor && nextFocusableActor.IsKeyboardFocusable() )
217 // Whether the next focusable actor is a layout control
218 if(IsLayoutControl(nextFocusableActor))
220 // If so, move the focus inside it.
221 Toolkit::Control layoutControl = Toolkit::Control::DownCast(nextFocusableActor);
222 succeed = DoMoveFocusWithinLayoutControl(layoutControl, currentFocusActor, direction);
226 // Otherwise, just set focus to the next focusable actor
227 succeed = SetCurrentFocusActor(nextFocusableActor);
235 bool KeyboardFocusManager::DoMoveFocusWithinLayoutControl(Toolkit::Control control, Actor actor, Toolkit::Control::KeyboardFocusNavigationDirection direction)
237 // Ask the control for the next actor to focus
238 Actor nextFocusableActor = control.GetImplementation().GetNextKeyboardFocusableActor(actor, direction, mFocusGroupLoopEnabled);
239 if(nextFocusableActor)
241 if(!nextFocusableActor.IsKeyboardFocusable())
243 // If the actor is not focusable, ask the same layout control for the next actor to focus
244 return DoMoveFocusWithinLayoutControl(control, nextFocusableActor, direction);
248 Actor currentFocusActor = GetCurrentFocusActor();
249 Actor committedFocusActor = nextFocusableActor;
251 // We will try to move the focus to the actor. Emit a signal to notify the proposed actor to focus
252 // Signal handler can check the proposed actor and return a different actor if it wishes.
253 if( !mPreFocusChangeSignalV2.Empty() )
255 mIsWaitingKeyboardFocusChangeCommit = true;
256 committedFocusActor = mPreFocusChangeSignalV2.Emit(currentFocusActor, nextFocusableActor, direction);
257 mIsWaitingKeyboardFocusChangeCommit = false;
260 if (committedFocusActor && committedFocusActor.IsKeyboardFocusable())
262 // Whether the commited focusable actor is a layout control
263 if(IsLayoutControl(committedFocusActor))
265 // If so, move the focus inside it.
266 Toolkit::Control layoutControl = Toolkit::Control::DownCast(committedFocusActor);
267 return DoMoveFocusWithinLayoutControl(layoutControl, currentFocusActor, direction);
271 // Otherwise, just set focus to the next focusable actor
272 if(committedFocusActor == nextFocusableActor)
274 // If the application hasn't changed our proposed actor, we informs the layout control we will
275 // move the focus to what the control returns. The control might wish to perform some actions
276 // before the focus is actually moved.
277 control.GetImplementation().OnKeyboardFocusChangeCommitted(committedFocusActor);
280 return SetCurrentFocusActor(committedFocusActor);
291 // No more actor can be focused in the given direction within the same layout control.
296 bool KeyboardFocusManager::DoMoveFocusToNextFocusGroup(bool forward)
298 bool succeed = false;
300 // Get the parent layout control of the current focus group
301 Toolkit::Control parentLayoutControl = GetParentLayoutControl(GetCurrentFocusGroup());
303 while(parentLayoutControl && !succeed)
305 // If the current focus group has a parent layout control, we can probably automatically
306 // move the focus to the next focus group in the forward or backward direction.
307 Toolkit::Control::KeyboardFocusNavigationDirection direction = forward ? Toolkit::Control::Right : Toolkit::Control::Left;
308 succeed = DoMoveFocusWithinLayoutControl(parentLayoutControl, GetCurrentFocusActor(), direction);
309 parentLayoutControl = GetParentLayoutControl(parentLayoutControl);
312 if(!mFocusGroupChangedSignalV2.Empty())
314 // Emit a focus group changed signal. The applicaton can move the focus to a new focus group
315 mFocusGroupChangedSignalV2.Emit(GetCurrentFocusActor(), forward);
321 void KeyboardFocusManager::DoActivate(Actor actor)
325 Toolkit::Control control = Toolkit::Control::DownCast(actor);
328 // Notify the control that it is activated
329 control.GetImplementation().OnActivated();
332 // Send notification for the activation of focused actor
333 if( !mFocusedActorActivatedSignalV2.Empty() )
335 mFocusedActorActivatedSignalV2.Emit(actor);
340 void KeyboardFocusManager::ClearFocus()
342 Actor actor = GetCurrentFocusActor();
345 if(mFocusIndicatorActor)
347 actor.Remove(mFocusIndicatorActor);
350 // Send notification for the change of focus actor
351 if( !mFocusChangedSignalV2.Empty() )
353 mFocusChangedSignalV2.Emit(actor, Actor());
357 mCurrentFocusActor = 0;
358 mIsFocusIndicatorEnabled = false;
361 void KeyboardFocusManager::SetFocusGroupLoop(bool enabled)
363 mFocusGroupLoopEnabled = enabled;
366 bool KeyboardFocusManager::GetFocusGroupLoop() const
368 return mFocusGroupLoopEnabled;
371 void KeyboardFocusManager::SetAsFocusGroup(Actor actor, bool isFocusGroup)
375 // Create focus group property if not already created.
376 Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(IS_FOCUS_GROUP_PROPERTY_NAME);
377 if(propertyIsFocusGroup == Property::INVALID_INDEX)
379 propertyIsFocusGroup = actor.RegisterProperty(IS_FOCUS_GROUP_PROPERTY_NAME, isFocusGroup);
383 actor.SetProperty(propertyIsFocusGroup, isFocusGroup);
388 bool KeyboardFocusManager::IsFocusGroup(Actor actor) const
390 // Check whether the actor is a focus group
391 bool isFocusGroup = false;
395 Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(IS_FOCUS_GROUP_PROPERTY_NAME);
396 if(propertyIsFocusGroup != Property::INVALID_INDEX)
398 isFocusGroup = actor.GetProperty<bool>(propertyIsFocusGroup);
405 Actor KeyboardFocusManager::GetFocusGroup(Actor actor)
407 // Go through the actor's hierarchy to check which focus group the actor belongs to
408 while (actor && !IsFocusGroup(actor))
410 actor = actor.GetParent();
416 void KeyboardFocusManager::SetFocusIndicatorActor(Actor indicator)
418 if(mFocusIndicatorActor != indicator)
420 Actor currentFocusActor = GetCurrentFocusActor();
421 if(currentFocusActor)
423 // The new focus indicator should be added to the current focused actor immediately
424 if(mFocusIndicatorActor)
426 currentFocusActor.Remove(mFocusIndicatorActor);
431 currentFocusActor.Add(indicator);
435 mFocusIndicatorActor = indicator;
439 Actor KeyboardFocusManager::GetFocusIndicatorActor()
441 return mFocusIndicatorActor;
444 void KeyboardFocusManager::CreateDefaultFocusIndicatorActor()
446 // Create a focus indicator actor shared by all the keyboard focusable actors
447 Image borderImage = Image::New(FOCUS_BORDER_IMAGE_PATH);
449 ImageActor focusIndicator = ImageActor::New(borderImage);
450 focusIndicator.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
451 focusIndicator.SetStyle( ImageActor::STYLE_NINE_PATCH );
452 focusIndicator.SetNinePatchBorder(FOCUS_BORDER_IMAGE_BORDER);
453 focusIndicator.SetPosition(Vector3(0.0f, 0.0f, 1.0f));
455 // Apply size constraint to the focus indicator
456 Constraint constraint = Constraint::New<Vector3>(Actor::SIZE,
457 ParentSource(Actor::SIZE),
458 EqualToConstraint());
459 focusIndicator.ApplyConstraint(constraint);
461 SetFocusIndicatorActor(focusIndicator);
464 void KeyboardFocusManager::OnPhysicalKeyboardStatusChanged(PhysicalKeyboard keyboard)
466 mIsKeyboardFocusEnabled = keyboard.IsAttached();
468 if(mIsKeyboardFocusEnabled)
470 // Show indicator when keyboard focus turned on if there is focused actor.
471 Actor actor = GetCurrentFocusActor();
474 if(mFocusIndicatorActor)
476 actor.Add(mFocusIndicatorActor);
479 mIsFocusIndicatorEnabled = true;
483 // Hide indicator when keyboard focus turned off
484 Actor actor = GetCurrentFocusActor();
487 actor.Remove(mFocusIndicatorActor);
489 mIsFocusIndicatorEnabled = false;
493 void KeyboardFocusManager::OnKeyEvent(const KeyEvent& event)
495 if(!mIsKeyboardFocusEnabled)
500 AccessibilityManager accessibilityManager = AccessibilityManager::Get();
501 bool isAccessibilityEnabled = accessibilityManager.IsEnabled();
503 Toolkit::FocusManager accessibilityFocusManager = Toolkit::FocusManager::Get();
505 std::string keyName = event.keyPressedName;
507 bool isFocusStartableKey = false;
509 if(event.state == KeyEvent::Down)
511 if (keyName == "Left")
513 if(!isAccessibilityEnabled)
515 if(!mIsFocusIndicatorEnabled)
517 // Show focus indicator
518 mIsFocusIndicatorEnabled = true;
522 // Move the focus towards left
523 MoveFocus(Toolkit::Control::Left);
526 isFocusStartableKey = true;
530 // Move the accessibility focus backward
531 accessibilityFocusManager.MoveFocusBackward();
534 else if (keyName == "Right")
536 if(!isAccessibilityEnabled)
538 if(!mIsFocusIndicatorEnabled)
540 // Show focus indicator
541 mIsFocusIndicatorEnabled = true;
545 // Move the focus towards right
546 MoveFocus(Toolkit::Control::Right);
549 isFocusStartableKey = true;
553 // Move the accessibility focus forward
554 accessibilityFocusManager.MoveFocusForward();
557 isFocusStartableKey = true;
559 else if (keyName == "Up" && !isAccessibilityEnabled)
561 if(!mIsFocusIndicatorEnabled)
563 // Show focus indicator
564 mIsFocusIndicatorEnabled = true;
568 // Move the focus towards up
569 MoveFocus(Toolkit::Control::Up);
572 isFocusStartableKey = true;
574 else if (keyName == "Down" && !isAccessibilityEnabled)
576 if(!mIsFocusIndicatorEnabled)
578 // Show focus indicator
579 mIsFocusIndicatorEnabled = true;
583 // Move the focus towards down
584 MoveFocus(Toolkit::Control::Down);
587 isFocusStartableKey = true;
589 else if (keyName == "Tab" && !isAccessibilityEnabled)
591 if(!mIsFocusIndicatorEnabled)
593 // Show focus indicator
594 mIsFocusIndicatorEnabled = true;
598 // "Tab" key changes the focus group in the forward direction and
599 // "Shift-Tab" key changes it in the backward direction.
600 DoMoveFocusToNextFocusGroup(!event.IsShiftModifier());
603 isFocusStartableKey = true;
605 else if (keyName == "space" && !isAccessibilityEnabled)
607 if(!mIsFocusIndicatorEnabled)
609 // Show focus indicator
610 mIsFocusIndicatorEnabled = true;
613 isFocusStartableKey = true;
615 else if (keyName == "" && !isAccessibilityEnabled)
617 // Check the fake key event for evas-plugin case
618 if(!mIsFocusIndicatorEnabled)
620 // Show focus indicator
621 mIsFocusIndicatorEnabled = true;
624 isFocusStartableKey = true;
626 else if (keyName == "Backspace" && !isAccessibilityEnabled)
628 // Emit signal to go back to the previous view???
631 else if(event.state == KeyEvent::Up)
633 if (keyName == "Return")
635 if(!mIsFocusIndicatorEnabled && !isAccessibilityEnabled)
637 // Show focus indicator
638 mIsFocusIndicatorEnabled = true;
642 // Activate the focused actor
644 if(!isAccessibilityEnabled)
646 actor = GetCurrentFocusActor();
650 actor = accessibilityFocusManager.GetCurrentFocusActor();
659 isFocusStartableKey = true;
663 if(isFocusStartableKey && mIsFocusIndicatorEnabled && !isAccessibilityEnabled)
665 Actor actor = GetCurrentFocusActor();
668 // No actor is focused but keyboard focus is activated by the key press
669 // Let's try to move the initial focus
670 MoveFocus(Toolkit::Control::Right);
672 else if(mFocusIndicatorActor)
674 // Make sure the focused actor is highlighted
675 actor.Add(mFocusIndicatorActor);
680 void KeyboardFocusManager::OnTouched(const TouchEvent& touchEvent)
682 // Clear the focus when user touch the screen
686 Toolkit::KeyboardFocusManager::PreFocusChangeSignalV2& KeyboardFocusManager::PreFocusChangeSignal()
688 return mPreFocusChangeSignalV2;
691 Toolkit::KeyboardFocusManager::FocusChangedSignalV2& KeyboardFocusManager::FocusChangedSignal()
693 return mFocusChangedSignalV2;
696 Toolkit::KeyboardFocusManager::FocusGroupChangedSignalV2& KeyboardFocusManager::FocusGroupChangedSignal()
698 return mFocusGroupChangedSignalV2;
701 Toolkit::KeyboardFocusManager::FocusedActorActivatedSignalV2& KeyboardFocusManager::FocusedActorActivatedSignal()
703 return mFocusedActorActivatedSignalV2;
706 bool KeyboardFocusManager::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
708 Dali::BaseHandle handle( object );
710 bool connected( true );
711 KeyboardFocusManager* manager = dynamic_cast<KeyboardFocusManager*>(object);
713 if( Dali::Toolkit::KeyboardFocusManager::SIGNAL_PRE_FOCUS_CHANGE == signalName )
715 manager->PreFocusChangeSignal().Connect( tracker, functor );
717 if( Dali::Toolkit::KeyboardFocusManager::SIGNAL_FOCUS_CHANGED == signalName )
719 manager->FocusChangedSignal().Connect( tracker, functor );
721 if( Dali::Toolkit::KeyboardFocusManager::SIGNAL_FOCUS_GROUP_CHANGED == signalName )
723 manager->FocusGroupChangedSignal().Connect( tracker, functor );
725 else if( Dali::Toolkit::KeyboardFocusManager::SIGNAL_FOCUSED_ACTOR_ACTIVATED== signalName )
727 manager->FocusedActorActivatedSignal().Connect( tracker, functor );
731 // signalName does not match any signal
738 } // namespace Internal
740 } // namespace Toolkit