2020-11-10 15:26:38 +01:00
|
|
|
#include "window.hpp"
|
|
|
|
|
2020-12-22 18:10:01 +01:00
|
|
|
#include <hex.hpp>
|
2021-08-29 22:15:18 +02:00
|
|
|
|
2022-02-01 18:09:40 +01:00
|
|
|
#include <hex/api/plugin_manager.hpp>
|
|
|
|
#include <hex/api/content_registry.hpp>
|
|
|
|
#include <hex/api/imhex_api.hpp>
|
2023-05-11 18:44:50 +02:00
|
|
|
#include <hex/api/layout_manager.hpp>
|
2022-02-01 18:09:40 +01:00
|
|
|
|
2021-08-29 22:15:18 +02:00
|
|
|
#include <hex/helpers/utils.hpp>
|
2022-03-04 11:36:37 +01:00
|
|
|
#include <hex/helpers/fs.hpp>
|
2021-08-29 22:15:18 +02:00
|
|
|
#include <hex/helpers/logger.hpp>
|
2023-01-17 21:38:56 +01:00
|
|
|
#include <hex/helpers/stacktrace.hpp>
|
2020-12-22 18:10:01 +01:00
|
|
|
|
2023-04-07 10:21:27 +02:00
|
|
|
#include <hex/ui/view.hpp>
|
|
|
|
#include <hex/ui/popup.hpp>
|
|
|
|
|
2021-03-06 13:09:20 +01:00
|
|
|
#include <chrono>
|
2021-08-17 13:41:44 +02:00
|
|
|
#include <csignal>
|
2022-01-23 02:28:38 +01:00
|
|
|
#include <set>
|
2021-03-06 13:09:20 +01:00
|
|
|
#include <thread>
|
2022-03-04 20:52:39 +01:00
|
|
|
#include <cassert>
|
2020-11-10 15:26:38 +01:00
|
|
|
|
2021-12-22 15:06:16 +01:00
|
|
|
#include <romfs/romfs.hpp>
|
|
|
|
|
2021-01-13 17:28:27 +01:00
|
|
|
#include <imgui.h>
|
|
|
|
#include <imgui_internal.h>
|
|
|
|
#include <imgui_impl_glfw.h>
|
|
|
|
#include <imgui_impl_opengl3.h>
|
2021-12-31 01:10:06 +01:00
|
|
|
#include <hex/ui/imgui_imhex_extensions.h>
|
2021-03-02 13:48:23 +01:00
|
|
|
#include <implot.h>
|
|
|
|
#include <implot_internal.h>
|
2021-08-17 13:41:19 +02:00
|
|
|
#include <imnodes.h>
|
|
|
|
#include <imnodes_internal.h>
|
2020-11-10 15:26:38 +01:00
|
|
|
|
2023-07-09 12:53:31 +02:00
|
|
|
#include <wolv/utils/string.hpp>
|
|
|
|
|
2022-07-26 14:59:08 +02:00
|
|
|
#include <fonts/codicons_font.h>
|
2021-02-24 22:42:26 +01:00
|
|
|
|
2022-08-08 21:23:52 +02:00
|
|
|
#include <hex/api/project_file_manager.hpp>
|
2020-12-22 18:10:01 +01:00
|
|
|
|
2020-11-10 15:26:38 +01:00
|
|
|
#include <GLFW/glfw3.h>
|
2021-08-29 14:18:45 +02:00
|
|
|
|
2020-11-10 15:26:38 +01:00
|
|
|
namespace hex {
|
|
|
|
|
2021-03-06 13:09:20 +01:00
|
|
|
using namespace std::literals::chrono_literals;
|
|
|
|
|
2023-06-01 18:35:41 +02:00
|
|
|
static std::fs::path s_imguiSettingsPath;
|
2023-05-22 13:24:48 +02:00
|
|
|
|
2023-06-01 18:35:41 +02:00
|
|
|
/**
|
|
|
|
* @brief returns the path to load/save imgui settings to, or an empty path if no location was found
|
|
|
|
*/
|
|
|
|
std::fs::path getImGuiSettingsPath() {
|
|
|
|
return s_imguiSettingsPath;
|
2022-10-02 17:30:26 +02:00
|
|
|
}
|
2022-08-08 21:23:52 +02:00
|
|
|
|
2022-02-15 23:07:48 +01:00
|
|
|
Window::Window() {
|
2023-01-17 21:38:56 +01:00
|
|
|
stacktrace::initialize();
|
|
|
|
|
2023-01-31 11:38:26 +01:00
|
|
|
constexpr static auto openEmergencyPopup = [](const std::string &title){
|
|
|
|
TaskManager::doLater([title] {
|
|
|
|
for (const auto &provider : ImHexApi::Provider::getProviders())
|
|
|
|
ImHexApi::Provider::remove(provider, false);
|
|
|
|
|
|
|
|
ImGui::OpenPopup(title.c_str());
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Handle fatal error popups for errors detected during initialization
|
2021-04-18 20:24:42 +02:00
|
|
|
{
|
2022-02-01 18:09:40 +01:00
|
|
|
for (const auto &[argument, value] : ImHexApi::System::getInitArguments()) {
|
|
|
|
if (argument == "no-plugins") {
|
2023-01-31 11:38:26 +01:00
|
|
|
openEmergencyPopup("No Plugins");
|
2022-02-01 23:57:48 +01:00
|
|
|
} else if (argument == "no-builtin-plugin") {
|
2023-01-31 11:38:26 +01:00
|
|
|
openEmergencyPopup("No Builtin Plugin");
|
2022-02-01 23:57:48 +01:00
|
|
|
} else if (argument == "multiple-builtin-plugins") {
|
2023-01-31 11:38:26 +01:00
|
|
|
openEmergencyPopup("Multiple Builtin Plugins");
|
2021-04-18 20:24:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-17 15:46:26 +02:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Initialize the window
|
2020-11-10 15:26:38 +01:00
|
|
|
this->initGLFW();
|
|
|
|
this->initImGui();
|
2021-11-28 11:57:52 +01:00
|
|
|
this->setupNativeWindow();
|
2023-02-17 12:03:53 +01:00
|
|
|
this->registerEventHandlers();
|
|
|
|
|
|
|
|
auto logoData = romfs::get("logo.png");
|
|
|
|
this->m_logoTexture = ImGui::Texture(reinterpret_cast<const ImU8 *>(logoData.data()), logoData.size());
|
|
|
|
|
2023-03-21 15:33:43 +01:00
|
|
|
ContentRegistry::Settings::impl::store();
|
2023-02-17 12:03:53 +01:00
|
|
|
EventManager::post<EventSettingsChanged>();
|
|
|
|
EventManager::post<EventWindowInitialized>();
|
2023-07-22 21:30:22 +02:00
|
|
|
EventManager::post<EventImHexStartupFinished>();
|
2023-02-17 12:03:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Window::~Window() {
|
|
|
|
EventManager::unsubscribe<EventProviderDeleted>(this);
|
|
|
|
EventManager::unsubscribe<RequestCloseImHex>(this);
|
|
|
|
EventManager::unsubscribe<RequestUpdateWindowTitle>(this);
|
|
|
|
EventManager::unsubscribe<EventAbnormalTermination>(this);
|
|
|
|
EventManager::unsubscribe<RequestOpenPopup>(this);
|
|
|
|
|
|
|
|
this->exitImGui();
|
|
|
|
this->exitGLFW();
|
|
|
|
}
|
2021-01-31 00:04:33 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
void Window::registerEventHandlers() {
|
2022-08-09 16:29:52 +02:00
|
|
|
// Initialize default theme
|
2022-12-29 19:26:00 +01:00
|
|
|
EventManager::post<RequestChangeTheme>("Dark");
|
2022-08-09 16:29:52 +02:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Handle the close window request by telling GLFW to shut down
|
2021-08-21 13:55:21 +02:00
|
|
|
EventManager::subscribe<RequestCloseImHex>(this, [this](bool noQuestions) {
|
2022-08-08 21:23:52 +02:00
|
|
|
glfwSetWindowShouldClose(this->m_window, GLFW_TRUE);
|
2021-08-21 13:55:21 +02:00
|
|
|
|
|
|
|
if (!noQuestions)
|
|
|
|
EventManager::post<EventWindowClosing>(this->m_window);
|
2021-02-01 19:03:45 +01:00
|
|
|
});
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Handle updating the window title
|
2023-01-07 17:31:22 +01:00
|
|
|
EventManager::subscribe<RequestUpdateWindowTitle>(this, [this]() {
|
2021-08-17 13:41:44 +02:00
|
|
|
std::string title = "ImHex";
|
|
|
|
|
2023-01-07 17:31:22 +01:00
|
|
|
if (ProjectFile::hasPath()) {
|
2023-02-17 12:03:53 +01:00
|
|
|
// If a project is open, show the project name instead of the file name
|
|
|
|
|
2023-01-07 17:31:22 +01:00
|
|
|
title += " - Project " + hex::limitStringLength(ProjectFile::getPath().stem().string(), 32);
|
|
|
|
|
|
|
|
if (ImHexApi::Provider::isDirty())
|
|
|
|
title += " (*)";
|
|
|
|
|
|
|
|
} else if (ImHexApi::Provider::isValid()) {
|
2022-08-08 21:23:52 +02:00
|
|
|
auto provider = ImHexApi::Provider::get();
|
2023-01-07 17:31:22 +01:00
|
|
|
if (provider != nullptr) {
|
|
|
|
title += " - " + hex::limitStringLength(provider->getName(), 32);
|
2021-09-08 15:18:24 +02:00
|
|
|
|
2022-08-22 11:00:31 +02:00
|
|
|
if (provider->isDirty())
|
|
|
|
title += " (*)";
|
2022-02-16 10:04:05 +01:00
|
|
|
|
2022-08-22 11:00:31 +02:00
|
|
|
if (!provider->isWritable())
|
|
|
|
title += " (Read Only)";
|
|
|
|
}
|
2021-09-08 15:18:24 +02:00
|
|
|
}
|
2021-08-17 13:41:44 +02:00
|
|
|
|
2021-08-18 22:36:46 +02:00
|
|
|
this->m_windowTitle = title;
|
2023-05-15 11:30:24 +02:00
|
|
|
|
|
|
|
if (this->m_window != nullptr)
|
|
|
|
glfwSetWindowTitle(this->m_window, title.c_str());
|
2021-08-17 13:41:44 +02:00
|
|
|
});
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Handle opening popups
|
2022-01-24 20:53:17 +01:00
|
|
|
EventManager::subscribe<RequestOpenPopup>(this, [this](auto name) {
|
2022-09-19 16:09:22 +02:00
|
|
|
std::scoped_lock lock(this->m_popupMutex);
|
|
|
|
|
2021-09-26 21:18:25 +02:00
|
|
|
this->m_popupsToOpen.push_back(name);
|
|
|
|
});
|
2020-11-10 15:26:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Window::loop() {
|
|
|
|
while (!glfwWindowShouldClose(this->m_window)) {
|
2023-05-11 23:22:06 +02:00
|
|
|
this->m_lastFrameTime = glfwGetTime();
|
|
|
|
|
2021-12-16 23:48:52 +01:00
|
|
|
if (!glfwGetWindowAttrib(this->m_window, GLFW_VISIBLE) || glfwGetWindowAttrib(this->m_window, GLFW_ICONIFIED)) {
|
2023-02-17 12:03:53 +01:00
|
|
|
// If the application is minimized or not visible, don't render anything
|
2021-06-07 18:14:40 +02:00
|
|
|
glfwWaitEvents();
|
2021-12-16 23:48:52 +01:00
|
|
|
} else {
|
2022-08-03 23:32:34 +02:00
|
|
|
glfwPollEvents();
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// If no events have been received in a while, lower the frame rate
|
|
|
|
{
|
|
|
|
// If the mouse is down, the mouse is moving or a popup is open, we don't want to lower the frame rate
|
2023-08-11 22:03:30 +02:00
|
|
|
bool frameRateUnlocked =
|
|
|
|
ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopupId) ||
|
|
|
|
TaskManager::getRunningTaskCount() > 0 ||
|
|
|
|
this->m_buttonDown ||
|
|
|
|
this->m_hadEvent ||
|
|
|
|
!this->m_pressedKeys.empty();
|
|
|
|
|
|
|
|
// Calculate the time until the next frame
|
|
|
|
const double timeout = std::max(0.0, (1.0 / 5.0) - (glfwGetTime() - this->m_lastFrameTime));
|
2023-02-17 12:03:53 +01:00
|
|
|
|
|
|
|
// If the frame rate has been unlocked for 5 seconds, lock it again
|
2023-08-11 22:03:30 +02:00
|
|
|
if ((this->m_lastFrameTime - this->m_frameRateUnlockTime) > 5 && this->m_frameRateTemporarilyUnlocked && !frameRateUnlocked) {
|
2023-02-17 12:03:53 +01:00
|
|
|
this->m_frameRateTemporarilyUnlocked = false;
|
|
|
|
}
|
2022-08-03 23:32:34 +02:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// If the frame rate is locked, wait for events with a timeout
|
2023-08-11 22:03:30 +02:00
|
|
|
if (frameRateUnlocked || this->m_frameRateTemporarilyUnlocked) {
|
|
|
|
if (!this->m_frameRateTemporarilyUnlocked) {
|
|
|
|
this->m_frameRateTemporarilyUnlocked = true;
|
|
|
|
this->m_frameRateUnlockTime = this->m_lastFrameTime;
|
|
|
|
}
|
2023-02-17 12:03:53 +01:00
|
|
|
} else {
|
2023-08-11 22:03:30 +02:00
|
|
|
glfwWaitEventsTimeout(timeout);
|
2022-08-03 23:32:34 +02:00
|
|
|
}
|
2023-02-17 12:03:53 +01:00
|
|
|
|
|
|
|
this->m_hadEvent = false;
|
2022-08-03 23:32:34 +02:00
|
|
|
}
|
2021-12-16 23:48:52 +01:00
|
|
|
}
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Render frame
|
2021-06-07 18:14:40 +02:00
|
|
|
this->frameBegin();
|
2022-10-03 10:36:19 +02:00
|
|
|
this->frame();
|
2020-11-10 15:26:38 +01:00
|
|
|
this->frameEnd();
|
2023-08-11 22:03:30 +02:00
|
|
|
|
|
|
|
glfwSwapInterval(0);
|
|
|
|
|
|
|
|
// Limit frame rate
|
|
|
|
// If the target FPS are below 15, use the monitor refresh rate, if it's above 200, don't limit the frame rate
|
|
|
|
const auto targetFPS = ImHexApi::System::getTargetFPS();
|
|
|
|
if (targetFPS < 15) {
|
|
|
|
glfwSwapInterval(1);
|
|
|
|
} else if (targetFPS > 200) {
|
|
|
|
glfwSwapInterval(0);
|
|
|
|
} else {
|
|
|
|
glfwSwapInterval(0);
|
|
|
|
const auto frameTime = glfwGetTime() - this->m_lastFrameTime;
|
|
|
|
const auto targetFrameTime = 1.0 / targetFPS;
|
|
|
|
if (frameTime < targetFrameTime) {
|
|
|
|
glfwWaitEventsTimeout(targetFrameTime - frameTime);
|
|
|
|
}
|
|
|
|
}
|
2020-11-10 15:26:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-20 13:11:43 +01:00
|
|
|
static void createNestedMenu(std::span<const std::string> menuItems, const Shortcut &shortcut, const std::function<void()> &callback, const std::function<bool()> &enabledCallback) {
|
|
|
|
const auto &name = menuItems.front();
|
|
|
|
|
|
|
|
if (name == ContentRegistry::Interface::impl::SeparatorValue) {
|
|
|
|
ImGui::Separator();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (name == ContentRegistry::Interface::impl::SubMenuValue) {
|
|
|
|
callback();
|
|
|
|
} else if (menuItems.size() == 1) {
|
|
|
|
if (ImGui::MenuItem(LangEntry(name), shortcut.toString().c_str(), false, enabledCallback()))
|
|
|
|
callback();
|
|
|
|
} else {
|
2023-04-06 22:33:02 +02:00
|
|
|
if (ImGui::BeginMenu(LangEntry(name), *(menuItems.begin() + 1) == ContentRegistry::Interface::impl::SubMenuValue ? enabledCallback() : true)) {
|
2023-03-20 13:11:43 +01:00
|
|
|
createNestedMenu({ menuItems.begin() + 1, menuItems.end() }, shortcut, callback, enabledCallback);
|
|
|
|
ImGui::EndMenu();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-25 09:26:40 +02:00
|
|
|
void Window::drawTitleBarBorderless() {
|
|
|
|
auto startX = ImGui::GetCursorPosX();
|
|
|
|
auto titleBarHeight = ImGui::GetCurrentWindow()->MenuBarHeight();
|
|
|
|
auto buttonSize = ImVec2(titleBarHeight * 1.5F, titleBarHeight - 1);
|
|
|
|
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_MenuBarBg));
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetColorU32(ImGuiCol_ScrollbarGrabActive));
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetColorU32(ImGuiCol_ScrollbarGrabHovered));
|
|
|
|
|
|
|
|
// custom titlebar buttons implementation for borderless window mode
|
|
|
|
auto &titleBarButtons = ContentRegistry::Interface::impl::getTitleBarButtons();
|
|
|
|
|
|
|
|
// Draw custom title bar buttons
|
2023-08-13 23:54:22 +02:00
|
|
|
if(!titleBarButtons.empty()) {
|
|
|
|
ImGui::SetCursorPosX(ImGui::GetWindowWidth() - buttonSize.x * (4 + titleBarButtons.size()));
|
|
|
|
for (const auto &[icon, tooltip, callback]: titleBarButtons) {
|
|
|
|
if (ImGui::TitleBarButton(icon.c_str(), buttonSize)) {
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
ImGui::InfoTooltip(LangEntry(tooltip));
|
2023-05-25 09:26:40 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw minimize, restore and maximize buttons
|
|
|
|
ImGui::SetCursorPosX(ImGui::GetWindowWidth() - buttonSize.x * 3);
|
|
|
|
if (ImGui::TitleBarButton(ICON_VS_CHROME_MINIMIZE, buttonSize))
|
|
|
|
glfwIconifyWindow(this->m_window);
|
|
|
|
if (glfwGetWindowAttrib(this->m_window, GLFW_MAXIMIZED)) {
|
|
|
|
if (ImGui::TitleBarButton(ICON_VS_CHROME_RESTORE, buttonSize))
|
|
|
|
glfwRestoreWindow(this->m_window);
|
|
|
|
} else {
|
|
|
|
if (ImGui::TitleBarButton(ICON_VS_CHROME_MAXIMIZE, buttonSize))
|
|
|
|
glfwMaximizeWindow(this->m_window);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, 0xFF7A70F1);
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, 0xFF2311E8);
|
|
|
|
|
|
|
|
// Draw close button
|
|
|
|
if (ImGui::TitleBarButton(ICON_VS_CHROME_CLOSE, buttonSize)) {
|
|
|
|
ImHexApi::System::closeImHex();
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::PopStyleColor(5);
|
|
|
|
ImGui::PopStyleVar();
|
|
|
|
|
|
|
|
ImGui::SetCursorPosX(std::max(startX, (ImGui::GetWindowWidth() - ImGui::CalcTextSize(this->m_windowTitle.c_str()).x) / 2));
|
|
|
|
ImGui::TextUnformatted(this->m_windowTitle.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
void Window::drawTitleBarBorder() {
|
|
|
|
auto titleBarHeight = ImGui::GetCurrentWindow()->MenuBarHeight();
|
|
|
|
auto buttonSize = ImVec2(titleBarHeight * 1.5F, titleBarHeight - 1);
|
|
|
|
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_MenuBarBg));
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetColorU32(ImGuiCol_ScrollbarGrabActive));
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetColorU32(ImGuiCol_ScrollbarGrabHovered));
|
|
|
|
|
|
|
|
auto &titleBarButtons = ContentRegistry::Interface::impl::getTitleBarButtons();
|
|
|
|
|
|
|
|
// Draw custom title bar buttons
|
2023-08-13 23:54:22 +02:00
|
|
|
if(!titleBarButtons.empty()) {
|
|
|
|
ImGui::SetCursorPosX(ImGui::GetWindowWidth() - buttonSize.x * (titleBarButtons.size() + 0.5));
|
|
|
|
for (const auto &[icon, tooltip, callback]: titleBarButtons) {
|
|
|
|
if (ImGui::TitleBarButton(icon.c_str(), buttonSize)) {
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
ImGui::InfoTooltip(LangEntry(tooltip));
|
2023-05-25 09:26:40 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::PopStyleColor(3);
|
|
|
|
ImGui::PopStyleVar();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Window::drawTitleBar() {
|
|
|
|
if (ImHexApi::System::isBorderlessWindowModeEnabled()) {
|
|
|
|
drawTitleBarBorderless();
|
|
|
|
} else {
|
|
|
|
drawTitleBarBorder();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-10 15:26:38 +01:00
|
|
|
void Window::frameBegin() {
|
2023-02-17 12:03:53 +01:00
|
|
|
// Start new ImGui Frame
|
2020-11-10 15:26:38 +01:00
|
|
|
ImGui_ImplOpenGL3_NewFrame();
|
|
|
|
ImGui_ImplGlfw_NewFrame();
|
|
|
|
ImGui::NewFrame();
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Handle all undocked floating windows
|
2022-01-24 20:53:17 +01:00
|
|
|
ImGuiViewport *viewport = ImGui::GetMainViewport();
|
2022-07-07 07:16:38 +02:00
|
|
|
ImGui::SetNextWindowPos(viewport->WorkPos);
|
2022-07-06 10:21:38 +02:00
|
|
|
ImGui::SetNextWindowSize(ImHexApi::System::getMainWindowSize() - ImVec2(0, ImGui::GetTextLineHeightWithSpacing()));
|
2020-11-10 15:26:38 +01:00
|
|
|
ImGui::SetNextWindowViewport(viewport->ID);
|
2023-05-11 23:56:51 +02:00
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0F);
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0F);
|
2021-08-21 00:52:11 +02:00
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
2020-11-30 00:03:12 +01:00
|
|
|
|
2022-01-24 20:53:17 +01:00
|
|
|
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
|
2020-11-10 15:26:38 +01:00
|
|
|
|
2021-01-21 17:48:24 +01:00
|
|
|
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Render main dock space
|
2022-02-06 21:39:10 +01:00
|
|
|
if (ImGui::Begin("ImHexDockSpace", nullptr, windowFlags)) {
|
2022-01-22 22:03:19 +01:00
|
|
|
auto drawList = ImGui::GetWindowDrawList();
|
2021-08-21 00:52:11 +02:00
|
|
|
ImGui::PopStyleVar();
|
2022-02-01 22:09:44 +01:00
|
|
|
auto sidebarPos = ImGui::GetCursorPos();
|
2023-03-21 15:33:43 +01:00
|
|
|
auto sidebarWidth = ContentRegistry::Interface::impl::getSidebarItems().empty() ? 0 : 30_scaled;
|
2022-01-22 22:03:19 +01:00
|
|
|
|
|
|
|
ImGui::SetCursorPosX(sidebarWidth);
|
|
|
|
|
2022-02-01 22:09:44 +01:00
|
|
|
auto footerHeight = ImGui::GetTextLineHeightWithSpacing() + ImGui::GetStyle().FramePadding.y * 2 + 1_scaled;
|
2022-02-01 18:09:40 +01:00
|
|
|
auto dockSpaceSize = ImVec2(ImHexApi::System::getMainWindowSize().x - sidebarWidth, ImGui::GetContentRegionAvail().y - footerHeight);
|
2022-01-22 22:03:19 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Render footer
|
|
|
|
{
|
2022-01-22 22:03:19 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
auto dockId = ImGui::DockSpace(ImGui::GetID("ImHexMainDock"), dockSpaceSize);
|
|
|
|
ImHexApi::System::impl::setMainDockSpaceId(dockId);
|
2021-02-18 12:09:19 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
drawList->AddRectFilled(ImGui::GetWindowPos(), ImGui::GetWindowPos() + ImGui::GetWindowSize() - ImVec2(dockSpaceSize.x, footerHeight - ImGui::GetStyle().FramePadding.y - 1_scaled), ImGui::GetColorU32(ImGuiCol_MenuBarBg));
|
2020-11-10 15:26:38 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
ImGui::Separator();
|
|
|
|
ImGui::SetCursorPosX(8);
|
2023-03-21 15:33:43 +01:00
|
|
|
for (const auto &callback : ContentRegistry::Interface::impl::getFooterItems()) {
|
2023-02-17 12:03:53 +01:00
|
|
|
auto prevIdx = drawList->_VtxCurrentIdx;
|
|
|
|
callback();
|
|
|
|
auto currIdx = drawList->_VtxCurrentIdx;
|
|
|
|
|
|
|
|
// Only draw separator if something was actually drawn
|
|
|
|
if (prevIdx != currIdx) {
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
|
|
|
ImGui::SameLine();
|
|
|
|
}
|
2022-08-04 11:00:49 +02:00
|
|
|
}
|
2021-02-18 12:09:19 +01:00
|
|
|
}
|
2021-02-11 23:09:45 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Render sidebar
|
2022-01-22 22:03:19 +01:00
|
|
|
{
|
|
|
|
ImGui::SetCursorPos(sidebarPos);
|
|
|
|
|
2022-01-22 22:37:52 +01:00
|
|
|
static i32 openWindow = -1;
|
2022-02-01 22:09:44 +01:00
|
|
|
u32 index = 0;
|
2022-01-22 22:03:19 +01:00
|
|
|
ImGui::PushID("SideBarWindows");
|
2023-03-21 15:33:43 +01:00
|
|
|
for (const auto &[icon, callback] : ContentRegistry::Interface::impl::getSidebarItems()) {
|
2022-01-22 22:03:19 +01:00
|
|
|
ImGui::SetCursorPosY(sidebarPos.y + sidebarWidth * index);
|
|
|
|
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_MenuBarBg));
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetColorU32(ImGuiCol_ScrollbarGrabActive));
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetColorU32(ImGuiCol_ScrollbarGrabHovered));
|
|
|
|
|
|
|
|
ImGui::BeginDisabled(!ImHexApi::Provider::isValid());
|
|
|
|
{
|
|
|
|
if (ImGui::Button(icon.c_str(), ImVec2(sidebarWidth, sidebarWidth))) {
|
2022-03-27 00:01:28 +01:00
|
|
|
if (static_cast<u32>(openWindow) == index)
|
2022-01-22 22:03:19 +01:00
|
|
|
openWindow = -1;
|
|
|
|
else
|
|
|
|
openWindow = index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::EndDisabled();
|
|
|
|
|
|
|
|
ImGui::PopStyleColor(3);
|
|
|
|
|
2022-03-27 00:01:28 +01:00
|
|
|
bool open = static_cast<u32>(openWindow) == index;
|
2022-01-22 22:03:19 +01:00
|
|
|
if (open) {
|
|
|
|
ImGui::SetNextWindowPos(ImGui::GetWindowPos() + sidebarPos + ImVec2(sidebarWidth - 2_scaled, 0));
|
|
|
|
ImGui::SetNextWindowSize(ImVec2(250_scaled, dockSpaceSize.y + ImGui::GetStyle().FramePadding.y + 2_scaled));
|
|
|
|
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1);
|
|
|
|
if (ImGui::Begin("Window", &open, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar)) {
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
ImGui::PopStyleVar();
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::NewLine();
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
ImGui::PopID();
|
|
|
|
}
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Render main menu
|
2023-05-02 20:35:30 +02:00
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0F);
|
2023-07-01 22:55:59 +02:00
|
|
|
ImGui::SetNextWindowScroll(ImVec2(0, 0));
|
2021-08-21 00:52:11 +02:00
|
|
|
if (ImGui::BeginMainMenuBar()) {
|
2021-02-18 12:09:19 +01:00
|
|
|
|
2022-02-15 23:07:48 +01:00
|
|
|
if (ImHexApi::System::isBorderlessWindowModeEnabled()) {
|
2022-02-15 22:36:36 +01:00
|
|
|
auto menuBarHeight = ImGui::GetCurrentWindow()->MenuBarHeight();
|
|
|
|
ImGui::SetCursorPosX(5);
|
2023-08-19 19:12:15 +02:00
|
|
|
|
2022-02-15 22:36:36 +01:00
|
|
|
ImGui::Image(this->m_logoTexture, ImVec2(menuBarHeight, menuBarHeight));
|
2023-08-19 19:12:15 +02:00
|
|
|
ImGui::SetCursorPosX(5);
|
|
|
|
ImGui::InvisibleButton("##logo", ImVec2(menuBarHeight, menuBarHeight));
|
|
|
|
ImGui::OpenPopupOnItemClick("WindowingMenu", ImGuiPopupFlags_MouseButtonLeft);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ImGui::BeginPopup("WindowingMenu")) {
|
|
|
|
bool maximized = glfwGetWindowAttrib(this->m_window, GLFW_MAXIMIZED);
|
|
|
|
|
|
|
|
ImGui::BeginDisabled(!maximized);
|
|
|
|
if (ImGui::MenuItem(ICON_VS_CHROME_RESTORE " Restore")) glfwRestoreWindow(this->m_window);
|
|
|
|
ImGui::EndDisabled();
|
|
|
|
|
|
|
|
if (ImGui::MenuItem(ICON_VS_CHROME_MINIMIZE " Minimize")) glfwIconifyWindow(this->m_window);
|
|
|
|
|
|
|
|
ImGui::BeginDisabled(maximized);
|
|
|
|
if (ImGui::MenuItem(ICON_VS_CHROME_MAXIMIZE " Maximize")) glfwMaximizeWindow(this->m_window);
|
|
|
|
ImGui::EndDisabled();
|
|
|
|
|
|
|
|
ImGui::Separator();
|
|
|
|
|
|
|
|
if (ImGui::MenuItem(ICON_VS_CHROME_CLOSE " Close")) ImHexApi::System::closeImHex();
|
|
|
|
|
|
|
|
ImGui::EndPopup();
|
2022-02-15 22:36:36 +01:00
|
|
|
}
|
2021-08-18 22:36:46 +02:00
|
|
|
|
2023-03-21 15:33:43 +01:00
|
|
|
for (const auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMainMenuItems()) {
|
2023-04-08 11:37:42 +02:00
|
|
|
ImGui::GetStyle().TouchExtraPadding = scaled(ImVec2(0, 2));
|
2022-01-23 20:45:51 +01:00
|
|
|
if (ImGui::BeginMenu(LangEntry(menuItem.unlocalizedName))) {
|
2021-08-18 22:36:46 +02:00
|
|
|
ImGui::EndMenu();
|
2020-11-28 22:01:50 +01:00
|
|
|
}
|
2023-04-08 11:37:42 +02:00
|
|
|
ImGui::GetStyle().TouchExtraPadding = ImVec2(0, 0);
|
2022-01-18 00:10:10 +01:00
|
|
|
}
|
2020-11-11 09:22:55 +01:00
|
|
|
|
2023-03-21 15:33:43 +01:00
|
|
|
for (auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMenuItems()) {
|
2023-03-20 13:11:43 +01:00
|
|
|
const auto &[unlocalizedNames, shortcut, callback, enabledCallback] = menuItem;
|
|
|
|
createNestedMenu(unlocalizedNames, shortcut, callback, enabledCallback);
|
2022-01-18 00:10:10 +01:00
|
|
|
}
|
2020-11-11 09:22:55 +01:00
|
|
|
|
2022-01-18 00:10:10 +01:00
|
|
|
this->drawTitleBar();
|
2020-11-10 15:26:38 +01:00
|
|
|
|
2022-01-18 00:10:10 +01:00
|
|
|
ImGui::EndMainMenuBar();
|
2021-08-21 00:52:11 +02:00
|
|
|
}
|
|
|
|
ImGui::PopStyleVar();
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Render toolbar
|
2021-08-21 00:52:11 +02:00
|
|
|
if (ImGui::BeginMenuBar()) {
|
|
|
|
|
2023-03-21 15:33:43 +01:00
|
|
|
for (const auto &callback : ContentRegistry::Interface::impl::getToolbarItems()) {
|
2021-08-21 00:52:11 +02:00
|
|
|
callback();
|
|
|
|
ImGui::SameLine();
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::EndMenuBar();
|
2020-11-23 23:57:19 +01:00
|
|
|
}
|
2020-11-11 14:41:44 +01:00
|
|
|
|
2021-12-10 16:09:55 +01:00
|
|
|
this->beginNativeWindowFrame();
|
2022-01-22 22:03:19 +01:00
|
|
|
|
|
|
|
drawList->AddLine(ImGui::GetWindowPos() + ImVec2(sidebarWidth - 2, 0), ImGui::GetWindowPos() + ImGui::GetWindowSize() - ImVec2(dockSpaceSize.x + 2, footerHeight - ImGui::GetStyle().FramePadding.y - 2), ImGui::GetColorU32(ImGuiCol_Separator));
|
|
|
|
drawList->AddLine(ImGui::GetWindowPos() + ImVec2(sidebarWidth, ImGui::GetCurrentWindow()->MenuBarHeight()), ImGui::GetWindowPos() + ImVec2(ImGui::GetWindowSize().x, ImGui::GetCurrentWindow()->MenuBarHeight()), ImGui::GetColorU32(ImGuiCol_Separator));
|
2020-11-11 14:41:44 +01:00
|
|
|
}
|
2020-11-23 23:57:19 +01:00
|
|
|
ImGui::End();
|
2021-08-21 00:52:11 +02:00
|
|
|
ImGui::PopStyleVar(2);
|
2021-04-21 23:31:51 +02:00
|
|
|
|
2022-02-01 23:57:48 +01:00
|
|
|
// Plugin load error popups. These are not translated because they should always be readable, no matter if any localization could be loaded or not
|
|
|
|
{
|
|
|
|
auto drawPluginFolderTable = []() {
|
|
|
|
ImGui::UnderlinedText("Plugin folders");
|
2023-07-22 22:59:05 +02:00
|
|
|
if (ImGui::BeginTable("plugins", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | ImGuiTableFlags_SizingFixedFit, ImVec2(0, 100_scaled))) {
|
2022-02-01 23:57:48 +01:00
|
|
|
ImGui::TableSetupScrollFreeze(0, 1);
|
|
|
|
ImGui::TableSetupColumn("Path", ImGuiTableColumnFlags_WidthStretch, 0.2);
|
|
|
|
ImGui::TableSetupColumn("Exists", ImGuiTableColumnFlags_WidthFixed, ImGui::GetTextLineHeight() * 3);
|
|
|
|
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
|
2022-03-04 11:36:37 +01:00
|
|
|
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Plugins, true)) {
|
2022-06-09 15:58:40 +02:00
|
|
|
const auto filePath = path / "builtin.hexplug";
|
2022-02-01 23:57:48 +01:00
|
|
|
ImGui::TableNextRow();
|
|
|
|
ImGui::TableNextColumn();
|
2023-03-12 18:43:05 +01:00
|
|
|
ImGui::TextUnformatted(wolv::util::toUTF8String(filePath).c_str());
|
2022-02-01 23:57:48 +01:00
|
|
|
ImGui::TableNextColumn();
|
2023-03-12 18:27:29 +01:00
|
|
|
ImGui::TextUnformatted(wolv::io::fs::exists(filePath) ? ICON_VS_CHECK : ICON_VS_CLOSE);
|
2022-02-01 23:57:48 +01:00
|
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// No plugins error popup
|
2022-02-01 23:57:48 +01:00
|
|
|
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F));
|
|
|
|
if (ImGui::BeginPopupModal("No Plugins", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove)) {
|
|
|
|
ImGui::TextUnformatted("No ImHex plugins loaded (including the built-in plugin)!");
|
|
|
|
ImGui::TextUnformatted("Make sure you installed ImHex correctly.");
|
|
|
|
ImGui::TextUnformatted("There should be at least a 'builtin.hexplug' file in your plugins folder.");
|
|
|
|
|
|
|
|
ImGui::NewLine();
|
|
|
|
|
|
|
|
drawPluginFolderTable();
|
|
|
|
|
2023-07-22 22:59:05 +02:00
|
|
|
ImGui::NewLine();
|
|
|
|
if (ImGui::Button("Close ImHex", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
|
|
|
|
ImHexApi::System::closeImHex(true);
|
|
|
|
|
2022-02-01 23:57:48 +01:00
|
|
|
ImGui::EndPopup();
|
|
|
|
}
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// No built-in plugin error popup
|
2022-02-01 23:57:48 +01:00
|
|
|
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F));
|
|
|
|
if (ImGui::BeginPopupModal("No Builtin Plugin", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove)) {
|
|
|
|
ImGui::TextUnformatted("The ImHex built-in plugins could not be loaded!");
|
|
|
|
ImGui::TextUnformatted("Make sure you installed ImHex correctly.");
|
|
|
|
ImGui::TextUnformatted("There should be at least a 'builtin.hexplug' file in your plugins folder.");
|
|
|
|
|
|
|
|
ImGui::NewLine();
|
|
|
|
|
|
|
|
drawPluginFolderTable();
|
|
|
|
|
2023-07-22 22:59:05 +02:00
|
|
|
ImGui::NewLine();
|
|
|
|
if (ImGui::Button("Close ImHex", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
|
|
|
|
ImHexApi::System::closeImHex(true);
|
|
|
|
|
2022-02-01 23:57:48 +01:00
|
|
|
ImGui::EndPopup();
|
|
|
|
}
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Multiple built-in plugins error popup
|
2022-02-01 23:57:48 +01:00
|
|
|
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F));
|
|
|
|
if (ImGui::BeginPopupModal("Multiple Builtin Plugins", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove)) {
|
|
|
|
ImGui::TextUnformatted("ImHex found and attempted to load multiple built-in plugins!");
|
|
|
|
ImGui::TextUnformatted("Make sure you installed ImHex correctly and, if needed,");
|
|
|
|
ImGui::TextUnformatted("cleaned up older installations correctly,");
|
|
|
|
ImGui::TextUnformatted("There should be exactly one 'builtin.hexplug' file in any one your plugin folders.");
|
|
|
|
|
|
|
|
ImGui::NewLine();
|
|
|
|
|
|
|
|
drawPluginFolderTable();
|
|
|
|
|
2023-07-22 22:59:05 +02:00
|
|
|
ImGui::NewLine();
|
|
|
|
if (ImGui::Button("Close ImHex", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
|
|
|
|
ImHexApi::System::closeImHex(true);
|
|
|
|
|
2022-02-01 23:57:48 +01:00
|
|
|
ImGui::EndPopup();
|
|
|
|
}
|
2021-04-21 23:31:51 +02:00
|
|
|
}
|
2023-02-17 12:03:53 +01:00
|
|
|
|
|
|
|
// Open popups when plugins requested it
|
2022-09-19 16:09:22 +02:00
|
|
|
{
|
|
|
|
std::scoped_lock lock(this->m_popupMutex);
|
|
|
|
this->m_popupsToOpen.remove_if([](const auto &name) {
|
|
|
|
if (ImGui::IsPopupOpen(name.c_str()))
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
ImGui::OpenPopup(name.c_str());
|
|
|
|
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
}
|
2022-01-18 00:10:10 +01:00
|
|
|
|
2023-04-07 10:21:27 +02:00
|
|
|
// Draw popup stack
|
|
|
|
{
|
2023-05-10 22:29:17 +02:00
|
|
|
static bool popupDisplaying = false;
|
|
|
|
static bool positionSet = false;
|
|
|
|
static bool sizeSet = false;
|
2023-07-23 18:22:53 +02:00
|
|
|
static double popupDelay = -2.0;
|
2023-05-10 22:29:17 +02:00
|
|
|
|
|
|
|
static std::unique_ptr<impl::PopupBase> currPopup;
|
|
|
|
static LangEntry name("");
|
2023-04-09 23:24:48 +02:00
|
|
|
|
2023-05-10 22:29:17 +02:00
|
|
|
if (auto &popups = impl::PopupBase::getOpenPopups(); !popups.empty()) {
|
|
|
|
if (!ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopupId)) {
|
2023-07-23 18:22:53 +02:00
|
|
|
if (popupDelay <= -1.0) {
|
|
|
|
popupDelay = 200;
|
|
|
|
} else {
|
|
|
|
popupDelay -= this->m_lastFrameTime;
|
|
|
|
if (popupDelay < 0) {
|
|
|
|
popupDelay = -2.0;
|
|
|
|
currPopup = std::move(popups.back());
|
|
|
|
name = LangEntry(currPopup->getUnlocalizedName());
|
|
|
|
|
|
|
|
ImGui::OpenPopup(name);
|
|
|
|
popups.pop_back();
|
|
|
|
}
|
|
|
|
}
|
2023-05-10 22:29:17 +02:00
|
|
|
}
|
|
|
|
}
|
2023-04-07 10:21:27 +02:00
|
|
|
|
2023-05-10 22:29:17 +02:00
|
|
|
if (currPopup != nullptr) {
|
2023-04-07 10:21:27 +02:00
|
|
|
bool open = true;
|
|
|
|
|
2023-04-16 21:34:29 +02:00
|
|
|
const auto &minSize = currPopup->getMinSize();
|
|
|
|
const auto &maxSize = currPopup->getMaxSize();
|
|
|
|
const bool hasConstraints = minSize.x != 0 && minSize.y != 0 && maxSize.x != 0 && maxSize.y != 0;
|
2023-04-09 15:28:48 +02:00
|
|
|
|
2023-04-16 21:34:29 +02:00
|
|
|
if (hasConstraints)
|
|
|
|
ImGui::SetNextWindowSizeConstraints(minSize, maxSize);
|
|
|
|
else
|
|
|
|
ImGui::SetNextWindowSize(ImVec2(0, 0), ImGuiCond_Appearing);
|
2023-04-08 00:58:53 +02:00
|
|
|
|
2023-04-16 21:34:29 +02:00
|
|
|
auto* closeButton = currPopup->hasCloseButton() ? &open : nullptr;
|
2023-04-09 15:28:48 +02:00
|
|
|
|
2023-04-16 21:34:29 +02:00
|
|
|
const auto flags = currPopup->getFlags() | (!hasConstraints ? (ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize) : ImGuiWindowFlags_None);
|
2023-04-08 00:58:53 +02:00
|
|
|
|
2023-04-16 21:34:29 +02:00
|
|
|
if (!positionSet) {
|
|
|
|
ImGui::SetNextWindowPos(ImHexApi::System::getMainWindowPosition() + (ImHexApi::System::getMainWindowSize() / 2.0F), ImGuiCond_Always, ImVec2(0.5F, 0.5F));
|
|
|
|
if (sizeSet)
|
|
|
|
positionSet = true;
|
|
|
|
}
|
2023-04-09 15:28:48 +02:00
|
|
|
|
2023-04-16 21:34:29 +02:00
|
|
|
const auto createPopup = [&](bool displaying) {
|
|
|
|
if (displaying) {
|
2023-04-08 00:58:53 +02:00
|
|
|
currPopup->drawContent();
|
2023-04-09 23:24:48 +02:00
|
|
|
popupDisplaying = true;
|
2023-04-16 21:34:29 +02:00
|
|
|
|
|
|
|
if (ImGui::GetWindowSize().x > ImGui::GetStyle().FramePadding.x * 10)
|
|
|
|
sizeSet = true;
|
2023-04-08 00:58:53 +02:00
|
|
|
|
|
|
|
ImGui::EndPopup();
|
2023-04-09 23:24:48 +02:00
|
|
|
} else {
|
|
|
|
popupDisplaying = false;
|
2023-04-08 00:58:53 +02:00
|
|
|
}
|
2023-04-16 21:34:29 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
if (currPopup->isModal())
|
|
|
|
createPopup(ImGui::BeginPopupModal(name, closeButton, flags));
|
|
|
|
else
|
|
|
|
createPopup(ImGui::BeginPopup(name, flags));
|
|
|
|
|
|
|
|
if (!popupDisplaying || currPopup->shouldClose()) {
|
|
|
|
log::debug("Closing popup '{}'", name);
|
|
|
|
positionSet = sizeSet = false;
|
2023-04-07 10:21:27 +02:00
|
|
|
|
2023-05-10 22:29:17 +02:00
|
|
|
currPopup = nullptr;
|
2023-04-09 23:24:48 +02:00
|
|
|
}
|
2023-04-07 10:21:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Run all deferred calls
|
2022-08-17 16:15:36 +02:00
|
|
|
TaskManager::runDeferredCalls();
|
|
|
|
|
2023-03-20 13:11:43 +01:00
|
|
|
// Draw main menu popups
|
2023-03-21 15:33:43 +01:00
|
|
|
for (auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMenuItems()) {
|
2023-03-20 13:11:43 +01:00
|
|
|
const auto &[unlocalizedNames, shortcut, callback, enabledCallback] = menuItem;
|
|
|
|
|
|
|
|
if (ImGui::BeginPopup(unlocalizedNames.front().c_str())) {
|
|
|
|
createNestedMenu({ unlocalizedNames.begin() + 1, unlocalizedNames.end() }, shortcut, callback, enabledCallback);
|
|
|
|
ImGui::EndPopup();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-18 00:10:10 +01:00
|
|
|
EventManager::post<EventFrameBegin>();
|
2020-11-10 15:26:38 +01:00
|
|
|
}
|
|
|
|
|
2021-06-07 18:14:40 +02:00
|
|
|
void Window::frame() {
|
2022-07-30 22:01:49 +02:00
|
|
|
auto &io = ImGui::GetIO();
|
2023-02-17 12:03:53 +01:00
|
|
|
|
2023-03-31 11:07:53 +02:00
|
|
|
const bool popupOpen = ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopupId);
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Loop through all views and draw them
|
2023-03-21 15:33:43 +01:00
|
|
|
for (auto &[name, view] : ContentRegistry::Views::impl::getEntries()) {
|
2021-12-12 21:46:48 +01:00
|
|
|
ImGui::GetCurrentContext()->NextWindowData.ClearFlags();
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Draw always visible views
|
2021-06-07 18:14:40 +02:00
|
|
|
view->drawAlwaysVisible();
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Skip views that shouldn't be processed currently
|
2021-06-07 18:14:40 +02:00
|
|
|
if (!view->shouldProcess())
|
|
|
|
continue;
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Draw view
|
2021-12-12 21:46:48 +01:00
|
|
|
if (view->isAvailable()) {
|
2023-04-10 21:30:27 +02:00
|
|
|
ImGui::SetNextWindowSizeConstraints(view->getMinSize(), view->getMaxSize());
|
2021-12-12 21:46:48 +01:00
|
|
|
view->drawContent();
|
|
|
|
}
|
2021-12-12 00:41:44 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Handle per-view shortcuts
|
2023-03-31 11:07:53 +02:00
|
|
|
if (view->getWindowOpenState() && !popupOpen) {
|
2022-02-01 22:09:44 +01:00
|
|
|
auto window = ImGui::FindWindowByName(view->getName().c_str());
|
2022-01-11 20:29:06 +01:00
|
|
|
bool hasWindow = window != nullptr;
|
2022-02-01 22:09:44 +01:00
|
|
|
bool focused = false;
|
2021-12-23 15:11:38 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Get the currently focused view
|
2022-01-11 20:29:06 +01:00
|
|
|
if (hasWindow && !(window->Flags & ImGuiWindowFlags_Popup)) {
|
2022-01-18 00:10:10 +01:00
|
|
|
ImGui::Begin(View::toWindowName(name).c_str());
|
2021-12-31 11:01:22 +01:00
|
|
|
|
2022-01-29 21:48:59 +01:00
|
|
|
focused = ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy);
|
2021-12-23 15:11:38 +01:00
|
|
|
ImGui::End();
|
|
|
|
}
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Pass on currently pressed keys to the shortcut handler
|
2021-12-23 15:11:38 +01:00
|
|
|
for (const auto &key : this->m_pressedKeys) {
|
|
|
|
ShortcutManager::process(view, io.KeyCtrl, io.KeyAlt, io.KeyShift, io.KeySuper, focused, key);
|
|
|
|
}
|
|
|
|
}
|
2021-06-07 18:14:40 +02:00
|
|
|
}
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Handle global shortcuts
|
2023-03-31 11:07:53 +02:00
|
|
|
if (!popupOpen) {
|
|
|
|
for (const auto &key : this->m_pressedKeys) {
|
|
|
|
ShortcutManager::processGlobals(io.KeyCtrl, io.KeyAlt, io.KeyShift, io.KeySuper, key);
|
|
|
|
}
|
2022-07-30 22:01:49 +02:00
|
|
|
}
|
|
|
|
|
2022-01-18 00:10:10 +01:00
|
|
|
this->m_pressedKeys.clear();
|
2021-06-07 18:14:40 +02:00
|
|
|
}
|
|
|
|
|
2020-11-10 15:26:38 +01:00
|
|
|
void Window::frameEnd() {
|
2022-01-18 00:10:10 +01:00
|
|
|
EventManager::post<EventFrameEnd>();
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Clean up all tasks that are done
|
2022-08-17 16:15:36 +02:00
|
|
|
TaskManager::collectGarbage();
|
|
|
|
|
2021-12-10 16:09:55 +01:00
|
|
|
this->endNativeWindowFrame();
|
2023-02-17 12:03:53 +01:00
|
|
|
|
|
|
|
// Render UI
|
2020-11-10 15:26:38 +01:00
|
|
|
ImGui::Render();
|
2020-11-17 13:58:50 +01:00
|
|
|
|
2020-11-23 23:57:19 +01:00
|
|
|
int displayWidth, displayHeight;
|
|
|
|
glfwGetFramebufferSize(this->m_window, &displayWidth, &displayHeight);
|
|
|
|
glViewport(0, 0, displayWidth, displayHeight);
|
2023-05-22 12:00:35 +02:00
|
|
|
glClearColor(0.00F, 0.00F, 0.00F, 0.00F);
|
2020-11-10 15:26:38 +01:00
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
|
|
|
|
2022-01-24 20:53:17 +01:00
|
|
|
GLFWwindow *backup_current_context = glfwGetCurrentContext();
|
2020-11-23 15:51:40 +01:00
|
|
|
ImGui::UpdatePlatformWindows();
|
|
|
|
ImGui::RenderPlatformWindowsDefault();
|
|
|
|
glfwMakeContextCurrent(backup_current_context);
|
|
|
|
|
2020-11-10 15:26:38 +01:00
|
|
|
glfwSwapBuffers(this->m_window);
|
2023-05-11 18:44:50 +02:00
|
|
|
|
|
|
|
// Process layout load requests
|
|
|
|
// NOTE: This needs to be done before a new frame is started, otherwise ImGui won't handle docking correctly
|
|
|
|
LayoutManager::process();
|
2020-11-10 15:26:38 +01:00
|
|
|
}
|
|
|
|
|
2021-03-01 08:56:49 +01:00
|
|
|
void Window::initGLFW() {
|
2023-07-31 11:17:37 +02:00
|
|
|
bool restoreWindowPos = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.restore_window_pos", false);
|
|
|
|
|
2022-01-24 20:53:17 +01:00
|
|
|
glfwSetErrorCallback([](int error, const char *desc) {
|
2023-07-12 14:33:09 +02:00
|
|
|
if (error == GLFW_PLATFORM_ERROR) {
|
|
|
|
// Ignore error spam caused by Wayland not supporting moving or resizing
|
|
|
|
// windows or querying their position and size.
|
|
|
|
if (std::string_view(desc).contains("Wayland"))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-05-14 21:39:18 +02:00
|
|
|
try {
|
2023-07-12 14:33:09 +02:00
|
|
|
log::error("GLFW Error [0x{:05X}] : {}", error, desc);
|
2023-05-14 21:39:18 +02:00
|
|
|
} catch (const std::system_error &) {
|
|
|
|
// Catch and ignore system error that might be thrown when too many errors are being logged to a file
|
|
|
|
}
|
2020-11-11 14:41:44 +01:00
|
|
|
});
|
|
|
|
|
2022-01-13 14:34:27 +01:00
|
|
|
if (!glfwInit()) {
|
|
|
|
log::fatal("Failed to initialize GLFW!");
|
|
|
|
std::abort();
|
|
|
|
}
|
2020-11-10 15:26:38 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Set up used OpenGL version
|
2022-08-02 08:24:46 +02:00
|
|
|
#if defined(OS_MACOS)
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
|
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
2022-11-05 21:49:17 +01:00
|
|
|
glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
|
2022-08-02 08:24:46 +02:00
|
|
|
#else
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
|
|
|
#endif
|
2020-11-10 15:26:38 +01:00
|
|
|
|
2022-02-15 23:07:48 +01:00
|
|
|
glfwWindowHint(GLFW_DECORATED, ImHexApi::System::isBorderlessWindowModeEnabled() ? GL_FALSE : GL_TRUE);
|
2021-04-21 20:06:48 +02:00
|
|
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
2023-05-22 12:00:35 +02:00
|
|
|
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
|
2021-11-28 11:57:52 +01:00
|
|
|
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
2020-11-30 00:03:12 +01:00
|
|
|
|
2023-07-31 11:17:37 +02:00
|
|
|
if (restoreWindowPos) {
|
|
|
|
int maximized = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.window.maximized", GLFW_FALSE);
|
|
|
|
glfwWindowHint(GLFW_MAXIMIZED, maximized);
|
|
|
|
}
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Create window
|
2021-08-18 22:36:46 +02:00
|
|
|
this->m_windowTitle = "ImHex";
|
2022-02-01 22:09:44 +01:00
|
|
|
this->m_window = glfwCreateWindow(1280_scaled, 720_scaled, this->m_windowTitle.c_str(), nullptr, nullptr);
|
2021-08-18 22:36:46 +02:00
|
|
|
|
2021-06-07 18:14:40 +02:00
|
|
|
glfwSetWindowUserPointer(this->m_window, this);
|
2020-11-23 23:57:19 +01:00
|
|
|
|
2022-01-13 14:34:27 +01:00
|
|
|
if (this->m_window == nullptr) {
|
|
|
|
log::fatal("Failed to create window!");
|
|
|
|
std::abort();
|
|
|
|
}
|
2020-11-10 15:26:38 +01:00
|
|
|
|
|
|
|
glfwMakeContextCurrent(this->m_window);
|
|
|
|
glfwSwapInterval(1);
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Center window
|
2021-11-28 11:57:52 +01:00
|
|
|
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
|
|
|
|
if (monitor != nullptr) {
|
|
|
|
const GLFWvidmode *mode = glfwGetVideoMode(monitor);
|
|
|
|
if (mode != nullptr) {
|
|
|
|
int monitorX, monitorY;
|
|
|
|
glfwGetMonitorPos(monitor, &monitorX, &monitorY);
|
|
|
|
|
|
|
|
int windowWidth, windowHeight;
|
|
|
|
glfwGetWindowSize(this->m_window, &windowWidth, &windowHeight);
|
|
|
|
|
|
|
|
glfwSetWindowPos(this->m_window, monitorX + (mode->width - windowWidth) / 2, monitorY + (mode->height - windowHeight) / 2);
|
|
|
|
}
|
|
|
|
}
|
2021-11-28 01:17:48 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Set up initial window position
|
2021-08-18 22:36:46 +02:00
|
|
|
{
|
|
|
|
int x = 0, y = 0;
|
|
|
|
glfwGetWindowPos(this->m_window, &x, &y);
|
2022-02-01 18:09:40 +01:00
|
|
|
|
2023-07-31 11:17:37 +02:00
|
|
|
if (restoreWindowPos) {
|
|
|
|
x = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.window.x", x);
|
|
|
|
y = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.window.y", y);
|
|
|
|
}
|
|
|
|
|
2022-02-01 18:09:40 +01:00
|
|
|
ImHexApi::System::impl::setMainWindowPosition(x, y);
|
2023-07-31 11:17:37 +02:00
|
|
|
glfwSetWindowPos(this->m_window, x, y);
|
2021-08-18 22:36:46 +02:00
|
|
|
}
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Set up initial window size
|
2021-08-18 22:36:46 +02:00
|
|
|
{
|
|
|
|
int width = 0, height = 0;
|
|
|
|
glfwGetWindowSize(this->m_window, &width, &height);
|
|
|
|
glfwSetWindowSize(this->m_window, width, height);
|
2023-07-31 11:17:37 +02:00
|
|
|
|
|
|
|
if (restoreWindowPos) {
|
|
|
|
width = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.window.width", width);
|
|
|
|
height = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.window.height", height);
|
|
|
|
}
|
|
|
|
|
2022-02-01 18:09:40 +01:00
|
|
|
ImHexApi::System::impl::setMainWindowSize(width, height);
|
2023-07-31 11:17:37 +02:00
|
|
|
glfwSetWindowSize(this->m_window, width, height);
|
2021-08-18 22:36:46 +02:00
|
|
|
}
|
2020-12-16 22:43:07 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Register window move callback
|
2021-08-18 22:36:46 +02:00
|
|
|
glfwSetWindowPosCallback(this->m_window, [](GLFWwindow *window, int x, int y) {
|
2022-02-01 18:09:40 +01:00
|
|
|
ImHexApi::System::impl::setMainWindowPosition(x, y);
|
2020-12-16 22:43:07 +01:00
|
|
|
|
2021-08-21 13:55:21 +02:00
|
|
|
if (auto g = ImGui::GetCurrentContext(); g == nullptr || g->WithinFrameScope) return;
|
2021-06-07 18:14:40 +02:00
|
|
|
|
2022-01-24 20:53:17 +01:00
|
|
|
auto win = static_cast<Window *>(glfwGetWindowUserPointer(window));
|
2021-08-18 22:36:46 +02:00
|
|
|
win->frameBegin();
|
|
|
|
win->frame();
|
|
|
|
win->frameEnd();
|
2022-08-03 23:32:34 +02:00
|
|
|
win->processEvent();
|
2021-08-18 22:36:46 +02:00
|
|
|
});
|
2020-12-16 22:43:07 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Register window resize callback
|
2020-12-16 22:43:07 +01:00
|
|
|
glfwSetWindowSizeCallback(this->m_window, [](GLFWwindow *window, int width, int height) {
|
2022-07-06 10:21:38 +02:00
|
|
|
if (!glfwGetWindowAttrib(window, GLFW_ICONIFIED))
|
|
|
|
ImHexApi::System::impl::setMainWindowSize(width, height);
|
2021-06-07 18:14:40 +02:00
|
|
|
|
2021-08-21 13:55:21 +02:00
|
|
|
if (auto g = ImGui::GetCurrentContext(); g == nullptr || g->WithinFrameScope) return;
|
2021-08-18 22:36:46 +02:00
|
|
|
|
2022-01-24 20:53:17 +01:00
|
|
|
auto win = static_cast<Window *>(glfwGetWindowUserPointer(window));
|
2021-06-07 18:14:40 +02:00
|
|
|
win->frameBegin();
|
|
|
|
win->frame();
|
|
|
|
win->frameEnd();
|
2022-08-03 23:32:34 +02:00
|
|
|
win->processEvent();
|
2020-12-16 22:43:07 +01:00
|
|
|
});
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Register mouse handling callback
|
2022-05-27 20:42:07 +02:00
|
|
|
glfwSetMouseButtonCallback(this->m_window, [](GLFWwindow *window, int button, int action, int mods) {
|
|
|
|
hex::unused(button, mods);
|
|
|
|
|
|
|
|
auto win = static_cast<Window *>(glfwGetWindowUserPointer(window));
|
|
|
|
|
|
|
|
if (action == GLFW_PRESS)
|
2023-06-02 14:43:45 +02:00
|
|
|
win->m_buttonDown = true;
|
2022-05-27 20:42:07 +02:00
|
|
|
else if (action == GLFW_RELEASE)
|
2023-06-02 14:43:45 +02:00
|
|
|
win->m_buttonDown = false;
|
2022-08-03 23:32:34 +02:00
|
|
|
win->processEvent();
|
2022-05-27 20:42:07 +02:00
|
|
|
});
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Register scrolling callback
|
2023-02-16 08:53:23 +01:00
|
|
|
glfwSetScrollCallback(this->m_window, [](GLFWwindow *window, double xOffset, double yOffset) {
|
|
|
|
hex::unused(xOffset, yOffset);
|
|
|
|
|
|
|
|
auto win = static_cast<Window *>(glfwGetWindowUserPointer(window));
|
|
|
|
win->processEvent();
|
|
|
|
});
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Register key press callback
|
2020-11-11 14:41:44 +01:00
|
|
|
glfwSetKeyCallback(this->m_window, [](GLFWwindow *window, int key, int scancode, int action, int mods) {
|
2022-08-03 23:32:34 +02:00
|
|
|
hex::unused(mods);
|
|
|
|
|
2023-06-02 14:43:45 +02:00
|
|
|
auto win = static_cast<Window *>(glfwGetWindowUserPointer(window));
|
|
|
|
|
|
|
|
if (action == GLFW_RELEASE) {
|
|
|
|
win->m_buttonDown = false;
|
|
|
|
} else {
|
|
|
|
win->m_buttonDown = true;
|
|
|
|
}
|
|
|
|
|
2023-05-23 13:20:18 +02:00
|
|
|
if (key == GLFW_KEY_UNKNOWN) return;
|
|
|
|
|
2021-03-26 21:40:35 +01:00
|
|
|
auto keyName = glfwGetKeyName(key, scancode);
|
|
|
|
if (keyName != nullptr)
|
|
|
|
key = std::toupper(keyName[0]);
|
|
|
|
|
2022-05-27 20:42:07 +02:00
|
|
|
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
2021-12-23 15:11:38 +01:00
|
|
|
win->m_pressedKeys.push_back(key);
|
2021-01-21 15:02:49 +01:00
|
|
|
}
|
2023-06-02 14:43:45 +02:00
|
|
|
|
2022-08-03 23:32:34 +02:00
|
|
|
win->processEvent();
|
2020-11-11 14:41:44 +01:00
|
|
|
});
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Register cursor position callback
|
|
|
|
glfwSetCursorPosCallback(this->m_window, [](GLFWwindow *window, double x, double y) {
|
|
|
|
hex::unused(x, y);
|
|
|
|
|
|
|
|
auto win = static_cast<Window *>(glfwGetWindowUserPointer(window));
|
|
|
|
win->processEvent();
|
|
|
|
});
|
|
|
|
|
|
|
|
// Register window close callback
|
|
|
|
glfwSetWindowCloseCallback(this->m_window, [](GLFWwindow *window) {
|
|
|
|
EventManager::post<EventWindowClosing>(window);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Register file drop callback
|
2022-03-27 00:01:28 +01:00
|
|
|
glfwSetDropCallback(this->m_window, [](GLFWwindow *, int count, const char **paths) {
|
2023-02-17 12:03:53 +01:00
|
|
|
// Loop over all dropped files
|
2022-03-27 00:01:28 +01:00
|
|
|
for (int i = 0; i < count; i++) {
|
2022-03-16 13:23:36 +01:00
|
|
|
auto path = std::fs::path(reinterpret_cast<const char8_t *>(paths[i]));
|
2021-09-26 21:17:46 +02:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Check if a custom file handler can handle the file
|
2022-01-13 14:34:27 +01:00
|
|
|
bool handled = false;
|
2023-03-21 15:33:43 +01:00
|
|
|
for (const auto &[extensions, handler] : ContentRegistry::FileHandler::impl::getEntries()) {
|
2022-01-13 14:34:27 +01:00
|
|
|
for (const auto &extension : extensions) {
|
|
|
|
if (path.extension() == extension) {
|
2023-02-17 12:03:53 +01:00
|
|
|
// Pass the file to the handler and check if it was successful
|
|
|
|
if (!handler(path)) {
|
2022-02-01 18:09:40 +01:00
|
|
|
log::error("Handler for extensions '{}' failed to process file!", extension);
|
2023-02-17 12:03:53 +01:00
|
|
|
break;
|
|
|
|
}
|
2022-01-13 14:34:27 +01:00
|
|
|
|
|
|
|
handled = true;
|
2021-09-30 12:29:03 +02:00
|
|
|
}
|
2021-09-26 21:17:46 +02:00
|
|
|
}
|
|
|
|
}
|
2022-01-13 14:34:27 +01:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// If no custom handler was found, just open the file regularly
|
2022-01-13 14:34:27 +01:00
|
|
|
if (!handled)
|
2022-02-16 10:04:05 +01:00
|
|
|
EventManager::post<RequestOpenFile>(path);
|
2021-09-26 21:17:46 +02:00
|
|
|
}
|
2020-11-17 13:58:50 +01:00
|
|
|
});
|
|
|
|
|
2022-01-11 23:48:18 +01:00
|
|
|
glfwSetWindowSizeLimits(this->m_window, 720_scaled, 480_scaled, GLFW_DONT_CARE, GLFW_DONT_CARE);
|
2021-11-28 11:57:52 +01:00
|
|
|
|
|
|
|
glfwShowWindow(this->m_window);
|
2020-11-10 15:26:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Window::initImGui() {
|
|
|
|
IMGUI_CHECKVERSION();
|
2021-02-03 00:56:33 +01:00
|
|
|
|
2022-02-01 18:09:40 +01:00
|
|
|
auto fonts = View::getFontAtlas();
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Initialize ImGui and all other ImGui extensions
|
2022-02-01 22:09:44 +01:00
|
|
|
GImGui = ImGui::CreateContext(fonts);
|
|
|
|
GImPlot = ImPlot::CreateContext();
|
2021-08-17 13:39:46 +02:00
|
|
|
GImNodes = ImNodes::CreateContext();
|
2021-02-03 00:56:33 +01:00
|
|
|
|
2022-02-01 22:09:44 +01:00
|
|
|
ImGuiIO &io = ImGui::GetIO();
|
2022-01-24 20:53:17 +01:00
|
|
|
ImGuiStyle &style = ImGui::GetStyle();
|
2020-11-23 15:51:40 +01:00
|
|
|
|
2022-12-29 19:26:00 +01:00
|
|
|
ImNodes::GetStyle().Flags = ImNodesStyleFlags_NodeOutline | ImNodesStyleFlags_GridLines;
|
|
|
|
|
2021-02-18 17:10:56 +01:00
|
|
|
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable | ImGuiConfigFlags_NavEnableKeyboard;
|
2022-11-07 00:04:47 +01:00
|
|
|
io.ConfigWindowsMoveFromTitleBarOnly = true;
|
2023-02-16 16:29:41 +01:00
|
|
|
io.FontGlobalScale = 1.0F;
|
2022-03-22 08:20:14 +01:00
|
|
|
|
2022-06-03 10:35:05 +02:00
|
|
|
if (glfwGetPrimaryMonitor() != nullptr) {
|
2023-06-26 09:02:23 +02:00
|
|
|
bool multiWindowEnabled = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.multi_windows", 0) != 0;
|
2023-04-10 22:51:21 +02:00
|
|
|
|
|
|
|
if (multiWindowEnabled)
|
2022-03-22 08:20:14 +01:00
|
|
|
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
|
|
|
|
}
|
2021-02-18 17:10:56 +01:00
|
|
|
|
2022-02-01 18:09:40 +01:00
|
|
|
for (auto &entry : fonts->ConfigData)
|
2021-08-31 15:22:00 +02:00
|
|
|
io.Fonts->ConfigData.push_back(entry);
|
2021-02-18 17:10:56 +01:00
|
|
|
|
2022-01-11 16:02:05 +01:00
|
|
|
io.ConfigViewportsNoTaskBarIcon = false;
|
2020-11-23 15:51:40 +01:00
|
|
|
|
2021-08-17 13:39:46 +02:00
|
|
|
ImNodes::PushAttributeFlag(ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
|
|
|
|
ImNodes::PushAttributeFlag(ImNodesAttributeFlags_EnableLinkCreationOnSnap);
|
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Allow ImNodes links to always be detached without holding down any button
|
2021-08-17 13:39:46 +02:00
|
|
|
{
|
2023-02-17 12:03:53 +01:00
|
|
|
static bool always = true;
|
2021-08-17 13:39:46 +02:00
|
|
|
ImNodes::GetIO().LinkDetachWithModifierClick.Modifier = &always;
|
|
|
|
}
|
|
|
|
|
2023-08-25 23:54:39 +02:00
|
|
|
io.UserData = &this->m_imguiCustomData;
|
2021-02-25 00:17:41 +01:00
|
|
|
|
2022-07-29 17:37:30 +02:00
|
|
|
auto scale = ImHexApi::System::getGlobalScale();
|
|
|
|
style.ScaleAllSizes(scale);
|
|
|
|
io.DisplayFramebufferScale = ImVec2(scale, scale);
|
2023-02-17 12:03:00 +01:00
|
|
|
io.Fonts->SetTexID(fonts->TexID);
|
2020-12-11 14:24:42 +01:00
|
|
|
|
2020-11-23 15:51:40 +01:00
|
|
|
style.WindowMenuButtonPosition = ImGuiDir_None;
|
2022-02-01 22:09:44 +01:00
|
|
|
style.IndentSpacing = 10.0F;
|
2023-03-17 19:58:08 +01:00
|
|
|
style.DisplaySafeAreaPadding = ImVec2(0.0F, 0.0F);
|
2020-11-10 15:26:38 +01:00
|
|
|
|
2020-11-15 15:49:21 +01:00
|
|
|
// Install custom settings handler
|
2023-02-17 12:03:53 +01:00
|
|
|
{
|
|
|
|
ImGuiSettingsHandler handler;
|
|
|
|
handler.TypeName = "ImHex";
|
|
|
|
handler.TypeHash = ImHashStr("ImHex");
|
|
|
|
|
|
|
|
handler.ReadOpenFn = [](ImGuiContext *ctx, ImGuiSettingsHandler *, const char *) -> void* { return ctx; };
|
|
|
|
|
|
|
|
handler.ReadLineFn = [](ImGuiContext *, ImGuiSettingsHandler *, void *, const char *line) {
|
2023-03-21 15:33:43 +01:00
|
|
|
for (auto &[name, view] : ContentRegistry::Views::impl::getEntries()) {
|
2023-02-17 12:03:53 +01:00
|
|
|
std::string format = view->getUnlocalizedName() + "=%d";
|
|
|
|
sscanf(line, format.c_str(), &view->getWindowOpenState());
|
|
|
|
}
|
2023-03-21 15:33:43 +01:00
|
|
|
for (auto &[name, function, detached] : ContentRegistry::Tools::impl::getEntries()) {
|
2023-02-17 12:03:53 +01:00
|
|
|
std::string format = name + "=%d";
|
|
|
|
sscanf(line, format.c_str(), &detached);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
handler.WriteAllFn = [](ImGuiContext *, ImGuiSettingsHandler *handler, ImGuiTextBuffer *buf) {
|
|
|
|
buf->appendf("[%s][General]\n", handler->TypeName);
|
|
|
|
|
2023-03-21 15:33:43 +01:00
|
|
|
for (auto &[name, view] : ContentRegistry::Views::impl::getEntries()) {
|
2023-02-17 12:03:53 +01:00
|
|
|
buf->appendf("%s=%d\n", name.c_str(), view->getWindowOpenState());
|
|
|
|
}
|
2023-03-21 15:33:43 +01:00
|
|
|
for (auto &[name, function, detached] : ContentRegistry::Tools::impl::getEntries()) {
|
2023-02-17 12:03:53 +01:00
|
|
|
buf->appendf("%s=%d\n", name.c_str(), detached);
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->append("\n");
|
|
|
|
};
|
|
|
|
|
|
|
|
handler.UserData = this;
|
|
|
|
ImGui::GetCurrentContext()->SettingsHandlers.push_back(handler);
|
|
|
|
|
2023-07-07 09:20:33 +02:00
|
|
|
io.IniFilename = nullptr;
|
2023-02-17 12:03:53 +01:00
|
|
|
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Config)) {
|
2023-06-01 18:35:41 +02:00
|
|
|
if (std::fs::exists(dir) && (fs::isPathWritable(dir))) {
|
|
|
|
s_imguiSettingsPath = dir / "interface.ini";
|
2023-02-17 12:03:53 +01:00
|
|
|
break;
|
|
|
|
}
|
2021-03-01 08:56:49 +01:00
|
|
|
}
|
2022-02-21 21:55:04 +01:00
|
|
|
|
2023-06-01 18:35:41 +02:00
|
|
|
if (!s_imguiSettingsPath.empty() && wolv::io::fs::exists(s_imguiSettingsPath)) {
|
|
|
|
ImGui::LoadIniSettingsFromDisk(wolv::util::toUTF8String(s_imguiSettingsPath).c_str());
|
|
|
|
}
|
2023-02-17 12:03:53 +01:00
|
|
|
}
|
2021-03-01 08:56:49 +01:00
|
|
|
|
2020-11-10 15:26:38 +01:00
|
|
|
ImGui_ImplGlfw_InitForOpenGL(this->m_window, true);
|
2021-04-21 20:06:48 +02:00
|
|
|
|
2022-08-02 13:12:12 +02:00
|
|
|
#if defined(OS_MACOS)
|
|
|
|
ImGui_ImplOpenGL3_Init("#version 150");
|
|
|
|
#else
|
|
|
|
ImGui_ImplOpenGL3_Init("#version 130");
|
|
|
|
#endif
|
2021-08-21 00:52:11 +02:00
|
|
|
|
|
|
|
for (const auto &plugin : PluginManager::getPlugins())
|
|
|
|
plugin.setImGuiContext(ImGui::GetCurrentContext());
|
2023-02-16 18:06:40 +01:00
|
|
|
|
|
|
|
EventManager::post<RequestInitThemeHandlers>();
|
2020-11-10 15:26:38 +01:00
|
|
|
}
|
|
|
|
|
2022-01-18 00:10:10 +01:00
|
|
|
void Window::exitGLFW() {
|
2023-07-31 11:17:37 +02:00
|
|
|
{
|
|
|
|
int x = 0, y = 0, width = 0, height = 0, maximized = 0;
|
|
|
|
glfwGetWindowPos(this->m_window, &x, &y);
|
|
|
|
glfwGetWindowSize(this->m_window, &width, &height);
|
|
|
|
maximized = glfwGetWindowAttrib(this->m_window, GLFW_MAXIMIZED);
|
|
|
|
|
|
|
|
ContentRegistry::Settings::write("hex.builtin.setting.interface", "hex.builtin.setting.interface.window.", x);
|
|
|
|
ContentRegistry::Settings::write("hex.builtin.setting.interface", "hex.builtin.setting.interface.window.y", y);
|
|
|
|
ContentRegistry::Settings::write("hex.builtin.setting.interface", "hex.builtin.setting.interface.window.width", width);
|
|
|
|
ContentRegistry::Settings::write("hex.builtin.setting.interface", "hex.builtin.setting.interface.window.height", height);
|
|
|
|
ContentRegistry::Settings::write("hex.builtin.setting.interface", "hex.builtin.setting.interface.window.maximized", maximized);
|
|
|
|
}
|
|
|
|
|
2020-11-10 15:26:38 +01:00
|
|
|
glfwDestroyWindow(this->m_window);
|
|
|
|
glfwTerminate();
|
2023-05-15 11:30:24 +02:00
|
|
|
|
|
|
|
this->m_window = nullptr;
|
2020-11-10 15:26:38 +01:00
|
|
|
}
|
|
|
|
|
2022-01-18 00:10:10 +01:00
|
|
|
void Window::exitImGui() {
|
2023-06-01 18:35:41 +02:00
|
|
|
ImGui::SaveIniSettingsToDisk(wolv::util::toUTF8String(s_imguiSettingsPath).c_str());
|
2022-02-21 21:55:04 +01:00
|
|
|
|
2020-11-10 15:26:38 +01:00
|
|
|
ImGui_ImplOpenGL3_Shutdown();
|
|
|
|
ImGui_ImplGlfw_Shutdown();
|
2021-03-02 13:48:23 +01:00
|
|
|
ImPlot::DestroyContext();
|
2020-11-10 15:26:38 +01:00
|
|
|
ImGui::DestroyContext();
|
|
|
|
}
|
|
|
|
|
2021-06-18 20:09:36 +02:00
|
|
|
}
|