paiagram/
main.rs

1use bevy::{log::LogPlugin, prelude::*};
2use clap::Parser;
3
4mod colors;
5mod i18n;
6mod interface;
7mod intervals;
8mod lines;
9mod rw_data;
10mod search;
11mod settings;
12mod status_bar_text;
13mod troubleshoot;
14mod units;
15mod vehicles;
16
17// TODO: make them resources instead of stuff in the app
18struct PaiagramApp {
19    bevy_app: App,
20}
21
22impl PaiagramApp {
23    fn new(_cc: &eframe::CreationContext) -> Self {
24        let mut app = App::new();
25        app.add_plugins(MinimalPlugins);
26        app.add_plugins(LogPlugin::default());
27        app.add_plugins((
28            interface::InterfacePlugin,
29            intervals::IntervalsPlugin,
30            rw_data::RwDataPlugin,
31            search::SearchPlugin,
32            settings::SettingsPlugin,
33            vehicles::VehiclesPlugin,
34            lines::LinesPlugin,
35            troubleshoot::TroubleShootPlugin,
36        ));
37        let args = Cli::parse();
38        app.insert_resource(args);
39        app.add_systems(Startup, handle_args);
40        Self { bevy_app: app }
41    }
42}
43
44impl eframe::App for PaiagramApp {
45    fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
46        self.bevy_app
47            .world_mut()
48            .resource_mut::<interface::MiscUiState>()
49            .on_new_frame(ctx.input(|i| i.time), frame.info().cpu_usage);
50        self.bevy_app.update();
51        if let Err(e) = interface::show_ui(self, ctx) {
52            error!("UI Error: {:?}", e);
53        }
54    }
55}
56
57#[derive(Parser, Resource)]
58#[command(version, about, long_about = None)]
59struct Cli {
60    #[arg(
61        short = 'o',
62        long = "open",
63        help = "Path to a .paiagram file (or any other compatible file formats) to open on startup"
64    )]
65    open: Option<String>,
66}
67
68fn handle_args(cli: Res<Cli>, mut msg: MessageWriter<rw_data::ModifyData>, mut commands: Commands) {
69    if let Some(path) = &cli.open {
70        use rw_data::ModifyData;
71        // match the ending of the path
72        match path.split('.').next_back() {
73            Some("paiagram") => {
74                warn!("Opening .paiagram files is not yet implemented.");
75            }
76            Some("json") | Some("pyetgr") => {
77                // read the file
78                let file_content = std::fs::read_to_string(path).expect("Failed to read file");
79                msg.write(ModifyData::LoadQETRC(file_content));
80            }
81            Some("oud2") => {
82                let file_content = std::fs::read_to_string(path).expect("Failed to read file");
83                msg.write(ModifyData::LoadOuDiaSecond(file_content));
84            }
85            _ => {
86                warn!("Unsupported file format: {}", path);
87            }
88        }
89    }
90    commands.remove_resource::<Cli>();
91}
92
93#[cfg(not(target_arch = "wasm32"))]
94fn main() -> eframe::Result<()> {
95    i18n::init();
96    let native_options = eframe::NativeOptions {
97        viewport: egui::ViewportBuilder::default()
98            .with_title("Paiagram Drawer")
99            .with_inner_size([1280.0, 720.0]),
100        ..default()
101    };
102
103    eframe::run_native(
104        "Paiagram Drawer",
105        native_options,
106        Box::new(|cc| Ok(Box::new(PaiagramApp::new(cc)))),
107    )
108}
109
110#[cfg(target_arch = "wasm32")]
111use wasm_bindgen::prelude::*;
112
113#[cfg(target_arch = "wasm32")]
114#[derive(Clone)]
115#[wasm_bindgen]
116pub struct WebHandle {
117    runner: eframe::WebRunner,
118}
119
120// When compiling to web using trunk:
121#[cfg(target_arch = "wasm32")]
122fn main() {
123    i18n::init();
124    use eframe::wasm_bindgen::JsCast as _;
125    use eframe::web_sys;
126
127    let web_options = eframe::WebOptions::default();
128
129    wasm_bindgen_futures::spawn_local(async {
130        let document = web_sys::window()
131            .expect("No window")
132            .document()
133            .expect("No document");
134
135        let canvas = if let Some(canvas) = document.get_element_by_id("paiagram_canvas") {
136            canvas
137                .dyn_into::<web_sys::HtmlCanvasElement>()
138                .expect("paiagram_canvas was not a HtmlCanvasElement")
139        } else {
140            let canvas = document
141                .create_element("canvas")
142                .expect("Failed to create canvas element");
143            canvas.set_id("paiagram_canvas");
144
145            // Set styles to ensure full screen and correct rendering
146            canvas
147                .set_attribute("style", "display: block; width: 100%; height: 100%;")
148                .ok();
149
150            let body = document.body().expect("Failed to get document body");
151            body.set_attribute(
152                "style",
153                "margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden;",
154            )
155            .ok();
156
157            let html = document.document_element().expect("No document element");
158            html.set_attribute(
159                "style",
160                "margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden;",
161            )
162            .ok();
163
164            body.append_child(&canvas).expect("Failed to append canvas");
165            canvas
166                .dyn_into::<web_sys::HtmlCanvasElement>()
167                .expect("Failed to cast canvas")
168        };
169
170        let start_result = eframe::WebRunner::new()
171            .start(
172                canvas,
173                web_options,
174                Box::new(|cc| Ok(Box::new(PaiagramApp::new(cc)))),
175            )
176            .await;
177
178        // Remove the loading text and spinner:
179        if let Some(loading_text) = document.get_element_by_id("loading_text") {
180            match start_result {
181                Ok(_) => {
182                    loading_text.remove();
183                }
184                Err(e) => {
185                    loading_text.set_inner_html(
186                        "<p> The app has crashed. See the developer console for details. </p>",
187                    );
188                    panic!("Failed to start eframe: {e:?}");
189                }
190            }
191        }
192    });
193}