* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (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 <functional>
#include <string>
#include <vector>
#include <cstdio>
#include <sstream>
#include <iomanip>
#include <arkui/native_node.h>
#include <arkui/native_type.h>
#include "ScrollableNode.h"
#include "ArkUINodeAdapter.h"
#include "GridMaker.h"
namespace {
constexpr char K_ROWS_TEMPLATE[] = "auto";
constexpr char K_COLUMNS_TEMPLATE[] = "1fr 1fr";
constexpr float K_COLUMNS_GAP = 10.0f;
constexpr float K_ROWS_GAP = 15.0f;
constexpr uint32_t K_ITEM_BG_COLOR = 0xFFF1F3F5U;
constexpr uint32_t K_GRID_CACHED_COUNT = 32;
constexpr bool K_GRID_SYNC_LOAD = true;
constexpr ArkUI_FocusWrapMode K_FOCUS_WRAP_MODE = ARKUI_FOCUS_WRAP_MODE_DEFAULT;
constexpr int K_ITEM_COUNT = 60;
constexpr float K_ITEM_HEIGHT = 72.0f;
constexpr int K_GRID_INDEX_WIDTH = 2;
}
static std::vector<std::string> MakeServicesData(size_t count = K_ITEM_COUNT)
{
std::vector<std::string> out;
out.reserve(count);
for (size_t i = 1; i <= count; ++i) {
std::ostringstream oss;
oss << "Grid" << std::setw(K_GRID_INDEX_WIDTH) << std::setfill('0') << i;
out.emplace_back(oss.str());
}
return out;
}
static void ConfigureGrid(const std::shared_ptr<GridMaker> &grid)
{
grid->SetWidthPercent(1.0f);
grid->SetDefaultScrollStyle();
grid->SetColumnsTemplate(K_COLUMNS_TEMPLATE);
grid->SetCachedCount(K_GRID_CACHED_COUNT);
grid->SetFocusWrapMode(K_FOCUS_WRAP_MODE);
grid->SetSyncLoad(K_GRID_SYNC_LOAD);
grid->SetColumnsGap(K_COLUMNS_GAP);
grid->SetRowsGap(K_ROWS_GAP);
}
static ArkUI_NodeHandle GridCreateItem(ArkUI_NativeNodeAPI_1 *api)
{
ArkUI_NodeHandle text = api->createNode(ARKUI_NODE_TEXT);
ArkUI_NodeHandle item = api->createNode(ARKUI_NODE_GRID_ITEM);
api->addChild(item, text);
return item;
}
static void GridBindItem(ArkUI_NativeNodeAPI_1 *api, ArkUI_NodeHandle item, int32_t index,
const std::shared_ptr<std::vector<std::string>> &data)
{
SetAttributeUInt32(api, item, NODE_BACKGROUND_COLOR, K_ITEM_BG_COLOR);
SetAttributeFloat32(api, item, NODE_HEIGHT, K_ITEM_HEIGHT);
ArkUI_NodeHandle text = api->getFirstChild(item);
if (!text) {
return;
}
const int32_t n = static_cast<int32_t>(data->size());
const char *s = (index >= 0 && index < n) ? (*data)[static_cast<size_t>(index)].c_str() : "<invalid>";
SetTextContent(api, text, s);
}
static std::shared_ptr<ArkUINodeAdapter> MakeGridAdapter(const std::shared_ptr<std::vector<std::string>> &data)
{
auto adapter = std::make_shared<ArkUINodeAdapter>();
adapter->EnsurePlaceholderTypeOr(static_cast<int32_t>(ARKUI_NODE_GRID_ITEM));
ArkUINodeAdapter::Callbacks cb{};
cb.getTotalCount = [data]() -> int32_t { return static_cast<int32_t>(data->size()); };
cb.getStableId = [data](int32_t i) -> uint64_t {
const int32_t n = static_cast<int32_t>(data->size());
if (i >= 0 && i < n) {
return static_cast<uint64_t>(std::hash<std::string>{}((*data)[static_cast<size_t>(i)]));
}
return static_cast<uint64_t>(i);
};
cb.onCreate = [](ArkUI_NativeNodeAPI_1 *api, int32_t ) -> ArkUI_NodeHandle { return GridCreateItem(api); };
cb.onBind = [data](ArkUI_NativeNodeAPI_1 *api, ArkUI_NodeHandle item, int32_t index) {
GridBindItem(api, item, index, data);
};
adapter->SetCallbacks(cb);
return adapter;
}
static std::shared_ptr<GridMaker> BuildGrid()
{
auto grid = std::make_shared<GridMaker>();
ConfigureGrid(grid);
auto data = std::make_shared<std::vector<std::string>>(MakeServicesData(K_ITEM_COUNT));
auto adapter = MakeGridAdapter(data);
grid->SetLazyAdapter(adapter);
adapter->ReloadAllItems();
GetKeepAliveContainer<GridMaker>().emplace_back(grid);
return grid;
}
ArkUI_NodeHandle GridMaker::CreateNativeNode()
{
ArkUI_NativeNodeAPI_1 *api = nullptr;
OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, api);
if (api == nullptr) {
return nullptr;
}
ArkUI_NodeHandle page = api->createNode(ARKUI_NODE_COLUMN);
if (page == nullptr) {
return nullptr;
}
SetAttributeFloat32(api, page, NODE_WIDTH_PERCENT, 1.0f);
SetAttributeFloat32(api, page, NODE_HEIGHT_PERCENT, 1.0f);
std::shared_ptr<GridMaker> grid = BuildGrid();
if (grid && grid->GetHandle() != nullptr) {
SetAttributeFloat32(api, grid->GetHandle(), NODE_LAYOUT_WEIGHT, 1.0f);
api->addChild(page, grid->GetHandle());
}
return page;
}