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 ControlImpl.
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 actor.Remove(mFocusIndicatorActor);
347 // Send notification for the change of focus actor
348 if( !mFocusChangedSignalV2.Empty() )
350 mFocusChangedSignalV2.Emit(actor, Actor());
354 mCurrentFocusActor = 0;
355 mIsFocusIndicatorEnabled = false;
358 void KeyboardFocusManager::SetFocusGroupLoop(bool enabled)
360 mFocusGroupLoopEnabled = enabled;
363 bool KeyboardFocusManager::GetFocusGroupLoop() const
365 return mFocusGroupLoopEnabled;
368 void KeyboardFocusManager::SetAsFocusGroup(Actor actor, bool isFocusGroup)
372 // Create focus group property if not already created.
373 Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(IS_FOCUS_GROUP_PROPERTY_NAME);
374 if(propertyIsFocusGroup == Property::INVALID_INDEX)
376 propertyIsFocusGroup = actor.RegisterProperty(IS_FOCUS_GROUP_PROPERTY_NAME, isFocusGroup);
380 actor.SetProperty(propertyIsFocusGroup, isFocusGroup);
385 bool KeyboardFocusManager::IsFocusGroup(Actor actor) const
387 // Check whether the actor is a focus group
388 bool isFocusGroup = false;
392 Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(IS_FOCUS_GROUP_PROPERTY_NAME);
393 if(propertyIsFocusGroup != Property::INVALID_INDEX)
395 isFocusGroup = actor.GetProperty<bool>(propertyIsFocusGroup);
402 Actor KeyboardFocusManager::GetFocusGroup(Actor actor)
404 // Go through the actor's hierarchy to check which focus group the actor belongs to
405 while (actor && !IsFocusGroup(actor))
407 actor = actor.GetParent();
413 void KeyboardFocusManager::SetFocusIndicatorActor(Actor indicator)
415 mFocusIndicatorActor = indicator;
418 Actor KeyboardFocusManager::GetFocusIndicatorActor()
420 return mFocusIndicatorActor;
423 void KeyboardFocusManager::CreateDefaultFocusIndicatorActor()
425 // Create a focus indicator actor shared by all the keyboard focusable actors
426 Image borderImage = Image::New(FOCUS_BORDER_IMAGE_PATH);
428 ImageActor focusIndicator = ImageActor::New(borderImage);
429 focusIndicator.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
430 focusIndicator.SetStyle( ImageActor::STYLE_NINE_PATCH );
431 focusIndicator.SetNinePatchBorder(FOCUS_BORDER_IMAGE_BORDER);
432 focusIndicator.SetPosition(Vector3(0.0f, 0.0f, 1.0f));
434 // Apply size constraint to the focus indicator
435 Constraint constraint = Constraint::New<Vector3>(Actor::SIZE,
436 ParentSource(Actor::SIZE),
437 EqualToConstraint());
438 focusIndicator.ApplyConstraint(constraint);
440 SetFocusIndicatorActor(focusIndicator);
443 void KeyboardFocusManager::OnPhysicalKeyboardStatusChanged(PhysicalKeyboard keyboard)
445 mIsKeyboardFocusEnabled = keyboard.IsAttached();
447 if(mIsKeyboardFocusEnabled)
449 // Show indicator when keyboard focus turned on if there is focused actor.
450 Actor actor = GetCurrentFocusActor();
453 if(mFocusIndicatorActor)
455 actor.Add(mFocusIndicatorActor);
458 mIsFocusIndicatorEnabled = true;
462 // Hide indicator when keyboard focus turned off
463 Actor actor = GetCurrentFocusActor();
466 actor.Remove(mFocusIndicatorActor);
468 mIsFocusIndicatorEnabled = false;
472 void KeyboardFocusManager::OnKeyEvent(const KeyEvent& event)
474 if(!mIsKeyboardFocusEnabled)
479 AccessibilityManager accessibilityManager = AccessibilityManager::Get();
480 bool isAccessibilityEnabled = accessibilityManager.IsEnabled();
482 Toolkit::FocusManager accessibilityFocusManager = Toolkit::FocusManager::Get();
484 std::string keyName = event.keyPressedName;
486 bool isFocusStartableKey = false;
488 if(event.state == KeyEvent::Down)
490 if (keyName == "Left")
492 if(!isAccessibilityEnabled)
494 if(!mIsFocusIndicatorEnabled)
496 // Show focus indicator
497 mIsFocusIndicatorEnabled = true;
501 // Move the focus towards left
502 MoveFocus(Toolkit::Control::Left);
505 isFocusStartableKey = true;
509 // Move the accessibility focus backward
510 accessibilityFocusManager.MoveFocusBackward();
513 else if (keyName == "Right")
515 if(!isAccessibilityEnabled)
517 if(!mIsFocusIndicatorEnabled)
519 // Show focus indicator
520 mIsFocusIndicatorEnabled = true;
524 // Move the focus towards right
525 MoveFocus(Toolkit::Control::Right);
528 isFocusStartableKey = true;
532 // Move the accessibility focus forward
533 accessibilityFocusManager.MoveFocusForward();
536 isFocusStartableKey = true;
538 else if (keyName == "Up" && !isAccessibilityEnabled)
540 if(!mIsFocusIndicatorEnabled)
542 // Show focus indicator
543 mIsFocusIndicatorEnabled = true;
547 // Move the focus towards up
548 MoveFocus(Toolkit::Control::Up);
551 isFocusStartableKey = true;
553 else if (keyName == "Down" && !isAccessibilityEnabled)
555 if(!mIsFocusIndicatorEnabled)
557 // Show focus indicator
558 mIsFocusIndicatorEnabled = true;
562 // Move the focus towards down
563 MoveFocus(Toolkit::Control::Down);
566 isFocusStartableKey = true;
568 else if (keyName == "Tab" && !isAccessibilityEnabled)
570 if(!mIsFocusIndicatorEnabled)
572 // Show focus indicator
573 mIsFocusIndicatorEnabled = true;
577 // "Tab" key changes the focus group in the forward direction and
578 // "Shift-Tab" key changes it in the backward direction.
579 DoMoveFocusToNextFocusGroup(!event.IsShiftModifier());
582 isFocusStartableKey = true;
584 else if (keyName == "space" && !isAccessibilityEnabled)
586 if(!mIsFocusIndicatorEnabled)
588 // Show focus indicator
589 mIsFocusIndicatorEnabled = true;
592 isFocusStartableKey = true;
594 else if (keyName == "" && !isAccessibilityEnabled)
596 // Check the fake key event for evas-plugin case
597 if(!mIsFocusIndicatorEnabled)
599 // Show focus indicator
600 mIsFocusIndicatorEnabled = true;
603 isFocusStartableKey = true;
605 else if (keyName == "Backspace" && !isAccessibilityEnabled)
607 // Emit signal to go back to the previous view???
610 else if(event.state == KeyEvent::Up)
612 if (keyName == "Return")
614 if(!mIsFocusIndicatorEnabled && !isAccessibilityEnabled)
616 // Show focus indicator
617 mIsFocusIndicatorEnabled = true;
621 // Activate the focused actor
623 if(!isAccessibilityEnabled)
625 actor = GetCurrentFocusActor();
629 actor = accessibilityFocusManager.GetCurrentFocusActor();
638 isFocusStartableKey = true;
642 if(isFocusStartableKey && mIsFocusIndicatorEnabled && !isAccessibilityEnabled)
644 Actor actor = GetCurrentFocusActor();
647 // No actor is focused but keyboard focus is activated by the key press
648 // Let's try to move the initial focus
649 MoveFocus(Toolkit::Control::Right);
651 else if(mFocusIndicatorActor)
653 // Make sure the focused actor is highlighted
654 actor.Add(mFocusIndicatorActor);
659 void KeyboardFocusManager::OnTouched(const TouchEvent& touchEvent)
661 // Clear the focus when user touch the screen
665 Toolkit::KeyboardFocusManager::PreFocusChangeSignalV2& KeyboardFocusManager::PreFocusChangeSignal()
667 return mPreFocusChangeSignalV2;
670 Toolkit::KeyboardFocusManager::FocusChangedSignalV2& KeyboardFocusManager::FocusChangedSignal()
672 return mFocusChangedSignalV2;
675 Toolkit::KeyboardFocusManager::FocusGroupChangedSignalV2& KeyboardFocusManager::FocusGroupChangedSignal()
677 return mFocusGroupChangedSignalV2;
680 Toolkit::KeyboardFocusManager::FocusedActorActivatedSignalV2& KeyboardFocusManager::FocusedActorActivatedSignal()
682 return mFocusedActorActivatedSignalV2;
685 bool KeyboardFocusManager::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
687 Dali::BaseHandle handle( object );
689 bool connected( true );
690 KeyboardFocusManager* manager = dynamic_cast<KeyboardFocusManager*>(object);
692 if( Dali::Toolkit::KeyboardFocusManager::SIGNAL_PRE_FOCUS_CHANGE == signalName )
694 manager->PreFocusChangeSignal().Connect( tracker, functor );
696 if( Dali::Toolkit::KeyboardFocusManager::SIGNAL_FOCUS_CHANGED == signalName )
698 manager->FocusChangedSignal().Connect( tracker, functor );
700 if( Dali::Toolkit::KeyboardFocusManager::SIGNAL_FOCUS_GROUP_CHANGED == signalName )
702 manager->FocusGroupChangedSignal().Connect( tracker, functor );
704 else if( Dali::Toolkit::KeyboardFocusManager::SIGNAL_FOCUSED_ACTOR_ACTIVATED== signalName )
706 manager->FocusedActorActivatedSignal().Connect( tracker, functor );
710 // signalName does not match any signal
717 } // namespace Internal
719 } // namespace Toolkit