ImGui Events

引言

上一节我们讨论了 ImGUI 并运行了官方示例,今天我们来为我们的引擎编写 ImGUI 事件。

事件声明

在头文件 ImGuiLayer.h 声明 ImGUI 的交互事件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#pragma once

#include "../Layer.h"

#include "../Events/KeyEvent.h"
#include "../Events/MouseEvent.h"
#include "../Events/ApplicationEvent.h"

namespace Infinite {

class INFINITE_API ImGuiLayer : public Layer
{
public:
ImGuiLayer();
~ImGuiLayer();

void OnAttach();
void OnDetach();
void OnUpdate();
void OnEvent(Event& event);
private:
+ bool OnMouseButtonPressedEvent(MouseButtonPressedEvent& e);
+ bool OnMouseButtonReleasedEvent(MouseButtonReleasedEvent& e);
+ bool OnMouseMovedEvent(MouseMovedEvent& e);
+ bool OnMouseScrolledEvent(MouseScrolledEvent& e);
+ bool OnKeyPressedEvent(KeyPressedEvent& e);
+ bool OnKeyReleasedEvent(KeyReleasedEvent& e);
+ bool OnKeyTypedEvent(KeyTypedEvent& e);
+ bool OnWindowResizeEvent(WindowResizeEvent& e);

private:
float m_Time = 0.0f;

};
}

事件实现

ImGuiLayer.cpp 中添加头文件和事件的模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include "ifnpch.h"
#include "ImGuiLayer.h"

#include "../imgui.h"
#include "Platform/OpenGL/ImGuiOpenGLRenderer.h"

// TEMPORARY
+#include <GLFW/glfw3.h>
+#include <glad/glad.h>

#include "../Application.h"

namespace Infinite {

ImGuiLayer::ImGuiLayer()
: Layer("ImGuiLayer")
{

}

ImGuiLayer::~ImGuiLayer()
{

}

void ImGuiLayer::OnAttach()
{
ImGui::CreateContext();
ImGui::StyleColorsDark();

ImGuiIO& io = ImGui::GetIO();
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos;

// TEMPORARY: should eventually use Infinite key codes
io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP;
io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN;
io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP;
io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN;
io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME;
io.KeyMap[ImGuiKey_End] = GLFW_KEY_END;
io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT;
io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE;
io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE;
io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE;
io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER;
io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE;
io.KeyMap[ImGuiKey_A] = GLFW_KEY_A;
io.KeyMap[ImGuiKey_C] = GLFW_KEY_C;
io.KeyMap[ImGuiKey_V] = GLFW_KEY_V;
io.KeyMap[ImGuiKey_X] = GLFW_KEY_X;
io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y;
io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z;

ImGui_ImplOpenGL3_Init("#version 410");
}

void ImGuiLayer::OnDetach()
{

}

void ImGuiLayer::OnUpdate()
{
ImGuiIO& io = ImGui::GetIO();
Application& app = Application::Get();
io.DisplaySize = ImVec2(app.GetWindow().GetWidth(), app.GetWindow().GetHeight());

float time = (float)glfwGetTime();
io.DeltaTime = m_Time > 0.0f ? (time - m_Time) : (1.0f / 60.0f);
m_Time = time;

ImGui_ImplOpenGL3_NewFrame();
ImGui::NewFrame();

static bool show = true;
ImGui::ShowDemoWindow(&show);

ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
}

+ void ImGuiLayer::OnEvent(Event& event)
+ {
+
+ }

+ bool ImGuiLayer::OnMouseButtonPressedEvent(MouseButtonPressedEvent& e)
+ {
+
+ }

+ bool ImGuiLayer::OnMouseButtonReleasedEvent(MouseButtonReleasedEvent& e)
+ {
+
+ }

+ bool ImGuiLayer::OnMouseMovedEvent(MouseMovedEvent& e)
+ {
+
+ }

+ bool ImGuiLayer::OnMouseScrolledEvent(MouseScrolledEvent& e)
+ {
+
+ }

+ bool ImGuiLayer::OnKeyPressedEvent(KeyPressedEvent& e)
+ {
+
+ }

+ bool ImGuiLayer::OnKeyReleasedEvent(KeyReleasedEvent& e)
+ {
+
+ }

+ bool ImGuiLayer::OnKeyTypedEvent(KeyTypedEvent& e)
+ {
+
+ }



+ bool ImGuiLayer::OnWindowResizeEvent(WindowResizeEvent& e)
+ {
+
+ }
}

下面我们依次实现这些事件:

Event

1
2
3
4
5
6
7
8
9
10
11
12
void ImGuiLayer::OnEvent(Event& event)
{
EventDispatcher dispatcher(event);
dispatcher.Dispatch<MouseButtonPressedEvent>(IFN_BIND_EVENT_FN(ImGuiLayer::OnMouseButtonPressedEvent));
dispatcher.Dispatch<MouseButtonReleasedEvent>(IFN_BIND_EVENT_FN(ImGuiLayer::OnMouseButtonReleasedEvent));
dispatcher.Dispatch<MouseMovedEvent>(IFN_BIND_EVENT_FN(ImGuiLayer::OnMouseMovedEvent));
dispatcher.Dispatch<MouseScrolledEvent>(IFN_BIND_EVENT_FN(ImGuiLayer::OnMouseScrolledEvent));
dispatcher.Dispatch<KeyPressedEvent>(IFN_BIND_EVENT_FN(ImGuiLayer::OnKeyPressedEvent));
dispatcher.Dispatch<KeyTypedEvent>(IFN_BIND_EVENT_FN(ImGuiLayer::OnKeyTypedEvent));
dispatcher.Dispatch<KeyReleasedEvent>(IFN_BIND_EVENT_FN(ImGuiLayer::OnKeyReleasedEvent));
dispatcher.Dispatch<WindowResizeEvent>(IFN_BIND_EVENT_FN(ImGuiLayer::OnWindowResizeEvent));
}

这里的 IFN_BIND_EVENT_FN 则在 Core.h 中定义:

1
#define IFN_BIND_EVENT_FN(fn) std::bind(&fn, this, std::placeholders::_1)

Mouse

MouseButtonPressed
1
2
3
4
5
6
7
bool ImGuiLayer::OnMouseButtonPressedEvent(MouseButtonPressedEvent& e)
{
ImGuiIO& io = ImGui::GetIO();
io.MouseDown[e.GetMouseButton()] = true;

return false;
}
MouseButtonReleased
1
2
3
4
5
6
7
bool ImGuiLayer::OnMouseButtonReleasedEvent(MouseButtonReleasedEvent& e)
{
ImGuiIO& io = ImGui::GetIO();
io.MouseDown[e.GetMouseButton()] = false;

return false;
}
MouseMoved
1
2
3
4
5
6
7
bool ImGuiLayer::OnMouseMovedEvent(MouseMovedEvent& e)
{
ImGuiIO& io = ImGui::GetIO();
io.MousePos = ImVec2(e.GetX(), e.GetY());

return false;
}
MouseScrolled
1
2
3
4
5
6
7
8
bool ImGuiLayer::OnMouseScrolledEvent(MouseScrolledEvent& e)
{
ImGuiIO& io = ImGui::GetIO();
io.MouseWheelH += e.GetXOffset();
io.MouseWheel += e.GetYOffset();

return false;
}

Key

KeyPressed
1
2
3
4
5
6
7
8
9
10
11
bool ImGuiLayer::OnKeyPressedEvent(KeyPressedEvent& e)
{
ImGuiIO& io = ImGui::GetIO();
io.KeysDown[e.GetKeyCode()] = true;

io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL];
io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT];
io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT];
io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER];
return false;
}
KeyReleased
1
2
3
4
5
6
7
bool ImGuiLayer::OnKeyReleasedEvent(KeyReleasedEvent& e)
{
ImGuiIO& io = ImGui::GetIO();
io.KeysDown[e.GetKeyCode()] = false;

return false;
}
KeyTyped

KeyTyped 的实现比较复杂,首先我们需要先在 Event.h 添加 KeyTyped 枚举类型:

1
2
3
4
5
6
7
8
	enum class EventType
{
None = 0,
WindowClose, WindowResize, WindowFocus, WindowLostFocus, WindowMoved,
AppTick, AppUpdate, AppRender,
+ KeyPressed, KeyReleased, KeyTyped,
MouseButtonPressed, MouseButtonReleased, MouseMoved, MouseScrolled
};

接着在 KeyEvent.h 中添加事件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class INFINITE_API KeyTypedEvent : public KeyEvent
{
public:
KeyTypedEvent(int keycode)
: KeyEvent(keycode) {}

std::string ToString() const override
{
std::stringstream ss;
ss << "KeyTypedEvent: " << m_KeyCode;
return ss.str();
}

EVENT_CLASS_TYPE(KeyTyped)
};

最后回到 ImGuiLayer.cpp 实现事件:

1
2
3
4
5
6
7
8
9
bool ImGuiLayer::OnKeyTypedEvent(KeyTypedEvent& e)
{
ImGuiIO& io = ImGui::GetIO();
int keycode = e.GetKeyCode();
if (keycode > 0 && keycode < 0x10000)
io.AddInputCharacter((unsigned short)keycode);

return false;
}

Windows

1
2
3
4
5
6
7
8
9
bool ImGuiLayer::OnWindowResizeEvent(WindowResizeEvent& e)
{
ImGuiIO& io = ImGui::GetIO();
io.DisplaySize = ImVec2(e.GetWidth(), e.GetHeight());
io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
glViewport(0, 0 , e.GetWidth(), e.GetHeight());

return false;
}

预生成文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
workspace "Infinite"
architecture "x64"
startproject "Sandbox"

configurations
{
"Debug",
"Release",
"Dist"
}

outputdir = "%{cfg.buildcfg}-%{cfg.system}-%{cfg.architecture}"

-- Include directories relative to root folder (solution directory)
IncludeDir = {}
IncludeDir["GLFW"] = "Infinite/vendor/GLFW/include"
IncludeDir["GLAD"] = "Infinite/vendor/GLAD/include"
IncludeDir["ImGui"] = "Infinite/vendor/imgui/include"

include "Infinite/vendor/GLFW"
include "Infinite/vendor/GLAD"
include "Infinite/vendor/imgui"

project "Infinite"
location "Infinite"
kind "SharedLib"
language "C++"
+ staticruntime "off"

targetdir ("bin/" .. outputdir .. "/%{prj.name}")
objdir ("bin-int/" .. outputdir .. "/%{prj.name}")

pchheader "ifnpch.h"
pchsource "Infinite/src/ifnpch.cpp"

files
{
"%{prj.name}/src/**.h",
"%{prj.name}/src/**.cpp"
}

includedirs
{
"%{prj.name}/src",
"%{prj.name}/vendor/spdlog/include",
"%{IncludeDir.GLFW}",
"%{IncludeDir.GLAD}",
"%{IncludeDir.ImGui}"
}

links
{
"GLFW",
"GLAD",
"ImGui",
"opengl32.lib"
}

filter "system:windows"
cppdialect "C++17"
systemversion "latest"

defines
{
"IFN_PLATFORM_WINDOWS",
"IFN_BUILD_DLL",
"GLFW_INCLUDE_NONE"
}

postbuildcommands
{
+ ("{COPY} %{cfg.buildtarget.relpath} \"../bin/" .. outputdir .. "/Sandbox/\"")
}

filter "configurations:Debug"
defines "IFN_DEBUG"
+ runtime "Debug"
symbols "On"

filter "configurations:Release"
defines "IFN_RELEASE"
+ runtime "Release"
optimize "On"

filter "configurations:Dist"
defines "IFN_DIST"
+ runtime "Release"
optimize "On"



project "Sandbox"
location "Sandbox"
kind "ConsoleApp"
+ staticruntime "off"

language "C++"

targetdir ("bin/" .. outputdir .. "/%{prj.name}")
objdir ("bin-int/" .. outputdir .. "/%{prj.name}")

files
{
"%{prj.name}/src/**.h",
"%{prj.name}/src/**.cpp"
}

includedirs
{
"Infinite/vendor/spdlog/include",
"Infinite/src/Infinite",
"Infinite/src/Events",
"Infinite/src/**.h",
"Infinite/src/**.cpp"
}

links
{
"Infinite"
}

filter "system:windows"
cppdialect "C++17"
systemversion "latest"

defines
{
"IFN_PLATFORM_WINDOWS"
}

filter "configurations:Debug"
defines "IFN_DEBUG"
+ runtime "Debug"
symbols "On"

filter "configurations:Release"
defines "IFN_RELEASE"
+ runtime "Release"
optimize "On"

filter "configurations:Dist"
defines "IFN_DIST"
+ runtime "Release"
optimize "On"

调试

在调试过程中由于没有给事件实现添加作用域,运行时报错外部符号无法解析,以后需要多加注意。

Mouse


Key