use std::rc::Rc; use uuid::Uuid; use wgui::{ assets::AssetPath, globals::WguiGlobals, i18n::Translation, layout::{Layout, WidgetID}, parser::{Fetchable, ParseDocumentParams}, renderer_vk::text::custom_glyph::CustomGlyphData, task::Tasks, }; use wlx_common::async_executor::AsyncExecutor; use crate::{ frontend::{FrontendTask, FrontendTasks}, util::{ networking::{ self, skymap_catalog::{SkymapCatalog, SkymapCatalogEntry, SkymapUuid}, }, popup_manager::{MountPopupOnceParams, PopupHolder}, wgui_simple, }, views::{self, ViewTrait, ViewUpdateParams}, }; pub struct Params<'a> { pub globals: &'a WguiGlobals, pub layout: &'a mut Layout, pub executor: &'a AsyncExecutor, pub parent_id: WidgetID, pub frontend_tasks: FrontendTasks, pub on_updated_library: Rc, } type SetSkymapPreview = (Uuid, Option<(CustomGlyphData, Rc>)>); #[derive(Clone)] enum Task { SetSkymapCatalog(Rc>), SetSkymapPreview(SetSkymapPreview), ShowRemoteSkymapDownloader(SkymapUuid), RefreshCells, } struct MountedCell { skymap_uuid: SkymapUuid, view: views::skymap_list_cell::View, preview_image_compressed: Option>>, } pub struct View { id_parent: WidgetID, id_loading: WidgetID, globals: WguiGlobals, tasks: Tasks, mounted_cells: Vec, executor: AsyncExecutor, frontend_tasks: FrontendTasks, catalog: Option, popup_remote_skymap_downloader: PopupHolder, on_updated_library: Rc, } fn get_entry_by_uuid(catalog: &SkymapCatalog, skymap_uuid: Uuid) -> Option<&SkymapCatalogEntry> { let entry = catalog.entries.iter().find(|entry| entry.uuid == skymap_uuid)?; Some(entry) } impl ViewTrait for View { fn update(&mut self, par: &mut ViewUpdateParams) -> anyhow::Result<()> { self.popup_remote_skymap_downloader.update(par)?; for task in self.tasks.drain() { match task { Task::SetSkymapCatalog(skymap_catalog) => { par.layout.remove_widget(self.id_loading); match &*skymap_catalog { Ok(skymap_catalog) => { self.mount_catalog(par.layout, skymap_catalog)?; } Err(e) => wgui_simple::create_label_error( par.layout, self.id_parent, format!("Failed to fetch skymap catalog: {:?}", e), )?, } } Task::SetSkymapPreview((skymap_uuid, opt_preview_image)) => { if let Some(cell) = &mut self .mounted_cells .iter_mut() .find(|cell| cell.skymap_uuid == skymap_uuid) { if let Some((preview_image, preview_image_compressed)) = opt_preview_image { cell.view.set_image(par.layout, Some(preview_image))?; cell.preview_image_compressed = Some(preview_image_compressed); } else { cell.view.set_image(par.layout, None)?; } } } Task::ShowRemoteSkymapDownloader(skymap_uuid) => { if let Some((preview_image, preview_image_compressed)) = self.get_image_preview(skymap_uuid) { self.show_remote_skymap_downloader(skymap_uuid, preview_image, preview_image_compressed)?; } else { log::error!("preview image not present, ignoring request"); } } Task::RefreshCells => { self.refresh_cells(par.layout)?; } } } Ok(()) } } impl View { async fn skymap_catalog_request_wrapper(tasks: Tasks, executor: AsyncExecutor) { let res = networking::skymap_catalog::request_catalog(&executor).await; tasks.push(Task::SetSkymapCatalog(Rc::new(res))) } pub fn new(par: Params) -> anyhow::Result { let id_loading = wgui_simple::create_loading(wgui_simple::CreateLoadingParams { layout: par.layout, parent_id: par.parent_id, with_text: true, })?; let tasks = Tasks::::new(); let fut = View::skymap_catalog_request_wrapper(tasks.clone(), par.executor.clone()); par.executor.spawn(fut).detach(); Ok(Self { id_parent: par.parent_id, id_loading, tasks, globals: par.globals.clone(), mounted_cells: Vec::new(), executor: par.executor.clone(), frontend_tasks: par.frontend_tasks, catalog: None, popup_remote_skymap_downloader: Default::default(), on_updated_library: par.on_updated_library, }) } async fn request_skymap_preview( globals: WguiGlobals, executor: AsyncExecutor, entry: SkymapCatalogEntry, tasks: Tasks, ) { tasks.push(Task::SetSkymapPreview(( entry.uuid, networking::image_fetch::fetch_to_glyph_data(&globals, &executor, &entry.files.get_url_preview()) .await .ok(), ))); } fn refresh_cells(&mut self, layout: &mut Layout) -> anyhow::Result<()> { let Some(catalog) = &self.catalog else { debug_assert!(false); return Ok(()); }; for cell in &mut self.mounted_cells { if let Some(entry) = get_entry_by_uuid(catalog, cell.skymap_uuid) { cell.view.refresh_resolution_pips(layout, entry)?; } } Ok(()) } fn mount_catalog( &mut self, layout: &mut Layout, catalog: &networking::skymap_catalog::SkymapCatalog, ) -> anyhow::Result<()> { let doc_params = &ParseDocumentParams { globals: self.globals.clone(), path: AssetPath::BuiltIn("gui/view/remote_skymap_list.xml"), extra: Default::default(), }; let parser_state = wgui::parser::parse_from_assets(doc_params, layout, self.id_parent)?; let id_list = parser_state.fetch_widget(&layout.state, "list")?.id; for entry in &catalog.entries { let task = View::request_skymap_preview( self.globals.clone(), self.executor.clone(), entry.clone(), self.tasks.clone(), ); let skymap_uuid = entry.uuid; self.mounted_cells.push(MountedCell { preview_image_compressed: None, skymap_uuid: entry.uuid, view: views::skymap_list_cell::View::new(views::skymap_list_cell::Params { id_parent: id_list, layout, entry: Some(entry.clone()), on_click: self .tasks .get_button_click_callback(Task::ShowRemoteSkymapDownloader(skymap_uuid)), })?, }); self.executor.spawn(task).detach(); } self.catalog = Some(catalog.clone()); Ok(()) } fn show_remote_skymap_downloader( &mut self, uuid: SkymapUuid, preview_image: CustomGlyphData, preview_image_compressed: Rc>, ) -> anyhow::Result<()> { let Some(catalog) = &self.catalog else { debug_assert!(false); // impossible return Ok(()); }; let Some(entry) = get_entry_by_uuid(catalog, uuid) else { return Ok(()); }; // call our task before calling underlying on_updated_library callback let on_updated_library = Rc::new({ let func = self.on_updated_library.clone(); let tasks = self.tasks.clone(); move || { tasks.push(Task::RefreshCells); (*func)(); } }); views::remote_skymap_downloader::mount_popup( self.frontend_tasks.clone(), self.executor.clone(), self.globals.clone(), entry.clone(), preview_image, preview_image_compressed, on_updated_library, self.popup_remote_skymap_downloader.clone(), ); Ok(()) } fn get_image_preview( &self, skymap_uuid: SkymapUuid, ) -> Option<(CustomGlyphData, Rc> /* preview_image_compressed */)> { if let Some(cell) = &self.mounted_cells.iter().find(|mc| mc.skymap_uuid == skymap_uuid) { let image = cell.view.get_image()?; let preview_image_compressed = cell.preview_image_compressed.clone()?; return Some((image, preview_image_compressed)); } None } } pub fn mount_popup( frontend_tasks: FrontendTasks, executor: AsyncExecutor, globals: WguiGlobals, on_updated_library: Rc, popup: PopupHolder, ) { frontend_tasks .clone() .push(FrontendTask::MountPopupOnce(MountPopupOnceParams::new( Translation::from_translation_key("APP_SETTINGS.BROWSE_ONLINE_CATALOG"), Box::new(move |data| { let view = View::new(Params { globals: &globals, layout: data.layout, executor: &executor, parent_id: data.id_content, frontend_tasks, on_updated_library, })?; popup.set_view(data.handle, view, None); Ok(popup.get_close_callback(data.layout)) }), Default::default(), /* extra */ ))); }