Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
file_dialog.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <string>
4#include <vector>
5#include <optional>
6#include <functional>
7
8namespace pulp::platform {
9
10// File filter (e.g., {"Audio Files", "wav;flac;mp3"})
11struct FileFilter {
12 std::string description;
13 std::string extensions; // Semicolon-separated: "wav;flac;mp3"
14};
15
16// Native file open/save dialogs.
17//
18// Platform notes:
19// - macOS: built-in NSOpenPanel/NSSavePanel dialogs.
20// - Windows: opt-in built-in IFileDialog backend via
21// FileDialog::install_native_backend().
22// - Linux: opt-in built-in xdg-desktop-portal FileChooser backend via
23// FileDialog::install_native_backend() when libdbus is loadable.
24// - iOS/Android: no built-in backend yet; hosts can register one via
25// FileDialog::set_backend().
26// Without a native or host-registered backend, calls return std::nullopt / {}
27// as explicit "no backend" rather than silent success.
29public:
30 // Show an open file dialog. Returns selected path or nullopt.
31 static std::optional<std::string> open_file(
32 const std::string& title = "Open",
33 const std::vector<FileFilter>& filters = {},
34 const std::string& default_path = "");
35
36 // Show an open file dialog allowing multiple selection.
37 static std::vector<std::string> open_files(
38 const std::string& title = "Open",
39 const std::vector<FileFilter>& filters = {},
40 const std::string& default_path = "");
41
42 // Show a save file dialog. Returns selected path or nullopt.
43 static std::optional<std::string> save_file(
44 const std::string& title = "Save",
45 const std::vector<FileFilter>& filters = {},
46 const std::string& default_path = "",
47 const std::string& default_name = "");
48
49 // Copy an existing file to a destination chosen by a native save dialog.
50 // The copy occurs while the platform still owns the selection grant. This
51 // distinction is load-bearing in sandboxed plugin hosts on macOS: returning
52 // a pathname from NSSavePanel and opening it later loses the granted
53 // capability and fails with EPERM. Returns the chosen destination on
54 // success, nullopt on cancellation or failure. Native backends populate
55 // `error` when their platform API distinguishes failure from cancellation;
56 // an empty error means cancellation or an undifferentiated backend refusal.
57 static std::optional<std::string> save_copy(
58 const std::string& source_path,
59 const std::string& title = "Save",
60 const std::vector<FileFilter>& filters = {},
61 const std::string& default_path = "",
62 const std::string& default_name = "",
63 std::string* error = nullptr);
64
65 // Show a folder selection dialog.
66 static std::optional<std::string> choose_folder(
67 const std::string& title = "Choose Folder",
68 const std::string& default_path = "");
69
70 // Show a folder selection dialog that allows several folders. Returns the
71 // chosen folders in the platform's order, or an empty vector on cancel or
72 // when no backend is available (has_backend() tells the two apart). A
73 // backend that only registered a single-folder handler yields at most one
74 // folder rather than failing; see choose_folders_with().
75 static std::vector<std::string> choose_folders(const std::string& title = "Choose Folders",
76 const std::string& default_path = "");
77
78 // ── Host-registered backend ────────────────────────────────────────
79 //
80 // On platforms without an auto-available native dialog (Windows,
81 // Linux, iOS, Android) the host app can install a backend that
82 // implements real native dialogs. Windows and Linux ship opt-in
83 // built-in backends through install_native_backend(); iOS and
84 // Android remain host-provided for now. Without a backend installed,
85 // each call returns no-selection and the JS bridge can probe
86 // `has_backend()` to distinguish "user cancelled" from
87 // "platform unsupported".
88
89 struct Backend {
90 std::function<std::optional<std::string>(
91 const std::string& title,
92 const std::vector<FileFilter>& filters,
93 const std::string& default_path)> open_file;
94 std::function<std::vector<std::string>(
95 const std::string& title,
96 const std::vector<FileFilter>& filters,
97 const std::string& default_path)> open_files;
98 std::function<std::optional<std::string>(
99 const std::string& title,
100 const std::vector<FileFilter>& filters,
101 const std::string& default_path,
102 const std::string& default_name)> save_file;
103 std::function<std::optional<std::string>(
104 const std::string& title,
105 const std::string& default_path)> choose_folder;
106 // Optional. When unset, choose_folders() falls back to choose_folder()
107 // and returns at most one folder.
108 std::function<std::vector<std::string>(const std::string& title,
109 const std::string& default_path)>
111 };
112
113 // The routing rule behind choose_folders(), exposed so it can be checked
114 // without raising a dialog: the backend's multi-folder handler when it has
115 // one, else its single-folder handler wrapped into a vector, else empty.
116 static std::vector<std::string> choose_folders_with(const Backend& backend,
117 const std::string& title,
118 const std::string& default_path);
119
120 using SaveCopyBackend = std::function<std::optional<std::string>(
121 const std::string& source_path,
122 const std::string& title,
123 const std::vector<FileFilter>& filters,
124 const std::string& default_path,
125 const std::string& default_name,
126 std::string* error)>;
127
128 static void set_backend(Backend backend);
131 static void clear_backend();
132 static bool has_backend();
133
134 // Install the platform's built-in backend, if one exists and no backend
135 // is already installed. Hosts call this once at startup to opt into
136 // native dialogs on platforms that ship a built-in bridge:
137 // - Linux: the xdg-desktop-portal FileChooser bridge — returns true
138 // when libdbus is loadable (the portal service is probed lazily, per
139 // call), false otherwise.
140 // - macOS: a native impl is already compiled in; returns has_backend().
141 // - Windows: Vista+ IFileDialog backend; returns true after installing.
142 // - iOS / Android: no built-in backend yet; returns has_backend()
143 // (false unless a host registered one).
144 // Idempotent: an already-installed (incl. host-set) backend is left in
145 // place. We deliberately do NOT auto-install — raising a portal dialog
146 // blocks, so the default "no backend → no selection" contract must hold
147 // for headless/test callers until a host opts in.
149};
150
151} // namespace pulp::platform
Definition file_dialog.hpp:28
static std::vector< std::string > open_files(const std::string &title="Open", const std::vector< FileFilter > &filters={}, const std::string &default_path="")
static std::optional< std::string > open_file(const std::string &title="Open", const std::vector< FileFilter > &filters={}, const std::string &default_path="")
static void set_save_copy_backend(SaveCopyBackend backend)
std::function< std::optional< std::string >( const std::string &source_path, const std::string &title, const std::vector< FileFilter > &filters, const std::string &default_path, const std::string &default_name, std::string *error)> SaveCopyBackend
Definition file_dialog.hpp:120
static std::optional< std::string > save_file(const std::string &title="Save", const std::vector< FileFilter > &filters={}, const std::string &default_path="", const std::string &default_name="")
static std::optional< std::string > save_copy(const std::string &source_path, const std::string &title="Save", const std::vector< FileFilter > &filters={}, const std::string &default_path="", const std::string &default_name="", std::string *error=nullptr)
static void set_backend(Backend backend)
static std::vector< std::string > choose_folders(const std::string &title="Choose Folders", const std::string &default_path="")
static std::optional< std::string > choose_folder(const std::string &title="Choose Folder", const std::string &default_path="")
static bool has_save_copy_backend()
static bool install_native_backend()
static std::vector< std::string > choose_folders_with(const Backend &backend, const std::string &title, const std::string &default_path)
Device capability vocabulary: one tier ladder, one thermal ladder, and the projection from observed i...
Definition child_process.hpp:15
Definition file_dialog.hpp:89
std::function< std::optional< std::string >(const std::string &title, const std::string &default_path)> choose_folder
Definition file_dialog.hpp:105
std::function< std::optional< std::string >(const std::string &title, const std::vector< FileFilter > &filters, const std::string &default_path, const std::string &default_name)> save_file
Definition file_dialog.hpp:102
std::function< std::optional< std::string >(const std::string &title, const std::vector< FileFilter > &filters, const std::string &default_path)> open_file
Definition file_dialog.hpp:93
std::function< std::vector< std::string >(const std::string &title, const std::vector< FileFilter > &filters, const std::string &default_path)> open_files
Definition file_dialog.hpp:97
std::function< std::vector< std::string >(const std::string &title, const std::string &default_path)> choose_folders
Definition file_dialog.hpp:110
Definition file_dialog.hpp:11
std::string description
Definition file_dialog.hpp:12
std::string extensions
Definition file_dialog.hpp:13