From 38a15e8787b74a8e16805c7224d9b52526cd94e4 Mon Sep 17 00:00:00 2001 From: zcdqs Date: Mon, 24 Oct 2022 14:43:40 +0800 Subject: [PATCH] ng accessibility focus/pageid Signed-off-by: zcdqs Change-Id: I359eeded768a983f88338ea81ea581adde311842 --- .../ohos/osal/js_accessibility_manager.cpp | 814 +++++++++++------- adapter/ohos/osal/js_accessibility_manager.h | 20 +- .../ng/page_router_manager.cpp | 2 + .../accessibility/accessibility_utils.cpp | 174 ++++ .../core/accessibility/accessibility_utils.h | 11 + .../core/components_ng/base/frame_node.cpp | 5 + .../core/components_ng/base/ui_node.cpp | 3 + frameworks/core/components_ng/base/ui_node.h | 3 + .../render/adapter/rosen_render_context.cpp | 39 + .../render/adapter/rosen_render_context.h | 7 +- .../components_ng/render/paint_wrapper.cpp | 4 + .../components_ng/render/render_context.h | 6 + 12 files changed, 780 insertions(+), 308 deletions(-) diff --git a/adapter/ohos/osal/js_accessibility_manager.cpp b/adapter/ohos/osal/js_accessibility_manager.cpp index 7e15bab99e0..d53d1d831ec 100644 --- a/adapter/ohos/osal/js_accessibility_manager.cpp +++ b/adapter/ohos/osal/js_accessibility_manager.cpp @@ -60,20 +60,19 @@ constexpr int32_t ROOT_DECOR_BASE = 3100000; constexpr int32_t CARD_NODE_ID_RATION = 10000; constexpr int32_t CARD_ROOT_NODE_ID_RATION = 1000; constexpr int32_t CARD_BASE = 100000; -constexpr int32_t WEIGHTED_VALUE = 13; - -const int32_t FOCUS_DIRECTION_UP = 1; -const int32_t FOCUS_DIRECTION_DOWN = 1 << 1; -const int32_t FOCUS_DIRECTION_LEFT = 1 << 2; -const int32_t FOCUS_DIRECTION_RIGHT = 1 << 3; -const int32_t FOCUS_DIRECTION_FORWARD = 1 << 4; -const int32_t FOCUS_DIRECTION_BACKWARD = 1 << 5; struct ActionTable { AceAction aceAction; ActionType action; }; +struct CommonProperty { + int32_t windowId = 0; + int32_t windowLeft = 0; + int32_t windowTop = 0; + int32_t pageId = 0; +}; + Accessibility::EventType ConvertStrToEventType(const std::string& type) { // static linear map must be sorted by key. @@ -401,12 +400,30 @@ bool FindAccessibilityFocus(const RefPtr& node, RefPtr FindAccessibilityFocus(const RefPtr& node) +{ + auto frameNode = AceType::DynamicCast(node); + if (frameNode) { + if (frameNode->GetRenderContext()->GetAccessibilityFocus().value_or(false)) { + LOGI("FindFocus nodeId(%{public}d)", node->GetId()); + return frameNode; + } + } + + if (!node->GetChildren().empty()) { + for (const auto& child : node->GetChildren()) { + return FindAccessibilityFocus(child); + } + } + return nullptr; +} + bool FindInputFocus(const RefPtr& node, RefPtr& resultNode) { if (!node) { return false; } - LOGI("FindFocus nodeId(%{public}d focus(%{public}d))", node->GetNodeId(), node->GetFocusedState()); + if (!node->GetFocusedState() && (node->GetParentId() != -1)) { return false; } @@ -421,11 +438,35 @@ bool FindInputFocus(const RefPtr& node, RefPtrGetFocusedState()) { - return true; + return node->GetFocusedState(); +} + +RefPtr FindInputFocus(const RefPtr& node) +{ + auto frameNode = AceType::DynamicCast(node); + if (frameNode) { + if (!(frameNode->GetFocusHub() ? frameNode->GetFocusHub()->IsCurrentFocus() : false)) { + return nullptr; + } + + if (frameNode->GetFocusHub()->IsChild()) { + LOGI("FindFocus nodeId(%{public}d)", node->GetId()); + if (frameNode->IsInternal()) { + return frameNode->GetFocusParent(); + } + return frameNode; + } } - return false; + if (!node->GetChildren().empty()) { + for (const auto& child : node->GetChildren()) { + auto childNode = FindInputFocus(child); + if (childNode) { + return childNode; + } + } + } + return nullptr; } void FindText( @@ -445,6 +486,26 @@ void FindText( } } +void FindText(const RefPtr& node, const std::string& text, std::list>& nodeList) +{ + CHECK_NULL_VOID(node); + + auto frameNode = AceType::DynamicCast(node); + if (frameNode) { + if (frameNode->GetAccessibilityProperty()->GetText().find(text) != + std::string::npos) { + LOGI("FindText find nodeId(%{public}d)", frameNode->GetId()); + nodeList.push_back(frameNode); + } + } + + if (!node->GetChildren().empty()) { + for (const auto& child : node->GetChildren()) { + FindText(child, text, nodeList); + } + } +} + RefPtr GetInspectorById(const RefPtr& root, int32_t id) { std::queue> nodes; @@ -467,63 +528,38 @@ RefPtr GetInspectorById(const RefPtr& root, int32_ return nullptr; } -bool ExecuteActionNG(RefPtr& context, int32_t elementId, ActionType action) +void GetFrameNodeChildren(const RefPtr& uiNode, std::vector& children, int32_t pageId) { - bool result = false; - auto ngPipeline = AceType::DynamicCast(context); - auto frameNode = GetInspectorById(ngPipeline->GetRootElement(), elementId); - if (!frameNode) { - LOGI("action:%{public}d on non exist elementId:%{public}d ", action, elementId); - - return result; - } - - switch (action) { - case ActionType::ACCESSIBILITY_ACTION_CLICK: { - auto gesture = frameNode->GetEventHub()->GetGestureEventHub(); - if (gesture) { - result = gesture->ActClick(); + if (AceType::InstanceOf(uiNode)) { + if (uiNode->GetTag() == "stage") { + } else if (uiNode->GetTag() == "page") { + if (uiNode->GetPageId() != pageId) { + return; } - break; - } - case ActionType::ACCESSIBILITY_ACTION_LONG_CLICK: { - auto gesture = frameNode->GetEventHub()->GetGestureEventHub(); - if (gesture) { - result = gesture->ActLongClick(); + } else { + auto frameNode = AceType::DynamicCast(uiNode); + if (!frameNode->IsInternal()) { + children.emplace_back(uiNode->GetId()); + return; } - break; } - default: - break; } - return result; -} - -void GetFrameNodeChildren(const RefPtr& uiNode, std::vector& children) -{ - if (AceType::InstanceOf(uiNode)) { - auto frameNode = AceType::DynamicCast(uiNode); - if (!frameNode->IsInternal()) { - children.emplace_back(uiNode->GetId()); - } - } else { - for (const auto& frameChild : uiNode->GetChildren()) { - GetFrameNodeChildren(frameChild, children); - } + for (const auto& frameChild : uiNode->GetChildren()) { + GetFrameNodeChildren(frameChild, children, pageId); } } -void AddChild(AccessibilityElementInfo& nodeInfo, const RefPtr& uiNode) +void GetFrameNodeChildren(const RefPtr& uiNode, std::list>& children) { if (AceType::InstanceOf(uiNode)) { auto frameNode = AceType::DynamicCast(uiNode); if (!frameNode->IsInternal()) { - nodeInfo.AddChild(uiNode->GetId()); + children.emplace_back(frameNode); } } else { for (const auto& frameChild : uiNode->GetChildren()) { - AddChild(nodeInfo, frameChild); + GetFrameNodeChildren(frameChild, children); } } } @@ -578,6 +614,194 @@ void UpdateSupportAction(const RefPtr& node, AccessibilityElement } } +void UpdateAccessibilityElementInfo( + const RefPtr& node, const CommonProperty& commonProperty, AccessibilityElementInfo& nodeInfo) +{ + nodeInfo.SetParent(GetParentId(node)); + std::vector children; + for (const auto& item : node->GetChildren()) { + GetFrameNodeChildren(item, children, commonProperty.pageId); + } + for (const auto& child : children) { + nodeInfo.AddChild(child); + } + + nodeInfo.SetAccessibilityId(node->GetId()); + nodeInfo.SetComponentType(node->GetTag()); + nodeInfo.SetEnabled(node->GetFocusHub() ? node->GetFocusHub()->IsEnabled() : true); + nodeInfo.SetFocusable(node->GetFocusHub() ? node->GetFocusHub()->IsFocusable() : false); + nodeInfo.SetFocused(node->GetFocusHub() ? node->GetFocusHub()->IsCurrentFocus() : false); + nodeInfo.SetAccessibilityFocus(node->GetRenderContext()->GetAccessibilityFocus().value_or(false)); + nodeInfo.SetInspectorKey(node->GetInspectorId().value_or("")); + nodeInfo.SetContent(node->GetAccessibilityProperty()->GetText()); + nodeInfo.SetVisible(node->IsVisible()); + if (node->IsVisible()) { + auto left = node->GetOffsetRelativeToWindow().GetX() + commonProperty.windowLeft; + auto top = node->GetOffsetRelativeToWindow().GetY() + commonProperty.windowTop; + auto right = left + node->GetGeometryNode()->GetFrameRect().Width(); + auto bottom = top + node->GetGeometryNode()->GetFrameRect().Height(); + Accessibility::Rect bounds { left, top, right, bottom }; + nodeInfo.SetRectInScreen(bounds); + } + + nodeInfo.SetWindowId(commonProperty.windowId); + nodeInfo.SetPageId(node->GetPageId()); + + UpdateSupportAction(node, nodeInfo); +} + +// focus move search +void AddFocusableNode(std::list>& nodeList, const RefPtr& node) +{ + nodeList.emplace_back(node); + + std::list> children; + for (const auto& child : node->GetChildren()) { + GetFrameNodeChildren(child, children); + } + + for (const auto& child : children) { + AddFocusableNode(nodeList, child); + } +} + +RefPtr GetNextFocusableNode( + const std::list>& nodeList, RefPtr& node) +{ + auto nodeItem = nodeList.begin(); + for (; nodeItem != nodeList.end(); nodeItem++) { + if ((*nodeItem)->GetId() == node->GetId()) { + break; + } + } + + if (nodeItem != nodeList.end()) { + if (++nodeItem != nodeList.end()) { + return (*nodeItem); + } + } + if (!nodeList.empty()) { + return (*nodeList.begin()); + } + + return nullptr; +} + +RefPtr GetPreviousFocusableNode( + const std::list>& nodeList, RefPtr& node) +{ + auto nodeItem = nodeList.rbegin(); + for (; nodeItem != nodeList.rend(); nodeItem++) { + if ((*nodeItem)->GetId() == node->GetId()) { + break; + } + } + + if (nodeItem != nodeList.rend()) { + if (++nodeItem != nodeList.rend()) { + return (*nodeItem); + } + } + if (!nodeList.empty()) { + return (*nodeList.rbegin()); + } + + return nullptr; +} + +RefPtr FindNodeInRelativeDirection( + const std::list>& nodeList, RefPtr& node, int direction) +{ + switch (direction) { + case FocusMoveDirection::FORWARD: + return GetNextFocusableNode(nodeList, node); + case FocusMoveDirection::BACKWARD: + return GetPreviousFocusableNode(nodeList, node); + default: + break; + } + + return nullptr; +} + +RefPtr FindNodeInAbsoluteDirection( + const std::list>& nodeList, RefPtr& node, const int direction) +{ + auto left = node->GetOffsetRelativeToWindow().GetX(); + auto top = node->GetOffsetRelativeToWindow().GetY(); + auto width = node->GetGeometryNode()->GetFrameRect().Width(); + auto height = node->GetGeometryNode()->GetFrameRect().Height(); + Rect tempBest(left, top, width, height); + auto nodeRect = tempBest; + switch (direction) { + case FocusMoveDirection::LEFT: + tempBest.SetLeft(left + width + 1); + break; + case FocusMoveDirection::RIGHT: + tempBest.SetLeft(left - width - 1); + break; + case FocusMoveDirection::UP: + tempBest.SetTop(top + height + 1); + break; + case FocusMoveDirection::DOWN: + tempBest.SetTop(top - height - 1); + break; + default: + break; + } + + RefPtr nearestNode = nullptr; + for (const auto& nodeItem : nodeList) { + if (nodeItem->GetId() == node->GetId() || nodeItem->IsRootNode()) { + continue; + } + Rect itemRect(nodeItem->GetOffsetRelativeToWindow().GetX(), nodeItem->GetOffsetRelativeToWindow().GetY(), + nodeItem->GetGeometryNode()->GetFrameRect().Width(), nodeItem->GetGeometryNode()->GetFrameRect().Height()); + if (CheckBetterRect(nodeRect, direction, itemRect, tempBest)) { + tempBest = itemRect; + nearestNode = nodeItem; + } + } + LOGI("found %{public}d", nearestNode ? nearestNode->GetId() : -1); + return nearestNode; +} + +// execute action +bool RequestFocus(RefPtr& frameNode) +{ + auto focusHub = frameNode->GetFocusHub(); + if (focusHub) { + return focusHub->RequestFocusImmediately(); + } + return false; +} + +bool ActClick(RefPtr& frameNode) +{ + auto gesture = frameNode->GetEventHub()->GetGestureEventHub(); + if (gesture) { + return gesture->ActClick(); + } + return false; +} + +bool ActLongClick(RefPtr& frameNode) +{ + auto gesture = frameNode->GetEventHub()->GetGestureEventHub(); + if (gesture) { + return gesture->ActLongClick(); + } + return false; +} + +void ClearAccessibilityFocus(const RefPtr& root, int32_t focusNodeId) +{ + auto oldFocusNode = GetInspectorById(root, focusNodeId); + if (oldFocusNode) { + oldFocusNode->GetRenderContext()->UpdateAccessibilityFocus(false); + } +} + } // namespace JsAccessibilityManager::~JsAccessibilityManager() @@ -647,10 +871,7 @@ bool JsAccessibilityManager::SubscribeStateObserver(const int eventType) Accessibility::RetError ret = instance->SubscribeStateObserver(stateObserver_, eventType); LOGD("SubscribeStateObserver:%{public}d", ret); - if (ret != RET_OK) { - return false; - } - return true; + return ret == RET_OK; } bool JsAccessibilityManager::UnsubscribeStateObserver(const int eventType) @@ -667,10 +888,7 @@ bool JsAccessibilityManager::UnsubscribeStateObserver(const int eventType) Accessibility::RetError ret = instance->UnsubscribeStateObserver(stateObserver_, eventType); LOGI("UnsubscribeStateObserver:%{public}d", ret); - if (ret != RET_OK) { - return false; - } - return true; + return ret == RET_OK; } void JsAccessibilityManager::InitializeCallback() @@ -824,10 +1042,6 @@ void JsAccessibilityManager::UpdateNodeChildIds(const RefPtr& void JsAccessibilityManager::DumpHandleEvent(const std::vector& params) { - if (params.empty()) { - DumpLog::GetInstance().Print("Error: params is empty!"); - return; - } if (!(params.size() == EVENT_DUMP_PARAM_LENGTH_LOWER || params.size() == EVENT_DUMP_PARAM_LENGTH_UPPER)) { DumpLog::GetInstance().Print("Error: params length is illegal!"); return; @@ -846,7 +1060,13 @@ void JsAccessibilityManager::DumpHandleEvent(const std::vector& par auto action = static_cast(StringUtils::StringToInt(params[EVENT_DUMP_ACTION_INDEX])); auto op = ConvertAceAction(action); if (AceType::InstanceOf(pipeline)) { - ExecuteActionNG(pipeline, nodeId, op); + pipeline->GetTaskExecutor()->PostTask( + [weak = WeakClaim(this), op, nodeId]() { + auto jsAccessibilityManager = weak.Upgrade(); + CHECK_NULL_VOID(jsAccessibilityManager); + jsAccessibilityManager->ExecuteActionNG(nodeId, op); + }, + TaskExecutor::TaskType::UI); return; } @@ -868,9 +1088,7 @@ void JsAccessibilityManager::DumpHandleEvent(const std::vector& par pipeline->GetTaskExecutor()->PostTask( [weak = WeakClaim(this), op, node, paramsMap, pipeline]() { auto jsAccessibilityManager = weak.Upgrade(); - if (!jsAccessibilityManager) { - return; - } + CHECK_NULL_VOID(jsAccessibilityManager); jsAccessibilityManager->AccessibilityActionEvent( op, paramsMap, node, AceType::DynamicCast(pipeline)); }, @@ -960,7 +1178,7 @@ void JsAccessibilityManager::DumpProperty(const std::vector& params } static void DumpTreeNG( - const RefPtr& parent, int32_t depth, NodeId nodeID, int32_t windowLeft, int32_t windowTop) + const RefPtr& parent, int32_t depth, NodeId nodeID, const CommonProperty& commonProperty) { auto node = GetInspectorById(parent, nodeID); if (!node) { @@ -971,8 +1189,10 @@ static void DumpTreeNG( DumpLog::GetInstance().AddDesc("ID: " + std::to_string(node->GetId())); DumpLog::GetInstance().AddDesc("compid: " + node->GetInspectorId().value_or("")); DumpLog::GetInstance().AddDesc("text: " + node->GetAccessibilityProperty()->GetText()); - DumpLog::GetInstance().AddDesc("top: " + std::to_string(node->GetOffsetRelativeToWindow().GetY() + windowTop)); - DumpLog::GetInstance().AddDesc("left: " + std::to_string(node->GetOffsetRelativeToWindow().GetX() + windowLeft)); + DumpLog::GetInstance().AddDesc( + "top: " + std::to_string(node->GetOffsetRelativeToWindow().GetY() + commonProperty.windowTop)); + DumpLog::GetInstance().AddDesc( + "left: " + std::to_string(node->GetOffsetRelativeToWindow().GetX() + commonProperty.windowLeft)); DumpLog::GetInstance().AddDesc("width: " + std::to_string(node->GetGeometryNode()->GetFrameRect().Width())); DumpLog::GetInstance().AddDesc("height: " + std::to_string(node->GetGeometryNode()->GetFrameRect().Height())); DumpLog::GetInstance().AddDesc("visible: " + std::to_string(node->IsVisible())); @@ -981,14 +1201,15 @@ static void DumpTreeNG( "clickable: " + std::to_string(gestureEventHub ? gestureEventHub->IsClickable() : false)); DumpLog::GetInstance().AddDesc( "checkable: " + std::to_string(node->GetAccessibilityProperty()->IsCheckable())); - DumpLog::GetInstance().Print(depth, node->GetTag(), node->GetChildren().size()); std::vector children; for (const auto& item : node->GetChildren()) { - GetFrameNodeChildren(item, children); + GetFrameNodeChildren(item, children, commonProperty.pageId); } + DumpLog::GetInstance().Print(depth, node->GetTag(), children.size()); + for (auto nodeId : children) { - DumpTreeNG(node, depth + 1, nodeId, windowLeft, windowTop); + DumpTreeNG(node, depth + 1, nodeId, commonProperty); } } @@ -1010,7 +1231,9 @@ void JsAccessibilityManager::DumpTree(int32_t depth, NodeId nodeID) } auto windowLeft = GetWindowLeft(ngPipeline->GetWindowId()); auto windowTop = GetWindowTop(ngPipeline->GetWindowId()); - DumpTreeNG(rootNode, depth, nodeID, windowLeft, windowTop); + auto pageId = ngPipeline->GetStageManager()->GetLastPage()->GetPageId(); + CommonProperty commonProperty { ngPipeline->GetWindowId(), windowLeft, windowTop, pageId }; + DumpTreeNG(rootNode, depth, nodeID, commonProperty); } } @@ -1128,7 +1351,7 @@ void JsAccessibilityManager::SearchElementInfoByAccessibilityId(const int32_t el } auto node = jsAccessibilityManager->GetAccessibilityNodeFromPage(nodeId); if (!node) { - LOGW("AccessibilityNodeInfo can't attach component by Id = %{public}d", nodeId); + LOGW("AccessibilityNodeInfo can't attach component by Id = %{public}d, window:%{public}d", nodeId, windowId_); SetSearchElementInfoByAccessibilityIdResult(callback, infos, requestId); return; } @@ -1163,38 +1386,47 @@ void JsAccessibilityManager::SearchElementInfoByAccessibilityIdNG( auto node = GetInspectorById(rootNode, nodeId); if (!node) { - LOGW("AccessibilityNodeInfo can't attach component by Id = %{public}d", nodeId); + LOGW("AccessibilityNodeInfo can't attach component by Id = %{public}d, window:%{public}d", nodeId, windowId_); return; } - nodeInfo.SetParent(GetParentId(node)); - for (const auto& child : node->GetChildren()) { - AddChild(nodeInfo, child); - } + auto pageId = ngPipeline->GetStageManager()->GetLastPage()->GetPageId(); + CommonProperty commonProperty { ngPipeline->GetWindowId(), GetWindowLeft(ngPipeline->GetWindowId()), + GetWindowTop(ngPipeline->GetWindowId()), pageId }; + UpdateAccessibilityElementInfo(node, commonProperty, nodeInfo); - nodeInfo.SetAccessibilityId(node->GetId()); - nodeInfo.SetComponentType(node->GetTag()); - nodeInfo.SetEnabled(node->GetFocusHub() ? node->GetFocusHub()->IsEnabled() : true); - nodeInfo.SetFocusable(node->GetFocusHub() != nullptr); - nodeInfo.SetFocused(node->GetFocusHub() ? node->GetFocusHub()->IsCurrentFocus() : false); - nodeInfo.SetInspectorKey(node->GetInspectorId().value_or("")); - nodeInfo.SetContent(node->GetAccessibilityProperty()->GetText()); - nodeInfo.SetVisible(node->IsVisible()); - if (node->IsVisible()) { - auto left = node->GetOffsetRelativeToWindow().GetX() + GetWindowLeft(ngPipeline->GetWindowId()); - auto top = node->GetOffsetRelativeToWindow().GetY() + GetWindowTop(ngPipeline->GetWindowId()); - auto right = left + node->GetGeometryNode()->GetFrameRect().Width(); - auto bottom = top + node->GetGeometryNode()->GetFrameRect().Height(); - Accessibility::Rect bounds { left, top, right, bottom }; - nodeInfo.SetRectInScreen(bounds); - } + infos.push_back(nodeInfo); +} - nodeInfo.SetWindowId(ngPipeline->GetWindowId()); - nodeInfo.SetPageId(node->GetPageId()); +void JsAccessibilityManager::SearchElementInfosByTextNG( + int32_t elementId, const std::string& text, std::list& infos) +{ + auto pipeline = context_.Upgrade(); + CHECK_NULL_VOID(pipeline); - UpdateSupportAction(node, nodeInfo); + auto ngPipeline = AceType::DynamicCast(pipeline); + auto rootNode = ngPipeline->GetRootElement(); + CHECK_NULL_VOID(rootNode); - infos.push_back(nodeInfo); + auto node = GetInspectorById(rootNode, elementId); + if (!node) { + LOGW("AccessibilityNodeInfo can't attach component by Id = %{public}d", elementId); + return; + } + + std::list> results; + FindText(node, text, results); + if (results.empty()) { + return; + } + auto pageId = ngPipeline->GetStageManager()->GetLastPage()->GetPageId(); + CommonProperty commonProperty { ngPipeline->GetWindowId(), GetWindowLeft(ngPipeline->GetWindowId()), + GetWindowTop(ngPipeline->GetWindowId()), pageId }; + for (const auto& node : results) { + AccessibilityElementInfo nodeInfo; + UpdateAccessibilityElementInfo(node, commonProperty, nodeInfo); + infos.emplace_back(nodeInfo); + } } void JsAccessibilityManager::JsInteractionOperation::SearchElementInfosByText(const int32_t elementId, @@ -1231,23 +1463,34 @@ void JsAccessibilityManager::SearchElementInfosByText(const int32_t elementId, c LOGW("Text is null"); return; } - auto weak = WeakClaim(this); - auto jsAccessibilityManager = weak.Upgrade(); - if (!jsAccessibilityManager) { + + if (elementId == -1) { return; } - NodeId nodeId = static_cast(elementId); - if (elementId == -1) { + std::list infos; + + auto pipeline = context_.Upgrade(); + if (pipeline) { + if (AceType::InstanceOf(pipeline)) { + SearchElementInfosByTextNG(elementId, text, infos); + SetSearchElementInfoByTextResult(callback, infos, requestId); + return; + } + } + + auto weak = WeakClaim(this); + auto jsAccessibilityManager = weak.Upgrade(); + if (!jsAccessibilityManager) { return; } + NodeId nodeId = elementId; auto node = jsAccessibilityManager->GetAccessibilityNodeFromPage(nodeId); if (!node) { return; } - std::list infos; std::list> nodeList; FindText(node, text, nodeList); if (!nodeList.empty()) { @@ -1296,6 +1539,18 @@ void JsAccessibilityManager::FindFocusedElementInfo(const int32_t elementId, con return; } + auto context = context_.Upgrade(); + if (!context) { + SetFindFocusedElementInfoResult(callback, nodeInfo, requestId); + return; + } + + if (AceType::InstanceOf(context)) { + FindFocusedElementInfoNG(elementId, focusType, nodeInfo); + SetFindFocusedElementInfoResult(callback, nodeInfo, requestId); + return; + } + NodeId nodeId = static_cast(elementId); if (elementId == -1) { nodeId = 0; @@ -1326,6 +1581,37 @@ void JsAccessibilityManager::FindFocusedElementInfo(const int32_t elementId, con SetFindFocusedElementInfoResult(callback, nodeInfo, requestId); } +void JsAccessibilityManager::FindFocusedElementInfoNG( + int32_t elementId, int32_t focusType, Accessibility::AccessibilityElementInfo& info) +{ + auto pipeline = context_.Upgrade(); + CHECK_NULL_VOID(pipeline); + + auto ngPipeline = AceType::DynamicCast(pipeline); + auto rootNode = ngPipeline->GetRootElement(); + CHECK_NULL_VOID(rootNode); + + auto node = GetInspectorById(rootNode, elementId); + if (!node) { + LOGW("AccessibilityNodeInfo can't attach component by Id = %{public}d", elementId); + return; + } + + RefPtr resultNode; + if (focusType == FOCUS_TYPE_ACCESSIBILITY) { + resultNode = FindAccessibilityFocus(node); + } + if (focusType == FOCUS_TYPE_INPUT) { + resultNode = FindInputFocus(node); + } + if (resultNode) { + auto pageId = ngPipeline->GetStageManager()->GetLastPage()->GetPageId(); + CommonProperty commonProperty { ngPipeline->GetWindowId(), GetWindowLeft(ngPipeline->GetWindowId()), + GetWindowTop(ngPipeline->GetWindowId()), pageId }; + UpdateAccessibilityElementInfo(resultNode, commonProperty, info); + } +} + void JsAccessibilityManager::JsInteractionOperation::ExecuteAction(const int32_t elementId, const int32_t action, const std::map& actionArguments, const int32_t requestId, AccessibilityElementOperatorCallback& callback) @@ -1430,6 +1716,58 @@ void JsAccessibilityManager::SendActionEvent(const Accessibility::ActionType& ac SendAccessibilityAsyncEvent(accessibilityEvent); } +bool JsAccessibilityManager::ExecuteActionNG(int32_t elementId, ActionType action) +{ + bool result = false; + auto context = context_.Upgrade(); + CHECK_NULL_RETURN(context, result); + auto ngPipeline = AceType::DynamicCast(context); + ContainerScope instance(ngPipeline->GetInstanceId()); + auto frameNode = GetInspectorById(ngPipeline->GetRootElement(), elementId); + CHECK_NULL_RETURN(frameNode, result); + + switch (action) { + case ActionType::ACCESSIBILITY_ACTION_FOCUS: { + result = RequestFocus(frameNode); + break; + } + case ActionType::ACCESSIBILITY_ACTION_CLICK: { + result = ActClick(frameNode); + break; + } + case ActionType::ACCESSIBILITY_ACTION_LONG_CLICK: { + result = ActLongClick(frameNode); + break; + } + case ActionType::ACCESSIBILITY_ACTION_ACCESSIBILITY_FOCUS: { + if (elementId == currentFocusNodeId_) { + LOGW("This node is focused."); + return result; + } + Framework::ClearAccessibilityFocus(ngPipeline->GetRootElement(), currentFocusNodeId_); + frameNode->GetRenderContext()->UpdateAccessibilityFocus(true); + currentFocusNodeId_ = elementId; + result = true; + break; + } + case ActionType::ACCESSIBILITY_ACTION_CLEAR_ACCESSIBILITY_FOCUS: { + if (elementId != currentFocusNodeId_) { + return result; + } + frameNode->GetRenderContext()->UpdateAccessibilityFocus(false); + currentFocusNodeId_ = -1; + result = true; + break; + } + case ActionType::ACCESSIBILITY_ACTION_SCROLL_FORWARD: + case ActionType::ACCESSIBILITY_ACTION_SCROLL_BACKWARD: + return true; + default: + break; + } + + return result; +} void JsAccessibilityManager::ExecuteAction(const int32_t elementId, const ActionType& action, const std::map actionArguments, const int32_t requestId, @@ -1445,7 +1783,7 @@ void JsAccessibilityManager::ExecuteAction(const int32_t elementId, const Action } if (AceType::InstanceOf(context)) { - actionResult = ExecuteActionNG(context, elementId, action); + actionResult = ExecuteActionNG(elementId, action); } else { auto node = GetAccessibilityNodeFromPage(elementId); if (!node) { @@ -1587,20 +1925,24 @@ void JsAccessibilityManager::JsInteractionOperation::FocusMoveSearch(const int32 void JsAccessibilityManager::FocusMoveSearch(const int32_t elementId, const int32_t direction, const int32_t requestId, Accessibility::AccessibilityElementOperatorCallback& callback) { - auto weak = WeakClaim(this); - auto jsAccessibilityManager = weak.Upgrade(); AccessibilityElementInfo nodeInfo; - auto node = jsAccessibilityManager->GetAccessibilityNodeFromPage((NodeId)elementId); - if (!node) { - LOGW("AccessibilityNodeInfo can't attach component by Id = %{public}d", (NodeId)elementId); + auto context = GetPipelineContext().Upgrade(); + if (!context) { + LOGI("FocusMoveSearch context is null"); nodeInfo.SetValidElement(false); SetFocusMoveSearchResult(callback, nodeInfo, requestId); return; } - auto context = GetPipelineContext().Upgrade(); - if (!context) { - LOGI("FocusMoveSearch context is null"); + if (AceType::InstanceOf(context)) { + FocusMoveSearchNG(elementId, direction, nodeInfo); + SetFocusMoveSearchResult(callback, nodeInfo, requestId); + return; + } + + auto node = GetAccessibilityNodeFromPage(elementId); + if (!node) { + LOGW("AccessibilityNodeInfo can't attach component by Id = %{public}d", (NodeId)elementId); nodeInfo.SetValidElement(false); SetFocusMoveSearchResult(callback, nodeInfo, requestId); return; @@ -1620,15 +1962,15 @@ void JsAccessibilityManager::FocusMoveSearch(const int32_t elementId, const int3 RefPtr resultNode; switch (direction) { - case FOCUS_DIRECTION_FORWARD: - case FOCUS_DIRECTION_BACKWARD: + case FocusMoveDirection::FORWARD: + case FocusMoveDirection::BACKWARD: // forward and backward resultNode = FindNodeInRelativeDirection(nodeList, node, direction); break; - case FOCUS_DIRECTION_UP: - case FOCUS_DIRECTION_DOWN: - case FOCUS_DIRECTION_LEFT: - case FOCUS_DIRECTION_RIGHT: + case FocusMoveDirection::UP: + case FocusMoveDirection::DOWN: + case FocusMoveDirection::LEFT: + case FocusMoveDirection::RIGHT: // up, down, left and right resultNode = FindNodeInAbsoluteDirection(nodeList, node, direction); break; @@ -1638,6 +1980,7 @@ void JsAccessibilityManager::FocusMoveSearch(const int32_t elementId, const int3 if (resultNode) { LOGI("FocusMoveSearch end nodeId:%{public}d", resultNode->GetNodeId()); + auto jsAccessibilityManager = Claim(this); UpdateAccessibilityNodeInfo(resultNode, nodeInfo, jsAccessibilityManager, windowId_, rootNode->GetNodeId()); } @@ -1669,9 +2012,9 @@ RefPtr JsAccessibilityManager::FindNodeInRelativeDirection( const std::list>& nodeList, RefPtr& node, const int direction) { switch (direction) { - case FOCUS_DIRECTION_FORWARD: + case FocusMoveDirection::FORWARD: return GetNextFocusableNode(nodeList, node); - case FOCUS_DIRECTION_BACKWARD: + case FocusMoveDirection::BACKWARD: return GetPreviousFocusableNode(nodeList, node); default: break; @@ -1688,16 +2031,16 @@ RefPtr JsAccessibilityManager::FindNodeInAbsoluteDirection( auto nodeRect = node->GetRect(); switch (direction) { - case FOCUS_DIRECTION_LEFT: + case FocusMoveDirection::LEFT: tempBest.SetLeft(node->GetLeft() + node->GetWidth() + 1); break; - case FOCUS_DIRECTION_RIGHT: + case FocusMoveDirection::RIGHT: tempBest.SetLeft(node->GetLeft() - node->GetWidth() - 1); break; - case FOCUS_DIRECTION_UP: + case FocusMoveDirection::UP: tempBest.SetTop(node->GetTop() + node->GetHeight() + 1); break; - case FOCUS_DIRECTION_DOWN: + case FocusMoveDirection::DOWN: tempBest.SetTop(node->GetTop() - node->GetHeight() - 1); break; default: @@ -1733,7 +2076,7 @@ RefPtr JsAccessibilityManager::GetNextFocusableNode( if (nodeItem != nodeList.end() && ++nodeItem != nodeList.end()) { return (*nodeItem); } - if (nodeList.size() > 0) { + if (!nodeList.empty()) { return (*nodeList.begin()); } @@ -1755,178 +2098,12 @@ RefPtr JsAccessibilityManager::GetPreviousFocusableNode( return (*nodeItem); } - if (nodeList.size() > 0) { + if (!nodeList.empty()) { return (*nodeList.rbegin()); } return nullptr; } -bool JsAccessibilityManager::CheckBetterRect(Rect nodeRect, const int direction, Rect itemRect, Rect tempBest) -{ - if (!IsCandidateRect(nodeRect, itemRect, direction)) { - return false; - } - - if (!IsCandidateRect(nodeRect, tempBest, direction)) { - return true; - } - - // now both of item and tempBest are all at the direction of node. - if (OutrightBetter(nodeRect, direction, itemRect, tempBest)) { - return true; - } - - if (OutrightBetter(nodeRect, direction, tempBest, itemRect)) { - return false; - } - - // otherwise, do fudge-tastic comparison of the major and minor axis - return (GetWeightedDistanceFor( - MajorAxisDistance(nodeRect, itemRect, direction), MinorAxisDistance(nodeRect, itemRect, direction)) < - GetWeightedDistanceFor( - MajorAxisDistance(nodeRect, tempBest, direction), MinorAxisDistance(nodeRect, tempBest, direction))); -} - -bool JsAccessibilityManager::IsCandidateRect(Rect nodeRect, Rect itemRect, const int direction) -{ - switch (direction) { - case FOCUS_DIRECTION_LEFT: - return nodeRect.Left() > itemRect.Left() && nodeRect.Right() > itemRect.Right(); - case FOCUS_DIRECTION_RIGHT: - return nodeRect.Left() < itemRect.Left() && nodeRect.Right() < itemRect.Right(); - case FOCUS_DIRECTION_UP: - return nodeRect.Top() > itemRect.Top() && nodeRect.Bottom() > itemRect.Bottom(); - case FOCUS_DIRECTION_DOWN: - return nodeRect.Top() < itemRect.Top() && nodeRect.Bottom() < itemRect.Bottom(); - default: - break; - } - return false; -} - -// Check whether rect1 is outright better than rect2. -bool JsAccessibilityManager::OutrightBetter(Rect nodeRect, const int direction, Rect Rect1, Rect Rect2) -{ - bool rect1InSrcBeam = CheckRectBeam(nodeRect, Rect1, direction); - bool rect2InSrcBeam = CheckRectBeam(nodeRect, Rect2, direction); - if (rect2InSrcBeam || !rect1InSrcBeam) { - return false; - } - - if (!IsToDirectionOf(nodeRect, Rect2, direction)) { - return true; - } - - // for direction left or right - if (direction == FOCUS_DIRECTION_LEFT || direction == FOCUS_DIRECTION_RIGHT) { - return true; - } - - return (MajorAxisDistance(nodeRect, Rect1, direction) < MajorAxisDistanceToFarEdge(nodeRect, Rect2, direction)); -} - -bool JsAccessibilityManager::CheckRectBeam(Rect nodeRect, Rect itemRect, const int direction) -{ - switch (direction) { - case FOCUS_DIRECTION_LEFT: - case FOCUS_DIRECTION_RIGHT: - return nodeRect.Top() < itemRect.Bottom() && itemRect.Top() < nodeRect.Bottom(); - case FOCUS_DIRECTION_UP: - case FOCUS_DIRECTION_DOWN: - return nodeRect.Left() < itemRect.Right() && itemRect.Left() < nodeRect.Right(); - default: - break; - } - return false; -} - -bool JsAccessibilityManager::IsToDirectionOf(Rect nodeRect, Rect itemRect, const int direction) -{ - switch (direction) { - case FOCUS_DIRECTION_LEFT: - return nodeRect.Left() >= itemRect.Right(); - case FOCUS_DIRECTION_RIGHT: - return nodeRect.Right() <= itemRect.Left(); - case FOCUS_DIRECTION_UP: - return nodeRect.Top() >= itemRect.Bottom(); - case FOCUS_DIRECTION_DOWN: - return nodeRect.Bottom() <= itemRect.Top(); - default: - break; - } - return false; -} - -double JsAccessibilityManager::MajorAxisDistanceToFarEdge(Rect nodeRect, Rect itemRect, const int direction) -{ - double distance = 0.0; - switch (direction) { - case FOCUS_DIRECTION_LEFT: - distance = nodeRect.Left() - itemRect.Left(); - break; - case FOCUS_DIRECTION_RIGHT: - distance = nodeRect.Right() - itemRect.Right(); - break; - case FOCUS_DIRECTION_UP: - distance = nodeRect.Top() - itemRect.Top(); - break; - case FOCUS_DIRECTION_DOWN: - distance = nodeRect.Bottom() - itemRect.Bottom(); - break; - default: - break; - } - - return distance > 1.0 ? distance : 1.0; -} - -double JsAccessibilityManager::MajorAxisDistance(Rect nodeRect, Rect itemRect, const int direction) -{ - double distance = 0.0; - switch (direction) { - case FOCUS_DIRECTION_LEFT: - distance = nodeRect.Left() - itemRect.Right(); - break; - case FOCUS_DIRECTION_RIGHT: - distance = nodeRect.Right() - itemRect.Left(); - break; - case FOCUS_DIRECTION_UP: - distance = nodeRect.Top() - itemRect.Bottom(); - break; - case FOCUS_DIRECTION_DOWN: - distance = nodeRect.Bottom() - itemRect.Top(); - break; - default: - break; - } - - return distance > 0.0 ? distance : 0.0; -} - -double JsAccessibilityManager::MinorAxisDistance(Rect nodeRect, Rect itemRect, const int direction) -{ - double distance = 0.0; - switch (direction) { - case FOCUS_DIRECTION_LEFT: - case FOCUS_DIRECTION_RIGHT: - distance = (nodeRect.Top() + nodeRect.Bottom()) / 2 - (itemRect.Top() + itemRect.Bottom()) / 2; - break; - case FOCUS_DIRECTION_UP: - case FOCUS_DIRECTION_DOWN: - distance = (nodeRect.Left() + nodeRect.Right()) / 2 - (itemRect.Left() + itemRect.Right()) / 2; - break; - default: - break; - } - - return distance > 0.0 ? distance : -distance; -} - -double JsAccessibilityManager::GetWeightedDistanceFor(double majorAxisDistance, double minorAxisDistance) -{ - return WEIGHTED_VALUE * majorAxisDistance * majorAxisDistance + minorAxisDistance * minorAxisDistance; -} - bool JsAccessibilityManager::RequestAccessibilityFocus(const RefPtr& node) { LOGI("RequestAccessibilityFocus"); @@ -1971,6 +2148,49 @@ bool JsAccessibilityManager::ClearCurrentFocus() return false; } +void JsAccessibilityManager::FocusMoveSearchNG( + int32_t elementId, int32_t direction, Accessibility::AccessibilityElementInfo& info) +{ + auto pipeline = context_.Upgrade(); + CHECK_NULL_VOID(pipeline); + + auto ngPipeline = AceType::DynamicCast(pipeline); + auto rootNode = ngPipeline->GetRootElement(); + CHECK_NULL_VOID(rootNode); + + auto node = GetInspectorById(rootNode, elementId); + if (!node) { + LOGW("AccessibilityNodeInfo can't attach component by Id = %{public}d", elementId); + return; + } + + std::list> nodeList; + Framework::AddFocusableNode(nodeList, rootNode); + + RefPtr resultNode; + switch (direction) { + case FocusMoveDirection::FORWARD: + case FocusMoveDirection::BACKWARD: + resultNode = Framework::FindNodeInRelativeDirection(nodeList, node, direction); + break; + case FocusMoveDirection::UP: + case FocusMoveDirection::DOWN: + case FocusMoveDirection::LEFT: + case FocusMoveDirection::RIGHT: + resultNode = Framework::FindNodeInAbsoluteDirection(nodeList, node, direction); + break; + default: + break; + } + + if (resultNode) { + auto pageId = ngPipeline->GetStageManager()->GetLastPage()->GetPageId(); + CommonProperty commonProperty { ngPipeline->GetWindowId(), GetWindowLeft(ngPipeline->GetWindowId()), + GetWindowTop(ngPipeline->GetWindowId()), pageId }; + UpdateAccessibilityElementInfo(resultNode, commonProperty, info); + } +} + // AccessibilitySystemAbilityClient will release callback after DeregisterElementOperator void JsAccessibilityManager::SetSearchElementInfoByAccessibilityIdResult(AccessibilityElementOperatorCallback& callback, const std::list& infos, const int32_t requestId) diff --git a/adapter/ohos/osal/js_accessibility_manager.h b/adapter/ohos/osal/js_accessibility_manager.h index 95240496ae8..3ebf2380644 100644 --- a/adapter/ohos/osal/js_accessibility_manager.h +++ b/adapter/ohos/osal/js_accessibility_manager.h @@ -35,7 +35,7 @@ class JsAccessibilityManager : public AccessibilityNodeManager { public: JsAccessibilityManager() = default; - virtual ~JsAccessibilityManager(); + ~JsAccessibilityManager() override; // JsAccessibilityManager overrides functions. void InitializeCallback() override; @@ -168,19 +168,19 @@ private: const std::list>& nodeList, RefPtr& node); RefPtr GetPreviousFocusableNode( const std::list>& nodeList, RefPtr& node); - bool CheckBetterRect(Rect nodeRect, const int direction, Rect itemRect, Rect tempBest); - bool IsCandidateRect(Rect nodeRect, Rect itemRect, const int direction); - bool OutrightBetter(Rect nodeRect, const int direction, Rect Rect1, Rect Rect2); - bool CheckRectBeam(Rect nodeRect, Rect itemRect, const int direction); - bool IsToDirectionOf(Rect nodeRect, Rect itemRect, const int direction); - double MajorAxisDistanceToFarEdge(Rect nodeRect, Rect itemRect, const int direction); - double MajorAxisDistance(Rect nodeRect, Rect itemRect, const int direction); - double MinorAxisDistance(Rect nodeRect, Rect itemRect, const int direction); - double GetWeightedDistanceFor(double majorAxisDistance, double minorAxisDistance); void SearchElementInfoByAccessibilityIdNG( int32_t elementId, int32_t mode, std::list& infos); + void SearchElementInfosByTextNG( + int32_t elementId, const std::string& text, std::list& infos); + + void FindFocusedElementInfoNG(int32_t elementId, int32_t focusType, Accessibility::AccessibilityElementInfo& info); + + void FocusMoveSearchNG(int32_t elementId, int32_t direction, Accessibility::AccessibilityElementInfo& info); + + bool ExecuteActionNG(int32_t elementId, Accessibility::ActionType action); + void SetSearchElementInfoByAccessibilityIdResult(Accessibility::AccessibilityElementOperatorCallback& callback, const std::list& infos, const int32_t requestId); diff --git a/frameworks/bridge/declarative_frontend/ng/page_router_manager.cpp b/frameworks/bridge/declarative_frontend/ng/page_router_manager.cpp index 7fe42befdce..85ec6cc79ff 100644 --- a/frameworks/bridge/declarative_frontend/ng/page_router_manager.cpp +++ b/frameworks/bridge/declarative_frontend/ng/page_router_manager.cpp @@ -506,6 +506,7 @@ void PageRouterManager::LoadPage(int32_t pageId, const RouterPageInfo& target, c auto pagePattern = AceType::MakeRefPtr(entryPageInfo); auto pageNode = FrameNode::CreateFrameNode(V2::PAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), pagePattern); + pageNode->SetHostPageId(pageId); pageRouterStack_.emplace_back(pageNode); auto result = loadJs_(target.path); if (!result) { @@ -529,6 +530,7 @@ void PageRouterManager::LoadCard(int32_t pageId, const RouterPageInfo& target, c auto pagePattern = AceType::MakeRefPtr(entryPageInfo); auto pageNode = FrameNode::CreateFrameNode(V2::PAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), pagePattern); + pageNode->SetHostPageId(pageId); pageRouterStack_.emplace_back(pageNode); if (!loadCard_) { diff --git a/frameworks/core/accessibility/accessibility_utils.cpp b/frameworks/core/accessibility/accessibility_utils.cpp index 8eca508655e..5ef06963ba0 100644 --- a/frameworks/core/accessibility/accessibility_utils.cpp +++ b/frameworks/core/accessibility/accessibility_utils.cpp @@ -55,4 +55,178 @@ const char ACCESSIBILITY_TAG_CLOCK[] = "clock"; const char ACCESSIBILITY_TAG_DIALOG[] = "dialog"; const char ACCESSIBILITY_TAG_SEARCH[] = "search"; +const int32_t WEIGHTED_VALUE = 13; +const int32_t FOCUS_DIRECTION_UP = 1; +const int32_t FOCUS_DIRECTION_DOWN = 1 << 1; +const int32_t FOCUS_DIRECTION_LEFT = 1 << 2; +const int32_t FOCUS_DIRECTION_RIGHT = 1 << 3; +const int32_t FOCUS_DIRECTION_FORWARD = 1 << 4; +const int32_t FOCUS_DIRECTION_BACKWARD = 1 << 5; + +static bool CheckRectBeam(Rect nodeRect, Rect itemRect, const int direction) +{ + switch (direction) { + case FOCUS_DIRECTION_LEFT: + case FOCUS_DIRECTION_RIGHT: + return nodeRect.Top() < itemRect.Bottom() && itemRect.Top() < nodeRect.Bottom(); + case FOCUS_DIRECTION_UP: + case FOCUS_DIRECTION_DOWN: + return nodeRect.Left() < itemRect.Right() && itemRect.Left() < nodeRect.Right(); + default: + break; + } + return false; +} + +static bool IsToDirectionOf(Rect nodeRect, Rect itemRect, const int direction) +{ + switch (direction) { + case FOCUS_DIRECTION_LEFT: + return nodeRect.Left() >= itemRect.Right(); + case FOCUS_DIRECTION_RIGHT: + return nodeRect.Right() <= itemRect.Left(); + case FOCUS_DIRECTION_UP: + return nodeRect.Top() >= itemRect.Bottom(); + case FOCUS_DIRECTION_DOWN: + return nodeRect.Bottom() <= itemRect.Top(); + default: + break; + } + return false; +} + +static double MajorAxisDistanceToFarEdge(Rect nodeRect, Rect itemRect, const int direction) +{ + double distance = 0.0; + switch (direction) { + case FOCUS_DIRECTION_LEFT: + distance = nodeRect.Left() - itemRect.Left(); + break; + case FOCUS_DIRECTION_RIGHT: + distance = nodeRect.Right() - itemRect.Right(); + break; + case FOCUS_DIRECTION_UP: + distance = nodeRect.Top() - itemRect.Top(); + break; + case FOCUS_DIRECTION_DOWN: + distance = nodeRect.Bottom() - itemRect.Bottom(); + break; + default: + break; + } + + return distance > 1.0 ? distance : 1.0; +} + +static double MajorAxisDistance(Rect nodeRect, Rect itemRect, const int direction) +{ + double distance = 0.0; + switch (direction) { + case FOCUS_DIRECTION_LEFT: + distance = nodeRect.Left() - itemRect.Right(); + break; + case FOCUS_DIRECTION_RIGHT: + distance = nodeRect.Right() - itemRect.Left(); + break; + case FOCUS_DIRECTION_UP: + distance = nodeRect.Top() - itemRect.Bottom(); + break; + case FOCUS_DIRECTION_DOWN: + distance = nodeRect.Bottom() - itemRect.Top(); + break; + default: + break; + } + + return distance > 0.0 ? distance : 0.0; +} + +static double MinorAxisDistance(Rect nodeRect, Rect itemRect, const int direction) +{ + double distance = 0.0; + switch (direction) { + case FOCUS_DIRECTION_LEFT: + case FOCUS_DIRECTION_RIGHT: + distance = (nodeRect.Top() + nodeRect.Bottom()) / 2 - (itemRect.Top() + itemRect.Bottom()) / 2; + break; + case FOCUS_DIRECTION_UP: + case FOCUS_DIRECTION_DOWN: + distance = (nodeRect.Left() + nodeRect.Right()) / 2 - (itemRect.Left() + itemRect.Right()) / 2; + break; + default: + break; + } + + return distance > 0.0 ? distance : -distance; +} + +static double GetWeightedDistanceFor(double majorAxisDistance, double minorAxisDistance) +{ + return WEIGHTED_VALUE * majorAxisDistance * majorAxisDistance + minorAxisDistance * minorAxisDistance; +} + +static bool IsCandidateRect(Rect nodeRect, Rect itemRect, const int direction) +{ + switch (direction) { + case FOCUS_DIRECTION_LEFT: + return nodeRect.Left() > itemRect.Left() && nodeRect.Right() > itemRect.Right(); + case FOCUS_DIRECTION_RIGHT: + return nodeRect.Left() < itemRect.Left() && nodeRect.Right() < itemRect.Right(); + case FOCUS_DIRECTION_UP: + return nodeRect.Top() > itemRect.Top() && nodeRect.Bottom() > itemRect.Bottom(); + case FOCUS_DIRECTION_DOWN: + return nodeRect.Top() < itemRect.Top() && nodeRect.Bottom() < itemRect.Bottom(); + default: + break; + } + return false; +} + +// Check whether rect1 is outright better than rect2. +static bool OutrightBetter(Rect nodeRect, const int direction, Rect Rect1, Rect Rect2) +{ + bool rect1InSrcBeam = CheckRectBeam(nodeRect, Rect1, direction); + bool rect2InSrcBeam = CheckRectBeam(nodeRect, Rect2, direction); + if (rect2InSrcBeam || !rect1InSrcBeam) { + return false; + } + + if (!IsToDirectionOf(nodeRect, Rect2, direction)) { + return true; + } + + // for direction left or right + if (direction == FOCUS_DIRECTION_LEFT || direction == FOCUS_DIRECTION_RIGHT) { + return true; + } + + return (MajorAxisDistance(nodeRect, Rect1, direction) < MajorAxisDistanceToFarEdge(nodeRect, Rect2, direction)); +} + +bool CheckBetterRect(Rect nodeRect, const int direction, Rect itemRect, Rect tempBest) +{ + if (!IsCandidateRect(nodeRect, itemRect, direction)) { + return false; + } + + if (!IsCandidateRect(nodeRect, tempBest, direction)) { + return true; + } + + // now both of item and tempBest are all at the direction of node. + if (OutrightBetter(nodeRect, direction, itemRect, tempBest)) { + return true; + } + + if (OutrightBetter(nodeRect, direction, tempBest, itemRect)) { + return false; + } + + // otherwise, do fudge-tastic comparison of the major and minor axis + return (GetWeightedDistanceFor( + MajorAxisDistance(nodeRect, itemRect, direction), MinorAxisDistance(nodeRect, itemRect, direction)) < + GetWeightedDistanceFor( + MajorAxisDistance(nodeRect, tempBest, direction), MinorAxisDistance(nodeRect, tempBest, direction))); +} + } // namespace OHOS::Ace \ No newline at end of file diff --git a/frameworks/core/accessibility/accessibility_utils.h b/frameworks/core/accessibility/accessibility_utils.h index b999fd06431..0d47a209abb 100644 --- a/frameworks/core/accessibility/accessibility_utils.h +++ b/frameworks/core/accessibility/accessibility_utils.h @@ -19,6 +19,8 @@ #include #include +#include "base/geometry/rect.h" + namespace OHOS::Ace { // define accessibility node tags @@ -60,6 +62,13 @@ extern const char ACCESSIBILITY_TAG_CLOCK[]; extern const char ACCESSIBILITY_TAG_DIALOG[]; extern const char ACCESSIBILITY_TAG_SEARCH[]; +extern const int32_t FOCUS_DIRECTION_UP; +extern const int32_t FOCUS_DIRECTION_DOWN; +extern const int32_t FOCUS_DIRECTION_LEFT; +extern const int32_t FOCUS_DIRECTION_RIGHT; +extern const int32_t FOCUS_DIRECTION_FORWARD; +extern const int32_t FOCUS_DIRECTION_BACKWARD; + enum class AccessibilityEventType : size_t { CLICK = 0x00000001, LONG_PRESS = 0x00000002, @@ -146,6 +155,8 @@ struct AceCollectionItemInfo { int32_t column = 0; }; +bool CheckBetterRect(Rect nodeRect, const int direction, Rect itemRect, Rect tempBest); + } // namespace OHOS::Ace #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_ACCESSIBILITY_ACCESSIBILITY_UTILS_H diff --git a/frameworks/core/components_ng/base/frame_node.cpp b/frameworks/core/components_ng/base/frame_node.cpp index 111cd1638e6..3ea44e52f1f 100644 --- a/frameworks/core/components_ng/base/frame_node.cpp +++ b/frameworks/core/components_ng/base/frame_node.cpp @@ -492,6 +492,11 @@ RefPtr FrameNode::CreatePaintWrapper() paintWrapper->SetNodePaintMethod(paintMethod); return paintWrapper; } + if (renderContext_->GetAccessibilityFocus().value_or(false)) { + auto paintWrapper = MakeRefPtr(renderContext_, geometryNode_->Clone(), paintProperty_); + paintWrapper->SetNodePaintMethod(MakeRefPtr()); + return paintWrapper; + } return nullptr; } diff --git a/frameworks/core/components_ng/base/ui_node.cpp b/frameworks/core/components_ng/base/ui_node.cpp index c3404e0df21..e19126d6518 100644 --- a/frameworks/core/components_ng/base/ui_node.cpp +++ b/frameworks/core/components_ng/base/ui_node.cpp @@ -141,6 +141,9 @@ void UINode::MountToParent(const RefPtr& parent, int32_t slot) { CHECK_NULL_VOID(parent); parent->AddChild(AceType::Claim(this), slot); + if (parent->GetPageId() != 0) { + SetHostPageId(parent->GetPageId()); + } } void UINode::OnRemoveFromParent() diff --git a/frameworks/core/components_ng/base/ui_node.h b/frameworks/core/components_ng/base/ui_node.h index 6c50bf12da7..872c4daebb2 100644 --- a/frameworks/core/components_ng/base/ui_node.h +++ b/frameworks/core/components_ng/base/ui_node.h @@ -143,6 +143,9 @@ public: void SetHostPageId(int32_t id) { hostPageId_ = id; + for (auto& child : children_) { + child->SetHostPageId(id); + } } void SetRemoveSilently(bool removeSilently) diff --git a/frameworks/core/components_ng/render/adapter/rosen_render_context.cpp b/frameworks/core/components_ng/render/adapter/rosen_render_context.cpp index c3e975b47df..9949de07793 100644 --- a/frameworks/core/components_ng/render/adapter/rosen_render_context.cpp +++ b/frameworks/core/components_ng/render/adapter/rosen_render_context.cpp @@ -528,6 +528,45 @@ void RosenRenderContext::OnBorderStyleUpdate(const BorderStyleProperty& value) RequestNextFrame(); } +void RosenRenderContext::OnAccessibilityFocusUpdate(bool /* isAccessibilityFocus */) +{ + auto uiNode = GetHost(); + CHECK_NULL_VOID(uiNode); + uiNode->MarkDirtyNode(false, true, PROPERTY_UPDATE_RENDER); + RequestNextFrame(); +} + +void RosenRenderContext::PaintAccessibilityFocus() +{ + CHECK_NULL_VOID(rsNode_); + constexpr uint32_t ACCESSIBILITY_FOCUS_COLOR = 0xbf39b500; + constexpr double ACCESSIBILITY_FOCUS_WIDTH = 4.0; + constexpr double ACCESSIBILITY_FOCUS_RADIUS_X = 2.0; + constexpr double ACCESSIBILITY_FOCUS_RADIUS_Y = 2.0; + + auto paintAccessibilityFocusTask = [weak = WeakClaim(this)](std::shared_ptr canvas) { + auto rosenRenderContext = weak.Upgrade(); + CHECK_NULL_VOID(rosenRenderContext); + auto paintRect = rosenRenderContext->GetPaintRectWithoutTransform(); + if (NearZero(paintRect.Width()) || NearZero(paintRect.Height())) { + LOGE("PaintAccessibilityFocus return"); + return; + } + RSCanvas rsCanvas(&canvas); + RSPen pen; + pen.SetAntiAlias(true); + pen.SetColor(ACCESSIBILITY_FOCUS_COLOR); + pen.SetWidth(ACCESSIBILITY_FOCUS_WIDTH); + rsCanvas.AttachPen(pen); + rsCanvas.Save(); + RSRect rect(0, 0, paintRect.Width(), paintRect.Height()); + RSRoundRect rrect(rect, ACCESSIBILITY_FOCUS_RADIUS_X, ACCESSIBILITY_FOCUS_RADIUS_Y); + rsCanvas.DrawRoundRect(rrect); + rsCanvas.Restore(); + }; + rsNode_->DrawOnNode(Rosen::RSModifierType::OVERLAY_STYLE, paintAccessibilityFocusTask); +} + void RosenRenderContext::PaintBorderImage() { CHECK_NULL_VOID(rsNode_); diff --git a/frameworks/core/components_ng/render/adapter/rosen_render_context.h b/frameworks/core/components_ng/render/adapter/rosen_render_context.h index 260adc5d49d..d53eabc1f5b 100644 --- a/frameworks/core/components_ng/render/adapter/rosen_render_context.h +++ b/frameworks/core/components_ng/render/adapter/rosen_render_context.h @@ -46,7 +46,7 @@ public: void SyncGeometryProperties(GeometryNode* geometryNode) override; - void SyncGeometryProperties(const RectF& rectF) override; + void SyncGeometryProperties(const RectF& paintRect) override; void RebuildFrame(FrameNode* self, const std::list>& children) override; @@ -163,6 +163,9 @@ public: const AnimationOption& option, const TransitionOptions& transOptions, bool isTransitionIn) override; void OpacityAnimation(const AnimationOption& option, double begin, double end) override; + + void PaintAccessibilityFocus() override; + private: void OnBackgroundColorUpdate(const Color& value) override; void OnBackgroundImageUpdate(const ImageSourceInfo& imageSourceInfo) override; @@ -213,6 +216,8 @@ private: void OnOverlayTextUpdate(const OverlayOptions& overlay) override; void OnMotionPathUpdate(const MotionPathOption& motionPath) override; + void OnAccessibilityFocusUpdate(bool isAccessibilityFocus) override; + void ReCreateRsNodeTree(const std::list>& children); void NotifyTransitionInner(const SizeF& frameSize, bool isTransitionIn); diff --git a/frameworks/core/components_ng/render/paint_wrapper.cpp b/frameworks/core/components_ng/render/paint_wrapper.cpp index da41aa6f769..e3455960cd3 100644 --- a/frameworks/core/components_ng/render/paint_wrapper.cpp +++ b/frameworks/core/components_ng/render/paint_wrapper.cpp @@ -75,6 +75,10 @@ void PaintWrapper::FlushRender() renderContext->FlushOverlayDrawFunction(std::move(overlayDraw)); } + if (renderContext->GetAccessibilityFocus().value_or(false)) { + renderContext->PaintAccessibilityFocus(); + } + renderContext->StopRecordingIfNeeded(); } } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/render/render_context.h b/frameworks/core/components_ng/render/render_context.h index 512cd97351c..d7310dc6093 100644 --- a/frameworks/core/components_ng/render/render_context.h +++ b/frameworks/core/components_ng/render/render_context.h @@ -190,6 +190,8 @@ public: return sharedTransitionOption_ != nullptr; } + virtual void PaintAccessibilityFocus() {}; + // transform matrix ACE_DEFINE_PROPERTY_ITEM_FUNC_WITHOUT_GROUP(TransformMatrix, Matrix4); @@ -275,6 +277,9 @@ public: ACE_DEFINE_PROPERTY_GROUP(Motion, MotionPathProperty); ACE_DEFINE_PROPERTY_FUNC_WITH_GROUP(Motion, MotionPath, MotionPathOption) + // accessibility + ACE_DEFINE_PROPERTY_ITEM_FUNC_WITHOUT_GROUP(AccessibilityFocus, bool); + protected: RenderContext() = default; std::shared_ptr sharedTransitionOption_; @@ -329,6 +334,7 @@ protected: virtual void OnOverlayTextUpdate(const OverlayOptions& overlay) {} virtual void OnMotionPathUpdate(const MotionPathOption& motionPath) {} + virtual void OnAccessibilityFocusUpdate(bool isAccessibilityFocus) {} private: std::function requestFrame_; -- Gitee