Skip to main content

ferroui_diagnostics/diagnostics/screenshots/
file_picker_handler.rs

1//! Port of `Diagnostics/Screenshots/FilePickerHandler.cs`.
2
3use super::BaseRenderToStreamHandler;
4use crate::diagnostics::views::MainWindow;
5use ferroui_base::input::LocalBoxFuture;
6use ferroui_base::platform::storage::{
7    FilePickerFileTypes, FilePickerSaveOptions, IStorageFolder, IStorageProvider, WellKnownFolder,
8};
9use ferroui_base::{Ref, Visual};
10use ferroui_controls::{Application, Control, TopLevel};
11use std::io::Write;
12use std::rc::Rc;
13
14/// Show a FileSavePicker to select where save screenshot
15pub struct FilePickerHandler {
16    title: String,
17    screenshot_root: Option<String>,
18}
19
20impl PartialEq for FilePickerHandler {
21    fn eq(&self, other: &Self) -> bool {
22        std::ptr::eq(self, other)
23    }
24}
25
26impl Default for FilePickerHandler {
27    fn default() -> Self {
28        Self::with_title(None, None)
29    }
30}
31
32impl FilePickerHandler {
33    /// Instance FilePickerHandler
34    pub fn new() -> Self {
35        Self::with_title(None, None)
36    }
37
38    /// Instance FilePickerHandler with specificated parameter
39    ///
40    /// `title` is the title of the save file picker; `screenshot_root` the folder it starts in.
41    pub fn with_title(title: Option<String>, screenshot_root: Option<String>) -> Self {
42        Self { title: title.unwrap_or_else(|| "Save Screenshot to ...".to_string()), screenshot_root }
43    }
44
45    fn get_top_level(control: &Ref<Control>) -> Option<Ref<TopLevel>> {
46        // If possible, use devtools main window.
47        let dev_tools_top_level: Option<Ref<TopLevel>> = Application::current()
48            .and_then(|application| application.application_lifetime())
49            .and_then(|lifetime| {
50                lifetime.as_classic_desktop_style_application_lifetime().and_then(|desktop| {
51                    desktop.windows().into_iter().find(|window| window.is::<MainWindow>()).map(|window| window.upcast())
52                })
53            });
54
55        let visual: &Visual = control;
56        dev_tools_top_level.or_else(|| TopLevel::get_top_level(Some(visual)))
57    }
58}
59
60impl BaseRenderToStreamHandler for FilePickerHandler {
61    fn get_stream(&self, control: &Ref<Control>) -> LocalBoxFuture<std::io::Result<Option<Box<dyn Write>>>> {
62        let Some(top_level) = Self::get_top_level(control) else {
63            return Box::pin(std::future::ready(Err::<Option<Box<dyn Write>>, _>(std::io::Error::other(
64                "No TopLevel is available.",
65            ))));
66        };
67        let storage_provider: Rc<dyn IStorageProvider> = top_level.storage_provider();
68        let title = self.title.clone();
69        let screenshot_root = self.screenshot_root.clone();
70
71        Box::pin(async move {
72            let mut default_folder: Option<Rc<dyn IStorageFolder>> = None;
73            if let Some(screenshot_root) = &screenshot_root {
74                default_folder = storage_provider.try_get_folder_from_path_str_async(screenshot_root).await;
75            }
76            if default_folder.is_none() {
77                default_folder = storage_provider.try_get_well_known_folder_async(WellKnownFolder::Pictures).await;
78            }
79
80            let mut options = FilePickerSaveOptions::new();
81            options.set_suggested_start_location(default_folder);
82            options.set_title(Some(title));
83            options.set_file_type_choices(Some(vec![FilePickerFileTypes::image_png()]));
84            options.set_default_extension(Some(".png".to_string()));
85
86            let result = storage_provider.save_file_picker_async(options).await?;
87            let Some(result) = result else {
88                return Ok(None);
89            };
90
91            Ok::<Option<Box<dyn Write>>, std::io::Error>(Some(result.open_write_async().await?))
92        })
93    }
94}