Portable File Dialogs

repository·main·Indexed 22 days ago

https://github.com/samhocevar/portable-file-dialogs

A lightweight, single-header C++11 library providing native, secure, and cross-platform file dialogs for Windows, macOS, and Linux. It supports open, save, folder selection, message boxes, and notifications with both synchronous and asynchronous execution modes. The library uses native backends such as Win32 API, AppleScript, Zenity, and KDialog to ensure a consistent user experience.

Tokens
5K
Snippets
21
Records
24
Agent score
78%

What's inside portable-file-dialogs

  1. Overview of Portable File Dialogs

    main

    Portable File Dialogs is a free C++11 library designed to provide native file dialogs across multiple platforms. It is a single-header library with no extra library dependencies, making it easy to integrate into existing C++ projects.

    Key features include:

    • Cross-platform support: Works on Windows, Mac OS X, and Linux.
    • Execution modes: Supports both synchronous (blocking) and asynchronous (non-blocking) dialogs.
    • Cancelable: Asynchronous dialogues can be killed without requiring user interaction.
    • Security: Designed to be immune to shell-quote vulnerabilities.
  2. Supported Platforms and Backends

    main

    The library uses native backends to ensure a consistent user experience on each operating system:

    • Windows: Uses the Win32 API (compatible with all known versions).
    • Mac OS X: Uses AppleScript.
    • Linux (GNOME): Uses Zenity or its clones Matedialog and Qarma.
    • Linux (KDE): Uses KDialog.

    Experimental support for Emscripten is currently in development.

  3. How dialog lifecycle and blocking behavior works

    main

    Dialogs in this library inherit from pfd::dialog. Their behavior regarding program execution depends on how you instantiate them:

    • Blocking Call: If you call a dialog method directly (e.g., pfd::message::message(...)), the execution will block at that line until the user interacts with the dialog.
    • Non-blocking (Scope-based): If you assign the dialog to a variable, the dialog will only block when that variable goes out of scope. This allows you to perform asynchronous operations while the dialog is visible.

    To check if a user has interacted with the dialog without blocking, use pfd::dialog::ready().

    // Blocking example
    pfd::message::message("Hi", "there");
    
    // Non-blocking example
    {
        auto m = pfd::message::message("Hi", "there");
    
        // ... perform asynchronous operations here
    }
  4. Use Portable File Dialogs as a header-only library

    main

    To use the library in header-only mode, simply include the main header file in your source code. This is the simplest way to integrate the library into your project.

    #include "portable-file-dialogs.h"
    
    /* ... */
    
        pfd::message::message("Hello", "This is a test");
    
    /* ... */
  5. Use Portable File Dialogs as a single-file library

    main

    To reduce compilation times, you can use the library as a single-file library. This requires defining the PFD_SKIP_IMPLEMENTATION macro before including the header in all files except for one dedicated implementation file.

    1. Create a file (e.g., pfd-impl.cpp) and include the header without any macros.
    2. In all other files where you need the library, define PFD_SKIP_IMPLEMENTATION 1 before including the header.
    // In pfd-impl.cpp
    #include "portable-file-dialogs.h"
    // In all other files
    #define PFD_SKIP_IMPLEMENTATION 1
    #include "portable-file-dialogs.h"
  6. Implement an asynchronous message box

    main

    To prevent your application from freezing while waiting for user input, use pfd::message::ready() in a loop. This allows you to perform other tasks (like printing status updates or processing background work) while the dialog is visible.

    // Create the message box
    auto box = pfd::message("Unsaved Files", "Do you want to save the current "
                            "document before closing the application?",
                            pfd::choice::yes_no_cancel,
                            pfd::icon::warning);
    
    // Perform other tasks while waiting for user input
    while (!box.ready(1000)) {
        std::cout << "Waited 1 second for user input...\n";
    }
    
    // Act depending on the selected button
    switch (box.result())
    {
        case pfd::button::yes:    std::cout << "User agreed.\n"; break;
        case pfd::button::no:     std::cout << "User disagreed.\n"; break;
        case pfd::button::cancel: std::cout << "User freaked out.\n"; break;
    }
    auto box = pfd::message("Unsaved Files", "Do you want to save the current "
                            "document before closing the application?",
                            pfd::choice::yes_no_cancel,
                            pfd::icon::warning);
    
    while (!box.ready(1000))
        std::cout << "Waited 1 second for user input...\n";
    
    switch (box.result())
    {
        case pfd::button::yes:    std::cout << "User agreed.\n"; break;
        case pfd::button::no:     std::cout << "User disagreed.\n"; break;
        case pfd::button::cancel: std::cout << "User freaked out.\n"; break;
    }
  7. Perform simple synchronous folder selection

    main

    For simple use cases where you want to block execution until the user selects a folder or cancels, call result() directly on the pfd::select_folder object. The call will wait for user action before returning the path.

    auto selection = pfd::select_folder("Select a folder").result();
    if (!selection.empty())
        std::cout << "User selected folder " << selection << "\n";
  8. Configure file filters in open_file dialogs

    main

    To restrict the files visible in the dialog, provide a vector of strings to the pfd::open_file constructor. Each entry in the vector should follow the pattern: "Filter Name", "*.extension1 *.extension2".

    Example of setting multiple filters and enabling multiselect:

    auto selection = pfd::open_file("Select a file", ".",
                                    { "Image Files", "*.png *.jpg *.jpeg *.bmp",
                                      "Audio Files", "*.wav *.mp3",
                                      "All Files", "*" },
                                    pfd::opt::multiselect).result();
    auto selection = pfd::open_file("Select a file", ".",
                                    { "Image Files", "*.png *.jpg *.jpeg *.bmp",
                                      "Audio Files", "*.wav *.mp3",
                                      "All Files", "*" },
                                    pfd::opt::multiselect).result();
    // Do something with selection
    for (auto const &filename : dialog.result())
        std::cout << "Selected file: " << filename << "\n";
  9. Perform asynchronous folder selection with ready()

    main

    To prevent your application from blocking while the dialog is open, use the ready(int timeout) method. This allows you to poll the dialog status and perform other tasks (like updating a UI or printing status messages) while waiting for user input.

    ready(timeout) returns true if the user has interacted with the dialog (selected or canceled) within the specified timeout milliseconds. If the timeout expires without user action, it returns false.

    // Folder selection dialog
    auto dialog = pfd::select_folder("Select folder to open");
    
    // Do something while waiting for user input
    while (!dialog.ready(1000))
        std::cout << "Waited 1 second for user input...\n";
    
    // Act depending on the user choice
    std::cout << "Selected folder: " << dialog.result() << "\n";
  10. Perform asynchronous file opening with pfd::open_file::ready()

    main

    By default, calling .result() on a pfd::open_file object is a blocking operation. To prevent your application from freezing while waiting for user input, use the ready() method. This allows you to perform other tasks in a loop while checking if the user has interacted with the dialog.

    Method Signature

    bool pfd::open_file::ready(int timeout = pfd::default_wait_timeout);

    Behavior

    • ready(timeout) returns true if the user has completed the dialog action (selection or cancellation) within the specified timeout milliseconds.
    • If the timeout expires before the user acts, it returns false.
    • Once ready() returns true, you can call .result() to retrieve the user's choice without blocking.
    // File open dialog
    auto dialog = pfd::open_file("Select file to open");
    
    // Do something while waiting for user input
    while (!dialog.ready(1000))
        std::cout << "Waited 1 second for user input...\n";
    
    // Act depending on the user choice
    std::cout << "Number of selected files: " << dialog.result().size() << "\n";
  11. Check if a message box is ready using pfd::message::ready()

    main

    The ready() method allows you to check if the user has interacted with the message box without blocking your application's execution. This is useful for implementing asynchronous behavior or timeouts.

    Method Signature

    bool pfd::message::ready(int timeout = pfd::default_wait_timeout);

    If the user does not press a button within the specified timeout (in milliseconds), the function returns false. If the user has interacted with the box, it returns true.

    bool pfd::message::ready(int timeout = pfd::default_wait_timeout);