Modern menu support (#639)

* Added DrawContents to GuiElement to be callable from GuiElement references (console, stats, etc, from port code).
Applied DrawContents to Stats, GfxDebugger and Console windows.

* Implement forced cursor visibility.
Implement cursor visibility for menu.
Implement gamepad menu navigation (toggle in settings, and application to menu).
Will need refinement to not rely on menu CVar soon.

* Add mMenu and GetMenu() functionality. Includes function for checking if either menu or menubar is open for purposes of gamepad capture and cursor display.
No longer relies on CVar from 2ship for functionality.

* clang-format

* Add cursor visibility restoration to SetMenu.

* Started process of abstracting GuiElement
Moved Begin and End from DrawElement to Draw in GuiWindow.
Moved CVar visibility stuff from Element to Window

* Finished abstracting GuiElement.
Moved Draw logic, including visibility CVar, to GuiMenuBar and GuiWindow.
DrawElements is public for now. Not sure if it will remain that way.
Removed all DrawContents references.

* clang-format

* Move cursor visibility stuff to Gui::Window.
Add optional structure for having the gamepad toggle button trigger the menu instead of the menubar.
Moved all menu/menubar opening into Gui.cpp (not needed in port anymore).

* Update src/window/gui/GuiWindow.cpp

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update src/window/gui/GuiWindow.cpp

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* tidy

---------

Co-authored-by: Kenix3 <kenixwhisperwind@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Malkierian
2024-06-30 19:15:47 -04:00
committed by GitHub
co-authored by github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Kenix3 github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
parent 31e9b009f9
commit 181465d2ab
17 changed files with 219 additions and 82 deletions
+2 -2
View File
@@ -65,8 +65,8 @@ bool ControlDeck::AllGameInputBlocked() {
bool ControlDeck::GamepadGameInputBlocked() {
// block controller input when using the controller to navigate imgui menus
return AllGameInputBlocked() ||
(CVarGetInteger(CVAR_MENU_BAR_OPEN, 0) && CVarGetInteger(CVAR_IMGUI_CONTROLLER_NAV, 0));
return AllGameInputBlocked() || Context::GetInstance()->GetWindow()->GetGui()->GetMenuOrMenubarVisible() &&
CVarGetInteger(CVAR_IMGUI_CONTROLLER_NAV, 0);
}
bool ControlDeck::KeyboardGameInputBlocked() {
+4 -1
View File
@@ -74,6 +74,8 @@ void Fast3dWindow::Init() {
height = Ship::Context::GetInstance()->GetConfig()->GetInt("Window.Height", 480);
}
SetForceCursorVisibility(CVarGetInteger("gForceCursorVisibility", 0));
InitWindowManager();
gfx_init(mWindowManagerApi, mRenderingApi, Ship::Context::GetInstance()->GetName().c_str(), isFullscreen, width,
@@ -252,7 +254,8 @@ void Fast3dWindow::OnFullscreenChanged(bool isNowFullscreen) {
if (isNowFullscreen) {
auto menuBar = wnd->GetGui()->GetMenuBar();
wnd->SetCursorVisibility(menuBar && menuBar->IsVisible());
wnd->SetCursorVisibility(menuBar && menuBar->IsVisible() || wnd->ShouldForceCursorVisibility() ||
CVarGetInteger("gWindows.Menu", 0));
} else {
wnd->SetCursorVisibility(true);
}
+10
View File
@@ -79,6 +79,16 @@ bool Window::IsAvailableWindowBackend(int32_t backendId) {
mAvailableWindowBackends->end();
}
bool Window::ShouldForceCursorVisibility() {
return mForceCursorVisibility;
}
void Window::SetForceCursorVisibility(bool visible) {
mForceCursorVisibility = visible;
CVarSetInteger("gForceCursorVisibility", visible);
CVarSave();
}
void Window::SetWindowBackend(WindowBackend backend) {
mWindowBackend = backend;
Context::GetInstance()->GetConfig()->SetWindowBackend(GetWindowBackend());
+3
View File
@@ -49,6 +49,8 @@ class Window {
float GetCurrentAspectRatio();
void SaveWindowToConfig();
std::shared_ptr<Gui> GetGui();
bool ShouldForceCursorVisibility();
void SetForceCursorVisibility(bool visible);
protected:
void SetWindowBackend(WindowBackend backend);
@@ -62,5 +64,6 @@ class Window {
// Hold a reference to Config because Window has a Save function called on Context destructor, where the singleton
// is no longer available.
std::shared_ptr<Config> mConfig;
bool mForceCursorVisibility;
};
} // namespace Ship
-4
View File
@@ -260,9 +260,6 @@ void ConsoleWindow::UpdateElement() {
void ConsoleWindow::DrawElement() {
bool inputFocus = false;
ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
ImGui::Begin("Console", &mIsVisible, ImGuiWindowFlags_NoFocusOnAppearing);
const ImVec2 pos = ImGui::GetWindowPos();
const ImVec2 size = ImGui::GetWindowSize();
@@ -458,7 +455,6 @@ void ConsoleWindow::DrawElement() {
}
ImGui::PopItemWidth();
}
ImGui::End();
}
void ConsoleWindow::Dispatch(const std::string& line) {
+1 -1
View File
@@ -30,12 +30,12 @@ class ConsoleWindow : public GuiWindow {
void Append(const std::string& channel, spdlog::level::level_enum priority, const char* fmt, ...);
std::string GetCurrentChannel();
void ClearBindings();
void DrawElement() override;
protected:
void Append(const std::string& channel, spdlog::level::level_enum priority, const char* fmt, va_list args);
void InitElement() override;
void UpdateElement() override;
void DrawElement() override;
private:
struct ConsoleLine {
-5
View File
@@ -408,9 +408,6 @@ void GfxDebuggerWindow::DrawDisas() {
void GfxDebuggerWindow::DrawElement() {
auto dbg = Ship::Context::GetInstance()->GetGfxDebugger();
ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
ImGui::Begin("GFX Debugger", &mIsVisible, ImGuiWindowFlags_NoFocusOnAppearing);
// const ImVec2 pos = ImGui::GetWindowPos();
// const ImVec2 size = ImGui::GetWindowSize();
@@ -429,8 +426,6 @@ void GfxDebuggerWindow::DrawElement() {
DrawDisas();
}
}
ImGui::End();
}
} // namespace LUS
+54 -10
View File
@@ -83,11 +83,13 @@ Gui::Gui(std::vector<std::shared_ptr<GuiWindow>> guiWindows) : mNeedsConsoleVari
}
if (GetGuiWindow("Console") == nullptr) {
AddGuiWindow(std::make_shared<ConsoleWindow>(CVAR_CONSOLE_WINDOW_OPEN, "Console"));
AddGuiWindow(std::make_shared<ConsoleWindow>(CVAR_CONSOLE_WINDOW_OPEN, "Console", ImVec2(520, 600),
ImGuiWindowFlags_NoFocusOnAppearing));
}
if (GetGuiWindow("GfxDebuggerWindow") == nullptr) {
AddGuiWindow(std::make_shared<LUS::GfxDebuggerWindow>(CVAR_GFX_DEBUGGER_WINDOW_OPEN, "GfxDebuggerWindow"));
AddGuiWindow(std::make_shared<LUS::GfxDebuggerWindow>(CVAR_GFX_DEBUGGER_WINDOW_OPEN, "GfxDebuggerWindow",
ImVec2(520, 600)));
}
}
@@ -134,7 +136,7 @@ void Gui::Init(GuiWindowInitData windowImpl) {
mImGuiIo->ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
}
if (CVarGetInteger(CVAR_IMGUI_CONTROLLER_NAV, 0) && GetMenuBar() && GetMenuBar()->IsVisible()) {
if (CVarGetInteger(CVAR_IMGUI_CONTROLLER_NAV, 0) && GetMenuOrMenubarVisible()) {
mImGuiIo->ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
} else {
mImGuiIo->ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad;
@@ -289,7 +291,7 @@ void Gui::BlockImGuiGamepadNavigation() {
}
void Gui::UnblockImGuiGamepadNavigation() {
if (CVarGetInteger(CVAR_IMGUI_CONTROLLER_NAV, 0) && GetMenuBar() && GetMenuBar()->IsVisible()) {
if (CVarGetInteger(CVAR_IMGUI_CONTROLLER_NAV, 0) && GetMenuOrMenubarVisible()) {
mImGuiIo->ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
}
}
@@ -337,13 +339,19 @@ void Gui::DrawMenu() {
ImGui::DockSpace(dockId, ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_None | ImGuiDockNodeFlags_NoDockingInCentralNode);
if (ImGui::IsKeyPressed(TOGGLE_BTN) ||
if (ImGui::IsKeyPressed(TOGGLE_BTN) || ImGui::IsKeyPressed(ImGuiKey_Escape) ||
(ImGui::IsKeyPressed(TOGGLE_PAD_BTN) && CVarGetInteger(CVAR_IMGUI_CONTROLLER_NAV, 0))) {
GetMenuBar()->ToggleVisibility();
if (wnd->IsFullscreen()) {
Context::GetInstance()->GetWindow()->SetCursorVisibility(GetMenuBar() && GetMenuBar()->IsVisible());
if (ImGui::IsKeyPressed(TOGGLE_BTN) || (ImGui::IsKeyPressed(TOGGLE_PAD_BTN) && !GetPadBtnTogglesMenu())) {
GetMenuBar()->ToggleVisibility();
} else if (ImGui::IsKeyPressed(ImGuiKey_Escape) ||
(ImGui::IsKeyPressed(TOGGLE_PAD_BTN) && GetPadBtnTogglesMenu())) {
GetMenu()->ToggleVisibility();
}
if (CVarGetInteger(CVAR_IMGUI_CONTROLLER_NAV, 0) && GetMenuBar() && GetMenuBar()->IsVisible()) {
if (wnd->IsFullscreen()) {
Context::GetInstance()->GetWindow()->SetCursorVisibility(GetMenuOrMenubarVisible() ||
wnd->ShouldForceCursorVisibility());
}
if (CVarGetInteger(CVAR_IMGUI_CONTROLLER_NAV, 0) && GetMenuOrMenubarVisible()) {
mImGuiIo->ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
} else {
mImGuiIo->ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad;
@@ -370,6 +378,10 @@ void Gui::DrawMenu() {
GetMenuBar()->Draw();
}
if (GetMenu()) {
GetMenu()->Draw();
}
ImGui::End();
for (auto& windowIter : mGuiWindows) {
@@ -898,11 +910,43 @@ void Gui::SetMenuBar(std::shared_ptr<GuiMenuBar> menuBar) {
}
if (Context::GetInstance()->GetWindow()->IsFullscreen()) {
Context::GetInstance()->GetWindow()->SetCursorVisibility(GetMenuBar() && GetMenuBar()->IsVisible());
Context::GetInstance()->GetWindow()->SetCursorVisibility(
(GetMenuBar() && GetMenuBar()->IsVisible()) ||
Context::GetInstance()->GetWindow()->ShouldForceCursorVisibility());
}
}
void Gui::SetMenu(std::shared_ptr<GuiWindow> menu) {
mMenu = menu;
if (GetMenu()) {
GetMenu()->Init();
}
if (Context::GetInstance()->GetWindow()->IsFullscreen()) {
Context::GetInstance()->GetWindow()->SetCursorVisibility(
(GetMenu() && GetMenu()->IsVisible()) ||
Context::GetInstance()->GetWindow()->ShouldForceCursorVisibility());
}
}
std::shared_ptr<GuiMenuBar> Gui::GetMenuBar() {
return mMenuBar;
}
bool Gui::GetMenuOrMenubarVisible() {
return (GetMenuBar() && GetMenuBar()->IsVisible()) || (GetMenu() && GetMenu()->IsVisible());
}
std::shared_ptr<GuiWindow> Gui::GetMenu() {
return mMenu;
}
bool Gui::GetPadBtnTogglesMenu() {
return mPadBtnTogglesMenu;
}
void Gui::SetPadBtnTogglesMenu() {
mPadBtnTogglesMenu = true;
}
} // namespace Ship
+7
View File
@@ -98,6 +98,11 @@ class Gui {
bool ImGuiGamepadNavigationEnabled();
void BlockImGuiGamepadNavigation();
void UnblockImGuiGamepadNavigation();
void SetMenu(std::shared_ptr<GuiWindow> menu);
bool GetMenuOrMenubarVisible();
std::shared_ptr<GuiWindow> GetMenu();
bool GetPadBtnTogglesMenu();
void SetPadBtnTogglesMenu();
protected:
void ImGuiWMInit();
@@ -115,8 +120,10 @@ class Gui {
bool mNeedsConsoleVariableSave;
std::shared_ptr<GameOverlay> mGameOverlay;
std::shared_ptr<GuiMenuBar> mMenuBar;
std::shared_ptr<GuiWindow> mMenu;
std::unordered_map<std::string, GuiTextureMetadata> mGuiTextures;
std::map<std::string, std::shared_ptr<GuiWindow>> mGuiWindows;
bool mPadBtnTogglesMenu = false;
};
} // namespace Ship
+2 -42
View File
@@ -3,21 +3,10 @@
#include "libultraship/libultraship.h"
namespace Ship {
GuiElement::GuiElement(const std::string& visibilityConsoleVariable, bool isVisible)
: mIsInitialized(false), mIsVisible(isVisible), mVisibilityConsoleVariable(visibilityConsoleVariable) {
if (!mVisibilityConsoleVariable.empty()) {
mIsVisible = CVarGetInteger(mVisibilityConsoleVariable.c_str(), mIsVisible);
SyncVisibilityConsoleVariable();
}
GuiElement::GuiElement(bool isVisible) : mIsInitialized(false), mIsVisible(isVisible) {
}
GuiElement::GuiElement(const std::string& visibilityConsoleVariable) : GuiElement(visibilityConsoleVariable, false) {
}
GuiElement::GuiElement(bool isVisible) : GuiElement("", isVisible) {
}
GuiElement::GuiElement() : GuiElement("", false) {
GuiElement::GuiElement() : GuiElement(false) {
}
GuiElement::~GuiElement() {
@@ -32,41 +21,12 @@ void GuiElement::Init() {
mIsInitialized = true;
}
void GuiElement::Draw() {
if (!IsVisible()) {
return;
}
DrawElement();
// Sync up the IsVisible flag if it was changed by ImGui
SyncVisibilityConsoleVariable();
}
void GuiElement::Update() {
UpdateElement();
}
void GuiElement::SyncVisibilityConsoleVariable() {
if (mVisibilityConsoleVariable.empty()) {
return;
}
bool shouldSave = CVarGetInteger(mVisibilityConsoleVariable.c_str(), 0) != IsVisible();
if (IsVisible()) {
CVarSetInteger(mVisibilityConsoleVariable.c_str(), IsVisible());
} else {
CVarClear(mVisibilityConsoleVariable.c_str());
}
if (shouldSave) {
Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
}
void GuiElement::SetVisiblity(bool visible) {
mIsVisible = visible;
SyncVisibilityConsoleVariable();
}
void GuiElement::Show() {
+3 -8
View File
@@ -5,14 +5,12 @@
namespace Ship {
class GuiElement {
public:
GuiElement(const std::string& visibilityConsoleVariable, bool isVisible);
GuiElement(const std::string& visibilityConsoleVariable);
GuiElement(bool isVisible);
GuiElement();
~GuiElement();
void Init();
void Draw();
virtual void Draw() = 0;
void Update();
void Show();
@@ -20,19 +18,16 @@ class GuiElement {
void ToggleVisibility();
bool IsVisible();
bool IsInitialized();
virtual void DrawElement() = 0;
protected:
virtual void InitElement() = 0;
virtual void DrawElement() = 0;
virtual void UpdateElement() = 0;
void SetVisiblity(bool visible);
virtual void SetVisiblity(bool visible);
bool mIsVisible;
private:
void SyncVisibilityConsoleVariable();
std::string mVisibilityConsoleVariable;
bool mIsInitialized;
};
+46 -1
View File
@@ -1,3 +1,48 @@
#include "window/gui/GuiMenuBar.h"
#include "libultraship/libultraship.h"
namespace Ship {} // namespace Ship
namespace Ship {
GuiMenuBar::GuiMenuBar(const std::string& visibilityConsoleVariable, bool isVisible)
: mVisibilityConsoleVariable(visibilityConsoleVariable), GuiElement(isVisible) {
if (!mVisibilityConsoleVariable.empty()) {
mIsVisible = CVarGetInteger(mVisibilityConsoleVariable.c_str(), mIsVisible);
SyncVisibilityConsoleVariable();
}
}
GuiMenuBar::GuiMenuBar(const std::string& visibilityConsoleVariable) : GuiMenuBar(visibilityConsoleVariable, false) {
}
void GuiMenuBar::Draw() {
if (!IsVisible()) {
return;
}
DrawElement();
// Sync up the IsVisible flag if it was changed by ImGui
SyncVisibilityConsoleVariable();
}
void GuiMenuBar::SyncVisibilityConsoleVariable() {
if (mVisibilityConsoleVariable.empty()) {
return;
}
bool shouldSave = CVarGetInteger(mVisibilityConsoleVariable.c_str(), 0) != IsVisible();
if (IsVisible()) {
CVarSetInteger(mVisibilityConsoleVariable.c_str(), IsVisible());
} else {
CVarClear(mVisibilityConsoleVariable.c_str());
}
if (shouldSave) {
Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
}
void GuiMenuBar::SetVisiblity(bool visible) {
mIsVisible = visible;
SyncVisibilityConsoleVariable();
}
} // namespace Ship
+10 -1
View File
@@ -6,6 +6,15 @@
namespace Ship {
class GuiMenuBar : public GuiElement {
public:
using GuiElement::GuiElement;
GuiMenuBar(const std::string& visibilityConsoleVariable, bool isVisible);
GuiMenuBar(const std::string& visibilityConsoleVariable);
void Draw() override;
protected:
void SetVisiblity(bool visible) override;
private:
void SyncVisibilityConsoleVariable();
std::string mVisibilityConsoleVariable;
};
} // namespace Ship
+66 -2
View File
@@ -1,12 +1,76 @@
#include "GuiWindow.h"
#include "libultraship/libultraship.h"
namespace Ship {
GuiWindow::GuiWindow(const std::string& consoleVariable, bool isVisible, const std::string& name, ImVec2 originalSize,
uint32_t windowFlags)
: mName(name), mVisibilityConsoleVariable(consoleVariable), mOriginalSize(originalSize), mWindowFlags(windowFlags),
GuiElement(isVisible) {
if (!mVisibilityConsoleVariable.empty()) {
mIsVisible = CVarGetInteger(mVisibilityConsoleVariable.c_str(), mIsVisible);
SyncVisibilityConsoleVariable();
}
}
GuiWindow::GuiWindow(const std::string& consoleVariable, bool isVisible, const std::string& name, ImVec2 originalSize)
: GuiWindow(consoleVariable, isVisible, name, originalSize, ImGuiWindowFlags_None) {
}
GuiWindow::GuiWindow(const std::string& consoleVariable, bool isVisible, const std::string& name)
: mName(name), GuiElement(consoleVariable, isVisible) {
: GuiWindow(consoleVariable, isVisible, name, ImVec2{ -1, -1 }, ImGuiWindowFlags_None) {
}
GuiWindow::GuiWindow(const std::string& consoleVariable, const std::string& name, ImVec2 originalSize,
uint32_t windowFlags)
: GuiWindow(consoleVariable, false, name, originalSize, windowFlags) {
}
GuiWindow::GuiWindow(const std::string& consoleVariable, const std::string& name, ImVec2 originalSize)
: GuiWindow(consoleVariable, false, name, originalSize, ImGuiWindowFlags_None) {
}
GuiWindow::GuiWindow(const std::string& consoleVariable, const std::string& name)
: GuiWindow(consoleVariable, false, name) {
: GuiWindow(consoleVariable, false, name, ImVec2{ -1, -1 }, ImGuiWindowFlags_None) {
}
void GuiWindow::SetVisiblity(bool visible) {
mIsVisible = visible;
SyncVisibilityConsoleVariable();
}
void GuiWindow::SyncVisibilityConsoleVariable() {
if (mVisibilityConsoleVariable.empty()) {
return;
}
bool shouldSave = CVarGetInteger(mVisibilityConsoleVariable.c_str(), 0) != IsVisible();
if (IsVisible()) {
CVarSetInteger(mVisibilityConsoleVariable.c_str(), IsVisible());
} else {
CVarClear(mVisibilityConsoleVariable.c_str());
}
if (shouldSave) {
Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
}
void GuiWindow::Draw() {
if (!IsVisible()) {
return;
}
if (mOriginalSize != ImVec2{ -1, -1 }) {
ImGui::SetNextWindowSize(mOriginalSize, ImGuiCond_FirstUseEver);
}
if (!ImGui::Begin(mName.c_str(), &mIsVisible, mWindowFlags)) {
ImGui::End();
}
DrawElement();
ImGui::End();
// Sync up the IsVisible flag if it was changed by ImGui
SyncVisibilityConsoleVariable();
}
std::string GuiWindow::GetName() {
+11 -1
View File
@@ -12,17 +12,27 @@ namespace Ship {
class GuiWindow : public GuiElement {
public:
GuiWindow() = default;
GuiWindow(const std::string& consoleVariable, bool isVisible, const std::string& name, ImVec2 originalSize,
uint32_t windowFlags);
GuiWindow(const std::string& consoleVariable, bool isVisible, const std::string& name, ImVec2 originalSize);
GuiWindow(const std::string& consoleVariable, bool isVisible, const std::string& name);
GuiWindow(const std::string& consoleVariable, const std::string& name, ImVec2 originalSize, uint32_t windowFlags);
GuiWindow(const std::string& consoleVariable, const std::string& name, ImVec2 originalSize);
GuiWindow(const std::string& consoleVariable, const std::string& name);
void Draw() override;
std::string GetName();
protected:
void SetVisiblity(bool visible) override;
void BeginGroupPanel(const char* name, const ImVec2& size);
void EndGroupPanel(float minHeight);
void SyncVisibilityConsoleVariable();
private:
std::string mVisibilityConsoleVariable;
std::string mName;
ImVector<ImRect> mGroupPanelLabelStack;
ImVec2 mOriginalSize;
uint32_t mWindowFlags;
};
} // namespace Ship
-2
View File
@@ -1637,12 +1637,10 @@ void InputEditorWindow::DrawSetDefaultsButton(uint8_t portIndex) {
}
void InputEditorWindow::DrawElement() {
ImGui::Begin("Controller Configuration", &mIsVisible);
ImGui::BeginTabBar("##ControllerConfigPortTabs");
for (uint8_t i = 0; i < 4; i++) {
DrawPortTab(i);
}
ImGui::EndTabBar();
ImGui::End();
}
} // namespace Ship
-2
View File
@@ -18,7 +18,6 @@ void StatsWindow::DrawElement() {
const float framerate = ImGui::GetIO().Framerate;
const float deltatime = ImGui::GetIO().DeltaTime;
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
ImGui::Begin("Debug Stats", &mIsVisible, ImGuiWindowFlags_NoFocusOnAppearing);
#if defined(_WIN32)
ImGui::Text("Platform: Windows");
@@ -32,7 +31,6 @@ void StatsWindow::DrawElement() {
ImGui::Text("Platform: Unknown");
#endif
ImGui::Text("Status: %.3f ms/frame (%.1f FPS)", deltatime * 1000.0f, framerate);
ImGui::End();
ImGui::PopStyleColor();
}