use std::collections::HashMap;
use dear_imgui_rs::Ui;
use crate::dialog_state::FileDialogState;
use crate::fs::{FileSystem, StdFileSystem};
use crate::ui::{FileDialogExt, WindowHostConfig};
use crate::{FileDialogError, Selection};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct DialogId(u64);
pub struct DialogManager {
next_id: u64,
browsers: HashMap<DialogId, FileDialogState>,
fs: Box<dyn FileSystem>,
}
impl DialogManager {
pub fn new() -> Self {
Self::with_fs(Box::new(StdFileSystem))
}
pub fn with_fs(fs: Box<dyn FileSystem>) -> Self {
Self {
next_id: 0,
browsers: HashMap::new(),
fs,
}
}
pub fn set_fs(&mut self, fs: Box<dyn FileSystem>) {
self.fs = fs;
}
pub fn fs(&self) -> &dyn FileSystem {
self.fs.as_ref()
}
pub fn open_browser(&mut self, mode: crate::DialogMode) -> DialogId {
self.open_browser_with_state(FileDialogState::new(mode))
}
pub fn open_browser_with_state(&mut self, state: FileDialogState) -> DialogId {
let mut state = state;
state.open();
self.next_id = self.next_id.wrapping_add(1);
let id = DialogId(self.next_id);
self.browsers.insert(id, state);
id
}
pub fn close(&mut self, id: DialogId) -> Option<FileDialogState> {
self.browsers.remove(&id)
}
pub fn contains(&self, id: DialogId) -> bool {
self.browsers.contains_key(&id)
}
pub fn dialog_state(&self, id: DialogId) -> Option<&FileDialogState> {
self.browsers.get(&id)
}
pub fn dialog_state_mut(&mut self, id: DialogId) -> Option<&mut FileDialogState> {
self.browsers.get_mut(&id)
}
pub fn show_browser(
&mut self,
ui: &Ui,
id: DialogId,
) -> Option<Result<Selection, FileDialogError>> {
let state = self.browsers.get_mut(&id)?;
let cfg = WindowHostConfig::for_mode(state.core.mode);
let res = ui
.file_browser()
.show_windowed_with(state, &cfg, self.fs.as_ref(), None, None);
if res.is_some() {
self.browsers.remove(&id);
}
res
}
pub fn show_browser_windowed(
&mut self,
ui: &Ui,
id: DialogId,
cfg: &WindowHostConfig,
) -> Option<Result<Selection, FileDialogError>> {
let state = self.browsers.get_mut(&id)?;
let res = ui
.file_browser()
.show_windowed_with(state, cfg, self.fs.as_ref(), None, None);
if res.is_some() {
self.browsers.remove(&id);
}
res
}
pub fn draw_browser_contents(
&mut self,
ui: &Ui,
id: DialogId,
) -> Option<Result<Selection, FileDialogError>> {
let state = self.browsers.get_mut(&id)?;
let res = ui
.file_browser()
.draw_contents_with(state, self.fs.as_ref(), None, None);
if res.is_some() {
self.browsers.remove(&id);
}
res
}
}
impl std::fmt::Debug for DialogManager {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DialogManager")
.field("next_id", &self.next_id)
.field("browsers_len", &self.browsers.len())
.finish()
}
}
impl Default for DialogManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::DialogMode;
#[test]
fn open_close_roundtrip() {
let mut mgr = DialogManager::new();
let id1 = mgr.open_browser(DialogMode::OpenFile);
let id2 = mgr.open_browser(DialogMode::SaveFile);
assert_ne!(id1, id2);
assert!(mgr.contains(id1));
assert!(mgr.contains(id2));
assert!(mgr.dialog_state(id1).unwrap().is_open());
assert!(mgr.dialog_state(id2).unwrap().is_open());
let s1 = mgr.close(id1).unwrap();
assert_eq!(s1.core.mode, DialogMode::OpenFile);
assert!(!mgr.contains(id1));
assert!(mgr.contains(id2));
}
}