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();
55 SingletonService singletonService( SingletonService::Get() );
56 if ( singletonService )
58 Toolkit::KeyboardFocusManager manager = Toolkit::KeyboardFocusManager( new Internal::KeyboardFocusManager() );
59 singletonService.Register( typeid( manager ), manager );
66 TypeRegistration KEYBOARD_FOCUS_MANAGER_TYPE( typeid(Dali::Toolkit::KeyboardFocusManager), typeid(Dali::BaseHandle), Create, true /* Create instance at startup */ );
68 } // unnamed namespace
70 Toolkit::KeyboardFocusManager KeyboardFocusManager::Get()
72 Toolkit::KeyboardFocusManager manager;
74 SingletonService singletonService( SingletonService::Get() );
75 if ( singletonService )
77 // Check whether the keyboard focus manager is already created
78 Dali::BaseHandle handle = singletonService.GetSingleton( typeid( Toolkit::KeyboardFocusManager ) );
81 // If so, downcast the handle of singleton to keyboard focus manager
82 manager = Toolkit::KeyboardFocusManager( dynamic_cast< KeyboardFocusManager* >( handle.GetObjectPtr() ) );
89 KeyboardFocusManager::KeyboardFocusManager()
90 : mCurrentFocusActor(0),
91 mFocusIndicatorActor(Actor()),
92 mFocusGroupLoopEnabled(false),
93 mIsKeyboardFocusEnabled(false),
94 mIsFocusIndicatorEnabled(false),
95 mIsWaitingKeyboardFocusChangeCommit(false),
98 CreateDefaultFocusIndicatorActor();
100 OnPhysicalKeyboardStatusChanged(PhysicalKeyboard::Get());
102 Toolkit::KeyInputFocusManager::Get().UnhandledKeyEventSignal().Connect(mSlotDelegate, &KeyboardFocusManager::OnKeyEvent);
103 Stage::GetCurrent().TouchedSignal().Connect(mSlotDelegate, &KeyboardFocusManager::OnTouched);
104 PhysicalKeyboard::Get().StatusChangedSignal().Connect(mSlotDelegate, &KeyboardFocusManager::OnPhysicalKeyboardStatusChanged);
107 KeyboardFocusManager::~KeyboardFocusManager()
111 bool KeyboardFocusManager::SetCurrentFocusActor(Actor actor)
113 DALI_ASSERT_DEBUG( !mIsWaitingKeyboardFocusChangeCommit && "Calling this function in the PreFocusChangeSignal callback?" );
117 return DoSetCurrentFocusActor(actor.GetId());
123 bool KeyboardFocusManager::DoSetCurrentFocusActor(const unsigned int actorID)
125 Actor rootActor = Stage::GetCurrent().GetRootLayer();
126 Actor actor = rootActor.FindChildById(actorID);
128 // Check whether the actor is in the stage
131 // Set the focus only when the actor is keyboard focusable
132 if(actor.IsKeyboardFocusable())
134 // Draw the focus indicator upon the focused actor
135 if(mIsFocusIndicatorEnabled && mFocusIndicatorActor)
137 actor.Add(mFocusIndicatorActor);
140 // Send notification for the change of focus actor
141 if( !mFocusChangedSignalV2.Empty() )
143 mFocusChangedSignalV2.Emit(GetCurrentFocusActor(), actor);
146 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] Focus Changed\n", __FUNCTION__, __LINE__);
148 // Save the current focused actor
149 mCurrentFocusActor = actorID;
151 // Move the accessibility focus to the same actor
152 // Toolkit::FocusManager focusManager = Toolkit::FocusManager::Get();
153 // focusManager.SetCurrentFocusActor(actor);
155 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] SUCCEED\n", __FUNCTION__, __LINE__);
160 DALI_LOG_WARNING("[%s:%d] FAILED\n", __FUNCTION__, __LINE__);
164 Actor KeyboardFocusManager::GetCurrentFocusActor()
166 Actor rootActor = Stage::GetCurrent().GetRootLayer();
167 return rootActor.FindChildById(mCurrentFocusActor);
170 Actor KeyboardFocusManager::GetCurrentFocusGroup()
172 return GetFocusGroup(GetCurrentFocusActor());
175 bool KeyboardFocusManager::IsLayoutControl(Actor actor) const
177 Toolkit::Control control = Toolkit::Control::DownCast(actor);
178 return control && control.GetImplementation().IsKeyboardNavigationSupported();
181 Toolkit::Control KeyboardFocusManager::GetParentLayoutControl(Actor actor) const
183 // Get the actor's parent layout control that supports two dimensional keyboard navigation
184 Actor rootActor = Stage::GetCurrent().GetRootLayer();
188 parent = actor.GetParent();
191 while( parent && !IsLayoutControl(parent) && parent != rootActor )
193 parent = parent.GetParent();
196 return Toolkit::Control::DownCast(parent);
199 bool KeyboardFocusManager::MoveFocus(Toolkit::Control::KeyboardFocusNavigationDirection direction)
201 Actor currentFocusActor = GetCurrentFocusActor();
203 bool succeed = false;
205 // Go through the actor's hierarchy until we find a layout control that knows how to move the focus
206 Toolkit::Control parentLayoutControl = GetParentLayoutControl(currentFocusActor);
207 while(parentLayoutControl && !succeed)
209 succeed = DoMoveFocusWithinLayoutControl(parentLayoutControl, currentFocusActor, direction);
210 parentLayoutControl = GetParentLayoutControl(parentLayoutControl);
213 if(!succeed && !mPreFocusChangeSignalV2.Empty())
215 // Don't know how to move the focus further. The application needs to tell us which actor to move the focus to
216 mIsWaitingKeyboardFocusChangeCommit = true;
217 Actor nextFocusableActor = mPreFocusChangeSignalV2.Emit(currentFocusActor, Actor(), direction);
218 mIsWaitingKeyboardFocusChangeCommit = false;
220 if ( nextFocusableActor && nextFocusableActor.IsKeyboardFocusable() )
222 // Whether the next focusable actor is a layout control
223 if(IsLayoutControl(nextFocusableActor))
225 // If so, move the focus inside it.
226 Toolkit::Control layoutControl = Toolkit::Control::DownCast(nextFocusableActor);
227 succeed = DoMoveFocusWithinLayoutControl(layoutControl, currentFocusActor, direction);
231 // Otherwise, just set focus to the next focusable actor
232 succeed = SetCurrentFocusActor(nextFocusableActor);
240 bool KeyboardFocusManager::DoMoveFocusWithinLayoutControl(Toolkit::Control control, Actor actor, Toolkit::Control::KeyboardFocusNavigationDirection direction)
242 // Ask the control for the next actor to focus
243 Actor nextFocusableActor = control.GetImplementation().GetNextKeyboardFocusableActor(actor, direction, mFocusGroupLoopEnabled);
244 if(nextFocusableActor)
246 if(!nextFocusableActor.IsKeyboardFocusable())
248 // If the actor is not focusable, ask the same layout control for the next actor to focus
249 return DoMoveFocusWithinLayoutControl(control, nextFocusableActor, direction);
253 Actor currentFocusActor = GetCurrentFocusActor();
254 Actor committedFocusActor = nextFocusableActor;
256 // We will try to move the focus to the actor. Emit a signal to notify the proposed actor to focus
257 // Signal handler can check the proposed actor and return a different actor if it wishes.
258 if( !mPreFocusChangeSignalV2.Empty() )
260 mIsWaitingKeyboardFocusChangeCommit = true;
261 committedFocusActor = mPreFocusChangeSignalV2.Emit(currentFocusActor, nextFocusableActor, direction);
262 mIsWaitingKeyboardFocusChangeCommit = false;
265 if (committedFocusActor && committedFocusActor.IsKeyboardFocusable())
267 // Whether the commited focusable actor is a layout control
268 if(IsLayoutControl(committedFocusActor))
270 // If so, move the focus inside it.
271 Toolkit::Control layoutControl = Toolkit::Control::DownCast(committedFocusActor);
272 return DoMoveFocusWithinLayoutControl(layoutControl, currentFocusActor, direction);
276 // Otherwise, just set focus to the next focusable actor
277 if(committedFocusActor == nextFocusableActor)
279 // If the application hasn't changed our proposed actor, we informs the layout control we will
280 // move the focus to what the control returns. The control might wish to perform some actions
281 // before the focus is actually moved.
282 control.GetImplementation().OnKeyboardFocusChangeCommitted(committedFocusActor);
285 return SetCurrentFocusActor(committedFocusActor);
296 // No more actor can be focused in the given direction within the same layout control.
301 bool KeyboardFocusManager::DoMoveFocusToNextFocusGroup(bool forward)
303 bool succeed = false;
305 // Get the parent layout control of the current focus group
306 Toolkit::Control parentLayoutControl = GetParentLayoutControl(GetCurrentFocusGroup());
308 while(parentLayoutControl && !succeed)
310 // If the current focus group has a parent layout control, we can probably automatically
311 // move the focus to the next focus group in the forward or backward direction.
312 Toolkit::Control::KeyboardFocusNavigationDirection direction = forward ? Toolkit::Control::Right : Toolkit::Control::Left;
313 succeed = DoMoveFocusWithinLayoutControl(parentLayoutControl, GetCurrentFocusActor(), direction);
314 parentLayoutControl = GetParentLayoutControl(parentLayoutControl);
317 if(!mFocusGroupChangedSignalV2.Empty())
319 // Emit a focus group changed signal. The applicaton can move the focus to a new focus group
320 mFocusGroupChangedSignalV2.Emit(GetCurrentFocusActor(), forward);
326 void KeyboardFocusManager::DoActivate(Actor actor)
330 Toolkit::Control control = Toolkit::Control::DownCast(actor);
333 // Notify the control that it is activated
334 control.GetImplementation().OnActivated();
337 // Send notification for the activation of focused actor
338 if( !mFocusedActorActivatedSignalV2.Empty() )
340 mFocusedActorActivatedSignalV2.Emit(actor);
345 void KeyboardFocusManager::ClearFocus()
347 Actor actor = GetCurrentFocusActor();
350 if(mFocusIndicatorActor)
352 actor.Remove(mFocusIndicatorActor);
355 // Send notification for the change of focus actor
356 if( !mFocusChangedSignalV2.Empty() )
358 mFocusChangedSignalV2.Emit(actor, Actor());
362 mCurrentFocusActor = 0;
363 mIsFocusIndicatorEnabled = false;
366 void KeyboardFocusManager::SetFocusGroupLoop(bool enabled)
368 mFocusGroupLoopEnabled = enabled;
371 bool KeyboardFocusManager::GetFocusGroupLoop() const
373 return mFocusGroupLoopEnabled;
376 void KeyboardFocusManager::SetAsFocusGroup(Actor actor, bool isFocusGroup)
380 // Create focus group property if not already created.
381 Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(IS_FOCUS_GROUP_PROPERTY_NAME);
382 if(propertyIsFocusGroup == Property::INVALID_INDEX)
384 propertyIsFocusGroup = actor.RegisterProperty(IS_FOCUS_GROUP_PROPERTY_NAME, isFocusGroup);
388 actor.SetProperty(propertyIsFocusGroup, isFocusGroup);
393 bool KeyboardFocusManager::IsFocusGroup(Actor actor) const
395 // Check whether the actor is a focus group
396 bool isFocusGroup = false;
400 Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(IS_FOCUS_GROUP_PROPERTY_NAME);
401 if(propertyIsFocusGroup != Property::INVALID_INDEX)
403 isFocusGroup = actor.GetProperty<bool>(propertyIsFocusGroup);
410 Actor KeyboardFocusManager::GetFocusGroup(Actor actor)
412 // Go through the actor's hierarchy to check which focus group the actor belongs to
413 while (actor && !IsFocusGroup(actor))
415 actor = actor.GetParent();
421 void KeyboardFocusManager::SetFocusIndicatorActor(Actor indicator)
423 if(mFocusIndicatorActor != indicator)
425 Actor currentFocusActor = GetCurrentFocusActor();
426 if(currentFocusActor)
428 // The new focus indicator should be added to the current focused actor immediately
429 if(mFocusIndicatorActor)
431 currentFocusActor.Remove(mFocusIndicatorActor);
436 currentFocusActor.Add(indicator);
440 mFocusIndicatorActor = indicator;
444 Actor KeyboardFocusManager::GetFocusIndicatorActor()
446 return mFocusIndicatorActor;
449 void KeyboardFocusManager::CreateDefaultFocusIndicatorActor()
451 // Create a focus indicator actor shared by all the keyboard focusable actors
452 Image borderImage = Image::New(FOCUS_BORDER_IMAGE_PATH);
454 ImageActor focusIndicator = ImageActor::New(borderImage);
455 focusIndicator.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
456 focusIndicator.SetStyle( ImageActor::STYLE_NINE_PATCH );
457 focusIndicator.SetNinePatchBorder(FOCUS_BORDER_IMAGE_BORDER);
458 focusIndicator.SetPosition(Vector3(0.0f, 0.0f, 1.0f));
460 // Apply size constraint to the focus indicator
461 Constraint constraint = Constraint::New<Vector3>(Actor::SIZE,
462 ParentSource(Actor::SIZE),
463 EqualToConstraint());
464 focusIndicator.ApplyConstraint(constraint);
466 SetFocusIndicatorActor(focusIndicator);
469 void KeyboardFocusManager::OnPhysicalKeyboardStatusChanged(PhysicalKeyboard keyboard)
471 mIsKeyboardFocusEnabled = keyboard.IsAttached();
473 if(mIsKeyboardFocusEnabled)
475 // Show indicator when keyboard focus turned on if there is focused actor.
476 Actor actor = GetCurrentFocusActor();
479 if(mFocusIndicatorActor)
481 actor.Add(mFocusIndicatorActor);
484 mIsFocusIndicatorEnabled = true;
488 // Hide indicator when keyboard focus turned off
489 Actor actor = GetCurrentFocusActor();
492 actor.Remove(mFocusIndicatorActor);
494 mIsFocusIndicatorEnabled = false;
498 void KeyboardFocusManager::OnKeyEvent(const KeyEvent& event)
500 if(!mIsKeyboardFocusEnabled)
505 AccessibilityManager accessibilityManager = AccessibilityManager::Get();
506 bool isAccessibilityEnabled = accessibilityManager.IsEnabled();
508 Toolkit::FocusManager accessibilityFocusManager = Toolkit::FocusManager::Get();
510 std::string keyName = event.keyPressedName;
512 bool isFocusStartableKey = false;
514 if(event.state == KeyEvent::Down)
516 if (keyName == "Left")
518 if(!isAccessibilityEnabled)
520 if(!mIsFocusIndicatorEnabled)
522 // Show focus indicator
523 mIsFocusIndicatorEnabled = true;
527 // Move the focus towards left
528 MoveFocus(Toolkit::Control::Left);
531 isFocusStartableKey = true;
535 // Move the accessibility focus backward
536 accessibilityFocusManager.MoveFocusBackward();
539 else if (keyName == "Right")
541 if(!isAccessibilityEnabled)
543 if(!mIsFocusIndicatorEnabled)
545 // Show focus indicator
546 mIsFocusIndicatorEnabled = true;
550 // Move the focus towards right
551 MoveFocus(Toolkit::Control::Right);
554 isFocusStartableKey = true;
558 // Move the accessibility focus forward
559 accessibilityFocusManager.MoveFocusForward();
562 isFocusStartableKey = true;
564 else if (keyName == "Up" && !isAccessibilityEnabled)
566 if(!mIsFocusIndicatorEnabled)
568 // Show focus indicator
569 mIsFocusIndicatorEnabled = true;
573 // Move the focus towards up
574 MoveFocus(Toolkit::Control::Up);
577 isFocusStartableKey = true;
579 else if (keyName == "Down" && !isAccessibilityEnabled)
581 if(!mIsFocusIndicatorEnabled)
583 // Show focus indicator
584 mIsFocusIndicatorEnabled = true;
588 // Move the focus towards down
589 MoveFocus(Toolkit::Control::Down);
592 isFocusStartableKey = true;
594 else if (keyName == "Tab" && !isAccessibilityEnabled)
596 if(!mIsFocusIndicatorEnabled)
598 // Show focus indicator
599 mIsFocusIndicatorEnabled = true;
603 // "Tab" key changes the focus group in the forward direction and
604 // "Shift-Tab" key changes it in the backward direction.
605 DoMoveFocusToNextFocusGroup(!event.IsShiftModifier());
608 isFocusStartableKey = true;
610 else if (keyName == "space" && !isAccessibilityEnabled)
612 if(!mIsFocusIndicatorEnabled)
614 // Show focus indicator
615 mIsFocusIndicatorEnabled = true;
618 isFocusStartableKey = true;
620 else if (keyName == "" && !isAccessibilityEnabled)
622 // Check the fake key event for evas-plugin case
623 if(!mIsFocusIndicatorEnabled)
625 // Show focus indicator
626 mIsFocusIndicatorEnabled = true;
629 isFocusStartableKey = true;
631 else if (keyName == "Backspace" && !isAccessibilityEnabled)
633 // Emit signal to go back to the previous view???
636 else if(event.state == KeyEvent::Up)
638 if (keyName == "Return")
640 if(!mIsFocusIndicatorEnabled && !isAccessibilityEnabled)
642 // Show focus indicator
643 mIsFocusIndicatorEnabled = true;
647 // Activate the focused actor
649 if(!isAccessibilityEnabled)
651 actor = GetCurrentFocusActor();
655 actor = accessibilityFocusManager.GetCurrentFocusActor();
664 isFocusStartableKey = true;
668 if(isFocusStartableKey && mIsFocusIndicatorEnabled && !isAccessibilityEnabled)
670 Actor actor = GetCurrentFocusActor();
673 // No actor is focused but keyboard focus is activated by the key press
674 // Let's try to move the initial focus
675 MoveFocus(Toolkit::Control::Right);
677 else if(mFocusIndicatorActor)
679 // Make sure the focused actor is highlighted
680 actor.Add(mFocusIndicatorActor);
685 void KeyboardFocusManager::OnTouched(const TouchEvent& touchEvent)
687 // Clear the focus when user touch the screen
691 Toolkit::KeyboardFocusManager::PreFocusChangeSignalV2& KeyboardFocusManager::PreFocusChangeSignal()
693 return mPreFocusChangeSignalV2;
696 Toolkit::KeyboardFocusManager::FocusChangedSignalV2& KeyboardFocusManager::FocusChangedSignal()
698 return mFocusChangedSignalV2;
701 Toolkit::KeyboardFocusManager::FocusGroupChangedSignalV2& KeyboardFocusManager::FocusGroupChangedSignal()
703 return mFocusGroupChangedSignalV2;
706 Toolkit::KeyboardFocusManager::FocusedActorActivatedSignalV2& KeyboardFocusManager::FocusedActorActivatedSignal()
708 return mFocusedActorActivatedSignalV2;
711 bool KeyboardFocusManager::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
713 Dali::BaseHandle handle( object );
715 bool connected( true );
716 KeyboardFocusManager* manager = dynamic_cast<KeyboardFocusManager*>(object);
718 if( Dali::Toolkit::KeyboardFocusManager::SIGNAL_PRE_FOCUS_CHANGE == signalName )
720 manager->PreFocusChangeSignal().Connect( tracker, functor );
722 if( Dali::Toolkit::KeyboardFocusManager::SIGNAL_FOCUS_CHANGED == signalName )
724 manager->FocusChangedSignal().Connect( tracker, functor );
726 if( Dali::Toolkit::KeyboardFocusManager::SIGNAL_FOCUS_GROUP_CHANGED == signalName )
728 manager->FocusGroupChangedSignal().Connect( tracker, functor );
730 else if( Dali::Toolkit::KeyboardFocusManager::SIGNAL_FOCUSED_ACTOR_ACTIVATED== signalName )
732 manager->FocusedActorActivatedSignal().Connect( tracker, functor );
736 // signalName does not match any signal
743 } // namespace Internal
745 } // namespace Toolkit