* Copyright (c) 2021-2026 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"
#include "interfaces/inner_api/ui_session/ui_session_manager.h"
#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_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_v2/grid/grid_event.h"
#include "core/components_ng/base/view_stack_model.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> GridModel::instance_ = nullptr;
std::mutex GridModel::mutex_;
GridModel* GridModel::GetInstance()
{
if (!instance_) {
std::lock_guard<std::mutex> 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::Framework {
namespace {
const std::vector<DisplayMode> DISPLAY_MODE = { DisplayMode::OFF, DisplayMode::AUTO, DisplayMode::ON };
const std::vector<EdgeEffect> EDGE_EFFECT = { EdgeEffect::SPRING, EdgeEffect::FADE, EdgeEffect::NONE };
const std::vector<FlexDirection> 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<JSVal>& value, GridItemSize& gridItemSize)
{
if (value->IsArray()) {
JSRef<JSArray> array = JSRef<JSArray>::Cast(value);
auto length = array->Length();
if (length != GRID_ITEM_SIZE_RESULT_LENGTH) {
return;
}
JSRef<JSVal> rows = array->GetValueAt(0);
if (rows->IsNumber()) {
gridItemSize.rows = rows->ToNumber<int32_t>();
}
JSRef<JSVal> columns = array->GetValueAt(1);
if (columns->IsNumber()) {
gridItemSize.columns = columns->ToNumber<int32_t>();
}
}
}
void ParseGridItemRect(const JSRef<JSVal>& value, GridItemRect& gridItemRect)
{
if (value->IsArray()) {
JSRef<JSArray> array = JSRef<JSArray>::Cast(value);
auto length = array->Length();
if (length != GRID_ITEM_RECT_RESULT_LENGTH) {
return;
}
JSRef<JSVal> rowStart = array->GetValueAt(GridItemRect::ROW_START);
if (rowStart->IsNumber()) {
gridItemRect.rowStart = rowStart->ToNumber<int32_t>();
}
JSRef<JSVal> rowSpan = array->GetValueAt(GridItemRect::ROW_SPAN);
if (rowSpan->IsNumber()) {
gridItemRect.rowSpan = rowSpan->ToNumber<int32_t>();
}
JSRef<JSVal> columnStart = array->GetValueAt(GridItemRect::COLUMN_START);
if (columnStart->IsNumber()) {
gridItemRect.columnStart = columnStart->ToNumber<int32_t>();
}
JSRef<JSVal> columnSpan = array->GetValueAt(GridItemRect::COLUMN_SPAN);
if (columnSpan->IsNumber()) {
gridItemRect.columnSpan = columnSpan->ToNumber<int32_t>();
}
}
}
void ParseGetGridItemSize(const JSCallbackInfo& info, JSRef<JSObject>& obj, GridLayoutOptions& option)
{
auto getSizeByIndex = obj->GetProperty("onGetIrregularSizeByIndex");
if (getSizeByIndex->IsFunction()) {
auto onGetIrregularSizeByIndex = [execCtx = info.GetExecutionContext(),
func = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(),
JSRef<JSFunc>::Cast(getSizeByIndex))](int32_t index) {
JAVASCRIPT_EXECUTION_SCOPE(execCtx);
GridItemSize gridItemSize;
JSRef<JSVal> itemIndex = JSRef<JSVal>::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 ParseGetStartIndexByOffset(const JSCallbackInfo& info, JSRef<JSObject>& obj, GridLayoutOptions& option)
{
auto getStartIndexByOffset = obj->GetProperty("onGetStartIndexByOffset");
if (getStartIndexByOffset->IsFunction()) {
auto onGetStartIndexByOffset = [execCtx = info.GetExecutionContext(),
func = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(),
JSRef<JSFunc>::Cast(getStartIndexByOffset))](float offset) {
JAVASCRIPT_EXECUTION_SCOPE(execCtx);
JSRef<JSVal> jsOffset = JSRef<JSVal>::Make(ToJSValue(Dimension(offset).ConvertToVp()));
auto result = func->ExecuteJS(1, &jsOffset);
int32_t startIndex_ = 0;
int32_t startLine_ = 0;
Dimension startOffset_;
Dimension totalOffset_;
if (result->IsObject()) {
JSRef<JSObject> obj = JSRef<JSObject>::Cast(result);
ConvertFromJSValue(obj->GetProperty("startIndex"), startIndex_);
ConvertFromJSValue(obj->GetProperty("startLine"), startLine_);
ConvertFromJSValue(obj->GetProperty("startOffset"), startOffset_);
ConvertFromJSValue(obj->GetProperty("totalOffset"), totalOffset_);
}
GridStartLineInfo startLineInfo { startIndex_, startLine_, startOffset_.ConvertToPx(),
totalOffset_.ConvertToPx() };
return startLineInfo;
};
option.getStartIndexByOffset = std::move(onGetStartIndexByOffset);
}
}
void ParseGetStartIndexByIndex(const JSCallbackInfo& info, JSRef<JSObject>& obj, GridLayoutOptions& option)
{
auto getStartIndexByIndex = obj->GetProperty("onGetStartIndexByIndex");
if (getStartIndexByIndex->IsFunction()) {
auto onGetStartIndexByIndex = [execCtx = info.GetExecutionContext(),
func = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(),
JSRef<JSFunc>::Cast(getStartIndexByIndex))](int32_t index) {
JAVASCRIPT_EXECUTION_SCOPE(execCtx);
JSRef<JSVal> itemIndex = JSRef<JSVal>::Make(ToJSValue(index));
auto result = func->ExecuteJS(1, &itemIndex);
int32_t startIndex_ = 0;
int32_t startLine_ = 0;
Dimension startOffset_;
Dimension totalOffset_;
if (result->IsObject()) {
JSRef<JSObject> obj = JSRef<JSObject>::Cast(result);
ConvertFromJSValue(obj->GetProperty("startIndex"), startIndex_);
ConvertFromJSValue(obj->GetProperty("startLine"), startLine_);
ConvertFromJSValue(obj->GetProperty("startOffset"), startOffset_);
ConvertFromJSValue(obj->GetProperty("totalOffset"), totalOffset_);
}
GridStartLineInfo startLineInfo { startIndex_, startLine_, startOffset_.ConvertToPx(),
totalOffset_.ConvertToPx() };
return startLineInfo;
};
option.getStartIndexByIndex = std::move(onGetStartIndexByIndex);
}
}
void ParseGetGridItemRect(const JSCallbackInfo& info, JSRef<JSObject>& obj, GridLayoutOptions& option)
{
auto getRectByIndex = obj->GetProperty("onGetRectByIndex");
if (getRectByIndex->IsFunction()) {
auto onGetRectByIndex = [execCtx = info.GetExecutionContext(),
func = AceType::MakeRefPtr<JsFunction>(
JSRef<JSObject>(), JSRef<JSFunc>::Cast(getRectByIndex))](int32_t index) {
JAVASCRIPT_EXECUTION_SCOPE(execCtx);
GridItemRect gridItemRect;
JSRef<JSVal> itemIndex = JSRef<JSVal>::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<JSObject>::Cast(info[1]);
auto value = obj->GetProperty("regularSize");
ParseGridItemSize(value, option.regularSize);
option.regularSize.rows = 1;
option.regularSize.columns = 1;
auto indexes = obj->GetProperty("irregularIndexes");
if (indexes->IsArray()) {
JSRef<JSArray> array = JSRef<JSArray>::Cast(indexes);
auto length = array->Length();
for (size_t i = 0; i < length; i++) {
JSRef<JSVal> index = array->GetValueAt(i);
if (!index->IsNumber()) {
continue;
}
auto indexNum = index->ToNumber<int32_t>();
if (indexNum >= 0) {
option.irregularIndexes.emplace(indexNum);
}
}
}
ParseGetGridItemSize(info, obj, option);
ParseGetGridItemRect(info, obj, option);
ParseGetStartIndexByOffset(info, obj, option);
ParseGetStartIndexByIndex(info, obj, option);
GridModel::GetInstance()->SetLayoutOptions(option);
}
void JsOnScrollToIndex(const JSCallbackInfo& info)
{
if (!info[0]->IsFunction()) {
return;
}
auto onScrollIndex = [execCtx = info.GetExecutionContext(), func = JSRef<JSFunc>::Cast(info[0])](
const BaseEventInfo* event) {
JAVASCRIPT_EXECUTION_SCOPE(execCtx);
const auto* eventInfo = TypeInfoHelper::DynamicCast<V2::GridEventInfo>(event);
if (!eventInfo) {
return;
}
auto params = ConvertToJSValues(eventInfo->GetScrollIndex());
func->Call(JSRef<JSObject>(), static_cast<int>(params.size()), params.data());
};
GridModel::GetInstance()->SetOnScrollToIndex(std::move(onScrollIndex));
}
}
void JSGrid::Create(const JSCallbackInfo& info)
{
RefPtr<ScrollControllerBase> positionController;
RefPtr<ScrollProxy> scrollBarProxy;
if (info.Length() > 0 && info[0]->IsObject()) {
JSScroller* jsScroller = JSRef<JSObject>::Cast(info[0])->Unwrap<JSScroller>();
if (jsScroller) {
jsScroller->SetInstanceId(Container::CurrentId());
positionController = GridModel::GetInstance()->CreatePositionController();
jsScroller->SetController(positionController);
scrollBarProxy = jsScroller->GetScrollBarProxy();
if (!scrollBarProxy) {
scrollBarProxy = GridModel::GetInstance()->CreateScrollBarProxy();
jsScroller->SetScrollBarProxy(scrollBarProxy);
}
}
}
GridModel::GetInstance()->Create(positionController, scrollBarProxy);
SetGridLayoutOptions(info);
}
void JSGrid::PopGrid()
{
if (ViewStackModel::GetInstance()->IsPrebuilding()) {
return ViewStackModel::GetInstance()->PushPrebuildCompCmd("[JSGrid][pop]", &JSGrid::PopGrid);
}
GridModel::GetInstance()->Pop();
}
void JSGrid::UseProxy(const JSCallbackInfo& args)
{
#ifdef NG_BUILD
args.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(false)));
#else
auto parentGrid = ViewStackProcessor::GetInstance()->GetTopGrid();
args.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(parentGrid ? !parentGrid->UseNonProxiedCodePath() : false)));
#endif
}
void JSGrid::SetColumnsTemplate(const JSCallbackInfo& info)
{
if (info.Length() < 1) {
return;
}
auto jsValue = info[0];
if (jsValue->IsObject()) {
JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(jsValue);
auto fillTypeParam = jsObj->GetProperty("fillType");
if (!fillTypeParam->IsNull()) {
auto type = JSScrollable::ParsePresetFillType(fillTypeParam);
if (type.has_value()) {
GridModel::GetInstance()->SetItemFillPolicy(type.value());
} else {
GridModel::GetInstance()->SetItemFillPolicy(PresetFillType::BREAKPOINT_DEFAULT);
}
} else {
GridModel::GetInstance()->SetItemFillPolicy(PresetFillType::BREAKPOINT_DEFAULT);
}
} else {
GridModel::GetInstance()->SetColumnsTemplate(jsValue->ToString());
}
}
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 (SystemProperties::ConfigChangePerform()) {
RefPtr<ResourceObject> resObj;
if (!JSViewAbstract::ParseJsDimensionVp(info[0], colGap, resObj) || colGap.Value() < 0) {
colGap.SetValue(0.0);
}
GridModel::GetInstance()->ParseResObjColumnsGap(resObj);
} else 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 (SystemProperties::ConfigChangePerform()) {
RefPtr<ResourceObject> resObj;
if (!JSViewAbstract::ParseJsDimensionVp(info[0], rowGap, resObj) || rowGap.Value() < 0) {
rowGap.SetValue(0.0);
}
GridModel::GetInstance()->ParseResObjRowsGap(resObj);
} else 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;
}
JSViewAbstract::JsHeight(info);
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<JsFunction>(JSRef<JSObject>(),
JSRef<JSFunc>::Cast(info[0]))](int32_t index, const Dimension& offset) {
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, std::pair<float, float>(0, 0));
JSRef<JSVal> itemIndex = JSRef<JSVal>::Make(ToJSValue(index));
JSRef<JSVal> itemOffset = ConvertToJSValue(offset);
JSRef<JSVal> params[2] = { itemIndex, itemOffset };
auto result = func->ExecuteJS(2, params);
if (result->IsObject()) {
JSRef<JSObject> obj = JSRef<JSObject>::Cast(result);
Dimension totalOffset_;
Dimension totalLength_;
if (!ConvertFromJSValue(obj->GetProperty("totalOffset"), totalOffset_) ||
!ConvertFromJSValue(obj->GetProperty("totalLength"), totalLength_)) {
return std::pair<float, float>(0, 0);
} else {
return std::pair<float, float>(totalOffset_.ConvertToPx(), totalLength_.ConvertToPx());
}
}
return std::pair<float, float>(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<JSGrid>::Declare("Grid");
MethodOptions opt = MethodOptions::NONE;
JSClass<JSGrid>::StaticMethod("create", &JSGrid::Create, opt);
JSClass<JSGrid>::StaticMethod("pop", &JSGrid::PopGrid, opt);
JSClass<JSGrid>::StaticMethod("willUseProxy", &JSGrid::UseProxy, opt);
JSClass<JSGrid>::StaticMethod("columnsTemplate", &JSGrid::SetColumnsTemplate, opt);
JSClass<JSGrid>::StaticMethod("rowsTemplate", &JSGrid::SetRowsTemplate, opt);
JSClass<JSGrid>::StaticMethod("columnsGap", &JSGrid::SetColumnsGap, opt);
JSClass<JSGrid>::StaticMethod("rowsGap", &JSGrid::SetRowsGap, opt);
JSClass<JSGrid>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
JSClass<JSGrid>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
JSClass<JSGrid>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
JSClass<JSGrid>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
JSClass<JSGrid>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
JSClass<JSGrid>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
JSClass<JSGrid>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
JSClass<JSGrid>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
JSClass<JSGrid>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
JSClass<JSGrid>::StaticMethod("scrollBar", &JSGrid::SetScrollBar, opt);
JSClass<JSGrid>::StaticMethod("scrollBarWidth", &JSGrid::SetScrollBarWidth, opt);
JSClass<JSGrid>::StaticMethod("scrollBarColor", &JSGrid::SetScrollBarColor, opt);
JSClass<JSGrid>::StaticMethod("clip", &JSScrollable::JsClip);
JSClass<JSGrid>::StaticMethod("onScrollBarUpdate", &JSGrid::JsOnScrollBarUpdate);
JSClass<JSGrid>::StaticMethod("cachedCount", &JSGrid::SetCachedCount);
JSClass<JSGrid>::StaticMethod("editMode", &JSGrid::SetEditMode, opt);
JSClass<JSGrid>::StaticMethod("editModeOptions", &JSGrid::SetEditModeOptions);
JSClass<JSGrid>::StaticMethod("enableEditMode", &JSGrid::JsEnableEditMode, opt);
JSClass<JSGrid>::StaticMethod("multiSelectable", &JSGrid::SetMultiSelectable, opt);
JSClass<JSGrid>::StaticMethod("maxCount", &JSGrid::SetMaxCount, opt);
JSClass<JSGrid>::StaticMethod("minCount", &JSGrid::SetMinCount, opt);
JSClass<JSGrid>::StaticMethod("cellLength", &JSGrid::CellLength, opt);
JSClass<JSGrid>::StaticMethod("layoutDirection", &JSGrid::SetLayoutDirection, opt);
JSClass<JSGrid>::StaticMethod("dragAnimation", &JSGrid::SetDragAnimation, opt);
JSClass<JSGrid>::StaticMethod("edgeEffect", &JSGrid::SetEdgeEffect, opt);
JSClass<JSGrid>::StaticMethod("direction", &JSGrid::SetDirection, opt);
JSClass<JSGrid>::StaticMethod("supportAnimation", &JSGrid::SetSupportAnimation, opt);
JSClass<JSGrid>::StaticMethod("onItemDragEnter", &JSGrid::JsOnGridDragEnter);
JSClass<JSGrid>::StaticMethod("onItemDragMove", &JSGrid::JsOnGridDragMove);
JSClass<JSGrid>::StaticMethod("onItemDragLeave", &JSGrid::JsOnGridDragLeave);
JSClass<JSGrid>::StaticMethod("onItemDragStart", &JSGrid::JsOnGridDragStart);
JSClass<JSGrid>::StaticMethod("height", &JSGrid::JsGridHeight);
JSClass<JSGrid>::StaticMethod("onItemDrop", &JSGrid::JsOnGridDrop);
JSClass<JSGrid>::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage);
JSClass<JSGrid>::StaticMethod("nestedScroll", &JSGrid::SetNestedScroll);
JSClass<JSGrid>::StaticMethod("enableScrollInteraction", &JSGrid::SetScrollEnabled);
JSClass<JSGrid>::StaticMethod("friction", &JSGrid::SetFriction);
JSClass<JSGrid>::StaticMethod("focusWrapMode", &JSGrid::SetFocusWrapMode);
JSClass<JSGrid>::StaticMethod("alignItems", &JSGrid::SetAlignItems);
JSClass<JSGrid>::StaticMethod("syncLoad", &JSGrid::SetSyncLoad);
JSClass<JSGrid>::StaticMethod("supportEmptyBranchInLazyLoading", &JSGrid::SetSupportLazyLoadingEmptyBranch);
JSClass<JSGrid>::StaticMethod("onScroll", &JSGrid::JsOnScroll);
JSClass<JSGrid>::StaticMethod("onReachStart", &JSGrid::JsOnReachStart);
JSClass<JSGrid>::StaticMethod("onReachEnd", &JSGrid::JsOnReachEnd);
JSClass<JSGrid>::StaticMethod("onScrollStart", &JSGrid::JsOnScrollStart);
JSClass<JSGrid>::StaticMethod("onScrollStop", &JSGrid::JsOnScrollStop);
JSClass<JSGrid>::StaticMethod("onScrollIndex", &JSGrid::JsOnScrollIndex);
JSClass<JSGrid>::StaticMethod("onScrollFrameBegin", &JSGrid::JsOnScrollFrameBegin);
JSClass<JSGrid>::StaticMethod("onEditModeChange", &JSGrid::JsOnEditModeChange);
JSClass<JSGrid>::InheritAndBind<JSScrollableBase>(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)
{
Color color;
RefPtr<ResourceObject> resObj;
if (JSViewAbstract::ParseJsColor(info[0], color, resObj)) {
GridModel::GetInstance()->SetScrollBarColor(color);
} else {
GridModel::GetInstance()->SetScrollBarColor(std::nullopt);
}
if (SystemProperties::ConfigChangePerform()) {
GridModel::GetInstance()->CreateWithResourceObjScrollBarColor(resObj);
}
}
void JSGrid::SetScrollBarWidth(const JSCallbackInfo& scrollWidth)
{
RefPtr<ResourceObject> resObj;
auto scrollWidthValue = scrollWidth[0];
auto scrollBarWidth = JSScrollable::ParseBarWidth(scrollWidth, resObj);
if (!scrollBarWidth.empty()) {
GridModel::GetInstance()->SetScrollBarWidth(scrollBarWidth);
}
GridModel::GetInstance()->CreateWithResourceObjScrollBarWidth(resObj);
}
void JSGrid::SetCachedCount(const JSCallbackInfo& info)
{
int32_t cachedCount = 1;
auto jsValue = info[0];
if (!jsValue->IsUndefined() && jsValue->IsNumber()) {
cachedCount = jsValue->ToNumber<int32_t>();
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)
{
bool editMode = false;
if (!info[0]->IsUndefined() && info[0]->IsBoolean()) {
ParseJsBool(info[0], editMode);
}
GridModel::GetInstance()->SetEditable(editMode);
}
void JSGrid::SetEditModeOptions(const JSCallbackInfo& info)
{
NG::EditModeOptions options;
JSScrollable::ParseEditModeOptions(info, options);
GridModel::GetInstance()->SetEditModeOptions(options);
}
void JSGrid::JsEnableEditMode(const JSCallbackInfo& info)
{
if (info.Length() < 1) {
return;
}
bool enableEditMode = false;
JSRef<JSVal> changeEventVal;
auto enableVal = info[0];
if (enableVal->IsObject()) {
JSRef<JSObject> obj = JSRef<JSObject>::Cast(enableVal);
enableVal = obj->GetProperty("value");
changeEventVal = obj->GetProperty("$value");
} else if (info.Length() > 1) {
changeEventVal = info[1];
}
if (enableVal->IsBoolean()) {
enableEditMode = enableVal->ToBoolean();
}
GridModel::GetInstance()->SetEnableEditMode(enableEditMode);
if (changeEventVal->IsFunction()) {
auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(changeEventVal));
auto targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
auto changeEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
bool param) {
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
ACE_SCORING_EVENT("Grid.EnableEditModeChangeEvent");
auto newJSVal = JSRef<JSVal>::Make(ToJSValue(param));
PipelineContext::SetCallBackNode(node);
func->ExecuteJS(1, &newJSVal);
};
GridModel::GetInstance()->SetEnableEditModeBindingEvent(std::move(changeEvent));
}
}
void JSGrid::JsOnEditModeChange(const JSCallbackInfo& info)
{
if (info.Length() < 1 || !info[0]->IsFunction()) {
return;
}
auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
auto targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
auto changeEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc),
node = targetNode](bool param) {
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
ACE_SCORING_EVENT("Grid.OnEditModeChange");
auto newJSVal = JSRef<JSVal>::Make(ToJSValue(param));
PipelineContext::SetCallBackNode(node);
func->ExecuteJS(1, &newJSVal);
};
GridModel::GetInstance()->SetEnableEditModeChangeEvent(std::move(changeEvent));
}
void JSGrid::SetMaxCount(const JSCallbackInfo& info)
{
int32_t maxCount = Infinity<int32_t>();
if (!info[0]->IsUndefined() && info[0]->IsNumber()) {
ParseJsInt32(info[0], maxCount);
if (maxCount < 1) {
maxCount = Infinity<int32_t>();
}
}
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;
auto effectEdge = EffectEdge::ALL;
if (info.Length() > 0) {
edgeEffect = JSScrollable::ParseEdgeEffect(info[0], EdgeEffect::NONE);
}
auto alwaysEnabled = false;
if (info.Length() > 1) {
alwaysEnabled = JSScrollable::ParseAlwaysEnable(info[1], false);
effectEdge = JSScrollable::ParseEffectEdge(info[1]);
}
GridModel::GetInstance()->SetEdgeEffect(edgeEffect, alwaysEnabled, effectEdge);
}
void JSGrid::SetLayoutDirection(int32_t value)
{
if (value < 0 || value >= static_cast<int32_t>(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<JsDragFunction> jsOnDragEnterFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::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<JsDragFunction> jsOnDragMoveFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::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<JsDragFunction> jsOnDragLeaveFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::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<JsDragFunction>(JSRef<JSFunc>::Cast(info[0]));
WeakPtr<NG::FrameNode> 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<JSObject>::Cast(ret);
auto builder = builderObj->GetProperty("builder");
if (!builder->IsFunction()) {
return;
}
auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::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<JsDragFunction> jsOnDropFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::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);
UiSessionManager::GetInstance()->ReportComponentChangeEvent("event", "Grid.onItemDrop",
ComponentEventType::COMPONENT_EVENT_SCROLL);
};
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<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
int32_t froward = 0;
JSViewAbstract::ParseJsInt32(obj->GetProperty("scrollForward"), froward);
if (froward < static_cast<int32_t>(NestedScrollMode::SELF_ONLY) ||
froward > static_cast<int32_t>(NestedScrollMode::PARALLEL)) {
froward = 0;
}
int32_t backward = 0;
JSViewAbstract::ParseJsInt32(obj->GetProperty("scrollBackward"), backward);
if (backward < static_cast<int32_t>(NestedScrollMode::SELF_ONLY) ||
backward > static_cast<int32_t>(NestedScrollMode::PARALLEL)) {
backward = 0;
}
nestedOpt.forward = static_cast<NestedScrollMode>(froward);
nestedOpt.backward = static_cast<NestedScrollMode>(backward);
GridModel::GetInstance()->SetNestedScroll(nestedOpt);
args.ReturnSelf();
}
void JSGrid::SetFriction(const JSCallbackInfo& info)
{
double friction = -1.0;
RefPtr<ResourceObject> resObj;
if (!JSViewAbstract::ParseJsDouble(info[0], friction, resObj)) {
friction = -1.0;
}
if (SystemProperties::ConfigChangePerform()) {
GridModel::GetInstance()->CreateWithResourceObjFriction(resObj);
}
GridModel::GetInstance()->SetFriction(friction);
}
void JSGrid::SetFocusWrapMode(const JSCallbackInfo& args)
{
auto focusWrapMode = static_cast<int32_t>(FocusWrapMode::DEFAULT);
if (!JSViewAbstract::ParseJsInt32(args[0], focusWrapMode) ||
focusWrapMode < static_cast<int32_t>(FocusWrapMode::DEFAULT) ||
focusWrapMode > static_cast<int32_t>(FocusWrapMode::WRAP_WITH_ARROW)) {
focusWrapMode = static_cast<int32_t>(FocusWrapMode::DEFAULT);
}
GridModel::GetInstance()->SetFocusWrapMode(static_cast<FocusWrapMode>(focusWrapMode));
}
void JSGrid::SetAlignItems(const JSCallbackInfo& info)
{
if (info.Length() < 1) {
GridModel::GetInstance()->SetAlignItems(GridItemAlignment::DEFAULT);
return;
}
if (info[0]->IsNumber()) {
auto itemAlign = static_cast<GridItemAlignment>(info[0]->ToNumber<int32_t>());
if (itemAlign < GridItemAlignment::DEFAULT || itemAlign > GridItemAlignment::STRETCH) {
itemAlign = GridItemAlignment::DEFAULT;
}
GridModel::GetInstance()->SetAlignItems(itemAlign);
} else {
GridModel::GetInstance()->SetAlignItems(GridItemAlignment::DEFAULT);
}
}
void JSGrid::SetSyncLoad(const JSCallbackInfo& info)
{
bool syncLoad = true;
if (info.Length() >= 1) {
auto value = info[0];
if (value->IsBoolean()) {
syncLoad = value->ToBoolean();
}
}
GridModel::GetInstance()->SetSyncLoad(syncLoad);
}
* JS API definition: supportEmptyBranchInLazyLoading(supported: boolean | undefined): GridAttribute;
* supported: true - enable lazy loading for empty branch, false - disable lazy loading for empty branch
* if supported is undefined, disable lazy loading for empty branch
*/
void JSGrid::SetSupportLazyLoadingEmptyBranch(const JSCallbackInfo& info)
{
bool enable = false;
if (info.Length() == 1) {
auto value = info[0];
if (value->IsBoolean()) {
enable = value->ToBoolean();
}
}
GridModel::GetInstance()->SetSupportLazyLoadingEmptyBranch(enable);
}
void JSGrid::JsOnScroll(const JSCallbackInfo& args)
{
if (args[0]->IsFunction()) {
auto onScroll = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
const CalcDimension& scrollOffset, const ScrollState& scrollState) {
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
auto params = ConvertToJSValues(scrollOffset, scrollState);
func->Call(JSRef<JSObject>(), 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<JSFunc>::Cast(args[0])]() {
func->Call(JSRef<JSObject>());
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<JSFunc>::Cast(args[0])]() {
func->Call(JSRef<JSObject>());
UiSessionManager::GetInstance()->ReportComponentChangeEvent("event", "Grid.onScrollStop",
ComponentEventType::COMPONENT_EVENT_SCROLL);
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<JSFunc>::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<JSObject>(), 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<JSFunc>::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<JSObject>(), params.size(), params.data());
if (result.IsEmpty()) {
return scrollRes;
}
if (!result->IsObject()) {
return scrollRes;
}
auto resObj = JSRef<JSObject>::Cast(result);
auto dxRemainValue = resObj->GetProperty("offsetRemain");
if (dxRemainValue->IsNumber()) {
scrollRes.offset = Dimension(dxRemainValue->ToNumber<float>(), 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<JSFunc>::Cast(args[0])]() {
func->Call(JSRef<JSObject>());
UiSessionManager::GetInstance()->ReportComponentChangeEvent("event", "Grid.onReachStart",
ComponentEventType::COMPONENT_EVENT_SCROLL);
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<JSFunc>::Cast(args[0])]() {
func->Call(JSRef<JSObject>());
UiSessionManager::GetInstance()->ReportComponentChangeEvent("event", "Grid.onReachEnd",
ComponentEventType::COMPONENT_EVENT_SCROLL);
return;
};
GridModel::GetInstance()->SetOnReachEnd(std::move(onReachEnd));
}
args.ReturnSelf();
}
}