dear_file_browser/
dialog_manager.rs1use std::collections::HashMap;
2
3use dear_imgui_rs::Ui;
4
5use crate::dialog_state::FileDialogState;
6use crate::fs::{FileSystem, StdFileSystem};
7use crate::ui::{FileDialogExt, WindowHostConfig};
8use crate::{FileDialogError, Selection};
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
12pub struct DialogId(u64);
13
14pub struct DialogManager {
21 next_id: u64,
22 browsers: HashMap<DialogId, FileDialogState>,
23 fs: Box<dyn FileSystem>,
24}
25
26impl DialogManager {
27 pub fn new() -> Self {
29 Self::with_fs(Box::new(StdFileSystem))
30 }
31
32 pub fn with_fs(fs: Box<dyn FileSystem>) -> Self {
34 Self {
35 next_id: 0,
36 browsers: HashMap::new(),
37 fs,
38 }
39 }
40
41 pub fn set_fs(&mut self, fs: Box<dyn FileSystem>) {
43 self.fs = fs;
44 }
45
46 pub fn fs(&self) -> &dyn FileSystem {
48 self.fs.as_ref()
49 }
50
51 pub fn open_browser(&mut self, mode: crate::DialogMode) -> DialogId {
53 self.open_browser_with_state(FileDialogState::new(mode))
54 }
55
56 pub fn open_browser_with_state(&mut self, state: FileDialogState) -> DialogId {
58 let mut state = state;
59 state.open();
62 self.next_id = self.next_id.wrapping_add(1);
63 let id = DialogId(self.next_id);
64 self.browsers.insert(id, state);
65 id
66 }
67
68 pub fn close(&mut self, id: DialogId) -> Option<FileDialogState> {
70 self.browsers.remove(&id)
71 }
72
73 pub fn contains(&self, id: DialogId) -> bool {
75 self.browsers.contains_key(&id)
76 }
77
78 pub fn dialog_state(&self, id: DialogId) -> Option<&FileDialogState> {
80 self.browsers.get(&id)
81 }
82
83 pub fn dialog_state_mut(&mut self, id: DialogId) -> Option<&mut FileDialogState> {
85 self.browsers.get_mut(&id)
86 }
87
88 pub fn show_browser(
93 &mut self,
94 ui: &Ui,
95 id: DialogId,
96 ) -> Option<Result<Selection, FileDialogError>> {
97 let state = self.browsers.get_mut(&id)?;
98 let cfg = WindowHostConfig::for_mode(state.core.mode);
99 let res = ui
100 .file_browser()
101 .show_windowed_with(state, &cfg, self.fs.as_ref(), None, None);
102 if res.is_some() {
103 self.browsers.remove(&id);
104 }
105 res
106 }
107
108 pub fn show_browser_windowed(
113 &mut self,
114 ui: &Ui,
115 id: DialogId,
116 cfg: &WindowHostConfig,
117 ) -> Option<Result<Selection, FileDialogError>> {
118 let state = self.browsers.get_mut(&id)?;
119 let res = ui
120 .file_browser()
121 .show_windowed_with(state, cfg, self.fs.as_ref(), None, None);
122 if res.is_some() {
123 self.browsers.remove(&id);
124 }
125 res
126 }
127
128 pub fn draw_browser_contents(
133 &mut self,
134 ui: &Ui,
135 id: DialogId,
136 ) -> Option<Result<Selection, FileDialogError>> {
137 let state = self.browsers.get_mut(&id)?;
138 let res = ui
139 .file_browser()
140 .draw_contents_with(state, self.fs.as_ref(), None, None);
141 if res.is_some() {
142 self.browsers.remove(&id);
143 }
144 res
145 }
146}
147
148impl std::fmt::Debug for DialogManager {
149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150 f.debug_struct("DialogManager")
151 .field("next_id", &self.next_id)
152 .field("browsers_len", &self.browsers.len())
153 .finish()
154 }
155}
156
157impl Default for DialogManager {
158 fn default() -> Self {
159 Self::new()
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166 use crate::DialogMode;
167
168 #[test]
169 fn open_close_roundtrip() {
170 let mut mgr = DialogManager::new();
171 let id1 = mgr.open_browser(DialogMode::OpenFile);
172 let id2 = mgr.open_browser(DialogMode::SaveFile);
173 assert_ne!(id1, id2);
174
175 assert!(mgr.contains(id1));
176 assert!(mgr.contains(id2));
177
178 assert!(mgr.dialog_state(id1).unwrap().is_open());
179 assert!(mgr.dialog_state(id2).unwrap().is_open());
180
181 let s1 = mgr.close(id1).unwrap();
182 assert_eq!(s1.core.mode, DialogMode::OpenFile);
183 assert!(!mgr.contains(id1));
184 assert!(mgr.contains(id2));
185 }
186}