paiagram/interface/
about.rs

1use crate::rw_data::ModifyData;
2use bevy::ecs::{message::MessageWriter, system::InMut};
3use egui::{Id, Modal, OpenUrl, Popup};
4use rfd::AsyncFileDialog;
5use std::collections::VecDeque;
6use std::sync::{Arc, Mutex, OnceLock};
7
8#[cfg(not(target_arch = "wasm32"))]
9use bevy::tasks::IoTaskPool;
10
11#[cfg(target_arch = "wasm32")]
12use wasm_bindgen_futures::spawn_local;
13
14static FILE_IMPORT_QUEUE: OnceLock<Arc<Mutex<VecDeque<ModifyData>>>> = OnceLock::new();
15
16fn shared_queue() -> Arc<Mutex<VecDeque<ModifyData>>> {
17    FILE_IMPORT_QUEUE
18        .get_or_init(|| Arc::new(Mutex::new(VecDeque::new())))
19        .clone()
20}
21
22fn show_import_button(
23    ui: &mut egui::Ui,
24    label: &'static str,
25    filter_label: &'static str,
26    extensions: &'static [&'static str],
27    constructor: fn(String) -> ModifyData,
28) {
29    if ui.button(label).clicked() {
30        start_file_import(filter_label, extensions, constructor);
31    }
32}
33
34fn start_file_import(
35    filter_label: &'static str,
36    extensions: &'static [&'static str],
37    constructor: fn(String) -> ModifyData,
38) {
39    let queue = shared_queue();
40    let task = async move {
41        if let Some(content) = pick_file_contents(filter_label, extensions).await {
42            if let Ok(mut guard) = queue.lock() {
43                guard.push_back(constructor(content));
44            }
45        }
46    };
47
48    #[cfg(target_arch = "wasm32")]
49    spawn_local(task);
50
51    #[cfg(not(target_arch = "wasm32"))]
52    IoTaskPool::get().spawn(task).detach();
53}
54
55async fn pick_file_contents(
56    filter_label: &'static str,
57    extensions: &'static [&'static str],
58) -> Option<String> {
59    let file = AsyncFileDialog::new()
60        .add_filter(filter_label, extensions)
61        .pick_file()
62        .await?;
63    let bytes = file.read().await;
64    String::from_utf8(bytes).ok()
65}
66
67pub fn show_about(
68    (InMut(ui), InMut(modal_open)): (InMut<egui::Ui>, InMut<bool>),
69    mut msg_read_file: MessageWriter<ModifyData>,
70) {
71    let queue = shared_queue();
72    let pending: Vec<_> = {
73        let mut guard = queue.lock().unwrap();
74        guard.drain(..).collect()
75    };
76    drop(queue);
77    for message in pending {
78        msg_read_file.write(message);
79    }
80
81    ui.horizontal(|ui| {
82        let button = ui.button("File");
83        Popup::menu(&button).show(|ui| {
84            if ui.button("Read...").clicked() {
85                // TODO
86            }
87            if ui.button("Save As...").clicked() {
88                // TODO
89            }
90            ui.separator();
91            show_import_button(
92                ui,
93                "Import qETRC...",
94                "qETRC Files",
95                &["pyetgr", "json"],
96                ModifyData::LoadQETRC,
97            );
98            show_import_button(
99                ui,
100                "Import OuDiaSecond...",
101                "OuDiaSecond Files",
102                &["oud2"],
103                ModifyData::LoadOuDiaSecond,
104            );
105        });
106        let button = ui.button("Help");
107        Popup::menu(&button).show(|ui| {
108            if ui.button("Check for Updates").clicked() {
109                // TODO
110            }
111            if ui.button("Report Issue").clicked() {
112                ui.ctx().open_url(OpenUrl {
113                    url: "https://github.com/WenSimEHRP/Paiagram/issues".into(),
114                    new_tab: true,
115                });
116            };
117            if ui.button("Help Translate").clicked() {
118                ui.ctx().open_url(OpenUrl {
119                    // TODO: add a real URL here
120                    url: "https://github.com/WenSimEHRP/Paiagram/issues".into(),
121                    new_tab: true,
122                });
123            };
124            ui.separator();
125            if ui.button("Documentation").clicked() {
126                ui.ctx().open_url(OpenUrl {
127                    // TODO: add a real URL here
128                    url: "https://github.com/WenSimEHRP/Paiagram/issues".into(),
129                    new_tab: true,
130                });
131            };
132            if ui.button("About Paiagram").clicked() {
133                *modal_open = true;
134            };
135        });
136    });
137    if *modal_open {
138        let modal = Modal::new(Id::new("Modal B")).show(ui.ctx(), |ui| {
139            ui.set_width(500.0);
140            ui.heading("About");
141            ui.label(format!("Paiagram ({})", git_version::git_version!()));
142            ui.label("© 2025 Jeremy Gao");
143            ui.label("Paiagram is a free and open-source application licensed under the AGPL v3.0 license.");
144            ui.separator();
145            egui::ScrollArea::vertical().show(ui, |ui| {
146                ui.set_width(ui.available_width());
147                ui.monospace(include_str!("../../LICENSE.md"));
148            });
149        });
150
151        if modal.should_close() {
152            *modal_open = false;
153        }
154    }
155}