From 1a8cbe885c31b0636df15e8003ed8ff2298777c1 Mon Sep 17 00:00:00 2001 From: bixuefeng Date: Sat, 26 Aug 2023 15:19:21 +0800 Subject: [PATCH] Bugfix: Modify focus scroll algorithm. Now scroll focus node by offset relative to parent. Signed-off-by: bixuefeng Change-Id: Ia1c07589cdcdc31c9e6b7161ac050ec02beef21e --- .../core/components_ng/event/focus_hub.cpp | 77 ++++++++++++++++--- .../core/components_ng/event/focus_hub.h | 3 + .../components_ng/pattern/grid/grid_pattern.h | 13 ++++ .../pattern/list/list_pattern.cpp | 30 +++++--- .../components_ng/pattern/list/list_pattern.h | 13 ++++ .../core/components_ng/pattern/pattern.h | 5 ++ .../pattern/scroll/scroll_pattern.cpp | 67 ---------------- .../pattern/scroll/scroll_pattern.h | 10 ++- .../mock/pattern/grid/mock_grid_pattern.cpp | 2 + .../mock/pattern/list/mock_list_pattern.cpp | 2 + 10 files changed, 134 insertions(+), 88 deletions(-) diff --git a/frameworks/core/components_ng/event/focus_hub.cpp b/frameworks/core/components_ng/event/focus_hub.cpp index 443a04e870c..cb1b10b9970 100644 --- a/frameworks/core/components_ng/event/focus_hub.cpp +++ b/frameworks/core/components_ng/event/focus_hub.cpp @@ -937,8 +937,10 @@ void FocusHub::OnFocusNode() if (parentFocusHub) { parentFocusHub->SetLastFocusNodeIndex(AceType::Claim(this)); } - HandleParentScroll(); // If current focus node has a scroll parent. Handle the scroll event. - PaintFocusState(); + if (PaintFocusState() || isFocusUnit_) { + // If current focus node has focus state or it's a focus unit. Scroll it in parent while it's focused. + HandleParentScroll(); + } auto frameNode = GetFrameNode(); CHECK_NULL_VOID_NOLOG(frameNode); frameNode->OnAccessibilityEvent(AccessibilityEventType::FOCUS); @@ -975,6 +977,13 @@ void FocusHub::CheckFocusStateStyle(bool onFocus) } } +bool FocusHub::HasFocusStateStyle() +{ + auto eventHub = eventHub_.Upgrade(); + CHECK_NULL_RETURN(eventHub, false); + return eventHub->HasStateStyle(UI_STATE_FOCUSED); +} + void FocusHub::OnFocusScope() { std::list> focusNodes; @@ -1020,10 +1029,6 @@ bool FocusHub::PaintFocusState(bool isNeedStateStyles) { auto context = PipelineContext::GetCurrentContext(); CHECK_NULL_RETURN(context, false); - if (isNeedStateStyles && context->GetIsFocusActive()) { - // check focus state style. - CheckFocusStateStyle(true); - } auto frameNode = GetFrameNode(); CHECK_NULL_RETURN(frameNode, false); auto renderContext = frameNode->GetRenderContext(); @@ -1032,6 +1037,11 @@ bool FocusHub::PaintFocusState(bool isNeedStateStyles) return false; } + if (isNeedStateStyles) { + // do focus state style. + CheckFocusStateStyle(true); + } + if (focusStyleType_ == FocusStyleType::CUSTOM_REGION) { CHECK_NULL_RETURN(getInnerFocusRectFunc_, false); RoundRect focusRectInner; @@ -1088,6 +1098,7 @@ bool FocusHub::PaintFocusState(bool isNeedStateStyles) bool FocusHub::PaintAllFocusState() { if (PaintFocusState()) { + HandleParentScroll(); return true; } std::list> focusNodes; @@ -1163,7 +1174,7 @@ void FocusHub::ClearAllFocusState() bool FocusHub::IsNeedPaintFocusState() { - if (focusType_ == FocusType::DISABLE || focusStyleType_ == FocusStyleType::NONE) { + if (focusType_ == FocusType::DISABLE || (focusStyleType_ == FocusStyleType::NONE && !HasFocusStateStyle())) { return false; } if (focusType_ == FocusType::NODE) { @@ -1466,7 +1477,7 @@ void FocusHub::HandleParentScroll() const { auto context = PipelineContext::GetCurrentContext(); CHECK_NULL_VOID(context); - if (!context->GetIsFocusActive() || (focusType_ != FocusType::NODE && !isFocusUnit_)) { + if (!context->GetIsFocusActive() || (focusType_ == FocusType::DISABLE && !isFocusUnit_)) { return; } auto parent = GetParentFocusHub(); @@ -1481,14 +1492,60 @@ void FocusHub::HandleParentScroll() const parent = parent->GetParentFocusHub(); continue; } - parentPattern = parentFrame->GetPattern(); - if (parentPattern && parentPattern->ScrollToNode(GetFrameNode())) { + if (ScrollToTargetFrameRect(parentFrame)) { return; } parent = parent->GetParentFocusHub(); } } +bool FocusHub::ScrollToTargetFrameRect(const RefPtr& tarFrameNode) const +{ + auto curFrameNode = GetFrameNode(); + CHECK_NULL_RETURN(curFrameNode, false); + CHECK_NULL_RETURN(tarFrameNode, false); + auto tarPattern = tarFrameNode->GetPattern(); + CHECK_NULL_RETURN(tarPattern, false); + tarPattern->ScrollToNode(curFrameNode); + + auto scrollAbility = tarPattern->GetScrollAbility(); + auto scrollFunc = scrollAbility.first; + auto scrollAxis = scrollAbility.second; + if (!scrollFunc || scrollAxis == Axis::NONE) { + return false; + } + auto tarGeometryNode = tarFrameNode->GetGeometryNode(); + CHECK_NULL_RETURN(tarGeometryNode, false); + auto tarFrameSize = tarGeometryNode->GetFrameSize(); + auto offsetToTarFrame = curFrameNode->GetOffsetRelativeToWindow() - tarFrameNode->GetOffsetRelativeToWindow(); + auto curGeometry = curFrameNode->GetGeometryNode(); + CHECK_NULL_RETURN(curGeometry, false); + auto curFrameSize = curGeometry->GetFrameSize(); + LOGD("Node: %{public}s/%{public}d - [%{public}f,%{public}f] on focus. Offset to target node: " + "%{public}s/%{public}d - [%{public}f,%{public}f] is (%{public}f,%{public}f).", + curFrameNode->GetTag().c_str(), curFrameNode->GetId(), curFrameSize.Width(), curFrameSize.Height(), + tarFrameNode->GetTag().c_str(), tarFrameNode->GetId(), tarFrameSize.Width(), tarFrameSize.Height(), + offsetToTarFrame.GetX(), offsetToTarFrame.GetY()); + + float diffToTarFrame = scrollAxis == Axis::VERTICAL ? offsetToTarFrame.GetY() : offsetToTarFrame.GetX(); + if (NearZero(diffToTarFrame)) { + return false; + } + float curFrameLength = scrollAxis == Axis::VERTICAL ? curFrameSize.Height() : curFrameSize.Width(); + float tarFrameLength = scrollAxis == Axis::VERTICAL ? tarFrameSize.Height() : tarFrameSize.Width(); + float moveOffset = 0.0; + if (LessNotEqual(diffToTarFrame, 0)) { + moveOffset = -diffToTarFrame; + } else if (GreatNotEqual(diffToTarFrame + curFrameLength, tarFrameLength)) { + moveOffset = tarFrameLength - diffToTarFrame - curFrameLength; + } + if (!NearZero(moveOffset)) { + LOGD("Scroll offset: %{public}f on axis: %{public}d", moveOffset, scrollAxis); + return scrollFunc(moveOffset); + } + return false; +} + bool FocusHub::RequestFocusImmediatelyById(const std::string& id) { auto focusNode = GetChildFocusNodeById(id); diff --git a/frameworks/core/components_ng/event/focus_hub.h b/frameworks/core/components_ng/event/focus_hub.h index 3cf2eafd28d..8f09d9a50cb 100644 --- a/frameworks/core/components_ng/event/focus_hub.h +++ b/frameworks/core/components_ng/event/focus_hub.h @@ -28,6 +28,7 @@ class FrameNode; class FocusHub; class EventHub; +using FocusScrollFunc = std::function; using TabIndexNodeList = std::list>>; constexpr int32_t DEFAULT_TAB_FOCUSED_INDEX = -2; constexpr int32_t NONE_TAB_FOCUSED_INDEX = -1; @@ -529,6 +530,7 @@ public: RefPtr GetChildFocusNodeByType(FocusNodeType nodeType = FocusNodeType::DEFAULT); RefPtr GetChildFocusNodeById(const std::string& id); void HandleParentScroll() const; + bool ScrollToTargetFrameRect(const RefPtr& tarFrameNode) const; int32_t GetFocusingTabNodeIdx(TabIndexNodeList& tabIndexNodes); bool RequestFocusImmediatelyById(const std::string& id); @@ -878,6 +880,7 @@ private: void ScrollToLastFocusIndex() const; void CheckFocusStateStyle(bool onFocus); + bool HasFocusStateStyle(); bool IsNeedPaintFocusState(); diff --git a/frameworks/core/components_ng/pattern/grid/grid_pattern.h b/frameworks/core/components_ng/pattern/grid/grid_pattern.h index 633ca0b2058..20feca4c960 100644 --- a/frameworks/core/components_ng/pattern/grid/grid_pattern.h +++ b/frameworks/core/components_ng/pattern/grid/grid_pattern.h @@ -104,6 +104,19 @@ public: void ScrollToFocusNodeIndex(int32_t index) override; + std::pair GetScrollAbility() override + { + return { [wp = WeakClaim(this)](float moveOffset) -> bool { + auto pattern = wp.Upgrade(); + if (pattern) { + pattern->ScrollBy(moveOffset); + return true; + } + return false; + }, + GetAxis() }; + } + bool ScrollToNode(const RefPtr& focusFrameNode) override; RefPtr CreateEventHub() override diff --git a/frameworks/core/components_ng/pattern/list/list_pattern.cpp b/frameworks/core/components_ng/pattern/list/list_pattern.cpp index f09887dab06..d61cce575bb 100644 --- a/frameworks/core/components_ng/pattern/list/list_pattern.cpp +++ b/frameworks/core/components_ng/pattern/list/list_pattern.cpp @@ -1092,16 +1092,28 @@ WeakPtr ListPattern::GetChildFocusNodeByIndex(int32_t tarMainIndex, in bool ListPattern::ScrollToNode(const RefPtr& focusFrameNode) { - CHECK_NULL_RETURN_NOLOG(focusFrameNode, false); - auto focusPattern = focusFrameNode->GetPattern(); - CHECK_NULL_RETURN_NOLOG(focusPattern, false); - auto curIndex = focusPattern->GetIndexInList(); - ScrollToIndex(curIndex, smooth_, ScrollAlign::AUTO); - auto pipeline = PipelineContext::GetCurrentContext(); - if (pipeline) { - pipeline->FlushUITasks(); + auto curFrameNode = GetHost(); + CHECK_NULL_RETURN(curFrameNode, false); + auto tarFrameNode = focusFrameNode; + while (tarFrameNode) { + auto tarPattern = tarFrameNode->GetPattern(); + auto tarFocusParent = tarFrameNode->GetFocusParent(); + if (!tarPattern || !tarFocusParent || tarFocusParent != curFrameNode) { + tarFrameNode = tarFocusParent; + continue; + } + auto curIndex = tarPattern->GetIndexInList(); + LOGI("Target scroll ListItem is %{public}s/%{public}d. Index in %{public}s/%{public}d is %{public}d", + tarFrameNode->GetTag().c_str(), tarFrameNode->GetId(), curFrameNode->GetTag().c_str(), + curFrameNode->GetId(), curIndex); + ScrollToIndex(curIndex, smooth_, ScrollAlign::AUTO); + auto pipeline = PipelineContext::GetCurrentContext(); + if (pipeline) { + pipeline->FlushUITasks(); + } + return true; } - return true; + return false; } WeakPtr ListPattern::ScrollAndFindFocusNode(int32_t nextIndex, int32_t curIndex, int32_t& nextIndexInGroup, diff --git a/frameworks/core/components_ng/pattern/list/list_pattern.h b/frameworks/core/components_ng/pattern/list/list_pattern.h index d52dc6af840..f8107ce78a2 100644 --- a/frameworks/core/components_ng/pattern/list/list_pattern.h +++ b/frameworks/core/components_ng/pattern/list/list_pattern.h @@ -136,6 +136,19 @@ public: }); } + std::pair GetScrollAbility() override + { + return { [wp = WeakClaim(this)](float moveOffset) -> bool { + auto pattern = wp.Upgrade(); + if (pattern) { + pattern->ScrollBy(-moveOffset); + return true; + } + return false; + }, + GetAxis() }; + } + bool ScrollToNode(const RefPtr& focusFrameNode) override; const ListLayoutAlgorithm::PositionMap& GetItemPosition() const diff --git a/frameworks/core/components_ng/pattern/pattern.h b/frameworks/core/components_ng/pattern/pattern.h index f80a31e7684..e00f653b683 100644 --- a/frameworks/core/components_ng/pattern/pattern.h +++ b/frameworks/core/components_ng/pattern/pattern.h @@ -308,6 +308,11 @@ public: return ScopeFocusAlgorithm(); } + virtual std::pair GetScrollAbility() + { + return { nullptr, Axis::NONE }; + } + virtual bool ScrollToNode(const RefPtr& focusFrameNode) { return false; diff --git a/frameworks/core/components_ng/pattern/scroll/scroll_pattern.cpp b/frameworks/core/components_ng/pattern/scroll/scroll_pattern.cpp index cd658a7a3bd..af2e2e49dab 100644 --- a/frameworks/core/components_ng/pattern/scroll/scroll_pattern.cpp +++ b/frameworks/core/components_ng/pattern/scroll/scroll_pattern.cpp @@ -603,73 +603,6 @@ void ScrollPattern::SetAccessibilityAction() }); } -OffsetF ScrollPattern::GetOffsetToScroll(const RefPtr& childFrame) const -{ - auto frameNode = GetHost(); - CHECK_NULL_RETURN(frameNode, OffsetF()); - CHECK_NULL_RETURN(childFrame, OffsetF()); - auto childGeometryNode = childFrame->GetGeometryNode(); - CHECK_NULL_RETURN(childGeometryNode, OffsetF()); - OffsetF result = childGeometryNode->GetFrameOffset(); - auto parent = childFrame->GetParent(); - while (parent) { - auto parentFrame = AceType::DynamicCast(parent); - if (!parentFrame) { - parent = parent->GetParent(); - continue; - } - if (parentFrame == frameNode) { - return result; - } - auto parentGeometryNode = parentFrame->GetGeometryNode(); - if (!parentGeometryNode) { - parent = parent->GetParent(); - continue; - } - result += parentGeometryNode->GetFrameOffset(); - parent = parent->GetParent(); - } - return OffsetF(0.0, 0.0); -} - -bool ScrollPattern::ScrollToNode(const RefPtr& focusFrameNode) -{ - CHECK_NULL_RETURN(focusFrameNode, false); - auto focusGeometryNode = focusFrameNode->GetGeometryNode(); - CHECK_NULL_RETURN(focusGeometryNode, false); - auto focusNodeSize = focusGeometryNode->GetFrameSize(); - auto focusNodeOffsetToScrolll = GetOffsetToScroll(focusFrameNode); - auto scrollFrame = GetHost(); - CHECK_NULL_RETURN(scrollFrame, false); - auto scrollGeometry = scrollFrame->GetGeometryNode(); - CHECK_NULL_RETURN(scrollGeometry, false); - auto scrollFrameSize = scrollGeometry->GetFrameSize(); - LOGD("Child: %{public}s/%{public}d on focus. Size is (%{public}f,%{public}f). Offset to Scroll is " - "(%{public}f,%{public}f). Scroll size is (%{public}f,%{public}f)", - focusFrameNode->GetTag().c_str(), focusFrameNode->GetId(), focusNodeSize.Width(), focusNodeSize.Height(), - focusNodeOffsetToScrolll.GetX(), focusNodeOffsetToScrolll.GetY(), scrollFrameSize.Width(), - scrollFrameSize.Height()); - - float focusNodeDiffToScroll = - GetAxis() == Axis::VERTICAL ? focusNodeOffsetToScrolll.GetY() : focusNodeOffsetToScrolll.GetX(); - if (NearZero(focusNodeDiffToScroll)) { - return false; - } - float focusNodeLength = GetAxis() == Axis::VERTICAL ? focusNodeSize.Height() : focusNodeSize.Width(); - float scrollFrameLength = GetAxis() == Axis::VERTICAL ? scrollFrameSize.Height() : scrollFrameSize.Width(); - float moveOffset = 0.0; - if (LessNotEqual(focusNodeDiffToScroll, 0)) { - moveOffset = -focusNodeDiffToScroll; - } else if (GreatNotEqual(focusNodeDiffToScroll + focusNodeLength, scrollFrameLength)) { - moveOffset = scrollFrameLength - focusNodeDiffToScroll - focusNodeLength; - } - if (!NearZero(moveOffset)) { - LOGD("Scroll offset: %{public}f on axis: %{public}d", moveOffset, GetAxis()); - return OnScrollCallback(moveOffset, SCROLL_FROM_FOCUS_JUMP); - } - return false; -} - std::optional ScrollPattern::CalePredictSnapOffset(float delta) { std::optional predictSnapOffset; diff --git a/frameworks/core/components_ng/pattern/scroll/scroll_pattern.h b/frameworks/core/components_ng/pattern/scroll/scroll_pattern.h index 91f9895eb9f..4e217156368 100644 --- a/frameworks/core/components_ng/pattern/scroll/scroll_pattern.h +++ b/frameworks/core/components_ng/pattern/scroll/scroll_pattern.h @@ -169,7 +169,14 @@ public: return { FocusType::SCOPE, true }; } - bool ScrollToNode(const RefPtr& focusFrameNode) override; + std::pair GetScrollAbility() override + { + return { [wp = WeakClaim(this)](float moveOffset) -> bool { + auto pattern = wp.Upgrade(); + return pattern ? pattern->OnScrollCallback(moveOffset, SCROLL_FROM_FOCUS_JUMP) : false; + }, + GetAxis() }; + } bool IsAtTop() const override; bool IsAtBottom() const override; @@ -291,7 +298,6 @@ private: void FireOnScrollStop(); void SetAccessibilityAction(); void CheckScrollable(); - OffsetF GetOffsetToScroll(const RefPtr& childFrame) const; RefPtr positionController_; float currentOffset_ = 0.0f; diff --git a/frameworks/core/components_ng/test/mock/pattern/grid/mock_grid_pattern.cpp b/frameworks/core/components_ng/test/mock/pattern/grid/mock_grid_pattern.cpp index ff11ff66f83..56f5d446342 100644 --- a/frameworks/core/components_ng/test/mock/pattern/grid/mock_grid_pattern.cpp +++ b/frameworks/core/components_ng/test/mock/pattern/grid/mock_grid_pattern.cpp @@ -53,6 +53,8 @@ bool GridPattern::ScrollToNode(const RefPtr& focusFrameNode) return false; } +void GridPattern::ScrollBy(float offset) {}; + CanvasDrawFunction GridPaintMethod::GetForegroundDrawFunction(PaintWrapper* /* paintWrapper */) { return [](RSCanvas& canvas) {}; diff --git a/frameworks/core/components_ng/test/mock/pattern/list/mock_list_pattern.cpp b/frameworks/core/components_ng/test/mock/pattern/list/mock_list_pattern.cpp index c84b08f8499..d9a6076f21a 100644 --- a/frameworks/core/components_ng/test/mock/pattern/list/mock_list_pattern.cpp +++ b/frameworks/core/components_ng/test/mock/pattern/list/mock_list_pattern.cpp @@ -55,6 +55,8 @@ bool ListPattern::ScrollToNode(const RefPtr& focusFrameNode) return false; } +void ListPattern::ScrollBy(float offset) {}; + bool ListPattern::IsAtBottom() const { return false; -- Gitee