K级sc

C C++ Nuklear 爱好者

在 EasyX 中使用 Nuklear UI 实现窗口和控件 银牌收录

一、Nuklear 简介

Nuklear 是著名的即时模式 GUI,类似 Dear ImGui,但 Nuklear 对后端的适应更灵活,性能要求也更低。

本文范例的执行效果如下:

二、Nuklear 基本用法及相关资源下载

Nuklear 及 EasyX 后端下载:nuklear_easyx.zip

只包括两个头文件,无需引用 lib 或 dll 文件:

  • nuklear.h 主文件,实现窗口和控件逻辑
  • nuklear_easyx.h 后端文件,实现 EasyX 的适配

使用方法非常简单,在代码里引用压缩包里的两个头文件即可。

Nuklear 官方项目:https://github.com/Immediate-Mode-UI/Nuklear

注意:和其它 STB-style headers 一样,Nuklear 头文件用预处理指令分为声明和定义两部分。使用时一个项目应该有且仅有一个源文件包含 Nuklear 的定义(#define NK_IMPLEMENTATION),不然就会在链接阶段报错。

三、使用范例

1. VS2022 下使用

Nuklear 要求字符串为 utf-8 编码,在 VS2022 里面需要设置一下中文格式为 utf-8,否则中文会乱码。

以下是上图界面的示例代码(注意 Nuklear 要求字符串为 utf8 编码):

#define NK_INCLUDE_FIXED_TYPES			// 这个宏会引入 <stdint.h> 以帮助 nuklear 使用正确的数据类型
#define NK_INCLUDE_STANDARD_IO			// 这个宏会引入 <stdio.h> 使得 nuklear 控件可以使用 printf 风格的 label
#define NK_INCLUDE_STANDARD_VARARGS		// 这个宏会引入 <stdarg.h> 使得 nuklear 控件可以使用 vprintf 风格的 label
#define NK_INCLUDE_DEFAULT_ALLOCATOR	// 这个宏会引入 <stdlib.h> 使得 nuklear 的使用者不需要关心内存分配
// nuklear 的设计目标是适配所有平台,以上几个宏是控制 nuklear 特性和行为的
// 不同平台基本数据类型大小不一样,指针长度不一样
// 有些平台(例如嵌入式)不提供 IO 功能,或者使用者希望 nuklear context 使用固定分配的内存等各种特殊需求
// 在 PC 平台上定义以上几个宏几乎是标准动作

#pragma warning(disable:4996)				// nuklear 需要使用新版本 VS 默认禁止的 vsprintf 等旧版 C 函数
#define NK_IMPLEMENTATION					// 包含 nuklear 的函数定义
#include "nuklear.h"
#define NK_EASYX_IMPLEMENTATION				// 包含 nuklear easyx 后端的函数定义
#include "nuklear_easyx.h"
#pragma execution_character_set("utf-8")	// VS 需要设定执行字符集为 utf8 格式,否则非英文字符会乱码

int main()
{
	// 初始化图形窗口
	initgraph(640, 480);
	BeginBatchDraw();
	// 初始化 GUI 环境
	EasyxFont* font = nk_EasyxFont_create("新宋体", 16);
	struct nk_context* ctx = nk_easyx_init(font);

	int r = 20;
	enum { OP_CIRCLE, OP_SQUARE };
	int op = OP_CIRCLE;
	COLORREF fc = RED;

	while (true)
	{
		/* Events */
		ExMessage m;
		nk_input_begin(ctx);
		while (peekmessage(&m))
		{
			nk_easyx_handle_event(&m);
		}
		nk_input_end(ctx);

		/* GUI */
		if (nk_begin(ctx, "例子", nk_rect(300, 50, 300, 200),
			NK_WINDOW_BORDER | NK_WINDOW_MOVABLE | NK_WINDOW_SCALABLE |
			NK_WINDOW_CLOSABLE | NK_WINDOW_MINIMIZABLE | NK_WINDOW_TITLE))
		{
			nk_layout_row_static(ctx, 0, 80, 1);
			if (nk_button_label(ctx, "变色"))
			{
				if (fc == RED)
					fc = GREEN;
				else
					fc = RED;
			}

			nk_layout_row_dynamic(ctx, 0, 2);
			if (nk_option_label(ctx, "圆形", op == OP_CIRCLE)) op = OP_CIRCLE;
			if (nk_option_label(ctx, "方形", op == OP_SQUARE)) op = OP_SQUARE;

			nk_layout_row_dynamic(ctx, 0, 1);
			nk_property_int(ctx, "半径:", 0, &r, 100, 10, 1);
		}
		nk_end(ctx);

		/* Draw */
		setbkcolor(RGB(128, 128, 128));
		cleardevice();

		setfillcolor(fc);
		if (op == OP_CIRCLE)
			fillcircle(200, 200, r);
		else
			fillrectangle(200 - r, 200 - r, 200 + r, 200 + r);

		nk_easyx_render();	// draw GUI
		FlushBatchDraw();
	}

	// 清理 GUI 环境
	nk_EasyxFont_del(font);
	nk_easyx_shutdown();
	// 关闭图形窗口
	EndBatchDraw();
	closegraph();
	return 0;
}

2. MinGW 下使用(例如 VS Code)

vscode 支持 utf-8 编码,所以可以直接用前面 VS2022 的代码,忽略掉 #pragma execution_character_set("utf-8") 那行即可。

3. DevCpp 下使用

DevCpp 使用 MinGW,但 DevCpp 不支持 utf-8 编码,需要手动设置项目的编码为 utf-8。

四、备注

关于 C/C++ 中的字符集:

  • 源码字符集:源文件是怎么编码的(毕竟源文件是个文本文件)
  • 执行字符集:可执行文件里面字符串(代码里的字符串字面值)是怎么编码的

对源码字符集和执行字符集的识别和决定,不同编译器有不同的行为:

微软系列

新版编译器一般能自动识别源码字符集,执行字符集默认为本地字符集(例如 GBK)。可以使用编译指令/execution-charset:utf-8 或代码 #pragma execution_character_set("utf-8") 手动指定。

GCC 系列

源码字符集和执行字符集默认都为 utf-8,可用如下设置手动指定:

-finput-charset=GBK
-fexec-charset=UTF-8

C++11

无论使用什么源码或执行字符集,都可以使用 u8"字符串" 这种语法形式表示 utf-8 字面量。

添加评论