Bloat-Free Dear Imgui pro C++
Vytváření uživatelského rozhraní pro desktopové aplikace může být často komplikované a časově náročné. Naštěstí existují nástroje, které tento proces výrazně zjednodušují, a jedním z mých oblíbených je Dear Imgui. Tato jednoduchá knihovna umožňuje rychlé a efektivní vytváření působivých grafických uživatelských rozhraní bez zbytečné složitosti.
Dear Imgui lze snadno integrovat do C++ projektů, díky čemuž není potřeba se trápit s těžkopádným kódem. Pouze přilinkujete knihovnu a můžete začít vytvářet elegantní GUI s minimální námahou.
Příklad: Jednoduchá aplikace pro hádání čísel
Pro lepší ilustraci uvedu příklad jednoduché aplikace na hádání čísel, kterou lze v Dear Imgui snadno vytvořit. Hlavní smyčka aplikace obsahuje veškerou logiku pro dynamické vykreslování prvků uživatelského rozhraní, což je nejen intuitivní, ale i efektivní.
Rozhraní aplikace vytvořené v Dear Imgui
Veškerá "magie" se odehrává v hlavní smyčce, kde se jednotlivé grafické prvky dynamicky generují. Možnosti této knihovny jsou neuvěřitelně široké a poskytují vývojáři volnost při vytváření různých typů aplikací. Za zmínku stojí i demo aplikace, kterou si lze stáhnout přímo z webu projektu ImGui. Obsahuje předpřipravené vizuální prvky a komplexní návod, což umožňuje rychle pochopit, jak knihovnu používat.
Přehledný a efektivní kód
Není potřeba detailně rozebírat zdrojový kód – každý, kdo se již orientuje ve vývoji v C++, pochopí jednoduchost implementace aplikace do okna. Hlavní funkce může sice postupně narůstat, ale i tak zůstává kód přehledný a snadno udržovatelný. Tento přístup minimalizuje potřebu refaktorizace, což šetří čas i úsilí.
I přesto, že je soubor main.cpp
trochu rozsáhlejší, pro základní ukázku, jak lze Dear Imgui využít, zcela postačuje.
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#define GL_SILENCE_DEPRECATION
#if defined(IMGUI_IMPL_OPENGL_ES2)
#include <GLES2/gl2.h>
#endif
#include <GLFW/glfw3.h> // Will drag system OpenGL headers
#include <random>
#include <exception>
#include <stdexcept>
#include <iostream>
#include <filesystem>
#if defined(_MSC_VER) && (_MSC_VER >= 1900) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
#pragma comment(lib, "legacy_stdio_definitions")
#endif
#ifdef __EMSCRIPTEN__
#include "../libs/emscripten/emscripten_mainloop_stub.h"
#endif
static void glfw_error_callback(int error, const char *description)
{
fprintf(stderr, "GLFW Error %d: %s\n", error, description);
}
// Main code
int main(int, char **)
{
glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit())
return 1;
// Decide GL+GLSL versions
#if defined(IMGUI_IMPL_OPENGL_ES2)
// GL ES 2.0 + GLSL 100
const char *glsl_version = "#version 100";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
#elif defined(__APPLE__)
// GL 3.2 + GLSL 150
const char *glsl_version = "#version 150";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
#else
// GL 3.0 + GLSL 130
const char *glsl_version = "#version 130";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
// glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
// glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only
#endif
// Create window with graphics context
GLFWwindow *window = glfwCreateWindow(1280, 720, "Game - Catch Number", nullptr, nullptr);
if (window == nullptr)
return 1;
glfwMakeContextCurrent(window);
glfwSwapInterval(1); // Enable vsync
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO();
(void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
// Setup Dear ImGui style
ImGui::StyleColorsDark();
ImGuiStyle &style = ImGui::GetStyle();
style.ScaleAllSizes(2.5f);
// Setup Platform/Renderer backends
ImGui_ImplGlfw_InitForOpenGL(window, true);
#ifdef __EMSCRIPTEN__
ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas");
#endif
ImGui_ImplOpenGL3_Init(glsl_version);
// Load Fonts
io.Fonts->AddFontFromFileTTF("../imgui/misc/fonts/Maximum Voltage Italic.ttf", 42.0f);
// Our state
bool show_demo_window = false;
bool show_another_window = false;
bool show_catch_number_window = true;
bool show_message_window = true;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution dis(1, 1000);
int randomNumber = dis(gen);
std::string message_text = "";
char input_buffer[256] = "";
auto clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
// Main loop
#ifdef __EMSCRIPTEN__
io.IniFilename = nullptr;
EMSCRIPTEN_MAINLOOP_BEGIN
#else
while (!glfwWindowShouldClose(window))
#endif
{
glfwPollEvents();
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
// 4. Show Catch Number Window
if (show_catch_number_window)
{
ImGui::Begin("Catch NUMBER", &show_catch_number_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
ImGui::Text("from 1 to 1000");
ImGui::Text("Random number is %d", randomNumber);
ImGui::InputText("Enter NUMBER", input_buffer, IM_ARRAYSIZE(input_buffer));
if (ImGui::Button("Check NUMBER") || ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))
{
int input_number(0);
try
{
input_number = std::stoi(input_buffer);
if (input_number == randomNumber)
{
message_text = "Congratulation you catch the number " + std::to_string(randomNumber) + "!";
randomNumber = dis(gen);
}
else if (input_number < randomNumber)
{
show_message_window = true;
message_text = "Try bigger number!";
}
else
{
show_message_window = true;
message_text = "Try smaller number!";
}
}
catch (const std::invalid_argument &e)
{
std::cerr << e.what() << '\n';
message_text = "Enter only NUMBER! Not a text!";
}
}
if (show_message_window)
{
ImGui::Begin("Message", &show_message_window);
ImGui::Text("%s", message_text.c_str());
if (ImGui::Button("Hide this window"))
show_message_window = false;
ImGui::End();
}
if (ImGui::Button("Exit Game"))
{
show_catch_number_window = false;
return 0;
}
ImGui::End();
}
// Rendering
ImGui::Render();
int display_w;
int display_h;
glfwGetFramebufferSize(window, &display_w, &display_h);
glViewport(0, 0, display_w, display_h);
glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(window);
}
#ifdef __EMSCRIPTEN__
EMSCRIPTEN_MAINLOOP_END;
#endif
// Cleanup
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}
Závěr
Dear Imgui je vynikající volbou pro každého vývojáře, který hledá jednoduchý a efektivní způsob, jak vytvářet grafická uživatelská rozhraní v C++. Tato knihovna se postará o všechny detaily, a přitom nabízí maximální flexibilitu a výkon.