diff --git a/dash-frontend/assets/gui/view/game_launcher.xml b/dash-frontend/assets/gui/view/game_launcher.xml new file mode 100644 index 0000000..6e214e5 --- /dev/null +++ b/dash-frontend/assets/gui/view/game_launcher.xml @@ -0,0 +1,19 @@ + + +
+
+
+
+
+ + \ No newline at end of file diff --git a/dash-frontend/assets/gui/view/popup_window.xml b/dash-frontend/assets/gui/view/popup_window.xml index c58377e..1f1f6fe 100644 --- a/dash-frontend/assets/gui/view/popup_window.xml +++ b/dash-frontend/assets/gui/view/popup_window.xml @@ -14,7 +14,6 @@ diff --git a/dash-frontend/src/tab/apps.rs b/dash-frontend/src/tab/apps.rs index 566f102..9b650cf 100644 --- a/dash-frontend/src/tab/apps.rs +++ b/dash-frontend/src/tab/apps.rs @@ -26,21 +26,16 @@ enum Task { } struct State { - launcher: Option<(PopupHandle, views::app_launcher::View)>, + view_launcher: Option<(PopupHandle, views::app_launcher::View)>, } pub struct TabApps { #[allow(dead_code)] - pub parser_state: ParserState, + parser_state: ParserState, - #[allow(dead_code)] state: Rc>, - - #[allow(dead_code)] entries: Vec, - #[allow(dead_code)] app_list: AppList, - tasks: Tasks, } @@ -54,11 +49,11 @@ impl Tab for TabApps { for task in self.tasks.drain() { match task { - Task::CloseLauncher => state.launcher = None, + Task::CloseLauncher => state.view_launcher = None, } } - if let Some((_, launcher)) = &mut state.launcher { + if let Some((_, launcher)) = &mut state.view_launcher { launcher.update(&mut frontend.layout, &mut frontend.interface)?; } Ok(()) @@ -105,7 +100,7 @@ fn on_app_click( on_launched, })?; - state.borrow_mut().launcher = Some((data.handle, view)); + state.borrow_mut().view_launcher = Some((data.handle, view)); Ok(()) }) }, @@ -129,7 +124,7 @@ impl TabApps { let globals = frontend.layout.state.globals.clone(); let tasks = Tasks::new(); - let state = Rc::new(RefCell::new(State { launcher: None })); + let state = Rc::new(RefCell::new(State { view_launcher: None })); let mut parser_state = wgui::parser::parse_from_assets(doc_params, &mut frontend.layout, parent_id)?; let app_list_parent = parser_state.fetch_widget(&frontend.layout.state, "app_list_parent")?; diff --git a/dash-frontend/src/tab/games.rs b/dash-frontend/src/tab/games.rs index 528d885..f09742d 100644 --- a/dash-frontend/src/tab/games.rs +++ b/dash-frontend/src/tab/games.rs @@ -23,10 +23,7 @@ impl Tab for TabGames { } fn update(&mut self, frontend: &mut Frontend) -> anyhow::Result<()> { - self - .view_game_list - .update(&mut frontend.layout, &mut frontend.executor)?; - + self.view_game_list.update(&mut frontend.layout, &frontend.executor)?; Ok(()) } } @@ -46,6 +43,7 @@ impl TabGames { let game_list_parent = state.get_widget_id("game_list_parent")?; let view_game_list = game_list::View::new(game_list::Params { + executor: frontend.executor.clone(), frontend_tasks: frontend.tasks.clone(), globals: frontend.layout.state.globals.clone(), layout: &mut frontend.layout, diff --git a/dash-frontend/src/tab/home.rs b/dash-frontend/src/tab/home.rs index 180abc3..d3d5d2f 100644 --- a/dash-frontend/src/tab/home.rs +++ b/dash-frontend/src/tab/home.rs @@ -40,7 +40,7 @@ fn configure_label_hello(common: &mut CallbackDataCommon, label_hello: Widget, s common.i18n().translate("HELLO").to_string() }; - let mut label_hello = label_hello.get_as_mut::().unwrap(); + let mut label_hello = label_hello.get_as::().unwrap(); label_hello.set_text(common, Translation::from_raw_text(&translated)); } diff --git a/dash-frontend/src/util/cached_fetcher.rs b/dash-frontend/src/util/cached_fetcher.rs new file mode 100644 index 0000000..d1a2ee3 --- /dev/null +++ b/dash-frontend/src/util/cached_fetcher.rs @@ -0,0 +1,103 @@ +use anyhow::Context; +use serde::Deserialize; +use wlx_common::cache_dir; + +use crate::util::{http_client, steam_utils::AppID, various::AsyncExecutor}; + +pub struct CoverArt { + // can be empty in case if data couldn't be fetched (use a fallback image then) + pub compressed_image_data: Vec, +} + +pub async fn request_image(executor: AsyncExecutor, app_id: AppID) -> anyhow::Result { + let cache_file_path = format!("cover_arts/{}.bin", app_id); + + // check if file already exists in cache directory + if let Some(data) = cache_dir::get_data(&cache_file_path).await { + return Ok(CoverArt { + compressed_image_data: data, + }); + } + + let url = format!( + "https://shared.steamstatic.com/store_item_assets/steam/apps/{}/library_600x900.jpg", + app_id + ); + + match http_client::get(&executor, &url).await { + Ok(response) => { + log::info!("Success"); + cache_dir::set_data(&cache_file_path, &response.data).await?; + Ok(CoverArt { + compressed_image_data: response.data, + }) + } + Err(e) => { + // fetch failed, write an empty file + log::error!("CoverArtFetcher: failed fetch for AppID {}: {}", app_id, e); + cache_dir::set_data(&cache_file_path, &[]).await?; + Ok(CoverArt { + compressed_image_data: Vec::new(), + }) + } + } +} + +#[derive(Deserialize, Clone)] +pub struct AppDetailsJSONData { + #[allow(dead_code)] + pub r#type: String, // "game" + #[allow(dead_code)] + pub name: String, // "Half-Life 3" + #[allow(dead_code)] + pub is_free: Option, // "false" + pub detailed_description: Option, // + pub short_description: Option, // + pub developers: Vec, // ["Valve"] +} + +async fn get_app_details_json_internal( + executor: AsyncExecutor, + cache_file_path: &str, + app_id: AppID, +) -> anyhow::Result { + // check if file already exists in cache directory + if let Some(data) = cache_dir::get_data(cache_file_path).await { + return Ok(serde_json::from_value(serde_json::from_slice(&data)?)?); + } + + // Fetch from Steam API + log::info!("Fetching app detail ID {}", app_id); + let url = format!("https://store.steampowered.com/api/appdetails?appids={}", app_id); + let response = http_client::get(&executor, &url).await?; + let res_utf8 = String::from_utf8(response.data)?; + let root = serde_json::from_str::(&res_utf8)?; + let body = root.get(&app_id).context("invalid body")?; + + if !body.get("success").is_some_and(|v| v.as_bool().unwrap_or(false)) { + anyhow::bail!("Failed"); + } + + let data = body.get("data").context("data null")?; + + let data_bytes = serde_json::to_vec(&data)?; + let app_details: AppDetailsJSONData = serde_json::from_value(data.clone())?; + + // cache for future use + cache_dir::set_data(cache_file_path, &data_bytes).await?; + + Ok(app_details) +} + +pub async fn get_app_details_json(executor: AsyncExecutor, app_id: AppID) -> Option { + let cache_file_path = format!("app_details/{}.json", app_id); + + match get_app_details_json_internal(executor, &cache_file_path, app_id).await { + Ok(r) => Some(r), + Err(e) => { + log::error!("Failed to get app details: {:?}", e); + let _ = cache_dir::set_data(&cache_file_path, &[]).await; // write empty data + None + } + } +} diff --git a/dash-frontend/src/util/cover_art_fetcher.rs b/dash-frontend/src/util/cover_art_fetcher.rs deleted file mode 100644 index 734604f..0000000 --- a/dash-frontend/src/util/cover_art_fetcher.rs +++ /dev/null @@ -1,42 +0,0 @@ -use wlx_common::cache_dir; - -use crate::util::{http_client, steam_utils::AppID, various::AsyncExecutor}; - -pub struct CoverArt { - // can be empty in case if data couldn't be fetched (use a fallback image then) - pub compressed_image_data: Vec, -} - -pub async fn request_image(executor: AsyncExecutor, app_id: AppID) -> anyhow::Result { - let cache_file_path = format!("cover_arts/{}.bin", app_id); - - // check if file already exists in cache directory - if let Some(data) = cache_dir::get_data(&cache_file_path).await { - return Ok(CoverArt { - compressed_image_data: data, - }); - } - - let url = format!( - "https://shared.steamstatic.com/store_item_assets/steam/apps/{}/library_600x900.jpg", - app_id - ); - - match http_client::get(&executor, &url).await { - Ok(response) => { - log::info!("Success"); - cache_dir::set_data(&cache_file_path, &response.data).await?; - Ok(CoverArt { - compressed_image_data: response.data, - }) - } - Err(e) => { - // fetch failed, write an empty file - log::error!("CoverArtFetcher: failed fetch for AppID {}: {}", app_id, e); - cache_dir::set_data(&cache_file_path, &[]).await?; - Ok(CoverArt { - compressed_image_data: Vec::new(), - }) - } - } -} diff --git a/dash-frontend/src/util/mod.rs b/dash-frontend/src/util/mod.rs index 3577d01..4314dbe 100644 --- a/dash-frontend/src/util/mod.rs +++ b/dash-frontend/src/util/mod.rs @@ -1,4 +1,4 @@ -pub mod cover_art_fetcher; +pub mod cached_fetcher; pub mod desktop_finder; pub mod http_client; pub mod pactl_wrapper; diff --git a/dash-frontend/src/views/game_launcher.rs b/dash-frontend/src/views/game_launcher.rs new file mode 100644 index 0000000..dcfc322 --- /dev/null +++ b/dash-frontend/src/views/game_launcher.rs @@ -0,0 +1,158 @@ +use crate::{ + frontend::{FrontendTask, FrontendTasks}, + util::{ + cached_fetcher::{self}, + steam_utils::{AppID, AppManifest}, + various::AsyncExecutor, + }, +}; +use wgui::{ + assets::AssetPath, + components::button::ComponentButton, + globals::WguiGlobals, + i18n::Translation, + layout::{Layout, WidgetID}, + parser::{Fetchable, ParseDocumentParams, ParserState}, + task::Tasks, + widget::label::WidgetLabel, +}; + +#[derive(Clone)] +enum Task { + FillAppDetails(cached_fetcher::AppDetailsJSONData), + Launch, +} + +pub struct Params<'a> { + pub globals: &'a WguiGlobals, + pub executor: AsyncExecutor, + pub manifest: AppManifest, + pub layout: &'a mut Layout, + pub parent_id: WidgetID, + pub frontend_tasks: &'a FrontendTasks, + pub on_launched: Box, +} +pub struct View { + #[allow(dead_code)] + state: ParserState, + tasks: Tasks, + on_launched: Box, + frontend_tasks: FrontendTasks, + + #[allow(dead_code)] + id_cover_art_parent: WidgetID, + #[allow(dead_code)] + executor: AsyncExecutor, + #[allow(dead_code)] + globals: WguiGlobals, + #[allow(dead_code)] + manifest: AppManifest, +} + +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 { + return; + }; + + tasks.push(Task::FillAppDetails(details)); + } + + pub fn new(params: Params) -> anyhow::Result { + let doc_params = &ParseDocumentParams { + globals: params.globals.clone(), + path: AssetPath::BuiltIn("gui/view/game_launcher.xml"), + extra: Default::default(), + }; + + let state = wgui::parser::parse_from_assets(doc_params, params.layout, params.parent_id)?; + + let mut label_title = state.fetch_widget_as::(¶ms.layout.state, "label_title")?; + label_title.set_text_simple( + &mut params.globals.get(), + Translation::from_raw_text(¶ms.manifest.name), + ); + + let tasks = Tasks::new(); + + // fetch details from the web + let fut = View::fetch_details(params.executor.clone(), tasks.clone(), params.manifest.app_id.clone()); + params.executor.spawn(fut).detach(); + + let id_cover_art_parent = state.get_widget_id("cover_art_parent")?; + let btn_launch = state.fetch_component_as::("btn_launch")?; + + tasks.handle_button(&btn_launch, Task::Launch); + + Ok(Self { + state, + tasks, + on_launched: params.on_launched, + id_cover_art_parent, + frontend_tasks: params.frontend_tasks.clone(), + executor: params.executor.clone(), + globals: params.globals.clone(), + manifest: params.manifest, + }) + } + + 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(), + } + } + } + + Ok(()) + } + + fn action_fill_app_details( + &mut self, + layout: &mut Layout, + mut details: cached_fetcher::AppDetailsJSONData, + ) -> anyhow::Result<()> { + let mut c = layout.start_common(); + + { + let label_author = self.state.fetch_widget(&c.layout.state, "label_author")?.widget; + let label_description = self.state.fetch_widget(&c.layout.state, "label_description")?.widget; + + if let Some(developer) = details.developers.pop() { + label_author + .cast::()? + .set_text(&mut c.common(), Translation::from_raw_text_string(developer)); + } + + let desc = if let Some(desc) = &details.short_description { + Some(desc) + } else if let Some(desc) = &details.detailed_description { + Some(desc) + } else { + None + }; + + if let Some(desc) = desc { + label_description + .cast::()? + .set_text(&mut c.common(), Translation::from_raw_text(desc)); + } + } + + c.finish()?; + Ok(()) + } + + fn action_launch(&mut self) { + self + .frontend_tasks + .push(FrontendTask::PushToast(Translation::from_raw_text("Game launch TODO"))); + (*self.on_launched)(); + } +} diff --git a/dash-frontend/src/views/game_list.rs b/dash-frontend/src/views/game_list.rs index 2ddbfad..fa2fc45 100644 --- a/dash-frontend/src/views/game_list.rs +++ b/dash-frontend/src/views/game_list.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, rc::Rc}; +use std::{cell::RefCell, collections::HashMap, rc::Rc}; use wgui::{ assets::AssetPath, @@ -34,43 +34,51 @@ use wgui::{ use crate::{ frontend::{FrontendTask, FrontendTasks}, util::{ - cover_art_fetcher::{self, CoverArt}, - popup_manager::MountPopupParams, + cached_fetcher::{self, CoverArt}, + popup_manager::{MountPopupParams, PopupHandle}, steam_utils::{self, AppID, AppManifest, SteamUtils}, various::AsyncExecutor, }, + views::{self, game_launcher}, }; #[derive(Clone)] enum Task { AppManifestClicked(steam_utils::AppManifest), SetCoverArt((AppID, Rc)), + CloseLauncher, Refresh, } pub struct Params<'a> { pub globals: WguiGlobals, + pub executor: AsyncExecutor, pub frontend_tasks: FrontendTasks, pub layout: &'a mut Layout, pub parent_id: WidgetID, } -struct Cell { +pub struct Cell { image_parent: WidgetID, manifest: AppManifest, } +struct State { + view_launcher: Option<(PopupHandle, views::game_launcher::View)>, +} + pub struct View { #[allow(dead_code)] - pub parser_state: ParserState, + parser_state: ParserState, tasks: Tasks, frontend_tasks: FrontendTasks, globals: WguiGlobals, id_list_parent: WidgetID, steam_utils: steam_utils::SteamUtils, - cells: HashMap, img_placeholder: Option, + executor: AsyncExecutor, + state: Rc>, } impl View { @@ -99,6 +107,8 @@ impl View { steam_utils, cells: HashMap::new(), img_placeholder: None, + state: Rc::new(RefCell::new(State { view_launcher: None })), + executor: params.executor, }) } @@ -113,10 +123,16 @@ impl View { Task::Refresh => self.refresh(layout, executor)?, Task::AppManifestClicked(manifest) => self.action_app_manifest_clicked(manifest)?, Task::SetCoverArt((app_id, cover_art)) => self.action_set_cover_art(layout, &app_id, cover_art)?, + Task::CloseLauncher => self.state.borrow_mut().view_launcher = None, } } } + let mut state = self.state.borrow_mut(); + if let Some((_, view)) = &mut state.view_launcher { + view.update(layout)?; + } + Ok(()) } } @@ -131,8 +147,12 @@ const BORDER_COLOR_HOVERED: drawing::Color = drawing::Color::new(1.0, 1.0, 1.0, const GAME_COVER_SIZE_X: f32 = 140.0; const GAME_COVER_SIZE_Y: f32 = 210.0; -async fn request_cover_image(executor: AsyncExecutor, manifest: steam_utils::AppManifest, tasks: Tasks) { - let cover_art = match cover_art_fetcher::request_image(executor, manifest.app_id.clone()).await { +async fn request_cover_image( + executor: AsyncExecutor, + manifest: steam_utils::AppManifest, + on_loaded: Box, +) { + let cover_art = match cached_fetcher::request_image(executor, manifest.app_id.clone()).await { Ok(cover_art) => cover_art, Err(e) => { log::error!("request_cover_image failed: {:?}", e); @@ -140,15 +160,15 @@ async fn request_cover_image(executor: AsyncExecutor, manifest: steam_utils::App } }; - tasks.push(Task::SetCoverArt((manifest.app_id, Rc::from(cover_art)))); + on_loaded(cover_art) } -fn construct_game_cover( +pub fn construct_game_cover( ess: &mut ConstructEssentials, executor: &AsyncExecutor, - tasks: &Tasks, _globals: &WguiGlobals, manifest: &steam_utils::AppManifest, + on_loaded: Box, ) -> anyhow::Result<(WidgetPair, Rc, Cell)> { let (widget_button, button) = components::button::construct( ess, @@ -257,7 +277,7 @@ fn construct_game_cover( // request cover image data from the internet or disk cache executor - .spawn(request_cover_image(executor.clone(), manifest.clone(), tasks.clone())) + .spawn(request_cover_image(executor.clone(), manifest.clone(), on_loaded)) .detach(); Ok(( @@ -279,7 +299,15 @@ fn fill_game_list( tasks: &Tasks, ) -> anyhow::Result<()> { for manifest in &games.manifests { - let (_, button, cell) = construct_game_cover(ess, executor, tasks, globals, manifest)?; + let on_loaded = { + let app_id = manifest.app_id.clone(); + let tasks = tasks.clone(); + move |cover_art: CoverArt| { + tasks.push(Task::SetCoverArt((app_id, Rc::from(cover_art)))); + } + }; + + let (_, button, cell) = construct_game_cover(ess, executor, globals, manifest, Box::new(on_loaded))?; button.on_click({ let tasks = tasks.clone(); @@ -352,8 +380,29 @@ impl View { self.frontend_tasks.push(FrontendTask::MountPopup(MountPopupParams { title: Translation::from_raw_text(&manifest.name), on_content: { - Rc::new(move |_data| { - // todo + let state = self.state.clone(); + let tasks = self.tasks.clone(); + 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(()) }) }, diff --git a/dash-frontend/src/views/mod.rs b/dash-frontend/src/views/mod.rs index 87c536a..c853011 100644 --- a/dash-frontend/src/views/mod.rs +++ b/dash-frontend/src/views/mod.rs @@ -1,5 +1,6 @@ pub mod app_launcher; pub mod audio_settings; +pub mod game_launcher; pub mod game_list; pub mod process_list; pub mod window_list; diff --git a/uidev/src/testbed/testbed_generic.rs b/uidev/src/testbed/testbed_generic.rs index 98d13ba..b0f7116 100644 --- a/uidev/src/testbed/testbed_generic.rs +++ b/uidev/src/testbed/testbed_generic.rs @@ -51,7 +51,7 @@ fn button_click_callback( ) -> ButtonClickCallback { Box::new(move |common, _e| { label - .get_as_mut::() + .get_as::() .unwrap() .set_text(common, Translation::from_raw_text(text)); @@ -161,7 +161,7 @@ impl TestbedGeneric { let cb_first = state.fetch_component_as::("cb_first")?; let label = label_cur_option.widget.clone(); cb_first.on_toggle(Box::new(move |common, e| { - let mut widget = label.get_as_mut::().unwrap(); + let mut widget = label.get_as::().unwrap(); let text = format!("checkbox toggle: {}", e.checked); widget.set_text(common, Translation::from_raw_text(&text)); Ok(()) diff --git a/wgui/src/components/button.rs b/wgui/src/components/button.rs index 78eeeae..cb1f924 100644 --- a/wgui/src/components/button.rs +++ b/wgui/src/components/button.rs @@ -340,6 +340,7 @@ fn register_event_mouse_press(state: Rc>, listeners: &mut EventLi if state.hovered { state.down = true; state.last_pressed = Instant::now(); + state.active_tooltip = None; Ok(EventResult::Consumed) } else { Ok(EventResult::Pass) diff --git a/wgui/src/layout.rs b/wgui/src/layout.rs index 2fbf5af..1fe1dd5 100644 --- a/wgui/src/layout.rs +++ b/wgui/src/layout.rs @@ -14,6 +14,7 @@ use crate::{ widget::{self, EventParams, EventResult, WidgetObj, WidgetState, WidgetStateFlags, div::WidgetDiv}, }; +use anyhow::Context; use glam::{Vec2, vec2}; use slotmap::{HopSlotMap, SecondaryMap, new_key_type}; use taffy::{NodeId, TaffyTree, TraversePartialTree}; @@ -31,10 +32,14 @@ impl Widget { Self(Rc::new(RefCell::new(widget_state))) } - pub fn get_as_mut(&self) -> Option> { + pub fn get_as(&self) -> Option> { RefMut::filter_map(self.0.borrow_mut(), |w| w.obj.get_as_mut::()).ok() } + pub fn cast(&self) -> anyhow::Result> { + self.get_as().context("Widget cast failed") + } + pub fn downgrade(&self) -> WeakWidget { WeakWidget(Rc::downgrade(&self.0)) } @@ -65,7 +70,7 @@ impl WidgetMap { } pub fn get_as(&self, handle: WidgetID) -> Option> { - self.0.get(handle)?.get_as_mut::() + self.0.get(handle)?.get_as::() } pub fn get(&self, handle: WidgetID) -> Option<&Widget> { @@ -97,7 +102,7 @@ impl WidgetMap { return; }; - if let Some(mut casted) = widget.get_as_mut::() { + if let Some(mut casted) = widget.get_as::() { func(&mut casted); } } diff --git a/wgui/src/parser/mod.rs b/wgui/src/parser/mod.rs index 77684ba..0c5a501 100644 --- a/wgui/src/parser/mod.rs +++ b/wgui/src/parser/mod.rs @@ -9,7 +9,7 @@ mod widget_rectangle; mod widget_sprite; use crate::{ - assets::{normalize_path, AssetPath, AssetPathOwned}, + assets::{AssetPath, AssetPathOwned, normalize_path}, components::{Component, ComponentWeak}, drawing::{self}, globals::WguiGlobals, @@ -183,7 +183,7 @@ impl Fetchable for ParserData { .ok_or_else(|| anyhow::anyhow!("fetch_widget_as({id}): widget not found"))?; let casted = widget - .get_as_mut::() + .get_as::() .ok_or_else(|| anyhow::anyhow!("fetch_widget_as({id}): failed to cast"))?; Ok(casted) @@ -1002,7 +1002,7 @@ impl CustomAttribsInfo<'_> { } pub fn get_widget_as(&self) -> Option> { - self.widgets.get(self.widget_id)?.get_as_mut::() + self.widgets.get(self.widget_id)?.get_as::() } pub fn get_value(&self, attrib_name: &str) -> Option> { diff --git a/wlx-overlay-s/src/overlays/custom.rs b/wlx-overlay-s/src/overlays/custom.rs index ae8290b..7b464d0 100644 --- a/wlx-overlay-s/src/overlays/custom.rs +++ b/wlx-overlay-s/src/overlays/custom.rs @@ -105,7 +105,7 @@ fn apply_custom_command( label.set_text(&mut com, Translation::from_raw_text(text)); } else if let Ok(button) = panel .parser_state - .fetch_component_as::(&element) + .fetch_component_as::(element) { button.set_text(&mut com, Translation::from_raw_text(text)); } else { @@ -118,15 +118,15 @@ fn apply_custom_command( .fetch_widget(&panel.layout.state, element) { let content = CustomGlyphContent::from_assets( - &mut app.wgui_globals, - wgui::assets::AssetPath::File(&path), + &app.wgui_globals, + wgui::assets::AssetPath::File(path), ) .context("Could not load content from supplied path.")?; let data = CustomGlyphData::new(content); - if let Some(mut sprite) = pair.widget.get_as_mut::() { + if let Some(mut sprite) = pair.widget.get_as::() { sprite.set_content(&mut com, Some(data)); - } else if let Some(mut image) = pair.widget.get_as_mut::() { + } else if let Some(mut image) = pair.widget.get_as::() { image.set_content(&mut com, Some(data)); } else { anyhow::bail!("No or with such id."); @@ -136,18 +136,18 @@ fn apply_custom_command( } } ModifyPanelCommand::SetColor(color) => { - let color = parse_color_hex(&color) + let color = parse_color_hex(color) .context("Invalid color format, must be a html hex color!")?; if let Ok(pair) = panel .parser_state .fetch_widget(&panel.layout.state, element) { - if let Some(mut rect) = pair.widget.get_as_mut::() { + if let Some(mut rect) = pair.widget.get_as::() { rect.set_color(&mut com, color); - } else if let Some(mut label) = pair.widget.get_as_mut::() { + } else if let Some(mut label) = pair.widget.get_as::() { label.set_color(&mut com, color, true); - } else if let Some(mut sprite) = pair.widget.get_as_mut::() { + } else if let Some(mut sprite) = pair.widget.get_as::() { sprite.set_color(&mut com, color); } else { anyhow::bail!("No or