1use std::collections::VecDeque;
11
12use blockworx_doc::block_model::Asset;
13
14use crate::export::ExportPayload;
15
16type Answer<T> = std::sync::mpsc::Receiver<T>;
19
20pub(crate) enum Dialogs {
22 Native,
23 Scripted(Scripted),
24}
25
26impl Default for Dialogs {
27 fn default() -> Self {
31 if cfg!(test) {
32 Dialogs::Scripted(Scripted::default())
33 } else {
34 Dialogs::Native
35 }
36 }
37}
38
39impl Dialogs {
40 pub(crate) fn image(&mut self, ctx: &egui::Context) -> Answer<Option<Asset>> {
42 match self {
43 Dialogs::Native => crate::import::spawn_image_dialog(ctx),
44 Dialogs::Scripted(scripted) => {
45 scripted.asked.push(Asked::Image);
46 answered(scripted.images.pop_front().flatten())
47 }
48 }
49 }
50
51 pub(crate) fn import(&mut self, ctx: &egui::Context) -> Answer<Option<(String, Vec<u8>)>> {
53 match self {
54 Dialogs::Native => crate::import::spawn_import_dialog(ctx),
55 Dialogs::Scripted(scripted) => {
56 scripted.asked.push(Asked::Import);
57 answered(scripted.imports.pop_front().flatten())
58 }
59 }
60 }
61
62 #[cfg(not(target_arch = "wasm32"))]
64 pub(crate) fn file(
65 &mut self,
66 ctx: &egui::Context,
67 request: crate::file::FileRequest,
68 ) -> crate::file::PickReceiver {
69 match self {
70 Dialogs::Native => crate::file::spawn_file_dialog(ctx, request),
71 Dialogs::Scripted(scripted) => {
72 scripted.asked.push(Asked::File(request));
73 answered(scripted.files.pop_front().flatten())
74 }
75 }
76 }
77
78 pub(crate) fn export(&mut self, ctx: &egui::Context, payload: ExportPayload) {
80 match self {
81 Dialogs::Native => crate::export::spawn_export(ctx, payload),
82 Dialogs::Scripted(scripted) => {
83 scripted.asked.push(Asked::Export(payload.name.clone()));
84 scripted.exports.push_back(payload);
85 }
86 }
87 }
88}
89
90pub(crate) struct Ask<'a> {
95 pub ctx: &'a egui::Context,
96 pub dialogs: &'a mut Dialogs,
97}
98
99impl Ask<'_> {
100 pub(crate) fn image(&mut self) -> Answer<Option<Asset>> {
101 self.dialogs.image(self.ctx)
102 }
103
104 pub(crate) fn import(&mut self) -> Answer<Option<(String, Vec<u8>)>> {
105 self.dialogs.import(self.ctx)
106 }
107
108 #[cfg(not(target_arch = "wasm32"))]
109 pub(crate) fn file(&mut self, request: crate::file::FileRequest) -> crate::file::PickReceiver {
110 self.dialogs.file(self.ctx, request)
111 }
112}
113
114#[derive(Clone, PartialEq, Eq, Debug)]
118pub(crate) enum Asked {
119 Image,
120 Import,
121 #[cfg(not(target_arch = "wasm32"))]
122 File(crate::file::FileRequest),
123 Export(String),
125}
126
127#[derive(Default)]
130pub(crate) struct Scripted {
131 asked: Vec<Asked>,
132 images: VecDeque<Option<Asset>>,
133 imports: VecDeque<Option<(String, Vec<u8>)>>,
134 #[cfg(not(target_arch = "wasm32"))]
135 files: VecDeque<Option<crate::file::FilePick>>,
136 exports: VecDeque<ExportPayload>,
139}
140
141fn answered<T>(value: T) -> Answer<T> {
144 let (tx, rx) = std::sync::mpsc::channel();
145 let _ = tx.send(value);
146 rx
147}
148
149#[cfg(test)]
150impl Dialogs {
151 pub(crate) fn asked(&self) -> &[Asked] {
153 match self {
154 Dialogs::Native => &[],
155 Dialogs::Scripted(scripted) => &scripted.asked,
156 }
157 }
158
159 pub(crate) fn answer_image(&mut self, asset: Option<Asset>) {
161 self.scripted().images.push_back(asset);
162 }
163
164 pub(crate) fn answer_import(&mut self, file: Option<(String, Vec<u8>)>) {
166 self.scripted().imports.push_back(file);
167 }
168
169 pub(crate) fn exported(&mut self) -> Option<ExportPayload> {
171 self.scripted().exports.pop_front()
172 }
173
174 #[cfg(not(target_arch = "wasm32"))]
176 pub(crate) fn answer_file(&mut self, pick: Option<crate::file::FilePick>) {
177 self.scripted().files.push_back(pick);
178 }
179
180 fn scripted(&mut self) -> &mut Scripted {
181 match self {
182 Dialogs::Scripted(scripted) => scripted,
183 Dialogs::Native => panic!("a test is driving the platform's own dialogs"),
184 }
185 }
186}