Skip to main content

winio_ui_gtk/ui/
filebox.rs

1use std::path::PathBuf;
2
3use gtk4::{
4    gio::prelude::FileExt,
5    glib::{GString, object::Cast},
6};
7use winio_handle::AsWindow;
8
9use crate::Result;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct FileFilter {
13    name: String,
14    pattern: String,
15}
16
17impl FileFilter {
18    pub fn new(name: &str, pattern: &str) -> Self {
19        Self {
20            name: name.to_string(),
21            pattern: pattern.to_string(),
22        }
23    }
24}
25
26#[derive(Debug, Default, Clone)]
27pub struct FileBox {
28    title: GString,
29    filename: GString,
30    filters: Vec<FileFilter>,
31}
32
33impl FileBox {
34    pub fn new() -> Self {
35        Self::default()
36    }
37
38    pub fn title(&mut self, title: &str) {
39        self.title = GString::from(title.to_string());
40    }
41
42    pub fn filename(&mut self, filename: &str) {
43        self.filename = GString::from(filename.to_string());
44    }
45
46    pub fn filters(&mut self, filters: impl IntoIterator<Item = FileFilter>) {
47        self.filters = filters.into_iter().collect();
48    }
49
50    pub fn add_filter(&mut self, filter: FileFilter) {
51        self.filters.push(filter);
52    }
53
54    pub async fn open(self, parent: Option<impl AsWindow>) -> Result<Option<PathBuf>> {
55        Ok(self
56            .filebox()
57            .open_future(parent.as_ref().map(|w| w.as_window().to_gtk()))
58            .await?
59            .path())
60    }
61
62    pub async fn open_multiple(self, parent: Option<impl AsWindow>) -> Result<Vec<PathBuf>> {
63        Ok(self
64            .filebox()
65            .open_multiple_future(parent.as_ref().map(|w| w.as_window().to_gtk()))
66            .await?
67            .into_iter()
68            .filter_map(|f| f.ok())
69            .filter_map(|f| f.dynamic_cast::<gtk4::gio::File>().ok())
70            .filter_map(|f| f.path())
71            .collect())
72    }
73
74    pub async fn open_folder(self, parent: Option<impl AsWindow>) -> Result<Option<PathBuf>> {
75        Ok(self
76            .filebox()
77            .select_folder_future(parent.as_ref().map(|w| w.as_window().to_gtk()))
78            .await?
79            .path())
80    }
81
82    pub async fn save(self, parent: Option<impl AsWindow>) -> Result<Option<PathBuf>> {
83        Ok(self
84            .filebox()
85            .save_future(parent.as_ref().map(|w| w.as_window().to_gtk()))
86            .await?
87            .path())
88    }
89
90    fn filebox(self) -> gtk4::FileDialog {
91        let filter = gtk4::FileFilter::new();
92        if !self.filters.is_empty() {
93            for f in self.filters {
94                filter.add_pattern(&f.pattern);
95            }
96        } else {
97            filter.add_pattern("*.*");
98        }
99
100        gtk4::FileDialog::builder()
101            .modal(true)
102            .title(self.title)
103            .initial_name(self.filename)
104            .default_filter(&filter)
105            .build()
106    }
107}