mirror of
https://gitee.com/openharmony/arkui_ace_engine
synced 2025-02-18 19:59:56 +00:00
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:
commit
093a3092bf
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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_);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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_;
|
||||
|
22
adapter/ohos/osal/thread_priority.cpp
Normal file
22
adapter/ohos/osal/thread_priority.cpp
Normal 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
|
@ -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",
|
||||
|
@ -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;
|
||||
}
|
||||
|
22
adapter/preview/osal/thread_priority.cpp
Normal file
22
adapter/preview/osal/thread_priority.cpp
Normal 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
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
})
|
||||
}
|
||||
|
@ -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 |
@ -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 |
@ -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 components–those 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'
|
||||
})
|
||||
}
|
||||
}
|
@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 components–those 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'
|
||||
})
|
||||
}
|
||||
}
|
@ -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 components–those 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,
|
||||
|
@ -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 })
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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_);
|
||||
|
32
frameworks/base/thread/thread_priority.h
Normal file
32
frameworks/base/thread/thread_priority.h
Normal 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
|
@ -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_;
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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])
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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}`);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
@ -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';
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ class RangeEdge {
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
class RatioRange {
|
||||
readonly start: RangeEdge;
|
||||
readonly end: RangeEdge;
|
||||
|
@ -13,4 +13,5 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
type ScrollDirection = 'UP' | 'DOWN' | 'UNKNOWN';
|
||||
|
@ -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",
|
||||
|
@ -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() {
|
||||
|
@ -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
@ -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();
|
||||
|
@ -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])
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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)) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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=')) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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_;
|
||||
|
@ -111,6 +111,7 @@ enum class AccessibilityEventType : size_t {
|
||||
PAGE_CLOSE = 0x08000000,
|
||||
ANNOUNCE_FOR_ACCESSIBILITY = 0x10000000,
|
||||
PAGE_OPEN = 0x20000000,
|
||||
ELEMENT_INFO_CHANGE = 0x40000000,
|
||||
UNKNOWN,
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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_;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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_;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -127,6 +127,8 @@ public:
|
||||
virtual void CloseAllSwipeActions(OnFinishFunc&& onFinishCallback) {}
|
||||
|
||||
virtual void SetObserver(const ScrollerObserver& observer) {}
|
||||
|
||||
virtual void StopAnimate() {}
|
||||
};
|
||||
} // namespace OHOS::Ace
|
||||
|
||||
|
@ -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
|
||||
|
@ -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_;
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user