Merge branch 'weekly_20241111' of gitee.com:openharmony/arkui_ace_engine into master

Signed-off-by: liulong <liusir666@163.com>
This commit is contained in:
liulong 2024-11-14 02:38:45 +00:00 committed by Gitee
commit 093a3092bf
404 changed files with 9735 additions and 3815 deletions

View File

@ -177,30 +177,30 @@ advanced_ui_component/atomicserviceweb/ @giteewzp
advanced_ui_component/BUILD.gn @arkuipopupwindow
advanced_ui_component/chipgroup/ @arkuipopupwindow
advanced_ui_component/chip/ @arkuipopupwindow
advanced_ui_component/composelistitem/ @hanyujia1
advanced_ui_component/composetitlebar/ @hanyujia1
advanced_ui_component/composelistitem/ @guozonghao11
advanced_ui_component/composetitlebar/ @guozonghao11
advanced_ui_component/counter/ @arkuipopupwindow
advanced_ui_component/dialog/ @hanyujia1
advanced_ui_component/dialog/ @guozonghao11
advanced_ui_component/downloadfilebutton/
advanced_ui_component/editabletitlebar/ @hanyujia1
advanced_ui_component/exceptionprompt/ @hanyujia1
advanced_ui_component/filter/ @hanyujia1
advanced_ui_component/editabletitlebar/ @guozonghao11
advanced_ui_component/exceptionprompt/ @guozonghao11
advanced_ui_component/filter/ @guozonghao11
advanced_ui_component/foldsplitcontainer/ @arkuilayout
advanced_ui_component/formmenu/
advanced_ui_component/fullscreenlaunchcomponent/ @arkuiabilitygroup
advanced_ui_component/gridobjectsortcomponent/ @hanyujia1
advanced_ui_component/gridobjectsortcomponent/ @guozonghao11
advanced_ui_component/interstitialdialogaction/ @giteewzp
advanced_ui_component/popup/ @arkuipopupwindow
advanced_ui_component/progressbutton/ @hanyujia1
advanced_ui_component/progressbutton/ @guozonghao11
advanced_ui_component/segmentbutton/ @arkuipopupwindow
advanced_ui_component/selectionmenu/ @huawei_g_five
advanced_ui_component/selecttitlebar/ @hanyujia1
advanced_ui_component/splitlayout/ @hanyujia1
advanced_ui_component/subheader/ @hanyujia1
advanced_ui_component/swiperefresher/ @hanyujia1
advanced_ui_component/tabtitlebar/ @hanyujia1
advanced_ui_component/toolbar/ @hanyujia1
advanced_ui_component/treeview/ @hanyujia1
advanced_ui_component/selecttitlebar/ @guozonghao11
advanced_ui_component/splitlayout/ @guozonghao11
advanced_ui_component/subheader/ @guozonghao11
advanced_ui_component/swiperefresher/ @guozonghao11
advanced_ui_component/tabtitlebar/ @guozonghao11
advanced_ui_component/toolbar/ @guozonghao11
advanced_ui_component/treeview/ @guozonghao11
[Extend UI Components]
component_ext/arc_swiper/
@ -661,7 +661,7 @@ frameworks/bridge/declarative_frontend/ark_mock/ @arkuiframework
frameworks/bridge/declarative_frontend/ark_node/ @arkuiframework
frameworks/bridge/declarative_frontend/ark_prefetcher/ @arkuistatemgmt
frameworks/bridge/declarative_frontend/ark_shape/ @arkui_superman
frameworks/bridge/declarative_frontend/ark_theme/ @hanyujia1
frameworks/bridge/declarative_frontend/ark_theme/ @guozonghao11
frameworks/bridge/declarative_frontend/BUILD.gn @arkui_architecture
frameworks/bridge/declarative_frontend/declarative_frontend.cpp @arkuiframework
frameworks/bridge/declarative_frontend/declarative_frontend.h @arkuiframework
@ -3053,7 +3053,7 @@ test/unittest/core/pattern/rating/ @arkuipopupwindow
test/unittest/core/pattern/recycle_node/ @Divanolin
test/unittest/core/pattern/refresh/ @arkuiscroll
test/unittest/core/pattern/relative_container/
test/unittest/core/pattern/render_node/ @arkuilayout
test/unittest/core/pattern/render_node/ @arkuiframework
test/unittest/core/pattern/rich_editor/ @zhuweifeng94
test/unittest/core/pattern/rich_editor_drag/ @zhuweifeng94
test/unittest/core/pattern/scroll/ @arkuiscroll

View File

@ -24,7 +24,9 @@
"https://gitee.com/piggyguy",
"https://gitee.com/zhaojian2021",
"https://gitee.com/benb365",
"https://gitee.com/zhou-chaobo"
"https://gitee.com/zhou-chaobo",
"https://gitee.com/liukaii",
"https://gitee.com/yihao-lin"
],
"https://gitee.com/arkui_architecture": [
"https://gitee.com/sunfei2021",
@ -148,6 +150,10 @@
"info": "[拖拽/事件手势/组件截图 jiadexiang]",
"group": "https://gitee.com/arkuievent"
},
"https://gitee.com/liukaii": {
"info": "[拖拽 liukai]",
"group": "https://gitee.com/arkuievent"
},
"https://gitee.com/zhaojian2021": {
"info": "[事件手势 zhaojian]",
"group": "https://gitee.com/arkuievent"
@ -160,6 +166,10 @@
"info": "[焦点/根视图/通用属性 liuyongchang]",
"group": "https://gitee.com/arkuievent"
},
"https://gitee.com/yihao-lin": {
"info": "[焦点/根视图/通用属性 linyihao]",
"group": "https://gitee.com/arkuievent"
},
"https://gitee.com/sunfei2021": {
"info": "[架构/编译gn相关 sunfei]",
"group": "https://gitee.com/arkui_architecture"

View File

@ -2104,6 +2104,21 @@ void AceContainer::AttachView(std::shared_ptr<Window> window, const RefPtr<AceVi
TaskExecutor::TaskType::PLATFORM, "ArkUIStatusBarColorChanged");
};
pipelineContext_->SetStatusBarEventHandler(setStatusBarEventHandler);
auto uiExtensionEventCallback = [weak = WeakClaim(this)] (uint32_t eventId) {
auto container = weak.Upgrade();
CHECK_NULL_VOID(container);
container->FireUIExtensionEventCallback(eventId);
};
pipelineContext_->SetUIExtensionEventCallback(uiExtensionEventCallback);
auto accessibilityEventCallback = [weak = WeakClaim(this)] (uint32_t eventId, int64_t parameter) {
auto container = weak.Upgrade();
CHECK_NULL_VOID(container);
container->FireAccessibilityEventCallback(eventId, parameter);
};
pipelineContext_->SetAccessibilityEventCallback(accessibilityEventCallback);
if (GetSettings().usePlatformAsUIThread) {
FrameReport::GetInstance().Init();
} else {
@ -2911,6 +2926,15 @@ bool AceContainer::IsUIExtensionWindow()
return uiWindow_->GetType() == Rosen::WindowType::WINDOW_TYPE_UI_EXTENSION;
}
void AceContainer::FireUIExtensionEventCallback(uint32_t eventId)
{
if (!IsUIExtensionWindow()) {
return;
}
ACE_SCOPED_TRACE("FireUIExtensionEventCallback event[%u]", eventId);
uiWindow_->NotifyExtensionEventAsync(eventId);
}
bool AceContainer::IsSceneBoardEnabled()
{
return Rosen::SceneBoardJudgement::IsSceneBoardEnabled();
@ -3027,7 +3051,7 @@ void AceContainer::SetCurPointerEvent(const std::shared_ptr<MMI::PointerEvent>&
}
callbacksIter = stopDragCallbackMap_.erase(callbacksIter);
} else {
if (!pointerItem.IsPressed()) {
if (!pointerItem.IsPressed() || pointerAction == MMI::PointerEvent::POINTER_ACTION_CANCEL) {
for (const auto& callback : callbacksIter->second) {
if (callback) {
callback();
@ -3043,7 +3067,7 @@ void AceContainer::SetCurPointerEvent(const std::shared_ptr<MMI::PointerEvent>&
bool AceContainer::GetCurPointerEventInfo(
int32_t& pointerId, int32_t& globalX, int32_t& globalY, int32_t& sourceType,
int32_t& sourceTool, StopDragCallback&& stopDragCallback)
int32_t& sourceTool, int32_t& displayId, StopDragCallback&& stopDragCallback)
{
std::lock_guard<std::mutex> lock(pointerEventMutex_);
MMI::PointerEvent::PointerItem pointerItem;
@ -3062,6 +3086,7 @@ bool AceContainer::GetCurPointerEventInfo(
globalX = pointerItem.GetDisplayX();
globalY = pointerItem.GetDisplayY();
sourceTool = pointerItem.GetToolType();
displayId = currentPointerEvent->GetTargetDisplayId();
RegisterStopDragCallback(pointerId, std::move(stopDragCallback));
return true;
}
@ -3219,6 +3244,24 @@ void AceContainer::HandleAccessibilityHoverEvent(float pointX, float pointY, int
TaskExecutor::TaskType::UI, "ArkUIHandleAccessibilityHoverEvent");
}
void AceContainer::FireAccessibilityEventCallback(uint32_t eventId, int64_t parameter)
{
CHECK_NULL_VOID(taskExecutor_);
taskExecutor_->PostTask(
[weak = WeakClaim(this), eventId, parameter] {
auto container = weak.Upgrade();
CHECK_NULL_VOID(container);
ContainerScope scope(container->GetInstanceId());
auto pipelineContext = container->GetPipelineContext();
auto ngPipeline = AceType::DynamicCast<NG::PipelineContext>(pipelineContext);
CHECK_NULL_VOID(ngPipeline);
auto accessibilityManager = ngPipeline->GetAccessibilityManager();
CHECK_NULL_VOID(accessibilityManager);
accessibilityManager->FireAccessibilityEventCallback(eventId, parameter);
},
TaskExecutor::TaskType::UI, "ArkUIHandleAccessibilityEventCallback");
}
std::vector<Ace::RectF> AceContainer::GetOverlayNodePositions()
{
auto pipeline = AceType::DynamicCast<NG::PipelineContext>(pipelineContext_);

View File

@ -592,7 +592,7 @@ public:
void SetCurPointerEvent(const std::shared_ptr<MMI::PointerEvent>& currentEvent);
bool GetCurPointerEventInfo(int32_t& pointerId, int32_t& globalX, int32_t& globalY, int32_t& sourceType,
int32_t& sourceTool, StopDragCallback&& stopDragCallback) override;
int32_t& sourceTool, int32_t& displayId, StopDragCallback&& stopDragCallback) override;
bool GetCurPointerEventSourceType(int32_t& sourceType) override;
@ -710,6 +710,8 @@ public:
auto rect = uiWindow_->GetHostWindowRect(instanceId);
return Rect(rect.posX_, rect.posY_, rect.width_, rect.height_);
}
void FireUIExtensionEventCallback(uint32_t eventId);
void FireAccessibilityEventCallback(uint32_t eventId, int64_t parameter);
private:
virtual bool MaybeRelease() override;

View File

@ -33,7 +33,6 @@
#include "adapter/ohos/entrance/ace_rosen_sync_task.h"
#endif
#include "adapter/ohos/entrance/ace_container.h"
#include "adapter/ohos/entrance/ace_view_ohos.h"
#include "adapter/ohos/entrance/dialog_container.h"
#include "adapter/ohos/entrance/ui_content_impl.h"
@ -141,7 +140,7 @@ Rosen::WindowType SubwindowOhos::GetToastRosenType(bool IsSceneBoardEnabled)
return Rosen::WindowType::WINDOW_TYPE_TOAST;
}
void SetToastWindowOption(RefPtr<Platform::AceContainer>& parentContainer,
void SubwindowOhos::SetToastWindowOption(RefPtr<Platform::AceContainer>& parentContainer,
OHOS::sptr<OHOS::Rosen::WindowOption>& windowOption,
const Rosen::WindowType& toastWindowType, uint32_t mainWindowId)
{
@ -156,6 +155,7 @@ void SetToastWindowOption(RefPtr<Platform::AceContainer>& parentContainer,
auto hostWindowId = parentPipeline->GetFocusWindowId();
windowOption->SetIsUIExtAnySubWindow(true);
windowOption->SetParentId(hostWindowId);
SetUIExtensionHostWindowId(hostWindowId);
} else {
windowOption->SetParentId(mainWindowId);
}
@ -726,13 +726,12 @@ void SubwindowOhos::ShowMenuNG(const RefPtr<NG::FrameNode> customNode, const NG:
{
TAG_LOGI(AceLogTag::ACE_SUB_WINDOW, "show menu ng enter");
CHECK_NULL_VOID(customNode);
CHECK_NULL_VOID(targetNode);
if (!targetNode) {
TAG_LOGI(AceLogTag::ACE_SUB_WINDOW, "targetNode is nullptr");
return;
}
ContainerScope scope(childContainerId_);
auto container = Container::Current();
CHECK_NULL_VOID(container);
auto context = DynamicCast<NG::PipelineContext>(container->GetPipelineContext());
CHECK_NULL_VOID(context);
auto overlay = context->GetOverlayManager();
auto overlay = GetOverlayManager();
CHECK_NULL_VOID(overlay);
auto menuNode = customNode;
if (customNode->GetTag() != V2::MENU_WRAPPER_ETS_TAG) {
@ -753,12 +752,12 @@ void SubwindowOhos::ShowMenuNG(std::function<void()>&& buildFunc, std::function<
const NG::MenuParam& menuParam, const RefPtr<NG::FrameNode>& targetNode, const NG::OffsetF& offset)
{
TAG_LOGI(AceLogTag::ACE_SUB_WINDOW, "show menu ng enter");
if (!targetNode) {
TAG_LOGI(AceLogTag::ACE_SUB_WINDOW, "targetNode is nullptr");
return;
}
ContainerScope scope(childContainerId_);
auto container = Container::Current();
CHECK_NULL_VOID(container);
auto context = DynamicCast<NG::PipelineContext>(container->GetPipelineContext());
CHECK_NULL_VOID(context);
auto overlay = context->GetOverlayManager();
auto overlay = GetOverlayManager();
CHECK_NULL_VOID(overlay);
ResizeWindow();
ShowWindow();
@ -788,11 +787,7 @@ void SubwindowOhos::HideMenuNG(bool showPreviewAnimation, bool startDrag)
return;
}
ContainerScope scope(childContainerId_);
auto container = Container::Current();
CHECK_NULL_VOID(container);
auto context = DynamicCast<NG::PipelineContext>(container->GetPipelineContext());
CHECK_NULL_VOID(context);
auto overlay = context->GetOverlayManager();
auto overlay = GetOverlayManager();
CHECK_NULL_VOID(overlay);
overlay->HideMenuInSubWindow(showPreviewAnimation, startDrag);
HideEventColumn();
@ -808,14 +803,9 @@ void SubwindowOhos::HideMenuNG(const RefPtr<NG::FrameNode>& menu, int32_t target
return;
}
TAG_LOGI(AceLogTag::ACE_SUB_WINDOW, "Subwindow hide menu for target id %{public}d", targetId);
targetId_ = targetId;
auto aceContainer = Platform::AceContainer::GetContainer(childContainerId_);
CHECK_NULL_VOID(aceContainer);
auto context = DynamicCast<NG::PipelineContext>(aceContainer->GetPipelineContext());
CHECK_NULL_VOID(context);
auto overlay = context->GetOverlayManager();
auto overlay = GetOverlayManager();
CHECK_NULL_VOID(overlay);
overlay->HideMenuInSubWindow(menu, targetId_);
overlay->HideMenuInSubWindow(menu, targetId);
HideEventColumn();
HidePixelMap(false, 0, 0, false);
HideFilter(false);
@ -825,9 +815,7 @@ void SubwindowOhos::HideMenuNG(const RefPtr<NG::FrameNode>& menu, int32_t target
void SubwindowOhos::UpdatePreviewPosition()
{
ContainerScope scope(childContainerId_);
auto pipelineContext = NG::PipelineContext::GetCurrentContext();
CHECK_NULL_VOID(pipelineContext);
auto overlay = pipelineContext->GetOverlayManager();
auto overlay = GetOverlayManager();
CHECK_NULL_VOID(overlay);
if (overlay->GetHasPixelMap()) {
return;
@ -838,9 +826,7 @@ void SubwindowOhos::UpdatePreviewPosition()
bool SubwindowOhos::GetMenuPreviewCenter(NG::OffsetF& offset)
{
ContainerScope scope(childContainerId_);
auto pipelineContext = NG::PipelineContext::GetCurrentContext();
CHECK_NULL_RETURN(pipelineContext, false);
auto overlay = pipelineContext->GetOverlayManager();
auto overlay = GetOverlayManager();
CHECK_NULL_RETURN(overlay, false);
return overlay->GetMenuPreviewCenter(offset);
}
@ -849,9 +835,7 @@ void SubwindowOhos::UpdateHideMenuOffsetNG(
const NG::OffsetF& offset, float menuScale, bool isRedragStart, int32_t menuWrapperId)
{
ContainerScope scope(childContainerId_);
auto pipelineContext = NG::PipelineContext::GetCurrentContext();
CHECK_NULL_VOID(pipelineContext);
auto overlay = pipelineContext->GetOverlayManager();
auto overlay = GetOverlayManager();
CHECK_NULL_VOID(overlay);
if (overlay->IsContextMenuDragHideFinished()) {
return;
@ -864,9 +848,7 @@ void SubwindowOhos::ContextMenuSwitchDragPreviewAnimationtNG(const RefPtr<NG::Fr
{
CHECK_NULL_VOID(dragPreviewNode);
ContainerScope scope(childContainerId_);
auto pipelineContext = NG::PipelineContext::GetCurrentContext();
CHECK_NULL_VOID(pipelineContext);
auto overlay = pipelineContext->GetOverlayManager();
auto overlay = GetOverlayManager();
CHECK_NULL_VOID(overlay);
overlay->ContextMenuSwitchDragPreviewAnimation(dragPreviewNode, offset);
}

View File

@ -23,6 +23,7 @@
#include "resource_manager.h"
#include "wm/window.h"
#include "adapter/ohos/entrance/ace_container.h"
#include "adapter/ohos/entrance/platform_event_callback.h"
#include "base/resource/asset_manager.h"
#include "base/subwindow/subwindow.h"
@ -201,6 +202,10 @@ private:
void HideEventColumn();
Rosen::WindowType GetToastRosenType(bool IsSceneBoardEnabled);
void SetToastWindowOption(RefPtr<Platform::AceContainer>& parentContainer,
OHOS::sptr<OHOS::Rosen::WindowOption>& windowOption,
const Rosen::WindowType& toastWindowType, uint32_t mainWindowId);
static int32_t id_;
int32_t windowId_ = 0;
int32_t parentContainerId_ = -1;
@ -213,7 +218,6 @@ private:
sptr<OHOS::Rosen::Window> dialogWindow_;
std::shared_ptr<AppExecFwk::EventRunner> eventLoop_;
std::shared_ptr<AppExecFwk::EventHandler> handler_;
int32_t targetId_ = -1;
bool isToastWindow_ = false;
int32_t popupTargetId_ = -1;
bool haveDialog_ = false;

View File

@ -95,6 +95,7 @@
#include "core/components_ng/base/inspector.h"
#include "core/components_ng/base/view_abstract.h"
#include "core/components_ng/pattern/text_field/text_field_manager.h"
#include "core/components_ng/pattern/ui_extension/ui_extension_config.h"
#include "core/image/image_file_cache.h"
#include "core/pipeline_ng/pipeline_context.h"
#ifdef FORM_SUPPORTED
@ -401,6 +402,9 @@ public:
auto pipeline = AceType::DynamicCast<NG::PipelineContext>(context);
if (pipeline) {
ContainerScope scope(instanceId_);
auto manager = pipeline->GetSafeAreaManager();
CHECK_NULL_VOID(manager);
manager->SetRawKeyboardHeight(keyboardRect.Height());
auto uiExtMgr = pipeline->GetUIExtensionManager();
if (uiExtMgr) {
SetUIExtensionImeShow(keyboardRect);
@ -441,6 +445,15 @@ private:
CHECK_NULL_RETURN(context, false);
auto pipeline = AceType::DynamicCast<NG::PipelineContext>(context);
CHECK_NULL_RETURN(pipeline, false);
auto textFieldManager = AceType::DynamicCast<NG::TextFieldManagerNG>(pipeline->GetTextFieldManager());
CHECK_NULL_RETURN(textFieldManager, false);
auto windowManager = pipeline->GetWindowManager();
CHECK_NULL_RETURN(windowManager, false);
auto windowMode = windowManager->GetWindowMode();
if (windowMode == WindowMode::WINDOW_MODE_FLOATING || windowMode == WindowMode::WINDOW_MODE_SPLIT_SECONDARY) {
textFieldManager->SetLaterAvoid(false);
return false;
}
bool isRotate = false;
auto displayInfo = container->GetDisplayInfo();
uint32_t lastKeyboardHeight = pipeline->GetSafeAreaManager() ?
@ -452,8 +465,6 @@ private:
} else {
lastRotation = -1;
}
auto textFieldManager = AceType::DynamicCast<NG::TextFieldManagerNG>(pipeline->GetTextFieldManager());
CHECK_NULL_RETURN(textFieldManager, false);
if (textFieldManager->GetLaterAvoid()) {
auto laterRect = textFieldManager->GetLaterAvoidKeyboardRect();
if (NearEqual(laterRect.Height(), keyboardRect.Height())) {
@ -2565,13 +2576,8 @@ void UIContentImpl::UpdateViewportConfigWithAnimation(const ViewportConfig& conf
container->SetWindowPos(config.Left(), config.Top());
auto pipelineContext = container->GetPipelineContext();
if (pipelineContext) {
auto uiExtensionFlushFinishCallback = [rsWindow]() {
CHECK_NULL_VOID(rsWindow);
rsWindow->NotifyExtensionEventAsync(0);
};
if (container->IsUIExtensionWindow()) {
pipelineContext->EnWaitFlushFinish();
pipelineContext->SetUIExtensionFlushFinishCallback(uiExtensionFlushFinishCallback);
pipelineContext->AddUIExtensionCallbackEvent(NG::UIExtCallbackEventId::ON_AREA_CHANGED);
}
UpdateSafeArea(pipelineContext, avoidAreas, config, container);
pipelineContext->SetDisplayWindowRectInfo(
@ -3631,7 +3637,9 @@ int32_t UIContentImpl::CreateCustomPopupUIExtension(
}
auto popupParam = CreateCustomPopupParam(true, config);
popupParam->SetBlockEvent(false);
auto uiExtNode = ModalUIExtension::Create(want, callbacks, false, false);
NG::InnerModalUIExtensionConfig innerModalUIExtensionConfig;
innerModalUIExtensionConfig.isModal = false;
auto uiExtNode = ModalUIExtension::Create(want, callbacks, innerModalUIExtensionConfig);
auto focusHub = uiExtNode->GetFocusHub();
if (focusHub) {
focusHub->SetFocusable(config.isFocusable);

View File

@ -76,6 +76,7 @@ template("ace_osal_ohos_source_set") {
"stylus_detector_mgr.cpp",
"system_properties.cpp",
"thp_extra_manager_impl.cpp",
"thread_priority.cpp",
"trace_id_impl.cpp",
"view_data_wrap_ohos.cpp",
"want_wrap_ohos.cpp",

View File

@ -35,6 +35,7 @@
#include "core/pipeline/pipeline_context.h"
#include "core/pipeline_ng/pipeline_context.h"
#include "frameworks/bridge/common/dom/dom_type.h"
#include "frameworks/core/components_ng/pattern/ui_extension/ui_extension_config.h"
#include "frameworks/core/components_ng/pattern/web/web_pattern.h"
#include "js_third_provider_interaction_operation.h"
#include "nlohmann/json.hpp"
@ -65,8 +66,9 @@ 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 UEC_EVENT_TASK_DELAY_MILLISECOND = 200;
constexpr int32_t DELAY_SEND_EVENT_MILLISECOND = 20;
constexpr uint32_t SUB_TREE_OFFSET_IN_PAGE_ID = 16;
constexpr int32_t MAX_PAGE_ID_WITH_SUB_TREE = (1 << SUB_TREE_OFFSET_IN_PAGE_ID);
const std::string ACTION_ARGU_SCROLL_STUB = "scrolltype"; // wait for change
const std::string ACTION_DEFAULT_PARAM = "ACCESSIBILITY_ACTION_INVALID";
@ -121,10 +123,6 @@ const std::map<Accessibility::ActionType, std::function<bool(const Accessibility
} },
};
const std::unordered_map<std::string, std::string> WEB_COMPONENT_TYPE_MAPPING = {
{ "textField", "textArea" }
};
bool IsExtensionComponent(const RefPtr<NG::UINode>& node)
{
return node && (node->GetTag() == V2::UI_EXTENSION_COMPONENT_ETS_TAG
@ -207,6 +205,7 @@ Accessibility::EventType ConvertAceEventType(AccessibilityEventType type)
{ AccessibilityEventType::ANNOUNCE_FOR_ACCESSIBILITY,
Accessibility::EventType::TYPE_VIEW_ANNOUNCE_FOR_ACCESSIBILITY },
{ AccessibilityEventType::PAGE_OPEN, Accessibility::EventType::TYPE_PAGE_OPEN },
{ AccessibilityEventType::ELEMENT_INFO_CHANGE, Accessibility::EventType::TYPE_ELEMENT_INFO_CHANGE },
};
Accessibility::EventType eventType = Accessibility::EventType::TYPE_VIEW_INVALID;
int64_t idx = BinarySearchFindIndex(eventTypeMap, ArraySize(eventTypeMap), type);
@ -531,15 +530,6 @@ void ConvertExtensionAccessibilityNodeId(std::list<AccessibilityElementInfo>& in
}
}
std::string ConvertWebComponentType(std::string type)
{
auto it = WEB_COMPONENT_TYPE_MAPPING.find(type);
if (it != WEB_COMPONENT_TYPE_MAPPING.end()) {
return it->second;
}
return type;
}
inline std::string BoolToString(bool tag)
{
return tag ? "true" : "false";
@ -1242,6 +1232,18 @@ bool IsUserCheckedOrSelected(const RefPtr<NG::FrameNode> frameNode)
}
return false;
}
void UpdateElementInfoPageIdWithTreeId(Accessibility::AccessibilityElementInfo& info, int32_t treeId)
{
int32_t pageId = info.GetPageId();
if ((pageId >= MAX_PAGE_ID_WITH_SUB_TREE) || (pageId < 0)) {
TAG_LOGE(AceLogTag::ACE_ACCESSIBILITY, "pageId %{public}d cannot set tree id", pageId);
} else {
uint32_t unsignedPageId = static_cast<uint32_t>(pageId);
uint32_t unsignedTreeId = static_cast<uint32_t>(treeId);
info.SetPageId((unsignedTreeId << SUB_TREE_OFFSET_IN_PAGE_ID) | unsignedPageId);
}
}
}
void UpdateAccessibilityTextValueInfo(
RefPtr<NG::AccessibilityProperty>& accessibilityProperty, AccessibilityElementInfo& nodeInfo)
@ -1678,7 +1680,7 @@ void JsAccessibilityManager::UpdateWebAccessibilityElementInfo(
}
nodeInfo.SetAccessibilityId(node->GetAccessibilityId());
nodeInfo.SetComponentType(ConvertWebComponentType(node->GetComponentType()));
nodeInfo.SetComponentType(node->GetComponentType());
nodeInfo.SetEnabled(node->GetIsEnabled());
nodeInfo.SetFocused(node->GetIsFocused());
nodeInfo.SetAccessibilityFocus(node->GetIsAccessibilityFocus());
@ -2436,6 +2438,7 @@ void JsAccessibilityManager::InitializeCallback()
auto container = Platform::AceContainer::GetContainer(pipelineContext->GetInstanceId());
if (container != nullptr && container->IsUIExtensionWindow()) {
pipelineContext->AddUIExtensionCallbackEvent(OHOS::Ace::NG::UIExtCallbackEventId::ON_UEA_ACCESSIBILITY_READY);
return;
}
@ -2466,6 +2469,7 @@ bool JsAccessibilityManager::SendAccessibilitySyncEvent(
eventInfo.SetSource(elementId);
UpdateElementInfoTreeId(info);
eventInfo.SetElementInfo(info);
eventInfo.SetPageId(info.GetPageId());
TAG_LOGI(AceLogTag::ACE_ACCESSIBILITY,
"send accessibility componentType:%{public}s event:%{public}d accessibilityId:%{public}" PRId64,
eventInfo.GetComponentType().c_str(), eventInfo.GetEventType(), eventInfo.GetAccessibilityId());
@ -5217,6 +5221,7 @@ bool JsAccessibilityManager::NeedRegisterChildTree(
void JsAccessibilityManager::RegisterInteractionOperationAsChildTree(
uint32_t parentWindowId, int32_t parentTreeId, int64_t parentElementId)
{
AccessibilitySystemAbilityClient::SetSplicElementIdTreeId(parentTreeId, parentElementId);
if (!NeedRegisterChildTree(parentWindowId, parentTreeId, parentElementId)) {
return;
}
@ -5231,7 +5236,6 @@ void JsAccessibilityManager::RegisterInteractionOperationAsChildTree(
} else if (pipelineContext->IsFormRender()) {
windowId_ = parentWindowId;
}
AccessibilitySystemAbilityClient::SetSplicElementIdTreeId(parentTreeId, parentElementId);
uint32_t windowId = GetWindowId();
auto interactionOperation = std::make_shared<JsInteractionOperation>(windowId);
@ -5329,17 +5333,6 @@ void JsAccessibilityManager::JsInteractionOperation::SetChildTreeIdAndWinId(
auto jsAccessibilityManager = GetHandler().Upgrade();
CHECK_NULL_VOID(jsAccessibilityManager);
jsAccessibilityManager->NotifySetChildTreeIdAndWinId(splitElementId, treeId, childWindowId);
auto context = jsAccessibilityManager->GetPipelineContext().Upgrade();
CHECK_NULL_VOID(context);
context->GetTaskExecutor()->PostDelayedTask(
[weak = GetHandler(), splitElementId] {
auto jsAccessibilityManager = weak.Upgrade();
CHECK_NULL_VOID(jsAccessibilityManager);
ACE_SCOPED_TRACE("SendUecOnTreeEvent");
jsAccessibilityManager->SendUecOnTreeEvent(splitElementId);
},
TaskExecutor::TaskType::UI, UEC_EVENT_TASK_DELAY_MILLISECOND, "ArkUIAccessibilitySendUecOnTreeEvent");
}
void JsAccessibilityManager::JsInteractionOperation::SetBelongTreeId(const int32_t treeId)
@ -5377,6 +5370,8 @@ void JsAccessibilityManager::UpdateElementInfoTreeId(Accessibility::Accessibilit
info.SetParent(parentId);
}
UpdateElementInfoPageIdWithTreeId(info, treeId);
std::vector<int64_t> childIds = info.GetChildIds();
for (int64_t child : childIds) {
info.RemoveChild(child);
@ -5403,6 +5398,8 @@ void JsAccessibilityManager::UpdateElementInfosTreeId(std::list<Accessibility::A
item.SetParent(parentId);
}
UpdateElementInfoPageIdWithTreeId(item, treeId);
std::vector<int64_t> childIds = item.GetChildIds();
for (int64_t child : childIds) {
item.RemoveChild(child);
@ -6199,4 +6196,19 @@ void JsAccessibilityManager::GetWebCursorPosition(const int64_t elementId, const
callback.SetCursorPositionResult(node->GetSelectionStart(), requestId);
}
#endif // WEB_SUPPORTED
void JsAccessibilityManager::FireAccessibilityEventCallback(uint32_t eventId, int64_t parameter)
{
auto eventType = static_cast<AccessibilityCallbackEventId>(eventId);
AccessibilityEvent event;
switch (eventType) {
case AccessibilityCallbackEventId::ON_LOAD_PAGE:
event.nodeId = parameter;
event.windowChangeTypes = WindowUpdateType::WINDOW_UPDATE_ACTIVE;
event.type = AccessibilityEventType::CHANGE;
SendAccessibilityAsyncEvent(event);
default:
break;
}
}
} // namespace OHOS::Ace::Framework

View File

@ -252,6 +252,8 @@ public:
const std::vector<std::string>& params,
std::vector<std::string>& info) override;
void FireAccessibilityEventCallback(uint32_t eventId, int64_t parameter) override;
protected:
void OnDumpInfoNG(const std::vector<std::string>& params, uint32_t windowId, bool hasJson = false) override;
void DumpHandleEvent(const std::vector<std::string>& params) override;

View File

@ -19,9 +19,9 @@
namespace OHOS::Ace {
RefPtr<NG::FrameNode> ModalUIExtension::Create(const AAFwk::Want& want, const ModalUIExtensionCallbacks& callbacks,
bool isAsyncModalBinding, bool isModal)
const NG::InnerModalUIExtensionConfig& config)
{
return NG::UIExtensionModelNG::Create(want, callbacks, isAsyncModalBinding, isModal);
return NG::UIExtensionModelNG::Create(want, callbacks, config);
}
int32_t ModalUIExtension::GetSessionId(const RefPtr<NG::FrameNode>& uiExtNode)

View File

@ -152,6 +152,11 @@ bool IsWindowRectResizeEnabled()
return (system::GetParameter("persist.ace.windowresize.enabled", "true") == "true");
}
bool IsCacheNavigationNodeEnable()
{
return system::GetParameter("persist.ace.navigation.groupnode.cached", "true") == "true";
}
bool IsHookModeEnabled()
{
#ifdef PREVIEW
@ -376,6 +381,7 @@ bool SystemProperties::imageFrameworkEnable_ = IsImageFrameworkEnabled();
std::atomic<bool> SystemProperties::traceInputEventEnable_(IsTraceInputEventEnabled() && developerModeOn_);
std::atomic<bool> SystemProperties::stateManagerEnable_(IsStateManagerEnable());
bool SystemProperties::buildTraceEnable_ = IsBuildTraceEnabled() && developerModeOn_;
bool SystemProperties::cacheNavigationNodeEnable_ = IsCacheNavigationNodeEnable();
bool SystemProperties::syncDebugTraceEnable_ = IsSyncDebugTraceEnabled();
bool SystemProperties::pixelRoundEnable_ = IsPixelRoundEnabled();
bool SystemProperties::textTraceEnable_ = IsTextTraceEnabled();
@ -744,6 +750,11 @@ bool SystemProperties::GetNavigationBlurEnabled()
return navigationBlurEnabled_;
}
bool SystemProperties::GetCacheNavigationNodeEnable()
{
return cacheNavigationNodeEnable_;
}
bool SystemProperties::GetGridCacheEnabled()
{
return gridCacheEnabled_;

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "base/thread/thread_priority.h"
namespace OHOS::Ace {
void ThreadPriority::SetThreadPriority(TaskExecutor::TaskType taskType) {}
void ThreadPriority::setBackGroundThreadPriority() {}
} // namespace OHOS::Ace

View File

@ -89,6 +89,7 @@ ohos_source_set("preview_osal_source") {
"system_bar_style_ohos.cpp",
"system_properties.cpp",
"task/task_runner_adapter_impl.cpp",
"thread_priority.cpp",
"time_event_proxy_preview.cpp",
"trace_id_impl.cpp",
"view_data_wrap_impl.cpp",

View File

@ -18,7 +18,7 @@
namespace OHOS::Ace {
RefPtr<NG::FrameNode> ModalUIExtension::Create(const AAFwk::Want& want, const ModalUIExtensionCallbacks& callbacks,
bool isAsyncModalBinding, bool isModal)
const NG::InnerModalUIExtensionConfig& config)
{
return nullptr;
}

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "base/thread/thread_priority.h"
namespace OHOS::Ace {
void ThreadPriority::SetThreadPriority(TaskExecutor::TaskType taskType) {}
void ThreadPriority::setBackGroundThreadPriority() {}
} // namespace OHOS::Ace

View File

@ -108,9 +108,20 @@ const RIGHT_CONTENT_NULL_LEFTWIDTH = '100%';
const RIGHT_CONTENT_NULL_RIGHTWIDTH = '0vp';
const LEFT_PART_WIDTH = 'calc(66% - 16vp)';
const RIGHT_PART_WIDTH = '34%';
const LEFT_ONLY_ARROW_WIDTH = 'calc(100% - 40vp)';
const RIGHT_ONLY_ARROW_WIDTH = '24vp';
const ACCESSIBILITY_LEVEL_NO: string = 'no';
const LEFT_ONLY_ARROW_WIDTH = 'calc(100% - ${RIGHT_ONLY_ARROW_WIDTH})';
const RIGHT_ONLY_IMAGE_WIDTH = '48vp';
const LEFT_ONLY_IMAGE_WIDTH = 'calc(100% - ${RIGHT_ONLY_IMAGE_WIDTH})';
const RIGHT_ONLY_ICON_WIDTH = '32vp';
const LEFT_ONLY_ICON_WIDTH = 'calc(100% - ${RIGHT_ONLY_ICON_WIDTH})';
const RIGHT_ICON_SUB_ICON_WIDTH = '72vp';
const LEFT_ICON_SUB_ICON_WIDTH = 'calc(100% - ${RIGHT_ICON_SUB_ICON_WIDTH})';
const RIGHT_ONLY_RADIO_WIDTH = '24vp';
const LEFT_ONLY_RADIO_WIDTH = 'calc(100% - ${RIGHT_ONLY_RADIO_WIDTH})';
const RIGHT_ONLY_CHECKBOX_WIDTH = '24vp';
const LEFT_ONLY_CHECKBOX_WIDTH = 'calc(100% - ${RIGHT_ONLY_CHECKBOX_WIDTH})';
const RIGHT_ONLY_SWITCH_WIDTH = '40vp';
const LEFT_ONLY_SWITCH_WIDTH = 'calc(100% - ${RIGHT_ONLY_SWITCH_WIDTH})';
const ICON_SIZE_MAP: Map<number, number> = new Map([
[IconType.BADGE, BADGE_SIZE],
@ -194,29 +205,23 @@ struct ContentItemStruct {
Text(this.primaryText)
.fontSize($r('sys.float.ohos_id_text_size_body1'))
.fontColor(this.primaryTextColor)
.maxLines(TEXT_MAX_LINE)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.fontWeight(FontWeight.Medium)
.focusable(true)
.draggable(false)
.accessibilityLevel(ACCESSIBILITY_LEVEL_NO)
if (this.secondaryText != null) {
Text(this.secondaryText)
.fontSize($r('sys.float.ohos_id_text_size_body2'))
.fontColor(this.secondaryTextColor)
.maxLines(TEXT_MAX_LINE)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.draggable(false)
.accessibilityLevel(ACCESSIBILITY_LEVEL_NO)
}
if (this.description != null) {
Text(this.description)
.fontSize($r('sys.float.ohos_id_text_size_body2'))
.fontColor(this.descriptionColor)
.maxLines(TEXT_MAX_LINE)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.draggable(false)
.accessibilityLevel(ACCESSIBILITY_LEVEL_NO)
}
}
.flexShrink(1)
@ -417,7 +422,6 @@ struct OperateItemStruct {
.fontColor(this.secondaryTextColor)
.draggable(false)
.flexShrink(1)
.accessibilityLevel(ACCESSIBILITY_LEVEL_NO)
}
@Builder
@ -561,7 +565,6 @@ struct OperateItemStruct {
.constraintSize({
maxWidth: `calc(100% - ${OPERATEITEM_ARROW_WIDTH}vp)`
})
.accessibilityLevel(ACCESSIBILITY_LEVEL_NO)
Image(this.arrow?.value)
.height(OPERATEITEM_ICONLIKE_SIZE)
.width(OPERATEITEM_ARROW_WIDTH)
@ -584,7 +587,6 @@ struct OperateItemStruct {
.constraintSize({
maxWidth: `calc(100% - ${OPERATEITEM_ARROW_WIDTH}vp)`
})
.accessibilityLevel(ACCESSIBILITY_LEVEL_NO)
Image(this.arrow?.value)
.height(OPERATEITEM_ICONLIKE_SIZE)
.width(OPERATEITEM_ARROW_WIDTH)
@ -702,7 +704,6 @@ export struct ComposeListItem {
@State textArrowLeftSafeOffset: number = 0;
private isFollowingSystemFontScale = this.getUIContext().isFollowingSystemFontScale();
private maxFontScale = this.getUIContext().getMaxFontScale();
@State accessibilityTextBuilder: string = '';
onWillApplyTheme(theme: Theme): void {
this.hoveringColor = theme.colors.interactiveHover;
@ -743,12 +744,6 @@ export struct ComposeListItem {
this.itemHeight = ICON_SIZE_MAP.get(this.contentItem?.iconStyle as number) as number + SAFE_LIST_PADDING;
}
this.accessibilityTextBuilder = `
${this.contentItem?.primaryText ?? ''}
${this.contentItem?.secondaryText ?? ''}
${this.contentItem?.description ?? ''}
${this.operateItem?.text ?? ''}
`;
}
aboutToAppear() {
@ -757,21 +752,47 @@ export struct ComposeListItem {
calculatedLeftWidth(): string {
if (this.operateItem === null || JSON.stringify(this.operateItem) === '{}') {
return RIGHT_CONTENT_NULL_LEFTWIDTH
return RIGHT_CONTENT_NULL_LEFTWIDTH;
} else if (this.operateItem?.switch != null && this.operateItem?.text == null) {
return LEFT_ONLY_SWITCH_WIDTH;
} else if (this.operateItem?.checkbox != null && this.operateItem?.text == null) {
return LEFT_ONLY_CHECKBOX_WIDTH;
} else if (this.operateItem?.radio != null && this.operateItem?.text == null) {
return LEFT_ONLY_RADIO_WIDTH;
} else if (this.operateItem?.icon != null && this.operateItem?.text == null) {
if (this.operateItem?.subIcon != null) {
return LEFT_ICON_SUB_ICON_WIDTH;
}
return LEFT_ONLY_ICON_WIDTH;
} else if (this.operateItem?.image != null && this.operateItem?.text == null) {
return LEFT_ONLY_IMAGE_WIDTH;
} else if (this.operateItem?.arrow != null && this.operateItem?.text == null) {
return LEFT_ONLY_ARROW_WIDTH
return LEFT_ONLY_ARROW_WIDTH;
} else {
return LEFT_PART_WIDTH
return LEFT_PART_WIDTH;
}
}
calculatedRightWidth(): string {
if (this.operateItem === null || JSON.stringify(this.operateItem) === '{}') {
return RIGHT_CONTENT_NULL_RIGHTWIDTH
return RIGHT_CONTENT_NULL_RIGHTWIDTH;
} else if (this.operateItem?.switch != null && this.operateItem?.text == null) {
return RIGHT_ONLY_SWITCH_WIDTH;
} else if (this.operateItem?.checkbox != null && this.operateItem?.text == null) {
return RIGHT_ONLY_CHECKBOX_WIDTH;
} else if (this.operateItem?.radio != null && this.operateItem?.text == null) {
return RIGHT_ONLY_RADIO_WIDTH;
} else if (this.operateItem?.icon != null && this.operateItem?.text == null) {
if (this.operateItem?.subIcon != null) {
return RIGHT_ICON_SUB_ICON_WIDTH;
}
return RIGHT_ONLY_ICON_WIDTH;
} else if (this.operateItem?.image != null && this.operateItem?.text == null) {
return RIGHT_ONLY_IMAGE_WIDTH;
} else if (this.operateItem?.arrow != null && this.operateItem?.text == null) {
return RIGHT_ONLY_ARROW_WIDTH
return RIGHT_ONLY_ARROW_WIDTH;
} else {
return RIGHT_PART_WIDTH
return RIGHT_PART_WIDTH;
}
}
@ -976,7 +997,6 @@ export struct ComposeListItem {
}
}).padding(this.containerPadding)
}
.accessibilityText(this.accessibilityTextBuilder)
.padding({
left: STACK_PADDING,
right: STACK_PADDING

View File

@ -210,7 +210,7 @@ CounterResource.COUNTER_INLINE_DATE_TEXT_MARGIN = 12;
CounterResource.COUNTER_INLINE_INPUT_TEXT_MARGIN = 12;
CounterResource.COUNTER_BUTTON_INITIAL_OPACITY = 1;
CounterResource.COUNTER_BUTTON_DISABLE_OPACITY = 0.4;
CounterResource.COUNTER_LABEL_MAX_FONT_SIZE_SCALE = 1.75;
CounterResource.COUNTER_LABEL_MAX_FONT_SIZE_SCALE = 2;
CounterResource.COUNTER_NUMBER_MAX_FONT_SIZE_SCALE = 1;
class CounterConstant {
@ -898,6 +898,17 @@ export class CounterComponent extends ViewPU {
this.__max.set(h19);
}
getTextInputFontSize() {
let fontSize = this.resourceToVp(CounterResource.COUNTER_NUMBER_SIZE);
let uiContext = this.getUIContext();
let fontSizeScale = uiContext.getHostContext()?.config?.fontSizeScale ?? 1;
if (fontSizeScale < 1) {
return fontSize + 'fp';
} else {
return fontSize + 'vp';
}
}
convertNumberToString(g19) {
if (g19 >= 0 && g19 < CounterConstant.COUNTER_TEN_NUMBER) {
return this.numberStrList[g19];
@ -2147,7 +2158,7 @@ export class CounterComponent extends ViewPU {
TextInput.type(InputType.PhoneNumber);
TextInput.caretColor(Color.Transparent);
TextInput.copyOption(CopyOptions.None);
TextInput.fontSize(CounterResource.COUNTER_NUMBER_SIZE);
TextInput.fontSize(this.getTextInputFontSize());
TextInput.fontWeight(FontWeight.Medium);
TextInput.fontColor(this.hasFocusText1 ? Color.White : CounterResource.COUNTER_TEXT_COLOR);
TextInput.maxLength(this.getMaxLength());
@ -2303,7 +2314,7 @@ export class CounterComponent extends ViewPU {
TextInput.type(InputType.PhoneNumber);
TextInput.caretColor(Color.Transparent);
TextInput.copyOption(CopyOptions.None);
TextInput.fontSize(CounterResource.COUNTER_NUMBER_SIZE);
TextInput.fontSize(this.getTextInputFontSize());
TextInput.fontWeight(FontWeight.Medium);
TextInput.fontColor(this.hasFocusText1 ? Color.White : CounterResource.COUNTER_TEXT_COLOR);
TextInput.maxLength(this.getMaxLength());
@ -2620,7 +2631,7 @@ export class CounterComponent extends ViewPU {
TextInput.type(InputType.Number);
TextInput.caretColor(Color.Transparent);
TextInput.copyOption(CopyOptions.None);
TextInput.fontSize(CounterResource.COUNTER_NUMBER_SIZE);
TextInput.fontSize(this.getTextInputFontSize());
TextInput.fontWeight(FontWeight.Medium);
TextInput.fontColor(this.hasFocusText1 ? Color.White : CounterResource.COUNTER_TEXT_COLOR);
TextInput.maxLength(5);
@ -2752,7 +2763,7 @@ export class CounterComponent extends ViewPU {
TextInput.type(InputType.Number);
TextInput.caretColor(Color.Transparent);
TextInput.copyOption(CopyOptions.None);
TextInput.fontSize(CounterResource.COUNTER_NUMBER_SIZE);
TextInput.fontSize(this.getTextInputFontSize());
TextInput.fontWeight(FontWeight.Medium);
TextInput.fontColor(this.hasFocusText2 ? Color.White : CounterResource.COUNTER_TEXT_COLOR);
TextInput.maxLength(3);
@ -2895,7 +2906,7 @@ export class CounterComponent extends ViewPU {
TextInput.type(InputType.Number);
TextInput.caretColor(Color.Transparent);
TextInput.copyOption(CopyOptions.None);
TextInput.fontSize(CounterResource.COUNTER_NUMBER_SIZE);
TextInput.fontSize(this.getTextInputFontSize());
TextInput.fontWeight(FontWeight.Medium);
TextInput.fontColor(this.hasFocusText3 ? Color.White : CounterResource.COUNTER_TEXT_COLOR);
TextInput.maxLength(3);

View File

@ -13,6 +13,7 @@
* limitations under the License.
*/
import { LengthMetrics } from '@ohos.arkui.node';
import common from '@ohos.app.ability.common';
export enum CounterType {
LIST = 0,
@ -144,7 +145,7 @@ class CounterResource {
public static readonly COUNTER_INLINE_INPUT_TEXT_MARGIN = 12;
public static readonly COUNTER_BUTTON_INITIAL_OPACITY = 1;
public static readonly COUNTER_BUTTON_DISABLE_OPACITY = 0.4;
public static readonly COUNTER_LABEL_MAX_FONT_SIZE_SCALE = 1.75;
public static readonly COUNTER_LABEL_MAX_FONT_SIZE_SCALE = 2;
public static readonly COUNTER_NUMBER_MAX_FONT_SIZE_SCALE = 1;
}
@ -252,6 +253,17 @@ export struct CounterComponent {
private controller2: TextInputController = new TextInputController();
private controller3: TextInputController = new TextInputController();
getTextInputFontSize() {
let fontSize = this.resourceToVp(CounterResource.COUNTER_NUMBER_SIZE);
let uiContext = this.getUIContext();
let fontSizeScale = (uiContext.getHostContext() as common.UIAbilityContext)?.config?.fontSizeScale ?? 1;
if (fontSizeScale < 1) {
return fontSize + 'fp';
} else {
return fontSize + 'vp';
}
}
convertNumberToString(value: number) {
if (value >= 0 && value < CounterConstant.COUNTER_TEN_NUMBER) {
return this.numberStrList[value];
@ -1370,7 +1382,7 @@ export struct CounterComponent {
.type(InputType.PhoneNumber)
.caretColor(Color.Transparent)
.copyOption(CopyOptions.None)
.fontSize(CounterResource.COUNTER_NUMBER_SIZE)
.fontSize(this.getTextInputFontSize())
.fontWeight(FontWeight.Medium)
.fontColor(this.hasFocusText1 ? Color.White : CounterResource.COUNTER_TEXT_COLOR)
.maxLength(this.getMaxLength())
@ -1520,7 +1532,7 @@ export struct CounterComponent {
.type(InputType.PhoneNumber)
.caretColor(Color.Transparent)
.copyOption(CopyOptions.None)
.fontSize(CounterResource.COUNTER_NUMBER_SIZE)
.fontSize(this.getTextInputFontSize())
.fontWeight(FontWeight.Medium)
.fontColor(this.hasFocusText1 ? Color.White : CounterResource.COUNTER_TEXT_COLOR)
.maxLength(this.getMaxLength())
@ -1798,7 +1810,7 @@ export struct CounterComponent {
.type(InputType.Number)
.caretColor(Color.Transparent)
.copyOption(CopyOptions.None)
.fontSize(CounterResource.COUNTER_NUMBER_SIZE)
.fontSize(this.getTextInputFontSize())
.fontWeight(FontWeight.Medium)
.fontColor(this.hasFocusText1 ? Color.White : CounterResource.COUNTER_TEXT_COLOR)
.maxLength(5)
@ -1928,7 +1940,7 @@ export struct CounterComponent {
.type(InputType.Number)
.caretColor(Color.Transparent)
.copyOption(CopyOptions.None)
.fontSize(CounterResource.COUNTER_NUMBER_SIZE)
.fontSize(this.getTextInputFontSize())
.fontWeight(FontWeight.Medium)
.fontColor(this.hasFocusText2 ? Color.White : CounterResource.COUNTER_TEXT_COLOR)
.maxLength(3)
@ -2066,7 +2078,7 @@ export struct CounterComponent {
.type(InputType.Number)
.caretColor(Color.Transparent)
.copyOption(CopyOptions.None)
.fontSize(CounterResource.COUNTER_NUMBER_SIZE)
.fontSize(this.getTextInputFontSize())
.fontWeight(FontWeight.Medium)
.fontColor(this.hasFocusText3 ? Color.White : CounterResource.COUNTER_TEXT_COLOR)
.maxLength(3)

View File

@ -800,7 +800,7 @@ export class PopupComponent extends ViewPU {
j28 -= (this.theme.windows.padding.bottom.value -
(this.theme.button.textMargin.bottom.value / 2));
if (Math.floor(this.textHeight) > Math.floor(j28 + 1)) {
return j28;
return this.textHeight;
}
else {
j28 = undefined;

View File

@ -488,7 +488,7 @@ export struct PopupComponent {
scrollMaxHeight -= (this.theme.windows.padding.bottom.value -
(this.theme.button.textMargin.bottom.value / 2))
if (Math.floor(this.textHeight) > Math.floor(scrollMaxHeight + 1)) {
return scrollMaxHeight
return this.textHeight
} else {
scrollMaxHeight = undefined
return scrollMaxHeight

View File

@ -261,7 +261,7 @@ let SegmentButtonOptions = o = class SegmentButtonOptions {
this.buttons = new SegmentButtonItemOptionsArray(options.buttons);
if (this.type === 'capsule') {
this.multiply = options.multiply ?? false;
this.n2();
this.w3();
this.selectedFontColor = options.selectedFontColor ?? e1.t1;
this.selectedBackgroundColor = options.selectedBackgroundColor ??
e1.z1;
@ -270,7 +270,8 @@ let SegmentButtonOptions = o = class SegmentButtonOptions {
}
this.m2 = this.multiply ? 0 : 2;
}
n2() {
w3() {
this.buttons?.forEach(button => {
this.i2 ||= button.text !== void 0;
this.showIcon ||= button.icon !== void 0 || button.selectedIcon !== void 0;
@ -1104,6 +1105,7 @@ class m1 extends ViewPU {
this.t3 = new ObservedPropertyObjectPU(Array.from({ length: a1 }, (i3, index) => 0), this, "buttonHeight");
this.buttonItemsRealHeight = Array.from({ length: a1 }, (h3, index) => 0);
this.groupId = util.generateRandomUUID(true);
this.onItemClicked = undefined;
this.setInitiallyProvidedValue(params);
this.declareWatch("optionsArray", this.onOptionsArrayChange);
this.declareWatch("options", this.onOptionsChange);
@ -1126,6 +1128,9 @@ class m1 extends ViewPU {
if (params.groupId !== undefined) {
this.groupId = params.groupId;
}
if (params.onItemClicked !== undefined) {
this.onItemClicked = params.onItemClicked;
}
}
updateStateVars(params) {
@ -1562,6 +1567,9 @@ class m1 extends ViewPU {
Gesture.create(GesturePriority.Low);
TapGesture.create();
TapGesture.onAction(() => {
if (this.onItemClicked) {
this.onItemClicked(index);
}
if (this.options.type === 'capsule' &&
(this.options.multiply ?? false)) {
if (this.selectedIndexes.indexOf(index) === -1) {
@ -1586,7 +1594,7 @@ class m1 extends ViewPU {
}, () => {
this.zoomScaleArray[index] = 0.95;
});
} else if (event.type === TouchType.Up) {
} else if (event.type === TouchType.Up || event.type === TouchType.Cancel) {
Context.animateTo({
curve: curves.interpolatingSpring(10, 1, 410, 38)
}, () => {
@ -1658,7 +1666,7 @@ class m1 extends ViewPU {
}, undefined, elmtId, () => {
}, {
page: "segmentbutton/src/main/ets/components/MainPage.ets",
line: 817,
line: 818,
u3: 15
});
ViewPU.create(componentCall);
@ -2025,7 +2033,7 @@ export class SegmentButton extends ViewPU {
return;
}
if (this.options.type === 'capsule') {
this.options.n2();
this.options.w3();
}
if (this.doSelectedChangeAnimate) {
this.updateAnimatedProperty(this.getSelectedChangeCurve());
@ -2166,7 +2174,6 @@ export class SegmentButton extends ViewPU {
GestureGroup.create(GestureMode.Parallel);
TapGesture.create();
TapGesture.onAction((event) => {
this.focusIndex = -1;
let a2 = event.fingerList.find(Boolean);
if (a2 === void 0) {
return;
@ -2319,6 +2326,18 @@ export class SegmentButton extends ViewPU {
});
this.isCurrentPositionSelected = false;
});
PanGesture.onActionCancel(() => {
if (this.options === void 0 || this.options.buttons === void 0) {
return;
}
if (this.options.type === 'capsule' && (this.options.multiply ?? false)) {
return;
}
Context.animateTo({ curve: curves.interpolatingSpring(10, 1, 410, 38) }, () => {
this.zoomScaleArray[this.selectedIndexes[0]] = 1;
});
this.isCurrentPositionSelected = false;
});
PanGesture.pop();
GestureGroup.pop();
Gesture.pop();
@ -2340,7 +2359,7 @@ export class SegmentButton extends ViewPU {
}, undefined, elmtId, () => {
}, {
page: "segmentbutton/src/main/ets/components/MainPage.ets",
line: 1121,
line: 1124,
u3: 11
});
ViewPU.create(componentCall);
@ -2412,7 +2431,7 @@ export class SegmentButton extends ViewPU {
}, undefined, elmtId, () => {
}, {
page: "segmentbutton/src/main/ets/components/MainPage.ets",
line: 1132,
line: 1135,
u3: 23
});
ViewPU.create(componentCall);
@ -2489,7 +2508,7 @@ export class SegmentButton extends ViewPU {
}, undefined, elmtId, () => {
}, {
page: "segmentbutton/src/main/ets/components/MainPage.ets",
line: 1158,
line: 1161,
u3: 13
});
ViewPU.create(componentCall);
@ -2522,7 +2541,7 @@ export class SegmentButton extends ViewPU {
}, undefined, elmtId, () => {
}, {
page: "segmentbutton/src/main/ets/components/MainPage.ets",
line: 1164,
line: 1167,
u3: 13
});
ViewPU.create(componentCall);
@ -2557,9 +2576,10 @@ export class SegmentButton extends ViewPU {
optionsArray: this.options.buttons,
options: this.options,
selectedIndexes: this.t2,
maxFontScale: this.getMaxFontSize()
maxFontScale: this.getMaxFontSize(),
onItemClicked: this.onItemClicked
}, undefined, elmtId, () => {
}, { page: "segmentbutton/src/main/ets/components/MainPage.ets", line: 1179, u3: 9 });
}, { page: "segmentbutton/src/main/ets/components/MainPage.ets", line: 1182, u3: 9 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
@ -2569,7 +2589,8 @@ export class SegmentButton extends ViewPU {
optionsArray: this.options.buttons,
options: this.options,
selectedIndexes: this.selectedIndexes,
maxFontScale: this.getMaxFontSize()
maxFontScale: this.getMaxFontSize(),
onItemClicked: this.onItemClicked
};
};
componentCall.paramsGenerator_ = paramsLambda;

View File

@ -627,6 +627,7 @@ struct SegmentButtonItemArrayComponent {
@State buttonHeight: number[] = Array.from({ length: MAX_ITEM_COUNT }, (_: Object, index) => 0)
private buttonItemsRealHeight: number[] = Array.from({ length: MAX_ITEM_COUNT }, (_: Object, index) => 0)
private groupId: string = util.generateRandomUUID(true)
public onItemClicked?: Callback<number>
onButtonItemsSizeChange() {
this.buttonItemsSize.forEach((value, index) => {
@ -877,6 +878,9 @@ struct SegmentButtonItemArrayComponent {
this.focusIndex = index
})
.gesture(TapGesture().onAction(() => {
if (this.onItemClicked) {
this.onItemClicked(index)
}
if (this.options.type === 'capsule' && (this.options.multiply ?? false)) {
if (this.selectedIndexes.indexOf(index) === -1) {
this.selectedIndexes.push(index)
@ -895,7 +899,7 @@ struct SegmentButtonItemArrayComponent {
animateTo({ curve: curves.interpolatingSpring(10, 1, 410, 38) }, () => {
this.zoomScaleArray[index] = 0.95
})
} else if (event.type === TouchType.Up) {
} else if (event.type === TouchType.Up || event.type === TouchType.Cancel) {
animateTo({ curve: curves.interpolatingSpring(10, 1, 410, 38) }, () => {
this.zoomScaleArray[index] = 1
})
@ -1182,7 +1186,8 @@ export struct SegmentButton {
optionsArray: this.options.buttons,
options: this.options,
selectedIndexes: $selectedIndexes,
maxFontScale: this.getMaxFontSize()
maxFontScale: this.getMaxFontSize(),
onItemClicked: this.onItemClicked
})
}
}
@ -1217,7 +1222,6 @@ export struct SegmentButton {
GestureGroup(GestureMode.Parallel,
TapGesture()
.onAction((event: GestureEvent) => {
this.focusIndex = -1
let fingerInfo = event.fingerList.find(Boolean)
if (fingerInfo === void 0) {
return
@ -1377,6 +1381,18 @@ export struct SegmentButton {
})
this.isCurrentPositionSelected = false
})
.onActionCancel(() => {
if (this.options === void 0 || this.options.buttons === void 0) {
return
}
if (this.options.type === 'capsule' && (this.options.multiply ?? false)) {
return
}
animateTo({ curve: curves.interpolatingSpring(10, 1, 410, 38) }, () => {
this.zoomScaleArray[this.selectedIndexes[0]] = 1
})
this.isCurrentPositionSelected = false
})
)
)
}

View File

@ -369,7 +369,7 @@ class SelectionMenuComponent extends ViewPU {
Scroll.shadow(this.theme.iconPanelShadowStyle);
Scroll.borderRadius(this.theme.containerBorderRadius);
Scroll.constraintSize({
maxHeight: `calc(100% - ${this.horizontalMenuHeight}vp)`,
maxHeight: `calc(100% - ${this.horizontalMenuHeight > 0 ? this.horizontalMenuHeight + this.theme.menuSpacing : 0}vp)`,
minWidth: this.theme.defaultMenuWidth
});
}, Scroll);

View File

@ -161,7 +161,7 @@ struct SelectionMenuComponent {
.shadow(this.theme.iconPanelShadowStyle)
.borderRadius(this.theme.containerBorderRadius)
.constraintSize({
maxHeight: `calc(100% - ${this.horizontalMenuHeight}vp)`,
maxHeight: `calc(100% - ${this.horizontalMenuHeight > 0 ? this.horizontalMenuHeight + this.theme.menuSpacing : 0}vp)`,
minWidth: this.theme.defaultMenuWidth
})
}

View File

@ -0,0 +1,17 @@
<svg width="20.000000" height="20.000000" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>
Created with Pixso.
</desc>
<defs>
<clipPath id="clip2_25">
<rect id="-" width="20.000000" height="20.000000" fill="white" fill-opacity="0"/>
</clipPath>
</defs>
<g clip-path="url(#clip2_25)">
<path id="直线 1" d="M3 8.5L8 8.5" stroke="#182431" stroke-opacity="0.900000" stroke-width="1.500000" stroke-linecap="round"/>
<path id="直线 1" d="M12 4L17 4" stroke="#182431" stroke-opacity="0.900000" stroke-width="1.500000" stroke-linecap="round"/>
<path id="直线 3" d="M12 8L17 8" stroke="#182431" stroke-opacity="0.900000" stroke-width="1.500000" stroke-linecap="round"/>
<path id="直线 4" d="M12 12L17 12" stroke="#182431" stroke-opacity="0.900000" stroke-width="1.500000" stroke-linecap="round"/>
<path id="直线 5" d="M3 16L17 16" stroke="#182431" stroke-opacity="0.900000" stroke-width="1.500000" stroke-linecap="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,17 @@
<svg width="20.000000" height="20.000000" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>
Created with Pixso.
</desc>
<defs>
<clipPath id="clip2_4">
<rect id="+" width="20.000000" height="20.000000" fill="white" fill-opacity="0"/>
</clipPath>
</defs>
<g clip-path="url(#clip2_4)">
<path id="矢量 1" d="M3 8.5L8 8.5M5.5 6L5.5 11" stroke="#182431" stroke-opacity="0.900000" stroke-width="1.500000" stroke-linecap="round"/>
<path id="直线 1" d="M12 4L17 4" stroke="#182431" stroke-opacity="0.900000" stroke-width="1.500000" stroke-linecap="round"/>
<path id="直线 3" d="M12 8L17 8" stroke="#182431" stroke-opacity="0.900000" stroke-width="1.500000" stroke-linecap="round"/>
<path id="直线 4" d="M12 12L17 12" stroke="#182431" stroke-opacity="0.900000" stroke-width="1.500000" stroke-linecap="round"/>
<path id="直线 5" d="M3 16L17 16" stroke="#182431" stroke-opacity="0.900000" stroke-width="1.500000" stroke-linecap="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,233 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Drawer } from 'common/src/main/ets/components/Drawer';
import { RadioBlock, ColorBlock, useEnabled } from 'common';
import { LengthMetrics, SelectionMenu, EditorMenuOptions, ExpandedMenuOptions, SelectionMenuOptions } from '@kit.ArkUI';
@Component
export struct TextBootcamp {
@Require @Prop title: ResourceStr;
@State showParameters: boolean = false;
@State isShow: boolean = false;
@State enableBackgroundColor: boolean = false;
@State compBackgroundColor: ResourceColor = 'rgb(255, 0, 0, 0.9)';
@State enableCaretColor: boolean = false;
@State caretColor: ResourceColor = 'rgb(255, 0, 0, 0.9)';
@State enableTextAlign: boolean = false;
@State textAlign: TextAlign = TextAlign.Center;
@State sliderShow: boolean = false;
@State enableCustomSelectionMenu: boolean = true;
@State customSelectionMenu: boolean = false;
@State enableSystemSelectionMenu: boolean = true;
@State systemSelectionMenu: boolean = true;
@State start: number = 0;
@State end: number = 0;
paragraph1: string =
'They can be classified as built-in componentsthose directly provided by the ArkUI framework and custom components \n\n';
paragraph2: string =
'those defined by developers. The built-in components include buttons radio buttonsprogress indicators and text.\n\n';
paragraph3: string =
'You can set the rendering effect of these components in method chaining mode, page components are divided into\n\n';
paragraph4: string =
'independent UI units to implement independent creation development and reuse of different units on pages making pages more engineering-oriented.';
paragraphStyleAttr: ParagraphStyle = new ParagraphStyle({ textIndent: LengthMetrics.vp(0) });
mutableStyledString1: MutableStyledString = new MutableStyledString(
this.paragraph1 + this.paragraph2 + this.paragraph3 + this.paragraph4, [
{
start: 0,
length: this.paragraph1.length + this.paragraph2.length + this.paragraph3.length + this.paragraph4.length,
styledKey: StyledStringKey.PARAGRAPH_STYLE,
styledValue: this.paragraphStyleAttr
},
])
controller: TextController = new TextController();
private expandedMenuOptions: Array<ExpandedMenuOptions> =
[
{
startIcon: $r('sys.media.ohos_ic_public_select_all'), content: '全选', action: () => {
if (!this.controller) {
return;
}
this.start = 0;
this.end = this.mutableStyledString1.length;
}
},
{
startIcon: $r('app.media.indent_plus'), content: '首行缩进+', action: () => {
if (!this.controller) {
return;
}
let textStyle = this.mutableStyledString1.getStyles(0, 1, StyledStringKey.PARAGRAPH_STYLE);
let paraAttr = textStyle[0].styledValue as ParagraphStyle;
let paraAttrIndent = paraAttr.textIndent! + 10;
this.mutableStyledString1.setStyle({
start: 0,
length: this.paragraph1.length + this.paragraph2.length +
this.paragraph3.length + this.paragraph4.length,
styledKey: StyledStringKey.PARAGRAPH_STYLE,
styledValue: new ParagraphStyle({ textIndent: LengthMetrics.vp(paraAttrIndent) })
})
this.controller.setStyledString(this.mutableStyledString1)
}
},
{
startIcon: $r('app.media.indent_minus'), content: '首行缩进-', action: () => {
if (!this.controller) {
return;
}
let textStyle = this.mutableStyledString1.getStyles(0, 1, StyledStringKey.PARAGRAPH_STYLE);
let paraAttr = textStyle[0].styledValue as ParagraphStyle;
let paraAttrIndent = paraAttr.textIndent! - 10;
this.mutableStyledString1.setStyle({
start: 0,
length: this.paragraph1.length + this.paragraph2.length +
this.paragraph3.length + this.paragraph4.length,
styledKey: StyledStringKey.PARAGRAPH_STYLE,
styledValue: new ParagraphStyle({ textIndent: LengthMetrics.vp(paraAttrIndent) })
})
this.controller.setStyledString(this.mutableStyledString1);
}
}]
private selectionMenuOptions: SelectionMenuOptions = {
expandedMenuOptions: this.expandedMenuOptions,
}
build() {
NavDestination() {
Drawer({
title: this.title,
showParameters: $showParameters,
content: () => {
this.Content()
},
parameters: () => {
this.Parameters()
}
})
}
.backgroundColor('#f1f3f5')
.hideTitleBar(true)
}
@Builder
Content() {
Column() {
Text(undefined, { controller: this.controller })
.bindSelectionMenu(
useEnabled(this.customSelectionMenu, TextSpanType.TEXT),
this.customSelectionMenu ? this.MyMenu() : undefined,
useEnabled(this.customSelectionMenu, TextResponseType.SELECT),
this.customSelectionMenu ? {
onDisappear: () => {
this.sliderShow = false;
}
} : undefined)
.onTextSelectionChange((selectionStart: number, selectionEnd: number) => {
this.start = selectionStart;
this.end = selectionEnd;
})
.copyOption(CopyOptions.LocalDevice)
.draggable(true)
.textAlign(useEnabled(this.enableTextAlign, this.textAlign))
.caretColor(useEnabled(this.enableCaretColor, this.caretColor))
.selectedBackgroundColor(useEnabled(this.enableBackgroundColor, this.compBackgroundColor))
.selection(this.start, this.end)
.margin({
start: LengthMetrics.vp(24),
end: LengthMetrics.vp(24)
})
}
.justifyContent(FlexAlign.Start)
.onAttach(() => {
this.controller.setStyledString(this.mutableStyledString1);
})
}
@Builder
Parameters() {
Scroll() {
Column({ space: 8 }) {
RadioBlock({
title: 'textAlign',
isEnabled: $enableTextAlign,
value: $textAlign,
dataSource: [
{ label: 'Start', value: TextAlign.Start },
{ label: 'Center', value: TextAlign.Center },
{ label: 'End', value: TextAlign.End },
{ label: 'JUSTIFY', value: TextAlign.JUSTIFY }
]
})
RadioBlock({
title: '是否设置自定义选择菜单',
isEnabled: this.enableCustomSelectionMenu,
value: this.customSelectionMenu,
dataSource: [
{ label: '自定义', value: true },
{ label: '默认', value: false }
]
})
if (this.customSelectionMenu) {
RadioBlock({
title: '是否显示首行缩进菜单',
isEnabled: this.enableSystemSelectionMenu,
value: this.systemSelectionMenu,
dataSource: [
{ label: '显示', value: true },
{ label: '隐藏', value: false }
]
})
}
ColorBlock({
title: '背景颜色',
isEnabled: this.enableBackgroundColor,
color: this.compBackgroundColor
})
ColorBlock({
title: '光标颜色',
isEnabled: this.enableCaretColor,
color: this.caretColor
})
}
.width('100%')
.height('100%')
.onAttach(() => {
this.controller.setStyledString(this.mutableStyledString1);
})
}
}
@Builder
MyMenu() {
Column() {
if (this.systemSelectionMenu) {
SelectionMenu(this.selectionMenuOptions)
}
}
.width(256)
}
}
@Preview
@Component
struct TextInputBootcampPreviewer {
build() {
TextBootcamp({
title: '文本/Text'
})
}
}

View File

@ -18,11 +18,13 @@ import { BindPopupBootcamp } from './BindPopupBootcamp';
import { ToastBootcamp } from './ToastBootcamp';
import { ImageboxGroupBootcamp } from '../displays/ImageBootcamp';
import { ScrollBarBootcamp } from './ScrollBarBootcamp';
import { TextBootcamp } from './TextBootcamp';
export const displaysRoute: RouteGroup = {
name: 'displays',
label: '展示类',
children: [
{ name: 'text', label: '文本/Text' },
{ name: 'bindpopup', label: '气泡/BindPopup' },
{ name: 'toast', label: '即时反馈/Toast' },
{ name: 'image', label: '图片/Image' },
@ -40,6 +42,8 @@ export function DisplaysDestination(name: string, route: Route) {
ImageboxGroupBootcamp({ title: route.label })
} else if (name === 'displays/scrollbar') {
ScrollBarBootcamp({ title: route.label })
} else if (name === 'displays/text') {
TextBootcamp({ title: route.label })
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Drawer } from 'common/src/main/ets/components/Drawer';
import { RadioBlock, ColorBlock, useEnabled } from 'common';
import { LengthMetrics } from '@kit.ArkUI';
@Component
export struct TextAreaBootcamp {
@Require @Prop title: ResourceStr;
@State showParameters: boolean = false;
@State isShow: boolean = false;
@State message1: string =
'They can be classified as built-in componentsthose directly provided \n\n by the ArkUI framework and custom components those defined by developers ' +
'The built-in components include buttons radio buttonsprogress indicators and text You can set the rendering effectof thesecomponents in method chaining mode,\n\n' +
'page components are divided into independent UI units to implementindependent creation development and reuse of different units on pages making pages more engineering-oriented.';
@State enableBackgroundColor: boolean = false;
@State compBackgroundColor: ResourceColor = 'rgb(255, 0, 0, 0.9)';
@State enableCaretColor: boolean = false;
@State caretColor: ResourceColor = 'rgb(255, 0, 0, 0.9)';
@State enableTextAlign: boolean = false;
@State textAlign: TextAlign = TextAlign.Start;
@State enableTextStyle: boolean = false;
@State textStyle: TextContentStyle = TextContentStyle.DEFAULT;
@State enableTextareaType: boolean = false;
@State textareaType: TextAreaType = TextAreaType.NORMAL;
build() {
NavDestination() {
Drawer({
title: this.title,
showParameters: $showParameters,
content: () => {
this.Content()
},
parameters: () => {
this.Parameters()
}
})
}
.backgroundColor('#f1f3f5')
.hideTitleBar(true)
}
@Builder
Content() {
Row() {
TextArea({ text: this.message1 })
.onChange((value: string) => {
this.message1 = value
})
.caretColor(useEnabled(this.enableCaretColor, this.caretColor))
.selectedBackgroundColor(useEnabled(this.enableBackgroundColor, this.compBackgroundColor))
.textAlign(useEnabled(this.enableTextAlign, this.textAlign))
.style(useEnabled(this.enableTextStyle, this.textStyle))
.type(useEnabled(this.enableTextareaType, this.textareaType))
.margin({
start: LengthMetrics.vp(24),
end: LengthMetrics.vp(24)
})
}.justifyContent(FlexAlign.Start)
}
@Builder
Parameters() {
Scroll() {
Column({ space: 8 }) {
RadioBlock({
title: 'textAlign',
isEnabled: $enableTextAlign,
value: $textAlign,
dataSource: [
{ label: 'Start', value: TextAlign.Start },
{ label: 'Center', value: TextAlign.Center },
{ label: 'End', value: TextAlign.End },
{ label: 'JUSTIFY', value: TextAlign.JUSTIFY }
]
})
RadioBlock({
title: 'textStyle',
isEnabled: $enableTextStyle,
value: $textStyle,
dataSource: [
{ label: 'DEFAULT', value: TextContentStyle.DEFAULT },
{ label: 'INLINE', value: TextContentStyle.INLINE }
]
})
RadioBlock({
title: 'textareaType',
isEnabled: $enableTextareaType,
value: $textareaType,
dataSource: [
{ label: 'NORMAL', value: TextAreaType.NORMAL },
{ label: 'URL', value: TextAreaType.URL }
]
})
ColorBlock({
title: '背景颜色',
isEnabled: this.enableBackgroundColor,
color: this.compBackgroundColor
})
ColorBlock({
title: '光标颜色',
isEnabled: this.enableCaretColor,
color: this.caretColor
})
}.width('100%')
}.height('50%')
}
}
@Preview
@Component
struct TextInputBootcampPreviewer {
build() {
TextAreaBootcamp({
title: '多行文本框/TextArea'
})
}
}

View File

@ -14,7 +14,8 @@
*/
import { Drawer } from 'common/src/main/ets/components/Drawer';
import { ColorBlock, useEnabled } from 'common';
import { RadioBlock, ColorBlock, useEnabled } from 'common';
import { LengthMetrics } from '@kit.ArkUI';
@Component
@ -22,11 +23,18 @@ export struct TextInputBootcamp {
@Require @Prop title: ResourceStr;
@State showParameters: boolean = false
@State isShow: boolean = false
@State text: string = ''
@State message1: string =
'They can be classified as built-in componentsthose directly provided \n\n by the ArkUI framework and custom components those defined by developers ' +
'The built-in components include buttons radio buttonsprogress indicators and text You can set the rendering effectof thesecomponents in method chaining mode,\n\n' +
'page components are divided into independent UI units to implementindependent creation development and reuse of different units on pages making pages more engineering-oriented.';
@State enablebackgroundColor: boolean = false;
@State ebackgroundColor: ResourceColor = 'rgb(255, 0, 0, 0.9)';
@State enablecaretColor: boolean = false;
@State ecaretColor: ResourceColor = 'rgb(255, 0, 0, 0.9)';
@State enableTextStyle: boolean = false;
@State textStyle: TextInputStyle = TextInputStyle.Default;
@State enableTextAlign: boolean = false;
@State textAlign: TextAlign = TextAlign.Start;
build() {
NavDestination() {
@ -48,12 +56,19 @@ export struct TextInputBootcamp {
@Builder
Content() {
Row() {
TextInput({ text: this.text })
TextInput({ text: this.message1 })
.onChange((value: string) => {
this.text = value
this.message1 = value
})
.caretColor(useEnabled(this.enablebackgroundColor, this.ebackgroundColor))
.selectedBackgroundColor(useEnabled(this.enablecaretColor, this.ecaretColor))
.style(useEnabled(this.enableTextStyle, this.textStyle))
.textAlign(useEnabled(this.enableTextAlign, this.textAlign))
.maxLines(10)
.margin({
start: LengthMetrics.vp(24),
end: LengthMetrics.vp(24)
})
}.justifyContent(FlexAlign.Start)
}
@ -61,6 +76,26 @@ export struct TextInputBootcamp {
Parameters() {
Scroll() {
Column({ space: 8 }) {
RadioBlock({
title: 'textStyle',
isEnabled: $enableTextStyle,
value: $textStyle,
dataSource: [
{ label: 'Default', value: TextInputStyle.Default },
{ label: 'Inline', value: TextInputStyle.Inline }
]
})
RadioBlock({
title: 'textAlign',
isEnabled: $enableTextAlign,
value: $textAlign,
dataSource: [
{ label: 'Start', value: TextAlign.Start },
{ label: 'Center', value: TextAlign.Center },
{ label: 'End', value: TextAlign.End },
{ label: 'JUSTIFY', value: TextAlign.JUSTIFY }
]
})
ColorBlock({
title: '光标颜色',
isEnabled: this.enablebackgroundColor,

View File

@ -15,6 +15,7 @@
import { Route, RouteGroup } from 'common/src/main/ets/common/route';
import { TextInputBootcamp } from './TextInputBootcamp';
import { TextAreaBootcamp } from './TextAreaBootcamp';
import { RichEditorBootcamp } from './RichEditorBootcamp';
@ -22,15 +23,18 @@ export const inputsRoute: RouteGroup = {
name: 'inputs',
label: '输入类',
children: [
{ name: 'text', label: '单行文本框/TextInput' },
{ name: 'textInput', label: '单行文本框/TextInput' },
{ name: 'textArea', label: '多行文本框/TextArea' },
{ name: 'richeditor', label: '富文本/RichEditor' },
]
};
@Builder
export function InputsDestination(name: string, route: Route) {
if (name === 'inputs/text') {
if (name === 'inputs/textInput') {
TextInputBootcamp({ title: route.label })
} else if (name === 'inputs/textArea') {
TextAreaBootcamp({ title: route.label })
} else if (name === 'inputs/richeditor') {
RichEditorBootcamp({ title: route.label })
}

View File

@ -25,7 +25,7 @@ struct VectorF {
bool operator==(const VectorF& other) const
{
return NearEqual(x, other.x) && NearEqual(y, other.y);
return NearEqual(x, other.x, 1e-5) && NearEqual(y, other.y, 1e-5);
}
VectorF operator*(float value) const

View File

@ -20,6 +20,8 @@
#include "base/log/log.h"
#include "base/memory/memory_monitor.h"
#include "base/thread/frame_trace_adapter.h"
#include "base/thread/task_executor.h"
#include "base/thread/thread_priority.h"
namespace OHOS::Ace {
namespace {
@ -190,6 +192,7 @@ void BackgroundTaskExecutor::ThreadLoop(uint32_t threadNo)
return;
}
SetThreadName(threadNo);
ThreadPriority::SetThreadPriority(TaskExecutor::TaskType::BACKGROUND);
Task task;
const uint32_t purgeFlag = (1u << (threadNo - 1u));
std::unique_lock<std::mutex> lock(mutex_);

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2021-2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FOUNDATION_ACE_FRAMEWORKS_BASE_THREAD_THREAD_PRIORITY_H
#define FOUNDATION_ACE_FRAMEWORKS_BASE_THREAD_THREAD_PRIORITY_H
#include "base/thread/task_executor.h"
namespace OHOS::Ace {
class ThreadPriority {
public:
~ThreadPriority() = default;
static void SetThreadPriority(TaskExecutor::TaskType taskType);
static void setBackGroundThreadPriority();
private:
ThreadPriority() = default;
};
} // namespace OHOS::Ace
#endif // FOUNDATION_ACE_FRAMEWORKS_BASE_THREAD_THREAD_PRIORITY_H

View File

@ -365,6 +365,8 @@ public:
return buildTraceEnable_;
}
static bool GetCacheNavigationNodeEnable();
static bool GetAccessibilityEnabled()
{
return accessibilityEnabled_;
@ -613,6 +615,7 @@ private:
static std::atomic<bool> layoutTraceEnable_;
static std::atomic<bool> traceInputEventEnable_;
static bool buildTraceEnable_;
static bool cacheNavigationNodeEnable_;
static bool syncDebugTraceEnable_;
static bool pixelRoundEnable_;
static bool textTraceEnable_;

View File

@ -1076,7 +1076,7 @@ class BorderModifier extends ModifierWithKey<ArkBorder> {
this.value.arkWidth.start, this.value.arkWidth.end, this.value.arkColor.startColor, this.value.arkColor.endColor,
this.value.arkRadius.topStart, this.value.arkRadius.topEnd, this.value.arkRadius.bottomStart, this.value.arkRadius.bottomEnd,
isLocalizedBorderWidth, isLocalizedBorderColor, isLocalizedBorderRadius,
this.value.arkDashGap.start, this.value.arkDashGap.end,this.value.arkDashWidth.start, this.value.arkDashWidth.end
this.value.arkDashGap.start, this.value.arkDashGap.end, this.value.arkDashWidth.start, this.value.arkDashWidth.end
);
}
}

View File

@ -125,6 +125,13 @@ class FrameNode {
getNodePtr(): NodePtr | null {
return this.nodePtr_;
}
getValidNodePtr(): NodePtr {
if (this._nativeRef) {
return this.nodePtr_;
} else {
throw Error('The FrameNode has been disposed!');
}
}
dispose(): void {
this.renderNode_?.dispose();
FrameNodeFinalizationRegisterProxy.ElementIdToOwningFrameNode_.delete(this._nodeId);
@ -359,47 +366,47 @@ class FrameNode {
}
getPositionToParent(): Position {
const position = getUINativeModule().frameNode.getPositionToParent(this.getNodePtr());
const position = getUINativeModule().frameNode.getPositionToParent(this.getValidNodePtr());
return { x: position[0], y: position[1] };
}
getPositionToScreen(): Position {
const position = getUINativeModule().frameNode.getPositionToScreen(this.getNodePtr());
const position = getUINativeModule().frameNode.getPositionToScreen(this.getValidNodePtr());
return { x: position[0], y: position[1] };
}
getPositionToWindow(): Position {
const position = getUINativeModule().frameNode.getPositionToWindow(this.getNodePtr());
const position = getUINativeModule().frameNode.getPositionToWindow(this.getValidNodePtr());
return { x: position[0], y: position[1] };
}
getPositionToParentWithTransform(): Position {
const position = getUINativeModule().frameNode.getPositionToParentWithTransform(this.getNodePtr());
const position = getUINativeModule().frameNode.getPositionToParentWithTransform(this.getValidNodePtr());
return { x: position[0], y: position[1] };
}
getPositionToScreenWithTransform(): Position {
const position = getUINativeModule().frameNode.getPositionToScreenWithTransform(this.getNodePtr());
const position = getUINativeModule().frameNode.getPositionToScreenWithTransform(this.getValidNodePtr());
return { x: position[0], y: position[1] };
}
getPositionToWindowWithTransform(): Position {
const position = getUINativeModule().frameNode.getPositionToWindowWithTransform(this.getNodePtr());
const position = getUINativeModule().frameNode.getPositionToWindowWithTransform(this.getValidNodePtr());
return { x: position[0], y: position[1] };
}
getMeasuredSize(): Size {
const size = getUINativeModule().frameNode.getMeasuredSize(this.getNodePtr());
const size = getUINativeModule().frameNode.getMeasuredSize(this.getValidNodePtr());
return { width: size[0], height: size[1] };
}
getLayoutPosition(): Position {
const position = getUINativeModule().frameNode.getLayoutPosition(this.getNodePtr());
const position = getUINativeModule().frameNode.getLayoutPosition(this.getValidNodePtr());
return { x: position[0], y: position[1] };
}
getUserConfigBorderWidth(): EdgesT<LengthMetrics> {
const borderWidth = getUINativeModule().frameNode.getConfigBorderWidth(this.getNodePtr());
const borderWidth = getUINativeModule().frameNode.getConfigBorderWidth(this.getValidNodePtr());
return {
top: new LengthMetrics(borderWidth[0], borderWidth[1]),
right: new LengthMetrics(borderWidth[2], borderWidth[3]),
@ -409,7 +416,7 @@ class FrameNode {
}
getUserConfigPadding(): EdgesT<LengthMetrics> {
const borderWidth = getUINativeModule().frameNode.getConfigPadding(this.getNodePtr());
const borderWidth = getUINativeModule().frameNode.getConfigPadding(this.getValidNodePtr());
return {
top: new LengthMetrics(borderWidth[0], borderWidth[1]),
right: new LengthMetrics(borderWidth[2], borderWidth[3]),
@ -419,7 +426,7 @@ class FrameNode {
}
getUserConfigMargin(): EdgesT<LengthMetrics> {
const margin = getUINativeModule().frameNode.getConfigMargin(this.getNodePtr());
const margin = getUINativeModule().frameNode.getConfigMargin(this.getValidNodePtr());
return {
top: new LengthMetrics(margin[0], margin[1]),
right: new LengthMetrics(margin[2], margin[3]),
@ -429,7 +436,7 @@ class FrameNode {
}
getUserConfigSize(): SizeT<LengthMetrics> {
const size = getUINativeModule().frameNode.getConfigSize(this.getNodePtr());
const size = getUINativeModule().frameNode.getConfigSize(this.getValidNodePtr());
return {
width: new LengthMetrics(size[0], size[1]),
height: new LengthMetrics(size[2], size[3])

View File

@ -13,20 +13,26 @@
* limitations under the License.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
class BasicPrefetcher implements IPrefetcher {
private readonly prefetcher = new Prefetcher(new PrefetchRangeEvaluator(), new DefaultTimeProvider());
private readonly fetchingDriver: FetchingDriver;
constructor(ds?: IDataSourcePrefetching) {
if (ds) {
this.prefetcher.setDataSource(ds);
}
const itemsOnScreen = new ItemsOnScreenProvider();
const fetchedRegistry = new FetchedRegistry();
const fetchingRegistry = new FetchingRegistry();
const prefetchRangeRatio = new PrefetchRangeRatio(itemsOnScreen, fetchedRegistry, fetchingRegistry);
const prefetchCount = new PrefetchCount(itemsOnScreen, prefetchRangeRatio);
const evaluator = new FetchingRangeEvaluator(itemsOnScreen, prefetchCount, prefetchRangeRatio, fetchedRegistry);
this.fetchingDriver = new FetchingDriver(fetchedRegistry, fetchingRegistry, evaluator, new DefaultTimeProvider());
this.fetchingDriver.setDataSource(ds);
}
setDataSource(ds: IDataSourcePrefetching): void {
this.prefetcher.setDataSource(ds);
this.fetchingDriver.setDataSource(ds);
}
visibleAreaChanged(minVisible: number, maxVisible: number): void {
this.prefetcher.visibleAreaChanged(minVisible, maxVisible);
this.fetchingDriver.visibleAreaChanged(minVisible, maxVisible);
}
}

View File

@ -13,6 +13,7 @@
* limitations under the License.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface IDataSourcePrefetching extends IDataSource {
prefetch(index: number): Promise<void> | void;
cancel?(index: number): Promise<void> | void;

View File

@ -13,6 +13,7 @@
* limitations under the License.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface IPrefetcher {
setDataSource(ds: IDataSourcePrefetching): void;
visibleAreaChanged(minVisible: number, maxVisible: number): void;

View File

@ -0,0 +1,181 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
interface DataCollectionChangeListener {
batchUpdate(operations: BatchOperation[]): void;
}
interface ItemsDeleted {
kind: 'deleted';
startIndex: number;
count: number;
}
interface ItemsAdded {
kind: 'added';
startIndex: number;
count: number;
}
interface ItemUpdated {
kind: 'updated';
index: number;
}
interface CollectionReloaded {
kind: 'reloaded';
totalCount: number;
}
interface ItemsSwapped {
kind: 'swapped';
a: number;
b: number;
}
interface ItemMoved {
kind: 'moved';
from: number;
to: number;
}
type BatchOperation = ItemsDeleted | ItemsAdded | ItemUpdated | CollectionReloaded | ItemsSwapped | ItemMoved;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
class DataSourceObserver implements DataChangeListener {
private dataSource: IDataSource;
constructor(private readonly simpleChangeListener: DataCollectionChangeListener) {}
onDataReloaded(): void {
this.simpleChangeListener.batchUpdate([
{
kind: 'reloaded',
totalCount: this.dataSource.totalCount(),
},
]);
}
onDataAdded(index: number): void {
this.simpleChangeListener.batchUpdate([
{
kind: 'added',
startIndex: index,
count: 1,
},
]);
}
onDataAdd(index: number): void {
this.onDataAdded(index);
}
onDataMoved(from: number, to: number): void {
this.simpleChangeListener.batchUpdate([
{
kind: 'swapped',
a: from,
b: to,
},
]);
}
onDataMove(from: number, to: number): void {
this.onDataMoved(from, to);
}
onDataDeleted(index: number): void {
this.simpleChangeListener.batchUpdate([
{
kind: 'deleted',
startIndex: index,
count: 1,
},
]);
}
onDataDelete(index: number): void {
this.onDataDeleted(index);
}
onDataChanged(index: number): void {
this.simpleChangeListener.batchUpdate([
{
kind: 'updated',
index,
},
]);
}
onDataChange(index: number): void {
this.onDataChanged(index);
}
onDatasetChange(dataOperations: DataOperation[]): void {
const operations: BatchOperation[] = [];
dataOperations.forEach((operation) => {
switch (operation.type) {
case 'add':
case 'delete':
if (operation.count === undefined || operation.count > 0) {
operations.push({
kind: operation.type === 'add' ? 'added' : 'deleted',
startIndex: operation.index,
count: operation.count ?? 1,
});
}
break;
case 'change':
operations.push({
kind: 'updated',
index: operation.index,
});
break;
case 'reload':
operations.push({
kind: 'reloaded',
totalCount: this.dataSource.totalCount(),
});
break;
case 'exchange':
operations.push({
kind: 'swapped',
a: operation.index.start,
b: operation.index.end,
});
break;
case 'move':
operations.push({
kind: 'moved',
from: operation.index.from,
to: operation.index.to,
});
break;
default:
assertNever(operation);
}
});
this.simpleChangeListener.batchUpdate(operations);
}
setDataSource(dataSource: IDataSource): void {
if (this.dataSource) {
this.dataSource.unregisterDataChangeListener(this);
}
this.dataSource = dataSource;
this.dataSource.registerDataChangeListener(this);
this.onDataReloaded();
}
}

View File

@ -13,26 +13,79 @@
* limitations under the License.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
class FetchedRegistry {
private readonly fetchedIndicies: Set<number> = new Set(); // {I where I is in cache} ∩ `prefetchRange`
private rangeToPrefetch: IndexRange = new IndexRange(0, 0);
private fetchedIndexes: Set<number> = new Set();
private rangeToFetchInternal: IndexRange = new IndexRange(0, 0);
private missedIndexes: Set<number> = new Set();
addFetched(index: number): void {
this.fetchedIndicies.add(index);
get rangeToFetch(): IndexRange {
return this.rangeToFetchInternal;
}
getFetchedInRange(range: IndexRange): void {
addFetched(index: number): void {
if (this.rangeToFetch.contains(index)) {
this.fetchedIndexes.add(index);
this.missedIndexes.delete(index);
}
}
removeFetched(index: number): void {
if (this.rangeToFetch.contains(index)) {
this.fetchedIndexes.delete(index);
this.missedIndexes.add(index);
}
}
has(index: number): boolean {
return this.fetchedIndexes.has(index);
}
getFetchedInRange(range: IndexRange): number {
let fetched = 0;
range.forEachIndex((index) => {
fetched += this.fetchedIndicies.has(index) ? 1 : 0;
fetched += this.fetchedIndexes.has(index) ? 1 : 0;
});
return fetched;
}
updateRangeToPrefetch(prefetchRange: IndexRange): void {
this.rangeToPrefetch.subtract(prefetchRange).forEachIndex((index) => {
this.fetchedIndicies.delete(index);
updateRangeToFetch(fetchRange: IndexRange): void {
this.rangeToFetch.subtract(fetchRange).forEachIndex((index) => {
this.fetchedIndexes.delete(index);
});
this.rangeToPrefetch = prefetchRange;
this.rangeToFetchInternal = fetchRange;
this.missedIndexes.clear();
this.rangeToFetch.forEachIndex((index) => {
if (!this.fetchedIndexes.has(index)) {
this.missedIndexes.add(index);
}
});
}
getItemsToFetch(): Set<number> {
return new Set(this.missedIndexes);
}
incrementFetchedGreaterThen(value: number, newFetchRange: IndexRange): void {
this.offsetAllGreaterThen(value, 1);
this.updateRangeToFetch(newFetchRange);
}
decrementFetchedGreaterThen(value: number, newFetchRange: IndexRange): void {
this.offsetAllGreaterThen(value, -1);
this.updateRangeToFetch(newFetchRange);
}
private offsetAllGreaterThen(value: number, offset: number): void {
const updated = new Set<number>();
this.fetchedIndexes.forEach((index) => {
updated.add(index > value ? index + offset : index);
});
this.fetchedIndexes = updated;
}
clearFetched(newFetchRange: IndexRange): void {
this.fetchedIndexes.clear();
this.updateRangeToFetch(newFetchRange);
}
}

View File

@ -0,0 +1,300 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
interface ITimeProvider {
getCurrent: () => number;
}
type Millisecond = number;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
class DefaultTimeProvider implements ITimeProvider {
getCurrent(): number {
return Date.now();
}
}
const dummyDataSource: IDataSourcePrefetching = {
prefetch: () => {},
totalCount: () => {
return 0;
},
getData: () => {
return undefined;
},
registerDataChangeListener: () => {},
unregisterDataChangeListener: () => {},
};
const DELAY_TO_REPEAT_FETCH_AFTER_ERROR = 500;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
class FetchingDriver implements DataCollectionChangeListener {
private dataSource: IDataSourcePrefetching | null = dummyDataSource;
private readonly dataSourceObserver = new DataSourceObserver(this);
private isPaused: boolean;
constructor(
private readonly fetchedRegistry: FetchedRegistry,
private readonly fetches: FetchingRegistry,
private readonly prefetchRangeEvaluator: IFetchingRangeEvaluator,
private readonly timeProvider: ITimeProvider,
private readonly logger: ILogger = dummyLogger,
autostart: boolean = true,
) {
this.isPaused = !autostart;
this.prefetchRangeEvaluator = prefetchRangeEvaluator;
this.timeProvider = timeProvider;
}
get afterErrorDelay(): Millisecond {
return DELAY_TO_REPEAT_FETCH_AFTER_ERROR;
}
batchUpdate(operations: BatchOperation[]): void {
this.logger.info('batchUpdate called with ' + JSON.stringify(operations));
try {
this.batchUpdateInternal(operations);
} catch (e) {
reportError(this.logger, 'batchUpdate', e);
throw e;
}
}
private batchUpdateInternal(operations: BatchOperation[]): void {
operations.forEach((operation) => {
switch (operation.kind) {
case 'deleted':
this.itemsDeleted(operation.startIndex, operation.count);
break;
case 'added':
this.itemsAdded(operation.startIndex, operation.count);
break;
case 'updated':
this.itemUpdated(operation.index);
break;
case 'reloaded':
this.collectionChanged(operation.totalCount);
break;
case 'swapped':
this.itemsSwapped(operation.a, operation.b);
break;
case 'moved':
this.itemMoved(operation.from, operation.to);
break;
}
});
this.prefetch(this.fetchedRegistry.getItemsToFetch());
}
private collectionChanged(totalCount: number): void {
this.prefetchRangeEvaluator.updateRangeToFetch({
kind: 'collection-changed',
totalCount: totalCount,
});
}
private itemUpdated(index: number): void {
this.fetchedRegistry.removeFetched(index);
this.fetches.deleteFetchByItem(index);
}
private itemsDeleted(index: number, count: number): void {
for (let i = 0; i < count; i++) {
this.fetches.decrementAllIndexesGreaterThen(index);
this.prefetchRangeEvaluator.updateRangeToFetch({ kind: 'item-removed', itemIndex: index });
}
}
private itemsAdded(index: number, count: number): void {
for (let i = 0; i < count; i++) {
this.fetches.incrementAllIndexesGreaterThen(index - 1);
this.prefetchRangeEvaluator.updateRangeToFetch({ kind: 'item-added', itemIndex: index });
}
}
private itemsSwapped(a: number, b: number): void {
if (!this.fetchedRegistry.has(a) || !this.fetchedRegistry.has(b)) {
this.fetchedRegistry.removeFetched(a);
this.fetchedRegistry.removeFetched(b);
}
}
private itemMoved(from: number, to: number): void {
if (!this.fetchedRegistry.has(from) || !this.fetchedRegistry.has(to)) {
const rangeToFetch = this.fetchedRegistry.rangeToFetch;
this.itemsDeleted(from, 1);
this.itemsAdded(to, 1);
this.fetchedRegistry.updateRangeToFetch(rangeToFetch);
}
}
setDataSource(ds: IDataSourcePrefetching = dummyDataSource): void {
this.logger.info(`setDataSource called with ${ds !== dummyDataSource ? 'a data source' : 'null or undefined'}`);
try {
this.setDataSourceInternal(ds);
} catch (e) {
reportError(this.logger, 'setDataSource', e);
throw e;
}
}
private setDataSourceInternal(ds: IDataSourcePrefetching): void {
this.dataSource = ds ?? dummyDataSource;
this.dataSourceObserver.setDataSource(this.dataSource);
}
stop(): void {
this.logger.info('Stop called');
try {
this.stopInternal();
} catch (e) {
reportError(this.logger, 'stop', e);
throw e;
}
}
private stopInternal(): void {
if (this.isPaused) {
return;
}
this.isPaused = true;
this.cancel(this.fetches.getAllIndexes());
}
start(): void {
this.logger.info('Start called');
try {
this.startInternal();
} catch (e) {
reportError(this.logger, 'start', e);
throw e;
}
}
private startInternal(): void {
if (!this.isPaused) {
return;
}
this.isPaused = false;
this.prefetch(this.fetchedRegistry.getItemsToFetch());
}
visibleAreaChanged(minVisible: number, maxVisible: number): void {
this.logger.info(`visibleAreaChanged min: ${minVisible} max: ${maxVisible}`);
try {
this.visibleAreaChangedInternal(minVisible, maxVisible);
} catch (e) {
reportError(this.logger, 'visibleAreaChanged', e);
throw e;
}
}
private visibleAreaChangedInternal(minVisible: number, maxVisible: number): void {
if (this.dataSource === dummyDataSource) {
throw new Error('No data source');
}
const oldRangeToPrefetch = this.fetchedRegistry.rangeToFetch;
this.prefetchRangeEvaluator.updateRangeToFetch({ kind: 'visible-area-changed', minVisible, maxVisible });
this.prefetch(this.fetchedRegistry.getItemsToFetch());
const toCancel = oldRangeToPrefetch.subtract(this.fetchedRegistry.rangeToFetch).toSet();
this.cancel(toCancel);
}
private prefetch(toPrefetch: ReadonlySet<number>): void {
if (this.isPaused) {
this.logger.debug('Prefetcher is paused. Do nothing.');
return;
}
toPrefetch.forEach(this.singleFetch);
}
private singleFetch = (itemIndex: ItemIndex): void => {
if (this.fetches.isFetchingItem(itemIndex) || this.fetchedRegistry.has(itemIndex)) {
return;
}
const prefetchStart = this.timeProvider.getCurrent();
const fetchId = this.fetches.registerFetch(itemIndex);
this.logger.info('to prefetch ' + itemIndex);
try {
const prefetchResponse = this.dataSource!.prefetch(itemIndex);
if (!(prefetchResponse instanceof Promise)) {
this.fetchedCallback(fetchId, prefetchStart);
return;
}
prefetchResponse
.then(() => this.fetchedCallback(fetchId, prefetchStart))
.catch((e) => {
this.errorOnFetchCallback(fetchId, e);
});
} catch (e) {
this.errorOnFetchCallback(fetchId, e);
}
};
private fetchedCallback(fetchId: FetchId, prefetchStart: number): void {
const itemIndex = this.fetches.getItem(fetchId);
this.fetches.deleteFetch(fetchId);
if (itemIndex === undefined) {
return;
}
this.prefetchRangeEvaluator.updateRangeToFetch({
kind: 'item-fetched',
itemIndex,
fetchDuration: this.timeProvider.getCurrent() - prefetchStart,
});
this.prefetch(this.fetchedRegistry.getItemsToFetch());
}
private errorOnFetchCallback(fetchId: FetchId, error: Error): void {
const itemIndex = this.fetches.getItem(fetchId);
if (itemIndex !== undefined) {
this.logger.warn(`failed to fetch item at ${itemIndex} ${JSON.stringify(error)}`);
}
this.fetches.deleteFetch(fetchId);
setTimeout(() => {
this.prefetch(this.fetchedRegistry.getItemsToFetch());
}, this.afterErrorDelay);
}
private cancel(toCancel: ReadonlySet<number>): void {
toCancel.forEach((itemIndex) => {
if (!this.fetches.isFetchingItem(itemIndex)) {
return;
}
this.fetches.deleteFetchByItem(itemIndex);
if (this.dataSource!.cancel) {
this.logger.info('to cancel ' + itemIndex);
this.dataSource.cancel(itemIndex);
}
});
}
}

View File

@ -0,0 +1,190 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
interface IFetchingRangeEvaluator {
updateRangeToFetch(whatHappened: RangeUpdateEvent): void;
}
type RangeUpdateEvent =
| {
kind: 'visible-area-changed';
minVisible: number;
maxVisible: number;
}
| {
kind: 'item-fetched';
itemIndex: number;
fetchDuration: number;
}
| {
kind: 'collection-changed';
totalCount: number;
}
| {
kind: 'item-added';
itemIndex: number;
}
| {
kind: 'item-removed';
itemIndex: number;
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
class FetchingRangeEvaluator implements IFetchingRangeEvaluator {
protected totalItems = 0;
constructor(
private readonly itemsOnScreen: ItemsOnScreenProvider,
private readonly prefetchCount: PrefetchCount,
private readonly prefetchRangeRatio: PrefetchRangeRatio,
protected readonly fetchedRegistry: FetchedRegistry,
private readonly logger: ILogger = dummyLogger,
) {}
updateRangeToFetch(whatHappened: RangeUpdateEvent): void {
switch (whatHappened.kind) {
case 'visible-area-changed':
this.onVisibleAreaChange(whatHappened.minVisible, whatHappened.maxVisible);
break;
case 'item-fetched':
this.onItemFetched(whatHappened.itemIndex, whatHappened.fetchDuration);
break;
case 'collection-changed':
this.onCollectionChanged(whatHappened.totalCount);
break;
case 'item-added':
this.onItemAdded(whatHappened.itemIndex);
break;
case 'item-removed':
this.onItemDeleted(whatHappened.itemIndex);
break;
default:
assertNever(whatHappened);
}
}
protected onVisibleAreaChange(minVisible: number, maxVisible: number): void {
const oldVisibleRange = this.itemsOnScreen.visibleRange;
this.itemsOnScreen.update(minVisible, maxVisible);
this.logger.debug(
`visibleAreaChanged itemsOnScreen=${this.itemsOnScreen.visibleRange.length}, meanImagesOnScreen=${this.itemsOnScreen.meanValue}, prefetchCountCurrentLimit=${this.prefetchCount.currentMaxItems}, prefetchCountMaxRatio=${this.prefetchRangeRatio.maxRatio}`,
);
if (!oldVisibleRange.equals(this.itemsOnScreen.visibleRange)) {
this.prefetchCount.prefetchCountValue = this.evaluatePrefetchCount('visible-area-changed');
const rangeToFetch = this.prefetchCount.getRangeToFetch(this.totalItems);
this.fetchedRegistry.updateRangeToFetch(rangeToFetch);
}
}
protected onItemFetched(index: number, fetchDuration: number): void {
if (!this.fetchedRegistry.rangeToFetch.contains(index)) {
return;
}
this.logger.debug(`onItemFetched`);
let maxRatioChanged = false;
if (this.prefetchRangeRatio.update(index, fetchDuration) === 'ratio-changed') {
maxRatioChanged = true;
this.logger.debug(
`choosePrefetchCountLimit prefetchCountMaxRatio=${this.prefetchRangeRatio.maxRatio}, prefetchCountMinRatio=${this.prefetchRangeRatio.minRatio}, prefetchCountCurrentLimit=${this.prefetchCount.currentMaxItems}`,
);
}
this.fetchedRegistry.addFetched(index);
this.prefetchCount.prefetchCountValue = this.evaluatePrefetchCount('resolved', maxRatioChanged);
const rangeToFetch = this.prefetchCount.getRangeToFetch(this.totalItems);
this.fetchedRegistry.updateRangeToFetch(rangeToFetch);
}
private evaluatePrefetchCount(event: 'resolved' | 'visible-area-changed', maxRatioChanged?: boolean): number {
let ratio = this.prefetchRangeRatio.calculateRatio(this.prefetchCount.prefetchCountValue, this.totalItems);
let evaluatedPrefetchCount = this.prefetchCount.getPrefetchCountByRatio(ratio);
if (maxRatioChanged) {
ratio = this.prefetchRangeRatio.calculateRatio(evaluatedPrefetchCount, this.totalItems);
evaluatedPrefetchCount = this.prefetchCount.getPrefetchCountByRatio(ratio);
}
if (!this.prefetchRangeRatio.hysteresisEnabled) {
if (event === 'resolved') {
this.prefetchRangeRatio.updateRatioRange(ratio);
this.prefetchRangeRatio.hysteresisEnabled = true;
} else if (event === 'visible-area-changed') {
this.prefetchRangeRatio.oldRatio = ratio;
}
} else if (this.prefetchRangeRatio.range.contains(ratio)) {
return this.prefetchCount.prefetchCountValue;
} else {
if (event === 'resolved') {
this.prefetchRangeRatio.updateRatioRange(ratio);
} else if (event === 'visible-area-changed') {
this.prefetchRangeRatio.setEmptyRange();
this.prefetchRangeRatio.oldRatio = ratio;
this.prefetchRangeRatio.hysteresisEnabled = false;
}
}
this.logger.debug(
`evaluatePrefetchCount event=${event}, ${this.prefetchRangeRatio.hysteresisEnabled ? 'inHysteresis' : 'setHysteresis'} prefetchCount=${evaluatedPrefetchCount}, ratio=${ratio}, hysteresisRange=${this.prefetchRangeRatio.range}`,
);
return evaluatedPrefetchCount;
}
protected onCollectionChanged(totalCount: number): void {
this.totalItems = Math.max(0, totalCount);
let newRangeToFetch: IndexRange;
if (this.fetchedRegistry.rangeToFetch.length > 0) {
newRangeToFetch = this.itemsOnScreen.visibleRange;
} else {
newRangeToFetch = this.fetchedRegistry.rangeToFetch;
}
if (newRangeToFetch.end > this.totalItems) {
const end = this.totalItems;
const start = newRangeToFetch.start < end ? newRangeToFetch.start : end;
newRangeToFetch = new IndexRange(start, end);
}
this.fetchedRegistry.clearFetched(newRangeToFetch);
}
private onItemDeleted(itemIndex: number): void {
if (this.totalItems === 0) {
return;
}
this.totalItems--;
this.fetchedRegistry.removeFetched(itemIndex);
const end =
this.fetchedRegistry.rangeToFetch.end < this.totalItems ? this.fetchedRegistry.rangeToFetch.end : this.totalItems;
const rangeToFetch = new IndexRange(this.fetchedRegistry.rangeToFetch.start, end);
this.fetchedRegistry.decrementFetchedGreaterThen(itemIndex, rangeToFetch);
}
private onItemAdded(itemIndex: number): void {
this.totalItems++;
if (itemIndex > this.fetchedRegistry.rangeToFetch.end) {
return;
}
const end = this.fetchedRegistry.rangeToFetch.end + 1;
const rangeToFetch = new IndexRange(this.fetchedRegistry.rangeToFetch.start, end);
this.fetchedRegistry.incrementFetchedGreaterThen(itemIndex - 1, rangeToFetch);
}
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
type FetchId = number;
type ItemIndex = number;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
class FetchingRegistry {
private readonly fetches = new Map<FetchId, ItemIndex>();
private fetching = new Map<ItemIndex, FetchId>();
private readonly fetchesBefore = new Map<ItemIndex, number>();
private fetchCounter = 0;
registerFetch(index: ItemIndex): FetchId {
let fetchId = this.fetching.get(index);
if (fetchId !== undefined) {
return fetchId;
}
fetchId = ++this.fetchCounter;
this.fetching.set(index, fetchId);
this.fetches.set(fetchId, index);
this.fetchesBefore.set(index, this.fetches.size);
return fetchId;
}
getItem(fetchId: FetchId): ItemIndex {
return this.fetches.get(fetchId);
}
deleteFetch(fetchId: FetchId): void {
const index = this.fetches.get(fetchId);
if (index !== undefined) {
this.fetching.delete(index);
this.fetches.delete(fetchId);
}
}
deleteFetchByItem(index: ItemIndex): void {
const fetchId = this.fetching.get(index);
if (fetchId !== undefined) {
this.fetching.delete(index);
this.fetches.delete(fetchId);
}
}
isFetchingItem(index: ItemIndex): boolean {
return this.fetching.has(index);
}
incrementAllIndexesGreaterThen(value: number): void {
this.offsetAllIndexesGreaterThen(value, 1);
}
getAllIndexes(): Set<number> {
const set = new Set<number>();
this.fetching.forEach((fetchId, itemIndex) => set.add(itemIndex));
return set;
}
getFetchesCount(): number {
return this.fetches.size;
}
isFetchLatecomer(index: ItemIndex, threshold: number): boolean {
return this.fetchesBefore.get(index) > threshold;
}
private offsetAllIndexesGreaterThen(value: number, offset: number): void {
const newFetching = new Map<ItemIndex, FetchId>();
this.fetches.forEach((index, fetchId) => {
const toSet = index > value ? index + offset : index;
newFetching.set(toSet, fetchId);
this.fetches.set(fetchId, toSet);
});
this.fetching = newFetching;
}
decrementAllIndexesGreaterThen(value: number): void {
this.offsetAllIndexesGreaterThen(value, -1);
}
}

View File

@ -20,16 +20,21 @@ interface IItemsOnScreenProvider {
get visibleRange(): IndexRange;
get meanValue(): number;
get direction(): ScrollDirection;
get speed(): number;
updateSpeed(minVisible: number, maxVisible: number): void;
update(minVisible: number, maxVisible: number): void;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
class ItemsOnScreenProvider implements IItemsOnScreenProvider {
private firstScreen = true;
private meanImagesOnScreen = 0;
private minVisible = 0;
private maxVisible = 0;
private _direction: ScrollDirection = 'UNKNOWN';
private _visibleRange: IndexRange = new IndexRange(0, 0);
private directionInternal: ScrollDirection = 'UNKNOWN';
private speedInternal = 0;
private lastUpdateTimestamp = 0;
private visibleRangeInternal: IndexRange = new IndexRange(0, 0);
private callbacks: VisibleRangeChangedCallback[] = [];
@ -38,7 +43,7 @@ class ItemsOnScreenProvider implements IItemsOnScreenProvider {
}
get visibleRange(): IndexRange {
return this._visibleRange;
return this.visibleRangeInternal;
}
get meanValue(): number {
@ -46,42 +51,64 @@ class ItemsOnScreenProvider implements IItemsOnScreenProvider {
}
get direction(): ScrollDirection {
return this._direction;
return this.directionInternal;
}
get speed(): number {
return this.speedInternal;
}
updateSpeed(minVisible: number, maxVisible: number): void {
const timeDifference = Date.now() - this.lastUpdateTimestamp;
if (timeDifference > 0) {
const speedTau = 100;
const speedWeight = 1 - Math.exp(-timeDifference / speedTau);
const distance =
minVisible + (maxVisible - minVisible) / 2 - (this.minVisible + (this.maxVisible - this.minVisible) / 2);
const rawSpeed = Math.abs(distance / timeDifference) * 1000;
this.speedInternal = speedWeight * rawSpeed + (1 - speedWeight) * this.speedInternal;
}
}
update(minVisible: number, maxVisible: number): void {
if (minVisible === this.minVisible && maxVisible === this.maxVisible) {
// Direction not changed
} else if (
Math.max(minVisible, this.minVisible) === minVisible &&
Math.max(maxVisible, this.maxVisible) === maxVisible
) {
this._direction = 'DOWN';
} else if (
Math.min(minVisible, this.minVisible) === minVisible &&
Math.min(maxVisible, this.maxVisible) === maxVisible
) {
this._direction = 'UP';
if (minVisible !== this.minVisible || maxVisible !== this.maxVisible) {
if (
Math.max(minVisible, this.minVisible) === minVisible &&
Math.max(maxVisible, this.maxVisible) === maxVisible
) {
this.directionInternal = 'DOWN';
} else if (
Math.min(minVisible, this.minVisible) === minVisible &&
Math.min(maxVisible, this.maxVisible) === maxVisible
) {
this.directionInternal = 'UP';
}
}
this.minVisible = minVisible;
this.maxVisible = maxVisible;
let imagesOnScreen = maxVisible - minVisible + 1;
let oldMeanImagesOnScreen = this.meanImagesOnScreen;
if (this.firstScreen) {
this.meanImagesOnScreen = imagesOnScreen;
this.firstScreen = false;
this.lastUpdateTimestamp = Date.now();
} else {
const weight = 0.95;
this.meanImagesOnScreen = this.meanImagesOnScreen * weight + (1 - weight) * imagesOnScreen;
imagesOnScreen = Math.ceil(this.meanImagesOnScreen);
{
const imagesWeight = 0.95;
this.meanImagesOnScreen = this.meanImagesOnScreen * imagesWeight + (1 - imagesWeight) * imagesOnScreen;
}
this.updateSpeed(minVisible, maxVisible);
}
const visibleRangeSizeChanged = this.visibleRange.length !== imagesOnScreen;
this._visibleRange = new IndexRange(minVisible, maxVisible + 1);
this.minVisible = minVisible;
this.maxVisible = maxVisible;
const visibleRangeSizeChanged = Math.ceil(oldMeanImagesOnScreen) !== Math.ceil(this.meanImagesOnScreen);
this.visibleRangeInternal = new IndexRange(minVisible, maxVisible + 1);
if (visibleRangeSizeChanged) {
this.notifyObservers();
}
this.lastUpdateTimestamp = Date.now();
}
private notifyObservers(): void {

View File

@ -0,0 +1,31 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
interface ILogger {
debug(message: string): void;
info(message: string): void;
warn(message: string): void;
}
const dummyLogger: ILogger = {
debug: () => {},
info: () => {},
warn: () => {},
};
function reportError(logger: ILogger, methodName: string, e: Error): void {
logger.warn(`Error in ${methodName}: ${e}\n${e.stack}`);
}

View File

@ -13,53 +13,60 @@
* limitations under the License.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
class PrefetchCount {
private readonly itemsOnScreen: IItemsOnScreenProvider;
private readonly MAX_SCREENS = 4;
private readonly MIN_SCREENS = 0.6;
private min = 0;
private max = 0;
private readonly MAX_SCREENS: number = 4;
private readonly speedCoef: number = 2.5;
private maxItems = 0;
private prefetchCountValueInternal = 0;
private currentMaxItemsInternal = 0;
private currentMinItemsInternal = 0;
constructor(itemsOnScreen: IItemsOnScreenProvider) {
constructor(
private readonly itemsOnScreen: IItemsOnScreenProvider,
private readonly prefetchRangeRatio: PrefetchRangeRatio,
private readonly logger: ILogger = dummyLogger,
) {
this.itemsOnScreen = itemsOnScreen;
this.itemsOnScreen.register(() => {
this.updateLimits();
});
this.prefetchRangeRatio.register(() => {
this.updateLimits();
});
}
private _prefetchCountValue = 0;
get prefetchCountValue(): number {
return this._prefetchCountValue;
return this.prefetchCountValueInternal;
}
set prefetchCountValue(v: number) {
this._prefetchCountValue = v;
Logger.log(`{"tm":${Date.now()},"prefetch_count":${v}}`);
this.prefetchCountValueInternal = v;
this.logger.debug(`{"tm":${Date.now()},"prefetch_count":${v}}`);
}
private _currentLimit = 0;
get currentLimit(): number {
return this._currentLimit;
get currentMaxItems(): number {
return this.currentMaxItemsInternal;
}
private _maxRatio = 0.5;
get maxRatio(): number {
return this._maxRatio;
}
set maxRatio(value: number) {
this._maxRatio = value;
this.updateCurrentLimit();
get currentMinItems(): number {
return this.currentMinItemsInternal;
}
getPrefetchCountByRatio(ratio: number): number {
return this.min + Math.ceil(ratio * (this.currentLimit - this.min));
this.itemsOnScreen.updateSpeed(this.itemsOnScreen.visibleRange.start, this.itemsOnScreen.visibleRange.end - 1);
const minItems = Math.min(
this.currentMaxItems,
Math.ceil(this.speedCoef * this.itemsOnScreen.speed * this.currentMaxItems),
);
const prefetchCount = minItems + Math.ceil(ratio * (this.currentMaxItems - minItems));
this.logger.debug(
`speed: ${this.itemsOnScreen.speed}, minItems: ${minItems}, ratio: ${ratio}, prefetchCount: ${prefetchCount}`,
);
return prefetchCount;
}
getRangeToPrefetch(totalCount: number): IndexRange {
getRangeToFetch(totalCount: number): IndexRange {
const visibleRange = this.itemsOnScreen.visibleRange;
let start = 0;
let end = 0;
@ -77,16 +84,22 @@ class PrefetchCount {
end = Math.min(totalCount, visibleRange.end + Math.round(this.prefetchCountValue));
break;
}
if (start > end) {
start = end;
}
return new IndexRange(start, end);
}
private updateLimits(): void {
this.min = Math.ceil(this.itemsOnScreen.meanValue * this.MIN_SCREENS);
this.max = Math.max(this.min, Math.ceil(this.MAX_SCREENS * this.itemsOnScreen.meanValue));
this.maxItems = Math.max(this.currentMinItems, Math.ceil(this.MAX_SCREENS * this.itemsOnScreen.meanValue));
this.updateCurrentLimit();
}
private updateCurrentLimit(): void {
this._currentLimit = Math.max(this.min, Math.ceil(this.max * this._maxRatio));
this.currentMaxItemsInternal = Math.max(
this.currentMinItems,
Math.ceil(this.maxItems * this.prefetchRangeRatio.maxRatio),
);
this.currentMinItemsInternal = Math.ceil(this.maxItems * this.prefetchRangeRatio.minRatio);
}
}

View File

@ -1,95 +0,0 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
interface IPrefetchRangeEvaluator {
get rangeToPrefetch(): IndexRange;
setTotal(totalItems: number): void;
visibleRangeChanged(minVisible: number, maxVisible: number): void;
itemPrefetched(index: number, fetchDuration: number): void;
}
class PrefetchRangeEvaluator implements IPrefetchRangeEvaluator {
private readonly itemsOnScreen = new ItemsOnScreenProvider();
private readonly prefetchCount = new PrefetchCount(this.itemsOnScreen);
private readonly fetchedRegistry = new FetchedRegistry();
private readonly prefetchRangeRatio = new PrefetchRangeRatio(this.itemsOnScreen, this.fetchedRegistry);
private totalItems = 0;
private rangeToPrefetch_ = new IndexRange(0, 0);
get rangeToPrefetch(): IndexRange {
return this.rangeToPrefetch_;
}
setTotal(totalItems: number): void {
this.totalItems = totalItems;
}
visibleRangeChanged(minVisible: number, maxVisible: number): void {
this.itemsOnScreen.update(minVisible, maxVisible);
Logger.log(
`visibleAreaChanged itemsOnScreen=${
Math.abs(maxVisible - minVisible) + 1
}, meanImagesOnScreen=${this.itemsOnScreen.meanValue}, prefetchCountCurrentLimit=${this.prefetchCount.currentLimit}, prefetchCountMaxRatio=${this.prefetchCount.maxRatio}`,
);
// +1 for inclusive maxVisible -> exclusive end
this.prefetchCount.prefetchCountValue = this.evaluatePrefetchCount('visible-area-changed');
this.rangeToPrefetch_ = this.prefetchCount.getRangeToPrefetch(this.totalItems);
this.fetchedRegistry.updateRangeToPrefetch(this.rangeToPrefetch_);
}
itemPrefetched(index: number, fetchDuration: number): void {
let maxRatioChanged = false;
if (this.prefetchRangeRatio.update(fetchDuration) === 'ratio-changed') {
this.prefetchCount.maxRatio = this.prefetchRangeRatio.maxRatio;
maxRatioChanged = true;
Logger.log(
`choosePrefetchCountLimit prefetchCountMaxRatio=${this.prefetchCount.maxRatio}, prefetchCountCurrentLimit=${this.prefetchCount.currentLimit}`,
);
}
this.fetchedRegistry.addFetched(index);
this.prefetchCount.prefetchCountValue = this.evaluatePrefetchCount('resolved', maxRatioChanged);
this.rangeToPrefetch_ = this.prefetchCount.getRangeToPrefetch(this.totalItems);
this.fetchedRegistry.updateRangeToPrefetch(this.rangeToPrefetch_);
}
private evaluatePrefetchCount(event: 'resolved' | 'visible-area-changed', maxRatioChanged?: boolean): number {
const ratio = this.prefetchRangeRatio.calculateRatio(this.prefetchCount.prefetchCountValue, this.totalItems);
let evaluatedPrefetchCount = this.prefetchCount.getPrefetchCountByRatio(ratio);
let nextRatio: number | undefined;
if (maxRatioChanged) {
nextRatio = this.prefetchRangeRatio.calculateRatio(evaluatedPrefetchCount, this.totalItems);
evaluatedPrefetchCount = this.prefetchCount.getPrefetchCountByRatio(nextRatio);
}
if (
this.prefetchRangeRatio.range.contains(ratio) ||
(event === 'resolved' && evaluatedPrefetchCount <= this.prefetchCount.prefetchCountValue)
) {
return this.prefetchCount.prefetchCountValue;
}
this.prefetchRangeRatio.updateRatioRange(ratio);
Logger.log(
`evaluatePrefetchCount prefetchCount=${evaluatedPrefetchCount}, ratio=${ratio}, nextRatio=${nextRatio}, hysteresisRange=${this.prefetchRangeRatio.range}`,
);
return evaluatedPrefetchCount;
}
}

View File

@ -16,64 +16,115 @@
interface ToleranceRange {
leftToleranceEdge: number;
rightToleranceEdge: number;
prefetchCountMinRatioLeft: number;
prefetchCountMaxRatioLeft: number;
prefetchCountMinRatioRight: number;
prefetchCountMaxRatioRight: number;
}
type PrefetchCountMaxChangedCallback = () => void;
type UpdateResult = 'ratio-changed' | 'ratio-not-changed';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
class PrefetchRangeRatio {
private readonly itemsOnScreen: ItemsOnScreenProvider;
private readonly fetchedRegistry: FetchedRegistry;
private readonly TOLERANCE_RANGES: [ToleranceRange, ToleranceRange] = [
private readonly TOLERANCE_RANGES: ToleranceRange[] = [
{
leftToleranceEdge: 180,
rightToleranceEdge: 250,
leftToleranceEdge: 140,
rightToleranceEdge: 290,
prefetchCountMinRatioLeft: 0.5,
prefetchCountMaxRatioLeft: 0.5,
prefetchCountMinRatioRight: 0.25,
prefetchCountMaxRatioRight: 1,
},
{
leftToleranceEdge: 3000,
rightToleranceEdge: 4000,
prefetchCountMinRatioLeft: 0.25,
prefetchCountMaxRatioLeft: 1,
prefetchCountMinRatioRight: 0.25,
prefetchCountMaxRatioRight: 0.25,
},
];
private readonly ACTIVE_DEGREE = 0.5;
private readonly VISIBLE_DEGREE = 2.5;
private readonly ACTIVE_DEGREE: number = 0;
private readonly VISIBLE_DEGREE: number = 2.5;
private meanPrefetchTime = 0;
private leftToleranceEdge = Number.MIN_VALUE;
private rightToleranceEdge = 250;
private oldRatio = 0;
constructor(itemsOnScreen: ItemsOnScreenProvider, fetchedRegistry: FetchedRegistry) {
this.itemsOnScreen = itemsOnScreen;
this.fetchedRegistry = fetchedRegistry;
constructor(
private readonly itemsOnScreen: ItemsOnScreenProvider,
private readonly fetchedRegistry: FetchedRegistry,
private readonly fetchingRegistry: FetchingRegistry,
private readonly logger: ILogger = dummyLogger,
) {}
private callbacks: PrefetchCountMaxChangedCallback[] = [];
register(callback: PrefetchCountMaxChangedCallback): void {
this.callbacks.push(callback);
}
private _range = RatioRange.newEmpty();
private rangeInternal = RatioRange.newEmpty();
get range(): RatioRange {
return this._range;
return this.rangeInternal;
}
private _maxRatio = 0.5;
setEmptyRange(): void {
this.rangeInternal = RatioRange.newEmpty();
}
private minRatioInternal = 0.25 * 0.6;
private maxRatioInternal = 0.5;
get maxRatio(): number {
return this._maxRatio;
return this.maxRatioInternal;
}
private updateTiming(prefetchDuration: number): void {
// Check if not from file storage
if (prefetchDuration > 20) {
const weight = 0.95;
get minRatio(): number {
return this.minRatioInternal;
}
private hysteresisEnabledInternal = false;
get hysteresisEnabled(): boolean {
return this.hysteresisEnabledInternal;
}
set hysteresisEnabled(value: boolean) {
this.hysteresisEnabledInternal = value;
}
private oldRatioInternal = 0;
set oldRatio(ratio: number) {
this.oldRatioInternal = ratio;
}
get oldRatio(): number {
return this.oldRatioInternal;
}
private updateTiming(index: number, prefetchDuration: number): void {
const weight = 0.95;
const localPrefetchDuration = 20;
let isFetchLocal = prefetchDuration < localPrefetchDuration;
let isFetchLatecomer = this.fetchingRegistry.isFetchLatecomer(index, this.itemsOnScreen.meanValue);
if (!isFetchLocal && !isFetchLatecomer) {
this.meanPrefetchTime = this.meanPrefetchTime * weight + (1 - weight) * prefetchDuration;
}
Logger.log(`prefetchDifference prefetchDur=${prefetchDuration}, meanPrefetchDur=${this.meanPrefetchTime}`);
this.logger.debug(
`prefetchDifference prefetchDur=${prefetchDuration}, meanPrefetchDur=${this.meanPrefetchTime}, ` +
`isFetchLocal=${isFetchLocal}, isFetchLatecomer=${isFetchLatecomer}`,
);
}
update(prefetchDuration: number): UpdateResult {
this.updateTiming(prefetchDuration);
update(index: number, prefetchDuration: number): UpdateResult {
this.updateTiming(index, prefetchDuration);
if (this.meanPrefetchTime >= this.leftToleranceEdge && this.meanPrefetchTime <= this.rightToleranceEdge) {
return 'ratio-not-changed';
@ -82,60 +133,112 @@ class PrefetchRangeRatio {
let ratioChanged = false;
if (this.meanPrefetchTime > this.rightToleranceEdge) {
for (let i = 0; i < this.TOLERANCE_RANGES.length; i++) {
const limit = this.TOLERANCE_RANGES[i];
if (this.meanPrefetchTime > limit.rightToleranceEdge) {
ratioChanged = true;
this._maxRatio = limit.prefetchCountMaxRatioRight;
this.leftToleranceEdge = limit.leftToleranceEdge;
if (i + 1 !== this.TOLERANCE_RANGES.length) {
this.rightToleranceEdge = this.TOLERANCE_RANGES[i + 1].rightToleranceEdge;
} else {
this.rightToleranceEdge = Number.MAX_VALUE;
}
}
}
ratioChanged = this.updateOnGreaterThanRight();
} else if (this.meanPrefetchTime < this.leftToleranceEdge) {
for (let i = this.TOLERANCE_RANGES.length - 1; i >= 0; i--) {
const limit = this.TOLERANCE_RANGES[i];
if (this.meanPrefetchTime < limit.leftToleranceEdge) {
ratioChanged = true;
this._maxRatio = limit.prefetchCountMaxRatioLeft;
this.rightToleranceEdge = limit.rightToleranceEdge;
if (i !== 0) {
this.leftToleranceEdge = this.TOLERANCE_RANGES[i - 1].leftToleranceEdge;
} else {
this.leftToleranceEdge = Number.MIN_VALUE;
}
ratioChanged = this.updateOnLessThanLeft();
}
if (ratioChanged) {
this.notifyObservers();
}
return ratioChanged ? 'ratio-changed' : 'ratio-not-changed';
}
private updateOnLessThanLeft(): boolean {
let ratioChanged = false;
for (let i = this.TOLERANCE_RANGES.length - 1; i >= 0; i--) {
const limit = this.TOLERANCE_RANGES[i];
if (this.meanPrefetchTime < limit.leftToleranceEdge) {
ratioChanged = true;
this.maxRatioInternal = limit.prefetchCountMaxRatioLeft;
this.minRatioInternal = limit.prefetchCountMinRatioLeft;
this.rightToleranceEdge = limit.rightToleranceEdge;
if (i !== 0) {
this.leftToleranceEdge = this.TOLERANCE_RANGES[i - 1].leftToleranceEdge;
} else {
this.leftToleranceEdge = Number.MIN_VALUE;
}
}
}
return ratioChanged ? 'ratio-changed' : 'ratio-not-changed';
return ratioChanged;
}
private updateOnGreaterThanRight(): boolean {
let ratioChanged = false;
for (let i = 0; i < this.TOLERANCE_RANGES.length; i++) {
const limit = this.TOLERANCE_RANGES[i];
if (this.meanPrefetchTime > limit.rightToleranceEdge) {
ratioChanged = true;
this.maxRatioInternal = limit.prefetchCountMaxRatioRight;
this.minRatioInternal = limit.prefetchCountMinRatioRight;
this.leftToleranceEdge = limit.leftToleranceEdge;
if (i + 1 !== this.TOLERANCE_RANGES.length) {
this.rightToleranceEdge = this.TOLERANCE_RANGES[i + 1].rightToleranceEdge;
} else {
this.rightToleranceEdge = Number.MAX_VALUE;
}
}
}
return ratioChanged;
}
calculateRatio(prefetchCount: number, totalCount: number): number {
const visibleRange = this.itemsOnScreen.visibleRange;
const start = Math.max(0, visibleRange.start - prefetchCount);
const end = Math.min(totalCount, visibleRange.end + prefetchCount);
const evaluatedPrefetchRange = new IndexRange(start, end);
let start: number = 0;
let end: number = 0;
switch (this.itemsOnScreen.direction) {
case 'UNKNOWN':
start = Math.max(0, visibleRange.start - prefetchCount);
end = Math.min(totalCount, visibleRange.end + prefetchCount);
break;
case 'UP':
start = Math.max(0, visibleRange.start - prefetchCount);
end = Math.min(totalCount, visibleRange.end + Math.round(0.5 * prefetchCount));
break;
case 'DOWN':
start = Math.max(0, visibleRange.start - Math.round(0.5 * prefetchCount));
end = Math.min(totalCount, visibleRange.end + prefetchCount);
break;
}
const evaluatedPrefetchRange = new IndexRange(start, end);
const completedActive = this.fetchedRegistry.getFetchedInRange(evaluatedPrefetchRange);
const completedVisible = this.fetchedRegistry.getFetchedInRange(visibleRange);
return Math.min(
1,
Math.pow(completedActive / evaluatedPrefetchRange.length, this.ACTIVE_DEGREE) *
Math.pow(completedVisible / visibleRange.length, this.VISIBLE_DEGREE),
if (evaluatedPrefetchRange.length === 0 || visibleRange.length === 0) {
return 0;
}
this.logger.debug(`active_degree=${this.ACTIVE_DEGREE}, visible_degree=${this.VISIBLE_DEGREE}`);
this.logger.debug(
`evaluatedPrefetchRange=${evaluatedPrefetchRange}, visibleRange=${visibleRange}, active_ratio=${Math.pow(completedActive / evaluatedPrefetchRange.length, this.ACTIVE_DEGREE)}, visible_ratio=${Math.pow(completedVisible / visibleRange.length, this.VISIBLE_DEGREE)}, completedActive=${completedActive}, evaluatedPrefetchRange.length=${evaluatedPrefetchRange.length}, visibleRange.length=${visibleRange.length}`,
);
const ratio =
Math.pow(completedActive / evaluatedPrefetchRange.length, this.ACTIVE_DEGREE) *
Math.pow(completedVisible / visibleRange.length, this.VISIBLE_DEGREE);
this.logger.debug(
`calculateRatio ratio=${ratio}, completedActive=${completedActive}, evaluatedPrefetchRange.length=${evaluatedPrefetchRange.length}, ` +
`completedVisible=${completedVisible}, visibleRange.length=${visibleRange.length}`,
);
return Math.min(1, ratio);
}
updateRatioRange(ratio: number): void {
if (ratio > this.oldRatio) {
this._range = new RatioRange(new RangeEdge(this.oldRatio, false), new RangeEdge(ratio, true));
if (ratio > this.oldRatioInternal) {
this.rangeInternal = new RatioRange(new RangeEdge(this.oldRatioInternal, false), new RangeEdge(ratio, true));
} else {
this._range = new RatioRange(new RangeEdge(ratio, true), new RangeEdge(this.oldRatio, false));
this.rangeInternal = new RatioRange(new RangeEdge(ratio, true), new RangeEdge(this.oldRatioInternal, false));
}
this.oldRatio = ratio;
this.oldRatioInternal = ratio;
}
private notifyObservers(): void {
this.callbacks.forEach((callback) => callback());
}
}

View File

@ -1,87 +0,0 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
interface ITimeProvider {
getCurrent: () => number;
}
class DefaultTimeProvider implements ITimeProvider {
getCurrent(): number {
return Date.now();
}
}
class Prefetcher implements IPrefetcher {
private datasource: IDataSourcePrefetching | null = null;
private readonly prefetchRangeEvaluator: IPrefetchRangeEvaluator;
private readonly timeProvider: ITimeProvider;
constructor(prefetchRangeEvaluator: IPrefetchRangeEvaluator, timeProvider: ITimeProvider) {
this.prefetchRangeEvaluator = prefetchRangeEvaluator;
this.timeProvider = timeProvider;
}
setDataSource(ds: IDataSourcePrefetching): void {
this.datasource = ds;
this.prefetchRangeEvaluator.setTotal(ds.totalCount());
}
visibleAreaChanged(minVisible: number, maxVisible: number): void {
if (!this.datasource) {
throw new Error('No data source');
}
const oldRangeToPrefetch = this.prefetchRangeEvaluator.rangeToPrefetch;
this.prefetchRangeEvaluator.visibleRangeChanged(minVisible, maxVisible);
this.prefetchDifference(oldRangeToPrefetch);
this.cancelDifference(oldRangeToPrefetch);
}
private prefetchDifference(oldRange: IndexRange): void {
this.prefetchRangeEvaluator.rangeToPrefetch.subtract(oldRange).forEachIndex((index) => {
const prefetchStart = this.timeProvider.getCurrent();
const prefetchedCallback = (): void => {
if (!this.prefetchRangeEvaluator.rangeToPrefetch.contains(index)) {
return;
}
const oldRangeToPrefetch = this.prefetchRangeEvaluator.rangeToPrefetch;
const prefetchDuration = this.timeProvider.getCurrent() - prefetchStart;
this.prefetchRangeEvaluator.itemPrefetched(index, prefetchDuration);
this.prefetchDifference(oldRangeToPrefetch);
};
const prefetchResponse = this.datasource!.prefetch(index);
if (prefetchResponse instanceof Promise) {
prefetchResponse.then(prefetchedCallback);
} else {
prefetchedCallback();
}
});
}
private cancelDifference(oldRangeToPrefetch: IndexRange): void {
if (!this.datasource!.cancel || this.prefetchRangeEvaluator.rangeToPrefetch.length > oldRangeToPrefetch.length) {
return;
}
oldRangeToPrefetch.subtract(this.prefetchRangeEvaluator.rangeToPrefetch).forEachIndex((index) => {
this.datasource!.cancel!(index);
});
}
}

View File

@ -13,6 +13,8 @@
* limitations under the License.
*/
class Logger {
static log(message: string): void {}
// istanbul ignore next
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function assertNever(_: never): void {
throw _ + 'assertNever';
}

View File

@ -29,11 +29,19 @@ class IndexRange {
return this.end - this.start;
}
toSet(target?: Set<number>): Set<number> {
const set = target ?? new Set<number>();
for (let i = this.start; i < this.end; ++i) {
set.add(i);
}
return set;
}
contains(value: IndexRange | number): boolean {
if (typeof value === 'object') {
return this.start <= (value as IndexRange).start && (value as IndexRange).end <= this.end;
return this.start <= value.start && value.end <= this.end;
} else {
return this.start <= (value as number) && (value as number) < this.end;
return this.start <= value && value < this.end;
}
}
@ -60,8 +68,12 @@ class IndexRange {
}
}
format(): string {
return `[${this.start}..${this.end})`;
equals(other: IndexRange): boolean {
return this.start === other.start && this.end === other.end;
}
toString(): string {
return `[${this.start}, ${this.end})`;
}
}
@ -71,4 +83,12 @@ class IndexRangeArray extends Array<IndexRange> {
range.forEachIndex(callback);
});
}
toSet(): Set<number> {
const set = new Set<number>();
this.forEach((range) => {
range.toSet(set);
});
return set;
}
}

View File

@ -22,6 +22,7 @@ class RangeEdge {
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
class RatioRange {
readonly start: RangeEdge;
readonly end: RangeEdge;

View File

@ -13,4 +13,5 @@
* limitations under the License.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
type ScrollDirection = 'UP' | 'DOWN' | 'UNKNOWN';

View File

@ -6,16 +6,19 @@
"src/BasicPrefetcher.ts",
"src/IDataSourcePrefetching.ts",
"src/IPrefetcher.ts",
"src/prefetcher/DataSourceObserver.ts",
"src/prefetcher/FetchingRegistry.ts",
"src/prefetcher/FetchedRegistry.ts",
"src/prefetcher/ItemsOnScreenProvider.ts",
"src/prefetcher/PrefetchCount.ts",
"src/prefetcher/PrefetchRangeEvaluator.ts",
"src/prefetcher/FetchingRangeEvaluator.ts",
"src/prefetcher/PrefetchRangeRatio.ts",
"src/prefetcher/Prefetcher.ts",
"src/prefetcher/logger.ts",
"src/prefetcher/FetchingDriver.ts",
"src/prefetcher/Logger.ts",
"src/utils/IndexRange.ts",
"src/utils/RatioRange.ts",
"src/utils/ScrollDirection.ts"
"src/utils/ScrollDirection.ts",
"src/utils/AssertNever.ts",
],
"compilerOptions": {
"module": "None",

View File

@ -963,7 +963,7 @@ class BorderModifier extends ModifierWithKey {
this.value.arkWidth.end, this.value.arkColor.startColor, this.value.arkColor.endColor,
this.value.arkRadius.topStart, this.value.arkRadius.topEnd, this.value.arkRadius.bottomStart,
this.value.arkRadius.bottomEnd, isLocalizedBorderWidth, isLocalizedBorderColor, isLocalizedBorderRadius,
this.value.arkDashGap.start, this.value.arkDashGap.end,this.value.arkDashWidth.start, this.value.arkDashWidth.end);
this.value.arkDashGap.start, this.value.arkDashGap.end, this.value.arkDashWidth.start, this.value.arkDashWidth.end);
}
}
checkObjectDiff() {

View File

@ -715,6 +715,8 @@ var FormDimension;
FormDimension["Dimension_2_1"] = 5;
FormDimension["DIMENSION_1_1"] = 6;
FormDimension["DIMENSION_6_4"] = 7;
FormDimension["DIMENSION_2_3"] = 8;
FormDimension["DIMENSION_3_3"] = 9;
})(FormDimension || (FormDimension = {}));
let FormShape;

File diff suppressed because it is too large Load Diff

View File

@ -816,38 +816,29 @@ class FocusController {
this.ohos_focusController = globalThis.requireNapi('arkui.focusController');
}
clearFocus() {
__JSScopeUtil__.syncInstanceId(this.instanceId_);
if (!this.ohos_focusController) {
DumpLog.print(0, `\nohos_focusController is not initialized.\n`);
if (this.ohos_focusController === null || this.ohos_focusController === undefined) {
return;
}
__JSScopeUtil__.syncInstanceId(this.instanceId_);
this.ohos_focusController.clearFocus();
__JSScopeUtil__.restoreInstanceId();
}
requestFocus(value) {
__JSScopeUtil__.syncInstanceId(this.instanceId_);
if (!this.ohos_focusController) {
DumpLog.print(0, `\nohos_focusController is not initialized.\n`);
return;
if (this.ohos_focusController === null || this.ohos_focusController === undefined) {
return false;
}
__JSScopeUtil__.syncInstanceId(this.instanceId_);
let result = this.ohos_focusController.requestFocus(value);
__JSScopeUtil__.restoreInstanceId();
return result;
}
activate(isActive, autoInactive) {
__JSScopeUtil__.syncInstanceId(this.instanceId_);
if (!this.ohos_focusController) {
DumpLog.print(0, `\nohos_focusController is not initialized.\n`);
return;
if (this.ohos_focusController === null || this.ohos_focusController === undefined) {
return false;
}
__JSScopeUtil__.syncInstanceId(this.instanceId_);
if (arguments.length === 2) {
let result = this.ohos_focusController.activate(isActive, autoInactive);
__JSScopeUtil__.restoreInstanceId();

View File

@ -817,6 +817,13 @@ class FrameNode {
getNodePtr() {
return this.nodePtr_;
}
getValidNodePtr() {
if (this._nativeRef) {
return this.nodePtr_;
} else {
throw Error('The FrameNode has been disposed!');
}
}
dispose() {
this.renderNode_?.dispose();
FrameNodeFinalizationRegisterProxy.ElementIdToOwningFrameNode_.delete(this._nodeId);
@ -1035,39 +1042,39 @@ class FrameNode {
return getUINativeModule().frameNode.getChildrenCount(this.nodePtr_, isExpanded);
}
getPositionToParent() {
const position = getUINativeModule().frameNode.getPositionToParent(this.getNodePtr());
const position = getUINativeModule().frameNode.getPositionToParent(this.getValidNodePtr());
return { x: position[0], y: position[1] };
}
getPositionToScreen() {
const position = getUINativeModule().frameNode.getPositionToScreen(this.getNodePtr());
const position = getUINativeModule().frameNode.getPositionToScreen(this.getValidNodePtr());
return { x: position[0], y: position[1] };
}
getPositionToWindow() {
const position = getUINativeModule().frameNode.getPositionToWindow(this.getNodePtr());
const position = getUINativeModule().frameNode.getPositionToWindow(this.getValidNodePtr());
return { x: position[0], y: position[1] };
}
getPositionToParentWithTransform() {
const position = getUINativeModule().frameNode.getPositionToParentWithTransform(this.getNodePtr());
const position = getUINativeModule().frameNode.getPositionToParentWithTransform(this.getValidNodePtr());
return { x: position[0], y: position[1] };
}
getPositionToScreenWithTransform() {
const position = getUINativeModule().frameNode.getPositionToScreenWithTransform(this.getNodePtr());
const position = getUINativeModule().frameNode.getPositionToScreenWithTransform(this.getValidNodePtr());
return { x: position[0], y: position[1] };
}
getPositionToWindowWithTransform() {
const position = getUINativeModule().frameNode.getPositionToWindowWithTransform(this.getNodePtr());
const position = getUINativeModule().frameNode.getPositionToWindowWithTransform(this.getValidNodePtr());
return { x: position[0], y: position[1] };
}
getMeasuredSize() {
const size = getUINativeModule().frameNode.getMeasuredSize(this.getNodePtr());
const size = getUINativeModule().frameNode.getMeasuredSize(this.getValidNodePtr());
return { width: size[0], height: size[1] };
}
getLayoutPosition() {
const position = getUINativeModule().frameNode.getLayoutPosition(this.getNodePtr());
const position = getUINativeModule().frameNode.getLayoutPosition(this.getValidNodePtr());
return { x: position[0], y: position[1] };
}
getUserConfigBorderWidth() {
const borderWidth = getUINativeModule().frameNode.getConfigBorderWidth(this.getNodePtr());
const borderWidth = getUINativeModule().frameNode.getConfigBorderWidth(this.getValidNodePtr());
return {
top: new LengthMetrics(borderWidth[0], borderWidth[1]),
right: new LengthMetrics(borderWidth[2], borderWidth[3]),
@ -1076,7 +1083,7 @@ class FrameNode {
};
}
getUserConfigPadding() {
const borderWidth = getUINativeModule().frameNode.getConfigPadding(this.getNodePtr());
const borderWidth = getUINativeModule().frameNode.getConfigPadding(this.getValidNodePtr());
return {
top: new LengthMetrics(borderWidth[0], borderWidth[1]),
right: new LengthMetrics(borderWidth[2], borderWidth[3]),
@ -1085,7 +1092,7 @@ class FrameNode {
};
}
getUserConfigMargin() {
const margin = getUINativeModule().frameNode.getConfigMargin(this.getNodePtr());
const margin = getUINativeModule().frameNode.getConfigMargin(this.getValidNodePtr());
return {
top: new LengthMetrics(margin[0], margin[1]),
right: new LengthMetrics(margin[2], margin[3]),
@ -1094,7 +1101,7 @@ class FrameNode {
};
}
getUserConfigSize() {
const size = getUINativeModule().frameNode.getConfigSize(this.getNodePtr());
const size = getUINativeModule().frameNode.getConfigSize(this.getValidNodePtr());
return {
width: new LengthMetrics(size[0], size[1]),
height: new LengthMetrics(size[2], size[3])

View File

@ -698,7 +698,7 @@ ArkUINativeModuleValue ImageBridge::SetColorFilter(ArkUIRuntimeCallInfo* runtime
return panda::JSValueRef::Undefined(vm);
}
auto array = panda::CopyableGlobal<panda::ArrayRef>(vm, jsObjArg);
if (array->Length(vm) != COLOR_FILTER_MATRIX_SIZE) {
if (array.IsEmpty() || array->IsUndefined() || array->IsNull() || array->Length(vm) != COLOR_FILTER_MATRIX_SIZE) {
GetArkUINodeModifiers()->getImageModifier()->setColorFilter(
nativeNode, &(*DEFAULT_COLOR_FILTER_MATRIX.begin()), COLOR_FILTER_MATRIX_SIZE);
return panda::JSValueRef::Undefined(vm);

View File

@ -400,6 +400,7 @@ void ParseCapsuleFontWeight(
{
Local<JSValueRef> weightArg = runtimeCallInfo->GetCallArgRef(index);
auto pipelineContext = PipelineContext::GetCurrentContext();
CHECK_NULL_VOID(pipelineContext);
auto theme = pipelineContext->GetTheme<TextTheme>();
std::string weight;
@ -420,6 +421,7 @@ void ParseCapsuleFontStyle(
{
Local<JSValueRef> styleArg = runtimeCallInfo->GetCallArgRef(index);
auto pipelineContext = PipelineContext::GetCurrentContext();
CHECK_NULL_VOID(pipelineContext);
auto theme = pipelineContext->GetTheme<TextTheme>();
uint8_t style = static_cast<uint8_t>(theme->GetTextStyle().GetFontStyle());
@ -438,6 +440,7 @@ void ParseCapsuleFontFamily(
{
Local<JSValueRef> familyArg = runtimeCallInfo->GetCallArgRef(index);
auto pipelineContext = PipelineContext::GetCurrentContext();
CHECK_NULL_VOID(pipelineContext);
auto theme = pipelineContext->GetTheme<TextTheme>();
std::vector<std::string> fontFamilies;
@ -508,6 +511,7 @@ ArkUINativeModuleValue ProgressBridge::SetProgressStyle(ArkUIRuntimeCallInfo* ru
Local<JSValueRef> firstArg = runtimeCallInfo->GetCallArgRef(ARG_NUM_NATIVE_NODE);
auto nativeNode = nodePtr(firstArg->ToNativePointer(vm)->Value());
auto pipelineContext = PipelineContext::GetCurrentContext();
CHECK_NULL_RETURN(pipelineContext, panda::JSValueRef::Undefined(vm));
auto theme = pipelineContext->GetTheme<TextTheme>();
auto fontFamilies = theme->GetTextStyle().GetFontFamilies();

View File

@ -201,6 +201,14 @@ void RenderNodeBridge::FireDrawCallback(EcmaVM* vm, JsWeak<panda::CopyableGlobal
panda::NumberRef::New(vm, static_cast<double>(PipelineBase::Px2VpWithCurrentDensity(context.width)))
};
auto sizeObj = panda::ObjectRef::NewWithNamedProperties(vm, ArraySize(keysOfSize), keysOfSize, valuesOfSize);
Local<JSValueRef> valuesOfSizeInPixel[] = {
panda::NumberRef::New(vm, static_cast<double>(context.height)),
panda::NumberRef::New(vm, static_cast<double>(context.width))
};
auto sizeInPixelObj =
panda::ObjectRef::NewWithNamedProperties(vm, ArraySize(keysOfSize), keysOfSize, valuesOfSizeInPixel);
auto jsCanvas = OHOS::Rosen::Drawing::JsCanvas::CreateJsCanvas(env, &context.canvas);
OHOS::Rosen::Drawing::JsCanvas* unwrapCanvas = nullptr;
napi_unwrap(env, jsCanvas, reinterpret_cast<void**>(&unwrapCanvas));
@ -210,8 +218,8 @@ void RenderNodeBridge::FireDrawCallback(EcmaVM* vm, JsWeak<panda::CopyableGlobal
}
auto jsCanvasVal = NapiValueToLocalValue(jsCanvas);
Local<JSValueRef> values[] = { sizeObj, jsCanvasVal };
const char* keys[] = { "size", "canvas" };
Local<JSValueRef> values[] = { sizeObj, sizeInPixelObj, jsCanvasVal };
const char* keys[] = { "size", "sizeInPixel", "canvas" };
auto contextObj = panda::ObjectRef::NewWithNamedProperties(vm, ArraySize(keys), keys, values);
contextObj->SetNativePointerFieldCount(vm, 1);
JSValueWrapper valueWrapper = contextObj;

View File

@ -4577,7 +4577,7 @@ class PUV2ViewBase extends NativeViewPartialUpdate {
return '-r'.match(param) != null || param.startsWith('-viewId=');
};
let dfxCommands = [];
for (var i = 0; i < commands.length; i++) {
for (let i = 0; i < commands.length; i++) {
let command = commands[i];
if (isFlag(command)) {
if (command.startsWith('-viewId=')) {
@ -9565,6 +9565,10 @@ class ViewV2 extends PUV2ViewBase {
debugInfoStateVars() {
let retVal = `|--${this.constructor.name}[${this.id__()}]\n`;
let meta = this[ObserveV2.V2_DECO_META];
if (!meta) {
retVal += ' No State Variables';
return retVal;
}
Object.getOwnPropertyNames(meta)
.filter((varName) => !varName.startsWith('___pc_alias__@')) // remove provider & consumer prefix
.forEach((varName) => {
@ -10814,9 +10818,9 @@ class __RepeatVirtualScrollImpl {
}
reRender() {
this.purgeKeyCache();
// When this.totalCount_ == 0 need render to clear visible items
if (this.hasVisibleItemsChanged() || this.totalCount_ === 0) {
this.purgeKeyCache();
RepeatVirtualScrollNative.updateRenderState(this.totalCount_, true);
}

View File

@ -157,7 +157,6 @@ void JSRefresh::Create(const JSCallbackInfo& info)
auto friction = paramObject->GetProperty("friction");
auto promptText = paramObject->GetProperty("promptText");
RefreshModel::GetInstance()->Create();
RefreshModel::GetInstance()->SetProgressColor(theme->GetProgressColor());
if (refreshing->IsBoolean()) {
RefreshModel::GetInstance()->SetRefreshing(refreshing->ToBoolean());

View File

@ -254,7 +254,7 @@ void JSSearch::SetSelectedBackgroundColor(const JSCallbackInfo& info)
}
Color selectedColor;
if (!ParseJsColor(info[0], selectedColor)) {
auto pipeline = PipelineBase::GetCurrentContext();
auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
CHECK_NULL_VOID(pipeline);
auto theme = pipeline->GetThemeManager()->GetTheme<TextFieldTheme>();
CHECK_NULL_VOID(theme);
@ -898,12 +898,11 @@ void JSSearch::JsBorderRadius(const JSCallbackInfo& info)
void JSSearch::CreateJsSearchCommonEvent(const JSCallbackInfo &info)
{
if (info.Length() < 1 || !info[0]->IsObject()) {
if (info.Length() < 1 || !info[0]->IsFunction()) {
return;
}
auto jsValue = info[0];
auto jsTextFunc = AceType::MakeRefPtr<JsCommonEventFunction<NG::TextFieldCommonEvent, 2>>(
JSRef<JSFunc>::Cast(jsValue));
JSRef<JSFunc>::Cast(info[0]));
WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
auto callback = [execCtx = info.GetExecutionContext(), func = std::move(jsTextFunc), node = targetNode](
const std::string& value, NG::TextFieldCommonEvent& event) {
@ -1255,7 +1254,7 @@ void JSSearch::SetDecoration(const JSCallbackInfo& info)
JSRef<JSVal> colorValue = obj->GetProperty("color");
JSRef<JSVal> styleValue = obj->GetProperty("style");
auto pipelineContext = PipelineBase::GetCurrentContext();
auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
CHECK_NULL_VOID(pipelineContext);
auto theme = pipelineContext->GetTheme<SearchTheme>();
CHECK_NULL_VOID(theme);
@ -1300,7 +1299,7 @@ void JSSearch::SetMaxFontSize(const JSCallbackInfo& info)
if (info.Length() < 1) {
return;
}
auto pipelineContext = PipelineBase::GetCurrentContext();
auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
CHECK_NULL_VOID(pipelineContext);
auto theme = pipelineContext->GetTheme<SearchTheme>();
CHECK_NULL_VOID(theme);

View File

@ -35,7 +35,7 @@ const auto INDEX_ZERO = 0;
const auto INDEX_ONE = 1;
const auto INDEX_TWO = 2;
const auto SHOW_TAB_BAR_DELAY = 2000;
const auto SCROLL_RANGE = 36;
const auto SCROLL_RATIO = 2.0f;
BindInfoMap bindInfoMap_;
@ -57,7 +57,7 @@ void HandleOnTouchEvent(WeakPtr<JSScroller> jsScrollerWeak, const TouchEventInfo
if (touchType == TouchType::DOWN) {
scrollInfo.isTouching = true;
if (!scrollInfo.isAtTop && !scrollInfo.isAtBottom && tabsController) {
tabsController->StopShowTabBar();
tabsController->CancelShowTabBar();
}
} else if (touchType == TouchType::UP || touchType == TouchType::CANCEL) {
scrollInfo.isTouching = false;
@ -68,25 +68,6 @@ void HandleOnTouchEvent(WeakPtr<JSScroller> jsScrollerWeak, const TouchEventInfo
}
}
void HandleOnPanActionEndEvent(WeakPtr<JSScroller> jsScrollerWeak, const GestureEvent& info)
{
auto velocity = info.GetMainVelocity();
if (!NearZero(velocity)) {
return;
}
for (auto& bindInfo : bindInfoMap_) {
auto& scrollInfoMap = bindInfo.second;
if (scrollInfoMap.find(jsScrollerWeak) == scrollInfoMap.end()) {
continue;
}
auto tabsController = bindInfo.first.Upgrade();
if (tabsController) {
tabsController->StartShowTabBar(SHOW_TAB_BAR_DELAY);
}
}
}
void HandleOnReachEvent(WeakPtr<JSScroller> jsScrollerWeak, bool isTopEvent)
{
for (auto& bindInfo : bindInfoMap_) {
@ -116,7 +97,7 @@ void HandleOnScrollStartEvent(WeakPtr<JSScroller> jsScrollerWeak)
scrollInfo.isScrolling = true;
auto tabsController = bindInfo.first.Upgrade();
if (!scrollInfo.isAtTop && !scrollInfo.isAtBottom && !scrollInfo.isTouching && tabsController) {
tabsController->StopShowTabBar();
tabsController->CancelShowTabBar();
}
}
}
@ -140,7 +121,7 @@ void HandleOnScrollStopEvent(WeakPtr<JSScroller> jsScrollerWeak)
}
void HandleOnDidScrollEvent(
WeakPtr<JSScroller> jsScrollerWeak, Dimension dimension, ScrollState state, bool isAtTop, bool isAtBottom)
WeakPtr<JSScroller> jsScrollerWeak, Dimension dimension, ScrollSource source, bool isAtTop, bool isAtBottom)
{
for (auto& bindInfo : bindInfoMap_) {
auto& scrollInfoMap = bindInfo.second;
@ -154,9 +135,10 @@ void HandleOnDidScrollEvent(
}
auto tabsController = bindInfo.first.Upgrade();
if (tabsController) {
if (scrollInfo.isScrolling) {
auto ratio = dimension.ConvertToPx() / Dimension(SCROLL_RANGE, DimensionUnit::VP).ConvertToPx();
tabsController->UpdateTabBarHiddenRatio(ratio);
auto offset = dimension.ConvertToPx() / SCROLL_RATIO;
if (NonPositive(offset) ||
!(source == ScrollSource::SCROLLER || source == ScrollSource::SCROLLER_ANIMATION)) {
tabsController->UpdateTabBarHiddenOffset(offset);
}
auto isChildReachTop = !scrollInfo.isAtTop && isAtTop;
@ -186,11 +168,6 @@ ScrollerObserver CreateObserver(WeakPtr<JSScroller> jsScrollerWeak)
};
observer.onTouchEvent = AceType::MakeRefPtr<NG::TouchEventImpl>(std::move(touchEvent));
auto panActionEndEvent = [jsScrollerWeak](const GestureEvent& info) {
HandleOnPanActionEndEvent(jsScrollerWeak, info);
};
observer.onPanActionEndEvent = panActionEndEvent;
auto reachStartEvent = [jsScrollerWeak]() {
HandleOnReachEvent(jsScrollerWeak, true);
};
@ -211,14 +188,34 @@ ScrollerObserver CreateObserver(WeakPtr<JSScroller> jsScrollerWeak)
};
observer.onScrollStopEvent = std::move(scrollStopEvent);
auto didScrollEvent = [jsScrollerWeak](Dimension dimension, ScrollState state, bool isAtTop, bool isAtBottom) {
HandleOnDidScrollEvent(jsScrollerWeak, dimension, state, isAtTop, isAtBottom);
auto didScrollEvent = [jsScrollerWeak](Dimension dimension, ScrollSource source, bool isAtTop, bool isAtBottom) {
HandleOnDidScrollEvent(jsScrollerWeak, dimension, source, isAtTop, isAtBottom);
};
observer.onDidScrollEvent = std::move(didScrollEvent);
return observer;
}
void HandleOnChangeEvent(WeakPtr<NG::TabsControllerNG> tabsControllerWeak, int32_t index)
{
auto bindInfoIter = bindInfoMap_.find(tabsControllerWeak);
if (bindInfoIter == bindInfoMap_.end()) {
return;
}
for (const auto& scrollInfo : bindInfoIter->second) {
auto jsScroller = scrollInfo.first.Upgrade();
if (jsScroller) {
auto scroller = jsScroller->GetController().Upgrade();
if (scroller) {
scroller->StopAnimate();
}
}
}
auto tabsController = tabsControllerWeak.Upgrade();
CHECK_NULL_VOID(tabsController);
tabsController->StartShowTabBar();
}
void HandleBindTabsToScrollable(const JSRef<JSObject>& jsTabsControllerVal, const JSRef<JSObject>& jsScrollerVal,
const std::optional<JSRef<JSObject>>& parentJsScrollerVal)
{
@ -239,6 +236,9 @@ void HandleBindTabsToScrollable(const JSRef<JSObject>& jsTabsControllerVal, cons
return;
}
}
tabsController->SetOnChangeImpl([tabsControllerWeak](int32_t index) {
HandleOnChangeEvent(tabsControllerWeak, index);
});
auto observer = CreateObserver(jsScrollerWeak);
jsScroller->SetObserver(observer);
ScrollInfo scrollInfo;

View File

@ -109,7 +109,7 @@ void JSText::SetHeight(const JSCallbackInfo& info)
void JSText::SetFont(const JSCallbackInfo& info)
{
Font font;
auto pipelineContext = PipelineContext::GetCurrentContext();
auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
CHECK_NULL_VOID(pipelineContext);
auto theme = pipelineContext->GetTheme<TextTheme>();
CHECK_NULL_VOID(theme);
@ -181,7 +181,7 @@ void JSText::SetFontSize(const JSCallbackInfo& info)
CalcDimension fontSize;
JSRef<JSVal> args = info[0];
if (!ParseJsDimensionFpNG(args, fontSize, false) || fontSize.IsNegative()) {
auto pipelineContext = PipelineBase::GetCurrentContext();
auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
CHECK_NULL_VOID(pipelineContext);
auto theme = pipelineContext->GetTheme<TextTheme>();
CHECK_NULL_VOID(theme);
@ -278,7 +278,7 @@ void JSText::SetTextColor(const JSCallbackInfo& info)
Color textColor;
JSRef<JSVal> args = info[0];
if (!ParseJsColor(args, textColor)) {
auto pipelineContext = PipelineBase::GetCurrentContext();
auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
CHECK_NULL_VOID(pipelineContext);
auto theme = pipelineContext->GetTheme<TextTheme>();
CHECK_NULL_VOID(theme);
@ -386,7 +386,7 @@ void JSText::SetTextCaretColor(const JSCallbackInfo& info)
}
Color caretColor;
if (!ParseJsColor(info[0], caretColor)) {
auto pipelineContext = PipelineContext::GetCurrentContextSafely();
auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
CHECK_NULL_VOID(pipelineContext);
auto theme = pipelineContext->GetTheme<TextTheme>();
CHECK_NULL_VOID(theme);
@ -402,7 +402,7 @@ void JSText::SetSelectedBackgroundColor(const JSCallbackInfo& info)
}
Color selectedColor;
if (!ParseJsColor(info[0], selectedColor)) {
auto pipelineContext = PipelineContext::GetCurrentContextSafely();
auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
CHECK_NULL_VOID(pipelineContext);
auto theme = pipelineContext->GetTheme<TextTheme>();
CHECK_NULL_VOID(theme);
@ -529,7 +529,7 @@ void JSText::SetMinFontSize(const JSCallbackInfo& info)
if (info.Length() < 1) {
return;
}
auto pipelineContext = PipelineBase::GetCurrentContext();
auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
CHECK_NULL_VOID(pipelineContext);
auto theme = pipelineContext->GetTheme<TextTheme>();
CHECK_NULL_VOID(theme);
@ -551,7 +551,7 @@ void JSText::SetMaxFontSize(const JSCallbackInfo& info)
if (info.Length() < 1) {
return;
}
auto pipelineContext = PipelineBase::GetCurrentContext();
auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
CHECK_NULL_VOID(pipelineContext);
auto theme = pipelineContext->GetTheme<TextTheme>();
CHECK_NULL_VOID(theme);

View File

@ -49,6 +49,7 @@
#include "core/components_ng/pattern/text_field/text_content_type.h"
#include "core/components_ng/pattern/text_field/text_field_model_ng.h"
#include "core/image/image_source_info.h"
#include "core/text/text_emoji_processor.h"
namespace OHOS::Ace {
@ -114,6 +115,19 @@ bool ParseJsLengthMetrics(const JSRef<JSObject>& obj, CalcDimension& result)
result = dimension;
return true;
}
void ProcessStringUnpairedSurrogates(std::optional<std::string>& value)
{
if (!value.has_value()) {
return;
}
std::u16string temp = StringUtils::Str8ToStr16(value.value());
std::string result(value.value().c_str());
if (temp.length() == 0 && result.length() != 0) {
result = TextEmojiProcessor::ConvertU8stringUnpairedSurrogates(result);
}
value = result;
}
} // namespace
void ParseTextFieldTextObject(const JSCallbackInfo& info, const JSRef<JSVal>& changeEventVal)
@ -164,6 +178,8 @@ void JSTextField::CreateTextInput(const JSCallbackInfo& info)
}
}
ProcessStringUnpairedSurrogates(placeholderSrc);
ProcessStringUnpairedSurrogates(value);
auto controller = TextFieldModel::GetInstance()->CreateTextInput(placeholderSrc, value);
if (jsController) {
jsController->SetController(controller);
@ -212,6 +228,9 @@ void JSTextField::CreateTextArea(const JSCallbackInfo& info)
jsController = JSRef<JSObject>::Cast(controllerObj)->Unwrap<JSTextEditableController>();
}
}
ProcessStringUnpairedSurrogates(placeholderSrc);
ProcessStringUnpairedSurrogates(value);
auto controller = TextFieldModel::GetInstance()->CreateTextArea(placeholderSrc, value);
if (jsController) {
jsController->SetController(controller);
@ -418,7 +437,7 @@ void JSTextField::SetCaretStyle(const JSCallbackInfo& info)
auto paramObject = JSRef<JSObject>::Cast(jsValue);
auto caretWidth = paramObject->GetProperty("width");
auto pipeline = PipelineBase::GetCurrentContext();
auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
CHECK_NULL_VOID(pipeline);
auto theme = pipeline->GetThemeManager()->GetTheme<TextFieldTheme>();
CHECK_NULL_VOID(theme);
@ -481,7 +500,7 @@ void JSTextField::SetSelectedBackgroundColor(const JSCallbackInfo& info)
Color selectedColor;
if (!ParseJsColor(info[0], selectedColor)) {
auto pipeline = PipelineBase::GetCurrentContext();
auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
CHECK_NULL_VOID(pipeline);
auto theme = pipeline->GetThemeManager()->GetTheme<TextFieldTheme>();
CHECK_NULL_VOID(theme);
@ -1670,7 +1689,7 @@ void JSTextField::SetDecoration(const JSCallbackInfo& info)
JSRef<JSVal> colorValue = obj->GetProperty("color");
JSRef<JSVal> styleValue = obj->GetProperty("style");
auto pipelineContext = PipelineBase::GetCurrentContext();
auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
CHECK_NULL_VOID(pipelineContext);
auto theme = pipelineContext->GetTheme<TextFieldTheme>();
CHECK_NULL_VOID(theme);
@ -1715,7 +1734,7 @@ void JSTextField::SetMaxFontSize(const JSCallbackInfo& info)
if (info.Length() < 1) {
return;
}
auto pipelineContext = PipelineBase::GetCurrentContext();
auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
CHECK_NULL_VOID(pipelineContext);
auto theme = pipelineContext->GetTheme<TextFieldTheme>();
CHECK_NULL_VOID(theme);

View File

@ -5162,10 +5162,6 @@ bool JSViewAbstract::ParseJsDimensionNG(
result = resourceWrapper->GetDimensionByName(param->ToString());
return true;
}
JSRef<JSVal> type = jsObj->GetProperty("type");
if (type->IsNull() || !type->IsNumber()) {
return false;
}
if (resType == static_cast<int32_t>(ResourceType::STRING)) {
auto value = resourceWrapper->GetString(resId->ToNumber<uint32_t>());
return StringUtils::StringToCalcDimensionNG(value, result, false, defaultUnit);
@ -8082,6 +8078,19 @@ void JSViewAbstract::ParseSheetStyle(
sheetStyle.shadow = shadow;
}
// Parse hoverMode
auto enableHoverModeValue = paramObj->GetProperty("enableHoverMode");
if (enableHoverModeValue->IsBoolean()) {
sheetStyle.enableHoverMode = enableHoverModeValue->ToBoolean();
}
auto hoverModeAreaValue = paramObj->GetProperty("hoverModeArea");
if (hoverModeAreaValue->IsNumber()) {
auto hoverModeArea = hoverModeAreaValue->ToNumber<int32_t>();
if (hoverModeArea >= 0 && hoverModeArea < static_cast<int32_t>(HOVER_MODE_AREA_TYPE.size())) {
sheetStyle.hoverModeArea = HOVER_MODE_AREA_TYPE[hoverModeArea];
}
}
auto widthValue = paramObj->GetProperty("width");
CalcDimension width;
if (ParseJsDimensionVpNG(widthValue, width, true)) {

View File

@ -164,17 +164,17 @@ class DumpInfo {
// global function used to throw error in Promise
declare function _arkUIUncaughtPromiseError(error: any);
function setAceDebugMode() {
function setAceDebugMode(): void {
stateMgmtDFX.enableDebug = true;
}
class aceDebugTrace {
public static begin(...args: any) {
public static begin(...args: any): void {
if (stateMgmtDFX.enableDebug) {
aceTrace.begin(...args);
}
}
public static end() {
public static end(): void {
if (stateMgmtDFX.enableDebug) {
aceTrace.end();
}

View File

@ -255,10 +255,9 @@ class __RepeatVirtualScrollImpl<T> {
private reRender(): void {
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl(${this.repeatElmtId_}): reRender ...`);
this.purgeKeyCache();
// When this.totalCount_ == 0 need render to clear visible items
if (this.hasVisibleItemsChanged() || this.totalCount_ === 0) {
this.purgeKeyCache();
RepeatVirtualScrollNative.updateRenderState(this.totalCount_, true);
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl: reRender - done.`);
} else {

View File

@ -714,7 +714,7 @@ abstract class PUV2ViewBase extends NativeViewPartialUpdate {
default:
DumpLog.print(0, `\nUnsupported JS DFX dump command: [${command.what}, viewId=${command.viewId}, isRecursive=${command.isRecursive}]\n`);
}
})
});
}
private printDFXHeader(header: string, command: DFXCommand): void {
@ -727,11 +727,11 @@ abstract class PUV2ViewBase extends NativeViewPartialUpdate {
private processOnDumpCommands(commands: string[]): DFXCommand[] {
let isFlag: Function = (param: string): boolean => {
return '-r'.match(param) != null || param.startsWith('-viewId=');
}
};
let dfxCommands: DFXCommand[] = [];
for (var i: number = 0; i < commands.length; i++) {
for (let i: number = 0; i < commands.length; i++) {
let command = commands[i];
if (isFlag(command)) {
if (command.startsWith('-viewId=')) {

View File

@ -473,6 +473,10 @@ abstract class ViewV2 extends PUV2ViewBase implements IView {
public debugInfoStateVars(): string {
let retVal: string = `|--${this.constructor.name}[${this.id__()}]\n`;
let meta = this[ObserveV2.V2_DECO_META];
if (!meta) {
retVal += ' No State Variables';
return retVal;
}
Object.getOwnPropertyNames(meta)
.filter((varName) => !varName.startsWith('___pc_alias__@')) // remove provider & consumer prefix
.forEach((varName) => {
@ -500,11 +504,11 @@ abstract class ViewV2 extends PUV2ViewBase implements IView {
} else {
retVal += ` PersistenceV2[${elmtId}]`;
}
})
});
}
retVal += '\n';
})
});
return retVal;
}

View File

@ -1092,6 +1092,12 @@ std::function<void(NG::DrawingContext&, CustomSpanOptions)> JSCustomSpan::ParseO
sizeObj->SetProperty<float>("height", PipelineBase::Px2VpWithCurrentDensity(context.height));
sizeObj->SetProperty<float>("width", PipelineBase::Px2VpWithCurrentDensity(context.width));
contextObj->SetPropertyObject("size", sizeObj);
JSRef<JSObject> sizeInPxObj = objectTemplate->NewInstance();
sizeInPxObj->SetProperty<float>("height", context.height);
sizeInPxObj->SetProperty<float>("width", context.height);
contextObj->SetPropertyObject("sizeInPixel", sizeInPxObj);
auto engine = EngineHelper::GetCurrentEngine();
CHECK_NULL_VOID(engine);
NativeEngine* nativeEngine = engine->GetNativeEngine();

View File

@ -84,6 +84,22 @@ struct AccessibilityParentRectInfo {
int32_t rotateDegree = 0; // final rotate degree of parent interface
};
enum class AccessibilityCallbackEventId : uint32_t {
ON_LOAD_PAGE = 0,
ON_SHOW = 1,
ON_HIDE = 2,
};
struct AccessibilityCallbackEvent {
AccessibilityCallbackEventId eventId;
int64_t parameter;
AccessibilityCallbackEvent(AccessibilityCallbackEventId id, int64_t para) : eventId(id), parameter(para) {}
bool operator < (const AccessibilityCallbackEvent& other) const
{
return eventId < other.eventId;
}
};
class AccessibilitySAObserverCallback {
public:
explicit AccessibilitySAObserverCallback(int64_t accessibilityId) : accessibilityId_(accessibilityId)
@ -244,6 +260,8 @@ public:
return false;
}
virtual void FireAccessibilityEventCallback(uint32_t eventId, int64_t parameter) {}
bool IsRegister()
{
return isReg_;

View File

@ -111,6 +111,7 @@ enum class AccessibilityEventType : size_t {
PAGE_CLOSE = 0x08000000,
ANNOUNCE_FOR_ACCESSIBILITY = 0x10000000,
PAGE_OPEN = 0x20000000,
ELEMENT_INFO_CHANGE = 0x40000000,
UNKNOWN,
};

View File

@ -83,6 +83,14 @@ public:
bool isShowCopy = true, bool isShowSelectText = true);
void ResponseBestMatchItem(const AISpan& aiSpan);
void GetAIEntityMenu();
bool GetCloseMenuForAISpanFlag()
{
return closeMenuForAISpanFlag_;
}
void SetCloseMenuForAISpanFlag(bool flag)
{
closeMenuForAISpanFlag_ = flag;
}
private:
friend class NG::TextPattern;
@ -95,6 +103,7 @@ private:
WeakPtr<NG::FrameNode> frameNode_;
bool aiDetectInitialized_ = false;
bool hasClickedAISpan_ = false;
bool closeMenuForAISpanFlag_ = false;
bool pressedByLeftMouse_ = false;
bool typeChanged_ = false;
bool hasClickedMenuOption_ = false;

View File

@ -498,7 +498,7 @@ public:
virtual bool GetCurPointerEventInfo(
int32_t& pointerId, int32_t& globalX, int32_t& globalY, int32_t& sourceType,
int32_t& sourceTool, StopDragCallback&& stopDragCallback)
int32_t& sourceTool, int32_t& displayId, StopDragCallback&& stopDragCallback)
{
return false;
}

View File

@ -40,6 +40,9 @@ enum class InstanceIdGenReason : uint32_t {
class ACE_EXPORT ContainerScope {
public:
template<typename T>
explicit ContainerScope(T) = delete;
explicit ContainerScope(int32_t id)
{
UpdateCurrent(id);

View File

@ -1316,24 +1316,23 @@ bool EventManager::DispatchMouseEventNG(const MouseEvent& event)
if (validAction.find(event.action) == validAction.end()) {
return false;
}
if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_THIRTEEN)) {
return DispatchMouseEventInGreatOrEqualAPI13(event);
}
return DispatchMouseEventInLessAPI13(event);
}
bool EventManager::DispatchMouseEventInGreatOrEqualAPI13(const MouseEvent& event)
{
MouseTestResult handledResults;
bool isStopPropagation = false;
if (event.button != MouseButton::NONE_BUTTON) {
auto mouseTargetIter = pressMouseTestResults_.find(event.button);
if (mouseTargetIter != pressMouseTestResults_.end()) {
for (const auto& mouseTarget : mouseTargetIter->second) {
if (!mouseTarget) {
continue;
}
handledResults.emplace_back(mouseTarget);
if (mouseTarget->HandleMouseEvent(event)) {
isStopPropagation = true;
break;
}
}
if (auto mouseTargetIter = pressMouseTestResultsMap_.find(event.button);
mouseTargetIter != pressMouseTestResultsMap_.end()) {
DispatchMouseEventToPressResults(event, mouseTargetIter->second, handledResults, isStopPropagation);
}
if (event.action == MouseAction::PRESS) {
pressMouseTestResults_[event.button] = currMouseTestResults_;
pressMouseTestResultsMap_[event.button] = currMouseTestResults_;
} else if (event.action == MouseAction::RELEASE) {
DoSingleMouseActionRelease(event.button);
}
@ -1344,6 +1343,59 @@ bool EventManager::DispatchMouseEventNG(const MouseEvent& event)
return DispatchMouseEventToCurResults(event, handledResults, isStopPropagation);
}
bool EventManager::DispatchMouseEventInLessAPI13(const MouseEvent& event)
{
MouseTestResult handledResults;
bool isStopPropagation = false;
if (event.button == MouseButton::LEFT_BUTTON) {
DispatchMouseEventToPressResults(event, pressMouseTestResults_, handledResults, isStopPropagation);
if (event.action == MouseAction::PRESS) {
pressMouseTestResults_ = currMouseTestResults_;
} else if (event.action == MouseAction::RELEASE) {
DoMouseActionRelease();
}
}
if (event.pullAction == MouseAction::PULL_UP) {
DoMouseActionRelease();
}
for (const auto& mouseTarget : currMouseTestResults_) {
if (!mouseTarget) {
continue;
}
if (!isStopPropagation) {
auto ret = std::find(handledResults.begin(), handledResults.end(), mouseTarget) == handledResults.end();
// if pressMouseTestResults doesn't have any isStopPropagation, use default handledResults.
if (ret && mouseTarget->HandleMouseEvent(event)) {
return true;
}
continue;
}
if (std::find(pressMouseTestResults_.begin(), pressMouseTestResults_.end(), mouseTarget) ==
pressMouseTestResults_.end()) {
// if pressMouseTestResults has isStopPropagation, use pressMouseTestResults as handledResults.
if (mouseTarget->HandleMouseEvent(event)) {
return true;
}
}
}
return false;
}
void EventManager::DispatchMouseEventToPressResults(const MouseEvent& event, const MouseTestResult& targetResults,
MouseTestResult& handledResults, bool& isStopPropagation)
{
for (const auto& mouseTarget : targetResults) {
if (!mouseTarget) {
continue;
}
handledResults.emplace_back(mouseTarget);
if (mouseTarget->HandleMouseEvent(event)) {
isStopPropagation = true;
break;
}
}
}
bool EventManager::DispatchMouseEventToCurResults(
const MouseEvent& event, const MouseTestResult& handledResults, bool isStopPropagation)
{
@ -1359,10 +1411,10 @@ bool EventManager::DispatchMouseEventToCurResults(
}
continue;
}
auto mouseTargetIter = pressMouseTestResults_.find(event.button);
if ((mouseTargetIter != pressMouseTestResults_.end() &&
auto mouseTargetIter = pressMouseTestResultsMap_.find(event.button);
if ((mouseTargetIter != pressMouseTestResultsMap_.end() &&
std::find(mouseTargetIter->second.begin(), mouseTargetIter->second.end(), mouseTarget) ==
mouseTargetIter->second.end()) || mouseTargetIter == pressMouseTestResults_.end()) {
mouseTargetIter->second.end()) || mouseTargetIter == pressMouseTestResultsMap_.end()) {
// if pressMouseTestResults has isStopPropagation, use pressMouseTestResults as handledResults.
if (mouseTarget->HandleMouseEvent(event)) {
return true;
@ -1379,7 +1431,7 @@ void EventManager::DoMouseActionRelease()
void EventManager::DoSingleMouseActionRelease(MouseButton button)
{
pressMouseTestResults_.erase(button);
pressMouseTestResultsMap_.erase(button);
}
void EventManager::DispatchMouseHoverAnimationNG(const MouseEvent& event)

View File

@ -130,8 +130,6 @@ public:
void UpdatePenHoverNode(const TouchEvent& event, const TouchTestResult& testResult);
void UpdateHoverNode(const MouseEvent& event, const TouchTestResult& testResult);
bool DispatchMouseEventNG(const MouseEvent& event);
bool DispatchMouseEventToCurResults(
const MouseEvent& event, const MouseTestResult& handledResults, bool isStopPropagation);
void DispatchMouseHoverAnimationNG(const MouseEvent& event);
bool DispatchMouseHoverEventNG(const MouseEvent& event);
void DispatchHoverEffectEvent(const MouseEvent& event);
@ -329,10 +327,19 @@ private:
void UpdateDragInfo(TouchEvent& point);
void UpdateInfoWhenFinishDispatch(const TouchEvent& point, bool sendOnTouch);
void DoSingleMouseActionRelease(MouseButton button);
bool DispatchMouseEventInGreatOrEqualAPI13(const MouseEvent& event);
bool DispatchMouseEventInLessAPI13(const MouseEvent& event);
void DispatchMouseEventToPressResults(const MouseEvent& event, const MouseTestResult& targetResults,
MouseTestResult& handledResults, bool& isStopPropagation);
bool DispatchMouseEventToCurResults(
const MouseEvent& event, const MouseTestResult& handledResults, bool isStopPropagation);
bool innerEventWin_ = false;
std::unordered_map<size_t, TouchTestResult> mouseTestResults_;
MouseTestResult currMouseTestResults_;
std::unordered_map<MouseButton, MouseTestResult> pressMouseTestResults_;
// used less than API13
MouseTestResult pressMouseTestResults_;
// used great or equal API13
std::unordered_map<MouseButton, MouseTestResult> pressMouseTestResultsMap_;
HoverTestResult currHoverTestResults_;
HoverTestResult lastHoverTestResults_;
HoverTestResult curAccessibilityHoverResults_;

View File

@ -19,6 +19,7 @@
#include <cstdint>
#include "core/components_ng/base/frame_node.h"
#include "core/components_ng/pattern/ui_extension/ui_extension_model.h"
namespace OHOS::AAFwk {
class Want;
@ -32,7 +33,7 @@ namespace OHOS::Ace {
class ModalUIExtension final {
public:
static RefPtr<NG::FrameNode> Create(const AAFwk::Want& want, const ModalUIExtensionCallbacks& callbacks,
bool isAsyncModalBinding = false, bool isModal = true);
const NG::InnerModalUIExtensionConfig& config);
static int32_t GetSessionId(const RefPtr<NG::FrameNode>& uiExtNode);

View File

@ -62,7 +62,7 @@ void PluginManager::UpdateConfigurationInPlugin(
pluginThemeManager->LoadResourceThemes();
CHECK_NULL_VOID(taskExecutor);
taskExecutor->PostTask(
[instanceId = pluginSubContainerMap.first,
[instanceId = static_cast<int32_t>(pluginSubContainerMap.first),
weak = AceType::WeakClaim(AceType::RawPtr(pluginSubContainer))]() {
ContainerScope scope(instanceId);
auto pluginSubContainer = weak.Upgrade();

View File

@ -189,11 +189,6 @@ public:
return isUserSetCursor_;
}
bool GetUiDvsyncSwitch() const
{
return dvsyncOn_;
}
virtual int32_t GetCurrentRefreshRateMode() const
{
return -1;
@ -215,6 +210,11 @@ public:
return 0;
}
virtual bool GetIsRequestVsync()
{
return false;
}
virtual void NotifyExtensionTimeout(int32_t errorCode) {}
protected:
bool isRequestVsync_ = false;
@ -232,7 +232,6 @@ protected:
uint64_t lastRequestVsyncTime_ = 0;
int64_t lastVsyncEndTimestamp_ = 0;
uint32_t windowId_ = 0;
bool dvsyncOn_ = false;
private:
std::function<Rect()> windowRectImpl_;

View File

@ -15,69 +15,24 @@
#include "core/components/common/painter/rosen_universal_painter.h"
#ifndef USE_ROSEN_DRAWING
#include "include/core/SkPaint.h"
#include "include/core/SkRRect.h"
#else
#include "core/components_ng/render/drawing.h"
#endif
namespace OHOS::Ace {
#ifndef USE_ROSEN_DRAWING
void RosenUniversalPainter::DrawHoverBackground(
SkCanvas* canvas, const Rect& paintRect, uint32_t hoverBackgroundColor, double borderRadius)
#else
void RosenUniversalPainter::DrawHoverBackground(
RSCanvas* canvas, const Rect& paintRect, uint32_t hoverBackgroundColor, double borderRadius)
#endif
{
RRect paintRRect = RRect::MakeRRect(paintRect, borderRadius, borderRadius);
RosenUniversalPainter::DrawRRectBackground(canvas, paintRRect, hoverBackgroundColor, 1.0);
}
#ifndef USE_ROSEN_DRAWING
void RosenUniversalPainter::DrawRRectBackground(
SkCanvas* canvas, const RRect& paintRRect, uint32_t backgroundColor, double dipScale)
#else
void RosenUniversalPainter::DrawRRectBackground(
RSCanvas* canvas, const RRect& paintRRect, uint32_t backgroundColor, double dipScale)
#endif
{
if (!canvas) {
LOGE("canvas is null.");
return;
}
#ifndef USE_ROSEN_DRAWING
SkPaint skPaint;
skPaint.setAntiAlias(true);
skPaint.setStyle(SkPaint::Style::kFill_Style);
skPaint.setColor(backgroundColor);
auto paintRect = paintRRect.GetRect();
auto corner = paintRRect.GetCorner();
SkRect skRect = SkRect::MakeXYWH(paintRect.Left(), paintRect.Top(), paintRect.Width(), paintRect.Height());
SkVector fRadii[4] = { { 0.0, 0.0 }, { 0.0, 0.0 }, { 0.0, 0.0 }, { 0.0, 0.0 } };
fRadii[SkRRect::kUpperLeft_Corner].set(
SkDoubleToScalar(std::max(RosenUniversalPainter::NormalizeToPx(corner.topLeftRadius.GetX(), dipScale), 0.0)),
SkDoubleToScalar(std::max(RosenUniversalPainter::NormalizeToPx(corner.topLeftRadius.GetY(), dipScale), 0.0)));
fRadii[SkRRect::kUpperRight_Corner].set(
SkDoubleToScalar(std::max(RosenUniversalPainter::NormalizeToPx(corner.topRightRadius.GetX(), dipScale), 0.0)),
SkDoubleToScalar(std::max(RosenUniversalPainter::NormalizeToPx(corner.topRightRadius.GetY(), dipScale), 0.0)));
fRadii[SkRRect::kLowerRight_Corner].set(
SkDoubleToScalar(
std::max(RosenUniversalPainter::NormalizeToPx(corner.bottomRightRadius.GetX(), dipScale), 0.0)),
SkDoubleToScalar(
std::max(RosenUniversalPainter::NormalizeToPx(corner.bottomRightRadius.GetY(), dipScale), 0.0)));
fRadii[SkRRect::kLowerLeft_Corner].set(
SkDoubleToScalar(std::max(RosenUniversalPainter::NormalizeToPx(corner.bottomLeftRadius.GetX(), dipScale), 0.0)),
SkDoubleToScalar(
std::max(RosenUniversalPainter::NormalizeToPx(corner.bottomLeftRadius.GetY(), dipScale), 0.0)));
SkRRect skRRect;
skRRect.setRectRadii(skRect, fRadii);
canvas->drawRRect(skRRect, skPaint);
#else
RSBrush brush;
brush.SetAntiAlias(true);
brush.SetColor(backgroundColor);
@ -111,7 +66,6 @@ void RosenUniversalPainter::DrawRRectBackground(
canvas->AttachBrush(brush);
canvas->DrawRoundRect(roundRect);
canvas->DetachBrush();
#endif
}
double RosenUniversalPainter::NormalizeToPx(const Dimension& dimension, double scale)

View File

@ -16,10 +16,6 @@
#ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_PAINTER_ROSEN_UNIVERSAL_PAINTER_H
#define FOUNDATION_ACE_FRAMEWORKS_CORE_PAINTER_ROSEN_UNIVERSAL_PAINTER_H
#ifndef USE_ROSEN_DRAWING
#include "include/core/SkCanvas.h"
#endif
#include "core/pipeline/base/render_node.h"
namespace OHOS::Ace {
@ -29,19 +25,11 @@ public:
RosenUniversalPainter() = default;
~RosenUniversalPainter() = default;
#ifndef USE_ROSEN_DRAWING
static void DrawHoverBackground(
SkCanvas* canvas, const Rect& paintRect, uint32_t hoverBackgroundColor, double borderRadius);
static void DrawRRectBackground(
SkCanvas* canvas, const RRect& paintRRect, uint32_t backgroundColor, double dipScale);
#else
static void DrawHoverBackground(
RSCanvas* canvas, const Rect& paintRect, uint32_t hoverBackgroundColor, double borderRadius);
static void DrawRRectBackground(
RSCanvas* canvas, const RRect& paintRRect, uint32_t backgroundColor, double dipScale);
#endif
static double NormalizeToPx(const Dimension& dimension, double scale);
};

View File

@ -735,8 +735,12 @@ void FormManagerDelegate::SetAllowUpdate(bool allowUpdate)
void FormManagerDelegate::NotifySurfaceChange(float width, float height, float borderWidth)
{
OHOS::AppExecFwk::FormMgr::GetInstance().UpdateFormSize(runningCardId_, width, height, borderWidth);
wantCache_.SetParam(OHOS::AppExecFwk::Constants::PARAM_FORM_WIDTH_KEY, static_cast<double>(width));
wantCache_.SetParam(OHOS::AppExecFwk::Constants::PARAM_FORM_HEIGHT_KEY, static_cast<double>(height));
wantCache_.SetParam(OHOS::AppExecFwk::Constants::PARAM_FORM_BORDER_WIDTH_KEY, borderWidth);
if (formRendererDispatcher_ == nullptr) {
TAG_LOGE(AceLogTag::ACE_FORM, "formRendererDispatcher_ is nullptr");
TAG_LOGW(AceLogTag::ACE_FORM, "formRendererDispatcher_ is nullptr");
return;
}
WindowSizeChangeReason sizeChangeReason = WindowSizeChangeReason::UNDEFINED;
@ -744,12 +748,10 @@ void FormManagerDelegate::NotifySurfaceChange(float width, float height, float b
sizeChangeReason = WindowSizeChangeReason::ROTATION;
}
std::shared_ptr<Rosen::RSTransaction> transaction;
if (sizeChangeReason != WindowSizeChangeReason::UNDEFINED) {
if (FormManager::GetInstance().GetRSTransaction().lock()) {
transaction = FormManager::GetInstance().GetRSTransaction().lock();
} else if (auto transactionController = Rosen::RSSyncTransactionController::GetInstance()) {
transaction = transactionController->GetRSTransaction();
}
if (FormManager::GetInstance().GetRSTransaction().lock()) {
transaction = FormManager::GetInstance().GetRSTransaction().lock();
} else if (auto transactionController = Rosen::RSSyncTransactionController::GetInstance()) {
transaction = transactionController->GetRSTransaction();
}
formRendererDispatcher_->DispatchSurfaceChangeEvent(width, height,

View File

@ -33,19 +33,8 @@ public:
scroll_ = scroll;
}
void SetObserver(const ScrollerObserver& observer) override
{
observer_ = observer;
}
ScrollerObserver GetObserver()
{
return observer_;
}
protected:
WeakPtr<RenderNode> scroll_;
ScrollerObserver observer_;
};
} // namespace OHOS::Ace

View File

@ -127,6 +127,8 @@ public:
virtual void CloseAllSwipeActions(OnFinishFunc&& onFinishCallback) {}
virtual void SetObserver(const ScrollerObserver& observer) {}
virtual void StopAnimate() {}
};
} // namespace OHOS::Ace

View File

@ -30,6 +30,7 @@ using TurnPageRateFunc = std::function<void(const int32_t, float)>;
using ChangeIndexImpl = std::function<void(const int32_t, bool)>;
using PreloadItemsFunc = std::function<void(const std::set<int32_t>)>;
using PreloadItemsFinishFunc = std::function<void(const int32_t, const std::string)>;
using OnChangeFunc = std::function<void(int32_t index)>;
class SwiperController : public virtual AceType {
DECLARE_ACE_TYPE(SwiperController, AceType);
@ -224,6 +225,18 @@ public:
}
}
void SetOnChangeImpl(const OnChangeFunc& onChangeImpl)
{
onChangeImpl_ = onChangeImpl;
}
void FireOnChangeEvent(int32_t index)
{
if (onChangeImpl_) {
onChangeImpl_(index);
}
}
private:
SwipeToImpl swipeToImpl_;
SwipeToWithoutAnimationImpl swipeToWithoutAnimationImpl_;
@ -242,6 +255,7 @@ private:
CommonFunc surfaceChangeCallback_;
PreloadItemsFinishFunc preloadFinishCallback_;
PreloadItemsFunc preloadItemsImpl_;
OnChangeFunc onChangeImpl_;
};
} // namespace OHOS::Ace

View File

@ -17,6 +17,7 @@
#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_TAB_BAR_TAB_THEME_H
#include "base/geometry/dimension.h"
#include "core/common/container.h"
#include "core/components/common/properties/text_style.h"
#include "core/components/theme/theme.h"
#include "core/components/theme/theme_constants.h"
@ -123,6 +124,8 @@ public:
theme->tabBarDefaultHeight_ = pattern->GetAttr<Dimension>("tab_bar_default_height", 0.0_vp);
theme->bottomTabBarDefaultHeight_ =
pattern->GetAttr<Dimension>("bottom_tab_bar_default_height", 0.0_vp);
theme->bottomTabBarDefaultNewHeight_ =
pattern->GetAttr<Dimension>("bottom_tab_bar_default_new_height", 48.0_vp);
theme->tabBarDefaultWidth_ = pattern->GetAttr<Dimension>("tab_bar_default_width", 0.0_vp);
theme->subTabBarMinWidth_ = pattern->GetAttr<Dimension>("sub_tab_bar_min_width", 0.0_vp);
theme->dividerColor_ = pattern->GetAttr<Color>("divider_color", Color::BLACK);
@ -441,7 +444,13 @@ public:
}
const Dimension& GetBottomTabBarDefaultWidth() const
{
return bottomTabBarDefaultHeight_;
if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_THIRTEEN)) {
return bottomTabBarDefaultNewHeight_;
} else if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
return bottomTabBarDefaultHeight_;
} else {
return tabBarDefaultHeight_;
}
}
const Dimension& GetDialogRadiusLevel10() const
{
@ -530,6 +539,7 @@ private:
double tabContentAnimationDuration_;
Dimension tabBarDefaultHeight_;
Dimension bottomTabBarDefaultHeight_;
Dimension bottomTabBarDefaultNewHeight_;
Dimension tabBarDefaultWidth_;
Dimension subTabBarMinWidth_;
Color dividerColor_;

View File

@ -3007,6 +3007,23 @@ void WebDelegate::UpdateInitialScale(float scale)
TaskExecutor::TaskType::PLATFORM, "ArkUIWebUpdateInitialScale");
}
void WebDelegate::UpdateLayoutMode(WebLayoutMode mode)
{
auto context = context_.Upgrade();
if (!context) {
return;
}
context->GetTaskExecutor()->PostTask(
[weak = WeakClaim(this), mode]() {
auto delegate = weak.Upgrade();
if (delegate && delegate->nweb_) {
std::shared_ptr<OHOS::NWeb::NWebPreference> setting = delegate->nweb_->GetPreference();
delegate->nweb_->SetFitContentMode(static_cast<int32_t>(mode));
}
},
TaskExecutor::TaskType::PLATFORM, "ArkUIWebUpdateLayoutMode");
}
void WebDelegate::Resize(const double& width, const double& height, bool isKeyboard)
{
if (width <= 0 || height <= 0) {
@ -7311,4 +7328,12 @@ bool WebDelegate::GetAccessibilityVisible(int64_t accessibilityId)
CHECK_NULL_RETURN(nweb_, true);
return nweb_->GetAccessibilityVisible(accessibilityId);
}
void WebDelegate::SetTransformHint(uint32_t rotation)
{
ACE_DCHECK(nweb_ != nullptr);
if (nweb_) {
nweb_->SetTransformHint(rotation);
}
}
} // namespace OHOS::Ace

View File

@ -734,6 +734,7 @@ public:
void UpdateUserAgent(const std::string& userAgent);
void UpdateBackgroundColor(const int backgroundColor);
void UpdateInitialScale(float scale);
void UpdateLayoutMode(WebLayoutMode mode);
void UpdateJavaScriptEnabled(const bool& isJsEnabled);
void UpdateAllowFileAccess(const bool& isFileAccessEnabled);
void UpdateBlockNetworkImage(const bool& onLineImageAccessEnabled);
@ -1083,6 +1084,8 @@ public:
bool GetAccessibilityVisible(int64_t accessibilityId);
void SetTransformHint(uint32_t rotation);
private:
void InitWebEvent();
void RegisterWebEvent();

View File

@ -975,6 +975,9 @@ void FrameNode::DumpSimplifyInfo(std::unique_ptr<JsonValue>& json)
DumpSimplifyCommonInfo(json);
DumpSimplifySafeAreaInfo(json);
DumpSimplifyOverlayInfo(json);
if (pattern_) {
DumpSimplifyInfo(json);
}
if (pattern_ && GetTag() == V2::UI_EXTENSION_COMPONENT_TAG) {
pattern_->DumpInfo(json);
}
@ -2510,24 +2513,30 @@ VectorF FrameNode::GetTransformScale() const
bool FrameNode::IsPaintRectWithTransformValid()
{
auto paintRectWithTransform = renderContext_->GetPaintRectWithTransform();
auto& paintRectWithTransform = GetOrRefreshMatrixFromCache().paintRectWithTransform;
if (NearZero(paintRectWithTransform.Width()) || NearZero(paintRectWithTransform.Height())) {
return true;
}
return false;
}
bool FrameNode::IsOutOfTouchTestRegion(const PointF& parentRevertPoint, const TouchEvent& touchEvent)
bool FrameNode::IsOutOfTouchTestRegion(const PointF& parentRevertPoint, const TouchEvent& touchEvent,
std::vector<RectF>* regionList)
{
bool isInChildRegion = false;
auto paintRect = renderContext_->GetPaintRectWithoutTransform();
if (pattern_->IsResponseRegionExpandingNeededForStylus(touchEvent)) {
paintRect = pattern_->ExpandDefaultResponseRegion(paintRect);
}
auto responseRegionList = GetResponseRegionList(paintRect, static_cast<int32_t>(touchEvent.sourceType));
std::vector<RectF> responseRegionList;
if (regionList) {
responseRegionList = *regionList;
} else {
responseRegionList = GetResponseRegionList(paintRect, static_cast<int32_t>(touchEvent.sourceType));
}
auto revertPoint = parentRevertPoint;
MapPointTo(revertPoint, GetOrRefreshRevertMatrixFromCache());
MapPointTo(revertPoint, GetOrRefreshMatrixFromCache().revertMatrix);
auto subRevertPoint = revertPoint - paintRect.GetOffset();
auto clip = renderContext_->GetClipEdge().value_or(false);
if (!InResponseRegionList(revertPoint, responseRegionList) || !GetTouchable()) {
@ -2578,7 +2587,8 @@ HitTestResult FrameNode::TouchTest(const PointF& globalPoint, const PointF& pare
const PointF& parentRevertPoint, TouchRestrict& touchRestrict, TouchTestResult& result, int32_t touchId,
ResponseLinkResult& responseLinkResult, bool isDispatch)
{
auto paintRect = renderContext_->GetPaintRectWithTransform();
auto& cacheMatrixInfo = GetOrRefreshMatrixFromCache();
auto paintRect = cacheMatrixInfo.paintRectWithTransform;
if (!isActive_) {
TAG_LOGW(AceLogTag::ACE_UIEVENT, "%{public}s is inActive, need't do touch test. Rect is %{public}s",
GetTag().c_str(), paintRect.ToString().c_str());
@ -2589,7 +2599,7 @@ HitTestResult FrameNode::TouchTest(const PointF& globalPoint, const PointF& pare
return HitTestResult::OUT_OF_REGION;
}
auto origRect = renderContext_->GetPaintRectWithoutTransform();
auto localMat = renderContext_->GetLocalTransformMatrix();
auto localMat = cacheMatrixInfo.localMatrix;
if (!touchRestrict.touchEvent.isMouseTouchTest) {
localMat_ = localMat;
}
@ -2614,7 +2624,7 @@ HitTestResult FrameNode::TouchTest(const PointF& globalPoint, const PointF& pare
}
{
ACE_DEBUG_SCOPED_TRACE("FrameNode::IsOutOfTouchTestRegion");
bool isOutOfRegion = IsOutOfTouchTestRegion(parentRevertPoint, touchRestrict.touchEvent);
bool isOutOfRegion = IsOutOfTouchTestRegion(parentRevertPoint, touchRestrict.touchEvent, &responseRegionList);
AddFrameNodeSnapshot(!isOutOfRegion, parentId, responseRegionList, touchRestrict.touchTestType);
if ((!isDispatch) && isOutOfRegion) {
return HitTestResult::OUT_OF_REGION;
@ -2644,7 +2654,7 @@ HitTestResult FrameNode::TouchTest(const PointF& globalPoint, const PointF& pare
auto localTransformOffset = preLocation - localPoint;
auto revertPoint = parentRevertPoint;
MapPointTo(revertPoint, GetOrRefreshRevertMatrixFromCache());
MapPointTo(revertPoint, cacheMatrixInfo.revertMatrix);
auto subRevertPoint = revertPoint - origRect.GetOffset();
bool consumed = false;
@ -2812,7 +2822,7 @@ std::vector<RectF> FrameNode::GetResponseRegionList(const RectF& rect, int32_t s
responseRegionList.emplace_back(rect);
return responseRegionList;
}
auto scaleProperty = ScaleProperty::CreateScaleProperty();
auto scaleProperty = ScaleProperty::CreateScaleProperty(GetContext());
bool isMouseEvent = (static_cast<SourceType>(sourceType) == SourceType::MOUSE);
if (isMouseEvent) {
if (gestureHub->GetResponseRegion().empty() && (gestureHub->GetMouseResponseRegion().empty())) {
@ -2992,7 +3002,7 @@ HitTestResult FrameNode::AxisTest(const PointF& globalPoint, const PointF& paren
auto localPoint = parentLocalPoint - renderContext_->GetPaintRectWithTransform().GetOffset();
renderContext_->GetPointWithTransform(localPoint);
auto revertPoint = parentRevertPoint;
MapPointTo(revertPoint, GetOrRefreshRevertMatrixFromCache());
MapPointTo(revertPoint, GetOrRefreshMatrixFromCache().revertMatrix);
auto subRevertPoint = revertPoint - renderContext_->GetPaintRectWithoutTransform().GetOffset();
bool consumed = false;
for (auto iter = frameChildren_.rbegin(); iter != frameChildren_.rend(); ++iter) {
@ -4716,7 +4726,7 @@ void FrameNode::RecordExposureInner()
void FrameNode::AddFrameNodeSnapshot(
bool isHit, int32_t parentId, std::vector<RectF> responseRegionList, EventTreeType type)
{
auto context = PipelineContext::GetCurrentContext();
auto context = GetContext();
CHECK_NULL_VOID(context);
auto eventMgr = context->GetEventManager();
CHECK_NULL_VOID(eventMgr);
@ -5268,24 +5278,28 @@ bool FrameNode::IsContextTransparent()
return true;
}
Matrix4& FrameNode::GetOrRefreshRevertMatrixFromCache(bool forceRefresh)
CacheMatrixInfo& FrameNode::GetOrRefreshMatrixFromCache(bool forceRefresh)
{
auto pipeline = NG::PipelineContext::GetCurrentContext();
CHECK_NULL_RETURN(pipeline, localRevertMatrix_);
auto pipeline = GetContext();
CHECK_NULL_RETURN(pipeline, cacheMatrixInfo_);
auto nanoTimestamp = pipeline->GetVsyncTime();
CHECK_NULL_RETURN(renderContext_, cacheMatrixInfo_);
auto rect = renderContext_->GetPaintRectWithoutTransform();
// the caller is trying to refresh cache forcedly or the cache is invalid
if (!isLocalRevertMatrixAvailable_ || forceRefresh || prePaintRect_ != rect ||
if (!isTransformNotChanged_ || forceRefresh || prePaintRect_ != rect ||
getCacheNanoTime_ + MATRIX_CACHE_TIME_THRESHOLD < nanoTimestamp) {
localRevertMatrix_ = renderContext_->GetRevertMatrix();
isLocalRevertMatrixAvailable_ = true;
cacheMatrixInfo_.revertMatrix = renderContext_->GetRevertMatrix();
cacheMatrixInfo_.paintRectWithTransform = renderContext_->GetPaintRectWithTransform();
cacheMatrixInfo_.localMatrix = Matrix4::CreateTranslate(-rect.GetOffset().GetX(),
-rect.GetOffset().GetY(), 0) * cacheMatrixInfo_.revertMatrix;
isTransformNotChanged_ = true;
getCacheNanoTime_ = nanoTimestamp;
prePaintRect_ = rect;
return localRevertMatrix_;
return cacheMatrixInfo_;
}
// cache valid
return localRevertMatrix_;
return cacheMatrixInfo_;
}
// apply the matrix to the given point specified by dst
@ -5953,4 +5967,27 @@ bool FrameNode::IsDebugInspectorId()
auto debugInspectorId = SystemProperties::GetDebugInspectorId();
return debugInspectorId == GetInspectorId().value_or("");
}
RefPtr<UINode> FrameNode::GetCurrentPageRootNode()
{
auto pageNode = GetPageNode();
CHECK_NULL_RETURN(pageNode, nullptr);
auto jsView = pageNode->GetChildAtIndex(0);
CHECK_NULL_RETURN(jsView, nullptr);
auto rootNode = jsView->GetChildAtIndex(0);
CHECK_NULL_RETURN(rootNode, nullptr);
return rootNode;
}
std::list<RefPtr<FrameNode>> FrameNode::GetActiveChildren()
{
std::list<RefPtr<FrameNode>> list;
for (int32_t i = 0; i < TotalChildCount(); i++) {
auto child = GetFrameNodeChildByIndex(i, false, false);
if (child->IsActive()) {
list.emplace_back(Referenced::Claim(child));
}
}
return list;
}
} // namespace OHOS::Ace::NG

View File

@ -78,6 +78,12 @@ struct CacheVisibleRectResult {
RectF innerBoundaryRect = RectF();
};
struct CacheMatrixInfo {
Matrix4 revertMatrix = Matrix4::CreateIdentity();
Matrix4 localMatrix = Matrix4::CreateIdentity();
RectF paintRectWithTransform;
};
// FrameNode will display rendering region in the screen.
class ACE_FORCE_EXPORT FrameNode : public UINode, public LayoutWrapper {
DECLARE_ACE_TYPE(FrameNode, UINode, LayoutWrapper);
@ -536,7 +542,8 @@ public:
void AddHotZoneRect(const DimensionRect& hotZoneRect) const;
void RemoveLastHotZoneRect() const;
virtual bool IsOutOfTouchTestRegion(const PointF& parentLocalPoint, const TouchEvent& touchEvent);
virtual bool IsOutOfTouchTestRegion(const PointF& parentLocalPoint, const TouchEvent& touchEvent,
std::vector<RectF>* regionList = nullptr);
bool IsLayoutDirtyMarked() const
{
@ -979,7 +986,7 @@ public:
// this flag will be used to refresh the transform matrix cache if it's dirty
void NotifyTransformInfoChanged()
{
isLocalRevertMatrixAvailable_ = false;
isTransformNotChanged_ = false;
}
void AddPredictLayoutNode(const RefPtr<FrameNode>& node)
@ -1025,7 +1032,7 @@ public:
// this method will check the cache state and return the cached revert matrix preferentially,
// but the caller can pass in true to forcible refresh the cache
Matrix4& GetOrRefreshRevertMatrixFromCache(bool forceRefresh = false);
CacheMatrixInfo& GetOrRefreshMatrixFromCache(bool forceRefresh = false);
// apply the matrix to the given point specified by dst
static void MapPointTo(PointF& dst, Matrix4& matrix);
@ -1123,6 +1130,10 @@ public:
return exposeInnerGestureFlag_;
}
RefPtr<UINode> GetCurrentPageRootNode();
std::list<RefPtr<FrameNode>> GetActiveChildren();
protected:
void DumpInfo() override;
std::unordered_map<std::string, std::function<void()>> destroyCallbacksMap_;
@ -1347,9 +1358,9 @@ private:
std::map<std::string, RefPtr<NodeAnimatablePropertyBase>> nodeAnimatablePropertyMap_;
Matrix4 localMat_ = Matrix4::CreateIdentity();
// this is just used for the hit test process of event handling, do not used for other purpose
Matrix4 localRevertMatrix_ = Matrix4::CreateIdentity();
CacheMatrixInfo cacheMatrixInfo_;
// control the localMat_ and localRevertMatrix_ available or not, set to false when any transform info is set
bool isLocalRevertMatrixAvailable_ = false;
bool isTransformNotChanged_ = false;
bool isFind_ = false;
bool isRestoreInfoUsed_ = false;

View File

@ -1631,6 +1631,9 @@ void ViewAbstract::SetPosition(const OffsetT<Dimension>& value)
if (!ViewStackProcessor::GetInstance()->IsCurrentVisualStateProcess()) {
return;
}
auto frameNode = ViewStackProcessor::GetInstance()->GetMainFrameNode();
CHECK_NULL_VOID(frameNode);
CheckIfParentNeedMarkDirty(frameNode);
ACE_RESET_RENDER_CONTEXT(RenderContext, PositionEdges);
ACE_UPDATE_RENDER_CONTEXT(Position, value);
}
@ -1640,10 +1643,30 @@ void ViewAbstract::SetPositionEdges(const EdgesParam& value)
if (!ViewStackProcessor::GetInstance()->IsCurrentVisualStateProcess()) {
return;
}
auto frameNode = ViewStackProcessor::GetInstance()->GetMainFrameNode();
CHECK_NULL_VOID(frameNode);
CheckIfParentNeedMarkDirty(frameNode);
ACE_RESET_RENDER_CONTEXT(RenderContext, Position);
ACE_UPDATE_RENDER_CONTEXT(PositionEdges, value);
}
void ViewAbstract::CheckIfParentNeedMarkDirty(FrameNode* frameNode)
{
CHECK_NULL_VOID(frameNode);
auto parentNode = frameNode->GetAncestorNodeOfFrame();
CHECK_NULL_VOID(parentNode);
// Row/Column/Flex measure and layout differently depending on whether the child nodes have position property,
// need to remeasure in the dynamic switch scenario.
if (parentNode->GetTag() == V2::COLUMN_ETS_TAG || parentNode->GetTag() == V2::ROW_ETS_TAG ||
parentNode->GetTag() == V2::FLEX_ETS_TAG) {
auto renderContext = frameNode->GetRenderContext();
CHECK_NULL_VOID(renderContext);
if (!renderContext->HasPositionEdges() || !renderContext->HasPosition()) {
parentNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
}
}
}
void ViewAbstract::SetOffset(const OffsetT<Dimension>& value)
{
if (!ViewStackProcessor::GetInstance()->IsCurrentVisualStateProcess()) {
@ -1992,15 +2015,8 @@ void ViewAbstract::BindMenuWithCustomNode(std::function<void()>&& buildFunc, con
}
if (menuParam.type == MenuType::MENU && expandDisplay && menuParam.isShowInSubWindow &&
targetNode->GetTag() != V2::SELECT_ETS_TAG) {
bool isShown = SubwindowManager::GetInstance()->GetShown();
if (!isShown) {
SubwindowManager::GetInstance()->ShowMenuNG(
std::move(buildFunc), std::move(previewBuildFunc), menuParam, targetNode, offset);
} else {
auto menuNode = overlayManager->GetMenuNode(targetNode->GetId());
TAG_LOGI(AceLogTag::ACE_MENU, "will hide menu, tagetNode id %{public}d.", targetNode->GetId());
SubwindowManager::GetInstance()->HideMenuNG(menuNode, targetNode->GetId());
}
SubwindowManager::GetInstance()->ShowMenuNG(
std::move(buildFunc), std::move(previewBuildFunc), menuParam, targetNode, offset);
return;
}
NG::ScopedViewStackProcessor builderViewStackProcessor;
@ -2945,6 +2961,7 @@ void ViewAbstract::ClearWidthOrHeight(FrameNode* frameNode, bool isWidth)
void ViewAbstract::SetPosition(FrameNode* frameNode, const OffsetT<Dimension>& value)
{
CHECK_NULL_VOID(frameNode);
CheckIfParentNeedMarkDirty(frameNode);
ACE_RESET_NODE_RENDER_CONTEXT(RenderContext, PositionEdges, frameNode);
ACE_UPDATE_NODE_RENDER_CONTEXT(Position, value, frameNode);
}
@ -2952,6 +2969,7 @@ void ViewAbstract::SetPosition(FrameNode* frameNode, const OffsetT<Dimension>& v
void ViewAbstract::SetPositionEdges(FrameNode* frameNode, const EdgesParam& value)
{
CHECK_NULL_VOID(frameNode);
CheckIfParentNeedMarkDirty(frameNode);
ACE_RESET_NODE_RENDER_CONTEXT(RenderContext, Position, frameNode);
ACE_UPDATE_NODE_RENDER_CONTEXT(PositionEdges, value, frameNode);
}
@ -3153,6 +3171,7 @@ void ViewAbstract::SetUseEffect(FrameNode* frameNode, bool useEffect, EffectType
void ViewAbstract::SetForegroundColor(FrameNode* frameNode, const Color& color)
{
auto renderContext = frameNode->GetRenderContext();
CHECK_NULL_VOID(renderContext);
if (renderContext->GetForegroundColorStrategy().has_value()) {
renderContext->UpdateForegroundColorStrategy(ForegroundColorStrategy::NONE);
renderContext->ResetForegroundColorStrategy();
@ -3951,6 +3970,12 @@ void ViewAbstract::SetNeedFocus(FrameNode* frameNode, bool value)
if (value) {
focusHub->RequestFocus();
} else {
if (!frameNode->IsOnMainTree()) {
TAG_LOGW(AceLogTag::ACE_FOCUS,
"Can't find Node %{public}s/%{public}d on tree, please check the timing of the function call.",
frameNode->GetTag().c_str(), frameNode->GetId());
return;
}
focusHub->LostFocusToViewRoot();
}
}
@ -4391,7 +4416,7 @@ Alignment ViewAbstract::GetAlign(FrameNode *frameNode)
Dimension ViewAbstract::GetWidth(FrameNode* frameNode)
{
Dimension value = Dimension(-1.0f);
Dimension value = Dimension(0.0f);
const auto& layoutProperty = frameNode->GetLayoutProperty();
CHECK_NULL_RETURN(layoutProperty, value);
const auto& property = layoutProperty->GetCalcLayoutConstraint();
@ -4408,7 +4433,7 @@ Dimension ViewAbstract::GetWidth(FrameNode* frameNode)
Dimension ViewAbstract::GetHeight(FrameNode* frameNode)
{
Dimension value = Dimension(-1.0f);
Dimension value = Dimension(0.0f);
const auto& layoutProperty = frameNode->GetLayoutProperty();
CHECK_NULL_RETURN(layoutProperty, value);
const auto& property = layoutProperty->GetCalcLayoutConstraint();

Some files were not shown because too many files have changed in this diff Show More