Skip to main content

launcher/pages/snowboards/
mod.rs

1use std::collections::HashMap;
2
3use crate::utils::texture_loader::load_texture_robust;
4use crate::widgets::gallery::{GalleryConfig, GalleryState, gallery_grid};
5use crate::widgets::tile::gallery_tile;
6use supreme_boards::BoardConfig;
7
8mod popup;
9use popup::show_board_config_popup;
10
11#[derive(Default)]
12pub struct SnowboardsPageState {
13    pub gallery: GalleryState,
14    pub board_configs: HashMap<String, BoardConfig>,
15
16    // Lightweight initialization tracking
17    is_initialized: bool,
18    init_error: Option<String>,
19}
20
21pub fn snowboards_page(
22    ui: &mut egui::Ui,
23    storage: &supreme_fs::GameStorage,
24    state: &mut SnowboardsPageState,
25) {
26    // =========================================================================
27    // 1. Page-Driven Initialization (Runs exactly once per session, or on retry)
28    // =========================================================================
29    if !state.is_initialized {
30        state.is_initialized = true;
31        let game_path = storage.game_path();
32        let boards_dir = game_path.join("Data/Boards");
33
34        // Synchronous call is fine here since the original UI freeze was
35        // caused by the nav button hover bug, not this function.
36        if let Err(e) = supreme_boards::generate_all_boards_files(&boards_dir, game_path) {
37            state.init_error = Some(format!("Failed to generate board files: {e}"));
38        }
39    }
40
41    // =========================================================================
42    // 2. Handle Initialization Errors (Early Return)
43    // =========================================================================
44    if let Some(err) = state.init_error.clone() {
45        ui.centered_and_justified(|ui| {
46            ui.vertical_centered(|ui| {
47                ui.colored_label(egui::Color32::RED, &err);
48                ui.add_space(8.0);
49                if ui.button("Retry").clicked() {
50                    state.is_initialized = false;
51                    state.init_error = None;
52                }
53            });
54        });
55        return;
56    }
57
58    // =========================================================================
59    // 3. Normal Page Rendering
60    // =========================================================================
61    let game_path = storage.game_path();
62    let boards_dir = game_path.join("Data/Boards");
63
64    if !boards_dir.exists() {
65        ui.colored_label(egui::Color32::RED, "Boards directory not found!");
66        return;
67    }
68
69    let board_dirs = supreme_boards::get_valid_board_directories(&boards_dir);
70    let available_boards = supreme_boards::get_available_boards(game_path);
71
72    gallery_grid(
73        ui,
74        GalleryConfig {
75            id: "snowboards_grid",
76            title: "Snowboards",
77            info_text: Some(
78                "To create a new snowboard: duplicate an original board folder, then edit the texture files. Ensure the .txt files inside are edited to match the new directory you created.",
79            ),
80            open_folder_path: Some(&boards_dir),
81        },
82        &mut state.gallery,
83        &board_dirs,
84        |_| {},
85        |ui, board_dir, tile_size, gallery_state| {
86            if let Some(board_name) = board_dir.file_name().and_then(|n| n.to_str()) {
87                let mut is_available = available_boards.contains(board_name);
88
89                // Resolve texture
90                let texture_key = board_name.to_string();
91                if !gallery_state.textures.contains_key(&texture_key)
92                    && let Some(texture_path) = supreme_boards::get_board_texture_path(board_dir)
93                {
94                    let full_texture_path = game_path.join(&texture_path);
95                    if let Ok(texture) =
96                        load_texture_robust(ui.ctx(), &full_texture_path, &texture_key)
97                    {
98                        gallery_state.textures.insert(texture_key.clone(), texture);
99                    }
100                }
101
102                let response = gallery_tile(
103                    ui,
104                    tile_size,
105                    2.0,
106                    board_name,
107                    |ui| {
108                        ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
109                            let response = ui.checkbox(&mut is_available, "");
110                            if response.changed()
111                                && let Err(e) = supreme_boards::set_board_available(
112                                    game_path,
113                                    board_name,
114                                    is_available,
115                                )
116                            {
117                                eprintln!("Error updating availability: {e}");
118                            }
119                        });
120                    },
121                    gallery_state.textures.get(&texture_key),
122                );
123
124                if response.clicked() {
125                    gallery_state.selected_item = Some(board_dir.clone());
126
127                    // Load config for popup
128                    if !state.board_configs.contains_key(board_name) {
129                        let config_path = board_dir.join("menu_board_cnf.txt");
130                        if let Ok(config) = supreme_boards::parse_board_config(&config_path) {
131                            state.board_configs.insert(board_name.to_string(), config);
132                        }
133                    }
134                }
135            }
136        },
137    );
138
139    // Configuration popup window
140    if let Some(selected_board_path) = state.gallery.selected_item.clone() {
141        show_board_config_popup(ui.ctx(), &selected_board_path, game_path, state);
142    }
143}