diff --git a/dash-frontend/src/util/networking/image_fetch.rs b/dash-frontend/src/util/networking/image_fetch.rs index 38c78348..75d7c892 100644 --- a/dash-frontend/src/util/networking/image_fetch.rs +++ b/dash-frontend/src/util/networking/image_fetch.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use wgui::{globals::WguiGlobals, renderer_vk::text::custom_glyph::CustomGlyphData}; use wlx_common::async_executor::AsyncExecutor; @@ -7,8 +9,8 @@ pub async fn fetch_to_glyph_data( globals: &WguiGlobals, executor: &AsyncExecutor, url: &str, -) -> anyhow::Result { +) -> anyhow::Result<(CustomGlyphData, Rc>)> { let res = http_client::get_simple(executor, url).await?; let glyph_data = CustomGlyphData::from_bytes_raster(globals, url, &res.data)?; - Ok(glyph_data) + Ok((glyph_data, Rc::new(res.data))) } diff --git a/dash-frontend/src/util/networking/skymap_catalog.rs b/dash-frontend/src/util/networking/skymap_catalog.rs index 545da274..577b1f64 100644 --- a/dash-frontend/src/util/networking/skymap_catalog.rs +++ b/dash-frontend/src/util/networking/skymap_catalog.rs @@ -2,14 +2,14 @@ use std::path::PathBuf; // TODO: Remove later -use serde::Deserialize; -use wlx_common::async_executor::AsyncExecutor; +use serde::{Deserialize, Serialize}; +use wlx_common::{async_executor::AsyncExecutor, config_io}; use crate::util::networking::{self, WAYVR_SKYMAPS_ROOT, http_client}; pub type SkymapUuid = uuid::Uuid; -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Serialize, Deserialize, Debug)] pub enum SkymapResolution { Res2k, Res4k, @@ -26,7 +26,7 @@ impl SkymapResolution { } } -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct SkymapCatalogEntryFiles { pub size_8k: Option, // "my_skymap_8k.png" pub size_4k: Option, // "my_skymap_4k.png" @@ -63,7 +63,7 @@ impl SkymapCatalogEntryFiles { } } -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct SkymapCatalogEntry { pub uuid: SkymapUuid, pub created_at: String, @@ -75,6 +75,34 @@ pub struct SkymapCatalogEntry { pub files: SkymapCatalogEntryFiles, } +impl SkymapCatalogEntry { + pub fn get_destination_path(&self, resolution: SkymapResolution) -> Option { + let Some(filename) = self.files.get_filename_from_res(resolution) else { + return None; + }; + + Some(config_io::get_skymaps_root().join(filename)) + } + + pub fn get_destination_metadata_path(&self) -> PathBuf { + config_io::get_skymaps_root().join(format!("{}.json", self.uuid)) + } + + pub fn is_downloaded(&self, resolution: SkymapResolution) -> anyhow::Result { + let Some(full_path) = self.get_destination_path(resolution) else { + return Ok(false); + }; + + Ok(std::fs::exists(full_path)?) + } + + pub fn save_metadata(&self) -> anyhow::Result<()> { + let json = serde_json::to_string_pretty(self)?; + std::fs::write(self.get_destination_metadata_path(), json)?; + Ok(()) + } +} + #[derive(Clone, Debug, Deserialize)] pub struct SkymapCatalog { pub version: u32, @@ -104,3 +132,21 @@ pub async fn request_catalog(executor: &AsyncExecutor) -> anyhow::Result anyhow::Result> { + let mut entries = Vec::::new(); + + let skymaps_root = config_io::get_skymaps_root(); + + for uuid in config_io::get_skymaps_uuids().unwrap_or_default() { + let metadata_path = skymaps_root.join(format!("{}.json", uuid)); + let Ok(data) = std::fs::read_to_string(metadata_path) else { + continue; + }; + + let entry = serde_json::from_str::(&data)?; + entries.push(entry); + } + + Ok(entries) +} diff --git a/dash-frontend/src/views/download_file.rs b/dash-frontend/src/views/download_file.rs index d3740fec..30b299ff 100644 --- a/dash-frontend/src/views/download_file.rs +++ b/dash-frontend/src/views/download_file.rs @@ -21,14 +21,12 @@ use wgui::{ }; use wlx_common::async_executor::AsyncExecutor; -pub struct Params<'a> { - pub globals: &'a WguiGlobals, - pub layout: &'a mut Layout, - pub executor: &'a AsyncExecutor, - pub parent_id: WidgetID, +pub struct Params { + pub globals: WguiGlobals, + pub executor: AsyncExecutor, pub target_path: PathBuf, pub url: String, - pub on_close_request: Box, + pub on_downloaded: Box, } #[derive(Clone)] @@ -41,7 +39,6 @@ enum Task { } pub struct View { - id_parent: WidgetID, globals: WguiGlobals, tasks: Tasks, executor: AsyncExecutor, @@ -53,9 +50,10 @@ pub struct View { id_loading_parent: WidgetID, id_content: WidgetID, on_close_request: Option>, + on_downloaded: Option>, } -fn doc_params(globals: &WguiGlobals) -> ParseDocumentParams { +fn doc_params(globals: &WguiGlobals) -> ParseDocumentParams<'_> { ParseDocumentParams { globals: globals.clone(), path: AssetPath::BuiltIn("gui/view/download_file.xml"), @@ -68,10 +66,18 @@ impl ViewTrait for View { for task in self.tasks.drain() { match task { Task::StartDownload(url, path) => { - self - .executor - .spawn(View::download(self.tasks.clone(), self.executor.clone(), url, path)) - .detach(); + if let Some(on_downloaded) = self.on_downloaded.take() { + self + .executor + .spawn(View::download( + self.tasks.clone(), + self.executor.clone(), + url, + path, + on_downloaded, + )) + .detach(); + } } Task::SetStatusText(text) => { let widgets = &mut par.layout.state.widgets; @@ -137,28 +143,31 @@ where } impl View { - pub fn new(par: Params) -> anyhow::Result { + pub fn new( + layout: &mut Layout, + id_parent: WidgetID, + on_close_request: Box, + par: Params, + ) -> anyhow::Result { let tasks = Tasks::::new(); - let parser_state = wgui::parser::parse_from_assets(&doc_params(&par.globals), par.layout, par.parent_id)?; + let parser_state = wgui::parser::parse_from_assets(&doc_params(&par.globals), layout, id_parent)?; let id_label_status = parser_state.get_widget_id("label_status")?; let id_content = parser_state.get_widget_id("content")?; let id_loading_parent = parser_state.get_widget_id("loading_parent")?; wgui_simple::create_loading(wgui_simple::CreateLoadingParams { parent_id: id_loading_parent, - layout: par.layout, + layout: layout, with_text: false, })?; let str_target_path = par.globals.i18n().translate("TARGET_PATH"); { - let label_target_path = parser_state - .fetch_widget(&par.layout.state, "label_target_path")? - .widget; + let label_target_path = parser_state.fetch_widget(&layout.state, "label_target_path")?.widget; label_target_path.cast::()?.set_text( - &mut par.layout.common(), + &mut layout.common(), Translation::from_raw_text_string(format!("{}: {}", str_target_path, par.target_path.display())), ); } @@ -166,7 +175,6 @@ impl View { tasks.push(Task::StartDownload(par.url, par.target_path)); Ok(Self { - id_parent: par.parent_id, tasks, globals: par.globals.clone(), executor: par.executor.clone(), @@ -174,11 +182,18 @@ impl View { id_label_status, id_loading_parent, id_content, - on_close_request: Some(par.on_close_request), + on_close_request: Some(on_close_request), + on_downloaded: Some(par.on_downloaded), }) } - async fn download(tasks: Tasks, executor: AsyncExecutor, url: String, target_path: PathBuf) -> Option<()> { + async fn download( + tasks: Tasks, + executor: AsyncExecutor, + url: String, + target_path: PathBuf, + on_downloaded: Box, + ) -> Option<()> { tasks.push(Task::SetStatusText(String::from("Connecting to the server..."))); // start downloading from the server with progress reporting @@ -223,18 +238,17 @@ impl View { tasks.push(Task::SetStatusText(String::from("Download finished"))); tasks.push(Task::ShowIconSuccess); + on_downloaded(); + None } } pub fn mount_popup( - frontend_tasks: FrontendTasks, - executor: AsyncExecutor, - globals: WguiGlobals, popup: PopupHolder, - target_path: PathBuf, - url: String, + frontend_tasks: FrontendTasks, on_view_close: Box, + params: Params, ) { frontend_tasks .clone() @@ -242,15 +256,7 @@ pub fn mount_popup( Translation::from_translation_key("DOWNLOADER"), Box::new(move |data| { let on_close_request = popup.get_close_callback(data.layout); - let view = View::new(Params { - globals: &globals, - layout: data.layout, - executor: &executor, - parent_id: data.id_content, - on_close_request, - target_path, - url, - })?; + let view = View::new(data.layout, data.id_content, on_close_request, params)?; popup.set_view(data.handle, view, Some(on_view_close)); Ok(popup.get_close_callback(data.layout)) diff --git a/dash-frontend/src/views/remote_skymap_downloader.rs b/dash-frontend/src/views/remote_skymap_downloader.rs index 8ff5cdd4..61573c62 100644 --- a/dash-frontend/src/views/remote_skymap_downloader.rs +++ b/dash-frontend/src/views/remote_skymap_downloader.rs @@ -1,12 +1,9 @@ -use std::{collections::HashMap, path::PathBuf, rc::Rc}; +use std::{collections::HashMap, rc::Rc}; use crate::{ frontend::{FrontendTask, FrontendTasks}, util::{ - networking::{ - self, - skymap_catalog::{SkymapCatalogEntry, SkymapResolution}, - }, + networking::{self, skymap_catalog::SkymapResolution}, popup_manager::{MountPopupOnceParams, PopupHolder}, }, views::{self, ViewTrait, ViewUpdateParams}, @@ -32,18 +29,19 @@ pub struct Params<'a> { pub frontend_tasks: FrontendTasks, pub parent_id: WidgetID, pub entry: networking::skymap_catalog::SkymapCatalogEntry, - pub on_close_request: Box, - pub preview_image: Option, + pub preview_image: CustomGlyphData, + pub preview_image_compressed: Rc>, + pub on_updated_library: Rc, } #[derive(Clone)] enum Task { Refresh, ResolutionClicked(networking::skymap_catalog::SkymapResolution), + DownloadFinished, } pub struct View { - id_parent: WidgetID, entry: networking::skymap_catalog::SkymapCatalogEntry, frontend_tasks: FrontendTasks, globals: WguiGlobals, @@ -56,6 +54,8 @@ pub struct View { parser_state: ParserState, popup_download: PopupHolder, + preview_image_compressed: Rc>, + on_updated_library: Rc, } fn mount_resolution_button( @@ -96,6 +96,9 @@ impl ViewTrait for View { Task::Refresh => { self.refresh(par.layout)?; } + Task::DownloadFinished => { + self.download_finished()?; + } } } @@ -104,23 +107,7 @@ impl ViewTrait for View { } } -fn get_skymap_resolution_full_path(entry: &SkymapCatalogEntry, resolution: SkymapResolution) -> Option { - let Some(filename) = entry.files.get_filename_from_res(resolution) else { - return None; - }; - - Some(config_io::get_skymaps_root().join(filename)) -} - -fn is_downloaded(entry: &SkymapCatalogEntry, resolution: SkymapResolution) -> anyhow::Result { - let Some(full_path) = get_skymap_resolution_full_path(entry, resolution) else { - return Ok(false); - }; - - Ok(std::fs::exists(full_path)?) -} - -fn doc_params(globals: &WguiGlobals) -> ParseDocumentParams { +fn doc_params(globals: &WguiGlobals) -> ParseDocumentParams<'_> { ParseDocumentParams { globals: globals.clone(), path: AssetPath::BuiltIn("gui/view/remote_skymap_downloader.xml"), @@ -132,7 +119,7 @@ impl View { pub fn new(par: Params) -> anyhow::Result { let tasks = Tasks::::new(); - let mut parser_state = wgui::parser::parse_from_assets(&doc_params(&par.globals), par.layout, par.parent_id)?; + let parser_state = wgui::parser::parse_from_assets(&doc_params(&par.globals), par.layout, par.parent_id)?; let id_resolution_buttons = parser_state.get_widget_id("resolution_buttons")?; let str_version = par.globals.i18n().translate("VERSION"); @@ -141,7 +128,7 @@ impl View { let image = parser_state.fetch_widget(&par.layout.state, "image")?.widget; let mut image = image.cast::()?; - image.set_content(&mut par.layout.alterables, par.preview_image); + image.set_content(&mut par.layout.alterables, Some(par.preview_image)); // Set author label parser_state @@ -186,7 +173,6 @@ impl View { tasks.push(Task::Refresh); Ok(Self { - id_parent: par.parent_id, tasks, globals: par.globals.clone(), executor: par.executor.clone(), @@ -195,6 +181,8 @@ impl View { frontend_tasks: par.frontend_tasks, popup_download: Default::default(), id_resolution_buttons, + preview_image_compressed: par.preview_image_compressed, + on_updated_library: par.on_updated_library, }) } @@ -210,7 +198,7 @@ impl View { self.id_resolution_buttons, res, &self.tasks, - is_downloaded(&self.entry, res)?, + self.entry.is_downloaded(res)?, ) }; @@ -224,23 +212,44 @@ impl View { Ok(()) } + fn download_finished(&mut self) -> anyhow::Result<()> { + self.entry.save_metadata()?; + let mut uuids = config_io::get_skymaps_uuids().unwrap_or_default(); + let uuid_str = self.entry.uuid.to_string(); + if !uuids.contains(&uuid_str) { + uuids.push(uuid_str); + } + config_io::set_skymaps_uuids(&uuids)?; + + // Save preview image + let preview_path = config_io::get_skymaps_root().join(&self.entry.files.preview); + std::fs::write(preview_path, self.preview_image_compressed.as_ref())?; + + (*self.on_updated_library)(); + + Ok(()) + } + fn run_download(&mut self, resolution: SkymapResolution) -> anyhow::Result<()> { let Some(url) = self.entry.files.get_url_from_res(resolution) else { return Ok(()); }; - let Some(full_path) = get_skymap_resolution_full_path(&self.entry, resolution) else { + let Some(target_path) = self.entry.get_destination_path(resolution) else { return Ok(()); }; views::download_file::mount_popup( - self.frontend_tasks.clone(), - self.executor.clone(), - self.globals.clone(), self.popup_download.clone(), - full_path, - url, + self.frontend_tasks.clone(), self.tasks.make_callback_box(Task::Refresh), + views::download_file::Params { + globals: self.globals.clone(), + executor: self.executor.clone(), + target_path, + url, + on_downloaded: self.tasks.make_callback_box(Task::DownloadFinished), + }, ); Ok(()) } @@ -251,7 +260,9 @@ pub fn mount_popup( executor: AsyncExecutor, globals: WguiGlobals, entry: networking::skymap_catalog::SkymapCatalogEntry, - preview_image: Option, + preview_image: CustomGlyphData, + preview_image_compressed: Rc>, + on_updated_library: Rc, popup: PopupHolder, ) { frontend_tasks @@ -259,16 +270,16 @@ pub fn mount_popup( .push(FrontendTask::MountPopupOnce(MountPopupOnceParams::new( Translation::from_raw_text(&entry.name), Box::new(move |data| { - let on_close_request = popup.get_close_callback(data.layout); let view = View::new(Params { globals: &globals, layout: data.layout, executor: &executor, parent_id: data.id_content, entry, - on_close_request, preview_image, frontend_tasks: frontend_tasks.clone(), + preview_image_compressed, + on_updated_library, })?; popup.set_view(data.handle, view, None); diff --git a/dash-frontend/src/views/remote_skymap_list.rs b/dash-frontend/src/views/remote_skymap_list.rs index 6aacb7a0..731e1d52 100644 --- a/dash-frontend/src/views/remote_skymap_list.rs +++ b/dash-frontend/src/views/remote_skymap_list.rs @@ -29,25 +29,33 @@ pub struct Params<'a> { pub layout: &'a mut Layout, pub executor: &'a AsyncExecutor, pub parent_id: WidgetID, - pub on_close_request: Box, pub frontend_tasks: FrontendTasks, + pub on_updated_library: Rc, } #[derive(Clone)] enum Task { SetSkymapCatalog(Rc>), - SetSkymapPreview((SkymapUuid, Option)), + SetSkymapPreview( + ( + SkymapUuid, + Option<( + CustomGlyphData, /* ready-to-use preview image data */ + Rc>, /* compressed preview image data (should weigh about 10-15 KiB) */ + )>, + ), + ), ShowRemoteSkymapDownloader(SkymapUuid), } struct MountedCell { skymap_uuid: SkymapUuid, view: views::skymap_list_cell::View, + preview_image_compressed: Option>>, } pub struct View { id_parent: WidgetID, - id_list: WidgetID, id_loading: WidgetID, globals: WguiGlobals, tasks: Tasks, @@ -56,6 +64,7 @@ pub struct View { frontend_tasks: FrontendTasks, catalog: Option, popup_remote_skymap_downloader: PopupHolder, + on_updated_library: Rc, } impl ViewTrait for View { @@ -77,18 +86,26 @@ impl ViewTrait for View { )?, } } - Task::SetSkymapPreview((skymap_uuid, glyph_data)) => { + Task::SetSkymapPreview((skymap_uuid, opt_preview_image)) => { if let Some(cell) = &mut self .mounted_cells .iter_mut() .find(|cell| cell.skymap_uuid == skymap_uuid) { - cell.view.set_image(par.layout, glyph_data)?; + 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) => { - let preview_image = self.get_image_preview(skymap_uuid); - self.show_remote_skymap_downloader(skymap_uuid, preview_image)?; + 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"); + } } } } @@ -113,7 +130,6 @@ impl View { par.executor.spawn(fut).detach(); Ok(Self { id_parent: par.parent_id, - id_list: WidgetID::default(), id_loading, tasks, globals: par.globals.clone(), @@ -122,6 +138,7 @@ impl View { frontend_tasks: par.frontend_tasks, catalog: None, popup_remote_skymap_downloader: Default::default(), + on_updated_library: par.on_updated_library, }) } @@ -131,10 +148,12 @@ impl View { entry: SkymapCatalogEntry, tasks: Tasks, ) { - let glyph_data = networking::image_fetch::fetch_to_glyph_data(&globals, &executor, &entry.files.get_url_preview()) - .await - .ok(); - tasks.push(Task::SetSkymapPreview((entry.uuid, glyph_data))); + tasks.push(Task::SetSkymapPreview(( + entry.uuid, + networking::image_fetch::fetch_to_glyph_data(&globals, &executor, &entry.files.get_url_preview()) + .await + .ok(), + ))); } fn mount_catalog( @@ -163,6 +182,7 @@ impl View { let skymap_uuid = entry.uuid.clone(); self.mounted_cells.push(MountedCell { + preview_image_compressed: None, skymap_uuid: entry.uuid.clone(), view: views::skymap_list_cell::View::new(views::skymap_list_cell::Params { id_parent: id_list, @@ -184,7 +204,8 @@ impl View { fn show_remote_skymap_downloader( &mut self, uuid: SkymapUuid, - preview_image: Option, + preview_image: CustomGlyphData, + preview_image_compressed: Rc>, ) -> anyhow::Result<()> { let Some(catalog) = &self.catalog else { debug_assert!(false); // impossible @@ -202,15 +223,27 @@ impl View { self.globals.clone(), entry.clone(), preview_image, + preview_image_compressed, + self.on_updated_library.clone(), self.popup_remote_skymap_downloader.clone(), ); Ok(()) } - fn get_image_preview(&self, skymap_uuid: SkymapUuid) -> Option { + 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) { - return cell.view.get_image(); + let Some(image) = cell.view.get_image() else { + return None; + }; + + let Some(preview_image_compressed) = cell.preview_image_compressed.clone() else { + return None; + }; + return Some((image, preview_image_compressed)); } None } @@ -220,6 +253,7 @@ pub fn mount_popup( frontend_tasks: FrontendTasks, executor: AsyncExecutor, globals: WguiGlobals, + on_updated_library: Rc, popup: PopupHolder, ) { frontend_tasks @@ -227,14 +261,13 @@ pub fn mount_popup( .push(FrontendTask::MountPopupOnce(MountPopupOnceParams::new( Translation::from_translation_key("APP_SETTINGS.DOWNLOAD_SKYMAPS"), Box::new(move |data| { - let on_close_request = popup.get_close_callback(data.layout); let view = View::new(Params { globals: &globals, layout: data.layout, executor: &executor, parent_id: data.id_content, - on_close_request, frontend_tasks, + on_updated_library, })?; popup.set_view(data.handle, view, None); diff --git a/dash-frontend/src/views/skymap_list.rs b/dash-frontend/src/views/skymap_list.rs index 466f0a5f..497bb0f9 100644 --- a/dash-frontend/src/views/skymap_list.rs +++ b/dash-frontend/src/views/skymap_list.rs @@ -1,3 +1,4 @@ +use anyhow::Context; use wgui::{ assets::AssetPath, components::button::ComponentButton, @@ -5,13 +6,18 @@ use wgui::{ i18n::Translation, layout::{Layout, WidgetID}, parser::{Fetchable, ParseDocumentParams, ParserState}, + renderer_vk::text::custom_glyph::CustomGlyphData, task::Tasks, }; use wlx_common::{async_executor::AsyncExecutor, config_io}; use crate::{ frontend::FrontendTasks, - util::{popup_manager::PopupHolder, wgui_simple}, + util::{ + networking::skymap_catalog::{self, SkymapCatalogEntry, SkymapResolution}, + popup_manager::PopupHolder, + wgui_simple, + }, views::{self, ViewTrait, ViewUpdateParams}, }; @@ -19,6 +25,7 @@ use crate::{ enum Task { DownloadSkymaps, Refresh, + SetSkymap(SkymapCatalogEntry), } pub struct Params<'a> { @@ -28,6 +35,11 @@ pub struct Params<'a> { pub frontend_tasks: &'a FrontendTasks, } +struct Cell { + #[allow(dead_code)] + view: views::skymap_list_cell::View, +} + pub struct View { #[allow(dead_code)] parser_state: ParserState, @@ -36,6 +48,7 @@ pub struct View { frontend_tasks: FrontendTasks, globals: WguiGlobals, popup_remote_skymap_list: PopupHolder, + cells: Vec, } impl ViewTrait for View { @@ -55,6 +68,9 @@ impl ViewTrait for View { Task::Refresh => { self.refresh(&mut par.layout)?; } + Task::SetSkymap(entry) => { + self.set_skymap(entry)?; + } } } } @@ -94,6 +110,7 @@ impl View { frontend_tasks: params.frontend_tasks.clone(), globals: params.globals.clone(), popup_remote_skymap_list: Default::default(), + cells: Vec::new(), }) } @@ -102,18 +119,38 @@ impl View { self.frontend_tasks.clone(), executor.clone(), self.globals.clone(), + self.tasks.make_callback_rc(Task::Refresh), /* on_updated_library */ self.popup_remote_skymap_list.clone(), ); Ok(()) } + fn set_skymap(&mut self, entry: SkymapCatalogEntry) -> anyhow::Result<()> { + let skymap_file_path = entry + .get_destination_path(SkymapResolution::Res2k /* todo: resolution selector */) + .context("Skymap not found" /* you shouldn't really see this, like ever. */)?; + + log::error!( + "not implemented (skymap path to be loaded: {})", + skymap_file_path.to_string_lossy() + ); + + Ok(()) + } + fn refresh(&mut self, layout: &mut Layout) -> anyhow::Result<()> { - let skymaps_uuids = config_io::get_skymaps_uuids().unwrap_or_default(); - log::info!("skymap uuids {:?}", skymaps_uuids); + let entries = match skymap_catalog::get_entries_from_disk() { + Ok(entries) => entries, + Err(e) => { + log::error!("failed to get skymap entries: {}", e); + Default::default() + } + }; layout.remove_children(self.list_parent); + self.cells.clear(); - if skymaps_uuids.is_empty() { + if entries.is_empty() { wgui_simple::create_label( layout, self.list_parent, @@ -122,6 +159,26 @@ impl View { return Ok(()); } + let skymaps_root = config_io::get_skymaps_root(); + + for entry in &entries { + let mut view = views::skymap_list_cell::View::new(views::skymap_list_cell::Params { + id_parent: self.list_parent, + layout, + entry: entry.clone(), + on_click: self.tasks.get_button_click_callback(Task::SetSkymap(entry.clone())), + })?; + + // load preview image + if let Ok(data) = std::fs::read(skymaps_root.join(&entry.files.preview)) { + if let Ok(glyph_data) = CustomGlyphData::from_bytes_raster(&self.globals, &entry.files.preview, &data) { + view.set_image(layout, Some(glyph_data))?; + } + } + + self.cells.push(Cell { view }); + } + Ok(()) } }