implement custom focus

Signed-off-by: Tianer Zhou <zhoutianer@huawei.com>
Change-Id: Ic5ae43d81f339605be34aeccb11055b7088a72d8
This commit is contained in:
Tianer Zhou 2024-05-31 14:31:08 +08:00
parent 230e32c908
commit 13f06d732e
6 changed files with 176 additions and 9 deletions

View File

@ -39,6 +39,11 @@ public:
return true;
}
FocusPattern GetFocusPattern() const override
{
return { FocusType::SCOPE, true };
}
OPINC_TYPE_E OpIncType() override
{
return OPINC_NODE;

View File

@ -674,11 +674,10 @@ void WaterFlowPattern::DumpAdvanceInfo()
property->IsReverse() ? DumpLog::GetInstance().AddDesc("isReverse:true")
: DumpLog::GetInstance().AddDesc("isReverse:false");
info->itemStart_ ? DumpLog::GetInstance().AddDesc("itemStart:true")
: DumpLog::GetInstance().AddDesc("itemStart:false");
info->itemEnd_ ? DumpLog::GetInstance().AddDesc("itemEnd:true")
: DumpLog::GetInstance().AddDesc("itemEnd:false");
: DumpLog::GetInstance().AddDesc("itemStart:false");
info->itemEnd_ ? DumpLog::GetInstance().AddDesc("itemEnd:true") : DumpLog::GetInstance().AddDesc("itemEnd:false");
info->offsetEnd_ ? DumpLog::GetInstance().AddDesc("offsetEnd:true")
: DumpLog::GetInstance().AddDesc("offsetEnd:false");
: DumpLog::GetInstance().AddDesc("offsetEnd:false");
footer_.Upgrade() ? DumpLog::GetInstance().AddDesc("footer:true") : DumpLog::GetInstance().AddDesc("footer:false");
property->GetItemMinSize().has_value()
@ -692,7 +691,7 @@ void WaterFlowPattern::DumpAdvanceInfo()
DumpLog::GetInstance().AddDesc("-----------start print sections_------------");
std::string res = std::string("");
int32_t index = 0;
for (auto &section : sections_->GetSectionInfo()) {
for (auto& section : sections_->GetSectionInfo()) {
res.append("[section:" + std::to_string(index) + "]");
res.append("{ itemCount:" + std::to_string(section.itemsCount) + " },")
.append("{ crossCount:" + std::to_string(section.crossCount.value_or(1)) + " },")
@ -706,4 +705,78 @@ void WaterFlowPattern::DumpAdvanceInfo()
DumpLog::GetInstance().AddDesc("-----------end print sections_------------");
}
}
ScopeFocusAlgorithm WaterFlowPattern::GetScopeFocusAlgorithm()
{
return { layoutInfo_->axis_ == Axis::VERTICAL, true, ScopeType::OTHERS,
[wp = WeakClaim(this)](
FocusStep step, const WeakPtr<FocusHub>& currFocusNode, WeakPtr<FocusHub>& nextFocusNode) {
auto self = wp.Upgrade();
if (self) {
nextFocusNode = self->GetNextFocusNode(step, currFocusNode);
}
} };
}
WeakPtr<FocusHub> WaterFlowPattern::GetNextFocusNode(FocusStep step, const WeakPtr<FocusHub>& currentFocusNode)
{
auto cur = currentFocusNode.Upgrade();
CHECK_NULL_RETURN(cur, nullptr);
auto host = GetHost();
CHECK_NULL_RETURN(host, nullptr);
int32_t curIdx = host->GetChildTrueIndex(cur->GetFrameNode());
int32_t diff = 0;
switch (step) {
case FocusStep::DOWN:
case FocusStep::DOWN_END:
case FocusStep::RIGHT:
case FocusStep::RIGHT_END:
case FocusStep::TAB:
diff = 1;
break;
case FocusStep::LEFT:
case FocusStep::LEFT_END:
case FocusStep::UP:
case FocusStep::UP_END:
case FocusStep::SHIFT_TAB:
diff = -1;
break;
default:
return currentFocusNode;
}
int32_t idx = curIdx + diff;
int32_t footerOffset = layoutInfo_->footerIndex_ + 1; // 1 if footer present, 0 if not
while (idx - footerOffset >= 0 && idx < GetChildrenCount()) {
int32_t itemIdx = idx - footerOffset;
if (itemIdx >= layoutInfo_->endIndex_ || itemIdx <= layoutInfo_->startIndex_) {
ScrollToIndex(itemIdx, false, ScrollAlign::AUTO);
host->SetActive();
host->CreateLayoutTask();
}
auto next = host->GetChildByIndex(idx);
CHECK_NULL_RETURN(next, nullptr);
auto focus = next->GetHostNode()->GetFocusHub();
if (focus && focus->IsFocusable()) {
return focus;
}
idx += diff;
}
return nullptr;
}
std::function<bool(int32_t)> WaterFlowPattern::GetScrollIndexAbility()
{
return [wp = WeakClaim(this)](int32_t index) -> bool {
auto self = wp.Upgrade();
CHECK_NULL_RETURN(self, false);
if (index == FocusHub::SCROLL_TO_HEAD) {
self->ScrollToEdge(ScrollEdgeType::SCROLL_TOP, false);
} else if (index == FocusHub::SCROLL_TO_TAIL) {
self->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false);
} else {
self->ScrollToIndex(index, false, ScrollAlign::AUTO);
}
return true;
};
}
} // namespace OHOS::Ace::NG

View File

@ -17,11 +17,11 @@
#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_WATERFLOW_WATER_FLOW_PATTERN_H
#include "core/components_ng/pattern/scrollable/scrollable_pattern.h"
#include "core/components_ng/pattern/waterflow/layout/water_flow_layout_algorithm_base.h"
#include "core/components_ng/pattern/waterflow/layout/water_flow_layout_info_base.h"
#include "core/components_ng/pattern/waterflow/water_flow_accessibility_property.h"
#include "core/components_ng/pattern/waterflow/water_flow_content_modifier.h"
#include "core/components_ng/pattern/waterflow/water_flow_event_hub.h"
#include "core/components_ng/pattern/waterflow/layout/water_flow_layout_algorithm_base.h"
#include "core/components_ng/pattern/waterflow/layout/water_flow_layout_info_base.h"
#include "core/components_ng/pattern/waterflow/water_flow_layout_property.h"
#include "core/components_ng/pattern/waterflow/water_flow_sections.h"
@ -72,7 +72,7 @@ public:
}
void TriggerModifyDone();
RefPtr<NodePaintMethod> CreateNodePaintMethod() override;
bool UpdateStartIndex(int32_t index);
@ -148,11 +148,18 @@ public:
* @param start the index of the first modified section.
*/
void OnSectionChanged(int32_t start);
void OnSectionChangedNow(int32_t start);
void DumpAdvanceInfo() override;
// ------------------------ Focus adapter --------------------------------
FocusPattern GetFocusPattern() const override
{
return { FocusType::SCOPE, true };
}
ScopeFocusAlgorithm GetScopeFocusAlgorithm() override;
std::function<bool(int32_t)> GetScrollIndexAbility() override;
// ------------------------ Focus ^^^ --------------------------------
private:
DisplayMode GetDefaultScrollBarDisplayMode() const override
{
@ -171,6 +178,14 @@ private:
void OnScrollEndCallback() override;
bool ScrollToTargetIndex(int32_t index);
bool NeedRender();
/**
* @param step FocusStep
* @param currentFocusNode the currently focused FlowItem.
* @return WeakPtr<FocusHub> of the next FlowItem to focus on.
*/
WeakPtr<FocusHub> GetNextFocusNode(FocusStep step, const WeakPtr<FocusHub>& currentFocusNode);
std::optional<int32_t> targetIndex_;
RefPtr<WaterFlowLayoutInfoBase> layoutInfo_ = WaterFlowLayoutInfoBase::Create(LayoutMode::TOP_DOWN);
RefPtr<WaterFlowSections> sections_;

View File

@ -539,4 +539,53 @@ HWTEST_F(WaterFlowScrollerTestNg, ScrollToIndex003, TestSize.Level1)
FlushLayoutTask(frameNode_);
EXPECT_FLOAT_EQ(pattern_->finalPosition_, 2100.f);
}
/**
* @tc.name: Focus001
* @tc.desc: Test WaterFlow scroll during focus change
* @tc.type: FUNC
*/
HWTEST_F(WaterFlowScrollerTestNg, Focus001, TestSize.Level1)
{
Create([](WaterFlowModelNG model) {
model.SetColumnsTemplate("1fr 1fr");
CreateFocusableItem(30);
});
auto next = pattern_->GetNextFocusNode(FocusStep::DOWN, GetChildFocusHub(frameNode_, 5)).Upgrade();
auto cmp = GetChildFocusHub(frameNode_, 6);
EXPECT_EQ(AceType::RawPtr(next), AceType::RawPtr(cmp));
cmp = GetChildFocusHub(frameNode_, 4);
next = pattern_->GetNextFocusNode(FocusStep::UP, GetChildFocusHub(frameNode_, 5)).Upgrade();
EXPECT_EQ(AceType::RawPtr(next), AceType::RawPtr(cmp));
auto info = pattern_->layoutInfo_;
EXPECT_EQ(info->startIndex_, 0);
EXPECT_EQ(info->endIndex_, 10);
next = pattern_->GetNextFocusNode(FocusStep::LEFT, GetChildFocusHub(frameNode_, 0)).Upgrade();
EXPECT_FALSE(next);
EXPECT_EQ(info->startIndex_, 0);
EXPECT_EQ(info->endIndex_, 10);
next = pattern_->GetNextFocusNode(FocusStep::RIGHT, GetChildFocusHub(frameNode_, 10)).Upgrade();
EXPECT_EQ(GetChildRect(frameNode_, 11).Bottom(), WATERFLOW_HEIGHT);
cmp = GetChildFocusHub(frameNode_, 11);
EXPECT_EQ(AceType::RawPtr(next), AceType::RawPtr(cmp));
pattern_->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false);
FlushLayoutTask(frameNode_);
next = pattern_->GetNextFocusNode(FocusStep::LEFT, GetChildFocusHub(frameNode_, 29)).Upgrade();
cmp = GetChildFocusHub(frameNode_, 28);
EXPECT_EQ(AceType::RawPtr(next), AceType::RawPtr(cmp));
next = pattern_->GetNextFocusNode(FocusStep::DOWN_END, GetChildFocusHub(frameNode_, 29)).Upgrade();
EXPECT_FALSE(next);
EXPECT_EQ(info->startIndex_, 19);
next = pattern_->GetNextFocusNode(FocusStep::UP_END, GetChildFocusHub(frameNode_, info->startIndex_)).Upgrade();
cmp = GetChildFocusHub(frameNode_, 18);
EXPECT_EQ(AceType::RawPtr(next), AceType::RawPtr(cmp));
EXPECT_EQ(GetChildY(frameNode_, 18), 0.0f);
}
} // namespace OHOS::Ace::NG

View File

@ -36,6 +36,7 @@
#include "core/components/button/button_theme.h"
#include "core/components/common/layout/constants.h"
#include "core/components_ng/base/view_stack_processor.h"
#include "core/components_ng/pattern/button/button_model_ng.h"
#include "core/components_ng/pattern/linear_layout/row_model_ng.h"
#include "core/components_ng/pattern/scrollable/scrollable.h"
#include "core/components_ng/pattern/waterflow/water_flow_accessibility_property.h"
@ -142,6 +143,29 @@ void WaterFlowTestNg::CreateItem(int32_t number)
}
}
void WaterFlowTestNg::CreateFocusableItem(int32_t number)
{
for (int32_t i = 0; i < number; i++) {
WaterFlowItemModelNG waterFlowItemModel;
waterFlowItemModel.Create();
ViewAbstract::SetWidth(CalcLength(FILL_LENGTH));
// set irregular height
int32_t two = 2;
if (i % two == 0) {
ViewAbstract::SetHeight(CalcLength(Dimension(ITEM_HEIGHT)));
} else {
ViewAbstract::SetHeight(CalcLength(Dimension(BIG_ITEM_HEIGHT)));
}
{
ButtonModelNG buttonModelNG;
std::list<RefPtr<Component>> buttonChildren;
buttonModelNG.CreateWithLabel({ .label = "label" }, buttonChildren);
ViewStackProcessor::GetInstance()->Pop();
}
ViewStackProcessor::GetInstance()->Pop();
}
}
void WaterFlowTestNg::CreateRandomItem(int32_t number)
{
for (int32_t i = 0; i < number; i++) {

View File

@ -58,6 +58,7 @@ protected:
void CreateWithItem(const std::function<void(WaterFlowModelNG)>& callback = nullptr);
static void CreateItem(int32_t number = 10);
static void CreateRandomItem(int32_t number);
static void CreateFocusableItem(int32_t number);
static void CreateItemWithHeight(float height);
void UpdateCurrentOffset(float offset, int32_t source = SCROLL_FROM_UPDATE);
void MouseSelect(Offset start, Offset end);