diff --git a/dash-frontend/src/tab/apps.rs b/dash-frontend/src/tab/apps.rs index c735c0e4..cf093f84 100644 --- a/dash-frontend/src/tab/apps.rs +++ b/dash-frontend/src/tab/apps.rs @@ -23,12 +23,10 @@ use crate::{ }; #[derive(Clone)] -enum Task { - CloseLauncher, -} +enum Task {} struct State { - view_launcher: Option>, + view_launcher: PopupHolder, } pub struct TabApps { @@ -47,21 +45,17 @@ impl Tab for TabApps { } fn update(&mut self, frontend: &mut Frontend, _time_ms: u32, data: &mut T) -> anyhow::Result<()> { - let mut state = self.state.borrow_mut(); + let state = self.state.borrow_mut(); for task in self.tasks.drain() { - match task { - Task::CloseLauncher => state.view_launcher = None, - } + match task {} } - self - .app_list - .tick(frontend, &self.state, &self.tasks, &mut self.parser_state)?; + self.app_list.tick(frontend, &self.state, &mut self.parser_state)?; - if let Some((_, launcher)) = &mut state.view_launcher { - launcher.update(&mut frontend.interface, data)?; - } + state + .view_launcher + .with_view_res(|view| view.update(&mut frontend.interface, data))?; Ok(()) } } @@ -79,18 +73,13 @@ fn on_app_click( globals: WguiGlobals, entry: DesktopEntry, state: Rc>, - tasks: Tasks, ) -> ButtonClickCallback { Rc::new(move |_common, _evt| { views::app_launcher::mount_popup( frontend_tasks.clone(), globals.clone(), entry.clone(), - tasks.make_callback_box(Task::CloseLauncher), - Box::new({ - let state = state.clone(); - move |popup| state.borrow_mut().view_launcher = Some(popup) - }), + state.borrow_mut().view_launcher.clone(), ); Ok(()) }) @@ -108,7 +97,9 @@ impl TabApps { pub fn new(frontend: &mut Frontend, parent_id: WidgetID, data: &mut T) -> anyhow::Result { let globals = frontend.layout.state.globals.clone(); let tasks = Tasks::new(); - let state = Rc::new(RefCell::new(State { view_launcher: None })); + let state = Rc::new(RefCell::new(State { + view_launcher: Default::default(), + })); let parser_state = wgui::parser::parse_from_assets(&doc_params(globals.clone()), &mut frontend.layout, parent_id)?; let app_list_parent = parser_state.fetch_widget(&frontend.layout.state, "app_list_parent")?; @@ -313,7 +304,6 @@ impl AppList { &mut self, frontend: &mut Frontend, state: &Rc>, - tasks: &Tasks, parser_state: &mut ParserState, ) -> anyhow::Result<()> { // load 4 entries for a single frame at most @@ -327,7 +317,6 @@ impl AppList { globals.clone(), entry.clone(), state.clone(), - tasks.clone(), )); } else { break; diff --git a/dash-frontend/src/util/popup_manager.rs b/dash-frontend/src/util/popup_manager.rs index 6f61b947..0fe6c60f 100644 --- a/dash-frontend/src/util/popup_manager.rs +++ b/dash-frontend/src/util/popup_manager.rs @@ -34,16 +34,105 @@ pub struct MountedPopup { frontend_tasks: FrontendTasks, } +#[derive(Default)] struct MountedPopupState { mounted_popup: Option, } -#[derive(Clone)] +#[derive(Default, Clone)] pub struct PopupHandle { state: Rc>, } -pub type PopupHolder = (PopupHandle, ViewType); +struct PopupHolderState { + popup_handle: PopupHandle, + view: Option, +} + +// we can't use #[derive(Default)] due to the fact that ViewType can't be Default. +impl Default for PopupHolderState { + fn default() -> Self { + Self { + popup_handle: Default::default(), + view: Default::default(), + } + } +} + +pub struct PopupHolder { + state: Rc>>, +} + +impl Default for PopupHolder { + fn default() -> Self { + Self { + state: Rc::new(RefCell::new(PopupHolderState::default())), + } + } +} + +// we can't derive(Clone) due to the fact that ViewType is non-cloneable +impl Clone for PopupHolder { + fn clone(&self) -> Self { + Self { + state: self.state.clone(), + } + } +} + +impl PopupHolder { + pub fn close(&self) { + let mut state = self.state.borrow_mut(); + state.view = None; + state.popup_handle.close(); + } + + pub fn set_view(&self, handle: PopupHandle, view: ViewType) { + let mut state = self.state.borrow_mut(); + state.view = Some(view); + state.popup_handle = handle; + } + + // Get underlying ViewType object in a closure and return its value + // example usage: + // + // ```rs + // holder.with_view(|view| { + // view.foo(); + // }) + // ``` + // + pub fn with_view(&self, f: F) -> Option + where + F: FnOnce(&mut ViewType) -> R, + { + let mut state = self.state.borrow_mut(); + if let Some(view) = state.view.as_mut() { + Some(f(view)) + } else { + None + } + } + + // Same as with_view, but the closure expects a simple anyhow::Result<()> type + pub fn with_view_res(&self, f: F) -> anyhow::Result<()> + where + F: FnOnce(&mut ViewType) -> anyhow::Result<()>, + { + if let Some(res) = self.with_view(f) { + return res; + } + Ok(()) + } + + pub fn get_close_callback(&self) -> Box + where + ViewType: 'static, + { + let this = self.clone(); + Box::new(move || this.close()) + } +} impl PopupHandle { pub fn close(&self) { diff --git a/dash-frontend/src/views/app_launcher.rs b/dash-frontend/src/views/app_launcher.rs index 2d419962..b8179ce8 100644 --- a/dash-frontend/src/views/app_launcher.rs +++ b/dash-frontend/src/views/app_launcher.rs @@ -424,13 +424,7 @@ impl View { } } -pub fn mount_popup( - frontend_tasks: FrontendTasks, - globals: WguiGlobals, - entry: DesktopEntry, - on_close_request: Box, - set_holder: Box)>, -) { +pub fn mount_popup(frontend_tasks: FrontendTasks, globals: WguiGlobals, entry: DesktopEntry, popup: PopupHolder) { frontend_tasks .clone() .push(FrontendTask::MountPopupOnce(MountPopupOnceParams::new( @@ -443,10 +437,10 @@ pub fn mount_popup( parent_id: data.id_content, frontend_tasks: &frontend_tasks, config: data.config, - on_launched: on_close_request, + on_launched: popup.get_close_callback(), })?; - set_holder((data.handle, view)); + popup.set_view(data.handle, view); Ok(()) }), ))); diff --git a/dash-frontend/src/views/game_launcher.rs b/dash-frontend/src/views/game_launcher.rs index d27e1049..36581044 100644 --- a/dash-frontend/src/views/game_launcher.rs +++ b/dash-frontend/src/views/game_launcher.rs @@ -198,8 +198,7 @@ pub fn mount_popup( executor: AsyncExecutor, globals: WguiGlobals, manifest: AppManifest, - on_close_request: Box, - set_holder: Box)>, + popup: PopupHolder, ) { frontend_tasks .clone() @@ -213,10 +212,10 @@ pub fn mount_popup( layout: data.layout, parent_id: data.id_content, frontend_tasks: &frontend_tasks, - on_launched: on_close_request, + on_launched: popup.get_close_callback(), })?; - set_holder((data.handle, view)); + popup.set_view(data.handle, view); Ok(()) }), ))); diff --git a/dash-frontend/src/views/game_list.rs b/dash-frontend/src/views/game_list.rs index 150381b4..89878143 100644 --- a/dash-frontend/src/views/game_list.rs +++ b/dash-frontend/src/views/game_list.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, collections::HashMap, rc::Rc}; +use std::{collections::HashMap, rc::Rc}; use wgui::{ assets::AssetPath, @@ -29,7 +29,6 @@ use crate::{ enum Task { AppManifestClicked(steam_utils::AppManifest), SetCoverArt(AppID, Rc), - CloseLauncher, LoadManifests, FillPage(u32), PrevPage, @@ -50,10 +49,6 @@ pub struct GameCoverCell { view_cover: game_cover::View, } -struct State { - view_launcher: Option>, -} - pub struct View { #[allow(dead_code)] parser_state: ParserState, @@ -63,12 +58,12 @@ pub struct View { id_list_parent: WidgetID, game_cover_view_common: game_cover::ViewCommon, executor: AsyncExecutor, - state: Rc>, mounted_game_covers: HashMap, all_manifests: Vec, cur_page: u32, page_count: u32, id_label_page: WidgetID, + view_launcher: PopupHolder, } impl View { @@ -105,12 +100,12 @@ impl View { id_list_parent: list_parent.id, mounted_game_covers: HashMap::new(), game_cover_view_common: game_cover::ViewCommon::new(params.globals.clone()), - state: Rc::new(RefCell::new(State { view_launcher: None })), executor: params.executor, all_manifests: Vec::new(), cur_page: 0, page_count: 0, id_label_page, + view_launcher: Default::default(), }) } @@ -131,17 +126,13 @@ impl View { 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::CloseLauncher => self.state.borrow_mut().view_launcher = None, Task::PrevPage => self.page_prev(), Task::NextPage => self.page_next(), } } } - let mut state = self.state.borrow_mut(); - if let Some((_, view)) = &mut state.view_launcher { - view.update(layout)?; - } + self.view_launcher.with_view_res(|view| view.update(layout))?; Ok(()) } @@ -286,11 +277,7 @@ impl View { self.executor.clone(), self.globals.clone(), manifest, - self.tasks.make_callback_box(Task::CloseLauncher), - Box::new({ - let state = self.state.clone(); - move |popup| state.borrow_mut().view_launcher = Some(popup) - }), + self.view_launcher.clone(), ); Ok(()) diff --git a/dash-frontend/src/views/remote_skymap_downloader.rs b/dash-frontend/src/views/remote_skymap_downloader.rs index 3b178f3b..c4dd89c0 100644 --- a/dash-frontend/src/views/remote_skymap_downloader.rs +++ b/dash-frontend/src/views/remote_skymap_downloader.rs @@ -169,8 +169,7 @@ pub fn mount_popup( globals: WguiGlobals, entry: networking::skymap_catalog::SkymapCatalogEntry, preview_image: Option, - on_close_request: Box, - set_holder: Box)>, + popup: PopupHolder, ) { frontend_tasks .clone() @@ -183,11 +182,11 @@ pub fn mount_popup( executor: &executor, parent_id: data.id_content, entry, - on_close_request, + on_close_request: popup.get_close_callback(), preview_image, })?; - set_holder((data.handle, view)); + popup.set_view(data.handle, view); Ok(()) }), ))); diff --git a/dash-frontend/src/views/remote_skymap_list.rs b/dash-frontend/src/views/remote_skymap_list.rs index 622ba58f..a6544760 100644 --- a/dash-frontend/src/views/remote_skymap_list.rs +++ b/dash-frontend/src/views/remote_skymap_list.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, rc::Rc}; +use std::rc::Rc; use wgui::{ assets::AssetPath, @@ -38,7 +38,6 @@ enum Task { SetSkymapCatalog(Rc>), SetSkymapPreview((SkymapUuid, Option)), ShowRemoteSkymapDownloader(SkymapUuid), - CloseRemoteSkymapDownloader, } struct MountedCell { @@ -46,10 +45,6 @@ struct MountedCell { view: views::skymap_list_cell::View, } -pub struct State { - popup_remote_skymap_downloader: Option>, -} - pub struct View { id_parent: WidgetID, id_list: WidgetID, @@ -60,7 +55,7 @@ pub struct View { executor: AsyncExecutor, frontend_tasks: FrontendTasks, catalog: Option, - state: Rc>, + popup_remote_skymap_downloader: PopupHolder, } impl View { @@ -88,9 +83,7 @@ impl View { executor: par.executor.clone(), frontend_tasks: par.frontend_tasks, catalog: None, - state: Rc::new(RefCell::new(State { - popup_remote_skymap_downloader: None, - })), + popup_remote_skymap_downloader: Default::default(), }) } @@ -171,11 +164,7 @@ impl View { self.globals.clone(), entry.clone(), preview_image, - self.tasks.make_callback_box(Task::CloseRemoteSkymapDownloader), - Box::new({ - let state = self.state.clone(); - move |popup| state.borrow_mut().popup_remote_skymap_downloader = Some(popup) - }), + self.popup_remote_skymap_downloader.clone(), ); Ok(()) @@ -189,12 +178,9 @@ impl View { } pub fn update(&mut self, layout: &mut Layout) -> anyhow::Result<()> { - { - let mut state = self.state.borrow_mut(); - if let Some(view) = &mut state.popup_remote_skymap_downloader { - view.1.update(layout)?; - } - } + self + .popup_remote_skymap_downloader + .with_view_res(|view| view.update(layout))?; for task in self.tasks.drain() { match task { @@ -224,9 +210,6 @@ impl View { let preview_image = self.get_image_preview(skymap_uuid); self.show_remote_skymap_downloader(skymap_uuid, preview_image)?; } - Task::CloseRemoteSkymapDownloader => { - self.state.borrow_mut().popup_remote_skymap_downloader = None; - } } } Ok(()) @@ -237,8 +220,7 @@ pub fn mount_popup( frontend_tasks: FrontendTasks, executor: AsyncExecutor, globals: WguiGlobals, - on_close_request: Box, - set_holder: Box)>, + popup: PopupHolder, ) { frontend_tasks .clone() @@ -250,11 +232,11 @@ pub fn mount_popup( layout: data.layout, executor: &executor, parent_id: data.id_content, - on_close_request, + on_close_request: popup.get_close_callback(), frontend_tasks, })?; - set_holder((data.handle, view)); + popup.set_view(data.handle, view); Ok(()) }), ))); diff --git a/dash-frontend/src/views/skymap_list.rs b/dash-frontend/src/views/skymap_list.rs index fa33077b..634d21d5 100644 --- a/dash-frontend/src/views/skymap_list.rs +++ b/dash-frontend/src/views/skymap_list.rs @@ -1,5 +1,3 @@ -use std::{cell::RefCell, rc::Rc}; - use wgui::{ assets::AssetPath, components::button::ComponentButton, @@ -20,7 +18,6 @@ use crate::{ #[derive(Clone)] enum Task { DownloadSkymaps, - ClosePopupDownloadSkymaps, Refresh, } @@ -31,10 +28,6 @@ pub struct Params<'a> { pub frontend_tasks: &'a FrontendTasks, } -pub struct State { - popup_remote_skymap_list: Option>, -} - pub struct View { #[allow(dead_code)] parser_state: ParserState, @@ -42,7 +35,7 @@ pub struct View { list_parent: WidgetID, frontend_tasks: FrontendTasks, globals: WguiGlobals, - state: Rc>, + popup_remote_skymap_list: PopupHolder, } impl View { @@ -69,27 +62,20 @@ impl View { Task::Refresh, ); - let state = Rc::new(RefCell::new(State { - popup_remote_skymap_list: None, - })); - Ok(Self { parser_state, tasks, list_parent, frontend_tasks: params.frontend_tasks.clone(), - state, globals: params.globals.clone(), + popup_remote_skymap_list: Default::default(), }) } 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_remote_skymap_list { - popup.1.update(layout)?; - } - } + self + .popup_remote_skymap_list + .with_view_res(|view| view.update(layout))?; loop { let tasks = self.tasks.drain(); @@ -104,9 +90,6 @@ impl View { Task::Refresh => { self.refresh(layout)?; } - Task::ClosePopupDownloadSkymaps => { - (*self.state.borrow_mut()).popup_remote_skymap_list = None; - } } } } @@ -119,13 +102,7 @@ impl View { 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_remote_skymap_list = Some(popup); - } - }), + self.popup_remote_skymap_list.clone(), ); Ok(()) } diff --git a/uidev/src/testbed/testbed_generic.rs b/uidev/src/testbed/testbed_generic.rs index 802863f4..d1d078da 100644 --- a/uidev/src/testbed/testbed_generic.rs +++ b/uidev/src/testbed/testbed_generic.rs @@ -46,7 +46,6 @@ pub struct TestbedGeneric { pub parser_state: ParserState, tasks: Tasks, - globals: WguiGlobals, data: Rc>, } @@ -183,7 +182,6 @@ impl TestbedGeneric { layout, parser_state, tasks: Default::default(), - globals: globals.clone(), data: Rc::new(RefCell::new(Data { popup_window: WguiWindow::default(), context_menu: context_menu::ContextMenu::default(), diff --git a/wgui/src/components/color_selector.rs b/wgui/src/components/color_selector.rs index 935cf390..65d6a4e7 100644 --- a/wgui/src/components/color_selector.rs +++ b/wgui/src/components/color_selector.rs @@ -7,10 +7,9 @@ use crate::{ }, drawing::{self}, event::CallbackDataCommon, - globals::WguiGlobals, i18n::Translation, layout::{Layout, WidgetID, WidgetPair}, - parser::{self, Fetchable, ParseDocumentParams, ParserState}, + parser::{self, Fetchable, ParseDocumentParams}, widget::{ConstructEssentials, rectangle::WidgetRectangle, util::WLength}, windowing::window::{WguiWindow, WguiWindowParams, WguiWindowParamsExtra}, }; diff --git a/wgui/src/layout.rs b/wgui/src/layout.rs index f33c7d1d..5ee939b2 100644 --- a/wgui/src/layout.rs +++ b/wgui/src/layout.rs @@ -321,13 +321,8 @@ impl Layout { self.needs_redraw = true; } - fn process_pending_components(&mut self, alterables: &mut EventAlterables) { + fn process_pending_components(&mut self) { for comp in std::mem::take(&mut self.components_to_refresh_once) { - let mut common = CallbackDataCommon { - state: &self.state, - alterables, - }; - comp.0.refresh(&mut RefreshData { layout: self }); } } @@ -684,7 +679,7 @@ impl Layout { pub fn tick(&mut self) -> anyhow::Result<()> { let mut alterables = EventAlterables::default(); self.animations.tick(&self.state, &mut alterables); - self.process_pending_components(&mut alterables); + self.process_pending_components(); self.process_pending_widget_ticks(&mut alterables); self.process_alterables(alterables)?; Ok(()) diff --git a/wgui/src/windowing/window.rs b/wgui/src/windowing/window.rs index 79804456..328ef746 100644 --- a/wgui/src/windowing/window.rs +++ b/wgui/src/windowing/window.rs @@ -9,7 +9,6 @@ use crate::{ components::button::ComponentButton, drawing, event::{EventListenerKind, StyleSetRequest}, - globals::WguiGlobals, i18n::Translation, layout::{Layout, LayoutTask, LayoutTasks, WidgetPair}, parser::{self, Fetchable, ParserState},