yNodeGUI_v2.0

紧跟着1.0版本的完成,又到了激动人心的C++期末作业了。(上学期是C语言的),这学期,我仅仅改了一点点上学期的期末作业,然后很轻松的水…啊不,完成了这次的期末作业。

所以,大家一定要注重复用!!! 很快乐。

这个改一改就是你的…啊不是,我是说基于框架进行开发(滑稽)

  1. 定位:针对于简易C++管理系统开发,提供简单的基于easyx的GUI固定渲染和简单的无限层级菜单解决方案

  2. 使用基础:有一定面向对象基础和IMGUI使用经验,或者easyx的使用经验

  3. 使用方式:仅在VisualStudio导入yNodeGUIFrameowrk目录下的文件到项目内,并在easyx官网(https://easyx.cn/ ).安装easyx(仅支持VisualStudio),在项目设置里设置多字符集的编码!!!

  4. 打包方式: 将VS切换到Release分支,在项目Release设置内,代码生成选择MT,即可脱离C++和easyx环境运行,如果在过程中加载了资源,请在打包后的目录里相对exe文件进行补充,框架不支持VS的Resources

功能

1.现成的GUI组件
包含按钮,缩放框,网格,文本,图片等多种组件

2.极低学习成本
框架内实现了NodeGUI,无限层级菜单管理方便,同时简单的机制容易上手

4.高效的菜单管理器
将菜单抽象为N叉树,能高效处置多级深度菜单,自动实现全流程的固定形式菜单渲染

API使用

0.引入头文件

#include"yNodeGUI.h"

1.Main函数内创建图形界面

	//创建画布
	Canvas canvas = { 1000,600,90, RGB(255,255,255) }; 
	//展示GUI并阻塞程序
	canvas.Show(OnStart, OnUpdate, OnGUI,true); 

2.在Start内定义菜单节点

  	//创建根节点和节点菜单管理器
	Node* root = new Node();
	menu = new Menu(root, &canvas);

	//主菜单节点
	Node* m1 = new Node(root, "课程信息录入");
	Node* m2 = new Node(root, "课程信息浏览", browsing, true, fresh);
	Node* m3 = new Node(root, "课程信息查询", queryshow, true, fresh_query);
	Node* m4 = new Node(root, "学生自由选课");
	Node* m6 = new Node(root, "安全退出系统", exit_system, true);

	//主菜单选项1  一级菜单节点
	Node* x1 = new Node(m1, "录入课程信息", input, true);
	Node* x2 = new Node(m1, "修改课程信息", set_course, true);
	Node* x3 = new Node(m1, "删除课程信息", del_course, true);
	Node* x4 = new Node(m1, "返回", last_menu, true);

	//主菜单选项4  一级菜单节点
	Node* n1 = new Node(m4, "自由选课", choose, true);
	Node* n2 = new Node(m4, "已选课程查看", view_selected, true, fresh_selected);
	Node* n3 = new Node(m4, "返回", last_menu, true);
  1. 注册和渲染 /这些内容可以在不同的位置实现,具体请看Core.cpp文件内的示例
	//注册重绘机制
	//注册控件
	canvas.Env(0).Register(id, btn);
	//绘制控件
	canvas.Env(0).Draw(id);

demo展示 图书馆管理系统

demo仓库(点击了解更多信息):https://github.com/yueh0607/yNodeGUI_Sample_

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sVWUMOzX-1690422875874)(https://github.com/yueh0607/yNodeGUI_v2.0/assets/102401735/b0065635-af57-4f58-a19b-8b8e358472f9)]

easyx是什么?

easyx是一个简单的图形库,能让你从小黑窗转变成图形界面!!!
从此高人一等,赢取白富美,走上人生巅峰。
所以进入官网,硕大的下载按钮,让我局促不安。慌忙之中点击下载,傻瓜式的安装流程简单的让我不知所措。(事情真的这么简单吗?是的!)
easyx.cn
在这里插入图片描述

确定一些基本的结构

为了表示方便,我写了自己的Vector2,写了自己的Rect,写了自己Object基类,写了GUIComponent作为可图形化对象的基类

Object的唯一作用就是让每个对象有一个唯一的ID

#pragma region 基本结构
//二维向量
typedef struct
{
	int x;
	int y;
	
} Vector2;


//矩形,包含左上角,右下角,长宽等确定矩形的基本要素
typedef struct
{
	Vector2 origin;
	Vector2 end;
	Vector2 center;
	int width;
	int height;
} Rect;


//实例基类,框架内所有需要实例化的类都需要继承本类,分配唯一ID
class Object
{
private:
	static int instanceId; //全局实例id位置
	static set<int> ids;  //已被占用的实例id
	int instance_id;  //当前实例id
	//分配一个与其他实例不冲突的ID
	static int AssignInstanceID()
	{
		do { if (instanceId == INT_MIN)instanceId == INT_MAX; instanceId--; } while (ExistID(instanceId));
		RegisterId(instanceId);
		return instanceId;
	}
	//检测是否存在ID
	static bool ExistID(int id)
	{
		return ids.find(id) != ids.end();
	}
	//注册一个ID
	static void RegisterId(int id)
	{
		ids.insert(id);
	}
public:
	//实例创建时分配ID
	Object()
	{
		instance_id = AssignInstanceID();  //分配一个新的实例ID
	}  
	//实例销毁回收ID
	~Object()
	{
		ids.erase(instance_id);
	}
	//获取当前实例的ID并返回
	int InstanceId()
	{
		return instance_id;
	}
	
};
int Object::instanceId = INT_MAX;
set<int> Object::ids;

//GUI接口,所有GUI组件继承该接口 
class GUIComponent :public Object
{
public:
	//负责GUI渲染
	virtual void OnGUI() = 0;
	//负责消息处理与事件响应
	virtual void OnEvent(ExMessage* message) = 0;
};

搭建Canvas和游戏循环

//画布类,负责画布生命维护,不实现具体逻辑
class Canvas :public Object
{
private:
#pragma region 信息字段
	int width;//宽度
	int height;//高度
	int fps = 60;//帧率
	int frameStart = 0;//当前帧开始时间
	int frameTime;//每帧时长
	bool life = true;//是否存活
	int deltaTime;//上一帧消耗的时间 ms
	COLORREF bgc;//背景色
	ExMessage message;//消息临时内存
	HWND window;//窗口句柄
#pragma endregion
#pragma region 环境与队列
	//GUI注册环境
	map<int, GUIComponent*> gui0;
	map<int, GUIComponent*> gui1;
	map<int, GUIComponent*> gui2;
	map<int, GUIComponent*> gui3;
	map<int, map<int, GUIComponent*>> envs{ { 0,gui0 }, {1,gui1}, {2,gui2},{3,gui3} };

	//GUI临时回收环境
	vector<GUIComponent*> collection0;
	vector<GUIComponent*> collection1;
	vector<GUIComponent*> collection2;
	vector<GUIComponent*> collection3;
	map<int, vector<GUIComponent*>> cenvs{ {0,collection0},{1,collection1},{2,collection2},{3,collection3} };

	//渲染队列/消息队列
	queue <GUIComponent* > renderQueue;
	queue<GUIComponent*> eventQueue;

	//当前环境ID
	int envid = 0;
#pragma endregion
#pragma region 队列化GUI处理
	//渲染GUI并清空队列
	void RenderAll()
	{
		while (!renderQueue.empty())
		{
			renderQueue.front()->OnGUI();
			renderQueue.pop();
		}
	}
	//处理GUI消息并清空队列
	void BroadcastAll(ExMessage* message)
	{
		while (!eventQueue.empty())
		{
			eventQueue.front()->OnEvent(message);
			eventQueue.pop();
		}
	}
#pragma endregion
protected:
#pragma region 生命周期
	//开始时调用
	void (*OnStart)(Canvas& canvas);
	//GUI渲染时调用
	void (*OnGUI)(Canvas& canvas);
	//帧更新时调用
	void (*OnUpdate)(Canvas& canvas);
#pragma endregion
public:
#pragma region 画布属性-ReadOnly
	//中心点
	Vector2 Center() { return { width / 2,height / 2 }; }
	//宽度
	int Width() { return width; }
	//高度
	int Height() { return height; }
	//背景色
	COLORREF BackgroundColor() { return bgc; }
	//画布是否存活
	bool Life() { return life; }
	//每帧时间
	int FrameTime() { return frameTime; }
	//每秒帧数
	int FrameCount() { return fps; }
	//上一帧的时间,ms单位
	int DeltaTime() { return deltaTime; }
	HWND* Window()
	{
		return &window;
	}
#pragma endregion

#pragma region 构造与析构
	//传入xy长度,秒帧数,背景色
	Canvas(int xLen, int yLen, int frame = INT_MAX, COLORREF color = WHITE)
	{

		width = xLen;
		height = yLen;
		bgc = color;
		fps = frame;
		frameTime = 1000 / fps;
		deltaTime = 0;

		
	}
#pragma endregion

#pragma region GUI操作
	//切换环境并返回引用
	Canvas& Env(int env)
	{
		assert(env >= 0 && env < 4);
		envid = env;
		return *this;
	}
	//渲染某个已注册GUI
	void Draw(int id)
	{
		renderQueue.push(envs[envid][id]);
		eventQueue.push(envs[envid][id]);
	}
	//检查Canvas释放包含某个GUI
	bool ContainsKey(int id)
	{
		return envs[envid].find(id) != envs[envid].end();
	}
	//注册GUI到当前环境
	void Register(int id, GUIComponent* gui)
	{
		envs[envid].insert({ id,gui });
	}
	//移除某个GUI的注册,但不释放内存
	void RemoveGUI(int id)
	{
		envs[envid].erase(id);
	}
	//从Canvas获取某个GUI并返回指针,如果不存在则报错
	GUIComponent* GetGUI(int id)
	{
		return envs[envid][id];
	}
	//释放某个id的GUI所占用的内存并解除注册
	void ReleaseGUI(int id)
	{
		delete envs[envid][id];
		RemoveGUI(id);
	}
	//把GUI引用收集到Canvas内,作为一个Collection统一管理,失去单一管理权限
	void Collect(GUIComponent* gui1, GUIComponent* gui2 = nullptr, GUIComponent* gui3 = nullptr, GUIComponent* gui4 = nullptr)
	{
		cenvs[envid].push_back(gui1);
		if (gui2 != nullptr) cenvs[envid].push_back(gui2);
		else if (gui3 != nullptr) cenvs[envid].push_back(gui3);
		else if (gui4 != nullptr) cenvs[envid].push_back(gui4);
	}
#pragma endregion

#pragma region 批量释放资源

	//释放GUI所有的内存,并清除注册
	void ReleaseAllGUIS()
	{
		for (auto& i : envs[envid])
		{
			delete i.second;
		}
		envs[envid].clear();
	}
	//只从Canvas内移除注册
	void RemoveAllGUIS()
	{
		envs[envid].clear();
	}
	//只在Canvas内移除Collection的注册
	void RemoveAllCollections()
	{
		cenvs[envid].clear();
	}
	//释放所有Collection的内存,并清除注册
	void ReleaseAllCollections()
	{
		for (auto& i : cenvs[envid])
		{
			delete i;
		}
		cenvs[envid].clear();
	}
	//移除所有Collection和GUI的注册
	void RemoveAll()
	{
		RemoveAllGUIS();
		RemoveAllCollections();
	}
	//释放所有Coloection和GUI的内存
	void ReleaseAll()
	{
		ReleaseAllGUIS();
		ReleaseAllCollections();
	}

#pragma endregion

#pragma region 生命周期干涉操作
	//画布初始化
	void Show(void start(Canvas& canvas), void update(Canvas& canvas), void ongui(Canvas& canvas),bool showConsole=false)
	{



		//生命周期
		OnStart = start;
		OnUpdate = update;
		OnGUI = ongui;

		//设置背景颜色
		window = showConsole?initgraph(width, height, EW_SHOWCONSOLE): initgraph(width, height);
		setbkcolor(bgc);
		cleardevice();

		//生命周期:Start
		OnStart(*this);

		while (life && IsWindow(window))
		{
			//帧开始计时
			frameStart = GetTickCount();
			//渲染与消息队列(将生命周期GUI和持久化渲染GUI添加到渲染队列和消息队列)
			OnGUI(*this);

			//清空画布开始渲染
			BeginBatchDraw();
			cleardevice();
			RenderAll();
			EndBatchDraw();

			//生命周期--消息分发
			if (peekmessage(&message))
			{
				BroadcastAll(&message);
				message = {};
			}
			else while (!eventQueue.empty()) eventQueue.pop();


			//生命周期--帧更新
			OnUpdate(*this);

			//帧数控制
			deltaTime = GetTickCount() - frameStart;
			if (frameTime - deltaTime > 0)
			{
				Sleep(frameTime - deltaTime);
			}
			//打印帧信息
			//cout << "Frame:" << count++ << "  " << "FrameTime:" << frameTime << "  " << "DeltaTime:" << deltaTime<<"  SleepTime:"<< frameTime - deltaTime << endl;
		}
		closegraph();
	}
	//关闭画布
	void Close()
	{
		life = false;
	}
#pragma endregion
};
#pragma endregion

写点组件

#pragma region 矩形操作

//通过四个边界坐标创建矩形
inline Rect createRectbyPoint(int left, int top, int right, int bottom)
{
	Rect rect;
	rect.center = { (right + left) / 2,(top + bottom) / 2 };
	rect.origin = { left,top };
	rect.end = { right,bottom };
	rect.width = right - left;
	rect.height = bottom - top;
	return rect;
}
//通过中心点和长宽创建矩形
inline Rect createRectbyCenter(int x, int y, int width, int height)
{
	Rect rect;
	rect.center = { x,y };
	rect.origin = { x - width / 2,y - height / 2 };
	rect.end = { x + width / 2,y + height / 2 };
	rect.width = width;
	rect.height = height;
	return rect;
}
//通过中心点和长宽创建矩形创建矩形
inline Rect createRectbyCenter(Vector2 center, int width, int height)
{
	Rect rect;
	rect.center = center;
	rect.origin = { center.x - width / 2,center.y - height / 2 };
	rect.end = { center.x + width / 2,center.y + height / 2 };
	rect.width = width;
	rect.height = height;
	return rect;
}
//移动矩形位置到新的矩形并返回,不改变原矩形
inline Rect moveRect(Vector2 offset, Rect rect)
{
	return createRectbyPoint(rect.origin.x + offset.x, rect.origin.y + offset.y, rect.end.x + offset.x, rect.end.y + offset.y);
}
//判断某位置是否在矩形内
inline bool inRect(int x, int y, const Rect* rect)
{
	if (x >= rect->origin.x && x <= rect->end.x && y >= rect->origin.y && y <= rect->end.y)
	{
		return true;
	}
	return false;
}
#pragma endregion

#pragma region GUI组件

//可以缩放的方形边框
class LineBox :public GUIComponent
{
	Rect rect;  //原矩形大小,也是响应事件的矩形大小
	Rect temp;  //缩放后矩形大小
	COLORREF color;  //线框颜色
	bool state = false;  //当前是否缩放

public:

	void OnGUI() override
	{
		setlinecolor(color);  //设置线颜色
		if (!state)   
			rectangle(rect.origin.x, rect.origin.y, rect.end.x, rect.end.y);  //绘制原线框
		else rectangle(temp.origin.x, temp.origin.y, temp.end.x, temp.end.y);  //绘制放大后线框

	}
	void OnEvent(ExMessage* message) override
	{
		if (inRect(message->x, message->y, &rect))  //如果鼠标在原线框范围内,则修改状态
		{
			state = true;
		}
		else
		{
			state = false;
		}
	}
	//形参:原线框矩形,线框颜色,缩放长度
	LineBox(Rect rct, COLORREF c, int s = 10)
	{
		rect = rct;
		color = c;
		temp = createRectbyCenter(rect.center, rect.width + s, rect.height + s);
	}
};
//允许加载纯色和图片的矩形
class Image : public GUIComponent
{
	IMAGE img;  //图片数据
	bool pureColor = false;   //是否为纯色图片
	COLORREF color;  //如果是纯色图片,颜色是什么
public:
	Rect rect;  //图片的矩形
	void OnGUI() override
	{
		if (!pureColor)putimage(rect.origin.x, rect.origin.y, &img);  //如果不是纯色,则渲染到屏幕的是图片
		else  //如果是纯色
		{
			setfillcolor(color);  //设置填充颜色
			fillrectangle(rect.origin.x, rect.origin.y, rect.end.x, rect.end.y);  //填充矩形并渲染到屏幕
		}
	}
	void OnEvent(ExMessage* message) override{}
	Image(Rect rct, string path)
	{
		color = NULL;
		pureColor = false;
		rect = rct;
		loadimage(&img, path.c_str(), rect.width, rect.height);
	}
	Image(Rect rct, COLORREF c)
	{
		img = NULL;
		pureColor = true;
		rect = rct;
		color = c;
	}
	

};
//显示文字的box
class Text : public GUIComponent
{
	string style;  //字体名称
	COLORREF color;  //字体颜色
	RECT rr;   //系统使用的矩形
public:
	Rect rect;  //框架使用的矩形
	string text;  //文本框的内容
	bool center = true;  //是否水平居中显示
	void OnGUI()  override
	{
		setbkmode(TRANSPARENT);  //文本背景透明
		settextcolor(color);  //设置文本颜色
		settextstyle(16, 0, style.c_str());  //设置字体
		if (center)drawtext(text.c_str(), &rr, DT_CENTER | DT_VCENTER | DT_SINGLELINE);  //渲染文字
		else drawtext(text.c_str(), &rr, DT_VCENTER | DT_SINGLELINE);
	}
	void OnEvent(ExMessage* message)override {}
	void SetText(string str, string sty = "宋体", COLORREF col = -1)
	{
		if (col != -1)  color = col;
		if (sty != "宋体") style = sty;
		text = str;
	}
	Text(string txt, Rect rct, string st = "宋体", const COLORREF c = BLACK, bool hcenter = true)
	{
		color = c;
		text = txt;
		rect = rct;
		style = st;
		center = hcenter;
		rr = { rect.origin.x,rect.origin.y,rect.end.x,rect.end.y };
	}
	Text(string txt, Rect rct, bool hcenter = true)
	{
		color = BLACK;
		text = txt;
		rect = rct;
		style = "宋体";
		center = hcenter;
		rr = { rect.origin.x,rect.origin.y,rect.end.x,rect.end.y };
	}
};
//能点击的按钮
class Button : public GUIComponent
{
	Image* a = nullptr;  //按钮的图片指针
	Text* t = nullptr;  //按钮文字指针
	LineBox* box = nullptr;  //按钮线框指针
	vector<function<void(void)>> onclicks;  //消息列表
public:
	void OnGUI() override
	{
		//分别调用子对象的渲染函数
		if (a != nullptr)a->OnGUI();  
		if (t != nullptr)t->OnGUI();
		if (box != nullptr)box->OnGUI();
	}
	void OnEvent(ExMessage* message) override
	{
		//处理子对象消息
		if (a != nullptr)a->OnEvent(message);
		if (t != nullptr)t->OnEvent(message);
		if (box != nullptr) box->OnEvent(message);
		
		//处理按下消息
		if (a!=NULL&&message->message==WM_LBUTTONDOWN&& inRect(message->x, message->y, &(a->rect)))
		{	
			for (auto& i : onclicks) i();
		}
	}


	//添加监听事件
	void AddListener(function<void(void)> onclick)
	{
		onclicks.push_back(onclick);
	}
	//移除监听事件,效率较低
	void RemoveListener(function<void(void)> onclick)
	{
		for (auto i = onclicks.begin(); i != onclicks.end(); i++)
		{
			bool eq = *(onclick.target<void(*)(void)>()) == *(i->target<void(*)(void)>());
			if (eq)
			{
				onclicks.erase(i);
			}
		}
	}
	//根据id移除监听
	void RemoveListener(int id)
	{
		onclicks.erase(onclicks.begin() + id);
	}
	//移除全部监听
	void RemoveAllListener()
	{
		onclicks.clear();
	}
	//形参:图片指针,文本指针,线框指针,点击范围以图片指针范围为准
	Button(Image* img, Text* txt = nullptr, LineBox* edge = nullptr)
	{
		a = img;
		t = txt;
		box = edge;
	}
	Button(Rect rct,COLORREF imgColor, string txt, COLORREF fColor, COLORREF edgeColor)
	{
		Image* img = new Image(rct, imgColor);
		Text* t = new Text(txt, rct,"宋体",fColor, true);
		LineBox* lb = new LineBox(rct, edgeColor);
		a = img;
		t = t;
		box = lb;
	}
	~Button()
	{
		delete a;
		delete t;
		delete box;
	}
};
//可自定义的网格
class Gird : public GUIComponent
{
private:
	Text* (**units);  //网格的文本指针 二维数组
	Rect rect;  //矩形范围
	COLORREF color;  //网格线颜色
	COLORREF fontcolor;  //网格文本颜色
	string dstyle;  //网格字体名称
public:


	int xCount;  //列数
	int yCount;  //行数
	Rect unitRect;  //单元格大小,左上角为0,0
#pragma region 生命
	//初始化网格文本
	void initUnits()
	{
		units = new Text * *[yCount];
		for (int i = 0; i < yCount; i++)
			units[i] = new Text * [xCount] {0};

		for (int y = 0; y < yCount; y++)for (int x = 0; x < xCount; x++)
			units[y][x] = new Text("", moveRect({ rect.origin.x + x * unitRect.width,rect.origin.y + unitRect.height * y }, unitRect), dstyle, fontcolor);

	}
	//rct指总网格大小,xy是网格数量
	Gird(Rect rct, int xC, int yC, COLORREF c = BLACK, string style = "宋体", COLORREF fontc = BLACK)
	{
		rect = rct;
		xCount = xC;
		yCount = yC;
		dstyle = style;
		unitRect = createRectbyPoint(0, 0, rct.width / xC, rct.height / yC);
		color = c;
		fontcolor = fontc;
		initUnits();
	}
	//leftTop为左上角位置,xC,yC指网格数量,width,height指网格宽高
	Gird(Vector2 leftTop, int xC, int yC, int width, int height, COLORREF c = BLACK, string style = "宋体", COLORREF fontc = BLACK)
	{
		xCount = xC;
		yCount = yC;
		rect = createRectbyPoint(leftTop.x, leftTop.y, leftTop.x+xC * width,leftTop.y+ yC * height);
		dstyle = style;
		unitRect = createRectbyPoint(0, 0, width, height);
		color = c;
		fontcolor = fontc;
		initUnits();
	}
	//leftTop为左上角位置,xC,yC指网格数量,width,height指网格宽高
	Gird(const int left, int top, int xC, int yC, int width, int height, COLORREF c = BLACK, string style = "宋体", COLORREF fontc = BLACK)
	{
		xCount = xC;
		yCount = yC;
		rect = createRectbyPoint(left, top, left+ xC * width, top + yC * height);
		dstyle = style;
		unitRect = createRectbyPoint(0, 0, width, height);
		color = c;
		fontcolor = fontc;
		initUnits();
	}
	~Gird()
	{
		for (int x = 0; x < xCount; x++)for (int y = 0; y < yCount; y++) delete units[x][y];
		for (int i = 0; i < xCount; i++)
		{
			delete(units[i]);
		}
		delete units;
	}
	//仅在GUI渲染时回调
	void OnGUI() override
	{
		setlinecolor(color);
		//绘制网格线
		for (int x = 0; x < xCount + 1; x++)
		{
			line(rect.origin.x + x * unitRect.width, rect.origin.y, rect.origin.x + x * unitRect.width, rect.end.y);
		}
		for (int y = 0; y < yCount + 1; y++)
		{
			line(rect.origin.x, rect.origin.y + y * unitRect.height, rect.end.x, rect.origin.y + y * unitRect.height);
		}
		//绘制文字
		for (int y = 0; y < yCount; y++)
		{
			for (int x = 0; x < xCount; x++)
			{
				units[y][x]->OnGUI();
			}
		}
	}
	//仅在处理事件时回调
	void OnEvent(ExMessage* message) override
	{
		//处理子对象消息
		for (int y = 0; y < yCount; y++)
		{
			for (int x = 0; x < xCount; x++)
			{
				units[y][x]->OnEvent(message);
			}
		}

	}
#pragma endregion

#pragma region 单元格操作

	//设置单元格文字
	void SetUnit(int x, int y, string text, const COLORREF color = BLACK)
	{
		assert(x < yCount&& y < xCount);
		units[x][y]->SetText(text);
	}
#pragma endregion

};

template <typename T>
class GirdList : public GUIComponent
{
	Gird* gird;
	COLORREF fontColor;
	int rowCount, columnCount;
	vector<T*>* origin;
	
	
	int currentPage = 0;

	int getMaxPage()
	{
		auto x = origin;
		int count = origin->size();
		int per = rowCount - 1;
		return count / per + 1;
	}

	function<vector<string>(T*)> handle;
	
public: 
	void next_page()
	{
		int maxpg = this->getMaxPage();
		currentPage = min(currentPage+1, maxpg);
	}
	void last_page()
	{
		currentPage = max(currentPage-1, 0);
	}

	void top_page()
	{
		int maxpg = getMaxPage();
		currentPage = min(0, maxpg);
	}
	void end_page()
	{
		int maxpg = getMaxPage();
		currentPage = min(maxpg, maxpg);
	}


	GirdList(int rowCount, int columnCount,Vector2 lefttop, int width, int height,
		string font,COLORREF line_color,COLORREF fontColor,int buttonHeight,int buttonWidth
		):
		fontColor(fontColor),columnCount(columnCount),rowCount(rowCount)
	{
		gird = new Gird(lefttop, columnCount, rowCount, width, height, line_color,font,fontColor);

	}

	~GirdList()
	{
		delete gird;
	}

	void SetOrigin(vector<T*>* origin)
	{
		this->origin = origin;
	}
	void SetHeader(vector<string> head)
	{
		if (head.size() >columnCount)
		{
			cout << "错误列数!";
			return;
		}
		for (int i = 0; i < head.size(); i++)
		{
			gird->SetUnit(0, i, head[i], fontColor);
		}
	}
	void SetColumn(function<vector<string>(T*)> hd)
	{
		handle = hd;
	}

	void OnGUI() override
	{
		//每页的数据行数
		int per = rowCount - 1;
		//遍历起点
		int from = currentPage * per;
		//遍历终点
		int to = from + per;
		//从1 - rowcount遍历行
		for (int i = 1; i < rowCount; i++)
		{
			if (from+i-1>= (*origin).size())
			{
				for (int j = 0; j < columnCount; j++)
				{
					gird->SetUnit(i, j, "", fontColor);
				}
			}
			else
			{
				vector<string> head = handle((*origin)[from+i-1]);
				for (int j = 0; j < head.size(); j++)
				{
					gird->SetUnit(i, j, (head)[j], fontColor);
				}
			}
		}

		gird->OnGUI();
	}
	void OnEvent(ExMessage* message) override
	{
		gird->OnEvent(message);

	}
};

#pragma region MyRegion