dash-frontend: remove dangling popups immediately

This commit is contained in:
Aleksander 2026-04-12 12:59:29 +02:00 committed by galister
parent aee5bf69da
commit 24e3099b7b
6 changed files with 48 additions and 56 deletions

View File

@ -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::{MountPopupOnceParams, MountPopupParams, PopupManager, PopupManagerParams}, popup_manager::{MountPopupOnceParams, PopupManager, PopupManagerParams},
toast_manager::ToastManager, toast_manager::ToastManager,
}, },
views, views,
@ -101,7 +101,6 @@ pub enum FrontendTask {
SetTab(TabType), SetTab(TabType),
RefreshClock, RefreshClock,
RefreshBackground, RefreshBackground,
MountPopup(MountPopupParams),
MountPopupOnce(MountPopupOnceParams), MountPopupOnce(MountPopupOnceParams),
RefreshPopupManager, RefreshPopupManager,
ShowAudioSettings, ShowAudioSettings,
@ -305,15 +304,6 @@ impl<T: 'static> Frontend<T> {
Ok(()) Ok(())
} }
fn mount_popup(&mut self, params: MountPopupParams, data: &mut T) -> anyhow::Result<()> {
let config = self.interface.general_config(data);
self
.popup_manager
.mount_popup(&self.globals, &mut self.layout, &self.tasks, params, config)?;
Ok(())
}
fn mount_popup_once(&mut self, params: MountPopupOnceParams, data: &mut T) -> anyhow::Result<()> { fn mount_popup_once(&mut self, params: MountPopupOnceParams, data: &mut T) -> anyhow::Result<()> {
let config = self.interface.general_config(data); let config = self.interface.general_config(data);
@ -357,7 +347,6 @@ impl<T: 'static> Frontend<T> {
FrontendTask::SetTab(tab_type) => self.set_tab(params.data, tab_type)?, FrontendTask::SetTab(tab_type) => self.set_tab(params.data, tab_type)?,
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::MountPopupOnce(popup_params) => self.mount_popup_once(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()?,

View File

@ -37,6 +37,7 @@ pub struct MountedPopup {
#[derive(Default)] #[derive(Default)]
struct MountedPopupState { struct MountedPopupState {
mounted_popup: Option<MountedPopup>, mounted_popup: Option<MountedPopup>,
closed_callback: Option<PopupClosedCallback>,
} }
#[derive(Default, Clone)] #[derive(Default, Clone)]
@ -71,6 +72,13 @@ impl<ViewType> Default for PopupHolder<ViewType> {
} }
} }
impl<ViewType> PopupHolderState<ViewType> {
fn close(&mut self) {
self.view = None;
self.popup_handle.close();
}
}
// we can't derive(Clone) due to the fact that ViewType is non-cloneable // 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> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
@ -82,9 +90,7 @@ impl<ViewType> Clone for PopupHolder<ViewType> {
impl<ViewType> PopupHolder<ViewType> { impl<ViewType> PopupHolder<ViewType> {
pub fn close(&self) { pub fn close(&self) {
let mut state = self.state.borrow_mut(); self.state.borrow_mut().close();
state.view = None;
state.popup_handle.close();
} }
pub fn set_view(&self, handle: PopupHandle, view: ViewType) { pub fn set_view(&self, handle: PopupHandle, view: ViewType) {
@ -129,8 +135,12 @@ impl<ViewType> PopupHolder<ViewType> {
where where
ViewType: 'static, ViewType: 'static,
{ {
let this = self.clone(); let weak_state = Rc::downgrade(&self.state);
Box::new(move || this.close()) Box::new(move || {
if let Some(state) = weak_state.upgrade() {
state.borrow_mut().close();
}
})
} }
} }
@ -152,22 +162,21 @@ pub struct PopupContentFuncData<'a> {
pub id_content: WidgetID, pub id_content: WidgetID,
} }
#[derive(Clone)] type PopupClosedCallback = Box<dyn FnOnce()>;
pub struct MountPopupParams {
pub title: Translation,
pub on_content: Rc<dyn Fn(PopupContentFuncData) -> anyhow::Result<()>>,
}
// we need to implement Clone here, but the underlying function can be called only once. // we need to implement Clone here, but the underlying function can be called only once.
// on_content will be cleared after the first call // on_content will be cleared after the first call
#[derive(Clone)] #[derive(Clone)]
pub struct MountPopupOnceParams { pub struct MountPopupOnceParams {
title: Translation, title: Translation,
on_content: Rc<RefCell<Option<Box<dyn FnOnce(PopupContentFuncData) -> anyhow::Result<()>>>>>, on_content: Rc<RefCell<Option<Box<dyn FnOnce(PopupContentFuncData) -> anyhow::Result<PopupClosedCallback>>>>>,
} }
impl MountPopupOnceParams { impl MountPopupOnceParams {
pub fn new(title: Translation, on_content: Box<dyn FnOnce(PopupContentFuncData) -> anyhow::Result<()>>) -> Self { pub fn new(
title: Translation,
on_content: Box<dyn FnOnce(PopupContentFuncData) -> anyhow::Result<PopupClosedCallback>>,
) -> Self {
Self { Self {
title, title,
on_content: Rc::new(RefCell::new(Some(on_content))), on_content: Rc::new(RefCell::new(Some(on_content))),
@ -186,10 +195,16 @@ impl State {
fn refresh_stack(&mut self, alterables: &mut EventAlterables) { fn refresh_stack(&mut self, alterables: &mut EventAlterables) {
// show only the topmost popup // show only the topmost popup
self.popup_stack.retain(|weak| { self.popup_stack.retain(|weak| {
let Some(popup) = weak.upgrade() else { let retain = {
return false; let Some(popup) = weak.upgrade() else {
return false;
};
popup.borrow_mut().mounted_popup.is_some()
}; };
popup.borrow_mut().mounted_popup.is_some() if !retain {
log::debug!("removing popup from popup_stack");
}
retain
}); });
for (idx, popup) in self.popup_stack.iter().enumerate() { for (idx, popup) in self.popup_stack.iter().enumerate() {
@ -257,6 +272,7 @@ impl PopupManager {
let mounted_popup_state = MountedPopupState { let mounted_popup_state = MountedPopupState {
mounted_popup: Some(mounted_popup), mounted_popup: Some(mounted_popup),
closed_callback: None,
}; };
let popup_handle = PopupHandle { let popup_handle = PopupHandle {
@ -264,13 +280,21 @@ impl PopupManager {
}; };
let mut state = self.state.borrow_mut(); let mut state = self.state.borrow_mut();
log::debug!("pushing popup to popup_stack");
state.popup_stack.push(Rc::downgrade(&popup_handle.state)); state.popup_stack.push(Rc::downgrade(&popup_handle.state));
but_back.on_click({ but_back.on_click({
let popup_handle = Rc::downgrade(&popup_handle.state); let popup_handle = Rc::downgrade(&popup_handle.state);
Rc::new(move |_common, _evt| { Rc::new(move |_common, _evt| {
if let Some(popup_handle) = popup_handle.upgrade() { if let Some(popup_handle) = popup_handle.upgrade() {
popup_handle.borrow_mut().mounted_popup = None; // will call Drop if let Some(closed_callback) = {
let mut state = popup_handle.borrow_mut();
state.mounted_popup = None; // will call Drop
state.closed_callback.take()
} {
log::debug!("closed_callback called");
closed_callback();
}
} }
Ok(()) Ok(())
}) })
@ -280,7 +304,7 @@ impl PopupManager {
Ok((popup_handle, id_content)) Ok((popup_handle, id_content))
} }
/// Mount a new popup on top of the existing popup stack (non-cloneable version). /// Mount a new popup on top of the existing popup stack.
/// Only the topmost popup is visible. /// Only the topmost popup is visible.
pub fn mount_popup_once( pub fn mount_popup_once(
&mut self, &mut self,
@ -298,35 +322,14 @@ impl PopupManager {
let (popup_handle, id_content) = self.mount_popup_prepare(globals, layout, frontend_tasks, &params.title)?; let (popup_handle, id_content) = self.mount_popup_prepare(globals, layout, frontend_tasks, &params.title)?;
// mount user-set popup content // mount user-set popup content
on_content_func(PopupContentFuncData { let closed_callback = on_content_func(PopupContentFuncData {
layout, layout,
handle: popup_handle.clone(), handle: popup_handle.clone(),
id_content, id_content,
config, config,
})?; })?;
Ok(()) popup_handle.state.borrow_mut().closed_callback = Some(closed_callback);
}
/// 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, &params.title)?;
// mount user-set popup content
(*params.on_content)(PopupContentFuncData {
layout,
handle: popup_handle.clone(),
id_content,
config,
})?;
Ok(()) Ok(())
} }

View File

@ -441,7 +441,7 @@ pub fn mount_popup(frontend_tasks: FrontendTasks, globals: WguiGlobals, entry: D
})?; })?;
popup.set_view(data.handle, view); popup.set_view(data.handle, view);
Ok(()) Ok(popup.get_close_callback())
}), }),
))); )));
} }

View File

@ -216,7 +216,7 @@ pub fn mount_popup(
})?; })?;
popup.set_view(data.handle, view); popup.set_view(data.handle, view);
Ok(()) Ok(popup.get_close_callback())
}), }),
))); )));
} }

View File

@ -187,7 +187,7 @@ pub fn mount_popup(
})?; })?;
popup.set_view(data.handle, view); popup.set_view(data.handle, view);
Ok(()) Ok(popup.get_close_callback())
}), }),
))); )));
} }

View File

@ -237,7 +237,7 @@ pub fn mount_popup(
})?; })?;
popup.set_view(data.handle, view); popup.set_view(data.handle, view);
Ok(()) Ok(popup.get_close_callback())
}), }),
))); )));
} }