#include "gui.hpp" #include "backends/imgui_impl_sdl2.h" #include "backends/imgui_impl_opengl3.h" namespace GraphC::gui { f32 GUI::getDPI(){ int w=0, h=0; SDL_GL_GetDrawableSize(sdl_window, &w, &h); int sim_w=0, sim_h=0; SDL_GetWindowSize(sdl_window, &sim_w, &sim_h); f32 wdpi=(f32)w / sim_w; f32 hdpi=(f32)h / sim_h; f32 dpi=SDL_sqrtf(wdpi*wdpi + hdpi*hdpi); return dpi; } void GUI::init(const char* window_title){ SDL_TRY(SDL_Init(SDL_INIT_VIDEO)); SDL_version v; SDL_GetVersion(&v); kprintf("SDL version: %u.%u.%u\n", v.major, v.minor, v.patch); // GL 3.0 + GLSL 130 const char* glsl_version = "#version 130"; SDL_TRY( SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0)); SDL_TRY( SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE)); SDL_TRY( SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3)); SDL_TRY( SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0)); // From 2.0.18: Enable native IME. #ifdef SDL_HINT_IME_SHOW_UI SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); #endif // Create window with graphics context SDL_TRY( SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1)); SDL_TRY( SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24)); SDL_TRY( SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8)); SDL_WindowFlags window_flags = (SDL_WindowFlags)( SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); sdl_window = SDL_CreateWindow(window_title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); if(sdl_window == nullptr) throw SDLException(); gl_context = SDL_GL_CreateContext(sdl_window); if(gl_context == nullptr) throw SDLException(); SDL_TRY( SDL_GL_MakeCurrent(sdl_window, gl_context)); SDL_TRY( SDL_GL_SetSwapInterval(1)); // Enable vsync // Setup Dear ImGui context IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking // io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows io.ConfigDockingTransparentPayload = true; io.ConfigInputTextCursorBlink = false; // io.ConfigViewportsNoTaskBarIcon = true; io.ConfigWindowsMoveFromTitleBarOnly = true; // Setup Platform/Renderer backends if(ImGui_ImplSDL2_InitForOpenGL(sdl_window, gl_context) != true) throw SDLException(); if(ImGui_ImplOpenGL3_Init(glsl_version) != true) throw SDLException(); // Setup Dear ImGui style ImGui::StyleColorsDark(); f32 dpi = getDPI(); io.FontDefault=fonts:: ImFont_LoadEmbedded(default_font, default_font_size, dpi); fonts:: ImFont_LoadEmbedded("Cousine-Regular", default_font_size, dpi); ImNodes::CreateContext(); ImNodes::StyleColorsDark(); ImNodes::PushAttributeFlag(ImNodesAttributeFlags_EnableLinkDetachWithDragClick); node_editor = new GraphEditor("node editor"); node_editor->show(); } // Wait, poll and handle events (inputs, window resize, etc.) void GUI::poll_events(u16& frame_updates_requested, bool wait){ SDL_Event event; if(wait){ // waits for first event in cpu-efficient way SDL_TRY(SDL_WaitEvent(&event) != 1); } // dont wait for event else if(!SDL_PollEvent(&event)) return; do { if(ImGui_ImplSDL2_ProcessEvent(&event)) frame_updates_requested=2; switch(event.type){ case SDL_QUIT: { close(); break; } case SDL_WINDOWEVENT: { if(event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(sdl_window)){ close(); } break; } } } while (SDL_PollEvent(&event)); // if there are more events, handles them } void ImGui_drawErrorWindow(bool* draw_error_window, std::string& msg){ ImGui::Begin("ERROR", draw_error_window); ImGui::Text("%s", msg.c_str()); ImGui::End(); } void GUI::draw_ui(){ static std::string error_message; static bool draw_error_window = false; try { // Draw UI if(draw_error_window) ImGui_drawErrorWindow(&draw_error_window, error_message); else { draw_bg_window(); draw_debug_window(); node_editor->draw(); } } catch(const std::exception& e){ error_message = "Catched exception: " + std::string(e.what()); draw_error_window = true; std::cerr<= prev_update_time_ms + 1000/fps_min){ // if frame rate < fps_min then requests frame draw // works only if main_loop_wait_for_input = false frame_updates_requested=1; prev_update_time_ms=update_time_ms; } else { // skips frame rendering and waits to limit fps u32 frame_delay_ms=1000/fps_max; SDL_Delay(frame_delay_ms); continue; } } // deaws requested number of frames while(frame_updates_requested>0) { draw_frame(); frame_updates_requested--; } } // Cleanup GUI::destroy(); } void GUI::close(){ loop_running=false; } void GUI::destroy(){ ImNodes::DestroyContext(); ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); SDL_GL_DeleteContext(gl_context); SDL_DestroyWindow(sdl_window); SDL_Quit(); } void GUI::draw_bg_window(){ const ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_PassthruCentralNode; const ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoBackground; // not dockable window that always bound to viewport ImGuiViewport* viewport = ImGui::GetWindowViewport(); ImGui::SetNextWindowPos(viewport->Pos, ImGuiCond_Always); ImGui::SetNextWindowSize(viewport->Size, ImGuiCond_Always); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::Begin("bg_window", nullptr, window_flags); ImGui::PopStyleVar(3); // DockSpace ImGuiID dockspace_id = ImGui::GetID("bg_dockspace"); ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags); // MenuBar if(ImGui::BeginMainMenuBar()){ if(ImGui::BeginMenu("test")){ if(ImGui::MenuItem("throw exception")){ ImGui::EndMenu(); ImGui::EndMainMenuBar(); ImGui::End(); throw UsefulException("example exception"); } if(ImGui::MenuItem("throw const char*")){ ImGui::EndMenu(); ImGui::EndMainMenuBar(); ImGui::End(); throw "cptr"; } if(ImGui::MenuItem("throw std::string")){ ImGui::EndMenu(); ImGui::EndMainMenuBar(); ImGui::End(); throw std::string("str"); } if(ImGui::MenuItem("throw unknown")){ ImGui::EndMenu(); ImGui::EndMainMenuBar(); ImGui::End(); throw 111; } ImGui::EndMenu(); } ImGui::EndMainMenuBar(); } ImGui::End(); } void GUI::draw_debug_window(){ ImGuiIO& io = ImGui::GetIO(); ImGui::Begin("Debug Options"); ImGui::ColorEdit3("clear_color", (float*)&clear_color); ImGui::Checkbox("main_loop_wait_for_input", &main_loop_wait_for_input); ImGui::Text("Application average %.3f ms/frame (%.2f FPS)", 1000.0f / io.Framerate, io.Framerate); ImGui::Checkbox("Demo Window", &show_demo_window); ImGui::Checkbox("Metrics/Debug Window", &show_metrics_window); ImGui::End(); if (show_demo_window) ImGui::ShowDemoWindow(&show_demo_window); if (show_metrics_window) ImGui::ShowMetricsWindow(&show_metrics_window); } }