Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions include/views/mods_list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

#include <borealis.hpp>

#include "views/recycling_grid.hpp"
#include "api/mod.hpp"
#include "api/game.hpp"

class ModCell : public brls::RecyclerCell
class ModCell : public RecyclingGridItem
{
public:
ModCell();
Expand Down Expand Up @@ -34,16 +35,21 @@ class CategorieCell : public brls::RecyclerCell
static CategorieCell* create();
};

class ModData : public brls::RecyclerDataSource
class ModData : public RecyclingGridDataSource
{
public:
ModData(Game game);

int numberOfSections(brls::RecyclerFrame* recycler) override;
int numberOfRows(brls::RecyclerFrame* recycler, int section) override;
brls::RecyclerCell* cellForRow(brls::RecyclerFrame* recycler, brls::IndexPath index) override;
void didSelectRowAt(brls::RecyclerFrame* recycler, brls::IndexPath indexPath) override;
std::string titleForHeader(brls::RecyclerFrame* recycler, int section) override;
// int numberOfSections(brls::RecyclerFrame* recycler) override;
// int numberOfRows(brls::RecyclerFrame* recycler, int section) override;
// brls::RecyclerCell* cellForRow(brls::RecyclerFrame* recycler, brls::IndexPath index) override;
// void didSelectRowAt(brls::RecyclerFrame* recycler, brls::IndexPath indexPath) override;
// std::string titleForHeader(brls::RecyclerFrame* recycler, int section) override;

RecyclingGridItem* cellForRow(RecyclingGrid* recycler, size_t index) override;
size_t getItemCount() override;
void onItemSelected(RecyclingGrid* recycler, size_t index) override;
void clearData() override;

std::unique_ptr<ModList>& getModList() { return modList; }
private:
Expand All @@ -57,11 +63,12 @@ class ModListTab : public brls::Box {
public:
ModListTab(Game& game);
ModListTab();
~ModListTab() override;

//static brls::View* create();

private:
BRLS_BIND(brls::RecyclerFrame, recycler, "modrecycler");
BRLS_BIND(RecyclingGrid, recycler, "modrecycler");

std::unique_ptr<ModData> modData;
};
267 changes: 267 additions & 0 deletions include/views/recycling_grid.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
//
// Created by fang on 2022/6/15.
//

// register this view in main.cpp
//#include "view/recycling_grid.hpp"
// brls::Application::registerXMLView("RecyclingGrid", RecyclingGrid::create);
// <brls:View xml=@res/xml/views/recycling_grid.xml

#pragma once

#include <map>
#include <borealis/core/application.hpp>
#include <borealis/core/bind.hpp>
#include <borealis/views/scrolling_frame.hpp>

namespace brls {
class Label;
class Image;
} // namespace brls
class RecyclingGrid;
class ButtonRefresh;

class RecyclingGridItem : public brls::Box {
public:
RecyclingGridItem();
~RecyclingGridItem() override;

/*
* Cell's position inside recycler frame
*/
size_t getIndex() const;

/*
* DO NOT USE! FOR INTERNAL USAGE ONLY!
*/
void setIndex(size_t value);

/*
* A string used to identify a cell that is reusable.
*/
std::string reuseIdentifier;

/*
* Prepares a reusable cell for reuse by the recycler frame's data source.
*/
virtual void prepareForReuse() {}

/*
* 表单项回收
*/
virtual void cacheForReuse() {}

private:
size_t index;
};

class RecyclingGridDataSource {
public:
virtual ~RecyclingGridDataSource() = default;

/*
* Tells the data source to return the number of items in a recycler frame.
*/
virtual size_t getItemCount() { return 0; }

/*
* Asks the data source for a cell to insert in a particular location of the recycler frame.
*/
virtual RecyclingGridItem* cellForRow(RecyclingGrid* recycler,
size_t index) {
return nullptr;
}

/*
* Asks the data source for the height to use for a row in a specified location.
* Return -1 to use autoscaling.
*/
virtual float heightForRow(RecyclingGrid* recycler, size_t index) {
return -1;
}

/*
* Tells the data source a row is selected.
*/
virtual void onItemSelected(RecyclingGrid* recycler, size_t index) {}

virtual void clearData() = 0;
};

class RecyclingGridContentBox;

class RecyclingGrid : public brls::ScrollingFrame {
public:
RecyclingGrid();

void draw(NVGcontext* vg, float x, float y, float width, float height,
brls::Style style, brls::FrameContext* ctx) override;

void registerCell(std::string identifier,
std::function<RecyclingGridItem*()> allocation);

void setDefaultCellFocus(size_t index);

size_t getDefaultCellFocus() const;

void setDataSource(RecyclingGridDataSource* source);

RecyclingGridDataSource* getDataSource() const;

void showSkeleton(unsigned int num = 30);

void refresh();

void setRefreshAction(const std::function<void()>& event);

// 重新加载数据
void reloadData();

void notifyDataChanged();

/// 获取当前指定索引数据所在的item指针
///(注意,因为是循环使用列表项的,所以此指针只能在获取时刻在主线程内使用)
RecyclingGridItem* getGridItemByIndex(size_t index);

std::vector<RecyclingGridItem*>& getGridItems();

void clearData();

void setEmpty(std::string msg = "");

void setError(std::string error = "");

void selectRowAt(size_t index, bool animated);

// 计算从start元素的顶点到index(不包含index)元素顶点的距离
float getHeightByCellIndex(size_t index, size_t start = 0);

View* getNextCellFocus(brls::FocusDirection direction, View* currentView);

void forceRequestNextPage();

void onLayout() override;

/// 当前数据总数量
size_t getItemCount();

/// 当前数据总行数
size_t getRowCount();

/// 导航到页面尾部时触发回调函数
void onNextPage(const std::function<void()>& callback = nullptr);

void setPadding(float padding) override;
void setPadding(float top, float right, float bottom, float left) override;
void setPaddingTop(float top) override;
void setPaddingRight(float right) override;
void setPaddingBottom(float bottom) override;
void setPaddingLeft(float left) override;

void setPaddingRightPercentage(float right);
void setPaddingLeftPercentage(float left);

float getPaddingLeft();
float getPaddingRight();

// 获取一个列表项组件
// 如果缓存列表中存在就从中取出一个
// 如果缓存列表为空则生成一个新的
RecyclingGridItem* dequeueReusableCell(std::string identifier);

brls::View* getDefaultFocus() override;

~RecyclingGrid() override;

static View* create();

/// 元素间距
float estimatedRowSpace = 20;

/// 默认行高(元素实际高度 = 默认行高 - 元素间隔)
float estimatedRowHeight = 240;

/// 列数
int spanCount = 3;

/// 预取的行数
int preFetchLine = 1;

/// 瀑布流模式,每一项高度不固定(仅在spanCount为1时可用)
bool isFlowMode = false;

private:
RecyclingGridDataSource* dataSource = nullptr;
bool layouted = false;
float oldWidth = -1;

bool requestNextPage = false;
// true表示正在请求下一页,此时不会再次触发下一页请求
// 数据为空时不请求下一页,因为有些时候首页和下一页请求的内容或方式不同
// 当列表元素有变动时(添加或修改数据源,会重置为false,这是将允许请求下一页)

uint32_t visibleMin, visibleMax;
size_t defaultCellFocus = 0;

float paddingTop = 0;
float paddingRight = 0;
float paddingBottom = 0;
float paddingLeft = 0;

bool paddingPercentage = false;

std::function<void()> nextPageCallback = nullptr;
std::function<void()> refreshAction = nullptr;

RecyclingGridContentBox* contentBox = nullptr;
brls::Image* hintImage;
brls::Label* hintLabel;
brls::Button* refreshButton;
brls::Rect renderedFrame;
std::vector<float> cellHeightCache;
std::map<std::string, std::vector<RecyclingGridItem*>*> queueMap;
std::map<std::string, std::function<RecyclingGridItem*(void)>>
allocationMap;

//检查宽度是否有变化
bool checkWidth();

// 回收列表项
void queueReusableCell(RecyclingGridItem* cell);

void itemsRecyclingLoop();

/**
* 在指定位置添加一个列表项
* 内部更新 renderedFrame 的值,假设有一个每一项都绘制的超长列表,renderedFrame 的 y 表示当前截取绘制的顶部坐标,height 表示当前绘制的高度
* 当添加一个列表项时,renderedFrame 的 height 增加一项的高度(注意,只在每行的第一个列表项添加时才更新列表项的高度)
* @param index 指定的位置
* @param downSide 是向下添加还是向上添加,当向上添加时 将 renderedFrame 的 y 减去当前列表项的高度。(y 的值只在向上添加或移除时候改变)
*/
void addCellAt(size_t index, bool downSide);

void removeCell(brls::View *view);
};

class RecyclingGridContentBox : public brls::Box {
public:
RecyclingGridContentBox(RecyclingGrid* recycler);
brls::View* getNextFocus(brls::FocusDirection direction,
brls::View* currentView) override;

private:
RecyclingGrid* recycler;
};

class SkeletonCell : public RecyclingGridItem {
public:
SkeletonCell();

static RecyclingGridItem* create();

void draw(NVGcontext* vg, float x, float y, float width, float height,
brls::Style style, brls::FrameContext* ctx) override;

private:
NVGcolor background = brls::Application::getTheme()["color/grey_3"];
};
2 changes: 1 addition & 1 deletion resources/xml/tabs/mods.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
height="auto"
axis="column">

<brls:RecyclerFrame
<RecyclingGrid
id="modrecycler"
grow="1.0"
width="auto"
Expand Down
2 changes: 2 additions & 0 deletions source/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "views/settings_tab.hpp"
#include "activity/main_activity.hpp"
#include "utils/config.hpp"
#include "views/recycling_grid.hpp"

void init();
void exit();
Expand Down Expand Up @@ -50,6 +51,7 @@ int main(int argc, char* argv[]) {
brls::Application::registerXMLView("GameListTab", GameListTab::create);
brls::Application::registerXMLView("InstallationTab", InstallationView::create);
brls::Application::registerXMLView("SettingsTab", SettingsTab::create);
brls::Application::registerXMLView("RecyclingGrid", RecyclingGrid::create);

// Add custom values to the theme
brls::Theme::getLightTheme().addColor("captioned_image/caption", nvgRGB(2, 176, 183));
Expand Down
Loading
Loading