* Copyright (c) 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.
*/
#ifndef ARKUI_GRID_NODE_H
#define ARKUI_GRID_NODE_H
#include <cstdint>
#include "ArkUIGridItemNode.h"
#include "ArkUINodeExpandAdapter.h"
#include "manager.h"
#include "../common/ArkUINode.h"
using OnItemDragStart = std::function<void(float, float, int32_t)>;
using OnItemDragEnter = std::function<void(float, float)>;
using OnItemDragMove = std::function<void(float, float, int32_t, int32_t)>;
using OnItemDragLeave = std::function<void(float, float, int32_t)>;
using OnItemDrop = std::function<void(float, float, int32_t, int32_t, bool)>;
constexpr uint32_t COLOR_LIGHT_BLUE = 0xFF00FFFF;
constexpr uint32_t PARAM_0 = 0;
constexpr uint32_t PARAM_1 = 1;
constexpr uint32_t PARAM_2 = 2;
constexpr uint32_t PARAM_3 = 3;
class ArkUIGridNode : public BaseNode {
public:
ArkUIGridNode()
: BaseNode(NodeApiInstance::GetInstance()->GetNativeNodeAPI()->createNode(ARKUI_NODE_GRID)),
nodeApi_(NodeApiInstance::GetInstance()->GetNativeNodeAPI())
{
if (!IsNotNull(nodeApi_) || !IsNotNull(GetHandle())) {
return;
}
nodeApi_->addNodeEventReceiver(GetHandle(), StaticEventReceiver);
}
~ArkUIGridNode() override
{
if (!IsNotNull(nodeApi_)) {
return;
}
UnRegisterSpecificEvents();
ResetGridAdapter();
}
void SetLazyAdapter(const std::shared_ptr<ItemAdapter<ArkUIGridItemNode>>& adapter)
{
if (!IsNotNull(adapter)) {
return;
}
ArkUI_AttributeItem item { nullptr, 0, nullptr, adapter->GetAdapter() };
nodeApi_->setAttribute(GetHandle(), NODE_GRID_NODE_ADAPTER, &item);
nodeAdapter_ = adapter;
}
std::shared_ptr<ItemAdapter<ArkUIGridItemNode>> GetLazyAdapter()
{
return nodeAdapter_;
}
void SetGridColumnsGap(float gap)
{
ArkUI_NumberValue value { .f32 = gap };
ArkUI_AttributeItem item = { &value, 1 };
nodeApi_->setAttribute(GetHandle(), NODE_GRID_COLUMN_GAP, &item);
}
void SetGridRowsGap(float gap)
{
ArkUI_NumberValue value { .f32 = gap };
ArkUI_AttributeItem item = { &value, 1 };
nodeApi_->setAttribute(GetHandle(), NODE_GRID_ROW_GAP, &item);
}
int32_t SetGridSupportDragAnimation(bool support)
{
ArkUI_NumberValue value { .i32 = support ? 1 : 0 };
ArkUI_AttributeItem item { &value, 1 };
return nodeApi_->setAttribute(GetHandle(), NODE_GRID_DRAG_ANIMATION, &item);
}
int32_t SetGridEditMode(bool enable)
{
ArkUI_NumberValue value { .i32 = enable ? 1 : 0 };
ArkUI_AttributeItem item { &value, 1 };
return nodeApi_->setAttribute(GetHandle(), NODE_GRID_EDIT_MODE, &item);
}
int32_t SetGridColumnsTemplate(const std::string& str)
{
ArkUI_AttributeItem item = { .string = str.c_str() };
return nodeApi_->setAttribute(GetHandle(), NODE_GRID_COLUMN_TEMPLATE, &item);
}
int32_t SetBorderWidth(float borderWidth)
{
ArkUI_NumberValue value[] = { { .f32 = borderWidth }, { .f32 = borderWidth }, { .f32 = borderWidth },
{ .f32 = borderWidth } };
ArkUI_AttributeItem item = { value, 4 };
return nodeApi_->setAttribute(GetHandle(), NODE_BORDER_WIDTH, &item);
}
int32_t SetGridMultiSelectable(bool enable)
{
ArkUI_NumberValue value { .i32 = enable ? 1 : 0 };
ArkUI_AttributeItem item { &value, 1 };
return nodeApi_->setAttribute(GetHandle(), NODE_GRID_MULTI_SELECTABLE, &item);
}
int32_t ResetGridMultiSelectable()
{
return nodeApi_->resetAttribute(GetHandle(), NODE_GRID_MULTI_SELECTABLE);
}
int32_t GetGridMultiSelectable()
{
int32_t value = -1;
auto getValue = nodeApi_->getAttribute(GetHandle(), NODE_GRID_MULTI_SELECTABLE);
if (getValue) {
value = getValue->value->i32;
}
return value;
}
void SetSelectedStates(std::shared_ptr<ArkUIGridItemNode>& gridItem)
{
if (!gridItem) {
return;
}
int32_t selectedState = ArkUI_UIState::UI_STATE_SELECTED;
std::weak_ptr<ArkUIGridItemNode> weakComponent = gridItem;
static auto selectedStateCallback = [](int32_t currentStates, void* userData) {
auto compPtr = static_cast<ArkUIGridItemNode*>(userData);
if (!compPtr) {
return;
}
if (currentStates & ArkUI_UIState::UI_STATE_SELECTED) {
compPtr->SetBorderWidth(5);
compPtr->SetBorderColor(COLOR_LIGHT_BLUE, COLOR_LIGHT_BLUE, COLOR_LIGHT_BLUE, COLOR_LIGHT_BLUE);
} else {
compPtr->SetBorderWidth(0);
}
};
ArkUI_ErrorCode result = OH_ArkUI_AddSupportedUIStates(gridItem->GetHandle(), selectedState,
selectedStateCallback,
false,
gridItem.get());
if (result != 0) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "GridDragEventMaker",
"SetSelectedStates fail, errorCode:%{public}d", result);
}
}
void SetGridLayoutOptions(ArkUI_GridLayoutOptions* layoutOptions)
{
if (layoutOptions == nullptr) {
return;
}
ArkUI_AttributeItem item = { .object = layoutOptions };
nodeApi_->setAttribute(GetHandle(), NODE_GRID_LAYOUT_OPTIONS, &item);
}
void RegisterOnItemDragStart(OnItemDragStart onItemDragStart)
{
onItemDragStart_ = onItemDragStart;
if (!isOnItemDragStartEventRegistered_) {
nodeApi_->registerNodeEvent(GetHandle(), NODE_GRID_ON_ITEM_DRAG_START, 0, this);
isOnItemDragStartEventRegistered_ = true;
}
}
void RegisterOnItemDragMove(OnItemDragMove onItemDragMove)
{
onItemDragMove_ = onItemDragMove;
if (!isOnItemDragMoveEventRegistered_) {
nodeApi_->registerNodeEvent(GetHandle(), NODE_GRID_ON_ITEM_DRAG_MOVE, 0, this);
isOnItemDragMoveEventRegistered_ = true;
}
}
void RegisterOnItemDragEnter(OnItemDragEnter onItemDragEnter)
{
onItemDragEnter_ = onItemDragEnter;
if (!isOnItemDragEnterEventRegistered_) {
nodeApi_->registerNodeEvent(GetHandle(), NODE_GRID_ON_ITEM_DRAG_ENTER, 0, this);
isOnItemDragEnterEventRegistered_ = true;
}
}
void RegisterOnItemDragLeave(OnItemDragLeave onItemDragLeave)
{
onItemDragLeave_ = onItemDragLeave;
if (!isOnItemDragLeaveEventRegistered_) {
nodeApi_->registerNodeEvent(GetHandle(), NODE_GRID_ON_ITEM_DRAG_LEAVE, 0, this);
isOnItemDragLeaveEventRegistered_ = true;
}
}
void RegisterOnItemDrop(OnItemDrop onItemDrop)
{
onItemDrop_ = onItemDrop;
if (!isOnItemDropEventRegistered_) {
nodeApi_->registerNodeEvent(GetHandle(), NODE_GRID_ON_ITEM_DROP, 0, this);
isOnItemDropEventRegistered_ = true;
}
}
protected:
void OnNodeEvent(ArkUI_NodeEvent* event) override
{
BaseNode::OnNodeEvent(event);
HandleGridEvent(event);
}
void HandleGridEvent(ArkUI_NodeEvent* event)
{
int32_t eventType = OH_ArkUI_NodeEvent_GetEventType(event);
switch (eventType) {
case NODE_GRID_ON_ITEM_DRAG_START:
HandleOnDragStartEvent(event);
break;
case NODE_GRID_ON_ITEM_DRAG_MOVE:
HandleOnDragMoveEvent(event);
break;
case NODE_GRID_ON_ITEM_DRAG_LEAVE:
HandleOnDragLeaveEvent(event);
break;
case NODE_GRID_ON_ITEM_DRAG_ENTER:
HandleOnDragEnterEvent(event);
break;
case NODE_GRID_ON_ITEM_DROP:
HandleOnDropEvent(event);
break;
default:
break;
}
}
void HandleOnDragStartEvent(ArkUI_NodeEvent* event)
{
{
ArkUI_NumberValue drag_value[] = { { .i32 = 1 } };
auto ret = OH_ArkUI_NodeEvent_SetReturnNumberValue(event, drag_value, 1);
if (ret != 0) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, LOG_TAG,
"SetOnItemDragStart setReturnNumberValue ret: %{public}d", ret);
}
if (onItemDragStart_) {
ArkUI_NumberValue value[PARAM_3];
OH_ArkUI_NodeEvent_GetNumberValue(event, PARAM_0, value);
OH_ArkUI_NodeEvent_GetNumberValue(event, PARAM_1, value);
OH_ArkUI_NodeEvent_GetNumberValue(event, PARAM_2, value);
auto x = value[PARAM_0].f32;
auto y = value[PARAM_1].f32;
auto index = value[PARAM_2].i32;
onItemDragStart_(x, y, index);
}
}
}
void HandleOnDragMoveEvent(ArkUI_NodeEvent* event)
{
{
if (onItemDragMove_) {
ArkUI_NodeComponentEvent* result = OH_ArkUI_NodeEvent_GetNodeComponentEvent(event);
float x = result->data[0].f32;
float y = result->data[1].f32;
int32_t itemIndex = result->data[2].i32;
int32_t insertIndex = result->data[3].i32;
onItemDragMove_(x, y, itemIndex, insertIndex);
}
}
}
void HandleOnDragLeaveEvent(ArkUI_NodeEvent* event)
{
{
if (onItemDragLeave_) {
ArkUI_NodeComponentEvent* result = OH_ArkUI_NodeEvent_GetNodeComponentEvent(event);
float x = result->data[0].f32;
float y = result->data[1].f32;
int32_t itemIndex = result->data[2].i32;
onItemDragLeave_(x, y, itemIndex);
}
}
}
void HandleOnDragEnterEvent(ArkUI_NodeEvent* event)
{
{
if (onItemDragEnter_) {
ArkUI_NodeComponentEvent* result = OH_ArkUI_NodeEvent_GetNodeComponentEvent(event);
float x = result->data[0].f32;
float y = result->data[1].f32;
onItemDragEnter_(x, y);
}
}
}
void HandleOnDropEvent(ArkUI_NodeEvent* event)
{
{
if (onItemDrop_) {
ArkUI_NodeComponentEvent* result = OH_ArkUI_NodeEvent_GetNodeComponentEvent(event);
float x = result->data[0].f32;
float y = result->data[1].f32;
int32_t itemIndex = result->data[2].i32;
int32_t insertIndex = result->data[3].i32;
bool isSuccess = result->data[4].i32;
onItemDrop_(x, y, itemIndex, insertIndex, isSuccess);
}
}
}
private:
void UnRegisterSpecificEvents()
{
if (isOnItemDragStartEventRegistered_) {
nodeApi_->unregisterNodeEvent(GetHandle(), NODE_GRID_ON_ITEM_DRAG_START);
}
if (isOnItemDragMoveEventRegistered_) {
nodeApi_->unregisterNodeEvent(GetHandle(), NODE_GRID_ON_ITEM_DRAG_MOVE);
}
if (isOnItemDragEnterEventRegistered_) {
nodeApi_->unregisterNodeEvent(GetHandle(), NODE_GRID_ON_ITEM_DRAG_ENTER);
}
if (isOnItemDragLeaveEventRegistered_) {
nodeApi_->unregisterNodeEvent(GetHandle(), NODE_GRID_ON_ITEM_DRAG_LEAVE);
}
if (isOnItemDropEventRegistered_) {
nodeApi_->unregisterNodeEvent(GetHandle(), NODE_GRID_ON_ITEM_DROP);
}
}
void ResetGridAdapter()
{
if (IsNotNull(nodeAdapter_)) {
nodeApi_->resetAttribute(GetHandle(), NODE_GRID_NODE_ADAPTER);
nodeAdapter_.reset();
}
}
static std::shared_ptr<ArkUIGridNode> BuildGrid();
ArkUI_NativeNodeAPI_1* nodeApi_ = nullptr;
std::shared_ptr<ItemAdapter<ArkUIGridItemNode>> nodeAdapter_;
OnItemDragStart onItemDragStart_;
OnItemDragEnter onItemDragEnter_;
OnItemDragMove onItemDragMove_;
OnItemDragLeave onItemDragLeave_;
OnItemDrop onItemDrop_;
bool isOnItemDragStartEventRegistered_;
bool isOnItemDragEnterEventRegistered_;
bool isOnItemDragMoveEventRegistered_;
bool isOnItemDragLeaveEventRegistered_;
bool isOnItemDropEventRegistered_;
};
#endif