/*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <dali-toolkit/public-api/controls/image-view/image-view.h>
#include <dali-toolkit/public-api/accessibility-manager/accessibility-manager.h>
#include <dali-toolkit/devel-api/focus-manager/keyinput-focus-manager.h>
+#include <dali-toolkit/devel-api/controls/control-devel.h>
namespace Dali
{
DALI_TYPE_REGISTRATION_END()
+const unsigned int MAX_HISTORY_AMOUNT = 30; ///< Max length of focus history stack
+
} // unnamed namespace
Toolkit::KeyboardFocusManager KeyboardFocusManager::Get()
}
KeyboardFocusManager::KeyboardFocusManager()
-: mCurrentFocusActor(0),
+: mPreFocusChangeSignal(),
+ mFocusChangedSignal(),
+ mFocusGroupChangedSignal(),
+ mFocusedActorEnterKeySignal(),
+ mCurrentFocusActor( 0 ),
mFocusIndicatorActor(),
- mFocusGroupLoopEnabled(false),
- mIsKeyboardFocusEnabled(false),
- mIsFocusIndicatorEnabled(false),
- mIsWaitingKeyboardFocusChangeCommit(false),
- mSlotDelegate(this)
-{
- OnPhysicalKeyboardStatusChanged(PhysicalKeyboard::Get());
-
+ mFocusGroupLoopEnabled( false ),
+ mIsFocusIndicatorEnabled( true ),
+ mIsWaitingKeyboardFocusChangeCommit( false ),
+ mFocusHistory(),
+ mSlotDelegate( this ),
+ mCustomAlgorithmInterface(NULL)
+{
+ // TODO: Get FocusIndicatorEnable constant from stylesheet to set mIsFocusIndicatorEnabled.
Toolkit::KeyInputFocusManager::Get().UnhandledKeyEventSignal().Connect(mSlotDelegate, &KeyboardFocusManager::OnKeyEvent);
Stage::GetCurrent().TouchSignal().Connect( mSlotDelegate, &KeyboardFocusManager::OnTouch );
- PhysicalKeyboard::Get().StatusChangedSignal().Connect(mSlotDelegate, &KeyboardFocusManager::OnPhysicalKeyboardStatusChanged);
}
KeyboardFocusManager::~KeyboardFocusManager()
{
DALI_ASSERT_DEBUG( !mIsWaitingKeyboardFocusChangeCommit && "Calling this function in the PreFocusChangeSignal callback?" );
- if( actor )
- {
- return DoSetCurrentFocusActor( actor.GetId() );
- }
-
- return false;
+ return DoSetCurrentFocusActor( actor );
}
-bool KeyboardFocusManager::DoSetCurrentFocusActor( const unsigned int actorID )
+bool KeyboardFocusManager::DoSetCurrentFocusActor( Actor actor )
{
- Actor rootActor = Stage::GetCurrent().GetRootLayer();
- Actor actor = rootActor.FindChildById( actorID );
bool success = false;
+ Actor currentFocusedActor = GetCurrentFocusActor();
+
+ // If developer set focus on same actor, doing nothing
+ if( actor == currentFocusedActor )
+ {
+ if( !actor )
+ {
+ return false;
+ }
+ return true;
+ }
+
// Check whether the actor is in the stage and is keyboard focusable.
if( actor && actor.IsKeyboardFocusable() )
{
- mIsFocusIndicatorEnabled = true;
- // Draw the focus indicator upon the focused actor
- actor.Add( GetFocusIndicatorActor() );
+ if( mIsFocusIndicatorEnabled )
+ {
+ actor.Add( GetFocusIndicatorActor() );
+ }
// Send notification for the change of focus actor
if( !mFocusChangedSignal.Empty() )
{
- mFocusChangedSignal.Emit(GetCurrentFocusActor(), actor);
+ mFocusChangedSignal.Emit(currentFocusedActor, actor);
+ }
+
+ Toolkit::Control currentlyFocusedControl = Toolkit::Control::DownCast(currentFocusedActor);
+ if( currentlyFocusedControl )
+ {
+ // Do we need it to remember if it was previously DISABLED?
+ currentlyFocusedControl.SetProperty(DevelControl::Property::STATE, DevelControl::NORMAL );
+ currentlyFocusedControl.ClearKeyInputFocus();
}
DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] Focus Changed\n", __FUNCTION__, __LINE__);
// Save the current focused actor
- mCurrentFocusActor = actorID;
+ mCurrentFocusActor = actor.GetId();
+
+ Toolkit::Control newlyFocusedControl = Toolkit::Control::DownCast(actor);
+ if( newlyFocusedControl )
+ {
+ newlyFocusedControl.SetProperty(DevelControl::Property::STATE, DevelControl::FOCUSED );
+ newlyFocusedControl.SetKeyInputFocus();
+ }
+
+ // Push Current Focused Actor to FocusHistory
+ mFocusHistory.PushBack( &actor.GetBaseObject() );
+
+ // Delete first element before add new element when Stack is full.
+ if( mFocusHistory.Count() > MAX_HISTORY_AMOUNT )
+ {
+ FocusStackIterator beginPos = mFocusHistory.Begin();
+ mFocusHistory.Erase( beginPos );
+ }
DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] SUCCEED\n", __FUNCTION__, __LINE__);
success = true;
return GetFocusGroup(GetCurrentFocusActor());
}
+void KeyboardFocusManager::MoveFocusBackward()
+{
+ // Find Pre Focused Actor when the list size is more than 1
+ if( mFocusHistory.Count() > 1 )
+ {
+ // Delete current focused actor in history
+ FocusStackIterator endPos = mFocusHistory.End();
+ endPos = mFocusHistory.Erase( --endPos );
+
+ // If pre-focused actors are not on stage, remove them in stack
+ while( !Dali::Actor::DownCast(BaseHandle(mFocusHistory[ mFocusHistory.Count() - 1 ])).OnStage() )
+ {
+ endPos = mFocusHistory.Erase( --endPos );
+ }
+
+ // Get pre focused actor
+ BaseObject* object = mFocusHistory[ mFocusHistory.Count() - 1 ];
+ BaseHandle handle( object );
+ Actor preFocusedActor = Dali::Actor::DownCast( handle );
+
+ // Delete pre focused actor in history because it will pushed again by SetCurrentFocusActor()
+ mFocusHistory.Erase( --endPos );
+
+ SetCurrentFocusActor( preFocusedActor );
+ }
+}
+
bool KeyboardFocusManager::IsLayoutControl(Actor actor) const
{
Toolkit::Control control = Toolkit::Control::DownCast(actor);
bool succeed = false;
// Go through the actor's hierarchy until we find a layout control that knows how to move the focus
- Toolkit::Control parentLayoutControl = GetParentLayoutControl(currentFocusActor);
- while(parentLayoutControl && !succeed)
+ Toolkit::Control parentLayoutControl = GetParentLayoutControl( currentFocusActor );
+ while( parentLayoutControl && !succeed )
{
- succeed = DoMoveFocusWithinLayoutControl(parentLayoutControl, currentFocusActor, direction);
- parentLayoutControl = GetParentLayoutControl(parentLayoutControl);
+ succeed = DoMoveFocusWithinLayoutControl( parentLayoutControl, currentFocusActor, direction );
+ parentLayoutControl = GetParentLayoutControl( parentLayoutControl );
}
- if(!succeed && !mPreFocusChangeSignal.Empty())
+ if( !succeed )
{
- // Don't know how to move the focus further. The application needs to tell us which actor to move the focus to
- mIsWaitingKeyboardFocusChangeCommit = true;
- Actor nextFocusableActor = mPreFocusChangeSignal.Emit(currentFocusActor, Actor(), direction);
- mIsWaitingKeyboardFocusChangeCommit = false;
+ Actor nextFocusableActor;
+
+ Toolkit::Control currentFocusControl = Toolkit::Control::DownCast(currentFocusActor);
+
+ // If the current focused actor is a control, then find the next focusable actor via the focusable properties.
+ if( currentFocusControl )
+ {
+ int actorId = -1;
+ Property::Index index = Property::INVALID_INDEX;
+ Property::Value value;
+
+ // Find property index based upon focus direction
+ switch ( direction )
+ {
+ case Toolkit::Control::KeyboardFocus::LEFT:
+ {
+ index = Toolkit::DevelControl::Property::LEFT_FOCUSABLE_ACTOR_ID;
+ break;
+ }
+ case Toolkit::Control::KeyboardFocus::RIGHT:
+ {
+ index = Toolkit::DevelControl::Property::RIGHT_FOCUSABLE_ACTOR_ID;
+ break;
+ }
+ case Toolkit::Control::KeyboardFocus::UP:
+ {
+ index = Toolkit::DevelControl::Property::UP_FOCUSABLE_ACTOR_ID;
+ break;
+ }
+ case Toolkit::Control::KeyboardFocus::DOWN:
+ {
+ index = Toolkit::DevelControl::Property::DOWN_FOCUSABLE_ACTOR_ID;
+ break;
+ }
+ default:
+ break;
+ }
+
+ // If the focusable property is set then determine next focusable actor
+ if( index != Property::INVALID_INDEX)
+ {
+ value = currentFocusActor.GetProperty( index );
+ actorId = value.Get<int>();
+
+ // If actor's id is valid then find actor form actor's id. The actor should be on the stage.
+ if( actorId != -1 )
+ {
+ if( currentFocusActor.GetParent() )
+ {
+ nextFocusableActor = currentFocusActor.GetParent().FindChildById( actorId );
+ }
+
+ if( !nextFocusableActor )
+ {
+ nextFocusableActor = Stage::GetCurrent().GetRootLayer().FindChildById( actorId );
+ }
+ }
+ }
+ }
+
+ if( !nextFocusableActor )
+ {
+ // If the implementation of CustomAlgorithmInterface is provided then the PreFocusChangeSignal is no longer emitted.
+ if( mCustomAlgorithmInterface )
+ {
+ mIsWaitingKeyboardFocusChangeCommit = true;
+ nextFocusableActor = mCustomAlgorithmInterface->GetNextFocusableActor( currentFocusActor, Actor(), direction );
+ mIsWaitingKeyboardFocusChangeCommit = false;
+ }
+ else if( !mPreFocusChangeSignal.Empty() )
+ {
+ // Don't know how to move the focus further. The application needs to tell us which actor to move the focus to
+ mIsWaitingKeyboardFocusChangeCommit = true;
+ nextFocusableActor = mPreFocusChangeSignal.Emit( currentFocusActor, Actor(), direction );
+ mIsWaitingKeyboardFocusChangeCommit = false;
+ }
+ }
- if ( nextFocusableActor && nextFocusableActor.IsKeyboardFocusable() )
+ if( nextFocusableActor && nextFocusableActor.IsKeyboardFocusable() )
{
// Whether the next focusable actor is a layout control
- if(IsLayoutControl(nextFocusableActor))
+ if( IsLayoutControl( nextFocusableActor ) )
{
// If so, move the focus inside it.
- Toolkit::Control layoutControl = Toolkit::Control::DownCast(nextFocusableActor);
- succeed = DoMoveFocusWithinLayoutControl(layoutControl, currentFocusActor, direction);
+ Toolkit::Control layoutControl = Toolkit::Control::DownCast( nextFocusableActor) ;
+ succeed = DoMoveFocusWithinLayoutControl( layoutControl, currentFocusActor, direction );
}
else
{
// Otherwise, just set focus to the next focusable actor
- succeed = SetCurrentFocusActor(nextFocusableActor);
+ succeed = SetCurrentFocusActor( nextFocusableActor );
}
}
}
void KeyboardFocusManager::ClearFocus()
{
Actor actor = GetCurrentFocusActor();
- if(actor)
+ if( actor )
{
- if(mFocusIndicatorActor)
+ if( mFocusIndicatorActor )
{
- actor.Remove(mFocusIndicatorActor);
+ actor.Remove( mFocusIndicatorActor );
}
// Send notification for the change of focus actor
if( !mFocusChangedSignal.Empty() )
{
- mFocusChangedSignal.Emit(actor, Actor());
+ mFocusChangedSignal.Emit( actor, Actor() );
+ }
+
+ Toolkit::Control currentlyFocusedControl = Toolkit::Control::DownCast( actor );
+ if( currentlyFocusedControl )
+ {
+ currentlyFocusedControl.SetProperty( DevelControl::Property::STATE, DevelControl::NORMAL );
+ currentlyFocusedControl.ClearKeyInputFocus();
}
}
{
// Create the default if it hasn't been set and one that's shared by all the keyboard focusable actors
mFocusIndicatorActor = Toolkit::ImageView::New( FOCUS_BORDER_IMAGE_PATH );
- mFocusIndicatorActor.SetParentOrigin( ParentOrigin::CENTER );
// Apply size constraint to the focus indicator
mFocusIndicatorActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
}
- return mFocusIndicatorActor;
-}
-
-void KeyboardFocusManager::OnPhysicalKeyboardStatusChanged(PhysicalKeyboard keyboard)
-{
- mIsKeyboardFocusEnabled = keyboard.IsAttached();
+ mFocusIndicatorActor.SetParentOrigin( ParentOrigin::CENTER );
+ mFocusIndicatorActor.SetAnchorPoint( AnchorPoint::CENTER );
+ mFocusIndicatorActor.SetPosition(0.0f, 0.0f);
- if(mIsKeyboardFocusEnabled)
- {
- // Show indicator when keyboard focus turned on if there is focused actor.
- Actor actor = GetCurrentFocusActor();
- if(actor)
- {
- actor.Add( GetFocusIndicatorActor() );
- }
- mIsFocusIndicatorEnabled = true;
- }
- else
- {
- // Hide indicator when keyboard focus turned off
- Actor actor = GetCurrentFocusActor();
- if( actor && mFocusIndicatorActor )
- {
- actor.Remove( mFocusIndicatorActor );
- }
- mIsFocusIndicatorEnabled = false;
- }
+ return mFocusIndicatorActor;
}
void KeyboardFocusManager::OnKeyEvent(const KeyEvent& event)
{
- if(!mIsKeyboardFocusEnabled)
- {
- return;
- }
-
AccessibilityAdaptor accessibilityAdaptor = AccessibilityAdaptor::Get();
bool isAccessibilityEnabled = accessibilityAdaptor.IsEnabled();
isFocusStartableKey = true;
}
+ else if (keyName == "Prior" && !isAccessibilityEnabled)
+ {
+ if(!mIsFocusIndicatorEnabled)
+ {
+ // Show focus indicator
+ mIsFocusIndicatorEnabled = true;
+ }
+ else
+ {
+ // Move the focus towards the previous page
+ MoveFocus(Toolkit::Control::KeyboardFocus::PAGE_UP);
+ }
+
+ isFocusStartableKey = true;
+ }
+ else if (keyName == "Next" && !isAccessibilityEnabled)
+ {
+ if(!mIsFocusIndicatorEnabled)
+ {
+ // Show focus indicator
+ mIsFocusIndicatorEnabled = true;
+ }
+ else
+ {
+ // Move the focus towards the next page
+ MoveFocus(Toolkit::Control::KeyboardFocus::PAGE_DOWN);
+ }
+
+ isFocusStartableKey = true;
+ }
else if (keyName == "Tab" && !isAccessibilityEnabled)
{
if(!mIsFocusIndicatorEnabled)
{
// Emit signal to go back to the previous view???
}
+ else if (keyName == "Escape" && !isAccessibilityEnabled)
+ {
+ }
}
else if(event.state == KeyEvent::Up)
{
return connected;
}
+void KeyboardFocusManager::SetCustomAlgorithm(CustomAlgorithmInterface& interface)
+{
+ mCustomAlgorithmInterface = &interface;
+}
+
} // namespace Internal
} // namespace Toolkit