imgui icon indicating copy to clipboard operation
imgui copied to clipboard

ImGui window accepting dropped file from external source

Open lyd405121 opened this issue 1 year ago • 4 comments

Version/Branch of Dear ImGui:

Version 1.91

Back-ends:

imgui_impl_iWin32.cpp

Compiler, OS:

Window10

Full config/build information:

No response

Details:

Background

  • I am trying to make a robot project, using imgui to replace the ui system of mujoco
  • If imgui window accpet the file dragging from system file browser, it will be more convinient

https://github.com/user-attachments/assets/e99f6c75-f23f-4a0e-b865-9c021b140d97

How to do this

  • First change the window property

WINDOW-PROPERTY

  • Then add WM_DROPFILES window message
    case WM_DROPFILES:
    {
        HDROP hDrop = reinterpret_cast<HDROP>(wParam);
        char filename[MAX_PATH];
        UINT count = DragQueryFileA(hDrop, -1, NULL, 0);
        for (UINT i = 0; i < count; ++i)
        {
            if (DragQueryFileA(hDrop, i, filename, MAX_PATH))
            {
                printf("%s\n", filename);
            }

        }
        DragFinish(hDrop);
        return 0;
    }
  • Add the code here
  • ⚠︎ Don't forget #include <shellapi.h>

wm

  • After modify the win32 backend, imgui window now can accpet drag files

https://github.com/user-attachments/assets/2be1d57c-cbf0-4ed8-8059-f4f89cddb9b0

Can it be a feature

  • Drag file is really common operation for interaction
  • I search for glfw window and it support such feature
  • As imgui use glfw window system in linux
// code generate by coploit
#include <GLFW/glfw3.h>
#include <iostream>

// File drop callback function
void drop_callback(GLFWwindow* window, int count, const char** paths) {
    for (int i = 0; i < count; i++) {
        std::cout << "Dropped file: " << paths[i] << std::endl;
    }
}

int main() {
    // Initialize GLFW
    if (!glfwInit()) {
        std::cerr << "Failed to initialize GLFW" << std::endl;
        return -1;
    }

    // Create a windowed mode window and its OpenGL context
    GLFWwindow* window = glfwCreateWindow(800, 600, "GLFW Drag and Drop Example", NULL, NULL);
    if (!window) {
        std::cerr << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }

    // Set the file drop callback
    glfwSetDropCallback(window, drop_callback);

    // Main loop
    while (!glfwWindowShouldClose(window)) {
        // Poll for and process events
        glfwPollEvents();

        // Rendering code (if any)
        // ...

        // Swap front and back buffers
        glfwSwapBuffers(window);
    }

    // Clean up and exit
    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;
}

  • I am considering add this to imgui dock branch
  • I found when creating a new window by viewport_flag in dock branch
  • And a callback for user to handle the file path like ImGuiSizeCallback
  // Callback and functions types
typedef int     (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data);    // Callback function for ImGui::InputText()
typedef void    (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data);              // Callback function for ImGui::SetNextWindowSizeConstraints()
typedef void*   (*ImGuiMemAllocFunc)(size_t sz, void* user_data);               // Function signature for ImGui::SetAllocatorFunctions()
typedef void    (*ImGuiMemFreeFunc)(void* ptr, void* user_data);                // Function signature for ImGui::SetAllocatorFunctions()

typedef int    (*ImGuiDropFile)(char* file_path);  
  • This won't affect user's code and can be easily implement by other backends.

Screenshots/Video:

No response

Minimal, Complete and Verifiable Example code:

No response

lyd405121 avatar Aug 01 '24 01:08 lyd405121

    case WM_DROPFILES:
    {
        HDROP hDrop = reinterpret_cast<HDROP>(wParam);
        char filename[MAX_PATH];
        UINT count = DragQueryFileA(hDrop, -1, NULL, 0);
        for (UINT i = 0; i < count; ++i)
        {
            if (DragQueryFileA(hDrop, i, filename, MAX_PATH))
            {
                printf("%s\n", filename);
            }

        }
        DragFinish(hDrop);
        return 0;
    }

Please note that there are a few things that could be improved here:

  • Use DragQueryFileW instead of DragQueryFileA, because this window is created with the W version of CreateWindowEx
  • Do not use a hardcoded MAX_PATH, but query DragQueryFileW for the required length

learn-more avatar Aug 01 '24 08:08 learn-more

I am not 100% sure there is value is handling this on our side. It may be done on user/app side just as well: your WM_DROPFILES event handler can be in app code without modifying the backend.

If we add some support then we need to do it and maintain it for all backends. I suppose it would be good to add it as a helper, but it doesn't seem like a very important thing, and I worry it could grow into a more complex thing if we try to design a generic version of it: It's quite unusual API design for us to use a callback for this, ideally we would expose it as a sort of getter then user is free to handle the event when they like in the frame. Also some people would like to connect this to BeginDragDropSource() with ImGuiDragDropFlags_SourceExtern, some people may want to query it differently. It's not something we can do without careful design.

ocornut avatar Aug 01 '24 12:08 ocornut

Also see #2602

ocornut avatar Aug 01 '24 12:08 ocornut

  • Thanks for your reply
  • Careful design indeed the most important part
  • I rethinked of that when I was laying on my bed last night
  • It is truely better to work with BeginDragDropSource() and put the file name in the payload
  • I will try it in my project, and search for better way for all platform

lyd405121 avatar Aug 02 '24 01:08 lyd405121