winio_ui_gtk/ui/
filebox.rs1use 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}