ImGui Events

引言

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

事件声明

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

#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 中添加头文件和事件的模板:

#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

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 中定义:

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

Mouse

MouseButtonPressed
bool ImGuiLayer::OnMouseButtonPressedEvent(MouseButtonPressedEvent& e)
{
ImGuiIO& io = ImGui::GetIO();
io.MouseDown[e.GetMouseButton()] = true;

return false;
}
MouseButtonReleased
bool ImGuiLayer::OnMouseButtonReleasedEvent(MouseButtonReleasedEvent& e)
{
ImGuiIO& io = ImGui::GetIO();
io.MouseDown[e.GetMouseButton()] = false;

return false;
}
MouseMoved
bool ImGuiLayer::OnMouseMovedEvent(MouseMovedEvent& e)
{
ImGuiIO& io = ImGui::GetIO();
io.MousePos = ImVec2(e.GetX(), e.GetY());

return false;
}
MouseScrolled
bool ImGuiLayer::OnMouseScrolledEvent(MouseScrolledEvent& e)
{
ImGuiIO& io = ImGui::GetIO();
io.MouseWheelH += e.GetXOffset();
io.MouseWheel += e.GetYOffset();

return false;
}

Key

KeyPressed
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
bool ImGuiLayer::OnKeyReleasedEvent(KeyReleasedEvent& e)
{
ImGuiIO& io = ImGui::GetIO();
io.KeysDown[e.GetKeyCode()] = false;

return false;
}
KeyTyped

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

	enum class EventType
{
None = 0,
WindowClose, WindowResize, WindowFocus, WindowLostFocus, WindowMoved,
AppTick, AppUpdate, AppRender,
+ KeyPressed, KeyReleased, KeyTyped,
MouseButtonPressed, MouseButtonReleased, MouseMoved, MouseScrolled
};

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

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 实现事件:

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

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;
}

预生成文件

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