├── .gitignore ├── .gitmodules ├── Cargo.toml ├── LICENSE.md ├── README.md ├── appveyor.yml ├── build.rs ├── examples ├── builder_open_jpg.rs ├── example.rs ├── open_multiple.rs └── pick_folder.rs ├── screenshots └── cocoa_el_capitan.png └── src ├── error.rs ├── ffi.rs └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "nativefiledialog"] 2 | path = nativefiledialog 3 | url = https://github.com/mlabbe/nativefiledialog 4 | branch = master 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nfd" 3 | version = "0.0.4" 4 | authors = [ 5 | "Saurav Sachidanand ", 6 | "Daniel Collin ", 7 | "Denis Kolodin ", 8 | ] 9 | 10 | license = "MIT" 11 | repository = "https://www.github.com/saurvs/nfd-rs" 12 | 13 | description = "Rust bindings to nativefiledialog" 14 | keywords = ["file", "dialog", "ui"] 15 | 16 | build = "build.rs" 17 | 18 | [build-dependencies] 19 | gcc = "0.3" 20 | 21 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Saurav Sachidanand 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nfd-rs [![](http://meritbadge.herokuapp.com/nfd)](https://crates.io/crates/nfd) [![](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/saurvs/nfd-rs/blob/master/LICENSE.md) 2 | 3 | `nfd-rs` is a Rust binding to the library [nativefiledialog](https://github.com/mlabbe/nativefiledialog), that provides a convenient cross-platform interface to opening file dialogs on Linux, OS X and Windows. 4 | 5 | This crate has been tested on Mac, Window and Linux (Ubuntu 14.04) and supports single/mutliple and save dialogs, notice APIs may break with newer versions. 6 | 7 | ## Usage 8 | 9 | * Add the dependency `nfd` in your ```Cargo.toml``` 10 | ```toml 11 | [dependencies] 12 | nfd = "0.0.4" 13 | ``` 14 | 15 | * Open a single file dialog 16 | ```rust 17 | extern crate nfd; 18 | 19 | use nfd::Response; 20 | 21 | fn main() { 22 | 23 | let result = nfd::open_file_dialog(None, None).unwrap_or_else(|e| { 24 | panic!(e); 25 | }); 26 | 27 | match result { 28 | Response::Okay(file_path) => println!("File path = {:?}", file_path), 29 | Response::OkayMultiple(files) => println!("Files {:?}", files), 30 | Response::Cancel => println!("User canceled"), 31 | } 32 | } 33 | ``` 34 | 35 | * Open a multi file dialog using builder with jpg files as filter 36 | ```rust 37 | extern crate nfd; 38 | 39 | use nfd::Response; 40 | 41 | fn main() { 42 | 43 | let result = nfd::dialog_multiple().filter("jpg").open().unwrap_or_else(|e| { 44 | panic!(e); 45 | }); 46 | 47 | match result { 48 | Response::Okay(file_path) => println!("File path = {:?}", file_path), 49 | Response::OkayMultiple(files) => println!("Files {:?}", files), 50 | Response::Cancel => println!("User canceled"), 51 | } 52 | } 53 | ``` 54 | 55 | ## Screenshot 56 | 57 | ![Cocoa on El Capitan](screenshots/cocoa_el_capitan.png?raw=true) 58 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - TARGET: x86_64-pc-windows-gnu 4 | MSYS_BITS: 64 5 | - TARGET: i686-pc-windows-gnu 6 | MSYS_BITS: 32 7 | - TARGET: x86_64-pc-windows-msvc 8 | - TARGET: i686-pc-windows-msvc 9 | 10 | install: 11 | - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:TARGET}.exe" 12 | - rust-nightly-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust" 13 | - set PATH=%PATH%;C:\Program Files (x86)\Rust\bin 14 | - if defined MSYS_BITS set PATH=C:\msys64\mingw%MSYS_BITS%\bin;C:\msys64\usr\bin;%PATH% 15 | - set CARGO_TARGET_DIR=%APPVEYOR_BUILD_FOLDER%\target 16 | - rustc -V 17 | - cargo -V 18 | 19 | build: false 20 | 21 | test_script: 22 | - git submodule update --init --recursive 23 | - cargo build --verbose 24 | - cargo test --verbose 25 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 Saurav Sachidanand 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | extern crate gcc; 24 | use std::env; 25 | use std::process::Command; 26 | 27 | macro_rules! nfd { 28 | ($suf:expr) => { 29 | concat!("nativefiledialog/src/", $suf); 30 | }; 31 | } 32 | 33 | fn main() { 34 | let mut cfg = gcc::Config::new(); 35 | let env = env::var("TARGET").unwrap(); 36 | 37 | cfg.include(nfd!("include")); 38 | cfg.file(nfd!("nfd_common.c")); 39 | 40 | if env.contains("darwin") { 41 | cfg.file(nfd!("nfd_cocoa.m")); 42 | cfg.compile("libnfd.a"); 43 | println!("cargo:rustc-link-lib=framework=AppKit"); 44 | } else if env.contains("windows") { 45 | cfg.cpp(true); 46 | cfg.file(nfd!("nfd_win.cpp")); 47 | cfg.compile("libnfd.a"); 48 | println!("cargo:rustc-link-lib=ole32"); 49 | println!("cargo:rustc-link-lib=shell32"); 50 | // MinGW doesn't link it by default 51 | println!("cargo:rustc-link-lib=uuid"); 52 | } else { 53 | let pkg_output = Command::new("pkg-config") 54 | .arg("--cflags") 55 | .arg("gtk+-3.0") 56 | .arg("glib-2.0") 57 | .arg("--libs") 58 | .arg("glib-2.0") 59 | .output(); 60 | match pkg_output { 61 | Ok(output) => { 62 | let t = String::from_utf8(output.stdout).unwrap(); 63 | let flags = t.split(" "); 64 | for flag in flags { 65 | if flag != "\n" && flag != "" { 66 | cfg.flag(flag); 67 | } 68 | } 69 | } 70 | _ => (), 71 | } 72 | cfg.file(nfd!("nfd_gtk.c")); 73 | cfg.compile("libnfd.a"); 74 | println!("cargo:rustc-link-lib=gdk-3"); 75 | println!("cargo:rustc-link-lib=gtk-3"); 76 | println!("cargo:rustc-link-lib=glib-2.0"); 77 | println!("cargo:rustc-link-lib=gobject-2.0"); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /examples/builder_open_jpg.rs: -------------------------------------------------------------------------------- 1 | extern crate nfd; 2 | 3 | use nfd::Response; 4 | 5 | fn main() { 6 | let result = nfd::dialog().filter("jpg").open().unwrap_or_else(|e| { 7 | panic!(e); 8 | }); 9 | 10 | match result { 11 | Response::Okay(file_path) => println!("File path = {:?}", file_path), 12 | Response::Cancel => println!("User canceled"), 13 | _ => (), 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/example.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 Saurav Sachidanand 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | extern crate nfd; 24 | 25 | use nfd::Response; 26 | 27 | fn main() { 28 | let result = nfd::open_file_dialog(None, None).unwrap_or_else(|e| { 29 | panic!(e); 30 | }); 31 | 32 | match result { 33 | Response::Okay(file_path) => println!("File path = {:?}", file_path), 34 | Response::Cancel => println!("User canceled"), 35 | _ => (), 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/open_multiple.rs: -------------------------------------------------------------------------------- 1 | extern crate nfd; 2 | 3 | use nfd::Response; 4 | 5 | fn main() { 6 | let result = nfd::dialog_multiple().open().unwrap_or_else(|e| { 7 | panic!(e); 8 | }); 9 | 10 | match result { 11 | Response::OkayMultiple(files) => println!("File path = {:?}", files), 12 | Response::Cancel => println!("User canceled"), 13 | _ => (), 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/pick_folder.rs: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Copyright (c) 2016 Saurav Sachidanand 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | extern crate nfd; 25 | 26 | use nfd::Response; 27 | 28 | fn main() { 29 | let result = nfd::open_pick_folder(None).unwrap_or_else(|e| { 30 | panic!(e); 31 | }); 32 | 33 | match result { 34 | Response::Okay(file_path) => println!("File path = {:?}", file_path), 35 | Response::Cancel => println!("User canceled"), 36 | _ => (), 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /screenshots/cocoa_el_capitan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saurvs/nfd-rs/07578c5fea4d7b1f35fbf128f04b6264c3bf3c5a/screenshots/cocoa_el_capitan.png -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::ffi; 2 | use std::fmt; 3 | use std::str; 4 | use std::error::Error; 5 | 6 | #[derive(Debug)] 7 | pub enum NFDError { 8 | NulError(ffi::NulError), 9 | Utf8Error(str::Utf8Error), 10 | Error(String), 11 | } 12 | 13 | impl fmt::Display for NFDError { 14 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 15 | match *self { 16 | NFDError::NulError(ref err) => err.fmt(f), 17 | NFDError::Error(ref err) => err.fmt(f), 18 | NFDError::Utf8Error(ref err) => err.fmt(f), 19 | } 20 | } 21 | } 22 | 23 | impl Error for NFDError { 24 | fn description(&self) -> &str { 25 | match *self { 26 | NFDError::NulError(ref err) => err.description(), 27 | NFDError::Error(ref err) => err, 28 | NFDError::Utf8Error(ref err) => err.description(), 29 | } 30 | } 31 | } 32 | 33 | impl From for NFDError { 34 | fn from(err: ffi::NulError) -> NFDError { 35 | NFDError::NulError(err) 36 | } 37 | } 38 | 39 | impl From for NFDError { 40 | fn from(err: str::Utf8Error) -> NFDError { 41 | NFDError::Utf8Error(err) 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/ffi.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 Saurav Sachidanand 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* automatically generated by rust-bindgen */ 24 | 25 | #![allow(dead_code, 26 | non_camel_case_types, 27 | non_upper_case_globals, 28 | non_snake_case)] 29 | use std::os::raw; 30 | pub type ptrdiff_t = isize; 31 | pub type size_t = usize; 32 | pub type nfdchar_t = raw::c_char; 33 | #[repr(C)] 34 | #[derive(Copy, Clone)] 35 | #[derive(Debug)] 36 | pub struct nfdpathset_t { 37 | pub buf: *mut nfdchar_t, 38 | pub indices: *mut size_t, 39 | pub count: size_t, 40 | } 41 | impl ::std::default::Default for nfdpathset_t { 42 | fn default() -> Self { unsafe { ::std::mem::zeroed() } } 43 | } 44 | #[derive(Copy, Clone)] 45 | #[repr(u32)] 46 | #[derive(Debug)] 47 | pub enum nfdresult_t { 48 | NFD_ERROR = 0, 49 | NFD_OKAY = 1, 50 | NFD_CANCEL = 2, 51 | } 52 | extern "C" { 53 | pub fn NFD_OpenDialog(filterList: *const nfdchar_t, defaultPath: *const nfdchar_t, outPath: *mut *mut nfdchar_t) -> nfdresult_t; 54 | pub fn NFD_OpenDialogMultiple(filterList: *const nfdchar_t, defaultPath: *const nfdchar_t, outPaths: *mut nfdpathset_t) -> nfdresult_t; 55 | pub fn NFD_SaveDialog(filterList: *const nfdchar_t, defaultPath: *const nfdchar_t, outPath: *mut *mut nfdchar_t) -> nfdresult_t; 56 | pub fn NFD_PickFolder(defaultPath: *const nfdchar_t, outPath: *mut *mut nfdchar_t) -> nfdresult_t; 57 | pub fn NFD_GetError() -> *const raw::c_char; 58 | pub fn NFD_PathSet_GetCount(pathSet: *const nfdpathset_t) -> size_t; 59 | pub fn NFD_PathSet_GetPath(pathSet: *const nfdpathset_t, index: size_t) -> *mut nfdchar_t; 60 | pub fn NFD_PathSet_Free(pathSet: *mut nfdpathset_t); 61 | } 62 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 Saurav Sachidanand 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | mod ffi; 24 | mod error; 25 | 26 | use ffi::*; 27 | use std::ffi::{CStr, CString}; 28 | use std::os::raw::c_char; 29 | use error::NFDError; 30 | 31 | /// Result of opening a file dialog 32 | pub enum Response { 33 | /// User pressed okay. `String` is the file path selected 34 | Okay(String), 35 | /// User pressed okay on mupliple selections. Result contains a Vec of all the files 36 | OkayMultiple(Vec), 37 | /// User pressed cancel 38 | Cancel, 39 | } 40 | 41 | #[derive(Copy, Clone, PartialEq)] 42 | pub enum DialogType { 43 | SingleFile, 44 | MultipleFiles, 45 | SaveFile, 46 | PickFolder, 47 | } 48 | 49 | pub struct DialogBuilder<'a> { 50 | filter: Option<&'a str>, 51 | default_path: Option<&'a str>, 52 | dialog_type: DialogType, 53 | } 54 | 55 | impl<'a> DialogBuilder<'a> { 56 | 57 | pub fn new(dialog_type: DialogType) -> Self { 58 | DialogBuilder { 59 | filter: None, 60 | default_path: None, 61 | dialog_type: dialog_type, 62 | } 63 | } 64 | 65 | pub fn filter(&'a mut self, filter: &'a str) -> &mut DialogBuilder { 66 | self.filter = Some(filter); 67 | self 68 | } 69 | 70 | pub fn default_path(&'a mut self, path: &'a str) -> &mut DialogBuilder { 71 | self.default_path = Some(path); 72 | self 73 | } 74 | 75 | pub fn open(&self) -> Result { 76 | open_dialog(self.filter, self.default_path, self.dialog_type.clone()) 77 | } 78 | } 79 | 80 | pub fn dialog<'a>() -> DialogBuilder<'a> { 81 | DialogBuilder::new(DialogType::SingleFile) 82 | } 83 | 84 | pub fn dialog_multiple<'a>() -> DialogBuilder<'a> { 85 | DialogBuilder::new(DialogType::MultipleFiles) 86 | } 87 | 88 | pub fn dialog_save<'a>() -> DialogBuilder<'a> { 89 | DialogBuilder::new(DialogType::SaveFile) 90 | } 91 | 92 | pub type Result = std::result::Result; 93 | 94 | /// Open single file dialog 95 | pub fn open_file_dialog(filter_list: Option<&str>, default_path: Option<&str>) -> Result { 96 | open_dialog(filter_list, default_path, DialogType::SingleFile) 97 | } 98 | 99 | /// Open mulitple file dialog 100 | pub fn open_file_multiple_dialog(filter_list: Option<&str>, default_path: Option<&str>) -> Result { 101 | open_dialog(filter_list, default_path, DialogType::MultipleFiles) 102 | } 103 | 104 | /// Open save dialog 105 | pub fn open_save_dialog(filter_list: Option<&str>, default_path: Option<&str>) -> Result { 106 | open_dialog(filter_list, default_path, DialogType::SaveFile) 107 | } 108 | 109 | /// Open save dialog 110 | pub fn open_pick_folder(default_path: Option<&str>) -> Result { 111 | open_dialog(None, default_path, DialogType::PickFolder) 112 | } 113 | 114 | pub fn open_dialog(filter_list: Option<&str>, default_path: Option<&str>, dialog_type: DialogType) -> Result { 115 | let result; 116 | let filter_list_cstring; 117 | let default_path_cstring; 118 | 119 | let filter_list_ptr = match filter_list { 120 | Some(fl_str) => { 121 | filter_list_cstring = try!(CString::new(fl_str)); 122 | filter_list_cstring.as_ptr() 123 | } 124 | None => std::ptr::null() 125 | }; 126 | 127 | let default_path_ptr = match default_path { 128 | Some(dp_str) => { 129 | default_path_cstring = try!(CString::new(dp_str)); 130 | default_path_cstring.as_ptr() 131 | } 132 | None => std::ptr::null() 133 | }; 134 | 135 | let mut out_path: *mut c_char = std::ptr::null_mut(); 136 | let ptr_out_path = &mut out_path as *mut *mut c_char; 137 | 138 | let mut out_multiple = nfdpathset_t::default(); 139 | let ptr_out_multiple = &mut out_multiple as *mut nfdpathset_t; 140 | 141 | unsafe { 142 | result = match dialog_type { 143 | DialogType::SingleFile => { 144 | NFD_OpenDialog(filter_list_ptr, default_path_ptr, ptr_out_path) 145 | }, 146 | 147 | DialogType::MultipleFiles => { 148 | NFD_OpenDialogMultiple(filter_list_ptr, default_path_ptr, ptr_out_multiple) 149 | }, 150 | 151 | DialogType::SaveFile => { 152 | NFD_SaveDialog(filter_list_ptr, default_path_ptr, ptr_out_path) 153 | }, 154 | 155 | DialogType::PickFolder => { 156 | NFD_PickFolder(default_path_ptr, ptr_out_path) 157 | }, 158 | }; 159 | 160 | match result { 161 | nfdresult_t::NFD_OKAY =>{ 162 | if dialog_type == DialogType::MultipleFiles { 163 | let count = NFD_PathSet_GetCount(&out_multiple); 164 | let mut res = Vec::with_capacity(count); 165 | for i in 0..count { 166 | let path = CStr::from_ptr(NFD_PathSet_GetPath(&out_multiple, i)).to_string_lossy().into_owned(); 167 | res.push(path) 168 | 169 | } 170 | 171 | NFD_PathSet_Free(ptr_out_multiple); 172 | 173 | Ok(Response::OkayMultiple(res)) 174 | } else { 175 | Ok(Response::Okay(CStr::from_ptr(out_path).to_string_lossy().into_owned())) 176 | } 177 | }, 178 | 179 | nfdresult_t::NFD_CANCEL => Ok(Response::Cancel), 180 | nfdresult_t::NFD_ERROR => Err(NFDError::Error(CStr::from_ptr(NFD_GetError()).to_string_lossy().into_owned())), 181 | } 182 | } 183 | } 184 | --------------------------------------------------------------------------------