FocusView可配置主动获取焦点

Signed-off-by: 胡清云 <huqingyun1@huawei.com>
Change-Id: I5699dfeefcf84e7066289adcbf85419e5b44d0cc
This commit is contained in:
胡清云 2024-11-12 22:41:24 +08:00 committed by wangxiuxiu96
parent 25f02007ce
commit 0cfc1235e6
11 changed files with 287 additions and 24 deletions

View File

@ -849,6 +849,12 @@ class FocusController {
return result;
}
}
setAutoFocusTransfer(value) {
__JSScopeUtil__.syncInstanceId(this.instanceId_);
this.ohos_focusController.setAutoFocusTransfer(value);
__JSScopeUtil__.restoreInstanceId();
}
}
class CursorController {

View File

@ -157,6 +157,15 @@ bool FrontendDelegate::Activate(bool isActive, bool autoInactive)
return pipeline->SetIsFocusActive(isActive, NG::FocusActiveReason::USE_API, autoInactive);
}
void FrontendDelegate::SetAutoFocusTransfer(bool isAutoFocusTransfer)
{
auto pipeline = NG::PipelineContext::GetCurrentContext();
CHECK_NULL_VOID(pipeline);
auto focusManager = pipeline->GetOrCreateFocusManager();
CHECK_NULL_VOID(focusManager);
focusManager->SetIsAutoFocusTransfer(isAutoFocusTransfer);
}
template<typename T>
bool FrontendDelegate::GetResourceData(const std::string& fileUri, T& content, std::string& ami)
{

View File

@ -136,6 +136,8 @@ public:
virtual bool Activate(bool isActive, bool autoInactive = true);
virtual void SetAutoFocusTransfer(bool autoFocusTransfer);
// restore
virtual std::pair<RouterRecoverRecord, UIContentErrorCode> RestoreRouterStack(
const std::string& contentInfo, ContentInfoType type)

View File

@ -1336,7 +1336,7 @@ void FrameNode::OnDetachFromMainTree(bool recursive, PipelineContext* context)
if (focusHub) {
auto focusView = focusHub->GetFirstChildFocusView();
if (focusView) {
focusView->FocusViewClose();
focusView->FocusViewClose(true);
}
focusHub->RemoveSelf();
}

View File

@ -41,7 +41,7 @@ void FocusManager::FocusViewShow(const RefPtr<FocusView>& focusView, bool isTrig
if (lastFocusView == focusView || lastFocusView->IsChildFocusViewOf(focusView)) {
return;
}
if (!focusView->IsChildFocusViewOf(lastFocusView)) {
if (!focusView->IsChildFocusViewOf(lastFocusView) && IsAutoFocusTransfer()) {
lastFocusView->LostViewFocus();
}
}
@ -51,6 +51,7 @@ void FocusManager::FocusViewShow(const RefPtr<FocusView>& focusView, bool isTrig
focusViewStack_.remove(focusViewWeak);
}
focusViewStack_.emplace_back(focusViewWeak);
focusViewStackState_ = FocusViewStackState::SHOW;
lastFocusView_ = focusViewWeak;
// do not set LastWeakFocus to Previous node/scope in focusView when FocusViewShow trigger by FocusStep
@ -66,18 +67,44 @@ void FocusManager::FocusViewShow(const RefPtr<FocusView>& focusView, bool isTrig
}
}
bool FocusManager::RearrangeViewStack()
{
auto curFocusView = FocusView::GetCurrentFocusView();
CHECK_NULL_RETURN(curFocusView, false);
auto curFocusViewHub = curFocusView->GetFocusHub();
CHECK_NULL_RETURN(curFocusViewHub, false);
auto curFocusViewWeak = AceType::WeakClaim(AceType::RawPtr(curFocusView));
if (!curFocusViewHub->IsCurrentFocus() && focusViewStackState_ == FocusViewStackState::SHOW) {
if (std::find(focusViewStack_.begin(), focusViewStack_.end(), curFocusViewWeak) != focusViewStack_.end()) {
focusViewStack_.remove(curFocusViewWeak);
}
lastFocusView_ = focusViewStack_.back();
return true;
}
if (focusViewStackState_ == FocusViewStackState::CLOSE) {
auto ret = SetFocusViewRootScope(curFocusView);
return ret;
}
return false;
}
void FocusManager::FocusViewHide(const RefPtr<FocusView>& focusView)
{
CHECK_NULL_VOID(focusView);
focusView->LostViewFocus();
if (IsAutoFocusTransfer()) {
focusView->LostViewFocus();
}
auto lastFocusView = lastFocusView_.Upgrade();
if (lastFocusView && (lastFocusView == focusView || lastFocusView->IsChildFocusViewOf(focusView))) {
lastFocusView_ = nullptr;
}
}
void FocusManager::FocusViewClose(const RefPtr<FocusView>& focusView)
void FocusManager::FocusViewClose(const RefPtr<FocusView>& focusView, bool isDetachFromTree)
{
if (!IsAutoFocusTransfer() && !isDetachFromTree) {
return;
}
CHECK_NULL_VOID(focusView);
focusView->LostViewFocus();
focusView->SetIsViewHasShow(false);
@ -89,6 +116,7 @@ void FocusManager::FocusViewClose(const RefPtr<FocusView>& focusView)
focusHub->RemoveFocusScopeIdAndPriority();
}
iter = focusViewStack_.erase(iter);
focusViewStackState_ = FocusViewStackState::CLOSE;
} else {
++iter;
}
@ -408,6 +436,13 @@ FocusManager::FocusGuard::~FocusGuard()
}
}
bool FocusManager::SetFocusViewRootScope(const RefPtr<FocusView>& focusView)
{
auto focusViewRootScope = focusView->GetViewRootScope();
focusView->SetIsViewRootScopeFocused(true);
return focusViewRootScope->RequestFocusImmediatelyInner();
}
void FocusManager::WindowFocus(bool isFocus)
{
if (!isFocus) {
@ -422,8 +457,12 @@ void FocusManager::WindowFocus(bool isFocus)
} else if (curFocusView->GetIsViewHasFocused() && !curFocusViewHub->IsCurrentFocus()) {
TAG_LOGI(AceLogTag::ACE_FOCUS, "Request focus on current focus view: %{public}s/%{public}d",
curFocusView->GetFrameName().c_str(), curFocusView->GetFrameId());
auto lastViewFocusNode = curFocusView->GetFocusLeaf(curFocusViewHub);
lastViewFocusNode->RequestFocusImmediatelyInner();
if (!IsAutoFocusTransfer()) {
SetFocusViewRootScope(curFocusView);
} else {
auto lastViewFocusNode = curFocusView->GetFocusLeaf(curFocusViewHub);
lastViewFocusNode->RequestFocusImmediatelyInner();
}
} else {
auto container = Container::Current();
if (container && (container->IsUIExtensionWindow() || container->IsDynamicRender())) {

View File

@ -38,6 +38,12 @@ enum class FocusActiveReason : int32_t {
USE_API = 2,
};
enum class FocusViewStackState : int32_t {
IDLE = 0,
SHOW = 1,
CLOSE = 2,
};
class FocusManager : public virtual AceType {
DECLARE_ACE_TYPE(FocusManager, AceType);
@ -59,7 +65,7 @@ public:
void FocusViewShow(const RefPtr<FocusView>& focusView, bool isTriggerByStep = false);
void FocusViewHide(const RefPtr<FocusView>& focusView);
void FocusViewClose(const RefPtr<FocusView>& focusView);
void FocusViewClose(const RefPtr<FocusView>& focusView, bool isDetachFromTree = false);
void FlushFocusView();
@ -115,6 +121,25 @@ public:
return isNeedTriggerScroll_.value_or(false);
}
void SetIsAutoFocusTransfer(bool isAutoFocusTransfer)
{
isAutoFocusTransfer_ = isAutoFocusTransfer;
}
bool IsAutoFocusTransfer() const
{
return isAutoFocusTransfer_;
}
bool RearrangeViewStack();
void SetFocusViewStackState(FocusViewStackState focusViewStackState)
{
focusViewStackState_ = focusViewStackState;
}
bool SetFocusViewRootScope(const RefPtr<FocusView>& focusView);
void PaintFocusState();
bool AddFocusScope(const std::string& focusScopeId, const RefPtr<FocusHub>& scopeFocusHub);
@ -171,6 +196,9 @@ private:
std::optional<SwitchingEndReason> endReason_;
std::optional<SwitchingUpdateReason> updateReason_;
bool isAutoFocusTransfer_ = true;
FocusViewStackState focusViewStackState_ = FocusViewStackState::IDLE;
ACE_DISALLOW_COPY_AND_MOVE(FocusManager);
};
} // namespace OHOS::Ace::NG

View File

@ -59,7 +59,7 @@ void FocusView::FocusViewHide()
pipeline->RequestFrame();
}
void FocusView::FocusViewClose()
void FocusView::FocusViewClose(bool isDetachFromTree)
{
if (!isViewHasShow_) {
TAG_LOGI(AceLogTag::ACE_FOCUS, "Focus view: %{public}s/%{public}d has not been shown",
@ -73,7 +73,7 @@ void FocusView::FocusViewClose()
CHECK_NULL_VOID(pipeline);
auto focusManager = pipeline->GetFocusManager();
CHECK_NULL_VOID(focusManager);
focusManager->FocusViewClose(AceType::Claim(this));
focusManager->FocusViewClose(AceType::Claim(this), isDetachFromTree);
pipeline->RequestFrame();
}
@ -252,6 +252,28 @@ bool FocusView::HasParentFocusHub()
return focusViewHub->GetParentFocusHub() != nullptr;
}
std::pair<bool, bool> FocusView::HandleDefaultFocusNode(
const RefPtr<FocusHub>& defaultFocusNode, bool isViewRootScopeHasChildFocused)
{
std::pair<bool, bool> pair = {false, false};
if (neverShown_ && !isViewRootScopeHasChildFocused) {
if (!defaultFocusNode) {
TAG_LOGI(AceLogTag::ACE_FOCUS, "Focus view has no default focus.");
} else if (!defaultFocusNode->IsFocusableWholePath()) {
TAG_LOGI(AceLogTag::ACE_FOCUS, "Default focus node: %{public}s/%{public}d is not focusable",
defaultFocusNode->GetFrameName().c_str(), defaultFocusNode->GetFrameId());
} else {
SetIsViewRootScopeFocused(false);
auto ret = defaultFocusNode->RequestFocusImmediatelyInner();
FocusViewDidShow(defaultFocusNode);
TAG_LOGI(AceLogTag::ACE_FOCUS, "Request focus on default focus: %{public}s/%{public}d return: %{public}d.",
defaultFocusNode->GetFrameName().c_str(), defaultFocusNode->GetFrameId(), ret);
pair = {true, ret};
}
}
return pair;
}
bool FocusView::RequestDefaultFocus()
{
TAG_LOGI(AceLogTag::ACE_FOCUS, "Request focus on focusView: %{public}s/%{public}d.", GetFrameName().c_str(),
@ -267,20 +289,21 @@ bool FocusView::RequestDefaultFocus()
auto defaultFocusNode = focusViewHub->GetChildFocusNodeByType(FocusNodeType::DEFAULT);
auto isViewRootScopeHasChildFocused = viewRootScope->HasFocusedChild();
if (neverShown_ && !isViewRootScopeHasChildFocused) {
if (!defaultFocusNode) {
TAG_LOGI(AceLogTag::ACE_FOCUS, "FocusView has no default focus.");
} else if (!defaultFocusNode->IsFocusableWholePath()) {
TAG_LOGI(AceLogTag::ACE_FOCUS, "Default focus node: %{public}s/%{public}d is not focusable",
defaultFocusNode->GetFrameName().c_str(), defaultFocusNode->GetFrameId());
} else {
SetIsViewRootScopeFocused(false);
auto ret = defaultFocusNode->RequestFocusImmediatelyInner();
FocusViewDidShow(defaultFocusNode);
TAG_LOGI(AceLogTag::ACE_FOCUS, "Request focus on default focus: %{public}s/%{public}d return: %{public}d.",
defaultFocusNode->GetFrameName().c_str(), defaultFocusNode->GetFrameId(), ret);
return ret;
}
auto node = GetFrameNode();
CHECK_NULL_RETURN(node, false);
auto pipeline = node->GetContextRefPtr();
CHECK_NULL_RETURN(pipeline, false);
auto focusManager = pipeline->GetFocusManager();
CHECK_NULL_RETURN(focusManager, false);
if (!focusManager->IsAutoFocusTransfer()) {
std::pair<bool, bool> pair = HandleDefaultFocusNode(defaultFocusNode, isViewRootScopeHasChildFocused);
CHECK_NULL_RETURN(!pair.second, false);
return focusManager->RearrangeViewStack();
}
std::pair<bool, bool> pair = HandleDefaultFocusNode(defaultFocusNode, isViewRootScopeHasChildFocused);
if (pair.first) {
return pair.second;
}
if (isViewRootScopeFocused_ && viewRootScope) {
SetIsViewRootScopeFocused(true);

View File

@ -50,7 +50,7 @@ public:
void FocusViewShow(bool isTriggerByStep = false);
void FocusViewHide();
void FocusViewClose();
void FocusViewClose(bool isDetachFromTree = false);
virtual void LostViewFocus();
@ -101,6 +101,9 @@ public:
return isViewHasShow_;
}
std::pair<bool, bool> HandleDefaultFocusNode(
const RefPtr<FocusHub>& defaultFocusNode, bool isViewRootScopeHasChildFocused);
void FocusViewDidShow(const RefPtr<FocusHub>& focusHub);
private:

View File

@ -914,6 +914,7 @@ void PipelineContext::FlushFocusView()
if (lastFocusView && (!lastFocusView->IsRootScopeCurrentFocus() || !lastFocusView->GetIsViewHasFocused()) &&
lastFocusViewHub->IsFocusableNode()) {
lastFocusView->RequestDefaultFocus();
focusManager_->SetFocusViewStackState(FocusViewStackState::IDLE);
}
}

View File

@ -81,6 +81,23 @@ static napi_value JSRequestFocus(napi_env env, napi_callback_info info)
return obj;
}
static napi_value JsSetAutoFocusTransfer(napi_env env, napi_callback_info info)
{
auto delegate = EngineHelper::GetCurrentDelegateSafely();
if (!delegate) {
return nullptr;
}
napi_value argv[1] = { 0 };
napi_valuetype valueType = napi_undefined;
if (!GetSingleParam(env, info, argv, valueType) || (valueType != napi_boolean)) {
return nullptr;
}
bool isAutoFocusTransfer = true;
napi_get_value_bool(env, argv[0], &isAutoFocusTransfer);
delegate->SetAutoFocusTransfer(isAutoFocusTransfer);
return nullptr;
}
static napi_value JSActivate(napi_env env, napi_callback_info info)
{
size_t argc = ARGC_ACTIVATE_PARAMTER;
@ -119,6 +136,7 @@ static napi_value registerFunc(napi_env env, napi_value exports)
DECLARE_NAPI_FUNCTION("clearFocus", JSClearFocus),
DECLARE_NAPI_FUNCTION("requestFocus", JSRequestFocus),
DECLARE_NAPI_FUNCTION("activate", JSActivate),
DECLARE_NAPI_FUNCTION("setAutoFocusTransfer", JsSetAutoFocusTransfer),
};
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(animatorDesc) / sizeof(animatorDesc[0]), animatorDesc));
return exports;

View File

@ -736,4 +736,138 @@ HWTEST_F(FocusViewTestNg, FocusViewTest007, TestSize.Level1)
EXPECT_FALSE(buttonFocusHub1->IsCurrentFocus());
EXPECT_TRUE(buttonFocusHub2->IsCurrentFocus());
}
/**
* @tc.name: FocusViewTest008
* @tc.desc: RearrangeViewStack
* @tc.type: FUNC
* @tc.author: catpoison
*/
HWTEST_F(FocusViewTestNg, FocusViewTest008, TestSize.Level1)
{
/**
* @tc.steps: step1. construct components.
* - Page1
* - Column1
* - Page2
* - Column2
*/
auto rootNode = FrameNodeOnTree::CreateFrameNode(V2::ROOT_ETS_TAG, -1, AceType::MakeRefPtr<RootPattern>());
auto rootFocusHub = rootNode->GetOrCreateFocusHub();
auto pagePattern1 = AceType::MakeRefPtr<PagePattern>(AceType::MakeRefPtr<PageInfo>());
auto pageNode1 = FrameNodeOnTree::CreateFrameNode(V2::PAGE_ETS_TAG, -1, pagePattern1);
auto pageFocusHub1 = pageNode1->GetOrCreateFocusHub();
rootNode->AddChild(pageNode1);
auto columnPattern1 = AceType::MakeRefPtr<LinearLayoutPattern>(true);
auto columnNode1 = FrameNodeOnTree::CreateFrameNode(V2::COLUMN_ETS_TAG, -1, columnPattern1);
auto columnFocusHub1 = columnNode1->GetOrCreateFocusHub();
pageNode1->AddChild(columnNode1);
auto pagePattern2 = AceType::MakeRefPtr<PagePattern>(AceType::MakeRefPtr<PageInfo>());
auto pageNode2 = FrameNodeOnTree::CreateFrameNode(V2::PAGE_ETS_TAG, -1, pagePattern2);
auto pageFocusHub2 = pageNode2->GetOrCreateFocusHub();
rootNode->AddChild(pageNode2);
auto columnPattern2 = AceType::MakeRefPtr<LinearLayoutPattern>(true);
auto columnNode2 = FrameNodeOnTree::CreateFrameNode(V2::COLUMN_ETS_TAG, -1, columnPattern2);
auto columnFocusHub2 = columnNode2->GetOrCreateFocusHub();
pageNode2->AddChild(columnNode2);
/**
* @tc.steps: step2. Page execute FocusViewShow()
* expected: pagePattern1->isViewHasFocused_ is false.
*/
pagePattern1->FocusViewShow();
EXPECT_FALSE(pagePattern1->isViewHasFocused_);
/**
* @tc.steps: step3. FlushFocusView()
* expected:
* - Page1 *
* - Column1 *
* - Page2
* - Column2
*/
PipelineContext::GetCurrentContext()->FlushFocusView();
EXPECT_TRUE(pagePattern1->isViewHasFocused_);
EXPECT_TRUE(pagePattern1->isViewRootScopeFocused_);
EXPECT_TRUE(pageFocusHub1->IsCurrentFocus());
EXPECT_TRUE(pagePattern1->IsRootScopeCurrentFocus());
EXPECT_TRUE(columnFocusHub1->IsCurrentFocus());
EXPECT_FALSE(pagePattern2->isViewHasFocused_);
EXPECT_TRUE(pagePattern2->isViewRootScopeFocused_);
EXPECT_FALSE(pageFocusHub2->IsCurrentFocus());
EXPECT_FALSE(pagePattern2->IsRootScopeCurrentFocus());
EXPECT_FALSE(columnFocusHub2->IsCurrentFocus());
/**
* @tc.steps: step4. Set AutoFocusTransfer False
* expected: isAutoFocusTransfer_ is false.
*/
auto focusManager = PipelineContext::GetCurrentContext()->GetFocusManager();
focusManager->SetIsAutoFocusTransfer(false);
EXPECT_FALSE(focusManager->IsAutoFocusTransfer());
/**
* @tc.steps: step5. Page execute FocusViewShow()
* expected: pagePattern2->isViewHasFocused_ is false.
*/
pagePattern2->FocusViewShow();
EXPECT_FALSE(pagePattern2->isViewHasFocused_);
/**
* @tc.steps: step6. FlushFocusView()
* expected:
* - Page1 *
* - Column1 *
* - Page2
* - Column2
*/
PipelineContext::GetCurrentContext()->FlushFocusView();
EXPECT_TRUE(pagePattern1->isViewHasFocused_);
EXPECT_TRUE(pagePattern1->isViewRootScopeFocused_);
EXPECT_TRUE(pageFocusHub1->IsCurrentFocus());
EXPECT_TRUE(pagePattern1->IsRootScopeCurrentFocus());
EXPECT_TRUE(columnFocusHub1->IsCurrentFocus());
EXPECT_TRUE(pagePattern2->isViewHasFocused_);
EXPECT_TRUE(pagePattern2->isViewRootScopeFocused_);
EXPECT_FALSE(pageFocusHub2->IsCurrentFocus());
EXPECT_FALSE(pagePattern2->IsRootScopeCurrentFocus());
EXPECT_FALSE(columnFocusHub2->IsCurrentFocus());
/**
* @tc.steps: step7. Page2 execute FocusViewClose()
* expected: pagePattern1->isViewHasFocused_ is false.
*/
pagePattern2->FocusViewClose();
/**
* @tc.steps: step8. FlushFocusView()
* expected:
* - Page1 *
* - Column1 *
* - Page2
* - Column2
*/
PipelineContext::GetCurrentContext()->FlushFocusView();
EXPECT_TRUE(pagePattern1->isViewHasFocused_);
EXPECT_TRUE(pagePattern1->isViewRootScopeFocused_);
EXPECT_TRUE(pageFocusHub1->IsCurrentFocus());
EXPECT_TRUE(pagePattern1->IsRootScopeCurrentFocus());
EXPECT_TRUE(columnFocusHub1->IsCurrentFocus());
EXPECT_TRUE(pagePattern2->isViewHasFocused_);
EXPECT_TRUE(pagePattern2->isViewRootScopeFocused_);
EXPECT_FALSE(pageFocusHub2->IsCurrentFocus());
EXPECT_FALSE(pagePattern2->IsRootScopeCurrentFocus());
EXPECT_FALSE(columnFocusHub2->IsCurrentFocus());
}
} // namespace OHOS::Ace::NG