mirror of https://github.com/wayvr-org/wayvr.git
dash-frontend: list skymaps from the server (wip), loading spinner, wgui: animation bugfix
This commit is contained in:
parent
a3460fbe70
commit
f2dcdcc6c3
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Material Design Icons by Pictogrammers - https://github.com/Templarian/MaterialDesign/blob/master/LICENSE --><path fill="currentColor" d="M12 4V2A10 10 0 0 0 2 12h2a8 8 0 0 1 8-8"/></svg>
|
||||||
|
After Width: | Height: | Size: 286 B |
|
|
@ -0,0 +1,16 @@
|
||||||
|
<layout>
|
||||||
|
<template name="LoadingWithText">
|
||||||
|
<div id="root" width="100%" height="100%" align_items="center" justify_content="center">
|
||||||
|
<div flex_direction="row" gap="8" align_items="center" >
|
||||||
|
<sprite id="sprite_loading" src_builtin="dashboard/loading.svg" width="32" height="32"/>
|
||||||
|
<label translation="LOADING" weight="bold"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template name="LoadingWithoutText">
|
||||||
|
<div id="root" width="100%" height="100%" align_items="center" justify_content="center">
|
||||||
|
<sprite id="sprite_loading" src_builtin="dashboard/loading.svg" width="32" height="32"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</layout>
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<layout>
|
||||||
|
<elements>
|
||||||
|
<div id="list" gap="8" flex_wrap="wrap">
|
||||||
|
<!-- filled-in at runtime -->
|
||||||
|
</div>
|
||||||
|
</elements>
|
||||||
|
</layout>
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<layout>
|
<layout>
|
||||||
<elements>
|
<elements>
|
||||||
<div flex_direction="column">
|
<div flex_direction="column">
|
||||||
<div id="list_parent" gap="8" flex_direction="row" flex_wrap="wrap" justify_content="center" />
|
<div id="list_parent" gap="8" flex_direction="row" flex_wrap="wrap" overflow_y="scroll"/>
|
||||||
</div>
|
</div>
|
||||||
<div flex_direction="row" gap="4">
|
<div flex_direction="row" gap="4">
|
||||||
<Button id="btn_refresh" tooltip="REFRESH" width="32" height="32" sprite_src_builtin="dashboard/refresh.svg" />
|
<Button id="btn_refresh" tooltip="REFRESH" width="32" height="32" sprite_src_builtin="dashboard/refresh.svg" />
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
<layout>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ids:
|
||||||
|
"image_preview"
|
||||||
|
"label_title"
|
||||||
|
"label_desc"
|
||||||
|
-->
|
||||||
|
<template name="Cell">
|
||||||
|
<rectangle
|
||||||
|
border="2"
|
||||||
|
color="~color_bg_translucent"
|
||||||
|
border_color="~color_accent"
|
||||||
|
padding="8"
|
||||||
|
round="8"
|
||||||
|
flex_direction="column"
|
||||||
|
gap="4"
|
||||||
|
width="256"
|
||||||
|
align_items="center"
|
||||||
|
align_self="start">
|
||||||
|
<image id="image_preview" width="100%" height="128"/>
|
||||||
|
<label id="label_title" wrap="1" weight="bold"/>
|
||||||
|
<label id="label_author" wrap="1"/>
|
||||||
|
</rectangle>
|
||||||
|
</template>
|
||||||
|
</layout>
|
||||||
|
|
@ -155,6 +155,7 @@
|
||||||
"HIDE": "Hide",
|
"HIDE": "Hide",
|
||||||
"HOME_SCREEN": "Home",
|
"HOME_SCREEN": "Home",
|
||||||
"MONADO_RUNTIME": "Monado runtime",
|
"MONADO_RUNTIME": "Monado runtime",
|
||||||
|
"LOADING": "Loading...",
|
||||||
"POPUP_ADD_DISPLAY": {
|
"POPUP_ADD_DISPLAY": {
|
||||||
"RESOLUTION": "Resolution"
|
"RESOLUTION": "Resolution"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ use crate::{
|
||||||
assets,
|
assets,
|
||||||
tab::{Tab, TabType, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, settings::TabSettings},
|
tab::{Tab, TabType, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, settings::TabSettings},
|
||||||
util::{
|
util::{
|
||||||
popup_manager::{MountPopupParams, PopupManager, PopupManagerParams},
|
popup_manager::{MountPopupOnceParams, MountPopupParams, PopupManager, PopupManagerParams},
|
||||||
toast_manager::ToastManager,
|
toast_manager::ToastManager,
|
||||||
},
|
},
|
||||||
views,
|
views,
|
||||||
|
|
@ -102,6 +102,7 @@ pub enum FrontendTask {
|
||||||
RefreshClock,
|
RefreshClock,
|
||||||
RefreshBackground,
|
RefreshBackground,
|
||||||
MountPopup(MountPopupParams),
|
MountPopup(MountPopupParams),
|
||||||
|
MountPopupOnce(MountPopupOnceParams),
|
||||||
RefreshPopupManager,
|
RefreshPopupManager,
|
||||||
ShowAudioSettings,
|
ShowAudioSettings,
|
||||||
UpdateAudioSettingsView,
|
UpdateAudioSettingsView,
|
||||||
|
|
@ -310,13 +311,18 @@ impl<T: 'static> Frontend<T> {
|
||||||
fn mount_popup(&mut self, params: MountPopupParams, data: &mut T) -> anyhow::Result<()> {
|
fn mount_popup(&mut self, params: MountPopupParams, data: &mut T) -> anyhow::Result<()> {
|
||||||
let config = self.interface.general_config(data);
|
let config = self.interface.general_config(data);
|
||||||
|
|
||||||
self.popup_manager.mount_popup(
|
self
|
||||||
self.globals.clone(),
|
.popup_manager
|
||||||
&mut self.layout,
|
.mount_popup(&self.globals, &mut self.layout, &self.tasks, params, config)?;
|
||||||
self.tasks.clone(),
|
Ok(())
|
||||||
params,
|
}
|
||||||
config,
|
|
||||||
)?;
|
fn mount_popup_once(&mut self, params: MountPopupOnceParams, data: &mut T) -> anyhow::Result<()> {
|
||||||
|
let config = self.interface.general_config(data);
|
||||||
|
|
||||||
|
self
|
||||||
|
.popup_manager
|
||||||
|
.mount_popup_once(&self.globals, &mut self.layout, &self.tasks, params, config)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -357,6 +363,7 @@ impl<T: 'static> Frontend<T> {
|
||||||
FrontendTask::RefreshClock => self.update_time(params.data)?,
|
FrontendTask::RefreshClock => self.update_time(params.data)?,
|
||||||
FrontendTask::RefreshBackground => self.update_background(params.data)?,
|
FrontendTask::RefreshBackground => self.update_background(params.data)?,
|
||||||
FrontendTask::MountPopup(popup_params) => self.mount_popup(popup_params, params.data)?,
|
FrontendTask::MountPopup(popup_params) => self.mount_popup(popup_params, params.data)?,
|
||||||
|
FrontendTask::MountPopupOnce(popup_params) => self.mount_popup_once(popup_params, params.data)?,
|
||||||
FrontendTask::RefreshPopupManager => self.refresh_popup_manager()?,
|
FrontendTask::RefreshPopupManager => self.refresh_popup_manager()?,
|
||||||
FrontendTask::ShowAudioSettings => self.action_show_audio_settings()?,
|
FrontendTask::ShowAudioSettings => self.action_show_audio_settings()?,
|
||||||
FrontendTask::UpdateAudioSettingsView => self.action_update_audio_settings()?,
|
FrontendTask::UpdateAudioSettingsView => self.action_update_audio_settings()?,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ use wgui::{
|
||||||
assets::AssetPath,
|
assets::AssetPath,
|
||||||
components::button::{ButtonClickCallback, ComponentButton},
|
components::button::{ButtonClickCallback, ComponentButton},
|
||||||
globals::WguiGlobals,
|
globals::WguiGlobals,
|
||||||
i18n::Translation,
|
|
||||||
layout::{WidgetID, WidgetPair},
|
layout::{WidgetID, WidgetPair},
|
||||||
parser::{Fetchable, ParseDocumentParams, ParserState},
|
parser::{Fetchable, ParseDocumentParams, ParserState},
|
||||||
task::Tasks,
|
task::Tasks,
|
||||||
|
|
@ -17,18 +16,19 @@ use wgui::{
|
||||||
use wlx_common::desktop_finder::DesktopEntry;
|
use wlx_common::desktop_finder::DesktopEntry;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
frontend::{Frontend, FrontendTask, FrontendTasks},
|
frontend::{Frontend, FrontendTasks},
|
||||||
tab::{Tab, TabType},
|
tab::{Tab, TabType},
|
||||||
util::popup_manager::{MountPopupParams, PopupHandle},
|
util::popup_manager::PopupHolder,
|
||||||
views::{self, app_launcher},
|
views::{self},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
enum Task {
|
enum Task {
|
||||||
CloseLauncher,
|
CloseLauncher,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
view_launcher: Option<(PopupHandle, views::app_launcher::View)>,
|
view_launcher: Option<PopupHolder<views::app_launcher::View>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TabApps<T> {
|
pub struct TabApps<T> {
|
||||||
|
|
@ -82,37 +82,16 @@ fn on_app_click(
|
||||||
tasks: Tasks<Task>,
|
tasks: Tasks<Task>,
|
||||||
) -> ButtonClickCallback {
|
) -> ButtonClickCallback {
|
||||||
Rc::new(move |_common, _evt| {
|
Rc::new(move |_common, _evt| {
|
||||||
frontend_tasks.push(FrontendTask::MountPopup(MountPopupParams {
|
views::app_launcher::mount_popup(
|
||||||
title: Translation::from_raw_text(&entry.app_name),
|
frontend_tasks.clone(),
|
||||||
on_content: {
|
globals.clone(),
|
||||||
// this is awful
|
entry.clone(),
|
||||||
|
tasks.make_callback_box(Task::CloseLauncher),
|
||||||
|
Box::new({
|
||||||
let state = state.clone();
|
let state = state.clone();
|
||||||
let entry = entry.clone();
|
move |popup| state.borrow_mut().view_launcher = Some(popup)
|
||||||
let globals = globals.clone();
|
}),
|
||||||
let frontend_tasks = frontend_tasks.clone();
|
);
|
||||||
let tasks = tasks.clone();
|
|
||||||
|
|
||||||
Rc::new(move |data| {
|
|
||||||
let on_launched = {
|
|
||||||
let tasks = tasks.clone();
|
|
||||||
Box::new(move || tasks.push(Task::CloseLauncher))
|
|
||||||
};
|
|
||||||
|
|
||||||
let view = app_launcher::View::new(app_launcher::Params {
|
|
||||||
entry: entry.clone(),
|
|
||||||
globals: &globals,
|
|
||||||
layout: data.layout,
|
|
||||||
parent_id: data.id_content,
|
|
||||||
frontend_tasks: &frontend_tasks,
|
|
||||||
config: data.config,
|
|
||||||
on_launched,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
state.borrow_mut().view_launcher = Some((data.handle, view));
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ use wlx_common::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
frontend::{Frontend, FrontendTask},
|
frontend::{Frontend, FrontendTask, FrontendTasks},
|
||||||
tab::{Tab, TabType, settings::macros::MacroParams},
|
tab::{Tab, TabType, settings::macros::MacroParams},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -83,6 +83,7 @@ enum Task {
|
||||||
struct SettingsMountParams<'a> {
|
struct SettingsMountParams<'a> {
|
||||||
mp: &'a mut MacroParams<'a>,
|
mp: &'a mut MacroParams<'a>,
|
||||||
globals: &'a WguiGlobals,
|
globals: &'a WguiGlobals,
|
||||||
|
frontend_tasks: &'a FrontendTasks,
|
||||||
parent_id: WidgetID,
|
parent_id: WidgetID,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,6 +108,7 @@ pub struct TabSettings<T> {
|
||||||
|
|
||||||
tasks: Tasks<Task>,
|
tasks: Tasks<Task>,
|
||||||
marker: PhantomData<T>,
|
marker: PhantomData<T>,
|
||||||
|
frontend_tasks: FrontendTasks,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Tab<T> for TabSettings<T> {
|
impl<T> Tab<T> for TabSettings<T> {
|
||||||
|
|
@ -550,6 +552,7 @@ impl<T> TabSettings<T> {
|
||||||
mp: &mut mp,
|
mp: &mut mp,
|
||||||
globals: &globals,
|
globals: &globals,
|
||||||
parent_id: root,
|
parent_id: root,
|
||||||
|
frontend_tasks: &self.frontend_tasks,
|
||||||
};
|
};
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
|
|
@ -611,6 +614,7 @@ impl<T> TabSettings<T> {
|
||||||
marker: PhantomData,
|
marker: PhantomData,
|
||||||
context_menu: ContextMenu::default(),
|
context_menu: ContextMenu::default(),
|
||||||
current_tab: None,
|
current_tab: None,
|
||||||
|
frontend_tasks: frontend.tasks.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ impl State {
|
||||||
globals: par.globals.clone(),
|
globals: par.globals.clone(),
|
||||||
layout: par.mp.layout,
|
layout: par.mp.layout,
|
||||||
parent_id: c,
|
parent_id: c,
|
||||||
|
frontend_tasks: par.frontend_tasks,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(Self { skymap_list })
|
Ok(Self { skymap_list })
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ use wlx_common::async_executor::AsyncExecutor;
|
||||||
|
|
||||||
use crate::util::{http_client, networking};
|
use crate::util::{http_client, networking};
|
||||||
|
|
||||||
|
pub type SkymapUuid = String;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct SkymapCatalogEntryFiles {
|
pub struct SkymapCatalogEntryFiles {
|
||||||
pub size_16k: Option<String>, // "my_skymap_16k.png"
|
pub size_16k: Option<String>, // "my_skymap_16k.png"
|
||||||
|
|
@ -15,10 +17,10 @@ pub struct SkymapCatalogEntryFiles {
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct SkymapCatalogEntry {
|
pub struct SkymapCatalogEntry {
|
||||||
pub uuid: String,
|
pub uuid: SkymapUuid,
|
||||||
pub created_at: String,
|
pub created_at: String,
|
||||||
pub modified_at: String,
|
pub modified_at: String,
|
||||||
pub entry_version: u32,
|
pub version: u32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
pub author: String,
|
pub author: String,
|
||||||
|
|
@ -32,11 +34,25 @@ pub struct SkymapCatalog {
|
||||||
pub entries: Vec<SkymapCatalogEntry>,
|
pub entries: Vec<SkymapCatalogEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SkymapCatalog {
|
||||||
|
fn validate(&self) -> anyhow::Result<()> {
|
||||||
|
if self.version != 1 {
|
||||||
|
anyhow::bail!("Unsupported version");
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.r#type != "wayvr_skymaps" {
|
||||||
|
anyhow::bail!("Unsupported type");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn request_catalog(executor: &AsyncExecutor) -> anyhow::Result<SkymapCatalog> {
|
pub async fn request_catalog(executor: &AsyncExecutor) -> anyhow::Result<SkymapCatalog> {
|
||||||
log::info!("Fetching skymap list");
|
log::info!("Fetching skymap list");
|
||||||
|
|
||||||
let res = http_client::get(executor, &format!("{}/catalog.json", networking::WAYVR_SKYMAPS_ROOT)).await?;
|
let res = http_client::get(executor, &format!("{}/catalog.json", networking::WAYVR_SKYMAPS_ROOT)).await?;
|
||||||
let catalog = res.as_json::<SkymapCatalog>()?;
|
let catalog = res.as_json::<SkymapCatalog>()?;
|
||||||
|
catalog.validate()?;
|
||||||
|
|
||||||
Ok(catalog)
|
Ok(catalog)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,8 @@ pub struct PopupHandle {
|
||||||
state: Rc<RefCell<MountedPopupState>>,
|
state: Rc<RefCell<MountedPopupState>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type PopupHolder<ViewType> = (PopupHandle, ViewType);
|
||||||
|
|
||||||
impl PopupHandle {
|
impl PopupHandle {
|
||||||
pub fn close(&self) {
|
pub fn close(&self) {
|
||||||
self.state.borrow_mut().mounted_popup = None; // Drop will be called
|
self.state.borrow_mut().mounted_popup = None; // Drop will be called
|
||||||
|
|
@ -67,6 +69,23 @@ pub struct MountPopupParams {
|
||||||
pub on_content: Rc<dyn Fn(PopupContentFuncData) -> anyhow::Result<()>>,
|
pub on_content: Rc<dyn Fn(PopupContentFuncData) -> anyhow::Result<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we need to implement Clone here, but the underlying function can be called only once.
|
||||||
|
// on_content will be cleared after the first call
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MountPopupOnceParams {
|
||||||
|
title: Translation,
|
||||||
|
on_content: Rc<RefCell<Option<Box<dyn FnOnce(PopupContentFuncData) -> anyhow::Result<()>>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MountPopupOnceParams {
|
||||||
|
pub fn new(title: Translation, on_content: Box<dyn FnOnce(PopupContentFuncData) -> anyhow::Result<()>>) -> Self {
|
||||||
|
Self {
|
||||||
|
title,
|
||||||
|
on_content: Rc::new(RefCell::new(Some(on_content))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Drop for MountedPopup {
|
impl Drop for MountedPopup {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.layout_tasks.push(LayoutTask::RemoveWidget(self.id_root));
|
self.layout_tasks.push(LayoutTask::RemoveWidget(self.id_root));
|
||||||
|
|
@ -116,16 +135,13 @@ impl PopupManager {
|
||||||
state.refresh_stack(alterables);
|
state.refresh_stack(alterables);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mount a new popup on top of the existing popup stack.
|
fn mount_popup_prepare(
|
||||||
/// Only the topmost popup is visible.
|
&self,
|
||||||
pub fn mount_popup(
|
globals: &WguiGlobals,
|
||||||
&mut self,
|
|
||||||
globals: WguiGlobals,
|
|
||||||
layout: &mut Layout,
|
layout: &mut Layout,
|
||||||
frontend_tasks: FrontendTasks,
|
frontend_tasks: &FrontendTasks,
|
||||||
params: MountPopupParams,
|
popup_title: &Translation,
|
||||||
config: &GeneralConfig,
|
) -> anyhow::Result<(PopupHandle, WidgetID /* content widget ID */)> {
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let doc_params = &ParseDocumentParams {
|
let doc_params = &ParseDocumentParams {
|
||||||
globals: globals.clone(),
|
globals: globals.clone(),
|
||||||
path: AssetPath::BuiltIn("gui/view/popup_window.xml"),
|
path: AssetPath::BuiltIn("gui/view/popup_window.xml"),
|
||||||
|
|
@ -138,7 +154,7 @@ impl PopupManager {
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut label_title = state.fetch_widget_as::<WidgetLabel>(&layout.state, "popup_title")?;
|
let mut label_title = state.fetch_widget_as::<WidgetLabel>(&layout.state, "popup_title")?;
|
||||||
label_title.set_text_simple(&mut globals.get(), params.title);
|
label_title.set_text_simple(&mut globals.get(), popup_title.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let but_back = state.fetch_component_as::<ComponentButton>("but_back")?;
|
let but_back = state.fetch_component_as::<ComponentButton>("but_back")?;
|
||||||
|
|
@ -172,6 +188,48 @@ impl PopupManager {
|
||||||
});
|
});
|
||||||
|
|
||||||
frontend_tasks.push(FrontendTask::RefreshPopupManager);
|
frontend_tasks.push(FrontendTask::RefreshPopupManager);
|
||||||
|
Ok((popup_handle, id_content))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mount a new popup on top of the existing popup stack (non-cloneable version).
|
||||||
|
/// Only the topmost popup is visible.
|
||||||
|
pub fn mount_popup_once(
|
||||||
|
&mut self,
|
||||||
|
globals: &WguiGlobals,
|
||||||
|
layout: &mut Layout,
|
||||||
|
frontend_tasks: &FrontendTasks,
|
||||||
|
params: MountPopupOnceParams,
|
||||||
|
config: &GeneralConfig,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let mut func = params.on_content.borrow_mut();
|
||||||
|
let Some(on_content_func) = func.take() else {
|
||||||
|
anyhow::bail!("mount_popup_once called more than once");
|
||||||
|
};
|
||||||
|
|
||||||
|
let (popup_handle, id_content) = self.mount_popup_prepare(globals, layout, frontend_tasks, ¶ms.title)?;
|
||||||
|
|
||||||
|
// mount user-set popup content
|
||||||
|
on_content_func(PopupContentFuncData {
|
||||||
|
layout,
|
||||||
|
handle: popup_handle.clone(),
|
||||||
|
id_content,
|
||||||
|
config,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mount a new popup on top of the existing popup stack.
|
||||||
|
/// Only the topmost popup is visible.
|
||||||
|
pub fn mount_popup(
|
||||||
|
&mut self,
|
||||||
|
globals: &WguiGlobals,
|
||||||
|
layout: &mut Layout,
|
||||||
|
frontend_tasks: &FrontendTasks,
|
||||||
|
params: MountPopupParams,
|
||||||
|
config: &GeneralConfig,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let (popup_handle, id_content) = self.mount_popup_prepare(globals, layout, frontend_tasks, ¶ms.title)?;
|
||||||
|
|
||||||
// mount user-set popup content
|
// mount user-set popup content
|
||||||
(*params.on_content)(PopupContentFuncData {
|
(*params.on_content)(PopupContentFuncData {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,18 @@
|
||||||
|
use glam::Mat4;
|
||||||
use wgui::{
|
use wgui::{
|
||||||
|
animation::{Animation, AnimationEasing},
|
||||||
|
assets::AssetPath,
|
||||||
|
drawing,
|
||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
layout::{Layout, WidgetID},
|
layout::{Layout, LayoutTask, WidgetID},
|
||||||
renderer_vk::text::TextStyle,
|
parser::{Fetchable, ParseDocumentParams},
|
||||||
|
renderer_vk::{
|
||||||
|
text::{FontWeight, TextStyle},
|
||||||
|
util::centered_matrix,
|
||||||
|
},
|
||||||
widget::label::{WidgetLabel, WidgetLabelParams},
|
widget::label::{WidgetLabel, WidgetLabelParams},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn create_label(layout: &mut Layout, parent: WidgetID, content: Translation) -> anyhow::Result<()> {
|
pub fn create_label(layout: &mut Layout, parent: WidgetID, content: Translation) -> anyhow::Result<()> {
|
||||||
let label = WidgetLabel::create(
|
let label = WidgetLabel::create(
|
||||||
&mut layout.state,
|
&mut layout.state,
|
||||||
|
|
@ -22,3 +29,73 @@ pub fn create_label(layout: &mut Layout, parent: WidgetID, content: Translation)
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_label_error(layout: &mut Layout, parent: WidgetID, content: String) -> anyhow::Result<()> {
|
||||||
|
let label = WidgetLabel::create(
|
||||||
|
&mut layout.state,
|
||||||
|
WidgetLabelParams {
|
||||||
|
content: Translation::from_raw_text_string(content),
|
||||||
|
style: TextStyle {
|
||||||
|
wrap: true,
|
||||||
|
color: Some(drawing::Color::new(1.0, 0.5, 0.0, 1.0)),
|
||||||
|
weight: Some(FontWeight::Bold),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
layout.add_child(parent, label, Default::default())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CreateLoadingParams<'a> {
|
||||||
|
pub layout: &'a mut Layout,
|
||||||
|
pub parent_id: WidgetID,
|
||||||
|
pub with_text: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_loading(par: CreateLoadingParams) -> anyhow::Result<WidgetID> {
|
||||||
|
let doc_params = ParseDocumentParams {
|
||||||
|
globals: par.layout.state.globals.clone(),
|
||||||
|
path: AssetPath::BuiltIn("gui/t_loading.xml"),
|
||||||
|
extra: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut parser_state = wgui::parser::parse_from_assets(&doc_params, par.layout, par.parent_id)?;
|
||||||
|
|
||||||
|
let data = parser_state.realize_template(
|
||||||
|
&doc_params,
|
||||||
|
if par.with_text {
|
||||||
|
"LoadingWithText"
|
||||||
|
} else {
|
||||||
|
"LoadingWithoutText"
|
||||||
|
},
|
||||||
|
par.layout,
|
||||||
|
par.parent_id,
|
||||||
|
Default::default(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let id_root = data.get_widget_id("root")?;
|
||||||
|
let id_sprite_loading = data.get_widget_id("sprite_loading")?;
|
||||||
|
|
||||||
|
par.layout.animations.add(Animation::new(
|
||||||
|
id_sprite_loading,
|
||||||
|
60 * 30, /* spin it for 30 seconds at most */
|
||||||
|
AnimationEasing::Linear,
|
||||||
|
Box::new(move |common, data| {
|
||||||
|
// spin it
|
||||||
|
data.data.transform = centered_matrix(data.widget_boundary.size, &Mat4::from_rotation_z(data.pos * 400.0));
|
||||||
|
if data.pos == 1.0 {
|
||||||
|
// remove the spinner, do not waste energy
|
||||||
|
common
|
||||||
|
.alterables
|
||||||
|
.tasks
|
||||||
|
.push(LayoutTask::RemoveWidget(id_sprite_loading));
|
||||||
|
}
|
||||||
|
common.alterables.mark_redraw();
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
|
||||||
|
Ok(id_root)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,10 @@ use wgui::{
|
||||||
};
|
};
|
||||||
use wlx_common::{config::GeneralConfig, dash_interface::BoxDashInterface, desktop_finder::DesktopEntry};
|
use wlx_common::{config::GeneralConfig, dash_interface::BoxDashInterface, desktop_finder::DesktopEntry};
|
||||||
|
|
||||||
use crate::frontend::{FrontendTask, FrontendTasks, SoundType};
|
use crate::{
|
||||||
|
frontend::{FrontendTask, FrontendTasks, SoundType},
|
||||||
|
util::popup_manager::{MountPopupOnceParams, PopupHolder},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, EnumString, VariantNames, AsRefStr)]
|
#[derive(Clone, Copy, Eq, PartialEq, EnumString, VariantNames, AsRefStr)]
|
||||||
enum PosMode {
|
enum PosMode {
|
||||||
|
|
@ -420,3 +423,31 @@ impl View {
|
||||||
[width as u32, height as u32]
|
[width as u32, height as u32]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mount_popup(
|
||||||
|
frontend_tasks: FrontendTasks,
|
||||||
|
globals: WguiGlobals,
|
||||||
|
entry: DesktopEntry,
|
||||||
|
on_close_request: Box<dyn Fn()>,
|
||||||
|
set_holder: Box<dyn FnOnce(PopupHolder<View>)>,
|
||||||
|
) {
|
||||||
|
frontend_tasks
|
||||||
|
.clone()
|
||||||
|
.push(FrontendTask::MountPopupOnce(MountPopupOnceParams::new(
|
||||||
|
Translation::from_raw_text(&entry.app_name),
|
||||||
|
Box::new(move |data| {
|
||||||
|
let view = View::new(Params {
|
||||||
|
entry: entry.clone(),
|
||||||
|
globals: &globals,
|
||||||
|
layout: data.layout,
|
||||||
|
parent_id: data.id_content,
|
||||||
|
frontend_tasks: &frontend_tasks,
|
||||||
|
config: data.config,
|
||||||
|
on_launched: on_close_request,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
set_holder((data.handle, view));
|
||||||
|
Ok(())
|
||||||
|
}),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,140 @@
|
||||||
|
use wgui::{
|
||||||
|
assets::AssetPath,
|
||||||
|
globals::WguiGlobals,
|
||||||
|
i18n::Translation,
|
||||||
|
layout::{Layout, WidgetID},
|
||||||
|
parser::{Fetchable, ParseDocumentParams},
|
||||||
|
task::Tasks,
|
||||||
|
};
|
||||||
|
use wlx_common::async_executor::AsyncExecutor;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
frontend::{FrontendTask, FrontendTasks},
|
||||||
|
util::{
|
||||||
|
networking::{self},
|
||||||
|
popup_manager::{MountPopupOnceParams, PopupHolder},
|
||||||
|
wgui_simple,
|
||||||
|
},
|
||||||
|
views,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Params<'a> {
|
||||||
|
pub globals: &'a WguiGlobals,
|
||||||
|
pub layout: &'a mut Layout,
|
||||||
|
pub executor: &'a AsyncExecutor,
|
||||||
|
pub parent_id: WidgetID,
|
||||||
|
pub on_close_request: Box<dyn Fn()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Task {
|
||||||
|
SetSkymapCatalog(anyhow::Result<networking::skymap_catalog::SkymapCatalog>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct View {
|
||||||
|
id_parent: WidgetID,
|
||||||
|
id_list: WidgetID,
|
||||||
|
id_loading: WidgetID,
|
||||||
|
globals: WguiGlobals,
|
||||||
|
tasks: Tasks<Task>,
|
||||||
|
mounted_cells: Vec<views::skymap_list_cell::View>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl View {
|
||||||
|
async fn skymap_catalog_request_wrapper(tasks: Tasks<Task>, executor: AsyncExecutor) {
|
||||||
|
let res = networking::skymap_catalog::request_catalog(&executor).await;
|
||||||
|
tasks.push(Task::SetSkymapCatalog(res))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(par: Params) -> anyhow::Result<Self> {
|
||||||
|
let id_loading = wgui_simple::create_loading(wgui_simple::CreateLoadingParams {
|
||||||
|
layout: par.layout,
|
||||||
|
parent_id: par.parent_id,
|
||||||
|
with_text: true,
|
||||||
|
})?;
|
||||||
|
let tasks = Tasks::<Task>::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_list: WidgetID::default(),
|
||||||
|
id_loading,
|
||||||
|
tasks,
|
||||||
|
globals: par.globals.clone(),
|
||||||
|
mounted_cells: Vec::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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/download_skymaps.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 view_cell = views::skymap_list_cell::View::new(views::skymap_list_cell::Params {
|
||||||
|
id_parent: id_list,
|
||||||
|
layout,
|
||||||
|
entry,
|
||||||
|
})?;
|
||||||
|
self.mounted_cells.push(view_cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, layout: &mut Layout) -> anyhow::Result<()> {
|
||||||
|
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),
|
||||||
|
)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mount_popup(
|
||||||
|
frontend_tasks: FrontendTasks,
|
||||||
|
executor: AsyncExecutor,
|
||||||
|
globals: WguiGlobals,
|
||||||
|
on_close_request: Box<dyn Fn()>,
|
||||||
|
set_holder: Box<dyn FnOnce(PopupHolder<View>)>,
|
||||||
|
) {
|
||||||
|
frontend_tasks
|
||||||
|
.clone()
|
||||||
|
.push(FrontendTask::MountPopupOnce(MountPopupOnceParams::new(
|
||||||
|
Translation::from_translation_key("APP_SETTINGS.DOWNLOAD_SKYMAPS"),
|
||||||
|
Box::new(move |data| {
|
||||||
|
let view = View::new(Params {
|
||||||
|
globals: &globals,
|
||||||
|
layout: data.layout,
|
||||||
|
executor: &executor,
|
||||||
|
parent_id: data.id_content,
|
||||||
|
on_close_request,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
set_holder((data.handle, view));
|
||||||
|
Ok(())
|
||||||
|
}),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
@ -30,6 +30,7 @@ use wlx_common::async_executor::AsyncExecutor;
|
||||||
use crate::util::{
|
use crate::util::{
|
||||||
cached_fetcher::{self, CoverArt},
|
cached_fetcher::{self, CoverArt},
|
||||||
steam_utils::{self, AppID},
|
steam_utils::{self, AppID},
|
||||||
|
wgui_simple,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ViewCommon {
|
pub struct ViewCommon {
|
||||||
|
|
@ -48,6 +49,7 @@ pub struct Params<'a, 'b> {
|
||||||
pub struct View {
|
pub struct View {
|
||||||
pub button: Rc<ComponentButton>,
|
pub button: Rc<ComponentButton>,
|
||||||
id_image_parent: WidgetID,
|
id_image_parent: WidgetID,
|
||||||
|
id_loading: WidgetID,
|
||||||
app_name: String,
|
app_name: String,
|
||||||
app_id: AppID,
|
app_id: AppID,
|
||||||
}
|
}
|
||||||
|
|
@ -143,6 +145,8 @@ impl View {
|
||||||
layout: &mut Layout,
|
layout: &mut Layout,
|
||||||
cover_art: &CoverArt,
|
cover_art: &CoverArt,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
layout.remove_widget(self.id_loading);
|
||||||
|
|
||||||
if cover_art.compressed_image_data.is_empty() {
|
if cover_art.compressed_image_data.is_empty() {
|
||||||
// mount placeholder
|
// mount placeholder
|
||||||
let img = view_common.get_placeholder_image()?.clone();
|
let img = view_common.get_placeholder_image()?.clone();
|
||||||
|
|
@ -271,6 +275,12 @@ impl View {
|
||||||
rect_gradient_style(taffy::AlignSelf::End, 0.05),
|
rect_gradient_style(taffy::AlignSelf::End, 0.05),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let id_loading = wgui_simple::create_loading(wgui_simple::CreateLoadingParams {
|
||||||
|
layout: params.ess.layout,
|
||||||
|
parent_id: image_parent.id,
|
||||||
|
with_text: false,
|
||||||
|
})?;
|
||||||
|
|
||||||
// request cover image data from the internet or disk cache
|
// request cover image data from the internet or disk cache
|
||||||
params
|
params
|
||||||
.executor
|
.executor
|
||||||
|
|
@ -286,6 +296,7 @@ impl View {
|
||||||
id_image_parent: image_parent.id,
|
id_image_parent: image_parent.id,
|
||||||
app_name: params.manifest.name.clone(),
|
app_name: params.manifest.name.clone(),
|
||||||
app_id: params.manifest.app_id.clone(),
|
app_id: params.manifest.app_id.clone(),
|
||||||
|
id_loading,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use crate::{
|
||||||
frontend::{FrontendTask, FrontendTasks, SoundType},
|
frontend::{FrontendTask, FrontendTasks, SoundType},
|
||||||
util::{
|
util::{
|
||||||
cached_fetcher::{self, CoverArt},
|
cached_fetcher::{self, CoverArt},
|
||||||
|
popup_manager::{MountPopupOnceParams, PopupHolder},
|
||||||
steam_utils::{self, AppID, AppManifest},
|
steam_utils::{self, AppID, AppManifest},
|
||||||
},
|
},
|
||||||
views::game_cover,
|
views::game_cover,
|
||||||
|
|
@ -193,3 +194,32 @@ impl View {
|
||||||
(*self.on_launched)();
|
(*self.on_launched)();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mount_popup(
|
||||||
|
frontend_tasks: FrontendTasks,
|
||||||
|
executor: AsyncExecutor,
|
||||||
|
globals: WguiGlobals,
|
||||||
|
manifest: AppManifest,
|
||||||
|
on_close_request: Box<dyn Fn()>,
|
||||||
|
set_holder: Box<dyn FnOnce(PopupHolder<View>)>,
|
||||||
|
) {
|
||||||
|
frontend_tasks
|
||||||
|
.clone()
|
||||||
|
.push(FrontendTask::MountPopupOnce(MountPopupOnceParams::new(
|
||||||
|
Translation::from_raw_text(&manifest.name),
|
||||||
|
Box::new(move |data| {
|
||||||
|
let view = View::new(Params {
|
||||||
|
manifest: manifest.clone(),
|
||||||
|
executor: executor.clone(),
|
||||||
|
globals: &globals,
|
||||||
|
layout: data.layout,
|
||||||
|
parent_id: data.id_content,
|
||||||
|
frontend_tasks: &frontend_tasks,
|
||||||
|
on_launched: on_close_request,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
set_holder((data.handle, view));
|
||||||
|
Ok(())
|
||||||
|
}),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,13 @@ use wgui::{
|
||||||
use wlx_common::async_executor::AsyncExecutor;
|
use wlx_common::async_executor::AsyncExecutor;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
frontend::{FrontendTask, FrontendTasks},
|
frontend::FrontendTasks,
|
||||||
util::{
|
util::{
|
||||||
cached_fetcher::CoverArt,
|
cached_fetcher::CoverArt,
|
||||||
popup_manager::{MountPopupParams, PopupHandle},
|
popup_manager::PopupHolder,
|
||||||
steam_utils::{self, AppID, SteamUtils},
|
steam_utils::{self, AppID, SteamUtils},
|
||||||
},
|
},
|
||||||
views::{self, game_cover, game_launcher},
|
views::{self, game_cover},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
@ -51,7 +51,7 @@ pub struct GameCoverCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
view_launcher: Option<(PopupHandle, views::game_launcher::View)>,
|
view_launcher: Option<PopupHolder<views::game_launcher::View>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct View {
|
pub struct View {
|
||||||
|
|
@ -283,36 +283,17 @@ impl View {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn action_app_manifest_clicked(&mut self, manifest: steam_utils::AppManifest) -> anyhow::Result<()> {
|
fn action_app_manifest_clicked(&mut self, manifest: steam_utils::AppManifest) -> anyhow::Result<()> {
|
||||||
self.frontend_tasks.push(FrontendTask::MountPopup(MountPopupParams {
|
views::game_launcher::mount_popup(
|
||||||
title: Translation::from_raw_text(&manifest.name),
|
self.frontend_tasks.clone(),
|
||||||
on_content: {
|
self.executor.clone(),
|
||||||
|
self.globals.clone(),
|
||||||
|
manifest,
|
||||||
|
self.tasks.make_callback_box(Task::CloseLauncher),
|
||||||
|
Box::new({
|
||||||
let state = self.state.clone();
|
let state = self.state.clone();
|
||||||
let tasks = self.tasks.clone();
|
move |popup| state.borrow_mut().view_launcher = Some(popup)
|
||||||
let executor = self.executor.clone();
|
}),
|
||||||
let globals = self.globals.clone();
|
);
|
||||||
let frontend_tasks = self.frontend_tasks.clone();
|
|
||||||
|
|
||||||
Rc::new(move |data| {
|
|
||||||
let on_launched = {
|
|
||||||
let tasks = tasks.clone();
|
|
||||||
Box::new(move || tasks.push(Task::CloseLauncher))
|
|
||||||
};
|
|
||||||
|
|
||||||
let view = game_launcher::View::new(game_launcher::Params {
|
|
||||||
manifest: manifest.clone(),
|
|
||||||
executor: executor.clone(),
|
|
||||||
globals: &globals,
|
|
||||||
layout: data.layout,
|
|
||||||
parent_id: data.id_content,
|
|
||||||
frontend_tasks: &frontend_tasks,
|
|
||||||
on_launched,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
state.borrow_mut().view_launcher = Some((data.handle, view));
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
pub mod app_launcher;
|
pub mod app_launcher;
|
||||||
pub mod audio_settings;
|
pub mod audio_settings;
|
||||||
|
pub mod download_skymaps;
|
||||||
pub mod game_cover;
|
pub mod game_cover;
|
||||||
pub mod game_launcher;
|
pub mod game_launcher;
|
||||||
pub mod game_list;
|
pub mod game_list;
|
||||||
pub mod running_games_list;
|
pub mod running_games_list;
|
||||||
pub mod skymap_list;
|
pub mod skymap_list;
|
||||||
|
pub mod skymap_list_cell;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use wgui::{
|
use wgui::{
|
||||||
assets::AssetPath,
|
assets::AssetPath,
|
||||||
components::button::ComponentButton,
|
components::button::ComponentButton,
|
||||||
|
|
@ -9,12 +11,16 @@ use wgui::{
|
||||||
};
|
};
|
||||||
use wlx_common::{async_executor::AsyncExecutor, config_io};
|
use wlx_common::{async_executor::AsyncExecutor, config_io};
|
||||||
|
|
||||||
use crate::util::{networking, wgui_simple};
|
use crate::{
|
||||||
|
frontend::FrontendTasks,
|
||||||
|
util::{popup_manager::PopupHolder, wgui_simple},
|
||||||
|
views,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum Task {
|
enum Task {
|
||||||
DownloadSkymaps,
|
DownloadSkymaps,
|
||||||
SetSkymapCatalog(Result<networking::skymap_catalog::SkymapCatalog, String>),
|
ClosePopupDownloadSkymaps,
|
||||||
Refresh,
|
Refresh,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -22,6 +28,11 @@ pub struct Params<'a> {
|
||||||
pub globals: WguiGlobals,
|
pub globals: WguiGlobals,
|
||||||
pub layout: &'a mut Layout,
|
pub layout: &'a mut Layout,
|
||||||
pub parent_id: WidgetID,
|
pub parent_id: WidgetID,
|
||||||
|
pub frontend_tasks: &'a FrontendTasks,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct State {
|
||||||
|
popup_download_skymaps: Option<PopupHolder<views::download_skymaps::View>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct View {
|
pub struct View {
|
||||||
|
|
@ -29,6 +40,9 @@ pub struct View {
|
||||||
parser_state: ParserState,
|
parser_state: ParserState,
|
||||||
tasks: Tasks<Task>,
|
tasks: Tasks<Task>,
|
||||||
list_parent: WidgetID,
|
list_parent: WidgetID,
|
||||||
|
frontend_tasks: FrontendTasks,
|
||||||
|
globals: WguiGlobals,
|
||||||
|
state: Rc<RefCell<State>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl View {
|
impl View {
|
||||||
|
|
@ -55,14 +69,28 @@ impl View {
|
||||||
Task::Refresh,
|
Task::Refresh,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let state = Rc::new(RefCell::new(State {
|
||||||
|
popup_download_skymaps: None,
|
||||||
|
}));
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
parser_state,
|
parser_state,
|
||||||
tasks,
|
tasks,
|
||||||
list_parent,
|
list_parent,
|
||||||
|
frontend_tasks: params.frontend_tasks.clone(),
|
||||||
|
state,
|
||||||
|
globals: params.globals.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, layout: &mut Layout, executor: &AsyncExecutor) -> anyhow::Result<()> {
|
pub fn update(&mut self, layout: &mut Layout, executor: &AsyncExecutor) -> anyhow::Result<()> {
|
||||||
|
{
|
||||||
|
let mut state = self.state.borrow_mut();
|
||||||
|
if let Some(popup) = &mut state.popup_download_skymaps {
|
||||||
|
popup.1.update(layout)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let tasks = self.tasks.drain();
|
let tasks = self.tasks.drain();
|
||||||
if tasks.is_empty() {
|
if tasks.is_empty() {
|
||||||
|
|
@ -76,8 +104,8 @@ impl View {
|
||||||
Task::Refresh => {
|
Task::Refresh => {
|
||||||
self.refresh(layout)?;
|
self.refresh(layout)?;
|
||||||
}
|
}
|
||||||
Task::SetSkymapCatalog(skymap_catalog) => {
|
Task::ClosePopupDownloadSkymaps => {
|
||||||
log::info!("{:?}", skymap_catalog);
|
(*self.state.borrow_mut()).popup_download_skymaps = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -86,14 +114,19 @@ impl View {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn skymap_catalog_request_wrapper(tasks: Tasks<Task>, executor: AsyncExecutor) {
|
|
||||||
let res = networking::skymap_catalog::request_catalog(&executor).await;
|
|
||||||
tasks.push(Task::SetSkymapCatalog(res.map_err(|e| format!("{}", e))));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn download_skymaps(&mut self, executor: &AsyncExecutor) -> anyhow::Result<()> {
|
fn download_skymaps(&mut self, executor: &AsyncExecutor) -> anyhow::Result<()> {
|
||||||
let fut = View::skymap_catalog_request_wrapper(self.tasks.clone(), executor.clone());
|
views::download_skymaps::mount_popup(
|
||||||
executor.spawn(fut).detach();
|
self.frontend_tasks.clone(),
|
||||||
|
executor.clone(),
|
||||||
|
self.globals.clone(),
|
||||||
|
self.tasks.make_callback_box(Task::ClosePopupDownloadSkymaps),
|
||||||
|
Box::new({
|
||||||
|
let state = self.state.clone();
|
||||||
|
move |popup| {
|
||||||
|
state.borrow_mut().popup_download_skymaps = Some(popup);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
use wgui::{
|
||||||
|
assets::AssetPath,
|
||||||
|
i18n::Translation,
|
||||||
|
layout::{Layout, WidgetID},
|
||||||
|
parser::{Fetchable, ParseDocumentParams, ParserState},
|
||||||
|
widget::label::WidgetLabel,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::util::{networking, wgui_simple};
|
||||||
|
|
||||||
|
pub struct Params<'a> {
|
||||||
|
pub id_parent: WidgetID,
|
||||||
|
pub layout: &'a mut Layout,
|
||||||
|
pub entry: networking::skymap_catalog::SkymapCatalogEntry,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct View {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
parser_state: ParserState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl View {
|
||||||
|
pub fn new(par: Params) -> anyhow::Result<Self> {
|
||||||
|
let globals = par.layout.state.globals.clone();
|
||||||
|
let doc_params = &ParseDocumentParams {
|
||||||
|
globals: globals.clone(),
|
||||||
|
path: AssetPath::BuiltIn("gui/view/skymap_list_cell.xml"),
|
||||||
|
extra: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut parser_state = wgui::parser::parse_from_assets(&doc_params, par.layout, par.id_parent)?;
|
||||||
|
|
||||||
|
let data = parser_state.realize_template(&doc_params, "Cell", par.layout, par.id_parent, Default::default())?;
|
||||||
|
|
||||||
|
let id_image_preview = data.get_widget_id("image_preview")?;
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut label_title = data.fetch_widget_as::<WidgetLabel>(&par.layout.state, "label_title")?;
|
||||||
|
let mut label_author = data.fetch_widget_as::<WidgetLabel>(&par.layout.state, "label_author")?;
|
||||||
|
|
||||||
|
label_title.set_text_simple(&mut globals.get(), Translation::from_raw_text_string(par.entry.name));
|
||||||
|
label_author.set_text_simple(
|
||||||
|
&mut globals.get(),
|
||||||
|
Translation::from_raw_text_string(format!("by {}", par.entry.author)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
wgui_simple::create_loading(wgui_simple::CreateLoadingParams {
|
||||||
|
layout: par.layout,
|
||||||
|
parent_id: id_image_preview,
|
||||||
|
with_text: false,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Self { parser_state })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -133,16 +133,16 @@ impl Animations {
|
||||||
|
|
||||||
anim.pos_prev = anim.pos;
|
anim.pos_prev = anim.pos;
|
||||||
anim.pos = pos;
|
anim.pos = pos;
|
||||||
anim.call(state, alterables, 1.0);
|
|
||||||
|
|
||||||
if anim.last_tick {
|
if anim.last_tick {
|
||||||
|
anim.call(state, alterables, 1.0);
|
||||||
alterables.needs_redraw = true;
|
alterables.needs_redraw = true;
|
||||||
|
} else {
|
||||||
|
anim.ticks_remaining -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
anim.ticks_remaining -= 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.running_animations.retain(|anim| anim.ticks_remaining > 0);
|
self.running_animations.retain(|anim| !anim.last_tick);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process(&mut self, state: &LayoutState, alterables: &mut EventAlterables, alpha: f32) {
|
pub fn process(&mut self, state: &LayoutState, alterables: &mut EventAlterables, alpha: f32) {
|
||||||
|
|
|
||||||
|
|
@ -41,10 +41,17 @@ impl<TaskType: Clone + 'static> Tasks<TaskType> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_callback(&self, task: TaskType) -> Rc<dyn Fn()> {
|
pub fn make_callback_rc(&self, task: TaskType) -> Rc<dyn Fn()> {
|
||||||
let this = self.clone();
|
let this = self.clone();
|
||||||
Rc::new(move || {
|
Rc::new(move || {
|
||||||
this.push(task.clone());
|
this.push(task.clone());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn make_callback_box(&self, task: TaskType) -> Box<dyn Fn()> {
|
||||||
|
let this = self.clone();
|
||||||
|
Box::new(move || {
|
||||||
|
this.push(task.clone());
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue