dash-frontend: popup manager

This commit is contained in:
Aleksander
2025-11-27 23:28:10 +01:00
parent 192ffab4fd
commit 7d581c1561
12 changed files with 259 additions and 13 deletions

View File

@@ -80,6 +80,7 @@
<rectangle <rectangle
position="absolute" width="100%" height="100%" position="absolute" width="100%" height="100%"
gradient="radial" color="#44BBFF22" color2="#00000000" /> gradient="radial" color="#44BBFF22" color2="#00000000" />
<div <div
id="content" id="content"
flex_direction="column" flex_direction="column"
@@ -95,6 +96,9 @@
> >
<!-- filled-in at runtime --> <!-- filled-in at runtime -->
</div> </div>
<div position="absolute" id="popup_manager" width="100%" height="100%" />
</rectangle> </rectangle>
<!-- BOTTOM PANEL --> <!-- BOTTOM PANEL -->
<rectangle <rectangle

View File

@@ -0,0 +1,7 @@
<layout>
<elements>
<rectangle new_pass="1" color="#ff000099" width="100%" height="100%">
<label text="TEST" size="80" />
</rectangle>
</elements>
</layout>

View File

@@ -0,0 +1,45 @@
<layout>
<include src="../theme.xml" />
<elements>
<div
id="root"
new_pass="1"
width="100%"
height="100%"
flex_direction="column"
>
<!-- Top black bar -->
<rectangle
position="relative"
color="#000000"
round="4"
width="100%" height="48"
>
<!-- Shine effect at the top -->
<rectangle position="absolute" width="100%" height="2" round="4" color="#ffffff55" />
<!-- Top bar contents -->
<div gap="16" align_items="center">
<!-- Back button -->
<Button id="but_back" width="48" height="48" color="#ffffff00" border_color="#ffffff00">
<sprite src="dashboard/back.svg" width="24" height="24" />
</Button>
<!-- Title -->
<label id="popup_title" weight="bold" size="18" text="Pop-up title" />
</div>
</rectangle>
<!-- Content -->
<rectangle width="100%" height="100%"
color="#010310cc"
color2="#061e4acc"
gradient="vertical"
padding="16"
id="content">
</rectangle>
</div>
</elements>
</layout>

View File

@@ -19,8 +19,14 @@ use crate::{
Tab, TabParams, TabType, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, processes::TabProcesses, Tab, TabParams, TabType, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, processes::TabProcesses,
settings::TabSettings, settings::TabSettings,
}, },
util::popup_manager::{PopupManager, PopupManagerParams},
}; };
pub struct FrontendWidgets {
pub id_label_time: WidgetID,
pub id_rect_content: WidgetID,
}
pub struct Frontend { pub struct Frontend {
pub layout: RcLayout, pub layout: RcLayout,
globals: WguiGlobals, globals: WguiGlobals,
@@ -36,8 +42,8 @@ pub struct Frontend {
ticks: u32, ticks: u32,
id_label_time: WidgetID, widgets: FrontendWidgets,
id_rect_content: WidgetID, popup_manager: PopupManager,
} }
pub struct InitParams { pub struct InitParams {
@@ -50,6 +56,7 @@ pub enum FrontendTask {
SetTab(TabType), SetTab(TabType),
RefreshClock, RefreshClock,
RefreshBackground, RefreshBackground,
MountPopup,
} }
impl Frontend { impl Frontend {
@@ -71,7 +78,7 @@ impl Frontend {
}, },
)?; )?;
let (layout, state) = wgui::parser::new_layout_from_assets( let (mut layout, state) = wgui::parser::new_layout_from_assets(
&ParseDocumentParams { &ParseDocumentParams {
globals: globals.clone(), globals: globals.clone(),
path: AssetPath::BuiltIn("gui/dashboard.xml"), path: AssetPath::BuiltIn("gui/dashboard.xml"),
@@ -80,6 +87,13 @@ impl Frontend {
&LayoutParams { resize_to_parent: true }, &LayoutParams { resize_to_parent: true },
)?; )?;
let id_popup_manager = state.get_widget_id("popup_manager")?;
let popup_manager = PopupManager::new(PopupManagerParams {
globals: globals.clone(),
layout: &mut layout,
parent_id: id_popup_manager,
})?;
let rc_layout = layout.as_rc(); let rc_layout = layout.as_rc();
let mut tasks = VecDeque::<FrontendTask>::new(); let mut tasks = VecDeque::<FrontendTask>::new();
@@ -95,9 +109,12 @@ impl Frontend {
globals, globals,
tasks, tasks,
ticks: 0, ticks: 0,
widgets: FrontendWidgets {
id_label_time, id_label_time,
id_rect_content, id_rect_content,
},
settings: params.settings, settings: params.settings,
popup_manager,
}; };
// init some things first // init some things first
@@ -142,7 +159,7 @@ impl Frontend {
let mut common = c.common(); let mut common = c.common();
{ {
let Some(mut label) = common.state.widgets.get_as::<WidgetLabel>(self.id_label_time) else { let Some(mut label) = common.state.widgets.get_as::<WidgetLabel>(self.widgets.id_label_time) else {
anyhow::bail!(""); anyhow::bail!("");
}; };
@@ -165,10 +182,22 @@ impl Frontend {
Ok(()) Ok(())
} }
fn mount_popup(&mut self) -> anyhow::Result<()> {
let mut layout = self.layout.borrow_mut();
self.popup_manager.push_popup(self.globals.clone(), &mut layout)?;
Ok(())
}
fn update_background(&self) -> anyhow::Result<()> { fn update_background(&self) -> anyhow::Result<()> {
let layout = self.layout.borrow_mut(); let layout = self.layout.borrow_mut();
let Some(mut rect) = layout.state.widgets.get_as::<WidgetRectangle>(self.id_rect_content) else { let Some(mut rect) = layout
.state
.widgets
.get_as::<WidgetRectangle>(self.widgets.id_rect_content)
else {
anyhow::bail!(""); anyhow::bail!("");
}; };
@@ -197,6 +226,7 @@ impl Frontend {
FrontendTask::SetTab(tab_type) => self.set_tab(tab_type, rc_this)?, FrontendTask::SetTab(tab_type) => self.set_tab(tab_type, rc_this)?,
FrontendTask::RefreshClock => self.update_time()?, FrontendTask::RefreshClock => self.update_time()?,
FrontendTask::RefreshBackground => self.update_background()?, FrontendTask::RefreshBackground => self.update_background()?,
FrontendTask::MountPopup => self.mount_popup()?,
} }
Ok(()) Ok(())
} }
@@ -212,6 +242,7 @@ impl Frontend {
layout: &mut layout, layout: &mut layout,
parent_id: widget_content.id, parent_id: widget_content.id,
frontend: rc_this, frontend: rc_this,
frontend_widgets: &self.widgets,
settings: self.settings.get_mut(), settings: self.settings.get_mut(),
}; };

View File

@@ -4,3 +4,4 @@ pub mod settings;
mod tab; mod tab;
mod util; mod util;
mod various; mod various;
mod views;

View File

@@ -8,8 +8,10 @@ use wgui::{
}; };
use crate::{ use crate::{
frontend::FrontendTask,
tab::{Tab, TabParams, TabType}, tab::{Tab, TabParams, TabType},
util::{self, desktop_finder::DesktopEntry}, util::{self, desktop_finder::DesktopEntry},
views,
}; };
pub struct TabApps { pub struct TabApps {
@@ -95,10 +97,14 @@ impl AppList {
let data = parser_state.parse_template(doc_params, "AppEntry", params.layout, list_parent.id, template_params)?; let data = parser_state.parse_template(doc_params, "AppEntry", params.layout, list_parent.id, template_params)?;
let button = data.fetch_component_as::<ComponentButton>("button")?; let button = data.fetch_component_as::<ComponentButton>("button")?;
button.on_click(Box::new(move |_common, _evt| {
log::info!("click"); button.on_click({
let frontend = params.frontend.clone();
Box::new(move |_common, _evt| {
frontend.borrow_mut().push_task(FrontendTask::MountPopup);
Ok(()) Ok(())
})); })
});
self.data.push(data); self.data.push(data);

View File

@@ -6,7 +6,7 @@ use wgui::{
layout::{Layout, WidgetID}, layout::{Layout, WidgetID},
}; };
use crate::frontend::{FrontendTask, RcFrontend}; use crate::frontend::{FrontendTask, FrontendWidgets, RcFrontend};
pub mod apps; pub mod apps;
pub mod games; pub mod games;
@@ -30,6 +30,7 @@ pub struct TabParams<'a> {
pub layout: &'a mut Layout, pub layout: &'a mut Layout,
pub parent_id: WidgetID, pub parent_id: WidgetID,
pub frontend: &'a RcFrontend, pub frontend: &'a RcFrontend,
pub frontend_widgets: &'a FrontendWidgets,
pub settings: &'a mut crate::settings::Settings, pub settings: &'a mut crate::settings::Settings,
} }

View File

@@ -1,7 +1,7 @@
use gio::prelude::{AppInfoExt, IconExt}; use gio::prelude::{AppInfoExt, IconExt};
use gtk::traits::IconThemeExt; use gtk::traits::IconThemeExt;
#[derive(Debug)] #[derive(Debug, Clone)]
#[allow(dead_code)] // TODO: remove this #[allow(dead_code)] // TODO: remove this
pub struct DesktopEntry { pub struct DesktopEntry {
pub exec_path: String, pub exec_path: String,

View File

@@ -1 +1,2 @@
pub mod desktop_finder; pub mod desktop_finder;
pub mod popup_manager;

View File

@@ -0,0 +1,110 @@
use std::{
cell::RefCell,
rc::{Rc, Weak},
};
use wgui::{
assets::AssetPath,
components::button::ComponentButton,
event::{EventAlterables, StyleSetRequest},
globals::WguiGlobals,
layout::{Layout, LayoutTask, WidgetID},
parser::{Fetchable, ParseDocumentParams, ParserState},
taffy::Display,
};
pub struct PopupManagerParams<'a> {
pub globals: WguiGlobals,
pub layout: &'a mut Layout,
pub parent_id: WidgetID,
}
struct MountedPopup {
#[allow(dead_code)]
state: ParserState,
id_root: WidgetID,
}
pub struct State {
popup_stack: Vec<MountedPopup>,
}
pub struct PopupManager {
pub state: Rc<RefCell<State>>,
globals: WguiGlobals,
parent_id: WidgetID,
}
pub struct PushPopupResult {
pub id_content: WidgetID,
}
impl State {
fn refresh_stack(&mut self, alterables: &mut EventAlterables) {
// show only the topmost popup
for (idx, popup) in self.popup_stack.iter().enumerate() {
alterables.set_style(
popup.id_root,
StyleSetRequest::Display(if idx == self.popup_stack.len() - 1 {
Display::Flex
} else {
Display::None
}),
);
}
}
fn pop_popup(&mut self, alterables: &mut EventAlterables) {
let Some(popup) = self.popup_stack.pop() else {
return;
};
alterables.tasks.push(LayoutTask::RemoveWidget(popup.id_root));
self.refresh_stack(alterables);
}
}
impl PopupManager {
pub fn new(params: PopupManagerParams) -> anyhow::Result<Self> {
Ok(Self {
globals: params.globals,
parent_id: params.parent_id,
state: Rc::new(RefCell::new(State {
popup_stack: Vec::new(),
})),
})
}
pub fn push_popup(&mut self, globals: WguiGlobals, layout: &mut Layout) -> anyhow::Result<PushPopupResult> {
let doc_params = &ParseDocumentParams {
globals,
path: AssetPath::BuiltIn("gui/view/popup_window.xml"),
extra: Default::default(),
};
let state = wgui::parser::parse_from_assets(doc_params, layout, self.parent_id)?;
let id_root = state.get_widget_id("root")?;
let id_content = state.get_widget_id("content")?;
let but_back = state.fetch_component_as::<ComponentButton>("but_back")?;
but_back.on_click({
let state = self.state.clone();
Box::new(move |common, _evt| {
state.borrow_mut().pop_popup(common.alterables);
Ok(())
})
});
let mounted_popup = MountedPopup { state, id_root };
let mut state = self.state.borrow_mut();
state.popup_stack.push(mounted_popup);
let mut c = layout.start_common();
state.refresh_stack(c.common().alterables);
c.finish()?;
Ok(PushPopupResult { id_content })
}
}

View File

@@ -0,0 +1,39 @@
use wgui::{
assets::AssetPath,
globals::WguiGlobals,
layout::{Layout, WidgetID},
parser::{ParseDocumentParams, ParserState},
};
use crate::util::desktop_finder::DesktopEntry;
pub struct View {
#[allow(dead_code)]
pub state: ParserState,
entry: DesktopEntry,
}
pub struct Params<'a> {
pub globals: WguiGlobals,
pub entry: DesktopEntry,
pub layout: &'a mut Layout,
pub parent_id: WidgetID,
}
impl View {
pub fn new(params: Params) -> anyhow::Result<Self> {
let doc_params = &ParseDocumentParams {
globals: params.globals.clone(),
path: AssetPath::BuiltIn("gui/view/app_launcher.xml"),
extra: Default::default(),
};
let mut state = wgui::parser::parse_from_assets(doc_params, params.layout, params.parent_id)?;
Ok(Self {
entry: params.entry,
state,
})
}
}

View File

@@ -0,0 +1 @@
pub mod app_launcher;