/* * Copyright (c) 2021-2022 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 "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_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/pattern/grid/grid_model_ng.h" #include "core/components_v2/grid/grid_event.h" namespace OHOS::Ace { std::unique_ptr GridModel::instance_ = nullptr; GridModel* GridModel::GetInstance() { 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 }; } // 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) { 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); } void JSGrid::PopGrid(const JSCallbackInfo& /*info*/) { GridModel::GetInstance()->Pop(); } void JSGrid::UseProxy(const JSCallbackInfo& args) { auto parentGrid = ViewStackProcessor::GetInstance()->GetTopGrid(); if (parentGrid == nullptr) { LOGE("no parent Grid"); } // 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 LOGD("parent Grid uses proxied code path %{public}s.", (parentGrid ? !parentGrid->UseNonProxiedCodePath() ? "yes" : "false" : "no parent grid (error)")); args.SetReturnValue(JSRef::Make(ToJSValue(parentGrid ? !parentGrid->UseNonProxiedCodePath() : false))); } 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) { LOGE("The arg is wrong, it is supposed to have at least 1 argument"); return; } Dimension 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) { LOGE("The arg is wrong, it is supposed to have at least 1 argument"); return; } Dimension 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) { LOGE("The arg is wrong, it is supposed to have at least 1 argument"); return; } Dimension value; if (!ParseJsDimensionVp(info[0], value)) { LOGE("parse height fail for grid, please check."); return; } if (LessNotEqual(value.Value(), 0.0)) { value.SetValue(0.0); } GridModel::GetInstance()->SetGridHeight(value); } void JSGrid::JsOnScrollIndex(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { LOGE("param not valid, need function"); 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)); } void JSGrid::JSBind(BindingTarget globalObj) { LOGD("JSGrid:Bind"); 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("onAppear", &JSInteractableView::JsOnAppear); 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("onScrollIndex", &JSGrid::JsOnScrollIndex); 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::Inherit(); JSClass::Inherit(); JSClass::Bind<>(globalObj); } void JSGrid::SetScrollBar(int32_t displayMode) { if (displayMode < 0 || displayMode >= static_cast(DISPLAY_MODE.size())) { LOGE("Param is not valid"); return; } GridModel::GetInstance()->SetScrollBarMode(displayMode); } void JSGrid::SetScrollBarColor(const std::string& color) { GridModel::GetInstance()->SetScrollBarColor(color); } void JSGrid::SetScrollBarWidth(const std::string& width) { GridModel::GetInstance()->SetScrollBarWidth(width); } void JSGrid::SetCachedCount(int32_t cachedCount) { GridModel::GetInstance()->SetCachedCount(cachedCount); } void JSGrid::SetEditMode(bool editMode) { GridModel::GetInstance()->SetEditable(editMode); } void JSGrid::SetMaxCount(double maxCount) { GridModel::GetInstance()->SetMaxCount(static_cast(maxCount)); } void JSGrid::SetMinCount(double minCount) { GridModel::GetInstance()->SetMinCount(static_cast(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(int32_t value) { if (value < 0 || value >= static_cast(EDGE_EFFECT.size())) { return; } GridModel::GetInstance()->SetEdgeEffect(EDGE_EFFECT[value]); } void JSGrid::SetLayoutDirection(int32_t value) { if (value < 0 || value >= static_cast(LAYOUT_DIRECTION.size())) { LOGE("Param is not valid"); return; } GridModel::GetInstance()->SetLayoutDirection(LAYOUT_DIRECTION[value]); } void JSGrid::SetDirection(const std::string& dir) { bool rightToLeft = false; if (dir == "Ltr") { rightToLeft = false; } else if (dir == "Rtl") { rightToLeft = true; } else { rightToLeft = AceApplicationInfo::GetInstance().IsRightToLeft(); } GridModel::GetInstance()->SetIsRTL(rightToLeft); } void JSGrid::JsOnGridDragEnter(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { LOGE("fail to bind onItemDragEnter event due to info is not function"); 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()) { LOGE("fail to bind onItemDragMove event due to info is not function"); 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()) { LOGE("fail to bind onItemDragLeave event due to info is not function"); 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()) { LOGE("fail to bind onItemDragStart event due to info is not function"); return; } auto jsOnDragFunc = AceType::MakeRefPtr(JSRef::Cast(info[0])); auto onItemDragStart = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragFunc)]( 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()) { LOGE("builder param is not an object."); return; } auto builderObj = JSRef::Cast(ret); auto builder = builderObj->GetProperty("builder"); if (!builder->IsFunction()) { LOGE("builder param is not a function."); return; } auto builderFunc = AceType::MakeRefPtr(JSRef::Cast(builder)); CHECK_NULL_VOID(builderFunc); builderFunc->Execute(); }; GridModel::GetInstance()->SetOnItemDragStart(std::move(onItemDragStart)); } void JSGrid::JsOnGridDrop(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { LOGE("fail to bind onItemDrop event due to info is not function"); 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); }; GridModel::GetInstance()->SetOnItemDrop(std::move(onItemDrop)); } void JSGrid::SetMultiSelectable(bool multiSelectable) { GridModel::GetInstance()->SetMultiSelectable(multiSelectable); } } // namespace OHOS::Ace::Framework