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();
|
|
|
|
|
2023-08-26 23:43:35 +02:00
|
|
|
this->m_logoTexture = ImGui::Texture(romfs::get("logo.png").span());
|
2023-02-17 12:03:53 +01:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2023-09-07 20:33:49 +02:00
|
|
|
void Window::fullFrame() {
|
2023-10-04 12:00:32 +02:00
|
|
|
this->m_lastFrameTime = glfwGetTime();
|
|
|
|
|
2023-09-07 20:33:49 +02:00
|
|
|
glfwPollEvents();
|
|
|
|
|
|
|
|
// Render frame
|
|
|
|
this->frameBegin();
|
|
|
|
this->frame();
|
|
|
|
this->frameEnd();
|
|
|
|
}
|
|
|
|
|
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 {
|
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-09-07 20:33:49 +02:00
|
|
|
this->fullFrame();
|
2023-08-11 22:03:30 +02:00
|
|
|
|
|
|
|
// 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));
|
|
|
|
|
2023-10-04 12:00:32 +02:00
|
|
|
// Custom titlebar buttons implementation for borderless window mode
|
2023-05-25 09:26:40 +02:00
|
|
|
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);
|
|
|
|
}
|
impr: Don't force using discrete graphics card on macOS (#1341)
<!--
Please provide as much information as possible about what your PR aims
to do.
PRs with no description will most likely be closed until more
information is provided.
If you're planing on changing fundamental behaviour or add big new
features, please open a GitHub Issue first before starting to work on
it.
If it's not something big and you still want to contact us about it,
feel free to do so !
-->
### Problem description
<!-- Describe the bug that you fixed/feature request that you
implemented, or link to an existing issue describing it -->
When starting ImHex on a MacBook model with both integrated and discrete
graphics, it will force the computer to use the discrete graphics card.
This causes increased power usage, meaning the fans will spin up, the
battery will drain faster, etc. This program is not very graphics
intensive, so using the discrete graphics card shouldn't be needed.
### Implementation description
<!-- Explain what you did to correct the problem -->
I changed the
[`GLFW_COCOA_GRAPHICS_SWITCHING`](https://www.glfw.org/docs/latest/window_guide.html#window_hints_osx)
setting in GLFW to not enforce using the discrete graphics.
### Screenshots
<!-- If your change is visual, take a screenshot showing it. Ideally,
make before/after sceenshots -->
### Additional things
<!-- Anything else you would like to say -->
My editor is configured to automatically remove trailing whitespace, so
I hope that those whitespace changes are ok
2023-10-05 08:39:53 +02:00
|
|
|
|
2023-05-25 09:26:40 +02:00
|
|
|
|
|
|
|
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());
|
|
|
|
}
|
impr: Don't force using discrete graphics card on macOS (#1341)
<!--
Please provide as much information as possible about what your PR aims
to do.
PRs with no description will most likely be closed until more
information is provided.
If you're planing on changing fundamental behaviour or add big new
features, please open a GitHub Issue first before starting to work on
it.
If it's not something big and you still want to contact us about it,
feel free to do so !
-->
### Problem description
<!-- Describe the bug that you fixed/feature request that you
implemented, or link to an existing issue describing it -->
When starting ImHex on a MacBook model with both integrated and discrete
graphics, it will force the computer to use the discrete graphics card.
This causes increased power usage, meaning the fans will spin up, the
battery will drain faster, etc. This program is not very graphics
intensive, so using the discrete graphics card shouldn't be needed.
### Implementation description
<!-- Explain what you did to correct the problem -->
I changed the
[`GLFW_COCOA_GRAPHICS_SWITCHING`](https://www.glfw.org/docs/latest/window_guide.html#window_hints_osx)
setting in GLFW to not enforce using the discrete graphics.
### Screenshots
<!-- If your change is visual, take a screenshot showing it. Ideally,
make before/after sceenshots -->
### Additional things
<!-- Anything else you would like to say -->
My editor is configured to automatically remove trailing whitespace, so
I hope that those whitespace changes are ok
2023-10-05 08:39:53 +02:00
|
|
|
|
2023-05-25 09:26:40 +02:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-07 16:40:37 +01:00
|
|
|
static bool isAnyViewOpen() {
|
|
|
|
const auto &views = ContentRegistry::Views::impl::getEntries();
|
|
|
|
return std::any_of(views.begin(), views.end(),
|
|
|
|
[](const auto &entry) {
|
|
|
|
return entry.second->getWindowOpenState();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
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();
|
2023-11-08 11:53:26 +01:00
|
|
|
|
|
|
|
const auto menuBarHeight = ImGui::GetCurrentWindow()->MenuBarHeight();
|
|
|
|
auto sidebarPos = ImGui::GetCursorPos() + ImVec2(0, menuBarHeight);
|
2023-11-07 16:40:37 +01:00
|
|
|
auto sidebarWidth = ContentRegistry::Interface::impl::getSidebarItems().empty() ? 0 : 20_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;
|
2023-11-07 16:40:37 +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);
|
|
|
|
|
2023-11-08 11:53:26 +01:00
|
|
|
auto sideBarFocused = ImGui::IsWindowFocused();
|
|
|
|
|
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) {
|
2023-11-08 11:53:26 +01:00
|
|
|
ImGui::SetNextWindowPos(ImGui::GetWindowPos() + sidebarPos + ImVec2(sidebarWidth - 1_scaled, -1_scaled));
|
|
|
|
ImGui::SetNextWindowSize(ImVec2(250_scaled, dockSpaceSize.y + 11_scaled - footerHeight));
|
2022-01-22 22:03:19 +01:00
|
|
|
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1);
|
2023-11-07 16:40:37 +01:00
|
|
|
if (ImGui::Begin("SideBarWindow", &open, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar)) {
|
2022-01-22 22:03:19 +01:00
|
|
|
callback();
|
2023-11-08 11:53:26 +01:00
|
|
|
|
|
|
|
if (!ImGui::IsWindowFocused() && !sideBarFocused) {
|
|
|
|
openWindow = -1;
|
|
|
|
}
|
2022-01-22 22:03:19 +01:00
|
|
|
}
|
|
|
|
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
|
|
|
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
|
|
|
|
2023-11-07 16:40:37 +01:00
|
|
|
if (ImHexApi::Provider::isValid() && isAnyViewOpen()) {
|
2023-11-08 11:53:26 +01:00
|
|
|
drawList->AddLine(
|
|
|
|
ImGui::GetWindowPos() + sidebarPos + ImVec2(sidebarWidth - 1_scaled, -2_scaled),
|
|
|
|
ImGui::GetWindowPos() + sidebarPos + ImGui::GetWindowSize() - ImVec2(dockSpaceSize.x + 1_scaled, footerHeight - ImGui::GetStyle().FramePadding.y - 1_scaled + menuBarHeight * 2),
|
|
|
|
ImGui::GetColorU32(ImGuiCol_Separator));
|
2023-11-07 16:40:37 +01:00
|
|
|
}
|
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
|
|
|
|
2023-10-05 08:55:54 +02:00
|
|
|
// Reset popup position if it's outside the main window when multi-viewport is not enabled
|
|
|
|
// If not done, the popup will be stuck outside the main window and cannot be accessed anymore
|
|
|
|
if ((ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == ImGuiConfigFlags_None) {
|
|
|
|
const auto currWindowPos = ImGui::GetWindowPos();
|
|
|
|
const auto minWindowPos = ImHexApi::System::getMainWindowPosition() - ImGui::GetWindowSize();
|
|
|
|
const auto maxWindowPos = ImHexApi::System::getMainWindowPosition() + ImHexApi::System::getMainWindowSize();
|
|
|
|
if (currWindowPos.x > maxWindowPos.x || currWindowPos.y > maxWindowPos.y || currWindowPos.x < minWindowPos.x || currWindowPos.y < minWindowPos.y) {
|
|
|
|
positionSet = false;
|
|
|
|
GImGui->MovingWindow = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-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
|
2023-10-30 21:53:44 +01:00
|
|
|
if (hasWindow && (window->Flags & ImGuiWindowFlags_Popup) != ImGuiWindowFlags_Popup) {
|
|
|
|
auto windowName = View::toWindowName(name);
|
|
|
|
ImGui::Begin(windowName.c_str());
|
2021-12-31 11:01:22 +01:00
|
|
|
|
2023-10-30 21:53:44 +01:00
|
|
|
// Detect if the window is focused
|
2022-01-29 21:48:59 +01:00
|
|
|
focused = ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy);
|
2023-10-30 21:53:44 +01:00
|
|
|
|
|
|
|
// Dock the window if it's not already docked
|
|
|
|
if (view->didWindowJustOpen() && !ImGui::IsWindowDocked()) {
|
|
|
|
ImGui::DockBuilderDockWindow(windowName.c_str(), ImHexApi::System::getMainDockSpaceId());
|
|
|
|
}
|
|
|
|
|
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);
|
impr: Don't force using discrete graphics card on macOS (#1341)
<!--
Please provide as much information as possible about what your PR aims
to do.
PRs with no description will most likely be closed until more
information is provided.
If you're planing on changing fundamental behaviour or add big new
features, please open a GitHub Issue first before starting to work on
it.
If it's not something big and you still want to contact us about it,
feel free to do so !
-->
### Problem description
<!-- Describe the bug that you fixed/feature request that you
implemented, or link to an existing issue describing it -->
When starting ImHex on a MacBook model with both integrated and discrete
graphics, it will force the computer to use the discrete graphics card.
This causes increased power usage, meaning the fans will spin up, the
battery will drain faster, etc. This program is not very graphics
intensive, so using the discrete graphics card shouldn't be needed.
### Implementation description
<!-- Explain what you did to correct the problem -->
I changed the
[`GLFW_COCOA_GRAPHICS_SWITCHING`](https://www.glfw.org/docs/latest/window_guide.html#window_hints_osx)
setting in GLFW to not enforce using the discrete graphics.
### Screenshots
<!-- If your change is visual, take a screenshot showing it. Ideally,
make before/after sceenshots -->
### Additional things
<!-- Anything else you would like to say -->
My editor is configured to automatically remove trailing whitespace, so
I hope that those whitespace changes are ok
2023-10-05 08:39:53 +02:00
|
|
|
glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, GLFW_TRUE);
|
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);
|
2023-10-04 12:00:32 +02:00
|
|
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
|
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
|
|
|
|
2023-10-30 16:40:14 +01:00
|
|
|
// Force window to be fully opaque by default
|
|
|
|
glfwSetWindowOpacity(this->m_window, 1.0F);
|
|
|
|
|
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-10-04 12:00:32 +02:00
|
|
|
#if !defined(OS_WEB)
|
|
|
|
// Register key press callback
|
|
|
|
glfwSetKeyCallback(this->m_window, [](GLFWwindow *window, int key, int scancode, int action, int mods) {
|
|
|
|
hex::unused(mods);
|
2022-08-03 23:32:34 +02:00
|
|
|
|
2023-10-04 12:00:32 +02:00
|
|
|
auto win = static_cast<Window *>(glfwGetWindowUserPointer(window));
|
2023-06-02 14:43:45 +02:00
|
|
|
|
2023-10-04 12:00:32 +02:00
|
|
|
if (action == GLFW_RELEASE) {
|
|
|
|
win->m_buttonDown = false;
|
|
|
|
} else {
|
|
|
|
win->m_buttonDown = true;
|
|
|
|
}
|
2023-06-02 14:43:45 +02:00
|
|
|
|
2023-10-04 12:00:32 +02:00
|
|
|
if (key == GLFW_KEY_UNKNOWN) return;
|
2023-05-23 13:20:18 +02:00
|
|
|
|
2023-10-04 12:00:32 +02:00
|
|
|
auto keyName = glfwGetKeyName(key, scancode);
|
|
|
|
if (keyName != nullptr)
|
|
|
|
key = std::toupper(keyName[0]);
|
2021-03-26 21:40:35 +01:00
|
|
|
|
2023-10-04 12:00:32 +02:00
|
|
|
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
|
|
|
win->m_pressedKeys.push_back(key);
|
|
|
|
}
|
2023-06-02 14:43:45 +02:00
|
|
|
|
2023-10-04 12:00:32 +02:00
|
|
|
win->processEvent();
|
|
|
|
});
|
|
|
|
#endif
|
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
|
|
|
}
|
|
|
|
|
2023-10-04 12:00:32 +02:00
|
|
|
void Window::resize(i32 width, i32 height) {
|
|
|
|
glfwSetWindowSize(this->m_window, width, height);
|
|
|
|
}
|
|
|
|
|
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-10-21 23:07:33 +02:00
|
|
|
bool multiWindowEnabled = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.multi_windows", false);
|
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
|
|
|
|
2023-10-04 12:00:32 +02: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");
|
2023-10-04 12:00:32 +02:00
|
|
|
#elif defined(OS_WEB)
|
|
|
|
ImGui_ImplOpenGL3_Init();
|
2022-08-02 13:12:12 +02:00
|
|
|
#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
|
|
|
}
|