• 明理
    • 个人网站
    • STM32
    • SCons
    • 图形界面
    • 使用GLFW构建一个窗口
    • Nuklear入门
      • Environment
      • 复制文件
        • 添加 Extensions
      • 创建窗口
        • 主窗体初始化时居中
      • 添加全屏切换按钮
      • 中文(CJK)字体显示
    • CubeMX + SCons 闪灯实验
    • 手搓 Gitbook 主题
    • 在本地测试个人网站
    • 想要一个个人网站
Nuklear入门

Nuklear入门

  • Windmill-City
  • 2023-01-29
  • 2023-02-01
    • 图形界面

上节: 使用GLFW构建一个窗口

Environment

软件 Nuklear 版本: 4.10.5

复制文件

git clone git@github.com:Immediate-Mode-UI/Nuklear.git

复制 nuklear.h demo-glfw_opengl4-nuklear_glfw_gl4.h 到项目nuklear-include中

CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(nuklear)

add_library(nuklear INTERFACE)
target_include_directories(nuklear INTERFACE include)

目录结构

nuklear
│  CMakeLists.txt
└─include
        nuklear.h
        nuklear_glfw_gl4.h

添加依赖

diff --git a/testWnd/CMakeLists.txt b/testWnd/CMakeLists.txt
index b186812..12f94b3 100644
--- a/testWnd/CMakeLists.txt
+++ b/testWnd/CMakeLists.txt
@@ -18,6 +18,7 @@ endif()
 
 add_subdirectory(glfw EXCLUDE_FROM_ALL)
 add_subdirectory(glad EXCLUDE_FROM_ALL)
+add_subdirectory(nuklear EXCLUDE_FROM_ALL)

 # ##############################################################################
 # Targets
@@ -25,7 +26,7 @@ add_subdirectory(glad EXCLUDE_FROM_ALL)

 add_executable(testWnd src/main.cpp)
 target_include_directories(testWnd PRIVATE include)
-target_link_libraries(testWnd PRIVATE glfw glad)
+target_link_libraries(testWnd PRIVATE glfw glad nuklear)

添加 Extensions

[main] Building folder: testWnd testWnd
[build] Starting build
[proc] Executing command: "C:\Program Files\CMake\bin\cmake.EXE" --build e:/Codes/IngameIME_Win32/IngameIME-Common/testWnd/build --config Debug --target testWnd -j 14 --
[build] MSBuild version 17.4.1+9a89d02ff for .NET Framework
[build]   glad.c
[build]   glfw.vcxproj -> E:\Codes\IngameIME_Win32\IngameIME-Common\testWnd\build\glfw\src\Debug\glfw3.lib
[build]   glad.vcxproj -> E:\Codes\IngameIME_Win32\IngameIME-Common\testWnd\build\glad\Debug\glad.lib
[build]   main.cpp
[build] E:\Codes\IngameIME_Win32\IngameIME-Common\testWnd\nuklear\include\nuklear_glfw_gl4.h(286,24): error C3861: “glGetTextureHandleARB”: 找不到标识符 [E:\Codes\IngameIME_Win32\IngameIME-Common\testWnd\build\testWnd.vcxproj]
[build] E:\Codes\IngameIME_Win32\IngameIME-Common\testWnd\nuklear\include\nuklear_glfw_gl4.h(287,5): error C3861: “glMakeTextureHandleResidentARB”: 找不到标识符 [E:\Codes\IngameIME_Win32\IngameIME-Common\testWnd\build\testWnd.vcxproj]
[build] E:\Codes\IngameIME_Win32\IngameIME-Common\testWnd\nuklear\include\nuklear_glfw_gl4.h(300,5): error C3861: “glMakeTextureHandleNonResidentARB”: 找不到标识符 [E:\Codes\IngameIME_Win32\IngameIME-Common\testWnd\build\testWnd.vcxproj]
[build] E:\Codes\IngameIME_Win32\IngameIME-Common\testWnd\nuklear\include\nuklear_glfw_gl4.h(436,18): error C3861: “glIsTextureHandleResidentARB”: 找不到标识符 [E:\Codes\IngameIME_Win32\IngameIME-Common\testWnd\build\testWnd.vcxproj]
[build] E:\Codes\IngameIME_Win32\IngameIME-Common\testWnd\nuklear\include\nuklear_glfw_gl4.h(437,17): error C3861: “glMakeTextureHandleResidentARB”: 找不到标识符 [E:\Codes\IngameIME_Win32\IngameIME-Common\testWnd\build\testWnd.vcxproj]
[build] E:\Codes\IngameIME_Win32\IngameIME-Common\testWnd\nuklear\include\nuklear_glfw_gl4.h(439,13): error C3861: “glUniformHandleui64ARB”: 找不到标识符 [E:\Codes\IngameIME_Win32\IngameIME-Common\testWnd\build\testWnd.vcxproj]
[proc] The command: "C:\Program Files\CMake\bin\cmake.EXE" --build e:/Codes/IngameIME_Win32/IngameIME-Common/testWnd/build --config Debug --target testWnd -j 14 -- exited with code: 1
[build] Build finished with exit code 1

尝试编译, 遇到上面的错误, 搜索到这个结果 glGetTextureHandleARB not included?

解决方案: 生成 GLAD 时添加所有的 Extensions

创建窗口

diff --git a/testWnd/src/main.cpp b/testWnd/src/main.cpp
index ff96351..11a4a44 100644
--- a/testWnd/src/main.cpp
+++ b/testWnd/src/main.cpp
@@ -4,8 +4,24 @@
 #include <glad/glad.h>
 #include <GLFW/glfw3.h>

+#define NK_INCLUDE_FIXED_TYPES
+#define NK_INCLUDE_STANDARD_IO
+#define NK_INCLUDE_STANDARD_VARARGS
+#define NK_INCLUDE_DEFAULT_ALLOCATOR
+#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
+#define NK_INCLUDE_FONT_BAKING
+#define NK_INCLUDE_DEFAULT_FONT
+#define NK_BUTTON_TRIGGER_ON_RELEASE
+#define NK_KEYSTATE_BASED_INPUT
+#define NK_IMPLEMENTATION
+#include <nuklear.h>
+#define NK_GLFW_GL4_IMPLEMENTATION
+#include <nuklear_glfw_gl4.h>
+
 void framebuffer_size_callback(GLFWwindow* window, int width, int height);

+struct nk_context* nk;
+
 int main()
 {
     // GLFW Init Start
@@ -19,7 +35,7 @@ int main()
     // GLFW Init End
 
     // Create Window Start
-    GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
+    GLFWwindow* window = glfwCreateWindow(800, 800, "testWnd", NULL, NULL);
     if (!window)
     {
         std::cout << "Failed to create GLFW window" << std::endl;
@@ -39,17 +55,39 @@ int main()
     glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
     // GLAD Init End

+    // Nuklear Init Start
+    nk = nk_glfw3_init(window, NK_GLFW3_INSTALL_CALLBACKS, 512 * 1024, 128 * 1024);
+
+    // Load Font
+    struct nk_font_atlas* atlas;
+    nk_glfw3_font_stash_begin(&atlas);
+    nk_glfw3_font_stash_end();
+    // Nuklear Init End
+
     // Event Loop
     while (!glfwWindowShouldClose(window))
     {
+        // Nuklear Window Start
+        nk_glfw3_new_frame();
+        if (nk_begin(nk,
+                     "main",
+                     nk_rect(800 / 2 - 300, 800 / 2 - 300, 600, 600),
+                     NK_WINDOW_BORDER | NK_WINDOW_MOVABLE | NK_WINDOW_TITLE))
+        {
+        }
+        nk_end(nk);
+        // Nuklear Window End
+
         // Background color
-        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
+        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
         glClear(GL_COLOR_BUFFER_BIT);

+        nk_glfw3_render(NK_ANTI_ALIASING_ON);
         glfwSwapBuffers(window);
         glfwPollEvents();
     }
     glfwTerminate();
     std::exit(EXIT_SUCCESS);
 }
@@ -57,4 +95,5 @@ int main()
 void framebuffer_size_callback(GLFWwindow* window, int width, int height)
 {
     glViewport(0, 0, width, height);
+    nk_window_set_position(nk, "main", nk_vec2(width / 2 - 300, height / 2 - 300));
 }

实际效果

NuklearBasic

主窗体初始化时居中

diff --git a/testWnd/src/main.cpp b/testWnd/src/main.cpp
index 11a4a44..29294b7 100644
--- a/testWnd/src/main.cpp
+++ b/testWnd/src/main.cpp
@@ -35,7 +35,9 @@ int main()
     // GLFW Init End

     // Create Window Start
-    GLFWwindow* window = glfwCreateWindow(800, 800, "testWnd", NULL, NULL);
+    int wnd_w = 800, wnd_h = 800;
+
+    GLFWwindow* window = glfwCreateWindow(wnd_w, wnd_h, "testWnd", NULL, NULL);
     if (!window)
     {
         std::cout << "Failed to create GLFW window" << std::endl;
@@ -44,7 +46,14 @@ int main()
         std::exit(EXIT_FAILURE);
     }
     glfwMakeContextCurrent(window);
-    // Create Window End
+
+    // Place the window at screen center
+    {
+        int x, y, sw, sh;
+        glfwGetMonitorWorkarea(glfwGetPrimaryMonitor(), &x, &y, &sw, &sh);
+        glfwSetWindowPos(window, x + sw / 2 - wnd_w / 2, y + sh / 2 - wnd_h / 2);
+    }
+    //  Create Window End

添加全屏切换按钮

Nuklear控件参考文档

diff --git a/testWnd/src/main.cpp b/testWnd/src/main.cpp
index 29294b7..84e3ef7 100644
--- a/testWnd/src/main.cpp
+++ b/testWnd/src/main.cpp
@@ -35,7 +35,7 @@ int main()
     // GLFW Init End
 
     // Create Window Start
-    int wnd_w = 800, wnd_h = 800;
+    int wnd_w = 800, wnd_h = 800, wnd_x = 0, wnd_y = 0;
 
     GLFWwindow* window = glfwCreateWindow(wnd_w, wnd_h, "testWnd", NULL, NULL);
     if (!window)
@@ -85,6 +85,22 @@ int main()
                      nk_rect(wnd_w / 2 - main_w / 2, wnd_h / 2 - main_h / 2, main_w, main_h),
                      NK_WINDOW_BORDER | NK_WINDOW_MOVABLE | NK_WINDOW_TITLE))
         {
+            nk_layout_row_dynamic(nk, 30, 1);
+            if (nk_button_label(nk, "Toggle Fullscreen"))
+            {
+                if (glfwGetWindowMonitor(window))
+                {
+                    glfwSetWindowMonitor(window, NULL, wnd_x, wnd_y, wnd_w, wnd_h, 0);
+                }
+                else
+                {
+                    GLFWmonitor*       monitor = glfwGetPrimaryMonitor();
+                    const GLFWvidmode* mode    = glfwGetVideoMode(monitor);
+                    glfwGetWindowPos(window, &wnd_x, &wnd_y);
+                    glfwGetWindowSize(window, &wnd_w, &wnd_h);
+                    glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
+                }
+            }
         }
         nk_end(nk);
         // Nuklear Window End

NuklaerToggleFullscreen

中文(CJK)字体显示

支持CJK的字体: https://github.com/googlefonts/noto-cjk

--- a/testWnd/CMakeLists.txt
+++ b/testWnd/CMakeLists.txt
@@ -27,6 +27,9 @@ add_subdirectory(nuklear EXCLUDE_FROM_ALL)
 add_executable(testWnd src/main.cpp)
 target_include_directories(testWnd PRIVATE include)
 target_link_libraries(testWnd PRIVATE glfw glad nuklear)
+add_custom_command(TARGET testWnd POST_BUILD
+    COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_LIST_DIR}/assets $<TARGET_FILE_DIR:testWnd>/assets
+)
diff --git a/testWnd/src/main.cpp b/testWnd/src/main.cpp
index 84e3ef7..1f9a6b2 100644
--- a/testWnd/src/main.cpp
+++ b/testWnd/src/main.cpp
@@ -10,7 +10,6 @@
 #define NK_INCLUDE_DEFAULT_ALLOCATOR
 #define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
 #define NK_INCLUDE_FONT_BAKING
-#define NK_INCLUDE_DEFAULT_FONT
 #define NK_BUTTON_TRIGGER_ON_RELEASE
 #define NK_KEYSTATE_BASED_INPUT
 #define NK_IMPLEMENTATION
@@ -22,6 +21,27 @@ void framebuffer_size_callback(GLFWwindow* window, int width, int height);

 struct nk_context* nk;

+/**
+ * @brief Range of commonly used CJK charactors, if there still missing charactors,
+ * refer to http://www.unicode.org/charts/ and fix the table
+ */
+// clang-format off
+
+const nk_rune ranges[] = {
+    0x0370, 0x03FF, // Greek
+    0x0020, 0x007F, // Basic Latin(ASCII)
+    0x3000, 0x303F, // CJK Symbols and Punctuation
+    0xFF00, 0xFFEF, // Halfwidth and Fullwidth Forms
+    0x4E00, 0x9FFF, // CJK Unified Ideographs (Han)
+    0x1100, 0x11FF, // Hangul Jamo
+    0xAC00, 0xD7AF, // Hangul Syllables
+    0x3040, 0x309F, // Hiragana
+    0x30A0, 0x30FF, // Katakana
+    0
+};
+
+// clang-format on
+
 int main()
 {
     // GLFW Init Start
@@ -68,9 +88,18 @@ int main()
     nk = nk_glfw3_init(window, NK_GLFW3_INSTALL_CALLBACKS, 512 * 1024, 128 * 1024);

     // Load Font
+    struct nk_font_config config = nk_font_config(0);
+    config.range                 = ranges;
+    /* align every character to pixel boundary (if true set oversample (1,1)) */
+    config.oversample_h          = 1;
+    config.oversample_v          = 1;
+    config.pixel_snap            = true;
+
     struct nk_font_atlas* atlas;
     nk_glfw3_font_stash_begin(&atlas);
+    struct nk_font* font = nk_font_atlas_add_from_file(atlas, "assets/fonts/NotoSansCJKsc-VF.ttf", 20, &config);
     nk_glfw3_font_stash_end();
+    nk_style_set_font(nk, &font->handle);
     // Nuklear Init End
@@ -101,6 +134,15 @@ int main()
                     glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
                 }
             }
+
+            // TextEdit
+            static char buffer[256];
+            nk_layout_row_dynamic(nk, 30, 1);
+            nk_edit_string_zero_terminated(nk,
+                                           NK_EDIT_FIELD | NK_EDIT_SIG_ENTER | NK_EDIT_GOTO_END_ON_ACTIVATE,
+                                           buffer,
+                                           sizeof(buffer),
+                                           nk_filter_default);
         }
         nk_end(nk);
         // Nuklear Window End

需要打上这个PR的Patch才能显示正确的中文字符

https://github.com/Immediate-Mode-UI/Nuklear/pull/531

需要打上这个PR才能从文本框中复制出中文字符

https://github.com/Immediate-Mode-UI/Nuklear/pull/543

NuklearCJKFont