fltk/
dialog.rs

1use crate::enums::{Color, Font};
2use crate::prelude::*;
3use crate::utils::{FlString, images_registered, register_images};
4use fltk_sys::dialog::*;
5use std::{
6    ffi::{CStr, CString},
7    mem,
8    os::raw,
9    path::{Path, PathBuf},
10};
11
12/// Color modes to be used with the color chooser
13#[repr(u8)]
14#[derive(Debug, Copy, Clone, PartialEq, Eq)]
15pub enum ColorMode {
16    /// Rgb color mode
17    Rgb = 0,
18    /// Byte color mode
19    Byte = 1,
20    /// Hex color mode
21    Hex = 2,
22    /// Hsv color mode
23    Hsv = 3,
24}
25
26/// FLTK's `NativeFileChooser`
27#[derive(Debug)]
28pub struct NativeFileChooser {
29    inner: *mut Fl_Native_File_Chooser,
30}
31
32/// Defines the type of dialog, which can be changed dynamically using the `set_type()` method
33#[repr(i32)]
34#[derive(Debug, Copy, Clone, PartialEq, Eq)]
35pub enum NativeFileChooserType {
36    /// Browse file
37    BrowseFile = 0,
38    /// Browse dir
39    BrowseDir,
40    /// Browse multiple files
41    BrowseMultiFile,
42    /// Browse multiple dirs
43    BrowseMultiDir,
44    /// Browse save file
45    BrowseSaveFile,
46    /// Browse save directory
47    BrowseSaveDir,
48}
49
50crate::macros::widget::impl_widget_type!(NativeFileChooserType);
51
52bitflags::bitflags! {
53    /// Defines the File dialog options, which can be set using the `set_option()` method.
54    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
55    pub struct NativeFileChooserOptions: i32 {
56        /// No options
57        const NoOptions = 0;
58        /// Confirm on save as
59        const SaveAsConfirm = 1;
60        /// New folder option
61        const NewFolder = 2;
62        /// Enable preview
63        const Preview = 4;
64        /// Use extension filter
65        const UseFilterExt = 8;
66    }
67}
68
69/// Rusult of `try_show`
70#[derive(Debug, Copy, Clone)]
71pub enum NativeFileChooserAction {
72    /// Cancelled operation
73    Cancelled,
74    /// User chose a file/dir
75    Success,
76}
77
78impl NativeFileChooser {
79    /// Creates an new file dialog
80    pub fn new(op: NativeFileChooserType) -> NativeFileChooser {
81        unsafe {
82            let file_dialog = Fl_Native_File_Chooser_new(mem::transmute(op));
83            assert!(!file_dialog.is_null());
84            NativeFileChooser { inner: file_dialog }
85        }
86    }
87
88    /// Returns the chosen file name
89    pub fn filename(&self) -> PathBuf {
90        assert!(!self.inner.is_null());
91        unsafe {
92            let cnt = Fl_Native_File_Chooser_count(self.inner);
93            if cnt == 0 {
94                return PathBuf::new();
95            }
96            let x = Fl_Native_File_Chooser_filenames(self.inner, 0);
97            PathBuf::from(
98                CStr::from_ptr(x as *mut raw::c_char)
99                    .to_string_lossy()
100                    .to_string(),
101            )
102        }
103    }
104
105    /// Returns the chosen file names
106    pub fn filenames(&self) -> Vec<PathBuf> {
107        assert!(!self.inner.is_null());
108        unsafe {
109            let cnt = Fl_Native_File_Chooser_count(self.inner);
110            let mut names: Vec<PathBuf> = vec![];
111            for i in 0..cnt {
112                let x = Fl_Native_File_Chooser_filenames(self.inner, i);
113                names.push(PathBuf::from(
114                    CStr::from_ptr(x as *mut raw::c_char)
115                        .to_string_lossy()
116                        .to_string(),
117                ));
118            }
119            names
120        }
121    }
122
123    /// Returns the preset directory
124    pub fn directory(&self) -> Option<PathBuf> {
125        assert!(!self.inner.is_null());
126        unsafe {
127            let x = Fl_Native_File_Chooser_directory(self.inner);
128            if x.is_null() {
129                None
130            } else {
131                Some(PathBuf::from(
132                    CStr::from_ptr(x as *mut raw::c_char)
133                        .to_string_lossy()
134                        .to_string(),
135                ))
136            }
137        }
138    }
139
140    /// Sets the starting directory
141    /// # Errors
142    /// Errors on non-existent path
143    pub fn set_directory<P: AsRef<Path>>(&mut self, dir: &P) -> Result<(), FltkError> {
144        assert!(!self.inner.is_null());
145        self.set_directory_(dir.as_ref())
146    }
147
148    fn set_directory_(&mut self, dir: &Path) -> Result<(), FltkError> {
149        assert!(!self.inner.is_null());
150        let dir = CString::new(dir.to_str().ok_or_else(|| {
151            FltkError::Unknown(String::from("Failed to convert path to string"))
152        })?)?;
153        unsafe { Fl_Native_File_Chooser_set_directory(self.inner, dir.as_ptr()) }
154        Ok(())
155    }
156
157    /// Shows the file dialog
158    pub fn show(&mut self) -> Result<NativeFileChooserAction, FltkError> {
159        assert!(!self.inner.is_null());
160        unsafe {
161            match Fl_Native_File_Chooser_show(self.inner) {
162                0 => Ok(NativeFileChooserAction::Success),
163                -1 => Err(FltkError::Unknown(self.error_message().unwrap())),
164                1 => Ok(NativeFileChooserAction::Cancelled),
165                _ => unreachable!(),
166            }
167        }
168    }
169
170    /// Sets the option for the dialog
171    pub fn set_option(&mut self, opt: NativeFileChooserOptions) {
172        assert!(!self.inner.is_null());
173        unsafe { Fl_Native_File_Chooser_set_option(self.inner, opt.bits()) }
174    }
175
176    /// Sets the type for the dialog
177    pub fn set_type(&mut self, op: NativeFileChooserType) {
178        assert!(!self.inner.is_null());
179        unsafe { Fl_Native_File_Chooser_set_type(self.inner, op as i32) }
180    }
181
182    /// Sets the title for the dialog
183    pub fn set_title(&mut self, title: &str) {
184        assert!(!self.inner.is_null());
185        let title = CString::safe_new(title);
186        unsafe { Fl_Native_File_Chooser_set_title(self.inner, title.as_ptr()) }
187    }
188
189    /// Sets the filter for the dialog, can be:
190    /// A single wildcard (e.g. `"*.txt"`).
191    /// Multiple wildcards (e.g. `"*.{cxx,h,H}"`).
192    /// A descriptive name followed by a `\t` and a wildcard (e.g. `"Text Files\t*.txt"`).
193    /// A list of separate wildcards with a `\n` between each (e.g. `"*.{cxx,H}\n*.txt"`).
194    /// A list of descriptive names and wildcards (e.g. `"C++ Files\t*.{cxx,H}\nTxt Files\t*.txt"`)
195    pub fn set_filter(&mut self, f: &str) {
196        assert!(!self.inner.is_null());
197        let f = CString::safe_new(f);
198        unsafe { Fl_Native_File_Chooser_set_filter(self.inner, f.as_ptr()) }
199    }
200
201    /// Gets the filter of the `FileChooser`
202    pub fn filter(&self) -> Option<String> {
203        assert!(!self.inner.is_null());
204        unsafe {
205            let ptr = Fl_Native_File_Chooser_filter(self.inner);
206            if ptr.is_null() {
207                None
208            } else {
209                Some(
210                    CStr::from_ptr(ptr as *mut raw::c_char)
211                        .to_string_lossy()
212                        .to_string(),
213                )
214            }
215        }
216    }
217
218    /// Gets the current filename filter selection
219    pub fn filter_value(&self) -> i32 {
220        assert!(!self.inner.is_null());
221        unsafe { Fl_Native_File_Chooser_filter_value(self.inner) }
222    }
223
224    /// Sets the filter value using an index to the '\t'separated filters
225    pub fn set_filter_value(&mut self, f: i32) {
226        assert!(!self.inner.is_null());
227        unsafe { Fl_Native_File_Chooser_set_filter_value(self.inner, f) }
228    }
229
230    /// Sets the default filename for the dialog
231    pub fn set_preset_file(&mut self, f: &str) {
232        assert!(!self.inner.is_null());
233        let f = CString::safe_new(f);
234        unsafe { Fl_Native_File_Chooser_set_preset_file(self.inner, f.as_ptr()) }
235    }
236
237    /// returns the error message from the file dialog
238    pub fn error_message(&self) -> Option<String> {
239        assert!(!self.inner.is_null());
240        unsafe {
241            let err_msg = Fl_Native_File_Chooser_errmsg(self.inner);
242            if err_msg.is_null() {
243                None
244            } else {
245                Some(
246                    CStr::from_ptr(err_msg as *mut raw::c_char)
247                        .to_string_lossy()
248                        .to_string(),
249                )
250            }
251        }
252    }
253}
254
255impl Drop for NativeFileChooser {
256    fn drop(&mut self) {
257        if !self.inner.is_null() {
258            unsafe { Fl_Native_File_Chooser_delete(self.inner) }
259            self.inner = std::ptr::null_mut();
260        }
261    }
262}
263
264/// Displays a message box
265pub fn message(txt: &str) {
266    unsafe {
267        let txt = CString::safe_new(txt);
268        Fl_message(txt.as_ptr());
269    }
270}
271
272/// Displays an alert box
273pub fn alert(txt: &str) {
274    unsafe {
275        let txt = CString::safe_new(txt);
276        Fl_alert(txt.as_ptr());
277    }
278}
279
280/// Displays a choice box with up to three choices.
281/// Closing the dialog returns None. Choosing a value returns its index from the arguments.
282pub fn choice(txt: &str, b0: &str, b1: &str, b2: &str) -> Option<i32> {
283    unsafe {
284        let txt = CString::safe_new(txt);
285        let b0 = CString::safe_new(b0);
286        let b1 = CString::safe_new(b1);
287        let b2 = CString::safe_new(b2);
288        let ret = Fl_choice_n(txt.as_ptr(), b0.as_ptr(), b1.as_ptr(), b2.as_ptr());
289        if ret < 0 { None } else { Some(ret) }
290    }
291}
292
293/// Displays an input box, which returns the inputted string.
294/// Can be used for gui io
295pub fn input(txt: &str, deflt: &str) -> Option<String> {
296    unsafe {
297        let temp = CString::safe_new(deflt);
298        let txt = CString::safe_new(txt);
299        let x = Fl_input(txt.as_ptr(), temp.as_ptr());
300        if x.is_null() {
301            None
302        } else {
303            Some(
304                CStr::from_ptr(x as *const raw::c_char)
305                    .to_string_lossy()
306                    .to_string(),
307            )
308        }
309    }
310}
311
312/// Shows an input box, but with hidden string
313pub fn password(txt: &str, deflt: &str) -> Option<String> {
314    unsafe {
315        let temp = CString::safe_new(deflt);
316        let txt = CString::safe_new(txt);
317        let x = Fl_password(txt.as_ptr(), temp.as_ptr());
318        if x.is_null() {
319            None
320        } else {
321            Some(
322                CStr::from_ptr(x as *const raw::c_char)
323                    .to_string_lossy()
324                    .to_string(),
325            )
326        }
327    }
328}
329
330/// Creates a help dialog
331#[derive(Debug)]
332pub struct HelpDialog {
333    inner: *mut Fl_Help_Dialog,
334}
335
336impl Default for HelpDialog {
337    fn default() -> Self {
338        unsafe {
339            let help_dialog = Fl_Help_Dialog_new();
340            assert!(!help_dialog.is_null());
341            HelpDialog { inner: help_dialog }
342        }
343    }
344}
345
346impl HelpDialog {
347    /// Creates a new Help dialog with position(x, y) and size(w, h)
348    pub fn new(x: i32, y: i32, w: i32, h: i32) -> HelpDialog {
349        let mut temp = HelpDialog::default();
350        temp.resize(x, y, w, h);
351        temp
352    }
353
354    /// Hides the help dialog
355    pub fn hide(&mut self) {
356        unsafe { Fl_Help_Dialog_hide(self.inner) }
357    }
358
359    /// Loads a file for the help dialog
360    /// # Errors
361    /// Errors on non-existent path
362    pub fn load<P: AsRef<Path>>(&mut self, file: P) -> Result<(), FltkError> {
363        self.load_(file.as_ref())
364    }
365
366    fn load_(&mut self, file: &Path) -> Result<(), FltkError> {
367        let f = file
368            .to_str()
369            .ok_or_else(|| FltkError::Unknown(String::from("Failed to convert path to string")))?;
370        let f = CString::new(f)?;
371        unsafe {
372            match Fl_Help_Dialog_load(self.inner, f.as_ptr()) {
373                0 => Ok(()),
374                _ => Err(FltkError::Internal(FltkErrorKind::ResourceNotFound)),
375            }
376        }
377    }
378
379    /// Sets the position of the help dialog
380    pub fn position(&mut self, x: i32, y: i32) {
381        unsafe { Fl_Help_Dialog_position(self.inner, x, y) }
382    }
383
384    /// Resizes the help dialog
385    pub fn resize(&mut self, x: i32, y: i32, w: i32, h: i32) {
386        unsafe { Fl_Help_Dialog_resize(self.inner, x, y, w, h) }
387    }
388
389    /// Shows the help dialog
390    pub fn show(&mut self) {
391        unsafe { Fl_Help_Dialog_show(self.inner) }
392    }
393
394    /// Sets the text size
395    pub fn set_text_size(&mut self, s: i32) {
396        unsafe { Fl_Help_Dialog_set_text_size(self.inner, s) }
397    }
398
399    /// Returns the text size
400    pub fn text_size(&self) -> i32 {
401        unsafe { Fl_Help_Dialog_text_size(self.inner) }
402    }
403
404    /// Sets the value of the help dialog
405    pub fn set_value(&mut self, f: &str) {
406        let f = CString::safe_new(f);
407        unsafe { Fl_Help_Dialog_set_value(self.inner, f.as_ptr()) }
408    }
409
410    /// Returns the value of the help dialog
411    pub fn value(&self) -> Option<String> {
412        unsafe {
413            let val = Fl_Help_Dialog_value(self.inner);
414            if val.is_null() {
415                None
416            } else {
417                Some(CStr::from_ptr(val).to_string_lossy().to_string())
418            }
419        }
420    }
421
422    /// Returns whether the help dialog is visible
423    pub fn visible(&self) -> bool {
424        unsafe { Fl_Help_Dialog_visible(self.inner) != 0 }
425    }
426
427    /// Returns whether the help dialog is visible
428    pub fn shown(&self) -> bool {
429        unsafe { Fl_Help_Dialog_visible(self.inner) != 0 }
430    }
431
432    /// Returns the width of the help dialog
433    pub fn w(&self) -> i32 {
434        unsafe { Fl_Help_Dialog_w(self.inner) }
435    }
436
437    /// Returns the height of the help dialog
438    pub fn h(&self) -> i32 {
439        unsafe { Fl_Help_Dialog_h(self.inner) }
440    }
441
442    /// Returns the x position of the help dialog
443    pub fn x(&self) -> i32 {
444        unsafe { Fl_Help_Dialog_x(self.inner) }
445    }
446
447    /// Returns the y position of the help dialog
448    pub fn y(&self) -> i32 {
449        unsafe { Fl_Help_Dialog_y(self.inner) }
450    }
451}
452
453impl Drop for HelpDialog {
454    fn drop(&mut self) {
455        unsafe { Fl_Help_Dialog_delete(self.inner) }
456    }
457}
458
459/// Defines the type of beep to be passed to the beep function
460#[repr(i32)]
461#[derive(Debug, Copy, Clone, PartialEq, Eq)]
462pub enum BeepType {
463    /// Default beep
464    Default = 0,
465    /// Message beep
466    Message,
467    /// Error beep
468    Error,
469    /// Question beep
470    Question,
471    /// Password sound
472    Password,
473    /// Notification sound
474    Notification,
475}
476
477/// Emits a beep
478pub fn beep(tp: BeepType) {
479    unsafe { Fl_beep(tp as i32) }
480}
481
482/**
483    FLTK's own `FileChooser`. Which differs for the Native `NativeFileChooser`
484    Example:
485    ```rust,no_run
486    use fltk::{prelude::*, *};
487    fn main() {
488        let app = app::App::default();
489        let mut win = window::Window::default().with_size(900, 300);
490        let mut chooser = dialog::FileChooser::new(
491            ".",                    // directory
492            "*",                    // filter or pattern
493            dialog::FileChooserType::Multi, // chooser type
494            "Title Of Chooser",     // title
495        );
496        chooser.show();
497        // Block until user picks something.
498        //     (The other way to do this is to use a callback())
499        //
500        while chooser.shown() {
501            app::wait();
502        }
503        // User hit cancel?
504        if chooser.value(1).is_none() {
505            println!("(User hit 'Cancel')");
506            return;
507        }
508        // Print what the user picked
509        println!("--------------------");
510        println!("DIRECTORY: '{}'", chooser.directory().unwrap().display());
511        println!("    VALUE: '{}'", chooser.value(1).unwrap()); // value starts at 1!
512        println!("    COUNT: {} files selected", chooser.count());
513        // Multiple files? Show all of them
514        if chooser.count() > 1 {
515            for t in 1..=chooser.count() {
516                println!(" VALUE[{}]: '{}'", t, chooser.value(t).unwrap());
517            }
518        }
519        win.end();
520        win.show();
521        app.run().unwrap();
522    }
523    ```
524*/
525pub struct FileChooser {
526    inner: *mut Fl_File_Chooser,
527}
528
529bitflags::bitflags! {
530    /// The types of FileChooser
531    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
532    pub struct FileChooserType: i32 {
533        /// Single file
534        const Single = 0;
535        /// Multiple files
536        const Multi = 1;
537        /// Allow creation of file/dir
538        const Create = 2;
539        /// Directory
540        const Directory = 4;
541    }
542}
543
544impl FileChooser {
545    /// Instantiates a new `FileChooser`
546    pub fn new<P: AsRef<Path>>(
547        dir: P,
548        pattern: &str,
549        typ: FileChooserType,
550        title: &str,
551    ) -> FileChooser {
552        Self::new_(dir.as_ref(), pattern, typ, title)
553    }
554
555    fn new_(dir: &Path, pattern: &str, typ: FileChooserType, title: &str) -> FileChooser {
556        if !images_registered() {
557            register_images();
558        }
559        let dir = dir.to_str().unwrap_or(".");
560        let dir = CString::safe_new(dir);
561        let pattern = CString::safe_new(pattern);
562        let title = CString::safe_new(title);
563        unsafe {
564            let ptr = Fl_File_Chooser_new(
565                dir.as_ptr(),
566                pattern.as_ptr(),
567                typ.bits(),
568                title.into_raw() as _,
569            );
570            assert!(!ptr.is_null());
571            FileChooser { inner: ptr }
572        }
573    }
574    /// Deletes a `FileChooser`
575    /// # Safety
576    /// Can invalidate the underlying pointer
577    pub unsafe fn delete(dlg: &Self) {
578        unsafe { Fl_File_Chooser_delete(dlg.inner) }
579    }
580
581    /// Gets the new button of the `FileChooser`
582    pub fn new_button(&self) -> Option<impl ButtonExt> {
583        assert!(!self.inner.is_null());
584        unsafe {
585            let ptr = Fl_File_Chooser_newButton(self.inner);
586            if ptr.is_null() {
587                None
588            } else {
589                Some(crate::button::Button::from_widget_ptr(ptr as *mut _))
590            }
591        }
592    }
593
594    /// Gets the preview button of the `FileChooser`
595    pub fn preview_button(&self) -> Option<impl ButtonExt> {
596        assert!(!self.inner.is_null());
597        unsafe {
598            let ptr = Fl_File_Chooser_previewButton(self.inner);
599            if ptr.is_null() {
600                None
601            } else {
602                Some(crate::button::CheckButton::from_widget_ptr(
603                    ptr as *mut fltk_sys::widget::Fl_Widget,
604                ))
605            }
606        }
607    }
608
609    /// Gets the show hidden button of the `FileChooser`
610    pub fn show_hidden_button(&self) -> Option<impl ButtonExt> {
611        assert!(!self.inner.is_null());
612        unsafe {
613            let ptr = Fl_File_Chooser_showHiddenButton(self.inner);
614            if ptr.is_null() {
615                None
616            } else {
617                Some(crate::button::CheckButton::from_widget_ptr(
618                    ptr as *mut fltk_sys::widget::Fl_Widget,
619                ))
620            }
621        }
622    }
623
624    /// Sets the callback of the `FileChooser`
625    pub fn set_callback<F: FnMut(&mut Self) + 'static>(&mut self, cb: F) {
626        assert!(!self.inner.is_null());
627        unsafe {
628            unsafe extern "C" fn shim(arg1: *mut Fl_File_Chooser, data: *mut raw::c_void) {
629                unsafe {
630                    let mut wid = FileChooser { inner: arg1 };
631                    let a: *mut Box<dyn FnMut(&mut FileChooser)> =
632                        data as *mut Box<dyn FnMut(&mut FileChooser)>;
633                    let f: &mut dyn FnMut(&mut FileChooser) = &mut **a;
634                    let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(&mut wid)));
635                }
636            }
637            let _old_data = self.user_data();
638            let a: *mut Box<dyn FnMut(&mut Self)> = Box::into_raw(Box::new(Box::new(cb)));
639            let data: *mut raw::c_void = a as *mut raw::c_void;
640            let callback: Option<
641                unsafe extern "C" fn(arg1: *mut Fl_File_Chooser, data: *mut raw::c_void),
642            > = Some(shim);
643            Fl_File_Chooser_set_callback(self.inner, callback, data);
644        }
645    }
646
647    /// Sets the color of the `FileChooser`
648    pub fn set_color(&mut self, c: Color) {
649        assert!(!self.inner.is_null());
650        unsafe { Fl_File_Chooser_set_color(self.inner, c.bits()) }
651    }
652
653    /// Gets the color of the `FileChooser`
654    pub fn color(&self) -> Color {
655        assert!(!self.inner.is_null());
656        unsafe { mem::transmute(Fl_File_Chooser_color(self.inner)) }
657    }
658
659    /// Gets the count of chosen items
660    pub fn count(&self) -> i32 {
661        assert!(!self.inner.is_null());
662        unsafe { Fl_File_Chooser_count(self.inner) }
663    }
664
665    /// Sets the directory of the `FileChooser`
666    pub fn set_directory<P: AsRef<Path>>(&mut self, dir: P) {
667        self.set_directory_(dir.as_ref());
668    }
669
670    fn set_directory_(&mut self, dir: &Path) {
671        assert!(!self.inner.is_null());
672        if let Some(dir) = dir.to_str() {
673            let dir = CString::safe_new(dir);
674            unsafe { Fl_File_Chooser_set_directory(self.inner, dir.as_ptr()) }
675        }
676    }
677
678    /// Gets the directory of the `FileChooser`
679    pub fn directory(&self) -> Option<PathBuf> {
680        assert!(!self.inner.is_null());
681        unsafe {
682            let ptr = Fl_File_Chooser_directory(self.inner);
683            if ptr.is_null() {
684                None
685            } else {
686                Some(PathBuf::from(
687                    CStr::from_ptr(ptr as *mut raw::c_char)
688                        .to_string_lossy()
689                        .to_string(),
690                ))
691            }
692        }
693    }
694
695    /// Sets the filter for the dialog, can be:
696    /// Multiple patterns can be used by separating them with tabs, like "*.jpg\t*.png\t*.gif\t*".
697    /// In addition, you can provide human-readable labels with the patterns inside parenthesis,
698    /// like "JPEG Files (*.jpg)\tPNG Files (*.png)\tGIF Files (*.gif)\tAll Files (*)
699    /// And "Rust Files (*.{rs,txt,toml})"
700    pub fn set_filter(&mut self, pattern: &str) {
701        assert!(!self.inner.is_null());
702        let pattern = CString::safe_new(pattern);
703        unsafe { Fl_File_Chooser_set_filter(self.inner, pattern.as_ptr()) }
704    }
705
706    /// Gets the filter of the `FileChooser`
707    pub fn filter(&self) -> Option<String> {
708        assert!(!self.inner.is_null());
709        unsafe {
710            let ptr = Fl_File_Chooser_filter(self.inner);
711            if ptr.is_null() {
712                None
713            } else {
714                Some(
715                    CStr::from_ptr(ptr as *mut raw::c_char)
716                        .to_string_lossy()
717                        .to_string(),
718                )
719            }
720        }
721    }
722
723    /// Gets the current filename filter selection
724    pub fn filter_value(&self) -> i32 {
725        assert!(!self.inner.is_null());
726        unsafe { Fl_File_Chooser_filter_value(self.inner) }
727    }
728
729    /// Sets the filter value using an index to the '\t'separated filters
730    pub fn set_filter_value(&mut self, f: i32) {
731        assert!(!self.inner.is_null());
732        unsafe { Fl_File_Chooser_set_filter_value(self.inner, f) }
733    }
734
735    /// Hides the file chooser
736    pub fn hide(&mut self) {
737        assert!(!self.inner.is_null());
738        unsafe { Fl_File_Chooser_hide(self.inner) }
739    }
740
741    /// Sets the icon size of the `FileChooser`
742    pub fn set_icon_size(&mut self, s: u8) {
743        assert!(!self.inner.is_null());
744        unsafe { Fl_File_Chooser_set_iconsize(self.inner, s) }
745    }
746
747    /// Gets the icon size of the `FileChooser`
748    pub fn icon_size(&self) -> u8 {
749        assert!(!self.inner.is_null());
750        unsafe { Fl_File_Chooser_iconsize(self.inner) }
751    }
752
753    /// Sets the label of the `FileChooser`
754    pub fn set_label(&mut self, l: &str) {
755        assert!(!self.inner.is_null());
756        let l = CString::safe_new(l);
757        let _old = unsafe { CString::from_raw(Fl_File_Chooser_label(self.inner) as _) };
758        unsafe { Fl_File_Chooser_set_label(self.inner, l.into_raw() as _) }
759    }
760
761    /// Gets the label of the `FileChooser`
762    pub fn label(&self) -> Option<String> {
763        assert!(!self.inner.is_null());
764        unsafe {
765            let ptr = Fl_File_Chooser_label(self.inner);
766            if ptr.is_null() {
767                None
768            } else {
769                Some(
770                    CStr::from_ptr(ptr as *mut raw::c_char)
771                        .to_string_lossy()
772                        .to_string(),
773                )
774            }
775        }
776    }
777
778    /// Sets the label of the Ok button
779    pub fn set_ok_label(&mut self, l: &'static str) {
780        assert!(!self.inner.is_null());
781        let l = CString::safe_new(l);
782        unsafe { Fl_File_Chooser_set_ok_label(self.inner, l.into_raw() as _) }
783    }
784
785    /// Gets the label of the Ok button
786    pub fn ok_label(&self) -> Option<String> {
787        assert!(!self.inner.is_null());
788        unsafe {
789            let ptr = Fl_File_Chooser_ok_label(self.inner);
790            if ptr.is_null() {
791                None
792            } else {
793                Some(
794                    CStr::from_ptr(ptr as *mut raw::c_char)
795                        .to_string_lossy()
796                        .to_string(),
797                )
798            }
799        }
800    }
801
802    /// Add preview to the `FileChooser`
803    pub fn set_preview(&mut self, e: bool) {
804        assert!(!self.inner.is_null());
805        unsafe { Fl_File_Chooser_set_preview(self.inner, i32::from(e)) }
806    }
807
808    /// Returns whether preview is enabled for the `FileChooser`
809    pub fn preview(&self) -> bool {
810        assert!(!self.inner.is_null());
811        unsafe { Fl_File_Chooser_preview(self.inner) != 0 }
812    }
813
814    /// Rescan the directory
815    pub fn rescan(&mut self) {
816        assert!(!self.inner.is_null());
817        unsafe { Fl_File_Chooser_rescan(self.inner) }
818    }
819
820    /// Rescan the directory while keeping the file name
821    pub fn rescan_keep_filename(&mut self) {
822        assert!(!self.inner.is_null());
823        unsafe { Fl_File_Chooser_rescan_keep_filename(self.inner) }
824    }
825
826    /// Shows the File Chooser
827    pub fn show(&mut self) {
828        assert!(!self.inner.is_null());
829        unsafe { Fl_File_Chooser_show(self.inner) }
830    }
831
832    /// Returns whether the file chooser is shown
833    pub fn shown(&self) -> bool {
834        assert!(!self.inner.is_null());
835        unsafe { Fl_File_Chooser_shown(self.inner) != 0 }
836    }
837
838    /// Sets the text color of the file chooser
839    pub fn set_text_color(&mut self, c: Color) {
840        assert!(!self.inner.is_null());
841        unsafe { Fl_File_Chooser_set_text_color(self.inner, c.bits()) }
842    }
843
844    /// Gets the text color of the file chooser
845    pub fn text_color(&self) -> Color {
846        assert!(!self.inner.is_null());
847        unsafe { mem::transmute(Fl_File_Chooser_text_color(self.inner)) }
848    }
849
850    /// Sets the text font of the file chooser
851    pub fn set_text_font(&mut self, f: Font) {
852        assert!(!self.inner.is_null());
853        unsafe { Fl_File_Chooser_set_text_font(self.inner, f.bits()) }
854    }
855
856    /// Gets the text font of the file chooser
857    pub fn text_font(&self) -> Font {
858        assert!(!self.inner.is_null());
859        unsafe { mem::transmute(Fl_File_Chooser_text_font(self.inner)) }
860    }
861
862    /// Sets the text size of the file chooser
863    pub fn set_text_size(&mut self, s: i32) {
864        assert!(!self.inner.is_null());
865        unsafe { Fl_File_Chooser_set_text_size(self.inner, s) }
866    }
867
868    /// Gets the text size of the file chooser
869    pub fn text_size(&self) -> i32 {
870        assert!(!self.inner.is_null());
871        unsafe { Fl_File_Chooser_text_size(self.inner) }
872    }
873
874    /// Sets the type of the `FileChooser`
875    pub fn set_type(&mut self, t: FileChooserType) {
876        assert!(!self.inner.is_null());
877        unsafe { Fl_File_Chooser_set_type(self.inner, t.bits()) }
878    }
879
880    /// Gets the type of the `FileChooser`
881    pub fn get_type(&self) -> FileChooserType {
882        assert!(!self.inner.is_null());
883        unsafe { mem::transmute(Fl_File_Chooser_type(self.inner)) }
884    }
885
886    /// Gets the user data of the `FileChooser`
887    /// # Safety
888    /// Can invalidate the user data while the `FileChooser` is in use
889    pub unsafe fn user_data(&self) -> Option<Box<dyn FnMut()>> {
890        unsafe {
891            let ptr = Fl_File_Chooser_user_data(self.inner);
892            if ptr.is_null() {
893                None
894            } else {
895                let x = ptr as *mut Box<dyn FnMut()>;
896                let x = Box::from_raw(x);
897                Fl_File_Chooser_set_callback(self.inner, None, std::ptr::null_mut());
898                Some(*x)
899            }
900        }
901    }
902
903    /// Gets the file or dir name chosen by the `FileChooser`
904    pub fn value(&mut self, f: i32) -> Option<String> {
905        assert!(!self.inner.is_null());
906        let f = if f == 0 { 1 } else { f };
907        unsafe {
908            let ptr = Fl_File_Chooser_value(self.inner, f);
909            if ptr.is_null() {
910                None
911            } else {
912                Some(
913                    CStr::from_ptr(ptr as *mut raw::c_char)
914                        .to_string_lossy()
915                        .to_string(),
916                )
917            }
918        }
919    }
920
921    /// Sets the file or dir name chosen by the `FileChooser`
922    pub fn set_value(&mut self, filename: &str) {
923        assert!(!self.inner.is_null());
924        let filename = CString::safe_new(filename);
925        unsafe { Fl_File_Chooser_set_value(self.inner, filename.as_ptr()) }
926    }
927
928    /// Returns whether the `FileChooser` is visible or not
929    pub fn visible(&self) -> bool {
930        assert!(!self.inner.is_null());
931        unsafe { Fl_File_Chooser_visible(self.inner) != 0 }
932    }
933
934    /// Return dialog window
935    pub fn window(&self) -> impl WindowExt {
936        // Shouldn't fail
937        unsafe {
938            let win_ptr = self
939                .new_button()
940                .unwrap()
941                .parent()
942                .unwrap()
943                .parent()
944                .unwrap()
945                .as_widget_ptr();
946            crate::window::Window::from_widget_ptr(win_ptr)
947        }
948    }
949
950    /// Set "Add favorites" label
951    pub fn set_add_favorites_label(msg: &'static str) {
952        let msg = CString::safe_new(msg);
953        unsafe { Fl_File_Chooser_set_add_favorites_label(msg.into_raw() as _) }
954    }
955
956    /// Set "All Files" label
957    pub fn set_all_files_label(msg: &'static str) {
958        let msg = CString::safe_new(msg);
959        unsafe { Fl_File_Chooser_set_all_files_label(msg.into_raw() as _) }
960    }
961
962    /// Set "Custom Filter" label
963    pub fn set_custom_filter_label(msg: &'static str) {
964        let msg = CString::safe_new(msg);
965        unsafe { Fl_File_Chooser_set_custom_filter_label(msg.into_raw() as _) }
966    }
967
968    /// Set "Existing file" label
969    pub fn set_existing_file_label(msg: &'static str) {
970        let msg = CString::safe_new(msg);
971        unsafe { Fl_File_Chooser_set_existing_file_label(msg.into_raw() as _) }
972    }
973
974    /// Set "Favorites" label
975    pub fn set_favorites_label(msg: &'static str) {
976        let msg = CString::safe_new(msg);
977        unsafe { Fl_File_Chooser_set_favorites_label(msg.into_raw() as _) }
978    }
979
980    /// Set "Filename" label
981    pub fn set_filename_label(msg: &'static str) {
982        let msg = CString::safe_new(msg);
983        unsafe { Fl_File_Chooser_set_filename_label(msg.into_raw() as _) }
984    }
985
986    /// Set "Filesystems" label
987    pub fn set_filesystems_label(msg: &'static str) {
988        let msg = CString::safe_new(msg);
989        unsafe { Fl_File_Chooser_set_filesystems_label(msg.into_raw() as _) }
990    }
991
992    /// Set "Manage favorites" label
993    pub fn set_manage_favorites_label(msg: &'static str) {
994        let msg = CString::safe_new(msg);
995        unsafe { Fl_File_Chooser_set_manage_favorites_label(msg.into_raw() as _) }
996    }
997
998    /// Set "New directory" label
999    pub fn set_new_directory_label(msg: &'static str) {
1000        let msg = CString::safe_new(msg);
1001        unsafe { Fl_File_Chooser_set_new_directory_label(msg.into_raw() as _) }
1002    }
1003
1004    /// Set "New directory" tooltip
1005    pub fn set_new_directory_tooltip(msg: &'static str) {
1006        let msg = CString::safe_new(msg);
1007        unsafe { Fl_File_Chooser_set_new_directory_tooltip(msg.into_raw() as _) }
1008    }
1009
1010    /// Set "Preview" label
1011    pub fn set_preview_label(msg: &'static str) {
1012        let msg = CString::safe_new(msg);
1013        unsafe { Fl_File_Chooser_set_preview_label(msg.into_raw() as _) }
1014    }
1015
1016    /// Set "Save" label
1017    pub fn set_save_label(msg: &'static str) {
1018        let msg = CString::safe_new(msg);
1019        unsafe { Fl_File_Chooser_set_save_label(msg.into_raw() as _) }
1020    }
1021
1022    /// Set "Show" label
1023    pub fn set_show_label(msg: &'static str) {
1024        let msg = CString::safe_new(msg);
1025        unsafe { Fl_File_Chooser_set_show_label(msg.into_raw() as _) }
1026    }
1027
1028    /// Set "hidden" label
1029    pub fn set_hidden_label(msg: &'static str) {
1030        let msg = CString::safe_new(msg);
1031        unsafe { Fl_File_Chooser_set_hidden_label(msg.into_raw() as _) }
1032    }
1033
1034    /// Set the position of the file chooser
1035    pub fn set_position(&mut self, x: i32, y: i32) {
1036        assert!(!self.inner.is_null());
1037        unsafe { Fl_File_Chooser_set_position(self.inner, x, y) }
1038    }
1039
1040    /// Set the size of the file chooser
1041    pub fn set_size(&mut self, w: i32, h: i32) {
1042        assert!(!self.inner.is_null());
1043        unsafe { Fl_File_Chooser_set_size(self.inner, w, h) }
1044    }
1045
1046    /// Get the x pos of the file chooser
1047    pub fn x(&self) -> i32 {
1048        assert!(!self.inner.is_null());
1049        unsafe { Fl_File_Chooser_x(self.inner) }
1050    }
1051
1052    /// Get the y pos of the file chooser
1053    pub fn y(&self) -> i32 {
1054        assert!(!self.inner.is_null());
1055        unsafe { Fl_File_Chooser_y(self.inner) }
1056    }
1057
1058    /// Get the width of the file chooser
1059    pub fn w(&self) -> i32 {
1060        assert!(!self.inner.is_null());
1061        unsafe { Fl_File_Chooser_w(self.inner) }
1062    }
1063
1064    /// Get the width of the file chooser
1065    pub fn h(&self) -> i32 {
1066        assert!(!self.inner.is_null());
1067        unsafe { Fl_File_Chooser_h(self.inner) }
1068    }
1069
1070    /// Get the size of the file chooser
1071    pub fn size(&self) -> (i32, i32) {
1072        (self.w(), self.h())
1073    }
1074
1075    /// Get the position of the file chooser
1076    pub fn pos(&self) -> (i32, i32) {
1077        (self.x(), self.y())
1078    }
1079}
1080
1081impl Drop for FileChooser {
1082    fn drop(&mut self) {
1083        unsafe { Fl_File_Chooser_delete(self.inner) }
1084    }
1085}
1086
1087/// Shows a directory chooser returning a String
1088pub fn dir_chooser(message: &str, fname: &str, relative: bool) -> Option<String> {
1089    unsafe {
1090        let message = CString::safe_new(message);
1091        let fname = CString::safe_new(fname);
1092        let ptr = Fl_dir_chooser(message.as_ptr(), fname.as_ptr(), i32::from(relative));
1093        if ptr.is_null() {
1094            None
1095        } else {
1096            Some(
1097                CStr::from_ptr(ptr as *mut raw::c_char)
1098                    .to_string_lossy()
1099                    .to_string(),
1100            )
1101        }
1102    }
1103}
1104
1105/**
1106    Shows a file chooser returning a String.
1107    The pattern field takes the same argument the [`FileChooser::set_filter`](`crate::dialog::FileChooser::set_filter`) method.
1108    Example:
1109    ```rust,no_run
1110    use fltk::{prelude::*, *};
1111    fn main() {
1112        let app = app::App::default();
1113        let mut win = window::Window::default().with_size(900, 300);
1114        let file = dialog::file_chooser("Choose File", "*.rs", ".", true).unwrap();
1115        println!("{}", file);
1116        win.end();
1117        win.show();
1118        app.run().unwrap();
1119    }
1120    ```
1121*/
1122pub fn file_chooser<P: AsRef<Path>>(
1123    message: &str,
1124    pattern: &str,
1125    dir: P,
1126    relative: bool,
1127) -> Option<String> {
1128    file_chooser_(message, pattern, dir.as_ref(), relative)
1129}
1130
1131fn file_chooser_(message: &str, pattern: &str, dir: &Path, relative: bool) -> Option<String> {
1132    if let Some(dir) = dir.to_str() {
1133        let message = CString::safe_new(message);
1134        let pattern = CString::safe_new(pattern);
1135        let dir = CString::safe_new(dir);
1136        unsafe {
1137            let ptr = Fl_file_chooser(
1138                message.as_ptr(),
1139                pattern.as_ptr(),
1140                dir.as_ptr(),
1141                i32::from(relative),
1142            );
1143            if ptr.is_null() {
1144                None
1145            } else {
1146                Some(
1147                    CStr::from_ptr(ptr as *mut raw::c_char)
1148                        .to_string_lossy()
1149                        .to_string(),
1150                )
1151            }
1152        }
1153    } else {
1154        None
1155    }
1156}
1157
1158/// Spawns a color chooser dialog.
1159pub fn color_chooser(name: &str, cmode: ColorMode) -> Option<(u8, u8, u8)> {
1160    unsafe {
1161        let name = CString::safe_new(name);
1162        let mut r = 255;
1163        let mut g = 255;
1164        let mut b = 255;
1165        let ret = Fl_color_chooser(name.as_ptr(), &mut r, &mut g, &mut b, cmode as i32);
1166        if ret == 0 { None } else { Some((r, g, b)) }
1167    }
1168}
1169
1170/// Spawns a color chooser dialog.
1171pub fn color_chooser_with_default(name: &str, cmode: ColorMode, col: (u8, u8, u8)) -> (u8, u8, u8) {
1172    unsafe {
1173        let name = CString::safe_new(name);
1174        let mut r = col.0;
1175        let mut g = col.1;
1176        let mut b = col.2;
1177        let ret = Fl_color_chooser(name.as_ptr(), &mut r, &mut g, &mut b, cmode as i32);
1178        if ret == 0 { col } else { (r, g, b) }
1179    }
1180}
1181
1182/// Set the next dialog's title
1183pub fn message_title(title: &str) {
1184    let title = CString::safe_new(title);
1185    unsafe { Fl_message_title(title.as_ptr() as _) }
1186}
1187
1188/// Set the default title for a dialog
1189pub fn message_title_default(title: &str) {
1190    let title = CString::safe_new(title);
1191    unsafe { Fl_message_title_default(title.as_ptr() as _) }
1192}
1193
1194/// Get the frame holding the icon of FLTK's dialog boxes
1195pub fn message_icon() -> impl WidgetExt {
1196    unsafe { crate::frame::Frame::from_widget_ptr(Fl_message_icon() as _) }
1197}
1198
1199/// Set whether hotspot is enabled for FLTK's dialog boxes
1200pub fn message_set_hotspot(enabled: bool) {
1201    unsafe { Fl_message_set_hotspot(enabled.into()) }
1202}
1203
1204/// Get whether hotspot is enabled for FLTK's dialog boxes
1205pub fn message_hotspot() -> bool {
1206    unsafe { Fl_message_hotspot() != 0 }
1207}
1208
1209/// Set the font and font size of FLTK's dialog boxes
1210pub fn message_set_font(font: Font, sz: i32) {
1211    unsafe { Fl_message_set_font(font.bits(), sz) }
1212}
1213
1214/// Set the next dialog's icon label
1215pub fn message_icon_label(label: &str) {
1216    let label = CString::safe_new(label);
1217    unsafe { Fl_message_icon_label(label.into_raw() as _) }
1218}
1219
1220/// Set the next dialog's position
1221pub fn message_position(x: i32, y: i32) {
1222    unsafe { Fl_message_position(x, y) }
1223}