diff --git a/dash-frontend/assets/gui/view/download_file.xml b/dash-frontend/assets/gui/view/download_file.xml new file mode 100644 index 00000000..a385c4d7 --- /dev/null +++ b/dash-frontend/assets/gui/view/download_file.xml @@ -0,0 +1,11 @@ + + +
+
+
+
+
+
diff --git a/dash-frontend/assets/lang/en.json b/dash-frontend/assets/lang/en.json index 98212c50..9497b114 100644 --- a/dash-frontend/assets/lang/en.json +++ b/dash-frontend/assets/lang/en.json @@ -142,6 +142,8 @@ "CREATION_DATE": "Creation date", "DEBUG_INFO": "Debug info", "DISPLAY_BRIGHTNESS": "Display brightness", + "DOWNLOADER": "Downloader", + "DOWNLOADING_FILE": "Downloading file...", "FAILED_TO_LAUNCH_APPLICATION": "Failed to launch a application:", "GAME_LAUNCHED": "Game launched", "GAME_LIST": { @@ -170,6 +172,7 @@ "REMOVE": "Remove", "SETTINGS": "Settings", "SHOW": "Show", + "TARGET_PATH": "Target path", "TERMINATE_PROCESS": "Terminate process", "VERSION": "Version", "WIDTH": "Width" diff --git a/dash-frontend/src/tab/games.rs b/dash-frontend/src/tab/games.rs index fcdc3cad..02082ef7 100644 --- a/dash-frontend/src/tab/games.rs +++ b/dash-frontend/src/tab/games.rs @@ -10,7 +10,7 @@ use crate::{ frontend::Frontend, tab::{Tab, TabType}, util::steam_utils::SteamUtils, - views::{game_list, running_games_list}, + views::{ViewTrait, ViewUpdateParams, game_list, running_games_list}, }; pub struct TabGames { @@ -19,7 +19,6 @@ pub struct TabGames { view_game_list: game_list::View, view_running_games_list: running_games_list::View, - steam_utils: SteamUtils, marker: PhantomData, } @@ -29,9 +28,11 @@ impl Tab for TabGames { } fn update(&mut self, frontend: &mut Frontend, time_ms: u32, _data: &mut T) -> anyhow::Result<()> { - self - .view_game_list - .update(&mut frontend.layout, &mut self.steam_utils, &frontend.executor)?; + self.view_game_list.update(&mut ViewUpdateParams { + layout: &mut frontend.layout, + executor: &mut frontend.executor, + })?; + self.view_running_games_list.update(&mut frontend.layout, time_ms)?; Ok(()) } @@ -54,16 +55,17 @@ impl TabGames { let game_list_parent = state.get_widget_id("game_list_parent")?; let id_running_games_list_parent = state.get_widget_id("running_games_list_parent")?; + let mut steam_utils = SteamUtils::new()?; + let view_game_list = game_list::View::new(game_list::Params { executor: frontend.executor.clone(), frontend_tasks: frontend.tasks.clone(), globals: globals.clone(), layout: &mut frontend.layout, parent_id: game_list_parent, + steam_utils: &steam_utils, })?; - let mut steam_utils = SteamUtils::new()?; - let view_running_games_list = running_games_list::View::new(running_games_list::Params { globals: globals.clone(), layout: &mut frontend.layout, @@ -77,7 +79,6 @@ impl TabGames { view_game_list, view_running_games_list, marker: PhantomData, - steam_utils, }) } } diff --git a/dash-frontend/src/tab/settings/mod.rs b/dash-frontend/src/tab/settings/mod.rs index 19df03c2..9ab2f0f4 100644 --- a/dash-frontend/src/tab/settings/mod.rs +++ b/dash-frontend/src/tab/settings/mod.rs @@ -19,13 +19,12 @@ use wgui::{ }, windowing::context_menu::{self, Blueprint, ContextMenu, TickResult}, }; -use wlx_common::{ - async_executor::AsyncExecutor, config::GeneralConfig, config_io::ConfigRoot, dash_interface::RecenterMode, -}; +use wlx_common::{config::GeneralConfig, config_io::ConfigRoot, dash_interface::RecenterMode}; use crate::{ frontend::{Frontend, FrontendTask, FrontendTasks}, tab::{Tab, TabType, settings::macros::MacroParams}, + views::ViewUpdateParams, }; mod macros; @@ -86,13 +85,8 @@ struct SettingsMountParams<'a> { parent_id: WidgetID, } -struct SettingsUpdateParams<'a> { - layout: &'a mut Layout, - executor: &'a AsyncExecutor, -} - trait SettingsTab { - fn update(&mut self, _par: SettingsUpdateParams) -> anyhow::Result<()> { + fn update(&mut self, _par: &mut ViewUpdateParams) -> anyhow::Result<()> { Ok(()) } } @@ -117,7 +111,7 @@ impl Tab for TabSettings { fn update(&mut self, frontend: &mut Frontend, _time_ms: u32, data: &mut T) -> anyhow::Result<()> { if let Some(tab) = &mut self.current_tab { - tab.update(SettingsUpdateParams { + tab.update(&mut ViewUpdateParams { layout: &mut frontend.layout, executor: &frontend.executor, })?; diff --git a/dash-frontend/src/tab/settings/tab_skybox.rs b/dash-frontend/src/tab/settings/tab_skybox.rs index 5e7dda79..a490bfcc 100644 --- a/dash-frontend/src/tab/settings/tab_skybox.rs +++ b/dash-frontend/src/tab/settings/tab_skybox.rs @@ -3,7 +3,7 @@ use crate::{ SettingType, SettingsMountParams, SettingsTab, macros::{options_category, options_checkbox}, }, - views::skymap_list, + views::{ViewTrait, ViewUpdateParams, skymap_list}, }; pub struct State { @@ -11,8 +11,8 @@ pub struct State { } impl SettingsTab for State { - fn update(&mut self, par: super::SettingsUpdateParams) -> anyhow::Result<()> { - self.skymap_list.update(par.layout, par.executor)?; + fn update(&mut self, par: &mut ViewUpdateParams) -> anyhow::Result<()> { + self.skymap_list.update(par)?; Ok(()) } } diff --git a/dash-frontend/src/util/popup_manager.rs b/dash-frontend/src/util/popup_manager.rs index 97aa7f13..75b076e5 100644 --- a/dash-frontend/src/util/popup_manager.rs +++ b/dash-frontend/src/util/popup_manager.rs @@ -16,7 +16,10 @@ use wgui::{ }; use wlx_common::config::GeneralConfig; -use crate::frontend::{FrontendTask, FrontendTasks}; +use crate::{ + frontend::{FrontendTask, FrontendTasks}, + views::{ViewTrait, ViewUpdateParams}, +}; pub struct PopupManagerParams { pub parent_id: WidgetID, @@ -45,13 +48,19 @@ pub struct PopupHandle { state: Rc>, } -struct PopupHolderState { +struct PopupHolderState +where + ViewType: ViewTrait, +{ popup_handle: PopupHandle, view: Option, } // we can't use #[derive(Default)] due to the fact that ViewType can't be Default. -impl Default for PopupHolderState { +impl Default for PopupHolderState +where + ViewType: ViewTrait, +{ fn default() -> Self { Self { popup_handle: Default::default(), @@ -60,11 +69,17 @@ impl Default for PopupHolderState { } } -pub struct PopupHolder { +pub struct PopupHolder +where + ViewType: ViewTrait, +{ state: Rc>>, } -impl Default for PopupHolder { +impl Default for PopupHolder +where + ViewType: ViewTrait, +{ fn default() -> Self { Self { state: Rc::new(RefCell::new(PopupHolderState::default())), @@ -72,7 +87,10 @@ impl Default for PopupHolder { } } -impl PopupHolderState { +impl PopupHolderState +where + ViewType: ViewTrait, +{ fn close(&mut self) { self.view = None; self.popup_handle.close(); @@ -80,7 +98,10 @@ impl PopupHolderState { } // we can't derive(Clone) due to the fact that ViewType is non-cloneable -impl Clone for PopupHolder { +impl Clone for PopupHolder +where + ViewType: ViewTrait, +{ fn clone(&self) -> Self { Self { state: self.state.clone(), @@ -88,9 +109,17 @@ impl Clone for PopupHolder { } } -impl PopupHolder { - pub fn close(&self) { - self.state.borrow_mut().close(); +impl PopupHolder +where + ViewType: ViewTrait, +{ + pub fn update(&self, par: &mut ViewUpdateParams) -> anyhow::Result<()> { + let mut state = self.state.borrow_mut(); + let Some(view) = &mut state.view else { + return Ok(()); + }; + + view.update(par) } pub fn set_view(&self, handle: PopupHandle, view: ViewType) { @@ -131,15 +160,20 @@ impl PopupHolder { Ok(()) } - pub fn get_close_callback(&self) -> Box + pub fn get_close_callback(&self, layout: &Layout) -> Box where ViewType: 'static, { + let layout_tasks = layout.tasks.clone(); let weak_state = Rc::downgrade(&self.state); Box::new(move || { - if let Some(state) = weak_state.upgrade() { - state.borrow_mut().close(); - } + // we can't borrow State here yet, dispatch it. + layout_tasks.push(LayoutTask::Dispatch(Box::new(move |_common| { + if let Some(state) = weak_state.upgrade() { + state.borrow_mut().close(); + } + Ok(()) + }))); }) } } diff --git a/dash-frontend/src/util/steam_utils.rs b/dash-frontend/src/util/steam_utils.rs index a2c6e778..3c7faa3a 100644 --- a/dash-frontend/src/util/steam_utils.rs +++ b/dash-frontend/src/util/steam_utils.rs @@ -2,6 +2,7 @@ use keyvalues_parser::{Obj, Vdf}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; +#[derive(Clone)] pub struct SteamUtils { steam_root: PathBuf, } diff --git a/dash-frontend/src/views/app_launcher.rs b/dash-frontend/src/views/app_launcher.rs index 93d4fb68..f343b201 100644 --- a/dash-frontend/src/views/app_launcher.rs +++ b/dash-frontend/src/views/app_launcher.rs @@ -17,6 +17,7 @@ use wlx_common::{config::GeneralConfig, dash_interface::BoxDashInterface, deskto use crate::{ frontend::{FrontendTask, FrontendTasks, SoundType}, util::popup_manager::{MountPopupOnceParams, PopupHolder}, + views::{ViewTrait, ViewUpdateParams}, }; #[derive(Clone, Copy, Eq, PartialEq, EnumString, VariantNames, AsRefStr)] @@ -69,7 +70,7 @@ struct LaunchParams<'a, T> { interface: &'a mut BoxDashInterface, auto_start: bool, data: &'a mut T, - on_launched: &'a dyn Fn(), + on_launched: Option>, } pub struct View { @@ -94,7 +95,7 @@ pub struct View { auto_start: bool, - on_launched: Box, + on_launched: Option>, } pub struct Params<'a> { @@ -104,7 +105,13 @@ pub struct Params<'a> { pub parent_id: WidgetID, pub config: &'a GeneralConfig, pub frontend_tasks: &'a FrontendTasks, - pub on_launched: Box, + pub on_launched: Box, +} + +impl ViewTrait for View { + fn update(&mut self, _par: &mut ViewUpdateParams) -> anyhow::Result<()> { + Ok(()) + } } impl View { @@ -283,7 +290,7 @@ impl View { entry: params.entry, frontend_tasks: params.frontend_tasks.clone(), globals: params.globals.clone(), - on_launched: params.on_launched, + on_launched: Some(params.on_launched), }) } @@ -319,7 +326,7 @@ impl View { auto_start: self.auto_start, interface, data, - on_launched: &self.on_launched, + on_launched: self.on_launched.take(), }); } @@ -337,7 +344,7 @@ impl View { )))); } - fn launch(params: LaunchParams) -> anyhow::Result<()> { + fn launch(mut params: LaunchParams) -> anyhow::Result<()> { let mut env = Vec::::new(); if params.compositor_mode == CompositorMode::Native { @@ -393,7 +400,9 @@ impl View { params.frontend_tasks.push(FrontendTask::PlaySound(SoundType::Launch)); - (*params.on_launched)(); + if let Some(on_launched) = params.on_launched.take() { + on_launched(); + } // we're done! Ok(()) @@ -430,6 +439,7 @@ pub fn mount_popup(frontend_tasks: FrontendTasks, globals: WguiGlobals, entry: D .push(FrontendTask::MountPopupOnce(MountPopupOnceParams::new( Translation::from_raw_text(&entry.app_name), Box::new(move |data| { + let on_launched = popup.get_close_callback(data.layout); let view = View::new(Params { entry: entry.clone(), globals: &globals, @@ -437,11 +447,11 @@ pub fn mount_popup(frontend_tasks: FrontendTasks, globals: WguiGlobals, entry: D parent_id: data.id_content, frontend_tasks: &frontend_tasks, config: data.config, - on_launched: popup.get_close_callback(), + on_launched, })?; popup.set_view(data.handle, view); - Ok(popup.get_close_callback()) + Ok(popup.get_close_callback(data.layout)) }), ))); } diff --git a/dash-frontend/src/views/download_file.rs b/dash-frontend/src/views/download_file.rs new file mode 100644 index 00000000..b7eea8da --- /dev/null +++ b/dash-frontend/src/views/download_file.rs @@ -0,0 +1,112 @@ +use crate::{ + frontend::{FrontendTask, FrontendTasks}, + util::popup_manager::{MountPopupOnceParams, PopupHolder}, + views::{ViewTrait, ViewUpdateParams}, +}; +use std::path::PathBuf; +use wgui::{ + assets::AssetPath, + globals::WguiGlobals, + i18n::Translation, + layout::{Layout, WidgetID}, + parser::{Fetchable, ParseDocumentParams, ParserState}, + task::Tasks, + widget::label::WidgetLabel, +}; +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 target_path: PathBuf, + pub url: String, + pub on_close_request: Box, +} + +#[derive(Clone)] +enum Task {} + +pub struct View { + id_parent: WidgetID, + globals: WguiGlobals, + tasks: Tasks, + executor: AsyncExecutor, + + #[allow(dead_code)] + parser_state: ParserState, +} + +impl ViewTrait for View { + fn update(&mut self, _par: &mut ViewUpdateParams) -> anyhow::Result<()> { + for task in self.tasks.drain() { + match task {} + } + Ok(()) + } +} + +impl View { + pub fn new(par: Params) -> anyhow::Result { + let tasks = Tasks::::new(); + + let doc_params = ParseDocumentParams { + globals: par.globals.clone(), + path: AssetPath::BuiltIn("gui/view/download_file.xml"), + extra: Default::default(), + }; + + let mut parser_state = wgui::parser::parse_from_assets(&doc_params, par.layout, par.parent_id)?; + + 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; + label_target_path.cast::()?.set_text( + &mut par.layout.common(), + Translation::from_raw_text_string(format!("{}: {}", str_target_path, par.target_path.display())), + ); + } + + Ok(Self { + id_parent: par.parent_id, + tasks, + globals: par.globals.clone(), + executor: par.executor.clone(), + parser_state, + }) + } +} + +pub fn mount_popup( + frontend_tasks: FrontendTasks, + executor: AsyncExecutor, + globals: WguiGlobals, + popup: PopupHolder, + target_path: PathBuf, + url: String, +) { + frontend_tasks + .clone() + .push(FrontendTask::MountPopupOnce(MountPopupOnceParams::new( + 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, + })?; + + popup.set_view(data.handle, view); + Ok(popup.get_close_callback(data.layout)) + }), + ))); +} diff --git a/dash-frontend/src/views/game_launcher.rs b/dash-frontend/src/views/game_launcher.rs index d70a8239..2e889299 100644 --- a/dash-frontend/src/views/game_launcher.rs +++ b/dash-frontend/src/views/game_launcher.rs @@ -7,7 +7,7 @@ use crate::{ popup_manager::{MountPopupOnceParams, PopupHolder}, steam_utils::{self, AppID, AppManifest}, }, - views::game_cover, + views::{ViewTrait, ViewUpdateParams, game_cover}, }; use wgui::{ assets::AssetPath, @@ -35,13 +35,14 @@ pub struct Params<'a> { pub layout: &'a mut Layout, pub parent_id: WidgetID, pub frontend_tasks: &'a FrontendTasks, - pub on_launched: Box, + pub on_launched: Box, } + pub struct View { #[allow(dead_code)] state: ParserState, tasks: Tasks, - on_launched: Box, + on_launched: Option>, frontend_tasks: FrontendTasks, game_cover_view_common: game_cover::ViewCommon, @@ -49,6 +50,30 @@ pub struct View { app_id: AppID, } +impl ViewTrait for View { + fn update(&mut self, par: &mut ViewUpdateParams) -> anyhow::Result<()> { + loop { + let tasks = self.tasks.drain(); + if tasks.is_empty() { + break; + } + for task in tasks { + match task { + Task::FillAppDetails(details) => self.action_fill_app_details(&mut par.layout, details)?, + Task::Launch => self.action_launch(), + Task::SetCoverArt(cover_art) => { + let _ = self + .view_cover + .set_cover_art(&mut self.game_cover_view_common, &mut par.layout, &cover_art); + } + } + } + } + + Ok(()) + } +} + impl View { async fn fetch_details(executor: AsyncExecutor, tasks: Tasks, app_id: AppID) { let Some(details) = cached_fetcher::get_app_details_json(executor, app_id).await else { @@ -105,7 +130,7 @@ impl View { Ok(Self { state, tasks, - on_launched: params.on_launched, + on_launched: Some(params.on_launched), frontend_tasks: params.frontend_tasks.clone(), game_cover_view_common: game_cover::ViewCommon::new(params.globals.clone()), view_cover, @@ -113,28 +138,6 @@ impl View { }) } - pub fn update(&mut self, layout: &mut Layout) -> anyhow::Result<()> { - loop { - let tasks = self.tasks.drain(); - if tasks.is_empty() { - break; - } - for task in tasks { - match task { - Task::FillAppDetails(details) => self.action_fill_app_details(layout, details)?, - Task::Launch => self.action_launch(), - Task::SetCoverArt(cover_art) => { - let _ = self - .view_cover - .set_cover_art(&mut self.game_cover_view_common, layout, &cover_art); - } - } - } - } - - Ok(()) - } - fn action_fill_app_details( &mut self, layout: &mut Layout, @@ -189,7 +192,9 @@ impl View { } } - (*self.on_launched)(); + if let Some(on_launched) = self.on_launched.take() { + on_launched(); + } } } @@ -205,6 +210,7 @@ pub fn mount_popup( .push(FrontendTask::MountPopupOnce(MountPopupOnceParams::new( Translation::from_raw_text(&manifest.name), Box::new(move |data| { + let on_launched = popup.get_close_callback(data.layout); let view = View::new(Params { manifest: manifest.clone(), executor: executor.clone(), @@ -212,11 +218,11 @@ pub fn mount_popup( layout: data.layout, parent_id: data.id_content, frontend_tasks: &frontend_tasks, - on_launched: popup.get_close_callback(), + on_launched, })?; popup.set_view(data.handle, view); - Ok(popup.get_close_callback()) + Ok(popup.get_close_callback(data.layout)) }), ))); } diff --git a/dash-frontend/src/views/game_list.rs b/dash-frontend/src/views/game_list.rs index 89878143..ab7e6448 100644 --- a/dash-frontend/src/views/game_list.rs +++ b/dash-frontend/src/views/game_list.rs @@ -22,7 +22,7 @@ use crate::{ popup_manager::PopupHolder, steam_utils::{self, AppID, SteamUtils}, }, - views::{self, game_cover}, + views::{self, ViewTrait, ViewUpdateParams, game_cover}, }; #[derive(Clone)] @@ -41,6 +41,7 @@ pub struct Params<'a> { pub frontend_tasks: FrontendTasks, pub layout: &'a mut Layout, pub parent_id: WidgetID, + pub steam_utils: &'a SteamUtils, } const MAX_GAMES_PER_PAGE: u32 = 30; @@ -64,6 +65,31 @@ pub struct View { page_count: u32, id_label_page: WidgetID, view_launcher: PopupHolder, + steam_utils: SteamUtils, +} + +impl ViewTrait for View { + fn update(&mut self, par: &mut ViewUpdateParams) -> anyhow::Result<()> { + loop { + let tasks = self.tasks.drain(); + if tasks.is_empty() { + break; + } + for task in tasks { + match task { + Task::LoadManifests => self.load_manifests(), + Task::FillPage(page_idx) => self.fill_page(&mut par.layout, &mut par.executor, page_idx)?, + Task::AppManifestClicked(manifest) => self.action_app_manifest_clicked(manifest)?, + Task::SetCoverArt(app_id, cover_art) => self.set_cover_art(&mut par.layout, app_id, cover_art), + Task::PrevPage => self.page_prev(), + Task::NextPage => self.page_next(), + } + } + } + + self.view_launcher.update(par)?; + Ok(()) + } } impl View { @@ -106,36 +132,9 @@ impl View { page_count: 0, id_label_page, view_launcher: Default::default(), + steam_utils: params.steam_utils.clone(), }) } - - pub fn update( - &mut self, - layout: &mut Layout, - steam_utils: &mut SteamUtils, - executor: &AsyncExecutor, - ) -> anyhow::Result<()> { - loop { - let tasks = self.tasks.drain(); - if tasks.is_empty() { - break; - } - for task in tasks { - match task { - Task::LoadManifests => self.load_manifests(steam_utils), - Task::FillPage(page_idx) => self.fill_page(layout, executor, page_idx)?, - Task::AppManifestClicked(manifest) => self.action_app_manifest_clicked(manifest)?, - Task::SetCoverArt(app_id, cover_art) => self.set_cover_art(layout, app_id, cover_art), - Task::PrevPage => self.page_prev(), - Task::NextPage => self.page_next(), - } - } - } - - self.view_launcher.with_view_res(|view| view.update(layout))?; - - Ok(()) - } } fn fill_game_list( @@ -178,8 +177,11 @@ fn fill_game_list( } impl View { - fn load_manifests(&mut self, steam_utils: &mut SteamUtils) { - match steam_utils.list_installed_games(steam_utils::GameSortMethod::PlayDateDesc) { + fn load_manifests(&mut self) { + match self + .steam_utils + .list_installed_games(steam_utils::GameSortMethod::PlayDateDesc) + { Ok(manifests) => { self.page_count = (manifests.len() as u32 + MAX_GAMES_PER_PAGE) / MAX_GAMES_PER_PAGE; self.all_manifests = manifests; diff --git a/dash-frontend/src/views/mod.rs b/dash-frontend/src/views/mod.rs index c5e89bba..e916e5bf 100644 --- a/dash-frontend/src/views/mod.rs +++ b/dash-frontend/src/views/mod.rs @@ -1,5 +1,8 @@ +use wlx_common::async_executor::AsyncExecutor; + pub mod app_launcher; pub mod audio_settings; +pub mod download_file; pub mod game_cover; pub mod game_launcher; pub mod game_list; @@ -8,3 +11,12 @@ pub mod remote_skymap_list; pub mod running_games_list; pub mod skymap_list; pub mod skymap_list_cell; + +pub struct ViewUpdateParams<'a> { + pub layout: &'a mut wgui::layout::Layout, + pub executor: &'a AsyncExecutor, +} + +pub trait ViewTrait { + fn update(&mut self, par: &mut ViewUpdateParams) -> anyhow::Result<()>; +} diff --git a/dash-frontend/src/views/remote_skymap_downloader.rs b/dash-frontend/src/views/remote_skymap_downloader.rs index 66700b6b..4a2326e0 100644 --- a/dash-frontend/src/views/remote_skymap_downloader.rs +++ b/dash-frontend/src/views/remote_skymap_downloader.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, rc::Rc}; +use std::{collections::HashMap, path::PathBuf, rc::Rc}; use crate::{ frontend::{FrontendTask, FrontendTasks}, @@ -6,6 +6,7 @@ use crate::{ networking::{self, skymap_catalog::SkymapResolution}, popup_manager::{MountPopupOnceParams, PopupHolder}, }, + views::{self, ViewTrait, ViewUpdateParams}, }; use wgui::{ assets::AssetPath, @@ -24,9 +25,10 @@ pub struct Params<'a> { pub globals: &'a WguiGlobals, pub layout: &'a mut Layout, pub executor: &'a AsyncExecutor, + pub frontend_tasks: FrontendTasks, pub parent_id: WidgetID, pub entry: networking::skymap_catalog::SkymapCatalogEntry, - pub on_close_request: Box, + pub on_close_request: Box, pub preview_image: Option, } @@ -38,12 +40,15 @@ enum Task { pub struct View { id_parent: WidgetID, entry: networking::skymap_catalog::SkymapCatalogEntry, + frontend_tasks: FrontendTasks, globals: WguiGlobals, tasks: Tasks, executor: AsyncExecutor, #[allow(dead_code)] parser_state: ParserState, + + popup_download: PopupHolder, } fn mount_resolution_button( @@ -62,6 +67,19 @@ fn mount_resolution_button( Ok(()) } +impl ViewTrait for View { + fn update(&mut self, par: &mut ViewUpdateParams) -> anyhow::Result<()> { + for task in self.tasks.drain() { + match task { + Task::ResolutionClicked(skymap_resolution) => { + self.run_download(&mut par.layout, skymap_resolution)?; + } + } + } + Ok(()) + } +} + impl View { pub fn new(par: Params) -> anyhow::Result { let tasks = Tasks::::new(); @@ -150,15 +168,25 @@ impl View { executor: par.executor.clone(), entry: par.entry, parser_state, + frontend_tasks: par.frontend_tasks, + popup_download: Default::default(), }) } - pub fn update(&mut self, layout: &mut Layout) -> anyhow::Result<()> { - for task in self.tasks.drain() { - match task { - Task::ResolutionClicked(skymap_resolution) => todo!(), - } - } + fn run_download(&mut self, layout: &mut Layout, resolution: SkymapResolution) -> anyhow::Result<()> { + let target_path = PathBuf::from(format!("")); + let Some(url) = self.entry.files.get_url_from_res(resolution) else { + return Ok(()); + }; + + views::download_file::mount_popup( + self.frontend_tasks.clone(), + self.executor.clone(), + self.globals.clone(), + self.popup_download.clone(), + target_path, + url, + ); Ok(()) } } @@ -176,18 +204,20 @@ 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: popup.get_close_callback(), + on_close_request, preview_image, + frontend_tasks: frontend_tasks.clone(), })?; popup.set_view(data.handle, view); - Ok(popup.get_close_callback()) + Ok(popup.get_close_callback(data.layout)) }), ))); } diff --git a/dash-frontend/src/views/remote_skymap_list.rs b/dash-frontend/src/views/remote_skymap_list.rs index 7b7f8f59..b3ce4754 100644 --- a/dash-frontend/src/views/remote_skymap_list.rs +++ b/dash-frontend/src/views/remote_skymap_list.rs @@ -21,7 +21,7 @@ use crate::{ popup_manager::{MountPopupOnceParams, PopupHolder}, wgui_simple, }, - views, + views::{self, ViewTrait, ViewUpdateParams}, }; pub struct Params<'a> { @@ -29,7 +29,7 @@ pub struct Params<'a> { pub layout: &'a mut Layout, pub executor: &'a AsyncExecutor, pub parent_id: WidgetID, - pub on_close_request: Box, + pub on_close_request: Box, pub frontend_tasks: FrontendTasks, } @@ -58,6 +58,44 @@ pub struct View { popup_remote_skymap_downloader: PopupHolder, } +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, glyph_data)) => { + 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)?; + } + } + Task::ShowRemoteSkymapDownloader(skymap_uuid) => { + let preview_image = self.get_image_preview(skymap_uuid); + self.show_remote_skymap_downloader(skymap_uuid, preview_image)?; + } + } + } + Ok(()) + } +} + impl View { async fn skymap_catalog_request_wrapper(tasks: Tasks, executor: AsyncExecutor) { let res = networking::skymap_catalog::request_catalog(&executor).await; @@ -176,44 +214,6 @@ impl View { } None } - - pub fn update(&mut self, layout: &mut Layout) -> anyhow::Result<()> { - self - .popup_remote_skymap_downloader - .with_view_res(|view| view.update(layout))?; - - for task in self.tasks.drain() { - match task { - Task::SetSkymapCatalog(skymap_catalog) => { - layout.remove_widget(self.id_loading); - match &*skymap_catalog { - Ok(skymap_catalog) => { - self.mount_catalog(layout, skymap_catalog)?; - } - Err(e) => wgui_simple::create_label_error( - layout, - self.id_parent, - format!("Failed to fetch skymap catalog: {:?}", e), - )?, - } - } - Task::SetSkymapPreview((skymap_uuid, glyph_data)) => { - if let Some(cell) = &mut self - .mounted_cells - .iter_mut() - .find(|cell| cell.skymap_uuid == skymap_uuid) - { - cell.view.set_image(layout, glyph_data)?; - } - } - Task::ShowRemoteSkymapDownloader(skymap_uuid) => { - let preview_image = self.get_image_preview(skymap_uuid); - self.show_remote_skymap_downloader(skymap_uuid, preview_image)?; - } - } - } - Ok(()) - } } pub fn mount_popup( @@ -227,17 +227,18 @@ 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: popup.get_close_callback(), + on_close_request, frontend_tasks, })?; popup.set_view(data.handle, view); - Ok(popup.get_close_callback()) + Ok(popup.get_close_callback(data.layout)) }), ))); } diff --git a/dash-frontend/src/views/skymap_list.rs b/dash-frontend/src/views/skymap_list.rs index 634d21d5..466f0a5f 100644 --- a/dash-frontend/src/views/skymap_list.rs +++ b/dash-frontend/src/views/skymap_list.rs @@ -12,7 +12,7 @@ use wlx_common::{async_executor::AsyncExecutor, config_io}; use crate::{ frontend::FrontendTasks, util::{popup_manager::PopupHolder, wgui_simple}, - views, + views::{self, ViewTrait, ViewUpdateParams}, }; #[derive(Clone)] @@ -38,6 +38,31 @@ pub struct View { popup_remote_skymap_list: PopupHolder, } +impl ViewTrait for View { + fn update(&mut self, par: &mut ViewUpdateParams) -> anyhow::Result<()> { + self.popup_remote_skymap_list.update(par)?; + + loop { + let tasks = self.tasks.drain(); + if tasks.is_empty() { + break; + } + for task in tasks { + match task { + Task::DownloadSkymaps => { + self.download_skymaps(&par.executor)?; + } + Task::Refresh => { + self.refresh(&mut par.layout)?; + } + } + } + } + + Ok(()) + } +} + impl View { pub fn new(params: Params) -> anyhow::Result { let doc_params = &ParseDocumentParams { @@ -72,31 +97,6 @@ impl View { }) } - pub fn update(&mut self, layout: &mut Layout, executor: &AsyncExecutor) -> anyhow::Result<()> { - self - .popup_remote_skymap_list - .with_view_res(|view| view.update(layout))?; - - loop { - let tasks = self.tasks.drain(); - if tasks.is_empty() { - break; - } - for task in tasks { - match task { - Task::DownloadSkymaps => { - self.download_skymaps(executor)?; - } - Task::Refresh => { - self.refresh(layout)?; - } - } - } - } - - Ok(()) - } - fn download_skymaps(&mut self, executor: &AsyncExecutor) -> anyhow::Result<()> { views::remote_skymap_list::mount_popup( self.frontend_tasks.clone(), diff --git a/wayvr/src/overlays/keyboard/mod.rs b/wayvr/src/overlays/keyboard/mod.rs index dc432395..4c0401f0 100644 --- a/wayvr/src/overlays/keyboard/mod.rs +++ b/wayvr/src/overlays/keyboard/mod.rs @@ -461,7 +461,7 @@ fn handle_mouse_motion( { if !swipe_manager.is_current_swipe_empty() { match &key.button_state { - KeyButtonData::Key { vk, pressed } => { + KeyButtonData::Key { vk: _, pressed: _ } => { if let Some(pos) = within_key_pos { // check because mouse motion can trigger despite hover being false if pos.x >= 0.0 && pos.x <= 1.0 && pos.y >= 0.0 && pos.y <= 1.0 {