Skip to content

Commit

Permalink
New feature: Accept native parent window handle
Browse files Browse the repository at this point in the history
This is necessary for platforms to present the dialog properly, e.g. ensuring that the dialog never goes behind the parent window.
  • Loading branch information
btzy committed May 26, 2024
1 parent 38da115 commit 47b449c
Show file tree
Hide file tree
Showing 11 changed files with 773 additions and 86 deletions.
76 changes: 74 additions & 2 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Installing Dependencies
- name: Install Dependencies
run: sudo apt-get update && sudo apt-get install dos2unix
- name: Convert to Unix line endings
run: dos2unix */*
Expand Down Expand Up @@ -72,7 +72,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Installing Dependencies
- name: Install Dependencies
run: sudo apt-get update && sudo apt-get install ${{ matrix.portal.dep }}
- name: Configure
run: mkdir build && mkdir install && cd build && cmake -DCMAKE_INSTALL_PREFIX="../install" -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=${{ matrix.compiler.c }} -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cpp }} -DCMAKE_CXX_STANDARD=${{ matrix.cppstd }} -DCMAKE_C_FLAGS="-Wall -Wextra -Werror -pedantic" -DCMAKE_CXX_FLAGS="-Wall -Wextra -Werror -pedantic" -DNFD_PORTAL=${{ matrix.portal.flag }} -DNFD_APPEND_EXTENSION=${{ matrix.autoappend.flag }} -DBUILD_SHARED_LIBS=${{ matrix.shared_lib.flag }} -DNFD_BUILD_TESTS=ON ..
Expand Down Expand Up @@ -189,3 +189,75 @@ jobs:
path: |
build/src/*
build/test/*
build-ubuntu-sdl2:

name: Ubuntu latest - GCC, ${{ matrix.portal.name }}, Static, SDL2
runs-on: ubuntu-latest

strategy:
matrix:
portal: [ {flag: OFF, dep: libgtk-3-dev, name: GTK}, {flag: ON, dep: libdbus-1-dev, name: Portal} ] # The NFD_PORTAL setting defaults to OFF (i.e. uses GTK)

steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install Dependencies
run: sudo apt-get update && sudo apt-get install ${{ matrix.portal.dep }} libsdl2-dev libsdl2-ttf-dev
- name: Configure
run: mkdir build && mkdir install && cd build && cmake -DCMAKE_INSTALL_PREFIX="../install" -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-Wall -Wextra -Werror -pedantic" -DCMAKE_CXX_FLAGS="-Wall -Wextra -Werror -pedantic" -DNFD_PORTAL=${{ matrix.portal.flag }} -DNFD_APPEND_EXTENSION=OFF -DNFD_BUILD_TESTS=OFF -DNFD_BUILD_SDL2_TESTS=ON ..
- name: Build
run: cmake --build build --target install
- name: Upload test binaries
uses: actions/upload-artifact@v2
with:
name: Ubuntu latest - GCC, ${{ matrix.portal.name }}, Static, SDL2
path: |
build/src/*
build/test/*
build-macos-sdl2:

name: MacOS latest - Clang, Static, SDL2
runs-on: macos-latest

steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install Dependencies
run: brew install sdl2 sdl2_ttf
- name: Configure
run: mkdir build && mkdir install && cd build && cmake -DCMAKE_INSTALL_PREFIX="../install" -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-Wall -Wextra -Werror -pedantic" -DCMAKE_CXX_FLAGS="-Wall -Wextra -Werror -pedantic" -DNFD_BUILD_TESTS=OFF -DNFD_BUILD_SDL2_TESTS=ON ..
- name: Build
run: cmake --build build --target install
- name: Upload test binaries
uses: actions/upload-artifact@v2
with:
name: MacOS latest - Clang, Static, SDL2
path: |
build/src/*
build/test/*
build-windows-sdl2:

name: Windows latest - MSVC, Static, SDL2
runs-on: windows-latest

steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install pkgconfiglite
run: choco install pkgconfiglite
- name: Install Dependencies
run: vcpkg integrate install && vcpkg install sdl2 sdl2-ttf --triplet=x64-windows-release
- name: Configure
run: mkdir build && mkdir install && cd build && cmake -DCMAKE_INSTALL_PREFIX="../install" -DCMAKE_TOOLCHAIN_FILE="C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET="x64-windows-release" -DNFD_BUILD_TESTS=OFF -DNFD_BUILD_SDL2_TESTS=ON ..
- name: Build
run: cmake --build build --target install --config Release
- name: Upload test binaries
uses: actions/upload-artifact@v2
with:
name: Windows latest - MSVC, Static, SDL2
path: |
build/src/Release/*
build/test/Release/*
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ endif ()

option(BUILD_SHARED_LIBS "Build a shared library instead of static" OFF)
option(NFD_BUILD_TESTS "Build tests for nfd" ${nfd_ROOT_PROJECT})
option(NFD_BUILD_SDL2_TESTS "Build SDL2 tests for nfd" OFF)
option(NFD_INSTALL "Generate install target for nfd" ${nfd_ROOT_PROJECT})

set(nfd_PLATFORM Undefined)
Expand Down Expand Up @@ -48,6 +49,6 @@ endif()

add_subdirectory(src)

if(${NFD_BUILD_TESTS})
if(${NFD_BUILD_TESTS} OR ${NFD_BUILD_SDL2_TESTS})
add_subdirectory(test)
endif()
24 changes: 24 additions & 0 deletions src/include/nfd.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,19 +93,39 @@ typedef struct {
typedef nfdu8filteritem_t nfdnfilteritem_t;
#endif // _WIN32

// The native window handle type.
enum {
NFD_WINDOW_HANDLE_TYPE_UNSET = 0,
// Windows: handle is HWND (the Windows API typedefs this to void*)
NFD_WINDOW_HANDLE_TYPE_WINDOWS = 1,
// Cocoa: handle is NSWindow*
NFD_WINDOW_HANDLE_TYPE_COCOA = 2,
// X11: handle is Window
NFD_WINDOW_HANDLE_TYPE_X11 = 3,
// Wayland support will be implemented separately in the future
};
// The native window handle. If using a platform abstraction framework (e.g. SDL), this should be
// obtained using the corresponding NFD glue header (e.g. nfd_sdl.h).
typedef struct {
size_t type; // this is one of the values of the enum above
void* handle;
} nfdwindowhandle_t;

typedef size_t nfdversion_t;

typedef struct {
const nfdu8filteritem_t* filterList;
nfdfiltersize_t filterCount;
const nfdu8char_t* defaultPath;
nfdwindowhandle_t parentWindow;
} nfdopendialogu8args_t;

#ifdef _WIN32
typedef struct {
const nfdnfilteritem_t* filterList;
nfdfiltersize_t filterCount;
const nfdnchar_t* defaultPath;
nfdwindowhandle_t parentWindow;
} nfdopendialognargs_t;
#else
typedef nfdopendialogu8args_t nfdopendialognargs_t;
Expand All @@ -116,6 +136,7 @@ typedef struct {
nfdfiltersize_t filterCount;
const nfdu8char_t* defaultPath;
const nfdu8char_t* defaultName;
nfdwindowhandle_t parentWindow;
} nfdsavedialogu8args_t;

#ifdef _WIN32
Expand All @@ -124,18 +145,21 @@ typedef struct {
nfdfiltersize_t filterCount;
const nfdnchar_t* defaultPath;
const nfdnchar_t* defaultName;
nfdwindowhandle_t parentWindow;
} nfdsavedialognargs_t;
#else
typedef nfdsavedialogu8args_t nfdsavedialognargs_t;
#endif // _WIN32

typedef struct {
const nfdu8char_t* defaultPath;
nfdwindowhandle_t parentWindow;
} nfdpickfolderu8args_t;

#ifdef _WIN32
typedef struct {
const nfdnchar_t* defaultPath;
nfdwindowhandle_t parentWindow;
} nfdpickfoldernargs_t;
#else
typedef nfdpickfolderu8args_t nfdpickfoldernargs_t;
Expand Down
72 changes: 72 additions & 0 deletions src/include/nfd_glfw.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
Native File Dialog Extended
Repository: https://github.com/btzy/nativefiledialog-extended
License: Zlib
Authors: Bernard Teo
This header contains a function to convert a GLFW window handle to a native window handle for
passing to NFDe.
*/

#ifndef _NFD_GLFW_H
#define _NFD_GLFW_H

#include <GLFW/glfw3native.h>
#include <nfd.h>
#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

/**
* Converts a GLFW window handle to a native window handle that can be passed to NFDe.
* @param sdlWindow The GLFW window handle.
* @param[out] nativeWindow The output native window handle, populated if and only if this function
* returns true.
* @return Either true to indicate success, or false to indicate failure. It is intended that
* users ignore the error and simply pass a value-initialized nfdwindowhandle_t to NFDe if this
* function fails. */
inline bool NFD_GetNativeWindowFromGLFWWindow(GLFWwindow* glfwWindow,
nfdwindowhandle_t* nativeWindow) {
GLFWerrorfun* oldCallback = glfwSetErrorCallback(NULL);
bool success = false;
#if defined(GLFW_EXPOSE_NATIVE_WIN32)
if (!success) {
const HWND hwnd = glfwGetWin32Window(glfwWindow);
if (hwnd) {
nativeWindow->type = NFD_WINDOW_HANDLE_TYPE_WINDOWS;
nativeWindow->handle = (void*)hwnd;
success = true;
}
}
#endif
#if defined(GLFW_EXPOSE_NATIVE_COCOA)
if (!success) {
NSWindow* const cocoa_window = glfwGetCocoaWindow(glfwWindow);
if (cocoa_window) {
nativeWindow->type = NFD_WINDOW_HANDLE_TYPE_COCOA;
nativeWindow->handle = (void*)cocoa_window;
success = true;
}
}
#endif
#if defined(NFD_WINDOW_HANDLE_TYPE_X11)
if (!success) {
const Window x11_window = glfwGetX11Window(glfwWindow);
if (x11_window != None) {
nativeWindow->type = NFD_WINDOW_HANDLE_TYPE_X11;
nativeWindow->handle = (void*)x11_window;
success = true;
}
}
#endif
glfwSetErrorCallback(oldCallback);
return success;
}

#ifdef __cplusplus
}
#endif // __cplusplus

#endif // _NFD_GLFW_H
70 changes: 70 additions & 0 deletions src/include/nfd_sdl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
Native File Dialog Extended
Repository: https://github.com/btzy/nativefiledialog-extended
License: Zlib
Authors: Bernard Teo
This header contains a function to convert an SDL window handle to a native window handle for
passing to NFDe.
This is meant to be used with SDL2, but if there are incompatibilities with future SDL versions,
we can conditionally compile based on SDL_MAJOR_VERSION.
*/

#ifndef _NFD_SDL_H
#define _NFD_SDL_H

#include <SDL_error.h>
#include <SDL_syswm.h>
#include <nfd.h>
#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

/**
* Converts an SDL window handle to a native window handle that can be passed to NFDe.
* @param sdlWindow The SDL window handle.
* @param[out] nativeWindow The output native window handle, populated if and only if this function
* returns true.
* @return Either true to indicate success, or false to indicate failure. If false is returned,
* you can call SDL_GetError() for more information. However, it is intended that users ignore the
* error and simply pass a value-initialized nfdwindowhandle_t to NFDe if this function fails. */
inline bool NFD_GetNativeWindowFromSDLWindow(SDL_Window* sdlWindow,
nfdwindowhandle_t* nativeWindow) {
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (SDL_GetWindowWMInfo(sdlWindow, &info)) {
return false;
}
switch (info.subsystem) {
#if defined(SDL_VIDEO_DRIVER_WINDOWS)
case SDL_SYSWM_WINDOWS:
nativeWindow->type = NFD_WINDOW_HANDLE_TYPE_WINDOWS;
nativeWindow->handle = (void*)info.info.win.window;
return true;
#endif
#if defined(SDL_VIDEO_DRIVER_COCOA)
case SDL_SYSWM_COCOA:
nativeWindow->type = NFD_WINDOW_HANDLE_TYPE_COCOA;
nativeWindow->handle = (void*)info.info.cocoa.window;
return true;
#endif
#if defined(SDL_VIDEO_DRIVER_X11)
case SDL_SYSWM_X11:
nativeWindow->type = NFD_WINDOW_HANDLE_TYPE_X11;
nativeWindow->handle = (void*)info.info.x11.window;
return true;
#endif
default:
SDL_SetError("Unsupported native window type.");
return false;
}
}

#ifdef __cplusplus
}
#endif // __cplusplus

#endif // _NFD_SDL_H
Loading

0 comments on commit 47b449c

Please sign in to comment.