From 3449525ead7c6f2ed5a97e71553266434cceb7af Mon Sep 17 00:00:00 2001 From: WerWolv Date: Wed, 10 Jan 2024 23:46:50 +0100 Subject: [PATCH] feat: Added drag-n-drop overlay for windows --- .../include/hex/api/event_manager.hpp | 3 + main/gui/source/window/win_window.cpp | 85 +++++++++++++++++++ main/gui/source/window/window.cpp | 28 +----- plugins/builtin/source/content/events.cpp | 23 +++++ plugins/builtin/source/content/ui_items.cpp | 45 ++++++++++ 5 files changed, 157 insertions(+), 27 deletions(-) diff --git a/lib/libimhex/include/hex/api/event_manager.hpp b/lib/libimhex/include/hex/api/event_manager.hpp index c552b6e2b..455b576aa 100644 --- a/lib/libimhex/include/hex/api/event_manager.hpp +++ b/lib/libimhex/include/hex/api/event_manager.hpp @@ -253,6 +253,9 @@ namespace hex { EVENT_DEF(EventSearchBoxClicked, u32); EVENT_DEF(EventViewOpened, View*); + EVENT_DEF(EventFileDragged, bool); + EVENT_DEF(EventFileDropped, std::fs::path); + EVENT_DEF(EventProviderDataModified, prv::Provider *, u64, u64, const u8*); EVENT_DEF(EventProviderDataInserted, prv::Provider *, u64, u64); EVENT_DEF(EventProviderDataRemoved, prv::Provider *, u64, u64); diff --git a/main/gui/source/window/win_window.cpp b/main/gui/source/window/win_window.cpp index 4081cd62b..6b186e8fb 100644 --- a/main/gui/source/window/win_window.cpp +++ b/main/gui/source/window/win_window.cpp @@ -21,6 +21,7 @@ #include #include #include + #include #include @@ -294,10 +295,94 @@ namespace hex { } } + class DropManager : public IDropTarget { + public: + DropManager() = default; + virtual ~DropManager() = default; + + ULONG AddRef() override { return 1; } + ULONG Release() override { return 0; } + + HRESULT QueryInterface(REFIID riid, void **ppvObject) override { + if (riid == IID_IDropTarget) { + *ppvObject = this; + + return S_OK; + } + + *ppvObject = nullptr; + return E_NOINTERFACE; + } + + HRESULT STDMETHODCALLTYPE DragEnter( + IDataObject *, + DWORD, + POINTL, + DWORD *pdwEffect) override + { + EventFileDragged::post(true); + + *pdwEffect = DROPEFFECT_NONE; + return S_OK; + } + + HRESULT STDMETHODCALLTYPE DragOver( + DWORD, + POINTL, + DWORD *pdwEffect) override + { + *pdwEffect = DROPEFFECT_NONE; + return S_OK; + } + + HRESULT STDMETHODCALLTYPE DragLeave() override + { + EventFileDragged::post(false); + return S_OK; + } + + HRESULT STDMETHODCALLTYPE Drop( + IDataObject *pDataObj, + DWORD, + POINTL, + DWORD *pdwEffect) override + { + FORMATETC fmte = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + STGMEDIUM stgm; + + if (SUCCEEDED(pDataObj->GetData(&fmte, &stgm))) { + auto hdrop = HDROP(stgm.hGlobal); + auto fileCount = DragQueryFile(hdrop, 0xFFFFFFFF, nullptr, 0); + + for (UINT i = 0; i < fileCount; i++) { + WCHAR szFile[MAX_PATH]; + UINT cch = DragQueryFileW(hdrop, i, szFile, MAX_PATH); + if (cch > 0 && cch < MAX_PATH) { + EventFileDropped::post(szFile); + } + } + + ReleaseStgMedium(&stgm); + } + + EventFileDragged::post(false); + + *pdwEffect &= DROPEFFECT_NONE; + return S_OK; + + return S_OK; + } + }; + void Window::setupNativeWindow() { // Setup borderless window auto hwnd = glfwGetWin32Window(m_window); + OleInitialize(nullptr); + + static DropManager dm; + RegisterDragDrop(hwnd, &dm); + bool borderlessWindowMode = ImHexApi::System::isBorderlessWindowModeEnabled(); // Set up the correct window procedure based on the borderless window mode state diff --git a/main/gui/source/window/window.cpp b/main/gui/source/window/window.cpp index 875ab5dd6..69beb2d12 100644 --- a/main/gui/source/window/window.cpp +++ b/main/gui/source/window/window.cpp @@ -565,11 +565,6 @@ namespace hex { ImGui::EndMenuBar(); } - if (ImGui::BeginDragDropTargetCustom(ImGui::GetCurrentWindow()->MenuBarRect(), ImGui::GetCurrentWindow()->ID)) { - - ImGui::EndDragDropTarget(); - } - this->beginNativeWindowFrame(); if (ImHexApi::Provider::isValid() && isAnyViewOpen()) { @@ -1156,29 +1151,8 @@ namespace hex { // Register file drop callback glfwSetDropCallback(m_window, [](GLFWwindow *, int count, const char **paths) { - // Loop over all dropped files for (int i = 0; i < count; i++) { - auto path = std::fs::path(reinterpret_cast(paths[i])); - - // Check if a custom file handler can handle the file - bool handled = false; - for (const auto &[extensions, handler] : ContentRegistry::FileHandler::impl::getEntries()) { - for (const auto &extension : extensions) { - if (path.extension() == extension) { - // Pass the file to the handler and check if it was successful - if (!handler(path)) { - log::error("Handler for extensions '{}' failed to process file!", extension); - break; - } - - handled = true; - } - } - } - - // If no custom handler was found, just open the file regularly - if (!handled) - RequestOpenFile::post(path); + EventFileDropped::post(reinterpret_cast(paths[i])); } }); diff --git a/plugins/builtin/source/content/events.cpp b/plugins/builtin/source/content/events.cpp index 3b109473e..bc147c4d6 100644 --- a/plugins/builtin/source/content/events.cpp +++ b/plugins/builtin/source/content/events.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include @@ -172,6 +173,28 @@ namespace hex::plugin::builtin { ImHexApi::HexEditor::impl::setCurrentSelection(region); }); + EventFileDropped::subscribe([](const std::fs::path &path) { + // Check if a custom file handler can handle the file + bool handled = false; + for (const auto &[extensions, handler] : ContentRegistry::FileHandler::impl::getEntries()) { + for (const auto &extension : extensions) { + if (path.extension() == extension) { + // Pass the file to the handler and check if it was successful + if (!handler(path)) { + log::error("Handler for extensions '{}' failed to process file!", extension); + break; + } + + handled = true; + } + } + } + + // If no custom handler was found, just open the file regularly + if (!handled) + RequestOpenFile::post(path); + }); + fs::setFileBrowserErrorCallback([](const std::string& errMsg){ #if defined(NFD_PORTAL) ui::PopupError::open(hex::format("hex.builtin.popup.error.file_dialog.portal"_lang, errMsg)); diff --git a/plugins/builtin/source/content/ui_items.cpp b/plugins/builtin/source/content/ui_items.cpp index a0d840713..8075729c2 100644 --- a/plugins/builtin/source/content/ui_items.cpp +++ b/plugins/builtin/source/content/ui_items.cpp @@ -54,8 +54,53 @@ namespace hex::plugin::builtin { } } + static bool s_drawDragDropOverlay = false; + static void drawDragNDropOverlay() { + if (!s_drawDragDropOverlay) + return; + + auto drawList = ImGui::GetForegroundDrawList(); + + drawList->PushClipRectFullScreen(); + { + const auto windowPos = ImHexApi::System::getMainWindowPosition(); + const auto windowSize = ImHexApi::System::getMainWindowSize(); + const auto center = windowPos + (windowSize / 2.0F) - scaled({ 0, 50 }); + + // Draw background + { + const ImVec2 margin = scaled({ 15, 15 }); + drawList->AddRectFilled(windowPos, windowPos + windowSize, ImGui::GetColorU32(ImGuiCol_WindowBg, 200.0/255.0)); + drawList->AddRect(windowPos + margin, (windowPos + windowSize) - margin, ImGuiExt::GetCustomColorU32(ImGuiCustomCol_Highlight), 10_scaled, ImDrawFlags_None, 7.5_scaled); + } + + // Draw drag n drop icon + { + const ImVec2 iconSize = scaled({ 64, 64 }); + const auto offset = scaled({ 15, 15 }); + const auto margin = scaled({ 20, 20 }); + + const auto text = "Drop files here to open them..."; + const auto textSize = ImGui::CalcTextSize(text); + + drawList->AddShadowRect(center - ImVec2(textSize.x, iconSize.y + 40_scaled) / 2.0F - offset - margin, center + ImVec2(textSize.x, iconSize.y + 75_scaled) / 2.0F + offset + ImVec2(0, textSize.y) + margin, ImGui::GetColorU32(ImGuiCol_WindowShadow), 20_scaled, ImVec2(), ImDrawFlags_None, 10_scaled); + drawList->AddRectFilled(center - ImVec2(textSize.x, iconSize.y + 40_scaled) / 2.0F - offset - margin, center + ImVec2(textSize.x, iconSize.y + 75_scaled) / 2.0F + offset + ImVec2(0, textSize.y) + margin, ImGui::GetColorU32(ImGuiCol_MenuBarBg, 10), 1_scaled, ImDrawFlags_None); + drawList->AddRect(center - iconSize / 2.0F - offset, center + iconSize / 2.0F - offset, ImGui::GetColorU32(ImGuiCol_Text), 5_scaled, ImDrawFlags_None, 7.5_scaled); + drawList->AddRect(center - iconSize / 2.0F + offset, center + iconSize / 2.0F + offset, ImGui::GetColorU32(ImGuiCol_Text), 5_scaled, ImDrawFlags_None, 7.5_scaled); + + drawList->AddText(center + ImVec2(-textSize.x / 2, 85_scaled), ImGui::GetColorU32(ImGuiCol_Text), text); + } + } + drawList->PopClipRect(); + } + void addGlobalUIItems() { EventFrameEnd::subscribe(drawGlobalPopups); + EventFrameEnd::subscribe(drawDragNDropOverlay); + + EventFileDragged::subscribe([](bool entered) { + s_drawDragDropOverlay = entered; + }); } void addFooterItems() {