dash-frontend: ViewTrait, refactoring, `download_file::View` (wip)

This commit is contained in:
Aleksander 2026-04-12 16:42:25 +02:00 committed by galister
parent 24e3099b7b
commit 2035d9ba76
16 changed files with 400 additions and 183 deletions

View File

@ -0,0 +1,11 @@
<layout>
<elements>
<div align_items="center" justify_content="center">
<div flex_direction="column" gap="8">
<label translation="DOWNLOADING_FILE" size="14"/>
<label id="label_target_path" />
<label id="label_progress"/>
</div>
</div>
</elements>
</layout>

View File

@ -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"

View File

@ -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<T> {
@ -19,7 +19,6 @@ pub struct TabGames<T> {
view_game_list: game_list::View,
view_running_games_list: running_games_list::View,
steam_utils: SteamUtils,
marker: PhantomData<T>,
}
@ -29,9 +28,11 @@ impl<T> Tab<T> for TabGames<T> {
}
fn update(&mut self, frontend: &mut Frontend<T>, 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<T> TabGames<T> {
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<T> TabGames<T> {
view_game_list,
view_running_games_list,
marker: PhantomData,
steam_utils,
})
}
}

View File

@ -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<T> Tab<T> for TabSettings<T> {
fn update(&mut self, frontend: &mut Frontend<T>, _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,
})?;

View File

@ -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(())
}
}

View File

@ -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<RefCell<MountedPopupState>>,
}
struct PopupHolderState<ViewType> {
struct PopupHolderState<ViewType>
where
ViewType: ViewTrait,
{
popup_handle: PopupHandle,
view: Option<ViewType>,
}
// we can't use #[derive(Default)] due to the fact that ViewType can't be Default.
impl<ViewType> Default for PopupHolderState<ViewType> {
impl<ViewType> Default for PopupHolderState<ViewType>
where
ViewType: ViewTrait,
{
fn default() -> Self {
Self {
popup_handle: Default::default(),
@ -60,11 +69,17 @@ impl<ViewType> Default for PopupHolderState<ViewType> {
}
}
pub struct PopupHolder<ViewType> {
pub struct PopupHolder<ViewType>
where
ViewType: ViewTrait,
{
state: Rc<RefCell<PopupHolderState<ViewType>>>,
}
impl<ViewType> Default for PopupHolder<ViewType> {
impl<ViewType> Default for PopupHolder<ViewType>
where
ViewType: ViewTrait,
{
fn default() -> Self {
Self {
state: Rc::new(RefCell::new(PopupHolderState::default())),
@ -72,7 +87,10 @@ impl<ViewType> Default for PopupHolder<ViewType> {
}
}
impl<ViewType> PopupHolderState<ViewType> {
impl<ViewType> PopupHolderState<ViewType>
where
ViewType: ViewTrait,
{
fn close(&mut self) {
self.view = None;
self.popup_handle.close();
@ -80,7 +98,10 @@ impl<ViewType> PopupHolderState<ViewType> {
}
// we can't derive(Clone) due to the fact that ViewType is non-cloneable
impl<ViewType> Clone for PopupHolder<ViewType> {
impl<ViewType> Clone for PopupHolder<ViewType>
where
ViewType: ViewTrait,
{
fn clone(&self) -> Self {
Self {
state: self.state.clone(),
@ -88,9 +109,17 @@ impl<ViewType> Clone for PopupHolder<ViewType> {
}
}
impl<ViewType> PopupHolder<ViewType> {
pub fn close(&self) {
self.state.borrow_mut().close();
impl<ViewType> PopupHolder<ViewType>
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<ViewType> PopupHolder<ViewType> {
Ok(())
}
pub fn get_close_callback(&self) -> Box<dyn Fn()>
pub fn get_close_callback(&self, layout: &Layout) -> Box<dyn FnOnce()>
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(())
})));
})
}
}

View File

@ -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,
}

View File

@ -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<T>,
auto_start: bool,
data: &'a mut T,
on_launched: &'a dyn Fn(),
on_launched: Option<Box<dyn FnOnce()>>,
}
pub struct View {
@ -94,7 +95,7 @@ pub struct View {
auto_start: bool,
on_launched: Box<dyn Fn()>,
on_launched: Option<Box<dyn FnOnce()>>,
}
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<dyn Fn()>,
pub on_launched: Box<dyn FnOnce()>,
}
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<T>(params: LaunchParams<T>) -> anyhow::Result<()> {
fn launch<T>(mut params: LaunchParams<T>) -> anyhow::Result<()> {
let mut env = Vec::<String>::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))
}),
)));
}

View File

@ -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<dyn FnOnce()>,
}
#[derive(Clone)]
enum Task {}
pub struct View {
id_parent: WidgetID,
globals: WguiGlobals,
tasks: Tasks<Task>,
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<Self> {
let tasks = Tasks::<Task>::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::<WidgetLabel>()?.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<View>,
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))
}),
)));
}

View File

@ -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<dyn Fn()>,
pub on_launched: Box<dyn FnOnce()>,
}
pub struct View {
#[allow(dead_code)]
state: ParserState,
tasks: Tasks<Task>,
on_launched: Box<dyn Fn()>,
on_launched: Option<Box<dyn FnOnce()>>,
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<Task>, 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))
}),
)));
}

View File

@ -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<views::game_launcher::View>,
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;

View File

@ -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<()>;
}

View File

@ -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<dyn Fn()>,
pub on_close_request: Box<dyn FnOnce()>,
pub preview_image: Option<CustomGlyphData>,
}
@ -38,12 +40,15 @@ enum Task {
pub struct View {
id_parent: WidgetID,
entry: networking::skymap_catalog::SkymapCatalogEntry,
frontend_tasks: FrontendTasks,
globals: WguiGlobals,
tasks: Tasks<Task>,
executor: AsyncExecutor,
#[allow(dead_code)]
parser_state: ParserState,
popup_download: PopupHolder<views::download_file::View>,
}
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<Self> {
let tasks = Tasks::<Task>::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))
}),
)));
}

View File

@ -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<dyn Fn()>,
pub on_close_request: Box<dyn FnOnce()>,
pub frontend_tasks: FrontendTasks,
}
@ -58,6 +58,44 @@ pub struct View {
popup_remote_skymap_downloader: PopupHolder<views::remote_skymap_downloader::View>,
}
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<Task>, 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))
}),
)));
}

View File

@ -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<views::remote_skymap_list::View>,
}
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<Self> {
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(),

View File

@ -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 {