/* * 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. */ #include "bridge/declarative_frontend/jsview/js_grid.h" #if !defined(PREVIEW) && defined(OHOS_PLATFORM) #include "interfaces/inner_api/ui_session/ui_session_manager.h" #endif #include "base/log/ace_scoring_log.h" #include "base/utils/utils.h" #include "bridge/declarative_frontend/engine/functions/js_drag_function.h" #include "bridge/declarative_frontend/jsview/js_interactable_view.h" #include "bridge/declarative_frontend/jsview/js_scrollable.h" #include "bridge/declarative_frontend/jsview/js_scroller.h" #include "bridge/declarative_frontend/jsview/js_view_common_def.h" #include "bridge/declarative_frontend/jsview/models/grid_model_impl.h" #include "core/common/ace_application_info.h" #include "core/common/container.h" #include "core/components_ng/base/view_stack_processor.h" #include "core/components_ng/pattern/grid/grid_model_ng.h" namespace OHOS::Ace { std::unique_ptr GridModel::instance_ = nullptr; std::mutex GridModel::mutex_; GridModel* GridModel::GetInstance() { if (!instance_) { std::lock_guard lock(mutex_); if (!instance_) { #ifdef NG_BUILD instance_.reset(new NG::GridModelNG()); #else if (Container::IsCurrentUseNewPipeline()) { instance_.reset(new NG::GridModelNG()); } else { instance_.reset(new Framework::GridModelImpl()); } #endif } } return instance_.get(); } } // namespace OHOS::Ace namespace OHOS::Ace::Framework { namespace { const std::vector DISPLAY_MODE = { DisplayMode::OFF, DisplayMode::AUTO, DisplayMode::ON }; const std::vector EDGE_EFFECT = { EdgeEffect::SPRING, EdgeEffect::FADE, EdgeEffect::NONE }; const std::vector LAYOUT_DIRECTION = { FlexDirection::ROW, FlexDirection::COLUMN, FlexDirection::ROW_REVERSE, FlexDirection::COLUMN_REVERSE }; const size_t GRID_ITEM_SIZE_RESULT_LENGTH = 2; const size_t GRID_ITEM_RECT_RESULT_LENGTH = 4; void ParseGridItemSize(const JSRef& value, GridItemSize& gridItemSize) { if (value->IsArray()) { JSRef array = JSRef::Cast(value); auto length = array->Length(); if (length != GRID_ITEM_SIZE_RESULT_LENGTH) { return; } JSRef rows = array->GetValueAt(0); if (rows->IsNumber()) { gridItemSize.rows = rows->ToNumber(); } JSRef columns = array->GetValueAt(1); if (columns->IsNumber()) { gridItemSize.columns = columns->ToNumber(); } } } void ParseGridItemRect(const JSRef& value, GridItemRect& gridItemRect) { if (value->IsArray()) { JSRef array = JSRef::Cast(value); auto length = array->Length(); if (length != GRID_ITEM_RECT_RESULT_LENGTH) { return; } JSRef rowStart = array->GetValueAt(GridItemRect::ROW_START); if (rowStart->IsNumber()) { gridItemRect.rowStart = rowStart->ToNumber(); } JSRef rowSpan = array->GetValueAt(GridItemRect::ROW_SPAN); if (rowSpan->IsNumber()) { gridItemRect.rowSpan = rowSpan->ToNumber(); } JSRef columnStart = array->GetValueAt(GridItemRect::COLUMN_START); if (columnStart->IsNumber()) { gridItemRect.columnStart = columnStart->ToNumber(); } JSRef columnSpan = array->GetValueAt(GridItemRect::COLUMN_SPAN); if (columnSpan->IsNumber()) { gridItemRect.columnSpan = columnSpan->ToNumber(); } } } void ParseGetGridItemSize(const JSCallbackInfo& info, JSRef& obj, GridLayoutOptions& option) { auto getSizeByIndex = obj->GetProperty("onGetIrregularSizeByIndex"); if (getSizeByIndex->IsFunction()) { auto onGetIrregularSizeByIndex = [execCtx = info.GetExecutionContext(), func = AceType::MakeRefPtr(JSRef(), JSRef::Cast(getSizeByIndex))](int32_t index) { GridItemSize gridItemSize; JSRef itemIndex = JSRef::Make(ToJSValue(index)); auto result = func->ExecuteJS(1, &itemIndex); if (!result->IsArray()) { return gridItemSize; } ParseGridItemSize(result, gridItemSize); return gridItemSize; }; option.getSizeByIndex = std::move(onGetIrregularSizeByIndex); } } void ParseGetGridItemRect(const JSCallbackInfo& info, JSRef& obj, GridLayoutOptions& option) { auto getRectByIndex = obj->GetProperty("onGetRectByIndex"); if (getRectByIndex->IsFunction()) { auto onGetRectByIndex = [execCtx = info.GetExecutionContext(), func = AceType::MakeRefPtr( JSRef(), JSRef::Cast(getRectByIndex))](int32_t index) { GridItemRect gridItemRect; JSRef itemIndex = JSRef::Make(ToJSValue(index)); auto result = func->ExecuteJS(1, &itemIndex); if (!result->IsArray()) { return gridItemRect; } ParseGridItemRect(result, gridItemRect); return gridItemRect; }; option.getRectByIndex = std::move(onGetRectByIndex); } } void SetGridLayoutOptions(const JSCallbackInfo& info) { if (!(info.Length() > 1 && info[1]->IsObject())) { return; } GridLayoutOptions option; auto obj = JSRef::Cast(info[1]); auto value = obj->GetProperty("regularSize"); ParseGridItemSize(value, option.regularSize); // only support regularSize(1, 1) option.regularSize.rows = 1; option.regularSize.columns = 1; auto indexes = obj->GetProperty("irregularIndexes"); if (indexes->IsArray()) { JSRef array = JSRef::Cast(indexes); auto length = array->Length(); for (size_t i = 0; i < length; i++) { JSRef index = array->GetValueAt(i); if (!index->IsNumber()) { continue; } auto indexNum = index->ToNumber(); if (indexNum >= 0) { option.irregularIndexes.emplace(indexNum); } } } ParseGetGridItemSize(info, obj, option); ParseGetGridItemRect(info, obj, option); GridModel::GetInstance()->SetLayoutOptions(option); } void JsOnScrollToIndex(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto onScrollIndex = [execCtx = info.GetExecutionContext(), func = JSRef::Cast(info[0])]( const BaseEventInfo* event) { JAVASCRIPT_EXECUTION_SCOPE(execCtx); const auto* eventInfo = TypeInfoHelper::DynamicCast(event); if (!eventInfo) { return; } auto params = ConvertToJSValues(eventInfo->GetScrollIndex()); func->Call(JSRef(), static_cast(params.size()), params.data()); }; GridModel::GetInstance()->SetOnScrollToIndex(std::move(onScrollIndex)); } } // namespace void JSGrid::Create(const JSCallbackInfo& info) { RefPtr positionController; RefPtr scrollBarProxy; if (info.Length() > 0 && info[0]->IsObject()) { JSScroller* jsScroller = JSRef::Cast(info[0])->Unwrap(); if (jsScroller) { jsScroller->SetInstanceId(Container::CurrentId()); positionController = GridModel::GetInstance()->CreatePositionController(); jsScroller->SetController(positionController); // Init scroll bar proxy. scrollBarProxy = jsScroller->GetScrollBarProxy(); if (!scrollBarProxy) { scrollBarProxy = GridModel::GetInstance()->CreateScrollBarProxy(); jsScroller->SetScrollBarProxy(scrollBarProxy); } } } GridModel::GetInstance()->Create(positionController, scrollBarProxy); SetGridLayoutOptions(info); } void JSGrid::PopGrid(const JSCallbackInfo& /* info */) { GridModel::GetInstance()->Pop(); } void JSGrid::UseProxy(const JSCallbackInfo& args) { #ifdef NG_BUILD args.SetReturnValue(JSRef::Make(ToJSValue(false))); #else auto parentGrid = ViewStackProcessor::GetInstance()->GetTopGrid(); // return true if code path for GridElement and its children will rely on // ElementProxy. Only in this case shallow render functionality can be used // see also GridLayoutComponent::CreateElement() and GridItemElementProxy class args.SetReturnValue(JSRef::Make(ToJSValue(parentGrid ? !parentGrid->UseNonProxiedCodePath() : false))); #endif } void JSGrid::SetColumnsTemplate(const std::string& value) { GridModel::GetInstance()->SetColumnsTemplate(value); } void JSGrid::SetRowsTemplate(const std::string& value) { GridModel::GetInstance()->SetRowsTemplate(value); } void JSGrid::SetColumnsGap(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } CalcDimension colGap; if (!ParseJsDimensionVp(info[0], colGap) || colGap.Value() < 0) { colGap.SetValue(0.0); } GridModel::GetInstance()->SetColumnsGap(colGap); } void JSGrid::SetRowsGap(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } CalcDimension rowGap; if (!ParseJsDimensionVp(info[0], rowGap) || rowGap.Value() < 0) { rowGap.SetValue(0.0); } GridModel::GetInstance()->SetRowsGap(rowGap); } void JSGrid::JsGridHeight(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } CalcDimension value; if (!ParseJsDimensionVp(info[0], value)) { return; } if (LessNotEqual(value.Value(), 0.0)) { value.SetValue(0.0); } GridModel::GetInstance()->SetGridHeight(value); } void JSGrid::JsOnScrollBarUpdate(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto onScrollBarUpdate = [execCtx = info.GetExecutionContext(), func = AceType::MakeRefPtr(JSRef(), JSRef::Cast(info[0]))](int32_t index, const Dimension& offset) { JSRef itemIndex = JSRef::Make(ToJSValue(index)); JSRef itemOffset = ConvertToJSValue(offset); JSRef params[2] = { itemIndex, itemOffset }; auto result = func->ExecuteJS(2, params); if (result->IsObject()) { JSRef obj = JSRef::Cast(result); Dimension totalOffset_; Dimension totalLength_; if (!ConvertFromJSValue(obj->GetProperty("totalOffset"), totalOffset_) || !ConvertFromJSValue(obj->GetProperty("totalLength"), totalLength_)) { return std::pair(0, 0); } else { return std::pair(totalOffset_.ConvertToPx(), totalLength_.ConvertToPx()); } } return std::pair(0, 0); }; GridModel::GetInstance()->SetOnScrollBarUpdate(std::move(onScrollBarUpdate)); } void JSGrid::SetScrollEnabled(const JSCallbackInfo& args) { GridModel::GetInstance()->SetScrollEnabled(args[0]->IsBoolean() ? args[0]->ToBoolean() : true); } void JSGrid::JSBind(BindingTarget globalObj) { JSClass::Declare("Grid"); MethodOptions opt = MethodOptions::NONE; JSClass::StaticMethod("create", &JSGrid::Create, opt); JSClass::StaticMethod("pop", &JSGrid::PopGrid, opt); JSClass::StaticMethod("willUseProxy", &JSGrid::UseProxy, opt); JSClass::StaticMethod("columnsTemplate", &JSGrid::SetColumnsTemplate, opt); JSClass::StaticMethod("rowsTemplate", &JSGrid::SetRowsTemplate, opt); JSClass::StaticMethod("columnsGap", &JSGrid::SetColumnsGap, opt); JSClass::StaticMethod("rowsGap", &JSGrid::SetRowsGap, opt); JSClass::StaticMethod("onClick", &JSInteractableView::JsOnClick); JSClass::StaticMethod("onAttach", &JSInteractableView::JsOnAttach); JSClass::StaticMethod("onAppear", &JSInteractableView::JsOnAppear); JSClass::StaticMethod("onDetach", &JSInteractableView::JsOnDetach); JSClass::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear); JSClass::StaticMethod("onTouch", &JSInteractableView::JsOnTouch); JSClass::StaticMethod("onHover", &JSInteractableView::JsOnHover); JSClass::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey); JSClass::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete); JSClass::StaticMethod("scrollBar", &JSGrid::SetScrollBar, opt); JSClass::StaticMethod("scrollBarWidth", &JSGrid::SetScrollBarWidth, opt); JSClass::StaticMethod("scrollBarColor", &JSGrid::SetScrollBarColor, opt); JSClass::StaticMethod("clip", &JSScrollable::JsClip); JSClass::StaticMethod("onScrollBarUpdate", &JSGrid::JsOnScrollBarUpdate); JSClass::StaticMethod("cachedCount", &JSGrid::SetCachedCount); JSClass::StaticMethod("editMode", &JSGrid::SetEditMode, opt); JSClass::StaticMethod("multiSelectable", &JSGrid::SetMultiSelectable, opt); JSClass::StaticMethod("maxCount", &JSGrid::SetMaxCount, opt); JSClass::StaticMethod("minCount", &JSGrid::SetMinCount, opt); JSClass::StaticMethod("cellLength", &JSGrid::CellLength, opt); JSClass::StaticMethod("layoutDirection", &JSGrid::SetLayoutDirection, opt); JSClass::StaticMethod("dragAnimation", &JSGrid::SetDragAnimation, opt); JSClass::StaticMethod("edgeEffect", &JSGrid::SetEdgeEffect, opt); JSClass::StaticMethod("direction", &JSGrid::SetDirection, opt); JSClass::StaticMethod("supportAnimation", &JSGrid::SetSupportAnimation, opt); JSClass::StaticMethod("onItemDragEnter", &JSGrid::JsOnGridDragEnter); JSClass::StaticMethod("onItemDragMove", &JSGrid::JsOnGridDragMove); JSClass::StaticMethod("onItemDragLeave", &JSGrid::JsOnGridDragLeave); JSClass::StaticMethod("onItemDragStart", &JSGrid::JsOnGridDragStart); JSClass::StaticMethod("height", &JSGrid::JsGridHeight); JSClass::StaticMethod("onItemDrop", &JSGrid::JsOnGridDrop); JSClass::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage); JSClass::StaticMethod("nestedScroll", &JSGrid::SetNestedScroll); JSClass::StaticMethod("enableScrollInteraction", &JSGrid::SetScrollEnabled); JSClass::StaticMethod("friction", &JSGrid::SetFriction); JSClass::StaticMethod("alignItems", &JSGrid::SetAlignItems); JSClass::StaticMethod("onScroll", &JSGrid::JsOnScroll); JSClass::StaticMethod("onReachStart", &JSGrid::JsOnReachStart); JSClass::StaticMethod("onReachEnd", &JSGrid::JsOnReachEnd); JSClass::StaticMethod("onScrollStart", &JSGrid::JsOnScrollStart); JSClass::StaticMethod("onScrollStop", &JSGrid::JsOnScrollStop); JSClass::StaticMethod("onScrollIndex", &JSGrid::JsOnScrollIndex); JSClass::StaticMethod("onScrollFrameBegin", &JSGrid::JsOnScrollFrameBegin); JSClass::InheritAndBind(globalObj); } void JSGrid::SetScrollBar(const JSCallbackInfo& info) { auto displayMode = JSScrollable::ParseDisplayMode(info, GridModel::GetInstance()->GetDisplayMode()); GridModel::GetInstance()->SetScrollBarMode(displayMode); } void JSGrid::SetScrollBarColor(const JSCallbackInfo& info) { auto scrollBarColor = JSScrollable::ParseBarColor(info); if (!scrollBarColor.empty()) { GridModel::GetInstance()->SetScrollBarColor(scrollBarColor); } } void JSGrid::SetScrollBarWidth(const JSCallbackInfo& scrollWidth) { auto scrollBarWidth = JSScrollable::ParseBarWidth(scrollWidth); if (!scrollBarWidth.empty()) { GridModel::GetInstance()->SetScrollBarWidth(scrollBarWidth); } } void JSGrid::SetCachedCount(const JSCallbackInfo& info) { int32_t cachedCount = 1; auto jsValue = info[0]; if (!jsValue->IsUndefined() && jsValue->IsNumber()) { cachedCount = jsValue->ToNumber(); if (cachedCount < 0) { cachedCount = 1; } } bool show = false; if (info.Length() > 1) { show = info[1]->ToBoolean(); } GridModel::GetInstance()->SetCachedCount(cachedCount, show); } void JSGrid::SetEditMode(const JSCallbackInfo& info) { // undefined means false to EditMode bool editMode = false; if (!info[0]->IsUndefined() && info[0]->IsBoolean()) { ParseJsBool(info[0], editMode); } GridModel::GetInstance()->SetEditable(editMode); } void JSGrid::SetMaxCount(const JSCallbackInfo& info) { int32_t maxCount = Infinity(); if (!info[0]->IsUndefined() && info[0]->IsNumber()) { ParseJsInt32(info[0], maxCount); if (maxCount < 1) { maxCount = Infinity(); } } GridModel::GetInstance()->SetMaxCount(maxCount); } void JSGrid::SetMinCount(const JSCallbackInfo& info) { int32_t minCount = 1; if (!info[0]->IsUndefined() && info[0]->IsNumber()) { ParseJsInt32(info[0], minCount); if (minCount < 1) { minCount = 1; } } GridModel::GetInstance()->SetMinCount(minCount); } void JSGrid::CellLength(int32_t cellLength) { GridModel::GetInstance()->SetCellLength(cellLength); } void JSGrid::SetSupportAnimation(bool supportAnimation) { GridModel::GetInstance()->SetSupportAnimation(supportAnimation); } void JSGrid::SetDragAnimation(bool value) { GridModel::GetInstance()->SetSupportDragAnimation(value); } void JSGrid::SetEdgeEffect(const JSCallbackInfo& info) { auto edgeEffect = EdgeEffect::NONE; if (info.Length() > 0) { edgeEffect = JSScrollable::ParseEdgeEffect(info[0], EdgeEffect::NONE); } auto alwaysEnabled = false; if (info.Length() > 1) { alwaysEnabled = JSScrollable::ParseAlwaysEnable(info[1], false); } GridModel::GetInstance()->SetEdgeEffect(edgeEffect, alwaysEnabled); } void JSGrid::SetLayoutDirection(int32_t value) { if (value < 0 || value >= static_cast(LAYOUT_DIRECTION.size())) { return; } GridModel::GetInstance()->SetLayoutDirection(LAYOUT_DIRECTION[value]); } void JSGrid::SetDirection(const std::string& dir) { TextDirection direction; if (dir == "Ltr") { direction = TextDirection::LTR; } else if (dir == "Rtl") { direction = TextDirection::RTL; } else { direction = TextDirection::AUTO; } GridModel::GetInstance()->SetIsRTL(direction); } void JSGrid::JsOnGridDragEnter(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } RefPtr jsOnDragEnterFunc = AceType::MakeRefPtr(JSRef::Cast(info[0])); auto onItemDragEnter = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragEnterFunc)]( const ItemDragInfo& dragInfo) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Grid.onItemDragEnter"); func->ItemDragEnterExecute(dragInfo); }; GridModel::GetInstance()->SetOnItemDragEnter(std::move(onItemDragEnter)); } void JSGrid::JsOnGridDragMove(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } RefPtr jsOnDragMoveFunc = AceType::MakeRefPtr(JSRef::Cast(info[0])); auto onItemDragMove = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragMoveFunc)]( const ItemDragInfo& dragInfo, int32_t itemIndex, int32_t insertIndex) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Grid.onItemDragMove"); func->ItemDragMoveExecute(dragInfo, itemIndex, insertIndex); }; GridModel::GetInstance()->SetOnItemDragMove(std::move(onItemDragMove)); } void JSGrid::JsOnGridDragLeave(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } RefPtr jsOnDragLeaveFunc = AceType::MakeRefPtr(JSRef::Cast(info[0])); auto onItemDragLeave = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragLeaveFunc)]( const ItemDragInfo& dragInfo, int32_t itemIndex) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Grid.onItemDragLeave"); func->ItemDragLeaveExecute(dragInfo, itemIndex); }; GridModel::GetInstance()->SetOnItemDragLeave(std::move(onItemDragLeave)); } void JSGrid::JsOnGridDragStart(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto jsOnDragFunc = AceType::MakeRefPtr(JSRef::Cast(info[0])); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onItemDragStart = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragFunc), node = targetNode]( const ItemDragInfo& dragInfo, int32_t itemIndex) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Grid.onItemDragStart"); auto ret = func->ItemDragStartExecute(dragInfo, itemIndex); if (!ret->IsObject()) { return; } auto builderObj = JSRef::Cast(ret); auto builder = builderObj->GetProperty("builder"); if (!builder->IsFunction()) { return; } auto builderFunc = AceType::MakeRefPtr(JSRef::Cast(builder)); CHECK_NULL_VOID(builderFunc); PipelineContext::SetCallBackNode(node); builderFunc->Execute(); }; GridModel::GetInstance()->SetOnItemDragStart(std::move(onItemDragStart)); } void JSGrid::JsOnGridDrop(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } RefPtr jsOnDropFunc = AceType::MakeRefPtr(JSRef::Cast(info[0])); auto onItemDrop = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDropFunc)]( const ItemDragInfo& dragInfo, int32_t itemIndex, int32_t insertIndex, bool isSuccess) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Grid.onItemDrop"); func->ItemDropExecute(dragInfo, itemIndex, insertIndex, isSuccess); #if !defined(PREVIEW) && defined(OHOS_PLATFORM) UiSessionManager::GetInstance().ReportComponentChangeEvent("event", "Grid.onItemDrop"); #endif }; GridModel::GetInstance()->SetOnItemDrop(std::move(onItemDrop)); } void JSGrid::SetMultiSelectable(bool multiSelectable) { GridModel::GetInstance()->SetMultiSelectable(multiSelectable); } void JSGrid::SetNestedScroll(const JSCallbackInfo& args) { NestedScrollOptions nestedOpt = { .forward = NestedScrollMode::SELF_ONLY, .backward = NestedScrollMode::SELF_ONLY, }; if (args.Length() < 1 || !args[0]->IsObject()) { GridModel::GetInstance()->SetNestedScroll(nestedOpt); return; } JSRef obj = JSRef::Cast(args[0]); int32_t froward = 0; JSViewAbstract::ParseJsInt32(obj->GetProperty("scrollForward"), froward); if (froward < static_cast(NestedScrollMode::SELF_ONLY) || froward > static_cast(NestedScrollMode::PARALLEL)) { froward = 0; } int32_t backward = 0; JSViewAbstract::ParseJsInt32(obj->GetProperty("scrollBackward"), backward); if (backward < static_cast(NestedScrollMode::SELF_ONLY) || backward > static_cast(NestedScrollMode::PARALLEL)) { backward = 0; } nestedOpt.forward = static_cast(froward); nestedOpt.backward = static_cast(backward); GridModel::GetInstance()->SetNestedScroll(nestedOpt); args.ReturnSelf(); } void JSGrid::SetFriction(const JSCallbackInfo& info) { double friction = -1.0; if (!JSViewAbstract::ParseJsDouble(info[0], friction)) { friction = -1.0; } GridModel::GetInstance()->SetFriction(friction); } void JSGrid::SetAlignItems(const JSCallbackInfo& info) { if (info.Length() < 1) { GridModel::GetInstance()->SetAlignItems(GridItemAlignment::DEFAULT); return; } if (info[0]->IsNumber()) { auto itemAlign = static_cast(info[0]->ToNumber()); if (itemAlign < GridItemAlignment::DEFAULT || itemAlign > GridItemAlignment::STRETCH) { itemAlign = GridItemAlignment::DEFAULT; } GridModel::GetInstance()->SetAlignItems(itemAlign); } else { GridModel::GetInstance()->SetAlignItems(GridItemAlignment::DEFAULT); } } void JSGrid::JsOnScroll(const JSCallbackInfo& args) { if (args[0]->IsFunction()) { auto onScroll = [execCtx = args.GetExecutionContext(), func = JSRef::Cast(args[0])]( const CalcDimension& scrollOffset, const ScrollState& scrollState) { auto params = ConvertToJSValues(scrollOffset, scrollState); func->Call(JSRef(), params.size(), params.data()); return; }; GridModel::GetInstance()->SetOnScroll(std::move(onScroll)); } args.ReturnSelf(); } void JSGrid::JsOnScrollStart(const JSCallbackInfo& args) { if (args[0]->IsFunction()) { auto onScrollStart = [execCtx = args.GetExecutionContext(), func = JSRef::Cast(args[0])]() { func->Call(JSRef()); return; }; GridModel::GetInstance()->SetOnScrollStart(std::move(onScrollStart)); } args.ReturnSelf(); } void JSGrid::JsOnScrollStop(const JSCallbackInfo& args) { if (args[0]->IsFunction()) { auto onScrollStop = [execCtx = args.GetExecutionContext(), func = JSRef::Cast(args[0])]() { func->Call(JSRef()); #if !defined(PREVIEW) && defined(OHOS_PLATFORM) UiSessionManager::GetInstance().ReportComponentChangeEvent("event", "Grid.onScrollStop"); #endif return; }; GridModel::GetInstance()->SetOnScrollStop(std::move(onScrollStop)); } args.ReturnSelf(); } void JSGrid::JsOnScrollIndex(const JSCallbackInfo& args) { if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) { JsOnScrollToIndex(args); return; } if (args[0]->IsFunction()) { auto onScrollIndex = [execCtx = args.GetExecutionContext(), func = JSRef::Cast(args[0])]( const int32_t first, const int32_t last) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); auto params = ConvertToJSValues(first, last); func->Call(JSRef(), params.size(), params.data()); return; }; GridModel::GetInstance()->SetOnScrollIndex(std::move(onScrollIndex)); } args.ReturnSelf(); } void JSGrid::JsOnScrollFrameBegin(const JSCallbackInfo& args) { if (args[0]->IsFunction()) { auto onScrollBegin = [execCtx = args.GetExecutionContext(), func = JSRef::Cast(args[0])]( const Dimension& offset, const ScrollState& state) -> ScrollFrameResult { ScrollFrameResult scrollRes { .offset = offset }; JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, scrollRes); auto params = ConvertToJSValues(offset, state); auto result = func->Call(JSRef(), params.size(), params.data()); if (result.IsEmpty()) { return scrollRes; } if (!result->IsObject()) { return scrollRes; } auto resObj = JSRef::Cast(result); auto dxRemainValue = resObj->GetProperty("offsetRemain"); if (dxRemainValue->IsNumber()) { scrollRes.offset = Dimension(dxRemainValue->ToNumber(), DimensionUnit::VP); } return scrollRes; }; GridModel::GetInstance()->SetOnScrollFrameBegin(std::move(onScrollBegin)); } } void JSGrid::JsOnReachStart(const JSCallbackInfo& args) { if (args[0]->IsFunction()) { auto onReachStart = [execCtx = args.GetExecutionContext(), func = JSRef::Cast(args[0])]() { func->Call(JSRef()); #if !defined(PREVIEW) && defined(OHOS_PLATFORM) UiSessionManager::GetInstance().ReportComponentChangeEvent("event", "Grid.onReachStart"); #endif return; }; GridModel::GetInstance()->SetOnReachStart(std::move(onReachStart)); } args.ReturnSelf(); } void JSGrid::JsOnReachEnd(const JSCallbackInfo& args) { if (args[0]->IsFunction()) { auto onReachEnd = [execCtx = args.GetExecutionContext(), func = JSRef::Cast(args[0])]() { func->Call(JSRef()); #if !defined(PREVIEW) && defined(OHOS_PLATFORM) UiSessionManager::GetInstance().ReportComponentChangeEvent("event", "Grid.onReachEnd"); #endif return; }; GridModel::GetInstance()->SetOnReachEnd(std::move(onReachEnd)); } args.ReturnSelf(); } } // namespace OHOS::Ace::Framework